jieba中文叫做結巴,是一款中文分詞工具,官方文檔鏈接:https://github.com/fxsjy/jieba
gensim.word2vec中文叫做詞向量模型,是是用來文章內(nèi)容向量化的工具。
gensim.word2vec官方文檔鏈接:https://radimrehurek.com/gensim/models/word2vec.html
LogisticRegression中文叫做邏輯回歸模型,是一種基礎、常用的分類方法。
完整項目壓縮文件基于word2vec的文本分類.zip上傳到百度云盤。
下載鏈接: https://pan.baidu.com/s/15qcM9rh2AzFyUNkeA61WPA 提取碼: awpk
0.環(huán)境配置
建議讀者安裝anaconda,這個集成開發(fā)環(huán)境自帶了很多包。
Anaconda5.2百度網(wǎng)盤下載鏈接: https://pan.baidu.com/s/1pbzVbr1ZJ-iQqJzy1wKs0A 密碼: g6ex
官網(wǎng)下載地址:https://repo.anaconda.com/archive/Anaconda3-5.2.0-Windows-x86_64.exe
下面代碼的開發(fā)環(huán)境為jupyter notebook,使用在jupyter notebook中的截圖表示運行結果。
各種軟件版本:
操作系統(tǒng):Windows10
Anaconda :5.2
python : 3.6
jieba : 0.39
gensim : 3.7.2
scikit_learn : 0.19
1.加載數(shù)據(jù)
1.1 下載數(shù)據(jù)
訓練集共有24000條樣本,12個分類,每個分類2000條樣本。
測試集共有12000條樣本,12個分類,每個分類1000條樣本。
在桌面新建文件夾命名為基于word2vec的文本分類
數(shù)據(jù)集壓縮文件data.zip,下載鏈接: https://pan.baidu.com/s/1PY3u-WtfBdZQ8FsKgWo_KA 密碼: hq5v
下載完成后,將壓縮文件data.zip放到桌面的文件夾基于word2vec的文本分類中,并將其解壓到當前文件夾,如下圖所示:

1.2 打開jupyter
在文件夾基于word2vec的文本分類中運行cmd,即在Windows資源管理器的路徑處輸入cmd,按Enter鍵運行,如下圖所示:

在cmd運行命令
jupyter notebook,如下圖所示:
cmd運行命令
jupyter notebook后,會自動打開網(wǎng)頁,點擊如下圖所示網(wǎng)頁中的按鈕:
代碼文件重命名為
word2vec_test,重命名按鈕位置如下圖所示:
1.3 使用pandas庫的read_csv方法加載文本文件
加載訓練集到變量train_df中,并打印訓練集前5行,代碼如下。
read_csv方法中有3個參數(shù),第1個參數(shù)是加載文本文件的路徑,第2個關鍵字參數(shù)sep是分隔符,第3個關鍵字參數(shù)header是文本文件的第1行是否為字段名。
import pandas as pd
train_df = pd.read_csv('sohu_train.txt', sep='\t', header=None)
train_df.columns = ['分類', '內(nèi)容']
train_df.head()
上面一段代碼的運行結果如下圖所示:

查看訓練集每個分類的名字以及樣本數(shù)量,代碼如下:
for name, group in train_df.groupby(train_df.columns[0]):
print(name,len(group))
上面一段代碼的運行結果如下圖所示:

加載測試集并查看每個分類的名字以及樣本數(shù)量,代碼如下:
test_df = pd.read_csv('sohu_test.txt', sep='\t', header=None)
for name, group in test_df.groupby(test_df.columns[0]):
print(name, len(group))
上面一段代碼的運行結果如下圖所示:

2.分詞
2.1 加載停頓詞
with open('./stopwords.txt', encoding='utf8') as file:
line_list = file.readlines()
stopword_list = [k.strip() for k in line_list]
stopword_set = set(stopword_list)
print('停頓詞列表,即變量stopword_list中共有%d個元素' %len(stopword_list))
print('停頓詞集合,即變量stopword_set中共有%d個元素' %len(stopword_set))
上面一段代碼的運行結果如下圖所示:

