最近在了解到,在機器學(xué)習(xí)中,自然語言處理是較大的一個分支。存在許多挑戰(zhàn)。例如: 如何分詞,識別實體關(guān)系,實體間關(guān)系,關(guān)系網(wǎng)絡(luò)展示等。
我用Jieba + Word2vec + NetworkX 結(jié)合在一起,做了一次自然語言分析。語料是 倚天屠龍記。 之前也有很多人用金庸的武俠小說做分析和處理,希望帶來一些不同的地方。截幾張圖來看看:
所有人物的相似圖連接。
關(guān)系同上。展示形式為多中心結(jié)構(gòu)
以張無忌的不同身份為中心的網(wǎng)絡(luò)關(guān)系圖。
這次分析的不一樣之處主要是:
1、Word2Vec的相似度結(jié)果 - 作為后期社交網(wǎng)絡(luò)權(quán)重
2、NetworkX中分析和展示
上面兩個方法結(jié)合起來,可以大幅減少日常工作中閱讀文章的時間。 采用機器學(xué)習(xí),可以從頭到尾半自動抽取文章中的實體信息,節(jié)約大量時間和成本。 在各種工作中都有利用的場景, 如果感興趣的朋友,可以聯(lián)系合作。
先來看看,用Word2Vec+NetworkX 可以發(fā)現(xiàn)什么。
一、分析結(jié)果
實體的不同屬性(張無忌的總多馬甲)
張無忌,無忌,張教主,無忌哥哥,張公子。同一個張無忌有多個身份,不同身份又和不同的人聯(lián)系,有不一樣的相似度。
先來看看圖:
無忌哥哥是過于親密的名字,一般不喊。好似和這個詞相似度高的都是比較奇怪的角色。
無忌是關(guān)系熟了以后,平輩或者長輩可以稱呼的名字。還有周姑娘,殷姑娘等
張無忌是通用的名字,人人可以稱呼 和馬甲聯(lián)系密切。
張公子是禮貌尊稱。 例如,黃衫女子,汝陽王等
張教主是頭銜。既要尊重,也表示其實不太熟,有時還有些敵意。 例如: 朱元璋
注:
1、圖是Networkx 基于Word2vex畫出來了,上面的描述是我的人工分析。
2、趙敏不在上面的網(wǎng)絡(luò)關(guān)系圖中。Word2Vec計算出來 張無忌和趙敏 相似度不太高。有些出乎我的意料。 仔細回憶一下,當(dāng)年看此書時,突然就發(fā)現(xiàn)二人在一起了,顯得比較突兀。推想起來,書中世界二人成婚了,如果變成現(xiàn)實世界,二人關(guān)系比較懸。
二、實現(xiàn)過程
主要步驟:
準備語料
倚天屠龍記 小說的文本文件
自定義分詞詞典 (小說中的人物名,網(wǎng)上有現(xiàn)成的,約180個)
停用詞表
準備工具
Python Pandas, Numpy,Scipy(標準庫)
Jieba(中文分詞)
Word2vec (單詞向量化工具,可以計算單詞之間的詳細度)
Networks(網(wǎng)絡(luò)圖工具,用于展示復(fù)雜的網(wǎng)絡(luò)關(guān)系
數(shù)據(jù)預(yù)處理
文本文件轉(zhuǎn)發(fā)成utf8(pandas)
文本文件分句,分詞(Jieba)
文本文件分句,分詞, 分析詞性,主要是人名(Jieba)
更新自定義詞典,重新分詞(整個過程需要幾遍,直至滿意)
手工少量刪除(分詞出來的人名誤判率不高,但是還是存在一些。例如:趙敏笑道,可以被識別的 一個叫 趙敏笑的人。 這部分工作還需要手工做。 除非有更好的分詞工具,或者可以訓(xùn)練的分詞工具,才能解決這一問題。
Word2Vec 訓(xùn)練模型。這個模型可以計算兩個人之間的相似度
采用300個維度
過濾詞頻小于20次
滑動窗口 為20
下采樣:0.001
生成實體關(guān)系矩陣。
網(wǎng)上沒找找到現(xiàn)成庫,我就自己寫了一個。
N*N 維度。 N是人名數(shù)量。
用上面WordVec的模型來,填充實體關(guān)系矩陣
NetworkX 生成網(wǎng)絡(luò)圖
節(jié)點是人名
邊是兩個節(jié)點之間的線條。也就是兩個人之間的關(guān)系。
三、部分代碼實現(xiàn)
初始化
import numpy as np
import pandas as pd
import jieba
import jieba.posseg as posseg
%matplotlib inline
數(shù)據(jù)分詞,清洗
renming_file = "yttlj_renming.csv"
jieba.load_userdict(renming_file)
stop_words_file = "stopwordshagongdakuozhan.txt"
stop_words = pd.read_csv(stop_words_file,header=None,quoting=3,sep=" ")[0].values
corpus = "yttlj.txt"
yttlj = pd.read_csv(corpus,encoding="gb18030",header=None,names=["sentence"])
def cut_join(s):
new_s=list(jieba.cut(s,cut_all=False)) #分詞
#print(list(new_s))
stop_words_extra =set([""])
for seg in new_s:
if len(seg)==1:
#print("aa",seg)
stop_words_extra.add(seg)
#print(stop_words_extra)
#print(len(set(stop_words)| stop_words_extra))
new_s =set(new_s) -set(stop_words)-stop_words_extra
#過濾標點符號
#過濾停用詞
result = ",".join(new_s)
return result
def extract_name(s):
new_s=posseg.cut(s) #取詞性
words=[]
flags=[]
for k,v in new_s:
if len(k)>1:
words.append(k)
flags.append(v)
full_wf["word"].extend(words)
full_wf["flag"].extend(flags)
return len(words)
def check_nshow(x):
nshow = yttlj["sentence"].str.count(x).sum()
#print(x, nshow)
return nshow
# extract name & filter times
full_wf={"word":[],"flag":[]}
possible_name = yttlj["sentence"].apply(extract_name)
#tmp_w,tmp_f
df_wf = pd.DataFrame(full_wf)
df_wf_renming = df_wf[(df_wf.flag=="nr")].drop_duplicates()
df_wf_renming.to_csv("tmp_renming.csv",index=False)
df_wf_renming = pd.read_csv("tmp_renming.csv")
df_wf_renming.head()
df_wf_renming["nshow"] = df_wf_renming.word.apply(check_nshow)
df_wf_renming[df_wf_renming.nshow>20].to_csv("tmp_filtered_renming.csv",index=False)
df_wf_renming[df_wf_renming.nshow>20].shape
#手工編輯,刪除少量非人名,分詞錯的人名
df_wf_renming=pd.read_csv("tmp_filtered_renming.csv")
my_renming = df_wf_renming.word.tolist()
external_renming = pd.read_csv(renming_file,header=None)[0].tolist()
combined_renming = set(my_renming) |set(external_renming)
pd.DataFrame(list(combined_renming)).to_csv("combined_renming.csv",header=None,index=False)
combined_renming_file ="combined_renming.csv"
jieba.load_userdict(combined_renming_file)
# tokening
yttlj["token"]=yttlj["sentence"].apply(cut_join)
yttlj["token"].to_csv("tmp_yttlj.csv",header=False,index=False)
sentences = yttlj["token"].str.split(",").tolist()
Word2Vec 向量化訓(xùn)練
# Set values for various parameters
num_features = 300 # Word vector dimensionality
min_word_count = 20 # Minimum word count
num_workers = 4 # Number of threads to run in parallel
context = 20 # Context window size
downsampling = 1e-3 # Downsample setting for frequent words
# Initialize and train the model (this will take some time)
from gensim.models import word2vec
model_file_name = 'yttlj_model.txt'
#sentences = w2v.LineSentence('cut_jttlj.csv')
model = word2vec.Word2Vec(sentences, workers=num_workers,
size=num_features, min_count = min_word_count,
window = context,
sample = downsampling
)
model.save(model_file_name)
建立實體關(guān)系矩陣
entity = pd.read_csv(combined_renming_file,header=None,index_col=None)
entity = entity.rename(columns={0:"Name"})
entity = entity.set_index(["Name"],drop=False)
ER = pd.DataFrame(np.zeros((entity.shape[0],entity.shape[0]),dtype=np.float32),index=entity["Name"],columns=entity["Name"])
ER["tmp"] = entity.Name
def check_nshow(x):
nshow = yttlj["sentence"].str.count(x).sum()
#print(x, nshow)
return nshow
ER["nshow"]=ER["tmp"].apply(check_nshow)
ER = ER.drop(["tmp"],axis=1)
count = 0
for i in entity["Name"].tolist():
count +=1
if count % round(entity.shape[0]/10) ==0:
print("{0:.1f}% relationship has been checked".format(100*count/entity.shape[0]))
elif count == entity.shape[0]:
print("{0:.1f}% relationship has been checked".format(100*count/entity.shape[0]))
for j in entity["Name"]:
relation =0
try:
relation = model.wv.similarity(i,j)
ER.loc[i,j] = relation
if i!=j:
ER.loc[j,i] = relation
except:
relation = 0
ER.to_hdf("ER.h5","ER")
NetworkX 展示人物關(guān)系圖
import networkx as nx
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import pygraphviz
from networkx.drawing.nx_agraph import graphviz_layout
-
機器學(xué)習(xí)
+關(guān)注
關(guān)注
66文章
8407瀏覽量
132567 -
python
+關(guān)注
關(guān)注
56文章
4792瀏覽量
84629 -
自然語言
+關(guān)注
關(guān)注
1文章
287瀏覽量
13347
原文標題:Python自然語言處理分析倚天屠龍記
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論