《Pytorch - RNN模型》

前言:
之前的博文部分地讲解了RNN的标准结构,也用pytorch的RNNCell类和RNN类实现了前向传播的计算,这里我们再举一个例子,做一个特别简单特别简单特别简单特别简单的翻译器,目标如下:
将英文hello and thank you翻译成汉语拼音ni hao qie duo xie ni

篇幅有限,我们拿这五个数据练手吧。
如下两个例子也是为了让我们学习一下RNN相关维度的概念。

一:使用RNNCell类实现简单的字符串翻译
这里我们需要用到one-hot vector技术,也就是我们建立一个字符表,包含空格字符和26个小写英文字符,也就是一共有27个字符的字典表,空格符号标号是0,az编号是126,我们定义有22个序列的输入(也就是最多一次能接受22个字符的输入),每个输入的x 也是27维度的向量,输出层也是27维度的,也就是多个分类的问题,用RNN实现。代码如下:

import torch
import numpy as np
from torch.autograd import Variable

batch_size = 1      # 批量数据的大小,数据的批次,比如这里有5个需要测试的单词。
seq_len = 22    # 一共是多少个时间序列,是指时间的维度,在这里的意思就是一次性最大能接受20个字符的输入。
input_size = 27     # 某一时刻下,输入的X的维度向量,比如这里的4指的是维度是[4, 1]
hidden_size = 27    # 每一层的激活后 h 的维度,比如这里的2指的是维度是[2, 1]

# num_layers = 1

# a~z标号是1~26, 空格是1,这个字典表,每个字符有个对应的编号,也就是下标就是编号
idx2char = [' ', # 0
            'a', 'b', 'c', 'd', 'e', 'f', 'g', # 1-7
            'h', 'i', 'j', 'k', 'l', 'm', 'n', # 8-14
            'o', 'p', 'q', 'r', 's', 't', # 15-20
            'u', 'v', 'w', 'x', 'y', 'z'] # 21-26
idx2char = np.array(idx2char)
# 每个编号有个对应的向量,也就是下标对应的向量
one_hot_look = np.array(np.eye(idx2char.shape[0])).astype(int)
print('idx2char.shape[0]=', idx2char.shape[0])

x_data = np.zeros((batch_size, seq_len)).astype(int)
y_data = np.zeros((batch_size, seq_len)).astype(int)

# 待翻译的句子
x_data_char = ['hello and thank you']
y_data_char = ['ni hao qie duo xie you']

# 填充每个字符的下标
i=0
for item in x_data_char:
    # fill each data
    j=0
    for letter in item:
        x_data[i][j] = np.where(idx2char == letter)[0][0]
        j = j + 1
    i = i + 1

i=0
for item in y_data_char:
    # fill each data
    j=0
    for letter in item:
        y_data[i][j] = np.where(idx2char == letter)[0][0]
        j = j + 1
    i = i + 1

#print(x_data)
#print(y_data)




# Step 2:============================定义一个RNNCell的模型===================
class RNNCellModel(torch.nn.Module):
    def __init__(self, input_size, hidden_size):
        super(RNNCellModel, self).__init__()
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.rnncell = torch.nn.RNNCell(input_size=self.input_size, hidden_size=self.hidden_size)

    def forward(self, input, hidden):
        hidden = self.rnncell(input, hidden)
        return hidden

model = RNNCellModel(input_size, hidden_size)



# Step 3:============================定义损失函数和优化器===================
# 定义 loss 函数,这里用的是交叉熵损失函数(Cross Entropy),这种损失函数之前博文也讲过的。
criterion = torch.nn.CrossEntropyLoss()
# 我们优先使用Adam下降,lr是学习率: 0.1
optimizer = torch.optim.Adam(model.parameters(), 1e-1)





# Step 4:============================开始训练===================
for e in range(200):
    loss = 0
    optimizer.zero_grad()
    hidden = torch.zeros(batch_size, hidden_size)  # 随机初始化的h0,且必须是Tensor类型的
    i = 0
    print('e============================')
    print('predicted str: ', end='')
    for cur_seq in zip(*x_data):   # for each seq
        cur_seq = np.array(cur_seq)
        #input 必须是Tensor类型的
        input = torch.Tensor([one_hot_look[x] for x in cur_seq])
        # print(input.shape)
        # y_label 必须是长整数型,且必须是Tensor类型的
        y_label = torch.LongTensor(np.array(y_data[:, i]))
        #print('real y_label: ', idx2char[y_label.item()], end='')

        # 前向传播
        hidden = model(input, hidden)
        _, idx = hidden.max(dim=1)
        print(idx2char[idx.item()], end='')
        # print(hidden)
        # 累加损失
        loss += criterion(hidden, y_label)

        i = i+1

    loss.backward()
    optimizer.step()
    print(',epoch [%d/200] loss=%.4f' % (e+1, loss.item()), end='')

