2018-12-24に更新

AIメーカーを支える技術(AIメーカーの作り方)

読了目安:24分

この記事は個人開発サービスに用いられている技術 Advent Calendar 2018 の22日目です。


aimaker_system.png


こんにちは、2z(Twitter: @2zn01 )です。

普段は会社員でWeb系の開発エンジニアとして働き、週末に趣味で個人開発をしています。

今年の7月に話題のAIをweb上で誰でも気軽に作れる「AIメーカー」というサービスをリリースしました!

■AIメーカー
https://aimaker.io/

この記事では、この「AIメーカー」で使っている技術をまとめたいと思います。

作ったもの

以下の3ステップで誰でも簡単にAIを作れます!

  1. AIに覚えさせたいタグを入力
  2. タグから自動で画像データを収集
  3. AIがデータから学習

画面イメージ

学習データの登録画面

モデルの学習画面

モデルの学習履歴

モデルの推論結果画面

システム構成図

AIメーカーのシステム構成図は以下の通りです。

aimaker_system.png

大きな構成としては、主にユーザからの様々なリクエストを受け付けるフロントサーバと機械学習の処理を取り扱うバックエンドサーバの2つがあります。

使用している技術

Linux

クラウドのホスティングは、Amazon Web Services(以下、AWS)をメインで使用しており、Amazon EC2でサーバを立てています。
サーバのOSはLinuxでAmazon Linux 2を使っています。

AWSがメインですが、文字起こし機能の方ではGoogle Cloud Platformで、Google Compute Engine(GCE)でサーバを立てています。
サーバのOSはLinuxでCentOSの7系を使っています。

なぜAWSの他にGCPも使っているかというと、文字起こし機能の方でGoogle Cloud Vision APIやGoogle Cloud Speech APIを使っており、大容量の音声ファイルや画像ファイルを取扱うのにGoogle Cloud Storage経由で使った方が都合がいいからというのが理由になります。

Apache

webサーバはApacheを使っています。
Nginxもありますが、Apacheは普段から使っており、設定も把握していたため、いつも通りの安定の選択としました。

MySQL

Amazon RDSを使ってMySQLを立てています。

Amazon EFS

複数のインスタンスで学習データや学習済みモデルを共有できるようにAWSのAmazon EFSというネットワークファイルシステムを使っています。

AIメーカーではフロントサーバとバックエンドサーバで分かれており、そこの連携が肝になってきます。
そこでAmazon EFSを使うことで、全インスタンスで同じファイルを共有することができるので、めんどいことを考えなくてもよくなります。

サーバをスケールさせる際に問題となってくるのがデータが格納されるDBとファイルになるかと思います。
Amazon EFSを使うことでファイルの読み込みや書き込みの速度は犠牲になるため、ユースケース次第かと思いますが、この辺の問題を考えなくてもよくなります。

ただ、ストレージ料金が高いのが悩みの種です。。><

PHP

フロントサーバ側のプログラムはPHPを使って実装しました。
フレームワークはZend Frameworkを使用しています。

Python

バックエンドサーバ側のプログラムはPythonを使って実装しました。
フレームワークはFlaskを使用しています。

Caffe

Deep LearningのフレームワークはCaffeを使用しています。

jQuery

クライアント側のJavaScriptは、jQueryを使って実装しました。

実装方法

AIメーカーを作るにあたって、一般的なwebサービスにはない機能をどう実装しているかをご紹介します。
主に以下の機能について、まとめてみます。

  • 学習データの自動収集
  • モデルの学習
  • モデルの推論

学習データの自動収集

ディープラーニングで画像分類のモデルを作る際には、AIにこの画像は〇〇であるということを教えるため、学習データにラベル付けを行います。
この学習データのラベル付けは、モデルの精度に大きく影響する重要な作業ですが、単調な作業なためにとても退屈です。

そこで、AIメーカーでは事前に登録していたラベル名から画像データを自動で収集する機能を用意いたしました。
ちなみにこちらの機能はAidemyの石川さんよりアドバイス頂いた機能です。大感謝!

ラベル名からの画像データの収集は、Bing Image Search APIを使用しています。
一つのラベルごとに50件の画像データを収集するようにしています。

この学習データの収集はブラウザからAjax通信でリクエストを投げ、サーバ側でバックグラウンドで処理させています。
処理時間はラベル数によりますが、少なくとも数分はかかるので、単純にリクエストを投げて応答を待っていると通信がタイムアウトしてしまう可能性があります。
そこで、サーバ側にリクエストを投げたら一度データベースへその処理をタスクとして保存しておき、一旦ブラウザ側へレスポンスを返しています。

サーバの裏側では常にデーモンが監視(待機)しているので、データベースへ学習データの収集タスクが登録されたら、収集処理が開始されるようになっています。
また、ブラウザ側では定期的にその進捗状況をAjax通信で見に行く(ポーリングする)ようにしています。

なお、AIメーカーでは手軽に利用できるように学習データの自動収集機能は用意しておりますが、それだけだと質の良い学習データは用意できないため、JPG、PNG、ZIPによる画像データのアップロードにももちろん対応しています。

モデルの学習

モデルの学習といえば、AIメーカーの根幹の機能になります。登録された学習データをもとにモデルを学習させます。

