Coggle 30 días de ML Check-In Tarea 3: Entrenamiento y predicción del modelo de enfermedad de Apple

Coggle 30 días de ML Check-In Tarea 3: Entrenamiento y predicción del modelo de enfermedad de Apple

Tarea 3: Entrenamiento y predicción del modelo de enfermedad de Apple

  • Dificultad/Puntuación: Media/2

Contenido del punzón:

  1. Nombre del concursante : AppleDoctor
  2. Fecha de finalización : 2023.6.11
  3. Estado de finalización de la tarea :
    • Lenguajes de programación utilizados: Python, PyTorch
    • Funciones implementadas:
      • Lectura de conjuntos de datos personalizados
      • Modelo CNN personalizado
      • Entrenamiento y Validación de Modelos
      • Hacer predicciones en el conjunto de prueba

introducción de fondo

Esta tarea de registro es la tercera tarea en Coggle 30 Days of ML. La tarea requiere que los concursantes usen el conjunto de datos de la enfermedad de la manzana provisto para construir un modelo y realizar el entrenamiento y la predicción del modelo. Los concursantes pueden elegir el marco de aprendizaje profundo y la arquitectura del modelo apropiados, y usar el conjunto de entrenamiento para el entrenamiento del modelo. Luego, los concursantes deben usar el modelo entrenado para predecir las imágenes de la enfermedad de la hoja de la manzana en el conjunto de prueba.

nombre de la misión Dificultad/Puntuación
Tarea 1: Visualización de datos de dos preguntas de competencia bajo/1
Tarea 2: carga y mejora de datos de la enfermedad de Apple Medio/2
Tarea 3: entrenamiento y predicción del modelo de enfermedad de Apple Medio/2
Tarea 4: Optimización del modelo de enfermedad de Apple y entrenamiento múltiple Alto/3
Tarea 5: Carga de datos de detección de edificios y mejora de datos Alto/2
Tarea 6: Entrenamiento y predicción del modelo de detección de edificios Medio/2
Tarea 7: Optimización del modelo de detección de edificios y entrenamiento múltiple Alto/3

conjunto de datos personalizado

Primero, usamos para PyTorchcrear un conjunto de datos personalizado que se adapte a nuestra tarea. Aquí defino una AppleDatasetclase llamada .

# 定义Apple数据集的类
class AppleDataset(Dataset):
    def __init__(self, img_path, transform=None):
        """
        构造函数,初始化数据集的路径和数据增强操作
        Args:
            - img_path: list类型,存储数据集中图像的路径
            - transform: torchvision.transforms类型,数据增强操作
        """
        self.img_path = img_path
        self.transform = transform
            
    def __getitem__(self, index):
        """
        获取一个样本
        Args:
            - index: 数据集中的索引
        Returns:
            - img: Tensor类型,经过处理后的图像
            - label: Tensor类型,标签
        """
        img = Image.open(self.img_path[index])
        if self.transform is not None:
            img = self.transform(img)

        # 将类别名转换为数字标签
        class_name = self.img_path[index].split('/')[-2]
        if class_name in ['d1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9']:
            label = ['d1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9'].index(class_name)
        else:
            label = -1

        return img, torch.from_numpy(np.array(label))
    
    def __len__(self):
        """
        获取数据集的大小
        Returns:
            - size: int类型,数据集的大小
        """
        return len(self.img_path)

A continuación, definimos el método de aumento de datos configurado anteriormente y lo aplicamos al conjunto de datos. Utilizamos las siguientes operaciones de aumento de datos:

  1. Redimensionar imagen: use transforms.Resize((224, 224))para redimensionar la imagen a 224x224 píxeles.
  2. Volteo horizontal aleatorio: se usa para voltear la imagen horizontalmente transforms.RandomHorizontalFlip()con una probabilidad de 0.5.
  3. Volteo vertical aleatorio: se utiliza para voltear la imagen verticalmente transforms.RandomVerticalFlip()con una probabilidad de 0,5.
  4. Rotación aleatoria: use para rotar la imagen transforms.RandomRotation(degrees=15)en un ángulo aleatorio, de -15 a 15 grados.
  5. Convertir a tensor: se utiliza transforms.ToTensor()para convertir una imagen en un tensor.
  6. Normalizar: se utiliza transforms.Normalize(mean, std)para normalizar la imagen con la media y la desviación estándar dadas.
