Seq2Seq原理讲解以及实战练手

前言

首先,在我看来,transformer就是nlp的编码解码思想(seq2seq model)加上注意力机制,下面简单介绍一下seq2seq model

Seq2Seq(Sequence to Sequence)模型是一种用于处理序列数据的深度学习模型,特别适用于处理不定长度的输入序列和输出序列的任务。该模型最初被设计用于机器翻译,但后来被广泛应用于其他自然语言处理任务,语音识别,文本生成等领域。

Seq2Seq模型由两个主要部分组成:编码器(Encoder)和解码器(Decoder)。

1. 编码器(Encoder):

编码器负责将输入序列转换为一个固定长度的表示(context向量),这个表示将包含输入序列的信息。常用的编码器是循环神经网络(RNN)或者长短时记忆网络(LSTM)。编码器的输出通常是输入序列全体时间步的隐藏状态或者最终隐藏状态。

2. 解码器(Decoder):

解码器接收编码器的输出(context向量)并生成输出序列。解码器同样可以是RNN或者LSTM。在生成序列的过程中,解码器逐步地生成一个元素,同时利用先前生成的元素作为上下文来指导生成下一个元素。Seq2Seq模型的训练过程通常使用Teacher Forcing方法。在训练时,解码器的输入是已知的目标序列(ground truth),而在推理阶段(生成阶段),解码器的输入是其自己先前生成的元素。在Seq2Seq的训练中,损失函数通常使用交叉熵损失函数来衡量生成序列与目标序列之间的差异。

3. 应用场景:

机器翻译: 将一个语言的句子翻译成另一个语言的句子。

文本摘要: 生成输入文本的摘要。

对话系统: 应用于生成对话系统的回复。

语音识别: 将语音信号转换为文本。

总体而言,Seq2Seq模型是一种强大的框架,可用于处理输入和输出序列不定长的任务,并在自然语言处理等领域取得了许多成功的应用。

框架原理详细解释

项目练手

项目地址:GitHub - bai-shang/crnn_seq2seq_ocr_pytorch: Extremely simple implement for Chinese OCR by PyTorch.

数据集地址:GitHub - senlinuc/caffe_ocr: 主流ocr算法研究实验性的项目,目前实现了CNN+BLSTM+CTC架构

项目简单介绍:该项目实现了卷积循环神经网络 (CRNN),它是 CNN 和序列到序列模型的组合,关注基于图像的序列识别任务,例如场景文本识别和 OCR

整体网络结构

编码器网络结构

编码器的主要作用是将输入的图像信息进行特征提取和编码,以便后续解码器进行处理。整个编码器由两个部分组成:卷积神经网络(CNN)和循环神经网络(RNN)。

编码器的 CNN 部分

卷积层 1:

输入通道:3(RGB图像)

输出通道:64

卷积核大小:(3, 3)

激活函数:ReLU

池化层(MaxPooling)

卷积层 2:

输入通道:64

输出通道:128

卷积核大小:(3, 3)

激活函数:ReLU

池化层(MaxPooling)

卷积层 3:

输入通道:128

输出通道:256

卷积核大小:(3, 3)

激活函数:ReLU

池化层(MaxPooling)

卷积层 4:

输入通道:256

输出通道:256

卷积核大小:(3, 3)

激活函数:ReLU

池化层(MaxPooling)

卷积层 5:

输入通道:256

输出通道:512

卷积核大小:(3, 3)

激活函数:ReLU

池化层(MaxPooling)

卷积层 6:

输入通道:512

输出通道:512

卷积核大小:(3, 3)

激活函数:ReLU

池化层(MaxPooling)

卷积层 7:

输入通道:512

输出通道:512

卷积核大小:(2, 2) #这是一个较小的卷积层,用于进一步提取图像的局部特征。

激活函数:ReLU

编码器的 RNN 部分

