用keras框架較為方便
創(chuàng)新互聯(lián)長期為成百上千客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為金口河企業(yè)提供專業(yè)的成都做網(wǎng)站、網(wǎng)站制作,金口河網(wǎng)站改版等技術(shù)服務(wù)。擁有10多年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
首先安裝anaconda,然后通過pip安裝keras
以下轉(zhuǎn)自wphh的博客。
#coding:utf-8
'''
GPU?run?command:
THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32?python?cnn.py
CPU?run?command:
python?cnn.py
2016.06.06更新:
這份代碼是keras開發(fā)初期寫的,當(dāng)時keras還沒有現(xiàn)在這么流行,文檔也還沒那么豐富,所以我當(dāng)時寫了一些簡單的教程。
現(xiàn)在keras的API也發(fā)生了一些的變化,建議及推薦直接上keras.io看更加詳細(xì)的教程。
'''
#導(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
#加載數(shù)據(jù)
data,?label?=?load_data()
#打亂數(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個類別,keras要求格式為binary?class?matrices,轉(zhuǎn)化一下,直接調(diào)用keras提供的這個函數(shù)
label?=?np_utils.to_categorical(label,?10)
###############
#開始建立CNN模型
###############
#生成一個model
model?=?Sequential()
#第一個卷積層,4個卷積核,每個卷積核大小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'))
#第二個卷積層,8個卷積核,每個卷積核大小3*3。4表示輸入的特征圖個數(shù),等于上一層的卷積核個數(shù)
#激活函數(shù)用tanh
#采用maxpooling,poolsize為(2,2)
model.add(Convolution2D(8,?3,?3,?border_mode='valid'))
model.add(Activation('tanh'))
model.add(MaxPooling2D(pool_size=(2,?2)))
#第三個卷積層,16個卷積核,每個卷積核大小3*3
#激活函數(shù)用tanh
#采用maxpooling,poolsize為(2,2)
model.add(Convolution2D(16,?3,?3,?border_mode='valid'))?
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2,?2)))
#全連接層,先將前一層輸出的二維特征圖flatten為一維的。
#Dense就是隱藏層。16就是上一層輸出的特征圖個數(shù)。4是根據(jù)每個卷積層計算出來的:(28-5+1)得到24,(24-3+1)/2得到11,(11-3+1)/2得到4
#全連接有128個神經(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方法,就是一個訓(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)練時每一個epoch都輸出accuracy。
#validation_split=0.2,將20%的數(shù)據(jù)作為驗證集。
model.fit(data,?label,?batch_size=100,?nb_epoch=10,shuffle=True,verbose=1,validation_split=0.2)
"""
#使用data?augmentation的方法
#一些參數(shù)和調(diào)用的方法,請看文檔
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ī)視覺中使用最為廣泛。首先了解CNN模型可視化的重要性,其次介紹可視化的幾種方法,同時以一個用例幫助讀者更好地理解模型可視化這一概念。
正如上文中介紹的癌癥腫瘤診斷案例所看到的,研究人員需要對所設(shè)計模型的工作原理及其功能掌握清楚,這點(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)的簡單方法
基于激活的方法:對單個或一組神經(jīng)元的激活狀態(tài)進(jìn)行破譯以了解其工作過程
基于梯度的方法:在訓(xùn)練過程中操作前向傳播和后向傳播形成的梯度
下面將具體介紹以上三種方法,所舉例子是使用Keras深度學(xué)習(xí)庫實現(xiàn),另外本文使用的數(shù)據(jù)集是由“識別數(shù)字”競賽提供。因此,讀者想復(fù)現(xiàn)文中案例時,請確保安裝好Kears以及執(zhí)行了這些步驟。
研究者能做的最簡單的事情就是繪制出模型結(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
還可以用一個更富有創(chuàng)造力和表現(xiàn)力的方式呈現(xiàn)模型結(jié)構(gòu)框圖,可以使用keras.utils.vis_utils函數(shù)完成模型體系結(jié)構(gòu)圖的繪制。
另一種方法是繪制訓(xùn)練模型的過濾器,這樣就可以了解這些過濾器的表現(xiàn)形式。例如,第一層的第一個過濾器看起來像:
top_layer = model.layers[0]plt.imshow(top_layer.get_weights()[0][:, :, :, 0].squeeze(), cmap='gray')
一般來說,神經(jīng)網(wǎng)絡(luò)的底層主要是作為邊緣檢測器,當(dāng)層數(shù)變深時,過濾器能夠捕捉更加抽象的概念,比如人臉等。
為了理解神經(jīng)網(wǎng)絡(luò)的工作過程,可以在輸入圖像上應(yīng)用過濾器,然后繪制其卷積后的輸出,這使得我們能夠理解一個過濾器其特定的激活模式是什么。比如,下圖是一個人臉過濾器,當(dāng)輸入圖像是人臉圖像時候,它就會被激活。
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])
同理,可以將這個想法應(yīng)用于所有的類別,并檢查它們的模式會是什么樣子。
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])
在圖像分類問題中,可能會遇到目標(biāo)物體被遮擋,有時候只有物體的一小部分可見的情況?;趫D像遮擋的方法是通過一個灰色正方形系統(tǒng)地輸入圖像的不同部分并監(jiān)視分類器的輸出。這些例子清楚地表明模型在場景中定位對象時,若對象被遮擋,其分類正確的概率顯著降低。
為了理解這一概念,可以從數(shù)據(jù)集中隨機(jī)抽取圖像,并嘗試?yán)L制該圖的熱圖(heatmap)。這使得我們直觀地了解圖像的哪些部分對于該模型而言的重要性,以便對實際類別進(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ù)測呢?為此,可以使用顯著圖解決這個問題。顯著圖首先在這篇文章中被介紹。
使用顯著圖的概念相當(dāng)直接——計算輸出類別相對于輸入圖像的梯度。這應(yīng)該告訴我們輸出類別值對于輸入圖像像素中的微小變化是怎樣變化的。梯度中的所有正值告訴我們,像素的一個小變化會增加輸出值。因此,將這些梯度可視化可以提供一些直觀的信息,這種方法突出了對輸出貢獻(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ù)第二個卷積層的輸出,這樣做是為了利用存儲在倒數(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')
本文簡單說明了CNN模型可視化的重要性,以及介紹了一些可視化CNN網(wǎng)絡(luò)模型的方法,希望對讀者有所幫助,使其能夠在后續(xù)深度學(xué)習(xí)應(yīng)用中構(gòu)建更好的模型。 免費(fèi)視頻教程:
寫了一個輸入和卷積核dim=2是一樣的(都是3)的卷積函數(shù),可以試試多加一個for循環(huán)變成三維卷積
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