# 定义图像预处理的参数
image_mean = [0.4940, 0.4187, 0.3855]
image_std = [0.2048, 0.1941, 0.1932]

# 定义训练集的数据增强操作
transform_train = transforms.Compose([
    transforms.Resize((224,224)), # 将图像大小调整为224*224
    transforms.RandomHorizontalFlip(), # 以0.5的概率对图像进行水平翻转
    transforms.RandomVerticalFlip(), # 以0.5的概率对图像进行垂直翻转
    transforms.RandomApply([transforms.GaussianBlur(kernel_size=3)], p=0.1), # 以0.1的概率对图像进行高斯模糊,卷积核大小为3
    transforms.RandomApply([transforms.ColorJitter(brightness=0.9)],p=0.5), # 以0.5的概率调整图像的亮度,亮度因子的范围为[0.1, 1.9]
    transforms.RandomApply([transforms.ColorJitter(contrast=0.9)],p=0.5), # 以0.5的概率调整图像的对比度,对比度因子的范围为[0.1, 1.9]
    transforms.RandomApply([transforms.ColorJitter(saturation=0.9),],p=0.5), # 以0.5的概率调整图像的饱和度,饱和度因子的范围为[0.1, 1.9]
    transforms.ToTensor(), # 将图像转换成张量
    transforms.Normalize(image_mean, image_std), # 对图像进行标准化处理
])

# 定义验证集的数据预处理操作
transform_valid = transforms.Compose([
    transforms.Resize((224,224)), # 将图像大小调整为224*224
    transforms.ToTensor(), # 将图像转换成张量
    transforms.Normalize(image_mean, image_std), # 对图像进行标准化处理
])

Luego podemos usar una clase de conjunto de datos personalizado para cargar los datos.

train_data = AppleDataset(train_path,transform_train)
valid_data = AppleDataset(val_path,transform_valid)

print("类别: {}".format(classes_name))
print("训练集: {}".format(len(train_data)))
print("验证集: {}".format(len(valid_data)))

Como se puede ver en los resultados de salida, el conjunto de datos tiene alrededor de 10 000 imágenes y lo divido en un conjunto de entrenamiento y un conjunto de prueba en una proporción de 8:2, lo que puede evitar el sobreajuste.

类别: ['d1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9']
训练集: 8165
验证集: 2046

A continuación, cargue los datos de entrenamiento y validación utilizando DataLoader de PyTorch. Puede ajustar batch_sizey num_workersparámetros de acuerdo a sus necesidades. (Win num_workerssolo se puede establecer en 0, si hay un problema de memoria de video, se puede ajustar hacia abajo batch_size)

# 使用PyTorch的DataLoader加载训练集数据
train_loader = torch.utils.data.DataLoader(
        train_data, batch_size=64, shuffle=True, num_workers= 8, pin_memory = True)

val_loader = torch.utils.data.DataLoader(
        valid_data, batch_size=64, shuffle=False, num_workers= 8, pin_memory = True)

Modelo CNN personalizado

En esta parte, usaremos la biblioteca PyTorch Image Models (timm) para construir rápidamente modelos CNN. timm es una biblioteca de aprendizaje profundo creada por Ross Wightman que integra modelos de visión por computadora, capas, utilidades, optimizadores, programadores, cargadores de datos, aumento y scripts de entrenamiento/validación de última generación. Esta biblioteca puede reproducir los resultados de entrenamiento de ImageNet y se usa convenientemente para construir modelos y modificar parámetros como categorías y canales.

Aquí hay un ejemplo de construcción de un modelo resnet34 usando timm:

import timm 

# 使用timm库中的resnet34模型
model = timm.create_model('resnet34')

# 生成一张224x224的RGB图像
x     = torch.randn(1, 3, 224, 224)

# 进行推理并打印输出的结果的形状
print(model(x).shape)

