[深度学习] 自然语言处理 --- 文本分类模型总结
文本分類
包括基于word2vec預(yù)訓(xùn)練的文本分類,與及基于最新的預(yù)訓(xùn)練模型(ELMO,BERT等)的文本分類
一 fastText 模型
fastText模型架構(gòu)和word2vec中的CBOW很相似, 不同之處是fastText預(yù)測(cè)標(biāo)簽而CBOW預(yù)測(cè)的是中間詞,即模型架構(gòu)類似但是模型的任務(wù)不同。
?
其中x1,x2,...,xN?1,xN表示一個(gè)文本中的n-gram向量,每個(gè)特征是詞向量的平均值。這和前文中提到的cbow相似,cbow用上下文去預(yù)測(cè)中心詞,而此處用全部的n-gram去預(yù)測(cè)指定類別。
?
import torch.nn as nn import torch.nn.functional as Fclass FastText(nn.Module):def __init__(self, vocab_size, embedding_dim, output_dim, pad_idx):super().__init__()self.embedding = nn.Embedding(vocab_size, embedding_dim, padding_idx=pad_idx)self.fc = nn.Linear(embedding_dim, output_dim)def forward(self, text):#text = [sent len, batch size]embedded = self.embedding(text)#embedded = [sent len, batch size, emb dim]embedded = embedded.permute(1, 0, 2)#embedded = [batch size, sent len, emb dim]pooled = F.avg_pool2d(embedded, (embedded.shape[1], 1)).squeeze(1) #pooled = [batch size, embedding_dim]return self.fc(pooled)?
二 TextCNN模型
TextCNN 是利用卷積神經(jīng)網(wǎng)絡(luò)對(duì)文本進(jìn)行分類的算法,由 Yoon Kim 在 “Convolutional Neural Networks for Sentence Classification” 一文? 中提出. 是2014年的算法.
將Text的詞向量拼接在一起,就好比一張圖,只不過(guò)這個(gè)圖只是一個(gè)channel的.這里使用的就是Conv1d.
模型的結(jié)構(gòu)是:
?
三 CharCNN模型
在charCNN論文Character-level Convolutional Networks for Text Classification中提出了6層卷積層 + 3層全連接層的結(jié)構(gòu),
在此之前很多基于深度學(xué)習(xí)的模型都是使用更高層面的單元對(duì)文本或者語(yǔ)言進(jìn)行建模,比如單詞(統(tǒng)計(jì)信息或者 n-grams、word2vec 等),短語(yǔ)(phrases),句子(sentence)層面,或者對(duì)語(yǔ)義和語(yǔ)法結(jié)構(gòu)進(jìn)行分析,但是CharCNN則提出了從字符層面進(jìn)行文本分類,提取出高層抽象概念。
字符編碼層 為了實(shí)現(xiàn) CharCNN,首先要做的就是構(gòu)建字母表,本文中使用的字母標(biāo)如下,共有 69 個(gè)字符,對(duì)其使用 one-hot 編碼,外加一個(gè)全零向量(用于處理不在該字符表中的字符),所以共 70 個(gè),所以每個(gè)字符轉(zhuǎn)化為一個(gè) 70 維的向量。文中還提到要反向處理字符編碼,即反向讀取文本,這樣做的好處是最新讀入的字符總是在輸出開始的地方。:
?
模型卷積 - 池化層 文中提出了兩種規(guī)模的神經(jīng)網(wǎng)絡(luò)–large 和 small。(kernel——size的不同)都由 6 個(gè)卷積層和 3 個(gè)全連接層共 9 層神經(jīng)網(wǎng)絡(luò)組成。這里使用的是 1-D 卷積神經(jīng)網(wǎng)絡(luò)。除此之外,在三個(gè)全連接層之間加入兩個(gè) dropout 層以實(shí)現(xiàn)模型正則化。
import torch from torch import nn import numpy as np from utils import *class CharCNN(nn.Module):def __init__(self, config, vocab_size, embeddings):super(CharCNN, self).__init__()self.config = configembed_size = vocab_size# Embedding Layerself.embeddings = nn.Embedding(vocab_size, embed_size)self.embeddings.weight = nn.Parameter(embeddings, requires_grad=False)# This stackoverflow thread explains how conv1d works# https://stackoverflow.com/questions/46503816/keras-conv1d-layer-parameters-filters-and-kernel-size/46504997conv1 = nn.Sequential(nn.Conv1d(in_channels=embed_size, out_channels=self.config.num_channels, kernel_size=7),nn.ReLU(),nn.MaxPool1d(kernel_size=3)) # (batch_size, num_channels, (seq_len-6)/3)conv2 = nn.Sequential(nn.Conv1d(in_channels=self.config.num_channels, out_channels=self.config.num_channels, kernel_size=7),nn.ReLU(),nn.MaxPool1d(kernel_size=3)) # (batch_size, num_channels, (seq_len-6-18)/(3*3))conv3 = nn.Sequential(nn.Conv1d(in_channels=self.config.num_channels, out_channels=self.config.num_channels, kernel_size=3),nn.ReLU()) # (batch_size, num_channels, (seq_len-6-18-18)/(3*3))conv4 = nn.Sequential(nn.Conv1d(in_channels=self.config.num_channels, out_channels=self.config.num_channels, kernel_size=3),nn.ReLU()) # (batch_size, num_channels, (seq_len-6-18-18-18)/(3*3))conv5 = nn.Sequential(nn.Conv1d(in_channels=self.config.num_channels, out_channels=self.config.num_channels, kernel_size=3),nn.ReLU()) # (batch_size, num_channels, (seq_len-6-18-18-18-18)/(3*3))conv6 = nn.Sequential(nn.Conv1d(in_channels=self.config.num_channels, out_channels=self.config.num_channels, kernel_size=3),nn.ReLU(),nn.MaxPool1d(kernel_size=3)) # (batch_size, num_channels, (seq_len-6-18-18-18-18-18)/(3*3*3))# Length of output after conv6 conv_output_size = self.config.num_channels * ((self.config.seq_len - 96) // 27)linear1 = nn.Sequential(nn.Linear(conv_output_size, self.config.linear_size),nn.ReLU(),nn.Dropout(self.config.dropout_keep))linear2 = nn.Sequential(nn.Linear(self.config.linear_size, self.config.linear_size),nn.ReLU(),nn.Dropout(self.config.dropout_keep))linear3 = nn.Sequential(nn.Linear(self.config.linear_size, self.config.output_size),nn.Softmax())self.convolutional_layers = nn.Sequential(conv1,conv2,conv3,conv4,conv5,conv6)self.linear_layers = nn.Sequential(linear1, linear2, linear3)def forward(self, x):embedded_sent = self.embeddings(x).permute(1,2,0) # shape=(batch_size,embed_size,seq_len)conv_out = self.convolutional_layers(embedded_sent)conv_out = conv_out.view(conv_out.shape[0], -1)linear_output = self.linear_layers(conv_out)return linear_output?
四 Bi-LSTM模型
Bi-LSTM即雙向LSTM,較單向的LSTM,Bi-LSTM能更好地捕獲句子中上下文的信息。
雙向循環(huán)神經(jīng)網(wǎng)絡(luò)(BRNN)的基本思想是提出每一個(gè)訓(xùn)練序列向前和向后分別是兩個(gè)循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN),而且這兩個(gè)都連接著一個(gè)輸出層。這個(gè)結(jié)構(gòu)提供給輸出層輸入序列中每一個(gè)點(diǎn)的完整的過(guò)去和未來(lái)的上下文信息。下圖展示的是一個(gè)沿著時(shí)間展開的雙向循環(huán)神經(jīng)網(wǎng)絡(luò)。六個(gè)獨(dú)特的權(quán)值在每一個(gè)時(shí)步被重復(fù)的利用,六個(gè)權(quán)值分別對(duì)應(yīng):輸入到向前和向后隱含層(w1, w3),隱含層到隱含層自己(w2, w5),向前和向后隱含層到輸出層(w4, w6)。值得注意的是:向前和向后隱含層之間沒(méi)有信息流,這保證了展開圖是非循環(huán)的。
?
五 Bi-LSTM+Attention 模型
Bi-LSTM + Attention模型來(lái)源于論文Attention-Based Bidirectional Long Short-Term Memory Networks for Relation Classification。關(guān)于Attention的介紹見(jiàn)這篇。
Bi-LSTM + Attention 就是在Bi-LSTM的模型上加入Attention層,在Bi-LSTM中我們會(huì)用最后一個(gè)時(shí)序的輸出向量 作為特征向量,然后進(jìn)行softmax分類。Attention是先計(jì)算每個(gè)時(shí)序的權(quán)重,然后將所有時(shí)序 的向量進(jìn)行加權(quán)和作為特征向量,然后進(jìn)行softmax分類。在實(shí)驗(yàn)中,加上Attention確實(shí)對(duì)結(jié)果有所提升。其模型結(jié)構(gòu)如下圖:
?
六 RCNN模型
Here, we have implemented Recurrent Convolutional Neural Network model for text classification, as proposed in the paper Recurrent Convolutional Neural Networks for Text Classification.
在文本表示方面,會(huì)有超過(guò)filter_size的上下文的語(yǔ)義缺失,因此本篇文章利用RNN來(lái)進(jìn)行文本表示,中心詞左側(cè)和右側(cè)的詞設(shè)為trainable,然后將中心詞左側(cè)和右側(cè)的詞concat作為中心詞的表示。 當(dāng)前step的中心詞不輸入lstm,僅僅與左側(cè)詞和右側(cè)詞在lstm的輸出concat。
先經(jīng)過(guò)1層雙向LSTM,該詞的左側(cè)的詞正向輸入進(jìn)去得到一個(gè)hidden state(從上往下),該詞的右側(cè)反向輸入進(jìn)去得到一個(gè)hidden state(從下往上)。再結(jié)合該詞的詞向量,生成一個(gè) 1 * 3k 的向量。
再經(jīng)過(guò)全連接層,tanh為非線性函數(shù),得到y(tǒng)2。
再經(jīng)過(guò)最大池化層,得出最大化向量y3.
再經(jīng)過(guò)全連接層,sigmod為非線性函數(shù),得到最終的多分類。
1、結(jié)合了中心詞窗口的輸入,其輸出的representation能很好的保留上下文語(yǔ)義信息
2、全連接層+pooling進(jìn)行特征選擇,獲取全局最重要的特征
RCNN 整體的模型構(gòu)建流程如下:
1)利用Bi-LSTM獲得上下文的信息,類似于語(yǔ)言模型。
2)將Bi-LSTM獲得的隱層輸出和詞向量拼接[fwOutput,wordEmbedding, bwOutput]。
3)將拼接后的向量非線性映射到低維。
4)向量中的每一個(gè)位置的值都取所有時(shí)序上的最大值,得到最終的特征向量,該過(guò)程類似于max-pool。
5)softmax分類。
七 Adversarial LSTM模型
模型來(lái)源于論文Adversarial Training Methods For Semi-Supervised Text Classification
??????? 上圖中左邊為正常的LSTM結(jié)構(gòu),右圖為Adversarial LSTM結(jié)構(gòu),可以看出在輸出時(shí)加上了噪聲。
Adversarial LSTM的核心思想是通過(guò)對(duì)word Embedding上添加噪音生成對(duì)抗樣本,將對(duì)抗樣本以和原始樣本 同樣的形式喂給模型,得到一個(gè)Adversarial Loss,通過(guò)和原始樣本的loss相加得到新的損失,通過(guò)優(yōu)化該新 的損失來(lái)訓(xùn)練模型,作者認(rèn)為這種方法能對(duì)word embedding加上正則化,避免過(guò)擬合。
八 Transformer模型
創(chuàng)新之處在于使用了scaled Dot-Product Attention和Multi-Head Attention
Encoder部分
上面的Q,K和V,被作為一種抽象的向量,主要目的是用來(lái)做計(jì)算和輔助attention。根據(jù)文章我們知道Attention的計(jì)算公式如下:
接著是Multi-head Attention:
?
這里的positional encoding需要說(shuō)明一下:
公式中pos就代表了位置index,然后i就是index所對(duì)應(yīng)的向量值,是一個(gè)標(biāo)量,然后dmodel就是512了。之所以選擇這個(gè)函數(shù)是因?yàn)樽髡呒僭O(shè)它能夠讓模型通過(guò)相關(guān)的位置學(xué)習(xí)Attend。
(引入這個(gè)的原因就是因?yàn)槟P屠餂](méi)有用到RNN和CNN,不能編碼序列順序,因此需要顯示的輸入位置信息.之前用到的由position embedding,作者發(fā)現(xiàn)上述方法與這個(gè)方法差不多.位置特征在這里是一種重要特征。)
Decoder部分
對(duì)比單個(gè)encoder和decoder,可以看出,decoder多出了一個(gè)encoder-decoder Attention layer,接收encoder部分輸出的向量和decoder自身的self attention出來(lái)的向量,然后再進(jìn)入到全連接的前饋網(wǎng)絡(luò)中去,最后向量輸出到下一個(gè)的decoder
最后一個(gè)decoder輸出的向量會(huì)經(jīng)過(guò)Linear層和softmax層。Linear層的作用就是對(duì)decoder部分出來(lái)的向量做映射成一個(gè)logits向量,然后softmax層根據(jù)這個(gè)logits向量,將其轉(zhuǎn)換為了概率值,最后找到概率最大值的位置。這樣就完成了解碼的輸出了。
?
?
九? ELMO 預(yù)訓(xùn)練模型
ELMo模型是利用BiLM(雙向語(yǔ)言模型)來(lái)預(yù)訓(xùn)練詞的向量表示,可以根據(jù)我們的訓(xùn)練集動(dòng)態(tài)的生成詞的向量表示。ELMo預(yù)訓(xùn)練模型來(lái)源于論文:Deep contextualized word representations。具體的ELMo模型的詳細(xì)介紹見(jiàn)ELMO模型(Deep contextualized word representation)。
ELMo的模型代碼發(fā)布在github上,我們?cè)谡{(diào)用ELMo預(yù)訓(xùn)練模型時(shí)主要使用到bilm中的代碼,因此可以將bilm這個(gè)文件夾拷貝到自己的項(xiàng)目路徑下,之后需要導(dǎo)入這個(gè)文件夾中的類和函數(shù)。此外,usage_cached.py,usage_character.py,usage_token.py這三個(gè)文件中的代碼是告訴你該怎么去調(diào)用ELMo模型動(dòng)態(tài)的生成詞向量。在這里我們使用usage_token.py中的方法,這個(gè)計(jì)算量相對(duì)要小一些。
在使用之前我們還需要去下載已經(jīng)預(yù)訓(xùn)練好的模型參數(shù)權(quán)重,打開https://allennlp.org/elmo鏈接,在Pre-trained ELMo Models 這個(gè)版塊下總共有四個(gè)不同版本的模型,可以自己選擇,我們?cè)谶@里選擇Small這個(gè)規(guī)格的模型,總共有兩個(gè)文件需要下載,一個(gè)"options"的json文件,保存了模型的配置參數(shù),另一個(gè)是"weights"的hdf5文件,保存了模型的結(jié)構(gòu)和權(quán)重值(可以用h5py讀取看看)。
?
十 BERT 預(yù)訓(xùn)練模型
?? BERT 模型來(lái)源于論文BERT: Pre-training of Deep Bidirectional Transformers for?Language Understanding。BERT模型是谷歌提出的基于雙向Transformer構(gòu)建的語(yǔ)言模型。BERT模型和ELMo有大不同,在之前的預(yù)訓(xùn)練模型(包括word2vec,ELMo等)都會(huì)生成詞向量,這種類別的預(yù)訓(xùn)練模型屬于domain transfer。而近一兩年提出的ULMFiT,GPT,BERT等都屬于模型遷移。
BERT 模型是將預(yù)訓(xùn)練模型和下游任務(wù)模型結(jié)合在一起的,也就是說(shuō)在做下游任務(wù)時(shí)仍然是用BERT模型,而且天然支持文本分類任務(wù),在做文本分類任務(wù)時(shí)不需要對(duì)模型做修改。谷歌提供了下面七種預(yù)訓(xùn)練好的模型文件。
BERT模型在英文數(shù)據(jù)集上提供了兩種大小的模型,Base和Large。Uncased是意味著輸入的詞都會(huì)轉(zhuǎn)變成小寫,cased是意味著輸入的詞會(huì)保存其大寫(在命名實(shí)體識(shí)別等項(xiàng)目上需要)。Multilingual是支持多語(yǔ)言的,最后一個(gè)是中文預(yù)訓(xùn)練模型。
?
總結(jié)
以上是生活随笔為你收集整理的[深度学习] 自然语言处理 --- 文本分类模型总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 如何做服务监控
- 下一篇: [深度学习] 自然语言处理---Tran