寫了一個(gè)輸入和卷積核dim=2是一樣的(都是3)的卷積函數(shù),可以試試多加一個(gè)for循環(huán)變成三維卷積
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、重慶小程序開發(fā)公司、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了岑溪免費(fèi)建站歡迎大家使用!
def conv3D(image, filter):
'''
三維卷積
:param image: 輸入,shape為 [h,w,c], c=3
:param filter: ?卷積核,shape為 [x,y,z], z=3
:return:
'''
h, w, c = image.shape
x, y, z = filter.shape
height_new = h - x + 1 ?# 輸出 h
width_new = w - y + 1 ?# 輸出 w
image_new = np.zeros((height_new, width_new), dtype=np.float)
for i in range(height_new):
for j in range(width_new):
r = np.sum(image[i:i+x, j:j+x, 0] * filter[:,:,0])
g = np.sum(image[i:i+y, j:j+y, 1] * filter[:,:,1])
b = np.sum(image[i:i+z, j:j+z, 2] * filter[:,:,2])
image_new[i, j] = np.sum([r,g,b])
image_new = image_new.clip(0, 255)
image_new = np.rint(image_new).astype('uint8')
return image_new
和不用聲明變量一樣,Python不用去聲明函數(shù)的返回類型,是由于其“若類型”的語言特性決定的。
在其他語言中,例如C/C++語言中在存儲(chǔ)一個(gè)數(shù)據(jù)之前,都需要在內(nèi)存中給這個(gè)數(shù)據(jù)開辟一個(gè)固定的內(nèi)存空間,并給這個(gè)類型空間指定一個(gè)唯一的 id(變量名),然后才把要存儲(chǔ)的數(shù)據(jù)放到這個(gè)變量名對(duì)于的內(nèi)存空間中。而Python的做法,是`以數(shù)據(jù)為中心`,上來就把要存儲(chǔ)的數(shù)據(jù)放到內(nèi)存,然后再去用一個(gè)變量名`引用`這個(gè)數(shù)據(jù)。
上周末利用python簡(jiǎn)單實(shí)現(xiàn)了一個(gè)卷積神經(jīng)網(wǎng)絡(luò),只包含一個(gè)卷積層和一個(gè)maxpooling層,pooling層后面的多層神經(jīng)網(wǎng)絡(luò)采用了softmax形式的輸出。實(shí)驗(yàn)輸入仍然采用MNIST圖像使用10個(gè)feature map時(shí),卷積和pooling的結(jié)果分別如下所示。
部分源碼如下:
[python]?view plain?copy
#coding=utf-8
'''''
Created?on?2014年11月30日
@author:?Wangliaofan
'''
import?numpy
import?struct
import?matplotlib.pyplot?as?plt
import?math
import?random
import?copy
#test
from?BasicMultilayerNeuralNetwork?import?BMNN2
def?sigmoid(inX):
if?1.0+numpy.exp(-inX)==?0.0:
return?999999999.999999999
return?1.0/(1.0+numpy.exp(-inX))
def?difsigmoid(inX):
return?sigmoid(inX)*(1.0-sigmoid(inX))
def?tangenth(inX):
return?(1.0*math.exp(inX)-1.0*math.exp(-inX))/(1.0*math.exp(inX)+1.0*math.exp(-inX))
def?cnn_conv(in_image,?filter_map,B,type_func='sigmoid'):
#in_image[num,feature?map,row,col]=in_image[Irow,Icol]
#features?map[k?filter,row,col]
#type_func['sigmoid','tangenth']
#out_feature[k?filter,Irow-row+1,Icol-col+1]
shape_image=numpy.shape(in_image)#[row,col]
#print?"shape_image",shape_image
shape_filter=numpy.shape(filter_map)#[k?filter,row,col]
if?shape_filter[1]shape_image[0]?or?shape_filter[2]shape_image[1]:
raise?Exception
shape_out=(shape_filter[0],shape_image[0]-shape_filter[1]+1,shape_image[1]-shape_filter[2]+1)
out_feature=numpy.zeros(shape_out)
k,m,n=numpy.shape(out_feature)
for?k_idx?in?range(0,k):
#rotate?180?to?calculate?conv
c_filter=numpy.rot90(filter_map[k_idx,:,:],?2)
for?r_idx?in?range(0,m):
for?c_idx?in?range(0,n):
#conv_temp=numpy.zeros((shape_filter[1],shape_filter[2]))
conv_temp=numpy.dot(in_image[r_idx:r_idx+shape_filter[1],c_idx:c_idx+shape_filter[2]],c_filter)
sum_temp=numpy.sum(conv_temp)
if?type_func=='sigmoid':
out_feature[k_idx,r_idx,c_idx]=sigmoid(sum_temp+B[k_idx])
elif?type_func=='tangenth':
out_feature[k_idx,r_idx,c_idx]=tangenth(sum_temp+B[k_idx])
else:
raise?Exception
return?out_feature
def?cnn_maxpooling(out_feature,pooling_size=2,type_pooling="max"):
k,row,col=numpy.shape(out_feature)
max_index_Matirx=numpy.zeros((k,row,col))
out_row=int(numpy.floor(row/pooling_size))
out_col=int(numpy.floor(col/pooling_size))
out_pooling=numpy.zeros((k,out_row,out_col))
for?k_idx?in?range(0,k):
for?r_idx?in?range(0,out_row):
for?c_idx?in?range(0,out_col):
temp_matrix=out_feature[k_idx,pooling_size*r_idx:pooling_size*r_idx+pooling_size,pooling_size*c_idx:pooling_size*c_idx+pooling_size]
out_pooling[k_idx,r_idx,c_idx]=numpy.amax(temp_matrix)
max_index=numpy.argmax(temp_matrix)
#print?max_index
#print?max_index/pooling_size,max_index%pooling_size
max_index_Matirx[k_idx,pooling_size*r_idx+max_index/pooling_size,pooling_size*c_idx+max_index%pooling_size]=1
return?out_pooling,max_index_Matirx
def?poolwithfunc(in_pooling,W,B,type_func='sigmoid'):
k,row,col=numpy.shape(in_pooling)
out_pooling=numpy.zeros((k,row,col))
for?k_idx?in?range(0,k):
for?r_idx?in?range(0,row):
for?c_idx?in?range(0,col):
out_pooling[k_idx,r_idx,c_idx]=sigmoid(W[k_idx]*in_pooling[k_idx,r_idx,c_idx]+B[k_idx])
return?out_pooling
#out_feature?is?the?out?put?of?conv
def?backErrorfromPoolToConv(theta,max_index_Matirx,out_feature,pooling_size=2):
k1,row,col=numpy.shape(out_feature)
error_conv=numpy.zeros((k1,row,col))
k2,theta_row,theta_col=numpy.shape(theta)
if?k1!=k2:
raise?Exception
for?idx_k?in?range(0,k1):
for?idx_row?in?range(?0,?row):
for?idx_col?in?range(?0,?col):
error_conv[idx_k,idx_row,idx_col]=\
max_index_Matirx[idx_k,idx_row,idx_col]*\
float(theta[idx_k,idx_row/pooling_size,idx_col/pooling_size])*\
difsigmoid(out_feature[idx_k,idx_row,idx_col])
return?error_conv
def?backErrorfromConvToInput(theta,inputImage):
k1,row,col=numpy.shape(theta)
#print?"theta",k1,row,col
i_row,i_col=numpy.shape(inputImage)
if?rowi_row?or?col?i_col:
raise?Exception
filter_row=i_row-row+1
filter_col=i_col-col+1
detaW=numpy.zeros((k1,filter_row,filter_col))
#the?same?with?conv?valid?in?matlab
for?k_idx?in?range(0,k1):
for?idx_row?in?range(0,filter_row):
for?idx_col?in?range(0,filter_col):
subInputMatrix=inputImage[idx_row:idx_row+row,idx_col:idx_col+col]
#print?"subInputMatrix",numpy.shape(subInputMatrix)
#rotate?theta?180
#print?numpy.shape(theta)
theta_rotate=numpy.rot90(theta[k_idx,:,:],?2)
#print?"theta_rotate",theta_rotate
dotMatrix=numpy.dot(subInputMatrix,theta_rotate)
detaW[k_idx,idx_row,idx_col]=numpy.sum(dotMatrix)
detaB=numpy.zeros((k1,1))
for?k_idx?in?range(0,k1):
detaB[k_idx]=numpy.sum(theta[k_idx,:,:])
return?detaW,detaB
def?loadMNISTimage(absFilePathandName,datanum=60000):
images=open(absFilePathandName,'rb')
buf=images.read()
index=0
magic,?numImages?,?numRows?,?numColumns?=?struct.unpack_from('IIII'?,?buf?,?index)
print?magic,?numImages?,?numRows?,?numColumns
index?+=?struct.calcsize('IIII')
if?magic?!=?2051:
raise?Exception
datasize=int(784*datanum)
datablock=""+str(datasize)+"B"
#nextmatrix=struct.unpack_from('47040000B'?,buf,?index)
nextmatrix=struct.unpack_from(datablock?,buf,?index)
nextmatrix=numpy.array(nextmatrix)/255.0
#nextmatrix=nextmatrix.reshape(numImages,numRows,numColumns)
#nextmatrix=nextmatrix.reshape(datanum,1,numRows*numColumns)
nextmatrix=nextmatrix.reshape(datanum,1,numRows,numColumns)
return?nextmatrix,?numImages
def?loadMNISTlabels(absFilePathandName,datanum=60000):
labels=open(absFilePathandName,'rb')
buf=labels.read()
index=0
magic,?numLabels??=?struct.unpack_from('II'?,?buf?,?index)
print?magic,?numLabels
index?+=?struct.calcsize('II')
if?magic?!=?2049:
raise?Exception
datablock=""+str(datanum)+"B"
#nextmatrix=struct.unpack_from('60000B'?,buf,?index)
nextmatrix=struct.unpack_from(datablock?,buf,?index)
nextmatrix=numpy.array(nextmatrix)
return?nextmatrix,?numLabels
def?simpleCNN(numofFilter,filter_size,pooling_size=2,maxIter=1000,imageNum=500):
decayRate=0.01
MNISTimage,num1=loadMNISTimage("F:\Machine?Learning\UFLDL\data\common\\train-images-idx3-ubyte",imageNum)
print?num1
row,col=numpy.shape(MNISTimage[0,0,:,:])
out_Di=numofFilter*((row-filter_size+1)/pooling_size)*((col-filter_size+1)/pooling_size)
MLP=BMNN2.MuiltilayerANN(1,[128],out_Di,10,maxIter)
MLP.setTrainDataNum(imageNum)
MLP.loadtrainlabel("F:\Machine?Learning\UFLDL\data\common\\train-labels-idx1-ubyte")
MLP.initialweights()
#MLP.printWeightMatrix()
rng?=?numpy.random.RandomState(23455)
W_shp?=?(numofFilter,?filter_size,?filter_size)
W_bound?=?numpy.sqrt(numofFilter?*?filter_size?*?filter_size)
W_k=rng.uniform(low=-1.0?/?W_bound,high=1.0?/?W_bound,size=W_shp)
B_shp?=?(numofFilter,)
B=?numpy.asarray(rng.uniform(low=-.5,?high=.5,?size=B_shp))
cIter=0
while?cItermaxIter:
cIter?+=?1
ImageNum=random.randint(0,imageNum-1)
conv_out_map=cnn_conv(MNISTimage[ImageNum,0,:,:],?W_k,?B,"sigmoid")
out_pooling,max_index_Matrix=cnn_maxpooling(conv_out_map,2,"max")
pool_shape?=?numpy.shape(out_pooling)
MLP_input=out_pooling.reshape(1,1,out_Di)
#print?numpy.shape(MLP_input)
DetaW,DetaB,temperror=MLP.backwardPropogation(MLP_input,ImageNum)
if?cIter%50?==0?:
print?cIter,"Temp?error:?",temperror
#print?numpy.shape(MLP.Theta[MLP.Nl-2])
#print?numpy.shape(MLP.Ztemp[0])
#print?numpy.shape(MLP.weightMatrix[0])
theta_pool=MLP.Theta[MLP.Nl-2]*MLP.weightMatrix[0].transpose()
#print?numpy.shape(theta_pool)
#print?"theta_pool",theta_pool
temp=numpy.zeros((1,1,out_Di))
temp[0,:,:]=theta_pool
back_theta_pool=temp.reshape(pool_shape)
#print?"back_theta_pool",numpy.shape(back_theta_pool)
#print?"back_theta_pool",back_theta_pool
error_conv=backErrorfromPoolToConv(back_theta_pool,max_index_Matrix,conv_out_map,2)
#print?"error_conv",numpy.shape(error_conv)
#print?error_conv
conv_DetaW,conv_DetaB=backErrorfromConvToInput(error_conv,MNISTimage[ImageNum,0,:,:])
#print?"W_k",W_k
#print?"conv_DetaW",conv_DetaW
我將對(duì)代碼進(jìn)行補(bǔ)充演練,以構(gòu)建在數(shù)據(jù)集上訓(xùn)練的任何類型的圖像分類器。在這個(gè)例子中,我將使用花卉數(shù)據(jù)集,其中包括102種不同類型的花。需要數(shù)據(jù)集和代碼都可以私信我。
Pytorch是機(jī)器學(xué)習(xí)和Python上的免費(fèi)軟件包,非常易于使用。語法模擬numpy,因此,如果你在python中有一些科學(xué)計(jì)算經(jīng)驗(yàn),那么會(huì)相當(dāng)有用的。只需幾行代碼,就可以下載預(yù)先訓(xùn)練的數(shù)據(jù)集,使用定義的變換對(duì)圖像進(jìn)行標(biāo)準(zhǔn)化,然后運(yùn)行訓(xùn)練。
創(chuàng)建和擴(kuò)充數(shù)據(jù)集
為了增加數(shù)據(jù)集,我使用' google_images_download'API 從互聯(lián)網(wǎng)上下載了相關(guān)圖像。顯然,您可以使用此API不僅可以擴(kuò)充現(xiàn)有數(shù)據(jù)集,還可以從頭開始創(chuàng)建自己的數(shù)據(jù)集。
確保從圖像中挑選出異常值(損壞的文件或偶然出現(xiàn)的無關(guān)圖像)。
圖像標(biāo)準(zhǔn)化
為了使圖像具有相同的大小和像素變化,可以使用pytorch的transfors模塊:
轉(zhuǎn)移學(xué)習(xí)
從頭開始訓(xùn)練的模型可能不是最明智的選擇,因?yàn)橛性S多網(wǎng)絡(luò)可用于各種數(shù)據(jù)集。簡(jiǎn)單地說,像edge-和其他簡(jiǎn)單形狀檢測(cè)器等低級(jí)特征對(duì)于不同的模型是相似的,即使clasificators是針對(duì)不同目的進(jìn)行訓(xùn)練的。在本項(xiàng)目中,我使用了一個(gè)預(yù)訓(xùn)練網(wǎng)絡(luò)Resnet152,只有最后一個(gè)完全連接的層重新用于新任務(wù),即使這樣也會(huì)產(chǎn)生相當(dāng)好的效果。
在這里,我將除最后一層之外的所有層都設(shè)置為具有固定權(quán)重(requires_grad = False),因此只有最后層中的參數(shù)將通過梯度下降進(jìn)行更新。
訓(xùn)練模型
下面介紹一下進(jìn)行訓(xùn)練的函數(shù):
如何獲得GPU?
當(dāng)然,對(duì)CPU的訓(xùn)練太慢了。根據(jù)我自己的經(jīng)驗(yàn),在GPU僅需要一個(gè)小時(shí)就可以完成12次訓(xùn)練周期,但是在CPU上相同數(shù)量的訓(xùn)練周期可能需要花費(fèi)大約15個(gè)小時(shí)。
如果您沒有本地可用的GPU,則可以考慮使用云GPU。為了加速CNN的訓(xùn)練,我使用了floydhub()上提供的云GPU 。
這項(xiàng)服務(wù)非常指的使用:總有很好的文檔和大量的提示,所以你會(huì)很清楚的知道下一步需要如何去做。在floydhub上對(duì)于使用GPU的收費(fèi)也是可以接受的。
首先,需要將數(shù)據(jù)集上傳到服務(wù)器
然后,需要?jiǎng)?chuàng)建項(xiàng)目。需要在計(jì)算機(jī)上安裝floydhub客戶端,將數(shù)據(jù)集上載到其網(wǎng)站并在終端中運(yùn)行以下命令:
其中'username'是您的登錄名,'i'是數(shù)據(jù)集所在的文件夾。
這樣子在訓(xùn)練網(wǎng)絡(luò)時(shí)就會(huì)很輕松了
結(jié)果和改進(jìn)想法
得到的模型在數(shù)據(jù)集上訓(xùn)練了1.5小時(shí),并在驗(yàn)證數(shù)據(jù)集上達(dá)到了95%的準(zhǔn)確度。
用keras框架較為方便
首先安裝anaconda,然后通過pip安裝keras
1、#導(dǎo)入各種用到的模塊組件
from __future__ import absolute_import
from __future__ import print_function
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation, Flatten
from keras.layers.advanced_activations import PReLU
from keras.layers.convolutional import Convolution2D, MaxPooling2D
from keras.optimizers import SGD, Adadelta, Adagrad
from keras.utils import np_utils, generic_utils
from six.moves import range
from data import load_data
import random
import numpy as np
np.random.seed(1024) ?# for reproducibility
2、。#打亂數(shù)據(jù)
index = [i for i in range(len(data))]
random.shuffle(index)
data = data[index]
label = label[index]
print(data.shape[0], ' samples')
#label為0~9共10個(gè)類別,keras要求格式為binary class matrices,轉(zhuǎn)化一下,直接調(diào)用keras提供的這個(gè)函數(shù)
label = np_utils.to_categorical(label, 10)
###############
#開始建立CNN模型
###############
#生成一個(gè)model
model = Sequential()
3、#第一個(gè)卷積層,4個(gè)卷積核,每個(gè)卷積核大小5*5。1表示輸入的圖片的通道,灰度圖為1通道。
#border_mode可以是valid或者full,具體看這里說明:
#激活函數(shù)用tanh
#你還可以在model.add(Activation('tanh'))后加上dropout的技巧: model.add(Dropout(0.5))
model.add(Convolution2D(4, 5, 5, border_mode='valid',input_shape=(1,28,28)))
model.add(Activation('tanh'))
#第二個(gè)卷積層,8個(gè)卷積核,每個(gè)卷積核大小3*3。4表示輸入的特征圖個(gè)數(shù),等于上一層的卷積核個(gè)數(shù)
4、全連接層,先將前一層輸出的二維特征圖flatten為一維的。
#Dense就是隱藏層。16就是上一層輸出的特征圖個(gè)數(shù)。4是根據(jù)每個(gè)卷積層計(jì)算出來的:(28-5+1)得到24,(24-3+1)/2得到11,(11-3+1)/2得到4
#全連接有128個(gè)神經(jīng)元節(jié)點(diǎn),初始化方式為normal
model.add(Flatten())
model.add(Dense(128, init='normal'))
model.add(Activation('tanh'))
#Softmax分類,輸出是10類別
model.add(Dense(10, init='normal'))
model.add(Activation('softmax'))
#############
#開始訓(xùn)練模型
##############
#使用SGD + momentum
#model.compile里的參數(shù)loss就是損失函數(shù)(目標(biāo)函數(shù))
sgd = SGD(lr=0.05, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy', optimizer=sgd,metrics=["accuracy"])
#調(diào)用fit方法,就是一個(gè)訓(xùn)練過程. 訓(xùn)練的epoch數(shù)設(shè)為10,batch_size為100.
#數(shù)據(jù)經(jīng)過隨機(jī)打亂shuffle=True。verbose=1,訓(xùn)練過程中輸出的信息,0、1、2三種方式都可以,無關(guān)緊要。show_accuracy=True,訓(xùn)練時(shí)每一個(gè)epoch都輸出accuracy。
#validation_split=0.2,將20%的數(shù)據(jù)作為驗(yàn)證集。
model.fit(data, label, batch_size=100, nb_epoch=10,shuffle=True,verbose=1,validation_split=0.2)
"""
#使用data augmentation的方法
#一些參數(shù)和調(diào)用的方法,請(qǐng)看文檔
datagen = ImageDataGenerator(
featurewise_center=True, # set input mean to 0 over the dataset
samplewise_center=False, # set each sample mean to 0
featurewise_std_normalization=True, # divide inputs by std of the dataset
samplewise_std_normalization=False, # divide each input by its std
zca_whitening=False, # apply ZCA whitening
rotation_range=20, # randomly rotate images in the range (degrees, 0 to 180)
width_shift_range=0.2, # randomly shift images horizontally (fraction of total width)
height_shift_range=0.2, # randomly shift images vertically (fraction of total height)
horizontal_flip=True, # randomly flip images
vertical_flip=False) # randomly flip images
# compute quantities required for featurewise normalization
# (std, mean, and principal components if ZCA whitening is applied)
datagen.fit(data)
for e in range(nb_epoch):
print('-'*40)
print('Epoch', e)
print('-'*40)
print("Training...")
# batch train with realtime data augmentation
progbar = generic_utils.Progbar(data.shape[0])
for X_batch, Y_batch in datagen.flow(data, label):
loss,accuracy = model.train(X_batch, Y_batch,accuracy=True)
progbar.add(X_batch.shape[0], values=[("train loss", loss),("accuracy:", accuracy)] )
在本文中,將探討如何可視化卷積神經(jīng)網(wǎng)絡(luò)(CNN),該網(wǎng)絡(luò)在計(jì)算機(jī)視覺中使用最為廣泛。首先了解CNN模型可視化的重要性,其次介紹可視化的幾種方法,同時(shí)以一個(gè)用例幫助讀者更好地理解模型可視化這一概念。
正如上文中介紹的癌癥腫瘤診斷案例所看到的,研究人員需要對(duì)所設(shè)計(jì)模型的工作原理及其功能掌握清楚,這點(diǎn)至關(guān)重要。一般而言,一名深度學(xué)習(xí)研究者應(yīng)該記住以下幾點(diǎn):
1.1 理解模型是如何工作的
1.2 調(diào)整模型的參數(shù)
1.3 找出模型失敗的原因
1.4 向消費(fèi)者/終端用戶或業(yè)務(wù)主管解釋模型做出的決定
2.可視化CNN模型的方法
根據(jù)其內(nèi)部的工作原理,大體上可以將CNN可視化方法分為以下三類:
初步方法:一種顯示訓(xùn)練模型整體結(jié)構(gòu)的簡(jiǎn)單方法
基于激活的方法:對(duì)單個(gè)或一組神經(jīng)元的激活狀態(tài)進(jìn)行破譯以了解其工作過程
基于梯度的方法:在訓(xùn)練過程中操作前向傳播和后向傳播形成的梯度
下面將具體介紹以上三種方法,所舉例子是使用Keras深度學(xué)習(xí)庫(kù)實(shí)現(xiàn),另外本文使用的數(shù)據(jù)集是由“識(shí)別數(shù)字”競(jìng)賽提供。因此,讀者想復(fù)現(xiàn)文中案例時(shí),請(qǐng)確保安裝好Kears以及執(zhí)行了這些步驟。
研究者能做的最簡(jiǎn)單的事情就是繪制出模型結(jié)構(gòu)圖,此外還可以標(biāo)注神經(jīng)網(wǎng)絡(luò)中每層的形狀及參數(shù)。在keras中,可以使用如下命令完成模型結(jié)構(gòu)圖的繪制:
model.summary()_________________________________________________________________Layer (type) ? ? ? ? ? ? ? ? Output Shape ? ? ? ? ? ? ?Param # ?
=================================================================conv2d_1 (Conv2D) ? ? ? ? ? ?(None, 26, 26, 32) ? ? ? ?320_________________________________________________________________conv2d_2 (Conv2D) ? ? ? ? ? ?(None, 24, 24, 64) ? ? ? ?18496_________________________________________________________________max_pooling2d_1 (MaxPooling2 (None, 12, 12, 64) ? ? ? ?0_________________________________________________________________dropout_1 (Dropout) ? ? ? ? ?(None, 12, 12, 64) ? ? ? ?0_________________________________________________________________flatten_1 (Flatten) ? ? ? ? ?(None, 9216) ? ? ? ? ? ? ?0_________________________________________________________________dense_1 (Dense) ? ? ? ? ? ? ?(None, 128) ? ? ? ? ? ? ? 1179776_________________________________________________________________dropout_2 (Dropout) ? ? ? ? ?(None, 128) ? ? ? ? ? ? ? 0_________________________________________________________________preds (Dense) ? ? ? ? ? ? ? ?(None, 10) ? ? ? ? ? ? ? ?1290 ? ? ?
=================================================================Total params: 1,199,882Trainable params: 1,199,882Non-trainable params: 0
還可以用一個(gè)更富有創(chuàng)造力和表現(xiàn)力的方式呈現(xiàn)模型結(jié)構(gòu)框圖,可以使用keras.utils.vis_utils函數(shù)完成模型體系結(jié)構(gòu)圖的繪制。
另一種方法是繪制訓(xùn)練模型的過濾器,這樣就可以了解這些過濾器的表現(xiàn)形式。例如,第一層的第一個(gè)過濾器看起來像:
top_layer = model.layers[0]plt.imshow(top_layer.get_weights()[0][:, :, :, 0].squeeze(), cmap='gray')
一般來說,神經(jīng)網(wǎng)絡(luò)的底層主要是作為邊緣檢測(cè)器,當(dāng)層數(shù)變深時(shí),過濾器能夠捕捉更加抽象的概念,比如人臉等。
為了理解神經(jīng)網(wǎng)絡(luò)的工作過程,可以在輸入圖像上應(yīng)用過濾器,然后繪制其卷積后的輸出,這使得我們能夠理解一個(gè)過濾器其特定的激活模式是什么。比如,下圖是一個(gè)人臉過濾器,當(dāng)輸入圖像是人臉圖像時(shí)候,它就會(huì)被激活。
from vis.visualization import visualize_activation
from vis.utils import utils
from keras import activations
from matplotlib import pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize'] = (18, 6)
# Utility to search for layer index by name.
# Alternatively we can specify this as -1 since it corresponds to the last layer.
layer_idx = utils.find_layer_idx(model, 'preds')
# Swap softmax with linear
model.layers[layer_idx].activation = activations.linear
model = utils.apply_modifications(model)
# This is the output node we want to maximize.filter_idx = 0
img = visualize_activation(model, layer_idx, filter_indices=filter_idx)
plt.imshow(img[..., 0])
同理,可以將這個(gè)想法應(yīng)用于所有的類別,并檢查它們的模式會(huì)是什么樣子。
for output_idx in np.arange(10):
# Lets turn off verbose output this time to avoid clutter and just see the output.
img = visualize_activation(model, layer_idx, filter_indices=output_idx, input_range=(0., 1.))
plt.figure()
plt.title('Networks perception of {}'.format(output_idx))
plt.imshow(img[..., 0])
在圖像分類問題中,可能會(huì)遇到目標(biāo)物體被遮擋,有時(shí)候只有物體的一小部分可見的情況。基于圖像遮擋的方法是通過一個(gè)灰色正方形系統(tǒng)地輸入圖像的不同部分并監(jiān)視分類器的輸出。這些例子清楚地表明模型在場(chǎng)景中定位對(duì)象時(shí),若對(duì)象被遮擋,其分類正確的概率顯著降低。
為了理解這一概念,可以從數(shù)據(jù)集中隨機(jī)抽取圖像,并嘗試?yán)L制該圖的熱圖(heatmap)。這使得我們直觀地了解圖像的哪些部分對(duì)于該模型而言的重要性,以便對(duì)實(shí)際類別進(jìn)行明確的區(qū)分。
def iter_occlusion(image, size=8):
# taken from
occlusion = np.full((size * 5, size * 5, 1), [0.5], np.float32)
occlusion_center = np.full((size, size, 1), [0.5], np.float32)
occlusion_padding = size * 2
# print('padding...')
image_padded = np.pad(image, ( \? (occlusion_padding, occlusion_padding), (occlusion_padding, occlusion_padding), (0, 0) \? ), 'constant', constant_values = 0.0)
for y in range(occlusion_padding, image.shape[0] + occlusion_padding, size):
? for x in range(occlusion_padding, image.shape[1] + occlusion_padding, size):
? ? ? tmp = image_padded.copy()
? ? ? tmp[y - occlusion_padding:y + occlusion_center.shape[0] + occlusion_padding, \
? ? ? ? x - occlusion_padding:x + occlusion_center.shape[1] + occlusion_padding] \? ? ? ? ? ? = occlusion
? ? ? tmp[y:y + occlusion_center.shape[0], x:x + occlusion_center.shape[1]] = occlusion_center? ? ? ? ? yield x - occlusion_padding, y - occlusion_padding, \
? ? ? ? tmp[occlusion_padding:tmp.shape[0] - occlusion_padding, occlusion_padding:tmp.shape[1] - occlusion_padding]i = 23 # for exampledata = val_x[i]correct_class = np.argmax(val_y[i])
# input tensor for model.predictinp = data.reshape(1, 28, 28, 1)# image data for matplotlib's imshowimg = data.reshape(28, 28)
# occlusionimg_size = img.shape[0]
occlusion_size = 4print('occluding...')heatmap = np.zeros((img_size, img_size), np.float32)class_pixels = np.zeros((img_size, img_size), np.int16)
from collections import defaultdict
counters = defaultdict(int)for n, (x, y, img_float) in enumerate(iter_occlusion(data, size=occlusion_size)):
X = img_float.reshape(1, 28, 28, 1)
out = model.predict(X)
#print('#{}: {} @ {} (correct class: {})'.format(n, np.argmax(out), np.amax(out), out[0][correct_class]))
#print('x {} - {} | y {} - {}'.format(x, x + occlusion_size, y, y + occlusion_size))
heatmap[y:y + occlusion_size, x:x + occlusion_size] = out[0][correct_class]
class_pixels[y:y + occlusion_size, x:x + occlusion_size] = np.argmax(out)
counters[np.argmax(out)] += 1
正如之前的坦克案例中看到的那樣,怎么才能知道模型側(cè)重于哪部分的預(yù)測(cè)呢?為此,可以使用顯著圖解決這個(gè)問題。顯著圖首先在這篇文章中被介紹。
使用顯著圖的概念相當(dāng)直接——計(jì)算輸出類別相對(duì)于輸入圖像的梯度。這應(yīng)該告訴我們輸出類別值對(duì)于輸入圖像像素中的微小變化是怎樣變化的。梯度中的所有正值告訴我們,像素的一個(gè)小變化會(huì)增加輸出值。因此,將這些梯度可視化可以提供一些直觀的信息,這種方法突出了對(duì)輸出貢獻(xiàn)最大的顯著圖像區(qū)域。
class_idx = 0indices = np.where(val_y[:, class_idx] == 1.)[0]
# pick some random input from here.idx = indices[0]
# Lets sanity check the picked image.from matplotlib import pyplot as plt%matplotlib inline
plt.rcParams['figure.figsize'] = (18, 6)plt.imshow(val_x[idx][..., 0])
from vis.visualization import visualize_saliency
from vis.utils import utilsfrom keras import activations# Utility to search for layer index by name.
# Alternatively we can specify this as -1 since it corresponds to the last layer.
layer_idx = utils.find_layer_idx(model, 'preds')
# Swap softmax with linearmodel.layers[layer_idx].activation = activations.linear
model = utils.apply_modifications(model)grads = visualize_saliency(model, layer_idx, filter_indices=class_idx, seed_input=val_x[idx])
# Plot with 'jet' colormap to visualize as a heatmap.plt.imshow(grads, cmap='jet')
# This corresponds to the Dense linear layer.for class_idx in np.arange(10):
indices = np.where(val_y[:, class_idx] == 1.)[0]
idx = indices[0]
f, ax = plt.subplots(1, 4)
ax[0].imshow(val_x[idx][..., 0])
for i, modifier in enumerate([None, 'guided', 'relu']):
? ? grads = visualize_saliency(model, layer_idx, filter_indices=class_idx,
? ? seed_input=val_x[idx], backprop_modifier=modifier)
? ? if modifier is None:
? ? ? ? modifier = 'vanilla'
? ? ax[i+1].set_title(modifier)
? ? ax[i+1].imshow(grads, cmap='jet')
類別激活映射(CAM)或grad-CAM是另外一種可視化模型的方法,這種方法使用的不是梯度的輸出值,而是使用倒數(shù)第二個(gè)卷積層的輸出,這樣做是為了利用存儲(chǔ)在倒數(shù)第二層的空間信息。
from vis.visualization import visualize_cam
# This corresponds to the Dense linear layer.for class_idx in np.arange(10):
indices = np.where(val_y[:, class_idx] == 1.)[0]
idx = indices[0]f, ax = plt.subplots(1, 4)
ax[0].imshow(val_x[idx][..., 0])
for i, modifier in enumerate([None, 'guided', 'relu']):
grads = visualize_cam(model, layer_idx, filter_indices=class_idx,
seed_input=val_x[idx], backprop_modifier=modifier)
if modifier is None:
? ? modifier = 'vanilla'
ax[i+1].set_title(modifier)
ax[i+1].imshow(grads, cmap='jet')
本文簡(jiǎn)單說明了CNN模型可視化的重要性,以及介紹了一些可視化CNN網(wǎng)絡(luò)模型的方法,希望對(duì)讀者有所幫助,使其能夠在后續(xù)深度學(xué)習(xí)應(yīng)用中構(gòu)建更好的模型。 免費(fèi)視頻教程: