外部コンペから学ぶ実務に活かせる機械学習 Carvana_Image_Masking_Challenge

# はじめに
## まえおき
このアカウントは、株式会社ブレインパッドのアナリティクス本部に所属する社員の有志によって運営されています。主に本部に所属する社員の活動をベースとした情報発信を行っていきます。今回は、kaggle活動推進プロジェクトメンバーの丸山がお送りします。


## kaggle活動推進プロジェクトとは

データ分析や機械学習の分野は、日々発展を続けています。したがってデータサイエンティストは、様々な分野の分析技術について幅広く・最新の知識を身につけていることが望ましく、そのために絶えずスキルアップに努める必要があります。
kaggleなどの外部分析コンペは、そのような新しい知識を身につける場としては最適であるものの、弊社でも「ハードルが高い」「一人だと続かない」といった声をよく聞きます。加えて、業務の傍ら業務に直接関係のない勉強をし続けるのは難しく、モチベーションが続かないことが多いですよね。
そこで弊社の有志メンバーが「社外分析知見の獲得・業務の質向上を目的とした、組織的な外部分析コンペへの参加」を推進する社内プロジェクトを立ち上げました。

本記事は、この活動の成果を社内だけでなく社外にも還元する試みとして書いたものです。

## 活動の目的

上記のコンセプトの通り、この活動では必ずしも「kaggleで良い成績を残す」ことは目指していません（もちろん、[現行のオープンコンペに参加](https://qiita.com/makotu1208/items/f10855aec2e67b4a44d1)してスコアを競っているメンバーもいます）。
過去に行われたコンペのkernel（kaggle上でコードが共有できるページ）やdiscussion（議論のための掲示板ページ）から、実業務に活かせそうな知見の整理を行い、参加メンバーの分析力向上を主な目的としています。

今回は、知見の整理の一環として、画像系コンペ「Carvana Image Masking Challenge」に取り組んだグループの活動をまとめました。

# コンペ概要

Carvana Image Masking Challengeは、Carvana社が主催し、2017年7月〜9月にkaggleで行われたコンペです。このコンペでは、「車の画像」と「その画像の車の領域のみマスクされた画像」を用いて、提供された画像から車の領域を特定することが要求されます。

## コンペの目的
このコンペの目的は、技術的には「Semantic Segmentation」による物体検出です。
簡単に言えば、車の部分だけを塗りつぶす「塗り絵」のようなものです。
主催社の背景も考慮して、実業務のクライアントを想定するのであれば、

```
このコンペを主催するCarvana社は、アメリカのオンライン中古車販売スタートアップです。
ユーザーがネット上で中古車を吟味・購入することのできるサービスを提供しており、
そのために車だけの画像を切り出す技術が必要とされています。従来は手作業やツールを
使っていましたが、時間と労力がかかる・精度がいまいちであるなどの問題があり、
車領域を高精度に判別できる機械学習モデルを望んでいます。
```

と、このような課題背景、目的になるんじゃないでしょうか。

## 目的と評価指標
車の画像に対して、車の領域を予測したマスク画像を生成します。この予測領域が、真の車の領域とどれくらい一致しているかをDice係数と呼ばれる指標で評価します。Dice係数は、集合の類似度を表す指標の一つです。真の車の領域と予測した車の領域が一致しているほど１に近い値を取り、反対に一致していないと０に近い値となります。
Dice係数の解説については、[他の方のこの記事](https://mieruca-ai.com/ai/jaccard_dice_simpson/)が参考になりました。

このコンペで参加者たちが競うスコアとしては、テストデータの画像１枚１枚のDice係数の平均値を採用しています（ただし、コンペの都合上全ての予測結果が評価されるわけではなく、スコアに反映されるのは一部のみ[^1]となります）。
[^1]: 1200枚がPublic LB（コンペ期間中に見ることのできるスコア）、3664枚がPrivate LB（最終的なスコア）で用いられた。残りの95200枚は手作業での塗り絵防止のためにあるもので、不正防止のダミーデータのような側面がある。

そして、予測結果は画像として提出するのではなく、RLE（Run-Length Encoding）というエンコーディングを行ったテキストを作成する必要があります。RLEは、各ピクセルが０（背景）か１（車）かを保存するデータから、「どこからどこまでが１で、次はここからここまでが１、・・・」という情報に変換するアルゴリズムです。
このようなスコアに直接関わりのないコードはコンペ参加者がkernelに投稿してくれることも多く、[このkernel](https://www.kaggle.com/paulorzp/run-length-encode-and-decode)を参考にしました。
## 提供データ

| ファイル | データ説明 |
|:-------:|:----|
| train/*.jpg | 学習データ。実際の車の画像。1918×1280 pxのJPG画像5088枚。ファイル名には車種IDなどが含まれる。|
|train_mask/*.gif | マスクデータ。車の領域を表すマスク画像。学習データと同じサイズのgif画像。枚数やファイル名は学習データと対応がある。 |
| metadata.csv | メタデータ。車種IDと車の生産年やメーカー名などのメタ情報が記録されているテーブルデータ。およそ200kb |
|test/*.jpg | テストデータ。この画像の車領域を判別する。1918×1280 pxのJPG画像100,064枚。 |
| train_masks.csv|車種ID、マスク領域の情報（RLEでエンコーディングされたもの）からなるテーブルデータ。|
| sample_submission.csv | 予測値を提出するときのサンプルフォーマット。|


提供されたデータのうち、train_masks.csvについては、データ確認やEDAで用いられていましたが、実際に学習に使われることはほとんどなかったようです。

# ベーシックなアプローチ・解法
## データ確認

予測対象は画像データなので、pythonであればopencvとmatplotlibなどを使って実際に画像を表示することができます。下図は、左が学習データで、右が学習用のマスクデータの一部を並べたものです。
![train.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394474/19d6789f-525d-52c8-9772-cc07c8ec0f2a.png)![train_mask.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394474/6a1c0b87-754f-3311-6637-9c4978ed700d.png)



実際に画像を見てみると、ひとつの車に対し１６枚の画像があり、異なるアングルの画像が撮影されていることがわかります。このような画像セットが300車程度存在していました。
また、だいたい画像の中心に車が配置されており、どの画像も比較的綺麗な条件で撮影されたこともわかります（そのため、このコンペのスコアは非常に高いところで争われることとなりました）。
## 前処理

画像系のタスクにおいては、汎化性能を高めたり、学習データをかさ増したりする目的で、Image Augmentationと呼ばれる前処理がよく行われます。
Image Augmentationは、学習データを水増しするような処理によって、データの多様性を向上させる手法です。これにより、より汎化性能の高い判別モデルの学習が期待できます。具体的には、画像のランダムな拡大・縮小・反転や、画像のHSV（色相・彩度・明度）の編集等の画像処理の総称です。
スコア上位者も利用していましたが、[imageaug](https://github.com/aleju/imgaug)というライブラリを用いることで容易に実行できます。

本来であれば、さらに前段階の前処理として、ルールベースで画像をトリミングしたり、不要なデータを取り除いたりする必要があります。今回のコンペでは、データ自体が非常に綺麗なため、そのような前処理を行わずともある程度の精度が出ます。ただし、上位入賞者はわずかな精度向上のため、根気よくデータの前処理を行っていたようです。


## モデル作成

主に"U-Net"と呼ばれるネットワークが用いられていました。
このモデルはSemantic SegmentationのためのCNN（畳み込みニューラルネットワーク）の１種で、畳み込み層→逆畳み込み層の構造を持ちます。畳み込み層がEncoder、逆畳み込み層がDecoderの役割を果たします。このネットワークを概略図にした時、「U」の形になることからU-Netと呼ばれています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394474/50b8a8d4-b302-6f54-fdad-6d814f1568b0.png" width=512px title="U-net">



具体的な原理等はすでに詳しい説明がほかにいくつもあるので省略します。
コンペではU-netをベースに、どのように層をカスタマイズするかが鍵となったようです。

私たちがまず目を通した[こちらのdisucussion](https://www.kaggle.com/c/carvana-image-masking-challenge/discussion/37523#latest-308905)では、Kerasを用いてU-Netが実装されています。
また、U-Netだけでなく、前処理なども丁寧に実装されているので、非常に勉強になるコードを提供してくれています（[githubレポジトリ](https://github.com/petrosgk/Kaggle-Carvana-Image-Masking-Challenge)）。

実際にこのコードを中心に参照しながら、私たちのグループではコンペに対する理解を進めて行きました。
弊社のGPU環境を、案件で使用していない隙を狙って学習を回し、手を動かして理解していきました。本部の社内プロジェクトとして認められているため、業務に差し障りのない範囲で社内環境を利用できることが理解を深める上で大きなメリットだったと思います（笑）。

# 上位解法
## 上位陣のアプローチなど

基本は上記の方法を踏襲していますが、上位陣は様々な工夫を凝らしていました。
kaggleに触れたことのない方のために補足しておくと、コンペ期間が終了し順位が確定すると、上位入賞者たちは自らの解法を簡単に説明したdiscussionを投稿することが慣例となっています（強制ではなく、入賞者の好意によって行われています）。

そこでは、以下のようなアプローチが言及されていました。

* U-netのEncoder部のカスタマイズ（畳み込みの構造をVGGにする、学習済みのパラメータを設定するなど）
* U-netのEncoder-Decoderの間の層で"dilated convolution[^2]"を行い、畳み込む情報量を増大させている。
* サイズを圧縮した小さな画像でU-netを学習させ、車と背景の領域をざっくり推論したのち、境界領域を重点的に学習し直す
* 損失関数を単にbinary cross entropyにせず、今回の評価指標であるDICE係数も考慮に入れた関数にする

[^2]: 畳み込む（convolution）範囲を拡張（dialate）すること。


## Winning Solution

このコンペでの優勝チームは、[kaggle公式ブログのインタビュー](http://blog.kaggle.com/2017/12/22/carvana-image-masking-first-place-interview/)を受けています。
そこでチームメンバーそれぞれのアプローチについて答えています。以下に要約を掲載します。

* 前処理
    + 水平反転・スケーリングなど単純なAugmentation
    + CLAHE(画素のヒストグラム平坦化)を前処理時に実施
* モデル
    + U-net、LinkNet
    + Encoder部にVGG-11やResNetの構造を利用したものとそうでないものを複数構築してアンサンブル
    + 損失関数として、```BCE-ln(DICE)```や```1+BCE-DICE```を試行
* その他のトリック
    + Pseudo Labeling（テストデータの予測結果を正解として、学習データとみなしてネットワークに再学習させる方法）
    + 車と背景のlossを車の中心部の3倍にするなど、境界との近さによって損失を重み付け

# 実業務に活かせそうな知見・生かせないであろうテクニック
この活動は、そもそも参加者自身のスキルアップと、実案件で生かせる知見の集積を目的としています。
このコンペから得られた知見を整理すると、以下のようになります。


## 案件に活かせそうな知見
* imageaugによるAugmentation
　深層学習の前処理としてよく用いられる画像処理をとりまとめたライブラリです。
　KerasにもAugmentationのための機能が用意されていますが、より細かい調整ができそうです。

* U-Netの汎用性の高さ
　多くのコンペ参加者がU-Netのアーキテクチャを用いていたことから、前処理→U-Netの基本形を作成しておけば、すぐに同様のタスクに取り組むことができるでしょう。

* U-NetのEncoder部のカスタマイズ
　Encoder部（畳み込み部）をVGG−11やResNet18のアーキテクチャに改変することで、より精度の高いネットワークとなることが期待できます。

実案件の課題に応じて、このようにアーキテクチャを改善できれば即戦力になれそうですね。
また、優勝者の解法には学習済みモデルをU-Netに組み込んでいたこともあり、U-Netや深層学習の奥の深さを垣間見ることができ、学びとなりました。加えて、優勝者の解法にもあった"LinkNet"は、U-Netに比べ精度では劣るものの高速で軽量であることから、場合によっては実案件に採用されることが多いかもしれません。



## 生かせないであろうテクニック

* モデルアンサンブリング
　速度が要求されることの多い実案件において、複数のモデルをアンサンブルして精度を向上させることはあまり行われないかもしれません。

* Pseudo Labeling
　テストデータを精度向上に使うのは、コンペ特有で実務ではタブーですよね。
* Test Time Augmentation
　テストデータにAugumentationを施すことで、１枚の画像について複数の予測を推論して平均する方法。
  アンサンブリングと同様の理由で、実案件ではコスト面から採用されにくいと考えられます。


# 活動の様子

Carvana Image Masking Challengeに取り組んだ私たちのグループは、５人で活動していました。
５人のうち画像系のタスクの実務経験者は一人で、かつ新卒1年目社員が３人と経験の少ないメンバーからなるグループでしたが、discussionや公開されているコードを読み解いていくうちに、画像系タスク（Semantic Segmentation）を行うときのフローや、モデル構築時の勘所についてなんとなく理解することができました。
メンバーそれぞれが別々の案件をこなす傍ら、隙間時間や業務外にチャットで連絡を取り合い、勉強になるdiscussionや情報の共有を行いました。
この活動を行っている最中に、新卒1年目のとあるメンバーが画像系の案件にアサインされることが決まり、実際に業務をこなしていましたが、きっとこの活動で得られた知見を存分に発揮し、活躍したことでしょう。

この活動を通じて、メンバー全員が新たな強みを獲得することができたのではないでしょうか。

# 終わりに

この記事を通して、kaggleのCarvana Image Masking Challengeを例に、画像のSemantic Segmentationに対するアプローチを解説しました。

加えて、弊社のスキルアップを推進する活動についても併せてご紹介しました。
当活動では、Carvanaコンペ以外にも、リクルートの開催した来店者数予測コンペやTalkingDataのアプリコンバージョン予測コンペについても同様にまとめ活動を実施してきました。これらの知見についても、後々公開していきたいと思います。そちらにもご期待ください！