当前位置: 首页 > news >正文

深度学习——基于卷积神经网络实现食物图像分类【1】(datalodar处理方法)

文章目录

    • 1. 项目概述
    • 2. 环境准备
    • 3. 数据准备与预处理
      • 3.1 数据转换定义
      • 3.2 数据文件准备
      • 3.3 自定义数据集类
      • 3.4 创建数据加载器
    • 4. 模型构建
    • 5. 训练与评估
      • 5.1 设备选择
      • 5.2 训练函数
      • 5.3 测试函数
      • 5.4 模型训练与评估
    • 6. 关键点解析
    • 7. 可能的改进方向
    • 8. 完整代码
    • 9. 总结

1. 项目概述

在这个项目中,我们将使用PyTorch框架构建一个卷积神经网络(CNN)来实现食物图像分类任务。我们的数据集包含20种不同的食物类别,包括八宝粥、巴旦木、白萝卜、板栗等常见食物。本文将详细介绍从数据准备、模型构建到训练和评估的完整流程。

2. 环境准备

首先,我们需要导入必要的Python库:

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

3. 数据准备与预处理

3.1 数据转换定义

我们定义了两组数据转换,分别用于训练集和验证集:

data_transforms = {'train': transforms.Compose([transforms.Resize([256,256]),   # 统一图像大小为256x256transforms.ToTensor(),          # 转换为PyTorch张量]),'valid': transforms.Compose([transforms.Resize([256,256]),transforms.ToTensor(),]),
}

3.2 数据文件准备

我们编写了一个函数来生成包含图像路径和标签的文本文件:

def train_test_file(root, dir):file_txt = open(dir+'.txt','w')path = os.path.join(root, dir)for roots, directories, files in os.walk(path):if len(directories) != 0:dirs = directorieselse:now_dir = roots.split('\\')for file in files:path_1 = os.path.join(roots, file)file_txt.write(path_1+' '+str(dirs.index(now_dir[-1]))+'\n')file_txt.close()# 调用函数生成训练集和测试集文件
root = r'.\食物分类\food_dataset'
train_dir = 'train'
test_dir = 'test'
train_test_file(root, train_dir)
train_test_file(root, test_dir)
  • 生成的 txt 文件如下所示,包含图片的路径、类别和对应的标签

在这里插入图片描述

3.3 自定义数据集类

我们创建了一个继承自torch.utils.data.Dataset的自定义数据集类:

class food_dataset(Dataset):def __init__(self, file_path, transform=None):self.file_path = file_pathself.imgs = []self.labels = []self.transform = transformwith 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))return image, label

3.4 创建数据加载器

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=16, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=16, shuffle=True)

4. 模型构建

我们定义了一个包含三个卷积层的CNN模型:

class CNN(nn.Module):def __init__(self):super(CNN, self).__init__()# 第一个卷积块self.conv1 = nn.Sequential(nn.Conv2d(3, 16, 5, 1, 2),  # (16,256,256)nn.ReLU(),nn.MaxPool2d(2),             # (16,128,128))# 第二个卷积块self.conv2 = nn.Sequential(nn.Conv2d(16, 32, 5, 1, 2),  # (32,128,128)nn.ReLU(),nn.MaxPool2d(2),            # (32,64,64))# 第三个卷积块self.conv3 = nn.Sequential(nn.Conv2d(32, 64, 5, 1, 2),  # (64,64,64)nn.ReLU(),nn.MaxPool2d(2),             # (64,32,32))# 全连接层self.out = nn.Linear(64*32*32, 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

5. 训练与评估

5.1 设备选择

device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
print(f"Using {device} device")
model = CNN().to(device)

5.2 训练函数

def train(dataloader, model, loss_fn, optimizer):model.train()batch_size_num = 1for X, y in dataloader:X, y = X.to(device), y.to(device)pred = model(X)loss = loss_fn(pred, y)optimizer.zero_grad()loss.backward()optimizer.step()if batch_size_num % 100 == 0:print(f"loss: {loss.item():>7f} [number:{batch_size_num}]")batch_size_num += 1

5.3 测试函数

