首页 > 解决方案 > 为什么使用 for-loop 实现的 RNN 学习缓慢?

问题描述

问题设置

作为 RNN 的初学者,我目前正在为 4 个字母的单词构建一个3 对 1 的 自动完成RNN 模型,其中输入是一个 3 个字母的不完整单词,输出是一个完成单词的单字母。例如,我希望有以下模型预测:


代码 - 生成数据集

为了从 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]

sofourList包含训练数据信息,first3Data包含训练数据的所有 one-hot 编码的前 3 个字母,并last1Data包含训练数据的所有 one-hot 编码的后 1 个字母。


代码 - 构建模型

按照3对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我认为学习曲线应该显示出使用内置函数 (*)预期的方形稳定衰减模式。但我认为这种不稳定性可以合理解释如下:

但我认为这些都没有起到至关重要的作用。还有其他解决方案可以帮助我吗?


tensorflow(*) 使用简单 RNN 的内置函数,我看到了几乎呈方形的损失衰减。但是很抱歉我没有包含要比较的结果,因为我的时间不多了……我想我可以很快编辑。

标签: pythontensorflow

解决方案


这种将初始状态设置为零的修改似乎解决了这个问题。

# 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]))
h_init = tf.zeros([1,n_hidden])

# Manual calculation of RNN output
def RNNoutput(Xinput):
    h_state    =  h_init # 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)

推荐阅读