使用Tensorflow收敛LSTM网络
Convergence of LSTM network using Tensorflow
我正在尝试检测长时间序列中的微事件。为此,我将训练一个 LSTM 网络。
Data. 每个时间样本的输入是 11 个不同的特征,这些特征在某种程度上被归一化以适合 0-1。输出将是两个 classes.
之一
批处理。由于巨大class不平衡,我每60个时间样本分批提取数据,其中至少 5 将永远是 class 1,其余的 class。通过这种方式,class 不平衡从 150:1 减少到 12:1 左右,然后我将所有批次的顺序随机化。
Model. 我正在尝试训练 LSTM,初始配置为 3 个不同的单元格和 5 个延迟步骤。我希望微事件按至少 3 个时间步长的顺序到达。
问题: 当我尝试训练网络时,它会很快收敛到说一切都属于多数 class。当我实现一个加权损失函数时,在某个特定的阈值下它会变成说一切都属于少数 class。我怀疑(不是专家)我的 LSTM 单元中没有学习,或者我的配置已关闭?
下面是我的实现代码。希望有人能告诉我
- 我的实现是否正确?
- 这种行为还有哪些其他原因?
ar_model.py
import numpy as np
import tensorflow as tf
from tensorflow.models.rnn import rnn
import ar_config
config = ar_config.get_config()
class ARModel(object):
def __init__(self, is_training=False, config=None):
# Config
if config is None:
config = ar_config.get_config()
# Placeholders
self._features = tf.placeholder(tf.float32, [None, config.num_features], name='ModelInput')
self._targets = tf.placeholder(tf.float32, [None, config.num_classes], name='ModelOutput')
# Hidden layer
with tf.variable_scope('lstm') as scope:
lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(config.num_hidden, forget_bias=0.0)
cell = tf.nn.rnn_cell.MultiRNNCell([lstm_cell] * config.num_delays)
self._initial_state = cell.zero_state(config.batch_size, dtype=tf.float32)
outputs, state = rnn.rnn(cell, [self._features], dtype=tf.float32)
# Output layer
output = outputs[-1]
softmax_w = tf.get_variable('softmax_w', [config.num_hidden, config.num_classes], tf.float32)
softmax_b = tf.get_variable('softmax_b', [config.num_classes], tf.float32)
logits = tf.matmul(output, softmax_w) + softmax_b
# Evaluate
ratio = (60.00 / 5.00)
class_weights = tf.constant([ratio, 1 - ratio])
weighted_logits = tf.mul(logits, class_weights)
loss = tf.nn.softmax_cross_entropy_with_logits(weighted_logits, self._targets)
self._cost = cost = tf.reduce_mean(loss)
self._predict = tf.argmax(tf.nn.softmax(logits), 1)
self._correct = tf.equal(tf.argmax(logits, 1), tf.argmax(self._targets, 1))
self._accuracy = tf.reduce_mean(tf.cast(self._correct, tf.float32))
self._final_state = state
if not is_training:
return
# Optimize
optimizer = tf.train.AdamOptimizer()
self._train_op = optimizer.minimize(cost)
@property
def features(self):
return self._features
@property
def targets(self):
return self._targets
@property
def cost(self):
return self._cost
@property
def accuracy(self):
return self._accuracy
@property
def train_op(self):
return self._train_op
@property
def predict(self):
return self._predict
@property
def initial_state(self):
return self._initial_state
@property
def final_state(self):
return self._final_state
ar_train.py
import os
from datetime import datetime
import numpy as np
import tensorflow as tf
from tensorflow.python.platform import gfile
import ar_network
import ar_config
import ar_reader
config = ar_config.get_config()
def main(argv=None):
if gfile.Exists(config.train_dir):
gfile.DeleteRecursively(config.train_dir)
gfile.MakeDirs(config.train_dir)
train()
def train():
train_data = ar_reader.ArousalData(config.train_data, num_steps=config.max_steps)
test_data = ar_reader.ArousalData(config.test_data, num_steps=config.max_steps)
with tf.Graph().as_default(), tf.Session() as session, tf.device('/cpu:0'):
initializer = tf.random_uniform_initializer(minval=-0.1, maxval=0.1)
with tf.variable_scope('model', reuse=False, initializer=initializer):
m = ar_network.ARModel(is_training=True)
s = tf.train.Saver(tf.all_variables())
tf.initialize_all_variables().run()
for batch_input, batch_target in train_data:
step = train_data.iter_steps
dict = {
m.features: batch_input,
m.targets: batch_target
}
session.run(m.train_op, feed_dict=dict)
state, cost, accuracy = session.run([m.final_state, m.cost, m.accuracy], feed_dict=dict)
if not step % 10:
test_input, test_target = test_data.next()
test_accuracy = session.run(m.accuracy, feed_dict={
m.features: test_input,
m.targets: test_target
})
now = datetime.now().time()
print ('%s | Iter %4d | Loss= %.5f | Train= %.5f | Test= %.3f' % (now, step, cost, accuracy, test_accuracy))
if not step % 1000:
destination = os.path.join(config.train_dir, 'ar_model.ckpt')
s.save(session, destination)
if __name__ == '__main__':
tf.app.run()
ar_config.py
class Config(object):
# Directories
train_dir = '...'
ckpt_dir = '...'
train_data = '...'
test_data = '...'
# Data
num_features = 13
num_classes = 2
batch_size = 60
# Model
num_hidden = 3
num_delays = 5
# Training
max_steps = 100000
def get_config():
return Config()
更新的架构:
# Placeholders
self._features = tf.placeholder(tf.float32, [None, config.num_features, config.num_delays], name='ModelInput')
self._targets = tf.placeholder(tf.float32, [None, config.num_output], name='ModelOutput')
# Weights
weights = {
'hidden': tf.get_variable('w_hidden', [config.num_features, config.num_hidden], tf.float32),
'out': tf.get_variable('w_out', [config.num_hidden, config.num_classes], tf.float32)
}
biases = {
'hidden': tf.get_variable('b_hidden', [config.num_hidden], tf.float32),
'out': tf.get_variable('b_out', [config.num_classes], tf.float32)
}
#Layer in
with tf.variable_scope('input_hidden') as scope:
inputs = self._features
inputs = tf.transpose(inputs, perm=[2, 0, 1]) # (BatchSize,NumFeatures,TimeSteps) -> (TimeSteps,BatchSize,NumFeatures)
inputs = tf.reshape(inputs, shape=[-1, config.num_features]) # (TimeSteps,BatchSize,NumFeatures -> (TimeSteps*BatchSize,NumFeatures)
inputs = tf.add(tf.matmul(inputs, weights['hidden']), biases['hidden'])
#Layer hidden
with tf.variable_scope('hidden_hidden') as scope:
inputs = tf.split(0, config.num_delays, inputs) # -> n_steps * (batchsize, features)
cell = tf.nn.rnn_cell.BasicLSTMCell(config.num_hidden, forget_bias=0.0)
self._initial_state = cell.zero_state(config.batch_size, dtype=tf.float32)
outputs, state = rnn.rnn(cell, inputs, dtype=tf.float32)
#Layer out
with tf.variable_scope('hidden_output') as scope:
output = outputs[-1]
logits = tf.add(tf.matmul(output, weights['out']), biases['out'])
奇数元素
加权损失
我不确定你的 "weighted loss" 是否如你所愿:
ratio = (60.00 / 5.00)
class_weights = tf.constant([ratio, 1 - ratio])
weighted_logits = tf.mul(logits, class_weights)
这是在计算损失函数之前应用的(此外,我认为您还需要逐元素乘法?而且您的比率高于 1,这使得第二部分为负数?)所以它会强制您的预测以应用 softmax 之前的某种方式。
如果你想要加权损失,你应该在
之后应用它
loss = tf.nn.softmax_cross_entropy_with_logits(weighted_logits, self._targets)
对你的权重进行一些元素乘法。
loss = loss * weights
你的权重形状像 [2,]
但是,我不建议您使用加权损失。也许尝试将比率提高到比 1:6.
更高的水平
建筑
据我所知,您使用的是 5 个堆叠 LSTM,每层有 3 个隐藏单元?
尝试删除多 rnn 并只使用一个 LSTM/GRU(甚至可能只是一个香草 RNN)并将隐藏单元增加到 ~100-1000。
调试
当您遇到行为异常的网络问题时,通常最好:
打印所有内容
直接打印模型中每个张量的形状和值,使用 sess 获取并打印。您的输入数据、第一个隐藏表示、您的预测、您的损失等
你也可以使用tensorflows tf.Print() x_tensor = tf.Print(x_tensor, [tf.shape(x_tensor)])
使用tensorboard
对梯度、准确度指标和直方图使用 tensorboard 摘要将揭示数据中可能解释某些行为的模式,例如导致权重爆炸的原因。就像你的遗忘偏差可能会变得无穷大,或者你没有通过某个层跟踪梯度等。
其他问题
你的数据集有多大?
你的序列有多长?
这 13 个特征是分类特征还是连续特征?您不应规范化分类变量或将它们表示为整数,而应使用单热编码。
Gunnar 已经提出了很多好的建议。对于这种架构,一般还需要注意一些小事情:
- 尝试调整 Adam 学习率。您应该通过交叉验证来确定合适的学习率;作为一个粗略的开始,您可以只检查较小的学习率是否可以使您的模型免于在训练数据上崩溃。
- 你绝对应该使用更多的隐藏单元。刚开始处理数据集时尝试更大的网络成本较低。尽可能大,以避免您观察到的欠拟合。稍后你可以在你得到它之后对网络进行规范化/削减以学习一些有用的东西。
具体来说,您传递到网络中的序列有多长?你说你有一个 30k 长的时间序列。我假设你传递的是这个序列的小节/样本?
我正在尝试检测长时间序列中的微事件。为此,我将训练一个 LSTM 网络。
Data. 每个时间样本的输入是 11 个不同的特征,这些特征在某种程度上被归一化以适合 0-1。输出将是两个 classes.
之一批处理。由于巨大class不平衡,我每60个时间样本分批提取数据,其中至少 5 将永远是 class 1,其余的 class。通过这种方式,class 不平衡从 150:1 减少到 12:1 左右,然后我将所有批次的顺序随机化。
Model. 我正在尝试训练 LSTM,初始配置为 3 个不同的单元格和 5 个延迟步骤。我希望微事件按至少 3 个时间步长的顺序到达。
问题: 当我尝试训练网络时,它会很快收敛到说一切都属于多数 class。当我实现一个加权损失函数时,在某个特定的阈值下它会变成说一切都属于少数 class。我怀疑(不是专家)我的 LSTM 单元中没有学习,或者我的配置已关闭?
下面是我的实现代码。希望有人能告诉我
- 我的实现是否正确?
- 这种行为还有哪些其他原因?
ar_model.py
import numpy as np
import tensorflow as tf
from tensorflow.models.rnn import rnn
import ar_config
config = ar_config.get_config()
class ARModel(object):
def __init__(self, is_training=False, config=None):
# Config
if config is None:
config = ar_config.get_config()
# Placeholders
self._features = tf.placeholder(tf.float32, [None, config.num_features], name='ModelInput')
self._targets = tf.placeholder(tf.float32, [None, config.num_classes], name='ModelOutput')
# Hidden layer
with tf.variable_scope('lstm') as scope:
lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(config.num_hidden, forget_bias=0.0)
cell = tf.nn.rnn_cell.MultiRNNCell([lstm_cell] * config.num_delays)
self._initial_state = cell.zero_state(config.batch_size, dtype=tf.float32)
outputs, state = rnn.rnn(cell, [self._features], dtype=tf.float32)
# Output layer
output = outputs[-1]
softmax_w = tf.get_variable('softmax_w', [config.num_hidden, config.num_classes], tf.float32)
softmax_b = tf.get_variable('softmax_b', [config.num_classes], tf.float32)
logits = tf.matmul(output, softmax_w) + softmax_b
# Evaluate
ratio = (60.00 / 5.00)
class_weights = tf.constant([ratio, 1 - ratio])
weighted_logits = tf.mul(logits, class_weights)
loss = tf.nn.softmax_cross_entropy_with_logits(weighted_logits, self._targets)
self._cost = cost = tf.reduce_mean(loss)
self._predict = tf.argmax(tf.nn.softmax(logits), 1)
self._correct = tf.equal(tf.argmax(logits, 1), tf.argmax(self._targets, 1))
self._accuracy = tf.reduce_mean(tf.cast(self._correct, tf.float32))
self._final_state = state
if not is_training:
return
# Optimize
optimizer = tf.train.AdamOptimizer()
self._train_op = optimizer.minimize(cost)
@property
def features(self):
return self._features
@property
def targets(self):
return self._targets
@property
def cost(self):
return self._cost
@property
def accuracy(self):
return self._accuracy
@property
def train_op(self):
return self._train_op
@property
def predict(self):
return self._predict
@property
def initial_state(self):
return self._initial_state
@property
def final_state(self):
return self._final_state
ar_train.py
import os
from datetime import datetime
import numpy as np
import tensorflow as tf
from tensorflow.python.platform import gfile
import ar_network
import ar_config
import ar_reader
config = ar_config.get_config()
def main(argv=None):
if gfile.Exists(config.train_dir):
gfile.DeleteRecursively(config.train_dir)
gfile.MakeDirs(config.train_dir)
train()
def train():
train_data = ar_reader.ArousalData(config.train_data, num_steps=config.max_steps)
test_data = ar_reader.ArousalData(config.test_data, num_steps=config.max_steps)
with tf.Graph().as_default(), tf.Session() as session, tf.device('/cpu:0'):
initializer = tf.random_uniform_initializer(minval=-0.1, maxval=0.1)
with tf.variable_scope('model', reuse=False, initializer=initializer):
m = ar_network.ARModel(is_training=True)
s = tf.train.Saver(tf.all_variables())
tf.initialize_all_variables().run()
for batch_input, batch_target in train_data:
step = train_data.iter_steps
dict = {
m.features: batch_input,
m.targets: batch_target
}
session.run(m.train_op, feed_dict=dict)
state, cost, accuracy = session.run([m.final_state, m.cost, m.accuracy], feed_dict=dict)
if not step % 10:
test_input, test_target = test_data.next()
test_accuracy = session.run(m.accuracy, feed_dict={
m.features: test_input,
m.targets: test_target
})
now = datetime.now().time()
print ('%s | Iter %4d | Loss= %.5f | Train= %.5f | Test= %.3f' % (now, step, cost, accuracy, test_accuracy))
if not step % 1000:
destination = os.path.join(config.train_dir, 'ar_model.ckpt')
s.save(session, destination)
if __name__ == '__main__':
tf.app.run()
ar_config.py
class Config(object):
# Directories
train_dir = '...'
ckpt_dir = '...'
train_data = '...'
test_data = '...'
# Data
num_features = 13
num_classes = 2
batch_size = 60
# Model
num_hidden = 3
num_delays = 5
# Training
max_steps = 100000
def get_config():
return Config()
更新的架构:
# Placeholders
self._features = tf.placeholder(tf.float32, [None, config.num_features, config.num_delays], name='ModelInput')
self._targets = tf.placeholder(tf.float32, [None, config.num_output], name='ModelOutput')
# Weights
weights = {
'hidden': tf.get_variable('w_hidden', [config.num_features, config.num_hidden], tf.float32),
'out': tf.get_variable('w_out', [config.num_hidden, config.num_classes], tf.float32)
}
biases = {
'hidden': tf.get_variable('b_hidden', [config.num_hidden], tf.float32),
'out': tf.get_variable('b_out', [config.num_classes], tf.float32)
}
#Layer in
with tf.variable_scope('input_hidden') as scope:
inputs = self._features
inputs = tf.transpose(inputs, perm=[2, 0, 1]) # (BatchSize,NumFeatures,TimeSteps) -> (TimeSteps,BatchSize,NumFeatures)
inputs = tf.reshape(inputs, shape=[-1, config.num_features]) # (TimeSteps,BatchSize,NumFeatures -> (TimeSteps*BatchSize,NumFeatures)
inputs = tf.add(tf.matmul(inputs, weights['hidden']), biases['hidden'])
#Layer hidden
with tf.variable_scope('hidden_hidden') as scope:
inputs = tf.split(0, config.num_delays, inputs) # -> n_steps * (batchsize, features)
cell = tf.nn.rnn_cell.BasicLSTMCell(config.num_hidden, forget_bias=0.0)
self._initial_state = cell.zero_state(config.batch_size, dtype=tf.float32)
outputs, state = rnn.rnn(cell, inputs, dtype=tf.float32)
#Layer out
with tf.variable_scope('hidden_output') as scope:
output = outputs[-1]
logits = tf.add(tf.matmul(output, weights['out']), biases['out'])
奇数元素
加权损失
我不确定你的 "weighted loss" 是否如你所愿:
ratio = (60.00 / 5.00)
class_weights = tf.constant([ratio, 1 - ratio])
weighted_logits = tf.mul(logits, class_weights)
这是在计算损失函数之前应用的(此外,我认为您还需要逐元素乘法?而且您的比率高于 1,这使得第二部分为负数?)所以它会强制您的预测以应用 softmax 之前的某种方式。
如果你想要加权损失,你应该在
之后应用它loss = tf.nn.softmax_cross_entropy_with_logits(weighted_logits, self._targets)
对你的权重进行一些元素乘法。
loss = loss * weights
你的权重形状像 [2,]
但是,我不建议您使用加权损失。也许尝试将比率提高到比 1:6.
更高的水平建筑
据我所知,您使用的是 5 个堆叠 LSTM,每层有 3 个隐藏单元?
尝试删除多 rnn 并只使用一个 LSTM/GRU(甚至可能只是一个香草 RNN)并将隐藏单元增加到 ~100-1000。
调试
当您遇到行为异常的网络问题时,通常最好:
打印所有内容
直接打印模型中每个张量的形状和值,使用 sess 获取并打印。您的输入数据、第一个隐藏表示、您的预测、您的损失等
你也可以使用tensorflows tf.Print() x_tensor = tf.Print(x_tensor, [tf.shape(x_tensor)])
使用tensorboard
对梯度、准确度指标和直方图使用 tensorboard 摘要将揭示数据中可能解释某些行为的模式,例如导致权重爆炸的原因。就像你的遗忘偏差可能会变得无穷大,或者你没有通过某个层跟踪梯度等。
其他问题
你的数据集有多大?
你的序列有多长?
这 13 个特征是分类特征还是连续特征?您不应规范化分类变量或将它们表示为整数,而应使用单热编码。
Gunnar 已经提出了很多好的建议。对于这种架构,一般还需要注意一些小事情:
- 尝试调整 Adam 学习率。您应该通过交叉验证来确定合适的学习率;作为一个粗略的开始,您可以只检查较小的学习率是否可以使您的模型免于在训练数据上崩溃。
- 你绝对应该使用更多的隐藏单元。刚开始处理数据集时尝试更大的网络成本较低。尽可能大,以避免您观察到的欠拟合。稍后你可以在你得到它之后对网络进行规范化/削减以学习一些有用的东西。
具体来说,您传递到网络中的序列有多长?你说你有一个 30k 长的时间序列。我假设你传递的是这个序列的小节/样本?