Seq2Seq(keras)的理解:关于encoder_model和decoder_model

一、三个model

(一)Seq2Seq之前的keras model:

1、创建模型

# 多层感知机(MLP)模型
from keras.models import Model
from keras.layers import Input, Dense
from keras.utils import plot_model

mnist_input = Input(shape=(784,), name='input')
hidden1 = Dense(512, activation='relu', name='hidden1')(mnist_input)
hidden2 = Dense(216, activation='relu', name='hidden2')(hidden1)
hidden3 = Dense(128, activation='relu', name='hidden3')(hidden2)
output = Dense(10, activation='softmax', name='output')(hidden3)

model = Model(inputs=mnist_input, outputs=output)

# 打印网络结构
model.summary()
model.save(r"xxx.h5")
# 產生網絡拓撲圖
plot_model(model, to_file='模型.png')

在这里插入图片描述

2、模型预测

model = load_model(r"xxx.h5")
model.predict()

(二)Seq2Seq的keras model

完整代码链接: 十分钟介绍 Keras 中的序列到序列学习.
此处会有三个模型需要进行保存

1.训练模型:
from keras.models import Model
from keras.layers import Input, LSTM, Dense

# Define an input sequence and process it.
encoder_inputs = Input(shape=(None, num_encoder_tokens))
encoder = LSTM(latent_dim, return_state=True)
encoder_outputs, state_h, state_c = encoder(encoder_inputs)
# We discard `encoder_outputs` and only keep the states.
encoder_states = [state_h, state_c]

# Set up the decoder, using `encoder_states` as initial state.
decoder_inputs = Input(shape=(None, num_decoder_tokens))
# We set up our decoder to return full output sequences,
# and to return internal states as well. We don't use the 
# return states in the training model, but we will use them in inference.
decoder_lstm = LSTM(latent_dim, return_sequences=True, return_state=True)
decoder_outputs, _, _ = decoder_lstm(decoder_inputs,
                                     initial_state=encoder_states)
decoder_dense = Dense(num_decoder_tokens, activation='softmax')
decoder_outputs = decoder_dense(decoder_outputs)

# Define the model that will turn
# `encoder_input_data` & `decoder_input_data` into `decoder_target_data`
model = Model([encoder_inputs, decoder_inputs], decoder_outputs)



2.推理模型
2.1编码模型
encoder_model = Model(encoder_inputs, encoder_states)
2.2解码模型
decoder_state_input_h = Input(shape=(latent_dim,))
decoder_state_input_c = Input(shape=(latent_dim,))
decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c]
decoder_outputs, state_h, state_c = decoder_lstm(
    decoder_inputs, initial_state=decoder_states_inputs)
decoder_states = [state_h, state_c]
decoder_outputs = decoder_dense(decoder_outputs)
decoder_model = Model(
    [decoder_inputs] + decoder_states_inputs,
    [decoder_outputs] + decoder_states)

总共三个模型!!!!!
初见,抓狂!!!
里面的lstm,state_h, state_c已经特别难理解了,后面还要三个模型,于是放弃治疗了。

二、再次遇见seq2seq

理解seq2seq有几个难点

难点一:lstm

通过可视化的操作理解了lstm
在这里插入图片描述

可视化的理解链接: LSTM这一篇就够了.

难点二:return_sequences和return_state

return_sequences:即返回序列,若为True,则返回每一个序列
要理解这一点,首先要明白rnn的基本原理

以自然语言处理为例:
1、我们的输入通常为字符级即一个一个字输入rnn中,
正常来说每个输入就有一个输出。
2、但是在文本分类或者情感分类中,
我们得到的结果为几个类别,如积极、消极,
从字符上来说明显不对应
3、这就是因为return_sequences=False的结果,
因为RNN会将预测的每个输入作为下一个输入的一起进行输入,
即输入为[y(上一个x的输出),x(当前输入)]
如下图:

在这里插入图片描述

return_sequences=True
就是每个时间步也就是每个x的y都输出来
如下文实验所展示的:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

而return_state则为计算每一个时间步的y即return_sequences的计算方式
如下文实验

在这里插入图片描述
在这里插入图片描述

这两个就决定了state_h, state_c的出现与否,
即return_sequences=state_h,return_state=state_c

综上我的理解就是,每个输入的x以某种计算方式计算出每个y,每个以某种方式计算出最终的y.
即f(f(x))=y

