文本分類模型第三彈:BoW(Bag of Words) + TF-IDF + LightGBM

一、前言

本文是文本分類模型的第三彈,利用詞袋模型(BoW),詞頻逆文檔頻率(TF-IDF)與 LightGBM 模型進行文本分類。

原本計劃的第三彈內容爲 TextCNN 網絡,介於最近剛剛利用 LightGBM 完成了一個簡單的文本分類的任務,就趁熱記錄一下,作爲第三彈的內容。

這裏是文本分類系列:

文本分類模型第一彈:關於Fasttext,看這一篇就夠了

文本分類模型第二彈:HAN(Hierarchy Attention Network)

文本分類模型第三彈:BoW(Bag of Words) + TF-IDF + LightGBM


二、相關論文及理論

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),這就是最簡單的文本表示方法。

3.TF-IDF

理論上來說,當我們構造出了基於 BoW 的文檔向量後,只要對向量稍加處理(歸一化),就可以使用這個文檔進行文本分類了,但這樣的效果並不是很好,因爲 BoW + TF 構造的向量只是對單個文檔進行了信息統計,並沒有考慮到所有文檔的統計信息,以及文檔之間究竟以什麼來區分。所以需要對文檔中出現的詞的 TF 值稍加處理,那就是計算TF-IDF。

網上搜一下 TF-IDF 的原理,大概可以搜到上萬篇博客文章,所以這裏就不再進行贅述。只用一句話來解釋它的作用就是:找出自己有什麼,而別人沒有。


三、代碼

代碼分爲兩部分來介紹,分別是向量構建及模型構建部分。

文本特徵向量構建其實就是機器學習過程中構造特徵工程的過程。這裏使用了 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 小於最大葉子節點數的前提下分別獨立調參。


四、結論

由於數據來源於公司,所以這裏訓練過程和分類結果就只有跳票,直接來下結論吧。

BoW + TF-IDF + LightGBM 的文本分類原理,表面來說,是基於關鍵詞。但實際上則是基於文檔中詞的統計信息,這種分類方法完全不會對文章的語義進行理解和分析,所以只適用於一些類別之間特徵明顯的簡單文本分類任務,當遇到未登錄詞或需要依靠複雜語義分析和語義特徵提取的任務時,就無法正確的進行分類判斷。當然也可通過分析 bad case,針對性的增加相應語料來提升分類性能。

當文本各個類別的樣本數量不均衡時,可以考慮使用 focal loss 來作爲 loss function,具體的方法後面單獨再寫一篇來記錄。

另外,當詞典過長時,構造的文本向量長度也就較長,這樣會造成 LightGBM 推理時間過長。所以需要對詞典中的一些詞頻較低的詞進行捨棄,但這樣也會造成一定的分類精度降低。

最後的一個 trick 是,在用這種方法分類中文文本時,分類的準確程度就變得更爲重要,因此可以在分詞的基礎上,在詞典和文本向量中加入單個漢字,對分類的性能也會有一定的提升。

 

 

如有錯誤遺漏歡迎交流指正,轉載請註明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章