一个双向的循环神经网络(BidirectionalLSTM)。它的作用是对来自卷积神经网络的特征进行更深层次的编码和建模。这里使用了两个双向 LSTM 层,每个层的输出维度都是 256。每个 LSTM 层之后都跟随一个线性层(Linear),用于映射隐藏状态到更高维度,以增强模型的表达能力。

LSTM(Long Short-Term Memory):LSTM 是一种专门用于处理序列数据的循环神经网络结构,可以有效地捕捉长距离依赖关系。

双向操作:双向 LSTM 允许模型同时考虑输入序列的过去和未来信息,从而更好地捕捉上下文信息。

Linear 层:线性层用于将 LSTM 层的隐藏状态映射到更高维度的特征表示,以供解码器使用。

编码器具体代码实现
class Encoder(nn.Module):
    def __init__(self, channel_size, hidden_size):
        super(Encoder, self).__init__()
        self.cnn = CNN(channel_size)
        self.rnn = nn.Sequential(
            BidirectionalLSTM(512, hidden_size, hidden_size),
            BidirectionalLSTM(hidden_size, hidden_size, hidden_size))

    def forward(self, input):
        # conv features
        conv = self.cnn(input)
        b, c, h, w = conv.size()
        assert h == 1, "the height of conv must be 1"

        # rnn feature
        conv = conv.squeeze(2)        # [b, c, 1, w] -> [b, c, w]
        conv = conv.permute(2, 0, 1)  # [b, c, w] -> [w, b, c]
        output = self.rnn(conv)
        return output

class CNN(nn.Module):

    def __init__(self, channel_size):
        super(CNN, self).__init__()
        self.cnn = nn.Sequential(
                      nn.Conv2d(channel_size, 64, 3, 1, 1), nn.ReLU(True), nn.MaxPool2d(2, 2),
                      nn.Conv2d(64, 128, 3, 1, 1), nn.ReLU(True), nn.MaxPool2d(2, 2),
                      nn.Conv2d(128, 256, 3, 1, 1), nn.BatchNorm2d(256), nn.ReLU(True),
                      nn.Conv2d(256, 256, 3, 1, 1), nn.ReLU(True), nn.MaxPool2d((2,2), (2,1), (0,1)),
                      nn.Conv2d(256, 512, 3, 1, 1), nn.BatchNorm2d(512), nn.ReLU(True),
                      nn.Conv2d(512, 512, 3, 1, 1), nn.ReLU(True), nn.MaxPool2d((2,2), (2,1), (0,1)),
                      nn.Conv2d(512, 512, 2, 1, 0), nn.BatchNorm2d(512), nn.ReLU(True))

    def forward(self, input):
        # [n, channel_size, 32, 280] -> [n, 512, 1, 71]
        conv = self.cnn(input)
        return conv

class BidirectionalLSTM(nn.Module):

    def __init__(self, input_size, hidden_size, output_size):
        super(BidirectionalLSTM, self).__init__()

        self.rnn = nn.LSTM(input_size, hidden_size, bidirectional=True)
        self.embedding = nn.Linear(hidden_size * 2, output_size)

    def forward(self, input):
        recurrent, _ = self.rnn(input)
        T, b, h = recurrent.size()
        t_rec = recurrent.view(T * b, h)

        output = self.embedding(t_rec)  # [T * b, output_size]
        output = output.view(T, b, -1)
        return output
解码器网络结构

解码器将编码器提取的特征信息解码成最终的文本序列。在这里,解码器采用了带有注意力机制(AttnDecoderRNN)的循环神经网络(GRU)。

Embedding 层(Embedding):将输入的标签索引映射为密集的词嵌入(word embedding)。这一层的输出将作为后续的输入提供给 GRU。

注意力层(attn):注意力机制用于动态地关注输入序列中的不同部分。这里通过一个线性层将解码器当前的隐藏状态和编码器的输出进行结合,产生注意力分布。

注意力结合层(attn_combine):将注意力权重应用于编码器的输出,以加权求和的方式结合编码器的输出和当前解码器的输入。

Dropout 层(dropout):用于在训练过程中随机丢弃一些神经元,以防止过拟合。

