使用VGG-16模型来对海贼王中的角色进行图像分类

动漫角色识别是计算机视觉的典型应用场景,可用于周边商品分类、动画制作辅助等。

这个案例是一个经典的深度学习应用,用于图像分类任务,它使用了一个自定义的VGG-16模型来对《海贼王》中的七个角色进行分类,演示如何将经典CNN模型应用于小规模自定义数据集。

1. 数据集准备

数据集包含7个类别的图片,每个类别对应一个《海贼王》的角色:

  • 路飞(lufei)
  • 罗宾(luobin)
  • 娜美(namei)
  • 乔巴(qiaoba)
  • 山治(shanzhi)
  • 索隆(suolong)
  • 乌索普(wusuopu)

每个角色有不同数量的图片,总共621张图片。

(1)导入必要的库和设置随机种子

python 复制代码
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, models
import pathlib
import matplotlib.pyplot as plt
import os, PIL

np.random.seed(1)
tf.random.set_seed(1)
#导入所需的Python库,并设置随机种子以确保实验的可重复性。

(2)设置数据目录和参数

python 复制代码
data_dir = r"D:\hzw_photos"
data_dir = pathlib.Path(data_dir)
image_count = len(list(data_dir.glob('*/*.png')))
print("图片总数为:", image_count)

batch_size = 32
img_height = 224
img_width = 224

#作用:指定数据集路径、统计图片总数,并定义批量大小和图片尺寸。
#运行结果:输出图片总数(621张)。

(3)加载训练集和验证集

python 复制代码
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split=0.2,
    subset="training",
    seed=123,
    image_size=(img_height, img_width),
    batch_size=batch_size
)

val_ds = tf.keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split=0.2,
    subset="validation",
    seed=123,
    image_size=(img_height, img_width),
    batch_size=batch_size
)
#作用:从指定目录加载图像数据集,并将其分为训练集和验证集。validation_split=0.2表示20%的数据用于验证,其余80%用于训练。
#运行结果:打印出找到的文件数量和类别信息。

(4)获取类别名称

python 复制代码
class_names = train_ds.class_names
print(class_names)
#作用:获取并打印数据集中所有类别的名称。
#运行结果:输出类别名称列表:['lufei', 'luobin', 'namei', 'qiaoba', 'shanzhi', 'suolong', 'wusuopu']。

(5)可视化数据

python 复制代码
plt.figure(figsize=(10, 5))
for images, labels in train_ds.take(1):
    for i in range(8):
        ax = plt.subplot(2, 4, i + 1)
        plt.imshow(images[i].numpy().astype("uint8"))
        plt.title(class_names[labels[i]])
        plt.axis("off")
plt.show()
#作用:从训练集中随机选取一批图像进行可视化展示。
#运行结果:显示8张随机选择的图像及其对应的标签。

  1. 数据预处理

(1)配置数据集

python 复制代码
AUTOTUNE = tf.data.AUTOTUNE
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
#作用:对数据集进行缓存、打乱和预取操作,以提高数据读取效率。
#运行结果:无直接输出,但优化了数据加载过程。

(2)归一化处理

python 复制代码
normalization_layer = tf.keras.layers.Rescaling(1. / 255)
train_ds = train_ds.map(lambda x, y: (normalization_layer(x), y))
val_ds = val_ds.map(lambda x, y: (normalization_layer(x), y))

image_batch, labels_batch = next(iter(val_ds))
first_image = image_batch[0]
print("归一化后数据范围:", np.min(first_image), np.max(first_image))
#作用:将图像像素值归一化到[0, 1]区间内。
#运行结果:输出归一化后的数据范围(0.0到0.9928046),表明归一化操作成功。

3. 模型构建

VGG-16 是一种经典的卷积神经网络(CNN)架构,通过堆叠多个卷积层和池化层来提取图像特征,最后通过全连接层进行分类。以其简单的结构和深度而闻名,尤其在图像分类任务中表现出色。

定义VGG-16模型

python 复制代码
def VGG16(nb_classes, input_shape):
    # 构建VGG-16模型...
    return model
#作用:定义一个自定义的VGG-16模型,包括多个卷积层和全连接层。
#运行结果:无直接输出,但生成了一个可以使用的模型结构。

(1) 输入层

python 复制代码
input_tensor = layers.Input(shape=input_shape)
#input_shape  输入图像的形状,通常为 (height, width, channels),例如 (224, 224, 3) 表示 224x224 像素的 RGB 图像
#Input 层定义了模型的输入张量。

(2)卷积块 1

