當(dāng)使用神經(jīng)網(wǎng)絡(luò)時(shí),我們可以通過(guò)它的準(zhǔn)確性來(lái)評(píng)估模型的性能,但是當(dāng)涉及到計(jì)算機(jī)視覺(jué)問(wèn)題時(shí),不僅要有最好的準(zhǔn)確性,還要有可解釋性和對(duì)哪些特征/數(shù)據(jù)點(diǎn)有助于做出決策的理解。模型專(zhuān)注于正確的特征比模型的準(zhǔn)確性更重要。
理解CNN的方法主要有類(lèi)激活圖(Class Activation Maps, CAM)、梯度加權(quán)類(lèi)激活圖(Gradient Weighted Class Activation Mapping, Grad-CAM)和優(yōu)化的 Grad-CAM( Grad-CAM++)。它們的思想都是一樣的:如果我們?nèi)∽詈笠粋€(gè)卷積層的輸出特征映射并對(duì)它們施加權(quán)重,就可以得到一個(gè)熱圖,可以表明輸入圖像中哪些部分的權(quán)重高(代表了整個(gè)圖的特征)。
Class Activation Maps
CAM是一種將CNN所看到或關(guān)注的內(nèi)容可視化并為我們生成類(lèi)輸出的方法。
通過(guò)將圖像傳遞給CNN,我們獲得了相同圖像的低分辨率特征圖。
CAM的思想是,刪除那些完全連接的神經(jīng)網(wǎng)絡(luò),并用全局平均池化層代替它們,特征圖中所有像素的平均值就是它的全局平均值。通過(guò)將GAP應(yīng)用于所有特征映射將獲得它們的標(biāo)量值。
對(duì)于這些標(biāo)量值,我們應(yīng)用表明每個(gè)特征映射對(duì)特定類(lèi)重要性的權(quán)重,權(quán)重是通過(guò)訓(xùn)練一個(gè)線(xiàn)性模型來(lái)學(xué)習(xí)的。
激活圖將是所有這些特征圖的加權(quán)組合。
defgenerate_cam(input_model, image, layer_name='block5_conv3', H=224, W=224):
cls=np.argmax(input_model.predict(image)) # Obtain the predicted class
conv_output=input_model.get_layer(layer_name).output#Get the weights of the last output layer
last_conv_layer_model=keras.Model(input_model.inputs, conv_output) #Create a model with the last output layer
class_weights=input_model.get_layer(layer_name).get_weights()[0] # Get the weights of the output layer\\
class_weights=class_weights[0,:,:,:]
class_weights=np.mean(class_weights, axis=(0, 1))
last_conv_output=last_conv_layer_model.predict(image) #The feature map output from last output layer
last_conv_output=last_conv_output[0, :]
cam=np.dot(last_conv_output, class_weights)
cam=zoom(cam, H/cam.shape[0]) #Spatial Interpolation/zooming to image size
cam=cam/np.max(cam) #Normalizing the gradcam
returncam
但是CAM有一個(gè)最大的缺點(diǎn)就是必須重新訓(xùn)練模型才能得到全局平均池化后得到的權(quán)重。對(duì)于每一類(lèi)必須學(xué)習(xí)一個(gè)線(xiàn)性模型。也就是說(shuō)將有n個(gè)權(quán)重(等于最后一層的過(guò)濾器)* n個(gè)線(xiàn)性模型(等于類(lèi))。并且還必須修改網(wǎng)絡(luò)架構(gòu)來(lái)創(chuàng)建CAM這對(duì)于現(xiàn)有的模型來(lái)說(shuō)改動(dòng)太大,所以Grad-CAM解決了這些缺點(diǎn)。
Grad-CAM( Gradient Weighted Class Activation Mapping)
Grad-CAM背后的思想是,依賴(lài)于最后一個(gè)卷積層的特征映射中使用的梯度,而不是使用網(wǎng)絡(luò)權(quán)重。這些梯度是通過(guò)反向傳播得到的。
這不僅解決了再訓(xùn)練問(wèn)題,還解決了網(wǎng)絡(luò)架構(gòu)修改問(wèn)題,因?yàn)橹皇褂锰荻榷皇褂肎AP層。
我們只要在最后一個(gè)卷積層中計(jì)算用于頂部預(yù)測(cè)類(lèi)的特征映射的梯度。然后我們對(duì)這些權(quán)重應(yīng)用全局平均。權(quán)重與最后一層得到的特征映射的點(diǎn)積就是Grad-CAM輸出。然后通過(guò)在其上應(yīng)用ReLU,識(shí)別圖像中僅對(duì)我們的圖像有積極貢獻(xiàn)的部分。
最后就是將Grad-CAM調(diào)整為圖像大小并規(guī)范化,以便它可以疊加在圖像上。
defgrad_cam(input_model, image, layer_name='block5_conv3',H=224,W=224):
cls=np.argmax(input_model.predict(image)) #Get the predicted class
y_c=input_model.output[0, cls] #Probability Score
conv_output=input_model.get_layer(layer_name).output#Tensor of the last layer of cnn
grads=K.gradients(y_c, conv_output)[0] #Gradients of the predicted class wrt conv_output layer
get_output=K.function([input_model.input], [conv_output, grads])
output, grads_val=get_output([image]) #Gives output of image till conv_output layer and the gradient values at that level
output, grads_val=output[0, :], grads_val[0, :, :, :]
weights=np.mean(grads_val, axis=(0, 1)) #Mean of gradients which acts as our weights
cam=np.dot(output, weights) #Grad-CAM output
cam=np.maximum(cam, 0) #Applying Relu
cam=zoom(cam,H/cam.shape[0]) #Spatial Interpolation/zooming to image size
cam=cam/cam.max() #Normalizing the gradcam
returncam
Grad-CAM++
Grad-CAM++不僅包括gradcam技術(shù),它增加了引導(dǎo)反向傳播,只通過(guò)類(lèi)別預(yù)測(cè)的正梯度進(jìn)行反向傳播。
Grad-CAM++這種優(yōu)化的原因是因?yàn)镚rad-CAM在識(shí)別和關(guān)注多次出現(xiàn)的對(duì)象或具有低空間占用的對(duì)象方面存在問(wèn)題。
所以Grad-CAM++給予與預(yù)測(cè)類(lèi)相關(guān)的梯度像素更多的重要性(正梯度),通過(guò)使用更大的因子而不是像Grad-CAM那樣使用常數(shù)因子來(lái)縮放它們。這個(gè)比例因子在代碼中用alpha表示。
defgrad_cam_plus(input_model, image, layer_name='block5_conv3',H=224,W=224):
cls=np.argmax(input_model.predict(image))
y_c=input_model.output[0, cls]
conv_output=input_model.get_layer(layer_name).output
grads=K.gradients(y_c, conv_output)[0]
first=K.exp(y_c)*grads#Variables used to calculate first second and third gradients
second=K.exp(y_c)*grads*grads
third=K.exp(y_c)*grads*grads*grads
#Gradient calculation
get_output=K.function([input_model.input], [y_c,first,second,third, conv_output, grads])
y_c, conv_first_grad, conv_second_grad,conv_third_grad, conv_output, grads_val=get_output([img])
global_sum=np.sum(conv_output[0].reshape((-1,conv_first_grad[0].shape[2])), axis=0)
#Used to calculate the alpha values for each spatial location
alpha_num=conv_second_grad[0]
alpha_denom=conv_second_grad[0]*2.0+conv_third_grad[0]*global_sum.reshape((1,1,conv_first_grad[0].shape[2]))
alpha_denom=np.where(alpha_denom!=0.0, alpha_denom, np.ones(alpha_denom.shape))
alphas=alpha_num/alpha_denom
#Calculating the weights and alpha's which is the scale at which we multiply the weights with more importance
weights=np.maximum(conv_first_grad[0], 0.0)
alpha_normalization_constant=np.sum(np.sum(alphas, axis=0),axis=0)
alphas/=alpha_normalization_constant.reshape((1,1,conv_first_grad[0].shape[2])) #Normalizing alpha
#Weights with alpha multiplied to get spatial importance
deep_linearization_weights=np.sum((weights*alphas).reshape((-1,conv_first_grad[0].shape[2])),axis=0)
grad_CAM_map=np.sum(deep_linearization_weights*conv_output[0], axis=2) #Grad-CAM++ map
cam=np.maximum(grad_CAM_map, 0)
cam=zoom(cam,H/cam.shape[0])
cam=cam/np.max(cam)
returncam
結(jié)果對(duì)比
這里我們使用VGG16,對(duì)一些圖像進(jìn)行了比較,下圖中可以看到CAM、Grad-CAM和Grad-CAM++的看法有多么不同。雖然它們都主要集中在它的上半身,但Grad-CAM++能夠?qū)⑵湔w視為重要部分,而CAM則將其某些部分視為非常重要的特征,而將一些部分視為其預(yù)測(cè)的輔助。而Grad-CAM只關(guān)注它的冠和翅膀作為決策的重要特征。
對(duì)于這張風(fēng)箏的圖像,CAM顯示它關(guān)注的是除了風(fēng)箏之外的所有東西(也就是天空),但是使用gradcam則看到到模型關(guān)注的是風(fēng)箏,而gradcam ++通過(guò)增加重要的突出空間進(jìn)一步加強(qiáng)了這一點(diǎn)。這里需要注意的是,模型錯(cuò)誤地將其分類(lèi)為降落傘,但風(fēng)箏類(lèi)緊隨其后。也就是說(shuō),其實(shí)CAM更好的捕捉到了錯(cuò)誤的原因。
-
神經(jīng)網(wǎng)絡(luò)
+關(guān)注
關(guān)注
42文章
4810瀏覽量
102905 -
過(guò)濾器
+關(guān)注
關(guān)注
1文章
436瀏覽量
20255 -
計(jì)算機(jī)視覺(jué)
+關(guān)注
關(guān)注
9文章
1706瀏覽量
46590 -
GAP
+關(guān)注
關(guān)注
0文章
15瀏覽量
8456
發(fā)布評(píng)論請(qǐng)先 登錄
評(píng)論