【天池大資料競賽】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著實有點。。