笔者最近在进行图像的二分类实验,感兴趣之余,用VGG16进行了对比实验。
在深度学习的世界里,VGG16 是一个具有重要地位的卷积神经网络架构。它以其简洁的设计、出色的性能和广泛的应用而备受瞩目。本文将详细介绍 VGG16 的基本原理,并附上相关图片以帮助大家更好地理解。
一、VGG16 的背景VGG16,全称为 Visual Geometry Group 16-layer network,由牛津大学的 Visual Geometry Group 提出。它在 2014 年的 ImageNet 大规模视觉识别挑战赛(ILSVRC)中取得了优异的成绩,证明了深度卷积神经网络在图像分类任务中的强大能力。二、VGG16 的基本原理
(一)网络结构VGG16 主要由一系列卷积层和全连接层组成。其网络结构非常规整,全部使用 3x3 的小卷积核和 2x2 的最大池化层。具体结构如下:
1. 输入层:接收大小为 224x224x3 的彩色图像。
2. 卷积层:由多个卷积层组成,每个卷积层后面跟着一个 ReLU 激活函数。
3. 最大池化层:使用 2x2 的窗口进行最大池化,步长为 2。
4. 全连接层:在卷积层和最大池化层之后,连接三个全连接层,最后一个全连接层输出。
(二)小卷积核的优势VGG16 全部使用 3x3 的小卷积核,具有以下几个优势:
(三)最大池化层的作用最大池化层的作用主要有以下几点:
1. 降低特征维度:通过对特征图进行下采样,可以降低特征维度,减少计算量。
2. 增强特征的鲁棒性:最大池化可以提取特征图中的局部最大值,对图像的平移、旋转等变换具有一定的不变性,从而增强特征的鲁棒性。
3. 防止过拟合:最大池化可以起到一定的正则化作用,防止过拟合。
(四)ReLU 激活函数ReLU(Rectified Linear Unit)激活函数是一种非常常用的激活函数,具有以下优点:1. 计算简单:ReLU 激活函数的计算非常简单,只需要对输入进行一个阈值操作,即大于 0 的输入保持不变,小于 0 的输入变为 0。2. 加速收敛:ReLU 激活函数可以加速网络的收敛速度,因为它在正区间是线性的,不会像 sigmoid 或 tanh 激活函数那样出现饱和现象。3. 缓解梯度消失问题:ReLU 激活函数可以缓解梯度消失问题,因为它在正区间的导数恒为 1,不会像 sigmoid 或 tanh 激活函数那样在饱和区域导数趋近于 0。
三、VGG16 的应用VGG16 由于其出色的性能和广泛的应用,已经成为了深度学习领域的经典之作。
它可以应用于以下几个方面:
1. 图像分类:VGG16 可以对各种类型的图像进行分类,如动物、植物、人物等。
2. 目标检测:VGG16 可以作为目标检测算法的基础网络,提取图像的特征,然后再进行目标的定位和分类。
3. 图像分割:VGG16 可以用于图像分割任务,将图像分割成不同的区域。
4. 人脸识别:VGG16 可以对人脸图像进行特征提取,然后进行人脸识别。
四、总结VGG16 是一个非常经典的卷积神经网络架构,它以其简洁的设计、出色的性能和广泛的应用而备受瞩目。通过本文的介绍,相信大家对 VGG16 的基本原理有了更深入的了解。在实际应用中,我们可以根据具体的任务需求,对 VGG16 进行适当的调整和优化,以获得更好的性能。
五、代码实现
注意笔者的样本txt文件格式为:图片路径,label
python=3.6 tensorflow=1.15
import time
from matplotlib import pyplot as plt
import numpy as np
import cv2
import os
from keras.applications import VGG16
from keras.applications.vgg16 import preprocess_input
from keras.models import Model
from keras.layers import Dense, Flatten
from keras.optimizers import Adam
from sklearn.model_selection import train_test_split
from tqdm import tqdm
# 读取数据(仅加载前1000个样本)
def load_data(txt_file, img_size, max_samples=1000):
images = []
labels = []
count = 0 # 计数器
with open(txt_file, 'r') as file:
for line in tqdm(file.readlines(),ncols=150,colour='red'):
if count >= max_samples: # 只加载前 max_samples 个数据
break
img_path, label = line.strip().split()
img = cv2.imread(img_path)
img = cv2.resize(img, (img_size, img_size)) # 调整图像大小
img = preprocess_input(img) # 预处理
images.append(img)
labels.append(int(label))
count += 1
return np.array(images), np.array(labels)
# 记录训练开始时间
start_time = time.time()
# 加载训练数据集
img_size = 224 # VGG16 输入尺寸
train_txt_file = r"data\datapath\training_set.txt" # 训练集 txt 文件路径
X_train, y_train = load_data(train_txt_file, img_size, max_samples=20000)
# 加载测试数据集
test_txt_file = r"data\datapath\testing_set.txt" # 测试集 txt 文件路径
X_test, y_test = load_data(test_txt_file, img_size, max_samples=1000)
# 使用 VGG16 模型
base_model = VGG16(weights=imagenet, include_top=False, input_shape=(img_size, img_size, 3))
# 取消 VGG16 的卷积层冻结,使模型可训练
for layer in base_model.layers:
layer.trainable = True
# 添加自定义的全连接层
x = Flatten()(base_model.output)
x = Dense(128, activation='relu')(x)
x = Dense(64, activation='relu')(x)
output = Dense(1, activation='sigmoid')(x) # 二分类问题
# 创建模型
model = Model(inputs=base_model.input, outputs=output)
# 编译模型
model.compile(optimizer=Adam(lr=0.0001), loss='binary_crossentropy', metrics=['acc'])
# 训练模型
batch_size = 32
epochs = 10
history = model.fit(X_train, y_train, batch_size=batch_size, epochs=epochs, validation_data=(X_test, y_test))
# 记录训练结束时间并计算训练时间
train_time = time.time() - start_time
print(f"Total Training Time: {train_time:.2f} seconds")
# 打印训练精度
train_acc = history.history['acc'][-1]
print(f"Final Training Accuracy: {train_acc * 100:.2f}%")
# 记录测试开始时间
start_test_time = time.time()
# 评估模型
test_loss, test_acc = model.evaluate(X_test, y_test)
# 记录测试结束时间并计算测试时间
test_time = time.time() - start_test_time
print(f"Total Testing Time: {test_time:.2f} seconds")
print(f"Test Accuracy: {test_acc * 100:.2f}%")
# 绘制损失曲线
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['acc'], label='Training accuracy')
plt.title('Training and Validation Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()