基于ResNetRS的宝可梦图像识别

简介: ResNetRS是在ResNet-D架构上面的改进

@toc

1、ResNet-D架构

  ResNetRS是在ResNet-D架构上面的改进,ResNet-D架构的结构如下:

image-20220907220047082

  注意,残差边上多了个池化操作。

2、ResNetRS架构

  我们提供了有关 ResNet-RS 架构更改的更多详细信息。我们重申 ResNet-RS 是:改进的缩放策略、改进的训练方法、ResNet-D 修改(He 等人,2018 年)和 SqueezeExcitation 模块(Hu 等人,2018 年)的组合

  表 11 显示了我们工作中使用的所有 ResNet 深度的块布局。 ResNet-50 到 ResNet-200 使用 He 等人的标准块配置。 (2015 年)。 ResNet-270 及更高版本主要扩展 c3 和 c4 中的块数,我们尝试保持它们的比例大致恒定。我们凭经验发现,在较低阶段添加块会限制过度拟合,因为较低层中的块具有显着较少的参数,即使所有块具有相同数量的 FLOP。图 6 显示了我们的 ResNet-RS 模型中使用的 ResNet-D 架构更改。

image-20220907221339109

   图 6. ResNet-RS 架构图。

   输出大小假定输入图像分辨率为 224×224。

   在卷积布局中,x2 是指第一个 3×3 卷积,步长为 2。

    ResNet-RS 架构是 Squeeze-and-Excitation 和 ResNet-D 的简单组合。

   × 符号表示块在 ResNet-101 架构中重复的次数。这些值根据表 11 中的块布局随深度变化。

3、手动搭建模型(Tensorflow)

   这里只是介绍网络的搭建方法,训练的时候我们直接使用迁移学习去做,用这个从头训练太慢了。
import tensorflow as tf
from tensorflow.keras import layers
from tensorflow.keras.models import Model
from typing import Callable, Dict, List, Union

3.1 模型配置项

DEPTH_TO_WEIGHT_VARIANTS = {
    50: [160],
    101: [160, 192],
    152: [192, 224, 256],
    200: [256],
    270: [256],
    350: [256, 320],
    420: [320],
}
BLOCK_ARGS = {
    50: [
        {
            "input_filters": 64,
            "num_repeats": 3
        },
        {
            "input_filters": 128,
            "num_repeats": 4
        },
        {
            "input_filters": 256,
            "num_repeats": 6
        },
        {
            "input_filters": 512,
            "num_repeats": 3
        },
    ],
    101: [
        {
            "input_filters": 64,
            "num_repeats": 3
        },
        {
            "input_filters": 128,
            "num_repeats": 4
        },
        {
            "input_filters": 256,
            "num_repeats": 23
        },
        {
            "input_filters": 512,
            "num_repeats": 3
        },
    ],
    152: [
        {
            "input_filters": 64,
            "num_repeats": 3
        },
        {
            "input_filters": 128,
            "num_repeats": 8
        },
        {
            "input_filters": 256,
            "num_repeats": 36
        },
        {
            "input_filters": 512,
            "num_repeats": 3
        },
    ],
    200: [
        {
            "input_filters": 64,
            "num_repeats": 3
        },
        {
            "input_filters": 128,
            "num_repeats": 24
        },
        {
            "input_filters": 256,
            "num_repeats": 36
        },
        {
            "input_filters": 512,
            "num_repeats": 3
        },
    ],
    270: [
        {
            "input_filters": 64,
            "num_repeats": 4
        },
        {
            "input_filters": 128,
            "num_repeats": 29
        },
        {
            "input_filters": 256,
            "num_repeats": 53
        },
        {
            "input_filters": 512,
            "num_repeats": 4
        },
    ],
    350: [
        {
            "input_filters": 64,
            "num_repeats": 4
        },
        {
            "input_filters": 128,
            "num_repeats": 36
        },
        {
            "input_filters": 256,
            "num_repeats": 72
        },
        {
            "input_filters": 512,
            "num_repeats": 4
        },
    ],
    420: [
        {
            "input_filters": 64,
            "num_repeats": 4
        },
        {
            "input_filters": 128,
            "num_repeats": 44
        },
        {
            "input_filters": 256,
            "num_repeats": 87
        },
        {
            "input_filters": 512,
            "num_repeats": 4
        },
    ],
}
CONV_KERNEL_INITIALIZER = {
    "class_name": "VarianceScaling",
    "config": {
        "scale": 2.0,
        "mode": "fan_out",
        "distribution": "truncated_normal"
    },
}
这里只搭建ResNet-RS101架构

