【发布时间】:2019-09-05 22:07:25
【问题描述】:
问题设置
作为 RNN 的初学者,我目前正在为 4 字母单词构建一个 3-to-1 自动补全 RNN 模型,其中输入是 3-字母不完整的单词,输出是完成单词的单个字母。例如,我希望有以下模型预测:
- 输入:“C”、“A”、“F”
- 输出:“E”
代码 - 生成数据集
为了从 RNN 模型中获得所需的结果,我制作了一个(不平衡的)数据集,如下所示:
import string
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
alphList = list(string.ascii_uppercase) # Define a list of alphabets
alphToNum = {n: i for i, n in enumerate(alphList)} # dic of alphabet-numbers
# Make dataset
# define words of interest
fourList = ['CARE', 'CODE', 'COME', 'CANE', 'COPE', 'FISH', 'JAZZ', 'GAME', 'WALK', 'QUIZ']
# (len(Sequence), len(Batch), len(Observation)) following tensorflow-style
first3Data = np.zeros((3, len(fourList), len(alphList)), dtype=np.int32)
last1Data = np.zeros((len(fourList), len(alphList)), dtype=np.int32)
for idxObs, word in enumerate(fourList):
# Make an array of one-hot vectors consisting of first 3 letters
first3 = [alphToNum[n] for n in word[:-1]]
first3Data[:,idxObs,:] = np.eye(len(alphList))[first3]
# Make an array of one-hot vectors consisting of last 1 letter
last1 = alphToNum[word[3]]
last1Data[idxObs,:] = np.eye(len(alphList))[last1]
所以fourList 包含训练数据信息,first3Data 包含训练数据的所有 one-hot 编码的前 3 个字母,last1Data 包含训练数据的所有 one-hot 编码的后 1 个字母。
代码 - 构建模型
按照3-to-1 RNN模型的标准设置,我做了如下代码。
# Hyperparameters
n_data = len(fourList)
n_input = len(alphList) # number of input units
n_hidden = 128 # number of hidden units
n_output = len(alphList) # number of output units
learning_rate = 0.01
total_epoch = 100000
# Variables (separate version)
W_in = tf.Variable(tf.random_normal([n_input, n_hidden]))
W_rec = tf.Variable(tf.random_normal([n_hidden, n_hidden]))
b_rec = tf.Variable(tf.random_normal([n_hidden]))
W_out = tf.Variable(tf.random_normal([n_hidden, n_output]))
b_out = tf.Variable(tf.random_normal([n_output]))
# Manual calculation of RNN output
def RNNoutput(Xinput):
h_state = tf.random_normal([1,n_hidden]) # initial hidden state
for iX in Xinput:
h_state = tf.nn.tanh(iX @ W_in + (h_state @ W_rec + b_rec))
rnn_output = h_state @ W_out + b_out
return(rnn_output)
请注意,Manual calculation of RNN output 部分基本上使用矩阵乘法和tanh 激活函数将隐藏状态精确滚动 4 次,如下所示:
tf.nn.tanh(iX @ W_in + (h_state @ W_rec + b_rec))
这里,每传递一个完整的数据,就完成一个epoch。因此,我每次传递数据时都会初始化 h_state。另外,请注意我没有使用占位符,这可能是导致学习不稳定的原因。
代码 - 训练
我使用以下代码来训练网络。
# Cost / optimizer definition
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=RNNoutput(first3Data),
labels=last1Data))
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(cost)
# Train and keep track of the loss history
sess = tf.Session()
sess.run(tf.global_variables_initializer())
lossHistory = []
for epoch in range(total_epoch):
_, loss = sess.run([optimizer, cost])
lossHistory.append(loss)
问题
生成的学习曲线如下所示。事实上,它显示出指数衰减。
但是,对我来说,这种简单的例子看起来太摇摆不定,即使在学习的后期也显示出一些不稳定性。
plt.plot(range(total_epoch), lossHistory)
plt.show()
可能的解释?
我认为学习曲线应该像使用 tensorflow 内置函数 (*) 所期望的那样显示出类似正方形的稳定衰减模式。但我认为这种不稳定性可以合理解释如下:
- 参数随机初始化不稳定
- 定义
RNNoutput时连续添加导致数值不稳定 - 不使用
tensor for loop,而是直接在数据中使用for循环
但我认为这些都没有起到至关重要的作用。 还有其他解决方案可以帮助我吗?
(*) 我已经看到使用简单 RNN 的 tensorflow 内置函数几乎呈正方形模式的损失衰减。但是很抱歉我没有包含要比较的结果,因为我的时间不多了……我想我可以很快编辑。
【问题讨论】:
-
每次将数据传入 RNN 时,您是否会随机化初始
h_state? -
@OpenSeason 是的,因为这里我每次传数据,就完成一个epoch。每次完成一个 epoch 时我们都会随机化初始 h_state 不是吗?
-
通常不会。你应该保持它总是零,或者总是一个固定的数字(每个时代都一样)。您实际上可以优化这个固定数字在训练中应该是什么。
-
@OpenSeason 谢谢,有趣的是我还没有意识到这一点。那么
tensorflow内置函数(例如tf.nn.rnn_cell.BasicRNNCell())是否会自动保持初始 h_state 为零或优化数字? -
可能。我没有使用过那个函数,但是这个想法并不新鲜,即使在 Ilya Sutskever 的论文 cs.utoronto.ca/~ilya/pubs/ilya_sutskever_phd_thesis.pdf 第 10 页也有描述如何计算可学习初始状态的梯度
h0
标签: python tensorflow