【深度学习:进阶篇】--4.3.seq2seq与Attention机制

学习目标

  • 目标
    • 掌握seq2seq模型特点
    • 掌握集束搜索方式
    • 掌握BLEU评估方法
    • 掌握Attention机制
  • 应用
    • 应用Keras实现seq2seq对日期格式的翻译

目录

学习目标

1.seq2seq

1.1.定义

1.2.条件语言模型理解

1.3.应用场景

2.注意力机制

2.1.长句子问题

2.2.定义

2.3.公式

3.机器翻译案例

3.1.环境配置

 3.2.代码分析

3.3.加载数据

3.4.定义网络

3.5.定义模型

3.6.测试模型


1.seq2seq

seq2seq模型是在2014年,是由Google Brain团队和Yoshua Bengio 两个团队各自独立的提出来。

1.1.定义

seq2seq是一个Encoder–Decoder 结构的网络,它的输入是一个序列,输出也是一个序列, Encoder 中将一个可变长度的信号序列变为固定长度的向量表达Decoder 将这个固定长度的向量变成可变长度的目标的信号序列

注:Cell可以用 RNN ,GRU,LSTM 等结构。

  • 相当于将RNN模型当中的s0输入变成一个encoder

1.2.条件语言模型理解

  • 1、编解码器作用
    • 编码器的作用是把一个不定长的输入序列x​1​​,…,x​t​​,输出到一个编码状态C
    • 解码器输出y​t​​的条件概率将基于之前的输出序列y​1​​,y​t−1​​和编码状态C

argmaxP(y1,…,yT′∣x1,…,xT),给定输入的序列,使得输出序列的概率值最大。

  • 2、根据最大似然估计,最大化输出序列的概率

由于这个公式需要求出:...这个概率连乘会非常非常小不利于计算存储,所以需要对公式取对数计算:

所以这样就变成了..概率相加。

这样也可以看成输出结果通过softmax就变成了概率最大,而损失最小的问题,输出序列损失最小化。

1.3.应用场景

  • 神经机器翻译(NMT)

  • 聊天机器人

接下来我们来看注意力机制,那么普通的seq2seq会面临什么样的问题?

2.注意力机制

2.1.长句子问题

对于更长的句子,seq2seq就显得力不从心了,无法做到准确的翻译,一下是通常BLEU的分数随着句子的长度变化,可以看到句子非常长的时候,分数就很低。

BLEU(Bilingual Evaluation Understudy)是一种用于评估机器翻译质量的自动评价指标,它通过比较机器翻译结果与人工参考翻译之间的相似度来打分。BLEU 分数范围在 0 到 1 之间(或 0% 到 100%),分数越高表示翻译质量越好。

​BLEU 的核心思想​

  1. ​N-gram 匹配​​:计算机器翻译(候选文本)和参考翻译之间的 ​​n-gram(连续词序列)​​ 重叠程度。
    • 例如,1-gram(单个词)、2-gram(双词组合)等。
  2. ​精度(Precision)​​:统计候选翻译中有多少 n-gram 出现在参考翻译中,并做标准化(避免长句惩罚)。
  3. ​短句惩罚(Brevity Penalty, BP)​​:防止短翻译因匹配少量 n-gram 而获得高分。

本质原因:在Encoder-Decoder结构中,Encoder把所有的输入序列都编码成一个统一的语义特征C再解码,因此, C中必须包含原始序列中的所有信息,它的长度就成了限制模型性能的瓶颈。当要翻译的句子较长时,一个C可能存不下那么多信息,就会造成翻译精度的下降。

2.2.定义

  • 建立Encoder的隐层状态输出到Decoder对应输出y所需要的上下文信息
    • 目的:增加编码器信息输入到解码器中相同时刻的联系,其它时刻信息减弱

2.3.公式