state_h,state_c就是提取计算最终y的f(),将编码器中的f()找出来然后作为解码器的初始参数,然后解码,实现可变长度序列。

难点三:三个模型

本质:三个模型本质上时一个模型
我重新修改了一下代码写的方式:
"""设计模型"""
def creat_model():
    # 定义输入的序列
    # 注意:因为输入序列长度(timesteps)可变的情况,使用input_shape =(None,num_features)
    encoder_inputs = Input(shape=(None, num_encoder_tokens), name='encoder_input')
    """后为特征数,前为批次数量!!!"""
    encoder = LSTM(latent_dim, return_state=True, name='encoder_lstm')  # 需要取得LSTM的内部state, 因此设定"return_state=True"
    encoder_outputs, state_h, state_c = encoder(encoder_inputs)
    """输出,输入最后一个状态(和前一个是一样的相当于下一轮输入的x),隐藏计算f"""
    # 我们抛弃掉`encoder_outputs`因为我们只需要LSTM cell的内部state参数
    encoder_states = [state_h, state_c]

    ###############解码器是一个模型,编码器只是解码器模型的前期数据处理!!!!!!!!!
    # ==== 解码 (decoder) ====

    # 设定解码器(decoder)
    # 注意:因为输出序列的长度(timesteps)是变动的,使用input_shape =(None,num_features)
    decoder_inputs = Input(shape=(None, num_decoder_tokens), name='decoder_input')

    # 我们设定我们的解码器回传整个输出的序列同时也回传内部的states参数
    decoder_lstm = LSTM(latent_dim, return_sequences=True, return_state=True, name='decoder_lstm')

    # 在训练时我们不会使用这些回传的解码的states, 但是在预测时我们会用到这些解码器states参数?????
    # **解码器的初始状态是使用编码器的最后的状态(states)**
    decoder_outputs, _, _ = decoder_lstm(decoder_inputs,
                                         initial_state=encoder_states)  # 我们使用`encoder_states`来做为初始值(initial state) <-- 重要

    # 接密集层(dense)来进行softmax运算每一个字符可能的机率
    decoder_dense = Dense(num_decoder_tokens, activation='softmax', name='decoder_output')
    decoder_outputs = decoder_dense(decoder_outputs)

    # 定义一个模型接收encoder_input_data` & `decoder_input_data`做为输入而输出`decoder_target_data`
    model = Model([encoder_inputs, decoder_inputs], decoder_outputs)

    # 打印出模型结构
    #model.summary()
    # from keras.utils import plot_model
    # 拓扑图
    # plot_model(model, to_file='seq2seq_graph.png')
    return model
"""这就是最开始的那个训练模型"""

#其余两个模型只是对这个模型参数的提取
model = creat_model()
# 设定模型超参数
model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=["accuracy"])

# 开始训练
model.fit([encoder_input_data, decoder_input_data], decoder_target_data,
          batch_size=256,
          epochs=2,
          validation_split=0.2)
model.save('model\s2s1.h5')

"""预测时"""
for layer in model.layers:
    if layer.name =="encoder_lstm":
        print(layer.name)
        encoder_lstm = layer
        encoder_inputs = Input(shape=(None, num_encoder_tokens), name='encoder_input')
        encoder_outputs, state_h, state_c = encoder_lstm(encoder_inputs)
        encoder_states = [state_h, state_c]
        encoder_model = Model(encoder_inputs, encoder_states)
    elif layer.name == "decoder_lstm" :
        decoder_lstm = layer
    elif layer.name == "decoder_output":
        decoder_dense = layer

        decoder_inputs = Input(shape=(None, num_decoder_tokens), name='decoder_input')
        decoder_state_input_h = Input(shape=(latent_dim,))
        decoder_state_input_c = Input(shape=(latent_dim,))
        decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c]

        # # 解码器(decoder)定义初始状态(initial decoder state)
        """用上_,_"""
        decoder_outputs, state_h, state_c = decoder_lstm(
            decoder_inputs, initial_state=decoder_states_inputs)  # 我们使用`decoder_states_inputs`来做为初始值(initial state)

        decoder_states = [state_h, state_c]
        decoder_outputs = decoder_dense(decoder_outputs)

        # 定义解码器(decoder)的模型
        decoder_model = Model([decoder_inputs] + decoder_states_inputs, [decoder_outputs] + decoder_states)

三、seq2seq全部代码

import  warnings
from keras.optimizers import Adam
from keras.models import load_model
warnings.filterwarnings("ignore")
from  keras.models import  Model
from  keras.layers  import  Input,LSTM,Dense
import  numpy  as  np
import  os

