利用LSTM判断词性(基于pytorch实现)

利用LSTM判断词性
给出一句话,判断里面的每个单词是什么类型。名词、代词、副词等等。

import torch
from torch import nn
from torch.autograd import Variable

# 给出两句话作为训练集,每个单词给出词性
train_data = [
    ("The dog ate the apple".split(), ["DET", "NN", "V", "DET", "NN"]),
    ("Everybody read that book".split(), ["NN", "V", "DET", "NN"])
]
print('======train_data=====')
print(train_data)

运行结果
在这里插入图片描述

# 对单词给出数字编码,以便传入Embedding中转化成词向量
word_to_idx = {}
tag_to_idx = {}

for context, tag in train_data:  # context时句子,tag是后面的词性[]里面的一长串
    for word in context:  # 给每个单词编号
        if word.lower() not in word_to_idx:
            word_to_idx[word.lower()] = len(word_to_idx)  # lower()函数,将字符串中的所有大写字母转换为小写字母。
    for label in tag:  # 给每个词性标label,以及编号
        if label.lower() not in tag_to_idx:
            tag_to_idx[label.lower()] = len(tag_to_idx)
#  定义数字和tag的字典,方便测试时使用,能够通过查找数字找到tag
idx_to_tag = {tag_to_idx[tag.lower()]: tag for tag in tag_to_idx}

# 对a-z的字符进行数字编码
alphabet = 'abcdefghijklmnopqrstuvwxyz'
character_to_idx = {}
for i in range(len(alphabet)):
    character_to_idx[alphabet[i]] = i
# 这三个编码之后,用字典这种容器存储,每个元素对应一个数字编号
print('=====字符对应数字=====')
print(tag_to_idx)
print(idx_to_tag)
print(word_to_idx)
print(character_to_idx)

运行结果
在这里插入图片描述


# 字符编码,将传入字符x中对应的编码,转化成LongTensor类型
def make_sequence(x, dic):
    idx = [dic[i.lower()] for i in x]
    idx = torch.LongTensor(idx)
    return idx


print('=====''make_sequence()函数输出结果查看''=====')
print(make_sequence('abcdef', character_to_idx).type())
print(make_sequence('abcdef', character_to_idx).size())
print(make_sequence('abcdef', character_to_idx))
print(make_sequence(train_data[0][0], word_to_idx))

运行结果
在这里插入图片描述


# 定义字母字符的LSTM
class char_lstm(nn.Module):
    def __init__(self, n_char, char_dim, char_hidden):
        super(char_lstm, self).__init__()
        self.char_embedding = nn.Embedding(n_char, char_dim)
        self.char_lstm = nn.LSTM(char_dim, char_hidden)

    def forward(self, x):
        x = self.char_embedding(x)
        out, _ = self.char_lstm(x)
        return out[-1]  # (batch, hidden_size)


# 定义词性分析的LSTM
class lstm_tagger(nn.Module):
    # n_word:单词的数目,n_dim:单词向量维度,n_char和char_dim同理,char_hidden:字母LSTM的输出维度,
    # n_hidden:单词词性预测LSTM的输出维度,n_tag:输出的词性分类
    def __init__(self, n_word, n_char, char_dim, n_dim, char_hidden, n_hidden, n_tag):
        super(lstm_tagger, self).__init__()
        self.word_embedding = nn.Embedding(n_word, n_dim)
        self.char_lstm = char_lstm(n_char, char_dim, char_hidden)
        self.lstm = nn.LSTM(n_dim + char_hidden, n_hidden)  # 词性分析LSTM输入:词向量维度数+字符LSTM输出维度数
        self.classify = nn.Linear(n_hidden, n_tag)

    # 字符增强,传入句子的同时作为序列的同时,还要传入句子中的单词,用word表示
    def forward(self, x, word):
        char = []
        for w in word:  # 对于每个单词,做字母字符的lstm
            char_list = make_sequence(w, character_to_idx)
            char_list = char_list.unsqueeze(1)  # (seq, batch, feature) 满足 lstm 输入条件
            # unsqueeze(1)在第二维度上增加一个维度
            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_embedding(x)  # (batch, seq, word_dim)
        x = x.permute(1, 0, 2)  # 改变顺序,变成(seq, batch, word_dim)
        x = torch.cat((x, char), dim=2)  # 沿着特征通道将每个词的词嵌入和字符 lstm 输出的结果拼接在一起
        x, _ = self.lstm(x)

        seq, batch, h = x.shape
        x = x.view(-1, h)  # 重新 reshape 进行分类线性层
        out = self.classify(x)
        return out


