文本分類悟民,屬于有監(jiān)督學(xué)習(xí)中的一部分厕氨,在很多場景下都有應(yīng)用,下面通過小數(shù)據(jù)的實例汹粤,一步步完成中文短文本的分類實現(xiàn)命斧,整個過程盡量做到少理論重實戰(zhàn)。
開發(fā)環(huán)境玄括,我們選擇:
- Windows 系統(tǒng)
- Python 3.6
- Jupyter Notebook
本文使用的數(shù)據(jù)是我曾經(jīng)做過的一份司法數(shù)據(jù)冯丙,需求是對每一條輸入數(shù)據(jù),判斷事情的主體是誰遭京,比如報警人被老公打,報警人被老婆打泞莉,報警人被兒子打哪雕,報警人被女兒打等來進行文本有監(jiān)督的分類操作。
整個過程分為以下幾個步驟:
- 語料加載
- 分詞
- 去停用詞
- 抽取詞向量特征
- 分別進行算法建模和模型訓(xùn)練
- 評估鲫趁、計算 AUC 值
- 模型對比
基本流程如下圖所示:
下面開始項目實戰(zhàn)斯嚎。
1. 首先進行語料加載,在這之前挨厚,引入所需要的 Python 依賴包堡僻,并將全部語料和停用詞字典讀入內(nèi)存中。
第一步疫剃,引入依賴庫钉疫,有隨機數(shù)庫、jieba 分詞巢价、pandas 庫等:
import random
import jieba
import pandas as pd
第二步牲阁,加載停用詞字典,停用詞詞典為 stopwords.txt 文件壤躲,可以根據(jù)場景自己在該文本里面添加要去除的詞(比如冠詞城菊、人稱、數(shù)字等特定詞):
#加載停用詞
stopwords=pd.read_csv('stopwords.txt',index_col=False,quoting=3,sep="\t",names=['stopword'], encoding='utf-8')
stopwords=stopwords['stopword'].values
第三步碉克,加載語料凌唬,語料是4個已經(jīng)分好類的 csv 文件,直接用 pandas 加載即可漏麦,加載之后可以首先刪除 nan 行客税,并提取要分詞的 content 列轉(zhuǎn)換為 list 列表:
#加載語料
laogong_df = pd.read_csv('beilaogongda.csv', encoding='utf-8', sep=',')
laopo_df = pd.read_csv('beilaogongda.csv', encoding='utf-8', sep=',')
erzi_df = pd.read_csv('beierzida.csv', encoding='utf-8', sep=',')
nver_df = pd.read_csv('beinverda.csv', encoding='utf-8', sep=',')
#刪除語料的nan行
laogong_df.dropna(inplace=True)
laopo_df.dropna(inplace=True)
erzi_df.dropna(inplace=True)
nver_df.dropna(inplace=True)
#轉(zhuǎn)換
laogong = laogong_df.segment.values.tolist()
laopo = laopo_df.segment.values.tolist()
erzi = erzi_df.segment.values.tolist()
nver = nver_df.segment.values.tolist()
2. 分詞和去停用詞。
第一步唁奢,定義分詞霎挟、去停用詞和批量打標(biāo)簽的函數(shù),函數(shù)包含3個參數(shù):content_lines
參數(shù)為語料列表麻掸;sentences 參數(shù)為預(yù)先定義的 list酥夭,用來存儲分詞并打標(biāo)簽后的結(jié)果;category 參數(shù)為標(biāo)簽 :
#定義分詞和打標(biāo)簽函數(shù)preprocess_text
#參數(shù)content_lines即為上面轉(zhuǎn)換的list
#參數(shù)sentences是定義的空list,用來儲存打標(biāo)簽之后的數(shù)據(jù)
#參數(shù)category 是類型標(biāo)簽
def preprocess_text(content_lines, sentences, category):
for line in content_lines:
try:
segs=jieba.lcut(line)
segs = [v for v in segs if not str(v).isdigit()]#去數(shù)字
segs = list(filter(lambda x:x.strip(), segs)) #去左右空格
segs = list(filter(lambda x:len(x)>1, segs)) #長度為1的字符
segs = list(filter(lambda x:x not in stopwords, segs)) #去掉停用詞
sentences.append((" ".join(segs), category))# 打標(biāo)簽
except Exception:
print(line)
continue
第二步熬北,調(diào)用函數(shù)疙描、生成訓(xùn)練數(shù)據(jù),根據(jù)我提供的司法語料數(shù)據(jù)讶隐,分為報警人被老公打起胰,報警人被老婆打,報警人被兒子打巫延,報警人被女兒打效五,標(biāo)簽分別為0、1炉峰、2畏妖、3,具體如下:
sentences = []
preprocess_text(laogong, sentences,0)
preprocess_text(laopo, sentences, 1)
preprocess_text(erzi, sentences, 2)
preprocess_text(nver, sentences, 3)
第三步疼阔,將得到的數(shù)據(jù)集打散戒劫,生成更可靠的訓(xùn)練集分布,避免同類數(shù)據(jù)分布不均勻:
random.shuffle(sentences)
第四步婆廊,我們在控制臺輸出前10條數(shù)據(jù)迅细,觀察一下:
for sentence in sentences[:10]:
print(sentence[0], sentence[1]) #下標(biāo)0是詞列表,1是標(biāo)簽
得到的結(jié)果如圖所示:
3. 抽取詞向量特征淘邻。
第一步茵典,抽取特征,我們定義文本抽取詞袋模型特征:
from sklearn.feature_extraction.text import CountVectorizer
vec = CountVectorizer(
analyzer='word', # tokenise by character ngrams
max_features=4000, # keep the most common 1000 ngrams
)
第二步列荔,把語料數(shù)據(jù)切分敬尺,用 sk-learn
對數(shù)據(jù)切分,分成訓(xùn)練集和測試集:
from sklearn.model_selection import train_test_split
x, y = zip(*sentences)
x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=1256)
第三步贴浙,把訓(xùn)練數(shù)據(jù)轉(zhuǎn)換為詞袋模型:
vec.fit(x_train)
4. 分別進行算法建模和模型訓(xùn)練砂吞。
定義樸素貝葉斯模型,然后對訓(xùn)練集進行模型訓(xùn)練崎溃,直接使用 sklearn 中的 MultinomialNB:
from sklearn.naive_bayes import MultinomialNB
classifier = MultinomialNB()
classifier.fit(vec.transform(x_train), y_train)
5. 評估蜻直、計算 AUC 值。
第一步袁串,上面步驟1-4完成了從語料到模型的訓(xùn)練概而,訓(xùn)練之后,我們要用測試集來計算 AUC 值:
print(classifier.score(vec.transform(x_test), y_test))
得到的結(jié)果評分為:0.647331786543囱修。
第二步赎瑰,進行測試集的預(yù)測:
pre = classifier.predict(vec.transform(x_test))
6. 模型對比。
整個模型從語料到訓(xùn)練評估步驟1-5就完成了破镰,接下來我們來看看餐曼,改變特征向量模型和訓(xùn)練模型對結(jié)果有什么變化压储。
(1)改變特征向量模型
下面可以把特征做得更強一點,嘗試加入抽取 2-gram
和 3-gram
的統(tǒng)計特征源譬,把詞庫的量放大一點集惋。
from sklearn.feature_extraction.text import CountVectorizer
vec = CountVectorizer(
analyzer='word', # tokenise by character ngrams
ngram_range=(1,4), # use ngrams of size 1 and 2
max_features=20000, # keep the most common 1000 ngrams
)
vec.fit(x_train)
#用樸素貝葉斯算法進行模型訓(xùn)練
classifier = MultinomialNB()
classifier.fit(vec.transform(x_train), y_train)
#對結(jié)果進行評分
print(classifier.score(vec.transform(x_test), y_test))
得到的結(jié)果評分為:0.649651972158,確實有一點提高踩娘,但是不太明顯刮刑。
(2)改變訓(xùn)練模型
使用 SVM 訓(xùn)練:
from sklearn.svm import SVC
svm = SVC(kernel='linear')
svm.fit(vec.transform(x_train), y_train)
print(svm.score(vec.transform(x_test), y_test))
使用決策樹、隨機森林养渴、XGBoost雷绢、神經(jīng)網(wǎng)絡(luò)等等:
import xgboost as xgb
from sklearn.model_selection import StratifiedKFold
import numpy as np
# xgb矩陣賦值
xgb_train = xgb.DMatrix(vec.transform(x_train), label=y_train)
xgb_test = xgb.DMatrix(vec.transform(x_test))
在 XGBoost 中,下面主要是調(diào)參指標(biāo)厚脉,可以根據(jù)參數(shù)進行調(diào)參:
params = {
'booster': 'gbtree', #使用gbtree
'objective': 'multi:softmax', # 多分類的問題习寸、
# 'objective': 'multi:softprob', # 多分類概率
#'objective': 'binary:logistic', #二分類
'eval_metric': 'merror', #logloss
'num_class': 4, # 類別數(shù),與 multisoftmax 并用
'gamma': 0.1, # 用于控制是否后剪枝的參數(shù),越大越保守傻工,一般0.1、0.2這樣子孵滞。
'max_depth': 8, # 構(gòu)建樹的深度中捆,越大越容易過擬合
'alpha': 0, # L1正則化系數(shù)
'lambda': 10, # 控制模型復(fù)雜度的權(quán)重值的L2正則化項參數(shù),參數(shù)越大坊饶,模型越不容易過擬合泄伪。
'subsample': 0.7, # 隨機采樣訓(xùn)練樣本
'colsample_bytree': 0.5, # 生成樹時進行的列采樣
'min_child_weight': 3,
# 這個參數(shù)默認(rèn)是 1,是每個葉子里面 h 的和至少是多少匿级,對正負(fù)樣本不均衡時的 0-1 分類而言
# 假設(shè) h 在 0.01 附近蟋滴,min_child_weight 為 1 葉子節(jié)點中最少需要包含 100 個樣本。
'silent': 0, # 設(shè)置成1則沒有運行信息輸出痘绎,最好是設(shè)置為0\.
'eta': 0.03, # 如同學(xué)習(xí)率
'seed': 1000,
'nthread': -1, # cpu 線程數(shù)
'missing': 1
}
總結(jié)
上面通過真實司法數(shù)據(jù)津函,一步步實現(xiàn)中文短文本分類的方法,整個示例代碼可以當(dāng)做模板來用孤页,從優(yōu)化和提高模型準(zhǔn)確率來說尔苦,主要有兩方面可以嘗試:
- 特征向量的構(gòu)建,除了詞袋模型行施,可以考慮使用 word2vec 和 doc2vec 等允坚;
- 模型上可以選擇有監(jiān)督的分類算法、集成學(xué)習(xí)以及神經(jīng)網(wǎng)絡(luò)等蛾号。
如有侵權(quán)請聯(lián)系QQ:758230255刪除