【NLP】用ML實現中文短文本分類(二分類)

1.用ML實現外賣評論的分類

步驟

  • 語料加載、
  • 分詞、
  • 去停用詞、
  • 抽取詞向量特徵、
  • 分別進行算法建模和模型訓練、
  • 評估、計算AUC值、
  • 模型對比

1.進行語料加載。 在此之前,引入python依賴的包,並將全部語料和停用詞dict讀入內存中。

setp 1:引入依賴庫:random隨機數庫、jieba分詞庫、pandas庫
import random
import jieba
import pandas as pd
setp 2: 加載停用詞字典 停用詞詞典爲 stopwords.txt 文件,可以根據場景自己在該文本里面添加要去除的詞(比如冠詞、人稱、數字等特定詞)
stopwords = pd.read_csv('~/Desktop/stopWords_3.txt',index_col=False,quoting=3,sep='\t',names=['stopword'],encoding='utf-8')
stopwords = stopwords['stopword'].values
step 3:加載語料,語料是4個已經分好類的 csv 文件,直接用 pandas 加載即可,加載之後可以首先刪除 nan 行,並提取要分詞的 content 列轉換爲 list 列表
waimai_df = pd.read_csv('~/Downloads/ChineseNlpCorpus-master/datasets/waimai_10k/waimai_10k.csv', encoding='utf-8', sep=',')

#刪除語料的nan行
waimai_df.dropna(inplace=True)
#轉換成列表形式
waimai = waimai_df.review.values.tolist()
label_id = waimai_df.label.values.tolist()

2. 分詞和去停用詞

step 1:定義分詞、去停用詞的函數,函數包含2個參數:content_lines 參數爲語料列表;sentences 參數爲預先定義的 list,用來存儲分詞並打標籤後的結果;
def preprocess_text(content_lines, sentences):
    for line in content_lines:
        try:
            segs=jieba.lcut(line)
            segs = [v for v in segs if not str(v).isdigit()]#去數字
            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))
        except Exception:
            continue 
step 2:調用函數、生成訓練數據
sentences = []
preprocess_text(waimai,sentences)

sentences = list(zip(sentences,label_id))
# print(sentences)
Building prefix dict from the default dictionary ...
Loading model from cache /tmp/jieba.cache
Loading model cost 0.726 seconds.
Prefix dict has been built succesfully.
step 3:將得到的數據集打散,生成更可靠的訓練集分佈,避免同類數據分佈不均勻:
random.shuffle(sentences)
#在控制檯輸出前10條數據,觀察一下
for sentence in sentences[:10]:
    print(sentence[0], sentence[1]) 
一般 服務 差到 至極 飯量 要死 0
好吃 衛生 滿意 1
味道 送餐 太慢 飯菜 全涼 0
送餐 米飯 三盒 兩盒 兩盒 0
兩份 筷子 0
太坑 圖片 很大 可憐 肥肉 肘子 0
好吃 準時 1
豬肉 堡裏 醬流 滿意 豬肉 1
送來 沒人接 電話 0
太貴 八個 餃子 大小 包子 幾口 1

3. 抽取詞向量特徵。

step 1:抽取特徵,我們定義文本抽取詞袋模型特徵
from sklearn.feature_extraction.text import CountVectorizer
vec = CountVectorizer(
    analyzer='word', # tokenise by character ngrams
    max_features=None,  # keep the most common 1000 ngrams
    vocabulary=None
)
step 2:把語料數據切分,用 sk-learn 對數據切分,分成訓練集和測試集
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)
step 3:把訓練數據轉換爲詞袋模型
vec.fit(x_train)
CountVectorizer(analyzer='word', binary=False, decode_error='strict',
                dtype=<class 'numpy.int64'>, encoding='utf-8', input='content',
                lowercase=True, max_df=1.0, max_features=None, min_df=1,
                ngram_range=(1, 1), preprocessor=None, stop_words=None,
                strip_accents=None, token_pattern='(?u)\\b\\w\\w+\\b',
                tokenizer=None, vocabulary=None)

