1. 程式人生 > >Pytorch LSTM 詞性判斷

Pytorch LSTM 詞性判斷

cti urn ever import rop 作用 for -a app

首先,我們定義好一個LSTM網絡,然後給出一個句子,每個句子都有很多個詞構成,每個詞可以用一個詞向量表示,這樣一句話就可以形成一個序列,我們將這個序列依次傳入LSTM,然後就可以得到與序列等長的輸出,每個輸出都表示的是一種詞性,比如名詞,動詞之類的,還是一種分類問題,每個單詞都屬於幾種詞性中的一種。

我們可以思考一下為什麽LSTM在這個問題裏面起著重要的作用。如果我們完全孤立的對一個詞做詞性的判斷這樣我們需要特別高維的詞向量,但是對於LSTM,它有著一個記憶的特性,這樣我們就能夠通過這個單詞前面記憶的一些詞語來對其做一個判斷,比如前面如果是my,那麽他緊跟的詞有很大可能就是一個名詞,這樣就能夠充分的利用上文來做這個問題。

同時我們還可以通過引入字符來增強表達,什麽意思呢?也就是說一個單詞有一些前綴和後綴,比如-ly這種後綴很大可能是一個副詞,這樣我們就能夠在字符水平得到一個詞性判斷的更好結果。

具體怎麽做呢?還是用LSTM。每個單詞有不同的字母組成,比如 apple 由a p p l e構成,我們同樣給這些字符詞向量,這樣形成了一個長度為5的序列,然後傳入另外一個LSTM網絡,只取最後輸出的狀態層作為它的一種字符表達,我們並不需要關心到底提取出來的字符表達是什麽樣的,在learning的過程中這些都是會被更新的參數,使得最終我們能夠正確預測。

  1 import torch
  2 import
