當(dāng)使用神經(jīng)網(wǎng)絡(luò)時(shí),我們可以通過它的準(zhǔn)確性來評估模型的性能,但是當(dāng)涉及到計(jì)算機(jī)視覺問題時(shí),不僅要有最好的準(zhǔn)確性,還要有可解釋性和對哪些特征/數(shù)據(jù)點(diǎn)有助于做出決策的理解。模型專注于正確的特征比模型的準(zhǔn)確性更重要。
理解CNN的方法主要有類激活圖(Class Activation Maps, CAM)、梯度加權(quán)類激活圖(Gradient Weighted Class Activation Mapping, Grad-CAM)和優(yōu)化的 Grad-CAM( Grad-CAM++)。它們的思想都是一樣的:如果我們?nèi)∽詈笠粋€卷積層的輸出特征映射并對它們施加權(quán)重,就可以得到一個熱圖,可以表明輸入圖像中哪些部分的權(quán)重高(代表了整個圖的特征)。
Class Activation Maps
CAM是一種將CNN所看到或關(guān)注的內(nèi)容可視化并為我們生成類輸出的方法。
通過將圖像傳遞給CNN,我們獲得了相同圖像的低分辨率特征圖。
CAM的思想是,刪除那些完全連接的神經(jīng)網(wǎng)絡(luò),并用全局平均池化層代替它們,特征圖中所有像素的平均值就是它的全局平均值。通過將GAP應(yīng)用于所有特征映射將獲得它們的標(biāo)量值。
對于這些標(biāo)量值,我們應(yīng)用表明每個特征映射對特定類重要性的權(quán)重,權(quán)重是通過訓(xùn)練一個線性模型來學(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有一個最大的缺點(diǎn)就是必須重新訓(xùn)練模型才能得到全局平均池化后得到的權(quán)重。對于每一類必須學(xué)習(xí)一個線性模型。也就是說將有n個權(quán)重(等于最后一層的過濾器)* n個線性模型(等于類)。并且還必須修改網(wǎng)絡(luò)架構(gòu)來創(chuàng)建CAM這對于現(xiàn)有的模型來說改動太大,所以Grad-CAM解決了這些缺點(diǎn)。
Grad-CAM( Gradient Weighted Class Activation Mapping)
Grad-CAM背后的思想是,依賴于最后一個卷積層的特征映射中使用的梯度,而不是使用網(wǎng)絡(luò)權(quán)重。這些梯度是通過反向傳播得到的。
這不僅解決了再訓(xùn)練問題,還解決了網(wǎng)絡(luò)架構(gòu)修改問題,因?yàn)橹皇褂锰荻榷皇褂肎AP層。
我們只要在最后一個卷積層中計(jì)算用于頂部預(yù)測類的特征映射的梯度。然后我們對這些權(quán)重應(yīng)用全局平均。權(quán)重與最后一層得到的特征映射的點(diǎn)積就是Grad-CAM輸出。然后通過在其上應(yīng)用ReLU,識別圖像中僅對我們的圖像有積極貢獻(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)反向傳播,只通過類別預(yù)測的正梯度進(jìn)行反向傳播。
Grad-CAM++這種優(yōu)化的原因是因?yàn)镚rad-CAM在識別和關(guān)注多次出現(xiàn)的對象或具有低空間占用的對象方面存在問題。
所以Grad-CAM++給予與預(yù)測類相關(guān)的梯度像素更多的重要性(正梯度),通過使用更大的因子而不是像Grad-CAM那樣使用常數(shù)因子來縮放它們。這個比例因子在代碼中用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é)果對比
這里我們使用VGG16,對一些圖像進(jìn)行了比較,下圖中可以看到CAM、Grad-CAM和Grad-CAM++的看法有多么不同。雖然它們都主要集中在它的上半身,但Grad-CAM++能夠?qū)⑵湔w視為重要部分,而CAM則將其某些部分視為非常重要的特征,而將一些部分視為其預(yù)測的輔助。而Grad-CAM只關(guān)注它的冠和翅膀作為決策的重要特征。
對于這張風(fēng)箏的圖像,CAM顯示它關(guān)注的是除了風(fēng)箏之外的所有東西(也就是天空),但是使用gradcam則看到到模型關(guān)注的是風(fēng)箏,而gradcam ++通過增加重要的突出空間進(jìn)一步加強(qiáng)了這一點(diǎn)。這里需要注意的是,模型錯誤地將其分類為降落傘,但風(fēng)箏類緊隨其后。也就是說,其實(shí)CAM更好的捕捉到了錯誤的原因。
-
神經(jīng)網(wǎng)絡(luò)
+關(guān)注
關(guān)注
42文章
4771瀏覽量
100720 -
過濾器
+關(guān)注
關(guān)注
1文章
428瀏覽量
19597 -
計(jì)算機(jī)視覺
+關(guān)注
關(guān)注
8文章
1698瀏覽量
45982 -
GAP
+關(guān)注
關(guān)注
0文章
15瀏覽量
8307
發(fā)布評論請先 登錄
相關(guān)推薦
評論