Teachable Machineでラーメン二郎の写真を学習させてみた

## ヤサイマシマシニンニクカラメ
皆さん、ラーメン二郎はお好きでしょうか？
ラーメン二郎は圧倒的なボリュームで人気のラーメン屋さんですね。
自分は以前の記事でも書いたようにラーメンが大好きなのですが、ラーメン二郎はたまに行くレベルです。

https://qiita.com/KendoLab/items/8558095ddcd7e415f20c

でもたまに無性に食べたくなり、食べに行ったら大体冒頭のコールで頼み楽しんでいます。
そんなラーメン二郎ですが、周りにもファンが多く、先日とある同僚が別の同僚に二郎の写真を見せたところ、それがどの店なのか言い当てるというシーンに出くわしました。

そこでふと思いつきました。
**「二郎の写真を機械学習させたらどの店か見分けることができるようになるのか？」**
という訳で、実際にやってみました！

## Teachable Machineで学習させてみた
自分で機械学習の環境を用意するのは大変なので、今回は**Teachable Machine**を使ってみました！
（Teachable Machineについて、詳しくは以下の記事をご覧ください）

https://ledge.ai/teachable-machine-try/

Teachable Machineの素晴らしいところは、とにかく簡単に機械学習ができるところだと思います。記事の通り、各店舗ごとのラーメンの画像をアップロードしてトレーニングしていきます。

![スクリーンショット 2022-06-10 165105.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607554/614533fb-ec53-c44e-19cc-1b25c863a197.png)
通常、画像認識のモデルを作るためには数千枚単位のイメージが必要になるかと思いますが、今回はサクッと各店舗20枚という少ないデータでどこまで精度が出るか試してみたいと思います。
（結果は記事の後ろの方に記載しているので、気になる方はそこまで読み飛ばしてください！）

## 「二郎Classifier」
上記で作った画像認識モデルを呼び出す形で作成したWebアプリが、こちらになります！

https://shimmering-smakager-c7e81c.netlify.app/

![スクリーンショット 2022-06-10 163719.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2607554/4524f59b-2fa9-5860-7e2f-30f64d0354d7.png)
使い方としては非常にシンプルで、二郎の画像をアップロードすると裏側でTeachable Machineの画像認識モデルを使用した推論処理が走り、結果が表示されます。
その結果が正しければ「その通りです」、間違っていたら「違います」ボタンを押してください。（押しても特に再学習とかは行われません。。ただ教えてあげるだけになります。）

なお、アプリの方にも記載していますが、今回はプロトタイプということですべての店舗について学習を行った訳ではありません。現在認識できるのは以下の店舗のみになります。
（選定基準は特になく、自分が実際に行ったことのある店にいわゆる人気店を加えた感じになっていますのでご了承ください）

三田本店、野猿街道店、関内店、ひばりヶ丘店、神田神保町店、池袋東口店、歌舞伎町店、目黒店、越谷店、桜台駅前店、小滝橋通り店、亀戸店

## 環境
Netlify
Vue.js
ml5.js
Teachable Machine

## ソースコード
今回作ったWebアプリのソースコードは以下になります。
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="oNEamzq" data-user="kelab_8" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/kelab_8/pen/oNEamzq">
  二郎Classifier</a> by KendoLab (<a href="https://codepen.io/kelab_8">@kelab_8</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## 解説
プログラムでポイントとなる部分は以下になります。

```js
// モデルのロード
let classifier = ml5.imageClassifier('https://teachablemachine.withgoogle.com/models/iahKvH4vq/' + 'model.json', () => {
  // ロード完了
  console.log('モデルのロードが完了しました。');
});

// モデルの呼び出し
async function classifyImage() {
  classifier.classify(document.getElementById("my_image"), getResult);
}

// 推論結果取得
function getResult(error, results) {
  if (error) {
    console.error(error);
  } 
  console.log(results);
  // 最も確信度の高い結果を表示
  let resultText = "こちらは" + results[0].label + "ですね？";
  document.querySelector("#alert").textContent = resultText;
}
```

まずml5.jsを利用してTeachable Machineのモデルをロードしています。
そして画面からイメージがアップロードされたタイミングでclassifyImageメソッドが呼び出され、推論が実行されます。
この時、結果取得するためのgetResultメソッドを第二引数として指定することで、推論結果を受け取りそのまま処理することができるようになっています。

**～つまづいたポイント～**
今回フレームワークとしてVue.jsを採用しているのですが、Vue.jsの中のメソッドとして推論処理を記述するとなぜかうまく動かないという事象が発生しました。
色々試行錯誤したのですが解決に至らなかったため、やむを得ず推論に関する処理だけ切り離してコーディングすることになりました。
この辺りは自分も理解が不足している部分なので、今後もう少し調べてみようと思います。

## 二郎Classifierの精度
それでは最後に、今回作ったモデルで結局どの程度の精度が出たのか記載していきます。
精度検証ですが、学習に使用していない画像を各店舗ごとに2枚ずつ用意して投入しました。結果は以下の通りです。

|店舗|1回目|2回目|
|:-:|:-:|:-:|
|野猿街道店|✖(三田本店)|✖(三田本店)|
|三田本店|〇|〇|
|関内店|✖(亀戸店)|✖(桜台駅前店)|
|ひばりヶ丘店|〇|〇|
|神田神保町店|〇|✖(桜台駅前店)|
|池袋東口店|〇|✖(歌舞伎町店)|
|歌舞伎町店|〇|〇|
|目黒店|✖(歌舞伎町店)|〇|
|越谷店|✖(神田神保町店)|✖(三田本店)|
|桜台駅前店|〇|〇|
|小滝橋通り店|〇|〇|
|亀戸店|〇|✖(野猿街道店)|

※()は誤って回答された結果

正解は24分の14で正解率は **58.3％** となりました。
…うーん、何とも言えない数字ですね。。

やはり20枚ずつという画像の枚数が少なかったのか、店によってはまったく当たっていないところもありますね。ただ、逆にこのくらいの枚数でも半分以上は正解していると言えるので、もっと学習させればさらに精度を上げることも可能なのではないでしょうか。

## あとがき
初めて画像の機械学習というものを自分の手でやってみましたが、やはり一番大変なのは学習データを集めるところでしたね。合計200枚ちょっとでも大変だったので、もっと本格的にやろうとしたらスクレイピング等を活用しないととても太刀打ちできなそうだなと思いました。
それにしても、Teachable Machineは本当に簡単に機械学習ができる素晴らしいサービスですので、機械学習に興味があってもなかなか手を出せなかったという方は是非使ってみていただきたいなと思います。

以上、ここまでお読みいただきありがとうございました！


**～追伸～**
作業中に二郎の画像を見すぎて二郎を食べたい欲求を抑えきれなくなってきたので、今度久しぶりに食べに行ってこようと思います…！