RNN、LSTM、GRU神经网络构建人名分类器(二)

简介: 这个文本描述了一个使用RNN(循环神经网络)、LSTM(长短期记忆网络)和GRU(门控循环单元)构建的人名分类器的案例。案例的主要目的是通过输入一个人名来预测它最可能属于哪个国家。这个任务在国际化的公司中很重要,因为可以自动为用户注册时提供相应的国家或地区选项。

RNN、LSTM、GRU神经网络构建人名分类器(一)+https://developer.aliyun.com/article/1544720?spm=a2c6h.13148508.setting.18.2a1e4f0eMtMqGK


构建RNN模型

1构建RNN模型


class RNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size,num_layers = 1):
        super(RNN,self).__init__()
        self.num_layers = num_layers
        self.hidden_size = hidden_size
        self.input_size = input_size
        self.output_size = output_size

        # RNN的输入数据通常被组织为形状为 [batch_size, seq_len, input_size] ,隐藏层:形状为[num_layers, batch_size, hidden_size]
        self.rnn = nn.RNN(input_size, hidden_size, num_layers)
        # 实例化线性层,将rnn的输出维度转换为指定维度
        self.linear = nn.Linear(hidden_size, output_size)

        self.softmax = nn.LogSoftmax(dim=-1)

    def forward(self, input1, hidden):
        # input1代表人名分类器中的输入张量, 形状为[1, n_letters] ,1代表batch_size
        # hidden代表隐藏层, 形状为[num_layers, batch_size, hidden_size]
        input1 = input1.unsqueeze(0)
        rr, hn = self.rnn(input1, hidden)  # 生成输出rr和新的隐藏层hn

        return self.softmax(self.linear(rr)) ,hn

    def initHidden(self):
        # 初始化隐藏层, 形状为[num_layers, batch_size, hidden_size]
        return torch.zeros(self.num_layers, 1, self.hidden_size)


torch.unsqueeze用法:


>>> x = torch.tensor([1, 2, 3, 4])
>>> torch.unsqueeze(x, 0)
tensor([[ 1,  2,  3,  4]])
>>> torch.unsqueeze(x, 1)
tensor([[ 1],
        [ 2],
        [ 3],
        [ 4]])


2 构建LSTM模型


class LSTM(nn.Module):
    def __init__(self, input_size, hidden_size, output_size,num_layers = 1):
        super(LSTM, self).__init__()
        # 初始化参数
        self.hidden_size = hidden_size
        self.input_size = input_size
        self.output_size = output_size
        self.num_layers = num_layers
        # 实例化LSTM层
        self.lstm = nn.LSTM(input_size, hidden_size,num_layers)
        # 实例化线性层
        self.linear = nn.Linear(hidden_size, output_size)
        # 实例化softmax层,得到类别结果
        self.softmax = nn.LogSoftmax(dim=-1)

    def forward(self, input1, hidden, c):
        # input1代表人名分类器中的输入张量, 形状为[1, n_letters]
        # hidden代表隐藏层, 形状为[num_layers, batch_size, hidden_size]
        input1 = input1.unsqueeze(0)
        rr , (hn,cn) = self.lstm(input1, (hidden,c))  # 生成输出output和新的隐藏层hn
        return self.softmax(self.linear(rr)) ,hn,cn

    def initHiddenAndC(self):
        # 初始化隐藏层, 形状为[num_layers, batch_size, hidden_size]
        # hidden和c的形状为[num_layers, batch_size, hidden_size]
        c = hidden = torch.zeros(self.num_layers, 1, self.hidden_size)
        return hidden,c


3 构建GRU模型


class GRU(nn.Module):
    def __init__(self, input_size, hidden_size, output_size,num_layers = 1):
        super(GRU, self).__init__()
        # 初始化参数
        self.hidden_size = hidden_size
        self.input_size = input_size
        self.output_size = output_size
        self.num_layers = num_layers
        # 实例化GRU层
        self.gru = nn.GRU(input_size, hidden_size,num_layers)
        # 实例化线性层
        self.linear = nn.Linear(hidden_size, output_size)
        # 实例化softmax层,得到类别结果
        self.softmax = nn.LogSoftmax(dim=-1)

    def forward(self, input1, hidden):
        # input1代表人名分类器中的输入张量, 形状为[1, n_letters]
        # hidden代表隐藏层, 形状为[num_layers, batch_size, hidden_size]
        input1 = input1.unsqueeze(0)
        rr, hn = self.gru(input1, hidden)  # 生成输出output和新的隐藏层hn
        return self.softmax(self.linear(rr)), hn

    def initHidden(self):
        # 初始化隐藏层, 形状为[num_layers, batch_size, hidden_size]
        return torch.zeros(self.num_layers, 1, self.hidden_size)


4 实例化参数


# 因为是onehot编码, 输入张量最后一维的尺寸就是n_letters
input_size = n_letters

# 定义隐层的最后一维尺寸大小
n_hidden = 128

# 输出尺寸为语言类别总数n_categories
output_size = n_categories

# num_layer使用默认值, num_layers = 1


5 输入参数


