TensorFlow 实战(八)(3)

简介: TensorFlow 实战(八)

TensorFlow 实战(八)(2)https://developer.aliyun.com/article/1522968

附录 B:计算机视觉

B.1 Grad-CAM:解释计算机视觉模型

Grad-CAM(代表梯度类激活映射)在第七章介绍过,是由 Ramprasaath R. Selvaraju 等人在“Grad-CAM: Visual Explanations from Deep Networks via Gradient-based Localization”(arxiv.org/pdf/1610.02391.pdf)中介绍的一种深度神经网络模型解释技术。深度网络以其难以解释的特性而臭名昭著,因此被称为黑盒子。因此,我们必须进行一些分析,并确保模型按预期工作。

让我们在第七章实现的模型上刷新一下记忆:一个名为 InceptionResNet v2 的预训练模型,其顶部是一个具有 200 个节点的 softmax 分类器(即我们的图像分类数据集 TinyImageNet 中的类别数量相同;请参阅下面的列表)。

清单 B.1 我们在第七章定义的 InceptionResNet v2 模型

import tensorflow as tf
import tensorflow.keras.backend as K
from tensorflow.keras.applications import InceptionResNetV2
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Input, Dense, Dropout
K.clear_session()
def get_inception_resnet_v2_pretrained():
    model = Sequential([                                       ❶
        Input(shape=(224,224,3)),                              ❷
        InceptionResNetV2(include_top=False, pooling='avg'),   ❸
        Dropout(0.4),                                          ❹
        Dense(200, activation='softmax')                       ❺
    ])
    loss = tf.keras.losses.CategoricalCrossentropy()
    adam = tf.keras.optimizers.Adam(learning_rate=0.0001)
    model.compile(loss=loss, optimizer=adam, metrics=['accuracy'])
    return model 
model = get_inception_resnet_v2_pretrained()
model.summary()

❶ 使用 Sequential API 定义一个模型。

❷ 定义一个输入层来接收大小为 224 × 224 × 3 的图像批次。

❸ 下载并使用预训练的 InceptionResNetV2 模型(不包括内置分类器)。

❹ 添加一个 dropout 层。

❺ 添加一个具有 200 个节点的新分类器层。

如果你打印此模型的摘要,你将得到以下输出:

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
inception_resnet_v2 (Model)  (None, 1536)              54336736  
_________________________________________________________________
dropout (Dropout)            (None, 1536)              0         
_________________________________________________________________
dense (Dense)                (None, 200)               307400    
=================================================================
Total params: 54,644,136
Trainable params: 54,583,592
Non-trainable params: 60,544
_________________________________________________________________

如您所见,InceptionResNet v2 模型被视为我们模型中的单个层。换句话说,它是一个嵌套模型,其中外层模型(sequential)有一个内层模型(inception_resnet_v2)。但是我们需要更多的透明度,因为我们将要访问 inception_resnet_v2 模型内的特定层,以实现 Grad-CAM。因此,我们将“解开”或移除此嵌套,并且只描述模型的层。我们可以使用以下代码实现这一点:

K.clear_session()
model = load_model(os.path.join('models','inception_resnet_v2.h5'))
def unwrap_model(model):
    inception = model.get_layer('inception_resnet_v2')
    inp = inception.input
    out = model.get_layer('dropout')(inception.output)
    out = model.get_layer('dense')(out)
    return Model(inp, out)   
unwrapped_model = unwrap_model(model)
unwrapped_model.summary()

实质上我们正在做的是取现有模型并略微更改其输入。在取得现有模型后,我们将输入更改为 inception_resnet_v2 模型的输入层。然后,我们定义一个新模型(本质上使用与旧模型相同的参数)。然后你将看到以下输出。没有更多的模型在模型内部:

