bert pytorch 序列标注_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。

importtorch

fromtorch importautograd

fromtorchvision importdatasets,transforms

importtorch.utils.data asdata

classPosTaggerData(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 = {}

forsent, tags inself.training_data:

forword insent:

ifword not inself.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):

returnlen(self.items)

def__getitem__(self,idx):

returnself.items[idx]

defgen_all_data(self):

self.items = []

forsent,tags inself.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))

defprepare_sequence(self,seq, to_ix):

idxs = [to_ix[w] forw inseq]

tensor = torch.LongTensor(idxs)

returntensor

lstm的网络结构一般都很简约,2层就了不起了,这个模型,我们用一层就好了。

BATCH_SIZE = 1classLSTMPosTagger(nn.Module):

def__init__(self):

super(LSTMPosTagger,self).__init__()

self.vocab_size = 9self.embedding_size = 6self.lstm_hidden_size = 6self.target_size = 3self.word_embedding = nn.Embedding(self.vocab_size,self.embedding_size)#词嵌入层 input:seq_len,N , output:seqlen,N,embedding_sizeself.lstm = nn.LSTM(self.embedding_size,self.lstm_hidden_size)# seq_len,N,hidden_sizeself.fully = nn.Linear(self.lstm_hidden_size,self.target_size)

self.hidden = self.init_hidden()

definit_hidden(self):

return(Variable(torch.zeros(1,BATCH_SIZE,self.lstm_hidden_size)),Variable(torch.zeros(1,BATCH_SIZE,self.lstm_hidden_size)))

defforward(self,input):

embedded = self.word_embedding(input)

lstm_out,self.hidden = self.lstm(embedded,self.hidden)#seq_len,N,hidden_sizetag_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)

returntag_score

然后是训练:

model = LSTMPosTagger()

optimizer = optim.SGD(model.parameters(), lr=0.1)

loss_function = nn.NLLLoss()

fromaipack.utils.data_utils importPosTaggerData

tagger = PosTaggerData()

forepoch inrange(300):

print('===========epoch===========',epoch)

#for data,target in tagger.items:fordata, target intagger.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)

你可能感兴趣的:(bert,pytorch,序列标注)