文章目录
基础
没有办法找到非常前面的信息,存在长时依赖问题。
RNN_pytorch实现
使用RnnCell
import torch
from torch.autograd import Variable
from torch import nn
#定义一个单步的rnn
rnn_single=nn.RNNCell(input_size=100,hidden_size=200)
print(rnn_single.weight_hh)
"""
Parameter containing:
1.00000e-02 *
6.2260 -5.3805 3.5870 ... -2.2162 6.2760 1.6760
-5.1878 -4.6751 -5.5926 ... -1.8942 0.1589 1.0725
3.3236 -3.2726 5.5399 ... 3.3193 0.2117 1.1730
... ⋱ ...
2.4032 -3.4415 5.1036 ... -2.2035 -0.1900 -6.4016
5.2031 -1.5793 -0.0623 ... 0.3424 6.9412 6.3707
-5.4495 4.5280 2.1774 ... 1.8767 2.4968 5.3403
[torch.FloatTensor of size 200x200]
"""
#构造一个序列,长为6,batch是5,特征是100
x=Variable(torch.randn(6,5,100))#这是rnn的输入格式(seq_len,batch,feature(input size))
#定义初始的记忆状态
h_t=Variable(torch.zero(5,200))
#传入rnn
out=[]
for i in range(6):
h_t=rnn_single(x[i],h_t)
out.append(h_t)
print(h_t)
"""
Variable containing:
0.0136 0.3723 0.1704 ... 0.4306 -0.7909 -0.5306
-0.2681 -0.6261 -0.3926 ... 0.1752 0.5739 -0.2061
-0.4918 -0.7611 0.2787 ... 0.0854 -0.3899 0.0092
0.6050 0.1852 -0.4261 ... -0.7220 0.6809 0.1825
-0.6851 0.7273 0.5396 ... -0.7969 0.6133 -0.0852
[torch.FloatTensor of size 5x200]
"""
print(len(out))#6
out[0].shape#torch.Size([5, 200])
"""
经过rnn后,隐藏状态的值已经被改变了,因为网络记忆了序列中的信息,同时输出6个结果
"""
使用Rnn
import torch
from torch.autograd import Variable
from torch import nn
rnn_seq=nn.RNN(100,200)#(input_size,hdden_size)
#访问其中的参数
rnn_seq.weight_hh_10
"""
Parameter containing:
1.00000e-02 *
1.0998 -1.5018 -1.4337 ... 3.8385 -0.8958 -1.6781
5.3302 -5.4654 5.5568 ... 4.7399 5.4110 3.6170
1.0788 -0.6620 5.7689 ... -5.0747 -2.9066 0.6152
... ⋱ ...
-5.6921 0.1843 -0.0803 ... -4.5852 5.6194 -1.4734
4.4306 6.9795 -1.5736 ... 3.4236 -0.3441 3.1397
7.0349 -1.6120 -4.2840 ... -5.5676 6.8897 6.1968
[torch.FloatTensor of size 200x200]
"""
x=Variable(torch.randn(6,5,100))
out,h_t=rnn_seq(x)#默认使用的券0隐藏状态
print(h_t)
"""
Variable containing:
( 0 ,.,.) =
0.2012 0.0517 0.0570 ... 0.2316 0.3615 -0.1247
0.5307 0.4147 0.7881 ... -0.4138 -0.1444 0.3602
0.0882 0.4307 0.3939 ... 0.3244 -0.4629 -0.2315
0.2868 0.7400 0.6534 ... 0.6631 0.2624 -0.0162
0.0841 0.6274 0.1840 ... 0.5800 0.8780 0.4301
[torch.FloatTensor of size 1x5x200]
"""
len(out)#6
"""
这里的h_t是网络最后的隐藏状态,网络也输出了6个结果,h_t是最后的结果
"""
#自己定义初始的隐藏状态
h_0=Variable(torch.randn(1,5,200))#(num_layers * num_direction, batch, hidden_size)
out,h_t=rnn_seq(x,h_0)
print(h_t)
"""
Variable containing:
( 0 ,.,.) =
0.2091 0.0353 0.0625 ... 0.2340 0.3734 -0.1307
0.5498 0.4221 0.7877 ... -0.4143 -0.1209 0.3335
0.0757 0.4204 0.3826 ... 0.3187 -0.4626 -0.2336
0.3106 0.7355 0.6436 ... 0.6611 0.2587 -0.0338
0.1025 0.6350 0.1943 ... 0.5720 0.8749 0.4525
[torch.FloatTensor of size 1x5x200]
"""
print(out.shape)#torch.Size([6, 5, 200]),(seq, batch, feature)
一般使用Rnn
,避免手动写循环,非常方便。
如果不特别说明,选择使用默认的全0初始化隐藏状态 。
LSTM(Long Short Term Memory networks)
能在一定程度上解决长时依赖问题。
由三个门来控制,分别是输入门,遗忘门和输出门。
输入门和输出门限制着输入和输出的大小,遗忘门控制记忆的保留。
对于一个任务,遗忘门能够自己学习到该保留多少以前的记忆,不需要人为进行干扰,所以具备长时记忆的功能。
1.f是衰减系数,表示保留过去的多少信息。
2.i表示保留接受的新信息的多少 ,c_ 表示新接受的信息。
3.将前面得到的两个衰减系数的分别乘上过去的信息和现在的新信息来确定t时刻真正的记忆状态c
4.o是一个系数,表示t时刻到底输出多少的记忆状态c,得到真正的模型输出h
LSTM_pytorch实现
import torch
from torch.autograd import Variable
from torch import nn
lstm_seq=nn.LSTM(50,100,num_layers=2)#(in_size,hidden_size,num_layers)
print(lstm_seq.weight_hh_10)#第一层的h_t权重
"""
Parameter containing:
1.00000e-02 *
3.8420 5.7387 6.1351 ... 1.2680 0.9890 1.3037
-4.2301 6.8294 -4.8627 ... -6.4147 4.3015 8.4103
9.4411 5.0195 9.8620 ... -1.6096 9.2516 -0.6941
... ⋱ ...
1.2930 -1.3300 -0.9311 ... -6.0891 -0.7164 3.9578
9.0435 2.4674 9.4107 ... -3.3822 -3.9773 -3.0685
-4.2039 -8.2992 -3.3605 ... 2.2875 8.2163 -9.3277
[torch.FloatTensor of size 400x100]
"""
lstm_input=Variable(torch.randn(10,3,50))#(seq_len.batch,in_size)
out,(h,c)=lstm_seq(lstm_input)#使用默认的全0的隐藏状态
"""
LSTM输出的隐藏状态有两个,h,c.就是图中每个cell之间的两个箭头。
这两个隐藏状态的大小都是相同的(num_layers*direction,batch,feature)
"""
h.shape#两层(2,3,100)
c.shape#torch.Size([2, 3, 100])
out.shape#(10,3,100)
#当不使用默认的隐藏状态时
h_init = Variable(torch.randn(2, 3, 100))
c_init = Variable(torch.randn(2, 3, 100))
out,(h,c)=lstm_seq(lstm_input,(h_init,c_init))
h.shape#(2,3,100)
c.shape#torch.Size([2, 3, 100])
out.shape#(10,3,100)
GRU(Gated Recurrent Unit)
2014年由Cho提出。
GRU和LSTM最大的不同在于GRU将遗忘门和输入门合成为一个更新门,同时网络不再额外给出记忆状态c,而是将输出结果作为记忆状态不断往后传。
重置门(reset gate)和更新门(update gate)。同时在这个结构中,把细胞状态和隐藏状态进行了合并。最后模型比标准的 LSTM 结构要简单,而且这个结构后来也非常流行。
import torch
from torch.autograd import Variable
from torch import nn
gru_seq=nn.GRU(10,20)#(input_size,outputz-size)
gru_input=Variable(torch.randn(3,32,10))#(seq_len,batch,inpput_size)
out,h=gru_seq(gru_input)
print(gru_seq.weight_hh_10)
"""
Parameter containing:
0.0766 -0.0548 -0.2008 ... -0.0250 -0.1819 0.1453
-0.1676 0.1622 0.0417 ... 0.1905 -0.0071 -0.1038
0.0444 -0.1516 0.2194 ... -0.0009 0.0771 0.0476
... ⋱ ...
0.1698 -0.1707 0.0340 ... -0.1315 0.1278 0.0946
0.1936 0.1369 -0.0694 ... -0.0667 0.0429 0.1322
0.0870 -0.1884 0.1732 ... -0.1423 -0.1723 0.2147
[torch.FloatTensor of size 60x20]
"""
h.shape#torch.Size([1, 32, 20])
out.shape#torch.Size([3, 32, 20])
RNN的应用
PyTorch 高维矩阵转置 Transpose 和 Permute
x.view(x.size(0), -1)的解释
python常用简写
用RNN做图像分类mnist
结果比用cnn差一点,因为这个数据集主要是视觉信息
pytorch实现
import torch
from torch.autograd import Variable
from torch import nn
from torch.utils.data import DataLoader
from torchvision import transforms as tfs
from torchvision.datasets import MNIST
from utils import train
#定义数据
data_tf=tfs.Compose([
tfs.ToTensor(),
tfs.Normalize([0.5],[0.5])#标准化
])
train_set=MNIST('./data',train=True,tranform=tfs)
test_set=MNIST('./data',train=False,tranform=tfs)
train_data=DataLoader(train_set,batch=64,shuffle=True)
test_data=DataLoader(test_set,batch=128,shuffle=False)
#定义模型
class rnn_classify(nn.Module):
def __init__(self,in_feature=28,hidden_feature=100,num_class=10,num_layers=2):
super(rnn_classify,self).__init()
self.rnn=nn.LSTM(in_feature,hidden_feature,num_layers)#使用两层lstm
#将最后一个rnn的输出使用全连接得到最后的分类结果
self.classifier=nn.Linear(hidden_feature,num_class)
def forward(self, x):
"""
x大小为(batch,1,28,28),所以我们需要将其转换成RNN的输入形式,即(28,batch,28)
"""
x=x.squeeze()#去掉(batch,1,28,28)的1
x=x.permute(2,0,1)#将最后一维放到第一维,变成(28,batch,28)
out,_=self.rnn(x)#使用默认的隐藏状态,得到的out是(28,batch,hidden_feature)
out=out[-1,:,:]#取序列中的最后一个,大小为(batch,hidden_feature)
out=self.classifier(out)#得到分类结果
return out
net=rnn_classify()
criterion=nn.CrossEntropyLoss()
optimizer=torch.optim.Adadelta(net.parameters,lr=1e-1)
#开始训练
train(net,train_data,test_data,20,optimizer,criterion)
RNN做时间序列(回归)
图像分类并不是RNN擅长的应用场景,RNN更加擅长处理时间序列关系的数据。
时间序列的数据:具有时间特性的数据,比如股票价格、机票价格。随着时间的变化数据会不断发生变化,而且变化依赖于时间。
举例:根据历史飞机流量数据,预测未来飞机流量
pytorch实现
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
#%matplotlib inline
import torch
from torch import nn
from torch.autograd import Variable
data_csv=pd.read_csv('./data.csv',usecols=[1])
plt.plot(data_csv)
"""
进行预处理,将数据中的na的数据去掉,然后将数据标准化到0~1之间
"""
#数据预处理
data_csv=data_csv.dropna()#去掉na(np.nan),清除缺失值
dataset=data_csv.values
dataset=dataset.astype('float32')#转换成float32字符
max_value=np.max(dataset)
min_value=np.min(dataset)
scalar=max_value-min_value
dataset=list(map(lambda x:x/scalar,dataset))
"""
进行数据集的闯进啊,想通过前两个月的流量预测当月的流量。
则将前两个月的流量当作输入,当月的流量当作输出。
同时需要将数据集分为训练集和测试集。
简单地将前几年的数据作为训练集,后面两年的数据作为测试集
"""
def create_dataset(dataset,look_back=2):
dataX,dataY=[],[]
for i in range(len(dataset)-look_back):
a=dataset[i:(i+look_back)]
dataX.append(a)
dataY.append(dataset[i+look_back])
return np.array(dataX),np.array(dataY)
#创建好输入输出
data_X,data_Y=create_dataset(dataset)
#划分训练集和数据集.70%作为训练集
train_size=int(len(data_X)*0.7)
test_size=len(data_X)-train_size
train_X=data_X[:train_size]
train_Y=data_Y[:train_size]
test_X=data_X[train_size:]
test_Y=data_Y[train_size:]
"""
将数据改变一下形状,RNN需要读入的数据为(seq_len,batch,input_size).
所以需要重新改变一下数据的维度,这里只有一个序列,所以batch是1.
而输入的feature就是我们希望依据的几个月份。
这里我们定的是两个月份,所以feature=2
"""
train_X=train_X.reshape(-1,1,2)
train_Y=train_Y.reshape(-1,1,1)
test_X = test_X.reshape(-1,1,2)
train_X=torch.from_numpy(train_X)
train_Y=torch.from_numpy(train_Y)
test_X=torch.from_numpy(test_X)
"""
定义模型
第一部分是一个两层的RNN,每一步模型接受两个月的输入作为特征,得到一个输出特征。
接着通过一个线性层将RNN的输出回归到流量的具体数值。
这里我们需要用view来重新排列,因为nn.Linear不接受三维的输入,
所以我们先将前两维合并在一起,然后通过线性层之后再将其分开,最后输出结果。
"""
#定义模型
class lstm_reg(nn.Module):
def __init__(self,input_size,hidden_size,output_size=1,num_layers=2):
super(lstm_reg, self).__init__()
self.rnn=nn.LSTM(input_size,hidden_size,num_layers)#rnn
self.reg=nn.Linear(hidden_size,output_size)#回归
def forward(self, x):
x,_=self.rnn(x)
s,b,h=x.shape#(seq_len,batch,hidden_size)
x=x.view(s*b,h)
x=self.reg(x)
x=x.view(s,b,-1)
return x
net=lstm_reg()
criterion=nn.MSELoss()#均方误差
optimizer=torch.optim.Adam(net.parameters(),lr=1e-2)
"""
定义好网络结构,输入的维度为2,因为我们选用两个月的流量作为输入。
隐藏层的维度可以任意指定,这里选择4
"""
#开始训练
for e in range(1000):
var_x=Variable(train_X)
var_y=Variable(train_Y)
#前向传播
out=net(var_x)
loss=criterion(out,var_y)
#反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (e+1)%100==0:#每100次输出结果
print('Epoch:{},Loss:{}'.format(e+1,loss.data[0]))
#训练完后,用训练好的模型取预测后面的结果
net=net.eval()#转换成测试模式
data_X=data_X.reshape(-1,1,2)
data_X=torch.from_numpy(data_X)
var_data=Variable(data_X)
pre_test=net(var_data)#测试集的预测结果
#改变输出的格式
pre_test=pre_test.view(-1).data.numpy()
#画出实际结果和预测的结果
plt.plot(pre_test,'r',label='prediction')
plt.plot(dataset,'b',label='real')
plt.lagend(loc='best')
自然语言处理
是RNN一个非常热门的方向。
有词嵌入,n-gram,序列lstm三个经典模型
词嵌入
pytorch实现
在pytorch中使用torch.nn.Embedding(m,n)
即可,m表示单词的总数,n表示词嵌入的维度。
词嵌入相当于一个巨大的矩阵,矩阵的每一行表示一个单词。
import torch
from torch import nn
from torch.autograd import Variable
#定义词嵌入
embeds=nn.Embedding(2,5)#两个单词,维度为4
#得到词嵌入矩阵
print(embeds.weight)
"""
Parameter containing:
-1.3426 0.7316 -0.2437 0.4925 -0.0191
-0.8326 0.3367 0.2135 0.5059 0.8326
[torch.FloatTensor of size 2x5]
"""
"""
通过weitht得到整个词嵌入的矩阵。
注意,该矩阵是一个可以改变的parameter,在网络的训练中会不断更新,
同时词嵌入的数值可以直接进行修改。
比如我们可以读入一个预训练好的词嵌入等
"""
#直接手动修改词嵌入的值
embeds.weight.data=torch.ones(2,5)
print(embeds.weight)
"""
Parameter containing:
1 1 1 1 1
1 1 1 1 1
[torch.FloatTensor of size 2x5]
"""
"""
如果要访问其中一个单词的词向量,我么可以直接调用定义好的词嵌入。
但输入必须传入一个Variable,且类型是LongTensor
"""
#访问第50个词的词向量
embeds=nn.Embedding(100,10)
single_word_embed=embeds(Variable(torch.LongTensor([50])))
print(single_word_embed)
"""
Variable containing:
-1.4954 -1.8475 0.2913 -0.9674 -2.1250 -0.5783 -0.6717 0.5638 0.7038 0.4437
[torch.FloatTensor of size 1x10]
"""
Skip-Gram模型
词嵌入是用来定义词向量的相似性。
如何得到词嵌入?
如果一个词嵌入100维,显然不可能人为去赋值,所以为了得到词向量,需要Skip-Gram。
Skip-Gram模型是word2vec的网络架构。
N-Gram模型
词嵌入如何来训练语言模型
pytorch实现
import torch
from torch import nn
from torch.autograd import Variable
import torch.nn.functional as F
CONTEXT_SIZE=2#依据的单词数,希望由前面两个单词来预测这个单词
EMBEDDING_DIM=10#词向量的维度
#我们使用莎士比亚的诗
test_sentence="""When forty winters shall besiege thy brow,
And dig deep trenches in thy beauty's field,
Thy youth's proud livery so gazed on now,
Will be a totter'd weed of small worth held:
Then being asked, where all thy beauty lies,
Where all the treasure of thy lusty days;
To say, within thine own deep sunken eyes,
Were an all-eating shame, and thriftless praise.
How much more praise deserv'd thy beauty's use,
If thou couldst answer 'This fair child of mine
Shall sum my count, and make my old excuse,'
Proving his beauty by succession thine!
This were to be new made when thou art old,
And see thy blood warm when thou feel'st it cold.""".split()
"""
建立训练集,遍历整个语料库,将单词三个分组,前两个作为输入,最后一个作为预测的结果
"""
trigram=[((test_sentence[i],test_sentence[i+1]),test_sentence[i+2])
for i in range(len(test_sentence-2))]
#总的数据量
len(trigram)#113
#取出一个数看看
print(trigram[0])#(('When', 'forty'), 'winters')
#建立每个词与数字的编码,据此构建词嵌入
vocb=set(test_sentence)#使用set将重复的元素去掉
word_to_idx={word:i for i,word in enumerate(vocb)}
idx_to_word={word_to_idx[word]:word for word in word_to_idx}
print(word_to_idx)
"""
{"'This": 94,
'And': 71,
'How': 18,
'If': 49,
'Proving': 78,
'Shall': 48,
'Then': 33,
'This': 68,
'Thy': 75,
'To': 81,
'Were': 61,
'When': 14,
'Where': 95,
'Will': 27,
'a': 21,
'all': 53,
'all-eating': 3,
'an': 15,
'and': 23,
'answer': 80,
'art': 70,
'asked,': 69,
'be': 29,
'beauty': 16,
"beauty's": 40,
'being': 79,
'besiege': 55,
'blood': 11,
'brow,': 1,
'by': 59,
'child': 8,
'cold.': 32,
'couldst': 26,
'count,': 77,
'days;': 43,
'deep': 62,
"deserv'd": 41,
'dig': 64,
"excuse,'": 86,
'eyes,': 84,
'fair': 56,
"feel'st": 44,
'field,': 9,
'forty': 46,
'gazed': 93,
'held:': 12,
'his': 89,
'in': 45,
'it': 34,
'lies,': 57,
'livery': 28,
'lusty': 65,
'made': 54,
'make': 42,
'mine': 13,
'more': 83,
'much': 30,
'my': 50,
'new': 92,
'now,': 25,
'of': 47,
'old': 22,
'old,': 19,
'on': 74,
'own': 20,
'praise': 38,
'praise.': 96,
'proud': 5,
'say,': 63,
'see': 58,
'shall': 87,
'shame,': 90,
'small': 31,
'so': 67,
'succession': 36,
'sum': 10,
'sunken': 60,
'the': 73,
'thine': 24,
'thine!': 0,
'thou': 51,
'thriftless': 72,
'thy': 76,
'to': 85,
"totter'd": 2,
'treasure': 17,
'trenches': 39,
'use,': 35,
'warm': 66,
'weed': 91,
'were': 82,
'when': 7,
'where': 37,
'winters': 88,
'within': 4,
'worth': 52,
"youth's": 6}
"""
"""
从上面可看出每个词都对应一个数字,且这里的单词都各不相同
接下来定义模型,模型输入就是前面两个词,输出就是预测单词的概率
"""
#定义模型
class n_gram(nn.Module):
def __init__(self,vocab_size,context_size=CONTEXT_SIZE,n_dim=EMBEDDING_DIM):
super(n_gram, self).__init__()
self.embeds=nn.Embedding(vocab_size,n_dim)
self.classify=nn.Sequential(
nn.Linear(context_size*n_dim,128),
nn.ReLU(),
nn.Linear(128,vocab_size)
)
def forward(self,x):
voc_embed=self.embeds(x)#得到词嵌入
voc_embed=voc_embed.view(1,-1)#将两个词向量拼在一起
out=self.classify(voc_embed)
return out
"""
最后输出条件概率,相当于是一个分类问题,可以使用交叉熵来方便地衡量误差
"""
net=n_gram(len(word_to_idx))
criterion=nn.CrossEntropyLoss()
optimizer=torch.optim.SGD(net.parameters(),lr=1e-2,weight_decay=1e-5)
for e in range(100):
train_loss=0
for word,label in trigram:#使用前100个作为数据集
word=Variable(torch.LongTensor([word_to_idx[i] for i in word]))#将前两个词作为输入,((2个单词),1个单词)
label=Variable(torch.LongTensor(word_to_idx[label]))
#前向传播
out=net(word)
loss=criterion(out,label)
train_loss+=loss.data[0]
#反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (e+1)%20==0:
print('epoch:{},Loss:{}'.format(e+1,train_loss/len(trigram)))
net=net.eval()
#测试结果
word,label=trigram[19]
print('input:{}'.format(word))
print('label:{}'.format(label))
print()
word=Variable(torch.LongTensor(word_to_idx[i] for i in word))
out=net(word)
pred_label_idx=out.max(1)[1].data[0]
predict_word=idx_to_word[pred_label_idx]
print('real word is {},predicted word is {}'.format(label,predict_word))
"""
input: ('so', 'gazed')
label: on
real word is on, predicted word is on
"""
word, label = trigram[75]
print('input: {}'.format(word))
print('label: {}'.format(label))
print()
word = Variable(torch.LongTensor([word_to_idx[i] for i in word]))
out = net(word)
pred_label_idx = out.max(1)[1].data[0]
predict_word = idx_to_word[pred_label_idx]
print('real word is {}, predicted word is {}'.format(label, predict_word))
"""
input: ("'This", 'fair')
label: child
real word is child, predicted word is child
"""
"""
这里样本过少,容易发生过拟合现象
"""
LSTM做词性预测
接着把这个单词和其前面几个单词构成序列,可以对这些单词构建新的词嵌入,最后输出结果是单词的词性,也就是根据前面几个词的词性进行分类。入上图右
pytorch实现
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)
print(word_to_idx)
"""
{'apple': 3,
'ate': 2,
'book': 7,
'dog': 1,
'everybody': 4,
'read': 5,
'that': 6,
'the': 0}
"""
print(tag_to_idx)
"""
{'det': 0, 'nn': 1, 'v': 2}
"""
#然后我们对字母进行编码
alphabet='abcdefghijklmnopqrstuvwxyz'
char_to_idx={}
for i in range(alphabet):
char_to_idx[alphabet[i]]=i
print(char_to_idx)
"""
{'a': 0,
'b': 1,
'c': 2,
'd': 3,
'e': 4,
'f': 5,
'g': 6,
'h': 7,
'i': 8,
'j': 9,
'k': 10,
'l': 11,
'm': 12,
'n': 13,
'o': 14,
'p': 15,
'q': 16,
'r': 17,
's': 18,
't': 19,
'u': 20,
'v': 21,
'w': 22,
'x': 23,
'y': 24,
'z': 25}
"""
#构建训练数据
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)#apple在字母表中对应的标签
"""
0
15
15
11
4
[torch.LongTensor of size 5]
"""
training_data[1][0]
"""
['Everybody', 'read', 'that', 'book']
"""
make_sequence(training_data[1][0],word_to_idx)
"""
4
5
6
7
[torch.LongTensor of size 4]
"""
#构建单个字符的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)#(input_size,hiddem_size,num_layers)
def forward(self,x):
x=self.char_embed(x)
out,_=self.lstm(x)
return out[-1]#(batch,hidden_size)
#构建词性分类的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_len,batch,char_dim)满足lstm输入条件
char_info=self.char_lstm(Variable(char_list))#(batch,char_hidden)
char.append(char_info)#(len(word)个)
char=torch.stack(char,dim=0)#(seq, batch, feature)
x=self.word_embed(x)#(batch,seq_len,word_dim)
x=x.permute(1,0,2)#(seq_len,batch,word_dim)改变顺序
x=torch.cat((x,char),dim=2)#沿着特征通道将没歌词的词嵌入和字符lstm输出的结果拼接在一起
x,_=self.word_lstm(x)
s,b,h=x.shape
x=x.view(s*b,h)
out=self.classify(x)
return out
net=lstm_tagger(len(word_to_idx),len(char_to_idx),char_dim=10,word_dim=100,
char_hidden=50,word_hidden=128,n_tag=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,seq_len,word_dim)
tag=make_sequence(tag,tag_to_idx)
word=Variable(word)
tag=Variable(tag)
#前向传播
out=net(word)
loss=criterion(word,tag)
train_loss+=loss
#反向传播
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()#(batch,seq_len,word_dim)
out=net(Variable(test))
print(out)#(n_tag,_)
"""
Variable containing:
-1.2148 1.9048 -0.6570
-0.9272 -0.4441 1.4009
1.6425 -0.7751 -1.1553
-0.6121 1.6036 -1.1280
[torch.FloatTensor of size 4x3]
"""
print(tag_to_idx)
"""
{'det': 0, 'nn': 1, 'v': 2}
"""
"""
因为最后一层的线性层没有使用softmax,所以数值不太像个概率,但是每一行数值最大的就表示该类。
"""