使用PyTorch构建和训练CNN模型进行食物分类

        

目录

一、数据准备

二、模型设计

三、训练过程

五、模型保存与加载

六、使用模型进行预测

1、构建CNN模型

2、模型加载与预测

3、结果展示


在这篇博客中,我们将展示如何使用PyTorch库来构建一个卷积神经网络(CNN)模型,用于食物图像的分类。我们将从头开始,包括数据准备、模型设计、训练过程、模型评估以及使用训练好的模型进行图像预测。

一、数据准备

        首先,我们需要准备训练数据和测试数据。这里我们有两个文本文件train.txttest.txt,分别包含训练集和测试集的图像路径和标签。每个文件中的每一行都包含一个图像路径和一个标签,用空格分隔。(本数据由合肥兰智数加学院李挺老师特别提供,提取码lzsj

        为了加载这些数据,我们定义了一个Datasetfood_dataset,它继承自torch.utils.data.Dataset。这个类负责从文本文件中读取图像路径和标签,并在请求时加载和预处理图像。

import torch
from torch.utils.data import Dataset, DataLoader
import numpy as np
from PIL import Image
from torchvision import transforms

# 定义数据转换
data_transforms = {
    'train':
        transforms.Compose([
        transforms.Resize([256,256]),  # 调整图像大小为256x256像素
        transforms.ToTensor(),  # 将图像转换为Tensor格式
    ]),
    'valid':
        transforms.Compose([
        transforms.Resize([256,256]),  # 调整图像大小为256x256像素
        transforms.ToTensor(),  # 将图像转换为Tensor格式
    ]),
}

class food_dataset(Dataset):
    def __init__(self, file_path, transform = None):
        self.file_path = file_path
        self.imgs = []
        self.labels = []
        self.transform = transform
        with open(self.file_path) as f:
            samples = [x.strip().split(' ') for x in f.readlines()]  # 读取文件中的样本数据
            for img_path, label in samples:
                self.imgs.append(img_path)  # 将图像路径添加到列表中
                self.labels.append(label)  # 将标签添加到列表中

    def __len__(self):
        return len(self.imgs)  # 返回数据集的长度

    def __getitem__(self, idx):
        image = Image.open(self.imgs[idx])  # 打开图像文件
        if self.transform:
            image = self.transform(image)  # 应用数据转换

        label = self.labels[idx]  # 获取图像对应的标签
        label = torch.from_numpy(np.array(label, dtype=np.int64))  # 将标签转换为Tensor格式
        return image, label

# 创建训练集和测试集的实例
training_data = food_dataset(file_path = 'train.txt', transform = data_transforms['train'])
test_data = food_dataset(file_path = 'test.txt', transform = data_transforms['valid'])

# 创建数据加载器
train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)  # 训练集数据加载器
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True)  # 测试集数据加载器
二、模型设计

        接下来,我们设计一个适用于食物图像分类的CNN模型。这个模型包含几个卷积层、ReLU激活层和最大池化层,最后是一个全连接层用于输出分类结果。

import torch.nn as nn
# 检测GPU是否可用,优先选择cuda,然后mps,否则选择cpu
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"

# 定义卷积神经网络模型
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        # 第一个卷积层
        self.conv1 = nn.Sequential(
            nn.Conv2d(
                in_channels = 3,         # 输入通道数为3(彩色图像)
                out_channels = 16,       # 输出通道数为16
                kernel_size = 5,         # 卷积核大小为5x5
                stride=1,                # 步长为1
                padding=2,               # 填充为2
            ),
            nn.ReLU(),                  # ReLU激活函数
            nn.MaxPool2d(kernel_size = 2),  # 最大池化层,池化核大小为2x2
        )
        # 第二个卷积层
        self.conv2 = nn.Sequential(
            nn.Conv2d(16, 32, 5, 1, 2),   # 输入通道数16,输出通道数32,卷积核大小5x5,填充2
            nn.ReLU(),                   # ReLU激活函数
            nn.Conv2d(32, 32, 5, 1, 2),   # 输入通道数32,输出通道数32,卷积核大小5x5,填充2
            nn.ReLU(),                   # ReLU激活函数
            nn.MaxPool2d(2),             # 最大池化层,池化核大小为2x2
        )
        # 第三个卷积层
        self.conv3 = nn.Sequential(
            nn.Conv2d(32, 128, 5, 1, 2),  # 输入通道数32,输出通道数128,卷积核大小5x5,填充2
            nn.ReLU(),                   # ReLU激活函数
        )
        # 全连接层
        self.out = nn.Linear(128 * 64 * 64, 20)  # 输入大小为128*64*64,输出大小为20(类别数)

    def forward(self, x):
        x = self.conv1(x)            # 第一层卷积
        x = self.conv2(x)            # 第二层卷积
        x = self.conv3(x)            # 第三层卷积
        x = x.view(x.size(0), -1)    # 展平多维的卷积图像
        output = self.out(x)         # 输出层
        return output