labels = {0:"八宝粥", 1:"巴旦木", 2:"白萝卜", 3:"板栗", 4:"菠萝", 5:"草莓",6:"蛋", 7:"蛋挞", 8:"骨肉相连", 9:"瓜子", 10:"哈密瓜", 11:"汉堡",12:"胡萝卜", 13:"火龙果", 14:"鸡翅", 15:"青菜", 16:"生肉", 17:"圣女果",18:"薯条", 19:"炸鸡"}def Test(dataloader, model, loss_fn):size = len(dataloader.dataset)num_batches = len(dataloader)model.eval()test_loss, correct = 0, 0with torch.no_grad():for X, y in dataloader:X, y = X.to(device), y.to(device)pred = model(X)test_loss += loss_fn(pred, y).item()correct += (pred.argmax(1) == y).type(torch.float).sum().item()test_loss /= num_batchescorrect /= sizeresult = zip(pred.argmax(1).tolist(), y.tolist())for i in result:print(f"当前测试的结果为:{labels[i[0]]}\t当前真实的结果为:{labels[i[1]]}")print(f"\n最终测试结果: \n 准确率:{(100*correct):.2f}%, 平均损失:{test_loss:.4f}")

5.4 模型训练与评估

loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)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)

6. 关键点解析

  1. 数据预处理

    • 统一图像大小(256x256)
    • 转换为PyTorch张量
    • 自动归一化到[0,1]范围
  2. 模型结构

    • 三个卷积块,每个块包含卷积层、ReLU激活和最大池化
    • 逐步增加通道数(16→32→64)
    • 逐步减小特征图尺寸(256→128→64→32)
    • 全连接层输出20个类别的概率分布
  3. 训练技巧

    • 使用Adam优化器,学习率设为0.001
    • 交叉熵损失函数
    • 批量大小为16
    • 训练10个epoch
  4. 评估方法

    • 计算整体准确率
    • 计算平均损失
    • 展示部分预测结果与真实标签的对比

7. 可能的改进方向

  1. 数据增强:在训练时添加随机翻转、旋转等增强技术,提高模型泛化能力
  2. 学习率调度:使用学习率衰减策略,如StepLR或ReduceLROnPlateau
  3. 模型优化:尝试更深的网络结构或预训练模型(如ResNet)
  4. 正则化:添加Dropout层或L2正则化防止过拟合
  5. 超参数调优:系统性地调整学习率、批量大小等超参数

8. 完整代码

