系列文章目录

decoder.py


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

最近正在看一个深度强化学习的代码,为了便于以后巩固,在此对一些不理解的难点进行总结。
视频链接:【强化学习】指针网络解决旅行商(TSP)问题
源代码:https://github.com/zifeiyu0531/PointerNetwork-RL-TSP
参考文献:
Vinyals O, Fortunato M, Jaitly N. Pointer Networks;
Bello I, Pham H, Le Q V, et al. Neural Combinatorial Optimization with Reinforcemen Learning


一、main.py

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

二、actor.py

tf.nn.dynamic_rnn函数讲解

tf.nn.dynamic_rnn 用于处理序列数据,将时间步作为主要维度,方便后续模型层的处理。

tf.nn.dynamic_rnn(
    cell,
    inputs,
    sequence_length=None,
    initial_state=None,
    dtype=None,
    parallel_iterations=None,
    swap_memory=False,
    time_major=False,
    scope=None
)

以下是重要参数的介绍:

  • cell:这是 LSTM、GRU 等记忆单元的实例。cell 参数代表一个 LSTM 或 GRU 的记忆单元,也就是一个 cell。例如,你可以创建一个 LSTM cell,如 cell = tf.nn.rnn_cell.LSTMCell(num_units),其中 num_units 表示 RNN cell 中神经元的个数,也就是下文中的 cell.output_size。
  • inputs:这是输入的训练或测试数据,一般格式为 [batch_size, max_time, embed_size],其中 batch_size 是输入数据的数量,max_time 是这批数据中序列的最长长度,embed_size 表示嵌入的词向量的维度。
  • sequence_length:这是一个列表,假设你输入了三句话,且三句话的长度分别是 5、10 和 25,那么 sequence_length 就是 [5, 10, 25]。
  • time_major:这决定了输出张量的格式。如果为 True,张量的形状必须是 [max_time, batch_size, cell.output_size]。如果为 False,张量的形状必须是 [batch_size, max_time, cell.output_size],其中 cell.output_size 表示 RNN cell 中神经元的个数。
    返回值是一个元组 (outputs, states):
  • outputs:这是每个 cell 的输出,形状为 [max_time, batch_size, cell.output_size] 或 [batch_size, max_time, cell.output_size],具体取决于 time_major 参数。
  • states:这表示最终的状态,即序列中最后一个 cell 输出的状态。一般情况下,states 的形状为 [batch_size, cell.output_size]。但当输入的 cell 为 BasicLSTMCell 时,state 的形状为 [2, batch_size, cell.output_size],其中 2 对应着 LSTM 中的 cell state 和 hidden state。

三、config.py

四、critic.py

五、dataset.py

六、decoder.py

源代码

import tensorflow as tf
from tensorflow.contrib.rnn import LSTMCell

