pip install networkx
pip install node2vec
pip install jieba
pip install pypinyin
pip install scikit-learn
01、案例实践
1●导入所需的包
import pandas as pd
import numpy as np
import networkx as nx
import jieba
import matplotlib.pyplot as plt
%matplotlib inline
import pypinyin as pypy
from node2vec import Node2Vec
from scipy.spatial.distance import cosine
2●准备data.txt文件
今天美国众议院金融服务委员会针对Facebook Libra举行听证会,针对所担心的问题向Facebook Libra项目负责人大卫·马库斯(David Marcus)发问。这也是Libra面临的第二场听证会。
美国众议院金融服务委员会议员询问,第三方Libra钱包是否可以整合入WhatsApp和Facebook Messenger中,马库斯没有正面回应这一问题。综合昨日马库斯与美国参议院的听证会证词,第三方Libra钱包可能无法整合入WhatsApp和Facebook Messenger。
读取后的full_data变量如下:
{
"libra": [
['今天', '美国', '众议院', '金融', '服务' ....],
['美国', '众议院', '金融', '服务' ....],
]
}
3●编写代码
下面一部分代码从data.txt文件读取若干段文本,构建文本中出现的单词的上下文图(以networkx库中的有向图表示)。
首先用jieba分词将每行文本转为单词序列,然后将单词作为节点加入上下文图,最后计算两个单词作为相邻单词出现的次数、并将次数作为节点之间连线的权重;调用PageRank算法计算节点的PageRank值;最后构建出每个节点的上下文关系数据集:
context1 {节点: 父节点 || 子节点}
context2 {节点: 父节点的父节点 || 子节点的子节点}
参考代码如下:
with open("data.txt","r") as f:
data = f.readlines()
full_data = {}
title = None
for l in data:
if l.startswith("#"):
title = l.strip("#").strip("\n").strip()
full_data[title]=[]
else:
if l!="\n":
full_data[title].append(jieba.lcut(l.strip("\n")))
data = full_data["libra"]
G = nx.DiGraph()
for s in data:
G.add_nodes_from(s)
for i,w in enumerate(s[1:]):
if (s[i],w) not in G.edges:
G.add_edge(s[i],w,weight=1)
else:
G[s[i]][w]["weight"]+=1
plt.figure(figsize=(20,20))
nx.draw(G,with_label=True,font_weight="bold")
pd.Series(nx.pagerank(G)).sort_values(ascending=False)
left1 = {n:set() for n in G.nodes}
left2 = {n:set() for n in G.nodes}
right1 = {n:set() for n in G.nodes}
right2 = {n:set() for n in G.nodes}
for n in G.nodes:
for l1 in G.predecessors(n):
left1[n]|=set([l1])
left2[n]|=set([l1])
for l2 in G.predecessors(l1):
left2[n]|=set([l2])
for r1 in G.successors(n):
right1[n]|=set([r1])
right2[n]|=set([r1])
for r2 in G.successors(r1):
right2[n]|=set([r2])
context1 = {n:set() for n in G.nodes}
context2 = {n:set() for n in G.nodes}
for w in context1.keys():
context1[w] = left1[w]|right1[w]
context2[w] = left2[w]|right2[w]
执行代码,所展示的单词节点上下文图如图1 所示。
图1 单词节点上下文图
在目标单词上下文中用jiacobian距离计算单词的相似性,意味着与目标单词紧挨着的两个词。
下面一部分代码基于上一步得到的context1和context2计算任意两个节点之间的相似性。基于context1计算相似性:节点a与节点b的相似性等于a的所有父子节点与b的所有父子节点的并集除以交集。输出相似性大于0.5的组合。基于context2计算相似性的方法与此类似。
similarity_matrix = pd.DataFrame(data=np.zeros((len(context1.keys()),len(context1.keys()))),index=list(context1.keys()),columns=list(context1.keys()))
for row in similarity_matrix.index:
for col in similarity_matrix.columns:
if row!=col:
similarity_matrix.loc[row,col]=len(context1[row]&context1[col])/len(context1[row]|context1[col])
print("below are the found similar words with window size 1, left1 and right1")
for i,row in enumerate(similarity_matrix.index):
for j,col in enumerate(similarity_matrix.columns):
if i>j:
if similarity_matrix.loc[row,col]>0.5:
print(row,col,similarity_matrix.loc[row,col])
单词相似性:
管制 数字 1.0
上线 都 1.0
事实 提问 1.0
阻止 要求 1.0
瑞士 日内瓦 1.0
称 表示 1.0
参考代码如下:
similarity_matrix = pd.DataFrame(data=np.zeros((len(context2.keys()),len(context2.keys()))),index=list(context2.keys()),columns=list(context2.keys()))
for row in similarity_matrix.index:
for col in similarity_matrix.columns:
if row!=col:
similarity_matrix.loc[row,col]=len(context2[row]&context2[col])/len(context2[row]|context2[col])
print("below are the found similar words with window size 2, left2 and right2")
for i,row in enumerate(similarity_matrix.index):
for j,col in enumerate(similarity_matrix.columns):
if i>j:
if similarity_matrix.loc[row,col]>0.9:
print(row,col,similarity_matrix.loc[row,col])
单词相似性:
需要 但 0.9166666666666666
需要 现在 0.9166666666666666
据 需要 0.9166666666666666
上线 强调 0.9090909090909091
事实 提问 1.0
那么 需要 0.9166666666666666
今天 需要 0.9166666666666666
对 需要 0.9166666666666666
透露 手续费 0.9090909090909091
透露 中 0.9090909090909091
透露 强调 0.9523809523809523
透露 上线 0.9523809523809523
透露 报道 0.9090909090909091
透露 日讯 0.9090909090909091
瑞士 日内瓦 1.0
称 表示 1.0
具体 需要 0.9166666666666666
时 透露 0.9090909090909091
node2vec和word2vec是等价图。不同的是在对如上所示的同样的单词上下文图运行了不同的算法。
node2vec = Node2Vec(G, dimensions=32, walk_length=30, num_walks=200, workers=4)
model = node2vec.fit(window=10, min_count=1, batch_words=4)
G.nodes
NodeView(('成为', '具有', '微信', '加密', '工具', '希望', '多少', '国家', '。', '提问', '众议院', '美元', '篮子', '综合', '手续费', '这', '但', '主权', '中', '货币', 'WhatsApp', '“', '现在', '委员会', '担心', '服务', '向', 'Messenger', '支付', 'Libra', '没有', '证词', '有', '面临', '方法', '为', '和', '数字', '一种', '需要', '不同', '它', '非常', '成', '事实', '马库斯', '显然', '如何', '为时过早', '你们', '更', '与', '行使主权', '据', 'Calibra', '项目', '将', '强调', '出现', '上线', '新浪', '监管', '日内瓦', '要求', '金融', '金融服务', '都', '表示', '可能', '、', '正面', '整合', 'PingWest', '地区', '报道', '那么', '试图', '兑换', '日讯', '竞争', '或', '了', '并', 'David', '(', '美国财政部', '月', '听证会', '默认', '称', '挂钩', '针对', '再次', '遵守', 'Facebook', '品玩', '是', '不会', '询问', '/', '网络', '今天', '对', 'Marcus', '回应', '?', ',', '将会', '18', '要', '可以', '所', '阻止', '许多', '第三方', '在', '入', '有人', '·', '科技', '如果', '支付宝', '讨论', ')', '央行', '这一', '第二场', '大卫', '为了', '同时', '把', '为何', '美国参议院', '交易所', '高质量', '取决于', '来说', '议员', '对于', '职责', '7', '不是', '其他', '无论', '我们', '产生', '银行', '证券', '这个', '制裁', '透露', '商品', '瑞士', '逃避', '发展', '举行', '管制', '运行', '具体', '什么样', '美国', '无法', 'ETF', '注册', '是否', '钱包', '美国众议院', '的', '少量', '合作', '也', '昨日', '时', '一', 'Blaine', '负责人', '适合', '问题', '”', '会', ' ', '愿意', '发问'))
def compare_words(w1,w2,model=model):
return cosine(model.wv.get_vector(w1),model.wv.get_vector(w2))
def get_top_similar_words(w,words,k=5,model=model):
distances = []
for tw in words:
if tw!=w:
distances.append([tw,compare_words(w,tw)])
results = pd.DataFrame(data=distances,columns=["top_{}_words".format(k),"scores"])
return results.sort_values(["scores"])[:k]
正如所见两个单词出现时彼此位置很近。
compare_words("日内瓦","瑞士")
0.0016219019889831543
并且为“表示”而选出的最相近前5个单词也是有意义的,如表1所示。
get_top_similar_words('表示',list(G.nodes))
表1 单词相似性