3.2 get_survival_probability

  根据区块数和初始速率获取生存概率

def get_survival_probability(init_rate, block_num, total_blocks):
    return init_rate * float(block_num) / total_blocks

3.3 fixed_padding

def fixed_padding(inputs, kernel_size):
    """沿空间维度填充输入,与输入大小无关"""
    pad_total = kernel_size - 1
    pad_beg = pad_total // 2
    pad_end = pad_total - pad_beg

    # 使用 ZeroPadding 来避免 TFOpLambda 层
    padded_inputs = layers.ZeroPadding2D(
        padding=((pad_beg, pad_end), (pad_beg, pad_end)))(inputs)

    return padded_inputs

3.4 Conv2DFixedPadding

Conv2D block with fixed padding

def Conv2DFixedPadding(filters, kernel_size, strides, name=None):

def apply(inputs):
    if strides > 1:
        inputs = fixed_padding(inputs, kernel_size)
    return layers.Conv2D(filters=filters,
                         kernel_size=kernel_size,
                         strides=strides,
                         padding='same' if strides == 1 else 'valid',
                         use_bias=False,
                         kernel_initializer=CONV_KERNEL_INITIALIZER,
                         name=name)(inputs)

return apply

3.5 STEM块

# ResNet-D型STEM块
def STEM(inputs,
         bn_momentum: float = 0.0,
         bn_epsilon: float = 1e-5,
         activation: str = 'relu',
         name=None):
    # first stem block
    x = Conv2DFixedPadding(filters=32,
                           kernel_size=3,
                           strides=2,
                           name=name + '_stem_conv_1')(inputs)
    x = layers.BatchNormalization(momentum=bn_momentum,
                                  epsilon=bn_epsilon,
                                  name=name + '_stem_batch_norm_1')(x)
    x = layers.Activation(activation, name=name + '_stem_act_1')(x)

    # second stem block
    x = Conv2DFixedPadding(filters=32,
                           kernel_size=3,
                           strides=1,
                           name=name + '_stem_conv_2')(x)
    x = layers.BatchNormalization(momentum=bn_momentum,
                                  epsilon=bn_epsilon,
                                  name=name + '_stem_batch_norm_2')(x)
    x = layers.Activation(activation, name=name + '_stem_act_2')(x)

    # final stem block
    x = Conv2DFixedPadding(filters=64,
                           kernel_size=3,
                           strides=1,
                           name=name + '_stem_conv_3')(x)
    x = layers.BatchNormalization(momentum=bn_momentum,
                                  epsilon=bn_epsilon,
                                  name=name + '_stem_batch_norm_3')(x)
    x = layers.Activation(activation, name=name + '_stem_act_3')(x)

    # Replace stem max pool:
    x = Conv2DFixedPadding(filters=64,
                           kernel_size=3,
                           strides=2,
                           name=name + '_stem_conv_4')(x)
    x = layers.BatchNormalization(momentum=bn_momentum,
                                  epsilon=bn_epsilon,
                                  name=name + 'stem_batch_norm_4')(x)
    x = layers.Activation(activation, name=name + '_stem_act_4')(x)

    return x

3.6 SE注意力机制模块

