こんにちは。世界では異常事態が続き、フルリモートとなった今、猫とペットボトルのキャップでサッカーをして遊ぶことだけが生きがいになりました。AndroidエンジニアのTsutouです。
子供やペットの写真を、手軽にアップロードしてもらいたい
弊社のアプリ、Fammは家族アルバムアプリなので、写真/動画アップロード機能の対象となる写真は子供、ペットの写真になります。
しかし、アップロード画面を表示した時、大抵は料理だったり、スクショだったり、風景だったり、データフォルダには色々な画像がありますね。
そこで、子供やペットの写真をもっと手軽にアップロードしてもらいたい、との思いからiOS版ではCore MLを使ってアップロードレコメンドが実現されています。
今回は、Androidでも実装する運びとなり、その際にML Kitを使用した際の事例になります。
MLKit (Auto ML Vision Edge) って?
ML Kitは、Googleの機械学習にまつわる様々な機能を、Firebaseを通してモバイルで実行できる様にするSDK群です。(まだBeta版です 2020/4/23現在)
顔認証や、画像ラベリング、オブジェクト認識、バーコードスキャン、翻訳と様々なものが用意されています。
今回使用するAuto ML Vision Edge(以下、AutoML)は、Firebaseのコンソール上で独自の画像分類モデルを作成でき、形式を選んで簡単にアプリ上で使用できるものです。
今回はAutoMLで、独自の画像分類モデルを作っていきたいと思います。
Firebase
ML Kitをはじめる
さて、コンソール上から早速ML Kitをはじめてみましょう。 (※オーナーしかはじめるを押せないケースを確認したので注意)
APIの一覧が出てきたら、Auto MLを選択します。
データセットを用意する
早速、分類したい画像のデータセットを用意し、追加していきます。(単一ラベル、複数ラベルを選びます)
以下のような形式を圧縮してzip形式で登録します。(登録済みのデータセットは、エクスポートできるCSVを元にGCSからインポートできます。一回、1GB以下でないとエラーになってしまうので注意)
ここでは、年齢で区分けされた顔認証用の画像を学習させたいと思います。
基本的な年齢幅、人種を網羅したデータセットを4000枚ほど + アジア人に特化したデータなども追加で1000枚程登録しました。 犬猫は、豊富にデータがあるので適当なものを4500〜5000枚ほど そして、検出してほしくない風景や、食べ物、お花の写真などを、まとめてothersとして認識させます。(ここはもっと細分化してあげてもいいかもしれません)
じっくりコトコト学習させる
オプションを指定し、学習時間を指定します。
じっくりコトコト学習させていきます。データセットにより、時間は変わりますが画像15000枚ほど、1コンピューティング時間で、4~5時間で焼きあがりました。 コストと応相談で、学習時間やレイテンシを調整し、モデルのスタイルをユースケースに合わせて選んでいきます。
レイテンシ
- 下限レイテンシ - 軽量、高速、低精度
- 汎用 - 中間
- 高精度 - 大きめ、低速、高精度
焼き上がりを待つ
座して待ちましょう。出来上がるとFirebaseからメールが送られてきます。 完了すると、以下のようにテスト、使用ができるようになります。
適合率/再現率について
- 適合率 : 高ければ高いほど、猫っぽいものが写っている画像を猫だと認識してしまうことが減る (以下のようなパターンが減る)
- 再現率 : 高ければ高いほど、本当は猫なのに、猫以外(犬とか物体)と認識してしまうことが減る (以下のようなパターンが減る)
平たい理解ですが、トレードオフな関係です。ここの閾値もコンソールで調節できるので、どういった機能に使うのかに合わせて、適宜調節いただくのがいいと思います。
結果
中々の精度です!
アプリ
運用方法を決める(ローカルモデル/リモートモデル)
モデルの運用方法を決めます。
ローカル
- メリット
- アプリをダウンロードするだけで使える
- デメリット
- アプリサイズ増
- モデルの更新にリリースが必要
リモート
- メリット
- アプリサイズ変わらない
- リリースせずにモデルを更新可能
- リモートコンフィグなどと組み合わせて、ABテストなどもコントロールできる
- デメリット
- モデルがダウンロードできるまで機能は使えない
- 通信が行われる前提なので、コード量が増える
ローカル/リモート両刀(リモートの取得に失敗したらローカルを使用)
- メリット
- アプリをダウンロードするだけで使える
- リリースせずにモデルを更新可能
- リモートコンフィグなどと組み合わせて、ABテストなどもコントロールできる
- デメリット
- アプリサイズ増
- 通信が行われる前提なので、コード量が増える
依存関係の追加
難読化回避(ローカルモデル使用時のみ)
aaptOptions {
noCompress "tflite"
}
implementation 'com.google.firebase:firebase-ml-vision:24.0.2' implementation 'com.google.firebase:firebase-ml-vision-automl:18.0.4'
モデルの公開 or ダウンロード
ローカルのモデル
assets
ディレクトリを用意し、コンソールからダウンロードしたモデルを配置します。
/assets/automl/dict.txt
/assets/automl/manifest.json
/assets/automl/model.tflite
val localModel = FirebaseAutoMLLocalModel .Builder() .setAssetFilePath("automl/manifest.json") .build()
リモートのモデル
コンソールから、モデルを公開し、モデル名をアプリ側で指定します。ダウンロードや、フラグの取得はTasks APIになっているので、非同期処理のハンドリングが必要になります。
val remoteModel = FirebaseAutoMLRemoteModel
.Builder(BuildConfig.AUTO_ML_MODEL_NAME)
.build()
val conditions = FirebaseModelDownloadConditions.Builder().build() val downloadTask = FirebaseModelManager .getInstance() .download(remoteModel, conditions) .addOnSuccessListener { //ダウンロード成功時にしたい処理 }
fun isModelDownloaded() = Tasks.await(
FirebaseModelManager
.getInstance().isModelDownloaded(
remoteModel
)
)
画像を認識させる
設定したラベルを用意します。
const val CATS = "cats" const val KIDS = "kids" const val ADULT = "adults" const val DOGS = "dogs" const val OTHERS = "others"
モデルを用意し、オプションを設定して、画像のラベラーを用意します。今回、モデルは両刀で使っていますが、片方だけでの利用ももちろんOKです。(FirebaseVisionOnDeviceAutoMLImageLabelerOptionsでは、主に、信頼度が調整できます。デフォルトは50%)
val mlModel = if (isModelDownloaded()) { remoteModel } else { localModel } val labeler = FirebaseVision.getInstance().getOnDeviceAutoMLImageLabeler( FirebaseVisionOnDeviceAutoMLImageLabelerOptions .Builder(mlModel) .setConfidenceThreshold(0.7f) .build() )
そうしたら、画像から取得できるラベルのテキストを元に、子供、大人、犬、猫、その他を判別していきます。
suspend fun detectKidsOrPet(context: Context, uri: Uri): Boolean = withContext(Dispatchers.Default) { val targetBitmap = getTargetBitmap(context, uri) val image = try { FirebaseVisionImage.fromBitmap(targetBitmap) } catch (e: IOException) { Timber.e(":$e") return@withContext false } return@withContext try { val labels = synchronized(this) { Tasks.await(labeler.processImage(image)) } labeler.close() labels.forEach { when (it.text) { ADULT, OTHERS -> return@withContext false CATS, KIDS, DOGS -> return@withContext true } } false } catch (e: ExecutionException) { Timber.e("$e") false } catch (e: InterruptedException) { Timber.e("$e") false } }
今回は、多くの画像からパパパっと読み込んで欲しかったので、URIからBitmapに、更に出来るだけ縮小して読み込ませましたが、FirebaseVisionImageは、他にもUriからでも、色々な作成方法あるので、アプリによって画像の取り扱いは変えていくのがいいと思います。
fun getFirebaseVisionImage(bitmap: Bitmap): FirebaseVisionImage? = try { FirebaseVisionImage.fromBitmap(bitmap) } catch (e: IOException) { Timber.e("$TAG:$e") null }
これで、コンソールで出た精度そのまま、アプリ状の機能に機械学習を導入できます。
結果
犬猫子供と、大人やその他を区別してくれるようになりました。
余談 : デフォルトの画像分類SDK
MLKitの画像分類SDKの方をバンドルすれば、高精度に犬猫、赤ちゃんは認識可能かつ、FoodやScreenshot、Paper なども出来合いのままで認識でき、対象からいち早く不要な画像を弾けるので、カスタムモデルと組み合わせてより精緻な画像分類ができると思いました。実際に、犬猫、赤ちゃんまではとても精度よく認識してくれました。
子供か、大人かを判定出来ない事や、アプリサイズの増加を懸念して、運用は断念しましたが、この機能がプロダクトに及ぼすインパクトが大きいのであれば、基本的な部分をデフォルトのものにお願いし、大事な部分の判定だけAuto MLに担ってもらう、ユースケースによっては、そんな両刀の運用もありなのかなと思いました。
まだBeta版ですし、オーバーかもしれませんがそんな運用も今後の進化によって可能となってくるかもしれません。
まとめ
最初は、機械学習と聞くと少し身構えてしまいましたが、高精度のモデルを作成し、ユーザーニーズの高い機能を、結果とても簡単にアプリに導入することができました。 Firebaseを通してモデルの更新をかけて育てていけるし、APIの柔軟性もとても高く、今後の進化がとても楽しみです。
スライド
こちらのイベントでお話しさせて頂く用のスライドです
積極採用中!!
子育て家族アプリFammを運営するTimers inc.では、現在エンジニアを積極採用中!
急成長中のサービスの技術の話を少しでも聞いてみたい方、スタートアップで働きたい方など、是非お気軽にご連絡ください!
採用HP: http://timers-inc.com/engineerings