La salida debe ser:

torch.Size([1, 1000])

La forma de la salida es correcta, un tensor de tamaño [1, 1000], lo que indica que el modelo tiene 1000 categorías de salida. Sin embargo, nuestro conjunto de datos solo tiene 9 categorías, no las 1000 categorías de ImageNet. timm proporciona un parámetro para modificar rápidamente el número de categorías en la última capa completamente conectada. Además, también podemos cargar pesos preentrenados para el aprendizaje por transferencia. En este ejemplo, usamos resnet18 como ejemplo, primero asegúrese de que todo el proceso se ejecute normalmente:

model = timm.create_model('resnet18', num_classes=9, pretrained=True)
x     = torch.randn(1, 3, 224, 224)
model(x).shape

La salida debe ser:

Downloading model.safetensors: 100% 46.8M/46.8M [00:03<00:00, 17.2MB/s]
torch.Size([1, 9])

De esta forma, construimos con éxito un modelo resnet18 y establecimos el número de categorías en 9. Al mismo tiempo, también cargamos pesos previamente entrenados, lo que es útil para el aprendizaje de transferencia.

Si necesita saber más sobre cómo usar timm, consulte la documentación y el almacén de timm:

  • documentación de timm: https://timm.fast.ai
  • repositorio timm: https://github.com/rwightman/pytorch-image-models

Entrenamiento y Validación de Modelos

Antes de entrenar el modelo, primero debemos definir la función de pérdida y el optimizador. En este ejemplo, usamos la función de pérdida de entropía cruzada y el optimizador SGD.

# 定义训练的超参数
epochs = 10
optimizer = torch.optim.SGD(model.parameters(), lr=0.05, momentum=0.9)

# 初始化损失函数
if cuda:
    loss_fn = nn.CrossEntropyLoss().cuda()
else:
    loss_fn = nn.CrossEntropyLoss()

A continuación, por conveniencia de entrenamiento, se define una función para calcular la precisión del modelo:

def get_acc(outputs, label):
    """
    计算模型的准确率
    Args:
    - outputs: Tensor类型,模型的输出
    - label: Tensor类型,标签
    Returns:
    - acc: float类型,模型的准确率
    """
    total = outputs.shape[0]
    probs, pred_y = outputs.data.max(dim=1) # 得到概率
    correct = (pred_y == label).sum().data
    acc = correct / total
    return acc

Luego, comenzamos el entrenamiento del modelo y, después de cada época, podemos guardar el estado del modelo según sea necesario para realizar inferencias posteriores o continuar con el entrenamiento.

Durante el entrenamiento, también podemos agregar un paso de validación para evaluar el rendimiento del modelo en el conjunto de validación.

epoch_step = len(train_loader)
if epoch_step == 0:
    raise ValueError("训练集过小,无法进行训练,请扩充数据集,或者减小batchsize")

epoch_step_val = len(val_loader)

if cuda:
    model.cuda()
    
