1. 程式人生 > >自然語言(NLP)處理流程—IF-IDF統計—jieba分詞—Word2Vec模型訓練使用

自然語言(NLP)處理流程—IF-IDF統計—jieba分詞—Word2Vec模型訓練使用

開發環境 jupyter notebook

一、資料感知—訓練與測試資料

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'攝影']