2.2 使用jieba庫制作分詞結果列表cutWords_list
需要安裝jieba庫,cmd中安裝命令:pip install jieba
對訓練集的24000條樣本循環(huán)遍歷,使用jieba庫的cut方法獲得分詞列表賦值給變量cutWords。
判斷分詞是否為停頓詞,如果不為停頓詞,則添加進變量cutWords中。
代碼如下:
import jieba
import time
cutWords_list = []
startTime = time.time()
content_series = train_df['內(nèi)容']
for i in range(len(content_series)):
content = content_series.iloc[i]
cutWords = [k for k in jieba.cut(content, True) if k not in stopword_set]
if (i+1) % 3000 == 0:
usedTime = time.time() - startTime
print('前%d篇文章分詞共花費%.2f秒' %(i+1, usedTime))
cutWords_list.append(cutWords)
上面一段代碼的運行結果如下:
前3000篇文章分詞共花費12.34秒
前6000篇文章分詞共花費29.44秒
前9000篇文章分詞共花費35.39秒
前12000篇文章分詞共花費43.45秒
前15000篇文章分詞共花費49.67秒
前18000篇文章分詞共花費56.70秒
前21000篇文章分詞共花費69.91秒
前24000篇文章分詞共花費75.45秒
2.3 保存分詞結果列表cutWords_list到文本文件
從上面的運行結果可以看出,對24000篇文章進行分詞共使用75秒,即1分15秒。
上面的運行結果為i7-8700k的cpu環(huán)境,一般cpu應該速度只有其一半。
在文件夾基于word2vec的文本分類中新建文件夾saved_variable
將分詞結果保存為文本文件cutWords_list.txt,代碼如下:
txtFilePath = 'saved_variable/cutWords_list.txt'
with open(txtFilePath, 'w', encoding='utf8') as file:
for cutWords in cutWords_list:
file.write(' '.join(cutWords))
file.write('\n')
2.4 從文本文件加載分詞結果列表cutWords_list
這種保存中間結果的做法,可以使下次運行時跳過2.2節(jié)。
直接加載文本文件并賦值給變量cutWords_list,能夠減少運行2.2節(jié)時花費的時間。
載入分詞文本文件cutWords_list.txt的代碼如下:
txtFilePath = 'saved_variable/cutWords_list.txt'
with open(txtFilePath, 'r', encoding='utf8') as file:
cutWords_list = [k.split(' ') for k in file.readlines()]
3.word2vec模型
3.1 word2vec模型實例化對象
完成此步驟需要先安裝gensim庫,安裝命令:pip install gensim
調(diào)用gensim.models庫中的Word2Vec類實例化模型對象,代碼如下:
from gensim.models import Word2Vec
startTime = time.time()
word2vec_model = Word2Vec(cutWords_list, size=200, iter=10, min_count=20)
usedTime = time.time() - startTime
print('形成word2vec模型共花費%.2f秒' %usedTime)
上面一段代碼的運行結果如下:
形成word2vec模型共花費176.25秒
3.2 通過word2vec對象的most_similar方法獲取詞義相近的詞
調(diào)用模型對象的方法時,一直提示警告信息,避免出現(xiàn)煩人的警告信息,代碼如下:
import warnings
warnings.filterwarnings('ignore')
調(diào)用Word2Vec模型對象的wv.most_similar方法查看與攝影含義最相近的詞。
wv.most_similar方法有2個參數(shù),第1個參數(shù)是要搜索的詞,第2個關鍵字參數(shù)topn數(shù)據(jù)類型為正整數(shù),是指需要列出多少個最相關的詞匯,默認為10,即列出10個最相關的詞匯。
wv.most_similar方法返回值的數(shù)據(jù)類型為列表,列表中的每個元素的數(shù)據(jù)類型為元組,元組有2個元素,第1個元素為相關詞匯,第2個元素為相關程度,數(shù)據(jù)類型為浮點型。
word2vec_model.wv.most_similar('攝影')
上面一段代碼的運行結果,如下圖所示:

wv.most_similar方法使用positive和negative這2個關鍵字參數(shù)的簡單示例。
查看女人+先生-男人的結果,代碼如下:
word2vec_model.most_similar(positive=['女人', '先生'], negative=['男人'], topn=1)
上面一段代碼的運行結果,如下圖所示:

查看兩個詞的相關性,如下圖所示:

