1. 程式人生 > >基於Python的卷積神經網路和特徵提取

基於Python的卷積神經網路和特徵提取

基於Python的卷積神經網路和特徵提取

發表於2015-08-27 21:39| 4577次閱讀| 來源blog.christianperone.com/| 13 條評論| 作者Christian S.Peron

深度學習特徵提取神經網路Pythonnolearntheano

摘要:本文展示瞭如何基於nolearn使用一些卷積層和池化層來建立一個簡單的ConvNet體系結構,以及如何使用ConvNet去訓練一個特徵提取器,然後在使用如SVM、Logistic迴歸等不同的模型之前使用它來進行特徵提取。

卷積神經網路(ConvNets)是受生物啟發的MLPs(多層感知器),它們有著不同類別的層,並且每層的工作方式與普通的MLP層也有所差異。如果你對ConvNets感興趣,這裡有個很好的教程

CS231n – Convolutional Neural Newtorks for Visual Recognition。CNNs的體系結構如下所示:

常規的神經網路(來自CS231n網站)

ConvNet網路體系結構(來自CS231n網站)

如你所見,ConvNets工作時伴隨著3D卷積並且在不斷轉變著這些3D卷積。我在這篇文章中不會再重複整個CS231n的教程,所以如果你真的感興趣,請在繼續閱讀之前先花點時間去學習一下。

Lasagne 和 nolearn

Lasagne和nolearn是我最喜歡使用的深度學習Python包。Lasagne是基於Theano的,所以GPU的加速將大有不同,並且其對神經網路建立的宣告方法也很有幫助。nolearn庫是一個神經網路軟體包實用程式集(包含Lasagne),它在神經網路體系結構的建立過程上、各層的檢驗等都能夠給我們很大的幫助。

在這篇文章中我要展示的是,如何使用一些卷積層和池化層來建立一個簡單的ConvNet體系結構。我還將向你展示如何使用ConvNet去訓練一個特徵提取器,在使用如SVM、Logistic迴歸等不同的模型之前使用它來進行特徵提取。大多數人使用的是預訓練ConvNet模型,然後刪除最後一個輸出層,接著從ImageNets資料集上訓練的ConvNets網路提取特徵。這通常被稱為是遷移學習,因為對於不同的問題你可以使用來自其它的ConvNets層,由於ConvNets的第一層過濾器被當做是一個邊緣探測器,所以它們可以用來作為其它問題的普通特徵探測器。

載入MNIST資料集

MNIST資料集是用於數字識別最傳統的資料集之一。我們使用的是一個面向Python的版本,但先讓我們匯入需要使用的包:

 

[py] view plaincopy

  1. import matplotlib  
  2. import matplotlib.pyplot as plt  
  3. import matplotlib.cm as cm  
  4. from urllib import urlretrieve  
  5. import cPickle as pickle  
  6. import os  
  7. import gzip  
  8. import numpy as np  
  9. import theano  
  10. import lasagne  
  11. from lasagne import layers  
  12. from lasagne.updates import nesterov_momentum  
  13. from nolearn.lasagne import NeuralNet  
  14. from nolearn.lasagne import visualize  
  15. from sklearn.metrics import classification_report  
  16. from sklearn.metrics import confusion_matrix  

[py] view plain copy

  1. import matplotlib  
  2. import matplotlib.pyplot as plt  
  3. import matplotlib.cm as cm  
  4. from urllib import urlretrieve  
  5. import cPickle as pickle  
  6. import os  
  7. import gzip  
  8. import numpy as np  
  9. import theano  
  10. import lasagne  
  11. from lasagne import layers  
  12. from lasagne.updates import nesterov_momentum  
  13. from nolearn.lasagne import NeuralNet  
  14. from nolearn.lasagne import visualize  
  15. from sklearn.metrics import classification_report  
  16. from sklearn.metrics import confusion_matrix  

 

正如你所看到的,我們匯入了用於繪圖的matplotlib包,一些用於下載MNIST資料集的原生Python模組,numpy, theano,lasagne,nolearn 以及 scikit-learn庫中用於模型評估的一些函式。

