「Teachable Machine」で画像認識！　～アパレル売り場写真の自動仕分け～

とある流通業で衣料品を販売している私。
この夏から業務の改善を目指して「デジタル」を学び始めて約１カ月となります。

9月某日　私の部署に衝撃が走りました。組織の機構改革です。
それに伴って、私の担当業務が変わります。
そこで問題が・・・


![スクリーンショット 2024-09-24 230305.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/407508b1-cf1b-f3a2-c2a8-522c5557630e.png)




今まで担当していた **【フォーマルウェア】**   と  **【水着】**  の大量の「売り場写真」をどうしよう・・・
店舗の売場つくりの参考資料として撮りためた売り場写真の数千枚が未整理のまま「Google Drive」のフォルダに入っています。後任に引き継ぐにしても、まずは整理が必要です。

そこで **「Teachable Machine」** で「売り場写真」を【フォーマル】と【水着】【その他】に仕分けて分類させてみることにしました。

# やりたいこと
「Teachable Machine」を使用して「売り場写真」の判定

# 使用ツール
「Teachable Machine」
「Google Drive」

# できたもの

https://x.com/A0Ishida2563/status/1838554220754313516


# プロセス
## 「Teachable Machine」で画像認識モデルの作成
### １・「Teachable Machine」の準備をする
↓こちらにアクセスして

https://teachablemachine.withgoogle.com/

使ってみるをクリック

![スクリーンショット 2024-09-24 201154.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/3e4801da-1aaf-675a-2f57-a82aa311db16.png)

画像プロジェクトを選択し、標準の画像モデルを選択します。

![スクリーンショット 2024-09-24 201306.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/6ae00da6-92b9-fd6c-79be-8d3040c96d18.png)

![スクリーンショット 2024-09-24 201344.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/41009aac-228f-4960-63f8-585524186f86.png)

### ２・クラス名を変更する
Class 1 を「フォーマル」に変更
Class 2 を「水着」に変更

![スクリーンショット 2024-09-24 201716.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/5ec377f9-706c-2195-8836-1ad64cc4db07.png)

### ３・クラスを追加
Class 3 を追加して「その他」に書き換えます。

![スクリーンショット 2024-09-24 202000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/c30cfe4b-67df-48ad-81a1-4a1d9902b430.png)

### ４・画像をアップロードする
今回は写真の仕分けのため、「アップロード」を選択。

![スクリーンショット 2024-09-24 202604.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/69e707d6-0fb0-8d37-cbe7-cc92fc433b40.png)

「Google Drive」から＜モデル作成用＞のサンプル写真のファイルを「フォーマル」「水着」「その他」の各Classにアップロードします。

今回は「Teachable Machine」の「機械学習」において画像の認識率を高めるため、
「フォーマル」160枚・「水着」100枚・「その他」50枚のサンプル画像を用意しました。
「その他」には「カジュアルウェア売場」「風景」「人物」「食べ物」など仕分けしたい「フォーマル」「水着」以外の要素のサンプル画像を集めています。

![スクリーンショット 2024-09-24 202749.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/bb3a825d-6910-2a98-4016-41f75f6283d8.png)

:::note warn
「Teachable Machine」で読み込む画像の拡張子はJPEG・PNG・GIF形式の画像がサポートされています。
:::

モデル画像を読み込んたところです。

![スクリーンショット 2024-09-24 204105.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/6a1b4222-8f7b-6b23-3e3c-1759d3508375.png)


### ５・トレーニング
モデルをトレーニングするボタンを押します。

![スクリーンショット 2024-09-24 204550.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/8ab47f64-a5d5-a17a-9ff5-d9852349ad06.png)

## 「画像認識モデル」完成 
しばらく待つとモデルが作成されます。

![スクリーンショット 2024-09-24 204959.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/5e68d0d7-d994-4656-c253-877c4ae8cbd3.png)

## 動作確認
プレビューに認識させたい写真をアップロードします。

