RANDOM NOTES
ush
ush
ush

master of universe

運用中の機械学習アプリでデータを収集してモデルを再学習したい


もう鍋も食べ飽きて、また以前のように作り慣れたレシピを再現する日々を続けています。

本当に1ヶ月以上鍋を食べ続けてきたので、食べ慣れたものでもかなり新鮮に感じます。

今は学部の集大成も終わり、もう春休みです。

以前からやりたいと思っていたことを、急いで終わらせたいですね。

以前作った機械学習アプリ「Mapchip Generator」

以前、マップチップという画像を自動生成する Web アプリ「Mapchip Generator」を作ったよという記事を書きました。

これは今も稼働していて、GCP 様のおかげて無料で運用できています。

このアプリを研究室内で紹介したとき、「利用されたデータを使って、学習データを作れるのではないか」というアドバイスをいただき、やってみることに。

運用中のデータから学習データを集める

今回、このアプリの利用データから、学習に使えそうなデータを集めるために、

  • マップチップの生成は一度に 3 つ提示する
  • 生成したマップチップは、選択することでダウンロードできる

という修正をしました。

これにより、ダウンロードされたマップチップが取得できるようになります。

つまり、

ダウンロードされたマップチップは、ユーザが気に入ったマップチップであると判断して、
そのようなマップチップはきっとクオリティの高いマップチップだろうから、それを利用してモデルを学習させる

という具合です。

このように、ダウンロードされたマップチップは、良いマップチップであり、されなかったものは悪いマップチップであるという仮定の元で、 これらのデータをディスクリミネータの学習データとしてみます!

ディスクリミネータを学習させる

GAN の構造については詳細な説明をしませんが、簡単にいうと、 マップチップを作るやつとマップチップを判別するやつを用意して、マップチップを判別するやつは、マップチップがマップチップを作るやつに作られたものかどうかを判別します。 マップチップを作るやつは、マップチップを判別するやつに、自分が作ったとバレないようにマップチップを作るように学習します。

マップチップを判別するやつをディスクリミネータというのですが、このディスクリミネータが賢ければ、 マップチップを生成するやつ(ジェネレータ)はより自然な(与えられるデータに近いような)マップチップを生成するよう学習できるようになります。

元々ディスクリミネータは、「データセット中のマップチップかどうか」を判別します。 つまり、ジェネレータはデータセット中のマップチップっぽいマップチップを生成するように学習していくわけです。

収集したデータでディスクリミネータを学習する

データセット中のマップチップは当然「良いマップチップ」であるため、私はディスクリミネータは良いマップチップかどうか」を判別していると言い換えて、今回収集したデータを使って学習させます。

つまり、良いマップチップとして収集したものは、データセット中に含まれるマップチップとして、ディスクリミネータを学習します。

学習手順としては、

  • 最初に、ディスクリミネータにこの良いマップチップを入力し、データセット中のマップチップか、ジェネレータにより生成されたものかを判別させ、
  • 次にジェネレータにより生成したもの入力し判別させ、学習させます。
  • 次に、ディスクリミネータの判別を元に、ジェネレータを学習させます。

といった具合です。

実装

ダウンロードされたマップチップとモデルは GCS をアップロードし、管理します。 アップロードされたマップチップをダウンロードし、モデルを学習させ、学習させたモデルをアップロードし、サーバ側でモデルをダウンロードさせます。 ここまではスクリプトを後は cron 等を設定して定期実行させるだけなんですが、学習は重い作業なのでとりあえずローカルで行います。 そのうち置物のデスクトップで cron を設定するか、モデルがかなり小さいので可能なら使ってないラスパイでやってみようかなと思ってます。

感想

作成に際して苦戦した箇所は色々あるけど、思い出せるところとして、

  • マップチップのクオリティが低い
  • モーフィングの生成がうまくいかない

    • クオリティが低い
    • モーフィングが指定した画像から始まってない
  • GitHub Actions

    • ssh

作り始めた当初、マップチップのクオリティが著しく低くなる現象があって原因がわからなかった。 具体的には、かなーり似たような画像が生成されるという具合。 最初はマップチップのクオリティが下がっていることにすら気づけず、「まあこんなもんか?」くらいにしか思ってなかった。 原因は batch size にあった。batch size を 1 にして生成していたが、これがよくなかったらしく、学習時に揃えたらうまく行った。 batch size を推論時に変えると生成結果のクオリティに大きく影響するといった話は聞いたことがないし、そんなこと想像しなかった(常識だけど自分が知らないだけ?)ため、解決に時間がかかった。 調べても具体的な情報は出て来ず、これは今回固有の問題なんだろうか?

モーフィングの生成は、batch size 1 だったらかなり実装しやすい機能だった。 しかし、上述したように、batch size 1  だとクオリティがかなーり落ちることが判明したため、batch size を大きくする必要が出た。 そうすると、モーフィングの生成が難しくなる。 このことから、一時はモーフィング gif 生成機能を消そうかとも思った。 しかし、実行時間とメモリを犠牲に、ギリ GCE 上で動かせる範囲に収まりそうだったため、batch size を維持したままモーフィング gif を生成することにした。 このときの実装でも色々ごたついた。異なる2つの batch の中のそれぞれ一つずつのマップチップについて、モーフィングを生成するため、気をつけなければいけない点が多く、煩わしかった。

ついこの間、初めて GitHub Actions による CI を導入した。 ssh で GCE にデプロイするのだが、ssh は interactive に実行しない時に alias を展開しないということを知るのに時間がかかった。 つまり、GCE では docker-compose を docker run  から起動するため、alias として docker-compose を登録しているのだが、これが使えないということに気づけなかった。 場合によっては shopt でオプションを指定することで、alias を展開するよう指定できるみたいだが、うまくいかなかったため普通に alias の元をベタ書きした。

個人的にはお気に入りのサイトができてよかったです。 技術的にも触ったことないものにさわれたし、念願の CI に入門できてよかったです。