net = lstm_tagger(len(word_to_idx), len(character_to_idx), 10, 100, 50, 128, len(tag_to_idx))
# (n_word, n_char, char_dim, n_dim, char_hidden, n_hidden, n_tag)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(net.parameters(), lr=1e-2)

print('=====开始训练=====')
# 开始训练
for e in range(500):
    train_loss = 0
    for word, tag in train_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.item()
        # 反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    if (e + 1) % 50 == 0:
        print('Epoch: {}, Loss: {:.5f}'.format(e + 1, train_loss / len(train_data)))

运行结果
在这里插入图片描述

# 看看预测的结果
print('=====测试阶段=====')
net = net.eval()

# test_sent = 'Everybody ate the apple'
test_sent = 'Everybody ate the apple read the book'
test = make_sequence(test_sent.split(), word_to_idx).unsqueeze(0)
test_set = test_sent.split()
out = net(Variable(test), test_set)
print('=====out的tensor类型输出======')
print(out)
print('out的size是: ', out.size())  # 输入的test_sent是7个单词,单词词有三种:det,nn,v,所以结果是torch.Size([7, 3])
print('每一行tensor的三个值代表着:', tag_to_idx)
# 最后可以得到一个7x3的tensor,因为最后一层的线性层没有使用 softmax,所以数值不太像一个概率,
# 但是每一行数值最大的就表示属于该类,可以看到第一个单词 'Everybody' 属于 nn,
# 第二个单词 'ate' 属于 v,第三个单词 'the' 属于det,第四个单词 'apple' 属于 nn,
# 所以得到的这个预测结果是正确的

运行结果
在这里插入图片描述

# pred_tag_idx = out[0].argmax().item()
# pred_word = idx_to_tag[pred_tag_idx]
# print('The word is: ', test_set[0], '. And the predicted word is: ', idx_to_tag[pred_tag_idx])

print('=====.item()方法的作用=====')
pred_tag = out[0].argmax()
print('*****不使用.item()时*****')
print('变量类型type:', type(pred_tag))
print('变量值:', pred_tag)
print('*****使用.item()后*****')
print('变量类型type:',type(pred_tag.item()))
print('变量值:', pred_tag.item())

运行结果
在这里插入图片描述

print('=====测试结果=====')
for i in range(len(test_set)):
    pred_tag_idx = out[i].argmax().item()
    # out[i]表示out这个tensor的第i行,
    # argmax()找出这一行最大值所在的位置,
    # .item()方法将tensor类型的pred_tag_idx变为int类型,才可以用于字典查询索引
    pred_word = idx_to_tag[pred_tag_idx]
    print('The word is: ', test_set[i], '. And the predicted word is: ', idx_to_tag[pred_tag_idx])

运行结果
在这里插入图片描述

tips:pytorch动态图,目前神经网络框架分为静态图框架和动态图框架, TensorFlow 使用静态图,这意味着我们先定义计算图,然后不断使用它,而在 PyTorch 中,每次都会重新构建一个新的计算图,称之为动态图结构。

pytorch中函数总结:
unsqueeze()函数
增加tensor维度
unsqueeze(0)就是在第一维度增加一个维度
unsqueeze(1)就是在第二维度增加一个维度

squeeze()函数
减少tensor维度
squeeze(0)就是在第一维度减少一个维度
squeeze(1)就是在第二维度减少一个维度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值