文章目录
- **T6周:好莱坞明星识别**
- **一、前期工作**
- 1.设置GPU(用CPU可忽略该步骤)
- 2.导入数据
- 3.查看数据
- **二、数据预处理**
- 1.加载数据
- 2.可视化数据
- 3.配置数据集
- **三、构建CNN网络模型**
- **四、编译模型**
- **五、训练模型**
- **六、模型评估**
- **七、预测**
- 八、总结(临时)
T6周:好莱坞明星识别
- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊
🍺 要求:
- 使用categorical_crossentropy(多分类的对数损失函数)完成本次选题
- 探究不同损失函数的使用场景与代码实现
🍻 拔高(可选):
- 自己搭建VGG-16网络框架
- 调用官方的VGG-16网络框架
- 使用VGG-16算法训练该模型
🔎 探索(难度有点大)
- 准确率达到60%
⛽ 我的环境
- 语言环境:Python3.10.12
- 编译器:Google Colab
- 深度学习环境:
- TensorFlow2.17.0
⛽ 参考博客:
- 深度学习 Day6——T6好莱坞明星识别
一、前期工作
1.设置GPU(用CPU可忽略该步骤)
#os提供了一些与操作系统交互的功能,比如文件和目录操作
import os
#提供图像处理的功能,包括打开和显示、保存、裁剪等
import PIL
#pathlib提供了一个面向对象的接口来处理文件系统路径。路径被表示为Path对象,可以调用方法来进行各种文件和目录操作。
import pathlib
#用于绘制图形和可视化数据
import tensorflow as tf
import matplotlib.pyplot as plt
#用于数值计算的库,提供支持多维数组和矩阵运算
import numpy as np
#keras作为高层神经网络API,已被集成进tensorflow,使得训练更方便简单
from tensorflow import keras
#layers提供了神经网络的基本构建块,比如全连接层、卷积层、池化层等
#提供了构建和训练神经网络模型的功能,包括顺序模型(Sequential)和函数式模型(Functional API)
from tensorflow.keras import layers, models
#导入两个重要的回调函数:前者用于训练期间保存模型最佳版本;后者监测到模型性能不再提升时提前停止训练,避免过拟合
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
# 获取所有可用的GPU设备列表,储存在变量gpus中
gpus = tf.config.list_physical_devices("GPU")
# 如果有GPU,即列表不为空
if gpus:
# 获取第一个 GPU 设备
gpu0 = gpus[0]
# 设置 GPU 内存增长策略。开启这个选项可以让tf按需分配gpu内存,而不是一次性分配所有可用内存。
tf.config.experimental.set_memory_growth(gpu0, True)
#设置tf只使用指定的gpu(gpu[0])
tf.config.set_visible_devices([gpu0],"GPU")
gpus
[PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]
2.导入数据
data_dir = "/content/drive/Othercomputers/My laptop/jupyter notebook/data/T6"
data_dir = pathlib.Path(data_dir)
3.查看数据
# 使用glob方法获取当前目录的子目录里所有以'.jpg'为结尾的文件
# '*/*.jpg' 是一個通配符模式
# 第一个星号表示当前目录
# 第二个星号表示子目录
image_count = len (list(data_dir.glob("*/*.jpg")))
print("图片总数:", image_count)
图片总数: 1800
ex = list(data_dir.glob("Jennifer Lawrence/*.jpg"))
image=PIL.Image.open(str(ex[5]))
#查看图像属性
print(image.format, image.size,image.mode)
plt.axis("off")
plt.imshow(image)
plt.show()
JPEG (474, 577) RGB
二、数据预处理
1.加载数据
#设置批量大小,即每次训练模型时输入图像数量
#每次训练迭代时,模型需处理32张图像
batch_size = 32
#图像的高度,加载图像数据时,将所有的图像调整为相同的高度
img_height = 224
#图像的宽度,加载图像数据时,将所有的图像调整为相同的宽度
img_width = 224
"""
关于image_dataset_from_directory()的详细介绍可以参考文章:https://mtyjkh.blog.csdn.net/article/details/117018789
"""
tr_ds = tf.keras.preprocessing.image_dataset_from_directory(
data_dir,
validation_split=0.1,
#指定数据集中分割出多少比例数据当作验证集,0.1表示10%数据会被用来当验证集
subset="training",
#指定是用于训练还是验证的数据子集,这里设定为training
label_mode = "categorical",
#标签编码为分类向量,独热编码数组--使用损失函数应为--categorical_crossentropy loss
seed=123,
#用于设置随机数种子,以确保数据集划分的可重复性和一致性
image_size=(img_height, img_width),
batch_size=batch_size)
Found 1800 files belonging to 17 classes.
Using 1620 files for training.
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
data_dir,
validation_split = 0.1,
subset = "validation",
label_mode="categorical",
seed = 123,
image_size=(img_height,img_width),
batch_size=batch_size
)
Found 1800 files belonging to 17 classes.
Using 180 files for validation.
class_names = tr_ds.class_names
# 可以通过class_names输出数据集的标签。标签将按字母顺序对应于目录名称
class_names
['Angelina Jolie',
'Brad Pitt',
'Denzel Washington',
'Hugh Jackman',
'Jennifer Lawrence',
'Johnny Depp',
'Kate Winslet',
'Leonardo DiCaprio',
'Megan Fox',
'Natalie Portman',
'Nicole Kidman',
'Robert Downey Jr',
'Sandra Bullock',
'Scarlett Johansson',
'Tom Cruise',
'Tom Hanks',
'Will Smith']
随机数种子相关可参考:https://blog.csdn.net/weixin_51390582/article/details/124246873
2.可视化数据
plt.figure(figsize=(20,10))
for images, labels in tr_ds.take(1):
for i in range(20):
ax = plt.subplot(5, 10, i + 1)
plt.imshow(images[i].numpy().astype("uint8"))
plt.title(class_names[np.argmax(labels[i])], fontsize=10)
plt.axis("off")
# 显示图片
plt.show()
for image_batch, labels_batch in tr_ds:
print(image_batch.shape)
print(labels_batch.shape)
break
#`(32, 224, 224, 3)`--最后一维指的是彩色通道RGB
#`label_batch`是形状(32,17)的张量,这些标签对应32张图片,分为17类
(32, 224, 224, 3)
(32, 17)
3.配置数据集
#自动调整数据管道性能
AUTOTUNE = tf.data.AUTOTUNE
# 使用 tf.data.AUTOTUNE 具体的好处包括:
#自动调整并行度:自动决定并行处理数据的最佳线程数,以最大化数据吞吐量。
#减少等待时间:通过优化数据加载和预处理,减少模型训练时等待数据的时间。
#提升性能:自动优化数据管道的各个环节,使整个训练过程更高效。
#简化代码:不需要手动调整参数,代码更简洁且易于维护。
#使用cache()方法将训练集缓存到内存中,这样加快数据加载速度
#当多次迭代训练数据时,可以重复使用已经加载到内存的数据而不必重新从磁盘加载
#使用shuffle()对训练数据集进行洗牌操作,打乱数据集中的样本顺序
#参数1000指缓冲区大小,即每次从数据集中随机选择的样本数量
#prefetch()预取数据,节约在训练过程中数据加载时间
tr_ds = tr_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
三、构建CNN网络模型
卷积神经网络(CNN)的输入是张量 (Tensor) 形式的 (image_height, image_width, color_channels)
,包含了图像高度、宽度及颜色信息。不需要输入batch size
。color_channels 为 (R,G,B) 分别对应 RGB 的三个颜色通道(color channel)。在此示例中,我们的 CNN 输入形状是 (224, 224, 3)
。我们需要在声明第一层时将形状赋值给参数input_shape。
CNN的输入张量表示图像的结构和颜色信息。每个像素点都被表示为具有color_channels个数值的向量,在训练时,通过一系列卷积层、池化层和全连接层等操作提取和处理图像特征。
num_classes = 17
#创建序列模型,一种线性堆叠模型,各层按照他们被添加到模型中的顺序来堆叠
model = models.Sequential([
#输入层
layers.Input(shape=(img_height,img_width,3)),
# 数据预处理层:将像素值从 [0, 255] 缩放到 [0, 1]即归一化,输入(224, 224 ,3),
layers.Rescaling(scale=1./255),
#2.17.0的tensorflow貌似已经移除了experimental.preprocessing,而直接放在layers库中(*最好用Input作为单独输入层)
#layers.experimental.preprocessing.Rescaling(1./255, input_shape=(img_height,img_width,3)),
# 卷积层1:16 个 3x3 的卷积核,使用 ReLU 激活函数,输出 (222, 222, 16),
layers.Conv2D(16, (3, 3), activation='relu', input_shape=(img_height, img_width, 3)),
# 池化层1:2x2 的平均池化,输出(111,111,16)
layers.AveragePooling2D((2, 2)), #池化层1,2*2采样
# 卷积层2:32 个 3x3 的卷积核,使用 ReLU 激活函数,(109,109,32)
layers.Conv2D(32, (3, 3), activation='relu'), #卷积层2,卷积核3*3
# 池化层2:2x2 的平均池化,(54,54,32)
layers.AveragePooling2D((2, 2)), #池化层2,2*2采样
# Dropout层:随机停止50%的神经元工作,防止过拟合
layers.Dropout(0.5),
# 卷积层3:64 个 3x3 的卷积核,使用 ReLU 激活函数 (52,52,64)
layers.Conv2D(64, (3, 3), activation='relu'),
# 池化层3:2x2 的平均池化,(26,26,64)
layers.AveragePooling2D((2, 2)),
# Dropout层:随机停止30%的神经元工作,防止过拟合
layers.Dropout(0.5),
# 卷积层4:128个 3x3的卷积核,使用ReLU 激活函数 (24,24,128)
layers.Conv2D(128, (3,3), activation ="relu"),
# Dropout层:随机停止30%的神经元工作,防止过拟合
layers.Dropout(0.5),
#flatten层
layers.Flatten(),
layers.Dense(128, activation="relu"), #全连接层,特征进一步提取
layers.Dense(num_classes)
])
model.summary() # 打印网络结构
/usr/local/lib/python3.10/dist-packages/keras/src/layers/convolutional/base_conv.py:107: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
super().__init__(activity_regularizer=activity_regularizer, **kwargs)
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ │ rescaling (Rescaling) │ (None, 224, 224, 3) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d (Conv2D) │ (None, 222, 222, 16) │ 448 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ average_pooling2d (AveragePooling2D) │ (None, 111, 111, 16) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_1 (Conv2D) │ (None, 109, 109, 32) │ 4,640 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ average_pooling2d_1 │ (None, 54, 54, 32) │ 0 │ │ (AveragePooling2D) │ │ │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout (Dropout) │ (None, 54, 54, 32) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_2 (Conv2D) │ (None, 52, 52, 64) │ 18,496 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ average_pooling2d_2 │ (None, 26, 26, 64) │ 0 │ │ (AveragePooling2D) │ │ │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout_1 (Dropout) │ (None, 26, 26, 64) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ conv2d_3 (Conv2D) │ (None, 24, 24, 128) │ 73,856 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dropout_2 (Dropout) │ (None, 24, 24, 128) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ flatten (Flatten) │ (None, 73728) │ 0 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense (Dense) │ (None, 128) │ 9,437,312 │ ├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤ │ dense_1 (Dense) │ (None, 17) │ 2,193 │ └──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
Total params: 9,536,945 (36.38 MB)
Trainable params: 9,536,945 (36.38 MB)
Non-trainable params: 0 (0.00 B)
四、编译模型
在准备对模型进行训练之前,还需要再对其进行一些设置。以下内容是在模型的编译步骤中添加的:
- 损失函数(loss):用于衡量模型在训练期间的准确率。
- 优化器(optimizer):决定模型如何根据其看到的数据和自身的损失函数进行更新。
- 指标(metrics):用于监控训练和测试步骤。以下示例使用了准确率,即被正确分类的图像的比率。
#本次使用代码
# 设置初始学习率
initial_learning_rate = 1e-4
lr_schedule = tf.keras.optimizers.schedules.ExponentialDecay(
initial_learning_rate,
decay_steps=60, # 敲黑板!!!这里是指 steps,不是指epochs
decay_rate=0.94, # lr经过一次衰减就会变成 decay_rate*lr
staircase=True)
# 将指数衰减学习率送入优化器
optimizer = tf.keras.optimizers.Adam(learning_rate=lr_schedule)
model.compile(optimizer=optimizer,
loss=tf.keras.losses.CategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
#Adam优化器是一种常用的梯度下降优化算法,用于更新模型的权重以最小化训练过程中的损失函数
#由于是多分类问题这里使用categorical crossentropy损失函数
五、训练模型
epochs = 100
# 保存最佳模型参数
checkpointer = ModelCheckpoint(
"/content/drive/Othercomputers/My laptop/jupyter notebook/xunlianying/6best.weights.h5",
monitor='val_accuracy',
verbose=1,
mode = "max",
save_best_only=True,
save_weights_only=True)
# 设置早停
earlystopper = EarlyStopping(monitor='val_accuracy',
min_delta=0.001,
patience=20,
mode = "max",
verbose=1)
history = model.fit(tr_ds,
validation_data=val_ds,
epochs=epochs,
callbacks=[checkpointer, earlystopper])
Epoch 1/100
六、模型评估
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs_range = range(len(loss))
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()
七、预测
注释参考:https://blog.csdn.net/qq_45735298/article/details/130056861?spm=1001.2014.3001.5502
model.load_weights('../xunlianying/6best.weights.h5')
#这段代码用于加载之前训练中保存的最佳模型权重。使用之前保存的模型权重文件路径和名称。
#这样可以避免从头开始训练模型,直接使用已经训练好的最佳模型进行预测的工作。
from PIL import Image
import numpy as np
img = Image.open("./5/test/nike/4.jpg") #使用 PIL 库中的 Image.open() 方法打开一张待预测的图片。
image = tf.image.resize(img, [img_height, img_width])
#这个函数调整输入图像的大小以符合模型的要求。
#在这个例子中,使用 TensorFlow 的 tf.image.resize() 函数将图像缩放为指定大小,其中 img_height 和 img_width 是指定的图像高度和宽度。
img_array = tf.expand_dims(image, 0)
'''
这个函数将输入图像转换为形状为 (1, height, width, channels) 的四维数组,
其中 height 和 width 是图像的高度和宽度,channels 是图像的通道数(例如 RGB 图像有 3 个通道)。
这里使用 TensorFlow 的 tf.expand_dims() 函数来扩展图像数组的维度,以匹配模型的输入格式。
具体来说:
image 是一个二维图片张量,它的形状是 (height, width, channels)。其中 height 和 width 分别为图片的高度和宽度,channels 为图片的颜色通道数。
0 是一个整数值,它指定在哪个维度上扩展此张量,这里表示在最前面(第一个)的维度上扩展。
因此,函数的作用是将输入张量 image 在最前面添加一个额外的维度(batch_size),生成一个四维张量。
tf.expand_dims(input, axis)
其中 input 表示要扩展的输入张量,axis 表示要在哪个维度上进行扩展。在这个例子中,input 是变量 image,axis 是 0。
'''
pre = model.predict(img_array)
#这个函数用于对输入图像进行分类预测。它使用已经训练好的模型来对输入数据进行推断,并输出每个类别的概率分布。
print("预测结果为:", class_names[np.argmax(pre)])
#将模型输出的概率分布转换为最终预测结果。
#具体来说,使用 np.argmax() 函数找到概率最大的类别索引,然后使用该索引在 class_names 列表中查找相应的类别名称,并输出预测结果。
八、总结(临时)
- 训练第一次结果val acc最高0.377,修改了一下decay_rate待再次训练
- 学习了不同损失函数的特点和适用的场景条件
- VGG16模型待搭建