#专案的根目录路径
ROOT_DIR  =  os.getcwd ()

#置放训练数据的目录
DATA_PATH  =  os.path.join ( ROOT_DIR,"data" )

#训练数据档
DATA_FILE  =  os.path.join ( DATA_PATH , "cmn-tw.txt" )

batch_size  =  32  #训练时的批次数量
epochs  =  100  #训练循环数
latent_dim  =  128  #编码后的潜在空间的维度(dimensions of latent space)
num_samples  =  10000  #用来训练的样本数

"""数据向量化"""
input_texts = []
target_texts = []
input_characters = set()  # 英文字符集
target_characters = set()  # 中文字符集
lines = open(DATA_FILE, mode="r", encoding="utf-8").read().split('\n')

# 逐行的读取与处理
for line in lines[:min(num_samples, len(lines) - 1)]:
    input_text, target_text = line.split('\t')

    # 我们使用“tab”作为“开始序列[SOS]”字符或目标,“\n”作为“结束序列[ EOS]”字符。<-- **重要
    target_text = '\t' + target_text + '\n'

    input_texts.append(input_text)
    target_texts.append(target_text)

    for char in input_text:
        if char not in input_characters:
            input_characters.add(char)
    for char in target_text:
        if char not in target_characters:
            target_characters.add(char)

input_characters = sorted(list(input_characters))  # 全部输入的字符集
target_characters = sorted(list(target_characters))  # 全部目标字符集

num_encoder_tokens = len(input_characters)  # 所有输入字符的数量
num_decoder_tokens = len(target_characters)  # 所有输目标字符的数量

max_encoder_seq_length = max([len(txt) for txt in input_texts])  # 最长的输入句子长度
max_decoder_seq_length = max([len(txt) for txt in target_texts])  # 最长的目标句子长度

"""基本信息"""
print('Number of samples:', len(input_texts))
print('Number of unique input tokens:', num_encoder_tokens)
print('Number of unique output tokens:', num_decoder_tokens)
print('Max sequence length for inputs:', max_encoder_seq_length)
print('Max sequence length for outputs:', max_decoder_seq_length)

# 输入字符的索引字典
input_token_index = dict(
    [(char, i) for i, char in enumerate(input_characters)])

# 输目标字符的索引字典
target_token_index = dict(
    [(char, i) for i, char in enumerate(target_characters)])

# 包含英文句子的one-hot向量化的三维形状数组(num_pairs,max_english_sentence_length,num_english_characters)
encoder_input_data = np.zeros(
    (len(input_texts), max_encoder_seq_length, num_encoder_tokens),
    dtype='float32')

# 包含中文句子的one-hot向量化的三维形状数组(num_pairs,max_chinese_sentence_length,num_chinese_characters)
decoder_input_data = np.zeros(
    (len(input_texts), max_decoder_seq_length, num_decoder_tokens),
    dtype='float32')

# decoder_target_data与decoder_input_data相同,但是偏移了一个时间步长。
# decoder_target_data [:, t,:]将与decoder_input_data [:,t + 1,:]相同
decoder_target_data = np.zeros(
    (len(input_texts), max_decoder_seq_length, num_decoder_tokens),
    dtype='float32')

# 把数据转换成要用来训练用的张量数据结构<--重要
for i, (input_text, target_text) in enumerate(zip(input_texts, target_texts)):
    for t, char in enumerate(input_text):
        encoder_input_data[i, t, input_token_index[char]] = 1.
        """在全零向量中找出为1的向量,这些即为索引"""

    for t, char in enumerate(target_text):
        # decoder_target_data 比decoder_input_data 领先一个时间步??????????
        """推断下一个字??????????"""
        decoder_input_data[i, t, target_token_index[char]] = 1.
        if t > 0:
            # decoder_target_data will be ahead by one timestep
            # and will not include the start character.
            decoder_target_data[i, t - 1, target_token_index[char]] = 1.


