月別アーカイブ: 2017年7月

ユニクロコン 実装編

オプトのユニクロコンペ(https://deepanalytics.jp/compe/36?tab=compedetail)について、もう少し実装のマニアックな話をしてみます。
コードはhttps://github.com/threecourse/opt-uniqlo-publicに上げています。

コードのインターフェイス

データ分析コンペ用のモデル構築用のクラスをどう作るかは長年の課題だったのですが、少しずつ固まってきました。

いろいろ考えた末、ベースとなるクラスはModel, Runnerに2つに分けた。

  • Modelはsklearnの各モデルをwrapするイメージ
  • Runnerは訓練や予測やクロスバリデーションなど一連の計算のもろもろ
  • 結構良い感じで、わりとモデル追加、クラスの拡張の際にストレス無くできた。
  • sklearnのインターフェイスがあまりしっくりこないのと、hyperoptに合わせたいこともあり、sklearnのベースクラスの継承はしていない。
  • データの読み込みをRunnerに持たせているが、微妙かもしれない。Modelに密接に結びついているので、そちらに定義させた方が良いかもしれない。
  • どう頑張っても時にはダーティーにやらなくてはいけないことがあるので、その時は諦める。
  • 型ヒントが効くようにちゃんとdocstring書いた方が良いかもしれない。

Model

  • Modelクラスはmodel.pyに定義。
  • 入力は、モデル名(とfold名)・データ・パラメータなど。
  • 責務は、訓練・予測・モデルの保存・モデルの読み込みなど。
  • 各foldごとに1つモデルのインスタンスが作成される。
  • baggingなどもこの中で処理することができる。

主なメソッドは以下のとおり。

シグネチャ 概要
__init__(run_fold_name) モデル名(とfold名)がないと不便。
train(prms, x_tr, y_tr, x_te, y_te) パラメータとtrainのx, yとvallidationのx, yを入力とし、モデルの訓練を行う。
train_without_validation(prms, x_tr, y_tr) パラメータとtrainのx, yを入力とし、モデルの訓練を行う。trainの全データで訓練したいとき用。
save_model() モデルを保存する。
load_model() モデルを読み込む。
predict(x_te) testやvalidationのxを入力とし、訓練されたモデルから予測値を返す。

Runner

  • Runnerクラスもmodel.pyに定義。
  • 入力は、モデル名・入力データのリスト・パラメータなど。
  • 責務は、データの読み込み、インデックスの読み込み、クロスバリデーションでの訓練(精度の出力も含む)、訓練されたモデルを使ってのテストデータの予測、hyperopt用の精度の出力など。
  • データの読み込みは単にファイル名の指定だけでなく細かく修正することが多いので、Runnerでそこだけ調整できるのは便利。
  • インデックスは複数に対応するようにしておかないと、後で面倒。
  • ログとクロスバリデーションの精度を標準出力とファイルに出力。
  • 動作確認用に少ないデータで流せるよう設計しておけば楽なのだが、対応が面倒なのであまり出来ていない。

主なメソッドは以下のとおり。
各Runnerで定義が必要なのはbuild_model, load_x_train, load_x_test。
ランなどで外部から使用する必要があるのはrun_train, run_test, run_hoptあたり。

シグネチャ 概要
__init__(run_name, flist, prms) モデル名、特徴量名(list)、パラメータ(dict)のセット。
build_model(run_fold_name) 使用するModelの定義。
load_x_train() trainのxの読み込み。
load_x_test() testのxの読み込み。
load_y_train() trainのyの読み込み。
load_w_train() trainのweightの読み込み。weightを変えて訓練したい場合に。
load_index_fold(i_fold) cv用インデックスの読み込み。(trainのidx, validationのidx)を返す。5foldの場合、基本はi_foldに0-4が入る。違うcv用インデックスで計算したい場合、”a0″や”a4″のように指定する。
load_x_fold(i_fold) foldを指定したxの読み込み。(trainのx, validationのx)を返す。
load_y_fold(i_fold) foldを指定したyの読み込み。(trainのy, validationのy)を返す。
load_w_fold(i_fold) foldを指定したweightの読み込み。(trainのweight, validationのweight)を返す。
train_fold(i_fold) 指定したfoldでの訓練を行い、訓練されたモデルを返す。
run_hopt(i_fold = “a0”) hyperopt用。指定したfoldでの訓練を行った後に、loss(と訓練されたモデル)を返す。
run_train() クロスバリデーションでの訓練を行う。とりあえず5cv固定。各foldのモデルの保存、精度のログ出力についても行う。
run_test() クロスバリデーションで訓練した各foldのモデルの平均により、testのyの予測を行う。
run_train_alltrain() trainの全データで訓練し、そのモデルを保存する。
run_test_alltrain() trainの全データで訓練したモデルにより、testのyの予測を行う。

パラメータチューニング

パラメータチューニングはhyperoptを用いて、5cvのうちの1foldのみで行い、メインの計算とは別のcv用インデックスで行った。
また、fit_generatorにより1epochを3000枚程度に小さくし、patience=3のearlystoppingで行った。
なお、訓練時はhyperoptの結果を元にepochを固定した。

  • コードはhopt/hopt_resnet.pyなどを参照。
  • 計算時間に余裕があり、精度のばらつきが激しい場合は5cvなどで評価した方が良いが、neural netだと計算時間はさすがにきつい。
  • 同じインデックスだと、hyperoptに使ったfoldだけoverfitすることがあるので、stackingを行う場合にちょっと気になる。気にし過ぎかもしれない。
  • epochをそのままだと、patience=3だとかなり行き過ぎてしまう感じ。
  • epochを固定するのではなく、early_stoppingでsave_best_onlyを使う方法もあるかもしれない。

kerasのジェネレータ、data augmemtation

頑張った。kerasのImageDataGeneratorは、場合によっては少し手を加えないと使いづらい。

  • コードはmodel_keras_util.py, model_keras_util_augmentation.pyなどを参照。
  • 汎用のジェネレータがサポートされていないようだったので、keras.preprocessing.imageのIteratorなどを参考に作成。
  • data augmentationも、ImageDataGeneratorを元に切り貼りして作成。

ログ

  • コードはutil_log.pyを参照。出力はlogフォルダを参照。
  • ログを標準出力とファイルの両方に出力するようにする。
  • 時間の情報を付加することにより、なんとなく時間のかかっている処理がわかる。
  • 一般的な情報とクロスバリデーションの精度の情報の2つに分ける。
  • クロスバリデーションの精度の情報はltsv形式で出力しておく。時々util_result.pyをランすることでモデルごとにまとめる。

ヴィジュアリゼーション、分析

  • コードはanalyzeフォルダを参照
  • 背景領域を0, それ以外を1としてベクトル化し、kmeansクラスタリングを行った。画像が整えられているので、単純だが結構きれいに商品カテゴリごとに分けられる。
  • 巡回サラリーマン問題を解いてカテゴリ間の距離が最小になるように並べた。近いカテゴリが近くにくるので見やすい。
  • カテゴリごとに画像・正解ラベルを並べたpdfを作った。(運営からの許可が出てないのでpdfはアップロードしてません)
  • どうもipython notebookは自分に合わなく、pdfを生成することが多い。

ユニクロコン 手法編

オプトのユニクロコンペ(https://deepanalytics.jp/compe/36?tab=comperank)に出ていました。
public 4th -> private 4thという結果で、運次第では賞金圏内もあったと思うので切ないですね。

0. 概要

基本データ

  • ユニクロの商品の色(24クラス)を判別する課題
  • Train:12399, Test:9801
  • 背景はちゃんと切り抜いてくれているので、わざわざwatershedなどで背景抽出をする必要が無い

課題

  • 色の判別はやや単純すぎる課題で、本来はCNNを使うのはオーバーキルな感じもある。
    ただ、後述のラベリングの微妙さも相まってコンペとしてはある意味おもしろい。
  • ラベリングが微妙。もはやどっちが正しいのかわからないの多数。特に3連靴下はなんだこれ。
  • 分析すると、女性物は同じような色でもオフホワイトになりやすかったり、ラベリングは服の種類に影響を受けていることがわかる
  • このコンペは色を判別するコンペではなく、人間がどのように色コードを設定するかを判別するコンペといえる。
  • train/testはrandom splitではなく、商品ラインごとのsplitをしている様子。
    なので、やりすぎるとtrainデータへのoverfitの危険もある

評価指標

  • balanced accuracyであることがポイント。比率の少ないものを間違えるとペナルティが大きい。
    完全に正しい確率を予測できているとするならば、確率÷正解ラベルの比率が最大となるものを予測するのが適切。
  • trainingデータのweightを変える方法も試したが、通常のweightで分類し上記の調整を行う方が精度が高かった。
  • balanced accuracyは安定しない、accuracyが良くても2番手以降の評価がいまいちだとスコアが出ないので、loglossを下げることに注力。

1. 特徴量抽出、分析

クラスタリングを少し試したが、結局モデリングには適用していない。

  • シルエットによるクラスタリング
    背景領域を0, それ以外を1としてベクトル化し、kmeansクラスタリング。
    画像が整えられているので、単純だが結構きれいに商品カテゴリごとに分けられる。

  • 分析、ヴィジュアライゼーション
    カテゴリ間の距離が最小になるように並べる(巡回サラリーマン問題を解く)と、近いカテゴリが近くなるので見やすい。
    カテゴリごとに画像を並べたPDFを作ったり、カテゴリごとの色の分布・正答率を見たりした。

  • 接続領域での分離
    繋がっている領域が何個あるかを求める(DFS)。3連靴下とかを綺麗にわけることができる。

  • その他
    SIFT、カラーヒストグラム、pretrained modelで予測したimagenetのカテゴリとか考えたが、結局使っていない。

2-1. モデリング

  • ニューラルネットのライブラリはkeras
  • ファインチューニングとスクラッチからのCNNの両方を行った。
  • テストデータには、5cvの平均を適用するようにした。
    ニューラルネットだと、trainの全データを使うとvalidation scoreが見れないので怖い。
  • パラメータ調整はhyperopt。ネットワーク構成、learning rate、optimizerの選択に大変役に立った。
  • 単発だとかなりぶれるので、10-20回回した平均をとった。
  • オプティマイザは、ファインチューニングはmomentum sgdで良さそうですが、スクラッチではadamが有効だった。
  • スクラッチから作る場合は、序盤に1*1フィルタを適用するモデルの精度が良かった。
  • 1epochで10000枚回すとearly stoppingの際に行き過ぎてしまうので、generatorを使うことで1 epochの枚数を制御した。
  • data augmentation, test-time augmentationを行った。
    左右反転・少し回転・少し拡大縮小させた。
    kerasのImageGeneratorは専用実装になっており、取り入れにくかったので、適宜コードをつぎはぎした。

2-2. 作成したモデル

ファインチューニング

  • vgg16
  • vgg16, augmentationあり
  • resnet
  • resnet, augmentationあり

スクラッチからのCNN

  • cnn1-5 : 序盤に1*1フィルタを適用したCNN(5種類)、augmentationあり
  • cnn0 : 1*1フィルタのみでピクセルの場所の情報を全く使用しないCNN

ネットワーク構造は以下のような感じ。怪しい。
Conv2d(filter=1 * 1) -> Conv2d(filter=1 * 1,stride=2) -> BN -> Maxpooling(2 * 2) ->
Conv2d(filter=2 * 2) -> Conv2d(filter=2 * 2,stride=2) -> BN -> MaxPooling(2 * 2) ->
Dense -> BN -> Prediction

各モデルのローカルのスコア(10-20個混ぜている)は以下のとおり。

model logloss balanced acc accuracy
vgg16 0.6876 0.7004 0.7695
vgg16_aug 0.6969 0.6910 0.7685
resnet 0.6687 0.6975 0.7747
resnet_aug 0.6462 0.7005 0.7809
cnn1-5 0.6548-0.6743 0.6993-0.7116 0.7672-0.7732
cnn0 0.7396 0.6624 0.7443

3. Ensemble、調整

  • 各モデルの加重平均をとる。
    加重平均を取る際には、loglossが最小になるような係数を探し、適用する。
  • スタッキングを行う。クラスが多いためか、スタッキングをしたモデル自体の精度はあまり良くない。
    スタッキングをしたモデルとの加重平均をとることで少し精度が上がる。
  • 最後の仕上げでloglossが最小になるように確率をn乗したり、色の比率のn乗を乗じた。
    この辺はいろいろやり方がありそう。

各モデルのローカルのスコアは以下のとおり。

model logloss balanced acc accuracy
加重平均 0.6154 0.7199 0.7880
スタッキング 0.6311 0.7154 0.7815
上2つの加重平均 0.6120 0.7213 0.7882
調整後 0.6069 0.7217 0.7874