深度学习模型保存_解读计算机视觉的深度学习模型
作者 |?Dipanjan(DJ)Sarkar
來源?|?Medium
編輯?|?代碼醫生團隊
介紹
人工智能(AI)不再僅限于研究論文和學術界。業內不同領域的企業和組織正在構建由AI支持的大規模應用程序。這里要考慮的問題是,“我們是否相信AI模型做出的決策?”和“機器學習或深度學習模型如何做出決策?”。解釋機器學習或深度學習模型一直是整個數據科學生命周期中經常被忽視的任務,因為數據科學家或機器學習工程師會更多地參與實際推動生產或建立和運行模型。
但是除非正在構建一個有趣的機器學習模型,否則準確性并不是唯一重要的!業務利益相關者和消費者經常會問到用于解決現實問題的任何機器學習模型的公平性,問責制和透明度等難題!
現在有可解釋的人工智能(XAI)的整個研究分支!在本文中,將研究用于解釋計算機視覺中使用的深度學習模型的概念,技術和工具,更具體地說 - 卷積神經網絡(CNN)。將采用實踐方法,使用Keras和TensorFlow 2.0實現深度學習模型,并利用開源工具來解釋這些模型所做出的決策!簡而言之本文的目的是找出 - 深度學習模型真正看到了什么?
卷積神經網絡
用于計算機視覺問題的最流行的深度學習模型是卷積神經網絡(CNN)!
資料來源:Becomehuman.aiCNN通常由多個卷積和池化層組成,這有助于深度學習模型從圖像等可視數據中自動提取相關特征。由于這種多層架構,CNN學習了強大的特征層次結構,即空間,旋轉和平移不變。
CNN模型中的關鍵操作如上圖所示。任何圖像都可以表示為像素值的張量。卷積層有助于從該圖像中提取特征(形成特征圖)。網絡中的較淺層(更接近輸入數據)學習非常通用的特征,如邊緣,角落等。網絡中更深的層(更靠近輸出層)學習與輸入圖像有關的非常具體的特征。下圖有助于總結任何CNN模型的關鍵方面。
由于只關心CNN模型如何感知圖像,因此不會從頭開始訓練任何CNN模型。相反將在示例中利用遷移學習的功能和預先訓練的CNN模型。
像VGG-16這樣的預訓練模型已經在具有大量不同圖像類別的大型數據集(ImageNet)上進行了預訓練。考慮到這一事實,該模型應該已經學習了強大的功能層次結構。因此該模型已經學習了屬于1,000個不同類別的超過一百萬個圖像的特征的良好表示,可以作為適合于計算機視覺問題的新圖像的良好特征提取器。
解讀CNN模型 - 深度學習模型真正看到了什么?
這是有趣的部分,真的可以通過一個看似黑盒子的CNN模型來解除呈現的不透明度,并嘗試理解幕后真正發生的事情以及模型在看到圖像時真正看到了什么?有許多技術和工具可用于解釋基于視覺的深度學習模型所做出的決策。本文中介紹的一些主要技術如下所示。
看看這些技術中的每一種,并解釋一些使用Keras和TensorFlow構建的基于CNN的深度學習模型。
SHAP Gradient Explainer
這種技術試圖結合來自Integrated Gradients,SHapley Additive exPlanations(SHAP)和SmoothGrad的眾多想法。該技術試圖使用預期梯度(集成梯度的擴展)來解釋模型決策。這是一種功能歸因方法,專為基于Shapley值擴展到無限玩家游戲的可微模型而設計。將在此處使用此框架來實現此技術。
https://github.com/slundberg/shap
集成梯度值與SHAP值略有不同,需要單個參考值進行集成。然而在SHAP Gradient Explainer中,預期梯度將積分重新表示為期望,并將該期望與來自背景數據集的采樣參考值相結合。因此該技術使用整個數據集作為背景分布而不是單個參考值。嘗試在一些示例圖像上實現這一點。首先加載一些基本依賴項和模型可視化函數實用程序。
import kerasfrom keras.applications.vgg16 import VGG16from keras.applications.vgg16 import preprocess_input, decode_predictionsfrom matplotlib.colors import LinearSegmentedColormapimport numpy as npimport shapimport keras.backend as Kimport json shap.initjs() # utility function to visualize SHAP values in larger image formats# this modifies the `shap.image_plot(...)` functiondef visualize_model_decisions(shap_values, x, labels=None, figsize=(20, 30)): colors = [] for l in np.linspace(1, 0, 100): colors.append((30./255, 136./255, 229./255,l)) for l in np.linspace(0, 1, 100): colors.append((255./255, 13./255, 87./255,l)) red_transparent_blue = LinearSegmentedColormap.from_list("red_transparent_blue", colors) multi_output = True if type(shap_values) != list: multi_output = False shap_values = [shap_values] # make sure labels if labels is not None: assert labels.shape[0] == shap_values[0].shape[0], "Labels must have same row count as shap_values arrays!" if multi_output: assert labels.shape[1] == len(shap_values), "Labels must have a column for each output in shap_values!" else: assert len(labels.shape) == 1, "Labels must be a vector for single output shap_values." # plot our explanations fig_size = figsize fig, axes = plt.subplots(nrows=x.shape[0], ncols=len(shap_values) + 1, figsize=fig_size) if len(axes.shape) == 1: axes = axes.reshape(1,axes.size) for row in range(x.shape[0]): x_curr = x[row].copy() # make sure if len(x_curr.shape) == 3 and x_curr.shape[2] == 1: x_curr = x_curr.reshape(x_curr.shape[:2]) if x_curr.max() > 1: x_curr /= 255. axes[row,0].imshow(x_curr) axes[row,0].axis('off') # get a grayscale version of the image if len(x_curr.shape) == 3 and x_curr.shape[2] == 3: x_curr_gray = (0.2989 * x_curr[:,:,0] + 0.5870 * x_curr[:,:,1] + 0.1140 * x_curr[:,:,2]) # rgb to gray else: x_curr_gray = x_curr if len(shap_values[0][row].shape) == 2: abs_vals = np.stack([np.abs(shap_values[i]) for i in range(len(shap_values))], 0).flatten() else: abs_vals = np.stack([np.abs(shap_values[i].sum(-1)) for i in range(len(shap_values))], 0).flatten() max_val = np.nanpercentile(abs_vals, 99.9) for i in range(len(shap_values)): if labels is not None: axes[row,i+1].set_title(labels[row,i]) sv = shap_values[i][row] if len(shap_values[i][row].shape) == 2 else shap_values[i][row].sum(-1) axes[row,i+1].imshow(x_curr_gray, cmap=plt.get_cmap('gray'), alpha=0.15, extent=(-1, sv.shape[0], sv.shape[1], -1)) im = axes[row,i+1].imshow(sv, cmap=red_transparent_blue, vmin=-max_val, vmax=max_val) axes[row,i+1].axis('off') cb = fig.colorbar(im, ax=np.ravel(axes).tolist(), label="SHAP value", orientation="horizontal", aspect=fig_size[0]/0.2) cb.outline.set_visible(False)下一步是加載預先訓練好的VGG-16模型,該模型先前已在Imagenet數據集上進行過訓練。可以使用以下代碼輕松完成。
model = VGG16(weights='imagenet', include_top=True)model.summary() # output_________________________________________________________________Layer (type) Output Shape Param # =================================================================input_3 (InputLayer) (None, 224, 224, 3) 0 _________________________________________________________________block1_conv1 (Conv2D) (None, 224, 224, 64) 1792 _________________________________________________________________block1_conv2 (Conv2D) (None, 224, 224, 64) 36928 _________________________________________________________________block1_pool (MaxPooling2D) (None, 112, 112, 64) 0 _________________________________________________________________......_________________________________________________________________fc2 (Dense) (None, 4096) 16781312 _________________________________________________________________predictions (Dense) (None, 1000) 4097000 =================================================================Total params: 138,357,544Trainable params: 138,357,544Non-trainable params: 0_________________________________________________________________加載CNN模型后,現在將加載一個可用作背景分布的小圖像數據集,將使用四個樣本圖像進行模型解釋。
# load sample imagesX, y = shap.datasets.imagenet50() # load sample cat image for testIMAGE_PATH = './cat2.jpg'img = keras.preprocessing.image.load_img(IMAGE_PATH, target_size=(224, 224))img = keras.preprocessing.image.img_to_array(img) # select 3 other sample images for testimport matplotlib.pyplot as plt%matplotlib inline to_predict = np.array([X[28], X[35], X[46], img])fig, ax = plt.subplots(1, 4, figsize=(18, 10))ax[0].imshow(to_predict[0]/255.)ax[1].imshow(to_predict[1]/255.)ax[2].imshow(to_predict[2]/255.)ax[3].imshow(to_predict[3]/255.)有四種不同類型的圖像,包括一只貓的照片!首先看一下模型對這些圖像的預測。
# get imagenet id to label name mappingsurl = "https://s3.amazonaws.com/deep-learning-models/image-models/imagenet_class_index.json"fname = shap.datasets.cache(url)with open(fname) as f: class_names = json.load(f) # make model predictionspredictions = model.predict(preprocess_input(to_predict.copy())) # get prediction labelspredicted_labels = [class_names.get(str(pred)) for pred in np.argmax(predictions, axis=1)]print(predicted_labels)[['n02999410','chain'],
['n01622779','great_grey_owl'],
['n03180011','desktop_computer'],
['n02124075','Egyptian_cat']]
首先嘗試可視化模型在神經網絡的第7層(通常是模型中較淺層之一)中看到的內容。
# utility function to pass inputs to specific model layersdef map2layer(x, layer): feed_dict = dict(zip([model.layers[0].input], [preprocess_input(x.copy())])) return K.get_session().run(model.layers[layer].input, feed_dict) # focus on the 7th layer of CNN modelprint(model.layers[7].input)Out [46]: <tf.Tensor 'block2_pool_2/MaxPool:0' shape=(?, 56, 56, 128) dtype=float32> # make model predictionse = shap.GradientExplainer((model.layers[7].input, model.layers[-1].output), map2layer(preprocess_input(X.copy()), 7))shap_values, indexes = e.shap_values(map2layer(to_predict, 7), ranked_outputs=2)index_names = np.vectorize(lambda x: class_names[str(x)][1])(indexes)print(index_names)Out [47]: array([['chain', 'chain_mail'], ['great_grey_owl', 'prairie_chicken'], ['desktop_computer', 'screen'], ['Egyptian_cat', 'tabby']], dtype=') # visualize model decisionsvisualize_model_decisions(shap_values=shap_values, x=to_predict, labels=index_names, figsize=(20, 40))這提供了一個很好的視角,可以看到模型對每個圖像做出的前兩個預測,以及為什么要做出這樣的決定。來看看VGG-16模型中的一個更深層,并可視化第14層的決策。
# focus on 14th layer of the CNN modelprint(model.layers[14].input)Out [49]: <tf.Tensor 'block4_conv3_2/Relu:0' shape=(?, 28, 28, 512) dtype=float32> # make model predictionse = shap.GradientExplainer((model.layers[14].input, model.layers[-1].output), map2layer(preprocess_input(X.copy()), 14))shap_values, indexes = e.shap_values(map2layer(to_predict, 14), ranked_outputs=2)index_names = np.vectorize(lambda x: class_names[str(x)][1])(indexes) # visualize model decisionsvisualize_model_decisions(shap_values=shap_values, x=to_predict, labels=index_names, figsize=(20, 40))現在可以看到模型變得更強大和更自信的預測判決基礎上,十八值的強度,模型預測一個方面screen對一個desktop_computer地方,它還要在鍵盤上。預測貓是tabby因為鼻子,胡須,面部圖案等特定功能!
解釋使用TensorFlow 2.0構建的CNN模型
對于其余四種技術,將使用TensorFlow 2.0預先訓練的模型,并使用流行的開源框架tf-explain。這里的想法是研究CNN的不同模型解釋技術。
https://github.com/sicara/tf-explain
加載預先訓練的CNN模型
加載一個最復雜的預訓練CNN模型,Xception模型聲稱比Inception V3模型略勝一籌。從加載必要的依賴項和預先訓練的模型開始。
# load dependenciesimport numpy as npimport tensorflow as tfimport matplotlib.pyplot as pltfrom tf_explain.core.activations import ExtractActivationsfrom tensorflow.keras.applications.xception import decode_predictions %matplotlib inline # load Xception pre-trained CNN modelmodel = tf.keras.applications.xception.Xception(weights='imagenet', include_top=True)model.summary() # OutputModel: "xception"__________________________________________________________________________________________________Layer (type) Output Shape Param # Connected to ==================================================================================================input_1 (InputLayer) [(None, 299, 299, 3) 0 __________________________________________________________________________________________________block1_conv1 (Conv2D) (None, 149, 149, 32) 864 input_1[0][0] __________________________________________________________________________________________________block1_conv1_bn (BatchNormaliza (None, 149, 149, 32) 128 block1_conv1[0][0] __________________________________________________________________________________________________......__________________________________________________________________________________________________block14_sepconv2_act (Activatio (None, 10, 10, 2048) 0 block14_sepconv2_bn[0][0] __________________________________________________________________________________________________avg_pool (GlobalAveragePooling2 (None, 2048) 0 block14_sepconv2_act[0][0] __________________________________________________________________________________________________predictions (Dense) (None, 1000) 2049000 avg_pool[0][0] ==================================================================================================Total params: 22,910,480Trainable params: 22,855,952Non-trainable params: 54,528可以從上面的模型體系結構快照中看到,該模型總共有14個塊,每個塊中有多個層。絕對是CNN更深的模型之一!
樣本圖像的模型預測
將重用貓的樣本圖像,并使用Xception模型進行前5個預測。在進行預測之前先加載圖像。
# load and pre-process cat imageIMAGE_PATH = './cat2.jpg'img = tf.keras.preprocessing.image.load_img(IMAGE_PATH, target_size=(299, 299))img = tf.keras.preprocessing.image.img_to_array(img) # view the imageplt.imshow(img/255.)現在使用Xception模型對此圖像進行前5個預測。將在推斷之前預處理圖像。
# load imagenet id to class label mappingsimport requests response = requests.get('https://storage.googleapis.com/download.tensorflow.org/data/imagenet_class_index.json')imgnet_map = response.json()imgnet_map = {v[1]: k for k, v in imgnet_map.items()} # make model predictionsimg = tf.keras.applications.xception.preprocess_input(img)predictions = model.predict(np.array([img]))decode_predictions(predictions, top=5)[[('n02124075', 'Egyptian_cat', 0.80723596),
??('n02123159', 'tiger_cat', 0.09508163),
??('n02123045', 'tabby', 0.042587988),
??('n02127052', 'lynx', 0.00547999),
??('n02971356', 'carton', 0.0014547487)]]
有趣的預測,至少前三名肯定是相關的!
激活層可視化
此技術通常用于可視化給定輸入如何來自特定激活層。關鍵的想法是探索在模型中激活哪些特征圖并將其可視化。通常這是通過查看每個特定層來完成的。以下代碼展示了CNN模型的塊2中的一個層的激活層可視化。
explainer = ExtractActivations()grid = explainer.explain((np.array([img]), None), model, ['block2_sepconv2_act'])fig, ax = plt.subplots(figsize=(18, 18))ax.imshow(grid, cmap='binary_r')這種方式了解哪些特征圖被激活以及它們通常關注的圖像部分。
遮擋敏感度
使用遮擋靈敏度進行解釋的想法非常直觀。基本上試圖通過迭代地遮擋(隱藏)部分來可視化圖像的各部分如何影響神經網絡模型的置信度。這是通過用灰色方塊系統地遮擋輸入圖像的不同部分并監視分類器的輸出來完成的。
# get label imagenet IDimgnet_map['Egyptian_cat'] from tf_explain.core.occlusion_sensitivity import OcclusionSensitivity explainer = OcclusionSensitivity()img_inp = tf.keras.preprocessing.image.load_img(IMAGE_PATH, target_size=(299, 299))img_inp = tf.keras.preprocessing.image.img_to_array(img_inp)grid = explainer.explain(([img_inp], None), model, 285, 7)fig, ax = plt.subplots(figsize=(8, 8))plt.imshow(grid)理想情況下,圖像的特定色塊應以紅色\黃色突出顯示,如熱圖,但對于貓圖像,它會突出顯示紅色色調的整體圖像,原因可能是因為貓的縮放圖像。然而,圖像的左側具有更高的強度,更多地關注貓的形狀,而不是圖像的紋理。
GradCAM
這可能是解釋CNN模型最流行和最有效的方法之一。使用GradCAM,嘗試通過查看類激活圖(CAM)來可視化圖像的各部分如何影響神經網絡的輸出。類激活圖是一種簡單的技術,用于獲取CNN使用的辨別圖像區域來識別圖像中的特定類。換句話說,類激活圖(CAM)可以看到圖像中的哪些區域與此類相關。
grad-CAM的輸出將是有助于目標函數最大化的像素。例如,如果對最大化類別編號285的內容感興趣,則將所有其他類別歸零。
相對于卷積層輸出計算目標函數的梯度。這可以通過反向傳播有效地完成
給定圖像和感興趣的類別(例如,“老虎貓”或任何其他類型的可微分輸出)作為輸入,通過模型的CNN部分向前傳播圖像,然后通過任務特定的計算獲得原始分數對于該類別。除了所需的類(虎貓)之外,所有類的梯度都設置為零,設置為1.然后將該信號反向傳播到感興趣的整流卷積特征圖,將它們組合起來計算粗Grad-CAM定位( blue heatmap)表示模型必須在哪里做出特定決定。
看一下CNN模型中特定塊的GradCAM可視化。首先從塊1(較淺層)可視化其中一個層。
from tf_explain.core.grad_cam import GradCAM explainer = GradCAM() # get imagenet IDs for cat breedsimgnet_map['tabby'], imgnet_map['Egyptian_cat']Out [24]: ('281', '285') # visualize GradCAM outputs in Block 1grid1 = explainer.explain(([img], None), model, 'block1_conv2', 281)grid2 = explainer.explain(([img], None), model, 'block1_conv2', 285) fig = plt.figure(figsize = (18, 8))ax1 = fig.add_subplot(1, 3, 1)ax1.imshow(img_inp / 255.)ax1.imshow(grid1, alpha=0.6)ax2 = fig.add_subplot(1, 3, 2)ax2.imshow(img_inp / 255.)ax2.imshow(grid2, alpha=0.6)ax3 = fig.add_subplot(1, 3, 3)ax3.imshow(img_inp / 255.)就像預期的那樣,這是淺層之一,看到更高級別的特征,如邊緣和角落在網絡中被激活。現在在塊6中可視化來自網絡中較深層之一的GradCAM輸出。
# visualize GradCAM output from Block 6grid1 = explainer.explain(([img], None), model, 'block6_sepconv1', 281)grid2 = explainer.explain(([img], None), model, 'block6_sepconv1', 285) fig = plt.figure(figsize = (18, 8))ax1 = fig.add_subplot(1, 3, 1)ax1.imshow(img_inp / 255.)ax1.imshow(grid1, alpha=0.6)ax2 = fig.add_subplot(1, 3, 2)ax2.imshow(img_inp / 255.)ax2.imshow(grid2, alpha=0.6)ax3 = fig.add_subplot(1, 3, 3)ax3.imshow(img_inp / 255.)事情肯定開始變得更有趣,可以清楚地看到,當模型預測貓時tabby,它關注的是紋理以及貓的整體形狀和結構,而不是它預測貓作為一個Egyptian_cat。最后來看看Block 14中模型中最深的一層。
# visualize GradCAM output from Block 14grid1 = explainer.explain(([img], None), model, 'block14_sepconv1', 281)grid2 = explainer.explain(([img], None), model, 'block14_sepconv1', 285) fig = plt.figure(figsize = (18, 8))ax1 = fig.add_subplot(1, 3, 1)ax1.imshow(img_inp / 255.)ax1.imshow(grid1, alpha=0.6)ax2 = fig.add_subplot(1, 3, 2)ax2.imshow(img_inp / 255.)ax2.imshow(grid2, alpha=0.6)ax3 = fig.add_subplot(1, 3, 3)ax3.imshow(img_inp / 255.)非常有趣的是,對于tabby貓標簽預測,該模型還在觀察貓周圍的區域,該區域主要關注貓的整體形狀\結構以及貓的面部結構的某些方面!
SmoothGrad
這種技術有助于在決策輸入上可視化穩定的梯度。關鍵目標是識別強烈影響最終決策的像素。該策略的起點是類別得分函數相對于輸入圖像的梯度。該梯度可以解釋為靈敏度圖,并且有幾種技術可以闡述這個基本思想。
SmoothGrad是一種簡單的方法,可以幫助在視覺上銳化基于梯度的靈敏度圖。核心思想是拍攝感興趣的圖像,通過向圖像添加噪聲來對相似圖像進行采樣,然后獲取每個采樣圖像的最終靈敏度圖的平均值。
from tf_explain.core.smoothgrad import SmoothGrad explainer = SmoothGrad() grid1 = explainer.explain(([img], None), model, 281, 80, .2)grid2 = explainer.explain(([img], None), model, 285, 80, .2) fig = plt.figure(figsize = (18, 8))ax1 = fig.add_subplot(1, 3, 1)ax1.imshow(img_inp / 255.)ax1.imshow(grid1, alpha=0.9, cmap='binary_r')ax2 = fig.add_subplot(1, 3, 2)ax2.imshow(img_inp / 255.)ax2.imshow(grid2, alpha=0.9, cmap='binary_r')ax3 = fig.add_subplot(1, 3, 3)ax3.imshow(img_inp / 255.)對于tabby貓來說,焦點肯定是在臉上的關鍵點,包括斑點和條紋,這是非常有區別的特征。
結論
這應該很好地了解如何利用預先訓練的復雜CNN模型來預測新圖像,甚至嘗試可視化神經網絡模型真正看到的內容!這里的技術列表并不詳盡,但絕對涵蓋了一些最流行和廣泛使用的解釋CNN模型的方法。建議您使用自己的數據和模型進行嘗試!
本文中使用的所有代碼都可以在GitHub中作為Jupyter筆記本在此存儲庫中獲得。
https://github.com/dipanjanS/data_science_for_all/tree/master/gde_cnn_interpretation_xai
推薦閱讀
在Python中逐步檢測Canny邊緣 - 計算機視覺
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的深度学习模型保存_解读计算机视觉的深度学习模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中的insert函数_Pyt
- 下一篇: unity怎么实现人脸追踪_Unity