entrenamiento de pytorch EfficientnetV2


prefacio

  No hace mucho, utilicé pytorch para reproducir la estructura de red deficientnetv2, pero tengo otras cosas que hacer más adelante, por lo que el artículo sobre la parte de capacitación se ha retrasado hasta ahora. Se puede hacer referencia a algunos enlaces a artículos sobre Efficientnet de la siguiente manera:
EfficientnetV1 training
Flask Implemente EfficientnetV1 clasification network
pytorch para construir la estructura de red EfficientnetV2
Luego, el código de capacitación en este artículo también se basa en el código de capacitación v1, por lo que habrá una brecha con el oficial


1. Colocación de datos

  Para la ubicación de los datos de entrenamiento, consulte el artículo de entrenamiento de EfficientnetV1, que es más o menos como sigue:
inserte la descripción de la imagen aquí
Hay varios tipos de datos en las carpetas de tren y val. Aquí, tomemos gatos y perros como ejemplo, de la siguiente manera:
inserte la descripción de la imagen aquí

dos, entrenando

  El código de entrenamiento se proporciona directamente aquí, y se han agregado algunos comentarios al código. Si observa detenidamente, encontrará que es similar al código de entrenamiento de V1, excepto que se han cambiado el método de empaquetado y algunos parámetros. Debe tenerse en cuenta que debe copiar la estructura de red eficientenetV2 reproducida con pytorch mencionada anteriormente en model.py y colocarla de la siguiente manera.
inserte la descripción de la imagen aquí
Los detalles del código son los siguientes:

from model import EfficientnetV2
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets,transforms
from torch.utils.data import DataLoader
import os,time,argparse

device="cuda" if torch.cuda.is_available() else "cpu"
#数据处理
def process(opt):
    # 数据增强
    data_transforms = {
    
    
        'train': transforms.Compose([
            # transforms.Resize((self.imgsz, self.imgsz)),  # resize
            transforms.CenterCrop((opt.imgsz, opt.imgsz)),  # 中心裁剪
            transforms.RandomRotation(10),  # 随机旋转,旋转范围为【-10,10】
            transforms.RandomHorizontalFlip(p=0.2),  # 水平镜像
            transforms.ToTensor(),  # 转换为张量
            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # 标准化
        ]),
        "val": transforms.Compose([
            # transforms.Resize((self.imgsz, self.imgsz)),  # resize
            transforms.CenterCrop((opt.imgsz, opt.imgsz)),  # 中心裁剪
            transforms.ToTensor(),  # 张量转换
            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
        ])
    }

    # 定义图像生成器
    image_datasets = {
    
    x: datasets.ImageFolder(os.path.join(opt.img_dir, x), data_transforms[x]) for x in
                      ['train', 'val']}
    # 得到训练集和验证集
    trainx = DataLoader(image_datasets["train"], batch_size=opt.batch_size, shuffle=True, drop_last=True)
    valx = DataLoader(image_datasets["val"], batch_size=opt.batch_size, shuffle=True, drop_last=True)

    b = image_datasets["train"].class_to_idx  # id和类别对应
    print(b)
    return trainx,valx,b