python 复制代码
x = layers.Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1')(input_tensor)
x = layers.Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2')(x)
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool')(x)
#两个 Conv2D 层,每个层使用 64 个 3x3 的卷积核,激活函数为 ReLU,padding='same' 表示输出特征图的大小与输入相同。
#MaxPooling2D 层使用 2x2 的池化窗口,步幅为 2,将特征图的大小减半。
#输入图像(224×224×3)
#→ 经过64个3×3卷积核提取特征(输出224×224×64)
#→ 再次卷积增强特征(保持尺寸)
#→ 2×2最大池化(输出112×112×64)

(3)卷积块 2

python 复制代码
x = layers.Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1')(x)
x = layers.Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv2')(x)
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name='block2_pool')(x)
#与第一个卷积块类似,但卷积核数量增加到 128。

(4) 卷积块 3

python 复制代码
x = layers.Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv1')(x)
x = layers.Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv2')(x)
x = layers.Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv3')(x)
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name='block3_pool')(x)
#卷积核数量增加到 256,并且有三个卷积层。

(5)卷积块 4

python 复制代码
x = layers.Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv1')(x)
x = layers.Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv2')(x)
x = layers.Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv3')(x)
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name='block4_pool')(x)
#卷积核数量增加到 512,同样有三个卷积层。

(6)卷积块 5

python 复制代码
x = layers.Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv1')(x)
x = layers.Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv2')(x)
x = layers.Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv3')(x)
x = layers.MaxPooling2D((2, 2), strides=(2, 2), name='block5_pool')(x)
#与卷积块 4 相同,卷积核数量保持为 512。
块编号 CONV层数量 输出尺寸 通道数 作用
1 2 112×112 64 提取边缘/颜色等低级特征
2 2 56×56 128 捕获纹理/简单形状
3 3 28×28 256 识别复杂图案(如草帽轮廓)
4 3 14×14 512 检测角色局部特征(娜美的头发等)
5 3 7×7 512 整合全局语义信息

(7) 全连接层

python 复制代码
x = layers.Flatten()(x)
x = layers.Dense(4096, activation='relu', name='fc1')(x)
x = layers.Dense(4096, activation='relu', name='fc2')(x)
output_tensor = layers.Dense(nb_classes, activation='softmax', name='predictions')(x)
#Flatten 层将多维特征图展平为一维向量。
#两个 Dense 层,每个层有 4096 个神经元,激活函数为 ReLU。
#最后的 Dense 层输出类别概率,使用 softmax 激活函数,nb_classes 是类别数量。

(8)构建模型

python 复制代码
model = models.Model(input_tensor, output_tensor)
#使用 Model 类将输入张量和输出张量组合成模型。

(9)初始化模型

python 复制代码
model = VGG16(nb_classes=7, input_shape=(img_width, img_height, 3))
model.summary()
#作用:初始化VGG-16模型,并打印模型结构摘要。
#运行结果:输出模型各层的详细信息,包括层名、输出形状和参数数量等。

输入图像 → [CONV→POOL]×5 → 展平 → FC×2 → 分类输出

(特征提取) (决策)

  1. 模型编译与训练

编译模型

python 复制代码
opt = tf.keras.optimizers.Adam(learning_rate=1e-4)
model.compile(
    optimizer=opt,
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
    metrics=['accuracy']
)
#作用:配置模型的优化器、损失函数和评估指标。

训练模型

python 复制代码
history = model.fit(
    train_ds,
    validation_data=val_ds,
    epochs=epochs
)
#作用:在训练集上训练模型,并在每个epoch结束后评估验证集上的性能。
#运行结果:输出每个epoch的训练准确率、训练损失、验证准确率和验证损失。

5. 结果分析

python 复制代码
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']

plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(range(epochs), acc, label='Training Accuracy')
plt.plot(range(epochs), val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(range(epochs), loss, label='Training Loss')
plt.plot(range(epochs), val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
#作用:绘制训练和验证过程中的准确率及损失变化曲线。
#运行结果:显示两个子图,分别表示准确率和损失的变化趋势。

  • 训练准确率验证准确率都随着训练轮数的增加而上升,表明模型逐渐学习到了数据的特征。
  • 训练损失验证损失则逐渐下降,说明模型的预测误差在减小。
python 复制代码
图片总数为: 621
Found 621 files belonging to 7 classes.
Using 497 files for training.
Found 621 files belonging to 7 classes.
Using 124 files for validation.
['lufei', 'luobin', 'namei', 'qiaoba', 'shanzhi', 'suolong', 'wusuopu']
归一化后数据范围: 0.0 0.9928046
Model: "functional"
┌─────────────────────────────────┬────────────────────────┬───────────────┐
│ Layer (type)                    │ Output Shape           │       Param # │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ input_layer (InputLayer)        │ (None, 224, 224, 3)    │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block1_conv1 (Conv2D)           │ (None, 224, 224, 64)   │         1,792 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block1_conv2 (Conv2D)           │ (None, 224, 224, 64)   │        36,928 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block1_pool (MaxPooling2D)      │ (None, 112, 112, 64)   │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block2_conv1 (Conv2D)           │ (None, 112, 112, 128)  │        73,856 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block2_conv2 (Conv2D)           │ (None, 112, 112, 128)  │       147,584 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block2_pool (MaxPooling2D)      │ (None, 56, 56, 128)    │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block3_conv1 (Conv2D)           │ (None, 56, 56, 256)    │       295,168 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block3_conv2 (Conv2D)           │ (None, 56, 56, 256)    │       590,080 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block3_conv3 (Conv2D)           │ (None, 56, 56, 256)    │       590,080 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block3_pool (MaxPooling2D)      │ (None, 28, 28, 256)    │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block4_conv1 (Conv2D)           │ (None, 28, 28, 512)    │     1,180,160 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block4_conv2 (Conv2D)           │ (None, 28, 28, 512)    │     2,359,808 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block4_conv3 (Conv2D)           │ (None, 28, 28, 512)    │     2,359,808 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block4_pool (MaxPooling2D)      │ (None, 14, 14, 512)    │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block5_conv1 (Conv2D)           │ (None, 14, 14, 512)    │     2,359,808 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block5_conv2 (Conv2D)           │ (None, 14, 14, 512)    │     2,359,808 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block5_conv3 (Conv2D)           │ (None, 14, 14, 512)    │     2,359,808 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ block5_pool (MaxPooling2D)      │ (None, 7, 7, 512)      │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ flatten (Flatten)               │ (None, 25088)          │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ fc1 (Dense)                     │ (None, 4096)           │   102,764,544 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ fc2 (Dense)                     │ (None, 4096)           │    16,781,312 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ predictions (Dense)             │ (None, 7)              │        28,679 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 134,289,223 (512.27 MB)
 Trainable params: 134,289,223 (512.27 MB)
 Non-trainable params: 0 (0.00 B)
Epoch 1/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 207s 13s/step - accuracy: 0.1736 - loss: 1.9444 - val_accuracy: 0.1935 - val_loss: 1.9390
Epoch 2/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 216s 14s/step - accuracy: 0.1594 - loss: 1.9357 - val_accuracy: 0.1935 - val_loss: 1.9355
Epoch 3/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 195s 12s/step - accuracy: 0.1656 - loss: 1.9362 - val_accuracy: 0.1935 - val_loss: 1.9293
Epoch 4/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 208s 13s/step - accuracy: 0.1749 - loss: 1.9240 - val_accuracy: 0.1452 - val_loss: 1.9054
Epoch 5/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 214s 13s/step - accuracy: 0.1787 - loss: 1.8650 - val_accuracy: 0.2419 - val_loss: 1.6483
Epoch 6/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 202s 13s/step - accuracy: 0.3046 - loss: 1.5703 - val_accuracy: 0.3306 - val_loss: 1.6471
Epoch 7/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 198s 13s/step - accuracy: 0.4451 - loss: 1.4282 - val_accuracy: 0.5403 - val_loss: 1.1161
Epoch 8/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 179s 11s/step - accuracy: 0.6045 - loss: 1.0102 - val_accuracy: 0.5081 - val_loss: 1.0964
Epoch 9/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 166s 11s/step - accuracy: 0.6386 - loss: 0.9255 - val_accuracy: 0.6935 - val_loss: 0.8652
Epoch 10/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 162s 10s/step - accuracy: 0.7404 - loss: 0.6550 - val_accuracy: 0.6290 - val_loss: 0.9989
Epoch 11/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 185s 11s/step - accuracy: 0.8052 - loss: 0.5281 - val_accuracy: 0.6855 - val_loss: 0.9217
Epoch 12/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 185s 12s/step - accuracy: 0.8136 - loss: 0.4523 - val_accuracy: 0.6613 - val_loss: 1.0901
Epoch 13/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 193s 12s/step - accuracy: 0.8089 - loss: 0.4674 - val_accuracy: 0.6935 - val_loss: 0.7750
Epoch 14/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 192s 12s/step - accuracy: 0.8577 - loss: 0.3848 - val_accuracy: 0.7339 - val_loss: 0.8414
Epoch 15/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 185s 12s/step - accuracy: 0.9164 - loss: 0.2603 - val_accuracy: 0.7419 - val_loss: 1.2181
Epoch 16/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 223s 14s/step - accuracy: 0.8789 - loss: 0.4077 - val_accuracy: 0.7258 - val_loss: 0.9584
Epoch 17/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 236s 15s/step - accuracy: 0.9123 - loss: 0.2405 - val_accuracy: 0.7419 - val_loss: 1.2041
Epoch 18/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 236s 15s/step - accuracy: 0.9637 - loss: 0.1224 - val_accuracy: 0.7339 - val_loss: 1.9659
Epoch 19/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 226s 14s/step - accuracy: 0.9677 - loss: 0.0793 - val_accuracy: 0.7339 - val_loss: 1.4271
Epoch 20/20
16/16 ━━━━━━━━━━━━━━━━━━━━ 224s 14s/step - accuracy: 0.9548 - loss: 0.1205 - val_accuracy: 0.7581 - val_loss: 1.4689
#指标	训练集变化	            验证集变化	            结论
#准确率	17.4% → 95.5% (↑78.1%)	19.4% → 75.8% (↑56.4%)	模型学习有效,但存在明显过拟合
#损失值	1.94 → 0.12 (↓93.8%)	1.94 → 1.47 (↓24.2%)	训练损失下降过快,验证损失震荡

进程已结束,退出代码为 0

6.从深度学习展开分析

(1)特征学习的革命性突破

传统方法 vs 深度学习

  • 传统CV方案

    需要手工设计特征(如HOG描述子、颜色直方图),但对于动漫人物:

    草帽、发型等特征难以用数学公式描述 不同姿势/角度下特征稳定性差

  • 深度学习方案

    VGG-16通过卷积层自动学习层次化特征:

底层特征(前几层):边缘/颜色 → 识别路飞的草帽红色边缘

中层特征:纹理/部件 → 组合出索隆的三把刀轮廓

高层特征:全局语义 → 理解"娜美的橘色头发+身体比例"这种复合特征

(2)处理图像数据的先天优势

  1. 空间不变性

    • 通过卷积核共享机制,无论路飞出现在图像左上角还是右下角都能被识别

    • 池化层使模型对小幅位移/旋转具有鲁棒性(适合动漫截图角度多变的特点)

  2. 通道维度理解

    • RGB三通道自动提取色彩特征(如乔巴的粉色帽子、山治的金发)

    • 相比灰度图,保留关键颜色线索

  3. 感受野递进

    • 从3×3小窗口逐步扩大到全图感知(最终7×7的特征图对应原图约200×200像素区域)

    • 这种机制天然适配"从局部到整体"的认知逻辑

(3)端到端训练的便捷性

传统流程
图像预处理 → 特征工程 → 分类器设计 → 结果优化(需分步调试)

深度学习流程
原始图片输入 → VGG网络 → 分类结果

所有优化自动完成

(4)针对动漫数据的特殊适配能力

  1. 风格化特征处理

    • 动漫人物线条鲜明、用色大胆,与真实照片差异大

    • CNN通过多层非线性变换,能更好捕捉这种艺术化表达

  2. 跨角色泛化

    • 即使训练集没有"戴草帽的罗宾"这类异常组合,模型也能通过:

      • 低层学到的"草帽特征"

      • 高层学到的"罗宾面部特征"

    • 组合推理出未知变体(比传统方法更具泛化潜力)

(5)延伸应用场景

这套技术方案稍加调整即可用于:

  1. 动漫产业:自动标注动画分镜中的人物出场

  2. 游戏开发:玩家上传截图自动识别角色阵容

  3. 周边电商:拍照搜索手办/服饰对应的

七、总结

在本案例中,深度学习的作用本质是:
通过多层非线性变换,自动从像素中学习到海贼王角色的抽象特征表达,并建立这些特征与人物类别的映射关系。其价值不在于替代人类认知,而是将人类难以显式描述的视觉模式(比如"如何定义乔巴的可爱感")编码成可优化的数学表示。未来结合注意力机制等新技术,还可进一步接近人类的分辨能力。

相关推荐
MARS_AI_15 分钟前
云蝠语音智能体——电话面试中的智能助手
人工智能·自然语言处理·面试·职场和发展·交互·信息与通信
whaosoft-14318 分钟前
w~大模型~合集4
人工智能
Mr.小海22 分钟前
AI 商业化部署中,ollama 和 vllm 的选型对比
人工智能·大模型
合方圆~小文28 分钟前
架空线路监控系统是针对高压架空输电线路设计的一种安全监测解决方案
c语言·人工智能·硬件工程·数据库架构
SatoshiGogo33 分钟前
李宏毅《机器学习2025》笔记 —— 更新中
人工智能·笔记
张彦峰ZYF35 分钟前
推进可解释人工智能迈向类人智能讨论总结分享
人工智能
一年春又来40 分钟前
AI-02a5a8.神经网络-与学习相关的技巧-超参数的验证
人工智能·神经网络·学习
程序猿阿伟1 小时前
《边缘算力困局突破:智能体模型动态调度全解析》
人工智能
Panesle1 小时前
ten-vad:低延迟、轻量化且高性能的流式语音活动检测系统
人工智能·语言模型·ffmpeg·开源·音视频·语音识别
东临碣石821 小时前
【AI论文】UniVG-R1:基于强化学习的推理引导通用视觉定位
人工智能