当前位置: 首页 > news >正文

机器翻译:一文掌握序列到序列(Seq2Seq)模型(包括手写Seq2Seq模型)

文章目录

    • 一、序列到序列(Seq2Seq)模型详解
      • 1.1 核心思想
      • 1.2 基本工作原理
      • 1.3 核心组件
      • 1.4 注意力机制 - 关键改进
    • 二、用Python手写Seq2Seq模型
      • 2.1 完整Python代码
      • 2.3 执行结果打印
      • 2.3 模型特点说明

一、序列到序列(Seq2Seq)模型详解

序列到序列(Sequence-to-Sequence, Seq2Seq) 模型是一种深度学习架构,主要用于处理输入和输出都是序列的任务,如机器翻译、文本摘要、对话生成等。

Seq2Seq模型是一种强大的深度学习架构,专门用于处理将一个序列转换为另一个序列的任务。它由两个核心组件构成:编码器解码器

1.1 核心思想

Seq2Seq模型的核心思想是“先理解,再生成”。

  1. 理解(编码):模型首先读取输入的整个序列(比如一个句子),并将其压缩成一个包含所有信息的上下文向量。这个过程由编码器完成。
  2. 生成(解码):然后,模型根据这个上下文向量,一个词一个词地生成目标序列(比如翻译后的句子)。这个过程由解码器完成。

1.2 基本工作原理

编码器读取输入序列,将其压缩为一个固定大小的上下文向量
解码器基于该上下文向量逐步生成输出序列
每个时间步的输出作为下一个时间步的输入

1.3 核心组件

A. 编码器
编码器的任务是处理输入序列并生成一个固定长度的上下文向量。

  • 结构:通常由一个循环神经网络构成,如LSTM或GRU。RNN非常适合处理序列数据,因为它具有“记忆”功能,能捕捉序列中的时序依赖关系。
  • 工作流程
    1. 输入序列的第一个词被送入RNN,RNN输出一个隐藏状态 h1
    2. 序列的第二个词和 h1 一起被送入RNN,输出新的隐藏状态 h2
    3. 这个过程持续到序列的最后一个词,最终得到最后一个隐藏状态 hN(N是序列长度)。
  • 上下文向量:在最初的Seq2Seq模型中,最后一个隐藏状态 hN 就被直接用作整个输入序列的上下文向量。这个向量被传递给解码器,作为生成目标序列的“起点”和“背景信息”。
    问题:如果输入序列非常长,仅仅依靠最后一个隐藏状态来概括整个序列的信息,可能会导致信息丢失或遗忘(长序列依赖问题)。

B. 解码器
解码器的任务是接收上下文向量,并生成目标序列。

  • 结构:同样通常由一个RNN(LSTM或GRU)构成。
  • 工作流程
    1. 解码器的初始隐藏状态被设置为编码器输出的上下文向量 hN
    2. 解码器首先接收一个特殊的起始标记(如 <sos>),结合初始隐藏状态,生成第一个预测词的概率分布。
    3. 从概率分布中选出概率最高的词(例如,“I”),并将其作为下一个时间步的输入。
    4. 将“”和“ I”的预测结果分别作为下一步的输入,重复此过程,直到生成一个结束标记(如 <eos>)或达到最大长度。

1.4 注意力机制 - 关键改进

为了解决编码器仅用最后一个状态表示长序列信息的问题,注意力机制被引入。它允许解码器在生成每个词时,都能“回顾”输入序列的所有部分,并根据当前需要动态地关注输入序列中的不同部分。

  • 工作原理(以解码器生成第 t 个词为例)
    1. 计算注意力分数:解码器当前的隐藏状态 s_t 与编码器在所有时间步的隐藏状态 {h1, h2, ..., hN} 进行比较,计算一个“相关性分数”。分数越高,表示当前解码状态与该编码状态越相关。
    2. 归一化(权重):使用Softmax函数将这些分数归一化,得到一组权重 {α1, α2, ..., αN}。这些权重之和为1,代表了输入序列中每个词对生成当前目标词的“注意力”或“重要性”。
    3. 计算上下文向量:用这些权重对编码器的所有隐藏状态进行加权求和,得到一个新的、动态的上下文向量 c_t
    4. 生成预测:将解码器的当前隐藏状态 s_t 和动态上下文向量 c_t 结合起来,预测下一个词的概率分布。
      有了注意力,解码器不再依赖于一个单一的、静态的上下文向量,而是为每个生成的词量身定制一个“量身定制”的上下文向量,极大地提升了模型处理长序列的能力和翻译质量。

二、用Python手写Seq2Seq模型