os.makedirs('checkpoint', exist_ok=True)
best_acc = 0
for epoch in range(epochs):
    model.train()
    train_loss = 0
    train_acc = 0
    print('Start Train')
    with tqdm(total=epoch_step,desc=f'Epoch {
      
      epoch + 1}/{
      
      epochs}',postfix=dict,mininterval=0.3) as pbar:
        for step,(im,label) in enumerate(train_loader,start=0):
            with torch.no_grad():
                if cuda:
                    im = im.cuda()
                    label = label.cuda()


            #----------------------#
            #   清零梯度
            #----------------------#
            optimizer.zero_grad()
            #----------------------#
            #   前向传播forward
            #----------------------#
            outputs = model(im)
            #----------------------#
            #   计算损失
            #----------------------#

            loss = loss_fn(outputs,label)
            #----------------------#
            #   反向传播
            #----------------------#
            # backward
            loss.backward()
            # 更新参数
            optimizer.step()
        
            train_loss += loss.data
            train_acc += get_acc(outputs,label)
            lr = optimizer.param_groups[0]['lr']
            # 在合适的位置添加以下代码
            pbar.set_postfix(**{
    
    
                'Train Loss': "{:.6f}".format(train_loss.item() / (step + 1)),
                'Train Acc': "{:.6f}".format(train_acc.item() / (step + 1)),
                'Lr': "{:.6f}".format(lr),
                'Memory': "{:.3g}G".format(torch.cuda.memory_reserved() / 1E9)
            })

            pbar.update(1)

    train_loss = train_loss.item() / len(train_loader)
    train_acc = train_acc.item() * 100 / len(train_loader)
    acc = train_acc
    print("Epoch: {}, Train Loss: {:.6f}, Train Acc: {:.6f}".format(epoch+1, train_loss, train_acc))

    state = {
    
    
        'net': model.state_dict(),
        'acc': acc,
        'epoch': epoch+1,
        'optimizer': optimizer.state_dict(),
    }
    torch.save(state, './checkpoint/last_resnet34_ckpt.pth')  # 模型保存路径
    
    if epoch_step_val != 0:
        model.eval()
        val_loss = 0
        val_acc = 0
        print('Start Val')
        #--------------------------------
        #   相同方法,同train
        #--------------------------------
        with tqdm(total=epoch_step_val,desc=f'Epoch {
      
      epoch + 1}/{
      
      epochs}',postfix=dict,mininterval=0.3) as pbar2:
            for step,(im,label) in enumerate(val_loader,start=0):
                with torch.no_grad():
                    if cuda:
                        im = im.cuda()
                        label = label.cuda()

                    #----------------------#
                    #   前向传播
                    #----------------------#
                    outputs = model(im)

                    loss = loss_fn(outputs,label)
                    val_loss += loss.data
                    val_acc += get_acc(outputs,label)
                    pbar2.set_postfix(**{
    
    'Val Loss': "{:.6f}".format(val_loss.item() / (step + 1)),
                                        'Val Acc': "{:.6f}".format(val_acc.item()/(step+1)),
                                        'Memory': "{:.3g}G".format(torch.cuda.memory_reserved() / 1E9)
                                        })
                    pbar2.update(1)

        lr = optimizer.param_groups[0]['lr']
        val_acc = val_acc.item() * 100 / len(val_loader)
        val_loss = val_loss.item() / len(val_loader)
        acc = val_acc
        print("Epoch: {}, Val Loss: {:.6f}, Val Acc: {:.6f}".format(epoch+1, val_loss, val_acc))
    
    # Save checkpoint.
    if acc > best_acc:
        print('Saving Best Model...')
        state = {
    
    
            'net': model.state_dict(),
            'acc': acc,
            'epoch': epoch+1,
            'optimizer': optimizer.state_dict(),
        }
        torch.save(state, './checkpoint/best_resnet34_ckpt.pth')
        best_acc = acc