distr = tf.contrib.distributions
# RNN decoder for pointer network
class Pointer_decoder(object):

    def __init__(self, encoder_output, config):
        self.encoder_output = encoder_output  # Tensor [Batch size x time steps x cell.state_size] to attend to
        self.h = tf.transpose(encoder_output, [1, 0, 2])

        batch_size = encoder_output.get_shape().as_list()[0]  # batch size
        self.seq_length = encoder_output.get_shape().as_list()[1]  # sequence length
        n_hidden = encoder_output.get_shape().as_list()[2]  # num_neurons

        self.temperature = config.temperature  # temperature parameter
        self.C = config.C  # logit clip
        self.training_mode = config.training_mode

        # Variables initializer 变量初始化器
        initializer = tf.contrib.layers.xavier_initializer()
        # Decoder LSTM cell   解码器LSTM单元
        self.cell = LSTMCell(n_hidden, initializer=initializer)
        # Attending mechanism 注意力机制
        with tf.variable_scope("glimpse") as glimpse:
            self.W_ref_g = tf.get_variable("W_ref_g", [1, n_hidden, n_hidden], initializer=initializer)
            self.W_q_g = tf.get_variable("W_q_g", [n_hidden, n_hidden], initializer=initializer)
            self.v_g = tf.get_variable("v_g", [n_hidden], initializer=initializer)
        # Pointing mechanism 指针机制
        with tf.variable_scope("pointer") as pointer:
            self.W_ref = tf.get_variable("W_ref", [1, n_hidden, n_hidden], initializer=initializer)
            self.W_q = tf.get_variable("W_q", [n_hidden, n_hidden], initializer=initializer)
            self.v = tf.get_variable("v", [n_hidden], initializer=initializer)

        self.log_softmax = []  # 存储log(p_theta(pi(t)|pi(<t),s))以进行反向传播
        self.positions = []  # 存储任务列表以进行奖励
        # Start from depot 从起点开始
        self.depot_position = tf.constant(self.seq_length - 1, shape=[batch_size])
        # self.depot_position 表示起点城市的位置。在这里,它被设置为 seq_length - 1,即最后一个城市。
        self.positions.append(self.depot_position)
        # 跟踪当前城市
        self.current_task = tf.one_hot(self.depot_position, self.seq_length)
        # 跟踪已访问的城市
        self.mask = tf.one_hot(self.depot_position, self.seq_length)

    def loop_decode(self, decoder_initial_state):
        # decoder_initial_state: Tuple Tensor (c,h) of size [batch_size x cell.state_size]
        # decoder_first_input: Tensor [batch_size x cell.state_size]
        # loop_decode方法用于循环执行解码过程,从起点开始,选择下一个要访问的城市,直到回到起点。最后,返回访问的城市索引和log_softmax的总和。
        #  解码器初始输入为depot(起点)
        decoder_first_input = tf.gather(self.h, self.depot_position)[0]
        # 循环解码过程并收集结果
        s, i = decoder_initial_state, decoder_first_input
        for step in range(self.seq_length - 1):
            s, i = self.decode(s, i, step)
        # 返回起点
        self.positions.append(self.depot_position)
        # 存储访问的索引
        self.positions = tf.stack(self.positions, axis=1)  # [Batch,seq_length]
        # 在输出步骤上求log_softmax的总和
        self.log_softmax = tf.add_n(self.log_softmax)  # [Batch,seq_length-1]
        # 返回存储的visited_indices和log_softmax以进行反向传播
        return self.positions, self.log_softmax
    # One pass of the decode mechanism
    # decode方法执行解码过程的一步,选择下一个要访问的城市,并更新相关变量。
    def decode(self, prev_state, prev_input, timestep):
        with tf.variable_scope("loop"):
            if timestep > 0:
                tf.get_variable_scope().reuse_variables()
            # 在先前输入和状态的组合上运行单元
            output, state = self.cell(prev_input, prev_state)
            # 注意力机制
            masked_scores = self.attention(self.encoder_output, output)
            #  多项分布
            prob = distr.Categorical(masked_scores)
            # 从分布中采样
            position = prob.sample()
            position = tf.cast(position, tf.int32)
            self.positions.append(position)
            # 存储log_prob以进行反向传播
            self.log_softmax.append(prob.log_prob(position))
            #  更新当前城市和掩码
            self.current_city = tf.one_hot(position, self.seq_length)
            self.mask = self.mask + self.current_city
            # 检索解码器的新输入
            new_decoder_input = tf.gather(self.h, position)[0]
            return state, new_decoder_input
    # From a query (decoder output) and a set of reference (encoder_output)
    # predict a distribution over next decoder input
    #attention方法定义了注意力机制,它对输入序列进行加权,生成一个新的查询向量,并应用指针机制来选择下一个要访问的城市。最后,返回经过掩码处理的分数。
    def attention(self, ref, query):
        # 注意力机制
        encoded_ref_g = tf.nn.conv1d(ref, self.W_ref_g, 1, "VALID",
                                     name="encoded_ref_g")  # [Batch size, seq_length, n_hidden]
        encoded_query_g = tf.expand_dims(tf.matmul(query, self.W_q_g, name="encoded_query_g"),
                                         1)  # [Batch size, 1, n_hidden]
        scores_g = tf.reduce_sum(self.v_g * tf.tanh(encoded_ref_g + encoded_query_g), [-1],
                                 name="scores_g")  # [Batch size, seq_length]
        # 参与当前任务和仅应用的任务(应用掩码)
        attention_g = tf.nn.softmax(scores_g - 100000000. * (self.mask - self.current_task), name="attention_g")
        # 1 glimpse = Linear combination of reference vectors (defines new query vector)
        # # 1个glimpse = 参考向量的线性组合(定义新的查询向量)
        glimpse = tf.multiply(ref, tf.expand_dims(attention_g, 2))
        glimpse = tf.reduce_sum(glimpse, 1) + query
        # Pointing mechanism with 1 glimpse
        # 具有1个glimpse的指针机制
        encoded_ref = tf.nn.conv1d(ref, self.W_ref, 1, "VALID",
                                   name="encoded_ref")  # [Batch size, seq_length, n_hidden]
        encoded_query = tf.expand_dims(tf.matmul(glimpse, self.W_q, name="encoded_query"),
                                       1)  # [Batch size, 1, n_hidden]
        scores = tf.reduce_sum(self.v * tf.tanh(encoded_ref + encoded_query), [-1],
                               name="scores")  # [Batch size, seq_length]
        if not self.training_mode:
            scores = scores / self.temperature  # control diversity of sampling (inference mode)
        scores = self.C * tf.tanh(scores)  # control entropy
        # Point to cities to visit only (Apply mask)
        # 指向仅访问的城市(应用掩码)
        masked_scores = scores - 100000000. * self.mask  # [Batch size, seq_length]
        return masked_scores

