ディープラーニングの学習に半教師あり学習、Active Learningを試す

# ディープラーニングの学習に半教師あり学習、Active Learningを使う
ディープラーニングの学習に半教師あり学習とActive Learningを応用してみます。
ディープラーニングでは学習に膨大なデータが必要になりますが、いつも必要な量のデータが用意されているとは限りません。
例えば画像分類のためにディープラーニングを使おうとする場合、ラベル付きの画像が大量に必要になります。
画像はあってもラベルがない、または画像が偏っている、ということはよくある課題です。

今回は学習データが不足している状態から半教師あり学習とActive Learningを使って、Accuracyを改善してみようと思います。
なお、学習データ不足のときには[転移学習](https://qiita.com/icoxfog417/items/48cbf087dd22f1f8c6f4)も便利です。

## 使うデータ
[Cifar10](https://www.cs.toronto.edu/~kriz/cifar.html)を使います。


## 半教師あり学習とActive Learning
半教師あり学習もActive Learningも、少ないデータで分類器をつくるときに学習データを増やす手法で、教師あり学習で使われます。

半教師あり学習では、少ないデータで学習した分類学習機に対し、ラベルのないデータで推論を行い、高い確率（確信度）で推論した結果は正しい結果と定めて、そのデータに推論したラベルを付与して学習データにする、というものです。
http://yamaguchiyuto.hatenablog.com/entry/machine-learning-advent-calendar-2014
自信をもって分類したデータは正しいだろうと考えるのですが、もちろん間違えていることもあります。

対してActive Learningは、ラベルのないデータで推論を行うまでは一緒ですが、こちらは重要なデータのみに人間がラベルをつけて学習データに取り込んでいきます。
どういうデータを重要とみなすかは様々なルールがありますが、詳しくは以下をご参照ください。
https://www.slideshare.net/shuyo/introduction-to-active-learning-25787487
今回はActive Learningでは、Margin Samplingを行います。つまり、推論した確率で上位2ラベルの差が小さいものを重要とみなし、ラベルをつけて学習データに追加します。
Margin Samplingを選ぶ理由は2つあります。
1. 半教師あり学習が高い確信度のデータをラベル付けるので、その対照となる低い確信度のものに焦点をあてます。
2. Cifar10データの性質上、猫、鹿、犬のデータが似ているため、低い確信度になる可能性が高く、分類精度も低くなりがちです。Margin Samplingであれば猫、鹿、犬を重視して分類できるようになる、と期待しています。

半教師あり学習とMargin SamplingのActive Learningでは違うデータが追加されます。
そのため、両方を追加していった場合の効果も測定するため、半教師あり学習とActive Learningを組み合わせる複合パターンも検証します。

## 状況、手順
画像データにはCifar10を使います。
Cifar10の画像には最初からラベルが付いていますが、一部を除いてラベルがないものとして、半教師あり学習、Active Learningに投入していきます。
画像は以下のように分割して使います。

- 学習データ総数：50,000枚
  - うち、初期学習用データ：10,000枚
  - 半教師あり学習やActive Learningで学習に適宜追加するデータ：40,000枚（この全てを追加するとは限らない）
- テストデータ：10,000枚

最初に初期学習用データでモデルを作ります。
その後、半教師あり学習、Active Learning、両方の複合をそれぞれ別々に検証します。
半教師あり学習、Active Learning、複合はいずれも11回ずつ繰り返します。
最後にそれぞれをテストデータに対するAccuracy、Loss、Confusion Matrixで比較します。

#### 半教師あり学習の手順
1. 初期モデルを10,000枚の初期学習用データで作ります。（このモデルはActive Learning、複合でも共有します）
2. 適宜追加データ（初期40,000枚）に対して最新のモデルで推論（softmax）し、確率が99.99％以上になるデータに、そのラベルを付けて学習データに組込みます。（学習データ
 = 学習データ + 追加されるデータ）
3. また、学習データに組み込まれるデータは適宜追加データ（初期40,000枚）から排除します。（適宜追加データ = 適宜追加データ - 追加されるデータ）
4. モデルの再学習を行い、2.に戻ります。
5. 2.～4.を20回繰り返します。
6. 最新のモデルでテストデータに対するAccuracy、Loss、ConfusionMatrixを出力します。

半教師あり学習ではラベル付けの誤りも記録します。

#### Active Learningの手順
1. 半教師あり学習の手順1.で作ったモデルをロードします。
2. 適宜追加データ（初期40,000枚）に対して最新のモデルで推論し、確率の上位2位の差が1％以下になるデータに、正解のラベルを付けて学習データに組込みます。（学習データ
 = 学習データ + 追加されるデータ）
3. また、学習データに組み込まれるデータは適宜追加データ（初期40,000枚）から排除します。（適宜追加データ = 適宜追加データ - 追加されるデータ）
4. モデルの再学習を行い、2.に戻ります。
5. 2.～4.を20回繰り返します。
6. 最新のモデルでテストデータに対するAccuracy、Loss、ConfusionMatrixを出力します。

なお、Active Learningでは本来は人間がラベルを付けるのですが、面倒なので、プログラムで自動的に正解ラベルを付けます。
ですので、人間が間違うリスクは検証外です。

#### 半教師あり学習とActive Learningの複合
1. 半教師あり学習の手順1.で作ったモデルをロードします。
2. 適宜追加データ（初期40,000枚）に対して最新のモデルで推論し、確率の上位2位の差が1％以下になるデータに、正解のラベルを付けて学習データに組込みます。同様に、確信度が99.99％以上になるデータに、そのラベルを付けて学習データに組込みます。（学習データ
 = 学習データ + 追加されるデータ）
3. また、学習データに組み込まれるデータは適宜追加データ（初期40,000枚）から排除します。（適宜追加データ = 適宜追加データ - 追加されるデータ）
4. モデルの再学習を行い、2.に戻ります。
5. 2.～4.を20回繰り返します。
6. 最新のモデルでテストデータに対するAccuracy、Loss、ConfusionMatrixを出力します。


いずれも、モデルの再学習（手順4.）ではモデルを初期からではなく、学習済み状態から追加で学習させます。

## やってみた
コードは長くなるので以下を参照してください。
https://github.com/shibuiwilliam/ActiveSemiSupervisedDeepLearning

学習するニューラルネットワークの構成は以下になります。

```python

model = Sequential()
model.add(Conv2D(64, (3, 3), padding='same', input_shape=x_train.shape[1:]))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Conv2D(128, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(128, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Flatten())
model.add(Dense(1024))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes))
model.add(Activation('softmax'))

opt = keras.optimizers.adam(lr=0.001, decay=1e-6)

model.compile(loss='categorical_crossentropy',
              optimizer=opt,
              metrics=['accuracy'])

es_cb = EarlyStopping(monitor='val_loss', patience=1, verbose=1, mode='auto')
chkpt = os.path.join(saveDir, 'Cifar10_.{epoch:02d}-{loss:.2f}-{val_loss:.2f}.hdf5')
cp_cb = ModelCheckpoint(filepath = chkpt, monitor='val_loss', verbose=1, save_best_only=True, mode='auto')

# train
model.fit(xTrain, yTrain,
          batch_size=50,
          epochs=100,
          verbose=0,
          validation_data=(xTest, yTest),
          callbacks=[es_cb, cp_cb],
          shuffle=True)

```

![0.png](https://qiita-image-store.s3.amazonaws.com/0/55384/71ad6a0c-65db-ffb1-6ddd-02d4def16404.png)


VGGの簡易版です。
初期モデル（半教師あり学習手順1.で作ったモデル）のAccuracyは65％程度です。
ここからどれだけ改善していけるか、検証します。
何度も再学習するため、EarlyStopping（patience=1）ですぐ中断するようにしています。

結果は以下のようになりました。
#### 半教師あり学習
- テストデータに対するConfusionMatrix
![s0.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/77bcc58b-3c37-81b4-81b8-c4135c5864df.png)

- 学習したデータ数、残りのデータ数、追加したデータ数中のラベル間違い数
![s1.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/e84b1ba5-b1ec-6fca-5aa4-314c602634f6.png)
![s1.5.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/c5e675f7-8643-9eff-7bd0-6da79ab5dd85.png)

- 学習毎のAccuracyとLossの推移
![s2.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/eb6a92ba-1b9f-9437-2335-c6934b89ab40.png)

半教師あり学習は確信度が高い（今回は99.99%以上）データのみが追加されていくので、自動車、船、馬、トラックのデータが多く追加されています。
逆に鳥、猫、鹿、犬のようなデータは追加数が少なかったです。
他方で、鳥、トラック、飛行機は間違ったラベルが追加されている確率が高かったです。鳥と飛行機も間違いやすい組み合わせですね。
ConfusionMatrixのとおり、追加の少ない鳥、猫、鹿、犬の推論は精度が低く、Accuracyの成長も鈍い傾向にあります。

#### Active Learning
- テストデータに対するConfusionMatrix
![a0.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/226d9e4b-dcd2-7f6d-8b4f-9ceff4569cc9.png)

- 学習したデータ数、残りのデータ数、追加したデータ数中のラベル間違い数
![a1.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/e5c04b11-608a-9ef3-d749-10643882d10b.png)

- 学習毎のAccuracyとLossの推移
![a2.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/1eeab3b8-ea5f-dfd5-c567-b8bd9a4e1678.png)

Active Learningは確率の上位2つが近いものを追加していきます。そのため、半教師あり学習と比べて鳥、猫、鹿、犬の追加が多い傾向にあります。
その甲斐あってか、Accuracyも高く、ConfusionMatrixの鳥、猫、鹿、犬分類もうまくいっています。
なお、11回の学習速度はActive Learningが一番早く済みました（が、本来は人間が重要な学習データにラベリングするので、実際にはもっと時間がかかるはず）。
確信度の境界次第ですが、今回設定した1％以下だと、Active Learningで追加されるデータは少なく済み、学習速度も高い状態を維持できたようです。

#### 半教師あり学習とActive Learningの複合
- テストデータに対するConfusionMatrix
![c0.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/1d6e2e19-7ba1-dfe2-d8d4-f0c801c80938.png)

- 学習したデータ数、残りのデータ数、半教師あり学習で追加したデータ数中のラベル間違い数
![c1.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/1a8455c9-5585-ac8f-dee9-d6ab1f13cfea.png)
![c1.4.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/c6165dc5-4016-910d-e015-df69fddb44df.png)

- 学習毎のAccuracyとLossの推移
![c2.PNG](https://qiita-image-store.s3.amazonaws.com/0/55384/db3c8826-94af-624d-7a0a-72d7cb209462.png)


半教師あり学習で確信度の高い飛行機、船、蛙が追加され、Active Learningで確信度の低い鳥、猫、鹿、犬が追加されるため、満遍なく各種類のデータが追加されました。
学習データ数も最多です（その分、学習時間も長くなります）。
半教師あり学習に比べて間違いラベル率も低く、安定して学習できたようです。
鳥、猫、鹿、犬判定も他の手法より高い結果になりました。
半教師あり同様、トラックの間違いラベルが他と比較して突出して高いのが難点ですが、テストデータのトラック自体は割りと推論できています。


## 最後に
今回の検証では、半教師あり学習、Active Learning、複合のうち、複合パターンが一番効果的で次にActive Learningという結果になりました。
しかし複合パターンとActive Learningは僅差であるため、Mergin Samplingがうまく機能したのだと思います。
逆に半教師あり学習は伸び悩みました。
この検証では初期モデルのテストデータAccuracyが65％程度でしたが、より高い精度のモデルから始めれば、恐らく半教師あり学習のラベル間違いが減ると期待できます。これで半教師あり学習でもより良いモデルを作れるようになるかもしれません。
また、半教師あり学習とActive Learningの複合ではそれぞれを同時に実行しましたが、Active Learningで充分に学習した後に半教師あり学習を適用するといったように、順番付けることで違う結果が出ると思われます。

## 参考
ディープラーニングへのActive Learningの適用：https://arxiv.org/pdf/1701.03551.pdf
転移学習（学習データが少ないときに有力な学習手法）：https://qiita.com/icoxfog417/items/48cbf087dd22f1f8c6f4