目录
VGGNet 是由牛津大学的视觉几何组(Visual Geometry Group)在 2014 年提出的一个深度卷积神经网络。它在 ImageNet 竞赛中取得了很好的成绩。VGGNet 的主要贡献是展示了网络深度对于性能的提升有显著影响。
1.VGGNet特点
- 网络深度:VGGNet 有多个版本,其中最常见的是 VGG16 和 VGG19,分别包含 13 层和 16 层卷积层。
- 卷积核大小:VGGNet 使用 3x3 的小卷积核,这使得网络更深但参数更少。
- 步长和填充:卷积层使用 1x1 的步长和 1 像素的填充,保持特征图的尺寸。
- 池化层:每两个卷积层后跟一个 2x2 的最大池化层,步长为 2。
- 全连接层:在卷积层之后,VGGNet 使用几个全连接层,最后是一个 softmax 层进行分类。
2.注意点
VGG16结构复杂,而MNIST数据集图像太小,在经过过多的池化层后,维度会简化到0然后报错,所以本代码将使用两个卷积层和两个池化层的简化版VGGnet。
3.导入数据集
import torch
from torch import nn
#导入神经网络模块
from torch.utils.data import DataLoader #数据包管理工具,打包数据
from torchvision import datasets #封装了很多与图像相关的模型,数据集
from torchvision.transforms import ToTensor
training_data = datasets.MNIST(#跳转到国数的内部源代码,pycharm 按ctrl +鼠标点击
root="data",
train=True,
download=True,
transform=ToTensor(),#张量,图片是不能直接传入神经网络模型
)
test_data = datasets.MNIST(
root="data",
train=False,
download=True,
transform=ToTensor(),#Tensor是在深度学习中提出并厂泛应用的数据类型,它与深度学习框架(如PyTorch、TensorFlow) 紧密集成
)
train_dataloader = DataLoader(training_data, batch_size=64) #64张图片为一个包
test_dataloader = DataLoader(test_data, batch_size=64)
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
4.定义简化版的VGG网络结构
class VGG_MNIST(nn.Module):
def __init__(self):
super(VGG_MNIST, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(1, 64, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(64, 128, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
# 可以继续添加层,但考虑到MNIST的简单性,这里就足够了
)
self.classifier = nn.Sequential(
nn.Linear(128 * 7 * 7, 1024), # 根据特征图大小调整这个值
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(1024, 10),
)
def forward(self, x):
x = self.features(x)
x = torch.flatten(x, 1)
x = self.classifier(x)
return x
model = VGG_MNIST().to(device) # 把刚刚创建的模型传入到 Gpu
5.定义训练和验证函数
def train(dataloader, model, loss_fn, optimizer):
model.train() # #告诉模型要开始训练,模型中ω进行随机化操作,以及更新ω,在训练过程中,ω会被修改
batch_size_num = 1
for X, y in dataloader: # 其中batch为每个数据的编号
X, y = X.to(device), y.to(device) # 把训练数据集和标签传入cpu或GPU
pred = model.forward(X) # 自动初始化ω权值
loss = loss_fn(pred, y) # 通过交叉熵损失函数计算损失值loss
optimizer.zero_grad() # 梯度值清零
loss.backward() # 反向传播计算得到每个参数的梯度值
optimizer.step() # 根据梯度更新网络参数
loss_value = loss.item() # item从tensor数据中提取数据出来,tensor获取损失值
print(f"loss: {loss_value:>7f} [number:{batch_size_num}]")
batch_size_num += 1
def test(dataloader, model, loss_fn):
size = len(dataloader.dataset)
num_batches = len(dataloader)
model.eval() # 测试,ω就不能再更新。
test_loss, correct = 0, 0
with torch.no_grad():# 一个上下文管理器,关闭梯度计算。当确认不会调用Tensor.backward()的时候。这可以减少计算所用内存消耗。
for X, y in dataloader:
X, y = X.to(device), y.to(device) # 数据传到GPU或者CPU中
pred = model.forward(X)
test_loss += loss_fn(pred, y).item() # test_loss 会自动累加每一个批次的损失值
correct += (pred.argmax(1) == y).type(torch.float).sum().item()
test_loss /= num_batches # 能来衡量模型测试的好坏。
correct /= size # 平均的正确率
print(f"Test result: \n Accuracy: {(100*correct)}%,Avg loss: {test_loss}")
6.调用函数
loss_fn = nn.CrossEntropyLoss() # 创建交叉熵损失函数对象,因为手写字识别中共有10个数字,输出会有10个结果
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
train(train_dataloader, model, loss_fn, optimizer) # 训练1次完整的数据,多轮训练
test(test_dataloader, model, loss_fn)
7.多批次训练
epochs = 10
for t in range(epochs):
print(f"Epoch {t+1}\n--------------------------")
train(train_dataloader, model, loss_fn, optimizer)
print("Done!")
test(test_dataloader, model, loss_fn)