使用神经网络实现图片分类

一.项目内容

根据已有的训练集去训练模型,再在验证集上验证模型的结果,用准确率来判断模型的好坏

训练集为带标签的数据

如:标签为00的图片如下

此时根据带标签数据280*11,即每类图片各有280张。去验证30*11的验证集,即每类图片30张的准确率情况。

二.代码实现

2.1导入python包

import random
import torch
import torch.nn as nn
import numpy as np
import os
from tqdm import tqdm

from PIL import Image #读取图片数据
from torch.utils.data import Dataset,DataLoader
from torchvision import transforms
import time

2.2设计一个函数固定随机数

#作用是为了固定随机数
def seed_everything(seed):
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    torch.backends.cudnn.benchmark=False
    torch.backends.cudnn.deterministic=True
    random.seed(seed)
    np.random.seed(seed)
    os.environ['PYTHONHASHSEED']=str(seed)

######################################################
seed_everything(1)
######################################################

**函数定义 seed_everything**:这个函数用于设置随机数种子,确保实验的可重复性。它包括 PyTorch 和 NumPy 的随机种子设置,以及 Python 内部 hash 的种子设置。

2.3定义图片的高度和宽度

HW=224

定义处理图片的高度和宽度为 224 像素(一般用在卷积神经网络中,如 ResNet)

2.4定义存储图片的空间大小

def read_file(path):
    for i in tqdm(range(11)):
        # tqdm 用来显示循环的进度
        file_dir=path+"/%02d"%i
        file_list=os.listdir(file_dir)   # 列出文件夹下所有的文件名字
        xi=np.zeros((len(file_list),HW,HW,3),dtype=np.uint8)
        yi=np.zeros(len(file_list),dtype=np.uint8)

**函数定义 read_file**:读取指定路径中的数据。

  • tqdm 用于显示读取文件的进度。
  • os.listdir 列出指定文件夹中的所有文件。
  • xiyi 分别用于存储图像数据和对应标签。在这里,xi 是一个 4D 数组,yi 是一个 1D 数组。

2.5将图片调整大小并装入到指定空间中

        for j,img_name in enumerate(file_list):
            img_path=os.path.join(file_dir,img_name)
            img=Image.open(img_path)
            img=img.resize((HW,HW))
            xi[j,...]=img
            yi[j]=i

取图片和标签:遍历每个图像文件名,加载图像并调整为 224x224 像素,然后将其存入 xi 和相应的类别标签存入 yi

2.6将每类数据进行拼接

        if i==0:
            X=xi
            Y=yi
        else:
            X=np.concatenate((X,xi),axis=0)
            Y=np.concatenate((Y,yi),axis=0)

组合数据:如果是第一个类别,直接将数据赋值给 XY;如果不是,将当前类别的数据拼接到已有数据上。

2.7显示读取数据的进度

    print("读到了%d个数据"%len(Y))
    return X,Y

2.8数据增强和预处理

train_transform=transforms.Compose(
    [
        transforms.ToPILImage(),  
        transforms.RandomResizedCrop(224),
        transforms.RandomRotation(50),
        transforms.ToTensor()
    ]
)

训练时的数据增强:定义一个数据处理管道,用于对训练图像进行数据增强。这包括将 NumPy 数组转换为 PIL 图像、随机裁切、旋转,以及转换为 PyTorch 张量。

val_transform=transforms.Compose(
    [
        transforms.ToPILImage(),
        transforms.ToTensor()
    ]
)

验证时的数据处理:定义验证时的处理流程,仅包括转换为张量,因为验证数据不需要增强。

2.9Dataset 类定义

class food_Dataset(Dataset):
    def __init__(self,path,mode='train'):
        self.X,self.Y=read_file(path)
        self.Y=torch.LongTensor(self.Y)   # 标签转为长整形
        if mode=="train":
            self.transform=train_transform
        else:
            self.transform=val_transform

**自定义数据集 food_Dataset**:继承 PyTorch 的 Dataset 类。

  • 在初始化方法中读取数据,并确定数据处理方式(增强或标准处理)。
    def __getitem__(self,item):
        return self.transform(self.X[item]),self.Y[item]

返回数据集大小:实现 __len__ 方法,返回数据集长度。

train_path=r"C:\Users\AIERXUAN\PycharmProjects\machinelearning\deep learning\food-11\training\labeled"
val_path=r"C:\Users\AIERXUAN\PycharmProjects\machinelearning\deep learning\food-11\validation"

训练和验证数据路径:定义训练和验证数据集的路径。

train_set=food_Dataset(train_path,"train")
val_set=food_Dataset(val_path,"val")

实例化数据集:分别创建训练集和验证集的实例。

train_loader=DataLoader(train_set,batch_size=16,shuffle=True)
val_loader=DataLoader(val_set,batch_size=16,shuffle=True)

数据加载器:使用 DataLoader 将数据集封装成可迭代的加载器,设置批次大小为 16,并打乱数据。

2.10定义模型

