veg_classify — 野菜分類アプリケーション¶
veg_classify は K230 向けの野菜画像分類チュートリアルです。PC 上での ResNet-18 モデル学習から、kmodel 変換、K230 実機デプロイまでの エンドツーエンドのワークフロー を step-by-step で学習できます。
前提条件¶
- K230 SDK がビルド済みであること(ツールチェーン展開済み、MPP ライブラリコンパイル済み)
- SDK がリポジトリルートの
k230_sdk/に配置されていること - Python 3.8 以上(
requirements.txt参照) - ホスト OS: x86_64 Linux
- CMake 3.16 以降
SDK のビルド
K230 SDK のビルド手順については SDK ビルド を参照してください。
全体ワークフロー¶
データセット (画像フォルダ)
│
├─ split_data.py: train/val/test 分割
│
├─ train.py: ResNet-18 学習 (CPU)
│ ├─ best.pth (PyTorch weights)
│ ├─ best.onnx (ONNX export)
│ └─ best.kmodel (nncase compile)
│
├─ step1: ONNX モデル解析
├─ step2: ONNX 簡略化 (onnxsim)
├─ step3: kmodel コンパイル (PTQ量子化)
├─ step4: kmodel シミュレーション
└─ step5: ONNX Runtime との精度比較
│
kmodel ──→ K230 実機アプリ
│
┌─────┴─────┐
│ カメラ入力 │
│ AI2D → KPU │
│ 分類結果表示 │
└───────────┘
Part 1: モデル学習 (PC)¶
環境セットアップ¶
リポジトリルートの .venv を使用します:
GPU なしで学習可能
config.yaml で device: cpu に設定されています。サンプルデータセット (5 カテゴリ × 50 枚) であれば CPU でも数分で学習が完了します。
データセットの構成¶
サンプルデータセットは apps/veg_classify/data/ に同梱されています(kendryte/K230_training_scripts より):
data/
├── broccoli/ # ブロッコリー (50枚)
├── carrot/ # にんじん (50枚)
├── eggplant/ # ナス (50枚)
├── spinach/ # ほうれん草 (50枚)
└── tomato/ # トマト (50枚)
カスタムデータセットを使う場合は、同じフォルダ構成(カテゴリ名ディレクトリ + 画像ファイル)で配置してください。
学習設定 (config.yaml)¶
| パラメータ | デフォルト値 | 説明 |
|---|---|---|
dataset.root_folder |
../data |
データセットルート |
dataset.split |
true |
データ分割を実行するか |
dataset.train_ratio |
0.7 |
学習用比率 |
dataset.val_ratio |
0.15 |
検証用比率 |
dataset.test_ratio |
0.15 |
テスト用比率 |
train.device |
cpu |
学習デバイス |
train.image_size |
[224, 224] |
入力画像サイズ |
train.mean |
[0.485, 0.456, 0.406] |
ImageNet 正規化 mean |
train.std |
[0.229, 0.224, 0.225] |
ImageNet 正規化 std |
train.epochs |
10 |
エポック数 |
train.batch_size |
8 |
バッチサイズ |
train.learning_rate |
0.001 |
学習率 |
deploy.target |
k230 |
ターゲットチップ |
deploy.ptq_option |
0 |
量子化タイプ (0=uint8) |
学習の実行¶
直接実行¶
CMake の train ターゲットを使うと、venv 作成・依存パッケージインストール・データ変更検知を自動で行います(詳細は CMake ターゲット を参照):
train.py は以下を一貫して実行します:
- データセット分割 (
split_data.py) - ResNet-18 学習 (PyTorch)
- ONNX エクスポート
- kmodel 変換 (nncase がインストール済みの場合)
出力ファイル (build/veg_classify/output/):
| ファイル | 説明 |
|---|---|
labels.txt |
カテゴリ名一覧 |
train.txt / val.txt / test.txt |
データ分割結果 |
samples.txt |
キャリブレーション用サンプルパス |
best.pth |
ベスト検証精度の重み |
last.pth |
最終エポックの重み |
best.onnx |
ONNX モデル |
best.kmodel |
K230 向け kmodel |
Part 2: kmodel 変換 (PC)¶
train.py で自動的に kmodel まで変換されますが、個別にステップを実行して精度を確認・改善することもできます。
スクリプト一覧¶
| スクリプト | 説明 |
|---|---|
step1_parse_model.py |
ONNX モデルの入出力解析 |
step2_simplify_model.py |
onnxsim による ONNX 簡略化 |
step3_compile_kmodel.py |
kmodel コンパイル (PTQ量子化) |
step4_simulate_kmodel.py |
kmodel シミュレーション実行 |
step5_compare_results.py |
ONNX Runtime との精度比較 |
Step 1: モデル解析¶
確認できる情報:
- 入力: float32
[1, 3, 224, 224](NCHW) - 出力: 1 テンソル
[1, num_classes]
Step 2: モデル簡略化¶
出力: apps/veg_classify/output/simplified.onnx
Step 3: kmodel コンパイル¶
コンパイル設定¶
| 項目 | 値 |
|---|---|
| preprocess | True |
| input_type | uint8 |
| input_range | [0, 1] |
| mean | [0.485, 0.456, 0.406] (ImageNet) |
| std | [0.229, 0.224, 0.225] (ImageNet) |
| quant_type | uint8 |
| calibrate_method | Kld |
preprocess=True の意味
kmodel 内部で uint8→float32 変換と mean/std 正規化を行います。 実機ではカメラの生データ (uint8) をそのまま入力可能です。
ランダムデータでコンパイル (初回)¶
キャプチャ画像でコンパイル (精度改善)¶
出力: apps/veg_classify/output/dump/veg_classify.kmodel
Step 4: シミュレーション¶
python apps/veg_classify/scripts/step4_simulate_kmodel.py
python apps/veg_classify/scripts/step4_simulate_kmodel.py --image photo.jpg
Step 5: 精度比較¶
精度の目安:
| コサイン類似度 | 評価 |
|---|---|
| 0.999 以上 | excellent — 非常に良好 |
| 0.99 以上 | good — 良好 |
| 0.95 以上 | acceptable — 許容範囲 |
| 0.95 未満 | poor — 要改善 |
キャリブレーション改善サイクル¶
精度を改善するには
- ランダムデータで初回コンパイル (step3) → 実機で動作確認
- 実機アプリで 'c' キーを使い実環境の画像をキャプチャ
- キャプチャ画像で
step3 --calib-dir→ 再コンパイル step5で精度を確認- 必要に応じて 2–4 を繰り返す
Part 3: C++ アプリケーション (K230)¶
ソースファイル¶
| ファイル | 説明 |
|---|---|
main.cc |
メインアプリケーション — VICAP/VO 初期化、推論ループ、キャプチャ機能 |
model.h / model.cc |
Model 抽象基底クラス — kmodel ロードと推論パイプライン |
classifier.h / classifier.cc |
Classifier クラス — AI2D リサイズ前処理、softmax 後処理 |
util.h / util.cc |
ユーティリティ (ScopedTiming 等) |
vo_test_case.h |
VO レイヤーヘルパー型宣言 |
推論パイプライン¶
センサー (OV5647)
│
├─ CHN0 (1920x1080 YUV420) ──→ VO レイヤー ──→ HDMI ディスプレイ
│
└─ CHN1 (1280x720 RGB888P) ──→ AI 推論
│
┌─────┴─────┐
│ AI2D 前処理 │
│ (224x224 │
│ ストレッチ) │
└─────┬─────┘
│
┌─────┴─────┐
│ KPU 推論 │
│ (ResNet-18) │
└─────┬─────┘
│
┌─────┴─────┐
│ 後処理 │
│ (softmax │
│ + argmax) │
└─────┬─────┘
│
┌─────────┼────────┐
│ │
コンソール出力 キャプチャ
(分類結果) ('c' キー)
│ │
↓ ↓
Class: bocai PNG 保存 (OpenCV)
(95.3%)
ビルド手順¶
1. 設定¶
cmake -B build/veg_classify -S apps/veg_classify \
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/cmake/toolchain-k230-rtsmart.cmake"
2. ビルド¶
3. 確認¶
期待される出力:
veg_classify: ELF 64-bit LSB executable, UCB RISC-V, RVC, double-float ABI, version 1 (SYSV), statically linked, ...
コマンドライン引数¶
| 引数 | 説明 |
|---|---|
<kmodel> |
分類用 kmodel ファイルのパス |
<labels.txt> |
カテゴリラベルファイル (1行1ラベル) |
[capture_dir] |
キャプチャ画像の保存先ディレクトリ(省略可) |
キー操作¶
| キー | 動作 |
|---|---|
| c + Enter | 現在のフレームを PNG 保存(capture_dir 指定時のみ) |
| q + Enter | アプリ終了 |
K230 への転送・実行¶
CMake の deploy / run ターゲットで転送・実行をワンコマンドで行えます(詳細は CMake ターゲット を参照):
cmake --build build --target deploy # ビルド + 学習 + SCP 転送
cmake --build build --target run # シリアル経由で実行 (Ctrl+C で終了)
手動で転送・実行する場合¶
SCP + minicom による手動操作
SCP で転送¶
scp build/veg_classify root@<K230_IP>:/sharefs/veg_classify/
scp build/output/best.kmodel root@<K230_IP>:/sharefs/veg_classify/veg_classify.kmodel
scp build/output/labels.txt root@<K230_IP>:/sharefs/veg_classify/
K230 bigcore (msh) で実行¶
msh /> /sharefs/veg_classify/veg_classify /sharefs/veg_classify/veg_classify.kmodel /sharefs/veg_classify/labels.txt
キャプチャモードで実行¶
msh /> mkdir /sharefs/calib
msh /> /sharefs/veg_classify/veg_classify /sharefs/veg_classify/veg_classify.kmodel /sharefs/veg_classify/labels.txt /sharefs/calib
シリアル接続¶
- Bigcore (RT-Smart msh):
/dev/ttyACM1、115200 bps
キャリブレーション用キャプチャ
capture_dir を指定して実行し、実環境で 'c' + Enter を数回押して画像をキャプチャします。
キャプチャした画像を PC に転送して step3 --calib-dir のキャリブレーションデータとして使用します。
CMake ターゲット¶
設定¶
cmake -B build -S apps/veg_classify \
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/cmake/toolchain-k230-rtsmart.cmake"
ターゲット一覧¶
| ターゲット | コマンド | 説明 |
|---|---|---|
| (デフォルト) | cmake --build build |
C++ バイナリのビルド |
train |
cmake --build build --target train |
モデル学習 (データ未変更時はスキップ) |
deploy |
cmake --build build --target deploy |
ビルド + 学習 + K230 への SCP 転送 |
run |
cmake --build build --target run |
シリアル経由で K230 実行 (Ctrl+C で終了) |
train¶
venv 作成・依存パッケージインストール・データ変更検知・学習を自動で行います:
動作:
.venvが無ければ作成し、requirements.txtの依存パッケージをインストール- データセットのファイル構成をハッシュ化し、前回と比較
- 変更がなければ学習をスキップ(
Dataset unchanged. Skipping training.) - 変更があれば
train.pyを実行
外部データセットを使う場合は DATA_DIR オプションで指定できます:
cmake -B build -S apps/veg_classify \
-DCMAKE_TOOLCHAIN_FILE="$(pwd)/cmake/toolchain-k230-rtsmart.cmake" \
-DDATA_DIR=/path/to/custom/dataset
変更検知の仕組み
check_data_hash.sh がデータディレクトリ内の全ファイルのパスとサイズから MD5 ハッシュを計算します。ファイル内容は読まないため、大量データでも高速に動作します。ファイルの追加・削除・サイズ変更を検知できます。
deploy¶
バイナリのビルド、モデル学習、K230 への SCP 転送を一括実行します:
転送されるファイル:
| ローカル | K230 上のパス |
|---|---|
build/veg_classify |
/sharefs/veg_classify/veg_classify |
build/output/best.kmodel |
/sharefs/veg_classify/veg_classify.kmodel |
build/output/labels.txt |
/sharefs/veg_classify/labels.txt |
K230 の IP アドレスは littlecore シリアル (/dev/ttyACM0) 経由で自動検出されます。手動指定も可能です:
自動検出が失敗する場合
littlecore のシリアルポートを picocom 等で開いていると自動検出できません。picocom を閉じるか、K230_IP を手動設定してください。
run¶
シリアルポート経由で K230 bigcore (msh) にコマンドを送信し、出力をリアルタイム表示します:
- キーボード入力はそのまま K230 に転送されます(
q+ Enter でアプリ終了) - Ctrl+C でシリアル接続を切断
run終了後も picocom/minicom で正常に接続可能です
ポート占有エラー
minicom/picocom が /dev/ttyACM1 を使用中の場合はエラーになります。先に閉じてから実行してください。
K230 接続設定¶
CMake キャッシュ変数で接続先をカスタマイズできます:
| 変数 | デフォルト | 説明 |
|---|---|---|
K230_IP |
(空 = 自動検出) | littlecore の IP アドレス |
K230_USER |
root |
SSH ユーザー |
K230_DEPLOY_DIR |
/sharefs/veg_classify |
転送先ディレクトリ |
K230_SERIAL |
/dev/ttyACM1 |
bigcore シリアルポート (run 用) |
K230_SERIAL_LC |
/dev/ttyACM0 |
littlecore シリアル (IP 自動検出用) |
K230_BAUD |
115200 |
ボーレート |