"""设计模型"""
def creat_model():
    # 定义输入的序列
    # 注意:因为输入序列长度(timesteps)可变的情况,使用input_shape =(None,num_features)
    encoder_inputs = Input(shape=(None, num_encoder_tokens), name='encoder_input')
    """后为特征数,前为批次数量!!!"""
    encoder = LSTM(latent_dim, return_state=True, name='encoder_lstm')  # 需要取得LSTM的内部state, 因此设定"return_state=True"
    encoder_outputs, state_h, state_c = encoder(encoder_inputs)
    """输出,输入最后一个状态(和前一个是一样的相当于下一轮输入的x),隐藏计算f"""
    # 我们抛弃掉`encoder_outputs`因为我们只需要LSTM cell的内部state参数
    encoder_states = [state_h, state_c]

    ###############解码器是一个模型,编码器只是解码器模型的前期数据处理!!!!!!!!!
    # ==== 解码 (decoder) ====

    # 设定解码器(decoder)
    # 注意:因为输出序列的长度(timesteps)是变动的,使用input_shape =(None,num_features)
    decoder_inputs = Input(shape=(None, num_decoder_tokens), name='decoder_input')

    # 我们设定我们的解码器回传整个输出的序列同时也回传内部的states参数
    decoder_lstm = LSTM(latent_dim, return_sequences=True, return_state=True, name='decoder_lstm')

    # 在训练时我们不会使用这些回传的解码的states, 但是在预测时我们会用到这些解码器states参数?????
    # **解码器的初始状态是使用编码器的最后的状态(states)**
    decoder_outputs, _, _ = decoder_lstm(decoder_inputs,
                                         initial_state=encoder_states)  # 我们使用`encoder_states`来做为初始值(initial state) <-- 重要

    # 接密集层(dense)来进行softmax运算每一个字符可能的机率
    decoder_dense = Dense(num_decoder_tokens, activation='softmax', name='decoder_output')
    decoder_outputs = decoder_dense(decoder_outputs)

    # 定义一个模型接收encoder_input_data` & `decoder_input_data`做为输入而输出`decoder_target_data`
    model = Model([encoder_inputs, decoder_inputs], decoder_outputs)

    # 打印出模型结构
    #model.summary()
    # from keras.utils import plot_model
    # 拓扑图
    # plot_model(model, to_file='seq2seq_graph.png')
    return model
#model,encoder_model,decoder_model=creat_model()
if os.path.exists(r"model\s2s.h5"):

    model = load_model(r"model\s2s.h5")
    model.summary()
else:
    model = creat_model()
model = creat_model()
# 设定模型超参数
model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=["accuracy"])

# 开始训练
model.fit([encoder_input_data, decoder_input_data], decoder_target_data,
          batch_size=256,
          epochs=2,
          validation_split=0.2)
model.save('model\s2s1.h5')
for layer in model.layers:
    if layer.name =="encoder_lstm":
        print(layer.name)
        encoder_lstm = layer
        encoder_inputs = Input(shape=(None, num_encoder_tokens), name='encoder_input')
        encoder_outputs, state_h, state_c = encoder_lstm(encoder_inputs)
        encoder_states = [state_h, state_c]
        encoder_model = Model(encoder_inputs, encoder_states)
    elif layer.name == "decoder_lstm" :
        decoder_lstm = layer
    elif layer.name == "decoder_output":
        decoder_dense = layer

        decoder_inputs = Input(shape=(None, num_decoder_tokens), name='decoder_input')
        decoder_state_input_h = Input(shape=(latent_dim,))
        decoder_state_input_c = Input(shape=(latent_dim,))
        decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c]

        # # 解码器(decoder)定义初始状态(initial decoder state)
        """用上_,_"""
        decoder_outputs, state_h, state_c = decoder_lstm(
            decoder_inputs, initial_state=decoder_states_inputs)  # 我们使用`decoder_states_inputs`来做为初始值(initial state)

        decoder_states = [state_h, state_c]
        decoder_outputs = decoder_dense(decoder_outputs)

        # 定义解码器(decoder)的模型
        decoder_model = Model([decoder_inputs] + decoder_states_inputs, [decoder_outputs] + decoder_states)

reverse_input_char_index = dict(
    (i, char) for char, i in input_token_index.items())

reverse_target_char_index = dict(
    (i, char) for char, i in target_token_index.items())