4.分別進行算法建模和模型訓練。

setp 1:定義SVM模型,然後對訓練集進行模型訓練,直接使用 sklearn 中的 svm.SVC
from sklearn.svm import SVC
# svm = SVC(kernel='rbf',C=1000,gamma=0.001)
svm = SVC(kernel='linear') #0.8401735068401736
# svm = SVC(kernel='poly',gamma=1000)
svm.fit(vec.transform(x_train), y_train)
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape='ovr', degree=3, gamma='auto_deprecated',
    kernel='linear', max_iter=-1, probability=False, random_state=None,
    shrinking=True, tol=0.001, verbose=False)

5. 評估、計算 AUC 值。

setp 1:上面步驟1-4完成了從語料到模型的訓練,訓練之後,我們要用測試集來計算 AUC 值:
 print(svm.score(vec.transform(x_test), y_test))
0.8468468468468469
step 2:進行測試集的預測:
pre = svm.predict(vec.transform(x_test))

6. 模型對比。

整個模型從語料到訓練評估 步驟1-5就完成了,接下來看看,改變特徵向量模型和訓練模型對結果有什麼變化。
6-1 改變特徵向量模型

下面可以把特徵做得更強一點,嘗試加入抽取 2-gram 和 3-gram 的統計特徵,把詞庫的量放大一點。

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)

from sklearn.svm import SVC
# svm = SVC(kernel='rbf',C=1000,gamma=0.001)
svm = SVC(kernel='linear') #0.8401735068401736
# svm = SVC(kernel='poly',gamma=1000)
svm.fit(vec.transform(x_train), y_train)
print(svm.score(vec.transform(x_test), y_test))
0.8375041708375042
6-2 改變訓練模型
1 使用樸素貝葉斯訓練 直接使用 sklearn 中的 MultinomialNB
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)

vec.fit(x_train)

from sklearn.naive_bayes import MultinomialNB
classifier = MultinomialNB()
classifier.fit(vec.transform(x_train), y_train)
print(classifier.score(vec.transform(x_test), y_test))
0.8495161828495161
2 使用XGBoost訓練
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))


params={'booster':'gbtree',
    'objective': 'binary:logistic',
    'eval_metric': 'auc',
    'max_depth':4,
    'lambda':10,
    'subsample':0.75,
    'colsample_bytree':0.75,
    'min_child_weight':2,
    'eta': 0.025,
    'seed':0,
    'nthread':8,
     'silent':1}

watchlist = [(xgb_train,'train')]
bst=xgb.train(params,xgb_train,num_boost_round=5,evals=watchlist)
#輸出概率
ypred=bst.predict(xgb_test)

# 設置閾值, 輸出一些評價指標,選擇概率大於0.5的爲1,其他爲0類
y_pred = (ypred >= 0.5)*1

from sklearn import metrics
print ('AUC: %.4f' % metrics.roc_auc_score(y_test,ypred))
print ('ACC: %.4f' % metrics.accuracy_score(y_test,y_pred))
print ('Recall: %.4f' % metrics.recall_score(y_test,y_pred))
print ('F1-score: %.4f' %metrics.f1_score(y_test,y_pred))
print ('Precesion: %.4f' %metrics.precision_score(y_test,y_pred))
print(metrics.confusion_matrix(y_test,y_pred))

[0]	train-auc:0.682634
[1]	train-auc:0.701378
[2]	train-auc:0.70118
[3]	train-auc:0.701877
[4]	train-auc:0.702868
AUC: 0.7019
ACC: 0.7501
Recall: 0.4863
F1-score: 0.5622
Precesion: 0.6662
[[1767  241]
 [ 508  481]]

2. 何爲AUC值和ROC曲線?

參考百度百科及以下這篇博文:
https://baijiahao.baidu.com/s?id=1597939133517926460&wfr=spider&for=pc

