seq2seq2

# -- encoding:utf-8 --
"""
原始数据:
[小明, 吃, 苹果] ----> [xiao, ming, eats, apples]
模型训练的时候,将原始数据进行转换:
编码器输入:
    [小明, 吃, 苹果]
解码器输入:
    无
解码器输出(实际值):
    [xiao, ming, eats, apples, EOS]

"""

import tensorflow as tf


def build_interface(encoder_inputs, encoder_vocab_size, decoder_inputs, decoder_vocab_size,
                    embedding_size=128, rnn_num_units=256, is_training=True,
                    project_output_weight=None, project_output_bias=None):
    """
    前向网络的构建
    :param encoder_inputs: List列表,内部为Tensor对象,编码器输入
    :param encoder_vocab_size: 编码器对应的词汇数目
    :param decoder_inputs:  List列表,内部为Tensor对象,解码器的输入
    :param decoder_vocab_size: 解码器对应的词汇数目
    :param embedding_size: 网络结构中,embedding操作的时候,转换的向量维度大小
    :param rnn_num_units: 网络结构中,RNN的神经元数目
    :param is_training: 训练还是推理预测
    :param project_output_weight: 推理预测的时候,所使用的全连接参数
    :param project_output_bias: 推理预测的时候,所使用的全连接参数
    :return:  返回解码器的输出(仅解码器输出, 还没有涉及到全连接的操作)
    """
    with tf.variable_scope("seq2seq"):
        # 一、编码器
        with tf.variable_scope("encoder"):
            # 1. 对输入的单词id做embedding转换,得到其对应的向量
            encoder_embedding_inputs = []
            with tf.variable_scope("embedding"):
                encoder_embedding_table = tf.get_variable(name="embedding_table",
                                                          shape=[encoder_vocab_size, embedding_size])
                for encoder_input in encoder_inputs:
                    # 将encoder_input从[N,]形状转换为[N,embedding_size]的形状, 也就是对于每个单词id转换其对应的词向量
                    encoder_embedding_inputs.append(tf.nn.embedding_lookup(encoder_embedding_table, encoder_input))
            # encoder_embedding_inputs: list([N,embedding_size], [N,embedding_size], ....)

            # 2. 对embedding转换后的值,做RNN操作,得到输出信息
            with tf.variable_scope("rnn"):
                # a. 构建rnn对象
                encoder_cell = tf.nn.rnn_cell.BasicRNNCell(num_units=rnn_num_units)
                # b. 执行得到编码器的输出值以及最终的状态值
                # encoder_output: list列表,列表内为每个时刻对应的输出值,类型为Tensor,形状为: [N,rnn_num_units]
                # encoder_state: Tensor对象,最后一个时刻的细胞状态值;在RNNCell,形状为:[N,rnn_num_units]
                encoder_output, encoder_state = tf.nn.static_rnn(
                    cell=encoder_cell,  # RNN Cell对象
                    inputs=encoder_embedding_inputs,  # 各个时刻的输入Tensor所组成的List列表
                    dtype=tf.float32  # 数据类型,会用来构建初始状态值(默认初始状态值为zero)
                )

        # 二、解码器
        with tf.variable_scope("decoder"):
            # 1. 对输入的单词id做embedding转换,得到其对应的向量
            decoder_embedding_inputs = []
            with tf.variable_scope("embedding"):
                decoder_embedding_table = tf.get_variable(name="embedding_table", shape=[decoder_vocab_size, 1])
                for decoder_input in decoder_inputs:
                    # 将decoder_input从[N,]形状转换为[N,embedding_size]的形状, 也就是对于每个单词id转换其对应的词向量
                    decoder_embedding_inputs.append(tf.nn.embedding_lookup(decoder_embedding_table, decoder_input))
            # decoder_embedding_inputs: list([N,embedding_size], [N,embedding_size], ....)

            # 2. 对embedding之后的结果做解码操作
            with tf.variable_scope("rnn"):
                # a. 构建rnn对象
                decoder_cell = tf.nn.rnn_cell.BasicRNNCell(num_units=rnn_num_units)
                # b. 遍历每个时刻,得到其预测结果
                state = encoder_state  # 将编码器的最终状态作为解码器的初始状态
                zero_inputs = [tf.zeros_like(decoder_embedding_inputs[0])] * len(decoder_embedding_inputs)
                # 直接调用得到rnn的输出
                outputs, _ = tf.nn.static_rnn(cell=decoder_cell, inputs=zero_inputs, initial_state=state)
                # c. 返回结果
                return outputs