#训练
def train(opt):
    start_time = (time.strftime("%m%d_%H%M", time.localtime()))
    save_weight = opt.save_dir + os.sep + start_time  # 保存路径
    os.makedirs(save_weight, exist_ok=True)
    model=EfficientnetV2(opt.model_type,opt.class_num).cuda()
    best_acc = 0
    best_epoch = 0  # 准确率最高的模型的训练周期
    model.train(True)
    # 优化器
    optimzer=optim.SGD(model.parameters(),lr=opt.lr,momentum=opt.m,weight_decay=0.0004)
    cross = nn.CrossEntropyLoss() #损失函数
    trainx, valx, b = process(opt)

    for ech in range(opt.epochs):
        optimzer1 = lrfn(ech, optimzer,opt.lr)

        print("----------Start Train Epoch %d----------" % (ech + 1))
        # 开始训练
        run_loss = 0.0  # 损失
        run_correct = 0.0  # 准确率
        count = 0.0  # 分类正确的个数

        for i, data in enumerate(trainx):

            inputs, label = data
            inputs, label = inputs.to(device), label.to(device)

            # 训练
            optimzer1.zero_grad()
            output = model(inputs)

            loss = cross(output, label)
            loss.backward()
            optimzer1.step()

            run_loss += loss.item()  # 损失累加
            _, pred = torch.max(output.data, 1)
            count += label.size(0)  # 求总共的训练个数
            run_correct += pred.eq(label.data).cpu().sum()  # 截止当前预测正确的个数
            # 每隔100个batch打印一次信息,这里打印的ACC是当前预测正确的个数/当前训练过的的个数
            if (i + 1) % 500 == 0:
                print('[Epoch:{}__iter:{}/{}] | Acc:{}'.format(ech + 1, i + 1, len(trainx), run_correct / count))

        train_acc = run_correct / count
        # 每次训完一批打印一次信息
        print('Epoch:{} | Loss:{} | Acc:{}'.format(ech + 1, run_loss / len(trainx), train_acc))

        # 训完一批次后进行验证
        print("----------Waiting Test Epoch {}----------".format(ech + 1))
        with torch.no_grad():
            correct = 0.  # 预测正确的个数
            total = 0.  # 总个数
            for inputs, labels in valx:
                inputs, labels = inputs.to(device), labels.to(device)
                outputs = model(inputs)

                # 获取最高分的那个类的索引
                _, pred = torch.max(outputs.data, 1)
                total += labels.size(0)
                correct += pred.eq(labels).cpu().sum()
            test_acc = correct / total
            print("批次%d的验证集准确率:" % (ech + 1), test_acc.cpu().detach().numpy())
        if best_acc < test_acc:
            best_acc = test_acc
            best_epoch = ech + 1

            torch.save(model, save_weight + os.sep + "best.pth")
        print(f'best epoch : {
      
      best_epoch}, best accuracy : {
      
      best_acc}')

#学习率设置
def lrfn(num_epoch,optim,lr):
    lr_start=lr
    max_lr=0.01
    lr_up_epoch = 5  # 学习率上升批次
    lr_sustain_epoch = 10  # 学习率保持不变
    lr_exp = .8  # 衰减因子
    if num_epoch < lr_up_epoch:  # 0-10个epoch学习率线性增加
        lr = (max_lr - lr_start) / lr_up_epoch * num_epoch + lr_start
    elif num_epoch < lr_up_epoch + lr_sustain_epoch:  # 学习率保持不变
        lr = max_lr
    else:  # 指数下降
        lr = (max_lr - lr_start) * lr_exp ** (num_epoch - lr_up_epoch - lr_sustain_epoch) + lr_start
    for param_group in optim.param_groups:
        param_group['lr'] = lr
    return optim


def parse_opt():
    parser = argparse.ArgumentParser()
    parser.add_argument("--model_type",type=str,default="S",help="Model type") #模型选型,可选s,m,l,大小写均可
    parser.add_argument("--img-dir", type=str, default="", help="train image path")  # 数据集的路径
    parser.add_argument("--imgsz", type=int, default=480, help="image size")  # 图像尺寸
    parser.add_argument("--epochs", type=int, default=100, help="train epochs")  # 训练批次
    parser.add_argument("--batch-size", type=int, default=16, help="train batch-size")  # batch-size
    parser.add_argument("--class_num", type=int, default=2, help="class num")  # 类别数
    parser.add_argument("--lr",type=float,default=0.0001,help="Init lr") #学习率初始值
    parser.add_argument("--m", type=float, default=0.9, help="optimer momentum")  # 动量
    parser.add_argument("--save_dir", type=str, default="",
                        help="save models dir")  # 保存模型路径
    opt = parser.parse_known_args()[0]
    return opt



if __name__ == '__main__':
    opt=parse_opt()
    models=train(opt)