def SE(inputs,
       in_filters: int,
       se_ratio: float = 0.25,
       expand_ratio: int = 1,
       name=None):
    x = layers.GlobalAveragePooling2D(name=name + '_se_squeeze')(inputs)

    se_shape = (1, 1, x.shape[-1])
    x = layers.Reshape(se_shape, name=name + '_se_reshape')(x)

    num_reduced_filters = max(1, int(in_filters * 4 * se_ratio))

    x = layers.Conv2D(filters=num_reduced_filters,
                      kernel_size=(1, 1),
                      strides=[1, 1],
                      kernel_initializer=CONV_KERNEL_INITIALIZER,
                      padding='same',
                      use_bias=False,
                      activation='relu',
                      name=name + '_se_reduce')(x)
    x = layers.Conv2D(filters=4 * in_filters * expand_ratio,  # Expand ratio is 1 by default
                      kernel_size=[1, 1],
                      strides=[1, 1],
                      kernel_initializer=CONV_KERNEL_INITIALIZER,
                      padding='same',
                      use_bias=False,
                      activation='sigmoid',
                      name=name + '_se_expand')(x)
    out = layers.multiply([inputs, x], name=name + '_se_excite')
    return out

3.7 Bottleneck Block

def BottleneckBlock(filters: int,
                    strides: int,
                    use_projection: bool,
                    bn_momentum: float = 0.0,
                    bn_epsilon: float = 1e-5,
                    activation: str = 'relu',
                    se_ratio: float = 0.25,
                    survival_probability: float = 0.8,
                    name=None):
    # 带有BN的残差网络的bottle block变体
    def apply(inputs):

        shortcut = inputs
        # 是否需要projection shortcut
        if use_projection:
            filters_out = filters * 4
            if strides == 2:
                shortcut = layers.AveragePooling2D(pool_size=(2, 2),
                                                   strides=(2, 2),
                                                   padding='same',
                                                   name=name + '_projection_pooling')(inputs)
                shortcut = Conv2DFixedPadding(filters=filters_out,
                                              kernel_size=1,
                                              strides=1,
                                              name=name + '_projection_conv')(shortcut)
            else:
                shortcut = Conv2DFixedPadding(filters=filters_out,
                                              kernel_size=1,
                                              strides=strides,
                                              name=name + '_projection_conv')(inputs)
                shortcut = layers.BatchNormalization(momentum=bn_momentum,
                                                     epsilon=bn_epsilon,
                                                     name=name + '_projection_batch_norm')(shortcut)

        # first conv layer:1x1 conv
        x = Conv2DFixedPadding(filters=filters,
                               kernel_size=1,
                               strides=1,
                               name=name + '_conv_1')(inputs)
        x = layers.BatchNormalization(momentum=bn_momentum,
                                      epsilon=bn_epsilon,
                                      name=name + 'batch_norm_1')(x)
        x = layers.Activation(activation, name=name + '_act_1')(x)

        # second conv layer:3x3 conv
        x = Conv2DFixedPadding(filters=filters,
                               kernel_size=3,
                               strides=strides,
                               name=name + '_conv_2')(x)
        x = layers.BatchNormalization(momentum=bn_momentum,
                                      epsilon=bn_epsilon,
                                      name=name + '_batch_norm_2')(x)
        x = layers.Activation(activation, name=name + '_act_2')(x)

        # third conv layer:1x1 conv
        x = Conv2DFixedPadding(filters=filters * 4,
                               kernel_size=1,
                               strides=1,
                               name=name + '_conv_3')(x)
        x = layers.BatchNormalization(momentum=bn_momentum,
                                      epsilon=bn_epsilon,
                                      name=name + '_batch_norm_3')(x)

        if 0 < se_ratio < 1:
            x = SE(x, filters, se_ratio=se_ratio, name=name + '_se')

        # Drop connect
        if survival_probability:
            x = layers.Dropout(survival_probability,
                               noise_shape=(None, 1, 1, 1),
                               name=name + '_drop')(x)
        x = layers.Add()([x, shortcut])

        return layers.Activation(activation, name=name + '_output_act')(x)

    return apply

3.8 Block Group

def BlockGroup(filters,
               strides,
               num_repeats,  # Block重复次数
               se_ratio: float = 0.25,
               bn_epsilon: float = 1e-5,
               bn_momentum: float = 0.0,
               activation: str = "relu",
               survival_probability: float = 0.8,
               name=None):
    """Create one group of blocks for the ResNet model."""

    def apply(inputs):
        # 只有每个block_group的第一个block块使用projection shortcut和strides
        x = BottleneckBlock(
            filters=filters,
            strides=strides,
            use_projection=True,
            se_ratio=se_ratio,
            bn_epsilon=bn_epsilon,
            bn_momentum=bn_momentum,
            activation=activation,
            survival_probability=survival_probability,
            name=name + "_block_0_",
        )(inputs)

        for i in range(1, num_repeats):
            x = BottleneckBlock(
                filters=filters,
                strides=1,
                use_projection=False,
                se_ratio=se_ratio,
                activation=activation,
                bn_epsilon=bn_epsilon,
                bn_momentum=bn_momentum,
                survival_probability=survival_probability,
                name=name + f"_block_{i}_",
            )(x)
        return x

    return apply

