1. 程式人生 > >CNN提取圖片特徵,之後用SVM分類

CNN提取圖片特徵,之後用SVM分類

https://blog.csdn.net/qq_27756361/article/details/80479278

先用CNN提取特徵,之後用SVM分類,平臺是TensorFlow 1.3.0-rc0,python3.6 

這個是我的一個小小的測試,下面這個連結是我主要參考的,在實現過程中,本來想使用vgg16或者VGG19做遷移來提取特徵,但是擔心自己的算力不夠,還有就是UCI手寫資料集本來就是一個不大的資料集,使用vgg16或vgg19有點殺雞用牛刀的感覺。於是放棄做遷移。

我的程式碼主要是基於下面連結來的。參考連結點選開啟連結

這個程式碼主要是,通過一個CNN網路,在網路的第一個全連線層也就h_fc1得到的一個一維的256的一個特徵向量,將這個特徵向量作為的svm的輸入。主要的操作是在程式碼行的140-146.    同時代碼也實現了CNN的過程(讀一下程式碼就知道)。

如果你要是知道你的CNN的結構,然後就知道在全連線層輸出的其實就是一個特徵向量。直接用這個特徵向量簡單處理輸入到svm中就可以。

具體的參考論文和程式碼資料集等,百度網盤

CNN卷積層簡介

CNN,有兩個卷積(5*5)池化層(2*2的maxPooling),然後兩個全連線層h_fc1和h_fc2,我只使用第一個全連線層h_fc1就提取了特徵。

然後中間的啟用函式使用的是relu函式,同時為了防止過擬合使用了dropout的技巧。然後這個程式碼中其實是實現了完整的CNN的的預測的,損失使用交叉熵,優化器使用了AdamOptimizer。

圖片大小的變化:

最後從全連線層提取的256維的向量。輸入svm。

 

SVM分類

SVM採用的是RBF核(高斯核),C取0.9

也可以嘗試線性核,我試了一下效果差不多,但是沒有高斯核分類效率好。

流程和實驗設計

 

流程:整理訓練網路的資料,對樣本進行處理 -> 建立卷積神經網路-> 將資料代入進行訓練 -> 儲存訓練好的模型(從全連線層提取特徵) -> 把資料代入模型獲得特徵向量 -> 用特徵向量代替原本的輸入送入SVM訓練 -> 測試時同樣將h_fc1轉換為特徵向量之後用SVM預測,獲得結果。

使用留出法樣本處理和評價:

1.將原樣本隨機地分為兩半。一份為訓練集,一份為測試集

2.重複1過程十次,得到十個訓練集和十個對應的測試集

3.取十份訓練集中的一份和其對應的測試集。代入到CNN和SVM中訓練。

4.依次取訓練集和測試集,則可完成十次第一步。