![スクリーンショット 2024-09-24 210946.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/d8a94de8-b7f9-bc6f-f386-b71c4b3e8d9d.png)

写真の下部にある「フォーマル」「水着」「その他」の＜出力バー＞で認識度を判定します。

![スクリーンショット 2024-09-24 205618.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/37208677-eb15-75cd-35b3-f4173de3b777.png)


https://gyazo.com/83b3b2664423ddf2a4eca878745b9b65


## 認識確認
今回は認識しやすく「フォーマル売場」「水着売場」「その他」の3つに分けましたが、
「フォーマル売場」の認識率は約80％・「水着売場」は約60％の認識率です。
「フォーマル」は＜黒＞で比較的認識しやすく、「水着」は＜ビキニ＞など特徴がはっきりしているものは認識するものの、＜ラッシュガード＞などカジュアル要素の高いものは「その他」に分類されてしまうようです。

## モデルをエクスポート→アップロードする
モデルをエクスポートボタンをクリックします。

![スクリーンショット 2024-09-24 221311.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/6128d448-a852-cbd6-e4d9-a9b514e29512.png)

モデルをアップロードします。

![スクリーンショット 2024-09-24 221527.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/2b68b394-e2c0-ffb4-d82b-9faccfd10189.png)


しばらくすると「クラウドモデルは最新です」と表示され、「共有可能なリンク」が表示されるのでコピーしておきます。このリンクを使用すると他のAPIやITツールとの連携が可能となります。

# まとめ
今回は「Teachable Machine」を使用して、「フォーマルウェア」「水着」を中心に衣料品の「売り場写真」の画像を認識させました。
モデル作成用の売り場サンプル写真は通常の事例で使用される約3倍用意し精度向上を目指しました。
それでも「フォーマル」「水着」のようにハッキリわかる売場の認識率は高いですが、「カジュアルウェア」のように＜機械学習＞の判断基準が **＜あいまい＞** で多岐に渡る画像の認識率は振れ幅が大きくなります。
実際の業務で【アパレル売り場の画像を認識させて出来栄えを確認】のように実装する場合は、認識範囲を例えば「マネキン」や「パンツ売り場」などさらに絞って実施するか、「Teachable Machine」と「ChatGPT」などの＜生成AI＞を組み合わせて判断基準を広げる必要があると感じました。
しかし、基準がはっきりしているもの、例えばインナー売場の「カラー別陳列」や「品番別陳列」であればその可否を判断でき、「新入社員への陳列教育」や「フック掛け商品の欠品確認」など実務への活用のアイデアが広がります。

# 追記　写真判定による自動分類の試み
今回の目的は未整理の **数千枚** の「売り場写真」を仕分けしたいというのがきっかけです。
「Teachable Machine」で「フォーマル」と「水着」と「その他」に判定することは出来ましたが、それを目視で仕分けるのは膨大な時間がかかります。
そのため、「CodePen」と「GitHub」を使用して、自動仕分けを試みます。
「Teachable Machine」でモデルを＜エクスポート→アップロード＞した結果の「URL」とコードス二ペット「p5.js」を使用します。

# 使用ツール①
「GitHub」

## できたもの

![スクリーンショット 2024-09-29 234135.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/2a9d090a-18b3-ecf3-97a6-835854fcdba1.png)


「GITHUB」でアプリ化を試みましたが、画像ファイルの分類でエラーがでて判定が出来ませんでした。
デバックを８回実施しましたが原因が分かりません。

<details><summary>エラーしたコード</summary>


