浅析深度学习(四)--图片分类回归实战

目标:通过11个类别的训练进行图片分类预测(本文不做测试集)

训练集,验证集

一.调包

import torch
from torch.utils.data import Dataset,DataLoader
import random
import numpy as np
import os
from PIL import Image
from tqdm import tqdm
from torchvision.transforms import transforms
import torch.nn as nn
import time
import matplotlib.pyplot as plt

二.数据增广

通过自由调整大小和颜色,识别更多样同个图片

同个图片调整大小,颜色

#图片的数据增广
train_transformer = transforms.Compose([
    transforms.ToPILImage(),  # 这个img 转为PIL
    transforms.RandomResizedCrop(224),  #随机resize 。 取中间的224.
    transforms.ToTensor()          # 转为张量

])

设下种子,让代码每次运行结果一样

#直接复制
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(0)   # 让代码每次运行结果一样,
########################

#######读数据。
HW = 224

三.创建函数--读文件

文件夹内数据

def read_file(path):
    #共11个类别
    for i in tqdm(range(11)):       #tqdm为进度条函数,能给出进度条
        img_path = path + "/%02d/"%i
        img_list = os.listdir(img_path) # 读取文件夹下所有文件
        #初始化xi yi
        xi = np.zeros((len(img_list), HW, HW, 3), dtype=np.uint8)  #共280 个 224* 224 *3
        yi = np.zeros(len(img_list), dtype=np.uint8)# 280*1
        for j, img in enumerate(img_list):  #j是下标0.1.2.3...,img是内容0276.0277....
            true_img_path = img_path + img
            img = Image.open(true_img_path)     #打开图片
            img = img.resize((HW,HW))       # 所有图片都要转为224*224(HW*HW)
            #输入数据
            xi[j, ...] = img        #列下标为j,行下标随便取
            yi[j] = i
        #第一次读数据
        if i == 0:
            X = xi
            Y = yi
        #之后就是不断合并Xi
        else:
            #concatenate是合并
            X = np.concatenate((X, xi), axis=0)
            Y = np.concatenate((Y, yi), axis=0)
    print("读取了%d个数据"%len(Y))
    return  X, Y

四.创建类--数据集

#数据集
class fooddataset(Dataset):
    def __init__(self, path):
        #要调整用super
        super(fooddataset, self).__init__()
        #读文件
        self.X, self.Y = read_file(path)
        # 只要是标签就用longtensor格式
        self.Y = torch.LongTensor(self.Y)
        #数据增广
        self.transformer = train_transformer

    def __getitem__(self, item):
        #返回值为x的数据增广和y标签
        return self.transformer(self.X[item]), self.Y[item]

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

#设置路径
train_set = fooddataset(r"D:\深度学习\分类实战\food\food-11_sample\training\labeled")
val_set = fooddataset(r"D:\深度学习\分类实战\food\food-11_sample\validation")
#将数据装入loader 方便取一个batch的数据
train_loader = DataLoader(train_set, batch_size=4)
val_loader = DataLoader(val_set, batch_size=4)

五.创建类--模型

class myModel(nn.Module):
    def __init__(self):
        super(myModel, self).__init__()
        #3*224*224 - 512*7*7
        self.layer0 = nn.Sequential(
            #卷积
            nn.Conv2d(3, 64, 3, 1, 1),
            #归一化
            nn.BatchNorm2d(64),
            #激活函数
            nn.ReLU(),
            #最大池化pooling
            nn.MaxPool2d(2)
        )  # Sequential把里面的操作合成一个————64*112*112

        self.layer1 = nn.Sequential(
            nn.Conv2d(64, 128, 3, 1, 1),
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )  # 把里面的操作合成一个    128*56*56

        self.layer2 = nn.Sequential(
            nn.Conv2d(128, 256, 3, 1, 1),
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )  # 把里面的操作合成一个    256*28*28

        self.layer3 = nn.Sequential(
            nn.Conv2d(256, 512, 3, 1, 1),
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )  # 把里面的操作合成一个    512*14*14
        self.pool1 = nn.MaxPool2d(2) #512*7*7
        #拉直为25088
        self.fc1 = nn.Linear(25088, 1000)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(1000, 11)
    #前向过程
    def forward(self, x):
        x = self.layer0(x)
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.pool1(x)
        #展平x.view
        x = x.view(x.size()[0], -1)
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        return x

六.设置参数

