当前位置:   article > 正文

基于Pytorch框架构建VGG-16模型_pytorch vgg16

pytorch vgg16

一、训练模型

1.导入工具包

torch: 这是PyTorch框架的基础库,提供了自动求导机制和丰富的张量运算支持,是构建和训练神经网络的基础。
torch.nn: PyTorch的神经网络库,包含多种构建神经网络所需的层结构(如卷积层、全连接层)和激活函数等
torch.utils.data: 提供了数据加载和处理的工具,是加载数据集并进行批处理的重要模块
torchvision.transforms: PyTorch的视觉库中的一个模块,提供了一系列图像处理的变换操作,用于数据增强和预处理
torchvision.datasets: 提供了常见的数据集和相关的数据加载方法,如MNIST、CIFAR-10、ImageNet等
DataLoader: torch.utils.data中的一个类,用于构建可迭代的数据加载器,可以方便地在训练循环中按批次加载数据
torch.optim.lr_scheduler: 提供了学习率调整策略,如学习率衰减,有助于训练过程中改善模型性能和减少过拟合
os: Python的标准库之一,提供了与操作系统交互的功能,如文件创建、路径操作等

import torch.optim as optim
import torch
import torch.nn as nn
import torch.utils.data
import torchvision.transforms as transforms
import torchvision.datasets as datasets
from torch.utils.data import DataLoader
import torch.optim.lr_scheduler as lr_scheduler
import os
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.判断环境是CPU运行还是GPU

DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
  • 1

3.数据预处理

transforms.RandomResizedCrop(size=256, scale=(0.8, 1.0)): 随机裁剪图像,裁剪后的图像大小为256x256像素。裁剪区域的大小是原始图像尺寸的0.8到1.0倍之间随机选择
transforms.RandomRotation(degrees=15): 随机旋转图像,旋转角度在-15度到15度之间随机选择
transforms.RandomHorizontalFlip(): 随机水平翻转图像,即有一半的概率会翻转,一半的概率不翻
transforms.CenterCrop(size=224): 从图像中心裁剪出224x224像素的区域
transforms.ToTensor(): 将图像转换为PyTorch张量,并且将像素值从[0, 255]范围缩放到[0, 1]范围
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]): 对图像进行标准化处理。这里使用的是ImageNet数据集的均值和标准差,这些值分别用于每个颜色通道的减均值和除以标准差操作
验证数据预处理 (transform[‘val’])
transforms.Resize(size=256): 将图像大小调整为256x256像素,这里没有使用随机裁剪,而是直接调整大小
transforms.CenterCrop(size=224): 从图像中心裁剪出224x224像素的区域
transforms.ToTensor(): 将图像转换为PyTorch张量,并且将像素值从[0, 255]范围缩放到[0, 1]范围
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]): 对图像进行标准化处理,使用的是ImageNet数据集的均值和标准差

# 定义数据预处理
transform = {
    'train': transforms.Compose([
        transforms.RandomResizedCrop(size=256, scale=(0.8, 1.0)),
        transforms.RandomRotation(degrees=15),
        transforms.RandomHorizontalFlip(),
        transforms.CenterCrop(size=224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406],
                             [0.229, 0.224, 0.225])
    ]),
    'val': transforms.Compose([
        transforms.Resize(size=256),
        transforms.CenterCrop(size=224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406],
                             [0.229, 0.224, 0.225])
    ])
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

4.读取数据

dataset = './dataset': 这行代码设置了数据集的根目录。这里的./表示当前工作目录,所以dataset变量指向的是当前工作目录下的dataset文件夹
train_directory = os.path.join(dataset, 'train'): 这行代码将dataset目录和train子目录连接起来,创建训练数据集的完整路径。如果当前工作目录是/home/user/project,那么train_directory将是/home/user/project/dataset/train
valid_directory = os.path.join(dataset, 'val'): 这行代码将dataset目录和val子目录连接起来,创建验证数据集的完整路径。同样,如果当前工作目录是/home/user/project,那么valid_directory将是/home/user/project/dataset/val