```html
</details>
<details open><summary><!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Teachable Machine Image Model - File Upload</title>
    <style>
        .progress-bar {
            width: 100%;
            background-color: #f3f3f3;
            height: 24px;
            position: relative;
            margin-bottom: 10px;
        }
        .progress-bar div {
            background-color: #4caf50;
            height: 100%;
            width: 0;
            transition: width 0.3s;
        }
    </style>
</head>
<body>
    <h1>Image Classification using Teachable Machine</h1>
    <p>Upload an image to classify it as "フォーマル", "水着", or "その他".</p>
    <input type="file" id="image-upload" accept="image/*">
    <div class="progress-bar">
        <div id="progress-bar-fill"></div>
    </div>
    <img id="uploaded-image" src="" alt="Uploaded Image" width="400px">
    <p id="prediction">Prediction: Loading...</p>
    <div>
        <p>フォーマル: <span id="formal-output"></span></p>
        <p>水着: <span id="swimwear-output"></span></p>
        <p>その他: <span id="other-output"></span></p>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@2.0.0"></script>
    <script src="https://cdn.jsdelivr.net/npm/@teachablemachine/image@0.8/dist/teachablemachine-image.min.js"></script>
    <script>
        const URL = "https://teachablemachine.withgoogle.com/models/O2CXrhSLy/";

        let model, maxPredictions;

        async function init() {
            const modelURL = URL + "model.json";
            const metadataURL = URL + "metadata.json";
            model = await tmImage.load(modelURL, metadataURL);
            maxPredictions = model.getTotalClasses();
        }

        async function predict() {
            const imageElement = document.getElementById('uploaded-image');
            const prediction = await model.predict(imageElement, false);
            
            document.getElementById('formal-output').innerText = (prediction[0].probability * 100).toFixed(2) + "%";
            document.getElementById('swimwear-output').innerText = (prediction[1].probability * 100).toFixed(2) + "%";
            document.getElementById('other-output').innerText = (prediction[2].probability * 100).toFixed(2) + "%";
            document.getElementById('prediction').innerText = "Prediction complete!";
        }

        document.getElementById('image-upload').addEventListener('change', function (event) {
            const file = event.target.files[0];
            const reader = new FileReader();
            const imgElement = document.getElementById('uploaded-image');
            const progressBar = document.getElementById('progress-bar-fill');

            reader.onload = function (e) {
                imgElement.src = e.target.result;
                imgElement.onload = function () {
                    progressBar.style.width = "100%";
                    predict();
                };
            };

            reader.onprogress = function (e) {
                if (e.lengthComputable) {
                    const percentLoaded = Math.round((e.loaded / e.total) * 100);
                    progressBar.style.width = percentLoaded + "%";
                }
            };

            reader.readAsDataURL(file);
        });

        init();
    </script>
</body>
</html>
</summary>
```
</details>


# 使用ツール②
「CodePen」

## できたもの１

![スクリーンショット 2024-09-30 003314.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/9df8633f-cd81-673c-35c5-d9fcb9dd0bbf.png)



画像ファイルのアップロードは出来るのですが、「CodePen」でも画像分類でエラーが出て判定結果が返ってきません。
「ChatGPT」を使用してこちらもデバックを10回行いましたが、最終的な「ChatGPT」の判断は以下の通りでした。

![スクリーンショット 2024-09-28 230516.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3822083/25bd3b5b-d351-c34e-430f-7cd7a7a56393.png)


## できたもの２
「CodePen」では画像をアップロードして「Teachable Machine」のモデルを出力できないのなら、インカメラを使用すればどうか？ということで、出来たのがこちらです。

https://gyazo.com/fb7880dcf078f8c33a9251eda87d0708

これなら、「Teachable Machine」のモデルで直接分類するのと変わりません（笑）

# 結果と考察
「Teachable Machine」でモデルを作成するのは比較的容易ですが、その結果を出力するのは難易度が高く実装には程遠い結果となりました。
「本の表・裏」や「犬・猫」のように２つのクラスのどちらかを判定するモデルであれば良いのですが、今回のモデルのように２クラス以上あり、各クラスの判定割合が **＜あいまい＞** な場合は基準を設けてどのクラスに分類するのかを決定するプロセスを入れないと、アウトプット先のツールに繋がらず「分類エラー」となってしまうのではないかと考察します。
自動分類のためにはさらに「ITツール」についてのスキルを向上させる必要があると実感しました。

＊24/9/29追記しました。