网络表示学习系列Network Embedding / Graph Representation:教十分钟一步步实现DeepWalk 模型 KDD'14

DeepWalk : DeepWalk: Online Learning of Social Representations发表于KDD 14年。

网络表示学习最近两年非常火热,这里的网络Network和图Graph意思相同,不再做区分。网络表示学习故名思义,是面向网络结构节点或者整个图的表示学习。

输入一个G=(V,E),V包含了网络中的节点,E包含了网络中的连边。传统的网络表表达方法,我们只需要,一个邻接矩阵A来表达整个网络的连边关系。网络表示学习的基础任务是给每个在V中的节点学习一个向量,当然也可以为E中的每条边学习一个向量表达。

网络表示学习是很早之前就有的任务,但是自从在顶级会议KDD 14年,DeepWalk提出来之后才渐渐火起来,到这两年火得一塌糊涂。如果你还不了解真的Out了。

话不多说,直接教你一步步实现DeepWalk模型,不用太久,只需要十分钟。

1.Word2vec

前提是需要了解Word2vec,不熟悉里面的公式也没关系,起码知道Word2vec的输入输出。输入是一堆训练的文字序列,输出是每个词语都有一个表示学习的向量。

例如:输入

[ ['I', 'Love', 'Food'],
  ['I', 'Like', 'Playing', 'Football']]

得到: 'Love'和‘Playing’的表示向量会相对较近。

2.Random Walk

随机游走,输入是一个图,这里用Networkx的图模型来表达,不熟悉Networkx的也没关系,可以看我写的Networkx系列。

我们就采用常用的BlogCatatlog数据,

导入数据

import math
import networkx as nx
import random
import numpy as np

file_name = 'data/blogCatalog/bc_edgelist.txt'
graph_format = 'edgelist'
if graph_format == 'edgelist':
    g = nx.read_edgelist(file_name)
    for edge in g.edges():
        u, v = edge[0], edge[1]
        g[u][v]['weight'] = 1.0

len(g)
# 10312

随机采样:每条长度 walk_length = 80,每个节点采样 number_walks = 15

def deepwalk_walk(g, walk_length=40, start_node=None):
    walks = [start_node]
    while len(walks) < walk_length:
        cur = walks[-1]
        cur_nbs = list(g.neighbors(cur))
        if len(cur_nbs) > 0:
            walks.append(random.choice(cur_nbs))
        else:
            raise ValueError('node with 0 in_degree')
    return walks

def sample_walks(g, walk_length=40, number_walks=10, shuffle=True):
    total_walks = []
    print('Start sampling walks:')
    nodes = list(g.nodes())
    for iter_ in range(number_walks):
        print('\t iter:{} / {}'.format(iter_+1, number_walks))
        if shuffle:
            random.shuffle(nodes)
        for node in nodes:
            total_walks.append(deepwalk_walk(g, walk_length, node))
    return total_walks
walk_length = 80
number_walks = 15
total_walks = sample_walks(g, walk_length=walk_length, number_walks=number_walks)

Start sampling walks:
	 iter:1 / 15
	 iter:2 / 15
	 iter:3 / 15
	 iter:4 / 15
	 iter:5 / 15
	 iter:6 / 15
	 iter:7 / 15
	 iter:8 / 15
	 iter:9 / 15
	 iter:10 / 15
	 iter:11 / 15
	 iter:12 / 15
	 iter:13 / 15
	 iter:14 / 15
	 iter:15 / 15

 

3. 训练

采样完路径后开始训练,训练非常简单,这里使用gensim。注意新版的gensim在设置workers时可能会出错。例如把workers=-1.

from gensim.models import Word2Vec
emb_size = 128
window_size = 10
model_w2v = Word2Vec(sentences=total_sents, sg=1, hs=0, size=emb_size, window=window_size,\
                     min_count=0, workers=10, iter=10)

训练好了,可以检查任何节点在嵌入空间中的表达,例如:

model_w2v['0'],会输出一个128维的向量。