先のシステム構成図で示しましたが、AIメーカーにはフロントサーバとバックエンドサーバがあり、モデルの学習はバックエンドサーバで行います。
具体的には以下の流れで行います。

1. フロントサーバ側で学習データに不足がないかチェック

2. フロントサーバ側でCaffeで学習を開始するのに必要なファイルを生成する

  • deploy.prototxt、solver.prototxt、train_val.prototxtを生成
  • 学習データのファイルパス、ラベルのテキストデータを生成(全データのうち、90%を学習に使用)
  • テストデータのファイルパス、ラベルのテキストデータを生成(全データのうち、10%をテストに使用)

3. フロントサーバ側からAmazon EC2のGPUインスタンス(バックエンドサーバ)を起動する

  • 既に作成済みのインスタンスがある場合はそのインスタンスを起動する
  • まだ起動済みのインスタンスがない場合は新たにインスタンスを作成して起動する

4. インスタンスの起動後、セットアップスクリプトを実行(具体的には以下のことをやっています)

  • Amazon EFSのマウント
  • 最新のソースをGitHubからpull
  • FlaskをuWSGIサーバで起動し、リクエストを受付できるようにする

5. バックエンドサーバへの疎通確認後、フロントサーバからバックエンドサーバへCaffeの学習開始のリクエストを投げる

6. バックエンドサーバ側でリクエストを受け、Caffeの学習コマンドを実行する

  • LMDBの作成
      リストファイルは先の2で生成しておいたテキストデータを指定します。
      EFSでファイルを共有しているため、そのままファイルの同期とか考えなくてもそのまま使えます
convert_imageset -resize_height=227 -resize_width=227 -shuffle / {学習データのリストファイル} train_lmdb
convert_imageset -resize_height=227 -resize_width=227 -shuffle / {テストデータのリストファイル} test_lmdb
  • 平均画像の作成
compute_image_mean train_lmdb mean.binaryproto
compute_image_mean test_lmdb test_mean.binaryproto
  • 学習を実行
     学習ログファイルへ進捗状況が出力されていきます
caffe train -solver=solver.prototxt -weights {モデル} 2>&1 | tee {学習ログファイル}  &")

7. フロントエンドサーバ側で学習ログファイルから進捗状況を把握し、学習状況をグラフで表示

学習ログファイルはAmazon EFSで共有しているので、そのログの中から正規表現でせっせと該当値を抽出します

if (preg_match('@]     Test net output #0: accuracy = ([0-9.e\-]+)@', $record[$i], $matches)) {
    $accuracy = rtrim(rtrim(sprintf('%F', $matches[1]), '0'), '.');
}
if (preg_match('@]     Test net output #1: loss = ([0-9.e\-]+)@', $record[$i], $matches)) {
    $loss_test = rtrim(rtrim(sprintf('%F', $matches[1]), '0'), '.');
}
if (preg_match('@] Iteration [0-9]+, loss = ([0-9.e\-]+)@', $record[$i], $matches)) {
    $loss_train = rtrim(rtrim(sprintf('%F', $matches[1]), '0'), '.');
}

8. 学習完了後、バックエンドサーバ側でモデルが生成される

9. フロントサーバ側でログから学習完了を検知し、学習結果をデータベースへ保存し、EC2インスタンスを停止する

以上が処理の流れになります。

モデルの推論

学習が完了し、生成されたモデルを使って画像分類の推論を行う機能です。
こちらは以下の処理で行っています。

1. ユーザーさんが推論したい画像をブラウザからアップロード

2. フロントサーバで画像データを保存し、バックエンドサーバへ画像ファイルパスをパラメータに推論のリクエストを投げる

3. バックエンドサーバでリクエストを受け、Caffeで推論処理を実行し、結果をJSON形式で返す

4. フロントサーバでレスポンスを受け取り、推論結果をグラフで表示する

最後に

今回、特にAIメーカー特有の機能に絞って、実装方法をまとめてみました。
AIメーカーではフロントサーバとバックエンドサーバの複数インスタンスで構成し、それぞれを連携させて実現している箇所が多いため、多少複雑になっています。

実際に作ってるときは頭の中でシステム構成をイメージして実装を進めていますが、後で見返すとどうだったっけな?となることがほとんどです。
このへんはきちんとメモするか、図解するかしてきちんと残しておいた方がよさそうですね。

なお、AIメーカーでは誰もが手軽にシンプルに使えること、そして学習が早く終わるようにするため、現時点ではモデルの精度を犠牲にしています。

そのため、今後は精度向上のために学習の際のハイパーパラメータのチューニングをしたいとか、学習の回数をもっと増やしたいとかなどの要望に応えられるようにする必要があると感じています。

今後、このあたりの機能拡張も行っていきたいと思います。


この記事に少しでも興味をもって頂けましたら、ブログの読者登録やTwitter: @2zn01をフォローして頂くと更新の励みになります!

皆さんもぜひ自分のサービスを作っていきましょう!

Originally published at www.sukimanote.com

2z@AIメーカー

中学~大学でガラケー向けサービスで1億PV超えを達成するもドメイン失効でふっとばす。 web上で誰でも手軽に機械学習を始められるプラットフォーム 「AIメーカー」を開発 → https://aimaker.io/ 話題のツイートがジャンルごとに探せる「ツイレポ」も → https://twirepo.com/

Crieitは個人で開発中です。 興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!

ボードとは?

関連記事

コメント