官术网_书友最值得收藏!

1.3.9 可視化CNN

CNN雖然接近人類識別物體的過程,但是理解CNN的原理卻是非常艱難的過程。人們試圖用可視化的方式來理解CNN各層對圖像特征提取的方法。Keras之父、谷歌大腦人工智能和深度學習研究員Francois Chollet在他的《Python深度學習》圖片源于http://blog.csdn.net/numeria/article/details/73611456。中給出一種可視化卷積的方法。卷積層通常由多個卷積核組成,每個卷積核都可以被視為一種特征提取方式。當使用一個卷積核處理圖像數據后,卷積核會提取它關注的特征并形成新的圖像,該圖像也被稱為特征圖。輸入的圖像包含的特征與卷積核越接近,其特征圖的值也越大。因此完全可以基于梯度,迭代調整輸入圖像的值,讓特征圖的值最大化。當特征圖的值達到最大或者迭代求解趨于穩定時,可以認為這時的輸入圖像就是該卷積核的可視化圖像。下面以Keras為例介紹核心代碼實現。

#獲取輸出層的tensor
layer_output = model.get_layer(layer_name).output
#獲取指定卷積核filter_index的輸出,并作為損失函數
loss = K.mean(layer_output[:, :, :, filter_index])
#根據損失函數和輸入層定義梯度
grads = K.gradients(loss, model.input)[0]
grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5)
#實例化計算梯度的函數
iterate = K.function([model.input], [loss, grads])
#定義一個隨機圖像,圖像底色為灰色,并疊加均值為0標準差為20的高斯噪聲
input_img_data = np.random.random((1, size, size, 3)) * 20 + 128.
#迭代40輪,使用梯度上升算法求解,學習速率(步長)為step
step = 1.
for i in range(40):
        loss_value, grads_value = iterate([input_img_data])
        input_img_data += grads_value * step      
img = input_img_data[0]

圖1-22 ResNet50結構圖圖片源于http://blog.csdn.net/mao_feng/article/details/52734438。

圖1-23 Inception單元結構https://github.com/fchollet/deep-learning-with-python-notebooks。

圖1-24 Keras下的VGG16結構圖

以VGG16為例,如圖1-24所示,在VGG16中具有多個卷積層,其中最典型的5個分別為block1_conv1、block2_conv1、block3_conv1、block4_conv1和block5_conv1。

Francois Chollet使用上述方法可視化了這5個卷積層,block1_conv1的可視化結果如圖1-25所示,block3_conv1的可視化結果如圖1-26所示,block5_conv1的可視化結果如圖1-27所示,可見第1層卷積提取的主要是邊緣和紋路特征,越往后的卷積提取的特征越高級,到了第5層卷積已經提取很抽象的高級特征了。

圖1-25 block1_conv1可視化結果

下面我們以經典的小豬圖像為例,展現在VGG16下各個卷積層處理的情況,相應的代碼路徑為:

https://github.com/duoergun0729/adversarial_examples/code/1-case2-keras.ipynb

首先實例化Keras下的VGG16模型,加載基于ImageNet 2012數據集預訓練的參數。

#使用VGG16
from keras.applications.vgg16 import VGG16
import matplotlib.pyplot as plt
%matplotlib inline
model = VGG16(weights='imagenet')

圖1-26 block3_conv1可視化結果

之后加載經典的小豬圖片(見圖1-28),并進行預處理。

from keras.preprocessing import image
from keras.applications.vgg16 import preprocess_input, decode_predictions
import numpy as np
#小豬的路徑
img_path = "../picture/pig.jpg"
#縮放到指定大小 224×224
img = image.load_img(img_path, target_size=(224, 224))
#展示圖片
plt.imshow(img)
plt.show()
x = image.img_to_array(img)
# 擴展維度,適配模型輸入大小 (1, 224, 224, 3)
x = np.expand_dims(x, axis=0)
# 圖像預處理
x = preprocess_input(x)

圖1-27 block5_conv1可視化結果

對該圖片進行預測,預測結果為小豬,滿足預期。

