Pytorch-情感分類實戰(基於LSTM,調用torchtext)


提前安裝torchtext和scapy,運行下面語句(壓縮包地址鏈接:https://pan.baidu.com/s/1_syic9B-SXKQvkvHlEf78w 提取碼:ahh3):

pip install torchtext

pip install scapy

pip install 你的地址\en_core_web_md-2.2.5.tar.gz  

  • en_core_web_md安裝成功了,但spyder還是沒法調用,把F:\Anaconda\Lib\site-packages這個目錄下的en_core_web_md復制一份放到當前.py文件相同目錄下。
  • 在torchtext中使用spacy時,由於field的默認屬性是tokenizer_language='en',當使用en_core_web_md時要改field.py文件中創建的field屬性為tokenizer_language='en_core_web_md',且data.Field()中的參數也要改為tokenizer_language='en_core_web_md'。

1.加載數據

 1 import numpy as np
 2 import torch
 3 from torch import nn, optim
 4 from torchtext import data, datasets
 5 
 6 #為CPU設置隨機種子
 7 torch.manual_seed(123)
 8 
 9 #兩個Field對象定義字段的處理方法(文本字段、標簽字段)
10 TEXT = data.Field(tokenize='spacy', tokenizer_language='en_core_web_md')
11 LABEL = data.LabelField(dtype=torch.float)
12 
13 #IMDB共50000影評,包含正面和負面兩個類別。數據被前面的Field處理
14 train_data, test_data = datasets.IMDB.splits(TEXT, LABEL)  
15 
16 print('len of train data:', len(train_data))        #25000
17 print('len of test data:', len(test_data))          #25000
18 
19 print(train_data.examples[15].text)
20 print(train_data.examples[15].label)

['Like', 'one', 'of', 'the', 'previous', 'commenters', 'said', ',', 'this', 'had', 'the', 'foundations', 'of', 'a', 'great', 'movie', 'but', 'something', 'happened', 'on', 'the', 'way', 'to', 'delivery', '.', 'Such', 'a', 'waste', 'because', 'Collette', "'s", 'performance', 'was', 'eerie', 'and', 'Williams', 'was', 'believable', '.', 'I', 'just', 'kept', 'waiting', 'for', 'it', 'to', 'get', 'better', '.', 'I', 'do', "n't", 'think', 'it', 'was', 'bad', 'editing', 'or', 'needed', 'another', 'director', ',', 'it', 'could', 'have', 'just', 'been', 'the', 'film', '.', 'It', 'came', 'across', 'as', 'a', 'Canadian', 'movie', ',', 'something', 'like', 'the', 'first', 'few', 'seasons', 'of', 'X', '-', 'Files', '.', 'Not', 'cheap', ',', 'just', 'hokey', '.', 'Also', ',', 'it', 'needed', 'a', 'little', 'more', 'suspense', '.', 'Something', 'that', 'makes', 'you', 'jump', 'off', 'your', 'seat', '.', 'The', 'movie', 'reached', 'that', 'moment', 'then', 'faded', 'away', ';', 'kind', 'of', 'like', 'a', 'false', 'climax', '.', 'I', 'can', 'see', 'how', 'being', 'too', 'suspenseful', 'would', 'have', 'taken', 'away', 'from', 'the', '"', 'reality', '"', 'of', 'the', 'story', 'but', 'I', 'thought', 'that', 'part', 'was', 'reached', 'when', 'Gabriel', 'was', 'in', 'the', 'hospital', 'looking', 'for', 'the', 'boy', '.', 'This', 'movie', 'needs', 'to', 'have', 'a', 'Director', "'s", 'cut', 'that', 'tries', 'to', 'fix', 'these', 'problems', '.']

pos

當我們把句子傳進模型的時候,是按照一個個batch傳進去的,而且每個batch中的句子必須是相同的長度。為了確保句子的長度相同,TorchText會把短的句子pad到和最長的句子等長。

創建vocabulary

vocabulary把每個單詞一一映射到一個數字。使用10k個單詞來構建單詞表(用max_size這個參數可以設定),所有其他的單詞都用<unk>來表示。

詞典中應當有10002個單詞,且有兩個label,可以通過TEXT.vocab和TEXT.label查詢,可以直接用stoi(stringtoint) 或者itos(inttostring) 來查看單詞表。

1 TEXT.build_vocab(train_data, max_size=10000, vectors='glove.6B.100d')
2 LABEL.build_vocab(train_data)
3 print(len(TEXT.vocab))                     #10002
4 print(TEXT.vocab.itos[:12])                #['<unk>', '<pad>', 'the', ',', '.', 'and', 'a', 'of', 'to', 'is', 'in', 'I']
5 print(TEXT.vocab.stoi['and'])              #5
6 print(LABEL.vocab.stoi)                    #defaultdict(None, {'neg': 0, 'pos': 1})

創建iteratiors

每個iterator中各有兩部分:詞(.text)和標簽(.label),其中text全部轉換成數字了。BucketIterator會把長度差不多的句子放到同一個batch中,確保每個batch中不出現太多的padding。這里因為pad比較少,所以把<pad>也當做了模型的輸入進行訓練。如果有GPU,還可以指定每個iteration返回的tensor都在GPU上。

1 batchsz = 30
2 train_iterator, test_iterator = data.BucketIterator.splits(
3                                 (train_data, test_data),
4                                 batch_size = batchsz,
5                                )

2.定義模型

 1 class RNN(nn.Module):
 2     
 3     def __init__(self, vocab_size, embedding_dim, hidden_dim):
 4        
 5         super(RNN, self).__init__()
 6         
 7         # [0-10001] => [100]
 8         self.embedding = nn.Embedding(vocab_size, embedding_dim)        #單詞數,嵌入向量維度
 9         # [100] => [256]
10         self.rnn = nn.LSTM(embedding_dim, hidden_dim, num_layers=2,     #雙向RNN,所以下面使用hidden_dim*2 11                            bidirectional=True, dropout=0.5)
12         # [256*2] => [1]
13         self.fc = nn.Linear(hidden_dim*2, 1)
14         self.dropout = nn.Dropout(0.5)
15         
16     
17     def forward(self, x):
18         """
19         x: [seq_len, b] vs [b, 3, 28, 28]
20         """
21         # [seq_len, b, 1] => [seq_len, b, 100]
22         embedding = self.dropout(self.embedding(x))
23         
24         # output: [seq, b, hidden_len*2]
25         # hidden/h: [num_layers*2, b, hidden_len]
26         # cell/c: [num_layers*2, b, hidden_len]
27         output, (hidden, cell) = self.rnn(embedding)          #[h0,c0]隨機初始化 28         
29         # [num_layers*2, b, hidden_len] => 2 of [b, hidden_len] => [b, hidden_len*2]
30         hidden = torch.cat([hidden[-2], hidden[-1]], dim=1)   #雙向,所以要把最后兩個輸出連接 31         
32         # [b, hidden_len*2] => [b, 1]
33         hidden = self.dropout(hidden)
34         out = self.fc(hidden)
35         
36         return out

使用預訓練過的embedding來替換隨機初始化(Tip:.copy_()這種帶着下划線的函數均代表替換inplace)

1 rnn = RNN(len(TEXT.vocab), 100, 256)                          #詞個數,詞嵌入維度,輸出維度
2 
3 pretrained_embedding = TEXT.vocab.vectors
4 print('pretrained_embedding:', pretrained_embedding.shape)    #torch.Size([10002, 100])
5 rnn.embedding.weight.data.copy_(pretrained_embedding)
6 print('embedding layer inited.')

或者在定義embedding的時候直接self.embedding = nn.Embedding.from_pretrained(pretrained_embedding)    #torch.tensor(embedding_matrix, dtype=torch.float)

3.訓練模型

首先定義模型和損失函數。

1 optimizer = optim.Adam(rnn.parameters(), lr=1e-3)
2 criteon = nn.BCEWithLogitsLoss()                  #BCEWithLogitsLoss是針對二分類的CrossEntropy

定義一個函數用於計算准確率

1 def binary_acc(preds, y):
2 
3      preds = torch.round(torch.sigmoid(preds))
4      correct = torch.eq(preds, y).float()
5      acc = correct.sum() / len(correct)
6      return acc  

定義一個訓練函數

 1 def train(rnn, iterator, optimizer, criteon):
 2     
 3     avg_acc = []
 4     rnn.train()        #表示進入訓練模式
 5     
 6     for i, batch in enumerate(iterator):
 7         
 8         # [seq, b] => [b, 1] => [b]
 9         pred = rnn(batch.text).squeeze(1)            #batch.text 就是上面forward函數的參數text,壓縮維度是為了和batch.label維度一致
10         
11         loss = criteon(pred, batch.label)
12         acc = binary_acc(pred, batch.label).item()   #計算每個batch的准確率
13         avg_acc.append(acc)
14         
15         optimizer.zero_grad()
16         loss.backward()
17         optimizer.step()                             #不斷訓練,pred的值會越來越接近真實的label值 18         
19         if i%10 == 0:
20             print(i, acc)
21         
22     avg_acc = np.array(avg_acc).mean()
23     print('avg acc:', avg_acc)

4.評估模型

定義一個評估函數,和訓練函數高度重合,區別是要把rnn.train()改為rnn.val(),不需要反向傳播過程。

 1 def evaluate(rnn, iterator, criteon):    
 2     avg_acc = []    
 3     rnn.eval()         #表示進入測試模式
 4     
 5     with torch.no_grad():
 6         for batch in iterator:
 7             
 8             pred = rnn(batch.text).squeeze(1)      #[b, 1] => [b]
 9             loss = criteon(pred, batch.label)
10             acc = binary_acc(pred, batch.label).item()
11             avg_acc.append(acc)
12         
13     avg_acc = np.array(avg_acc).mean()
14     
15     print('test acc:', avg_acc)

運行

1 for epoch in range(10):
2     
3     train(rnn, train_iterator, optimizer, criteon)
4     evaluate(rnn, test_iterator, criteon)

渣渣本實在是跑不動,結果就先不放了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM