文本分類:TextCNN / FastText / SVM / 貝葉斯

文本分類:TextCNN / FastText / SVM / 貝葉斯

來自專欄數據革命

文本分類實踐及分析

起因是在知乎看到清華的某官方專欄翻譯的一片文本分類博客,排版慘不忍睹。。。於是找到原文:A Comprehensive Guide to Understand and Implement Text Classification in Python,裡面對比了很多模型的分類性能,但是深度學習的模型並沒有很好訓練,沒法有效進行對比,於是有了此文。

今天測試的兩個文本分類器分別基於Facebook論文Bag of Tricks for Efficient Text Classification, 2016和 Convolutional Neural Networks for Sentence Classification, 2014。

於是,我們開始吧!

首先基於facebook的論文,搭建我們的fasttext模型:

import torchfrom torch import nnfrom torch.nn import functional as Fclass FastText(nn.Module): def __init__(self, param: dict): super().__init__() embed_dim = param[embed_dim] vocab_size = param[vocab_size] class_num = param[class_num] # hidden_size = param["hidden_size"] self.param = param self.embed = nn.Embedding(vocab_size, embed_dim) self.fc = nn.Linear(embed_dim, class_num) def forward(self, x): x = self.embed(x) x = torch.mean(x, dim=1, keepdim=False) output = self.fc(x) output = F.log_softmax(output, dim=1) return output

模型結構很簡單,文本輸入x,先進行embedding,然後將整句話的每個單詞向量計算平均值,最後接fc+softmot進行分類。官方論文中的關鍵點:

1)不使用預訓練word2vec,直接利用標籤樣本進行學習詞嵌入矩陣,也許是因為我們最終要通過對詞向量做平均得到句向量,所以不追求單個詞向量性能?

2)fc隱層輸出可作為句向量,供其他任務使用;

3)無序N-gram,大幅降低了計算複雜度,但實際性能不比有序N-gram差(未實現)

然後,我們來搭建TextCNN模型:

import torchfrom torch import nnfrom torch.nn import functional as Fclass TextCNN(nn.Module): def __init__(self, param: dict): super().__init__() ci = 1 # input chanel size kernel_num = param[kernel_num] # output chanel size kernel_size = param[kernel_size] vocab_size = param[vocab_size] embed_dim = param[embed_dim] dropout = param[dropout] class_num = param[class_num] self.param = param self.embed = nn.Embedding(vocab_size, embed_dim, padding_idx=1) self.conv11 = nn.Conv2d(ci, kernel_num, (kernel_size[0], embed_dim)) self.conv12 = nn.Conv2d(ci, kernel_num, (kernel_size[1], embed_dim)) self.conv13 = nn.Conv2d(ci, kernel_num, (kernel_size[2], embed_dim)) self.dropout = nn.Dropout(dropout) self.fc1 = nn.Linear(len(kernel_size) * kernel_num, class_num) def init_embed(self, embed_matrix): self.embed.weight = nn.Parameter(torch.Tensor(embed_matrix)) @staticmethod def conv_and_pool(x, conv): # x: (batch, 1, sentence_length, embed_dim) x = conv(x) # x: (batch, kernel_num, H_out, 1) x = F.relu(x.squeeze(3)) # x: (batch, kernel_num, H_out) x = F.max_pool1d(x, x.size(2)).squeeze(2) # (batch, kernel_num) return x def forward(self, x): # x: (batch, sentence_length) x = self.embed(x) # x: (batch, sentence_length, embed_dim) # TODO init embed matrix with pre-trained x = x.unsqueeze(1) # x: (batch, 1, sentence_length, embed_dim) x1 = self.conv_and_pool(x, self.conv11) # (batch, kernel_num) x2 = self.conv_and_pool(x, self.conv12) # (batch, kernel_num) x3 = self.conv_and_pool(x, self.conv13) # (batch, kernel_num) x = torch.cat((x1, x2, x3), 1) # (batch, 3 * kernel_num) x = self.dropout(x) logit = F.log_softmax(self.fc1(x), dim=1) return logit