然後,我們定義一個載入MNIST資料集的函式(這個功能與Lasagne教程上使用的非常相似)

 

[py] view plaincopy

  1. def load_dataset():  
  2.     url = 'http://deeplearning.net/data/mnist/mnist.pkl.gz'  
  3.     filename = 'mnist.pkl.gz'  
  4.     if not os.path.exists(filename):  
  5.         print("Downloading MNIST dataset...")  
  6.         urlretrieve(url, filename)  
  7.     with gzip.open(filename, 'rb') as f:  
  8.         data = pickle.load(f)  
  9.     X_train, y_train = data[0]  
  10.     X_val, y_val = data[1]  
  11.     X_test, y_test = data[2]  
  12.     X_train = X_train.reshape((-1, 1, 28, 28))  
  13.     X_val = X_val.reshape((-1, 1, 28, 28))  
  14.     X_test = X_test.reshape((-1, 1, 28, 28))  
  15.     y_train = y_train.astype(np.uint8)  
  16.     y_val = y_val.astype(np.uint8)  
  17.     y_test = y_test.astype(np.uint8)  
  18.     return X_train, y_train, X_val, y_val, X_test, y_test  

[py] view plain copy

  1. def load_dataset():  
  2.     url = 'http://deeplearning.net/data/mnist/mnist.pkl.gz'  
  3.     filename = 'mnist.pkl.gz'  
  4.     if not os.path.exists(filename):  
  5.         print("Downloading MNIST dataset...")  
  6.         urlretrieve(url, filename)  
  7.     with gzip.open(filename, 'rb') as f:  
  8.         data = pickle.load(f)  
  9.     X_train, y_train = data[0]  
  10.     X_val, y_val = data[1]  
  11.     X_test, y_test = data[2]  
  12.     X_train = X_train.reshape((-1, 1, 28, 28))  
  13.     X_val = X_val.reshape((-1, 1, 28, 28))  
  14.     X_test = X_test.reshape((-1, 1, 28, 28))  
  15.     y_train = y_train.astype(np.uint8)  
  16.     y_val = y_val.astype(np.uint8)  
  17.     y_test = y_test.astype(np.uint8)  
  18.     return X_train, y_train, X_val, y_val, X_test, y_test  

 

正如你看到的,我們正在下載處理過的MNIST資料集,接著把它拆分為三個不同的資料集,分別是:訓練集、驗證集和測試集。然後重置影象內容,為之後的Lasagne輸入層做準備,與此同時,由於GPU/theano資料型別的限制,我們還把numpy的資料型別轉換成了uint8。

隨後,我們準備載入MNIST資料集並檢驗它:

 

[py] view plaincopy

  1. X_train, y_train, X_val, y_val, X_test, y_test = load_dataset()  
  2. plt.imshow(X_train[0][0], cmap=cm.binary)  

[py] view plain copy

  1. X_train, y_train, X_val, y_val, X_test, y_test = load_dataset()  
  2. plt.imshow(X_train[0][0], cmap=cm.binary)  

 

這個程式碼將輸出下面的影象(我用的是IPython Notebook)

一個MNIST資料集的數字例項(該例項是5)

ConvNet體系結構與訓練

現在,定義我們的ConvNet體系結構,然後使用單GPU/CPU來訓練它(我有一個非常廉價的GPU,但它很有用)

 