1、注意力机制

# 注意力机制
        with tf.variable_scope("glimpse") as glimpse:
            self.W_ref_g = tf.get_variable("W_ref_g", [1, n_hidden, n_hidden], initializer=initializer)
            self.W_q_g = tf.get_variable("W_q_g", [n_hidden, n_hidden], initializer=initializer)
            self.v_g = tf.get_variable("v_g", [n_hidden], initializer=initializer)

这段代码涉及注意力机制(Attention Mechanism),它在深度学习中被广泛应用于处理序列数据,如文本、语音或图像序列。

注意力机制的产生:

注意力机制模仿人类视觉和认知系统,允许神经网络在处理输入数据时集中注意力于相关的部分。
通过引入注意力机制,神经网络能够自动地学习并选择性地关注输入中的重要信息,从而提高模型的性能和泛化能力。

自注意力机制(Self-Attention Mechanism):

自注意力机制的基本思想是,在处理序列数据时,每个元素都可以与序列中的其他元素建立关联,而不仅仅是依赖于相邻位置的元素。
它通过计算元素之间的相对重要性来自适应地捕捉元素之间的长程依赖关系。
对于序列中的每个元素,自注意力机制计算其与其他元素之间的相似度,并将这些相似度归一化为注意力权重。
最后,通过将每个元素与对应的注意力权重进行加权求和,得到自注意力机制的输出。
代码解析:

首先,代码中定义了三个变量:
self.W_ref_g:用于计算参考权重的参数矩阵。
self.W_q_g:用于计算查询权重的参数矩阵。
self.v_g:用于计算注意力值的参数向量。
这些参数将在自注意力机制的计算中起到关键作用,帮助模型自适应地关注输入中的重要信息。

2、指针机制

# 指针机制
        with tf.variable_scope("pointer") as pointer:
            self.W_ref = tf.get_variable("W_ref", [1, n_hidden, n_hidden], initializer=initializer)
            self.W_q = tf.get_variable("W_q", [n_hidden, n_hidden], initializer=initializer)
            self.v = tf.get_variable("v", [n_hidden], initializer=initializer)