# 创建模型实例,并移动到对应的设备(GPU或CPU)
model = CNN().to(device)
print(model)  # 打印模型结构
三、训练过程

        在训练过程中,我们使用定义的train函数来迭代训练数据,更新模型权重。这个函数计算每个批次的损失,并使用反向传播来优化模型。

# 定义训练函数
def train(dataloader, model, loss_fn, optimizer):
    model.train()  # 将模型设为训练模式
    batch_size_num = 1  # 记录批次号,用于打印输出
    for X, y in dataloader:
        X, y = X.to(device), y.to(device)  # 将数据移动到设备(GPU或CPU)
        pred = model.forward(X)  # 前向传播得到预测值
        loss = loss_fn(pred, y)  # 计算损失
        optimizer.zero_grad()  # 梯度清零
        loss.backward()  # 反向传播计算梯度
        optimizer.step()  # 更新参数

        loss_value = loss.item()  # 获取损失值
        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():
        for X, y in dataloader:
            X, y = X.to(device), y.to(device)  # 将数据移动到设备
            pred = model.forward(X)  # 前向传播得到预测值
            test_loss += loss_fn(pred, y).item()  # 计算测试集损失
            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}")  # 打印测试结果

loss_fn = nn.CrossEntropyLoss()  # 损失函数为交叉熵损失
optimizer = torch.optim.Adam(model.parameters(), lr=0.000175)  # Adam优化器,学习率为0.000175

# 训练模型一次
train(train_dataloader, model, loss_fn, optimizer)
# 在测试集上评估模型表现
test(test_dataloader, model, loss_fn)

epochs = 30  # 定义总的训练轮数
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)  # 最终在测试集上评估模型性能
五、模型保存与加载

        训练好的模型需要被保存下来以便将来使用。我们可以使用torch.save函数来保存模型的状态字典,并在需要时加载它。

model_path = 'fooddataset2_cnn_model.pth'  # 定义模型保存路径和文件名
torch.save(model, model_path)  # 使用PyTorch的torch.save函数保存模型到指定路径

# 注释: 将训练好的CNN模型保存到文件'fooddataset2_cnn_model.pth'
六、使用模型进行预测

        最后,定义一个函数来使用训练好的模型对新的图像进行预测。这个函数加载模型,对图像进行预处理,并通过模型进行前向传播来获取预测结果。

1、构建CNN模型

        在PyTorch中,模型通常是通过继承nn.Module类来构建的。下面是一个简单的CNN模型处理大小为(3, 256, 256)的RGB图像,并输出20个类别的预测结果。

import torch
from PIL import Image
from torchvision import transforms
import torch.nn as nn

class CNN(nn.Module):#类的名称。
    def __init__(self):  #输入大小(3, 256, 256)
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential( # 将多个层组合成一起。
            nn.Conv2d(      #2d一般用于图像,3d用于视频数据(多一个时间维度),1d -一般用于结构化的序列数据
                in_channels = 3,    # 图像通道个数,1表示灰度图(确定了卷积核组中的个数)
                out_channels = 16,  # 要得到多少个特征图,卷积核的个数
                kernel_size = 5,    # 卷积核大小, 5*5
                stride=1,           # 步长
                padding=2,          # 参数5,1,2可以使卷积核处理后的结果大小与处理前的数据大小相同
            ),
            nn.ReLU(),  # relu层,不会改变特征图的大小
            nn.MaxPool2d(kernel_size = 2),  # 进行池化操作(2x2区域)
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(16, 32, 5, 1, 2),
            nn.ReLU(),  # relu层
            nn.Conv2d(32, 32, 5, 1, 2),
            nn.ReLU(),
            nn.MaxPool2d(2),
        )
        self.conv3 = nn.Sequential(  # 输入 (32,64,64)
            nn.Conv2d(32, 128, 5, 1, 2),
            nn.ReLU(),
        )
        self.out = nn.Linear(128 * 64 * 64, 20)  # 全连接层得到的结果

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = x.view(x.size(0), -1)
        output = self.out(x)
        return output
