提前安裝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)
渣渣本實在是跑不動,結果就先不放了。