3.3 使用pickle庫保存 word2vec模型
import pickle
pickleFilePath = 'saved_variable/word2vec_model.pickle'
with open(pickleFilePath, 'wb') as file:
pickle.dump(word2vec_model, file)
3.4 使用pickle庫加載word2vec模型
import pickle
pickleFilePath = 'saved_variable/word2vec_model.pickle'
with open(pickleFilePath, 'rb') as file:
word2vec_model = pickle.load(file)
4.特征工程
4.1 每篇文章的內(nèi)容表示成向量
對于每一篇文章,獲取文章的每一個分詞在word2vec模型的相關性向量。
然后把一篇文章的所有分詞在word2vec模型中的相關性向量求和取平均數(shù),即此篇文章在word2vec模型中的相關性向量。
實例化Word2Vec對象時,關鍵字參數(shù)size定義為200,則相關性矩陣都為200維。
定義getVector函數(shù)獲取每個文章的詞向量,傳入2個參數(shù),第1個參數(shù)是文章分詞的結果,第2個參數(shù)是word2vec模型對象。
import numpy as np
def get_contentVector(cutWords, word2vec_model):
vector_list = [word2vec_model.wv[k] for k in cutWords if k in word2vec_model]
contentVector = np.array(vector_list).mean(axis=0)
return contentVector
變量vector_list是通過列表推導式得出單篇文章所有分詞的詞向量,通過np.array方法轉(zhuǎn)成ndarray對象再對每一列求平均值。
代碼經(jīng)過作者實驗,使用numpy庫計算速度最快,讀者如果發(fā)現(xiàn)運行更快的代碼寫法可以留言。
每當完成3000篇文章詞向量轉(zhuǎn)換的時候,打印花費時間。
最終將24000篇文章的詞向量賦值給變量X,即X為特征矩陣。
import time
startTime = time.time()
contentVector_list = []
for i in range(len(cutWords_list)):
cutWords = cutWords_list[i]
if (i+1) % 3000 == 0:
usedTime = time.time() - startTime
print('前%d篇文章內(nèi)容表示成向量共花費%.2f秒' %(i+1, usedTime))
contentVector_list.append(get_contentVector(cutWords, word2vec_model))
X = np.array(contentVector_list)
上面一段代碼的運行結果如下:
前3000篇文章內(nèi)容表示成向量共花費21.11秒
前6000篇文章內(nèi)容表示成向量共花費53.62秒
前9000篇文章內(nèi)容表示成向量共花費64.50秒
前12000篇文章內(nèi)容表示成向量共花費79.25秒
前15000篇文章內(nèi)容表示成向量共花費90.29秒
前18000篇文章內(nèi)容表示成向量共花費102.94秒
前21000篇文章內(nèi)容表示成向量共花費127.81秒
前24000篇文章內(nèi)容表示成向量共花費136.92秒
4.2 使用ndarray對象的dump方法保存文章向量化結果X
因為形成特征矩陣的花費時間較長,為了避免以后重復花費時間,把特征矩陣保存為文件。
使用ndarray對象的dump方法,需要1個參數(shù),數(shù)據(jù)類型為字符串,為保存文件的文件名,代碼如下:
txtFilePath = 'saved_variable/X.txt'
X.dump(txtFilePath)
4.3 使用numpy庫的load方法加載文章向量化結果
加載此文件中的內(nèi)容賦值給變量X,代碼如下:
import numpy as np
txtFilePath = 'saved_variable/X.txt'
X = np.load(txtFilePath)
5.模型訓練
5.1 標簽編碼
調(diào)用sklearn.preprocessing庫的LabelEncoder類實例化對象賦值給變量labelEncoder。
調(diào)用變量labelEncoder的fit_transform方法對新聞分類做標簽編碼。
from sklearn.preprocessing import LabelEncoder
labelEncoder = LabelEncoder()
y = labelEncoder.fit_transform(train_df['分類'])
5.2 檢查特征矩陣和預測目標值
print(X.shape, y.shape)
上面一段代碼的運行結果如下:
(24000, 100), (24000, )
5.3 邏輯回歸模型
調(diào)用sklearn.linear_model庫的LogisticRegression方法實例化模型對象。
調(diào)用sklearn.model_selection庫的train_test_split方法劃分訓練集和測試集。
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
train_X, test_X, train_y, test_y = train_test_split(X, y, test_size=0.2)
logisticRegression_model = LogisticRegression()
logisticRegression_model.fit(train_X, train_y)
logisticRegression_model.score(test_X, test_y)
上面一段代碼的運行結果如下:
0.8033333333333333
5.4 使用pickle庫保存邏輯回歸模型
調(diào)用pickle庫中的dump方法保存模型為pickle文件。
import pickle
pickleFilePath = 'saved_variable/logisticRegression_model.pickle'
with open(pickleFilePath, 'wb') as file:
pickle.dump(logisticRegression_model, file)
5.5 使用pickle庫加載邏輯回歸模型
調(diào)用pickle庫中的dump方法加載模型。
import pickle
pickleFilePath = 'saved_variable/logisticRegression_model.pickle'
with open(pickleFilePath, 'rb') as file:
logisticRegression_model = pickle.load(file)
6.模型評估
6.1 交叉驗證
交叉驗證的結果更具有說服力。
調(diào)用sklearn.model_selection庫的ShuffleSplit方法實例化交叉驗證對象。
調(diào)用sklearn.model_selection庫的cross_val_score方法獲得交叉驗證每一次的得分。
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import ShuffleSplit
from sklearn.model_selection import cross_val_score
cv_split = ShuffleSplit(n_splits=5, train_size=0.7, test_size=0.2)
score_ndarray = cross_val_score(LogisticRegression(), X, y, cv=cv_split)
print(score_ndarray)
print(score_ndarray.mean())
上面一段代碼的運行結果如下:
[0.80041667 0.79833333 0.79645833 0.79041667 0.7975 ]
0.796625
6.2 混淆矩陣
6.2.1 獲取訓練集文本內(nèi)容向量化后的特征矩陣
import pickle
import numpy as np
import pandas as pd
import jieba
import time
pickleFilePath = 'saved_variable/word2vec_model.pickle'
with open(pickleFilePath, 'rb') as file:
word2vec_model = pickle.load(file)
def get_featureMatrix(content_series):
vector_list = []
for content in content_series:
vector = get_contentVector(jieba.cut(content, True), word2vec_model)
vector_list.append(vector)
featureMatrix = np.array(vector_list)
return featureMatrix
test_df = pd.read_csv('sohu_test.txt', sep='\t', header=None)
test_df.columns = ['分類', '內(nèi)容']
startTime = time.time()
featureMatrix = getVectorMatrix(test_df['內(nèi)容'])
usedTime = time.time() - startTime
print('測試集文本內(nèi)容向量化花費時間%.2f秒' %usedTime)
上面一段代碼的運行結果如下:
測試集文本內(nèi)容向量化花費時間119.32秒
6.2.2 繪制混淆矩陣
from sklearn.metrics import confusion_matrix
pickleFilePath = 'saved_variable/logisticRegression_model.pickle'
with open(pickleFilePath, 'rb') as file:
logisticRegression_model = pickle.load(file)
test_label_list = labelEncoder.transform(test_df['分類'])
predict_label_list = logisticRegression_model.predict(featureMatrix)
pd.DataFrame(confusion_matrix(test_label_list, predict_label_list),
columns=labelEncoder.classes_,
index=labelEncoder.classes_ )
上面一段代碼的運行結果如下:

6.3 報告表
from sklearn.metrics import precision_recall_fscore_support
def eval_model(test_label_list, predict_label_list, className_list):
# 計算每個分類的Precision, Recall, f1, support
p, r, f1, s = precision_recall_fscore_support(test_label_list, predict_label_list)
# 計算總體的平均Precision, Recall, f1, support
total_p = np.average(p, weights=s)
total_r = np.average(r, weights=s)
total_f1 = np.average(f1, weights=s)
total_s = np.sum(s)
res1 = pd.DataFrame({
u'Label': className_list,
u'Precision': p,
u'Recall': r,
u'F1': f1,
u'Support': s
})
res2 = pd.DataFrame({
u'Label': ['總體'],
u'Precision': [total_p],
u'Recall': [total_r],
u'F1': [total_f1],
u'Support': [total_s]
})
res2.index = [999]
res = pd.concat([res1, res2])
return res[['Label', 'Precision', 'Recall', 'F1', 'Support']]
eval_model(test_label_list, predict_label_list, labelEncoder.classes_)\
上面一段代碼的運行結果如下:

7.結論
1.word2vec模型應用的第1個小型項目,訓練集數(shù)據(jù)共有24000條,測試集數(shù)據(jù)共有12000條。
經(jīng)過交叉驗證,模型平均得分為0.80左右。
2.測試集的驗證效果中,體育、教育、健康、文化、旅游、汽車、娛樂這7個分類得分較高,即容易被正確分類。
女人、娛樂、新聞、科技、財經(jīng)這5個分類得分較低,即難以被正確分類。
3.學習如何提高文本分類的準確率,請查看我的另外一篇文章
《基于jieba、TfidfVectorizer、LogisticRegression的搜狐新聞文本分類》。
文章鏈接:http://www.itdecent.cn/p/ec053e920a3b
4.本文作者在2018年8月30日寫作此文,2019年4月18日花費4個小時完善文章,文章內(nèi)容保證完全正確。