数据科学竞赛-文本分类
文本分類
簡介
這是達觀在2018年舉辦的一個文本分類比賽,是一場經典的NLP比賽,關于NLP賽的思路在之前的博客中提到過,目前這場比賽已經結束,但是仍舊可以在DC上提交成績,作為一個demo的比賽了。本文將簡要對該比賽的思路進行介紹,采用傳統方法和深度方法提交baseline模型。
數據探索
數據集可以直接到官網下載,下載后解壓文件可以得到訓練集和測試集,均為CSV格式的表格文件,可以采用Pandas進行讀取和分析。
對訓練集的數據進行初步探索,結果如下。其中id為文本標識碼,article為文本字表示(每個數字對應一個漢字,不知道字表無法還原,這是為了保護文本中的隱私信息,稱為脫敏操作,該操作不允許建模預測等),word_seg是文本的詞表示,每個數字編號了一個單詞,class為該文本的類別標簽號。
為了后面模型的設計,有必要知道文本的長度范圍是什么(下圖探索詞長度,即一個文本多少個詞)。文本平均含有716個單詞,是一個典型的長文本分類。
對詞信息進行計數統計,結果如下。高頻詞出現了500萬次,總共有875129個詞。當然,低頻詞也有很多,這里不多說明,具體見文末Github地址。
對字的處理類似上面對于詞的處理,不多說明,不過在NLP中詞的信息遠遠多于字的信息量。
最后,可以看看標簽的分布,可以使用Pandas接口輕易完成。
經過數據探索,不難發現這是一個長文本分類問題,且樣本類別分布不均衡,同時詞量特別大,會導致特征數量很多。
Pipeline制定(傳統方法baseline)
在這一部分會進行數據集的特征工程,構建模型,預測提交結果。
特征工程
首先采用傳統方法提取文本特征,分別采用TFIDF和N-Gram,這兩種方法都是在NLP中比較傳統的基于統計的文本特征提取思路,其原理這里不多贅述,這里使用sklearn中封裝好的API,其具體使用可以參考scikit-learn的官方文檔。
使用下面的代碼生成每個文本的特征向量,生成的特征向量是稀疏矩陣,scikit-learn中模型支持稀疏矩陣的輸入。
from sklearn.feature_extraction.text import TfidfVectorizerword_vec = TfidfVectorizer(analyzer='word',ngram_range=(1,2),min_df=3, # 低頻詞max_df=0.9, # 高頻詞use_idf=True,smooth_idf=True, sublinear_tf=True)train_doc = word_vec.fit_transform(df_train['word_seg']) test_doc = word_vec.transform(df_test['word_seg'])模型構建
這一部分先是采用最基本的機器學習分類模型—邏輯回歸進行模型的訓練及測試集的預測。
from sklearn.linear_model import LogisticRegression clf = LogisticRegression(C=4) clf.fit(train_doc, df_train['label']) test_prob = clf.predict_proba(test_doc) test_pred = np.argmax(test_prob, axis=1) df_test['class'] = lb.inverse_transform(test_pred) df_test[["id","class"]].to_csv("submission.csv", index=False, header=True, encoding='utf-8')這個baseline的提交成績如下圖,采用的metric是F1得分,這個成績的排名是648位。
優化思路
后續的優化都是基于上面的baseline進行的,本部分具體代碼見文末Github。
首先,上述的特征工程均只使用了詞的信息,沒有使用字的信息,可以使用字的特征組合詞的特征從而達到充分利用數據的目的,這里簡單的將兩種特征向量橫向堆疊,這樣會出現特征維度太高的問題,需要進行降維。
接著,邏輯回歸畢竟只是一個基本的分類模型,其實可以使用更強的集成模型如LightGBM、XGBoost等,這里使用LightGBM進行建模(關于LGB調參技巧本文不多說明)。
上述的思路其實有一個問題,我們始終沒有在線下得到模型的效果(事實上,正規的比賽對提交次數都是有限制的,不可能優化一次代碼就提交一次觀察線上得分變化,這會造成部分人的刷分,必須在線下進行模型評估。),通常,我們采用構建驗證集的方法在線下進行模型評估(k折交叉驗證得到的驗證得分更加合適,但是資源消耗大)。
這一部分主要是使用集成模型且進行交叉驗證,得到平均的預測結果。當然,這部分需要大量人工的特征工程和模型調參,后面會介紹效果更佳顯著的深度學習方法。
Pipeline制定(深度方法)
在這一部分不會過度強調特征工程、模型等步驟,因為在深度學習方法中主要目的是構建端到端的一個應用系統(如文本類別識別系統)。
數據準備
首先,將文本轉化為序列(分詞),這個過程會建立詞表,這樣每個文本變為了一個序列。(模型期待輸入是固定的維度所以對不等長文本需要進行截斷和補全,截取或者補全后的序列長度視情況而定)
同時,需要對標簽進行onehot編碼以便于使用softmax進行輸出層激活且計算loss。
文本特征
詞有很多表示方法,最簡單的是onehot編碼單詞,每個單詞就是其對應的onehot編碼,但是onehot有一個顯著的特征—無法衡量不同單詞之間的距離(因為距離都是相同的)且維度很高。后來提出的Word2Vec方法可以構造低維有距的詞向量,它的產生有兩種策略分別為CBOW和Skip-gram,具體的理論這里不多贅述。
這里將輸入的文本詞序列輸入模型構建詞嵌入(word embedding),利用詞表和詞向量構建詞嵌入可以大幅減少內存消耗。
模型構建
下面構建整個深度模型,使用雙向GRU+池化層構建網絡,全連接層作為分類器。
def build_model(sequence_length, embedding_weight, class_num):content = Input(shape=(sequence_length, ), dtype='int32')embedding = Embedding(name='word_embedding',input_dim=embedding_weight.shape[0],weights=[embedding_weight],output_dim=embedding_weight.shape[1],trainable=False)x = SpatialDropout1D(0.2)(embedding(content))x = Bidirectional(GRU(200, return_sequences=True))(x)x = Bidirectional(GRU(200, return_sequences=True))(x)avg_pool = GlobalAveragePooling1D()(x)max_pool = GlobalMaxPooling1D()(x)conc = concatenate([avg_pool, max_pool])x = Dense(1000)(conc)x = BatchNormalization()(x)x = Activation('relu')(x)x = Dropout(0.2)(x)x = Dense(500)(x)x = BatchNormalization()(x)x = Activation('relu')(x)output = Dense(19, activation='softmax')(x)model = tf.keras.models.Model(inputs=content, outputs=output)model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])return model模型訓練
訓練采用交叉驗證,并綜合多次的預測結果以獲得更好的模型表現。(綜合的方法是10折的預測取均值,即線性加權。)
kf = KFold(n_splits=10, shuffle=True, random_state=2019) train_pre_matrix = np.zeros((df_train.shape[0], 19)) test_pre_matrix = np.zeros((10, df_test.shape[0], 19)) cv_scores = []for i, (train_index, valid_index) in enumerate(kf.split(train_)):x_train, x_valid = train_[train_index, :], train_[valid_index, :]y_train, y_valid = train_label[train_index], train_label[valid_index]train_ds = tf.data.Dataset.from_tensor_slices((x_train, y_train)).shuffle(10000).batch(64)valid_ds = tf.data.Dataset.from_tensor_slices((x_valid, y_valid)).batch(64)test_ds = tf.data.Dataset.from_tensor_slices((test_, np.zeros((test_.shape[0], 19)))).batch(64)model = build_model(1800, embedding_matrix, 19)model.fit(train_ds, epochs=30, validation_data=valid_ds, verbose=1)valid_prob = model.predict(valid_ds)valid_pred = np.argmax(valid_prob, axis=1)valid_pred = lb.inverse_transform(valid_pred)y_valid = np.argmax(y_valid, axis=1)y_valid = lb.inverse_transform(y_valid)f1_score = f1_score(y_valid, valid_pred, average='macro')print("F1 score", f1_score)train_pre_matrix[valid_index, :] = valid_probtest_pre_matrix[i, :, :] = model.predict(test_ds)del modelgc.collect()tf.keras.backend.clear_session()np.save('test.npy', test_pre_matrix)結果提交
將綜合后的預測結果提交到比賽平臺,可以看到,得分如下,排名從傳統方法的600多到達了前100,說明深度方法的學習能力是很強的。
補充說明
本文簡要以達觀的文本分類為例,講述了NLP賽的如今主流思路,2019年達觀舉辦了另外一場比賽,有興趣也可以參與。本文所有代碼開源于我的Github倉庫,歡迎star或者fork。
總結
以上是生活随笔為你收集整理的数据科学竞赛-文本分类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据科学竞赛-自然语言处理赛流程
- 下一篇: 新的一年2020