【Graph Net学习】LINE实现Graph Embedding

2023-09-22 00:09:03

一、简介

        LINE (Large-scale Information Network Embedding,2015) 是一种设计用于处理大规模信息网络的算法。它主要的目标是在给定的大规模信息网络中学习高质量的节点嵌入,并尽量保留网络中信息的丰富性。其具体的表现为在一个低 维空间里以向量形式表示网络中的节点,以便后续的机器学习任务可以更好地理解。

LINE算法根据两种相互关联的线性化策略去处理信息图,分别考虑了图节点的一阶邻居和二阶邻居。通过这种方式,LINE既能反映出网络的全局属性又能反映出网络的局部属性。

        调用算法流程如下:

  1. 首先,为图中的每个节点初始化一个随机向量。

  2. 接着,使用一阶邻居的优化原型函数进行训练。在一阶近邻策略中,若两个节点存在直接连接,则他们的向量应该尽可能相近。

  3. 然后,使用二阶邻居的优化原型函数进行训练。在二阶近邻策略中,考虑两节点间的间接联系。例如,若两节点存在共享的邻居,即使他们之间没有直接的联系,他们的向量也应该相近。

  4. 对每个节点,计算其在一阶和二阶优化下的损失函数值,并对其进行优化。

  5. 优化完成后,此时每个节点上的向量就是最终的嵌入表示。

  6. 基于得到的嵌入表示进行后续的分析或机器学习任务。

        接下来就是快乐的代码时间嘿嘿嘿

二、代码

import os
import pandas as pd
import numpy as np
import networkx as nx
import time
import scipy.sparse as sp
from torch_geometric.data import Data
from torch_geometric.transforms import ToSparseTensor
import torch_geometric.utils
from sklearn.preprocessing import LabelEncoder

import torch
import torch.nn as nn

#配置项
class configs():
    def __init__(self):
        # Data
        self.data_path = r'./data'
        self.save_model_dir = r'./'
        self.num_nodes = 2708
        self.embedding_dim = 128
        self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

        self.learning_rate = 0.01
        self.epoch = 30
        self.criterion = nn.BCEWithLogitsLoss()

        self.istrain = True
        self.istest = True

cfg = configs()

def load_cora_data(data_path = './data/cora'):

    content_df = pd.read_csv(os.path.join(data_path,"cora.content"), delimiter="\t", header=None)
    content_df.set_index(0, inplace=True)
    index = content_df.index.tolist()
    features = sp.csr_matrix(content_df.values[:,:-1], dtype=np.float32)

    # 处理标签
    labels = content_df.values[:,-1]
    class_encoder = LabelEncoder()
    labels = class_encoder.fit_transform(labels)

    # 读取引用关系
    cites_df = pd.read_csv(os.path.join(data_path,"cora.cites"), delimiter="\t", header=None)
    cites_df[0] = cites_df[0].astype(str)
    cites_df[1] = cites_df[1].astype(str)
    cites = [tuple(x) for x in cites_df.values]
    edges = [(index.index(int(cite[0])), index.index(int(cite[1]))) for cite in cites]
    edges = np.array(edges).T

    # 构造Data对象
    data = Data(x=torch.from_numpy(np.array(features.todense())),
                edge_index=torch.LongTensor(edges),
                y=torch.from_numpy(labels))

    idx_train = range(140)
    idx_val = range(200, 500)
    idx_test = range(500, 1500)

    # 读取Cora数据集 return geometric Data格式
    def index_to_mask(index, size):
        mask = np.zeros(size, dtype=bool)
        mask[index] = True
        return mask

    data.train_mask = index_to_mask(idx_train, size=labels.shape[0])
    data.val_mask = index_to_mask(idx_val, size=labels.shape[0])
    data.test_mask = index_to_mask(idx_test, size=labels.shape[0])

    def to_networkx(data):
        edge_index = data.edge_index.to(torch.device('cpu')).numpy()
        G = nx.DiGraph()
        for src, tar in edge_index.T:
            G.add_edge(src, tar)
        return G

    networkx_data = to_networkx(data)

    return data,networkx_data
#获取数据:pyg_data:torch_geometric格式;networkx_data:networkx格式

def generate_pairs(adj_matrix):
    # 根据邻接矩阵生成正例和负例
    pos_pairs = torch.nonzero(adj_matrix, as_tuple=True)
    pos_u = pos_pairs[0]
    pos_v = pos_pairs[1]

    mask = torch.ones_like(adj_matrix)
    for i in range(len(pos_u)):
        mask[pos_u[i]][pos_v[i]] = 0
        mask[pos_v[i]][pos_u[i]] = 0

    tmp = torch.nonzero(mask, as_tuple=True)

    #TODO 随机选取负例
    idx = torch.randperm(tmp[0].size(0))
    neg_u = tmp[0][idx][:pos_u.size(0)]
    neg_v = tmp[1][idx][:pos_v.size(0)]

    return pos_u, pos_v, neg_u, neg_v