3.9 ResNetRS

# 构建ResNet-RS模型:这里复现ResNet-RS101
def ResNetRS(depth: int,  # ResNet网络的深度,101:[160,192]
             input_shape=None,
             bn_momentum=0.0,  # BN层的动量参数
             bn_epsilon=1e-5,  # BN层的Epsilon参数
             activation: str = 'relu',  # 激活函数
             se_ratio=0.25,  # 挤压和激发曾的比例
             dropout_rate=0.25,  # 最终分类曾之前的dropout
             drop_connect_rate=0.2,  # skip connection的丢失率
             include_top=True,  # 是否在网络顶部包含全连接层
             block_args: List[Dict[str, int]] = None,  # 字典列表,构造块模块的参数
             model_name='resnet-rs',  # 模型的名称
             pooling=None,  # 可选的池化模式
             weights='imagenet',
             input_tensor=None,
             classes=1000,  # 分类数
             classifier_activation: Union[str, Callable] = 'softmax',  # 分类器激活
             include_preprocessing=True):  # 是否包含预处理层(对输入图像通过ImageNet均值和标准差进行归一化):

    img_input = layers.Input(shape=input_shape)
    x = img_input
    inputs = img_input

    # 这里本来有个预处理判断,tensorflow版本太低。
    # if include_preprocessing:
    #     num_channels=input_shape[-1]
    #     if num_channels==3:
    #         # 预处理

    # Build stem
    x = STEM(x, bn_momentum=bn_momentum, bn_epsilon=bn_epsilon, activation=activation, name='stem')

    # Build blocks
    if block_args is None:
        block_args = BLOCK_ARGS[depth]

    for i, args in enumerate(block_args):
        # print(i,args)
        survival_probability = get_survival_probability(init_rate=drop_connect_rate,
                                                        block_num=i + 2,
                                                        total_blocks=len(block_args) + 1)
        # args['input_filters']=[64,128,256,512]
        # 只有第一个BlockGroup的stride=1,后面三个都是stride=2
        x = BlockGroup(filters=args['input_filters'],
                       activation=activation,
                       strides=(1 if i == 0 else 2),
                       num_repeats=args['num_repeats'],
                       se_ratio=se_ratio,
                       bn_momentum=bn_momentum,
                       bn_epsilon=bn_epsilon,
                       survival_probability=survival_probability,
                       name=f"BlockGroup{i + 2}_")(x)
    # Build head:
    if include_top:
        x = layers.GlobalAveragePooling2D(name='avg_pool')(x)
        if dropout_rate > 0:
            x = layers.Dropout(dropout_rate, name='top_dropout')(x)
        x = layers.Dense(classes, activation=classifier_activation, name='predictions')(x)
    else:
        if pooling == 'avg':
            x = layers.GlobalAveragePooling2D(name='avg_pool')(x)
        elif pooling == 'max':
            x = layers.GlobalMaxPooling2D(name='max_pool')(x)

    # Create model
    model = Model(inputs, x, name=model_name)

    return model

3.10 ResNetRS101架构

# build ResNet-RS101 model
def ResNetRS101(include_top=True,
                weights='imagenet',
                classes=1000,
                input_shape=None,
                input_tensor=None,
                pooling=None,
                classifier_activation='softmax',
                include_preprocessing=True):
    return ResNetRS(depth=101,
                    include_top=include_top,
                    drop_connect_rate=0.0,
                    dropout_rate=0.25,
                    weights=weights,
                    classes=classes,
                    input_shape=input_shape,
                    input_tensor=input_tensor,
                    pooling=pooling,
                    classifier_activation=classifier_activation,
                    model_name='resnet-rs-101',
                    include_preprocessing=include_preprocessing)