# 对序列进行解码
def decode_sequence(input_seq):
    # 将输入编码成为state向量中间语义向量
    states_value = encoder_model.predict(input_seq)

    # 产生长度为1的空白目标序列
    target_seq = np.zeros((1, 1, num_decoder_tokens))

    # 添加特定的目标序列起始字符"[SOS]",在这个范例中是使用 "\t"字符,为什么要增加起始符,不是训练中有吗?????
    #因为输入的数据没有起始符????
    target_seq[0, 0, target_token_index['\t']] = 1.

    # 对批次的序列进行抽样
    stop_condition = False
    decoded_sentence = ''
    while not stop_condition:
        """解码器是新模型!!!!"""
        output_tokens, h, c = decoder_model.predict(
            [target_seq] + states_value)

        # 对标签抽样,一个一个预测
        sampled_token_index = np.argmax(output_tokens[0, -1, :])
        sampled_char = reverse_target_char_index[sampled_token_index]
        decoded_sentence += sampled_char

        # 停止循环的条件: 到达最大的长度或是找到"停止[EOS]"字符,在这个范例中是使用 "\n"字符
        if (sampled_char == '\n' or
                len(decoded_sentence) > max_decoder_seq_length):
            stop_condition = True

        # 更新目标序列(of length 1).
        target_seq = np.zeros((1, 1, num_decoder_tokens))
        target_seq[0, 0, sampled_token_index] = 1.

        # 更新 states
        states_value = [h, c]

    return decoded_sentence


for seq_index in range(100):
    # 从训练集中取出一个序列并试著解码
    input_seq = encoder_input_data[seq_index: seq_index + 1]
    decoded_sentence = decode_sequence(input_seq)
    print('-')
    print('Input sentence:', input_texts[seq_index])
    print('Decoded sentence:', decoded_sentence)

参考文章:
《Keras中return_sequences和return_state有什么用?》 - 知乎
链接: Keras中return_sequences和return_state有什么用?.
《LSTM神经网络输入输出究竟是怎样的?》 - 知乎
链接: LSTM神经网络输入输出究竟是怎样的?.

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
这个错误发生在使用 TensorFlow seq2seq 模型训练时,因为 `encoder_input_data` 、`decoder_input_data` 和 `decoder_target_data` 这三个变量没有被定义,Python 解释器无法识别这些变量。解决这个问题,你需要定义这些变量并将它们传递给 `model.fit()` 函数。以下是一个可能的解决方案示例: ```python import tensorflow as tf import numpy as np # 定义模型参数 vocab_size = 10000 embedding_dim = 128 hidden_units = 256 max_len = 50 # 定义编码器和解码器的输入 encoder_inputs = tf.keras.layers.Input(shape=(max_len,)) decoder_inputs = tf.keras.layers.Input(shape=(max_len,)) # 定义编码器和解码器的嵌入层 encoder_embedded = tf.keras.layers.Embedding(vocab_size, embedding_dim)(encoder_inputs) decoder_embedded = tf.keras.layers.Embedding(vocab_size, embedding_dim)(decoder_inputs) # 定义编码器和解码器的 LSTM 层 encoder_lstm = tf.keras.layers.LSTM(hidden_units, return_state=True) decoder_lstm = tf.keras.layers.LSTM(hidden_units, return_sequences=True, return_state=True) # 编码器的输出和状态 encoder_outputs, state_h, state_c = encoder_lstm(encoder_embedded) encoder_states = [state_h, state_c] # 解码器的输出和状态 decoder_outputs, _, _ = decoder_lstm(decoder_embedded, initial_state=encoder_states) # 定义全连接层,并将输出转换为概率分布 decoder_dense = tf.keras.layers.Dense(vocab_size, activation='softmax') decoder_outputs = decoder_dense(decoder_outputs) # 定义模型 model = tf.keras.models.Model([encoder_inputs, decoder_inputs], decoder_outputs) # 编译模型 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # 生成训练数据 encoder_input_data = np.random.randint(0, vocab_size, size=(1000, max_len)) decoder_input_data = np.random.randint(0, vocab_size, size=(1000, max_len)) decoder_target_data = np.random.randint(0, vocab_size, size=(1000, max_len, vocab_size)) # 训练模型 model.fit([encoder_input_data, decoder_input_data], decoder_target_data, batch_size=64, epochs=50, validation_split=0.2) ``` 在上面的示例中,我们首先定义了模型的参数,然后使用 `Input` 和 `Embedding` 层定义了编码器和解码器的输入和嵌入层。接下来,我们使用 LSTM 层和 `Dense` 层定义了编码器和解码器的计算图,并使用 `Model` 类将这些层组合在一起形成一个完整的模型。然后,我们使用 `compile` 函数编译模型,并使用 `fit` 函数训练模型。最后,我们生成了训练数据 `encoder_input_data`、`decoder_input_data` 和 `decoder_target_data`,并将它们传递给 `model.fit()` 函数。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值