# 构建LINE网络
class LINE(nn.Module):
    def __init__(self, num_nodes, embed_dim):
        super(LINE, self).__init__()
        #num_nodes为Node个数 , embed_dim为描述Node的Embedding维度
        self.embed_dim = embed_dim
        self.num_nodes = num_nodes
        self.embeddings = nn.Embedding(self.num_nodes, self.embed_dim)
        self.reset_parameters()

    def reset_parameters(self):
        self.embeddings.weight.data.normal_(std=1 / self.embed_dim)

    def forward(self, pos_u, pos_v, neg_v):
        emb_pos_u = self.embeddings(pos_u)
        emb_pos_v = self.embeddings(pos_v)
        emb_neg_v = self.embeddings(neg_v)

        pos_scores = torch.sum(torch.mul(emb_pos_u, emb_pos_v), dim=1)
        neg_scores = torch.sum(torch.mul(emb_pos_u, emb_neg_v), dim=1)

        return pos_scores, neg_scores


class LINE_run():
    def train(self):
        t = time.time()
        # 创建一个模型
        _, networkx_data = load_cora_data()
        adj_matrix = torch.tensor(
            nx.adjacency_matrix(networkx_data).toarray()
            , dtype=torch.float32)
        model = LINE(num_nodes=cfg.num_nodes, embed_dim=cfg.embedding_dim).to(cfg.device)
        optimizer = torch.optim.Adam(model.parameters(), lr=cfg.learning_rate)

        #Train
        model.train()
        for epoch in range(cfg.epoch):
            optimizer.zero_grad()
            pos_u, pos_v, neg_u, neg_v = generate_pairs(adj_matrix)
            pos_u = pos_u.to(cfg.device)
            pos_v = pos_v.to(cfg.device)
            neg_v = neg_v.to(cfg.device)
            pos_scores, neg_scores = model(pos_u, pos_v, neg_v)
            pos_losses = cfg.criterion(pos_scores, torch.ones(len(pos_scores)).to(cfg.device))
            neg_losses = cfg.criterion(neg_scores, torch.zeros(len(neg_scores)).to(cfg.device))
            loss = pos_losses + neg_losses
            loss.backward()
            optimizer.step()
            print('Epoch: {:04d}'.format(epoch + 1),
                  'loss_train: {:.4f}'.format(loss.item()),
                  'time: {:.4f}s'.format(time.time() - t))
        torch.save(model, os.path.join(cfg.save_model_dir, 'latest.pth'))  # 模型保存
        print('Embedding dim : ({},{})'.format(model.embeddings.weight.shape[0],model.embeddings.weight.shape[1]))

    def infer(self):
        # Create Test Processing
        _, networkx_data = load_cora_data()
        adj_matrix = torch.tensor(
            nx.adjacency_matrix(networkx_data).toarray()
            , dtype=torch.float32)

        model_path = os.path.join(cfg.save_model_dir, 'latest.pth')
        model = torch.load(model_path, map_location=torch.device(cfg.device))
        model.eval()

        _, networkx_data = load_cora_data()
        pos_u, pos_v, neg_u, neg_v = generate_pairs(adj_matrix)
        pos_u = pos_u.to(cfg.device)
        pos_v = pos_v.to(cfg.device)
        neg_v = neg_v.to(cfg.device)
        pos_scores, neg_scores = model(pos_u, pos_v, neg_v)
        pos_losses = cfg.criterion(pos_scores, torch.ones(len(pos_scores)).to(cfg.device))
        neg_losses = cfg.criterion(neg_scores, torch.zeros(len(neg_scores)).to(cfg.device))
        loss = pos_losses + neg_losses
        print("Test set results:",
              "loss= {:.4f}".format(loss.item()),
              'Embedding dim : ({},{})'.format(model.embeddings.weight.shape[0], model.embeddings.weight.shape[1]))


if __name__ == '__main__':
    mygraph = LINE_run()
    if cfg.istrain == True:
        mygraph.train()
    if cfg.istest == True:
        mygraph.infer()

三、输出结果

        跑的是Cora数据,共2708个Node,设置的Embedding维度是128维。上面代码运行完就是长下面这个样子。

Epoch: 0001 loss_train: 1.3863 time: 3.0867s
Epoch: 0002 loss_train: 1.3832 time: 3.7739s
Epoch: 0003 loss_train: 1.3768 time: 4.4471s
...
Epoch: 0028 loss_train: 0.7739 time: 21.3568s
Epoch: 0029 loss_train: 0.7694 time: 22.0310s
Epoch: 0030 loss_train: 0.7663 time: 22.7042s
Embedding dim : (2708,128)
Test set results: loss= 0.7609 Embedding dim : (2708,128)

        效果未知,没有用下游聚类测一下,反正看起来BCE loss是降了哈哈,这期就到这里。
        

