pytorch : LSTM做詞性預測
阿新 • • 發佈:2018-11-09
本文學習於書籍《深度學習入門之pytorch》
對於一個單詞,會有這不同的詞性,首先能夠根據一個單詞的字尾來初步判斷,比如 -ly 這種字尾,很大概率是一個副詞,除此之外,一個相同的單詞可以表示兩種不同的詞性,比如 book 既可以表示名詞,也可以表示動詞,所以到底這個詞是什麼詞性需要結合前後文來具體判斷。
根據這個問題,我們可以使用 lstm 模型來進行預測,首先對於一個單詞,可以將其看作一個序列,比如 apple 是由 a p p l e 這 5 個單詞構成,這就形成了 5 的序列,我們可以對這些字元構建詞嵌入,然後輸入 lstm,就像 lstm 做影象分類一樣,只取最後一個輸出作為預測結果,整個單詞的字串能夠形成一種記憶的特性,幫助我們更好的預測詞性。
接著我們把這個單詞和其前面幾個單詞構成序列,可以對這些單詞構建新的詞嵌入,最後輸出結果是單詞的詞性,也就是根據前面幾個詞的資訊對這個詞的詞性進行分類。
import torch from torch import nn from torch.autograd import Variable #使用下面簡單的訓練集 training_data = [("The dog ate the apple".split(), ["DET", "NN", "V", "DET", "NN"]), ("Everybody read that book".split(), ["NN", "V", "DET", "NN"])] #對單詞和標籤進行編碼 word_to_idx = {} tag_to_idx = {} for context, tag in training_data: for word in context: if word.lower() not in word_to_idx: word_to_idx[word.lower()] = len(word_to_idx) for label in tag: if label.lower() not in tag_to_idx: tag_to_idx[label.lower()] = len(tag_to_idx) #對字母進行編碼 alphabet = 'abcdefghijklmnopqrstuvwxyz' char_to_idx = {} for i in range(len(alphabet)): char_to_idx[alphabet[i]] = i #構建訓練資料 def make_sequence(x, dic): # 字元編碼 idx = [dic[i.lower()] for i in x] idx = torch.LongTensor(idx) return idx make_sequence('apple', char_to_idx) training_data[1][0] make_sequence(training_data[1][0], word_to_idx) #構建單個字元的 lstm 模型 class char_lstm(nn.Module): def __init__(self, n_char, char_dim, char_hidden): super(char_lstm, self).__init__() self.char_embed = nn.Embedding(n_char, char_dim) self.lstm = nn.LSTM(char_dim, char_hidden) def forward(self, x): x = self.char_embed(x) out, _ = self.lstm(x) return out[-1] # (batch, hidden) #構建詞性分類的 lstm 模型 class lstm_tagger(nn.Module): def __init__(self, n_word, n_char, char_dim, word_dim, char_hidden, word_hidden, n_tag): super(lstm_tagger, self).__init__() self.word_embed = nn.Embedding(n_word, word_dim) self.char_lstm = char_lstm(n_char, char_dim, char_hidden) self.word_lstm = nn.LSTM(word_dim + char_hidden, word_hidden) self.classify = nn.Linear(word_hidden, n_tag) def forward(self, x, word): char = [] for w in word: # 對於每個單詞做字元的 lstm char_list = make_sequence(w, char_to_idx) char_list = char_list.unsqueeze(1) # (seq, batch, feature) 滿足 lstm 輸入條件 char_infor = self.char_lstm(Variable(char_list)) # (batch, char_hidden) char.append(char_infor) char = torch.stack(char, dim=0) # (seq, batch, feature) x = self.word_embed(x) # (batch, seq, word_dim) x = x.permute(1, 0, 2) # 改變順序 x = torch.cat((x, char), dim=2) # 沿著特徵通道將每個詞的詞嵌入和字元 lstm 輸出的結果拼接在一起 x, _ = self.word_lstm(x) s, b, h = x.shape x = x.view(-1, h) # 重新 reshape 進行分類線性層 out = self.classify(x) return out net = lstm_tagger(len(word_to_idx), len(char_to_idx), 10, 100, 50, 128, len(tag_to_idx)) criterion = nn.CrossEntropyLoss() optimizer = torch.optim.SGD(net.parameters(), lr=1e-2) # 開始訓練 for e in range(300): train_loss = 0 for word, tag in training_data: word_list = make_sequence(word, word_to_idx).unsqueeze(0) # 新增第一維 batch tag = make_sequence(tag, tag_to_idx) word_list = Variable(word_list) tag = Variable(tag) # 前向傳播 out = net(word_list, word) loss = criterion(out, tag) train_loss += loss.item() # 反向傳播 optimizer.zero_grad() loss.backward() optimizer.step() if (e + 1) % 50 == 0: print('Epoch: {}, Loss: {:.5f}'.format(e + 1, train_loss / len(training_data))) net = net.eval() test_sent = 'Everybody ate the apple' test = make_sequence(test_sent.split(), word_to_idx).unsqueeze(0) out = net(Variable(test), test_sent.split()) print(out) print(tag_to_idx) #最後可以得到上面的結果,因為最後一層的線性層沒有使用 softmax,所以數值不太像 #一個概率,但是每一行數值最大的就表示屬於該類,可以看到第一個單詞 'Everybody' #屬於 nn,第二個單詞 'ate' 屬於 v,第三個單詞 'the' 屬於det,第四個單詞 #'apple' 屬於 nn,所以得到的這個預測結果是正確的
老實說這個程式碼我還沒有太理解,先貼出來,慢慢學習。