深度学习 Day 15——利用卷神经网络实现好莱坞明星识别

深度学习 Day 15——利用卷神经网络实现好莱坞明星识别

一、前言

在这里插入图片描述

365天深度学习训练营的难度是层层叠加的,虽然内容都是CNN,但是每一期我们都会比前一期多学一些知识,所以,本期内容我们仍然是学习有关卷神经网络的知识,但增加了一个新的知识点,那就是VGG-16网络框架,它具体是什么,我们后面会给大家解释的。

本期,我们就继续使用CNN实现好莱坞明星识别,前面相似的内容我们就简单的给代码,就不做过多的说明,具体知识点大家可以去我之前的博客去看看,或者上网查阅相关知识点,谢谢。

这是我的深度学习专栏:Python深度学习

二、我的环境

  • 电脑系统:Windows 11
  • 语言环境:Python 3.8.5
  • 编译器:DataSpell 2022.2
  • 深度学习环境:TensorFlow 2.3.4
  • 显卡及显存:RTX 3070 8G

三、前期工作

1、导入依赖项并设置GPU

导入依赖项:

from tensorflow import keras
from tensorflow.keras import layers,models
import os, PIL, pathlib
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np

和之前一样,如果你GPU很好就只使用GPU进行训练,如果GPU不行就推荐使用CPU训练加GPU加速。

只使用GPU:

if gpus:
    gpu0 = gpus[0]                                        #如果有多个GPU,仅使用第0个GPU
    tf.config.experimental.set_memory_growth(gpu0, True)  #设置GPU显存用量按需使用
    tf.config.set_visible_devices([gpu0],"GPU")

使用CPU+GPU:

os.environ["CUDA_VISIBLE_DEVICES"] = "-1"

2、导入数据集

data_dir = "E:\Deep_Learning\data\Day15"
data_dir = pathlib.Path(data_dir)

3、查看数据集

查看数据集内有多少张图片:

image_count = len(list(data_dir.glob('*/*.jpg')))

print("图片总数为:",image_count)

运行的结果是:

图片总数为: 1800

从数据集内返回一张图片查看一下:

roses = list(data_dir.glob('Jennifer Lawrence/*.jpg'))
PIL.Image.open(str(roses[0]))

在这里插入图片描述

四、数据预处理

1、加载数据

我们使用image_dataset_from_directory方法将我们本地的数据加载到tf.data.Dataset

中,并设置训练图片模型参数:

batch_size = 32
img_height = 224
img_width = 224

接下来加载数据:

train_ds = tf.keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split=0.1,
    subset="training",
    label_mode = "categorical",
    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.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 1620 files for training.
Found 1800 files belonging to 17 classes.
Using 180 files for validation.

这里也可以看到我们两个数据分别有多少文件。

然后我们再利用class_name输出我们本地数据集的标签,标签也就是对应数据所在的文件目录名:

class_names = train_ds.class_names
print(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']

2、检查数据并可视化数据

在可视化数据前,我们来检查一下我们的数据信息是否是正确的:

for image_batch, labels_batch in train_ds:
    print(image_batch.shape)
    print(labels_batch.shape)
    break
(32, 224, 224, 3)
(32, 17)

这是一批形状224x224x3的32张图片,我们将数据进行可视化看看:

plt.figure(figsize=(20, 10))

for images, labels in train_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])])
        
        plt.axis("off")

在这里插入图片描述

3、配置数据集

AUTOTUNE = tf.data.experimental.AUTOTUNE

train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

五、构建CNN网络


model = models.Sequential([
    layers.experimental.preprocessing.Rescaling(1./255, input_shape=(img_height, img_width, 3)),
    
    layers.Conv2D(16, (3, 3), activation='relu', input_shape=(img_height, img_width, 3)), # 卷积层1,卷积核3*3  
    layers.AveragePooling2D((2, 2)),               # 池化层1,2*2采样
    layers.Conv2D(32, (3, 3), activation='relu'),  # 卷积层2,卷积核3*3
    layers.AveragePooling2D((2, 2)),               # 池化层2,2*2采样
    layers.Dropout(0.5),  
    layers.Conv2D(64, (3, 3), activation='relu'),  # 卷积层3,卷积核3*3
    layers.AveragePooling2D((2, 2)),     
    layers.Dropout(0.5),  
    layers.Conv2D(128, (3, 3), activation='relu'),  # 卷积层3,卷积核3*3
    layers.Dropout(0.5), 
    
    layers.Flatten(),                       # Flatten层,连接卷积层与全连接层
    layers.Dense(128, activation='relu'),   # 全连接层,特征进一步提取
    layers.Dense(len(class_names))               # 输出层,输出预期结果
])

