pytorch实现part-of-speech(POS)序列标注
Posted AI量化实验室
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pytorch实现part-of-speech(POS)序列标注相关的知识,希望对你有一定的参考价值。
深度学习最迷人的地方在于,它基础概念极简,我们很容易理解的线性变换,说白了,就是y=ax+b,换成矩阵就是y=x.W +b。然后加一个非线性的激活函数,比如logistic,relu等,就构成了一个基本的神经信号单元。但它的内涵和外延变化都是近乎无穷的。首先参数矩阵从维度,初始化是任意的,网络的层数是任意的,还是网络的连接方式也是任意的,激活函数也是可以更换的。这就有无穷种可能性。
传统的机器学习,就像我们设计的软件程序一样,是为某种特定的任务服务的。比如SVM就是向量分类,你让它搞序列预测,就要转成分类问题。如果你想让它做序列标注,那是做不到的,因为它只关心向量本身,向量元素之间的顺序它就无能为力。决策树,HMM,CRF都类似。天下武功招式这么多,是学不完的。
神经网络不一样,你加一个卷积层,它就可以看图片,你加一个RNN层,它就记忆序列。这很像人脑,这种感觉很棒,而且它们还能联合起来,做很多很酷的事情,能看,能听,能下棋,能无人驾驶。
自然语言处理基本任务里,有一个任务叫词性标注(Part-of-speech POS),就是把句子里的词,分成名词,动词,副词等。这个看似简单,但其实真的很难。一个词可以有多种词性,出现在不同的句子位置,词性会不一样。这就是一个序列标注问题,传统最好的方法是CRF(条件随机场),计算序列出现的全局最大似然率。
下面我们用一层LSTM来实现这个模型。
我们自己“造”一些数据。就两句话,我们给标注好,最后按下标生成tensor。
import torch
from torch import autograd
from torchvision import datasets,transforms
import torch.utils.data as data
class PosTaggerData(data.Dataset):
def __init__(self):
self.training_data = [
("The dog ate the apple".split(), ["DET", "NN", "V", "DET", "NN"]),
("Everybody read that book".split(), ["NN", "V", "DET", "NN"])
]
self.word_to_ix = {}
for sent, tags in self.training_data:
for word in sent:
if word not in self.word_to_ix:
self.word_to_ix[word] = len(self.word_to_ix)
print(self.word_to_ix)
self.tag_to_ix = {"DET": 0, "NN": 1, "V": 2}
print(self.tag_to_ix)
self.gen_all_data()
def __len__(self):
return len(self.items)
def __getitem__(self,idx):
return self.items[idx]
def gen_all_data(self):
self.items = []
for sent,tags in self.training_data:
data = self.prepare_sequence(sent,self.word_to_ix)
targets = self.prepare_sequence(tags,self.tag_to_ix)
print('data:',data)
print('targets:',targets)
self.items.append((data,targets))
def prepare_sequence(self,seq, to_ix):
idxs = [to_ix[w] for w in seq]
tensor = torch.LongTensor(idxs)
return tensor
lstm的网络结构一般都很简约,2层就了不起了,这个模型,我们用一层就好了。
BATCH_SIZE = 1
class LSTMPosTagger(nn.Module):
def __init__(self):
super(LSTMPosTagger,self).__init__()
self.vocab_size = 9
self.embedding_size = 6
self.lstm_hidden_size = 6
self.target_size = 3
self.word_embedding = nn.Embedding(self.vocab_size,self.embedding_size)#词嵌入层 input:seq_len,N , output:seqlen,N,embedding_size
self.lstm = nn.LSTM(self.embedding_size,self.lstm_hidden_size)# seq_len,N,hidden_size
self.fully = nn.Linear(self.lstm_hidden_size,self.target_size)
self.hidden = self.init_hidden()
def init_hidden(self):
return (Variable(torch.zeros(1,BATCH_SIZE,self.lstm_hidden_size)),Variable(torch.zeros(1,BATCH_SIZE,self.lstm_hidden_size)))
def forward(self,input):
embedded = self.word_embedding(input)
lstm_out,self.hidden = self.lstm(embedded,self.hidden)#seq_len,N,hidden_size
tag_space = self.fully(lstm_out.view(-1,self.lstm_hidden_size))# [seq_len*N,hidden_size] -> [seq_len*N,target_size]
tag_score = F.log_softmax(tag_space)
return tag_score
然后是训练:
model = LSTMPosTagger()
optimizer = optim.SGD(model.parameters(), lr=0.1)
loss_function = nn.NLLLoss()
from aipack.utils.data_utils import PosTaggerData
tagger = PosTaggerData()
for epoch in range(300):
print('===========epoch===========',epoch)
#for data,target in tagger.items:
for data, target in tagger.items:
model.zero_grad()
model.hidden = model.init_hidden()
data = Variable(data)
target = Variable(target)
tag_scores = model(data)
# Step 4. Compute the loss, gradients, and update the parameters by
# calling optimizer.step()
loss = loss_function(tag_scores, target)
loss.backward()
optimizer.step()
print(loss.data[0])
对面构架一个模型,主要是维度要对齐,这是由矩阵乘法所决定的。
关于维度的要点:
1,我们input的维度是(seq_len,batch_size),就是一个列向量,或者说[seq_len,1]
2, embedding层,把[seq_len,1,embedding_size]
3, lstm层,[seq_len,1,hidden_size]
4, 在全连接层之前,有一个降维的过程,就是[seq_len*1,hidden_size] ->[seq_len*1,target_size]
# 训练之后我们看下成果
inputs = Variable(tagger.items[0][0])
tag_scores = model(inputs)
# 句子 "the dog ate the apple".
# 我们看到预测的结果序列为 0 1 2 0 1
# 就是DET NOUN VERB DET NOUN, 100%预测正确!
print('tag_scores',tag_scores)
扫描下方二维码,关注:AI量化实验室(ailabx),了解AI量化最前沿技术、资讯。
以上是关于pytorch实现part-of-speech(POS)序列标注的主要内容,如果未能解决你的问题,请参考以下文章