2017-08-08 144 views
0

我刚刚在我的机器学习生涯的开始,并想创建简单的CNN来分类2种不同的树叶(属于2种不同树种)。在收集大量的树叶图片之前,我决定在Tensorflow中创建非常小巧,简单的CNN,并仅在一幅图像上进行训练,以检查代码是否正常。我将大小为256x256(x 3通道)的照片标准化为< 0,1>,并创建了4层(2 conv和2 dense)网络。不幸的是,从一开始,损失几乎总是趋向于一些常数值(通常是一些整数)。我认为图片有些问题,所以我用相同尺寸的随机数组替换它。不幸的是,损失仍然不变。有时网络似乎在学习,因为损失在减少,但大多数时候从一开始就是不变的。任何人都可以帮助解释,为什么这样呢?我读过一个例子的培训是检查你的代码是否缺乏错误的最好方法,但是我与它斗争的时间越长,我越是看不到。CNN在Tensorflow - 损失保持不变

这是我的代码(基于此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个时代。原来,实现不断损失的可能性增加了。这很奇怪,因为在我看来,在训练的早期阶段,时代的数量应该会改变任何事情。
  3. 我把特征图的数量更改为I层中的32,II层中的64和致密层中的100个神经元
  4. 因为我的输出是二进制的,所以最初我只使用单个输出。我将它改为排除2个输出。它将损失改为2.5。事实证明,我的输出倾向于[-1,-1],而标签是[1,0]
  5. 我尝试了各种学习率,从0.001到0.00005
  6. 我初始化了标准偏差的权重和偏差等于2而不是0.1。损失似乎减少了,但是达到了很高的价值,如1e10。所以我把时代的数量从10个改为100个,而且从一开始,损失就是2.5个。在回到10个时期后,损失仍然是2.5
  7. 我扩展了数据集到6个元素。损失与之前相同。

有没有人有任何想法,为什么会发生这种情况?据我所知,如果网络不能一概而论,损失不会减少,反而会增加/波动,但不会保持不变?

回答

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,而你只是发送空白图像,这会在你遇到时收敛到一个损失。

您的第一个调试步骤应该是将图像保存在行之前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

我以前用tf.nn.sigmoid_cross_entropy_with_logits尝试过,然后用tf.losses.mean_squared_error替换它而不更改变量名称(cross_entrophy) - 我的不好:)我在正常化后检查照片并非空白,它们被转换为浮动他们的价值被挤压到<0,1>。正如我所说,我用浮点值和标准偏差= <0.1; 2>随机numpy阵列替换我的照片。删除多余的呼叫后,开始减少,但现在趋向于恰好为2.0的值。事实证明,输出几乎都是-1的所有向量。我不知道为什么,我不使用-1的地方 – tech2nick

1

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

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教程中看到了同样的问题)?我不明白,我认为每一层都应该有自己的激活功能?

+1

哦,我完全错过了。这绝对是错误的。当您应用交叉熵时,您需要一个介于0和1之间的值,损失函数将在最后一层应用sigmoid或softmax。所以最后一层应该是一个简单的线性层。如果使用平方损失总和,则需要最后一层(-inf,+ inf),因此在这种情况下,只需将其保留为可以接受任何实际值的简单线性层。 –

+1

通常,最后一层应该是线性的(不要应用任何非线性变换),然后以任何损失函数所需的方式对其进行变换,每个损失函数将定义它的域应该是什么,并且tensorflow应用正确的转换作为大多数情况下损失函数的一部分(例如“sigmoid交叉熵”=应用sigmoid,馈送结果到交叉熵损失函数中) –

相关问题