基于ResNetRS的宝可梦图像识别

本文涉及的产品
视觉智能开放平台,视频资源包5000点
视觉智能开放平台,图像资源包5000点
视觉智能开放平台,分割抠图1万点
简介: ResNetRS是在ResNet-D架构上面的改进

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

  效果还是不错的。
目录
相关文章
|
6天前
|
机器学习/深度学习 计算机视觉
深度学习在图像识别中的突破与挑战####
本文深入探讨了深度学习技术在图像识别领域的最新进展,重点分析了卷积神经网络(CNN)的架构创新、生成对抗网络(GANs)的应用拓展以及自监督学习策略的兴起。通过综述近年来的研究成果,本文揭示了深度学习如何不断推动图像识别技术的边界,同时指出了当前面临的主要挑战,包括数据偏差、模型泛化能力及计算资源依赖等问题,并展望了未来的发展方向。 ####
23 2
|
7天前
|
机器学习/深度学习 传感器 人工智能
深度学习在图像识别中的应用与挑战
本文探讨了深度学习技术在图像识别领域的应用,分析了其在提高图像处理效率、准确性方面的贡献,同时指出了面临的数据依赖性、模型泛化能力不足等挑战。通过具体案例分析,文章展示了深度学习如何推动图像识别技术的发展,并对未来可能的研究方向进行了展望。
31 3
|
13天前
|
机器学习/深度学习 人工智能 计算机视觉
深度学习在图像识别中的应用与挑战####
本文深入探讨了深度学习技术在图像识别领域的革命性进展,特别是卷积神经网络(CNN)的架构创新、优化策略及面临的挑战。通过综述经典CNN架构如AlexNet、VGG、ResNet的发展历程,揭示了深度学习如何不断突破性能瓶颈,实现图像识别准确率的飞跃。文章还详细阐述了数据增强、迁移学习等策略在提升模型泛化能力方面的关键作用,并讨论了过拟合、计算资源依赖等核心挑战,为未来研究提供了方向指引。 ####
|
4月前
|
机器学习/深度学习 自动驾驶 算法框架/工具
深度学习在图像识别中的应用及其挑战
【8月更文挑战第30天】本文将深入探讨深度学习技术在图像识别领域的应用,并分析其面临的主要挑战。我们将通过实例展示深度学习模型如何提高图像处理的准确率,并讨论数据偏差、模型泛化等问题对性能的影响。文章旨在为读者提供深度学习技术的全面视角,同时指出未来研究的方向。
|
5月前
|
机器学习/深度学习 人工智能 算法
探索深度学习在图像识别中的应用及其挑战
本文深入探讨了深度学习技术在图像识别领域的应用,分析了其背后的原理、当前的研究进展以及面临的主要挑战。通过对比传统图像处理方法,我们展示了深度学习如何提高识别准确率和效率。同时,本文还讨论了数据偏差、模型泛化能力等关键问题,并提出了未来研究的可能方向。
|
6月前
|
机器学习/深度学习 人工智能 监控
深度学习在图像识别中的应用与挑战
【6月更文挑战第25天】 本文深入探讨了深度学习技术,尤其是卷积神经网络(CNN)在图像识别领域的应用及其面临的挑战。通过分析深度学习模型在处理复杂图像数据时的强大能力,以及在实际部署中遇到的困难,如过拟合、数据集偏差和计算资源限制,本文旨在为读者提供一个关于如何优化模型性能和克服技术难题的全面视角。
|
5月前
|
机器学习/深度学习 算法 计算机视觉
探索深度学习在图像识别中的应用及挑战
本文深入探讨了深度学习技术在图像识别领域的应用,并分析了其面临的主要挑战。通过实例和数据分析,本文旨在揭示深度学习如何推动图像识别技术的发展,同时指出当前技术的局限性和未来的发展方向。
36 0
|
5月前
|
机器学习/深度学习 监控 自动驾驶
深度学习在图像识别中的应用及挑战
随着人工智能技术的不断进步,深度学习已成为推动图像识别领域发展的核心动力。本文将深入分析深度学习在图像识别中的应用,包括其技术原理、成功案例以及面临的主要挑战。通过具体数据和案例的支撑,本文旨在提供一个全面且深入的视角,帮助读者理解深度学习如何革新了图像识别领域,并探讨未来的发展方向。
45 0
|
6月前
|
机器学习/深度学习 自动驾驶 算法
探索深度学习在图像识别中的应用与挑战
深度学习技术已经成为图像识别领域的主导力量,通过模拟人脑处理信息的方式,它已经实现了对复杂图像数据的高效处理。然而,尽管取得了显著进展,深度学习在图像识别上的应用仍面临数据依赖性、模型泛化能力不足等挑战。本文将深入探讨深度学习在图像识别方面的应用实例和存在的挑战,并展望未来可能的发展方向。
|
7月前
|
机器学习/深度学习 边缘计算 安全
深度学习在图像识别中的应用和挑战
【5月更文挑战第14天】 随着人工智能的飞速发展,深度学习技术已成为推动计算机视觉领域革新的主要动力。本文将深入探讨深度学习在图像识别任务中的关键应用,并剖析当前面临的技术挑战与潜在解决方案。我们将从基础原理出发,透过案例分析,探索卷积神经网络(CNN)的优化策略、数据增强的重要性以及对抗性网络的创新应用。同时,文章也将关注模型泛化能力、计算效率及安全性问题,旨在为读者提供一个关于深度学习在图像识别领域的综合性视角。