import torch
import torchvision.models as models
from torch import nn
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
from PIL import Image
import numpy as np
import osdata_transforms = { #字典'train':transforms.Compose([            #对图片预处理的组合transforms.Resize([256,256]),   #对数据进行改变大小transforms.ToTensor(),          #数据转换为tensor]),'valid':transforms.Compose([transforms.Resize([256,256]),transforms.ToTensor(),]),
}def train_test_file(root,dir):file_txt = open(dir+'small.txt','w')path = os.path.join(root,dir)for roots,directories,files in os.walk(path):if len(directories) !=0:dirs = directorieselse:now_dir = roots.split('\\')for file in files:path_1 = os.path.join(roots,file)print(path_1)file_txt.write(path_1+' '+str(dirs.index(now_dir[-1]))+'\n')file_txt.close()root = r'.\食物分类\food_dataset'
train_dir = 'train'
test_dir = 'test'
train_test_file(root,train_dir)
train_test_file(root,test_dir)#Dataset是用来处理数据的
class food_dataset(Dataset):        # food_dataset是自己创建的类名称,可以改为你需要的名称def __init__(self,file_path,transform=None):    #类的初始化,解析数据文件txtself.file_path = file_pathself.imgs = []self.labels = []self.transform = transformwith open(self.file_path) as f: #是把train.txt文件中的图片路径保存在self.imgssamples = [x.strip().split(' ') for x in f.readlines()]for img_path,label in samples:self.imgs.append(img_path)  #图像的路径self.labels.append(label)   #标签,还不是tensor# 初始化:把图片目录加到selfdef __len__(self):  #类实例化对象后,可以使用len函数测量对象的个数return  len(self.imgs)#training_data[1]def __getitem__(self, idx):    #关键,可通过索引的形式获取每一个图片的数据及标签image = Image.open(self.imgs[idx])  #读取到图片数据,还不是tensor,BGRif self.transform:                  #将PIL图像数据转换为tensorimage = self.transform(image)   #图像处理为256*256,转换为tensorlabel = self.labels[idx]    #label还不是tensorlabel = torch.from_numpy(np.array(label,dtype=np.int64))    #label也转换为tensorreturn image,label
#training_data包含了本次需要训练的全部数据集
training_data = food_dataset(file_path='train.txt', transform=data_transforms['train'])
test_data = food_dataset(file_path='test.txt', transform=data_transforms['valid'])#training_data需要具备索引的功能,还要确保数据是tensor
train_dataloader = DataLoader(training_data,batch_size=16,shuffle=True)
test_dataloader = DataLoader(test_data,batch_size=16,shuffle=True)'''判断当前设备是否支持GPU,其中mps是苹果m系列芯片的GPU'''
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
print(f"Using {device} device")   #字符串的格式化,CUDA驱动软件的功能:pytorch能够去执行cuda的命令
# 神经网络的模型也需要传入到GPU,1个batch_size的数据集也需要传入到GPU,才可以进行训练''' 定义神经网络  类的继承这种方式'''
class CNN(nn.Module): #通过调用类的形式来使用神经网络,神经网络的模型,nn.mdouledef __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,      # 卷积核大小 3×3stride=1,           # 步长padding=2,          # 一般希望卷积核处理后的结果大小与处理前的数据大小相同,效果会比较好),                      # 输出的特征图为(16,256,256)nn.ReLU(),  # Relu层,不会改变特征图的大小nn.MaxPool2d(kernel_size=2),    # 进行池化操作(2×2操作),输出结果为(16,128,128))self.conv2 = nn.Sequential(nn.Conv2d(16,32,5,1,2),  #输出(32,128,128)nn.ReLU(),  #Relu层  (32,128,128)nn.MaxPool2d(kernel_size=2),    #池化层,输出结果为(32,64,64))self.conv3 = nn.Sequential(nn.Conv2d(32, 64, 5, 1, 2),  # 输出(64,64,64)nn.ReLU(),  # Relu层  (64,64,64)nn.MaxPool2d(kernel_size=2),  # 池化层,输出结果为(64,32,32))self.out = nn.Linear(64*32*32,20)  # 全连接层得到的结果def forward(self,x):   #前向传播,你得告诉它 数据的流向 是神经网络层连接起来,函数名称不能改x = self.conv1(x)x = self.conv2(x)x = self.conv3(x)x = x.view(x.size(0),-1)    # flatten操作,结果为:(batch_size,32 * 64 * 64)output = self.out(x)return output
model = CNN().to(device) #把刚刚创建的模型传入到GPU
print(model)def train(dataloader,model,loss_fn,optimizer):model.train() #告诉模型,我要开始训练,模型中w进行随机化操作,已经更新w,在训练过程中,w会被修改的
# pytorch提供2种方式来切换训练和测试的模式,分别是:model.train() 和 mdoel.eval()
# 一般用法是:在训练开始之前写上model.train(),在测试时写上model.eval()batch_size_num = 1for X,y in dataloader:              #其中batch为每一个数据的编号X,y = X.to(device),y.to(device) #把训练数据集和标签传入cpu或GPUpred = model.forward(X)         # .forward可以被省略,父类种已经对此功能进行了设置loss = loss_fn(pred,y)          # 通过交叉熵损失函数计算损失值loss# Backpropagation 进来一个batch的数据,计算一次梯度,更新一次网络optimizer.zero_grad()           # 梯度值清零loss.backward()                 # 反向传播计算得到每个参数的梯度值woptimizer.step()                # 根据梯度更新网络w参数loss_value = loss.item()        # 从tensor数据种提取数据出来,tensor获取损失值if batch_size_num %100 ==0:print(f"loss: {loss_value:>7f} [number:{batch_size_num}]")batch_size_num += 1labels={0:"八宝粥",1:"巴旦木",2:"白萝卜",3:"板栗",4:"菠萝",5:"草莓",6:"蛋",7:"蛋挞",8:"骨肉相连",9:"瓜子",10:"哈密瓜",11:"汉堡",12:"胡萝卜",13:"火龙果",14:"鸡翅",15:"青菜",16:"生肉",17:"圣女果",18:"薯条",19:"炸鸡"}def Test(dataloader,model,loss_fn):size = len(dataloader.dataset)num_batches = len(dataloader)  # 打包的数量model.eval()        #测试,w就不能再更新test_loss,correct =0,0with torch.no_grad():       #一个上下文管理器,关闭梯度计算。当你确认不会调用Tensor.backward()的时候for X,y in dataloader:X,y = X.to(device),y.to(device)pred = model(X) #等价于model.forward(X)test_loss += loss_fn(pred,y).item() #test_loss是会自动累加每一个批次的损失值correct += (pred.argmax(1) == y).type(torch.float).sum().item()a = (pred.argmax(1) == y) #dim=1表示每一行中的最大值对应的索引号,dim=0表示每一列中的最大值对应的索引号b = (pred.argmax(1) == y).type(torch.float)test_loss /= num_batches #能来衡量模型测试的好坏correct /= size  #平均的正确率result = zip(pred.argmax(1).tolist(), y.tolist())for i in result:print(f"当前测试的结果为:{labels[i[0]]}\t当前真实的结果为:{labels[i[1]]}")print(f"\n最终测试结果: \n 准确率:{(100*correct):.2f}%, 平均损失:{test_loss:.4f}")loss_fn = nn.CrossEntropyLoss()  #创建交叉熵损失函数对象,因为手写字识别一共有十种数字,输出会有10个结果optimizer = torch.optim.Adam(model.parameters(),lr=0.001) #创建一个优化器,SGD为随机梯度下降算法
# # params:要训练的参数,一般我们传入的都是model.parameters()
# # lr:learning_rate学习率,也就是步长
#
# # loss表示模型训练后的输出结果与样本标签的差距。如果差距越小,就表示模型训练越好,越逼近真实的模型
# train(train_dataloader,model,loss_fn,optimizer) #训练1次完整的数据。多轮训练
# Test(test_dataloader,model,loss_fn)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)

