【问题标题】:CNN in Tensorflow - loss remains constantTensorflow 中的 CNN - 损失保持不变
【发布时间】:2017-08-08 20:46:18
【问题描述】:

我刚刚开始从事机器学习工作,想创建简单的 CNN 来分类 2 种不同种类的树叶(属于 2 种不同的树木)。在收集大量树叶图片之前,我决定在 Tensorflow 中创建非常小的、简单的 CNN,并仅在一张图像上对其进行训练,以检查代码是否正常。我将大小为 256x256(x 3 个通道)的照片归一化为 并创建了 4 层(2 个卷积和 2 个密集)网络。不幸的是,损失几乎总是从一开始就趋向于某个恒定值(通常是某个整数)。我认为图片有问题,所以我将其替换为相同尺寸的随机 numpy 数组。不幸的是,损失仍然不断。有时网络似乎在学习,因为损失在减少,但大多数时候从一开始就保持不变。谁能帮忙解释一下,为什么会这样?我读到用一个例子进行培训是检查你的代码是否缺少错误的最佳方法,但我越努力,我看到的就越少。

这是我的代码(基于此 TensorFlow 教程 1)。我使用了指数线性单位,因为我认为我的问题是由糟糕初始化的 ReLU 中的 0 梯度引起的。

import matplotlib.pyplot as plt
import numpy as np
from numpy import random
from sklearn import utils
import tensorflow as tf

#original dataset of 6 leaves
# input = [ndimage.imread("E:\leaves\dab1.jpg"),
#         ndimage.imread("E:\leaves\dab2.jpg"),
#        ndimage.imread("E:\leaves\dab3.jpg"),
#        ndimage.imread("E:\leaves\klon1.jpg"),
#        ndimage.imread("E:\leaves\klon2.jpg"),
#        ndimage.imread("E:\leaves\klon3.jpg")]

#normalize each image (originally uint8)
#input=[input/255 for i in range(len(input))

#temporary testing dataset, mimicking 6 images, each 3-channel, of dimension 256x256
input=[random.randn(256,256,3)]
       # random.randn(256, 256, 3),
       # random.randn(256, 256, 3),
       # random.randn(256, 256, 3),
       # random.randn(256, 256, 3),
       # random.randn(256, 256, 3)]

#each image belong to one of two classes
labels=[[1]]#,[1,0],[1,0],[0,1],[0,1],[0,1]]


def weight_variable(shape):
  initial = tf.truncated_normal(shape, stddev=.1)
  return tf.Variable(initial)

def bias_variable(shape):
  initial = tf.truncated_normal(shape, stddev=.1)
  return tf.Variable(initial)

def conv2d(x, W):
  return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

x = tf.placeholder(tf.float32, shape=[None, 256,256,3])
y_ = tf.placeholder(tf.float32, shape=[None, 1])

x_image = tf.reshape(x, [-1,256,256,3])

#first conv layer
W_conv1 = weight_variable([5,5, 3,8])
b_conv1 = bias_variable([8])
h_conv1 = tf.nn.elu(conv2d(x_image, W_conv1) + b_conv1)

#second conv layer
W_conv2 = weight_variable([5,5, 8,16])
b_conv2 = bias_variable([16])
h_conv2 = tf.nn.elu(conv2d(h_conv1, W_conv2) + b_conv2)

#first dense layer
W_fc1 = weight_variable([256*256*16, 10])
b_fc1 = bias_variable([10])
out_flat = tf.reshape(h_conv2, [-1, 256*256*16])
h_fc1 = tf.nn.elu(tf.matmul(out_flat, W_fc1) + b_fc1)

#second dense layer
W_fc2 = weight_variable([10, 1])
b_fc2 = bias_variable([1])
h_fc2 = tf.nn.elu(tf.matmul(h_fc1, W_fc2) + b_fc2)

#tried also with softmax with logits
cross_entropy=tf.losses.mean_squared_error(predictions=h_fc2, labels=y_)
train_step = tf.train.AdamOptimizer(1e-3).minimize(cross_entropy)

print("h2", h_fc2.shape)
print("y", y_.shape)

sess=tf.Session()
sess.run(tf.global_variables_initializer())
loss = []
for i in range(10):
    sess.run(train_step, feed_dict={x:input, y_:labels})
    input, labels = utils.shuffle(input, labels)
    loss.append(sess.run(cross_entropy, feed_dict={x:input, y_:labels}))
    print(i, " LOSS: ", loss[-1])

np.set_printoptions(precision=3, suppress=True)
for i in range(len(input)):
    print(labels[i], sess.run(h_fc2, feed_dict={x:[input[i]], y_:[labels[i]]}))

plt.plot(loss)
plt.show()