array([-2.4944900e-02,  8.3439365e-02, -3.0993205e-01, -1.3582650e-01,
        5.8874243e-01, -1.2916335e-01,  1.1895838e-01, -6.6258423e-03,
        7.1198274e-03, -3.4604724e-02,  2.7921599e-01,  2.0141172e-01,
        2.5975418e-01, -1.3706718e-01,  1.4594914e-01,  3.4300745e-01,...])

4.测试效果

读入label,建立一个罗辑回归分类模型,并且嵌入一个multi-label的分类器中,测试效果:

def read_node_label(filename):
    fin = open(filename, 'r')
    X = []
    Y = []
    while 1:
        l = fin.readline()
        if l == '':
            break
        vec = l.strip().split(' ')
        X.append(vec[0])
        Y.append(vec[1:])
    fin.close()
    return X, Y


from sklearn.linear_model import LogisticRegression
file_name = 'data/blogCatalog/bc_labels.txt'
X, Y = read_node_label(file_name)
clf = Classifier(vectors=model_w2v.wv, clf=LogisticRegression(solver='liblinear'))
for split_ratio in [0.1, 0.3, 0.5]:
    print(clf.split_train_evaluate(X, Y, split_ratio))

分别以取[0.1, 0.3, ....0.9] 的数据作为训练集,剩下的作为测试集。

'micro': 0.31360220825026836, 'macro': 0.16568788514916136

可以看到,结果和论文还是有差别的,有人也在文章中反应,实现不到原来论文里的效果。可能是由于gensim以及一些参数的原因,略有不同,也可以理解。例如设walk-length=40 ,但耗时太久。

这就是顶级会议KDD论文DeepWalk的全部工作,谢谢关注。下篇文章我要解释下DeepWalk的论文公式和Node2vec方法。

参考了代码:https://github.com/phanein/deepwalk

测试还需要这个类:

import numpy
from sklearn.multiclass import OneVsRestClassifier
from sklearn.metrics import f1_score
from sklearn.preprocessing import MultiLabelBinarizer


class TopKRanker(OneVsRestClassifier):
    def predict(self, X, top_k_list):
        probs = numpy.asarray(super(TopKRanker, self).predict_proba(X))
        all_labels = []
        for i, k in enumerate(top_k_list):
            probs_ = probs[i, :]
            labels = self.classes_[probs_.argsort()[-k:]].tolist()
            probs_[:] = 0
            probs_[labels] = 1
            all_labels.append(probs_)
        return numpy.asarray(all_labels)


class Classifier(object):

    def __init__(self, vectors, clf):
        self.embeddings = vectors
        self.clf = TopKRanker(clf)
        self.binarizer = MultiLabelBinarizer(sparse_output=True)

    def train(self, X, Y, Y_all):
        self.binarizer.fit(Y_all)
        X_train = [self.embeddings[x] for x in X]
        Y = self.binarizer.transform(Y)
        self.clf.fit(X_train, Y)

    def evaluate(self, X, Y):
        top_k_list = [len(l) for l in Y]
        Y_ = self.predict(X, top_k_list)
        Y = self.binarizer.transform(Y)
        averages = ["micro", "macro", "samples", "weighted"]
        results = {}
        for average in averages:
            results[average] = f1_score(Y, Y_, average=average)
        print(results)
        return results

    def predict(self, X, top_k_list):
        X_ = numpy.asarray([self.embeddings[x] for x in X])
        Y = self.clf.predict(X_, top_k_list=top_k_list)
        return Y

    def split_train_evaluate(self, X, Y, train_precent, seed=0):
        state = numpy.random.get_state()

        training_size = int(train_precent * len(X))
        numpy.random.seed(seed)
        shuffle_indices = numpy.random.permutation(numpy.arange(len(X)))
        X_train = [X[shuffle_indices[i]] for i in range(training_size)]
        Y_train = [Y[shuffle_indices[i]] for i in range(training_size)]
        X_test = [X[shuffle_indices[i]] for i in range(training_size, len(X))]
        Y_test = [Y[shuffle_indices[i]] for i in range(training_size, len(X))]
        self.train(X_train, Y_train, Y)
        numpy.random.set_state(state)
        return self.evaluate(X_test, Y_test)

 

你可能感兴趣的:(Python,Networkx,网络表示学习)