首页 文章

Tensorflow中的CNN - 损失保持不变

提问于
浏览
1

我刚刚开始我的机器学习生涯,并希望创建简单的CNN来分类2种不同的叶子(属于2种不同的树种) . 在收集大量的叶子图片之前,我决定在Tensorflow中创建一个非常小的,简单的CNN,并仅在一个图像上进行训练,以检查代码是否正常 . 我将尺寸为256x256(x 3通道)的照片标准化为<0,1>,并创建了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()

这里列出了我尝试的内容:

  • 上面的基本代码导致损失几乎总是等于4.0

  • 将训练时间扩大到100个时期 . 事实证明,实现持续损失的可能性增加了 . 这很奇怪,因为在我看来,在训练的早期阶段,时代的数量应该改变 .

  • 我将特征图的数量在I层中改为32,在II层中改为64,在密集层中改为100个神经元

  • 因为我的输出是二进制的,所以最初我只用了单输出 . 我将其更改为排除2个输出 . 它将损失变为2.5 . 事实证明,我的输出往往是[-1,-1],而标签是[1,0]

  • 我尝试了各种学习率,从0.001到0.00005

  • 我初始化了权重和偏差,标准偏差等于2而不是0.1 . 损失似乎减少了,但实现了高 Value ,如1e10 . 所以我把时代的数量从10改为100 ......再次,损失从一开始就是2.5 . 回到10个时代后,损失仍为2.5

  • 我将数据集扩展为6个元素 . 损失与以前相同 .

有谁有任何想法,为什么会这样?据我所知,如果网不能概括,损失不会减少而是增加/振荡但不会保持不变?

4 回答

  • 0

    我找到了答案 . 这个问题是由这条线引起的:

    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

    我看到的一些问题:

    你使用方损,而不是交叉熵,用于分类使用 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并且您只是发送空白图像,这会在您遇到时收敛到一个损失 .

    您的第一个调试步骤应该是将图像保存在 the line before 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开始作为一个良好的起点 .

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

  • 0

    我也很难用自己的一项工作来解决这个问题 . 结果降低学习率帮助我摆脱了不断的损失 .

    对于你的问题,我会建议接近5e-5的东西 . 希望问题能够得到解决

  • 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]
    

    我希望能帮助你 .

相关问题