模型也很簡單,文本輸入x,先進性embedding,然後不同的卷積做feature mapping 並進行max_pooling操作,最後是全聯接分類層。

論文中的一些重點:

1)預訓練詞向量,情況A:將詞向量分為2組,作為不同的channel,一組不fine-tuned,一組fine-tuned,效果較好,情況B:不分組,訓練過程中詞向量fine-tuned效果也不錯;

2)不同filter-size,提取不同的N-gram信息;

3)單層卷積後,接max-pooling,提取對於任務的最關鍵信息,並且天然的,max-pooling後向量與句子長度無關,對於變長輸入的處理變得簡單;

4)網路結構:cnn+max-pooling+dropout+fc(l2正則)+softmax。

現在模型搭建完畢,用語料測試一下~,這裡面我用到很多正在開發的chatbot框架的文本處理模塊,這些工作你也可以自己寫函數完成-.-

首先,工具全部import進來;

import syssys.path.append("/Users/zhouzhirui/project/Task-Oriented-Chatbot/")from pathlib import Pathimport reimport gcimport numpy as npimport torch# 下面都是正在開發chatbot框架的模塊- -from chatbot.models.intent.fast_text import FastTextfrom chatbot.models.intent.text_cnn import TextCNNfrom chatbot.cparse.vocabulary import Vocabularyfrom chatbot.cparse.label import Labelfrom chatbot.models.intent.pytorch import predict, evaluate, batch_generator, trainfrom chatbot.cparse.text import read_fasttext_filefrom chatbot.evaluate.plot import plot_confusion_matrix

將語料讀進來

def precessing(s): s = re.sub("[.,!?;\/]", " ", s) return s.lower()x, y = read_fasttext_file("/Users/zhouzhirui/project/Task-Oriented-Chatbot/corpus/intent/fastText/corpus", precessing)train_x, train_y = x[:5000], y[:5000]test_x, test_y = x[5000:], y[5000:]del x, y; gc.collect()

這裡語料只是簡單的處理,替換一些標點,全部小些,處理結果如下:

接著,我們要把詞以及標籤全部轉換成 int類型的id,便於模型輸入,這裡我會用到自己的chatbot模塊,大家關注結果就好:

vocab = Vocabulary()vocab.update(train_x)label = Label()label.update(train_y)train_x = np.array(vocab.transform(train_x, max_length=50))test_x = np.array(vocab.transform(test_x, max_length=50))train_y = np.array(label.transform(train_y))test_y = np.array(label.transform(test_y))

結果如下,每個詞及label全部轉換成id,同時把每句話處理成同樣長度(50):

然後就是初始化我們的模型,訓練之~

param_fasttext = { "vocab_size": len(vocab), "embed_dim": 60, "class_num": len(label), }fasttext = FastText(param_fasttext)train(fasttext, train_x, train_y, test_x, test_y, lr=0.01, epochs=10, init_epochs=0, batch_size=64)

訓練過程中的日誌看一下:

可以看到,模型收斂很快,第三個epoch就在測試集達到了0.791的accuracy;

再看看textcnn表現,還是剛才那個味道:

textcnn_param = { "vocab_size": len(vocab), "embed_dim": 60, "class_num": len(label), "kernel_num": 16, "kernel_size": [3, 4, 5], "dropout": 0.5, }textcnn = TextCNN(textcnn_param)train(textcnn, train_x, train_y, test_x, test_y, 0.01, 10, 0, 64)

日誌看一下:

嗯。。第8個epoch達到測試集最優,準確率0.769,比fasttext差不多少呀。。

以上兩個模型都沒有調參,沒有實現一些能顯著提升性能的一些流行方法,我們將他們和傳統機器學習方法對比一下看看,孰優孰劣。

預告:下一期會在今天的模型基礎上,做優化,並對模型性能,二分類、多分類做詳細測試。

安利:我的Github,歡迎follow

如果覺得有用,記得關注點贊=.=


推薦閱讀:

TAG:深度學習DeepLearning | 機器學習 | 自然語言處理 |