PaddlePaddle práctica introductoria-clasificación del zodíaco

Requisitos de la tarea

  Encontrar un algoritmo óptimo que permita a la máquina distinguir fotos de cada animal del zodíaco es una tarea de clasificación basada en imágenes .

Clasificación de imágenes

Idea de realización

Inserte la descripción de la imagen aquí

Principios de clasificación de imágenes

Inserte la descripción de la imagen aquí

preparación de datos

Descomprime el conjunto de datos

  Cargamos el conjunto de datos obtenido en línea al conjunto de datos de aistudio en un paquete comprimido y lo cargamos en nuestro proyecto. Antes de usar, realizamos una descompresión del paquete de compresión del conjunto de datos (conjunto de datos del zodíaco chino
)

!unzip -q -o <压缩包路径>

Anotación de datos

  Estructura del conjunto de datos:

.
├── test
│   ├── dog
│   ├── dragon
│   ├── goat
│   ├── horse
│   ├── monkey
│   ├── ox
│   ├── pig
│   ├── rabbit
│   ├── ratt
│   ├── rooster
│   ├── snake
│   └── tiger
├── train
│   ├── dog
│   ├── dragon
│   ├── goat
│   ├── horse
│   ├── monkey
│   ├── ox
│   ├── pig
│   ├── rabbit
│   ├── ratt
│   ├── rooster
│   ├── snake
│   └── tiger
└── valid
    ├── dog
    ├── dragon
    ├── goat
    ├── horse
    ├── monkey
    ├── ox
    ├── pig
    ├── rabbit
    ├── ratt
    ├── rooster
    ├── snake
    └── tiger

  El conjunto de datos se divide en tres carpetas, entrenamiento, válido y prueba.Cada carpeta contiene 12 carpetas de clasificación y cada carpeta de clasificación contiene imágenes de muestra específicas. Realizamos un proceso de anotación en estas muestras y finalmente generamos train.txt/valid.txt/test.txttres archivos de anotación de datos.

import io
import os
from PIL import Image
from config import get


# 数据集根目录
DATA_ROOT = 'signs'

# 标签List
LABEL_MAP = get('LABEL_MAP')

# 标注生成函数
def generate_annotation(mode):
    # 建立标注文件
    with open('{}/{}.txt'.format(DATA_ROOT, mode), 'w') as f:
        # 对应每个用途的数据文件夹,train/valid/test
        train_dir = '{}/{}'.format(DATA_ROOT, mode)

        # 遍历文件夹,获取里面的分类文件夹
        for path in os.listdir(train_dir):
            # 标签对应的数字索引,实际标注的时候直接使用数字索引
            label_index = LABEL_MAP.index(path)

            # 图像样本所在的路径
            image_path = '{}/{}'.format(train_dir, path)

            # 遍历所有图像
            for image in os.listdir(image_path):
                # 图像完整路径和名称
                image_file = '{}/{}'.format(image_path, image)
                
                try:
                    # 验证图片格式是否ok
                    with open(image_file, 'rb') as f_img:
                        image = Image.open(io.BytesIO(f_img.read()))
                        image.load()
                        
                        if image.mode == 'RGB':
                            f.write('{}\t{}\n'.format(image_file, label_index))
                except:
                    continue


generate_annotation('train')  # 生成训练集标注文件
generate_annotation('valid')  # 生成验证集标注文件
generate_annotation('test')   # 生成测试集标注文件

Definición de conjunto de datos

  A continuación, usamos el archivo marcado para definir la clase del conjunto de datos para facilitar el entrenamiento posterior del modelo.

import paddle
import numpy as np
from config import get

# 导入数据集的定义实现
from dataset import ZodiacDataset

# 实例化数据集类
train_dataset = ZodiacDataset(mode='train')
valid_dataset = ZodiacDataset(mode='valid')

print('训练数据集:{}张;验证数据集:{}张'.format(len(train_dataset), len(valid_dataset)))

Inserte la descripción de la imagen aquí

Importar dataset.pyarchivo de conjunto de datos

import paddle
import paddle.vision.transforms as T
import numpy as np
from config import get
from PIL import Image

__all__ = ['ZodiacDataset']

# 定义图像的大小
image_shape = get('image_shape')
IMAGE_SIZE = (image_shape[1], image_shape[2])


class ZodiacDataset(paddle.io.Dataset):
    """
    十二生肖数据集类的定义
    """

    def __init__(self, mode='train'):
        """
        初始化函数
        """
        assert mode in ['train', 'test', 'valid'], 'mode is one of train, test, valid.'

        self.data = []

        with open('signs/{}.txt'.format(mode)) as f:
            for line in f.readlines():
                info = line.strip().split('\t')

                if len(info) > 0:
                    self.data.append([info[0].strip(), info[1].strip()])

        if mode == 'train':
            self.transforms = T.Compose([
                T.RandomResizedCrop(IMAGE_SIZE),    # 随机裁剪大小
                T.RandomHorizontalFlip(0.5),        # 随机水平翻转
                T.ToTensor(),                       # 数据的格式转换和标准化 HWC => CHW  
                T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # 图像归一化
            ])
        else:
            self.transforms = T.Compose([
                T.Resize(256),                 # 图像大小修改
                T.RandomCrop(IMAGE_SIZE),      # 随机裁剪
                T.ToTensor(),                  # 数据的格式转换和标准化 HWC => CHW
                T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])   # 图像归一化
            ])
        
    def __getitem__(self, index):
        """
        根据索引获取单个样本
        """
        image_file, label = self.data[index]
        image = Image.open(image_file)

        if image.mode != 'RGB':
            image = image.convert('RGB')

        image = self.transforms(image)

        return image, np.array(label, dtype='int64')

    def __len__(self):
        """
        获取样本总数
        """
        return len(self.data)

