亂燉「簡書交友」數據之代碼(2):關鍵詞抽取、Word2Vec詞向量

繼續更新出來本系列的代碼:亂燉數據之2700餘篇「簡書交友」專題文章數據的花式玩法html

亂燉「簡書交友」數據之代碼(1)一文裏,主要涉及結構化數據的分析,文本挖掘如詞頻統計、詞雲圖等。本文繼續用jieba庫抽取文本關鍵詞,並調用百度雲NLP的API獲取關鍵詞的Word2Vec詞向量,並用t-SNE可視化高維數據,以後用文本本身訓練了Word2Vec詞向量,效果稍好些,最後嘗試了下LDA主題模型python

代碼見於 GitHub - DesertsX / JianShuJiaoYou,下一篇也是本系列最後一篇會涉及文章的照片爬取、人臉識別及顏值打分和照片牆等更新後也會開源在此項目,歡迎star。git

另外先預告下,以後打算開個「Kaggle Kernel 學習系列」GitHub - DesertsX / Kaggle-Kernel-Learning,主要是翻譯和學習下kaggle上優秀的kernels。其中第一篇很是粗糙,還沒潤色、修改排版佈局的notebook可供瀏覽下,也歡迎關注、star和提供寶貴建議:
desertsx.github.io/2018/06/09/…
desertsx.github.io/2018/06/09/…程序員

關鍵詞抽取

基於 TF-IDF 算法的關鍵詞抽取

fxsjy/jieba:結巴中文分詞github

  • jieba.analyse.extract_tags(sentence, topK=20, withWeight=False, allowPOS=())

百度雲AI-詞性縮略說明
算法

import pandas as pd
import jieba
import jieba.analyse
df12 = pd.read_csv('JianShuJiaoYou-All-Data.csv', encoding='utf-8')
contents = " ".join(df12.Artical_Content.values.tolist())
複製代碼

Top200
基於 TF-IDF 算法抽取前200個關鍵詞(普通名詞和地點名詞)。api

textrank1 = " ".join(jieba.analyse.extract_tags(contents, topK=200, withWeight=False, allowPOS=('ns', 'n')))
print(textrank1)
複製代碼

簡書 時候 朋友 簡友 文章 交友 我會 文字 故事 投稿 愛情 感受 你們 大學 時間 專題 事情 人生 老師 樹洞 世界 東西 同窗 照片 地方 女生 電影 情書 性格 暱稱 有點 交流 學校 夢想 心裏 經歷 時光 男生 做者 讀書 城市 女孩 姑娘 孩子 單身 畢業 感情 凡人 青春 樣子 名字 攝影 男友 校園 靈魂 父母 日子 對方 問題 學生 手機 朋友圈 小說 年齡 美食 專業 職業 評論 哥哥 心情 星座 女友 跑步 帳號 素材 體重 陌生人 小夥伴 音樂 唱歌 好友 文藝 自我介紹 見面 畫畫 室友 我的 姐姐 陪伴 風景 媽媽 想象 地點 緣分 閨蜜 社羣 回家 習慣 篇文章 現實 記錄 沒法 圖書館 性別 公衆 理想 緣由 關係 平臺 北京 興趣 遊戲 結果 粉絲 小時候 美麗 情感 禮物 男人 總會 女孩子 方式 機會 宿舍 程序員 長大 舍友 陌生 印象 文學 私信 妹子 嘉賓 校友 話題 男孩 學會 模樣 交朋友 聯繫 聲音 眼睛 家鄉 記憶 編輯 小時 女人 顏值 無戒 世間 能力 缺點 寫文章 脾氣 先生 家庭 家人 寫字 身體 大神 咖啡 讀者 碼字 上海 線下 晚安 熱情 社會 幻想 衣服 羣裏 生氣 教室 吉他 學歷 成績 筆名 味道 情緒 意義 狀態 異地 民謠 勵志 學姐 爸媽 天空 做品 書寫 友情 妹妹 思想 心靈 成都 老爸 學長 文筆 婚姻 小學 目標bash

基於 TextRank 算法的關鍵詞抽取