dataset = './dataset'
train_directory = os.path.join(dataset, 'train')
valid_directory = os.path.join(dataset, 'val')
  • 1
  • 2
  • 3

5.设置超参数

batch_size = 32
num_classes = 2  # 修改为您的分类数
  • 1
  • 2

6.创建训练(train)和验证(val)数据集的数据加载器

data = { ... }: 这个字典定义了两个键:‘train’和’val’,它们分别对应于训练和验证数据集。对于每个键,都使用torchvision.datasets.ImageFolder类来创建一个数据集。ImageFolder假设每个类别的图像都存储在一个单独的文件夹中,并且文件夹的名称是类别的名称。root参数指定了数据集的根目录,transform参数指定了应用于每个图像的预处理操作
train_loader = DataLoader(data['train'], batch_size=batch_size, shuffle=True, num_workers=8): 这行代码创建了训练数据加载器。DataLoader类接受一个数据集作为输入,并提供一个可迭代的数据加载器。batch_size参数指定了每个批次中图像的数量。shuffle=True表示在每次遍历数据集时都会随机打乱数据的顺序,这对于训练是有益的,因为它可以减少模型的过拟合。num_workers=8表示在加载数据时使用8个子进程。这可以加快数据加载速度,尤其是在使用GPU进行训练时
test_loader = DataLoader(data['val'], batch_size=batch_size, shuffle=False, num_workers=8): 这行代码创建了验证数据加载器。与训练数据加载器类似,但它设置了shuffle=False,因为在验证阶段,我们不希望数据顺序被打乱,以便于评估模型的性能

data = {
    'train': datasets.ImageFolder(root=train_directory, transform=transform['train']),
    'val': datasets.ImageFolder(root=valid_directory, transform=transform['val'])
}
train_loader = DataLoader(data['train'], batch_size=batch_size, shuffle=True, num_workers=8)
test_loader = DataLoader(data['val'], batch_size=batch_size, shuffle=False, num_workers=8)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

二、定义VGG16模型

1. 定义VGG16模型

class VGG16(nn.Module): 这行代码定义了一个新的类VGG16,它继承自nn.Module。nn.Module是所有神经网络模块的基类
def __init__(self, num_classes=1000): 构造函数接收一个参数num_classes,表示模型输出的类别数。默认值为1000,这是ImageNet数据集的类别数
super(VGG16, self).__init__():这行代码调用了基类的构造函数
self.features = nn.Sequential(...): 这部分定义了VGG-16的特征提取部分,包括5个卷积块,每个卷积块包含多个卷积层和一个池化层。每个卷积层后面都跟着一个ReLU激活函数
self.classifier = nn.Sequential(...): 这部分定义了VGG-16的分类器部分,包括3个全连接层,每个全连接层后面都跟着一个ReLU激活函数和一个Dropout层。最后一个全连接层的输出节点数与num_classes参数相同
def forward(self, x): 前向传播函数接收输入x,首先通过特征提取部分self.features,然后将特征图展开为一维向量,最后通过分类器部分self.classifier得到最终的分类结果