config.pyElemento de configuración

__all__ = ['CONFIG', 'get']

CONFIG = {
    
    
    'model_save_dir': "./output/zodiac",
    'num_classes': 12,
    'total_images': 7096,
    'epochs': 20,
    'batch_size': 32,
    'image_shape': [3, 224, 224],
    'LEARNING_RATE': {
    
    
        'params': {
    
    
            'lr': 0.00375             
        }
    },
    'OPTIMIZER': {
    
    
        'params': {
    
    
            'momentum': 0.9
        },
        'regularizer': {
    
    
            'function': 'L2',
            'factor': 0.000001
        }
    },
    'LABEL_MAP': [
        "ratt",
        "ox",
        "tiger",
        "rabbit",
        "dragon",
        "snake",
        "horse",
        "goat",
        "monkey",
        "rooster",
        "dog",
        "pig",
    ]
}

def get(full_path):
    for id, name in enumerate(full_path.split('.')):
        if id == 0:
            config = CONFIG
        
        config = config[name]
    
    return config

Modelo de desarrollo

  La red ResNet50 se utiliza para construir el modelo. La estructura del modelo es más complicada, pero la API de alto nivel de PaddlePaddle tiene una paddle.vision.models.resnet50interfaz incorporada , que nos permite generar un modelo con una línea de código, para que podamos experimentar el efecto más rápido.
  La siguiente figura muestra la estructura de ResNet50: la configuración de los parámetros

  paddle.vision.models.resnet50internos pretrainedestá Truediseñada para cargar los pesos previos al entrenamiento en el conjunto de datos de imagenet , es decir, entrenar iterativamente nuestro propio modelo basado en los parámetros que han sido bien entrenados por predecesores.

network = paddle.vision.models.resnet50(num_classes=get('num_classes'), pretrained=True)
model = paddle.Model(network)
model.summary((-1, ) + tuple(get('image_shape')))

Inserte la descripción de la imagen aquí

Optimización del entrenamiento de modelos

EPOCHS = get('epochs')
BATCH_SIZE = get('batch_size')

def create_optim(parameters):
    step_each_epoch = get('total_images') // get('batch_size')
    lr = paddle.optimizer.lr.CosineAnnealingDecay(learning_rate=get('LEARNING_RATE.params.lr'),
                                                  T_max=step_each_epoch * EPOCHS)
    
    return paddle.optimizer.Momentum(learning_rate=lr,
                                      parameters=parameters,
                                      weight_decay=paddle.regularizer.L2Decay(get('OPTIMIZER.regularizer.factor')))

# 模型训练配置
model.prepare(create_optim(network.parameters()),  # 优化器
              paddle.nn.CrossEntropyLoss(),        # 损失函数
              paddle.metric.Accuracy(topk=(1, 5))) # 评估指标

# 训练可视化VisualDL可视化工具的回调函数
visualdl = paddle.callbacks.VisualDL(log_dir='visuald_log')

#启动模型全流程训练
model.fit(train_dataset,             # 训练数据集
          valid_dataset,             # 评估数据集
          epochs=EPOCHS,             # 总的训练轮次
          batch_size=BATCH_SIZE,     # 批次样本的样本量大小
          shuffle=True,              # 是否打乱样本集
          verbose=1,                 # 日志展示格式
          save_dir='./chk_points/',  # 分阶段的训练模型存储路径
          callbacks=[visualdl])      # 回调函数

Inserte la descripción de la imagen aquí


Visualización VisualDL

Inserte la descripción de la imagen aquí

Evaluación del modelo

Predicción por lotes

predict_dataset = ZodiacDataset(mode='test')
print('测试数据集样本量:{}'.format(len(predict_dataset)))

from paddle.static import InputSpec

# 网络结构
network = paddle.vision.models.resnet50(num_classes=get('num_classes'))

# 模型封装
model_2 = paddle.Model(network, inputs=[InputSpec(shape=[-1] + get('image_shape'), dtype='float32', name='image')])

# 模型文件加载
model_2.load(get('model_save_dir'))
# 模型配置
model_2.prepare()

# 执行预测
result = model_2.predict(predict_dataset)

# 样本映射
LABEL_MAP = get('LABEL_MAP')

# 随机取样本展示
indexs = [2, 38, 56, 92, 100, 303]

for idx in indexs:
    predict_label = np.argmax(result[0][idx])
    real_label = predict_dataset[idx][1]

    print('样本ID:{}, 真实标签:{}, 预测值:{}'.format(idx, LABEL_MAP[real_label], LABEL_MAP[predict_label]))

Inserte la descripción de la imagen aquí

referencia

Supongo que te gusta

Origin blog.csdn.net/weixin_40807714/article/details/113701611
Recomendado
Clasificación