- 地址:https://github.com/fendouai/PyTorchDocs/blob/master/FifthSection/Sequence_and_LSTM_Network.md
- 主要分为:训练数据的预处理、神经网络的设计、损失函数的选择,训练、测试
训练数据的预处理、
- 创建一个word_to_ix
训练数据是有监督的,将句子切分为列表,每个单词对应一个词性。
training_data = [
("The dog ate the apple".split(), ["DET", "NN", "V", "DET", "NN"]), # 前边是单词,后边是词性
("Everybody read that book".split(), ["NN", "V", "DET", "NN"])
]
word_to_ix = {}
for sent, tags in training_data:
for word in sent:
if word not in word_to_ix:
word_to_ix[word] = len(word_to_ix)
print(word_to_ix)
创建的word_to_ix:
{'The': 0, 'dog': 1, 'ate': 2, 'the': 3, 'apple': 4, 'Everybody': 5, 'read': 6, 'that': 7, 'book': 8}
神经网络的搭建
- 这里用的是LSTM
# 实际中通常使用更大的维度如32维, 64维.
# 这里我们使用小的维度, 为了方便查看训练过程中权重的变化.
EMBEDDING_DIM = 6
HIDDEN_DIM = 6
class LSTMTagger(nn.Module):
def __init__(self, embedding_dim, hidden_dim, vocab_size, tagset_size):
super(LSTMTagger, self).__init__()
self.hidden_dim = hidden_dim
self.word_embeddings = nn.Embedding(vocab_size, embedding_dim)
# LSTM以word_embeddings作为输入, 输出维度为 hidden_dim 的隐藏状态值
self.lstm = nn.LSTM(embedding_dim, hidden_dim)
# 线性层将隐藏状态空间映射到标注空间
self.hidden2tag = nn.Linear(hidden_dim, tagset_size)
self.hidden = self.init_hidden()
def init_hidden(self):
# 一开始并没有隐藏状态所以我们要先初始化一个
# 各个维度的含义是 (num_layers, minibatch_size, hidden_dim)
return (torch.zeros(1, 1, self.hidden_dim),
torch.zeros(1, 1, self.hidden_dim))
def forward(self, sentence):
embeds = self.word_embeddings(sentence)
lstm_out, self.hidden = self.lstm(
embeds.view(len(sentence), 1, -1), self.hidden)
tag_space = self.hidden2tag(lstm_out.view(len(sentence), -1))
tag_scores = F.log_softmax(tag_space, dim=1)
return tag_scores
初始化神经网络
model = LSTMTagger(EMBEDDING_DIM, HIDDEN_DIM, len(word_to_ix), len(tag_to_ix))
损失函数选择
loss_function = nn.NLLLoss()
optimizer = optim.SGD(model.parameters(), lr=0.1)
训练
- 先准备一个将词或者词性转换为tensor的函数
def prepare_sequence(seq, to_ix):
idxs = [to_ix[w] for w in seq]
return torch.tensor(idxs, dtype=torch.long)
- 然后开始训练
for epoch in range(300):
for sentence , tags in training_data:
# 清空梯度
model.zero_grad()
#情空lstm历史状态
model.hidden = model.init_hidden()
#准备网络输入,将其变为词索引的tensor类型数据
sentence_in = prepare_sequence(sentence, word_to_ix)
targets = prepare_sequence(tags, tag_to_ix)
# 向前传播
tag_scores = model(sentence_in)
# 第四步,损失函数。更细梯度
loss = loss_function(tag_scores, targets)
# 反向传播
loss.backward()
# 更新参数
optimizer.step()
测试效果
with torch.no_grad():
inputs = prepare_sequence(training_data[0][0], word_to_ix)# S输入测试样例,The dog ate the apple
tag_scores = model(inputs)
print(tag_scores) # 查看得分,最高的下标是 0 1 2 0 1
句子是 "the dog ate the apple", i,j 表示对于单词 i, 标签 j 的得分.
我们采用得分最高的标签作为预测的标签. 从下面的输出我们可以看到, 预测得
到的结果是0 1 2 0 1. 因为 索引是从0开始的, 因此第一个值0表示第一行的
最大值, 第二个值1表示第二行的最大值, 以此类推. 所以最后的结果是 DET
NOUN VERB DET NOUN, 整个序列都是正确的!
- 输出的结果,可以发现概率最大的下标为 0 1 2 0 1
tensor([[-0.0726, -2.9010, -4.1978],
[-4.0754, -0.1101, -2.4391],
[-3.4370, -2.0435, -0.1764],
[-0.0322, -4.1292, -4.1580],
[-4.2778, -0.0268, -4.3764]])