自然語言處理中一個很常見的操作就是文本分類,比如一組新聞文本,通過分類模型,將新聞文本分為政治、體育、軍事、娛樂、財經(jīng)等等幾大類。那么分類第一步就是文本向量化,前一篇博客講了一些,本文可以說是前文的實踐版本。本文主要介紹一些常見的文本分類模型,說是介紹,其實主要以代碼和結果為主,并不會詳細的介紹每個算法的思想、原理、推導過程等,那樣的話,估計可以寫一個7、8篇的系列了,另外我也發(fā)現(xiàn)很多博客都是理論為主,代碼非常少,給人的感覺就是這件事我弄明白了,但具體如何干不知道,講的似乎很難、很神秘,沒有相應代碼,讓人望而生畏。所以本文還是偏工程一些,閱讀本文的同學希望已經(jīng)有了這些文本分類算法的理論基礎。先說說我用的數(shù)據(jù),約20萬短文本,包含8個大類,分別為:餐飲、交通、購物、娛樂、居家等,每個大類約25000條數(shù)據(jù),文本平均20個字左右,最短的文本僅有2個字。如下面所示: __label__1 天貓 超市 慕滋 五谷 無 添加 糖 粗糧 雜糧 消化 餅干 g 代 早餐 糕點
__label__1 天貓 超市 滿 減 云南 紅 提 kg 提子 葡萄 新鮮 水果
__label__1 天貓 超市 原裝 進口 噓 噓 樂 成長 褲 紙尿褲 拉拉 褲 L19 片 Kg
__label__1 天貓 超市 衛(wèi)龍 小 面筋 g 零食 辣條 辣片 麻辣 素食 豆干 制品 大刀 肉
__label__1 天貓 超市 康師傅 礦物質(zhì) 水 ml 瓶 整箱 飲用水
__label__1 天貓 超市 紅牛 維生素 功能 飲料 整箱 裝 原味 型 ml 罐 箱
__label__1 天貓 超市 香楠 蛋羹 味 麻薯 夾心 麻 糬 糕點 休閑 零食 小吃 g
__label__1 天貓 超市 蒙牛 特侖蘇 醇 纖 牛奶 ml 盒 平衡 搭檔 平衡 好搭檔
__label__1 天貓 超市 味全 每日 C 純 果汁 胡蘿卜 果蔬汁 ml16 截單
__label__1 天貓 超市 金 菜地 豆干 五香 茶 干 g 豆腐干 特色 休閑 零食 豆制品
__label__1 天貓 超市 新 希望 牛奶 香蕉 牛奶 ml 盒 箱 甜蜜 好 滋味
__label__1 天貓 超市 良品 鋪子 爆漿 麻薯 抹 茶味 g 糕點 點心 零食 特產(chǎn) 小吃
__label__1 天貓 超市 森永 嗨 酸酸 噠 酸果 軟糖 青 檸味 g 維 c 水果 糖果 零食
__label__1 天貓 超市 桂格 即食 純 燕麥片 粗糧 原味 沖 飲 谷物 早餐 g 袋裝
__label__1 天貓 超市 滿 減 挪威 冰凍 青花魚 柳 g 包 冷凍 海鮮 魚肉 鯖 魚
__label__1 天貓 超市 甘 竹牌 豆豉 鯪魚 罐頭 g 盒 下 飯菜 特產(chǎn) 小吃 休閑 食品
__label__1 天貓 超市 姚 太太 青口 梅 g 蜜餞 果脯 話梅 肉 梅子 青梅 酸甜 涼果
__label__1 天貓 超市 蒙牛 特侖蘇 醇 纖 牛奶 ml 盒 平衡 搭檔 平衡 好搭檔
很多文本內(nèi)容都是淘寶體,也就是商品標題,當然上面僅僅是個示例。我這里已經(jīng)分好詞,并且為了方便后面使用fastText分類模型,已經(jīng)按照fastText格式做了排版,也就是第一項__label__1是標簽, 后面是文本正文,這個訓練集需要人工標記好,這是一件費時費力的事情。下面是不同分類模型代碼,因為加了注釋,所以這里就不一一解釋了。 from sklearn.svm import SVC from sklearn.naive_bayes import MultinomialNB from sklearn.neighbors import KNeighborsClassifier from sklearn.ensemble import GradientBoostingClassifier from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer def nb_model(train, train_label, test, test_label): clf_model = MultinomialNB(alpha=0.01) clf_model.fit(train, train_label) predict_results = clf_model.predict(test) predict_list = predict_results.tolist() for i, pred in enumerate(predict_list): if (pred == test_label[i]): print("nb_model_precision_score: " + str(float(count) / len(predict_list))) def knn_model(train, train_label, test, test_label): knn_model = KNeighborsClassifier(n_neighbors=8) knn_model.fit(train, train_label) predict_results = knn_model.predict(test) predict_list = predict_results.tolist() for i, pred in enumerate(predict_list): if (pred == test_label[i]): print("knn_model_precision_score: " + str(float(count) / len(predict_list))) def svm_model(train, train_label, test, test_label): svm_clf = SVC(kernel="linear", verbose=False) svm_clf.fit(train, train_label) predict_results = svm_clf.predict(test) predict_list = predict_results.tolist() for i, pred in enumerate(predict_list): if (pred == test_label[i]): print("svm_model_precision_score: " + str(float(count) / len(predict_list))) def text_classification(): print("start loading data...") finput = open("data/filter_total_half.txt", encoding='utf-8') text_array = line.split("\t", 1) if (len(text_array) != 2): total_text_list.append(text_array[1]) total_label_list.append(text_array[0]) probability = random.random() train_text_list.append(text_array[1]) train_label_list.append(text_array[0]) test_text_list.append(text_array[1]) test_label_list.append(text_array[0]) print("load data is finished...") print("start building vector model...") vec_total = CountVectorizer() vec_total.fit_transform(total_text_list) # 基于構建的詞典分別統(tǒng)計訓練集/測試集詞頻, 即每個詞出現(xiàn)1次、2次、3次等 vec_train = CountVectorizer(vocabulary=vec_total.vocabulary_) tf_train = vec_train.fit_transform(train_text_list) vec_test = CountVectorizer(vocabulary=vec_total.vocabulary_) tf_test = vec_test.fit_transform(test_text_list) tfidftransformer = TfidfTransformer() tfidf_train = tfidftransformer.fit(tf_train).transform(tf_train) tfidf_test = tfidftransformer.fit(tf_test).transform(tf_test) print("building vector model is finished...") nb_model(tfidf_train, train_label_list, tfidf_test, test_label_list) knn_model(tfidf_train, train_label_list, tfidf_test, test_label_list) svm_model(tfidf_train, train_label_list, tfidf_test, test_label_list) print("building predict model is finished...") foutput_test = open("data/data_test.txt", 'w', encoding='utf-8') foutput_train = open("data/data_train.txt", 'w', encoding='utf-8') with open("data/filter_total_half.txt", encoding='utf-8') as finput: probability = random.random() foutput_train.write(line.strip() + "\n") foutput_test.write(line.strip() + "\n") classifier = fasttext.supervised("data/data_train.txt", "data/cooking_fasttext_bkk.model", label_prefix="__label__", lr=0.25, dim=100, silent=False, epoch=25, word_ngrams=3, loss="hs", bucket=2000000) result = classifier.test("data/data_test.txt") if __name__ == '__main__': print("\n傳統(tǒng)方法文本分類...") print("\n----------------------------------------------\n")
程序運行結果如下: 
我還寫了一個基于卷積神經(jīng)網(wǎng)絡的版本,修改自github,由于公司也有在用,這里就不把代碼貼出來了??傮w看,cnn的準確度最高,fastText次之。不過基于cnn的方法,需要事先訓練詞向量,訓練過程也比較慢。而傳統(tǒng)方法,如svm,準確度達0.95,已經(jīng)很高了,從這一點也說明,不管是基于深度學習的卷積神經(jīng)網(wǎng)絡分類方法,還是傳統(tǒng)的分類方法,其實模型反而是其次,最重要的是數(shù)據(jù)集的質(zhì)量,模型選擇和模型調(diào)參,對最終精度提升都是小幅度的,而數(shù)據(jù)集的質(zhì)量高低則是精度提升的瓶頸,有時真得不怕麻煩,整理出一份高質(zhì)量的數(shù)據(jù)集,才能訓練出精度更準、召回更高的模型??吹竭@里,是不是很多同學覺得文本分類其實沒什么神秘的,有現(xiàn)成的訓練框架使用,如:sklearn,還有那么多文獻資料可供查閱,唯獨沒有適合自己業(yè)務的訓練集,整理訓練集,這可能是整個模型訓練過程中最花時間的事情了。當然,這里面也涉及很多模型調(diào)參細節(jié),需要深入算法原理才能真正玩轉(zhuǎn)。
|