Pytorch的LSTM的理解
20211227
?
lstm和gru的區(qū)別
?
Pytorch實(shí)現(xiàn)LSTM案例學(xué)習(xí)(1)_ch206265的博客-CSDN博客_pytorch搭建lstm
lstm案例
class torch.nn.LSTM(*args, **kwargs)
參數(shù)列表
- input_size:x的特征維度
- hidden_size:隱藏層的特征維度
- num_layers:lstm隱層的層數(shù),默認(rèn)為1
- bias:False則bih=0和bhh=0. 默認(rèn)為True
- batch_first:True則輸入輸出的數(shù)據(jù)格式為 (batch, seq, feature)
- dropout:除最后一層,每一層的輸出都進(jìn)行dropout,默認(rèn)為: 0
- bidirectional:True則為雙向lstm默認(rèn)為False
- 輸入:input, (h0, c0)
- 輸出:output, (hn,cn)
輸入數(shù)據(jù)格式:?
input(seq_len, batch, input_size)?
h0(num_layers * num_directions, batch, hidden_size)?
c0(num_layers * num_directions, batch, hidden_size)
輸出數(shù)據(jù)格式:?
output(seq_len, batch, hidden_size * num_directions)?
hn(num_layers * num_directions, batch, hidden_size)?
cn(num_layers * num_directions, batch, hidden_size)
Pytorch里的LSTM單元接受的輸入都必須是3維的張量(Tensors).每一維代表的意思不能弄錯(cuò)。
第一維體現(xiàn)的是序列(sequence)結(jié)構(gòu),也就是序列的個(gè)數(shù),用文章來說,就是每個(gè)句子的長度,因?yàn)槭俏菇o網(wǎng)絡(luò)模型,一般都設(shè)定為確定的長度,也就是我們喂給LSTM神經(jīng)元的每個(gè)句子的長度,當(dāng)然,如果是其他的帶有帶有序列形式的數(shù)據(jù),則表示一個(gè)明確分割單位長度,
例如是如果是股票數(shù)據(jù)內(nèi),這表示特定時(shí)間單位內(nèi),有多少條數(shù)據(jù)。這個(gè)參數(shù)也就是明確這個(gè)層中有多少個(gè)確定的單元來處理輸入的數(shù)據(jù)。
第二維度體現(xiàn)的是batch_size,也就是一次性喂給網(wǎng)絡(luò)多少條句子,或者股票數(shù)據(jù)中的,一次性喂給模型多少是個(gè)時(shí)間單位的數(shù)據(jù),具體到每個(gè)時(shí)刻,也就是一次性喂給特定時(shí)刻處理的單元的單詞數(shù)或者該時(shí)刻應(yīng)該喂給的股票數(shù)據(jù)的條數(shù)
第三位體現(xiàn)的是輸入的元素(elements of input),也就是,每個(gè)具體的單詞用多少維向量來表示,或者股票數(shù)據(jù)中 每一個(gè)具體的時(shí)刻的采集多少具體的值,比如最低價(jià),最高價(jià),均價(jià),5日均價(jià),10均價(jià),等等
H0-Hn是什么意思呢?就是每個(gè)時(shí)刻中間神經(jīng)元應(yīng)該保存的這一時(shí)刻的根據(jù)輸入和上一課的時(shí)候的中間狀態(tài)值應(yīng)該產(chǎn)生的本時(shí)刻的狀態(tài)值,
這個(gè)數(shù)據(jù)單元是起的作用就是記錄這一時(shí)刻之前考慮到所有之前輸入的狀態(tài)值,形狀應(yīng)該是和特定時(shí)刻的輸出一致
c0-cn就是開關(guān),決定每個(gè)神經(jīng)元的隱藏狀態(tài)值是否會(huì)影響的下一時(shí)刻的神經(jīng)元的處理,形狀應(yīng)該和h0-hn一致。
當(dāng)然如果是雙向,和多隱藏層還應(yīng)該考慮方向和隱藏層的層數(shù)。
注:上式中的 q后面跟一個(gè)單詞,表示該單詞的一定維度的向量表示,該維度即是LSTM接受的張量中的第三個(gè)維度。
記住這里存在一個(gè)尺寸為1的第二維度。此外,如果你希望一次在網(wǎng)絡(luò)中走完整個(gè)序列,你可以將第一個(gè)維度的尺寸也設(shè)為1。
下面我簡單看一下例子:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optimtorch.manual_seed(1)lstm = nn.LSTM(3, 3) # 輸入單詞用一個(gè)維度為3的向量表示, 隱藏層的一個(gè)維度3,僅有一層的神經(jīng)元,
#記住就是神經(jīng)元,這個(gè)時(shí)候神經(jīng)層的詳細(xì)結(jié)構(gòu)還沒確定,僅僅是說這個(gè)網(wǎng)絡(luò)可以接受[seq_len,batch_size,3]的數(shù)據(jù)輸入
print(lstm.all_weights)inputs = [torch.randn(1, 3) for _ in range(5)]# 構(gòu)造一個(gè)由5個(gè)單單詞組成的句子 構(gòu)造出來的形狀是 [5,1,3]也就是明確告訴網(wǎng)絡(luò)結(jié)構(gòu)我一個(gè)句子由5個(gè)單詞組成,
#每個(gè)單詞由一個(gè)1X3的向量組成,就是這個(gè)樣子[1,2,3]
#同時(shí)確定了網(wǎng)絡(luò)結(jié)構(gòu),每個(gè)批次只輸入一個(gè)句子,其中第二維的batch_size很容易迷惑人
#對整個(gè)這層來說,是一個(gè)批次輸入多少個(gè)句子,具體但每個(gè)神經(jīng)元,就是一次性喂給神經(jīng)元多少個(gè)單詞。
print('Inputs:',inputs)# 初始化隱藏狀態(tài)
hidden = (torch.randn(1, 1, 3),torch.randn(1, 1, 3))
print('Hidden:',hidden)
for i in inputs:# 將序列的元素逐個(gè)輸入到LSTM,這里的View是把輸入放到第三維,看起來有點(diǎn)古怪,
#回頭看看上面的關(guān)于LSTM輸入的描述,這是固定的格式,以后無論你什么形式的數(shù)據(jù),
#都必須放到這個(gè)維度。就是在原Tensor的基礎(chǔ)之上增加一個(gè)序列維和MiniBatch維,
#這里可能還會(huì)有迷惑,前面的1是什么意思啊,就是一次把這個(gè)輸入處理完,
#在輸入的過程中不會(huì)輸出中間結(jié)果,這里注意輸入的數(shù)據(jù)的形狀一定要和LSTM定義的輸入形狀一致。# 經(jīng)過每步操作,hidden 的值包含了隱藏狀態(tài)的信息out, hidden = lstm(i.view(1, 1, -1), hidden)
print('out1:',out)
print('hidden2:',hidden)
# 另外, 我們還可以一次對整個(gè)序列進(jìn)行訓(xùn)練. LSTM 返回的第一個(gè)值表示所有時(shí)刻的隱狀態(tài)值,
# 第二個(gè)值表示最近的隱狀態(tài)值 (因此下面的 "out"的最后一個(gè)值和 "hidden" 的值是一樣的).
# 之所以這樣設(shè)計(jì), 是為了通過 "out" 的值來獲取所有的隱狀態(tài)值, 而用 "hidden" 的值來
# 進(jìn)行序列的反向傳播運(yùn)算, 具體方式就是將它作為參數(shù)傳入后面的 LSTM 網(wǎng)絡(luò).# 增加額外的第二個(gè)維度
inputs = torch.cat(inputs).view(len(inputs), 1, -1)
hidden = (torch.randn(1, 1, 3), torch.randn(1, 1, 3)) # clean out hidden state
out, hidden = lstm(inputs, hidden)
print('out2',out)
print('hidden3',hidden)
運(yùn)行輸出:
out2 tensor([[[-0.0187, 0.1713, -0.2944]],[[-0.3521, 0.1026, -0.2971]],[[-0.3191, 0.0781, -0.1957]],[[-0.1634, 0.0941, -0.1637]],[[-0.3368, 0.0959, -0.0538]]])
hidden3 (tensor([[[-0.3368, 0.0959, -0.0538]]]), tensor([[[-0.9825, 0.4715, -0.0633]]]))
接下來我們要做一件事,
就是訓(xùn)練網(wǎng)絡(luò)幫我我們標(biāo)注詞性,當(dāng)然實(shí)際的自然語言處理我們有很多成功的算法,但是應(yīng)對新詞總會(huì)有點(diǎn)麻煩,我們想啊,既然神經(jīng)網(wǎng)絡(luò)可以幫我們做了很多神奇的事,那么我們可不可以訓(xùn)練一個(gè)網(wǎng)路模型來幫我我們自動(dòng)的標(biāo)注詞性呢,顯然這個(gè)思路靠譜,使用神經(jīng)網(wǎng)絡(luò)的套路:
- 準(zhǔn)備訓(xùn)練數(shù)據(jù),這一步最是頭大的,最好的辦法就是找各大機(jī)構(gòu)提供的標(biāo)準(zhǔn)的標(biāo)注庫,實(shí)在找不到,自己處理,國內(nèi)外很多的分詞標(biāo)準(zhǔn)庫和工具可以用,jieba分詞標(biāo)注是一個(gè)不錯(cuò)的選擇,使用起來也簡單。
- 讀取數(shù)據(jù)文件
- 分詞
- 把詞語和標(biāo)注分別放在兩個(gè)數(shù)組里面
- 構(gòu)建詞匯表、構(gòu)建標(biāo)注表
- 把分詞結(jié)果轉(zhuǎn)換成對應(yīng)詞匯表和標(biāo)簽表中的序號。
- 構(gòu)建網(wǎng)絡(luò)模型,這里使用Word2Vec預(yù)處理一下輸入文本
- 訓(xùn)練網(wǎng)絡(luò)
- 分析結(jié)果
下面按照這個(gè)套路上源碼:
import jieba.posseg
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
# import sysimport gensim
torch.manual_seed(2)
# sys.stdout = open('1.log', 'a')
sent='明天是榮耀運(yùn)營十周年紀(jì)念日。' \'榮耀從兩周年紀(jì)念日開始,' \'在每年的紀(jì)念日這天凌晨零點(diǎn)會(huì)開放一個(gè)新區(qū)。' \'第十版賬號卡的銷售從三個(gè)月前就已經(jīng)開始。' \'在老區(qū)玩的不順心的老玩家、準(zhǔn)備進(jìn)入榮耀的新手,都已經(jīng)準(zhǔn)備好了新區(qū)賬號對這個(gè)日子翹首以盼。' \'陳果坐到了葉修旁邊的機(jī)器,隨手登錄了她的逐煙霞。' \'其他九大區(qū)的玩家人氣并沒有因?yàn)榈谑畢^(qū)的新開而降低多少,' \'越老的區(qū)越是如此,實(shí)在是因?yàn)闃s耀的一個(gè)賬號想經(jīng)營起來并不容易。' \'陳果的逐煙霞用了五年時(shí)間才在普通玩家中算是翹楚,哪舍得輕易拋棄。' \'更何況到最后大家都會(huì)沖著十大區(qū)的共同地圖神之領(lǐng)域去。'
words=jieba.posseg.cut(sent,HMM=True) #分詞
processword=[]
tagword=[]
for w in words:processword.append(w.word)tagword.append(w.flag)
#詞語和對應(yīng)的詞性做一一對應(yīng)
texts=[(processword,tagword)]#使用gensim構(gòu)建本例的詞匯表
id2word=gensim.corpora.Dictionary([texts[0][0]])
#每個(gè)詞分配一個(gè)獨(dú)特的ID
word2id=id2word.token2id#使用gensim構(gòu)建本例的詞性表
id2tag=gensim.corpora.Dictionary([texts[0][1]])
#為每個(gè)詞性分配ID
tag2id=id2tag.token2iddef sen2id(inputs):return [word2id[word] for word in inputs]def tags2id(inputs):return [tag2id[word] for word in inputs]
#根據(jù)詞匯表把文本輸入轉(zhuǎn)換成對應(yīng)的詞匯表的序號張量
def formart_input(inputs):return torch.tensor(sen2id(inputs),dtype=torch.long)#根據(jù)詞性表把文本標(biāo)注輸入轉(zhuǎn)換成對應(yīng)的詞匯標(biāo)注的張量
def formart_tag(inputs):return torch.tensor(tags2id(inputs),dtype=torch.long)
#定義網(wǎng)絡(luò)結(jié)構(gòu)
class LSTMTagger(torch.nn.Module):def __init__(self,embedding_dim,hidden_dim,voacb_size,target_size):super(LSTMTagger,self).__init__()self.embedding_dim=embedding_dimself.hidden_dim=hidden_dimself.voacb_size=voacb_sizeself.target_size=target_size# 使用Word2Vec預(yù)處理一下輸入文本self.embedding=nn.Embedding(self.voacb_size,self.embedding_dim)# LSTM 以 word_embeddings 作為輸入, 輸出維度為 hidden_dim 的隱狀態(tài)值self.lstm=nn.LSTM(self.embedding_dim,self.hidden_dim)## 線性層將隱狀態(tài)空間映射到標(biāo)注空間self.out2tag=nn.Linear(self.hidden_dim,self.target_size)self.hidden = self.init_hidden()def init_hidden(self):# 開始時(shí)刻, 沒有隱狀態(tài)# 關(guān)于維度設(shè)置的詳情,請參考 Pytorch 文檔# 各個(gè)維度的含義是 (Seguence, minibatch_size, hidden_dim)return (torch.zeros(1, 1, self.hidden_dim),torch.zeros(1, 1, self.hidden_dim))def forward(self,inputs):# 預(yù)處理文本轉(zhuǎn)成稠密向量embeds=self.embedding((inputs))#根據(jù)文本的稠密向量訓(xùn)練網(wǎng)絡(luò)out,self.hidden=self.lstm(embeds.view(len(inputs),1,-1),self.hidden)#做出預(yù)測tag_space=self.out2tag(out.view(len(inputs),-1))tags=F.log_softmax(tag_space,dim=1)return tagsmodel=LSTMTagger(10,10,len(word2id),len(tag2id))
loss_function=nn.NLLLoss()
optimizer=optim.SGD(model.parameters(),lr=0.1)
#看看隨機(jī)初始化網(wǎng)絡(luò)的分析結(jié)果
with torch.no_grad():input_s=formart_input(texts[0][0])print(input_s)print(processword)tag_s=model(input_s)for i in range(tag_s.shape[0]):print(tag_s[i])# print(tag_s)
for epoch in range(300):# 再說明下, 實(shí)際情況下你不會(huì)訓(xùn)練300個(gè)周期, 此例中我們只是構(gòu)造了一些假數(shù)據(jù)for p ,t in texts:# Step 1. 請記住 Pytorch 會(huì)累加梯度# 每次訓(xùn)練前需要清空梯度值model.zero_grad()# 此外還需要清空 LSTM 的隱狀態(tài)# 將其從上個(gè)實(shí)例的歷史中分離出來# 重新初始化隱藏層數(shù)據(jù),避免受之前運(yùn)行代碼的干擾,如果不重新初始化,會(huì)有報(bào)錯(cuò)。model.hidden = model.init_hidden()# Step 2. 準(zhǔn)備網(wǎng)絡(luò)輸入, 將其變?yōu)樵~索引的Tensor 類型數(shù)據(jù)sentence_in=formart_input(p)tags_in=formart_tag(t)# Step 3. 前向傳播tag_s=model(sentence_in)# Step 4. 計(jì)算損失和梯度值, 通過調(diào)用 optimizer.step() 來更新梯度loss=loss_function(tag_s,tags_in)loss.backward()print('Loss:',loss.item())optimizer.step()#看看訓(xùn)練后的結(jié)果
with torch.no_grad():input_s=formart_input(texts[0][0])tag_s=model(input_s)for i in range(tag_s.shape[0]):print(tag_s[i])
總結(jié)
以上是生活随笔為你收集整理的Pytorch的LSTM的理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pytorch版的bilstm+crf实
- 下一篇: pytorch nn.LSTM()参数详