注意上述的几个细节,颜色的连接深浅不一样,假设Encoder的时刻记为t,而Decoder的时刻记为t​​′​​​​。

  • 1、

    • α​t​​′​​​​t​​为参数,在网络中训练得到
    • 理解:蓝色的解码器中的cell举例子
  • 2、α​t​​′​​​​t​​的N个权重系数由来?

    • 权重系数通过softmax计算:\alpha_{t'}{t} = \frac{\exp(e_{t'}{t})}{\sum_{k=1}^{T}\exp(e_{t'}{k})}, \quad t = 1,\ldots,T
    • e_{t'}{t} = g(s_{t'-1}, h_t) = v^\top \tanh(W_s s + W_h h)
      • et′t​​是由t时刻的编码器隐层状态输出和解码器t​​′​​​​−1时刻的隐层状态输出计算出来的
      • s为解码器隐层状态输出,h为编码器隐层状态输出
      • v,Ws,Wh​​都是网络学习的参数

3.机器翻译案例

使用简单的“日期转换”任务代替翻译任务,为了不然训练时间变得太长。

网络将输入以各种可能格式(例如“1958年8月29日”,“03/30/1968”,“1987年6月24日”,“July 3, 2025”)编写的日期,并将其翻译成标准化的机器可读日期(例如“1958 -08-29“,”1968-03-30“,”1987-06-24“)。使用seq2seq网络学习以通用机器可读格式YYYY-MM-DD输出日期。

3.1.环境配置

pip install faker
pip install tqdm
pip install babel
pip install keras==2.2.4
  • faker:生成数据包
  • tqdm:python扩展包
  • babel:代码装换器
  • keras:更加方便简洁的深度学习库
    • 为了快速编写代码

 3.2.代码分析

  • Seq2seq():

    • 序列模型类
    • load_data(self,m):加载数据类,选择加载多少条数据
    • init_seq2seq(self):初始化模型,需要自定义自己的模型
      • self.get_encoder(self):定义编码器
      • self.get_decoder(self):定义解码器
      • self.get_attention(self):定义注意力机制
      • self.get_output_layer(self):定义解码器输出层
    • model(self):定义模型整体输入输出逻辑
    • train(self, X_onehot, Y_onehot):训练模型
    • test(self):测试模型
  • 训练

if __name__ == '__main__':s2s = Seq2seq()X_onehot, Y_onehot = s2s.load_data(10000)s2s.init_seq2seq()s2s.train(X_onehot, Y_onehot)#s2s.test()

整个数据集特征值的形状: (10000, 30, 37)
整个数据集目标值的形状: (10000, 10, 11)
查看第一条数据集格式:特征值:9 may 1998, 目标值: 1998-05-09
[12  0 24 13 34  0  4 12 12 11 36 36 36 36 36 36 36 36 36 36 36 36 36 36
 36 36 36 36 36 36] [ 2 10 10  9  0  1  6  0  1 10]
one_hot编码: [[0. 0. 0. ... 0. 0. 0.]
 [1. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 ...
 [0. 0. 0. ... 0. 0. 1.]
 [0. 0. 0. ... 0. 0. 1.]
 [0. 0. 0. ... 0. 0. 1.]] [[0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
 [1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
 [1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]]
Epoch 1/1
  100/10000 [..............................] - ETA: 10:52 - loss: 23.9884 - dense_1_loss: 2.3992 - dense_1_acc: 0.3200 - dense_1_acc_1: 0.0000e+00 - dense_1_acc_2: 0.0100 - dense_1_acc_3: 0.1300 - dense_1_acc_4: 0.0000e+00 - dense_1_acc_5: 0.0400 - dense_1_acc_6: 0.0900 - dense_1_acc_7: 0.0000e+00 - dense_1_acc_8: 0.3500 - dense_1_acc_9: 0.1100
  200/10000 [..............................] - ETA: 5:27 - loss: 23.9289 - dense_1_loss: 2.3991 - dense_1_acc: 0.2550 - dense_1_acc_1: 0.0000e+00 - dense_1_acc_2: 0.0050 - dense_1_acc_3: 0.1150 - dense_1_acc_4: 0.0950 - dense_1_acc_5: 0.0250 - dense_1_acc_6: 0.1150 - dense_1_acc_7: 0.0800 - dense_1_acc_8: 0.3400 - dense_1_acc_9: 0.1050

  • 测试

3.3.加载数据

  • 模型参数:
    def __init__(self, Tx=30, Ty=10, n_x=32, n_y=64):# 定义网络的相关参数self.model_param = {"Tx": Tx,  # 定义encoder序列最大长度"Ty": Ty,  # decoder序列最大长度"n_x": n_x,  # encoder的隐层输出值大小"n_y": n_y  # decoder的隐层输出值大小和cell输出值大小}

对于加载数据来说,我们不需要去进行编写逻辑了,看一下大概的逻辑.

  • 加载数据
    • 加载的代码逻辑在nmt_utils当中
      • from nmt_utils import *

nmt_utils.py:

import numpy as np
from faker import Faker  # 用于生成虚假数据
import random
from tqdm import tqdm  # 用于显示进度条
from babel.dates import format_date  # 用于格式化日期
from tensorflow.keras.utils import to_categorical  # 用于one-hot编码
import tensorflow.keras.backend as K  # Keras后端引擎
import matplotlib.pyplot as plt  # 绘图库# 设置随机种子以保证结果可复现
from faker import Faker
import random# 设置全局随机种子(推荐方式)
Faker.seed(12345)  # ✅ 使用类方法设置 Faker 的种子
random.seed(12345)  # 设置 Python random 模块的种子fake = Faker()  # 创建实例# 定义日期格式列表
FORMATS = ['short','medium','long','full','full','full','full','full','full','full','full','full','full','d MMM YYY','d MMMM YYY','dd MMM YYY','d MMM, YYY','d MMMM, YYY','dd, MMM YYY','d MM YY','d MMMM YYY','MMMM d YYY','MMMM d, YYY','dd.MM.YY']# 设置语言环境(可修改为其他语言)
LOCALES = ['en_US']#这是us英文,可修改为其他语言def load_date():"""生成虚假日期数据返回: 元组包含(人类可读日期字符串, 机器可读日期字符串, 日期对象)"""dt = fake.date_object()  # 生成随机日期对象,# 输出: datetime.date(2023, 5, 15)try:# 格式化人类可读日期human_readable = format_date(dt, format=random.choice(FORMATS), locale='en_US')# 格式化日期,locale为'en_US',可修改为其他语言,输出: 'May 15, 2023'human_readable = human_readable.lower()  # 转为小写,输出: 'may 15, 2023'human_readable = human_readable.replace(',', '')  # 移除逗号,输出:'may 15 2023'machine_readable = dt.isoformat()  # 机器可读格式,输出: '2023-05-15'except AttributeError as e:return None, None, Nonereturn human_readable, machine_readable, dtdef load_dataset(m):"""加载包含m个样本的数据集并构建词汇表:m: 要生成的样本数量返回:dataset -- 包含(人类可读日期字符串, 机器可读日期字符串)对的列表human -- 人类可读词汇表字典machine -- 机器可读词汇表字典"""human_vocab = set()  # 人类可读日期字符集machine_vocab = set()  # 机器可读日期字符集dataset = []  # 数据集Tx = 30  # 输入序列长度# 生成m个样本for i in tqdm(range(m)):h, m, _ = load_date()if h is not None:dataset.append((h, m))  # 添加(人类可读, 机器可读)对human_vocab.update(tuple(h))  # 更新人类可读字符集machine_vocab.update(tuple(m))  # 更新机器可读字符集# 构建人类可读词汇表字典(添加未知词和填充词标记)human = dict(zip(sorted(human_vocab) + ['<unk>', '<pad>'],list(range(len(human_vocab) + 2))))# 构建机器可读词汇表字典和反向字典inv_machine = dict(enumerate(sorted(machine_vocab)))machine = {v: k for k, v in inv_machine.items()}return dataset, human, machinedef preprocess_data(dataset, human_vocab, machine_vocab, Tx, Ty):"""预处理数据:将字符串转换为整数序列并进行one-hot编码"""X, Y = zip(*dataset)  # 解压数据集# 将字符串转换为整数序列X = np.array([string_to_int(i, Tx, human_vocab) for i in X])Y = [string_to_int(t, Ty, machine_vocab) for t in Y]# 进行one-hot编码Xoh = np.array(list(map(lambda x: to_categorical(x, num_classes=len(human_vocab)), X)))Yoh = np.array(list(map(lambda x: to_categorical(x, num_classes=len(machine_vocab)), Y)))return X, np.array(Y), Xoh, Yohdef string_to_int(string, length, vocab):"""将字符串转换为整数序列表示参数:string -- 输入字符串,如 'Wed 10 Jul 2007'length -- 时间步长,决定输出是填充还是截断vocab -- 词汇表字典返回:rep -- 整数列表(或'<unk>'),表示字符串字符在词汇表中的位置"""# 标准化处理:转为小写并移除逗号string = string.lower()string = string.replace(',', '')# 如果字符串过长则截断if len(string) > length:string = string[:length]# 将每个字符映射到词汇表中的索引,未知字符用'<unk>'表示rep = list(map(lambda x: vocab.get(x, '<unk>'), string))# 如果字符串过短则填充if len(string) < length:rep += [vocab['<pad>']] * (length - len(string))return repdef softmax(x, axis=1):"""Softmax激活函数参数:x: 张量axis: 应用softmax归一化的轴返回:softmax变换后的张量异常:当输入是一维张量时抛出ValueError"""ndim = K.ndim(x)if ndim == 2:return K.softmax(x)elif ndim > 2:e = K.exp(x - K.max(x, axis=axis, keepdims=True))s = K.sum(e, axis=axis, keepdims=True)return e / selse:raise ValueError('Cannot apply softmax to a tensor that is 1D')
函数名核心功能典型应用场景
​load_date()​1. 生成随机日期对象
2. 格式化为人类可读字符串(如"may 15 2023")
3. 生成机器可读ISO格式(如"2023-05-15")
生成单条日期训练样本
​load_dataset(m)​m个日期样本
2. 构建人类可读字符词汇表
3. 构建机器可读字符词汇表
4. 自动添加<unk><pad>特殊标记
初始化训练数据集和词汇表
​preprocess_data()​1. 将字符串转为整数序列
2. 统一序列长度(填充/截断)
3. 生成one-hot编码
4. 输出可直接用于Keras模型的张量
数据预处理管道
​string_to_int()​1. 字符到词汇表索引的映射
2. 处理未知字符(返回<unk>
3. 序列长度标准化(填充<pad>
文本到数值的转换
​softmax()​1. 实现多维度softmax
2. 数值稳定性优化(减最大值)
3. 支持Keras后端运算
神经网络激活函数

 主函数加载数据:

我们先看整个训练的逻辑,并从中来实现整个模型的定义,计算逻辑

    def load_data(self, m):"""指定获取m条数据:param m: 数据的总样本数:return:dataset:[('9 may 1998', '1998-05-09'), ('10.09.70', '1970-09-10')]x_vocab:翻译前的格式对应数字{' ': 0, '.': 1, '/': 2, '0': 3, '1': 4, '2': 5, '3': 6, '4': 7,....}y_vocab:翻译后的格式对应数字{'-': 0, '0': 1, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6, '6': 7, '7': 8, '8': 9, '9': 10}"""# 获取3个值:数据集,特征词的字典映射,目标词字典映射dataset, x_vocab, y_vocab = load_dataset(m)# 获取处理好的数据:特征x以及目标y的one_hot编码X, Y, X_onehot, Y_onehot = preprocess_data(dataset, x_vocab, y_vocab, self.model_param["Tx"], self.model_param["Ty"])print("整个数据集特征值的形状:", X_onehot.shape)print("整个数据集目标值的形状:", Y_onehot.shape)# 打印数据集print("查看第一条数据集格式:特征值:%s, 目标值: %s" % (dataset[0][0], dataset[0][1]))print(X[0], Y[0])print("one_hot编码:", X_onehot[0], Y_onehot[0])# 添加特征词个不重复个数以及目标词的不重复个数self.model_param["x_vocab"] = x_vocabself.model_param["y_vocab"] = y_vocabself.model_param["x_vocab_size"] = len(x_vocab)self.model_param["y_vocab_size"] = len(y_vocab)return X_onehot, Y_onehot

3.4.定义网络

  • (1)定义好网络的输入输出格式
  • (2)定义好优化器(选择Adam,参数lr=0.005, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.001)
    • from keras.optimizers import Adam
    • model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
  • (3)模型训练,
    • model.fit(inputs, outputs, epochs=1,batch_size=100)
def train(self, X_onehot, Y_onehot):"""训练:param X_onehot: 特征值的one_hot编码:param Y_onehot: 目标值的one_hot编码:return:"""# 利用网络结构定义好模型输入输出model = self.model()opt = Adam(lr=0.005, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.001)model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])s0 = np.zeros((10000, self.model_param["n_y"]))c0 = np.zeros((10000, self.model_param["n_y"]))outputs = list(Y_onehot.swapaxes(0, 1))# 输入x,以及decoder中LSTM的两个初始化值model.fit([X_onehot, s0, c0], outputs, epochs=1, batch_size=100)return None
  • (1)定义网络好的输入到输出流程
    • 步骤1、定义模型的输入
    • 步骤2:使用encoder的双向LSTM结构得输出a
    • 步骤3:循环decoder的Ty次序列输入,获取decoder最后输出
      • 1: 定义decoder第t'时刻的注意力结构并输出context
        • context = self.computer_one_attention(a, s)(需要实现Attention结构的计算过程)
      • 2: 对"context" 和初始两个状态s0,c0输入到deocder当中,返回两个输出
        • s, _, c = self.decoder(context, initial_state=[s, c])
      • 3: 应用 Dense layere获取deocder的t'时刻的输出 out = self.output_layer(s)
    • 步骤 4: 创建model实例,定义输入输出
      • from keras.models import Model

定义整个网络模型:

    def model(self):"""定义整个网络模型:return: keras 当中的model类型"""# 1、定义encoder的输入X (30, 37)X = Input(shape=(self.model_param["Tx"], self.model_param["x_vocab_size"]), name="X")# 定义一个初始输入的s0, 64大小s0 = Input(shape=(self.model_param["n_y"],), name="s0")c0 = Input(shape=(self.model_param["n_y"],), name="c0")s = s0c = c0# 定义一个装有输出的列表outputs = []# 2、输入到encoder当中,得到aa = self.encoder(X)# 3、计算输出结果,循环deocder当中t'个时刻,计算每个LSTM的输出结果for t in range(self.model_param["Ty"]):# (1)循环计算每一个时刻的contextcontext = self.computer_one_attention(a, s)# (2)输入s0,c0,context到某个时刻decoder得到下次的输出s, c# 因为是LSTM结构,所以有两个隐层状态,其中s可以用作输出s, _, c = self.decoder(context, initial_state=[s, c])# (3)s输出到最后一层softmax得到预测结果out = self.output_layer(s)outputs.append(out)# 输入输出定义好了model = Model(inputs=(X, s0, c0), outputs=outputs)return model

3.5.定义模型

模型初始化模型结构定义

  • 在训练中有一些模型结构,所以现需要定义这些结构统一初始化,这些模型结构作为整个Seq2Seq类的属性,初始化逻辑。
def init_seq2seq(self):"""初始化网络结构:return:"""# 添加encoder属性self.get_encoder()# 添加decoder属性self.get_decoder()# 添加attention属性self.get_attention()# 添加get_output_layer属性self.get_output_layer()return None
  • 定义编解码器、Attention机制、输出层

Keras是一个高级神经网络API,用Python编写,能够在TensorFlow之上运行。它的开发重点是实现快速实验。能够以最小的延迟从想法到结果是进行良好研究的关键。

如果您需要深度学习库,请使用Keras:允许简单快速的原型设计(通过用户友好性,模块化和可扩展性)

  • 编码器
    • 编码器:使用双向LSTM(隐层传递有双向值传递)
    • from keras.layers import LSTM, Bidirectional
    • LSTM(units, return_sequences=False,name="")
      • units: 正整数, units状态输出的维度
      • return_sequences:布尔类型 是否返回输出序列
      • return:LSTM layer
    • Bidirectional(layer, merge_mode='concat')
      • 对RNN、LSTM进行双向装饰
      • layer:RNN layer或者LSTM layer
      • merge_mode:将RNN/LSTM的前向和后向输出值进行合并
        • {'sum', 'mul', 'concat', 'ave', None}

   def get_encoder(self):"""定义编码器结构:return:"""# 指定隐层值输出的大小self.encoder = Bidirectional(LSTM(self.model_param["n_x"], return_sequences=True, name='bidirectional_1'), merge_mode='concat')return None
  • 解码器
    • return_state: 布尔,是否返回输出以及状态值
      • if return_state: 第一个值是output. 后面的值是状态值shape 为 (batch_size, units).

    def get_decoder(self):"""定义解码器结构:return:"""# 定义decoder结构,指定隐层值的形状大小,return_state=Trueself.decoder = LSTM(self.model_param["n_y"], return_state=True)return None
  • 输出层
    • from keras.layer import Dense
    • 指定一个普通的全连接层,并且可以指定激活函数
    • Dense(units, activation=None)
      • 神经元个数(输出大小)
      • activation=None:激活函数

    def get_output_layer(self):"""定义输出层:return: output_layer"""# 对decoder输出进行softmax,输出向量大小为y_vocab大小self.output_layer = Dense(self.model_param["y_vocab_size"], activation=softmax)return None

computer_one_attention函数实现:attention层结构

  • 1、定义结构
  • 2、实现输入输出结果

 

  • from keras.layers import RepeatVector, Concatenate, Dot, Activation

 def get_attention(self):"""定义Attention的结构:return: attention结构"""# 定义RepeatVector复制成多个维度repeator = RepeatVector(self.model_param["Tx"])# 进行矩阵拼接concatenator = Concatenate(axis=-1)# 进行全连接层10个神经元densor1 = Dense(10, activation="tanh", name='Dense1')# 接着relu函数densor2 = Dense(1, activation="relu", name='Dense2')# softmaxactivator = Activation(softmax,name='attention_weights')# context计算dotor = Dot(axes=1)# 将结构存储在attention当中self.attention = {"repeator": repeator,"concatenator": concatenator,"densor1": densor1,"densor2": densor2,"activator": activator,"dotor": dotor}return None
  • Attention输入输出逻辑
    • 使用Attention结构去实现输入到输出的逻辑
def computer_one_attention(self, a, s_prev):"""利用定义好的attention结构计算中的alpha系数与a对应输出:param a:隐层状态值 (m, Tx, 2*n_a):param s_prev: LSTM的初始隐层状态值, 形状(sample, n_s):return: context"""# 使用repeator扩大数据s_prev的维度为(sample, Tx, n_y),这样可以与a进行合并s_prev = self.attention["repeator"](s_prev)# 将a和s_prev 按照最后一个维度进行合并计算concat = self.attention["concatenator"]([a, s_prev])# 使用densor1全连接层网络计算出ee = self.attention["densor1"](concat)# 使用densor2增加relu激活函数计算energies = self.attention["densor2"](e)# 使用"activator"的softmax函数计算权重"alphas"# 这样一个attention的系数计算完成alphas = self.attention["activator"](energies)# 使用dotor,矩阵乘法,将 "alphas" and "a" 去计算context/ccontext = self.attention["dotor"]([alphas, a])return context

3.6.测试模型

训练:

  def train(self, X_onehot, Y_onehot):"""训练:param X_onehot: 特征值的one_hot编码:param Y_onehot: 目标值的one_hot编码:return:"""# 利用网络结构定义好模型输入输出model = self.model()opt = Adam(lr=0.005, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.001)model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])s0 = np.zeros((10000, self.model_param["n_y"]))c0 = np.zeros((10000, self.model_param["n_y"]))outputs = list(Y_onehot.swapaxes(0, 1))# 输入x,以及decoder中LSTM的两个初始化值model.fit([X_onehot, s0, c0], outputs, epochs=1, batch_size=100)return None

测试逻辑

  • model.load_weights(path):加载模型
def test(self):"""测试:return:"""model = self.model()model.load_weights("./models/model.h5")example = '1 March 2001'source = string_to_int(example, self.model_param["Tx"], self.model_param["x_vocab"])source = np.expand_dims(np.array(list(map(lambda x:to_categorical(x, num_classes=self.model_param["x_vocab_size"]),source))), axis=0)s0 = np.zeros((10000, self.model_param["n_y"]))c0 = np.zeros((10000, self.model_param["n_y"]))prediction = model.predict([source, s0, c0])prediction = np.argmax(prediction, axis=-1)output = [dict(zip(self.model_param["y_vocab"].values(), self.model_param["y_vocab"].keys()))[int(i)] for i in prediction]print("source:", example)print("output:", ''.join(output))return None

 

完整代码:

 RNN.py:

# 替换原来的导入方式
# 使用 tensorflow.keras 替代 keras 或 keras.src
from tensorflow.keras.layers import Input, Dense, RepeatVector, Concatenate, Dot, Activation, LSTM, Bidirectional
from tensorflow.keras.optimizers import Adam, SGD, RMSprop
from tensorflow.keras.models import Model
from tensorflow.keras.utils import to_categorical
from nmt_utils import *
import numpy as npclass Seq2seq(object):"""序列模型去进行日期的翻译"""def __init__(self, Tx=30, Ty=10, n_x=32, n_y=64):self.model_param = {"Tx": Tx,  # 定义encoder序列最大长度"Ty": Ty,  # decoder序列最大长度"n_x": n_x,  # encoder的隐层输出值大小"n_y": n_y  # decoder的隐层输出值大小和cell输出值大小}def load_data(self, m):"""指定获取m条数据:param m: 数据的总样本数:return:dataset:[('9 may 1998', '1998-05-09'), ('10.09.70', '1970-09-10')]x_vocab:翻译前的格式对应数字{' ': 0, '.': 1, '/': 2, '0': 3, '1': 4, '2': 5, '3': 6, '4': 7,....}y_vocab:翻译后的格式对应数字{'-': 0, '0': 1, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6, '6': 7, '7': 8, '8': 9, '9': 10}"""# 获取3个值:数据集,特征词的字典映射,目标词字典映射dataset, x_vocab, y_vocab = load_dataset(m)# 获取处理好的数据:特征x以及目标y的one_hot编码X, Y, X_onehot, Y_onehot = preprocess_data(dataset, x_vocab, y_vocab, self.model_param["Tx"], self.model_param["Ty"])print("整个数据集特征值的形状:", X_onehot.shape)print("整个数据集目标值的形状:", Y_onehot.shape)# 打印数据集print("查看第一条数据集格式:特征值:%s, 目标值: %s" % (dataset[0][0], dataset[0][1]))print(X[0], Y[0])print("one_hot编码:", X_onehot[0], Y_onehot[0])# 添加特征词个不重复个数以及目标词的不重复个数self.model_param["x_vocab"] = x_vocabself.model_param["y_vocab"] = y_vocabself.model_param["x_vocab_size"] = len(x_vocab)self.model_param["y_vocab_size"] = len(y_vocab)return X_onehot, Y_onehotdef get_encoder(self):"""获取encoder属性:return: None"""self.encoder = Bidirectional(LSTM(self.model_param["n_x"], return_sequences=True, name="bidirectional_1"), merge_mode='concat')return Nonedef get_decoder(self):"""获取deocder属性:return: None"""self.decoder = LSTM(self.model_param["n_y"], return_state=True)return Nonedef get_output_layer(self):"""获取输出层:return: None"""self.output_layer = Dense(self.model_param["y_vocab_size"], activation=softmax)return Nonedef get_attention(self):"""实现attention的结构属性:return: None"""# 1、定义Repeat函数repeator = RepeatVector(self.model_param["Tx"])# 2、定义Concat函数concatenator = Concatenate(axis=-1)# 3、定义Densedensor1 = Dense(10, activation="tanh", name="Dense1")densor2 = Dense(1, activation="relu", name='Dense2')# 4、Activatationactivator = Activation(softmax,name='attention_weights')# 5、Dot相当于npt.dotdotor = Dot(axes=1)# 将结构存储在attention当中self.attention = {"repeator": repeator,"concatenator": concatenator,"densor1": densor1,"densor2": densor2,"activator": activator,"dotor": dotor}return Nonedef init_seq2seq(self):"""初始化网络结构:return:"""# 添加encoder属性self.get_encoder()# 添加decoder属性self.get_decoder()# 添加attention属性self.get_attention()# 添加get_output_layer属性self.get_output_layer()return Nonedef computer_one_attention(self, a, s_prev):"""逻辑函数,计算context:param a: encoder的所有输出,t'时刻,a=t=1,2,3,4,......Tx:param s_prev: decoder的输出,t'-1:return: context"""# - 1、扩展s_prev的维度到encoder的所有时刻,编程Tx份s_prev = self.attention["repeator"](s_prev)# - 2、进行s_prev和a进行拼接concat = self.attention["concatenator"]([a, s_prev])# - 3、进行全连接计算得到e, 经过激活函数relu计算出e'e = self.attention["densor1"](concat)en = self.attention["densor2"](e)# - 4、e'进过softmax计算,得到系数,每个attention 有Tx个alphas参数alphas = self.attention["activator"](en)# - 5、系数与a进行计算得到contextcontext = self.attention["dotor"]([alphas, a])return contextdef model(self):"""定义整个网络模型:return: keras 当中的model类型"""# 1、定义encoder的输入X (30, 37)X = Input(shape=(self.model_param["Tx"], self.model_param["x_vocab_size"]), name="X")# 定义一个初始输入的s0, 64大小s0 = Input(shape=(self.model_param["n_y"],), name="s0")c0 = Input(shape=(self.model_param["n_y"],), name="c0")s = s0c = c0# 定义一个装有输出的列表outputs = []# 2、输入到encoder当中,得到aa = self.encoder(X)# 3、计算输出结果,循环deocder当中t'个时刻,计算每个LSTM的输出结果for t in range(self.model_param["Ty"]):# (1)循环计算每一个时刻的contextcontext = self.computer_one_attention(a, s)# (2)输入s0,c0,context到某个时刻decoder得到下次的输出s, c# 因为是LSTM结构,所以有两个隐层状态,其中s可以用作输出s, _, c = self.decoder(context, initial_state=[s, c])# (3)s输出到最后一层softmax得到预测结果out = self.output_layer(s)outputs.append(out)# 输入输出定义好了model = Model(inputs=(X, s0, c0), outputs=outputs)return modeldef train(self, X_onehot, Y_onehot):"""训练:param X_onehot: 特征值的one_hot编码:param Y_onehot: 目标值的one_hot编码:return:"""# 利用网络结构定义好模型输入输出model = self.model()opt = Adam(lr=0.005, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.001)model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])s0 = np.zeros((10000, self.model_param["n_y"]))c0 = np.zeros((10000, self.model_param["n_y"]))outputs = list(Y_onehot.swapaxes(0, 1))# 输入x,以及decoder中LSTM的两个初始化值model.fit([X_onehot, s0, c0], outputs, epochs=1, batch_size=100)return Nonedef test(self):model = self.model()model.load_weights("./models/model.h5")example = '1 March 2001'source = string_to_int(example, self.model_param["Tx"], self.model_param["x_vocab"])source = np.expand_dims(np.array(list(map(lambda x:to_categorical(x, num_classes=self.model_param["x_vocab_size"]),source))), axis=0)# 修改这里:s0和c0的形状应与source一致(1个样本)s0 = np.zeros((1, self.model_param["n_y"]))  # 原来是(10000, n_y)c0 = np.zeros((1, self.model_param["n_y"]))  # 原来是(10000, n_y)prediction = model.predict([source, s0, c0])prediction = np.argmax(prediction, axis=-1)output = [dict(zip(self.model_param["y_vocab"].values(),self.model_param["y_vocab"].keys()))[int(i)] for i in prediction]print("source:", example)print("output:", ''.join(output))if __name__ == '__main__':s2s = Seq2seq()X_onehot, Y_onehot = s2s.load_data(10000)s2s.init_seq2seq()# s2s.train(X_onehot, Y_onehot)s2s.test()

nmt_utils.py:

import numpy as np
from faker import Faker  # 用于生成虚假数据
import random
from tqdm import tqdm  # 用于显示进度条
from babel.dates import format_date  # 用于格式化日期
from tensorflow.keras.utils import to_categorical  # 用于one-hot编码
import tensorflow.keras.backend as K  # Keras后端引擎
import matplotlib.pyplot as plt  # 绘图库# 设置随机种子以保证结果可复现
from faker import Faker
import random# 设置全局随机种子(推荐方式)
Faker.seed(12345)  # ✅ 使用类方法设置 Faker 的种子
random.seed(12345)  # 设置 Python random 模块的种子fake = Faker()  # 创建实例# 定义日期格式列表
FORMATS = ['short','medium','long','full','full','full','full','full','full','full','full','full','full','d MMM YYY','d MMMM YYY','dd MMM YYY','d MMM, YYY','d MMMM, YYY','dd, MMM YYY','d MM YY','d MMMM YYY','MMMM d YYY','MMMM d, YYY','dd.MM.YY']# 设置语言环境(可修改为其他语言)
LOCALES = ['en_US']#这是us英文,可修改为其他语言def load_date():"""生成虚假日期数据返回: 元组包含(人类可读日期字符串, 机器可读日期字符串, 日期对象)"""dt = fake.date_object()  # 生成随机日期对象,# 输出: datetime.date(2023, 5, 15)try:# 格式化人类可读日期human_readable = format_date(dt, format=random.choice(FORMATS), locale='en_US')# 格式化日期,locale为'en_US',可修改为其他语言,输出: 'May 15, 2023'human_readable = human_readable.lower()  # 转为小写,输出: 'may 15, 2023'human_readable = human_readable.replace(',', '')  # 移除逗号,输出:'may 15 2023'machine_readable = dt.isoformat()  # 机器可读格式,输出: '2023-05-15'except AttributeError as e:return None, None, Nonereturn human_readable, machine_readable, dtdef load_dataset(m):"""加载包含m个样本的数据集并构建词汇表:m: 要生成的样本数量返回:dataset -- 包含(人类可读日期字符串, 机器可读日期字符串)对的列表human -- 人类可读词汇表字典machine -- 机器可读词汇表字典"""human_vocab = set()  # 人类可读日期字符集machine_vocab = set()  # 机器可读日期字符集dataset = []  # 数据集Tx = 30  # 输入序列长度# 生成m个样本for i in tqdm(range(m)):h, m, _ = load_date()if h is not None:dataset.append((h, m))  # 添加(人类可读, 机器可读)对human_vocab.update(tuple(h))  # 更新人类可读字符集machine_vocab.update(tuple(m))  # 更新机器可读字符集# 构建人类可读词汇表字典(添加未知词和填充词标记)human = dict(zip(sorted(human_vocab) + ['<unk>', '<pad>'],list(range(len(human_vocab) + 2))))# 构建机器可读词汇表字典和反向字典inv_machine = dict(enumerate(sorted(machine_vocab)))machine = {v: k for k, v in inv_machine.items()}return dataset, human, machinedef preprocess_data(dataset, human_vocab, machine_vocab, Tx, Ty):"""预处理数据:将字符串转换为整数序列并进行one-hot编码"""X, Y = zip(*dataset)  # 解压数据集# 将字符串转换为整数序列X = np.array([string_to_int(i, Tx, human_vocab) for i in X])Y = [string_to_int(t, Ty, machine_vocab) for t in Y]# 进行one-hot编码Xoh = np.array(list(map(lambda x: to_categorical(x, num_classes=len(human_vocab)), X)))Yoh = np.array(list(map(lambda x: to_categorical(x, num_classes=len(machine_vocab)), Y)))return X, np.array(Y), Xoh, Yohdef string_to_int(string, length, vocab):"""将字符串转换为整数序列表示参数:string -- 输入字符串,如 'Wed 10 Jul 2007'length -- 时间步长,决定输出是填充还是截断vocab -- 词汇表字典返回:rep -- 整数列表(或'<unk>'),表示字符串字符在词汇表中的位置"""# 标准化处理:转为小写并移除逗号string = string.lower()string = string.replace(',', '')# 如果字符串过长则截断if len(string) > length:string = string[:length]# 将每个字符映射到词汇表中的索引,未知字符用'<unk>'表示rep = list(map(lambda x: vocab.get(x, '<unk>'), string))# 如果字符串过短则填充if len(string) < length:rep += [vocab['<pad>']] * (length - len(string))return repdef softmax(x, axis=1):"""Softmax激活函数参数:x: 张量axis: 应用softmax归一化的轴返回:softmax变换后的张量异常:当输入是一维张量时抛出ValueError"""ndim = K.ndim(x)if ndim == 2:return K.softmax(x)elif ndim > 2:e = K.exp(x - K.max(x, axis=axis, keepdims=True))s = K.sum(e, axis=axis, keepdims=True)return e / selse:raise ValueError('Cannot apply softmax to a tensor that is 1D')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/87523.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/87523.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MYSQL与PostgreSQL的差异

一、架构设计的根本差异 进程模型 vs 线程模型 ​PostgreSQL​&#xff1a;采用多进程架构&#xff08;每个连接独立进程&#xff09;&#xff0c;通过共享内存通信。优势在于进程隔离性强&#xff0c;单连接崩溃不影响整体服务&#xff0c;但资源消耗较高。 ​MySQL​&…

Wpf布局之StackPanel!

文章目录 前言一、引言二、使用步骤 前言 Wpf布局之StackPanel&#xff01; 一、引言 StackPanel面板在水平或垂直的堆栈中放置元素。这个布局容器通常用于更大、更复杂窗口中的一些区域。 二、使用步骤 StackPanel默认是垂直堆叠 <Grid><StackPanel><Butt…

【MySQL】 内置函数

目录 1.时间函数2.字符串函数3.数学函数4.其他函数 1.时间函数 函数名称描述current_date()当前日期current_time()当前时间current_timestamp()当前时间戳date(datetime)返回datetime参数的日期部分date_add(date,interval d_value_type)在date中添加日期/时间&#xff0c;in…

【RK3568+PG2L50H开发板实验例程】Linux部分/FAN 检测案例

本原创文章由深圳市小眼睛科技有限公司创作&#xff0c;版权归本公司所有&#xff0c;如需转载&#xff0c;需授权并注明出处&#xff08;www.meyesemi.com) 1.案例简介 本案例旨在介绍如何测试开发板上风扇接口控制风扇启停与调速功能 2. FAN接口介绍 开发板上 FAN接口是一个…

Spring AI ETL Pipeline使用指南

前言&#xff08;Introduction&#xff09; 版本声明&#xff1a;本文基于 Spring AI 1.0.0 版本编写。由于 Spring AI 目前仍处于活跃开发阶段&#xff0c;API 和组件可能在后续版本中发生变化&#xff0c;请注意及时关注官方文档更新以保持兼容性。 在当今大数据和人工智能快…

Docker 入门教程(九):容器网络与通信机制

文章目录 &#x1f433; Docker 入门教程&#xff08;九&#xff09;&#xff1a;容器网络与通信机制一、Docker 网络模型二、Docker 的四种网络类型三、容器间通信机制四、相关指令 &#x1f433; Docker 入门教程&#xff08;九&#xff09;&#xff1a;容器网络与通信机制 一…

从进攻性安全角度简析 Windows PowerShell

PowerShell 是 Windows 系统中强大的脚本语言和命令行工具&#xff0c;因其灵活性和与 .NET 框架的深度集成&#xff0c;成为攻击者执行恶意操作的热门选择。从进攻性安全视角看&#xff0c;PowerShell 的语言模式、执行策略&#xff08;Execution Policy&#xff09;、AMSI 绕…

MySQL的深度分页如何优化!

MySQL深度分页&#xff08;例如 LIMIT 1000000, 20&#xff09;性能差的主要原因在于 OFFSET 需要扫描并跳过大量数据&#xff0c;即使这些数据最终并不返回。随着 OFFSET 增大&#xff0c;性能会急剧下降。 以下是优化深度分页的常用策略&#xff0c;根据场景选择最适合的方案…

K8s Pod 调度基础——1

目录 一、Replication Controller&ReplicaSet ‌一、Replication Controller (RC)‌ ‌原理‌ ‌特性‌ ‌意义‌ ‌示例与逐行解释‌ ‌二、ReplicaSet (RS)‌ ‌原理‌ ‌特性‌ ‌意义‌ ‌示例与逐行解释‌ ‌三、RC 与 RS 的对比‌ ‌四、总结‌ 二、Dea…

C# Task异步的常用方法

Task异步的常用方法 C# 中的 Task 类是 System.Threading.Tasks 命名空间的一部分&#xff0c;用于表示异步操作。 一、Task.Run(Action action): 此静态方法用于在后台运行一个新任务&#xff0c;并返回与该任务关联的 Task 实例。 本质是将任务放入线程池执行&#xff0c;自…

OpenResty实战之PB级物联网数据处理:时序数据库优化实战

某智慧能源平台通过本方案成功处理了日均1.2万亿数据点&#xff0c;存储成本降低70%&#xff0c;查询延迟从分钟级优化到亚秒级。本文将深入解析PB级物联网数据处理的核心挑战与时序数据库深度优化技巧。 一、物联网数据特性与存储挑战 1.1 物联网数据核心特征 #mermaid-svg-U…

聊聊架构(5)数字化时代的平台商业架构

在数字化浪潮的推动下&#xff0c;平台经济已成为全球经济增长的关键驱动力。作为架构师&#xff0c;不仅要精通架构设计的基础方法论&#xff0c;还需具备敏锐的商业洞察力。架构的价值在于服务业务和商业&#xff0c;而业务的发展又促使架构不断演进。本文将深入探讨平台的商…

【数据增强】精细化贴图数据增强

1.任务背景 假设我有100个苹果的照片&#xff0c;我需要把这些照片粘贴到传送带照片上&#xff0c;模拟“传送带苹果检测”场景。 这种贴图的方式更加合理一些&#xff0c;因为yolo之类的mosaic贴图&#xff0c;会把图像弄的非常支离破碎。 现在我需要随机选择几张苹果图像&am…

HTML响应式Web设计

什么是响应式Web设计&#xff1f; RWD指的是响应式Web设计&#xff08;Responsive Web Design)RWD能够以可变尺寸传递网页RWD对于平板和移动设备是必需的 创建一个响应式设计&#xff1a; <!DOCTYPE html> <html lang"en-US"> <head> <styl…

【读代码】百度开源大模型:ERNIE项目解析

一、项目基本介绍 1.1 项目概述 ERNIE(Enhanced Representation through kNowledge IntEgration)是百度基于PaddlePaddle深度学习框架开发的多模态预训练模型体系。最新发布的ERNIE 4.5系列包含10个不同变体,涵盖从300B参数的巨型MoE模型到0.3B的轻量级模型,形成完整的多…

2025年6月:技术探索与生活平衡的协奏曲

> 当代码与晨跑轨迹在初夏的阳光下交织,我找到了程序员生活的黄金分割点 --- ### 一、技术突破:AI驱动的智能工作流优化系统 这个月我成功部署了第三代自动化工作流系统,核心创新在于**动态决策树+实时反馈机制**。系统可自主优化处理路径,错误率下降62%! ```pyth…

如何查看服务器运行了哪些服务?

&#x1f7e2; 一、Linux服务器Linux下&#xff0c;常用以下几种方法&#xff1a;✅ 1. 查看所有正在监听端口的服务netstat -tulnp 含义&#xff1a;-t TCP-u UDP-l 监听状态-n 显示端口号-p 显示进程号和程序名示例输出&#xff1a;pgsql复制编辑Proto Recv-Q Send-Q Local A…

【Linux基础知识系列】第三十八篇 - 打印系统与 PDF 工具

在Linux系统中&#xff0c;打印和PDF处理是日常办公和文档管理中不可或缺的功能。CUPS&#xff08;Common Unix Printing System&#xff09;是Linux中常用的打印服务&#xff0c;它提供了打印任务的管理和打印设备的配置功能。同时&#xff0c;Linux也提供了多种PDF处理工具&a…

STM32CUBEMX 使用教程6 — TIM 定时器配置、定时中断

往期文章推荐&#xff1a; STM32CUBEMX 使用教程5 — DMA配置 & 串口结合DMA实现数据搬运 STM32CUBEMX 使用教程4 — 串口 (USART) 配置、重定向 printf 输出 STM32CUBEMX 使用教程3 — 外部中断&#xff08;EXTI&#xff09;的使用 STM32CUBEMX 使用教程2 — GPIO的使…

微信小程序实现table表格

微信小程序没有table标签&#xff0c;运用display:table和display:flex实现一个内容字数不固定表格…… wxml&#xff1a; <view class"ContentShow"> <view class"conht">烟台市新闻发布会登记审批表</view> <view class"tabl…