顔画像から年齢・性別を推定するためのデータセットIMDB-WIKI

# はじめに
機械学習、特にdeep learningではデータが命である。ここでは、顔画像から年齢・性別を推定するタスクの学習に利用できる[IMDB-WIKIデータセット](https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/)を紹介する。
本稿では、学習のためのデータの整形まで。次回はCNNを利用した年齢・性別推定CNNの学習をやりたい。

コードは下記。
https://github.com/yu4u/age-gender-estimation

# IMDB-WIKIデータセット
このデータセットは、Internet Movie Database（IMDb; 映画やテレビ番組の俳優に関するオンラインデータベース）およびWikipediaをクローリングして作られたデータベースで、プロフィール画像、プロフィール画像から顔領域を抽出した画像、人物に関するメタデータから構成される。IMDbには、460,723枚、Wikipediaには62,328枚の顔画像が含まれる。
このデータセットは、[ChaLearn apparent age estimation challenge](http://chalearnlap.cvc.uab.es/)という画像から年齢推定を行うコンペの優勝チームが、事前学習のデータセットとして利用した（その後、コンペの訓練データでfine-tuning）

## データセットの取得
オリジナルの画像は非常にサイズが大きいので、顔領域を抽出した画像およびメタデータのアーカイブをダウンロードする。

```sh
# IMDbのデータ
wget https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/static/imdb_crop.tar
# Wikipediaのデータ
wget https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/static/wiki_crop.tar
```

上記のアーカイブ内の`.mat`ファイル（Wikipediaなら`wiki.mat`）にメタデータが全て格納されている。Matlabのデータだが、`scipy`で下記のように読み込める。

```py3
meta = scipy.io.loadmat("wiki.mat")
```

含まれているデータは下記の通り。

- `dob`：生年月日
- `photo_taken`：写真が撮影された「年」
- `full_path`：画像のパス
- `gender`：性別
- `name`：名前
- `face_location`：顔領域の矩形情報
- `face_score`：顔検出スコア
- `second_face_score`：2つ目に大きい顔検出スコア

これらは下記のように取得できる。

```py3
full_path = meta[db][0, 0]["full_path"][0]
dob = meta[db][0, 0]["dob"][0]
```

## 年齢の計算
画像に写っている人物の年齢は直接メタデータに含まれていないので計算する必要がある。方針としては、撮影日時（`photo_taken`）から生年月日（`dob`）を引けば良いが、下記のような細かい対応が必要となる。
まず、`dob`は、`Matlab serial date number`にフォーマットされた値で、時間情報に変換しないと使えない。Pythonにも同様のフォーマットを扱う機能があり変換することができるが、Matlabは西暦**0**年1月1日からの日数として定義されるのに対し、Pythonでは西暦**1**年1月1日からの日数として定義されているという[罠がある](http://sociograph.blogspot.jp/2011/04/how-to-avoid-gotcha-when-converting.html)（なんだそりゃ）。というわけで、下記のように変換する必要がある（西暦0年はうるう年！）

```py3
python_datetime = datetime.fromordinal(matlab_serial_date_numer - 366)
```

また、`photo_taken`は年の情報しかないので、期待値としてその年の真ん中の日時を利用するとすると、下記のように年齢が求められる。

```py3
def calc_age(taken, dob):
    birth = datetime.fromordinal(max(int(dob) - 366, 1))

    # assume the photo was taken in the middle (Jul. 1) of the year
    if birth.month < 7:
        return taken - birth.year
    else:
        return taken - birth.year - 1
```

## クリーニング
提供されているデータは基本的にクローリングしてきたデータなので、ノイズデータが多数含まれ、それらを除去する必要がある。例えば、Wikipediaデータベースの場合は下記のようなデータが含まれる。

- `face_score`が`-inf`なものが`18016`件存在する
- `second_face_score`が`nan`でないものが`4096`件存在する。2つ以上の顔が存在する場合、メタデータがsecond_faceに関するものである可能性がある
- 計算した年齢がマイナスだったり100より大きかったりする

ということで、`face_score`が一定以上、`second_face`が`nan`、年齢が0〜100のレンジに入っているものだけを抽出するといったことが必要となる。

## データセットの詳細
最後にWikipediaのほうのデータセットの内容を概観する。
詳細は下記。
https://github.com/yu4u/age-gender-estimation/blob/master/check_dataset.ipynb

`face_score`の分布。`-inf`は除いたもの。
![face_score.png](https://qiita-image-store.s3.amazonaws.com/0/139809/6d362e1a-036c-2d6d-81e3-05d5624dcc8d.png)

`second_face_score`の分布。`nan`は除いたもの。
![second_face_score.png](https://qiita-image-store.s3.amazonaws.com/0/139809/fe5549ca-02a6-10c7-3775-be17eaa90797.png)


`face_score`が5より大きい画像の例
![high_score.png](https://qiita-image-store.s3.amazonaws.com/0/139809/20996c2e-c54c-fa3d-db72-9616e1950070.png)

`face_score`が0から1の画像の例
![low_score.png](https://qiita-image-store.s3.amazonaws.com/0/139809/9e9b7ee3-a4d5-4af5-20aa-f0b172e0e28b.png)

`face_score`が`-inf`の画像の例
![zero_score.png](https://qiita-image-store.s3.amazonaws.com/0/139809/5d565971-6426-0d8e-148c-9bb573867461.png)

計算された年齢が100より大きい画像の例
![age_100.png](https://qiita-image-store.s3.amazonaws.com/0/139809/15afa382-711f-6cc1-1492-8d53ee250a83.png)

計算された年齢がマイナスの画像の例
![age_0.png](https://qiita-image-store.s3.amazonaws.com/0/139809/31cd80ee-f296-74e2-12a3-afdb8335e610.png)