输出结果如下:训练的字符串是hello and thank you

在这里插入图片描述

这个模型基本就差不多了训练出来是 ni hao qie duo xie ni

二:使用RNN类实现简单的字符串翻译
或者还可以用RNN模型来做,这样省去了自己写循环迭代序列的过程
,这里需要注意模型的建立需要传入层数,以及需要传递batch_size,因为hidden节点需要这个参数。

import torch
import numpy as np
from torch.autograd import Variable

batch_size = 1      # 批量数据的大小,数据的批次,比如这里有5个需要测试的单词。
seq_len = 22    # 一共是多少个时间序列,是指时间的维度,在这里的意思就是一次性最大能接受22个字符的输入。
input_size = 27     # 某一时刻下,输入的X的维度向量,比如这里的4指的是维度是[4, 1]
hidden_size = 27    # 每一层的激活后 h 的维度,比如这里的2指的是维度是[2, 1]
num_layers = 3      # 共有三层RNN

# a~z标号是1~26, 空格是1,这个字典表,每个字符有个对应的编号,也就是下标就是编号
idx2char = [' ', # 0
            'a', 'b', 'c', 'd', 'e', 'f', 'g', # 1-7
            'h', 'i', 'j', 'k', 'l', 'm', 'n', # 8-14
            'o', 'p', 'q', 'r', 's', 't', # 15-20
            'u', 'v', 'w', 'x', 'y', 'z'] # 21-26
idx2char = np.array(idx2char)
# 每个编号有个对应的向量,也就是下标对应的向量
one_hot_look = np.array(np.eye(idx2char.shape[0])).astype(int)
print('idx2char.shape[0]=', idx2char.shape[0])

x_data = np.zeros((batch_size, seq_len)).astype(int)
y_data = np.zeros((batch_size, seq_len)).astype(int)

# 待翻译的句子
x_data_char = ['hello and thank you']
y_data_char = ['ni hao qie duo xie you']

# 填充每个字符的下标
i=0
for item in x_data_char:
    # fill each data
    j=0
    for letter in item:
        x_data[i][j] = np.where(idx2char == letter)[0][0]
        j = j + 1
    i = i + 1

i=0
for item in y_data_char:
    # fill each data
    j=0
    for letter in item:
        y_data[i][j] = np.where(idx2char == letter)[0][0]
        j = j + 1
    i = i + 1

#print(x_data)
#print(y_data)
x_one_hot = [one_hot_look[x] for x in x_data]
inputs = torch.Tensor(x_one_hot).view(seq_len, batch_size, input_size)
lables = torch.LongTensor(y_data).view(-1)



# Step 2:============================定义一个RNNCell的模型===================
class RNNModel(torch.nn.Module):
    def __init__(self, input_size, hidden_size, batch_size, num_layers):
        super(RNNModel, self).__init__()
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.batch_size = batch_size
        self.num_layers = num_layers
        self.rnn = torch.nn.RNN(input_size=self.input_size, hidden_size=self.hidden_size,
                                num_layers=num_layers)

    def forward(self, input):
        # 由于不需要自己写序列的迭代,因此直接将随机初始化h0写道这里
        hidden = torch.zeros(num_layers, batch_size, hidden_size)  # 随机初始化的h0,且必须是Tensor类型的

        # 我们只关心output: [seq_len, batch_size, hidden_size]
        # input是: [seq_len, batch_size, input_size]
        output, _ = self.rnn(input, hidden)

        # 将out reshape到[seq_len * batch_size, hidden_size]
        return output.view(-1, self.hidden_size)

model = RNNModel(input_size, hidden_size, batch_size, num_layers)



# Step 3:============================定义损失函数和优化器===================
# 定义 loss 函数,这里用的是交叉熵损失函数(Cross Entropy),这种损失函数之前博文也讲过的。
criterion = torch.nn.CrossEntropyLoss()
# 我们优先使用Adam下降,lr是学习率: 0.1
optimizer = torch.optim.Adam(model.parameters(), lr=0.05)





# Step 4:============================开始训练===================
for e in range(200):
    loss = 0
    optimizer.zero_grad()

    # 前向传播
    hidden = model(inputs)
    # 计算损失
    loss = criterion(hidden, lables)
    # 反向传播,更新参数
    loss.backward()
    optimizer.step()

    _, idx = hidden.max(dim=1)
    idx = idx.data.numpy()
    print('e============================')
    print('predicted str: ', end='')
    print(''.join([idx2char[x] for x in idx]), end='')
    print(',epoch [%d/200] loss=%.4f' % (e+1, loss.item()), end='')

效果如下:很明显,损失好像更低了些。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_29367075/article/details/108946762