“在计算机视觉领域,深度学习模型的崭新发展为解决复杂的图像分类任务带来了巨大的机会。AlexNet作为深度学习的开创性模型,为识别大规模图像数据奠定了基础。然而,现实世界中的图像分类问题涵盖了许多不同的领域和场景,从自然界的昆虫到工业品质检测。本文旨在探讨如何通过微调AlexNet模型,以实现对蚂蚁和蜜蜂图像的精准分类。通过在预训练的AlexNet基础上进行微调,结合适当的数据增强和优化策略,我们可以有效地将这一经典模型应用于特定领域的图像分类任务,为我们更好地理解深度学习在实际问题中的应用提供了一个生动的案例。本文不仅关注算法和技术,更关注如何将先进的人工智能技术融入到日常生活的点滴中,从而推动技术的可持续发展。”

数据准备与数据增强

在图像分类任务中,数据的多样性对于模型的泛化能力至关重要。我们首先需要加载并预处理数据。上述Python代码片段中,我们使用了PyTorch中的transforms模块,定义了针对训练集和验证集的数据增强操作。这些操作包括图像大小调整、随机水平翻转、随机仿射变换、颜色抖动以及标准化。通过这些操作,我们可以有效地增加训练数据的多样性,提升模型的鲁棒性。

import torch
import numpy as np
import matplotlib.pyplot as plt
import torch.nn.functional as F
from torch import nn
from torchvision import datasets, transforms, models

device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

# 定义数据增强的转换操作
transform_train = transforms.Compose([
    transforms.Resize((224, 224)),  # 调整图像大小为224x224像素
    transforms.RandomHorizontalFlip(),  # 随机水平翻转
    transforms.RandomAffine(0, shear=10, scale=(0.8, 1.2)),  # 随机仿射变换,包括剪切和缩放
    transforms.ColorJitter(brightness=1, contrast=1, saturation=1),  # 随机颜色抖动,增加图像的多样性
    transforms.ToTensor(),  # 转换为张量
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # 标准化,将图像像素值缩放到[-1, 1]的范围
])

transform = transforms.Compose([
    transforms.Resize((224, 224)),  # 调整图像大小为224x224像素
    transforms.ToTensor(),  # 转换为张量
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # 标准化,将图像像素值缩放到[-1, 1]的范围
])

root_train = 'ants_and_bees/train'  # 训练集数据文件夹的路径
root_val = 'ants_and_bees/val'  # 验证集数据文件夹的路径

# 创建训练集和验证集的数据集对象
training_dataset = datasets.ImageFolder(root=root_train, transform=transform_train)
validation_dataset = datasets.ImageFolder(root=root_val, transform=transform)

# 创建训练数据加载器,用于按批次加载训练数据
training_loader = torch.utils.data.DataLoader(
    training_dataset,  # 训练数据集
    batch_size=20,  # 批大小,每个批次包含20个样本
    shuffle=True  # 在每个训练周期之前随机打乱数据
)

# 创建验证数据加载器,用于按批次加载验证数据
validation_loader = torch.utils.data.DataLoader(
    validation_dataset,  # 验证数据集
    batch_size=20,  # 批大小,每个批次包含20个样本
    shuffle=False  # 不对验证数据进行打乱,保持原有顺序
)

构建AlexNet模型

AlexNet是深度学习领域的一个重要里程碑,它的架构对于后续的深度卷积神经网络产生了深远的影响。在本例中,我们将使用AlexNet作为基础模型,并在其基础上进行微调。

classes = ('ant', 'bee')
dataiter = iter(training_loader)  # 创建迭代器,用于按批次获取训练数据
images, labels = dataiter.next()  # 获取一个批次的训练数据和标签
fig = plt.figure(figsize=(25, 6))  # 创建绘图窗口,设置尺寸为25x6

def im_convert(tensor):
    image = tensor.cpu().clone().detach().numpy()  # 将张量转换为NumPy数组
    image = image.transpose(1, 2, 0)  # 转置图像维度,使其符合NumPy数组的通道顺序
    # 反标准化 - 乘以标准差并加上均值
    image = image * np.array((0.5, 0.5, 0.5)) + np.array((0.5, 0.5, 0.5))
    image = image.clip(0, 1)  # 将图像像素值限制在0和1之间
    return image

# 遍历批次中的每个样本并显示图像和标签
for idx in np.arange(20):
    ax = fig.add_subplot(2, 10, idx+1, xticks=[], yticks=[])
    plt.imshow(im_convert(images[idx]))  # 显示转换后的图像
    #print(labels[idx].item())
    ax.set_title(classes[labels[idx].item()])  # 设置图像标题为对应的类别名称
plt.show()  # 显示绘制的图像

model = models.alexnet(pretrained=True) # 使用了PyTorch中的预训练的AlexNet模型
for param in model.features.parameters():
    param.requires_grad = False