torch.nn.functional as F 3 from torch import nn, optim 4 from torch.autograd import Variable 5 6 training_data = [("The dog ate the apple".split(), 7 ["DET", "NN", "V", "DET", "NN"]), 8 ("Everybody read that book".split(), ["NN", "V", "DET", 9
"NN"])] 10 # 每個單詞就用一個數字表示,每種詞性也用一個數字表示 11 word_to_idx = {} 12 tag_to_idx = {} 13 for context, tag in training_data: 14 for word in context: 15 if word not in word_to_idx: 16 # 對詞進行編碼 17 word_to_idx[word] = len(word_to_idx) 18 for label in tag: 19 if label not in tag_to_idx: 20 # 對詞性編碼 21 tag_to_idx[label] = len(tag_to_idx) 22 alphabet = abcdefghijklmnopqrstuvwxyz 23 character_to_idx = {} 24 for i in range(len(alphabet)): 25 # 對字母編碼 26 character_to_idx[alphabet[i]] = i 27 28 # 字符LSTM 29 class CharLSTM(nn.Module): 30 def __init__(self, n_char, char_dim, char_hidden): 31 super(CharLSTM, self).__init__() 32 self.char_embedding = nn.Embedding(n_char, char_dim) 33 self.char_lstm = nn.LSTM(char_dim, char_hidden, batch_first=True) 34 35 def forward(self, x): 36 x = self.char_embedding(x) 37 _, h = self.char_lstm(x) 38 # 取隱層 39 return h[0] 40 41 42 class LSTMTagger(nn.Module): 43 def __init__(self, n_word, n_char, char_dim, n_dim, char_hidden, n_hidden, 44 n_tag): 45 super(LSTMTagger, self).__init__() 46 self.word_embedding = nn.Embedding(n_word, n_dim) 47 self.char_lstm = CharLSTM(n_char, char_dim, char_hidden) 48 self.lstm = nn.LSTM(n_dim + char_hidden, n_hidden, batch_first=True) 49 self.linear1 = nn.Linear(n_hidden, n_tag) 50 51 def forward(self, x, word): 52 char = torch.FloatTensor() 53 for each in word: 54 char_list = [] 55 for letter in each: 56 # 對詞進行字母編碼 57 char_list.append(character_to_idx[letter.lower()]) 58 char_list = torch.LongTensor(char_list) 59 char_list = char_list.unsqueeze(0) 60 if torch.cuda.is_available(): 61 tempchar = self.char_lstm(Variable(char_list).cuda()) 62 else: 63 tempchar = self.char_lstm(Variable(char_list)) 64 tempchar = tempchar.squeeze(0) 65 char = torch.cat((char, tempchar.cpu().data), 0) 66 if torch.cuda.is_available(): 67 char = char.cuda() 68 char = Variable(char) 69 x = self.word_embedding(x) 70 x = torch.cat((x, char), 1) # char編碼與word編碼cat 71 x = x.unsqueeze(0) 72 # 取輸出層 句長*n_hidden 73 x, _ = self.lstm(x) 74 x = x.squeeze(0) 75 x = self.linear1(x) 76 y = F.log_softmax(x) 77 return y 78 79 80 model = LSTMTagger( 81 len(word_to_idx), len(character_to_idx), 10, 100, 50, 128, len(tag_to_idx)) 82 if torch.cuda.is_available(): 83 model = model.cuda() 84 criterion = nn.CrossEntropyLoss() 85 optimizer = optim.SGD(model.parameters(), lr=1e-2) 86 87 88 def make_sequence(x, dic): 89 idx = [dic[i] for i in x] 90 idx = Variable(torch.LongTensor(idx)) 91 return idx 92 93 94 for epoch in range(300): 95 print(* * 10) 96 print(epoch {}.format(epoch + 1)) 97 running_loss = 0 98 for data in training_data: 99 word, tag = data 100 word_list = make_sequence(word, word_to_idx) 101 tag = make_sequence(tag, tag_to_idx) 102 if torch.cuda.is_available(): 103 word_list = word_list.cuda() 104 tag = tag.cuda() 105 # forward 106 out = model(word_list, word) 107 loss = criterion(out, tag) 108 running_loss += loss.data[0] 109 # backward 三步常規操作 110 optimizer.zero_grad() 111 loss.backward() 112 optimizer.step() 113 print(Loss: {}.format(running_loss / len(data))) 114 print() 115 input = make_sequence("Everybody ate the apple".split(), word_to_idx) 116 if torch.cuda.is_available(): 117 input = input.cuda() 118 model.eval() #對dropout和batch normalization的操作在訓練和測試的時候是不一樣 119 out = model(input, "Everybody ate the apple".split()) 120 print(out)

首先n_word 和 n_dim來定義單詞的詞向量維度,n_char和char_dim來定義字符的詞向量維度,char_hidden表示CharLSTM輸出的維度,n_hidden表示每個單詞作為序列輸入的LSTM輸出維度,最後n_tag表示輸出的詞性的種類。

接著開始前向傳播,不僅要傳入一個編碼之後的句子,同時還需要傳入原本的單詞,因為需要對字符做一個LSTM,所以傳入的參數多了一個word_data表示一個句子的所有單詞。

然後就是將每個單詞傳入CharLSTM,得到的結果和單詞的詞向量拼在一起形成一個新的輸入,將輸入傳入LSTM裏面,得到輸出,最後接一個全連接層,將輸出維數定義為label的數目。

特別要註意裏面有一些unsqueeze(增維)和squeeze(降維)是因為LSTM的輸入要求要帶上batch_size(這裏是1),torch.cat裏面0和1分別表示沿著行和列來拼接。

預測一下 Everybody ate the apple 這句話每個詞的詞性,一共有3種詞性,DET,NN,V。最後得到的結果為:

技術分享圖片

一共有4行,每行裏面取最大的,那麽第一個詞的詞性就是NN,第二個詞是V,第三個詞是DET,第四個詞是NN。這個是相符的。

參考自:https://sherlockliao.github.io/2017/06/05/lstm%20language/

Pytorch LSTM 詞性判斷