Model: "model"
___________________________________________________________________________
➥ ________________
Layer (type)                    Output Shape         Param #     Connected 
➥ to                     
===========================================================================
➥ ================
input_2 (InputLayer)            [(None, None, None,  0                                            
___________________________________________________________________________
➥ ________________
conv2d (Conv2D)                 (None, None, None, 3 864         
➥ input_2[0][0]                    
___________________________________________________________________________
➥ ________________
batch_normalization (BatchNorma (None, None, None, 3 96          
➥ conv2d[0][0]                     
___________________________________________________________________________
➥ ________________
activation (Activation)         (None, None, None, 3 0           
➥ batch_normalization[0][0]        
___________________________________________________________________________
➥ ________________
...
___________________________________________________________________________
➥ ________________
conv_7b (Conv2D)                (None, None, None, 1 3194880     
➥ block8_10[0][0]                  
___________________________________________________________________________
➥ ________________
conv_7b_bn (BatchNormalization) (None, None, None, 1 4608        
➥ conv_7b[0][0]                    
___________________________________________________________________________
➥ ________________
conv_7b_ac (Activation)         (None, None, None, 1 0           
➥ conv_7b_bn[0][0]                 
___________________________________________________________________________
➥ ________________
global_average_pooling2d (Globa (None, 1536)         0           
➥ conv_7b_ac[0][0]                 
___________________________________________________________________________
➥ ________________
dropout (Dropout)               (None, 1536)         0           
➥ global_average_pooling2d[0][0]   
___________________________________________________________________________
➥ ________________
dense (Dense)                   (None, 200)          307400      
➥ dropout[1][0]                    
===========================================================================
➥ ================
Total params: 54,644,136
Trainable params: 54,583,592
Non-trainable params: 60,544
___________________________________________________________________________
➥ ________________

接下来,我们将进行一次更改:向我们的模型引入一个新输出。请记住,我们使用功能 API 来定义我们的模型。这意味着我们可以在我们的模型中定义多个输出。我们需要的输出是 inception_resnet_v2 模型中最后一个卷积层产生的特征图。这是 Grad-CAM 计算的核心部分。您可以通过查看解开模型的模型摘要来获得最后一个卷积层的层名称:

last_conv_layer = 'conv_7b' # This is the name of the last conv layer of the model
grad_model = Model(
    inputs=unwrapped_model.inputs, 
    outputs=[
        unwrapped_model.get_layer(last_conv_layer).output,
        unwrapped_model.output
    ]    
)

有了我们的模型准备好后,让我们转向数据。我们将使用验证数据集来检查我们的模型。特别地,我们将编写一个函数(见清单 B.2)来接收以下内容:

  • image_path(str)- 数据集中图像的路径。
  • val_df(pd.DataFrame)—一个包含从图像名称到 wnid(即 WordNet ID)的映射的 pandas 数据框。请记住,wnid 是用于识别特定对象类的特殊编码。
  • class_indices(dict)—一个 wnid(字符串)到类别(0-199 之间的整数)的映射。这保留了关于哪个 wnid 在模型的最终输出层中由哪个索引表示的信息。
  • words(pd.DataFrame)—一个包含从 wnid 到类别的可读描述的映射的 pandas 数据框。

清单 B.2 检索转换后的图像、类别索引和人类可读标签

img_path = 'data/tiny-imagenet-200/val/images/val_434.JPEG'
val_df = pd.read_csv(                                                          ❶
    os.path.join('data','tiny-imagenet-200', 'val', 'val_annotations.txt'),
    sep='\t', index_col=0, header=None
)
with open(os.path.join('data','class_indices'),'rb') as f:                     ❷
    class_indices = pickle.load(f)
words = pd.read_csv(                                                           ❸
    os.path.join('data','tiny-imagenet-200', 'words.txt'), 
    sep='\t', index_col=0, header=None
)
def get_image_class_label(img_path, val_df, class_indices, words):
    """ Returns the normalized input, class (int) and the label name for a given image"""
    img = np.expand_dims(                                                      ❹
        np.asarray(
            Image.open(img_path).resize((224,224)                              ❺
    )
    img /= 127.5                                                               ❻
    img -= 1                                                                   ❻
    if img.ndim == 3:
        img = np.repeat(np.expand_dims(img, axis=-1), 3, axis=-1)              ❼
    _, img_name = os.path.split(img_path)
    wnid = val_df.loc[img_name,1]                                              ❽
    cls = class_indices[wnid]                                                  ❾
    label = words.loc[wnid, 1]                                                 ❿
    return img, cls, label
# Test the function with a test image
img, cls, label = get_image_class_label(img_path, val_df, class_indices, words)⓫

❶ 读取 val_annotations.txt。这将创建一个数据框,其中包含从图像文件名到 wnid(即 WordNet ID)的映射。

❷ 加载将 wnid 映射到类索引(整数)的类索引。

❸ 这将创建一个数据框,其中包含从 wnid 到类描述的映射。

❹ 加载由文件路径给出的图像。首先,我们添加一个额外的维度来表示批次维度。

❺ 将图像调整大小为 224×224 大小的图像。

❻ 将图像像素值调整到[-1, 1]的范围内。

❼ 如果图像是灰度的,则在通道维度上将图像重复三次,以与 RGB 图像具有相同的格式。

❽ 获取图像的 wnid。

❾ 获取图像的类别索引。

❿ 获取类的字符串标签。

⓫ 对一个示例图像运行该函数。

TensorFlow 实战(八)(4)https://developer.aliyun.com/article/1522971

相关文章
|
26天前
|
机器学习/深度学习 自然语言处理 TensorFlow
TensorFlow 实战(六)(2)
TensorFlow 实战(六)
24 0
|
13天前
|
机器学习/深度学习 TensorFlow API
TensorFlow与Keras实战:构建深度学习模型
本文探讨了TensorFlow和其高级API Keras在深度学习中的应用。TensorFlow是Google开发的高性能开源框架,支持分布式计算,而Keras以其用户友好和模块化设计简化了神经网络构建。通过一个手写数字识别的实战案例,展示了如何使用Keras加载MNIST数据集、构建CNN模型、训练及评估模型,并进行预测。案例详述了数据预处理、模型构建、训练过程和预测新图像的步骤,为读者提供TensorFlow和Keras的基础实践指导。
144 59
|
26天前
|
机器学习/深度学习 自然语言处理 TensorFlow
TensorFlow 实战(五)(5)
TensorFlow 实战(五)
19 1
|
26天前
|
自然语言处理 算法 TensorFlow
TensorFlow 实战(六)(3)
TensorFlow 实战(六)
21 0
|
26天前
|
机器学习/深度学习 数据可视化 TensorFlow
TensorFlow 实战(六)(1)
TensorFlow 实战(六)
25 0
|
26天前
|
存储 自然语言处理 TensorFlow
TensorFlow 实战(五)(4)
TensorFlow 实战(五)
21 0
|
26天前
|
数据可视化 TensorFlow 算法框架/工具
TensorFlow 实战(八)(4)
TensorFlow 实战(八)
25 1
|
26天前
|
机器学习/深度学习 自然语言处理 TensorFlow
TensorFlow 实战(八)(5)
TensorFlow 实战(八)
25 0
|
26天前
|
并行计算 TensorFlow 算法框架/工具
TensorFlow 实战(八)(2)
TensorFlow 实战(八)
21 0
|
26天前
|
并行计算 Ubuntu TensorFlow
TensorFlow 实战(八)(1)
TensorFlow 实战(八)
22 0