[py] view plaincopy

  1. net1 = NeuralNet(  
  2.     layers=[('input', layers.InputLayer),  
  3.             ('conv2d1', layers.Conv2DLayer),  
  4.             ('maxpool1', layers.MaxPool2DLayer),  
  5.             ('conv2d2', layers.Conv2DLayer),  
  6.             ('maxpool2', layers.MaxPool2DLayer),  
  7.             ('dropout1', layers.DropoutLayer),  
  8.             ('dense', layers.DenseLayer),  
  9.             ('dropout2', layers.DropoutLayer),  
  10.             ('output', layers.DenseLayer),  
  11.             ],  
  12.     # input layer  
  13.     input_shape=(None, 1, 28, 28),  
  14.     # layer conv2d1  
  15.     conv2d1_num_filters=32,  
  16.     conv2d1_filter_size=(5, 5),  
  17.     conv2d1_nonlinearity=lasagne.nonlinearities.rectify,  
  18.     conv2d1_W=lasagne.init.GlorotUniform(),    
  19.     # layer maxpool1  
  20.     maxpool1_pool_size=(2, 2),      
  21.     # layer conv2d2  
  22.     conv2d2_num_filters=32,  
  23.     conv2d2_filter_size=(5, 5),  
  24.     conv2d2_nonlinearity=lasagne.nonlinearities.rectify,  
  25.     # layer maxpool2  
  26.     maxpool2_pool_size=(2, 2),  
  27.     # dropout1  
  28.     dropout1_p=0.5,      
  29.     # dense  
  30.     dense_num_units=256,  
  31.     dense_nonlinearity=lasagne.nonlinearities.rectify,      
  32.     # dropout2  
  33.     dropout2_p=0.5,      
  34.     # output  
  35.     output_nonlinearity=lasagne.nonlinearities.softmax,  
  36.     output_num_units=10,  
  37.     # optimization method params  
  38.     update=nesterov_momentum,  
  39.     update_learning_rate=0.01,  
  40.     update_momentum=0.9,  
  41.     max_epochs=10,  
  42.     verbose=1,  
  43.     )  
  44. # Train the network  
  45. nn = net1.fit(X_train, y_train)  

[py] view plain copy

  1. net1 = NeuralNet(  
  2.     layers=[('input', layers.InputLayer),  
  3.             ('conv2d1', layers.Conv2DLayer),  
  4.             ('maxpool1', layers.MaxPool2DLayer),  
  5.             ('conv2d2', layers.Conv2DLayer),  
  6.             ('maxpool2', layers.MaxPool2DLayer),  
  7.             ('dropout1', layers.DropoutLayer),  
  8.             ('dense', layers.DenseLayer),  
  9.             ('dropout2', layers.DropoutLayer),  
  10.             ('output', layers.DenseLayer),  
  11.             ],  
  12.     # input layer  
  13.     input_shape=(None, 1, 28, 28),  
  14.     # layer conv2d1  
  15.     conv2d1_num_filters=32,  
  16.     conv2d1_filter_size=(5, 5),  
  17.     conv2d1_nonlinearity=lasagne.nonlinearities.rectify,  
  18.     conv2d1_W=lasagne.init.GlorotUniform(),    
  19.     # layer maxpool1  
  20.     maxpool1_pool_size=(2, 2),      
  21.     # layer conv2d2  
  22.     conv2d2_num_filters=32,  
  23.     conv2d2_filter_size=(5, 5),  
  24.     conv2d2_nonlinearity=lasagne.nonlinearities.rectify,  
  25.     # layer maxpool2  
  26.     maxpool2_pool_size=(2, 2),  
  27.     # dropout1  
  28.     dropout1_p=0.5,      
  29.     # dense  
  30.     dense_num_units=256,  
  31.     dense_nonlinearity=lasagne.nonlinearities.rectify,      
  32.     # dropout2  
  33.     dropout2_p=0.5,      
  34.     # output  
  35.     output_nonlinearity=lasagne.nonlinearities.softmax,  
  36.     output_num_units=10,  
  37.     # optimization method params  
  38.     update=nesterov_momentum,  
  39.     update_learning_rate=0.01,  
  40.     update_momentum=0.9,  
  41.     max_epochs=10,  
  42.     verbose=1,  
  43.     )  
  44. # Train the network  
  45. nn = net1.fit(X_train, y_train)  

 

如你所視,在layers的引數中,我們定義了一個有層名稱/型別的元組字典,然後定義了這些層的引數。在這裡,我們的體系結構使用的是兩個卷積層,兩個池化層,一個全連線層(稠密層,dense layer)和一個輸出層。在一些層之間也會有dropout層,dropout層是一個正則化矩陣,隨機的設定輸入值為零來避免過擬合(見下圖)。

