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,比隨機猜測還差;但只要總是反預測而行,就優於隨機猜測。