1. 程式人生 > >【天池大資料競賽】FashionAI全球挑戰賽—服飾屬性標籤識別【決賽第21名解決方案】

【天池大資料競賽】FashionAI全球挑戰賽—服飾屬性標籤識別【決賽第21名解決方案】

        折騰了兩個月的比賽終於結束了,名次出乎了最初的預料

        但是也有些許不甘,畢竟前20都有獎勵,尷尬的21名

        想想有接近3000支隊伍參賽,好像又心理平衡了不少

        比賽其實就是一個分類問題,給定衣服的圖片然後分類到對應的標籤上面去。資料集大致如下,看了兩個月女裝突然覺得自己可以去當個設計師,好多衣服都好醜好醜。



        要進行分類的服裝一共分成八個大類,每個大類下又細分成若干個子類,如下。如collar_design是一個大類,其下有5個子類。

task_list = {
    'collar_design_labels': 5,
    'neck_design_labels': 5,
    'lapel_design_labels': 5,
    'neckline_design_labels': 10,
    'coat_length_labels': 8,  
    'skirt_length_labels': 6,
    'pant_length_labels': 6,
    'sleeve_length_labels': 9
}

初賽思路:遷移學習

        每個大類中,子類的樣本相互獨立。但是大類之間的樣本不相互獨立,最簡單的辦法就是對八個大類分別訓練八個模型,相當於八個獨立的分類器,再在測試集上用八個模型分別對八個類別進行預測。

        優點:不用動腦;缺點:只用到當前大類的資料,而無法同時使用所有資料,泛化效能固然會差點

        然後以77名進入複賽

複賽思路:遷移學習+多工學習+模型融合

        在此感謝楊培文大神在知乎提供的思路:

        輸入一張圖片,有可能是識別這8個大類裡面的任意一個類別。每一個輸出都是一個softmax分類器,輸入的圖片將會在對應的分類器輸出分類結果,也就是多輸出。我將資料分成兩個部分,一部分是包含領的所有類別,另一部分是包含長度的所有類別。然後訓練兩個模型,這兩個模型都是多輸出。結構如下圖所示:

        長度類別分類器模型結構:


        領子設計分類器模型結構:


        對應程式碼:

task_list_length = {
    'pant_length_labels': 6,
    'skirt_length_labels': 6,
    'sleeve_length_labels': 9,
    'coat_length_labels': 8
}
task_list_design = {
    'collar_design_labels': 5,
    'lapel_design_labels': 5,
    'neckline_design_labels': 10,
    'neck_design_labels': 5,
}
base_model =inception_v4.create_model(weights='imagenet',width=width, include_top=False)  
input = Input((width, width, 3))  
x = input  
x = Lambda(preprocess_input, name='preprocessing')(x)  
x = base_model(x)  
x = GlobalAveragePooling2D()(x)  
x = Dropout(0.5)(x)  
x = [Dense(count, activation='softmax', name=name)(x) for name, count in task_list.items()]  
  
model = Model(input, x)  

        對應標籤設定,分類不存在即設為0就行,會產生類似於[[0,0,0,0,0],[0,1,0,0,0,0],[0,0,0,0],[0,0,0,0,0]]的標籤

y = [np.zeros((n, task_list[x])) for x in task_list.keys()]

        訓練兩個模型並對預測結果取平均,得到更魯棒的結果。

成績提高技巧:

1、shuffle,打亂資料進行訓練是必須的,防止相鄰樣本有較強相關性。

2、資料增廣很重要,好的資料增廣可以提高2-3個百分點,但是要注意方式,比如在這個問題上沒有必要對影象上下反轉。深度學習框架一般能夠提供的影象增廣方法很有限,需要使用額外的庫進行,推薦imgaug,神器


附上使用的增廣程式碼:

def customizedImgAug(input_img):
    rarely = lambda aug: iaa.Sometimes(0.1, aug)
    sometimes = lambda aug: iaa.Sometimes(0.25, aug)
    often = lambda aug: iaa.Sometimes(0.5, aug)

    seq = iaa.Sequential([
        iaa.Fliplr(0.5),
        often(iaa.Affine(
            scale={"x": (0.9, 1.1), "y": (0.9, 1.1)},
            translate_percent={"x": (-0.1, 0.1), "y": (-0.12, 0)},
            rotate=(-10, 10),
            shear=(-8, 8),
            order=[0, 1],
            cval=(0, 255),
        )),
        iaa.SomeOf((0, 4), [
            rarely(
                iaa.Superpixels(
                    p_replace=(0, 0.3),
                    n_segments=(20, 200)
                )
            ),
            iaa.OneOf([
                iaa.GaussianBlur((0, 2.0)),
                iaa.AverageBlur(k=(2, 4)),
                iaa.MedianBlur(k=(3, 5)),
            ]),
            iaa.Sharpen(alpha=(0, 0.3), lightness=(0.75, 1.5)),
            iaa.Emboss(alpha=(0, 1.0), strength=(0, 0.5)),
            rarely(iaa.OneOf([
                iaa.EdgeDetect(alpha=(0, 0.3)),
                iaa.DirectedEdgeDetect(
                    alpha=(0, 0.7), direction=(0.0, 1.0)
                ),
            ])),
            iaa.AdditiveGaussianNoise(
                loc=0, scale=(0.0, 0.05 * 255), per_channel=0.5
            ),
            iaa.OneOf([
                iaa.Dropout((0.0, 0.05), per_channel=0.5),
                iaa.CoarseDropout(
                    (0.03, 0.05), size_percent=(0.01, 0.05),
                    per_channel=0.2
                ),
            ]),
            rarely(iaa.Invert(0.05, per_channel=True)),
            often(iaa.Add((-40, 40), per_channel=0.5)),
            iaa.Multiply((0.7, 1.3), per_channel=0.5),
            iaa.ContrastNormalization((0.5, 2.0), per_channel=0.5),
            iaa.Grayscale(alpha=(0.0, 1.0)),
            sometimes(iaa.PiecewiseAffine(scale=(0.01, 0.03))),
            sometimes(
                iaa.ElasticTransformation(alpha=(0.5, 1.5), sigma=0.25)
            ),

        ], random_order=True),
        iaa.Fliplr(0.5),
        iaa.AddToHueAndSaturation(value=(-10, 10), per_channel=True)
    ], random_order=True)  # apply augmenters in random order

    output_img = seq.augment_image(input_img)
    return output_img

3、影象標準化,計算資料集的std與mean,而不是直接使用imagenet的std與mean

4、增大影象的輸入尺寸可獲得客觀的提升,本例最終使用了480*480的輸入尺寸

4、選擇合適的遷移學習方式,本例進行全域性finetune比只訓練最後1層或幾層好很多

5、可以先用Adam快速收斂,後面階段用SGD慢慢調

6、模型融合,舉辦方在複賽限制最多隻能用兩個模型是明智的,初賽都有隊伍用接近10個模型進行融合,如此刷分就沒意義了

7、對測試集圖片進行增強,比如映象,旋轉,再預測並取平均。可以得到更魯棒的結果。這裡沒有用到tencrop,因為樣本有些特徵在頂部或者底部,tencrop會將特徵截走,導致成績降低。

賽組委送的傘,印著大大的fashion著實有點。。