class VGG16(nn.Module):
    def __init__(self, num_classes=1000):
        super(VGG16, self).__init__()
        self.features = nn.Sequential(
            # Block 1
            nn.Conv2d(3, 64, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(64, 64, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
            # Block 2
            nn.Conv2d(64, 128, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(128, 128, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
            # Block 3
            nn.Conv2d(128, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
            # Block 4
            nn.Conv2d(256, 512, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
            # Block 5
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
        )
        self.classifier = nn.Sequential(
            nn.Linear(512 * 7 * 7, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            nn.Linear(4096, num_classes)  # 修改这里,默认为1000个类别
        )
    def forward(self, x):
        x = self.features(x)
        x = torch.flatten(x, 1)  # 展开特征图
        x = self.classifier(x)
        return x
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

2.初始化VGG16模型

vgg16 = VGG16(num_classes=2): 这行代码创建了一个VGG-16模型的实例,并将输出类别数设置为2。这意味着模型的最后一层全连接层将有2个输出节点,对应于两个类别
vgg16 = vgg16.to(DEVICE): 这行代码将创建的VGG-16模型移动到指定的设备上。这里的DEVICE应该是一个之前定义的变量,表示您希望模型运行的设备。例如,如果DEVICE是torch.device(‘cuda’),则模型将被移动到GPU上;如果DEVICE是torch.device(‘cpu’),则模型将在CPU上运行

vgg16 = VGG16(num_classes=2)
vgg16 = vgg16.to(DEVICE)
  • 1
  • 2

3.定义损失函数和优化器

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(vgg16.parameters(), lr=0.001, momentum=0.9)
  • 1
  • 2

4.定义学习率调整策略

scheduler = lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)
  • 1

三、训练模型

model.train(): 这行代码将模型设置为训练模式。在某些模型中,训练模式和评估模式(例如BatchNorm和Dropout)的行为是不同的
running_loss = 0.0: 初始化运行损失为0,用于累加每个批次的损失
correct = 0: 初始化正确分类的样本数为0
total = 0: 初始化总样本数为0
for batch_idx, (data, target) in enumerate(train_loader): 这行代码开始一个循环,遍历训练数据加载器train_loader中的每个批次。batch_idx是批次的索引,data是当前批次的数据,target是当前批次的目标标签
data, target = data.to(device), target.to(device): 这行代码将数据和目标标签移动到之前定义的设备上,如果设备是GPU,这将使数据能够在GPU上进行计算
optimizer.zero_grad(): 清空模型的梯度。在PyTorch中,梯度是累加的,所以在每次反向传播之前需要清空之前的梯度
output = model(data): 将当前批次的数据通过模型前向传播,得到模型的输出
loss = criterion(output, target): 计算模型输出和目标标签之间的损失
loss.backward(): 执行反向传播,计算模型的梯度
running_loss += loss.item(): 累加当前批次的损失到运行损失中
predicted = torch.max(output.data, 1): 获取模型预测的最大概率类别。torch.max返回最大值和最大值的索引,这里我们只关心索引,即预测的类别
total += target.size(0): 累加当前批次的目标标签数量到总样本数中
correct += (predicted == target).sum().item(): 计算当前批次中正确分类的样本数,并累加到正确分类的总数中
if batch_idx % 10 == 0: : 如果当前批次的索引是10的倍数,打印当前批次的损失
print(f'Epoch {epoch}, Loss: {running_loss / len(train_loader)}, Accuracy: {100 * correct / total}%'): 在一个训练周期结束后,打印出整个周期的平均损失和准确率

def train(model, device, train_loader, optimizer, criterion, epoch):
    model.train()
    running_loss = 0.0
    correct = 0
    total = 0
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)  # 修正缩进
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, target)
        loss.backward()
        optimizer.step()
        running_loss += loss.item()
        _, predicted = torch.max(output.data, 1)
        total += target.size(0)
        correct += (predicted == target).sum().item()

        if batch_idx % 10 == 0:  # 每10个批次打印一次
            print(f'Epoch {epoch}, Batch {batch_idx}, Loss: {loss.item()}')
    print(f'Epoch {epoch}, Loss: {running_loss / len(train_loader)}, Accuracy: {100 * correct / total}%')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

四、验证模型

1.定义验证过程

model.eval(): 这行代码将模型设置为评估模式。在某些模型中,训练模式和评估模式(例如BatchNorm和Dropout)的行为是不同的
running_loss = 0.0: 初始化运行损失为0,用于累加每个批次的损失
correct = 0: 初始化正确分类的样本数为0
total = 0: 初始化总样本数为0
with torch.no_grad(): 这个上下文管理器用于告诉PyTorch在接下来的代码块中不要计算梯度。因为在验证过程中我们不需要更新模型参数,所以不需要计算梯度
for data, target in test_loader: 这行代码开始一个循环,遍历验证数据加载器test_loader中的每个批次。data是当前批次的数据,target是当前批次的目标标签
data, target = data.to(device), target.to(device): 这行代码将数据和目标标签移动到之前定义的设备上,如果设备是GPU,这将使数据能够在GPU上进行计算
output = model(data): 将当前批次的数据通过模型前向传播,得到模型的输出
loss = criterion(output, target): 计算模型输出和目标标签之间的损失
running_loss += loss.item(): 累加当前批次的损失到运行损失中
predicted = torch.max(output.data, 1): 获取模型预测的最大概率类别。torch.max返回最大值和最大值的索引,这里我们只关心索引,即预测的类别
total += target.size(0): 累加当前批次的目标标签数量到总样本数中
correct += (predicted == target).sum().item(): 计算当前批次中正确分类的样本数,并累加到正确分类的总数中
print(f'Validation, Loss: {running_loss / len(test_loader)}, Accuracy: {100 * correct / total}%'): 在验证结束后,打印出整个验证周期的平均损失和准确率

# 定义验证过程
def val(model, device, test_loader, criterion):
    model.eval()
    running_loss = 0.0
    correct = 0
    total = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            loss = criterion(output, target)
            running_loss += loss.item()
            _, predicted = torch.max(output.data, 1)
            total += target.size(0)
            correct += (predicted == target).sum().item()

    print(f'Validation, Loss: {running_loss / len(test_loader)}, Accuracy: {100 * correct / total}%')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2.训练模型

EPOCHS = 10
for epoch in range(1, EPOCHS + 1):
    train(vgg16, DEVICE, train_loader, optimizer, criterion, epoch)
    val(vgg16, DEVICE, test_loader, criterion)
    scheduler.step()  # 调整学习率
  • 1
  • 2
  • 3
  • 4
  • 5

3.保存模型的状态字典

torch.save(vgg16.state_dict(), 'vgg16_model_weights.pth')
  • 1

五、测试模型

import torch
from PIL import Image
import torchvision.transforms as transforms
from torchvision import models
from torch.autograd import Variable
import torch.optim as optim
import torch
import torch.nn as nn
# 定义数据预处理
transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

# 定义类别
classes = ['cat', 'dog']  # 替换为您的实际类别名称

# 检查是否有可用的 GPU
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")


class VGG16(nn.Module):
    def __init__(self, num_classes=1000):
        super(VGG16, self).__init__()
        self.features = nn.Sequential(
            # Block 1
            nn.Conv2d(3, 64, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(64, 64, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
            # Block 2
            nn.Conv2d(64, 128, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(128, 128, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
            # Block 3
            nn.Conv2d(128, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
            # Block 4
            nn.Conv2d(256, 512, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
            # Block 5
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(512, 512, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=2, stride=2),
        )
        self.classifier = nn.Sequential(
            nn.Linear(512 * 7 * 7, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            nn.Linear(4096, num_classes)  # 修改这里,默认为1000个类别
        )

    def forward(self, x):
        x = self.features(x)
        x = torch.flatten(x, 1)  # 展开特征图
        x = self.classifier(x)
        return x


# 初始化VGG16模型
model = VGG16(num_classes=2)
model = vgg16.to(DEVICE)
# 加载权重
model.load_state_dict(torch.load("vgg16_model.pth"))
model.to(DEVICE)
model.eval()

# 定义预测函数
def predict_image(image_path):
    # 打开图片
    image = Image.open(image_path)
    # 应用预处理
    image = transform(image).unsqueeze(0)  # 添加batch维度
    # 转换为Variable(如果模型需要)
    image = Variable(image).to(DEVICE)
    # 获取模型预测
    output = model(image)
    _, prediction = torch.max(output.data, 1)
    return classes[prediction.item()]

# 上传的图片路径
uploaded_image_path = '44.jpg'
# 进行预测
predicted_class = predict_image(uploaded_image_path)

print(f"The uploaded image is predicted as: {predicted_class}")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108

选取图片

在这里插入图片描述

运行结果:
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/957637
推荐阅读
相关标签
  

闽ICP备14008679号