GRU 层(GRU):GRU 是一种循环神经网络结构,用于处理序列数据。它接受当前时刻的输入和先前时刻的隐藏状态,并生成当前时刻的输出和新的隐藏状态。

线性输出层(out):将 GRU 层的输出映射到最终的输出空间,这里是对应词汇表的大小(5992),用于预测下一个标签的概率分布。

解码器具体代码实现

class Decoder(nn.Module):

    def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=71):
        super(Decoder, self).__init__()
        self.hidden_size = hidden_size
        self.decoder = AttnDecoderRNN(hidden_size, output_size, dropout_p, max_length)

    def forward(self, input, hidden, encoder_outputs):
        return self.decoder(input, hidden, encoder_outputs)

    def initHidden(self, batch_size):
        result = torch.autograd.Variable(torch.zeros(1, batch_size, self.hidden_size))
        return result

class AttnDecoderRNN(nn.Module):

    def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=71):
        super(AttnDecoderRNN, self).__init__()
        self.hidden_size = hidden_size
        self.output_size = output_size
        self.dropout_p = dropout_p
        self.max_length = max_length

        self.embedding = nn.Embedding(self.output_size, self.hidden_size)
        self.attn = nn.Linear(self.hidden_size * 2, self.max_length)
        self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size)
        self.dropout = nn.Dropout(self.dropout_p)
        self.gru = nn.GRU(self.hidden_size, self.hidden_size)
        self.out = nn.Linear(self.hidden_size, self.output_size)


    def forward(self, input, hidden, encoder_outputs):
        embedded = self.embedding(input)
        embedded = self.dropout(embedded)

        attn_weights = F.softmax(self.attn(torch.cat((embedded, hidden[0]), 1)), dim=1)
        attn_applied = torch.bmm(attn_weights.unsqueeze(1), encoder_outputs.permute(1, 0, 2))

        output = torch.cat((embedded, attn_applied.squeeze(1)), 1)
        output = self.attn_combine(output).unsqueeze(0)

        output = F.relu(output)
        output, hidden = self.gru(output, hidden)

        output = F.log_softmax(self.out(output[0]), dim=1)
        return output, hidden, attn_weights

    def initHidden(self):
        return torch.zeros(1, 1, self.hidden_size, device=device)

开始训练

这个项目运行过程中会有一些问题,报错可以评论,如果我会的话,可以帮助解决...........

  • 22
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
seq2seq模型存在一些不足之处,可以通过以下方式进行改进: 1. 处理长距离依赖:为了解决信息传递损失和长距离依赖问题,可以使用注意力机制(attention mechanism)。注意力机制允许解码器在生成每个输出时,根据输入序列的不同部分分配不同的权重,从而更好地捕捉输入序列的上下文信息。 2. 解决歧义问题:为了解决歧义问题,可以尝试使用更复杂的解码器结构,如递归神经网络(RNN)的变体(如LSTM、GRU)或者Transformer模型。这些模型具有更强的建模能力,能够更好地处理复杂的语言结构和歧义情况。 3. 处理大规模训练困难:针对大规模训练困难,可以采用分布式训练策略,将训练过程分布在多个计算节点上进行并行计算。此外,使用更高效的优化算法和硬件加速(如GPU)可以加快训练速度。 4. 减少错误累积:为了减少错误累积,可以采用一些技术来提高生成质量。例如,引入强化学习方法,通过引入奖励机制来指导生成过程,从而减少错误累积。另外,可以使用预训练模型或者多任务学习来提升生成的准确性和一致性。 5. 结合外部知识:为了增强模型的表达能力和语义理解能力,可以引入外部知识,如知识图谱、词义相似度等。将这些知识融合到模型中,可以提高模型对复杂语义和推理任务的处理能力。 综上所述,通过引入注意力机制、使用更复杂的解码器结构、采用分布式训练策略、减少错误累积和结合外部知识等方式,可以改进seq2seq模型的不足,提高其在各种任务中的性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值