2、模型加载与预测

        上文中训练好的模型保存为.pth的文件,现在加载模型并使用它对单张图像进行预测。

# 定义一个函数来进行预测
def predict_image(image_path, model_path='fooddataset2_cnn_model.pth'):
    # 加载模型
    model = torch.load(model_path, map_location=torch.device('cpu'))  # 加载模型并确保在CPU上运行
    model.eval()  # 将模型设为评估模式,不更新梯度

    # 图像预处理
    preprocess = transforms.Compose([
        transforms.Resize([256, 256]),  # 调整图像大小为256x256像素
        transforms.ToTensor(),  # 将图像转换为Tensor格式
    ])

    # 读取图像
    image = Image.open(image_path)  # 使用PIL库打开图像

    # 预处理图像
    image_tensor = preprocess(image)  # 对图像进行预处理
    image_tensor = image_tensor.unsqueeze_(0)  # 添加一个维度作为batch维度

    # 使用模型进行预测
    with torch.no_grad():  # 在预测阶段不需要计算梯度
        output = model(image_tensor)  # 将预处理后的图像输入模型进行预测

    # 获取预测类别
    _, predicted_idx = torch.max(output, 1)  # 获取预测结果中概率最高的类别索引

    food_classes = {
        0: "哈密瓜", 1: "圣女果", 2: "巴旦木", 3: "坚果", 4: "汉堡",
        5: "火龙果", 6: "炸鸡", 7: "瓜子", 8: "澳洲牛肉", 9: "白萝卜",
        10: "胡萝卜", 11: "草莓", 12: "菠萝", 13: "薯条", 14: "鸡蛋",
        15: "蛋挞", 16: "菠菜", 17: "骨肉相连", 18: "鸡翅", 19: "八宝粥"
    }

    # 获取食物类别名称
    predicted_label = predicted_idx.item()
    food_name = food_classes[predicted_label]

    return food_name

# 使用函数预测图像
image_path = r'C:\Users\tonyzhu\Desktop\哈密瓜.jpg'  # 替换为你的图像路径
predicted_label = predict_image(image_path)  # 调用预测函数获取预测标签
print(f"The predicted label is: {predicted_label}")  # 打印预测标签

# 注释: 定义了一个函数来预测输入图像的类别,包括模型加载、图像预处理和预测过程。
3、结果展示

下课!!! 

  • 9
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以帮你解答这个问题。以下是一个使用PyTorch构建CNN模型对CIFAR-10数据集进行分类的示例代码: ``` import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.transforms as transforms # 加载数据集并进行预处理 transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform) trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=2) testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform) testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=2) # 定义模型 class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(3, 6, 5) self.pool = nn.MaxPool2d(2, 2) self.conv2 = nn.Conv2d(6, 16, 5) self.fc1 = nn.Linear(16 * 5 * 5, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x): x = self.pool(F.relu(self.conv1(x))) x = self.pool(F.relu(self.conv2(x))) x = x.view(-1, 16 * 5 * 5) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x net = Net() # 定义损失函数和优化器 criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) # 训练模型 for epoch in range(2): # 多次循环数据集 running_loss = 0.0 for i, data in enumerate(trainloader, 0): # 获取输入数据 inputs, labels = data # 梯度清零 optimizer.zero_grad() # 前向传播,反向传播和优化 outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() # 打印统计信息 running_loss += loss.item() if i % 2000 == 1999: # 每2000个小批量数据打印一次 print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000)) running_loss = 0.0 print('Finished Training') # 在测试集上测试模型 correct = 0 total = 0 with torch.no_grad(): for data in testloader: images, labels = data outputs = net(images) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print('Accuracy of the network on the 10000 test images: %d %%' % ( 100 * correct / total)) ``` 在上面的代码示例中,我们使用了`torchvision`模块来加载和预处理CIFAR-10数据集,并使用了`DataLoader`来生成训练和测试数据集。 我们定义了一个包含两个卷积层和三个全连接层的CNN模型,并使用了ReLU激活函数和最大池化操作来提取图像中的特征。 在训练模型时,我们使用了交叉熵损失函数和随机梯度下降优化器。我们将数据集循环多次,并使用每个小批量数据进行前向传播、反向传播和优化。在每个小批量数据处理完后,我们打印损失值的统计信息。 最后,我们在测试集上测试了模型,并计算了模型的准确率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值