5.將十次的表現綜合評價,十次驗證取平均值,計算正確率、準確率、召回率、F1值。比如 F1 分數 , 用於測量不均衡資料的精度. 

 

 
  1. # coding=utf8

  2. import random

  3.  
  4. import numpy as np

  5. import tensorflow as tf

  6. from sklearn import svm

  7. from sklearn import preprocessing

  8. import time

  9.  
  10. start = time.clock()

  11.  
  12. right0 = 0.0  # 記錄預測為1且實際為1的結果數

  13. error0 = 0  # 記錄預測為1但實際為0的結果數

  14. right1 = 0.0  # 記錄預測為0且實際為0的結果數

  15. error1 = 0  # 記錄預測為0但實際為1的結果數

  16.  
  17. for file_num in range(10):

  18.     # 在十個隨機生成的不相干資料集上進行測試,將結果綜合

  19.     print('testing NO.%d dataset.......' % file_num)

  20.     ff = open('digit_train_' + file_num.__str__() + '.data')

  21.     rr = ff.readlines()

  22.     x_test2 = []

  23.     y_test2 = []

  24.     

  25.     for i in range(len(rr)):

  26.         x_test2.append(list(map(int, map(float, rr[i].split(' ')[:256]))))

  27.         y_test2.append(list(map(int, rr[i].split(' ')[256:266])))

  28.     ff.close()

  29.     # 以上是讀出訓練資料

  30.     ff2 = open('digit_test_' + file_num.__str__() + '.data')

  31.     rr2 = ff2.readlines()

  32.     x_test3 = []

  33.     y_test3 = []

  34.     for i in range(len(rr2)):

  35.         x_test3.append(list(map(int, map(float, rr2[i].split(' ')[:256]))))

  36.         y_test3.append(list(map(int, rr2[i].split(' ')[256:266])))

  37.     ff2.close()

  38.     # 以上是讀出測試資料

  39.  
  40.     sess = tf.InteractiveSession()

  41.     # 建立一個tensorflow的會話

  42.  
  43.     # 初始化權值向量

  44.     def weight_variable(shape):

  45.         initial = tf.truncated_normal(shape, stddev=0.1)

  46.         return tf.Variable(initial)

  47.  
  48.  
  49.     # 初始化偏置向量

  50.     def bias_variable(shape):

  51.         initial = tf.constant(0.1, shape=shape)

  52.         return tf.Variable(initial)

  53.  
  54.  
  55.     # 二維卷積運算,步長為1,輸出大小不變

  56.     def conv2d(x, W):

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

  58.  
  59.  
  60.     # 池化運算,將卷積特徵縮小為1/2

  61.     def max_pool_2x2(x):

  62.         return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

  63.  
  64.     # 給x,y留出佔位符,以便未來填充資料

  65.     x = tf.placeholder("float", [None, 256])

  66.     y_ = tf.placeholder("float", [None, 10])

  67.     # 設定輸入層的W和b

  68.     W = tf.Variable(tf.zeros([256, 10]))

  69.     b = tf.Variable(tf.zeros([10]))

  70.     # 計算輸出,採用的函式是softmax(輸入的時候是one hot編碼)

  71.     y = tf.nn.softmax(tf.matmul(x, W) + b)

  72.     # 第一個卷積層,5x5的卷積核,輸出向量是32維

  73.     w_conv1 = weight_variable([5, 5, 1, 32])

  74.     b_conv1 = bias_variable([32])

  75.     x_image = tf.reshape(x, [-1, 16, 16, 1])

  76.     # 圖片大小是16*16,,-1代表其他維數自適應

  77.     h_conv1 = tf.nn.relu(conv2d(x_image, w_conv1) + b_conv1)

  78.     h_pool1 = max_pool_2x2(h_conv1)

  79.     # 採用的最大池化,因為都是1和0,平均池化沒有什麼意義

  80.  
  81.     # 第二層卷積層,輸入向量是32維,輸出64維,還是5x5的卷積核

  82.     w_conv2 = weight_variable([5, 5, 32, 64])

  83.     b_conv2 = bias_variable([64])

  84.  
  85.     h_conv2 = tf.nn.relu(conv2d(h_pool1, w_conv2) + b_conv2)

  86.     h_pool2 = max_pool_2x2(h_conv2)

  87.  
  88.     # 全連線層的w和b

  89.     w_fc1 = weight_variable([4 * 4 * 64, 256])

  90.     b_fc1 = bias_variable([256])

  91.     # 此時輸出的維數是256維

  92.     h_pool2_flat = tf.reshape(h_pool2, [-1, 4 * 4 * 64])

  93.     h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, w_fc1) + b_fc1)

  94.     # h_fc1是提取出的256維特徵,很關鍵。後面就是用這個輸入到SVM中

  95.  
  96.     # 設定dropout,否則很容易過擬合

  97.     keep_prob = tf.placeholder("float")

  98.     h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

  99.  
  100.     # 輸出層,在本實驗中只利用它的輸出反向訓練CNN,至於其具體數值我不關心

  101.     w_fc2 = weight_variable([256, 10])

  102.     b_fc2 = bias_variable([10])

  103.  
  104.     y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, w_fc2) + b_fc2)

  105.     cross_entropy = -tf.reduce_sum(y_ * tf.log(y_conv))

  106.     # 設定誤差代價以交叉熵的形式

  107.     train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

  108.     # 用adma的優化演算法優化目標函式

  109.     correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))

  110.     accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

  111.     sess.run(tf.global_variables_initializer())

  112.     for i in range(3000):

  113.         # 跑3000輪迭代,每次隨機從訓練樣本中抽出50個進行訓練

  114.         batch = ([], [])

  115.         p = random.sample(range(795), 50)

  116.         for k in p:

  117.             batch[0].append(x_test2[k])

  118.             batch[1].append(y_test2[k])

  119.         if i % 100 == 0:

  120.             train_accuracy = accuracy.eval(feed_dict={x: batch[0], y_: batch[1], keep_prob: 1.0})

  121.             # print "step %d, train accuracy %g" % (i, train_accuracy)

  122.         train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.6})

  123.         # 設定dropout的引數為0.6,測試得到,大點收斂的慢,小點立刻出現過擬合

  124.  
  125.     print("test accuracy %g" % accuracy.eval(feed_dict={x: x_test3, y_: y_test3, keep_prob: 1.0}))

  126.     # def my_test(input_x):

  127.     #     y = tf.nn.softmax(tf.matmul(sess.run(x), W) + b)

  128.     

  129.     for h in range(len(y_test2)):

  130.         if np.argmax(y_test2[h]) == 7:

  131.             y_test2[h] = 1

  132.         else:

  133.             y_test2[h] = 0

  134.     for h in range(len(y_test3)):

  135.         if np.argmax(y_test3[h]) == 7:

  136.             y_test3[h] = 1

  137.         else:

  138.             y_test3[h] = 0

  139.     # 以上兩步都是為了將源資料的one hot編碼改為1和0,我的學號尾數為7

  140.     x_temp = []

  141.     for g in x_test2:

  142.         x_temp.append(sess.run(h_fc1, feed_dict={x: np.array(g).reshape((1, 256))})[0])

  143.     # 將原來的x帶入訓練好的CNN中計算出來全連線層的特徵向量,將結果作為SVM中的特徵向量

  144.     x_temp2 = []

  145.     for g in x_test3:

  146.         x_temp2.append(sess.run(h_fc1, feed_dict={x: np.array(g).reshape((1, 256))})[0])

  147.  
  148.     clf = svm.SVC(C=0.9, kernel='linear')  #linear kernel

  149. #    clf = svm.SVC(C=0.9, kernel='rbf')   #RBF kernel

  150.     # SVM選擇了RBF核,C選擇了0.9

  151. #    x_temp = preprocessing.scale(x_temp)  #normalization

  152.     clf.fit(x_temp, y_test2)

  153.     

  154.     print('svm testing accuracy:')

  155.     print(clf.score(x_temp2,y_test3))

  156.     for j in range(len(x_temp2)):

  157.         # 驗證時出現四種情況分別對應四個變數儲存

  158.         #這裡報錯了,需要對其進行reshape(1,-1)

  159.         if clf.predict(x_temp2[j].reshape(1,-1))[0] == y_test3[j] == 1:

  160.             right0 += 1

  161.         elif clf.predict(x_temp2[j].reshape(1,-1))[0] == y_test3[j] == 0:

  162.             right1 += 1

  163.         elif clf.predict(x_temp2[j].reshape(1,-1))[0] == 1 and y_test3[j] == 0:

  164.             error0 += 1

  165.         else:

  166.             error1 += 1

  167.     

  168. accuracy = right0 / (right0 + error0)  # 準確率

  169. recall = right0 / (right0 + error1)  # 召回率

  170. print('svm right ratio ', (right0 + right1) / (right0 + right1 + error0 + error1))

  171. print ('accuracy ', accuracy)

  172. print ('recall ', recall)

  173. print ('F1 score ', 2 * accuracy * recall / (accuracy + recall))  # 計算F1值

  174.  
  175. end = time.clock()

  176. print("time is :")

  177. print(end-start)

使用CNN之後用SVM分類。這個操作有很多。比如RCNN(Regions with CNN features)用於目標檢測的網路的一系列的演算法【SPP-Net】。基本就是CNN之後svm。

 

參考文獻

[1] Deep Learning using Linear Support Vector Machines, ICML 2013

[2] How transferable are features in deep neural networks?, Jason Yosinski,1 Jeff Clune,2 Yoshua Bengio, NIPS 2014

[3] CNN Features off-the-shelf: an Astounding Baseline for Recognition, Ali Sharif Razavian Hossein Azizpour Josephine Sullivan Stefan Carlsson CVAP, KTH (Royal Institute of Technology). CVPR 2014

主要參考第一篇,具體的論文我把論文放到百度網盤中了:

https://pan.baidu.com/s/1Ghh4nfjfBKDyA47fc6M4JQ

有相同的CNN之後使用SVM的一些GitHub的開原始碼:

https://github.com/Fdevmsy/Image_Classification_with_5_methods

https://github.com/efidalgo/AutoBlur_CNN_Features

https://github.com/tomrunia/TF_FeatureExtraction