問題定義
在這個項目中會采用20 Newgroups的數據(http://qwone.com/~jason/20Newsgroups/),這時網上非常流行的對文本進行分類和聚類的數據集。
數據集中的數據分為兩部分,一部分是用來訓練算法模型的數據,一部分是用來評估算法的新數據。
網上提供了3個數據集,這里采用20news-bydate這個數據集進行項目研究。這個數據集是按照日期進行排序的,並去掉了部分重復數據和Header,共包含18846個文檔。
導入數據
這里使用scikit-learn的Loadfiles導入文檔數據,文檔是按照不同的分類目錄來保存的,文件目錄名稱即所屬類別,文檔目錄結構如下:
在導入文檔數據之前,要導入項目中所需的類庫:
1 from sklearn.datasets import load_files 2 from sklearn.feature_extraction.text import CountVectorizer 3 from sklearn.feature_extraction.text import TfidfVectorizer 4 from sklearn.linear_model import LogisticRegression 5 from sklearn.naive_bayes import MultinomialNB 6 from sklearn.neighbors import KNeighborsClassifier 7 from sklearn.svm import SVC 8 from sklearn.tree import DecisionTreeClassifier 9 from sklearn.metrics import classification_report 10 from sklearn.metrics import accuracy_score 11 from sklearn.model_selection import cross_val_score 12 from sklearn.model_selection import KFold 13 from sklearn.model_selection import GridSearchCV 14 15 from sklearn.ensemble import AdaBoostClassifier 16 from sklearn.ensemble import RandomForestClassifier 17 from matplotlib import pyplot as plt 18 19 #導入數據 20 categories=['alt.atheism', 'rec.sport.hockey', 'comp.graphics', 21 'sci.crypt','comp.os.ms-windows.misc','sci.electronics', 22 'comp.sys.ibm.pc.hardware', 23 'sci.med','comp.sys.mac.hardware', 'sci.space', 24 'comp.windows.x','soc.religion.christian','misc.forsale', 25 'talk.politics.gus','rec.autos','talk.politics.mideast', 26 'rec.motorcycles','talk.politics.misc','rec.sport.baseball', 27 'talk.religion.misc' 28 ] 29 #導入訓練數據 30 train_path='/home/aistudio/work/20news-bydate-train' 31 dataset_train=load_files(container_path=train_path,categories=categories) 32 #導入評估數據 33 test_path='/home/aistudio/work/20news-bydate-test' 34 dataset_test=load_files(container_path=test_path,categories=categories)
利用機器學習對文本進行分類,與對數值特征進行分類最大的區別是,對文本進行分類時先要提取文本特征,相對於之前的項目來說,提取道德文本特征屬性個數是巨大的,會有超過萬個的特征屬性,甚至超過10萬個。
文本特征提取
文本數據屬性屬於非結構化的數據,一般要轉換成結構化的數據才能夠通過機器學習算法進行文本分類,常見的做法是將文本轉換成文檔詞項矩陣,矩陣中的元素可以使用詞頻或TF-IDF值等。
TF-IDF值是一種用於信息檢索與數據挖掘的常用加權術。TF的意思是詞頻(Term Frequency),IDF是逆向文件頻率(Inverse Document Frequency)。
TF-IDF的主要思想是:如果某一個詞或短語在一篇文章中出現的頻率高,並且在其他文章中很少出現,則認為此詞或短語具有很好的類別區分能力,適合用來分類。
IDF的主要思想是:如果包含詞條t的文檔越少,也就是n越小,IDF越大,則說明詞條t具有很好的類別區分能力。
如果某一類文檔C中包含詞條t的文檔數為m,而其他類包含t的文檔總數為k,顯然,所有包含t的文檔數為n=m+k,當m大的時候,n也大,按照IDF公式得到的IDF值小,這說明該詞條t的類別區分能力不強。但實際上,如果一個詞條在一個類的文檔中頻繁出現,則說明該詞條能夠很好的代表這個類的文本特征,以區別於其他類文檔,這就是IDF的不足之處。
在一份給定的文件中,TF值某一個給定的詞語在該文件中出現的頻率,這是對詞數(Term Count)的歸一化,以防止它偏向長的文件。
IDF是一個詞語普遍重要性的度量,某一個特定的詞語IDF,可以有總文件數目除以包含該詞語的文件數目,再將得到的商取對數得到。
在scikit-learn中提供了詞頻和TF-IDF來進行文檔特征提取的實現,分別是CountVectorizer和TfidTransformer。下面對訓練數據集分別進行詞頻和TF-IDF的計算:
1 #數據准備和理解 2 #計算詞頻 3 count_vect=CountVectorizer(stop_words='english',decode_error='ignore') 4 x_train_counts=count_vect.fit_transform(dataset_train.data) 5 #查看數據維度 6 print(x_train_counts.shape)
詞頻的計算結果如下:
(10768, 124899)
接下來計算一下TF-IDF,
1 #計算TF-IDF 2 tf_transformer=TfidfVectorizer(stop_words='english',decode_error='ignore') 3 x_train_counts_tf=tf_transformer.fit_transform(dataset_train.data) 4 #查看數據維度 5 print(x_train_counts_tf.shape)
TF-IDF的計算結果如下:
(10768, 124899)
兩次得到的結果相同。
評估算法
通過簡單的查看數據維度,不能確定哪個算法對這個問題比較有效。下面采用10折交叉驗證方式比較算法的准確度。
1 #設置評估算法的基准 2 num_folds=10 3 seed=7 4 scoring='accuracy'
接下來將會利用提取到的文本特征TF-IDF來對算法進行審查,審查的算法如下:
線性算法:邏輯回歸(LR)
非線性算法:分類與回歸樹(CART),支持向量機(SVM),朴素貝葉斯分類器(MNB)可K近鄰(KNN)
算法模型的初始化代碼如下:
1 #評估算法:生成算法模型 2 models={} 3 models['LR']=LogisticRegression() 4 models['SVM']=SVC() 5 models['CART']=DecisionTreeClassifier() 6 models['MNB']=MultinomialNB() 7 models['KNN']=KNeighborsClassifier()
所有的算法使用默認參數,比較算法模型的准確度和標准方差,以便從中選擇兩道三種可以進一步研究的算法:
1 #比較算法 2 results=[] 3 for key in models: 4 kfold=KFold(n_splits=num_folds,random_state=seed) 5 cv_results=cross_val_score(models[key],x_train_counts_tf,dataset_train.target,cv=kfold,scoring=scoring) 6 results.append(cv_results) 7 print('%s : %f (%f)' % (key, cv_results.mean(),cv_results.std()))
執行結果顯示,羅輯回歸(LR)具有最好的准確度,朴素貝葉斯分類器(MNB)和K近鄰(KNN)也值得進一步研究。
LR : 0.900726 (0.007471)
SVM : 0.049873 (0.013272) CART : 0.662983 (0.007810) MNB : 0.882708 (0.007777) KNN : 0.795786 (0.009604)
接下來看一下算法每次執行結果的分布情況--采用箱線圖:
從圖中可以看到,朴素貝葉斯分類器數據離散程度比較好,羅輯回歸的偏差比較大。
算法結果的離散程度能夠反應算法對數據的使用情況,所以對羅輯回歸和朴素貝葉斯進行進一步研究,實行算法調參。
算法調參
通過上面的分析,邏輯回歸(LR)和朴素貝葉斯分類器(MNB)效果比較好,可以對其進行參數調參來進一步提高准確度。
在邏輯回歸中的超參數是C,C是目標的約束函數,C值越小則正則化強度越大。對C進行調參,每次給C設定一定數量的值,重復這個步驟,直到找到最優值。
1 #算法調參--LR 2 param_grid={} 3 param_grid['C']=[0.1,3,5,13,15] 4 model=LogisticRegression() 5 kfold=KFold(n_splits=num_folds,random_state=seed) 6 grid=GridSearchCV(estimator=model,param_grid=param_grid,scoring=scoring,cv=kfold) 7 grid_result=grid.fit(X=x_train_counts_tf,y=dataset_train.target) 8 print('最優:%s s使用 %s' % (grid_result.best_score_,grid_result.best_params_))
可以看到C的最優參數是15(15是通過多次調整param_grid參數得到的)。執行結果如下:
最優:0.9221768202080238 s使用 {'C': 15}
通過對羅輯回歸調參,准確度提升到0.92,提升還是比較大的。
朴素貝葉斯分類器有一個alpha參數,該參數是一個平滑參數,默認值為1.0,可以嘗試對此進行調試。
1 #算法調參--MNB 2 param_grid={} 3 param_grid['alpha']=[0.001,0.01,0.1,1.5] 4 model=MultinomialNB() 5 kfold=KFold(n_splits=num_folds,random_state=seed) 6 grid=GridSearchCV(estimator=model,param_grid=param_grid,scoring=scoring,cv=kfold) 7 grid_result=grid.fit(X=x_train_counts_tf,y=dataset_train.target) 8 print('最優:%s s使用 %s' % (grid_result.best_score_,grid_result.best_params_))
同樣,通過多次調整param_grid,得到朴素貝葉斯分類器的alpha參數最優值0.01.
最優:0.9153046062407132 s使用 {'alpha': 0.01}
接下來審查集成算法。
集成算法
除了調參,提高算法准確度的方法是使用集成算法。下面對以下兩種集成算法進行比較,看看能否進一步提高模型的准確度。
- 隨機森林(RF)
- AdaBoost(AB)
1 #集成算法 2 ensembles={} 3 ensembles['RF']=RandomForestClassifier() 4 ensembles['AB']=AdaBoostClassifier() 5 #比較集成算法 6 results=[] 7 for key in ensembles: 8 kfold=KFold(n_splits=num_folds,random_state=seed) 9 cv_results=cross_val_score(ensembles[key],x_train_counts_tf,dataset_train.target,cv=kfold,scoring=scoring) 10 results.append(cv_results) 11 print('%s: %f (%f)' % (key,cv_results.mean(),cv_results.std())) 12 #箱線圖 13 fig=plt.figure() 14 fig.suptitle('Algorithm Comparision') 15 ax=fig.add_subplot(111) 16 plt.boxplot(results) 17 ax.set_xticklabels(ensembles.keys()) 18 plt.show()
RF: 0.739134 (0.011091) AB: 0.548477 (0.011514)
從箱線圖來看,隨機森林(RF)的分布比較均勻,對數據的適用性比較高,更值得進一步研究。
集成算法調參
通過集成算法的分析,發現隨機森林算法具有較高的准確度和非常穩定的數據分布。隨機森林有一根很重要的參數n_estimators,下面對n_estimators進行調參優化,爭取找到最優解。
1 #調參RF 2 param_grid={} 3 param_grid['n_estimators']=[10,100,150,200] 4 model=RandomForestClassifier() 5 kfold=KFold(n_splits=num_folds,random_state=seed) 6 grid=GridSearchCV(estimator=model,param_grid=param_grid,scoring=scoring,cv=kfold) 7 grid_result=grid.fit(X=x_train_counts_tf,y=dataset_train.target) 8 print('最優:%s s使用 %s' % (grid_result.best_score_,grid_result.best_params_))
調參之后的最優結果如下:
最優:0.8697065378900446 s使用 {'n_estimators': 200}
確定最終模型
通過對算法的比較和調參發現,邏輯回歸具有最高的准確度,因此使用邏輯回歸算法生成算法模型。
為了保持數據特征的一致性,對新數據進行文本特征提取時應進行特征擴充,下面使用之前生成的tf_transformer的transform方法來處理評估數據集。
1 #生成模型 2 model=LogisticRegression(C=15) 3 model.fit(x_train_counts_tf,dataset_train.target) 4 x_test_counts=tf_transformer.transform(dataset_test.data) 5 predictions=model.predict(x_test_counts) 6 print(accuracy_score(dataset_test.target,predictions)) 7 print(classification_report(dataset_test.target,predictions))
從結果可以看到,准確度大概達到85%,與期待的結果比較一致。
0.8515625 precision recall f1-score support 0 0.81 0.76 0.79 319 1 0.74 0.80 0.77 389 2 0.78 0.74 0.76 394 3 0.70 0.75 0.73 392 4 0.82 0.85 0.84 385 5 0.85 0.77 0.81 395 6 0.81 0.90 0.85 390 7 0.90 0.90 0.90 396 8 0.96 0.95 0.96 398 9 0.90 0.94 0.92 397 10 0.96 0.97 0.97 399 11 0.96 0.93 0.95 396 12 0.78 0.79 0.78 393 13 0.91 0.87 0.89 396 14 0.90 0.92 0.91 394 15 0.84 0.93 0.88 398 16 0.97 0.88 0.92 376 17 0.87 0.76 0.81 310 18 0.67 0.62 0.64 251 micro avg 0.85 0.85 0.85 7168 macro avg 0.85 0.84 0.85 7168 weighted avg 0.85 0.85 0.85 7168