9. 总结

本文详细介绍了使用PyTorch实现食物图像分类的完整流程,包括数据准备、模型构建、训练和评估。通过这个项目,我们学习到了:

  1. 如何自定义数据集类来处理图像数据
  2. 如何构建一个有效的CNN模型
  3. PyTorch训练循环的基本结构
  4. 模型评估的方法和指标

这个项目可以作为计算机视觉任务的入门实践,为后续更复杂的图像分类任务打下基础。读者可以尝试修改模型结构或调整超参数,观察对模型性能的影响。

相关文章:

  • 商用密码基础知识介绍(上)
  • 区块链与人工智能的融合:从信任到智能的IT新引擎
  • JAVA中关于Animal和Dog类的类型转换,可能出现ClassCastException的情况
  • PyTorch张量操作中dim参数的核心原理与应用技巧:
  • 使用DuckDB查询DeepSeek历史对话
  • 《生成式人工智能服务管理暂行办法》合规的“三重门”与破局之道
  • LeetCode面试经典150题—旋转数组—LeetCode189
  • 数据结构 学习 图 2025年6月14日 12点57分
  • linux开机原理以及如何开关机-linux023
  • 基于ssm专利服务系统微信小程序源码数据库文档
  • React 第三方状态管理库的比较与选择
  • Spring中观察者模式的应用
  • UE5反射系统分析(一)generated.h
  • uniapp 腾讯地图服务
  • 1.0 前言(Python系列教程)
  • 面试问题总结——关于C++(四)
  • spring如何处理bean的循环依赖
  • java面试总结-20250610
  • 开疆智能ModbusTCP转Devicenet网关连接FANUC机器人配置案例
  • Elasticsearch高效文章搜索实践
  • vs做网站如何放背景图/郑州网络推广平台有哪些
  • 电脑维护网站模板/免费个人网站模板
  • 网站怎么登陆后台/常用的网络营销方法及效果
  • 做网站背景/微信指数怎么看
  • 网站建设素材/营销推广活动方案
  • 网站手机css模板下载/推广app赚钱项目