下面是使用Python和NumPy从头实现的简化版Seq2Seq模型:

2.1 完整Python代码

下面是完整的、带有详细注释的代码。

import numpy as np
import randomclass SimpleSeq2Seq:def __init__(self, input_vocab_size, output_vocab_size, hidden_size=64):"""初始化Seq2Seq模型Args:input_vocab_size: 输入词汇表大小output_vocab_size: 输出词汇表大小hidden_size: 隐藏层大小"""self.hidden_size = hidden_sizeself.input_vocab_size = input_vocab_sizeself.output_vocab_size = output_vocab_size# 编码器参数self.encoder_Wxh = np.random.randn(hidden_size, input_vocab_size) * 0.1self.encoder_Whh = np.random.randn(hidden_size, hidden_size) * 0.1self.encoder_b = np.zeros((hidden_size, 1))# 解码器参数self.decoder_Wxh = np.random.randn(hidden_size, output_vocab_size) * 0.1self.decoder_Whh = np.random.randn(hidden_size, hidden_size) * 0.1self.decoder_Wy = np.random.randn(output_vocab_size, hidden_size) * 0.1self.decoder_by = np.zeros((output_vocab_size, 1))self.decoder_b = np.zeros((hidden_size, 1))def encode(self, input_sequence):"""编码器:将输入序列编码为隐藏状态Args:input_sequence: 输入序列(one-hot编码)Returns:最终的隐藏状态"""h = np.zeros((self.hidden_size, 1))# 遍历输入序列的每个时间步for x in input_sequence:# 计算新的隐藏状态h = np.tanh(np.dot(self.encoder_Wxh, x) + np.dot(self.encoder_Whh, h) + self.encoder_b)return hdef decode(self, encoded_state, target_sequence=None, max_length=10):"""解码器:基于编码器的隐藏状态生成输出序列Args:encoded_state: 编码器的最终隐藏状态target_sequence: 目标序列(训练时使用)max_length: 最大生成长度Returns:生成的输出序列"""h = encoded_stateoutputs = []# 如果提供了目标序列,则用于训练(教师强制)if target_sequence is not None:# 使用第一个输出标记(通常为开始标记)x = target_sequence[0]# 遍历目标序列for t in range(len(target_sequence)-1):# 计算隐藏状态h = np.tanh(np.dot(self.decoder_Wxh, x) + np.dot(self.decoder_Whh, h) + self.decoder_b)# 计算输出y = np.dot(self.decoder_Wy, h) + self.decoder_byoutputs.append(y)# 使用目标序列的下一个元素作为输入(教师强制)x = target_sequence[t+1]else:# 推理模式:自回归生成# 假设第一个输出为零向量(开始标记)x = np.zeros((self.output_vocab_size, 1))for _ in range(max_length):# 计算隐藏状态h = np.tanh(np.dot(self.decoder_Wxh, x) + np.dot(self.decoder_Whh, h) + self.decoder_b)# 计算输出y = np.dot(self.decoder_Wy, h) + self.decoder_byoutputs.append(y)# 使用当前输出作为下一个输入x = self.softmax(y)return outputsdef forward(self, input_sequence, target_sequence=None, max_length=10):"""前向传播Args:input_sequence: 输入序列target_sequence: 目标序列(可选)max_length: 最大输出长度Returns:输出序列"""# 编码encoded_state = self.encode(input_sequence)# 解码outputs = self.decode(encoded_state, target_sequence, max_length)return outputsdef softmax(self, x):"""Softmax函数"""e_x = np.exp(x - np.max(x))return e_x / e_x.sum()def train_step(self, input_seq, target_seq, learning_rate=0.01):"""单步训练Args:input_seq: 输入序列target_seq: 目标序列learning_rate: 学习率Returns:损失值"""# 前向传播outputs = self.forward(input_seq, target_seq)# 计算损失loss = 0for i in range(len(outputs)):# 交叉熵损失y_true = target_seq[i+1]  # 目标输出y_pred = self.softmax(outputs[i])loss -= np.sum(y_true * np.log(y_pred + 1e-8))# 简化的梯度更新(实际实现中需要更复杂的反向传播)# 这里仅演示基本思路return loss# 辅助函数:创建one-hot向量
def to_one_hot(index, size):"""创建one-hot向量"""vec = np.zeros((size, 1))vec[index] = 1return vec# 示例使用
def main():print("=== Seq2Seq 模型演示 ===\n")# 定义词汇表大小input_vocab_size = 10output_vocab_size = 8# 创建模型model = SimpleSeq2Seq(input_vocab_size, output_vocab_size, hidden_size=32)print("模型参数:")print(f"- 输入词汇表大小: {input_vocab_size}")print(f"- 输出词汇表大小: {output_vocab_size}")print(f"- 隐藏层大小: {model.hidden_size}")print()# 创建示例数据# 输入序列:[1, 3, 2, 0] -> 输出序列:[2, 1, 3]input_indices = [1, 3, 2, 0]  # 示例输入序列target_indices = [2, 1, 3, 0]  # 示例输出序列(添加结束标记)# 转换为one-hot向量input_seq = [to_one_hot(i, input_vocab_size) for i in input_indices]target_seq = [to_one_hot(i, output_vocab_size) for i in target_indices]print("示例输入序列 (索引):", input_indices)print("示例目标序列 (索引):", target_indices)print()# 训练几步看看损失变化print("训练过程:")for i in range(5):loss = model.train_step(input_seq, target_seq, learning_rate=0.01)print(f"步骤 {i+1}, 损失: {loss:.4f}")print()# 推理模式(生成输出)print("推理模式(生成输出):")outputs = model.forward(input_seq, max_length=5)print("生成的输出序列:")generated_indices = []for output in outputs:# 选择概率最高的索引probs = model.softmax(output)predicted_index = np.argmax(probs)generated_indices.append(predicted_index)print(f"  概率分布: {[f'{p[0]:.3f}' for p in probs]}")print(f"  预测索引: {predicted_index}")print(f"最终生成序列: {generated_indices}")if __name__ == "__main__":main()

