当前位置:   article > 正文

词性判断(LSTM)_you没有一个对于英文单词词性的判断算法

you没有一个对于英文单词词性的判断算法


参考:《深度学习入门之Pytorch》

基本原理

定义好一个LSTM网络,然后给出一个由很多个词构成的句子,每个词可以用一个词向量表示,这样一句话就可以看做是一个序列,序列中的每个元素都是一个高维向量,将这个序列传入LSTM,可以得到与序列等长的输出,每个输出都表示为对词性的判断,比如名词、动词等。从本质上看,这是一个分类问题,虽然使用了LSTM,但实际上是根据这个词前面的一些词来对它进行分类,看它是属于几种词性中的哪一种。如果完全孤立地对一个词做词性的判断,往往无法得到比较准确的结果,但是通过LSTM,根据它记忆的特性,就能够通过这个单词前面记忆的一些词语来对它做一个判断,比如前面的单词如果是my,那么它紧跟的词很有可能就是一个名词,这样就能够充分地利用上文来处理这个问题。

字符增强

还可以通过引入字符来增强表达,这是什么意思呢?就是说一些单词存在着前缀或者后缀,比如-ly这种后缀很可能是一个副词,这样我们就能够在字符水平上对词性进行进一步判断,把两种方法集成起来,能够得到一个更好的结果。**在实现上还是用LSTM,只是这次不再将句子作为一个序列,而是将每个单词作为一个序列。**每个单词由不同的字母组成,比如apple由a p p l e构成,给这些字符建立词向量,形成了一个长度为5的序列,将它传入LSTM网络,只取最后输出的状态层作为它的一种字符表达,不需要关心提取出来的字符表达到底是什么样,它作为一种抽象的特征,能够更好地预测结果
接着我们把这个单词和其前面几个单词构成序列,可以对这些单词构建新的词嵌入,最后输出结果是单词的词性,也就是根据前面几个词的信息对这个词的词性进行分类。

代码

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)
print(tag_to_idx)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在这里插入图片描述

#然后我们对字母进行编码
alphabet = 'abcdefghijklmnopqrstuvwxyz'
char_to_idx = {}
for i in range(len(alphabet)):
    char_to_idx[alphabet[i]] = i
print(char_to_idx)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在这里插入图片描述

#构建训练数据
def make_sequence(x, dic): # 字符编码
    idx = [dic[i.lower()] for i in x]
    idx = torch.LongTensor(idx)
    return idx

#构建单个字符的 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)
        
    def forward(self, x):
        x = self.char_embed(x)
        out, _ = self.lstm(x)
        return out[-1] # (batch, hidden)
#构建词性分类的 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, batch, feature) 满足 lstm 输入条件
            char_infor = self.char_lstm(Variable(char_list)) # (batch, char_hidden)
            char.append(char_infor)
        char = torch.stack(char, dim=0) # (seq, batch, feature)
        
        x = self.word_embed(x) # (batch, seq, word_dim)
        x = x.permute(1, 0, 2) # 改变顺序
        x = torch.cat((x, char), dim=2) # 沿着特征通道将每个词的词嵌入和字符 lstm 输出的结果拼接在一起
        x, _ = self.word_lstm(x)
        
        s, b, h = x.shape
        x = x.view(-1, h) # 重新 reshape 进行分类线性层
        out = self.classify(x)
        return out

net = lstm_tagger(len(word_to_idx), len(char_to_idx), 10, 100, 50, 128, 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
        tag = make_sequence(tag, tag_to_idx)
        word_list = Variable(word_list)
        tag = Variable(tag)
        # 前向传播
        out = net(word_list, word)
        loss = criterion(out, 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(0)
out = net(Variable(test), test_sent.split())

print(out)
print(tag_to_idx)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

在这里插入图片描述
最后可以得到上面的结果,因为最后一层的线性层没有使用 softmax,所以数值不太像一个概率,但是每一行数值最大的就表示属于该类,可以看到第一个单词 ‘Everybody’ 属于 nn,第二个单词 ‘ate’ 属于 v,第三个单词 ‘the’ 属于det,第四个单词 ‘apple’ 属于 nn,所以得到的这个预测结果是正确的.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/794567
推荐阅读
相关标签
  

闽ICP备14008679号