Start Train
Epoch 1/10: 100%|██████████| 128/128 [00:18<00:00,  7.06it/s, Lr=0.050000, Memory=1.93G, Train Acc=0.549712, Train Loss=1.246984]
Epoch: 1, Train Loss: 1.246984, Train Acc: 54.971230
Start Val
Epoch 1/10: 100%|██████████| 32/32 [00:05<00:00,  5.92it/s, Memory=1.93G, Val Acc=0.765074, Val Loss=0.703293]
Epoch: 1, Val Loss: 0.703293, Val Acc: 76.507372
Saving Best Model...
Start Train
Epoch 2/10: 100%|██████████| 128/128 [00:17<00:00,  7.37it/s, Lr=0.050000, Memory=1.93G, Train Acc=0.745348, Train Loss=0.759731]
Epoch: 2, Train Loss: 0.759731, Train Acc: 74.534816
Start Val
Epoch 2/10: 100%|██████████| 32/32 [00:04<00:00,  7.71it/s, Memory=1.93G, Val Acc=0.839828, Val Loss=0.468506]
Epoch: 2, Val Loss: 0.468506, Val Acc: 83.982801
Saving Best Model...
Start Train
Epoch 3/10: 100%|██████████| 128/128 [00:17<00:00,  7.48it/s, Lr=0.050000, Memory=1.93G, Train Acc=0.809956, Train Loss=0.566048]
Epoch: 3, Train Loss: 0.566048, Train Acc: 80.995631
Start Val
Epoch 3/10: 100%|██████████| 32/32 [00:04<00:00,  6.73it/s, Memory=1.93G, Val Acc=0.873488, Val Loss=0.382040]
Epoch: 3, Val Loss: 0.382040, Val Acc: 87.348789
Saving Best Model...
Start Train
Epoch 4/10: 100%|██████████| 128/128 [00:19<00:00,  6.66it/s, Lr=0.050000, Memory=1.93G, Train Acc=0.839854, Train Loss=0.477816]
Epoch: 4, Train Loss: 0.477816, Train Acc: 83.985364
Start Val
Epoch 4/10: 100%|██████████| 32/32 [00:05<00:00,  6.09it/s, Memory=1.93G, Val Acc=0.887664, Val Loss=0.329272]
Epoch: 4, Val Loss: 0.329272, Val Acc: 88.766378
Saving Best Model...
Start Train
Epoch 5/10: 100%|██████████| 128/128 [00:17<00:00,  7.33it/s, Lr=0.050000, Memory=1.93G, Train Acc=0.862014, Train Loss=0.408341]
Epoch: 5, Train Loss: 0.408341, Train Acc: 86.201435
Start Val
Epoch 5/10: 100%|██████████| 32/32 [00:04<00:00,  6.64it/s, Memory=1.93G, Val Acc=0.924757, Val Loss=0.251357]
Epoch: 5, Val Loss: 0.251357, Val Acc: 92.475742
Saving Best Model...
Start Train
Epoch 6/10: 100%|██████████| 128/128 [00:17<00:00,  7.43it/s, Lr=0.050000, Memory=1.93G, Train Acc=0.876175, Train Loss=0.372916]
Epoch: 6, Train Loss: 0.372916, Train Acc: 87.617451
Start Val
Epoch 6/10: 100%|██████████| 32/32 [00:04<00:00,  6.86it/s, Memory=1.93G, Val Acc=0.908628, Val Loss=0.268046]
Epoch: 6, Val Loss: 0.268046, Val Acc: 90.862840
Start Train
Epoch 7/10: 100%|██████████| 128/128 [00:19<00:00,  6.54it/s, Lr=0.050000, Memory=1.93G, Train Acc=0.881424, Train Loss=0.353227]
Epoch: 7, Train Loss: 0.353227, Train Acc: 88.142353
Start Val
Epoch 7/10: 100%|██████████| 32/32 [00:05<00:00,  6.40it/s, Memory=1.93G, Val Acc=0.936980, Val Loss=0.213115]
Epoch: 7, Val Loss: 0.213115, Val Acc: 93.698019
Saving Best Model...
Start Train
Epoch 8/10: 100%|██████████| 128/128 [00:18<00:00,  6.91it/s, Lr=0.050000, Memory=1.93G, Train Acc=0.890658, Train Loss=0.313763]
Epoch: 8, Train Loss: 0.313763, Train Acc: 89.065802
Start Val
Epoch 8/10: 100%|██████████| 32/32 [00:05<00:00,  5.44it/s, Memory=1.93G, Val Acc=0.924301, Val Loss=0.221276]
Epoch: 8, Val Loss: 0.221276, Val Acc: 92.430067
Start Train
Epoch 9/10: 100%|██████████| 128/128 [00:17<00:00,  7.28it/s, Lr=0.050000, Memory=1.93G, Train Acc=0.900245, Train Loss=0.303409]
Epoch: 9, Train Loss: 0.303409, Train Acc: 90.024549
Start Val
Epoch 9/10: 100%|██████████| 32/32 [00:05<00:00,  6.01it/s, Memory=1.93G, Val Acc=0.926207, Val Loss=0.237639]
Epoch: 9, Val Loss: 0.237639, Val Acc: 92.620653
Start Train
Epoch 10/10: 100%|██████████| 128/128 [00:18<00:00,  6.80it/s, Lr=0.050000, Memory=1.93G, Train Acc=0.907415, Train Loss=0.280949]
Epoch: 10, Train Loss: 0.280949, Train Acc: 90.741462
Start Val
Epoch 10/10: 100%|██████████| 32/32 [00:06<00:00,  5.20it/s, Memory=1.93G, Val Acc=0.944808, Val Loss=0.167078]
Epoch: 10, Val Loss: 0.167078, Val Acc: 94.480848
Saving Best Model...

