Consejos para aumentar las puntuaciones en la competencia de clasificación de imágenes: tomando como ejemplo el desafío de clasificación de imágenes de tiempo-frecuencia controlado por control de voz de hardware inteligente

I. Introducción

Antes de leer este artículo, lea: Línea de base de la competencia de clasificación de imágenes: tomando como ejemplo el desafío de clasificación de imágenes de tiempo y frecuencia del control de voz de hardware inteligente . Este artículo presenta los antecedentes del evento y el formato del conjunto de datos, y proporciona una línea de base basada en paletas voladoras. Todas las operaciones en este artículo se realizan sobre la base de la línea de base. A continuación solo publicaré los procedimientos clave para estos consejos para mejorar las puntuaciones. , los específicos La implementación en la capacitación debe ser explorada por todos.

Mi puntuación actual es 0,94203 , ocupando el sexto lugar a largo plazo.

Insertar descripción de la imagen aquí

2. Consejos para mejorar puntos

2.1 Segmentación de datos

En la línea de base, mezclamos los datos originales y tomamos los últimos doscientos datos como conjunto de validación. Dado que el número de muestras de cada tipo en los datos originales es básicamente el mismo, este método generará grandes diferencias en las muestras de cada tipo en el conjunto de entrenamiento, lo que hará que el modelo esté "sesgado"; al mismo tiempo, el Las diferencias en las muestras de cada tipo en el conjunto de verificación también serán grandes, lo que provocará una pérdida de verificación y la acc no puede reflejar correctamente el rendimiento del modelo. Por lo tanto, debemos tomar la misma cantidad de muestras de los datos originales que del conjunto de validación.

#每一类抽取十张作为验证集
val_df = pd.DataFrame()
for i in range(24):
    val_df = val_df.append(train_df[train_df['label']==i][-10:])
    train_df = train_df.drop(labels=train_df[train_df['label']==i][-10:].index)
val_df = val_df.reset_index(drop=True)
train_df = train_df.reset_index(drop=True)

2.2 Mejora de datos

Cuando los datos originales son insuficientes, el aumento de datos es una forma muy eficaz de mejorar la diversidad de datos. En esta competencia trabajé principalmente en este aspecto.

paddle.vision.tranformsProporciona muchos métodos comunes de mejora de datos, incluido recorte aleatorio, volteo aleatorio, rotación aleatoria, normalización, etc. El método de uso también es muy simple: simplemente llame a la API directamente. Pero además, hay muchos métodos de mejora de datos muy útiles que no proporcionan API. Aquí proporciono tres métodos efectivos de mejora de datos que utilizo: borrado aleatorio, mejora de clases mixtas y recorte y mezcla .

2.2.1 Borrado aleatorio

Durante el entrenamiento, se selecciona aleatoriamente una región rectangular de la imagen y sus píxeles se borran utilizando valores aleatorios. Generar imágenes de entrenamiento con niveles de oclusión reducirá el riesgo de sobreajuste y hará que el modelo sea resistente a las oclusiones.