def train():
    # 一、定义编码器的输入
    # 假设编码器有5个时刻输出,每个时刻输出的是具体对应的单词id;如果数据长度不够,那么进行填充,如果数据太长,进行截断。
    encoder_inputs = []
    for idx in range(5):
        encoder_inputs.append(tf.placeholder(dtype=tf.int32, shape=[None], name="encoder_{}".format(idx)))

    # 二、定义解码器的输出和输出
    # 假设解码器实际长度为6个时刻,加上特殊一个特殊时刻(输入:GO, 输出:EOS);如果数据长度不够,那么进行填充,如果数据太长,进行截断。
    decoder_placeholders = []
    for idx in range(8):
        decoder_placeholders.append(tf.placeholder(dtype=tf.int32, shape=[None], name='decoder_{}'.format(idx)))
    decoder_inputs = decoder_placeholders[:-1]  # 解码器的输入(7个时刻=6个实际值+1个特殊值)
    decoder_targets = decoder_placeholders[1:]  # 解码器的输出(7个时刻=6个实际值+1个特殊值)

    # 三、前向网络的构建,得到解码器RNN的输出
    project_output_weight = tf.get_variable("w", shape=[256, 7897])
    project_output_bias = tf.get_variable("b", shape=[7897])
    # decoder_rnn_outputs: List列表,内部为解码器RNN的每个时刻的输出;形状为:[N,rnn_num_units]
    decoder_rnn_outputs = build_interface(
        encoder_inputs=encoder_inputs,  # 编码器输入,List列表
        encoder_vocab_size=10000,  # 编码器词汇数目
        decoder_inputs=decoder_inputs,  # 解码器输入,List列表
        decoder_vocab_size=7897,  # 解码器词汇数目
        embedding_size=128,  # embedding转换后的词向量大小
        rnn_num_units=256,  # RNN中神经元数目
        is_training=True,  # 训练阶段还是预测推理阶段
        project_output_weight=project_output_weight,  # 全连接参数
        project_output_bias=project_output_bias  # 全连接参数
    )

    # 四、对于RNN的输出,做一个全连接转换,得到其最终结果
    project_logits = []
    for decoder_rnn_output in decoder_rnn_outputs:
        # [N,256] ---> [N,7897]  也就是预测为每个字/词的置信度
        logits = tf.nn.xw_plus_b(decoder_rnn_output, project_output_weight, project_output_bias)
        project_logits.append(logits)
    print("最终形状:\n{}".format(project_logits))

    # 五、损失函数构建
    loss = 0
    for logits, targets in zip(project_logits, decoder_targets):
        _loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=targets, logits=logits)
        loss = loss + tf.reduce_mean(_loss)

    # TODO: 下面训练的代码如果想实现就自己完善
    """
    TODO:训练数据的时候,只需要将原始数据转换,比如:
    0. 假定词汇转换id表为:
        编码器转换表为:
            PAD   -->   0
            小明  -->   1
            吃    -->   2
            苹果  -->   3
            栗子  -->   4
            .....
        解码器词汇转换表为:
            PAD   -->   0
            GO    -->   1
            EOS   -->   2
            xiao  -->   3
            ming  -->   4
            eats  -->   5
            apples -->  6
            chestnut --> 7
            .....
    1. 原始数据转换为序号id:
        [小明, 吃, 苹果] ----> [xiao, ming, eats, apples]
        ====>
        [1,2,3] ----> [3,4,5,6]
    2. 数据填充(因为编码器要求输入长度为5,解码器要求输出长度为8)
        [小明, 吃, 苹果] ----> [xiao, ming, eats, apples]
        ====>
        [小明, 吃, 苹果, PAD, PAD] ----> [GO, xiao, ming, eats, apples, EOS, PAD, PAD]
        ====>
        [1,2,3,0,0] ----> [1,3,4,5,6,2,0,0]
    3. 上面是描述一条数据的处理方式,那么batch_size条数据,处理方式是完全一样的,最终假定形成两个对象:
            X:[N,5]
            Y:[N,8]
        对X和Y进行转置的操作,也就是形状进行变换:
            X:[5,N]
            Y:[8,N]
    4. 给定feed_dict的值:
        feed_dict = {}
        for encoder_input,x in zip(encoder_inputs,X):
            feed_dict[encoder_input] = x
        for decoder_placeholder, y in zip(decoder_placeholders, Y):
            feed_dict[decoder_placeholder] = y 
    """


