项目背景:
随着人工智能和深度学习技术的迅速发展,图像分类任务在计算机视觉领域取得了显著的成就。在日常生活中,猫和狗是人们常见且喜爱的宠物,对它们进行准确的分类具有一定的实际需求和应用价值。
同时,大规模的图像数据集的可用性以及强大的计算资源的普及,为开展基于深度学习的图像分类研究提供了良好的条件。此外,深度学习在图像识别领域的卓越表现,使得其能够处理复杂的图像特征,从而为实现高精度的猫狗分类提供了技术支持。
项目目的:
- 探索和应用深度学习技术在图像分类任务中的有效性和潜力,特别是在处理具有相似特征的猫和狗图像时。
- 提高对猫狗图像的分类准确率,为相关的图像识别应用提供可靠的模型和算法。
- 深入理解深度学习模型在学习和提取图像特征方面的机制,通过对猫狗分类的研究,优化模型结构和训练参数,以提升模型的性能和泛化能力。
- 为动物保护、宠物管理等领域提供技术支持,例如自动识别流浪猫狗、辅助宠物医疗诊断等。
- 培养和提升团队在深度学习、计算机视觉和数据处理方面的技术能力和创新思维
摘要:本文介绍了使用深度学习技术实现猫狗分类的项目,包括数据预处理、模型选择、训练过程以及结果评估。通过这个项目,我们能够深入了解深度学习在图像分类任务中的应用。
一、引言
在计算机视觉领域,图像分类是一个基础且重要的任务。猫狗分类作为一个经典的二分类问题,具有较高的研究价值和实际应用场景。通过对大量的猫狗图片进行学习,我们可以训练出一个能够准确区分猫狗的模型。
二、数据预处理
- 数据收集
我们从网络上收集了大量的猫狗图片数据集,包括不同品种、姿态和背景的图片,以增加数据的多样性和泛化能力。 - 在当前目录创建一个dataset文件夹,用来存放数据集。在dataset文件夹下创建train、val、test文件夹。在train文件夹下创建cat(🐱)、dog(🐕)文件夹,这是当前猫狗实验分类创建的,对应的文件夹下放对应的图片,比如说cat文件夹下放的都是猫的图片。val、test文件夹与train文件夹相同。
导入相关包的代码import torch.optim as optim import torch import torch.nn as nn import torch.nn.parallel import torch.optim import torch.utils.data import torch.utils.data.distributed import torchvision.transforms as transforms import torchvision.datasets as datasets import torchvision.models from torch.autograd import Variable from tqdm import tqdm import os from PIL import Image import cv2 #判断环境是CPU运行还是GPU DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu') DEVICE
- 数据清洗
对收集到的数据进行清洗,去除模糊、重复和质量较差的图片,确保数据的准确性和有效性。 - 数据标注
为每张图片标注其类别(猫或狗),以便模型能够进行有监督的学习。 - 数据增强
通过随机旋转、裁剪、翻转、亮度调整等操作对数据进行增强,以增加数据量和减少过拟合的风险。这里我的代码是进行随机 resize 裁剪到 256 x 256,随机旋转,随机水平翻转,中心裁剪到 224 x 224,转化成 Tensor,正规化等。
代码如下:transform111 = { '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]) ]) } transform111
三、加载数据
torchvision.transforms包DataLoader是 Pytorch 重要的特性,它们使得数据增加和加载数据变得非常简单。 使用 DataLoader 加载数据的时候就会将之前定义的数据 transform 就会应用的数据上了。
代码如下
import shutil
modellr = 1e-3
BATCH_SIZE = 64
EPOCHS = 100
# 删除隐藏文件/文件夹
for root, dirs, files in os.walk('./dataset'):
for file in files:
if 'ipynb_checkpoints' in file:
os.remove(os.path.join(root, file))
if 'ipynb_checkpoints' in root:
shutil.rmtree(root)
# 读取数据
dataset_train = datasets.ImageFolder('dataset/train', transform111)
print(dataset_train.imgs)
# 对应文件夹的label
print(dataset_train.class_to_idx)
dataset_test = datasets.ImageFolder('dataset/val', transform111)
# 对应文件夹的label
print(dataset_test.class_to_idx)
dataset = './dataset'
train_directory = os.path.join(dataset, 'train')
valid_directory = os.path.join(dataset, 'val')
batch_size = 32
num_classes = 6
print(train_directory)
data = {
'train': datasets.ImageFolder(root=train_directory, transform=transform111['train']),
'val': datasets.ImageFolder(root=valid_directory, transform=transform111['val'])
}
train_data_size = len(data['train'])
valid_data_size = len(data['val'])
train_loader = torch.utils.data.DataLoader(data['train'], batch_size=batch_size, shuffle=True, num_workers=8)
test_loader = torch.utils.data.DataLoader(data['val'], batch_size=batch_size, shuffle=True, num_workers=8)
print(train_data_size, valid_data_size)
四、迁移学习
由于我们的数据量相对有限,采用迁移学习的方法,利用在大规模数据集上预训练好的模型参数,然后在我们的猫狗数据集上进行微调。
代码如下:
# 下载预训练模型
model = torchvision.models.resnet50(pretrained=False)#不使用训练好的预训练模型
model
五、模型调优
为了适应自己的数据集,将ResNet-50的最后一层替换为,将原来最后一个全连接层的输入喂给一个有256个输出单元的线性层,接着再连接ReLU层和Dropout层,然后是256 x 2的线性层,输出为 2通道的softmax层。
# 实例化模型并且移动到GPU
criterion = nn.CrossEntropyLoss()
num_ftrs = model.fc.in_features
# model.fc = nn.Linear(num_ftrs, 10)
model.fc = nn.Sequential(
nn.Linear(num_ftrs, 256),
nn.ReLU(),
nn.Dropout(0.4),
nn.Linear(256, 6),#猫狗是二分类,故是2
nn.LogSoftmax(dim=1)
)
model.to(DEVICE)
# 选择简单暴力的Adam优化器,学习率调低
optimizer = optim.Adam(model.parameters(), lr=modellr)
def adjust_learning_rate(optimizer, epoch):
modellrnew = modellr * (0.1 ** (epoch // 50))
print("lr:", modellrnew)
for param_group in optimizer.param_groups:
param_group['lr'] = modellrnew
六、定义训练过程和验证过程
# 定义训练过程
def train(model, device, train_loader, optimizer, epoch):
model.train()
sum_loss = 0
train_acc = 0
total_num = len(train_loader.dataset)
# print(total_num, len(train_loader))
for batch_idx, (data, target) in enumerate(tqdm(train_loader)):
data, target = Variable(data).to(device), Variable(target).to(device)
im = Variable(data)
output = model(data)
loss = criterion(output, target)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print_loss = loss.data.item()
sum_loss += print_loss
out_t = output.argmax(dim=1) #取出预测的最大值
num_correct = (out_t == target).sum().item()
acc = num_correct / im.shape[0]
train_acc += acc
if (batch_idx + 1) % 50 == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, (batch_idx + 1) * len(data), len(train_loader.dataset),
100. * (batch_idx + 1) / len(train_loader), loss.item()))
ave_loss = sum_loss / len(train_loader)
ave_acc = train_acc / len(train_loader)
print('epoch:{}, train_acc: {}, loss:{}'.format(epoch, ave_acc, ave_loss))
return ave_acc, ave_loss
def val(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
total_num = len(test_loader.dataset)
# print(total_num, len(test_loader))
with torch.no_grad():
for data, target in test_loader:
data, target = Variable(data).to(device), Variable(target).to(device)
output = model(data)
loss = criterion(output, target)
_, pred = torch.max(output.data, 1)
correct += torch.sum(pred == target)
print_loss = loss.data.item()
test_loss += print_loss
correct = correct.data.item()
acc = correct / total_num
avgloss = test_loss / len(test_loader)
print('Val set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
avgloss, correct, len(test_loader.dataset), 100 * acc))
return acc, avgloss
四、训练过程
- 优化算法
选择合适的优化算法,如随机梯度下降(SGD)、Adagrad、Adadelta 等,并调整学习率、动量等参数。 - 损失函数
使用交叉熵损失函数来衡量模型的预测与真实标签之间的差异。 - 训练策略
采用批量训练的方式,设置合适的批量大小,并进行多次迭代训练,直到模型收敛。train_acc_list, train_loss_list, val_acc_list, val_loss_list = [], [], [], [] for epoch in range(1, EPOCHS + 1): adjust_learning_rate(optimizer, epoch) train_acc, train_loss = train(model, DEVICE, train_loader, optimizer, epoch) val_acc, val_loss = val(model, DEVICE, test_loader) train_acc_list.append(train_acc) val_acc_list.append(val_acc) train_loss_list.append(train_loss) val_loss_list.append(val_loss) torch.save(model, 'model.pth')
七、结果评估
- 准确率
使用准确率作为主要的评估指标,计算模型在测试集上正确分类的比例。 - 混淆矩阵
绘制混淆矩阵,直观地展示模型对猫和狗的分类情况,分析错误分类的样本特征。 - 可视化
通过对模型的中间特征图进行可视化,了解模型对图像的学习和理解过程。import matplotlib.pyplot as plt epochs_range = range(EPOCHS) print(epochs_range, train_acc_list) plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.plot(epochs_range, train_acc_list, label='Training Accuracy') plt.plot(epochs_range, val_acc_list, label='Validation Accuracy') plt.legend(loc='lower right') plt.title('Training and Validation Accuracy') plt.subplot(1, 2, 2) plt.plot(epochs_range, train_loss_list, label='Training Loss') plt.plot(epochs_range, val_loss_list, label='Validation Loss') plt.legend(loc='upper right') plt.title('Training and Validation Loss') plt.savefig('./acc-loss.jpg')
八、模型推理预测
import torch.utils.data.distributed
import torchvision.transforms as transforms
import torchvision.datasets as datasets
from torch.autograd import Variable
from pathlib import Path
classes = ('cat', 'dog')
transform_test = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
])
DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = torch.load("model.pth")
model.eval()
model.to(DEVICE)
dataset_test = datasets.ImageFolder('dataset/test', transform_test)
print(len(dataset_test))
# 对应文件夹的label
y_true, y_sore = [], []
for index in range(len(dataset_test)):
item = dataset_test[index]
img, label = item
img.unsqueeze_(0)
data = Variable(img).to(DEVICE)
output = model(data)
_, pred = torch.max(output.data, 1)
y_true.append(label)
y_sore.append(pred.data.item())
print('Image Name:{}, label:{}, predict:{}'.format(dataset_test.imgs[index][0], classes[label], classes[pred.data.item()]))
index += 1
六、总结与展望
通过本次猫狗分类项目的实践,我们深入掌握了深度学习在图像分类中的应用技术。未来,可以进一步探索更先进的模型架构和训练方法,提高分类的准确率和泛化能力,并将其应用到更广泛的图像分类任务中。