class myModel(nn.Module):
    def __init__(self,num_class):   #num_class为分类的个数
        super(myModel,self).__init__()
        #3*224*224->512*7*7->拉直->全连接分类
        self.conv1=nn.Conv2d(3, 64, 3, 1, 1)  # ->64*224*224
        self.bn1=nn.BatchNorm2d(64)  # 归一化
        self.relu=nn.ReLU()  # 激活
        self.pool1=nn.MaxPool2d(2)  # 池化 64*112*112


        self.layer1=nn.Sequential(
            nn.Conv2d(64, 128, 3, 1, 1), # ->128*112*112
            nn.BatchNorm2d(128), # 归一化
            nn.ReLU(),  # 激活
            nn.MaxPool2d(2)  # 池化 128*56*56
        )

        self.layer2=nn.Sequential(
             nn.Conv2d(128, 256, 3, 1, 1),  # ->256*112*112
             nn.BatchNorm2d(256) , # 归一化
             nn.ReLU(),  # 激活
             nn.MaxPool2d(2)  # 池化 256*28*28
        )

        self.layer3 = nn.Sequential(
            nn.Conv2d(256, 512, 3, 1, 1),  # ->512*28*28
            nn.BatchNorm2d(512),  # 归一化
            nn.ReLU(),  # 激活
            nn.MaxPool2d(2)  # 池化 512*14*14
        )

        self.pool2=nn.MaxPool2d(2)   #512*7*7
        self.fc1=nn.Linear(25088,1000)        #25088->1000
        self.relu2=nn.ReLU()
        self.fc2 = nn.Linear(1000, num_class)  #1000-11

    def forward(self,x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.pool1(x)
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.pool2(x)
        x = x.view(x.size()[0],-1)  #保持第一维不变,剩下的维全部加到第一维上去
        x = self.fc1(x)
        x = self.relu2(x)
        x = self.fc2(x)
        return x

**定义模型 myModel**:继承自 PyTorch 的 nn.Module,在初始化方法中定义网络的各层结构。

前向传播:实现 forward 方法,定义输入数据如何通过网络传播,从输入到输出的过程。

2.11定义训练和验证函数

def train_val(model,train_loader,val_loader,device,epochs,optimizer,loss,save_path):
    model=model.to(device)

    plt_train_loss=[]
    plt_val_loss=[]

    # 分类任务使用准确率来判断模型的好坏
    plt_train_acc =[]
    plt_val_acc = []

#保存准确率较高的模型
    max_acc=0.0

    for epoch in range(epochs): #冲锋的号角
        train_loss=0.0
        val_loss=0.0

        train_acc= 0.0
        val_acc = 0.0


        start_time=time.time()

        model.train()  #模型调为训练模式
        for batch_x,batch_y in train_loader:
            x,target=batch_x.to(device),batch_y.to(device)
            pred=model(x)
            train_bat_loss=loss(pred,target)
            train_bat_loss.backward()
            optimizer.step() #更新模型的作用
            optimizer.zero_grad()
            train_loss += train_bat_loss.cpu().item()
            train_acc+=np.sum(np.argmax(pred.detach().cpu().numpy(),axis=1)==target.cpu().numpy())
        plt_train_loss.append(train_loss/train_loader.__len__())
        plt_train_acc.append(train_acc/train_loader.dataset.__len__())  #记录准确率

        model.eval() #验证模式
        with torch.no_grad():
        #所有在模型上的训练会计算梯度,而在验证集上是不需要更新模型的
            for batch_x,batch_y in val_loader:
                x, target = batch_x.to(device), batch_y.to(device)
                pred = model(x)
                val_bat_loss = loss(pred, target)
                val_loss+=val_bat_loss.cpu().item()
                val_acc += np.sum(np.argmax(pred.detach().cpu().numpy(), axis=1) == target.cpu().numpy())
        plt_val_loss.append(val_loss / val_loader.__len__())
        plt_val_acc.append(val_acc / val_loader.dataset.__len__())  # 记录准确率
        if val_acc>max_acc:
            torch.save(model,save_path)
            max_acc=val_acc

        print("[%03d/%03d] %2.2f sec(s) Trainloss: %.6f |Valloss:%.6f Trainacc : %.6f |valacc: %.6f"%\
              (epoch,epochs,time.time()-start_time,plt_train_loss[-1],plt_val_loss[-1],plt_train_acc[-1],
                plt_val_acc[-1]))

    # 画图呈现
    plt.plot(plt_train_loss)
    plt.plot(plt_val_loss)
    plt.title("loss图")
    plt.legend(["train", "val"])
    plt.show()

    plt.plot(plt_train_acc)
    plt.plot(plt_val_acc)
    plt.title("acc图")
    plt.legend(["train", "val"])
    plt.show()

2.12模型实例化和超参数设置

model=myModel(11)  #定义一个模型
# from torchvision.models import resnet18
# model=resnet18(pretrained=True)   #表示模型即继承架构又继承既有的参数
# in_features=model.fc.in_features
# model.fc=nn.Linear(in_features,11)   #设计分类头的大小


#定义超参
lr=0.001
loss=nn.CrossEntropyLoss()
optimizer=torch.optim.AdamW(model.parameters(),lr=lr,weight_decay=1e-4)
device="cuda" if torch.cuda.is_available() else "cpu"
save_path="model_save/sample_class_model.pth"
epochs=5

train_val(model,train_loader,val_loader,device,epochs,optimizer,loss,save_path)

2.13结果呈现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值