更多推荐

java版工程管理系统Spring Cloud+Spring Boot+Mybatis实现工程管理系统源码

工程项目管理软件(工程项目管理系统)对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营,全过程、全方位的对项目进行综合管理工程项目各模块及其功能点清单一、系统管理1、数据字典:实现对数据字典标签的增删改查操作2、编码管理:实现对系统编码的增删改查操作3、用户管理:管理和查看用户角

高云FPGA系列教程(10):letter-shell移植

文章目录letter-shell简介letter-shell源码获取letter-shell移植函数和变量应用示例本文是高云FPGA系列教程的第10篇文章。shell,中文是外壳的意思,就是操作系统的外壳。通过shell命令可以操作和控制操作系统,比如Linux中的Shell命令就包括ls、cd、pwd等等。总结来说,

OpenCV(四十五):ORB特征点

1.特征点的组成特征点由关键点和描述子两部分组成:关键点是在图像中检测到的具有显著变化的位置坐标。描述子是用于表示关键点周围局部区域特征的向量或特征向量。2.ORB特征点原理ORB特征点由关键点FAST角点和描述子BRIEF组成。2.1提取FAST角点的算法FAST算法的思想:通过比较像素相对于中心像素有较大差别(过亮

flutter聊天界面-TextField输入框buildTextSpan实现@功能展示高亮功能

flutter聊天界面-TextField输入框buildTextSpan实现@功能展示高亮功能最近有位朋友讨论的时候,提到了输入框的高亮展示。在flutterTextField中需要插入特殊样式的标签,比如:“请@张三回答一下”,这一串字符在TextField中输入,当输入@时弹出好友列表选择,然后将“@张三”高亮显

用Vite从零到一创建React+ts项目

方式一:使用create-react-app命令创建项目1、使用以下命令初始化一个空的npm项目npminit-y2、输入以下命令安装Reactnpmicreate-react-appps:如果失败的话尝试(1:使用管理员身份执行命令(2:切换镜像重试3、输入以下命令创建项目create-react-app项目文件夹名

flask 插件 Flask-RESTful

1、安装pipinstallflask-restful2、使用HelloWorld一个简单的例子:#-*-coding:utf-8-*-fromflaskimportFlaskfromflask_restfulimportResource,Apiapp=Flask(__name__)api=Api(app)classH

解决react使用redux toolkits时出现的数组对象长度始终为0的怪异问题

有个react项目在添加购物车后,立马白屏,看一下console报错propertiesofundefined(readinglength)那意思是说数组没有长度,然后定位Header.tsx的182行,果然是数组长度报错回到具体代码中:发现shoppingCartItems实际是通过reduxToolkit(RTK)

大模型从入门到应用——LangChain:代理(Agents)-[工具(Tools):人工确认工具验证和Tools作为OpenAI函数]

分类目录:《大模型从入门到应用》总目录LangChain系列文章:基础知识快速入门安装与环境配置链(Chains)、代理(Agent:)和记忆(Memory)快速开发聊天模型模型(Models)基础知识大型语言模型(LLMs)基础知识LLM的异步API、自定义LLM包装器、虚假LLM和人类输入LLM(HumanInpu

Pytorch-YOLOv4梳理——原理和复现

yolov1到yolov3的梳理:YOLO总结,从YOLOv1到YOLOv3_追忆苔上雪的博客-CSDN博客首先说一点,就是yolov4的分支有点多,先梳理一下出现的顺序。AlexeyBochkovskiy提出了YOLOv4然后针对YOLOv4的模型缩放(modelscale),提出了Scaled-YOLOv4Scal

黑马JVM总结(八)

(1)StringTable面试题1.81.6时(2)StringTable的位置jvm1.6时StringTable是常量池的一部分,它随着常量池存储在永久代当中,在1.7、1.8中从永久代变成了堆中,为什么做这个更改呢?因为永久代的内存效率很低,永久代是在FullGC的时候才会触发永久代的垃圾回收,FullGC只有

Archicad 26 for Mac - 打造卓越的3D建模工具

随着建筑设计和规划的日益复杂化,寻找一款功能强大且易于使用的3D建模工具变得至关重要。而Archicad26forMac正是您在建筑设计领域中的理想选择。无论您是一名建筑师、室内设计师还是建筑工程师,Archicad26都将成为您的得力助手。作为一款全球领先的BIM(建筑信息模型)软件,Archicad26forMac

热文推荐