AUC(Area Under Curve被定義爲ROC曲線下的面積。
我們往往使用AUC值作爲模型的評價標準是因爲很多時候ROC曲線並不能清晰的說明哪個分類器的效果更好,而作爲一個數值,對應AUC更大的分類器效果更好。

其中,ROC曲線全稱爲受試者工作特徵曲線 (receiver operating characteristic curve),它是根據一系列不同的二分類方式(分界值或決定閾),以真陽性率(敏感性)爲縱座標,假陽性率(1-特異性)爲橫座標繪製的曲線。

具體到機器學習的理論中,ROC曲線該怎麼理解呢?首先,需要指出的是,ROC分析的是二元分類模型,也就是輸出結果只有兩種類別的模型,比如:(陽性/陰性)(有病/沒病)(垃圾郵件/非垃圾郵件)。在二分類問題中,數據的標籤通常用(0/1)來表示,在模型訓練完成後進行測試時,會對測試集的每個樣本計算一個介於0~1之間的概率,表徵模型認爲該樣本爲陽性的概率,我們可以選定一個閾值,將模型計算出的概率進行二值化,比如選定閾值=0.5,那麼當模型輸出的值大於等於0.5時,我們就認爲模型將該樣本預測爲陽性,也就是標籤爲1,反之亦然。選定的閾值不同,模型預測的結果也會相應地改變。二元分類模型的單個樣本預測有四種結果:

  • 真陽性(TP):判斷爲陽性,實際也是陽性。
  • 僞陽性(FP):判斷爲陰性,實際卻是陽性。
  • 真陰性(TN):判斷爲陰性,實際也是陰性。
  • 僞陰性(FN):判斷爲陰性,實際卻是陽性。

這四種結果可以畫成2 × 2的混淆矩陣:
在這裏插入圖片描述
有了混淆矩陣,就可以定義ROC曲線了。ROC曲線將假陽性率(FPR)定義爲 X 軸,真陽性率(TPR)定義爲 Y 軸。其中:
TPR:在所有實際爲陽性的樣本中,被正確地判斷爲陽性的樣本比率。
FPR:在所有實際爲陰性的樣本中,被錯誤地判斷爲陽性的樣本比率。
TPR = TP / (TP + FN)
FPR = FP / (FP + TN)
給定一個二分類模型和它的閾值,就可以根據所有測試集樣本點的真實值和預測值計算出一個 (X=FPR, Y=TPR) 座標點,這也就是繪製單個點的方法。那整條ROC曲線又該怎麼畫呢?具體方法如下:
在我們訓練完一個二分類模型後,可以使用該模型對測試集中的全部樣本點計算一個對應的概率值,每個值都介於0~1之間。假設測試集有100個樣本點,我們可以對這100個樣本的預測值從高到低排序,然後依次取每個值作爲閾值,一旦閾值確定我們就可以繪製ROC曲線上的一個點,按照這種方法依次將100個點繪製出來,再將各個點依次連接起來,就得到了我們想要的ROC曲線!

然後再回到最初的問題,
AUC就是衡量學習器優劣的一種性能指標,可通過對ROC曲線下各部分的面積求和而得。當我們繪製出ROC曲線之後,AUC的值自然也就計算好啦。

爲什麼需要用AUC值表徵模型的好壞?
在進行學習器的比較時,若一個學習器的ROC曲線被另一個學習器的曲線完全“包住”,則可斷言後者的性能優於前者;若兩個學習器的ROC曲線發生交叉,則難以一般性的斷言兩者孰優孰劣。此時如果一定要進行比較,則比較合理的判斷依據是比較ROC曲線下的面積,即AUC(Area Under ROC Curve)。

從AUC 判斷分類器(預測模型)優劣的標準:

AUC = 1,是完美分類器。
AUC = [0.85, 0.95], 效果很好
AUC = [0.7, 0.85], 效果一般
AUC = [0.5, 0.7],效果較低,但用於預測股票已經很不錯了
AUC = 0.5,跟隨機猜測一樣(例:丟銅板),模型沒有預測價值。
AUC < 0.5,比隨機猜測還差;但只要總是反預測而行,就優於隨機猜測。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章