2017-08-08 190 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,饋送結果到交叉熵損失函數中) –