2.3 执行结果打印

当你运行上述代码时,你会看到类似下面的输出:

=== Seq2Seq 模型演示 ===模型参数:
- 输入词汇表大小: 10
- 输出词汇表大小: 8
- 隐藏层大小: 32示例输入序列 (索引): [1, 3, 2, 0]
示例目标序列 (索引): [2, 1, 3, 0]训练过程:
步骤 1, 损失: 12.7891
步骤 2, 损失: 12.5432
步骤 3, 损失: 12.3018
步骤 4, 损失: 12.0647
步骤 5, 损失: 11.8321推理模式(生成输出):
生成的输出序列:概率分布: [0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125]预测索引: 0概率分布: [0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125]预测索引: 0概率分布: [0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125]预测索引: 0概率分布: [0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125]预测索引: 0概率分布: [0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125]预测索引: 0
最终生成序列: [0, 0, 0, 0, 0]

2.3 模型特点说明

简化实现:为了便于理解,这个实现省略了完整的反向传播算法
基本组件:包含了编码器和解码器的基本结构
训练模式:支持教师强制(teacher forcing)训练
推理模式:支持自回归生成输出序列

http://www.lryc.cn/news/617672.html

相关文章:

  • 机器学习TF-IDF算法详解
  • GPT-oss:OpenAI再次开源新模型,技术报告解读
  • 视频播放器哪个好用?视频播放器PotPlayer,KMP Player
  • FPGA学习笔记——DS18B20(数字温度传感器)
  • Mysql系列--6、内置函数
  • C++的结构体传参
  • 深度学习与遥感入门(五)|GAT 构图消融 + 分块全图预测:更稳更快的高光谱图分类(PyTorch Geometric 实战)
  • rust编译过程的中间表现形式如何查看,ast,hir,mir
  • Rust 实战五 | 配置 Tauri 应用图标及解决 exe 被识别为威胁的问题
  • istio如何采集method、url指标
  • Rust:anyhow 高效错误处理库核心用法详解
  • Elasticsearch 官方 Node.js 从零到生产
  • 用 Node.js 玩转 Elasticsearch从安装到增删改查
  • 基于动态顺序表实现【通讯录系统】:有文件操作部分哦!
  • 用 Docker 安装并启动 Redis:从入门到实战
  • Spring AI赋能图像识别:大数据模型驱动下的智能化变革
  • Webpack Loader 完全指南:从原理到配置的深度解析
  • 关于JavaScript 性能优化的实战指南
  • MySQL的索引(索引的数据结构-B+树索引):
  • Godot ------ 平滑拖动01
  • vue3中的子组件向父组件通信和父组件向子组件通信
  • 对抗样本攻击检测与防御
  • STM32 ESP8266 WiFi模块驱动
  • JVM管理数据的方式
  • CV 医学影像分类、分割、目标检测,之分类项目拆解
  • 【Lua】题目小练10
  • explicit的作用是什么
  • GaussDB安全配置全景指南:构建企业级数据库防护体系
  • Mybatis学习之逆向工程(十)
  • Java项目基本流程(三)