En train.py, solo necesita seleccionar un tipo de modelo en parse_opt(), como S, M, L, etc. En segundo lugar, establezca parámetros como la ruta del conjunto de datos, el número de categorías y la ruta de guardado para tu propio.
Nota: hay una letra (b) cerca de la línea 38 en el código. Esta es una lista de categorías. Asegúrese de recordar esta lista y usarla en la prueba, de lo contrario, las categorías pueden ser incorrectas durante la prueba.

2. prueba

La parte de prueba proporciona directamente el código, donde las líneas 44 y 48 deben ser su propia ruta de datos de prueba y ruta de modelo, el código es el siguiente

import torch
import torchvision
from PIL import Image
import cv2,glob,os,time
import shutil
from pathlib import Path

def expend_img(img,img_size=480,expand_pix=0):
    '''
    :param img: 图片数据
    :param fill_pix: 填充像素,默认为灰色,自行更改
    :return:
    '''
    h, w = img.shape[:2]
    if h > w and h >= img_size:  # 左右padding
        top_expand = 0
        bottom_expand = 0
        left_expand = int((h - w) / 2)
        right_expand = left_expand
        new_img = cv2.copyMakeBorder(img, top_expand, bottom_expand, left_expand, right_expand, cv2.BORDER_CONSTANT,
                                     value=expand_pix)
    elif w > h and w >= img_size:  # 上下padding
        left_expand = 0
        right_expand = 0
        top_expand = int((w - h) / 2)
        bottom_expand = top_expand
        new_img = cv2.copyMakeBorder(img, top_expand, bottom_expand, left_expand, right_expand, cv2.BORDER_CONSTANT,
                                     value=expand_pix)
    elif w < img_size and h < img_size:  # 四周padding
        left_expand = int((img_size - w) / 2)
        right_expand = left_expand
        top_expand = int((img_size - h) / 2)
        bottom_expand = top_expand
        new_img = cv2.copyMakeBorder(img, top_expand, bottom_expand, left_expand, right_expand, cv2.BORDER_CONSTANT,
                                     value=expand_pix)
    else:
        new_img = img

    new_img = cv2.resize(new_img, (img_size, img_size))
    return new_img

#模糊分类
if __name__ == '__main__':
    img_dir="" #测试数据路径
    img_list=glob.glob(img_dir+os.sep+"*.jpg")
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    #加载模型
    model=torch.load("").to(device)
    model.eval()

    class_list=[]

    for imgpath in img_list:
        img=cv2.imread(imgpath)
        s=time.time()
        img=expend_img(img)

        #PIL
        img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))  # 注意时间
        # # img= img[:, :, ::-1].transpose(2, 0, 1).copy() #注意时间

        data_transorform = torchvision.transforms.Compose([
            torchvision.transforms.ToTensor(),
            torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
        ])
        img = data_transorform(img)
        pred_img = torch.reshape(img, (-1, 3, 480,480)).to(device)
        start_time=time.time()
        pred=model(pred_img)[0]
        pred = torch.nn.Softmax(dim=0)(pred)
        end_time=time.time()
        score, pred_id = torch.max(pred, dim=0)
        #预测类别
        pred_class=class_list[pred_id]
        e=time.time()

        print(f"{imgpath} is {pred_class},score is {score},inference time is {e-s}")
    print("Finished!")



3. Resultados de pruebas y entrenamiento

Aquí solo se brinda una parte de los resultados, de la siguiente manera:
cuando entreno 12epoch, tengo algo para usar el servidor, así que interrumpí el entrenamiento, la tasa de precisión en este momento:
inserte la descripción de la imagen aquí
al probar 3000 piezas de datos con este modelo, el resultado es :
inserte la descripción de la imagen aquí
Nota: entrenamiento y prueba En el código, el tamaño de imagen establecido es 480, que se puede cambiar según sus propias necesidades


Resumir

  Lo anterior es todo el contenido de este artículo. Si tiene alguna pregunta, no dude en comunicarse en el área de comentarios.

Supongo que te gusta

Origin blog.csdn.net/qq_55068938/article/details/130952629
Recomendado
Clasificación