# 假如我们以一个字母B作为RNN的首次输入, 它通过lineToTensor转为张量
# 因为我们的lineToTensor输出是三维张量, 而RNN类需要的二维张量
# 因此需要使用squeeze(0)降低一个维度
input = lineToTensor('B').squeeze(0)

# 初始化一个三维的隐层0张量, 也是初始的细胞状态张量
hidden = c = torch.zeros(1, 1, n_hidden)


调用


rnn = RNN(n_letters, n_hidden, n_categories)
lstm = LSTM(n_letters, n_hidden, n_categories)
gru = GRU(n_letters, n_hidden, n_categories)

rnn_output, next_hidden = rnn(input, hidden)
print("rnn:", rnn_output)
lstm_output, next_hidden, c = lstm(input, hidden, c)
print("lstm:", lstm_output)
gru_output, next_hidden = gru(input, hidden)
print("gru:", gru_output)


输出


rnn: tensor([[[-2.8822, -2.8615, -2.9488, -2.8898, -2.9205, -2.8113, -2.9328,
          -2.8239, -2.8678, -2.9474, -2.8724, -2.9703, -2.9019, -2.8871,
          -2.9340, -2.8436, -2.8442, -2.9047]]], grad_fn=<LogSoftmaxBackward>)
lstm: tensor([[[-2.9427, -2.8574, -2.9175, -2.8492, -2.8962, -2.9276, -2.8500,
          -2.9306, -2.8304, -2.9559, -2.9751, -2.8071, -2.9138, -2.8196,
          -2.8575, -2.8416, -2.9395, -2.9384]]], grad_fn=<LogSoftmaxBackward>)
gru: tensor([[[-2.8042, -2.8894, -2.8355, -2.8951, -2.8682, -2.9502, -2.9056,
          -2.8963, -2.8671, -2.9109, -2.9425, -2.8390, -2.9229, -2.8081,
          -2.8800, -2.9561, -2.9205, -2.9546]]], grad_fn=<LogSoftmaxBackward>)


构建训练函数并进行训练

1从输出结果中获得指定类别函数


def categoryFromOutput(output):
    """从输出结果中获得指定类别, 参数为输出张量output"""
    # 从输出张量中返回最大的值和索引对象, 我们这里主要需要这个索引
    top_n, top_i = output.topk(1)
    # top_i对象中取出索引的值
    category_i = top_i[0].item()
    # 根据索引值获得对应语言类别, 返回语言类别和索引值
    return all_categories[category_i], category_i


2 输入参数


output = gru_output


3 随机生成训练数据


def randomTrainingExample():
    """该函数用于随机产生训练数据"""
    # 首先使用random的choice方法从all_categories随机选择一个类别
    category = random.choice(all_categories)
    # 然后在通过category_lines字典取category类别对应的名字列表
    # 之后再从列表中随机取一个名字
    line = random.choice(category_lines[category])
    # 接着将这个类别在所有类别列表中的索引封装成tensor, 得到类别张量category_tensor
    category_tensor = torch.tensor([all_categories.index(category)], dtype=torch.long)
    # 最后, 将随机取到的名字通过函数lineToTensor转化为onehot张量表示
    line_tensor = lineToTensor(line)
    return category, line, category_tensor, line_tensor


# 我们随机取出十个进行结果查看
for i in range(10):
    category, line, category_tensor, line_tensor = randomTrainingExample()
    print('category =', category, '/ line =', line, '/ category_tensor =', category_tensor)


4 构建RNN训练函数


# 定义损失函数为nn.NLLLoss,因为RNN的最后一层是nn.LogSoftmax, 两者的内部计算逻辑正好能够吻合.  
criterion = nn.NLLLoss()

# 设置学习率为0.005
learning_rate = 0.005 

def trainRNN(category_tensor, line_tensor):
    """定义训练函数, 它的两个参数是category_tensor类别的张量表示, 相当于训练数据的标签,
       line_tensor名字的张量表示, 相当于对应训练数据"""

    # 在函数中, 首先通过实例化对象rnn初始化隐层张量
    hidden = rnn.initHidden()

    # 然后将模型结构中的梯度归0
    rnn.zero_grad()

    # 下面开始进行训练, 将训练数据line_tensor的每个字符逐个传入rnn之中, 得到最终结果
    for i in range(line_tensor.size()[0]):
        output, hidden = rnn(line_tensor[i], hidden)

    # 因为我们的rnn对象由nn.RNN实例化得到, 最终输出形状是三维张量, 为了满足于category_tensor
    # 进行对比计算损失, 需要减少第一个维度, 这里使用squeeze()方法
    loss = criterion(output.squeeze(0), category_tensor)

    # 损失进行反向传播
    loss.backward()
    # 更新模型中所有的参数
    for p in rnn.parameters():
        # 将参数的张量表示与参数的梯度乘以学习率的结果相加以此来更新参数
        p.data.add_(-learning_rate, p.grad.data)
    # 返回结果和损失的值
    return output, loss.item()


  • torch.add演示:
>>> a = torch.randn(4)
>>> a
tensor([-0.9732, -0.3497,  0.6245,  0.4022])
>>> b = torch.randn(4, 1)
>>> b
tensor([[ 0.3743],
        [-1.7724],
        [-0.5811],
        [-0.8017]])