这段代码涉及指针机制(Pointer Mechanism),它在序列到序列(Seq2Seq)模型中用于处理输入和输出之间的对应关系。

指针机制的作用:

在某些任务中,直接生成输出序列中的某些元素可能不够灵活或准确。例如,机器翻译中的单词对齐问题,或者问答系统中的实体链接。
指针机制允许模型根据输入序列中的位置信息,动态地选择输入序列中的某个元素作为输出序列的一部分。
代码解析:

首先,代码中定义了三个变量:
self.W_ref:用于计算参考权重的参数矩阵。
self.W_q:用于计算查询权重的参数矩阵。
self.v:用于计算注意力值的参数向量。
这些参数将在指针机制的计算中起到关键作用,帮助模型根据输入和查询信息选择性地关注输入序列中的某个位置。
指针机制的计算:
首先,通过计算输入序列中每个位置与查询信息之间的相似度,得到一个注意力分布。
然后,根据注意力分布,选择输入序列中的某个位置作为输出序列的一部分。

3、假设batch_size是64,seq_length是20,那么self.depot_position = tf.constant(self.seq_length - 1, shape=[batch_size]) 的运行结果是啥?

当 batch_size 为 64,seq_length 为 20 时,运行 self.depot_position = tf.constant(self.seq_length - 1, shape=[batch_size]) 的结果如下:

首先,计算 self.seq_length - 1,即 20 - 1 = 19。。
然后,将这个值作为常量张量,形状为 [batch_size],其中 batch_size 是 64。
因此,self.depot_position 的值将是一个包含 64 个元素的张量,每个元素都是 19。这表示起点城市的位置在序列中的索引为 19。

4、self.depot_position 的值将是一个包含 8个元素的张量,每个元素都是 19。self.seq_length=20。这表示起点城市的位置在序列中的索引为 19。self.current_task = tf.one_hot(self.depot_position, self.seq_length) 分析代码原理及结果

  • 原理:

tf.one_hot(indices, depth, on_value, off_value, axis)是 TensorFlow 中的一个函数,用于将整数索引转换为独热编码(one-hot encoding)。
它将输入的整数张量(一维或多维)转换为一个新的张量,其中每个整数对应一个独立的向量,只有一个元素为1,其他元素都为0。
参数解释:
indices:输入的整数张量,通常是给定的标签(labels),可以是数字列表,也可以是多维。
depth:一个标量,定义了输出的 one-hot 维度的深度,即输出向量的长度。
on_value 和 off_value:定义在 indices[j] = i 时填充输出的值和在 indices[j] != i 时填充输出的值,默认分别为1和0。
axis:要填充的轴,默认为-1,即一个新的最内层轴。

  • 结果分析:

结果是一个张量,其中只有一个元素为1,表示当前正在访问的城市(在这里是起点城市),其他元素都为0。
示例结果:

import tensorflow as tf
# Given values
depot_position = tf.constant(19, shape=[8])
# Convert to one-hot encoding
current_task = tf.one_hot(depot_position, 20)
# Print the result
with tf.Session() as sess:
    result = sess.run(current_task)
    print(result)

运行上述代码得到的结果如下所示,输出结果应该是一个形状为 (8, 20) 的二维数组,其中每行都是一个长度为20的独热编码向量,表示当前正在访问的城市。

[[0. 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. 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.]
 [0. 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. 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.]
 [0. 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. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]]

5、decoder_first_input = tf.gather(self.h, self.depot_position)[0]得到的结果是啥?

