【theano-windows】学习笔记十二——卷积神经网络
前言
按照進度, 學習theano中的卷積操作
國際慣例, 來一波參考網址
Convolutional Neural Networks (LeNet)
卷積神經網絡如何應用在彩色圖像上?
卷積小知識
三大特性:局部感知(稀疏連接), 權值共享, 池化
上圖很重要, 描述的是前一個隱層m-1具有四個特征圖, 第m個隱層具有兩個特征圖, 四個權重框代表的是第m-1層的所有特征圖與第m層的第一個特征圖的連接,注意前一層的所有特征圖與后一層第二個特征圖的權重并沒有畫出來, 所以說, 由第m-1層映射到第m層, 總共需要4*2=8個不同的權重矩陣, 而不是四個.
代碼實現
前面折騰過各種函數,其中就有conv2d, 然后我們就用它來嘗試一下2D卷積,需要注意的是定義的輸入、輸出、卷積核矩陣它們每個維度都代表什么,詳細可以戳我前面的【theano-windows】學習筆記十一——theano中與神經網絡相關函數, 這里也寫一下:
對于輸入:是一個4D張量,分別代表 [批大小,輸入特征數(即通道數),圖像高度,圖像寬度]
對于卷積核: 是一個4D張量,分別代表(第m層的特征圖數目,第m?1層的特征圖數目,卷積核高,卷積核寬)
無反向傳播的卷積網絡
其實這個代碼與第一篇關于theano的博客一樣,戳這里
寫一遍便于理解, 順序就是
定義輸入張量
import theano import theano.tensor as T import numpy as np#隨機數種子初始化rng=np.random.RandomState(23455) input=T.tensor4(name='input')#定義輸入張量初始化權重張量
#初始化權重張量w_shp=(2,3,9,9)#下一層兩個特征圖,上一層三個特征圖,卷積核大小9*9 w_bound=np.sqrt(3*9*9) W=theano.shared(np.asarray(rng.uniform(low=-1.0/w_bound,high=1.0/w_bound,size=w_shp),dtype=input.dtype),name='W')初始化偏置張量
#初始化偏置,如果需要訓練,一般設置為0,此處模仿訓練好的偏置b_shp=(2,) b=theano.shared(np.asarray(rng.uniform(low=-.5,high=.5,size=b_shp),dtype=input.dtype),name='b')卷積并用sigmoid激活
#卷積操作conv_out=T.nnet.conv2d(input,W)#用sigmoid激活output=T.nnet.sigmoid(conv_out+b.dimshuffle('x',0,'x','x'))丟入到function中供后續使用
#整個操作的函數f=theano.function([input],output)讀取并處理圖片:需要注意的是圖片必須改成(批大小,通道,高,寬)
############################注意事項#################################### 圖片的原始數據是一個3D數據【高,寬,通道數量】, # 經過數據置換(transpose(2,0,1))之后,變成了【通道數量,高,寬】, # 因為f中傳入參數需要4D,因此需要將圖片數據reshape成為一個【1, 通道數量, 高, 寬】這樣的4D張量, # reshape的參數一定要注意,1就是最外的那一維度,3就是通道數量,然后是【高】和【寬】, # 這樣結果的 img_.shape =【1, 3, 寬, 高】 # # 為什么reshape為這樣的size呢?因為調用f時需要傳入一個input,而這個input就是4D,最終的這個input是傳入到 # conv2d中的第一個參數,而那個參數的格式是什么呢?[mini-batch size,特征圖的數量,圖像高度,圖像寬度] # 這樣就串起來了吧,第一個參數是batch size,據我所知應該是指卷積核的數量吧,但是不知道為什么這里是1? # 第二個參數代表輸入層的特征圖數量,這個地方是3,其實就是把一張彩色圖片按照3個通道作為3個特征圖進行輸入; # 最后兩個是圖像的高度和寬度,正好一一對應。 #使用此函數對一張圖片進行操作import pylab from PIL import Imageimg = Image.open('F:\\Photo\\2.jpg')#讀取圖片 img_w,img_h=img.size#圖像的寬和高 img=np.asarray(img,dtype=input.dtype)/256. img_=img.transpose(2,0,1).reshape(1,3,img_h,img_w) filtered_img=f(img_)可視化結果
pylab.subplot(1,3,1);pylab.axis('off');pylab.imshow(img) pylab.gray(); pylab.subplot(1,3,2);pylab.axis('off');pylab.imshow(filtered_img[0,0,:,:]) pylab.subplot(1,3,3);pylab.axis('off');pylab.imshow(filtered_img[0,1,:,:]) pylab.show()
最大值池化
是卷積中非常重要的非線性下采樣方法,它將圖像分成不重疊的塊,對每一塊選擇最大值輸出
兩個理由告訴你為什么最大值池化重要:
- 通過消除非極大值,可以降低高層的計算
- 提供了平移不變性, 圖像中的每個像素就有8個平移自由度, 如果將最大值池化級聯到卷積層,假設是2*2的池化區域, 8種平移可能有3個相同結果, 但是如果用3×3的池化區域,就可能有5種相同結果(這個原因還在探索中)
函數調用格式是:
theano.tensor.signal.pool.pool_2d(input, ws=None, ignore_border=None, stride=None, pad=(0, 0), mode='max', ds=None, st=None, padding=None)參數說明:
- input: N維的圖片輸入張量
- ws:長度為2的元組, 縮減因子,在每個維度上將圖像減半
- ignore_border: 若為true, 則(5,5)的輸入經過ws=(2,2)得到(2,2)的輸出, 否則是(3,3)的輸出
- stride:步長, 池化操作在行/列上移動的長度,如果是None,那么與ws相同
- pad:兩個int型元組,或者兩個int型的theano向量,大小為2, 用于填充圖像的四個邊界,第一個是上下填充大小, 第二個是左右填充大小
- mode是每個池化窗口的操作方法,包括max,’sum’,’average_inc_pad’,’average_exc_pad’
- ds,st,padding丟棄使用了,分別用ws,stride,pad代替
此處我們就不用官方的例子了,直接把上面的圖片進行池化操作:
import theano import theano.tensor as T import numpy as np import pylab from PIL import Image #隨機數種子初始化 rng=np.random.RandomState(23455) input=T.dtensor4(name='input')#定義輸入張量 img = Image.open('F:\\Photo\\2.jpg')#讀取圖片 img_w,img_h=img.size#圖像的寬和高 img=np.asarray(img,dtype=input.dtype)/256. img_=img.transpose(2,0,1).reshape(1,3,img_h,img_w) maxpool_shape=(2,2) pool_out=T.signal.pool.pool_2d(input,maxpool_shape,ignore_border=False) f_pool=theano.function([input],pool_out) pool_img=f_pool(img_) img_pool=pool_img[0,:,:,:].transpose(1,2,0)print img.shape print img_pool.shapepylab.subplot(1,2,1);pylab.axis('off');pylab.imshow(img) pylab.subplot(1,2,2);pylab.axis('off');pylab.imshow(img_pool) pylab.show()輸出
上述程序一定要注意定義的容器input的數據類型,我剛開始寫的程序如下:
#錯誤寫法 import theano import theano.tensor as T import numpy as np import pylab from PIL import Image #隨機數種子初始化 rng=np.random.RandomState(23455) input=T.tensor4(name='input')#定義輸入張量 img = Image.open('F:\\Photo\\2.jpg')#讀取圖片 img_w,img_h=img.size#圖像的寬和高 img=np.asarray(img,dtype=input.dtype)/256. img_=img.transpose(2,0,1).reshape(1,3,img_h,img_w) maxpool_shape=(2,2) pool_out=T.signal.pool.pool_2d(input,maxpool_shape,ignore_border=False) f_pool=theano.function([input],pool_out) print input.dtype#float32 print img_.dtype#float32 pool_img=f_pool(img_) img_pool=pool_img[0,:,:,:].transpose(1,2,0)print img.shape print img_pool.shapepylab.subplot(1,2,1);pylab.axis('off');pylab.imshow(img) pylab.subplot(1,2,2);pylab.axis('off');pylab.imshow(img_pool) pylab.show()雖然我們的input容器和img輸入都是float32,但是不知道為什么一直出現下列錯誤:
ValueError: GpuDownsampleFactorMax: last dimention size of 600 is bigger then 512. This case is not implemented. Apply node that caused the error: GpuDownsampleFactorMax{(2, 2),False}(GpuFromHost.0) Toposort index: 1 Inputs types: [CudaNdarrayType(float32, 4D)] Inputs shapes: [(1, 3, 800, 1200)] Inputs strides: [(0, 960000, 1200, 1)] Inputs values: ['not shown'] Outputs clients: [[HostFromGpu(GpuDownsampleFactorMax{(2, 2),False}.0)]]HINT: Re-running with most Theano optimization disabled could give you a back-trace of when this node was created. This can be done with by setting the Theano flag 'optimizer=fast_compile'. If that does not work, Theano optimizations can be disabled with 'optimizer=None'. HINT: Use the Theano flag 'exception_verbosity=high' for a debugprint and storage map footprint of this apply node.網上的解釋戳這里,它的意思是池化以后的矩陣的最后一個維度(第四個維度)不支持大于512的維度,建議翻轉一下后兩個維度. 但是如果我們的圖片長寬都大于1024,那么翻轉就沒用了,這時候只要把input容器變成dtensor,而非tensor就可以運行了,很神奇,卡了我好幾個小時, 原理在于dtensor是在CPU上運行, 而CPU的內存交換沒有GPU那么小,tensor是運行在GPU上的.
另一種可以處理任意維度的數據的方法是使用theano.sandbox.cuda.dnn.dnn_pool,詳細介紹戳這里, 代碼如下
#錯誤寫法 import theano import theano.tensor as T import numpy as np import pylab from PIL import Image #random seeds rng=np.random.RandomState(23455) input=T.tensor4(name='input',dtype=theano.config.floatX)#input tensor# img = Image.open('F:\\Photo\\2.jpg')#read image # img_w,img_h=img.size# with and height of the image # img=np.asarray(img,dtype=theano.config.floatX)/256. #normalize # img_=img.transpose(2,0,1).reshape(1,3,img_h,img_w) #reshape for inputimg=np.random.RandomState(1).rand(5000, 5000,3) img_=np.asarray(img,dtype=theano.config.floatX) img_=img.transpose(2,0,1).reshape(1,3,img_.shape[0],img_.shape[1]) img_=np.asarray(img_,dtype=theano.config.floatX)print img_.shape maxpool_shape=(2,2) # pool_out=T.signal.pool.pool_2d(input,maxpool_shape,ignore_border=False)#limited by 512 pool_out=theano.sandbox.cuda.dnn.dnn_pool(input,maxpool_shape)# non-limitedf_pool=theano.function([input],pool_out) print input.dtype#float32 print img_.dtype#float32 pool_img=f_pool(img_)pool_img=np.asarray(pool_img,dtype=theano.config.floatX) img_pool=pool_img[0,:,:,:].transpose(1,2,0)print img.shape print img_pool.shapepylab.subplot(1,2,1);pylab.axis('off');pylab.imshow(img) pylab.subplot(1,2,2);pylab.axis('off');pylab.imshow(img_pool) pylab.show()Lenet分類模型
模型結構如下:
如此就可以按照順序依次搭建: 卷積+池化, 全連接+中間隱層, softmax
讀數據
這個沒什么好說的,跟前面博客一樣, 重點是記得把數據放入到共享區域
#引入相關庫 import theano import theano.tensor as T import numpy as np import os import cPickle,gzip import timeit#定義讀數據的函數,把數據丟入到共享區域 def load_data(dataset):data_dir,data_file=os.path.split(dataset)if os.path.isfile(dataset):with gzip.open(dataset,'rb') as f:train_set,valid_set,test_set=cPickle.load(f)#共享數據集def shared_dataset(data_xy,borrow=True):data_x,data_y=data_xyshared_x=theano.shared(np.asarray(data_x,dtype=theano.config.floatX),borrow=borrow)shared_y=theano.shared(np.asarray(data_y,dtype=theano.config.floatX),borrow=borrow)return shared_x,T.cast(shared_y,'int32')#定義三個元組分別存儲訓練集,驗證集,測試集train_set_x,train_set_y=shared_dataset(train_set)valid_set_x,valid_set_y=shared_dataset(valid_set)test_set_x,test_set_y=shared_dataset(test_set)rval=[(train_set_x,train_set_y),(valid_set_x,valid_set_y),(test_set_x,test_set_y)]return rval定義卷積池化層
使用的fan_in,fan_out權重初始化準則, 具體公式戳這里, 前提是需要知道對于每個隱單元有fan_in=(輸入特征圖個數×卷積核高×卷積核寬)個連接,對于低層的每個單元接受的梯度來自fan_out=(輸出特征圖個數×卷積核高×卷積核寬)/池化大小.
實現此層過程就是, 先定義并初始化權重和偏置, 隨后進行卷積操作, 池化, 激活, (貌似正常情況下是卷積->激活->池化).
#定義卷積和最大池化 class ConvPool(object):def __init__(self,rng,input,filter_shape,img_shape,pool_shape):#input是容器,img_shape是(批大小,輸入特征圖數,高,寬)self.input=input#初始化參數按照fan_in,fan_out準則#對于每個隱單元,有(輸入特征圖*高*寬)個輸入神經元fan_in=np.prod(filter_shape[1:])#對于低層隱單元接收(輸出特征圖*濾波器高*濾波器寬)/池化大小的梯度fan_out=(filter_shape[0]*np.prod(filter_shape[2:])//np.prod(pool_shape))#隨機初始化權重W_bound=np.sqrt(6./(fan_in+fan_out))self.W=theano.shared(np.asarray(rng.uniform(low=-W_bound,high=W_bound,size=filter_shape),dtype=theano.config.floatX),borrow=True)#初始化偏置全零b_values=np.zeros((filter_shape[0],),dtype=theano.config.floatX)self.b=theano.shared(value=b_values,borrow=True)#定義卷積操作conv_out=T.nnet.conv2d(input=input,filters=self.W,filter_shape=filter_shape,input_shape=img_shape)#定義池化操作pool_out=T.signal.pool.pool_2d(input=conv_out,ws=pool_shape,ignore_border=True)#激活函數self.output=T.tanh(pool_out+self.b.dimshuffle('x',0,'x','x'))#存儲參數self.params=[self.W,self.b]#更新輸出self.input=input定義隱層
這一個可以戳前面的博客, 大概過程也是: 定義并初始化權重和偏置, 乘積激活
#定義隱層 class HiddenLayer(object):def __init__(self,rng,input,n_in,n_out,W=None,b=None,activitation=T.tanh):self.input=input #輸入#根據fan_in,fan_out初始化權重if W is None:W_values=np.asarray(rng.uniform(low=- np.sqrt(6./(n_in+n_out)),high=np.sqrt(6./(n_in+n_out)),size=(n_in,n_out)),dtype=theano.config.floatX)if activitation==T.nnet.sigmoid:W_values*=4if b is None:b_values=np.zeros((n_out,),dtype=theano.config.floatX)#將權重和偏置放入到共享變量中W=theano.shared(value=W_values,name='W',borrow=True)b=theano.shared(value=b_values,name='b',borrow=True)self.W=Wself.b=bself.params=[self.W,self.b]#激活lin_ouput=T.dot(input,self.W)+self.bself.output=(lin_ouput if activitation is None else activitation(lin_ouput))定義softmax層
過程是: 定義并初始化權重, 定義損失函數, 定義測試誤差函數
#定義輸出層,softmax class SoftMax(object):def __init__(self,rng,input,n_in,n_out):#定義權重W_values=np.asarray(rng.uniform(low=-np.sqrt(6./(n_in+n_out)),high=np.sqrt(6./(n_in+n_out)),size=(n_in,n_out)),dtype=theano.config.floatX)#定義偏置b_values=np.zeros((n_out,),dtype=theano.config.floatX)#共享變量self.W=theano.shared(value=W_values,borrow=True,name='W')self.b=theano.shared(value=b_values,borrow=True,name='b')#softmax函數值self.p_y_given_x=T.nnet.softmax(T.dot(input,self.W)+self.b)#預測值self.y_pred=T.argmax(self.p_y_given_x,axis=1)self.params=[self.W,self.b]self.input=inputdef negative_log_likelihood(self,y):#定義對數似然return - T.mean(T.log(self.p_y_given_x)[T.arange(y.shape[0]),y])def errors(self,y):#定義誤差if y.ndim!=self.y_pred.ndim:raise TypeError('y should have the same shape as self.y_pred',('y',y.type,'y_pred',self.y_pred.type))if y.dtype.startswith('int'):return T.mean(T.neq(self.y_pred,y))else:raise NotImplementedError()搭建網絡
所需要的層已經定義完畢, 然后按照上面的圖中所示的網絡結構搭建.
需要注意的問題有:
- 原始圖片存儲方法為每一行代表一張圖片,所以在丟入到卷積層之前需要重新組織成(批大小, 通道數, 圖片高, 圖片寬), 因為是灰度圖,所以通道為1,圖片大小是標準的mnist手寫數字大小(28\times28)
- 最好事先推導一下每層卷積的特征圖大小, 全連接層單元數, 因為定義的網絡需要輸入特征圖大小, 我剛開始想的是取出第一層卷積過后得到的ouput, 然后用shape自動得到第二層所需要的分別得到下一個卷積所需要的特征圖大小, 但是死活取不出來這個值, 尷了個尬.
- 存儲模型的方法還是沿用上一篇博客介紹的方法,存儲為pkl格式
其實如果你用pycharm之類的工具調試以后,可以發現這個best_model_Conv.pkl存儲的就是四個層的模型參數
測試網絡
首先是載入模型
mnist_class=cPickle.load(open('best_model_Conv.pkl'))然后構建一個前向網絡
x=T.matrix('x') single_input=x.reshape((1,1,28,28)) hidden_num=mnist_class[2].b.container.data.shape[0]; kerns_num=[mnist_class[1].W.container.data.shape[0],mnist_class[1].W.container.data.shape[1]] classifier_test=Lenet(rng=np.random.RandomState(1234),batch_size=1,input=single_input,n_hidden=hidden_num,nkerns=kerns_num,n_out=10)給網絡參數賦值
#逐層賦值 #第一層卷積 classifier_test.layer0.W.set_value(mnist_class[0].W.get_value()) classifier_test.layer0.b.set_value(mnist_class[0].b.get_value()) #第二層卷積 classifier_test.layer1.W.set_value(mnist_class[1].W.get_value()) classifier_test.layer1.b.set_value(mnist_class[1].b.get_value()) #第三層全連接->隱層 classifier_test.layer2.W.set_value(mnist_class[2].W.get_value()) classifier_test.layer2.b.set_value(mnist_class[2].b.get_value()) #第四層softmax classifier_test.layer3.W.set_value(mnist_class[3].W.get_value()) classifier_test.layer3.b.set_value(mnist_class[3].b.get_value())定義前向計算的操作
#前向計算 forward_compute=theano.function([single_input],classifier_test.layer3.y_pred)測試網絡,這里我們使用前面學習caffe時候手工制作的mnist數據集,戳這里下載, 密碼是bead.當然也可以按照MLP中的方法采用mnist原始數據集.
from PIL import Image import pylab img=Image.open('E:\\code_test\\theano\\binarybmp\\9.bmp') img_w,img_h=img.size#圖像的寬和高 img=np.asarray(img,dtype='float32') pylab.imshow(img) pylab.show() #原始圖片是28*28,要增加兩個維度 img=img.reshape((1,1,28,28))識別結果輸出
label_pre=forward_compute(img) print label_pre #9目前就是0識別成6, 6識別成5,剩下的幾個全對
博客code打包:鏈接: https://pan.baidu.com/s/1i5xipiH 密碼: rvit
后記
通過池化那部分的研究我們發現關于卷積的實現在theano.tensor.nnet戳這里以及theano.sandbox.cuda.dnn戳這里中也有對應實現。這里要注意, 我們以后使用卷積是否要研究后者的使用而非前者。
總結
以上是生活随笔為你收集整理的【theano-windows】学习笔记十二——卷积神经网络的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 交行魔都优逸白金卡年费多少?怎么免年费?
- 下一篇: 【theano-windows】学习笔记