model.summary()  # 打印网络结构

打印出来的结果是:

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
rescaling (Rescaling)        (None, 224, 224, 3)       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 222, 222, 16)      448       
_________________________________________________________________
average_pooling2d (AveragePo (None, 111, 111, 16)      0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 109, 109, 32)      4640      
_________________________________________________________________
average_pooling2d_1 (Average (None, 54, 54, 32)        0         
_________________________________________________________________
dropout (Dropout)            (None, 54, 54, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 52, 52, 64)        18496     
_________________________________________________________________
average_pooling2d_2 (Average (None, 26, 26, 64)        0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 26, 26, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 24, 24, 128)       73856     
_________________________________________________________________
dropout_2 (Dropout)          (None, 24, 24, 128)       0         
_________________________________________________________________
flatten (Flatten)            (None, 73728)             0         
_________________________________________________________________
dense (Dense)                (None, 128)               9437312   
_________________________________________________________________
dense_1 (Dense)              (None, 17)                2193      
=================================================================
Total params: 9,536,945
Trainable params: 9,536,945
Non-trainable params: 0
_________________________________________________________________

六、训练模型

1、设置动态学习率、损失函数、优化器,指标为准确率。

在这里我们沿用前一篇博客的内容,在这里设置动态学习率,但在这里我们相比上一期博客我们直接使用了多分类的对数损失函数。

# 设置初始学习率
initial_learning_rate = 1e-4

lr_schedule = tf.keras.optimizers.schedules.ExponentialDecay(
        initial_learning_rate, 
        decay_steps=60,      # 敲黑板!!!这里是指 steps,不是指epochs
        decay_rate=0.96,     # 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'])

这里损失函数categorical_crossentropy的函数模型是:

tf.keras.metrics.categorical_crossentropy(
    y_true, y_pred, from_logits=False, label_smoothing=0.0, axis=-1
)

其中参数说明:

参数说明
y_trueone-hot 真实目标的张量
y_pred预测目标的张量
from_logits是否预计是一个 logits 张量。默认情况下,我们假设对概率分布进行编码。 y_pred``y_pred
label_smoothing浮动在 [0, 1] 中。如果 >0则平滑标签。例如,如果,用于非目标标签和目标标签。 0.1``0.1 / num_classes``0.9 + 0.1 / num_classes
axis默认为 -1。计算熵的维度

2、损失函数类型

官方文档里面有很多损失函数介绍,在这里我就介绍其中的几个:

  • binary_crossentropy(对数损失函数)

    tf.keras.metrics.binary_crossentropy(
        y_true, y_pred, from_logits=False, label_smoothing=0.0, axis=-1
    )
    

    sigmoid 相对应的损失函数,针对于二分类问题。

  • sparse_categorical_crossentropy(稀疏性多分类的对数损失函数)

    tf.keras.metrics.sparse_categorical_crossentropy(
        y_true, y_pred, from_logits=False, axis=-1, ignore_class=None
    )
    

    softmax 相对应的损失函数,如果是整数编码,则使用 sparse_categorical_crossentropy

3、One-Hot编码

这里提到了一个知识点——one-hot编码,肯定有人和我一样不理解这是什么编码,我也是上网查了一下,在这里我就简单的对其介绍一下:

One-Hot编码,又称为一位有效编码,主要是采用N位状态寄存器来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候只有一位有效。

One-Hot编码是分类变量作为二进制向量的表示。这首先要求将分类值映射到整数值。然后,每个整数值被表示为二进制向量,除了整数的索引之外,它都是零值,它被标记为1。

概念很复杂,那我们就来举个容易理解的例子:

假如我有一群朋友,他们可以通过四个特征来形容,分别是:

  • 性别:[“男生”, “女生”]
  • 学历:[“本科”, “研究生”, “博士生”]
  • 学校:[“二本”, “一本”, “211”, “985”]

好,现在我有个朋友他是男生,本科学历,就读于一所一本学校,如果我们使用特征列表是有序的话,我们就可以使用带有顺序的数值来表示他:“男生,本科,一本”文字对应特征值为:[0,0,1]

但是这样的特征处理并不能直接放入机器学习算法中,因为类别之间是无序的

这时候就可以用独热编码的形式来表示了,我们用采用N位状态寄存器来对N个状态进行编码,拿上面的例子来说,就是:

性别[“男生”, “女生”]N=2男生:1 0 女生:0 1
学历[“本科”, “研究生”, “博士生”]N=3本科:1 0 0 研究生:0 1 0 博士生: 0 0 1
学校[“二本”, “一本”, “211”, “985”]N=4二本: 1 0 0 0 一本:0 1 0 0 211: 0 0 1 0 985: 0 0 0 1

现在,我们来描述上面那个朋友就可以使用[1 0 1 0 0 0 1 0 0]来表示了,如果大家还是不懂的话可以上网学习一下。

4、早停与保存最佳模型参数

这里早停策略我们仍然使用的是上一期博客中使用过的EarlyStopping

from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping

epochs = 100

# 保存最佳模型参数
checkpointer = ModelCheckpoint('best_model.h5',
                                monitor='val_accuracy',
                                verbose=1,
                                save_best_only=True,
                                save_weights_only=True)

# 设置早停
earlystopper = EarlyStopping(monitor='val_accuracy', 
                             min_delta=0.001,
                             patience=20, 
                             verbose=1)

5、模型训练

history = model.fit(train_ds,
                    validation_data=val_ds,
                    epochs=epochs,
                    callbacks=[checkpointer, earlystopper])
Epoch 1/100
51/51 [==============================] - ETA: 0s - loss: 2.8120 - accuracy: 0.1019
Epoch 00001: val_accuracy improved from -inf to 0.13889, saving model to best_model.h5
51/51 [==============================] - 20s 400ms/step - loss: 2.8120 - accuracy: 0.1019 - val_loss: 2.7736 - val_accuracy: 0.1389
Epoch 2/100
51/51 [==============================] - ETA: 0s - loss: 2.7358 - accuracy: 0.1142
Epoch 00002: val_accuracy improved from 0.13889 to 0.15000, saving model to best_model.h5
51/51 [==============================] - 20s 387ms/step - loss: 2.7358 - accuracy: 0.1142 - val_loss: 2.6664 - val_accuracy: 0.1500
Epoch 3/100
51/51 [==============================] - ETA: 0s - loss: 2.6239 - accuracy: 0.1586
Epoch 00003: val_accuracy did not improve from 0.15000
51/51 [==============================] - 21s 407ms/step - loss: 2.6239 - accuracy: 0.1586 - val_loss: 2.5923 - val_accuracy: 0.1389
Epoch 4/100
51/51 [==============================] - ETA: 0s - loss: 2.5188 - accuracy: 0.1852
Epoch 00004: val_accuracy improved from 0.15000 to 0.19444, saving model to best_model.h5
51/51 [==============================] - 19s 379ms/step - loss: 2.5188 - accuracy: 0.1852 - val_loss: 2.5703 - val_accuracy: 0.1944
...
Epoch 62/100
51/51 [==============================] - ETA: 0s - loss: 0.1853 - accuracy: 0.9420
Epoch 00062: val_accuracy did not improve from 0.36667
51/51 [==============================] - 19s 377ms/step - loss: 0.1853 - accuracy: 0.9420 - val_loss: 3.9986 - val_accuracy: 0.3556
Epoch 63/100
51/51 [==============================] - ETA: 0s - loss: 0.1803 - accuracy: 0.9549
Epoch 00063: val_accuracy did not improve from 0.36667
51/51 [==============================] - 19s 378ms/step - loss: 0.1803 - accuracy: 0.9549 - val_loss: 4.0658 - val_accuracy: 0.3556
Epoch 00063: early stopping

我们查看一下最后的准确率:

val_loss,val_acc=model.evaluate(val_ds,verbose=2)
6/6 - 1s - loss: 4.0658 - accuracy: 0.3556

可以看出来准确率只有0.3556,很低。

七、模型评估

1、绘制Loss与Accuracy图

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()

在这里插入图片描述

2、指定图片进行预测

# 加载效果最好的模型权重
model.load_weights('best_model.h5')

from PIL import Image
import numpy as np

img = Image.open("E:\Deep_Learning\data\Day15\Jennifer Lawrence\003_963a3627.jpg")  #这里选择你需要预测的图片
image = tf.image.resize(img, [img_height, img_width])

img_array = tf.expand_dims(image, 0) 

predictions = model.predict(img_array) # 这里选用你已经训练好的模型
print("预测结果为:",class_names[np.argmax(predictions)])

预测的结果是:

预测结果为: Jennifer Lawrence

八、VGG-16介绍

1、VGG16函数模型

此模型的默认输入大小为 224x224

tf.keras.applications.VGG16(
    include_top=True,
    weights="imagenet",
    input_tensor=None,
    input_shape=None,
    pooling=None,
    classes=1000,
    classifier_activation="softmax",
)

其参数说明:

参数说明
include_top是否在网络顶部包含 3 个全连接层
weightsNone随机初始化)、“imagenet”(ImageNet 上的预训练)或要加载的权重文件的路径之一
input_tensor可选的 Keras 张量(即 的输出layers.Input()),用作模型的图像输入
input_shape可选形状元组,只有在include_top为 False 时才指定(否则输入形状必须是(224, 224, 3) (有channels_last数据格式)或(3, 224, 224)(有channels_first数据格式)。它应该有正好 3 个输入通道,并且宽度和高度应该不小于32. eg(200, 200, 3)将是一个有效值
pooling用于特征提取的可选池模式include_topFalse。-None表示模型的输出将是最后一个卷积块的 4D 张量输出。-avg表示全局平均池将应用于最后一个卷积块的输出,因此模型的输出将是一个 2D 张量。-max表示将应用全局最大池化
classes可选的类数,用于将图像分类,仅在include_top为 True 且未weights指定参数时指定
classifier_activation一个str或可调用的。在“顶层”层上使用的激活函数。忽略除非include_top=True。设置 classifier_activation=None为返回“顶层”层的 logits。加载预训练权重时,classifier_activation只能是None"softmax"

2、VGG网络结构

在这里插入图片描述

其中VGG16包含了16个隐藏层(13个卷积层和3个全连接层),如上图中的D列所示。

3、直接调用官方VGG16模型

from tensorflow.keras.applications.vgg16 import VGG16
model = VGG16(
    include_top=True,
    weights=None,
    input_tensor=None,
    input_shape=(img_height, img_width, 3),
    pooling=max,
    classes=len(class_names),
    classifier_activation='softmax'
)
model.summary()  # 打印网络结构

打印的结果是:

Model: "vgg16"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         [(None, 224, 224, 3)]     0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 224, 224, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 224, 224, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 112, 112, 64)      0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 112, 112, 128)     73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 112, 112, 128)     147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 56, 56, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 56, 56, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 28, 28, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 28, 28, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 14, 14, 512)       0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 7, 7, 512)         0         
_________________________________________________________________
flatten (Flatten)            (None, 25088)             0         
_________________________________________________________________
fc1 (Dense)                  (None, 4096)              102764544 
_________________________________________________________________
fc2 (Dense)                  (None, 4096)              16781312  
_________________________________________________________________
predictions (Dense)          (None, 17)                69649     
=================================================================
Total params: 134,330,193
Trainable params: 134,330,193
Non-trainable params: 0
_________________________________________________________________

直接调用官方VGG16模型时,模型训练的极其缓慢,所以我就选择了终止没有再继续进行。

目前来说我还是不太懂这个,所以本期就不进行尝试自己搭建VGG16模型,等我学习一段时间之后在下期博客中我们再进行尝试。

九、最后我想说

本期深度学习的内容就到这里结束了,剩下的时间我要去学习其中的有关知识了,一起加油!

最后有关VGG16的相关知识大家可以去看一下这篇文章,可能会对你有所启发:

VGG16 – 用于分类和检测的卷积网络

365天深度学习群里有很多大佬,我也要去学习一下大佬们都是如何进行调参模型优化的。

谢谢你们的阅读!

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-北天-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值