if __name__ == '__main__':
    model = ResNetRS101(input_shape=(224, 224, 3), classes=1000)
    model.summary()

image-20220907222127622

4、宝可梦数据集

  部分数据展示
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  都按照文件夹分好了,那么我们对数据集进行切分就可以了。
  按照8:2切分数据集。
import os
import random
import shutil
import numpy as np

# 数据集路径
DATASET_DIR = "pokeman"
# DATASET_DIR = "../第13章-验证码识别项目/captcha"
# 数据切分后存放路径
NEW_DIR = "data"
# 测试集占比
num_test = 0.2

# 打乱所有种类数据,并分割训练集和测试集
def shuffle_all_files(dataset_dir, new_dir, num_test):
    # 先删除已有new_dir文件夹
    if not os.path.exists(new_dir):
        pass
    else:
        # 递归删除文件夹
        shutil.rmtree(new_dir)
    # 重新创建new_dir文件夹
    os.makedirs(new_dir)
    # 在new_dir文件夹目录下创建train文件夹
    train_dir = os.path.join(new_dir, 'train')
    os.makedirs(train_dir)
    # 在new_dir文件夹目录下创建test文件夹
    test_dir = os.path.join(new_dir, 'test')
    os.makedirs(test_dir)
    # 原始数据类别列表
    directories = []
    # 新训练集类别列表
    train_directories = []
    # 新测试集类别列表
    test_directories = []
    # 类别名称列表
    class_names = []
    # 循环所有类别
    for filename in os.listdir(dataset_dir):
        # 原始数据类别路径
        path = os.path.join(dataset_dir, filename)
        # 新训练集类别路径
        train_path = os.path.join(train_dir, filename)
        # 新测试集类别路径
        test_path = os.path.join(test_dir, filename)
        # 判断该路径是否为文件夹
        if os.path.isdir(path):
            # 加入原始数据类别列表
            directories.append(path)
            # 加入新训练集类别列表
            train_directories.append(train_path)
            # 新建类别文件夹
            os.makedirs(train_path)
            # 加入新测试集类别列表
            test_directories.append(test_path)
            # 新建类别文件夹
            os.makedirs(test_path)
            # 加入类别名称列表
            class_names.append(filename)
    print('类别列表:', class_names)

    # 循环每个分类的文件夹
    for i in range(len(directories)):
        # 保存原始图片路径
        photo_filenames = []
        # 保存新训练集图片路径
        train_photo_filenames = []
        # 保存新测试集图片路径
        test_photo_filenames = []
        # 得到所有图片的路径
        for filename in os.listdir(directories[i]):
            # 原始图片路径
            path = os.path.join(directories[i], filename)
            # 训练图片路径
            train_path = os.path.join(train_directories[i], filename)
            # 测试集图片路径
            test_path = os.path.join(test_directories[i], filename)
            # 保存图片路径
            photo_filenames.append(path)
            train_photo_filenames.append(train_path)
            test_photo_filenames.append(test_path)
        # list转array
        photo_filenames = np.array(photo_filenames)
        train_photo_filenames = np.array(train_photo_filenames)
        test_photo_filenames = np.array(test_photo_filenames)
        # 打乱索引
        index = [i for i in range(len(photo_filenames))]
        random.shuffle(index)
        # 对3个list进行相同的打乱,保证在3个list中索引一致
        photo_filenames = photo_filenames[index]
        train_photo_filenames = train_photo_filenames[index]
        test_photo_filenames = test_photo_filenames[index]
        # 计算测试集数据个数
        test_sample_index = int((1 - num_test) * float(len(photo_filenames)))
        # 复制测试集图片
        for j in range(test_sample_index, len(photo_filenames)):
            # 复制图片
            shutil.copyfile(photo_filenames[j], test_photo_filenames[j])
        # 复制训练集图片
        for j in range(0, test_sample_index):
            # 复制图片
            shutil.copyfile(photo_filenames[j], train_photo_filenames[j])

# 打乱并切分数据集
shuffle_all_files(DATASET_DIR, NEW_DIR, num_test)

  切分之后:
在这里插入图片描述

5、ResNetRS50架构实现宝可梦图像识别

import tensorflow as tf
import numpy as np
from tensorflow.keras import layers
from tensorflow.keras.models import Model
from tensorflow.keras.applications import ResNetRS50
import math
import os
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.optimizers import Adam
import matplotlib.pyplot as plt
from tensorflow.keras.callbacks import LearningRateScheduler
from tensorflow.keras.models import Model

5.1 ResNetRS50架构

  这里去掉顶部的全连接层。
model=ResNetRS50(include_top=False,input_shape=(224,224,3))
model.summary()

image-20220917091212916

5.2 超参数与模型结构

# 类别数
num_classes = 5
# 批次大小
batch_size = 32
# 周期数
epochs=100
# 图片大小
image_size = 224

model=tf.keras.Sequential([
    model,
    layers.GlobalAveragePooling2D(),
    layers.Dense(num_classes,activation='softmax')
])
model.summary()

image-20220917091310191

5.3 数据增强

# 训练集数据进行数据增强
train_datagen = ImageDataGenerator(
    rotation_range=20,  # 随机旋转度数
    width_shift_range=0.1,  # 随机水平平移
    height_shift_range=0.1,  # 随机竖直平移
    rescale=1 / 255,  # 数据归一化
    shear_range=10,  # 随机错切变换
    zoom_range=0.1,  # 随机放大
    horizontal_flip=True,  # 水平翻转
    brightness_range=(0.7, 1.3),  # 亮度变化
    fill_mode='nearest',  # 填充方式
)
# 测试集数据只需要归一化就可以
test_datagen = ImageDataGenerator(
    rescale=1 / 255,  # 数据归一化
)

5.4 数据生成器

# 训练集数据生成器,可以在训练时自动产生数据进行训练
# 从'data/train'获得训练集数据
# 获得数据后会把图片resize为image_size×image_size的大小
# generator每次会产生batch_size个数据
train_generator = train_datagen.flow_from_directory(
    '../data/pokeman-dataset/train',
    target_size=(image_size, image_size), # 调整图像尺寸
    batch_size=batch_size,
)

# 测试集数据生成器
test_generator = test_datagen.flow_from_directory(
    '../data/pokeman-dataset/test',
    target_size=(image_size, image_size),
    batch_size=batch_size,
)
# 字典的键为17个文件夹的名字,值为对应的分类编号
print(train_generator.class_indices)

image-20220917091401404

5.5 callbacks

# 学习率调节函数,逐渐减小学习率
def adjust_learning_rate(epoch):
    # 前40周期
    if epoch<=30:
        lr = 1e-4
    # 前40到80周期
    elif 30 < epoch <= 80:
        lr = 1e-5
    # 80到100周期
    else:
        lr = 1e-6
    return lr

# 定义优化器
adam = Adam(learning_rate=1e-4)

# 读取模型
checkpoint_save_path = "./checkpoint/ResNetRS50-pokeman.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
    print('-------------load the model-----------------')
    model.load_weights(checkpoint_save_path)
# 保存模型
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
                                                 save_weights_only=True,
                                                 save_best_only=True)
# 早停
early_stop=tf.keras.callbacks.EarlyStopping(monitor='val_loss',patience=3,verbose=0)

# 定义学习率衰减策略
callbacks = []
callbacks.append(LearningRateScheduler(adjust_learning_rate))
# callbacks.append(early_stop)
callbacks.append(cp_callback)

5.6 compile&fit

# 定义优化器,loss function,训练过程中计算准确率
model.compile(optimizer=adam,loss='categorical_crossentropy',metrics=['accuracy'])

# Tensorflow2.1版本(包括2.1)之后可以直接使用fit训练模型
history = model.fit(x=train_generator,epochs=epochs,validation_data=test_generator,callbacks=callbacks)

image-20220917091514686

5.7 保存模型

# 保存模型
model.save('model/ResNetRS50-pokeman.h5')

5.8 acc和loss可视化

