1. 程式人生 > >基於cnn的影象二分類演算法(一)

基於cnn的影象二分類演算法(一)

本演算法是基於tensorflow,使用python語言進行的一種影象分類演算法,參考於谷歌的mnist手寫識別,包括以下幾個模組:影象讀取,影象處理,影象增強。卷積神經網路部分包括:卷積層1,匯合層1(部分文獻也有叫池化層的),卷積層2,匯合層2,全連線層1,全連線層2,共6層神經網路。損失函式採用交叉熵,優化則採用adam優化法,由於資料集大小較小,只有200張圖片,故沒有采用MBGD梯度下降演算法,直接採用BGD梯度下降演算法。

首先是影象讀取模組,由於本方案是應用於識別渣土車頂棚是否遮蓋好的演算法,所以沒有網上現成的資料庫,目前只有從網上收集圖片,並轉換成資料。

"""
定義一個遍歷資料夾下所有圖片,並轉化為矩陣,壓縮為特定大小,並傳入一個總
的矩陣中去的函式
"""      
def creat_x_database(rootdir,resize_row,resize_col):
    #列出資料夾下所有的,目錄和檔案
    list = os.listdir(rootdir)
    #建立一個隨機矩陣,作為多個圖片轉換為矩陣後傳入其中
    database=np.arange(len(list)*resize_row*resize_col*3).reshape(len(list)
    ,resize_row,resize_col,3)
    for i in range(0,len(list)):
        path = os.path.join(rootdir,list[i])    #把目錄和檔名合成一個路徑
        if os.path.isfile(path):                ##判斷路徑是否為檔案
            image_raw_data = tf.gfile.FastGFile(path,'rb').read()#讀取圖片
            with tf.Session() as sess:
                img_data = tf.image.decode_jpeg(image_raw_data)#圖片解碼
                #壓縮圖片矩陣為指定大小
                resized=tf.image.resize_images(img_data,[resize_row,resize_col],method=0)
                database[i]=resized.eval()
    return database                          
  

以上是建立資料集,還有標籤集,網上部分建立標籤集的方法是直接讀取已經設定好標籤的圖片名字建立,比較適合分類數目較多的且在一個資料夾下的圖片,這裡筆者因為圖片數量不多加之圖片全是網上搜索,故採用以下方法

def creat_y_database(length,classfication_value,one_hot_value):
    #建立一個適當大小的矩陣來接收
    array=np.arange(length*classfication_value).reshape(length,classfication_value)
    for i in range(0,length):
        array[i]=one_hot_value #這裡採用one hot值來區別合格與不合格
    return array

下面是卷積神經網路的搭建,這裡參考的mnist手寫輸入識別的分類的神經網路

'''
初步打造一個卷積神經網,使其能夠對輸入圖片進行二分類
'''
#計算準確率

def compute_accuracy(v_xs, v_ys):
    global prediction
    y_pre = sess.run(prediction, feed_dict={xs: v_xs, keep_prob: 1})
    correct_prediction = tf.equal(tf.argmax(y_pre,1), tf.argmax(v_ys,1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    result = sess.run(accuracy, feed_dict={xs: v_xs, ys: v_ys, keep_prob: 1})
    return result          
#定義各引數變數並初始化            
def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial)

def conv2d(x, W):
    # stride [1, x_movement, y_movement, 1]
    # Must have strides[0] = strides[3] = 1
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

def max_pool_2x2(x):
    # stride [1, x_movement, y_movement, 1]
    return tf.nn.max_pool(x, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME')



#建立訓練集
fail_x_data=creat_x_database('E:/data/muck truck pic/failed',128,128)
true_x_data=creat_x_database('E:/data/muck truck pic/qualified',128,128) 
x_data=np.vstack((fail_x_data,true_x_data))  #兩個矩陣在列上進行合併
#建立標籤集    
fail_y_data=creat_y_database(fail_x_data.shape[0],2,[0,1])
true_y_data=creat_y_database(true_x_data.shape[0],2,[1,0])
y_data=np.vstack((fail_y_data,true_y_data))
#劃分訓練集和測試集
x_train,x_test,y_train,y_test = train_test_split(x_data,y_data,test_size=0.1,random_state=0)
#train_test_split函式用於將矩陣隨機劃分為訓練子集和測試子集,並返回劃分好的訓練集測試集樣本和訓練集測試集標籤。
    
    
xs = tf.placeholder(tf.float32, [None, 128,128,3])/255 #歸一化
ys = tf.placeholder(tf.float32, [None, 2])
keep_prob = tf.placeholder(tf.float32)
#x_image = tf.reshape(xs, [-1, 50, 50, 3])

W_conv1 = weight_variable([5,5, 3,32]) # patch 5x5, in size 3, out size 32
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(xs, W_conv1) + b_conv1) # output size 128x128x32
h_pool1 = max_pool_2x2(h_conv1)                          # output size 64x64x32

W_conv2 = weight_variable([5,5, 32, 64]) # patch 5x5, in size 32, out size 64
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) # output size 64x64x64
h_pool2 = max_pool_2x2(h_conv2) #32x32x64

W_fc1 = weight_variable([32*32*64, 1024])
b_fc1 = bias_variable([1024])
   
h_pool2_flat = tf.reshape(h_pool2, [-1, 32*32*64])
h_fc1 = tf.nn.sigmoid(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

W_fc2 = weight_variable([1024, 2])
b_fc2 = bias_variable([2])
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction+ 1e-10), reduction_indices=[1]))
#由於 prediction 可能為 0, 導致 log 出錯,最後結果會出現 NA      
#train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)    
sess=tf.Session()
init=tf.global_variables_initializer()
sess.run(init)

for i in range(1000):
#    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={xs: x_train, ys: y_train, keep_prob: 0.5})
    if i % 50 == 0:
        print(compute_accuracy(x_test,y_test))
        print(sess.run(cross_entropy, feed_dict={xs: x_train, ys: y_train, keep_prob: 1}))

這裡有一個坑點要說明一下,在修改神經網路的過程中,發現精確度一直不變,這說明模型不收斂,一層一層的查詢問題才發現是prediction的值可能為0,導致交叉熵出現-Naf這種情況。解決辦法如上所示,在prediction的後面加一個極小數,防止log出現負無窮的情況。同時參考其他文章,在倒數第二層的啟用函式上由ReLU改為sigmoid,原因是ReLU輸出可能相差很大(比如0和幾十),這時再經過softmax就會出現一個節點為1其它全0的情況。softmax的cost function裡包含一項log(y),如果y正好是0就沒法算了。

最終訓練結果如圖所示:

訓練結果並不好,最高有75%,可能原因有超引數的調整沒到位,影象樣本太少,訓練層數太少,訓練次數太少等。後期需要改進這個模型。