jieba.analyse.textrank(sentence, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v'))app

算法基本思想:dom

  • 將待抽取關鍵詞的文本進行分詞
  • 以固定窗口大小(默認爲5,經過span屬性調整),詞之間的共現關係,構建圖
  • 計算圖中節點的PageRank,注意是無向帶權圖

Top200
基於 TextRank 算法抽取前200個關鍵詞(allowPOS=('ns', 'n'),普通名詞和地點名詞),並返回關鍵詞權重值(withWeight=True)。篇幅所限,僅羅列前幾個詞語。

textrank4 = jieba.analyse.textrank(contents, topK=200, withWeight=True, allowPOS=('ns', 'n'))
print(textrank4)
複製代碼

[('時候', 1.0),
('簡書', 0.9286492277441794),
('朋友', 0.5604058706337074),
('文章', 0.5119472310224017),
('你們', 0.4435913680744001),
('交友', 0.4205067493937958),
('時間', 0.41736915018165616),
('大學', 0.4169278527224929),
('文字', 0.3963519612404718),
('故事', 0.3672191429321304),
('簡友', 0.36709289668920975),
('感受', 0.35411529061992564),]

抽取出這些關鍵詞後,忽然想到可使用word2vec詞向量,看看這些詞語在向量空間中會是怎樣分佈的?哪些詞語會在類似的區域?

本來想用gensim庫本身訓練word2vec,可是沒成功(後面從新研究了下,已經搞定了,後面再介紹),機緣巧合接觸到百度雲的產品,因而調用下看看效果如何。

Word2Vec 詞向量

詞向量,就是將詞語映射成一個固定維度的向量。詞向量可能具有必定的語義信息,如類似的詞語在相近的向量空間(如西瓜和蘋果都屬於水果,但蘋果也存在歧義);能夠學到詞語之間的關係,如經典的「男人-女人=國王-王后」(King – Man + Woman = Queen)。

又好比,國家與首都之間的對應關係也能經過詞向量反映出來。後文也調用百度雲的api試了一下幾組詞,有相似效果。

相關原理可自行了解,文本不展開了:

百度雲NLP-詞向量表示

再貼下官網的解釋:

  • 詞向量計算是經過訓練的方法,將語言詞表中的詞映射成一個長度固定的向量。詞表中全部的詞向量構成一個向量空間,每個詞都是這個詞向量空間中的一個點,利用這種方法,實現文本的可計算。

先官網註冊下以便調用API,再按照Python庫:pip install baidu-aip

# pip install baidu-aip
# https://cloud.baidu.com/doc/NLP/NLP-Python-SDK.html#.E6.96.B0.E5.BB.BAAipNlp

# 新建一個AipNlp
from aip import AipNlp
""" 你的 APP_ID/API_KEY/SECRET_KEY """
APP_ID = '你的 APP_ID'
API_KEY = '你的 API_KEY'
SECRET_KEY = 'SECRET_KEY'
client = AipNlp(APP_ID, API_KEY, SECRET_KEY)
複製代碼

官網示例:調用詞向量表示
每一個詞語均用1024維的詞向量表示,固然也有不少詞不存在語料庫裏,因此也就沒法用詞向量表示。先看看官網給出的「俺也同樣」的「張飛」的示例,一行代碼就能獲取相應詞語的詞向量。不過因爲太佔篇幅,文章裏就不放了,很是詳細的代碼在: GitHub - DesertsX / JianShuJiaoYou

word = "張飛"
""" 調用詞向量表示 """
w = client.wordEmbedding(word);
print(w['word'], len(w['vec'])  # 張飛 1024 
print(w)  
複製代碼

獲取textrank4中Top200詞語的詞向量

羅列出了詞表中不存在的詞語共13個,拿到187個詞語的詞向量。

import numpy as np
words_list = []
word_vectors = []
for word,_ in textrank4:
    try:
        data = client.wordEmbedding(word)
        word_vector = data['vec']
        #print(data['word'])
        words_list.append(data['word'])
        word_vectors.append(word_vector)
    except:
        print("No word:{}".format(word))
        
word_vectors = np.array(word_vectors)
# print("Total words:", len(words_list)) 
print(words_list)
print("Total words:", len(words_list), '\tWord Embedding shapes:', word_vectors.shape)
# Total words: 187
# Word Embedding shapes: (187, 1024)
複製代碼

No word:簡書
No word:簡友
No word:有點
No word:小時候
No word:圖書館
No word:男友
No word:線下
No word:陌生人
No word:朋友圈
No word:小夥伴
No word:大學生
No word:女孩子
No word:程序員
Total words: 187 Word Embedding shapes: (187, 1024)

t-SNE 可視化高維數據

使用 t-SNE 來查看可視化高維數據,分別在2維和3維下查看下詞向量分佈效果。
sklearn.manifold.TSNE

t-SNE 2維

import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] # 用來正常顯示中文標籤
plt.rcParams['axes.unicode_minus']=False # 用來正常顯示負號
%matplotlib inline
#%config InlineBackend.figure_format='svg'

def plot_tsne_2D(word_vectors, words_list):
    tsne = TSNE(n_components=2, random_state=0, n_iter=10000, perplexity=3)
    np.set_printoptions(suppress=True)
    T = tsne.fit_transform(word_vectors)
    labels = words_list

    plt.figure(figsize=(8, 6))
    plt.scatter(T[:,0], T[:,1], c='steelblue', edgecolors='k')
    for label, x, y in zip(labels, T[:,0], T[:,1]):
        plt.annotate(label, xy=(x+1, y+1),xytext=(0,0), textcoords='offset points')

plot_tsne_2D(word_vectors, words_list)
複製代碼

效果很差,比較雜亂,相關類似的詞語沒有分佈在相近區域,多是百度使用的語料和本項目用的簡書交友文章語料的不一樣致使的。

t-SNE 3維

仍是很雜亂,看到「哥哥」、「姐姐」、「女人」、「女孩」、「孩子」等詞語分佈的很分散,感受必定是哪出了問題。

# https://stackoverflow.com/questions/10374930/matplotlib-annotating-a-3d-scatter-plot
from mpl_toolkits.mplot3d import Axes3D

def plot_tsne_3D(word_vectors, words_list):
    tsne = TSNE(n_components=3, random_state=0, n_iter=10000, perplexity=2)
    np.set_printoptions(suppress=True)
    T = tsne.fit_transform(word_vectors)
    labels = words_list

    plt.figure(figsize=(10, 6))
    ax = plt.subplot(111,projection='3d')

    for i in range(len(T)): # plot each point + it's index as text above
        x = T[i,0]
        y = T[i,1]
        z = T[i,2]
        label = labels[i]
        ax.scatter(x, y, z, color='b');
        ax.text(x, y, z, '%s' % (label), size=9, zorder=1, color='k');

    ax.set_title('word2vec t-SNE 3D');
    ax.set_xlabel('x');
    ax.set_ylabel('y');
    ax.set_zlabel('z');

plot_tsne_3D(word_vectors, words_list)  
複製代碼

復現下官網的例子

因爲上述詞向量可視化的結果不太理想,爲了探索下哪出了問題,因而復現下官網配圖的效果。固然一開始並不清楚它是舉例用的、隨便畫的,仍是實際經過計算後繪製的。我的傾向於後者,那麼應該是能復現吧?!

將獲取詞向量,2維、3維 t-SNE 可視化均寫成函數,方便重複使用。

words = ['姨夫', '老公', '妹妹', '奶奶', '表哥', '乾爸', '外爺', '繼父', 
 '性感', '沮喪', '真夠', '悲憤', '探詢', '懷想', '甜美', 
 '南寧', '合肥', '淮海', '珠江', '津蒙', '奎河', '邕江', '灤河']

def get_word2vec(words):
    words_list = []
    word_vectors = []
    for word in words:
        try:
            data = client.wordEmbedding(word)
            word_vector = data['vec']
            #print(data['word'])
            words_list.append(data['word'])
            word_vectors.append(word_vector)
        except:
            print("No word:{}".format(word))

    word_vectors = np.array(word_vectors)
    print(words_list)
    print("Total words:", len(words_list), '\tWord Embedding shapes:', word_vectors.shape)
    return word_vectors, words_list

word_vectors, words_list = get_word2vec(words)
plot_tsne_2D(word_vectors, words_list)        
複製代碼

額...地名的大體在右下角,人物關係的在左上角,但仍是區分度不夠好。

plot_tsne_3D(word_vectors, words_list)    
複製代碼

不好......徹底摸不着頭腦,多是須要繼續調參吧???

Try Again: 想些詞再試試

本身不死心的,又想了三類詞語,這回的效果理想了不少。不過「騰
訊」一詞有些突兀,因而去掉後再試試。

words = ['中國', '北京', '日本', '東京', '法國', '巴黎', '俄羅斯', '莫斯科', 
 '百度', '李彥宏', '京東', '劉強東', '騰訊', '馬化騰', '阿里巴巴','馬雲', 
 '三國', '曹操', '劉備', '西遊記', '唐僧', '悟空', '紅樓', '寶玉', '黛玉','王熙鳳']

word_vectors, words_list = get_word2vec(words)
plot_tsne_2D(word_vectors, words_list)
複製代碼

去掉「騰訊」一詞後,除了「京東」一詞明顯突兀,其餘都還不錯,類似相關的詞分佈在了一塊兒

words = ['中國', '北京', '日本', '東京', '法國', '巴黎', '俄羅斯', '莫斯科', 
 '百度', '李彥宏', '京東', '劉強東', '馬化騰', '阿里巴巴','馬雲', 
 '三國', '曹操', '劉備', '西遊記', '唐僧', '悟空', '紅樓', '寶玉', '黛玉','王熙鳳']

word_vectors, words_list = get_word2vec(words)
plot_tsne_2D(word_vectors, words_list)
複製代碼
plot_tsne_3D(word_vectors, words_list)
複製代碼

訓練 Word2Vec 模型

Gensim - models.word2vec – Deep learning with word2vec

預處理

讀取數據,並去掉停用詞,注意sentences是列表嵌套列表的格式**

import pandas as pd
import jieba
df12 = pd.read_csv('JianShuJiaoYou-All-Data.csv', encoding='utf-8')
content = df12.Artical_Content.values.tolist()

stopwords1 = [line.rstrip() for line in open('./Stopwords/中文停用詞庫.txt', 'r', encoding='utf-8')]
stopwords2 = [line.rstrip() for line in open('./Stopwords/哈工大停用詞表.txt', 'r', encoding='utf-8')]
stopwords3 = [line.rstrip() for line in open('./Stopwords/四川大學機器智能實驗室停用詞庫.txt', 'r', encoding='utf-8')]
stopwords = stopwords1 + stopwords2 + stopwords3
print(stopwords[10:20])

sentences = []
for line in content:
    try:
        segs = jieba.lcut(line)
        segs = filter(lambda x:len(x)>1, segs)
        segs = filter(lambda x:x not in stopwords, segs)
        # sentences.append(segs)
        sentences.append(list(segs))
    except Exception as e:
        print(line)
        continue

import multiprocessing
from gensim.models import Word2Vec

model_csv = Word2Vec(sentences, min_count=20, sg=0, workers=multiprocessing.cpu_count())
model_csv.wv.most_similar(['簡書'], topn=15)
複製代碼

訓練好後,找出與「簡書」一詞,最相近最相關的詞語,羅列以下,效果還行:

[('平臺', 0.9340553283691406), ('簽約', 0.905404269695282),
('書上', 0.8926113843917847), ('一位', 0.8816096186637878),
('簡書裏', 0.8752850294113159), ('加入', 0.8719199895858765),
('創做', 0.8692747354507446), ('這篇', 0.8666418790817261),
('大神', 0.8640251755714417), ('下載', 0.8557288646697998),
('篇文章', 0.8523578643798828), ('第一篇', 0.8458684682846069),
('做者', 0.8452268838882446), ('專題', 0.8378645181655884),
('讀者', 0.8378232717514038)]

查詢前文抽取的關鍵詞textrank4的最相近詞語,並拿到關鍵詞的詞向量。

import numpy as np
words_list = []
word_vectors = []
for word,_ in textrank4:
    try:
        result = model_csv.wv.most_similar(positive=[word],topn=15)
        print("關鍵詞: {}".format(word))
        print(result)
        print("+++++++++++++++++++++++++++++++++++")
        w2v = model_csv.wv[word]
        words_list.append(word)
        word_vectors.append(w2v)
        #for v in w2v:
        # print(v[0],v[1])
    except:
        print("No word: {}".format(word))
        
word_vectors = np.array(word_vectors)
複製代碼

挑選了些查詢的結果,供讀者瀏覽,看看你們都在談些什麼,蠻有趣的:

關鍵詞: 電影
    [('拍照', 0.9252004623413086), ('登山', 0.920052170753479), ('美食', 0.9192012548446655), ('音樂', 0.9168093204498291), ('看書', 0.9157875180244446), ('聽歌', 0.9069955945014954), ('旅遊', 0.9057952165603638), ('唱歌', 0.899057149887085), ('吉他', 0.8984708786010742), ('跑步', 0.8956234455108643), ('民謠', 0.8925215601921082), ('一部', 0.8880782127380371), ('運動', 0.8865675330162048), ('羽毛球', 0.8812819123268127), ('動漫', 0.8731318116188049)]
    +++++++++++++++++++++++++++++++++++
    關鍵詞: 青春
    [('記憶', 0.9561529755592346), ('回憶', 0.955527126789093), ('曾經', 0.9253653287887573), ('時光', 0.905220627784729), ('歲月', 0.896970272064209), ('最美', 0.8751366138458252), ('美麗', 0.8565613031387329), ('走過', 0.8368370532989502), ('春天', 0.8309674263000488), ('難忘', 0.8301017880439758), ('一片', 0.8259316682815552), ('美好', 0.8249017000198364), ('一段', 0.8218579292297363), ('依舊', 0.8214970231056213), ('心中', 0.8203814029693604)]
    +++++++++++++++++++++++++++++++++++
    關鍵詞: 美麗
    [('最美', 0.9421555995941162), ('天空', 0.9361464381217957), ('即是', 0.9163937568664551), ('流浪', 0.908709704875946), ('陽光', 0.907963752746582), ('春天', 0.9046747088432312), ('歲月', 0.9010506868362427), ('幻想', 0.8919180035591125), ('定格', 0.8899471163749695), ('純真', 0.8851020932197571), ('年華', 0.8850700259208679), ('一片', 0.8834213614463806), ('難忘', 0.8828067183494568), ('遠方', 0.8826968669891357), ('繁華', 0.8787059187889099)]
    +++++++++++++++++++++++++++++++++++
    關鍵詞: 日子
    [('遺憾', 0.9469071626663208), ('度過', 0.9386813640594482), ('多年', 0.9268732070922852), ('依舊', 0.9203112125396729), ('依然', 0.9201472401618958), ('變成', 0.9193578362464905), ('痛苦', 0.9121938943862915), ('後悔', 0.9080638885498047), ('過得', 0.9079610109329224), ('慢慢', 0.9073663949966431), ('珍惜', 0.9073460102081299), ('孤單', 0.9017938375473022), ('時光', 0.8997607231140137), ('過去', 0.8995781540870667), ('漫長', 0.8980476260185242)]
    +++++++++++++++++++++++++++++++++++
    關鍵詞: 習慣
    [('鍛鍊', 0.8789036870002747), ('享受', 0.8728272914886475), ('不愛', 0.8702492117881775), ('獨處', 0.8683050870895386), ('發呆', 0.862934947013855), ('空閒', 0.856212854385376), ('人去', 0.8532602787017822), ('打籃球', 0.8526784777641296), ('沒事', 0.8500849008560181), ('超級', 0.8474704027175903), ('放鬆', 0.844687819480896), ('熱鬧', 0.8433003425598145), ('走路', 0.8428263664245605), ('閒暇', 0.8407423496246338), ('養成', 0.8402824401855469)]
    +++++++++++++++++++++++++++++++++++
    關鍵詞: 男友
    [('女友', 0.9782456159591675), ('結婚', 0.9226555228233337), ('分手', 0.9135688543319702), ('距離', 0.880342423915863), ('男朋友', 0.8786644339561462), ('找個', 0.8754839897155762), ('擔憂', 0.8725529313087463), ('吵架', 0.8680383563041687), ('爸媽', 0.8625649213790894), ('兩個', 0.8591646552085876), ('不想', 0.8558708429336548), ('我要', 0.853802502155304), ('我怕', 0.8512692451477051), ('分開', 0.8507095575332642), ('老婆', 0.844429075717926)]
    +++++++++++++++++++++++++++++++++++
    關鍵詞: 家人
    [('作好', 0.964654803276062), ('房子', 0.9459130167961121), ('掙錢', 0.9400242567062378), ('分開', 0.9261205196380615), ('爸媽', 0.9220873117446899), ('有錢', 0.9190271496772766), ('子女', 0.9171360731124878), ('壓力', 0.9167039394378662), ('辦法', 0.9156253337860107), ('懂事', 0.9149639010429382), ('打拼', 0.9143495559692383), ('談過', 0.9131268858909607), ('依靠', 0.9117845892906189), ('談戀愛', 0.9093905687332153), ('留在', 0.9089971780776978)]
    +++++++++++++++++++++++++++++++++++
    關鍵詞: 長大
    [('學會', 0.9459168910980225), ('身體', 0.9209215641021729), ('經常', 0.9176377058029175), ('懂事', 0.9148654937744141), ('適應', 0.9044305086135864), ('堅強', 0.9038822650909424), ('孩子', 0.9017991423606873), ('掙錢', 0.898858904838562), ('羨慕', 0.8973740935325623), ('年輕', 0.8969836235046387), ('房子', 0.8969626426696777), ('談戀愛', 0.8935214877128601), ('模樣', 0.8843299150466919), ('過得', 0.8807798624038696), ('保護', 0.8800650835037231)]
    +++++++++++++++++++++++++++++++++++
    關鍵詞: 味道
    [('房間', 0.9597378969192505), ('調皮', 0.957197904586792), ('嘴裏', 0.9569690227508545), ('小孩子', 0.9566539525985718), ('陽臺', 0.9550118446350098), ('空氣', 0.9540750980377197), ('顯得', 0.9536328315734863), ('零食', 0.9528669118881226), ('時常', 0.9519882202148438), ('多愁善感', 0.9519380331039429), ('傷感', 0.9509293437004089), ('有種', 0.9501902461051941), ('一雙', 0.9479855298995972), ('韓劇', 0.9476990699768066), ('僞裝', 0.946105420589447)]
    +++++++++++++++++++++++++++++++++++
    關鍵詞: 妹子
    [('天秤座', 0.9551429748535156), ('典型', 0.9542213678359985), ('巨蟹座', 0.9520383477210999), ('處女座', 0.951753556728363), ('天蠍座', 0.9511485695838928), ('射手座', 0.9477106332778931), ('水瓶', 0.9455941915512085), ('水瓶座', 0.9437704682350159), ('160', 0.9407752156257629), ('白羊座', 0.937430202960968), ('雙子座', 0.934114933013916), ('星座', 0.9333192110061646), ('摩羯座', 0.9331715703010559), ('雙魚座', 0.9310780167579651), ('湖北', 0.9304903745651245)]
    +++++++++++++++++++++++++++++++++++
複製代碼

這回的 t-SNE 結果總算好了許多,看來訓練語料仍是影響很大的,具體圖表裏詞語分佈就不過多講解了,可能圖上傳後也不太清晰,想看高清的SVG矢量圖請到GitHub獲取: GitHub - DesertsX / JianShuJiaoYou

plot_tsne_2D(word_vectors, words_list)
複製代碼
plot_tsne_3D(word_vectors, words_list)
複製代碼

本文先更新到此,主要涉及關鍵詞的抽取、Word2Vec詞向量的嘗試和探索。後續LDA主題模型及主題的可視化、文章照片爬取、人臉識別及顏值打分和照片牆等等更新後也會開源在GitHub - DesertsX / JianShuJiaoYou,歡迎star與指正。

系列文章:
亂燉數據之2700餘篇「簡書交友」專題文章數據的花式玩法
亂燉「簡書交友」數據之代碼(1)

PS:預告下,即將開啓「Kaggle Kernel 學習系列」,GitHub - DesertsX / Kaggle-Kernel-Learning,歡迎star。

PPS:歡迎關注公衆號:牛衣古柳(ID:Deserts-X),以及歡迎加QQ羣:Python交友娛樂會所(613176398)哈。娛樂會所,沒有嫩模。

相關文章
相關標籤/搜索