Dropout層效果(來自CS231n網站)

呼叫訓練方法後,nolearn包將會顯示學習過程的狀態,我的機器使用的是低端的的GPU,得到的結果如下:

 

[py] view plaincopy

  1. # Neural Network with 160362 learnable parameters  
  2.   
  3. ## Layer information  
  4.   
  5.   #  name      size  
  6. ---  --------  --------  
  7.   0  input     1x28x28  
  8.   1  conv2d1   32x24x24  
  9.   2  maxpool1  32x12x12  
  10.   3  conv2d2   32x8x8  
  11.   4  maxpool2  32x4x4  
  12.   5  dropout1  32x4x4  
  13.   6  dense     256  
  14.   7  dropout2  256  
  15.   8  output    10  
  16.   
  17. epoch   train loss    valid loss    train/val    valid acc  dur  
  18. ------- ------------  ------------  -----------  ---------  ---  
  19.       1     0.85204   0.16707      5.09977      0.95174  33.71s  
  20.       2     0.27571   0.10732      2.56896      0.96825  33.34s  
  21.       3     0.20262   0.08567      2.36524      0.97488  33.51s  
  22.       4     0.16551   0.07695      2.15081      0.97705  33.50s  
  23.       5     0.14173   0.06803      2.08322      0.98061  34.38s  
  24.       6     0.12519   0.06067      2.06352      0.98239  34.02s  
  25.       7     0.11077   0.05532      2.00254      0.98427  33.78s  
  26.       8     0.10497   0.05771      1.81898      0.98248  34.17s  
  27.       9     0.09881   0.05159      1.91509      0.98407  33.80s  
  28.      10     0.09264   0.04958      1.86864      0.98526  33.40s  

[py] view plain copy

  1. # Neural Network with 160362 learnable parameters  
  2.   
  3. ## Layer information  
  4.   
  5.   #  name      size  
  6. ---  --------  --------  
  7.   0  input     1x28x28  
  8.   1  conv2d1   32x24x24  
  9.   2  maxpool1  32x12x12  
  10.   3  conv2d2   32x8x8  
  11.   4  maxpool2  32x4x4  
  12.   5  dropout1  32x4x4  
  13.   6  dense     256  
  14.   7  dropout2  256  
  15.   8  output    10  
  16.   
  17. epoch   train loss    valid loss    train/val    valid acc  dur  
  18. ------- ------------  ------------  -----------  ---------  ---  
  19.       1     0.85204   0.16707      5.09977      0.95174  33.71s  
  20.       2     0.27571   0.10732      2.56896      0.96825  33.34s  
  21.       3     0.20262   0.08567      2.36524      0.97488  33.51s  
  22.       4     0.16551   0.07695      2.15081      0.97705  33.50s  
  23.       5     0.14173   0.06803      2.08322      0.98061  34.38s  
  24.       6     0.12519   0.06067      2.06352      0.98239  34.02s  
  25.       7     0.11077   0.05532      2.00254      0.98427  33.78s  
  26.       8     0.10497   0.05771      1.81898      0.98248  34.17s  
  27.       9     0.09881   0.05159      1.91509      0.98407  33.80s  
  28.      10     0.09264   0.04958      1.86864      0.98526  33.40s  

 

正如你看到的,最後一次的精度可以達到0.98526,是這10個單元訓練中的一個相當不錯的效能。

預測和混淆矩陣

現在,我們使用這個模型來預測整個測試集:

 

[py] view plaincopy

  1. preds = net1.predict(X_test)  

[py] view plain copy

  1. preds = net1.predict(X_test)  

 

我們還可以繪製一個混淆矩陣來檢查神經網路的分類效能:

 

[py] view plaincopy

  1. cm = confusion_matrix(y_test, preds)  
  2. plt.matshow(cm)  
  3. plt.title('Confusion matrix')  
  4. plt.colorbar()  
  5. plt.ylabel('True label')  
  6. plt.xlabel('Predicted label')  
  7. plt.show()  

