我正在尝试在 mnist 数据集上的 tensorflow 中实现卷积自动编码器。
问题是自动编码器似乎没有正确学习:它总是会学习重现 0 形状,但没有其他形状,实际上我通常会得到大约 0.09 的平均损失,这是它的类的 1/10应该学习。
我正在使用步长为 2 的 2x2 内核进行输入和输出卷积,但过滤器似乎已正确学习。当我可视化数据时,输入图像通过 16 个(第一次转换)和 32 个过滤器(第二次转换),通过图像检查,它似乎运行良好(即,显然检测到曲线、十字等特征)。
问题似乎出现在网络的全连接部分:无论输入图像是什么,它的编码总是相同的。
我的第一个想法是“我可能只是在训练时用零喂它”,但我不认为我犯了这个错误(见下面的代码)。
编辑我意识到数据集没有被洗牌,这引入了偏差,可能是问题的原因。引入后,平均损失更低(0.06而不是0.09),实际上输出图像看起来像一个模糊的8,但结论是一样的:无论输入图像是什么,编码后的输入都是一样的。
这里是一个带有相对输出的样本输入
这是上图的激活,底部有两个完全连接的层(编码是最底部的)。
最后,这里有针对不同输入的全连接层的激活。每个输入图像对应于激活图像中的一条线。
如您所见,它们总是产生相同的输出。如果我使用转置权重而不是初始化不同的权重,第一个 FC 层(中间的图像)看起来更加随机,但底层模式仍然很明显。在编码层(底部的图像)中,无论输入是什么,输出总是相同的(当然,模式因一次训练和下一次训练而异)。
这是相关代码
# A placeholder for the input data
x = tf.placeholder('float', shape=(None, mnist.data.shape[1]))
# conv2d_transpose cannot use -1 in output size so we read the value
# directly in the graph
batch_size = tf.shape(x)[0]
# Variables for weights and biases
with tf.variable_scope('encoding'):
# After converting the input to a square image, we apply the first convolution, using 2x2 kernels
with tf.variable_scope('conv1'):
wec1 = tf.get_variable('w', shape=(2, 2, 1, m_c1), initializer=tf.truncated_normal_initializer())
bec1 = tf.get_variable('b', shape=(m_c1,), initializer=tf.constant_initializer(0))
# Second convolution
with tf.variable_scope('conv2'):
wec2 = tf.get_variable('w', shape=(2, 2, m_c1, m_c2), initializer=tf.truncated_normal_initializer())
bec2 = tf.get_variable('b', shape=(m_c2,), initializer=tf.constant_initializer(0))
# First fully connected layer
with tf.variable_scope('fc1'):
wef1 = tf.get_variable('w', shape=(7*7*m_c2, n_h1), initializer=tf.contrib.layers.xavier_initializer())
bef1 = tf.get_variable('b', shape=(n_h1,), initializer=tf.constant_initializer(0))
# Second fully connected layer
with tf.variable_scope('fc2'):
wef2 = tf.get_variable('w', shape=(n_h1, n_h2), initializer=tf.contrib.layers.xavier_initializer())
bef2 = tf.get_variable('b', shape=(n_h2,), initializer=tf.constant_initializer(0))
reshaped_x = tf.reshape(x, (-1, 28, 28, 1))
y1 = tf.nn.conv2d(reshaped_x, wec1, strides=(1, 2, 2, 1), padding='VALID')
y2 = tf.nn.sigmoid(y1 + bec1)
y3 = tf.nn.conv2d(y2, wec2, strides=(1, 2, 2, 1), padding='VALID')
y4 = tf.nn.sigmoid(y3 + bec2)
y5 = tf.reshape(y4, (-1, 7*7*m_c2))
y6 = tf.nn.sigmoid(tf.matmul(y5, wef1) + bef1)
encode = tf.nn.sigmoid(tf.matmul(y6, wef2) + bef2)
with tf.variable_scope('decoding'):
# for the transposed convolutions, we use the same weights defined above
with tf.variable_scope('fc1'):
#wdf1 = tf.transpose(wef2)
wdf1 = tf.get_variable('w', shape=(n_h2, n_h1), initializer=tf.contrib.layers.xavier_initializer())
bdf1 = tf.get_variable('b', shape=(n_h1,), initializer=tf.constant_initializer(0))
with tf.variable_scope('fc2'):
#wdf2 = tf.transpose(wef1)
wdf2 = tf.get_variable('w', shape=(n_h1, 7*7*m_c2), initializer=tf.contrib.layers.xavier_initializer())
bdf2 = tf.get_variable('b', shape=(7*7*m_c2,), initializer=tf.constant_initializer(0))
with tf.variable_scope('deconv1'):
wdd1 = tf.get_variable('w', shape=(2, 2, m_c1, m_c2), initializer=tf.contrib.layers.xavier_initializer())
bdd1 = tf.get_variable('b', shape=(m_c1,), initializer=tf.constant_initializer(0))
with tf.variable_scope('deconv2'):
wdd2 = tf.get_variable('w', shape=(2, 2, 1, m_c1), initializer=tf.contrib.layers.xavier_initializer())
bdd2 = tf.get_variable('b', shape=(1,), initializer=tf.constant_initializer(0))
u1 = tf.nn.sigmoid(tf.matmul(encode, wdf1) + bdf1)
u2 = tf.nn.sigmoid(tf.matmul(u1, wdf2) + bdf2)
u3 = tf.reshape(u2, (-1, 7, 7, m_c2))
u4 = tf.nn.conv2d_transpose(u3, wdd1, output_shape=(batch_size, 14, 14, m_c1), strides=(1, 2, 2, 1), padding='VALID')
u5 = tf.nn.sigmoid(u4 + bdd1)
u6 = tf.nn.conv2d_transpose(u5, wdd2, output_shape=(batch_size, 28, 28, 1), strides=(1, 2, 2, 1), padding='VALID')
u7 = tf.nn.sigmoid(u6 + bdd2)
decode = tf.reshape(u7, (-1, 784))
loss = tf.reduce_mean(tf.square(x - decode))
opt = tf.train.AdamOptimizer(0.0001).minimize(loss)
try:
tf.global_variables_initializer().run()
except AttributeError:
tf.initialize_all_variables().run() # Deprecated after r0.11
print('Starting training...')
bs = 1000 # Batch size
for i in range(501): # Reasonable results around this epoch
# Apply permutation of data at each epoch, should improve convergence time
train_data = np.random.permutation(mnist.data)
if i % 100 == 0:
print('Iteration:', i, 'Loss:', loss.eval(feed_dict={x: train_data}))
for j in range(0, train_data.shape[0], bs):
batch = train_data[j*bs:(j+1)*bs]
sess.run(opt, feed_dict={x: batch})
# TODO introduce noise
print('Training done')