>>> torch.add(a, b, alpha=10)
tensor([[  2.7695,   3.3930,   4.3672,   4.1450],
        [-18.6971, -18.0736, -17.0994, -17.3216],
        [ -6.7845,  -6.1610,  -5.1868,  -5.4090],
        [ -8.9902,  -8.3667,  -7.3925,  -7.6147]])


5 构建LSTM训练函数
def trainLSTM(category_tensor, line_tensor):
    hidden, c = lstm.initHiddenAndC()
    lstm.zero_grad()
    for i in range(line_tensor.size()[0]):
        # 返回output, hidden以及细胞状态c
        output, hidden, c = lstm(line_tensor[i], hidden, c)
    loss = criterion(output.squeeze(0), category_tensor)
    loss.backward()

    for p in lstm.parameters():
        p.data.add_(-learning_rate, p.grad.data)
    return output, loss.item()


6构建GRU训练函数


# 与RNN完全相同, 只不过名字改成了GRU

def trainGRU(category_tensor, line_tensor):
    hidden = gru.initHidden()
    gru.zero_grad()
    for i in range(line_tensor.size()[0]):
        output, hidden= gru(line_tensor[i], hidden)
    loss = criterion(output.squeeze(0), category_tensor)
    loss.backward()

    for p in gru.parameters():
        p.data.add_(-learning_rate, p.grad.data)
    return output, loss.item()


optimizer.step()  # 这一行替换了原来的手动更新参数代码


RNN、LSTM、GRU神经网络构建人名分类器(三)+https://developer.aliyun.com/article/1544722?spm=a2c6h.13148508.setting.16.2a1e4f0eMtMqGK

相关文章
|
5天前
|
机器学习/深度学习 PyTorch 算法框架/工具
RNN、LSTM、GRU神经网络构建人名分类器(三)
这个文本描述了一个使用RNN(循环神经网络)、LSTM(长短期记忆网络)和GRU(门控循环单元)构建的人名分类器的案例。案例的主要目的是通过输入一个人名来预测它最可能属于哪个国家。这个任务在国际化的公司中很重要,因为可以自动为用户注册时提供相应的国家或地区选项。
|
1月前
|
机器学习/深度学习 自然语言处理 TensorFlow
tensorflow循环神经网络(RNN)文本生成莎士比亚剧集
我们将使用 Andrej Karpathy 在《循环神经网络不合理的有效性》一文中提供的莎士比亚作品数据集。给定此数据中的一个字符序列 (“Shakespear”),训练一个模型以预测该序列的下一个字符(“e”)。通过重复调用该模型,可以生成更长的文本序列。
|
14天前
|
机器学习/深度学习
【从零开始学习深度学习】33.语言模型的计算方式及循环神经网络RNN简介
【从零开始学习深度学习】33.语言模型的计算方式及循环神经网络RNN简介
【从零开始学习深度学习】33.语言模型的计算方式及循环神经网络RNN简介
|
5天前
|
机器学习/深度学习 自然语言处理 算法
RNN-循环神经网络
自然语言处理(Nature language Processing, NLP)研究的主要是通过计算机算法来理解自然语言。对于自然语言来说,处理的数据主要就是人类的语言,我们在进行文本数据处理时,需要将文本进行数据值化,然后进行后续的训练工作。
|
14天前
|
机器学习/深度学习 自然语言处理 算法
【从零开始学习深度学习】49.Pytorch_NLP项目实战:文本情感分类---使用循环神经网络RNN
【从零开始学习深度学习】49.Pytorch_NLP项目实战:文本情感分类---使用循环神经网络RNN
|
1月前
|
机器学习/深度学习 自然语言处理 语音技术
深度学习500问——Chapter06: 循环神经网络(RNN)(3)
深度学习500问——Chapter06: 循环神经网络(RNN)(3)
40 3
|
1月前
|
机器学习/深度学习 自然语言处理 PyTorch
使用Python实现循环神经网络(RNN)的博客教程
使用Python实现循环神经网络(RNN)的博客教程
76 1
|
13天前
|
机器学习/深度学习 存储 自然语言处理
RNN与LSTM:循环神经网络的深入理解
【6月更文挑战第14天】本文深入探讨RNN和LSTM,两种关键的深度学习模型在处理序列数据时的作用。RNN利用记忆单元捕捉时间依赖性,但面临梯度消失和爆炸问题。为解决此问题,LSTM引入门控机制,有效捕获长期依赖,适用于长序列处理。RNN与LSTM相互关联,LSTM可视为RNN的优化版本。两者在NLP、语音识别等领域有广泛影响,未来潜力无限。
|
1月前
|
机器学习/深度学习 算法 搜索推荐
深度学习500问——Chapter06: 循环神经网络(RNN)(2)
深度学习500问——Chapter06: 循环神经网络(RNN)(2)
29 3
|
1月前
|
机器学习/深度学习 算法 Scala
深度学习500问——Chapter06: 循环神经网络(RNN)(4)
深度学习500问——Chapter06: 循环神经网络(RNN)(4)
46 1