三、自然语言分类
文章目錄
- 1 數據準備
- 1.1 數據集拆分
- 1.2 創建詞庫vocabulary
- 1.3 batch數據,創建Iterator
- 2 Word Averaging模型
- 3 RNN模型
- 4 CNN
三種分類方式:Word Averaging模型、RNN、CNN。
1 數據準備
第一步是準備數據。代碼中用到的類庫有spacy、torchtext。
torchtext中有data.Field用來處理每個文本字段。dataLabelFiled處理標簽字段。
1.1 數據集拆分
將數據集分為訓練集、驗證集和測試集。了解每種數據集的數據量,查看每一條數據的樣子。
每一條句子是一個樣本。
1.2 創建詞庫vocabulary
TEXT.build_vocab(train_data, max_size=25000, vectors="glove.6B.100d", unk_init=torch.Tensor.normal_) LABEL.build_vocab(train_data)設置詞表大小,用詞向量初始化詞表。
1.3 batch數據,創建Iterator
訓練的時候是一個batch,一個batch訓練的。torchtext會將短句子pad到和最長的句子長度相同。
train_iterator, valid_iterator, test_iterator = data.BucketIterator.splits((train_data, valid_data, test_data), batch_size=BATCH_SIZE,device=device)數據準備好了,就開始用模型訓練吧。
2 Word Averaging模型
我們首先介紹一個簡單的Word Averaging模型。這個模型非常簡單,我們把每個單詞都通過Embedding層投射成word embedding vector,然后把一句話中的所有word vector做個平均,就是整個句子的vector表示了。
接下來把這個sentence vector傳入一個Linear層,做分類即可。
怎么做平均呢?我們使用avg_pool2d來做average pooling。我們的目標是把sentence length那個維度平均成1,然后保留embedding這個維度。
avg_pool2d的kernel size是 (embedded.shape[1], 1),所以句子長度的那個維度會被壓扁。
接下來就是模型訓練、評價。
優化函數:nn.BCEWithLogitsLoss()
優化方法:Adm
評價:損失函數值,準確率。
3 RNN模型
我們使用最后一個hidden state hTh_ThT?來表示整個句子。
然后我們把hTh_ThT?通過一個線性變換f,然后用來預測句子的情感。
4 CNN
CNN可以把每個局域的特征提出來。在用于句子情感分類的時候,其實是做了一個ngram的特征抽取。
首先做一個embedding,得到每個詞的詞向量,是一個k維的向量。每個詞的詞向量結合到一起,得到一個nxk的矩陣。n是單詞個數。
其次卷積層filter。現在相當于一個hgram。最后轉化為h個單詞。
w是hxk的。
每一個h單詞的窗口都會被這個filter轉化。c=[c1,c2,...cn?h?1]c=[c_1,c_2,...c_{n-h-1}]c=[c1?,c2?,...cn?h?1?]
上圖中是一個卷積核為(3,embedding_size)的卷積。每次作用于3個單詞,形成3-gram結果。
一般來說會選用多個卷積核:下圖是作用了3個(3,embedding_size)卷積核。也可以使用不同尺寸的卷積核。
第三步,做一個max-over-time pooling。C^=max{c}\widehat{C}=max\{c\}C=max{c}
如果有m個filter,我們會得到z=[C1^,C2^,...Cm^]z=[\widehat{C_1},\widehat{C_2},...\widehat{C_m}]z=[C1??,C2??,...Cm??]
第四步,對z做一個線性變換,得到分類。
class CNN(nn.Module):def __init__(self, vocab_size, embedding_size, output_size, pad_idx, n_filters, filter_sizes, dropout):super(CNN, self).__init__()self.embedding = nn.Embedding(vocab_size, embedding_size, padding_idx=pad_idx)self.convs = nn.ModuleList([nn.Conv2d(in_channels=1, out_channels=n_filters, kernel_size=(fs, embedding_size)) for fs in filter_sizes])self.linear = nn.Linear(len(filter_sizes) * n_filters, output_size)self.dropout = nn.Dropout(dropout)def forward(self, text):# text : [seq_len, batch_size]text = text.permute(1, 0) # [batch_size,seq_len]embed = self.embedding(text) # [batch_size, seq_len, embedding_size]embed = embed.unsqueeze(1) # [batch_size, 1, seq_len, embedding_size]# conved中每一個的形狀是:[batch_size, n_filters, seq_len-filter_sizes[i]+1,embedding_size-embedding_size+1]# 因為kernel_size的第二個維度是embedding_sizeconved = [conv(embed) for conv in self.convs]# conved中每一個的形狀是:[batch_size, n_filters, seq_len-filter_sizes[i]+1]conved = [F.relu(conv).squeeze(3) for conv in conved]# pooled中的每一個形狀是:[batch_size, n_filters, 1]pooled = [F.max_pool1d(conv, conv.shape[2]) for conv in conved]# pooled中的每一個形狀是:[batch_size, n_filters]pooled = [conv.squeeze(2) for conv in pooled]cat = self.dropout(torch.cat(pooled, dim=1)) # [batch_size, len(filter_sizes) * n_filters]return self.linear(cat) # [batch_size,output_size]N_FILTERS = 100 FILTER_SIZES = [3, 4, 5] DROPOUT = 0.5 cnn_model = CNN(VOCAB_SIZE, EMBEDDING_SIZE, OUTPUT_SIZE, PAD_IDX, N_FILTERS, FILTER_SIZES, DROPOUT)代碼中的卷積核分別為:(3,embedding_size),(4,embedding_size),(5,embedding_size)
每次卷積完成之后,[batch_size, n_filters, seq_len-filter_sizes[i]+1,embedding_size-embedding_size+1],最后一個維度為1。
因為多個卷積之后的形狀不一樣,通過max_pooling層后統一變為[batch_size, n_filters]。這樣就可以將多個卷積的結果拼接在一起,送入全連接層。
總結
- 上一篇: AMESim2020MATLAB2020
- 下一篇: 每天一个linux命令(9):nl命令