def prediction():
    # 一、定义编码器的输入
    # 假设编码器有5个时刻输出,每个时刻输出的是具体对应的单词id;如果数据长度不够,那么进行填充,如果数据太长,进行截断。
    encoder_inputs = []
    for idx in range(5):
        encoder_inputs.append(tf.placeholder(dtype=tf.int32, shape=[None], name="encoder_{}".format(idx)))

    # 二、定义解码器的输出和输出
    # 假设解码器实际长度为6个时刻,加上特殊一个特殊时刻(输入:GO, 输出:EOS);如果数据长度不够,那么进行填充,如果数据太长,进行截断。
    decoder_placeholders = []
    for idx in range(8):
        decoder_placeholders.append(tf.placeholder(dtype=tf.int32, shape=[None], name='decoder_{}'.format(idx)))
    decoder_inputs = decoder_placeholders[:-1]  # 解码器的输入(7个时刻=6个实际值+1个特殊值)
    decoder_targets = decoder_placeholders[1:]  # 解码器的输出(7个时刻=6个实际值+1个特殊值)

    # 三、前向网络的构建,得到解码器RNN的输出
    project_output_weight = tf.get_variable("w", shape=[256, 7897])
    project_output_bias = tf.get_variable("b", shape=[7897])
    # decoder_rnn_outputs: List列表,内部为解码器RNN的每个时刻的输出;形状为:[N,rnn_num_units]
    decoder_rnn_outputs = build_interface(
        encoder_inputs=encoder_inputs,  # 编码器输入,List列表
        encoder_vocab_size=10000,  # 编码器词汇数目
        decoder_inputs=decoder_inputs,  # 解码器输入,List列表
        decoder_vocab_size=7897,  # 解码器词汇数目
        embedding_size=128,  # embedding转换后的词向量大小
        rnn_num_units=256,  # RNN中神经元数目
        is_training=False,  # 训练阶段还是预测推理阶段
        project_output_weight=project_output_weight,  # 全连接参数
        project_output_bias=project_output_bias  # 全连接参数
    )

    # 四、对于RNN的输出,做一个全连接转换,得到其最终结果
    project_logits = []
    project_predictions = []
    for decoder_rnn_output in decoder_rnn_outputs:
        # [N,256] ---> [N,7897]  也就是预测为每个字/词的置信度
        logits = tf.nn.xw_plus_b(decoder_rnn_output, project_output_weight, project_output_bias)
        project_logits.append(logits)
        project_predictions.append(tf.argmax(logits, -1))

    print("最终形状:\n{}".format(project_predictions))
    # TODO: 下面推理预测的代码如果想实现就自己完善
    """
    TODO:推理预测数据的时候,只需要将原始数据转换,比如:
    0. 假定词汇转换id表为:
        编码器转换表为:
            PAD   -->   0
            小明  -->   1
            吃    -->   2
            苹果  -->   3
            栗子  -->   4
            .....
        解码器词汇转换表为:
            PAD   -->   0
            GO    -->   1
            EOS   -->   2
            xiao  -->   3
            ming  -->   4
            eats  -->   5
            apples -->  6
            chestnut --> 7
            .....
    1. 原始数据转换为序号id:
        [小明, 吃, 苹果] ----> []
        ====>
        [1,2,3] ----> []
    2. 数据填充(因为编码器要求输入长度为5,解码器要求输出长度为8<但是实际上解码器预测的时候,仅输出第一个占位符>)
        [小明, 吃, 苹果] ----> []
        ====>
        [小明, 吃, 苹果, PAD, PAD] ----> [GO]
        ====>
        [1,2,3,0,0] ----> [1]
    3. 上面是描述一条数据的处理方式,那么batch_size条数据,处理方式是完全一样的,最终假定形成两个对象:
            X:[N,5]
            Y:[N,1]
        对X和Y进行转置的操作,也就是形状进行变换:
            X:[5,N]
            Y:[1,N]
    4. 给定feed_dict的值:
        feed_dict = {}
        for encoder_input,x in zip(encoder_inputs,X):
            feed_dict[encoder_input] = x
        feed_dict[decoder_placeholders[0]] = Y[0] # 解码的时候,只需要给定解码器的第一个时刻输出 
    """


if __name__ == '__main__':
    prediction()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值