Se puede ver que el conjunto de verificación final también puede alcanzar una tasa de precisión de aproximadamente 94.5, y puede continuar trabajando más duro

Hacer predicciones en el conjunto de prueba

A continuación, usamos el modelo entrenado para hacer predicciones sobre el conjunto de prueba y generar el archivo CSV final para enviarlo.

Primero, defina una función para realizar la inferencia del modelo.

from tqdm import tqdm

def predict(test_loader, model):
    """
    进行模型的推理
    Args:
    - test_loader: DataLoader类型,测试集数据加载器
    - model: 模型
    Returns:
    - test_pred: numpy数组类型,模型的输出结果
    """
    # 将模型设为评估模式
    model.eval()
    
    test_pred = []
    with torch.no_grad():
        # 遍历测试集数据
        for input, _ in tqdm(test_loader):
            input = input.cuda()

            # 进行模型的推理
            output = model(input)
            test_pred.append(output.data.cpu().numpy())
            
    # 将模型的输出结果转换为numpy数组类型
    return np.vstack(test_pred)

A continuación, cargue los datos del conjunto de prueba y realice la inferencia del modelo repetida 10 veces, y sume los resultados de cada inferencia, lo que equivale a integrar 10 modelos, pero integrándose con usted mismo.

# 获取测试集数据的路径
test_path = glob.glob(f'{
      
      root}/test/*')

# 加载测试集数据
test_data = AppleDataset(test_path, transform_valid)

# 使用PyTorch的DataLoader加载测试集数据
test_loader = torch.utils.data.DataLoader(
        test_data, batch_size=1, shuffle=False, num_workers= 0, pin_memory = True)

pred = None
print("-----------------Repeat 10 times-----------------")

# 进行10次模型推理,并将结果相加
for _ in range(10):
    if pred is None:
        pred = predict(test_loader, model)
    else:
        pred += predict(test_loader, model)

Finalmente, guarde los resultados de la inferencia en un archivo CSV para enviarlos a la plataforma de competencia.

# 创建包含标签的DataFrame
submit = pd.DataFrame(
    {
    
    
        'uuid': [x.split('/')[-1] for x in test_path],
        'label': [['d1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9'][x] for x in pred.argmax(1)]
    }
)

# 生成CSV文件的时间戳
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")

# 保存包含标签的DataFrame
label_csv_file = f'submit/predictions_{
      
      timestamp}.csv'
submit = submit.sort_values(by='uuid')
submit.to_csv(label_csv_file, index=None)

# 打印保存路径
print("Predictions saved to: {}".format(label_csv_file))

Resumir

En esta parte, he completado con éxito las tareas de entrenamiento y predicción del modelo de enfermedad de la manzana. Utilice Python y PyTorch como lenguaje de programación y personalice una clase de conjunto de datos llamada AppleDataset según los requisitos. En términos de entrenamiento de modelos, utilicé un modelo CNN personalizado y cargué los datos del conjunto de entrenamiento y el conjunto de validación usando DataLoader de PyTorch. Además, también usé la biblioteca PyTorch Image Models (timm) para construir rápidamente un modelo CNN y usé la función de pérdida de entropía cruzada y el optimizador SGD para el entrenamiento y la validación del modelo.

Después de completar esta tarea, optimizaré y mejoraré aún más el modelo. Por ejemplo, puede probar arquitecturas de modelos más complejas, ajustar hiperparámetros o probar diferentes algoritmos de optimización. Además, se pueden probar otros métodos de aumento de datos para mejorar la capacidad de generalización del modelo.

Supongo que te gusta

Origin blog.csdn.net/weixin_45508265/article/details/131183652
Recomendado
Clasificación