这里是我尝试过的列表:

  1. 上面的基本代码导致损失几乎总是正好等于 4.0
  2. 将训练时间扩展到 100 个 epoch。事实证明,实现持续亏损的可能性增加了。这很奇怪,因为在我看来,epoch 的数量应该会在训练的早期阶段改变任何东西。
  3. 我将特征图的数量更改为 I 层 32 个,II 层 64 个,密集层 100 个神经元
  4. 因为我的输出是二进制的,最初我只使用了单输出。我将其更改为排除 2 个输出。它将损失更改为 2.5。事实证明,我的输出往往是 [-1,-1],而标签是 [1,0]
  5. 我尝试了各种学习率,从 0.001 到 0.00005
  6. 我初始化了权重和偏差,标准差等于 2 而不是 0.1。损失似乎减少了,但达到了很高的值,比如 1e10。所以我将 epoch 的数量从 10 改为 100 .. 再一次,损失从一开始就是 2.5。回到 10 个 epoch 后,loss 仍然是 2.5
  7. 我将数据集扩展为 6 个元素。损失与之前相同。

有谁知道,为什么会这样?据我所知,如果网络不能泛化,损失不会减少而是增加/振荡但不会保持不变?

【问题讨论】:

    标签: python numpy tensorflow conv-neural-network loss


    【解决方案1】:

    在我的例子中,我没有标准化图像input(cifar-10)。这应该从[0, 255] 标准化为[0 ,1]

    这是我的代码:

    (x_train, y_train), (x_test, y_test) = cifar10.load_data()
    
    x_train = x_train.astype('float32')
    
    x_test = x_test.astype('float32')
    
    x_train /= 255 
    
    x_test /= 255 # [0, 255] ---> [0, 1]
    

    希望对你有帮助。

    【讨论】:

      【解决方案2】:

      我找到了答案。问题是由以下行引起的:

      h_fc2 = tf.nn.elu(tf.matmul(h_fc1, W_fc2) + b_fc2)
      

      我不知道为什么,但它使输出等于-1。当我将其更改为

      h_fc2 = f.matmul(h_fc1, W_fc2) + b_fc2
      

      它就像一种魅力,损失开始减少。谁能解释一下,为什么我们要避免在最后一层使用激活函数(我在前面提到的 TensorFlow 教程中看到了同样的问题)?没看懂,我以为每一层都应该有自己的激活函数?

      【讨论】:

      • 哦,我完全错过了。那肯定是错的。当您应用交叉熵时,您需要一个介于 0 和 1 之间的值,损失函数将对最后一层应用 sigmoid 或 softmax。所以最后一层应该是一个简单的线性层。如果你使用平方和损失,你需要最后一层是 (-inf, +inf),所以在这种情况下,你只需将它保留为一个简单的线性层,它可以采用任何实际值。
      • 通常最后一层应该是线性的(不要应用任何非线性变换),然后以任何必要的方式对你的损失函数进行变换,每个损失函数都将定义它的域应该是,并且在大多数情况下,张量流将正确的变换作为损失函数的一部分(例如“sigmoid 交叉熵”=应用 sigmoid,将结果输入交叉熵损失函数)
      【解决方案3】:

      我看到的几个问题:

      您使用的是平方损失,而不是交叉熵,用于分类使用 tf.nn.sigmoid_cross_entropy_with_logits(...),而不是 tf.losses.mean_squared_error

      在这段代码中:

      #normalize each image (originally uint8)
      #input=[input/255 for i in range(len(input))
      

      如果您的输入是 uint8,则您的数据可能被四舍五入为 0,而您只是发送空白图像,这将在您遇到时收敛到一个损失。

      您的第一个调试步骤应该是将图像保存在前行 sess.run。保存您发送到网络的确切图像以进行验证。不要让它变得复杂,只需使用 scipy 将图像保存到文件并进行完整性检查即可。

      此外,您在此处对 TF 进行了冗余调用:

      sess.run(train_step, feed_dict={x:input, y_:labels})
      input, labels = utils.shuffle(input, labels)
      loss.append(sess.run(cross_entropy, feed_dict={x:input, y_:labels}))
      

      将其替换为:

      result_train_step, result_cross_entropy = sess.run([train_step, cross_entropy], feed_dict={x:input, y_:labels})
      

      注意学习率,以1e-4为起点。

      此外,完整性检查您的标签是否与您的图像正确匹配,在您转储图像和完整性检查时将标签保存到文件中。置换标签非常容易。

      【讨论】:

      • 我之前尝试过 tf.nn.sigmoid_cross_entropy_with_logits,然后我用 tf.losses.mean_squared_error 替换它而不更改变量名(cross_entrophy)-我的错:) 我检查了标准化后的照片不为空白,它们被转换为浮点数,它们的值被压缩到 。正如我所说,我用浮点值和标准差 = 。去除多余的调用后,开始减少,但现在趋向于正好 2.0 的值。事实证明,输出几乎都是 -1 的向量。我不知道为什么,我不在任何地方使用 -1
      【解决方案4】:

      我也很难通过自己的一项工作来解决这个问题。结果证明降低学习率帮助我摆脱了持续损失。

      对于您的问题,我将建议接近 5e-5 的内容。希望问题能解决

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-03-20
        • 2020-07-20
        • 2016-08-11
        • 2017-12-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-12
        相关资源
        最近更新 更多