# 画出训练集准确率曲线图
plt.plot(np.arange(epochs),history.history['accuracy'],c='b',label='train_accuracy')
# 画出验证集准确率曲线图
plt.plot(np.arange(epochs),history.history['val_accuracy'],c='y',label='val_accuracy')
# 图例
plt.legend()
# x坐标描述
plt.xlabel('epochs')
# y坐标描述
plt.ylabel('accuracy')
# 显示图像
plt.show()

image-20220917091559190

# 画出训练集loss曲线图
plt.plot(np.arange(epochs),history.history['loss'],c='b',label='train_loss')
# 画出验证集loss曲线图
plt.plot(np.arange(epochs),history.history['val_loss'],c='y',label='val_loss')
# 图例
plt.legend()
# x坐标描述
plt.xlabel('epochs')
# y坐标描述
plt.ylabel('loss')
# 显示图像
plt.show()

image-20220917091617981

5.9 test

  这里随便用一些图片测试下这个模型

import tensorflow as tf
import numpy as np
import os
import matplotlib.pyplot as plt
from tensorflow.keras import layers
from tensorflow.keras.models import load_model

# from PIL import Image

model = load_model('model/ResNetRS50-pokeman.h5')
model.summary()

# 类别总数
dataset_dir = 'data/train'
classes = []
for filename in os.listdir(dataset_dir):
    classes.append(filename)
# print('classes:',classes)

# 预测单张图片
def predict_single_image(img_path):
    # string类型的tensor
    img = tf.io.read_file(img_path)
    # 将jpg格式转换为tensor
    img = tf.image.decode_jpeg(img, channels=3)
    # 数据归一化
    img = tf.image.convert_image_dtype(img, dtype=tf.float32)
    # resize
    img = tf.image.resize(img, size=[224, 224])
    # 扩充一个维度
    img = np.expand_dims(img, axis=0)

    # 预测:结果是二维的
    test_result = model.predict(img)
    # print('test_result:', test_result)
    # 转化为一维
    result = np.squeeze(test_result)
    # print('转化后result:', result)

    # 找到概率值最大的索引
    predict_class = np.argmax(result)
    # print('概率值最大的索引:', predict_class)

    # 返回类别和所属类别的概率
    return classes[int(predict_class)], result[predict_class]

# 对整个文件夹的图片进行预测
def predict_directory(file_path):
    classes_pred=[]
    classes_true=[]
    probs=[]
    for file in os.listdir(file_path):
        # 测试图片完整路径
        file_dir=os.path.join(file_path,file)
        # 打印文件路径
        print(file_dir)
        # 传入文件路径进行预测
        preds,prob=predict_single_image(file_dir)

        # 取出图片的真实标签(这里直接将文件夹名称作为真实标签值了)
        # label_true=file.split('_')[0].title()
        label_true = file_dir.split('\\')[0].split('/')[-1]
        # 保存真实值和预测值结果
        classes_true.append(label_true)
        classes_pred.append(preds)
        probs.append(prob)
    return classes_pred,classes_true,probs

# img_path = 'Gemstones/train/Almandine/almandine_0.jpg'
# classes, prob = predict_single_image(img_path)
# print(classes, prob)

file_path= 'data/test/bulbasaur'
classes_pred,classes_true,probs=predict_directory(file_path)
print(classes_pred)
print(classes_true)
print(probs)

image-20220917102034556

  效果还是不错的。
