Kaggle蝦皮商品匹配大賽銀牌方案覆盤


歷時兩個月,Shopee比賽終於落下帷幕,我們隊伍ID爲Team Name,隊員爲“蘭恆強”,在Private leadboard取得44名成績,排名top2%,非常感謝隊友小白Lanzhengheng的強力付出與貢獻,另外我們也很感謝那些分享了他們的想法和知識的參賽選手,特別是@chirs 和@ragnar。

1 比賽簡介

2 方案總結

我們的總體思路爲:

  • 圖像特徵: CNN+Arcface
  • 標題文本: TF-IDF 、基於ArcFace微調的Bert/Indonesian Bert
  • 後處理:將預測結果數量爲1的結果擴大爲2
  • 投票選擇結果:根據“少數服從多數”對不同模型的預測結果進行篩選

3 圖像匹配

  • 餘弦相似度:Efficient-B1,Efficient-B3,Efficient-B4,Efficient-B5,Efficient-B7,Desnet 101,eca_nfnet_l1
  • 損失函數: ArcFace
  • 優化器: Adam
  • 組合:通過組合不同圖像模型的特徵,輸入到KNN計算目標商品與候選商品的相似度

4 文本匹配

  • 餘弦相似度:基於Bert-en和Indonesian(馬來語) Bert提取文本的特徵向量,基於TfidfVectorizer提取詞袋向量,輸入到KNN獲取目標商品與候選商品的相似度
  • Bert微調:基於ArcFace 損失函數利用Bert進行商品類別識別訓練,獲取模型權重

5 後處理與投票

5.1 重新召回結果

前期根據最優lb分數的模型預測結果,我們統計結果發現存在大量沒有匹配的樣本,也就是匹配結果只有它,自己匹配到了自己,所以可以降低匹配標準(增加匹配距離閾值或者調小相似度閾值)

def combine_init_recall(row):
    x = (row['matches_init'] + ' ' + row['matches_recl']).split()
    # 初始預測匹配數爲1再召回,否則不召回
    if 1 == len(row['matches_init'].split()):
        return ' '.join( np.unique(x) )
    else:
        return row['matches_init']

df['matches_recl'] = df.apply(vote_recall_predictions, axis=1)
df['matches'] = df.apply(combine_init_recall, axis=1)

最開始結果數量爲1的樣本數爲:

train data matches none recall one match shape:  (5283, 24)

後期可以減少到

train data recalled twice, only one match shape:  (592, 24)

提分有:0.01+

5.2 投票選擇結果

因爲不同模型的預測結果不一樣或者存在噪音,那麼可以根據模型性能設置投票的順序,首選利用效果好的模型得到第一次結果,如果沒有匹配到再使用次優模型的投票結果,合併之後得到最後的結果。

from collections import Counter
def vote_recall_predictions(row):
    x = np.concatenate([  row['image_recall1'] , row['image_recall3'] , row['image_recall3']
                        , row['image_recall13'], row['image_recall17'], row['image_recall37']
                        , row['image_recall137']
                       ])
    collection_matches = Counter(x)
    # 存在兩個以上召回兩個頻次最大的,否則返回自身
    try:
        new_x = collection_matches.most_common(2)[0][0] + ' ' + collection_matches.most_common(2)[1][0]
    except:
        new_x = collection_matches.most_common(1)[0][0]

    new_x = new_x.split()
    new_x = np.concatenate([ row['oof_hash'], row['text_recall_660'], new_x ])
    
    # bert 放在最後做召回
    if len( np.unique(new_x) ) < 2:
        new_x = np.concatenate([ row['text_bert1'] , row['text_bert3'] ])

    return ' '.join( np.unique(new_x) )

6 做過的嘗試

成功的嘗試有:

  1. concat embedding:拼接不同模型的向量和組合不同模型的向量
  2. vote ensemble:投票選擇結果
  3. post process:重新召回數量爲1的結果

試過沒用的:

  1. tta
  2. meta emb
  3. ....

另外“二分類”沒有提升很多,其實第二名的第二階段用到了lgb模型。

7 比賽總結

比賽已經結束,看到了前排大佬們的方案,有些想法還是不謀而合,有些想法是想到了但是沒有嘗試成功(需要堅持~),還有些想法之前沒有聽說過,學了很多名詞,還有些想法非常細,很微妙。有遺憾有收穫,最後再次感謝Team Mate:Lan和zhengheng,另外歡迎大家關注小白Lan-知乎,可以獲取更多比賽baseline以及比賽方案推送。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章