1、前言
本文是文本分類模型的第三彈,利用詞袋模型(BoW),詞頻逆文檔頻率(TF-IDF)與 LightGBM 模型進行文本分類。html
本來計劃的第三彈內容爲 TextCNN 網絡,介於最近剛剛利用 LightGBM 完成了一個簡單的文本分類的任務,就趁熱記錄一下,做爲第三彈的內容。python
這裏是文本分類系列:算法
文本分類模型第一彈:關於Fasttext,看這一篇就夠了網絡
文本分類模型第二彈:HAN(Hierarchy Attention Network)app
文本分類模型第三彈:BoW(Bag of Words) + TF-IDF + LightGBM機器學習
2、相關論文及理論
1.LightGBM
這裏是 LightGBM 提出的論文《LightGBM: A Highly Efficient Gradient Boosting Decision Tree》,LightGBM 本質上也是 梯度提高樹(GBDT)的優化模型,GBDT 的本質是迴歸樹模型,在作分類任務時,經過「迴歸相應類別」的機率值,來曲線完成分類任務。LightGBM 與上一代 kaggle 神器 Xgboost 相比,因爲採用了直方圖算法(用於特徵處理),和 Leaf-wise 的樹分裂方法(用於模型構建),模型準確度更高,訓練耗時更低。其不只應用於傳統的機器學習迴歸及二分類,多分類任務,在 CTR 預估,推薦系統中也有着普遍的應用。性能
2.詞袋模型(BoW)
再囉嗦一句詞袋模型,Bag of Words,這是我學習的第一個 NLP 語言模型。Bag of Words 會將詞典裏面的每個單詞編一個序號,這個序號從 0 開始累加,這樣當構造一個長度等於詞典的 list 時,它其中的每一維就對應了詞典中的每個字。學習
當須要表示某一個詞時,這個向量對應這個單詞的那一維的值爲1,其他爲0,這就是詞的 one-hot 表示方法。優化
當須要表示一篇文章時,文章中的每個詞對應的那一維的值爲單詞出現的次數,也叫作詞頻(TF),這就是最簡單的文本表示方法。url
3.TF-IDF
理論上來講,當咱們構造出了基於 BoW 的文檔向量後,只要對向量稍加處理(歸一化),就可使用這個文檔進行文本分類了,但這樣的效果並非很好,由於 BoW + TF 構造的向量只是對單個文檔進行了信息統計,並無考慮到全部文檔的統計信息,以及文檔之間究竟以什麼來區分。因此須要對文檔中出現的詞的 TF 值稍加處理,那就是計算TF-IDF。
網上搜一下 TF-IDF 的原理,大概能夠搜到上萬篇博客文章,因此這裏就再也不進行贅述。只用一句話來解釋它的做用就是:找出本身有什麼,而別人沒有。
3、代碼
代碼分爲兩部分來介紹,分別是向量構建及模型構建部分。
文本特徵向量構建其實就是機器學習過程當中構造特徵工程的過程。這裏使用了 gensim 和 LightGBM 包進行構建,sklearn 中也集成有調用 BoW, TF-IDF 和 LightGBM 的方法,你也能夠經過 sklearn 一站式搞定它們。
1.詞袋模型及文本特徵向量構建
from gensim import corpora, models # bulid dictionay and tfidf model all_corporas = [] for data in raw_data: text = clean_data(data) all_corporas.append(text) dictionary = corpora.Dictionary(all_corporas) corpus = [dictionary.doc2bow(text) for text in all_corporas] tfidf = models.TfidfModel(corpus, id2word = dictionary) # bulid one hot doc vector def bulid_onehot_vector(data): tfidf_vec = tfidf[dictionary.doc2bow(clean_data(data))] one_hot = [0] * len(dictionary) for x in tfidf_vec: one_hot[x[0]] = x[1] return one_hot
clean_data() 中完成了對原始語料的分詞以及去停用詞操做,去停用詞的操做。特別要說明的是,因爲是基於文本中詞的統計信息來分類,因此去停用詞很重要,能夠去掉一些不重要詞的干擾。
接下來就是構造詞典,詞典中能夠去除一些詞頻太低的詞語,這裏能夠本身查閱 gensim 官方文檔對一些參數進行設置。最後創建 TF-IDF 模型,對特徵向量進行構造。
2.LightGBM
import lightgbm as lgb lgb_train = lgb.Dataset(np.array(train_data), np.array(train_label)) lgb_val = lgb.Dataset(np.array(test_data), np.array(test_label)) params = {'max_depth': 15, 'min_data_in_leaf': 55, 'num_leaves': 80, 'learning_rate': 0.1, 'lambda_l1': 0.1, 'lambda_l2': 0.2, 'objective': 'multiclass', 'num_class': 3, 'verbose': -1} num_boost_round = 200 gbm = lgb.train(params, lgb_train, num_boost_round, verbose_eval=50, valid_sets=lgb_val) result = gbm.predict(np.array(test_data), num_iteration=gbm.best_iteration)
LightGBM 的構建就更加簡單了,首先將訓練數據封裝成 Dataset 格式,設置好參數,直接 train 就ok,這裏主要說一下參數的設置吧。
LightGBM 調參中,最重要的三個參數分別是 max_depth, min_data_in_leaf, num_leaves。
LightGBM 採用的分裂方式是 Leaf-wise,這樣每一棵 LightGBM 可以更好的對上一棵樹預測殘差的負梯度進行擬合,這使得 LightGBM 的精度更高,但也更容易過擬合,這三個參數是控制 LightGBM 模型擬合程度的關鍵所在。
首先,max_depth 表明樹的深度,樹越深越容易過擬合。
min_data_in_leaf 表明每一個葉子節點上的最小樣本數量。樣本過少容易過擬合。樣本過多容易欠擬合,何以理解爲預剪枝的操做。
num_leaves 表明樹的葉子節點個數。節點數過多容易過擬合,節點數過少容易欠擬合。
也是因爲 LightGBM 採用的分裂方式是 Leaf-wise,所以 max_depth 與 num_leaves 並不存在任何關聯,須要在 num_leaves 小於最大葉子節點數的前提下分別獨立調參。
4、結論
因爲數據來源於公司,因此這裏訓練過程和分類結果就只有跳票,直接來下結論吧。
BoW + TF-IDF + LightGBM 的文本分類原理,表面來講,是基於關鍵詞。但實際上則是基於文檔中詞的統計信息,這種分類方法徹底不會對文章的語義進行理解和分析,因此只適用於一些類別之間特徵明顯的簡單文本分類任務,當遇到未登陸詞或須要依靠複雜語義分析和語義特徵提取的任務時,就沒法正確的進行分類判斷。固然也可經過分析 bad case,針對性的增長相應語料來提高分類性能。
當文本各個類別的樣本數量不均衡時,能夠考慮使用 focal loss 來做爲 loss function,具體的方法後面單獨再寫一篇來記錄。
另外,當詞典過長時,構造的文本向量長度也就較長,這樣會形成 LightGBM 推理時間過長。因此須要對詞典中的一些詞頻較低的詞進行捨棄,但這樣也會形成必定的分類精度下降。
最後的一個 trick 是,在用這種方法分類中文文本時,分類的準確程度就變得更爲重要,所以能夠在分詞的基礎上,在詞典和文本向量中加入單個漢字,對分類的性能也會有必定的提高。
若有錯誤遺漏歡迎交流指正,轉載請註明出處。