提示:山花如绣颊,江火似流萤。


这里若是你要写论文用到这篇文章,你使用的技术主要包括深度学习框架PyTorch ,Web框架:Flask,图像处理:PIL,Numpy,机器学习算法:KNN,前端:HTML,CSS,JavaScript。

五 分类模块

项目简介

这是一个基于深度学习的图片识别系统(其实人脸识别也是同样的原理),集成了图像相似性搜索,图像去噪,商品分类等功能,通过PyTorch深度学习框架开发,使用Flask提供Web界面,这个项目是一个很常见的项目,但是对于理解深度学习是一个很好项目、等你随着我看完这个项目,你会知道很多领域其实都用到了这些功能。上述已经实现了图像去噪,这里主要是实现分类模块。分类模块可以做很多东西,比如人脸识别某种程度上也是能做的,比如手写字识别都是如此,并且这里流程和之前几乎差不多

5.1 模型框架

一般来说卷积改变通道形状不变,311,池化形状减半,210(不知道这个的请翻看深度学习项目step1)
在这里插入图片描述

5.2 构建文件目录

将要处理的大量数据放在common目录下,数据包是dataset
在image_classification包下创建以下文件夹
config.py 项目配置
data.py 数据处理
model.py 模型定义模块
train.py 数据加载,预处理,

5.3 配置文件

可以看出和之前的文件构建是几乎一致的,依然是和上文一样,首先构建项目配置,这里几乎不用改变,直接将其复制过来便可。配置文件中放的都是项目一些所需要的配置项。
config.py

import os

import torch

from common.utils import seed_everything

current_dir = os.getcwd()
parent_dir = os.path.dirname(current_dir)# 上一级目录
IMG_HEIGHT = 256
IMG_WIDTH = 256 # 图片尺寸
SEED = 42 # 随机种子
seed_everything(SEED)
BATCH_SIZE = 128
Train_ratio = 0.7 # 训练集比例
VAL_ratio = 0.8
Noise_level = 0.1 # 噪声等级
Epochs = 1
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

Learning_rate = 1e-3 # 学习率

5.4 数据处理模块

和前两个模型一样,这个模块要做的其实就是定义模块,也就是假如给我们文件地址,我们要返回此文件的数据,这里只需要对深度学习项目step2中的数据读取部分做一个稍微的调整便可,调整的部分,首先这里需要一个标签文件,也就是每一个图片所对应的一个标签,之前说过若是之间根据地址读取文件会导致读取的文件是按照字典的顺序来进行排序的,若是去噪模块,是无所谓的,取一个图片,加上噪音,用这个有噪音的图片和原来的图片做一个损失,得到模型,有噪音的图片和原始图片顺序无所谓,而这里我们做的是分类,若是文件和对应的标签不一致,就会导致无法训练,所以这里我们要对读取的文件进行排序,排序之后的数据和标签数据的顺序是一致的,这样直接进行读取便可,

import os
import re

import pandas as pd
from PIL import Image
from torch.utils.data import Dataset
from torchvision import transforms


class MyDataset(Dataset):
    def __init__(self, img_dir, transform=None):
        # img_dir:指定图像文件所在的目录路径,用于定位和读取图像数据。
        # transform:图像变换操作,用于对图像进行预处理(如缩放、裁剪、转换为张量等)。当值为 None 时,表示不进行任何变换处理,读取图片因为要进行维度的转化所以需要这个参数
        self.img_dir = img_dir
        self.transform = transform
        self.img_name_list = sorted(os.listdir(img_dir), key=lambda x: int(re.search(r'\d+', x).group()))
        # print(self.img_name_list)
        # 读取分类标签文件CSV文件,是一个DataFrame,创建一个字典,将标签id映射到标签名称,可以根据这个id直接得到标签所对应的图片,和此图片所对应的标签
        self.lables = pd.read_csv("../common/fashion-labels.csv")
        self.lables_dict = dict(zip(self.lables['id'], self.lables['target']))

    def __len__(self):
        return len(self.img_name_list)

    def __getitem__(self, idx):
        # 先找文件的完整路径
        img_name = self.img_name_list[idx]
        img_path = os.path.join(self.img_dir, img_name)
        # 读取图片,但是要注意图片的格式,使用Image读取图片的时候默认是RGBA
        # 这里我使用的是RGB格式的图片,所以这里需要转化为RGB格式
        img = Image.open(img_path).convert('RGB')
        if self.transform:
            img = self.transform(img)
        else:
            raise ValueError('transform is None')

        label = self.lables_dict[idx]
        return img, label  # 返回原始图片 和此图片对应的标签