相关实践学习
基于函数计算实现AI推理
本场景基于函数计算建立一个TensorFlow Serverless AI推理平台。
目录
相关文章
|
2天前
|
机器学习/深度学习 固态存储 计算机视觉
深度学习在图像识别中的应用与挑战
【5月更文挑战第6天】 随着科技的飞速发展,深度学习技术在图像识别领域取得了显著的成果。本文将探讨深度学习在图像识别中的应用,分析其优势和面临的挑战。我们将介绍卷积神经网络(CNN)的基本概念,以及如何利用深度学习进行图像分类、目标检测和语义分割等任务。此外,我们还将讨论数据不平衡、过拟合和计算资源等挑战,以及如何解决这些问题。
|
11天前
|
机器学习/深度学习 计算机视觉
深度学习在图像识别中的应用及挑战
【4月更文挑战第28天】 随着人工智能技术的飞速发展,深度学习已成为图像识别领域的核心驱动力。本文将探讨深度学习技术在图像识别任务中的关键应用,并剖析当前面临的主要技术挑战。我们将重点讨论卷积神经网络(CNN)的架构优化、数据增强的策略以及对抗性网络的进展。同时,文中也将提及硬件限制、过拟合问题和模型泛化能力等现实挑战。通过分析这些问题,我们期望为未来研究提供方向性的思考。
|
1月前
|
机器学习/深度学习 监控 算法
深度学习在图像识别中的应用及其挑战
【4月更文挑战第8天】 随着人工智能技术的飞速发展,深度学习已经成为了计算机视觉领域的核心动力,特别是在图像识别任务中展现出了卓越的性能。本文章将深入探讨深度学习模型在图像识别中的应用,包括卷积神经网络(CNN)的基础架构、训练策略以及优化方法。同时,我们也将审视当前技术面临的主要挑战,诸如数据偏差问题、对抗性攻击的防御、以及模型泛化能力的提升等。本文旨在为读者提供一个关于深度学习在图像识别领域的全面视角,并对未来发展趋势进行展望。
|
2月前
|
机器学习/深度学习 自动驾驶 算法
探索深度学习在图像识别中的应用与挑战
【2月更文挑战第23天】 随着人工智能技术的迅猛发展,深度学习已成为推动计算机视觉领域进步的核心技术之一。本文旨在深入剖析深度学习技术在图像识别任务中的应用及其面临的挑战。首先,文中概述了深度学习的基础理论和关键技术,包括卷积神经网络(CNN)和循环神经网络(RNN)。其次,通过实例分析,展示了深度学习在图像分类、目标检测和语义分割等方面的应用成效。接着,讨论了数据偏差、模型泛化能力不足、计算资源限制等现实问题对深度学习在图像识别中的影响。最后,探讨了未来可能的研究方向和技术趋势,为该领域的研究提供参考。
|
机器学习/深度学习 编解码 BI
RegNet架构复现--CVPR2020
在这项工作中,我们**提出了一种新的网络设计范式**。我们的目标是帮助促进对网络设计的理解,并发现跨环境通用的设计原则。我们不是专注于设计单个网络实例,而是设计参数化网络群体的网络设计空间。整个过程类似于经典的网络手动设计,但提升到了设计空间级别。使用我们的方法,我们探索了网络设计的结构方面,并**得出了一个由简单、规则的网络组成的低维设计空间,我们称之为** ==RegNet==。
993 0
RegNet架构复现--CVPR2020
|
机器学习/深度学习 编解码 数据挖掘
ResNet-RS架构复现--CVPR2021
我们的工作重新审视了规范的 ResNet (He et al., 2015),并研究了这三个方面,以试图解开它们。也许令人惊讶的是,我们发现训练和扩展策略可能比架构变化更重要,而且由此产生的 ResNet 与最近最先进的模型相匹配。**我们展示了表现最佳的缩放策略取决于训练方案,并提供了两种新的缩放策略:(1)在可能发生过度拟合的情况下缩放模型深度(否则宽度缩放更可取); (2) 提高图像分辨率的速度比之前推荐的要慢(Tan & Le,2019)**。使用改进的训练和扩展策略,我们设计了一系列 ResNet 架构 **ResNet-RS**,它比 TPU 上的 EfficientNets 快
398 0
ResNet-RS架构复现--CVPR2021
|
11月前
|
机器学习/深度学习 程序员 API
【深度学习】基于卷积神经网络的验证码识别
【深度学习】基于卷积神经网络的验证码识别
191 0
|
12月前
|
编解码 人工智能 安全
安卓手机「跑」Stable Diffusion创新纪录,15秒内出图
安卓手机「跑」Stable Diffusion创新纪录,15秒内出图
678 0
|
Python
完美解决 TypeError: Invalid shape (3, 224, 224) for image data
完美解决 TypeError: Invalid shape (3, 224, 224) for image data
623 0
|
PyTorch 算法框架/工具
使用transformer的YOLOv7及TensorRT部署
使用transformer的YOLOv7及TensorRT部署
使用transformer的YOLOv7及TensorRT部署