[py] view plain copy

  1. cm = confusion_matrix(y_test, preds)  
  2. plt.matshow(cm)  
  3. plt.title('Confusion matrix')  
  4. plt.colorbar()  
  5. plt.ylabel('True label')  
  6. plt.xlabel('Predicted label')  
  7. plt.show()  

 

上面的程式碼將繪製下面的混淆矩陣:

混淆矩陣

如你所視,對角線上的分類更密集,表明我們的分類器有一個良好的效能。

過濾器的視覺化

我們還可以從第一個卷積層中視覺化32個過濾器:

 

[py] view plaincopy

  1. visualize.plot_conv_weights(net1.layers_['conv2d1'])  

[py] view plain copy

  1. visualize.plot_conv_weights(net1.layers_['conv2d1'])  

 

上面的程式碼將繪製下面的過濾器:

第一層的5x5x32過濾器

如你所視,nolearn的plot_conv_weights函式在我們指定的層中繪製出了所有的過濾器。

Theano層的功能和特徵提取

現在可以建立theano編譯的函數了,它將前饋輸入資料輸送到結構體系中,甚至是你感興趣的某一層中。接著,我會得到輸出層的函式和輸出層前面的稠密層函式。

 

[py] view plaincopy

  1. dense_layer = layers.get_output(net1.layers_['dense'], deterministic=True)  
  2. output_layer = layers.get_output(net1.layers_['output'], deterministic=True)  
  3. input_var = net1.layers_['input'].input_var  
  4. f_output = theano.function([input_var], output_layer)  
  5. f_dense = theano.function([input_var], dense_layer)  

[py] view plain copy

  1. dense_layer = layers.get_output(net1.layers_['dense'], deterministic=True)  
  2. output_layer = layers.get_output(net1.layers_['output'], deterministic=True)  
  3. input_var = net1.layers_['input'].input_var  
  4. f_output = theano.function([input_var], output_layer)  
  5. f_dense = theano.function([input_var], dense_layer)  

 

如你所視,我們現在有兩個theano函式,分別是f_output和f_dense(用於輸出層和稠密層)。請注意,在這裡為了得到這些層,我們使用了一個額外的叫做“deterministic”的引數,這是為了避免dropout層影響我們的前饋操作。

現在,我們可以把例項轉換為輸入格式,然後輸入到theano函式輸出層中:

 

[py] view plaincopy

  1. instance = X_test[0][None, :, :]  
  2. %timeit -n 500 f_output(instance)  
  3. 500 loops, best of 3: 858 µs per loop  

[py] view plain copy

  1. instance = X_test[0][None, :, :]  
  2. %timeit -n 500 f_output(instance)  
  3. 500 loops, best of 3: 858 µs per loop  

 

如你所視,f_output函式平均需要858µs。我們同樣可以為這個例項繪製輸出層啟用值結果:

 

[py] view plaincopy

  1. pred = f_output(instance)  
  2. N = pred.shape[1]  
  3. plt.bar(range(N), pred.ravel())  

[py] view plain copy

  1. pred = f_output(instance)  
  2. N = pred.shape[1]  
  3. plt.bar(range(N), pred.ravel())  

 

上面的程式碼將繪製出下面的圖:

輸出層啟用值

正如你所看到的,數字被認為是7。事實是為任何網路層建立theano函式都是非常有用的,因為你可以建立一個函式(像我們以前一樣)得到稠密層(輸出層前一個)的啟用值,然後你可以使用這些啟用值作為特徵,並且使用你的神經網路作為特徵提取器而不是分類器。現在,讓我們為稠密層繪製256個啟用單元:

 

[py] view plaincopy

  1. pred = f_dense(instance)  
  2. N = pred.shape[1]  
  3. plt.bar(range(N), pred.ravel())  

[py] view plain copy

  1. pred = f_dense(instance)  
  2. N = pred.shape[1]  
  3. plt.bar(range(N), pred.ravel())  

 

上面的程式碼將繪製下面的圖:

稠密層啟用值

現在,你可以使用輸出的這256個啟用值作為線性分類器如Logistic迴歸或支援向量機的特徵了。

最後,我希望你會喜歡這個教程。