# 随机擦除
def random_erase(img,n_holes,length,rate): #输入img为PIL图片格式的图片
    if np.random.rand(1)[0]<rate:
        img = np.array(img)
        h = img.shape[0] #图片的高
        w = img.shape[1] #图片的宽
        
        n_holes = np.random.randint(n_holes)
        mask = np.ones((h, w), np.float32) #32*32w*h的全1矩阵

        for n in range(n_holes): #n_holes=2,length=4 选择2个区域;每个区域的边长为4
            y = np.random.randint(h) #0~31随机选择一个数 y=4
            x = np.random.randint(w) #0~31随机选择一个数 x=24

            y1 = np.clip(y - length // 2, 0, h) #2,0,32 ->2
            y2 = np.clip(y + length // 2, 0, h) #6,0,32 ->6
            x1 = np.clip(x - length // 2, 0, w) #24-2,0,32 ->22
            x2 = np.clip(x + length // 2, 0, w) #24+2,0,32 ->26

            mask[y1: y2, x1: x2] = 0. #将这一小块区域去除
        img[:,:,0] = img[:,:,0] * mask
        img[:,:,1] = img[:,:,1] * mask
        img[:,:,2] = img[:,:,2] * mask
        return Image.fromarray(img)
    else:
        return img

Parámetros de entrada:

  • img : Imagen en formato PIL;
  • n_holes : el número máximo de cuadrados, es decir, el número es un valor aleatorio de 0 a n_holes;
  • longitud : la longitud del lado del cuadrado;
  • tasa : probabilidad de borrado aleatorio;

Parámetros de salida:

  • Imágenes en formato PIL después de un borrado aleatorio

A continuación se muestra una imagen para mostrar el efecto:

img = Image.open(train_df['path'].values[0]).convert('RGB')
img2 = random_erase(img,100,10,0.2)

Insertar descripción de la imagen aquí

2.2.2 Mejora mixta (Mixup)

Insertar descripción de la imagen aquí

Mezclar las imágenes y etiquetas de las dos muestras en proporción expande la distribución de la muestra y hace que el modelo entrenado sea más sólido.

def random_mixup(img ,label, mixup_img, mixup_label):#输入img和mixup为IMG格式的图片,label和mixup_label为int类型
    img = np.array(img)
    mixup_img = np.array(mixup_img)
    label_onehot = np.zeros(24)
    label_onehot[label] = 1
    mixup_label_onehot = np.zeros(24)
    mixup_label_onehot[mixup_label] = 1

    alpha = 1
    lam = np.random.beta(alpha,alpha) #混合比例

    img_new = lam*img + (1-lam)*mixup_img
    label_new = lam*label_onehot + (1-lam)*mixup_label_onehot
    
    return Image.fromarray(np.uint8(img_new)), paddle.to_tensor(np.float32(label_new))

Parámetros de entrada:

  • img & mixup_img : Dos imágenes en formato PIL para mezclar;
  • label & mixup_label : Las etiquetas correspondientes a las dos imágenes, en formato int

Parámetros de salida:

  • Imágenes mixtas en formato PIL.
  • Etiqueta de tipo tensor mixto, codificada como one_hot

A continuación se muestran dos imágenes para mostrar el efecto:

img1 = Image.open(train_df['path'].values[0]).convert('RGB')
label1 = train_df['label'].values[0]
img2 = Image.open(train_df['path'].values[1]).convert('RGB')
label2 = train_df['label'].values[1]
img_new, label_new = random_mixup(img1, label1, img2, label2)

Insertar descripción de la imagen aquí
⭐Cabe señalar que para usar la confusión, la etiqueta de datos debe tener la codificación one_hot y la función de pérdida debe configurarse en criterion = nn.CrossEntropyLoss(soft_label=True), lo que indica el uso de la codificación one_hot.

2.2.3 Mezcla cortada

Insertar descripción de la imagen aquí
Una determinada parte de una imagen se recorta y se superpone a otra imagen como una nueva imagen de entrada y se coloca en la red para entrenamiento. Las etiquetas se ponderan y suman de acuerdo con la proporción de área de los dos tipos de elementos en la imagen.

def rand_bbox(size, lam):
    if len(size) == 4:
        W = size[2]
        H = size[3]
    elif len(size) == 3 or len(size) == 2:
        W = size[0]
        H = size[1]
    else:
        raise Exception

    cut_rat = np.sqrt(1. - lam)
    cut_w = np.int(W * cut_rat)
    cut_h = np.int(H * cut_rat)

    # uniform
    cx = np.random.randint(W)
    cy = np.random.randint(H)

    bbx1 = np.clip(cx - cut_w // 2, 0, W)
    bby1 = np.clip(cy - cut_h // 2, 0, H)
    bbx2 = np.clip(cx + cut_w // 2, 0, W)
    bby2 = np.clip(cy + cut_h // 2, 0, H)

    return bbx1, bby1, bbx2, bby2

def cutmix(img ,label, cutmix_img, cutmix_label):
    #int转化为one_hot
    label_onehot = np.zeros(24)
    label_onehot[label] = 1
    cutmix_label_onehot = np.zeros(24)
    cutmix_label_onehot[cutmix_label] = 1

    alpha = 1
    lam = np.random.beta(alpha,alpha)

    bbx1, bby1, bbx2, bby2 = rand_bbox(img.size, lam)

    img_new = img.copy()
    img_new.paste(cutmix_img.crop((bbx1, bby1, bbx2, bby2)),(bbx1, bby1, bbx2, bby2))

    # 计算 1 - bbox占整张图像面积的比例
    lam = 1 - ((bbx2 - bbx1) * (bby2 - bby1) / (img_new.size[0] * img_new.size[1]))
    label_new = lam*label_onehot + (1-lam)*cutmix_label_onehot

    return img_new,paddle.to_tensor(np.float32(label_new))

Parámetros de entrada:

  • img & cutmix_img : Dos imágenes en formato PIL para mezclar;
  • label & cutmix_label : Las etiquetas correspondientes a las dos imágenes, en formato int

Parámetros de salida:

  • Imágenes mixtas en formato PIL.
  • Etiqueta de tipo tensor mixto, codificada como one_hot

A continuación se muestran dos imágenes para mostrar el efecto:

img1 = Image.open(train_df['path'].values[0]).convert('RGB')
label1 = train_df['label'].values[0]
img2 = Image.open(train_df['path'].values[1]).convert('RGB')
label2 = train_df['label'].values[1]
img_new, label_new = cutmix(img1, label1, img2, label2)

Insertar descripción de la imagen aquí

2.2.4 Normalizar

La función de la normalización es limitar el valor de los datos que deben procesarse a un cierto rango mediante un determinado método de procesamiento. En el procesamiento de imágenes de aprendizaje profundo, después del procesamiento de normalización, los datos pueden responder mejor a la función de activación, mejorar la expresividad de los datos y reducir la aparición de explosión y desaparición de gradientes. El procesamiento de normalización común consiste en procesar los datos en una distribución gaussiana con un valor medio de 0 y una varianza de 1, y paddle.vision.transforms.Normalize()esta función se puede lograr.

Cuando la imagen pasa paddle.vision.transforms.ToTensor(), la imagen se convierte al formato Tensor. Después de la conversión, la forma es (CxHxW) y el rango de valores es [0,1]. Dado que la media y la desviación estándar son diferentes para diferentes datos, necesitamos calcular la media y la desviación estándar de los tres canales en todo el conjunto de datos.

#获取所有图片三通道的均值和方差
all_df = train_df.append(test_df,ignore_index=True).append(val_df, ignore_index=True)
all_dataset = XunFeiDataset(all_df['path'].values, all_df['label'].values,
            transforms.Compose([
                transforms.RandomCrop((450,750)),
                transforms.ToTensor()
                ]),mode='test')

def getStat(train_data):
    '''
    Compute mean and variance for training data
    :param train_data: 自定义类Dataset(或ImageFolder即可)
    :return: (mean, std)
    '''
    print('Compute mean and variance for training data.')
    print(len(train_data))
    train_loader = DataLoader(
        train_data, batch_size=1, shuffle=False, num_workers=0)
    mean = np.zeros(3)
    std = np.zeros(3)
    for X, _ in train_loader:
        for d in range(3):
            mean[d] += X[:, d, :, :].mean().cpu().numpy()[0]
            std[d] += X[:, d, :, :].std().cpu().numpy()[0]
    mean = mean/len(train_data)
    std = std/len(train_data)
    return list(mean), list(std)

print(getStat(all_dataset))

El resultado después de ejecutar es:

Compute mean and variance for training data.
3163
([0.6766141943829066, 0.06216619949979672, 0.2686088090203644], [0.24656723146663978, 0.14537001843179825, 0.17407946023116036])

Simplemente complete los datos calculados con Normalizelos parámetros:

Normalize((0.677, 0.062, 0.268), (0.246, 0.145, 0.174))

2.2.5 Etiqueta suave

Como técnica de regularización simple, el suavizado de etiquetas puede mejorar el rendimiento de generalización y la precisión del modelo en tareas de clasificación y aliviar el problema de la distribución de datos desequilibrada. Después del suavizado de etiquetas, one_label ya no solo tiene 0 y 1, sino que puede entenderse como la forma de probabilidad de esta clase. El suavizado de etiquetas puede reducir la confianza del modelo y evitar que el modelo caiga en las profundas grietas de pérdida que ocurren con el sobreajuste.

Paddle proporciona Label Smooth API.

label_onehot = paddle.to_tensor(np.float32([0,0,1,0]))
nn.functional.label_smooth(label_onehot) 

La salida es:

Tensor(shape=[4], dtype=float32, place=Place(cpu), stop_gradient=True,
       [0.02500000, 0.02500000, 0.92499995, 0.02500000])

2.2.6 Conjunto de datos modificado

Después de agregar estos métodos de mejora de datos, nuestro conjunto de datos también necesita realizar algunas modificaciones:

# 定义数据集读取方法
class XunFeiDataset(Dataset):
    def __init__(self, img_path, label, transforms=None, mode='train'):        
        self.img_path = img_path        
        self.label = label   
        self.transforms = transforms 
        self.mode = mode           
    def __getitem__(self, index):        
        img = Image.open(self.img_path[index]).convert('RGB') 

        #将label转化为one_hot编码
        label_onehot = np.zeros(24)
        label_onehot[self.label[index]] = 1
        label_onehot = paddle.to_tensor(np.float32(label_onehot))   

        if self.mode == 'train': #训练时才做数据增强
            #随机擦除 100代表100个正方形,10代表每个正方形边长为10,0.2代表20%的概率
            img = random_erase(img,100,10,0.2) 
            #mixup,0.2的概率
            if np.random.rand(1)[0]<0.2:
                mixup_idx = np.random.randint(0, len(self.img_path)-1)
                mixup_img = Image.open(self.img_path[mixup_idx]).convert('RGB')
                mixup_label = self.label[mixup_idx]
                img, label_onehot = random_mixup(img, self.label[index], mixup_img, mixup_label)
            #cutmix,0.2的概率
            if np.random.rand(1)[0]<0.2:
                cutmix_idx = np.random.randint(0, len(self.img_path)-1)
                cutmix_img = Image.open(self.img_path[cutmix_idx]).convert('RGB')
                cutmix_label = self.label[cutmix_idx]
                img, label_onehot = cutmix(img, self.label[index], cutmix_img, cutmix_label)

        if self.transforms is not None:           
            img = self.transforms(img)   

        label_onehot = nn.functional.label_smooth(label_onehot)    
        return img, label_onehot    
    
    def __len__(self):        
        return len(self.img_path) 

2.3 Tasa de aprendizaje y función de optimización

El método de disminución de la tasa de aprendizaje puede mejorar efectivamente la precisión del modelo, y la función de optimización utiliza AdamW.

Adamw es Adam + decate de peso. El efecto es el mismo que la regularización Adam + L2, pero la eficiencia de cálculo es mayor, porque la regularización L2 requiere agregar términos regulares a la pérdida, luego calcular el gradiente y finalmente propagar hacia atrás, mientras que Adamw regulariza directamente El gradiente del término se agrega a la fórmula de retropropagación, eliminando la necesidad de agregar manualmente términos regulares a la pérdida.

scheduler = paddle.optimizer.lr.StepDecay(0.0001,15,gamma=0.1,verbose=False)
optimizer = paddle.optimizer.AdamW(learning_rate=scheduler, parameters=model.parameters())

Para obtener más métodos de atenuación de la tasa de aprendizaje, consulte: Métodos de ajuste de la tasa de aprendizaje en Pytorch y las paletas voladoras API que contiene también están básicamente disponibles.

2.4 Métodos de entrenamiento

Después de la prueba, el método de validación cruzada quíntuple puede mejorar en gran medida la precisión de la predicción. Para una implementación específica, consulte la línea de base. Al predecir el conjunto de prueba, podemos usar los parámetros que funcionan mejor en el conjunto de validación para predecir los resultados de cada pliegue.

for i in range(k_fold):
    ······
    for epoch in range(epoches):  
 		······
        if val_acc>max_acc:
            test_pred = predict(test_loader, model, criterion)
            max_acc = val_acc

3. Mi camino hacia puntuaciones más altas

Preprocesamiento de datos/ingeniería de características Modelo Fracción
Cambiar tamaño (256), recorte aleatorio (224), normalización (parámetro de referencia) resnet18 0,80-0,86
Recorte aleatorio (450,750), normalización (parámetro de referencia) resnet18 0.91126
Recorte aleatorio (450,750), borrado aleatorio, normalización (parámetros de referencia) resnet18 0.91533
Volver a dividir el conjunto de validación, recorte aleatorio (450,750), borrado aleatorio, confusión, normalización (parámetros de actualización) resnet34 0.92375
Volver a dividir el conjunto de validación, recorte aleatorio (450,750), borrado aleatorio, confusión, normalización (actualización de parámetros), validación cruzada quíntuple resnet34 0.93375
Volver a dividir el conjunto de validación, recorte aleatorio (450,750), borrado aleatorio, confusión, etiqueta_smooth, normalización (parámetros de actualización), validación cruzada quíntuple resnet34 0.93518
Volver a dividir el conjunto de validación, recorte aleatorio (450,750), borrado aleatorio, confusión, cutmix, label_smooth, normalización (actualizar parámetros) resnet34 0.93509
Volver a dividir el conjunto de validación, recorte aleatorio (450,750), borrado aleatorio, mezcla, mezcla de corte, etiqueta_smooth, normalización (parámetros de actualización), validación cruzada quíntuple resnet34 0.94203

4. Finalmente

Este debería ser el final del camino hacia los puntos altos en esta competencia. Esta vez comencé principalmente con la ingeniería de características de datos y otros aspectos, y no hice demasiados cambios en el modelo. Puede considerar mejorar la estructura del modelo sobre esta base. Optimice aún más para obtener una puntuación más alta.

Supongo que te gusta

Origin blog.csdn.net/cyj972628089/article/details/126321929
Recomendado
Clasificación