import torch.nn as nn

n_inputs = model.classifier[6].in_features  # 获取原始模型中最后一层的输入特征数量,即4096
last_layer = nn.Linear(n_inputs, len(classes))  # 创建一个全新的线性层,输出特征数量为类别的数量
model.classifier[6] = last_layer  # 将最后一层替换为新创建的线性层
model.to(device)  # 将模型移动到设备(GPU或CPU)上

print(model)  # 打印模型的结构和参数信息
criterion = nn.CrossEntropyLoss()  # 定义交叉熵损失函数
optimizer = torch.optim.Adam(model.parameters(), lr=0.0001)  # 定义优化器

epochs = 5  # 训练周期数
losses = []  # 记录每个周期的训练损失
accuracy = []  # 记录每个周期的训练准确率
val_losses = []  # 记录每个周期的验证损失
val_accuracies = []  # 记录每个周期的验证准确率

for e in range(epochs):
    running_loss = 0.0  # 记录每个周期的累计训练损失
    running_accuracy = 0.0  # 记录每个周期的累计训练准确率
    val_loss = 0.0  # 记录每个周期的累计验证损失
    val_accuracy = 0.0  # 记录每个周期的累计验证准确率

    for images, labels in training_loader:
        images = images.to(device)  # 将训练图像移动到设备上(GPU或CPU)
        labels = labels.to(device)  # 将训练标签移动到设备上(GPU或CPU)
        outputs = model(images)  # 前向传播,计算输出
        loss = criterion(outputs, labels)  # 计算训练损失

        optimizer.zero_grad()  # 清零梯度
        loss.backward()  # 反向传播,计算梯度
        optimizer.step()  # 更新模型参数

        _, preds = torch.max(outputs, 1)  # 预测每个样本的类别
        running_accuracy += torch.sum(preds == labels.data)  # 累计正确预测的数量
        running_loss += loss.item()  # 累计训练损失

    with torch.no_grad():
        for val_images, val_labels in validation_loader:
            val_images = val_images.to(device)  # 将验证图像移动到设备上(GPU或CPU)
            val_labels = val_labels.to(device)  # 将验证标签移动到设备上(GPU或CPU)
            val_outputs = model(val_images)  # 前向传播,计算验证集输出
            val_loss = criterion(val_outputs, val_labels)  # 计算验证损失

            _, val_preds = torch.max(val_outputs, 1)  # 预测每个样本的类别
            val_accuracy += torch.sum(val_preds == val_labels.data)  # 累计验证准确预测的数量
            val_loss += val_loss.item()  # 累计验证损失

    # 计算每个周期的平均训练损失和准确率
    epoch_loss = running_loss / len(training_loader.dataset)
    epoch_accuracy = running_accuracy.float() / len(training_loader.dataset)
    losses.append(epoch_loss)
    accuracy.append(epoch_accuracy)



    # 计算每个周期的平均验证损失和准确率
    val_epoch_loss = val_loss / len(validation_loader.dataset)
    val_epoch_accuracy = val_accuracy.float() / len(validation_loader.dataset)
    val_losses.append(val_epoch_loss)
    val_accuracies.append(val_epoch_accuracy)

    # 打印训练和验证指标
    print("epoch:", e+1)
    print('training loss: {:.6f}, acc {:.6f}'.format(epoch_loss, epoch_accuracy.item()))
    print('validation loss: {:.6f}, acc {:.6f}'.format(val_epoch_loss, val_epoch_accuracy.item()))


plt.plot(losses, label='training loss')  # 绘制训练损失曲线
plt.plot(val_losses, label='validation loss')  # 绘制验证损失曲线
plt.legend()  # 显示图例
plt.show()  # 显示绘制的图形

plt.plot(accuracy, label='training accuracy')  # 绘制训练准确率曲线
plt.plot(val_accuracies, label='validation accuracy')  # 绘制验证准确率曲线
plt.legend()  # 显示图例
plt.show()  # 显示绘制的图形

dataiter = iter(validation_loader)
images, labels = dataiter.next()
images = images.to(device)  # 将验证图像移动到设备上(GPU或CPU)
labels = labels.to(device)  # 将验证标签移动到设备上(GPU或CPU)
output = model(images)  # 前向传播,计算输出
_, preds = torch.max(output, 1)  # 预测每个样本的类别

fig = plt.figure(figsize=(25, 4))

for idx in np.arange(20):
    ax = fig.add_subplot(2, 10, idx+1, xticks=[], yticks=[])
    plt.imshow(im_convert(images[idx]))  # 显示图像
    ax.set_title("{} ({})".format(str(classes[preds[idx].item()]), str(classes[labels[idx].item()])) , color=("green" if preds[idx]==labels[idx] else "red"))

plt.show()

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