preds = model.predict(x)
print('Predicted:', decode_predictions(preds, top=3)[0])
Predicted: [('n03935335', 'piggy_bank', 0.6222573), ('n02395406', 'hog', 
0.3228228), ('n02108915', 'French_bulldog', 0.013370045)]

圖1-28 經典的小豬圖片

獲取block1_conv1、block3_conv1和block5_conv1對應的輸出tensor,并基于VGG16定義新的模型,該模型的輸入為圖像,輸出為以上三層的輸出tensor。

from keras import models
layer_names=['block1_conv1', 'block3_conv1','block5_conv1']
# 獲取指定層的輸出:
layer_outputs = [model.get_layer(layer_name).output for layer_name in 
layer_names]
# 創建新的模型,該模型的輸出為指定的層的輸出
activation_model = models.Model(inputs=model.input, outputs=layer_outputs)

針對小豬圖像進行預測,獲得指定層的輸出結果。

#獲得小豬的輸出
activations = activation_model.predict(x)

遍歷block1_conv1、block3_conv1和block5_conv1的各個卷積核的輸出并可視化。這里需要指出的是,卷積核的輸出結果的范圍并不是固定的,為了可以展示成圖片,需要根據對應的均值channel_image.mean()和標準差channel_image.std()進行歸一化,然后再轉換到圖片對應的像素范圍[0,255]。為了便于顯示,每層以8×8的格式展示前64個卷積核對應的圖像。block1_conv1層可視化的結果如圖1-29所示,block3_conv1層可視化的結果如圖1-30所示,block5_conv1層可視化的結果如圖1-31所示。可見第1層卷積層提取特征時盡可能保留了原圖的細節,越往后的卷積層提取的特征越高級,保留原始圖片的細節越來越少。到了最后幾層,出現的空白越來越多,這意味著部分卷積核無法在圖像中匹配特定的特征了,這也意味著越高級別的特征對應的矩陣越稀疏。

images_per_row = 8
for layer_name, layer_activation in zip(layer_names, activations):
    # 獲取卷積核的個數
    n_features = layer_activation.shape[-1]
    # 特征圖的形狀 (1, size, size, n_features)
    size = layer_activation.shape[1]
    #最多展現8行
    n_cols=8
    display_grid = np.zeros((size * n_cols, images_per_row * size))
    for col in range(n_cols):
        for row in range(images_per_row):
            channel_image = layer_activation[0,:, :,
                                               col * images_per_row + row]
            # 歸一化處理
            channel_image -= channel_image.mean()
            channel_image /= channel_image.std()
            # 數據擴展到[0,255]范圍
            channel_image *= 128
            channel_image += 128    
            channel_image = np.clip(channel_image, 0, 255).astype('uint8')
            display_grid[col * size : (col + 1) * size,
                         row * size : (row + 1) * size] = channel_image
    # 展示圖片
    scale = 1. / size
    plt.figure(figsize=(scale * display_grid.shape[1],
                        scale * display_grid.shape[0]))
    plt.title(layer_name)
    plt.grid(False)
    plt.imshow(display_grid, aspect='auto', cmap='viridis')
plt.show()

圖1-29 小豬圖片在block1_conv1層的可視化結果

圖1-30 小豬圖片在block3_conv1層的可視化結果

圖1-31 小豬圖片在block5_conv1層的可視化結果

主站蜘蛛池模板: 荣成市| 新乡市| 台南市| 华亭县| 宁晋县| 专栏| 榕江县| 金塔县| 道孚县| 石棉县| 夹江县| 永兴县| 南乐县| 会宁县| 得荣县| 平湖市| 龙川县| 浑源县| 潼南县| 南安市| 山阳县| 大洼县| 色达县| 昌江| 武汉市| 札达县| 和平区| 孙吴县| 鄢陵县| 凭祥市| 屏东市| 南部县| 林芝县| 民和| 汝南县| 德江县| 旬阳县| 祁连县| 渭南市| 年辖:市辖区| 莎车县|