FirebaseとFlutterで誰でも機械学習プログラミング

この記事は[ドワンゴ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/dwango) 15日目の記事です。

昨日は @ru_shalm さんの[【縦書きCSSの夢：電子書籍みたいにページ単位でページ送りをする話】](https://qiita.com/ru_shalm/items/4d038b8ed14c99040617)でした。
初めてカクヨムの縦書きモードを使った時は本当に感動しました。小説が縦書きになるだけでこんなに読みやすくなるとは思っていませんでした。ただ、スクロールスタイルだと電子書籍よりも読みにくいと感じてしまうのが正直なところなので、Webの小説もこの記事のように、電子書籍アプリで読んでいるような読み心地になるととても良いと思います。
また、僕は仕事で[N予備校](https://www.nnn.ed.nico/)のiOSアプリを開発しています。N予備校にも現代文や古典のコースがあり、そこでは縦書きで教材を提供しています。縦書き関係のCSSはOSバージョンでの動作の違いもあるなど、なかなか開発が難しい部分でした。

今回の記事では僕のアドベントカレンダー史上初めて、仕事でやっているiOSアプリ開発について書こうと思います。 また、Flutterについて記述しますが現在は仕事でFlutterを使っているわけではないのでご了承ください。

# Flutterとは？
アプリの開発ではSwiftやKotlinを使ったプラットフォームごとに用意されたフレームワークを使って別々に開発していく方法や、React NativeやCordovaのようなフレームワークを使い、クロスプラットフォームアプリとして開発していく方法があります。[^1]
[^1]: もちろんこれらを組み合わせる方法もあります。

[Flutter](https://flutter.io/)は後者に近いフレームワークです。1ソースでiOS、Androidの各アプリを開発できます。Googleが主導して開発を行っていて、言語はDartで開発します。

Googleが開発していることもあり、Googleが提唱している[Material Design](https://material.io/)に従ったアプリをとても簡単に作ることができます。[^2]
[^2]: というより、僕はMaterial Design以外でアプリを作るのはとても難しいと感じています。Flutterをもし仕事で使うことになった場合は、まずMaterial Designでいいかどうかの合意を取るところから始めると思います。

ここでFlutterについて詳しくは記述しませんが、Flutterは勉強もしやすく、チュートリアルも充実しています。僕のおすすめは、まず[Get Started](https://flutter.io/docs/get-started/install)を一通りやった後、[Codelabs](https://flutter.io/docs/codelabs)の順でやることです。
Codelabsの[Write Your First Flutter App, part 1](https://codelabs.developers.google.com/codelabs/first-flutter-app-pt1/#0)と、[Write Your First Flutter App, part 2](https://codelabs.developers.google.com/codelabs/first-flutter-app-pt2/#0)をやるだけでいろいろなアプリを作ることができるようになると思います。[^3]
[^3]: [Codelabs](https://codelabs.developers.google.com/)のことはFlutterを勉強している時に知ったのですが、Flutter以外にもいろいろなサンプルコード、ドキュメント、チュートリアルが充実していてとても良さそうでした。僕はFlutter関係のCodelabをメインに見ていますが、余裕ができれば他のものも見てみようと思っています。

# Firebaseとは？
[Firebase](https://firebase.google.com/)は言わずとしれたmBaaSで、モバイルバックエンドとしてとても優秀なサービスです。[Firestore](https://firebase.google.com/products/firestore/)などのデータベースが有名ですが、それ以外にも[認証を受け持ってくれたり](https://firebase.google.com/products/auth/)、[アクセス解析もしてくれたり](https://firebase.google.com/products/analytics/)、[機械学習を用いたユーザー行動の予想](https://firebase.google.com/products/predictions/)なんてこともできます。
Firebaseはアプリを開発してグロースさせていくためには必須で入れる必要があると言っても良いものであると感じています。

Firebaseには様々なプロダクトがあり、すべてのプロダクトは[こちら](https://firebase.google.com/products/)から見ることができます。
今回の記事ではこの中から、[ML Kit](https://firebase.google.com/products/ml-kit/)について記述します。

## ML Kit
機械学習は最近のトレンドの一つです。[ガートナー](https://it.impressbm.co.jp/articles/-/16586)もAIは現実の問題を解決していく時期に入っていると説明していたり、最近では[NTTデータがAI技術者に最大で3000万の給与を支給する](https://www.nikkei.com/article/DGXMZO38512250U8A201C1TJ2000/)など、バズワードで片付けられる時期は終わり、実際にAIを活用し問題を解決していくフェーズに入っています。AI技術者は給与が高いことも多いらしく、アプリ開発者の僕としてはAIを勉強しておけば良かったと後悔しているかもしれません。[^4]
[^4]: https://twitter.com/dangiruba/status/1069893610315821056

ML Kitはそんな機械学習の難しい部分をすべてGoogleがやってくれて、機械学習のすごい部分だけを簡単に扱うことができるフレームワークです。似たようなものにAppleのCore MLがあります。[^5]
[^5]: Googleが用意している難しい部分は汎用的に作られているため、個別の問題を解決するために、Googleが用意したモデル以外にも、自分たちで作成したドメインに特化させたモデルも扱うことができます。このモデルを作る人が給与をたくさんもらっているのでしょうね。目指していきたいものです。

# FlutterとFirebaseでアプリを作ろう！
FlutterとFirebaseは組み合わせてアプリを開発することができます！

まずは[Get Started](https://flutter.io/docs/get-started/install)を参考にFlutterをインストールしましょう。

インストールが終わった後はFirebaseをFlutterで使えるようにします。
FirebaseとFlutterを接続する方法として、[こちらのFirebaseのドキュメント](https://firebase.google.com/docs/flutter/setup?hl=ja)が参考になります。

今回はML Kitを使用するため、`pubspec.yaml` を以下のように記述しましょう。

```pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  firebase_ml_vision: ^0.2.0
  image_picker: ^0.4.5
```

今回作るサンプルプログラムでは画像の解析を行ってみたいと思います。ML Kitのライブラリである`firebase_ml_vision`と、解析するための画像を取得するために`image_picker`をインストールしましょう。
pubspec.yamlはPodfileのようなもので、いろいろな情報が記述されています。依存しているライブラリを記述すれば、Flutterがpackageをインストールしてくれます。依存関係以外にも様々なことを記述するので、どちらかというとpackage.jsonに近いものだと思います。

さて、次は実際にこれらのライブラリを使って機械学習をするアプリを作ってみましょう。
今回はML Kitのラベル推定を行います。ML Kitに画像を与えると、その画像を説明するテキストが返ってきます。Googleのサーバーを使って推定する有料のクラウド版と端末内で推定する現時点では無料のものがあります。クラウド版の方が優れた結果が出るのですが、今回は端末内での機械学習でラベル付けを行っていきます。

## コードを書いてみる
Flutterで新規プロジェクトを作ったときに作成される `_MyHomePageState` クラスにML Kit用のコードを追加していきましょう。

まずは、画像と機械学習後のラベルを格納する変数を作成します。

```main.dart
class _MyHomePageState extends State<MyHomePage> {
  File _imageFile;
  List<Label>  _scanResults;
  // ...
}
```

次に、image_pickerで端末内の画像を読み込む関数を作成しましょう

```main.dart
class _MyHomePageState extends State<MyHomePage> {
  // ...
  Future<void> _loadImage() async {
    setState(() {
      _imageFile = null;
    });

    final File imageFile =
        await ImagePicker.pickImage(source: ImageSource.gallery);

    if (imageFile != null) {
      _scanImage(imageFile);
    }

    setState(() {
      _imageFile = imageFile;
    });
  }
  // ...
}
```

`_loadImage`関数でも使ってい、画像を推定する、ML Kitを呼び出している`_scanImage`関数はこのように実装します。

```main.dart
class _MyHomePageState extends State<MyHomePage> {
  // ...
  Future<void> _scanImage(File imageFile) async {
    setState(() {
      _scanResults = null;
    });

    final FirebaseVisionImage visionImage =
        FirebaseVisionImage.fromFile(imageFile);

    FirebaseVisionDetector detector = FirebaseVision.instance.labelDetector();

    final List<Label> results =
        await detector.detectInImage(visionImage);

    setState(() {
      _scanResults = results;
    });
  }
  // ...
}
```

ここまでのコードでは何も難しいことはしていなく、

1. image_pickerのライブラリを使用して画像を取得する(_loadImage)
2. 取得した画像をML Kitに渡す。detectorにはlabelDetectorを使用する(_scanImage)

と、これだけのコードです。
後はこれらの関数をUIに組み込めば完成です。
FlutterのUIはbuild関数で組み立てます。

```main.dart
class _MyHomePageState extends State<MyHomePage> {
  // ...
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: buildImage(context),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _loadImage,
        tooltip: 'Load Image',
        child: Icon(Icons.add),
      ),
    );
  }
  // ...
}
```

BodyとFloating Action Buttonを定義し、Bodyには画像データを、Floating Action Buttonが押された時は先程作成した`_loadImage`関数を呼ぶように指定します。
最後に`buildImage`関数の実装を見てみましょう。

```main.dart
class _MyHomePageState extends State<MyHomePage> {
  // ...
  List<Widget> buildImage(BuildContext context) {
    if (_imageFile == null) {
      return <Widget>[
        Text(
          'Select your photo from Floating Action Button.',
        ),
      ];
    } else {
      String labelString = "";
      if (_scanResults != null) {
        for (Label label in _scanResults) {
          labelString += 'Label: ${label.label}, '
              'Confidence: ${label.confidence.toStringAsFixed(2)}\n';
        }
      }
      return <Widget>[
        new Image.file(_imageFile),
        new Text(labelString),
      ];
    }
  }
  // ...
}
```

画像が無い時は説明の文字列を、画像がある時はその画像と画像から推定したラベルの一覧をテキストとして表示しています。
画像やラベルが変わった時に再描画などをするコードは記述していませんが、FlutterではStatefulWidgetの内容に変更があると自動で再描画を行います。今回のケースだと、画像が追加された時、推定が終わってラベルが生成された時に自動的に再描画され、画面が更新されます。

たったこれだけの実装で、機械学習を画像に適用して、画像にどんなものが写っているのか表示するプログラムができてしまいます！とても簡単ですね！

作成したアプリのプロジェクト全体はこちらです: https://github.com/kouki-dan/flutter-firebase-ml

実際にどんな結果が出るのか見てみましょう。
作成したアプリに僕の銀座ランチコレクションを与えていきたいと思います。[^6]
[^6]: ドワンゴは銀座にあり、銀座はランチが美味しいお店が多いことに加え、築地も近いため多種多様でとても美味なランチを堪能することができます。

## 東銀座ランチコレクション
### しゃぶしゃぶ [銀座しゃぶ通 好の笹](https://tabelog.com/tokyo/A1301/A130101/13043452/)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/41033/3a6f4c52-75f3-8764-ac45-df85cbfff7c6.png)

Labelの値が推定された文字列、Confidenceは確からしさ（どれくらいの自信があるか、範囲は0~1)です。
Food, Cuisineあたりが0.9を超えていますね。見事に食べ物だということを当てています。

### オムライス [ア・ヴォートル・サンテ・エンドー](https://tabelog.com/tokyo/A1301/A130101/13010659/)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/41033/11ef660d-bec8-623d-ae93-5b9022417862.png)

こちらも見事に食べ物だということを当てています。

### 寿司 [かつら](https://tabelog.com/tokyo/A1313/A131301/13121289/)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/41033/f018d232-1e1f-84db-7a14-90da7337c993.png)

こちらも見事にFoodだと言うことを当てていることに加え、Sushiとも推定されています。完璧ですね。

### ラーメン [船見坂](https://tabelog.com/tokyo/A1301/A130101/13004985/)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/41033/7c9665ba-a917-7f63-f8e6-b6292edf5dcc.png)

もちろんラーメンも完璧に認識しています。さすがに味噌バターコーンラーメンだということは認識されてはいませんが。

### 牡蠣 [かなわ](https://tabelog.com/tokyo/A1301/A130101/13097235/)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/41033/1024417e-bf80-8973-0514-105b5d421e19.png)

少し難しいかと思いましたが、カキフライも完全に食べ物だと理解されています。

### ピザ [Vomero](https://tabelog.com/tokyo/A1301/A130101/13139857/)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/41033/9e9e8679-d933-d0b1-24a5-f1a33b32fbea.png)

最後はピザです。こちらは Food より Pizza の方が高いですね。Firebaseはアメリカ育ちなのできっとPizzaをよく食べていたのでしょう。

# まとめ
ここまで見てきたように、FlutterとFirebaseを使うことで簡単に機械学習を使ったアプリを作ることができます。ML Kitはラベル付け以外にもテキスト認識や、クラウドを利用したより高度なラベル付け、顔検出やバーコードスキャンもできます。まだベータ版ですが、機械学習の知識が全く無くてもこのような機械学習を利用したアプリを作成することができます。

本来ならここでFirebaseのML Kitを使ってこのようなアプリを作成しました！と宣伝する予定でした。しかし、急遽スマブラが発売されたからなのかはわかりませんが、作成したアプリのリンクが見つかりません。
Firebaseを使ったアプリをいろいろと製作中なので、興味があれば[僕のTwitter](https://twitter.com/dangiruba)をフォローして早くアプリのリリースしろよ！！と急かしていただければと思います。

今回はML Kitを使用しましたが、Firebaseはアプリ開発に必須だったり便利だったりするものがたくさん含まれています。ぜひドキュメントを参照し、これからのアプリ開発に役立てていただければと思います。

また、ドワンゴでは[エンジニアの採用](https://progres05.jposting.net/pgdwango/u/job.phtml?job_field=001#job_field)も積極的に行っています！ [N予備校のiOSアプリエンジニア](https://progres05.jposting.net/pgdwango/u/job.phtml?job_code=1541&_type=&business=&job_field=001#job_field)のポジションも募集中で、こちらは今の所Flutterは使っておらず、SwiftとRxSwiftを用いたネイティブアプリですが、興味を持たれた方はぜひご応募お待ちしております！

ドワンゴに入社して銀座の美味しいランチを食べつくしましょう！