自然語言(NLP)處理流程—IF-IDF統計—jieba分詞—Word2Vec模型訓練使用
阿新 • • 發佈:2018-12-11
一、資料感知—訓練與測試資料
import numpy as np
import pandas as pd
# 建立輸出目錄
output_dir = u'output_word2vec'
import os
if not os.path.exists(output_dir):
os.mkdir(output_dir)
# 檢視訓練資料
train_data = pd.read_csv('data/sohu_train.txt', sep='\t', header=None,
dtype= np.str_, encoding='utf8', names=[u'頻道', u'文章'])
train_data.head()
# 檢視每個頻道下文章數量
train_data.groupby(u'頻道')[u'頻道'].count()
# 檢視每個頻道下最短、最長文章字數
train_data[u'文章長度'] = train_data[u'文章'].apply(len)
train_data.groupby(u'頻道')[u'文章長度'].agg([np.min, np.max])
頻道 | 文章 | |
---|---|---|
0 | 娛樂 | 《青蛇》造型師預設新《紅樓夢》額妝抄襲(圖) 凡是看過電影《青蛇》的人,都不會忘記青白二蛇的… |
1 | 娛樂 | 6.16日劇榜 <最後的朋友> 亮最後殺招成功登頂 《最後的朋友》本週的電視劇排行榜單依然只… |
2 | 娛樂 | 超乎想象的好看《納尼亞傳奇2:凱斯賓王子》 現時資訊如此發達,搜狐電影評審團幾乎人人在沒有看… |
3 | 娛樂 | 吳宇森:赤壁大戰不會出現在上集 “希望《赤壁》能給你們不一樣的感覺。”對於自己剛剛拍完的影片… |
4 | 娛樂 | 組圖:《多情女人痴情男》陳浩民現場耍寶 陳浩民:外面的朋友大家好,現在是搜狐現場直播,歡迎《… |
# 檢視測試資料
test_data = pd.read_csv('data/sohu_test.txt', sep='\t', header=None,
dtype= np.str_, encoding='utf8', names=[u'頻道', u'文章'])
test_data.head()
# 檢視每個頻道下文章數量
test_data.groupby(u'頻道')[u'頻道'].count()
# 檢視每個頻道下最短、最長文章字數
test_data[u'文章長度'] = train_data[u'文章'].apply(len)
test_data.groupby(u'頻道')[u'文章長度'].agg([np.min, np.max])
# 載入停用詞
stopwords = set()
with open('data/stopwords.txt', 'rb') as infile:
for line in infile:
line = line.decode('utf8').rstrip('\n')
if line:
stopwords.add(line.lower())
二:文件分詞(精確—全—搜尋引擎模式)—詞性標註
2.1 三種不同分詞方式
import jieba # 載入jieba分詞包
text = u'小明碩士畢業於中國科學院計算所,後在日本京都大學深造'
"""
精確模式: 試圖將句子最精確的分開,適合文字分析
jieba.lcut,結果返回一個列表
jieba.cut,結果返回一個迭代器
全模式: 把句子中所有可以成詞的詞語都掃描出來,速度非常快,但是不能解決歧義
搜尋引擎模式:在精確模式的基礎上對長詞再次切分,適合用於搜尋引擎分詞
"""
print (u'精確模式分詞: ' + u'/'.join(jieba.lcut(text)))
print (u'全模式分詞: ' + u'/'.join(jieba.lcut(text, cut_all=True)))
print (u'搜尋引擎模式: ' + u'/'.join(jieba.lcut_for_search(text)))
--------------------------------------------------------------------
依次輸出:
精確模式分詞: 小明/碩士/畢業/於/中國科學院/計算所/,/後/在/日本京都大學/深造
全模式分詞: 小/明/碩士/畢業/於/中國/中國科學院/科學/科學院/學院/計算/計算所///
後/在/日本/日本京都大學/京都/京都大學/大學/深造
搜尋引擎模式: 小明/碩士/畢業/於/中國/科學/學院/科學院/中國科學院/計算/計算所/,
/後/在/日本/京都/大學/日本京都大學/深造
2.2 詞性標註
from jieba import posseg as psg # 載入詞性標註模組
text = u'小明碩士畢業於中國科學院計算所,後在日本京都大學深造'
"""
jieba.posseg.lcut 進行詞性標註
結果為 jieba.posseg.pair 型別的列表
每個pair物件中,word屬性表示詞語,flag表示詞性
詞性符合的解釋可見:https://gist.github.com/luw2007/6016931
"""
res = psg.lcut(text)
print ('repr: ' + repr(res[0]))
print ('詞: {}, 詞性: {}'.format(res[0].word, res[0].flag))
print ('詞性標註: ' + ' '.join(['{}/{}'.format(x.word, x.flag) for x in res]))
---------------------------------------------------------------------------------
輸出:
repr: pair('小明', 'nr')
詞: 小明, 詞性: nr
詞性標註: 小明/nr 碩士/n 畢業/n 於/p 中國科學院/nt 計算所/n ,/x 後在/t 日本京都大學/nt 深造/v
三、用 TF-IDF 和詞袋錶示文件特徵
import jieba
import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer,TfidfTransformer, TfidfVectorizer
contents = [u'小明喜歡看電影,小紅也喜歡看電影。',u'小明還喜歡看足球比賽。']
stopwords = {u',', u'。'} # 定義【,。】為停用詞
3.1 方法一:使用 CounterVectorizer 和 TfidfTransformer 計算 TF-IDF
"""
計算TF(每個詞的出現次數,未歸一)
tokenizer: 定義一個函式,接受文字,返回分詞的list
stop_words: 定義停用詞詞典,會在結果中刪除詞典中包含的詞
"""
tf = CountVectorizer(tokenizer=jieba.lcut, stop_words=stopwords)
res1 = tf.fit_transform(contents) # 使用函式擬合轉置contents
tf.vocabulary_ # 檢視詞彙對應關係
pd.DataFrame(res1.toarray(), # 檢視TF結果
columns=[x[0] for x in sorted(tf.vocabulary_.items(),
key=lambda x: x[1])])
# 檢視TF結果如下表:
也 | 喜歡 | 小明 | 小紅 | 電影 | 看 | 足球比賽 | 還 | |
---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 1 | 1 | 2 | 2 | 0 | 0 |
1 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 1 |
"""
use_idf: 表示在TF矩陣的基礎上計算IDF,並相乘得到TF-IDF
smooth_idf: 表示計算IDF時,分子上的總文件數+1
sublinear_tf: 表示使用 1+log(tf)替換原來的tf
norm: 表示對TF-IDF矩陣的每一行使用l2範數歸一化
"""
tfidf = TfidfTransformer(norm='l2', use_idf=True, smooth_idf=True, sublinear_tf=False)
res2 = tfidf.fit_transform(res1)
tfidf.idf_ # 檢視每個詞的IDF
pd.DataFrame(res2.toarray(),
columns=[x[0] for x in sorted(tf.vocabulary_.items(),
key=lambda x: x[1])])
# 檢視TF-IDF結果如下表:
也 | 喜歡 | 小明 | 小紅 | 電影 | 看 | 足球比賽 | 還 | |
---|---|---|---|---|---|---|---|---|
0 | 0.307784 | 0.437982 | 0.218991 | 0.307784 | 0.615568 | 0.437982 | 0.000000 | 0.000000 |
1 | 0.000000 | 0.379303 | 0.379303 | 0.000000 | 0.000000 | 0.379303 | 0.533098 | 0.533098 |
3.2 方法二:直接使用 TfidfVectorizer
# 引數為 CounterVectorizer 和 TfidfTransformer 的所有引數
tfidf = TfidfVectorizer(tokenizer=jieba.lcut,
stop_words=stopwords,
norm='l2',
use_idf=True,
smooth_idf=True,
sublinear_tf=False)
res = tfidf.fit_transform(contents)
tfidf.idf_ # 檢視每個詞的IDF,順序和 tfidf.vocabulary_ 對應(ndarray型別)
tfidf.vocabulary_ # 檢視每一列所代表的詞(字典)
輸出:{'也': 0, '喜歡': 1, '小明': 2, '小紅': 3, '電影': 4,
'看': 5, '足球比賽': 6, '還': 7}
pd.DataFrame({'詞': [x[0] for x in sorted(tfidf.vocabulary_.items(),key=lambda x: x[1])],
'IDF': tfidf.idf_}, columns=['詞', 'IDF'])
輸出:
| | 詞 | IDF |
| ---- | -------- | -------- |
| 0 | 也 | 1.405465 |
| 1 | 喜歡 | 1.000000 |
| 2 | 小明 | 1.000000 |
| 3 | 小紅 | 1.405465 |
| 4 | 電影 | 1.405465 |
| 5 | 看 | 1.000000 |
| 6 | 足球比賽 | 1.405465 |
| 7 | 還 | 1.405465 |
pd.DataFrame(res.toarray(),
columns=[x[0] for x in sorted(tfidf.vocabulary_.items(),
key=lambda x: x[1])])
也 | 喜歡 | 小明 | 小紅 | 電影 | 看 | 足球比賽 | 還 | |
---|---|---|---|---|---|---|---|---|
0 | 0.307784 | 0.437982 | 0.218991 | 0.307784 | 0.615568 | 0.437982 | 0.000000 | 0.000000 |
1 | 0.000000 | 0.379303 | 0.379303 | 0.000000 | 0.000000 | 0.379303 | 0.533098 | 0.533098 |
四、jieba 分詞
import jieba
article_words = []
for article in train_data[u'文章']: # 遍歷每篇文章
curr_words = []
for word in jieba.cut(article): # 遍歷文章中的每個詞並分詞
if word not in stopwords: # 去除停用詞
curr_words.append(word)
article_words.append(curr_words)
# 分詞結果儲存到seg_word_file.txt檔案
seg_word_file = os.path.join(output_dir, 'seg_words.txt')
with open(seg_word_file, 'wb') as outfile:
for words in article_words:
outfile.write(u' '.join(words).encode('utf8') + '\n')
print (u'分詞結果儲存到檔案:{}'.format(seg_word_file))
五、訓練word2vec模型
from gensim.models import Word2Vec
from gensim.models.word2vec import LineSentence
# 建立一個句子迭代器,一行為一個句子,詞和詞之間用空格分開
# 這裡我們把一篇文章當作一個句子
sentences = LineSentence(seg_word_file)
"""
訓練word2vec模型 引數說明:
sentences: 包含句子的list,或迭代器
size: 詞向量的維數,size越大需要越多的訓練資料,同時能得到更好的模型
alpha: 初始學習速率,隨著訓練過程遞減,最後降到 min_alpha
window: 上下文視窗大小,即預測當前這個詞的時候最多使用距離為window大小的詞
max_vocab_size: 詞表大小,如果實際詞的數量超過了這個值,過濾那些頻率低的
workers: 並行度
iter: 訓練輪數
min_count: 忽略出現次數小於該值的詞
"""
model = Word2Vec(sentences=sentences, size=100, iter=10, min_count=20)
# 儲存模型
model_file = os.path.join(output_dir, 'model.w2v')
model.save(model_file)
5.1 word2vec模型的使用
# 讀取模型
model2 = Word2Vec.load(model_file)
# 查詢語義相近的詞
def invest_similar(*args, **kwargs):
res = model2.most_similar(*args, **kwargs)
print u'\n'.join([u'{}:{}'.format(x[0], x[1]) for x in res])
invest_similar(u'攝影', topn=1)
"""
女人 + 先生 - 男人 = 女士
先生 - 女士 = 男人 - 女人,這個向量的方向就代表了性別!
"""
invest_similar(positive=[u'女人', u'先生'], negative=[u'男人'], topn=1)
# 計算兩個詞的相似度
model2.similarity(u'攝影', u'攝像')
# 查詢某個詞的詞向量
model2[u'攝影']