DF機器圖像算法賽道-雲狀識別(天氣識別)比賽總結

DF機器圖像算法賽道-雲狀識別(天氣識別)比賽總結

比賽目的

之前一直用的tensorflow框架,想上手一下pytorch框架,然後一番搜索之後發現了基於pytorch的fastai高級庫,fastai對於pytorch的關係和keras對於tensorflow的關係非常相似。正好拿這個比賽學習一下fastai,擴展一下pytorch方面的知識。最後成績天氣識別56/1054,雲狀識別31//1318
因爲是練手,所以實現的功能非常簡單,基本就是baseline級別的東西。不得不說fastai使用起來非常簡便,從開始寫到出答案只需要幾個小時的時間,並且想法迭代起來也非常快速。
與其說這是一個比賽的總結,不如說這是一個安利fastai的廣告,fastai賽高。

題目簡介

兩個賽題都是分類問題,輸入爲尺寸不一的圖片,輸出爲圖片的類別。
例如天氣的類別爲:

天氣現象 編號
雨凇 1
霧凇 2
霧霾 3
4
5
結冰 6
降雨 7
降雪 8
冰雹 9

雲狀的類別和天氣差不多,分爲1~29個類別,並且包含一張圖片對應多個分類的情況,難度比天氣大。

數據分析

類別比例

總的數據數量爲10665,多標籤的數據爲454條,佔比較少,由於只實現了單標籤的網絡,因此數據統計的時候去掉了多標籤的數據。
最多的5個種類
在這裏插入圖片描述
數據非常不平衡,多的種類數量有1500+,少的種類數量只有個位數,這裏不禁要吐槽一下數據的質量。
雖然做了數據類別的可視化,類別非常不平衡,但是方案中並沒有實現,改進一下的話應該是可以提分的。
感覺在強大的數據平衡手段都拯救不了個位數和1500+的差距,深度學習,數據還是第一重要的。

圖片尺寸

由於網絡在訓練是要resize到一個固定的尺寸,因此,統計一下數據圖片的尺寸十分有必要。
下圖所示,X軸爲圖片的寬,Y軸爲圖片的高。
在這裏插入圖片描述
統計之後可以發現圖片大多數分佈在0~2000之間,在幾次控制變量試驗後,選定了resize爲512*512的訓練尺寸。

方案實現

  • 網絡:Densenet201
  • 損失函數:FocalLoss
  • 在線數據增強
    • 左右翻轉
    • 10°-10\degree ~ 10°10\degree的旋轉
    • 11~1.11.1倍的縮放
    • 亮度調節
  • 其他Trick:
    • mixup
    • 使用fp_16訓練,減少顯存的消耗增大BatchSize
    • onecycle學習率策略

代碼

代碼非常簡潔,基本上能想到的常見的trick,fastai都已經給你封裝好了。

from fastai.vision import *
import pandas as pd
import numpy as np
import os
from fastai.callbacks import *
os.environ["CUDA_VISIBLE_DEVICES"] = "1,0"
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

data_path = '/home/sjw/Desktop/cloud/data/Train'
test_data_path = '/home/sjw/Desktop/cloud/data/Test'
df = pd.read_csv('/home/sjw/Desktop/cloud/data/Train_label.csv')
# 去除多標籤的數據
classes_list = [str(i) for i in range(1,30)]
df_single = df[df.Code.isin(classes_list)]
# 重新排列索引
df_single = df_single.reset_index(drop=True)
# 洗牌
df_single = df_single.sample(frac=1).reset_index(drop=True)
for i in range(len(df_single)):
    df_single.Code[i] = int(df_single.Code[i])
test_df = pd.read_csv('/home/sjw/Desktop/cloud/data/submit_example.csv')
save_dir = '/home/sjw/Desktop/cloud/fastai/densenet201'
data_num = len(df_single)
for part in range(1,5):
    val_list = list(range(data_num//5 * part + 1,  data_num//5 * (part + 1) + 1))
    part_save_dir = os.path.join(save_dir, 'part_{}'.format(part))
    # 準備數據
    
    data_set = (ImageList.from_df(df_single, data_path)  # 從csv中讀取dilenames
                .split_by_idx(val_list)  # 驗證集百分之20
                .label_from_df()
                .add_test(ImageList.from_df(test_df, test_data_path))
                .transform(get_transforms(do_flip=True, flip_vert=False,
                                          max_rotate=10.0, max_zoom=1.1,
                                          max_lighting=0.2, max_warp=0.2,
                                          p_affine=0.75, p_lighting=0.75,
                                          xtra_tfms=[*rand_resize_crop(512)]),
                           size=(512, 512), resize_method=ResizeMethod.SQUISH)
                .databunch(bs=32, num_workers=6)
                .normalize())
    class FocalLoss(nn.Module):
        def __init__(self, alpha=1., gamma=1.):
            super().__init__()
            self.alpha = alpha
            self.gamma = gamma
    
        def forward(self, inputs, targets, **kwargs):
            CE_loss = nn.CrossEntropyLoss(reduction='none')(inputs, targets)
            pt = torch.exp(-CE_loss)
            F_loss = self.alpha * ((1 - pt) ** self.gamma) * CE_loss
            return F_loss.mean()
#     learn = cnn_learner(data_set, models.densenet201,
#                         metrics=[accuracy],
#                         loss_func=LabelSmoothingCrossEntropy(),
#                         model_dir=part_save_dir)
    learn = cnn_learner(data_set, models.densenet201,
                        metrics=[accuracy],
                        loss_func=FocalLoss(),
                        model_dir=part_save_dir)
    learn = learn.mixup().to_fp16()
    learn.model = nn.DataParallel(learn.model, device_ids=[0,1])
#     learn.lr_find(stop_div=True, num_it=250)
#     learn.recorder.plot(suggestion=True)
    step_name = 'best_' + 'part' + str(part)
    learn.freeze()
    learn.fit(2, 1e-3)
    learn.unfreeze()
    unfreeze_min_grad_lr = 1e-3
    learn.fit_one_cycle(20, [unfreeze_min_grad_lr / 100, unfreeze_min_grad_lr / 10, unfreeze_min_grad_lr],
                        callbacks=[SaveModelCallback(learn, every='improvement', monitor='accuracy', name=step_name)])
    learn.load(step_name)
    learn = learn.to_fp32()
    x, y = learn.TTA(ds_type=DatasetType.Test)
    write_csv = test_df
    for i in range(len(x)):
        write_csv.iloc[i, 1] = np.argmax(x[i]).tolist() + 1

    write_csv.to_csv(os.path.join(part_save_dir,'part{}_newlabel.csv'.format(part)), index=False)
    learn.destroy()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章