self.h:这是一个经过转置的张量,其形状为 [sequence_length, batch_size, hidden_size]。在自然语言处理(NLP)中,这种转置通常用于将时间步作为主要维度,以便更方便地处理序列数据。
self.depot_position:这是一个索引,表示你想从 self.h 中获取哪个时间步的隐藏状态。假设 self.depot_position 的值为 k,那么你将从 self.h 中获取第 k 个时间步的隐藏状态。
tf.gather(self.h, self.depot_position):这是 TensorFlow 中的操作,用于从 self.h 中按照指定的索引获取切片。具体来说,它会返回一个形状为 [batch_size, hidden_size] 的张量,其中包含了 self.h 中第 k 个时间步的隐藏状态。
[0]:这是对切片的进一步操作,表示从获取的切片中仅选择第一个样本(即 batch_size 维度上的第一个元素)。因此,decoder_first_input 是一个形状为 [hidden_size] 的张量,包含了 self.h 中第 k 个时间步的隐藏状态。

6、 W_ref_g、W_q_g、v_g讲解

self.W_ref_g:这是一个参数矩阵,用于计算参考权重。在注意力机制中,我们需要根据输入的查询(Query)和参考(Reference)之间的关系来计算权重。这个参数矩阵将参考的隐藏状态映射到一个新的隐藏状态空间,以便计算注意力权重。
self.W_q_g:这是另一个参数矩阵,用于计算查询的权重。查询是我们希望关注的部分,它通常是当前时间步的隐藏状态。这个参数矩阵将查询的隐藏状态映射到与参考相同的隐藏状态空间,以便与参考进行比较。
self.v_g:这是一个参数向量,用于计算注意力值。一旦我们得到了参考和查询之间的相似性,我们将使用这个参数向量来计算最终的注意力权重。这个向量的维度通常与隐藏状态的维度相同。

7、 glimpse = tf.multiply(ref, tf.expand_dims(attention_g, 2)),[batch_size, seq_length, n-hidden]和[batch_size, seq_length,1]的张量是如何相乘的?

tf.expand_dims(attention_g, 2) 将 attention_g 扩展为一个形状为 (batch_size, seq_len, 1) 的张量,使其与 ref 的最后一个维度对齐。

在深度学习中,两个张量相乘的前提是它们的形状要满足矩阵乘法的规则。对于两个形状为[batch_size, seq_length, 1]和[batch_size, seq_length, n-hidden]的张量,它们可以相乘,但是这不是标准的矩阵乘法,而是一种逐元素的乘法(element-wise multiplication)。在这种情况下,两个张量的对应元素相乘,结果的形状仍然是[batch_size, seq_length, n-hidden]。这也被称为逐元素乘法或哈达玛积(Hadamard product)。

执行逐元素乘法,则会沿第三轴广播形状为 [batch_size, seq_length, 1] 的张量,以匹配形状为 [batch_size, seq_length, n_hidden] 的张量的大小。 广播可以自动扩展维度,使其兼容元素操作。
举个例子:

import numpy as n
# Assuming tensor1 has shape [2, 3, 4]
tensor1 = np.array([[[1, 2, 3, 4],
                    [5, 6, 7, 8],
                    [9, 10, 11, 12]],
                   [[13, 14, 15, 16],
                    [17, 18, 19, 20],
                    [21, 22, 23, 24]]])
# Assuming tensor2 has shape [2, 3, 1]
tensor2 = np.array([[[2],
                    [3],
                    [4]],
                   [[5],
                    [6],
                    [7]]])
# Performing element-wise multiplication
result = tensor1 * tensor2
# The shape of the result will be [2, 3, 4]
print(result.shape)

这里的关键概念是广播。 NumPy 和其他类似的库允许广播,这是一组允许对不同形状的数组进行元素级操作的规则。
在示例中,沿第三个轴的维度为 4 和 1。根据广播规则,沿该轴大小为 1 的数组将扩展以匹配另一个数组的大小。 结果是逐元素乘法,其中第一个数组中的每个元素都与第二个数组中的相应元素相乘。

1.引入库

代码如下(示例):

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import  ssl
ssl._create_default_https_context = ssl._create_unverified_context

2.读入数据

代码如下(示例):

data = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())

该处使用的url网络请求的数据。


总结

提示:这里对文章进行总结:

例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