if __name__ == '__main__':
    # 测试数据集排序是否正确
    img_dir = '../common/dataset'
    transform = transforms.Compose([
        transforms.Resize((64, 64)),
        transforms.ToTensor(),
    ])
    # 测试此模块的代码是否正确
    dataset = MyDataset(img_dir, transform=transform)
    # 打印第一个样本的图像和标签
    img, label = dataset[0]
    print(img.shape)
    print(label)

5.5 模型定义

数据处理好之后,接下来就是定义模型,按照上述的模型框架,我们很容易就写出模型的,一般来说卷积改变通道而形状不变,参数设置为311,而池化形状减半通道数不变,参数通常设置为210(不知道这个的请翻看深度学习项目step1) 编码器我们使用前面卷积池化定义,解码器我们使用一个线性层便可

import torch
import torch.nn as nn
# 定义模型
class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.encoder = nn.Sequential(
            # 先卷积层
            nn.Conv2d(3, 8, kernel_size=3, stride=1, padding=1),
            # 再通过一个激活函数
            nn.ReLU(),
            # 再池化层
            nn.MaxPool2d(kernel_size=2, stride=2),
            # 通过一个激活函数
            nn.ReLU(),
            # 再卷积层
            nn.Conv2d(8, 16, kernel_size=3,
                      stride=1, padding=1),
            # 再通过一个激活函数
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),
            # 再通过一个激活函数
            nn.ReLU(),
            # 再扁平化处理
            nn.Flatten()
            #或者重新定义形状

        )
        self.decoder = nn.Sequential(
            # 再通过一个线性层进行分类,最后是分为5类
            nn.Linear(1 * 4096, 5)
        )

    def forward(self, x):
        x = self.encoder(x)
        x = self.decoder(x)
        return x


if __name__ == '__main__':
    # 测试模型是否正确
    input = torch.randn(1, 3, 64, 64)
    model = MyModel()
    output = model(input)
    print(output.shape)

5.6 训练模块

模型定义好了之后接下来就是训练模型,这个模块主要实现的就是加载数据,数据集划分,训练模型,测试模型。这里做的是分类,所以这里的损失和之前定义MSELoss是不一致的,之前相当于是回归任务,这里是预测任务所以使用的损失是CrossEntropyLoss,其他来说几乎是一致的

from tqdm import tqdm
from image_classification.data import MyDataset
from image_denoising.config import device
import torch
import torch.nn as nn
from torch.utils.data import random_split
from model import MyModel
from torchvision import transforms
from matplotlib import pyplot as plt
# 建议使用 weights_only=True 参数

img_dir = '../common/dataset'
transform = transforms.Compose([
    transforms.Resize((64, 64)),
    transforms.ToTensor(),
])

# 测试此模块的代码是否正确
dataset = MyDataset(img_dir, transform=transform)
# 数据集划分
train_set, test_set = random_split(dataset, [0.7, 0.3])
# 创建数据加载器
train_loader = torch.utils.data.DataLoader(train_set, batch_size=64, shuffle=True, drop_last=True)
test_loader = torch.utils.data.DataLoader(test_set, batch_size=64, shuffle=False)

#训练模型
model = MyModel()
#定义损失函数和优化器
loss = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

num_epochs = 10
model.train()
model.to(device)
min_loss = float('inf')
for epoch in tqdm(range(num_epochs)):
    train_loss = 0
    for inputs, labels in train_loader:
        inputs = inputs.to(device)
        labels = labels.to(device)
        # 前向传播
        outputs = model(inputs)
        # 计算损失
        loss_value = loss(outputs, labels)
        # 反向传播
        loss_value.backward()
        optimizer.step()
        optimizer.zero_grad()
        train_loss += loss_value.item()
    print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {train_loss:.4f}')
    if train_loss < min_loss:
        min_loss = train_loss
        torch.save(model.state_dict(), 'model.pth')
        print(f'Save model with loss: {min_loss:.4f}')


# 测试模型 准确率
model = MyModel()
model.load_state_dict(torch.load('model.pth',weights_only=True))
model.eval()
model.to(device)
test_count = 0
for inputs, labels in test_loader:
    inputs = inputs.to(device)
    labels = labels.to(device)
    outputs = model(inputs)
    y_pred = outputs.argmax(dim=1)#获取预测结果
    #打印出预测准确率
    test_count += (y_pred == labels).sum().item()
print(f'Accuracy:',test_count/test_loader.dataset.__len__())


其他来看和上一篇文章深度学习Step2数据处理的过程差不多,拿到数据,定义模型,计算损失,进行测试,画图,相信你将上一个理解了再来理解这个就很简单,这个相当于是上一篇的进阶,最后今天的全部代码包请关注公众号“Sun小明同学获取”

在这里插入图片描述

这里可以更加优化一些,比如打印出此时图片,以及这个图片预测的标签,和此标签所对应的东西(这个可以用一个字典来实现)

更多推荐