权重缩减:通过降低w,使曲线更加平滑

mymodel = myModel()
#
# for data in train_loader:
#     print(data)
#     x, y = data[0], data[1]
#     out = mymodel(x)


lr = 0.001
loss = nn.CrossEntropyLoss()    #分类专用loss
epochs = 20 #20轮
# AdamW  weight_dacay权重缩减
optimizer = torch.optim.AdamW(mymodel.parameters(), lr=lr, weight_decay=1e-4)             
device = 'cuda' if torch.cuda.is_available() else 'cpu'         #设备
save_ = 'model_save/mymodel.pth'        #保存路径

七.开始训练

#万能的训练代码
def train_val(model, trainloader, valloader,optimizer, loss, epoch, device, save_):
    # trainloader = DataLoader(trainset,batch_size=batch,shuffle=True)
    # valloader = DataLoader(valset,batch_size=batch,shuffle=True)
    model = model.to(device)                # 模型和数据 ,要在一个设备上。  cpu - gpu
    plt_train_loss = []
    plt_val_loss = []


    val_rel = []
    min_val_loss = 100000                 # 记录训练验证loss 以及验证loss和结果

    for i in range(epoch):                 # 训练epoch 轮
        train_acc = 0.0
        val_acc = 0.0
        start_time = time.time()             # 记录开始时间
        model.train()                         # 模型设置为训练状态      结构
        train_loss = 0.0
        val_loss = 0.0
        for data in trainloader:                     # 从训练集取一个batch的数据
            optimizer.zero_grad()                   # 梯度清0
            x, target = data[0].to(device), data[1].to(device)       # 将数据放到设备上
            pred = model(x)                          # 用模型预测数据
            bat_loss = loss(pred, target)       # 计算loss
            bat_loss.backward()                        # 梯度回传, 反向传播。
            optimizer.step()                            #用优化器更新模型。  轮到SGD出手了
            train_loss += bat_loss.detach().cpu().item()             #记录loss和
            train_acc += np.sum(np.argmax(pred.cpu().data.numpy(), axis=1) == data[1].cpu().numpy())

        plt_train_loss. append(train_loss/trainloader.dataset.__len__())   #记录loss到列表。注意是平均的loss ,因此要除以数据集长度。

        model.eval()                 # 模型设置为验证状态
        with torch.no_grad():                    # 模型不再计算梯度
            for data in valloader:                      # 从验证集取一个batch的数据
                val_x , val_target = data[0].to(device), data[1].to(device)          # 将数据放到设备上
                val_pred = model(val_x)                 # 用模型预测数据
                val_bat_loss = loss(val_pred, val_target)          # 计算loss
                val_loss += val_bat_loss.detach().cpu().item()                  # 计算loss
                val_rel.append(val_pred)                 #记录预测结果
        if val_loss < min_val_loss:
            torch.save(model, save_)               #如果loss比之前的最小值小, 说明模型更优, 保存这个模型
        val_acc += np.sum(np.argmax(val_pred.cpu().data.numpy(), axis=1) == val_target[1].cpu().numpy())    #argmax为最大值的下标
        print("val_acc:",val_acc)
        plt_val_loss.append(val_loss/valloader.dataset.__len__())  #记录loss到列表。注意是平均的loss ,因此要除以数据集长度。
        #
        print('[%03d/%03d] %2.2f sec(s) TrainLoss : %.6f | valLoss: %.6f' % \
              (i, epoch, time.time()-start_time, plt_train_loss[-1], plt_val_loss[-1])
              )              #打印训练结果。 注意python语法, %2.2f 表示小数位为2的浮点数, 后面可以对应。

        # print('[%03d/%03d] %2.2f sec(s) TrainLoss : %3.6f | valLoss: %.6f' % \
        #       (i, epoch, time.time()-start_time, 2210.2255411, plt_val_loss[-1])
        #       )              #打印训练结果。 注意python语法, %2.2f 表示小数位为2的浮点数, 后面可以对应。
    plt.plot(plt_train_loss)              # 画图, 向图中放入训练loss数据
    plt.plot(plt_val_loss)                # 画图, 向图中放入训练loss数据
    plt.title('loss')                      # 画图, 标题
    plt.legend(['train', 'val'])             # 画图, 图例
    plt.show()                                 # 画图, 展示


train_val(mymodel, train_loader, val_loader,optimizer, loss, epochs, device, save_)
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值