0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RON-V #22補足 — Kaggleの学習コマンドと再学習の備忘録:道路・冠水・煙・視界

0
Last updated at Posted at 2026-10-12

RON-V 連載リンク
← 前の記事:#22 — 道路・災害画像AIの構築
→ 次の記事:#23 — 4モデル画像AIと根拠画像の保存・転送

この補足の使い方

#22本文で考え方を説明し、この補足で「Kaggleで何を実行したか」「次に学習するとき何が必要か」を残します。自分の作業をやり直すための備忘録です。

Kaggle Notebookの操作を先に確認する

Kaggle Notebookを初めて使う場合や、操作を思い出したい場合は、次のQiita記事を順番に参照します。コードを実行する → 結果を保存する → 次回のInputとして読み込む流れを確認するための参考先です。

順番・確認したいこと 参考記事 先に読む部分
1. Codeセルへ貼り付けて、順番に実行する Kaggleに登録したら次にやること ~ これだけやれば十分闘える!Titanicの先へ行く入門 10 Kernel ~(@upura) 「環境構築不要な『Code(Notebook, Kernel)』の使い方」。Code/Markdownの違い、セルの追加・実行、操作動画を確認する
2. Input・working・Outputと、保存したモデルの関係を知る Kaggle モデル保存と読み出し(@greco_guitar) 「保存ディレクトリ」「保存方法」。作業場所、Outputの確認、保存した出力を読み出す流れを確認する
3. 保存した結果を、次のNotebookへ渡す 【Kaggle】学習と推論を分離!ノートブック間のデータ連携でワークフローを綺麗に保つ方法(@ktdatascience) 「Step 1〜3」。学習結果の保存 → 別Notebookへの追加 → ファイルの読み込みを確認する

上の記事は別の学習課題を例にしています。RON-Vで実行するコード、画像の配置先、ZIPの復元先は、この補足の手順に合わせます。参考記事の画面や Add data / Add Input などの表示名は、現在のKaggleと違う場合があります。

この補足では、Inputの元画像 → workingで学習データとモデルを作成 → OutputのZIPを保存 → 次回のInputへ追加して復元という流れを、道路モデルから追っていきます。

長いコードの仕組みを全部理解する必要はありません。まずは元画像をどこに置き、どのセルで何が作られ、そのファイルを次のどの処理が使うかを押さえます。前半では、道路モデルを例に、Kaggle内のフォルダーが増えていく順に実行手順を追います。煙・視界・冠水のコマンド、ONNX変換、数値の記録は後半の「詳しい学習記録」にまとめました。

記事公開時点でRON-VのGitHubリポジトリは非公開です。 以下の取得セルを実行できるのは、リポジトリを読む権限があるアカウントです。一般の読者が記事だけで同じ処理を実行できる状態にはなっていません。

この記事でできることと、先に必要なもの

「もう一度学習できること」と「採用したモデルを完全に再現できること」は別です。 この補足には、保存済みの処理、成功ログから書き直したセル、今回追加した確認・保存セルが混在します。追記したセルのKaggleでの一連の実行確認は、まだ済んでいません。

やりたいこと 先に必要なもの この記事で進める範囲
道路・煙を元データから学習する RON-Vを読む権限、Roboflowの対象データを取得できるキー、GPU 取得 → 整理 → 学習 → C270確認 → 保存。道路は前半、煙は詳しい記録4
視界を元データから学習する 上と同じもの、データ作成前に追加したC270画像 整理 → 分類学習 → test・C270評価 → 保存。詳しい記録5
冠水v04/v05を学習し直す 保存済みの完成データ。v05には追加学習元のv04の best.pt も必要 復元後の学習。元画像からの選別・加工セルは未回収
保存したモデルを再評価する best.pt と評価画像。この記事の道路・煙の入口では学習データも参照する 学習をやり直さず、C270確認やONNX変換へ進む
ONNXを作る 変換するモデルの best.pt 道路1つから変換できる。詳しい記録7で対象を選ぶ
Raspberry Piで運用する ONNX、Pi側のソフト・設定・カメラ 配置と動作確認は#23の手順へ進む

初めてなら前半の道路手順1〜9を順番に実行します。保存データから再開するなら、環境とコードを用意する手順1〜4の後、「9-3. 保存ZIPから次回の作業へ戻る」を読みます。冠水は完成データが見つかるまで、学習セルへ進みません。

道路・煙・視界も、取得元が利用できなくなった場合は元データからの作り直しが止まります。完成データを保存しておくと、その取得に頼らず再学習できます。同じデータ・条件をそろえても、GPUやソフトの違いにより、学習結果やファイルの照合値が完全に一致する保証はありません。

まず道路モデルを1つ学習する

ここにあるコードは、Kaggle NotebookのCodeセル(Python)へ、上から順に貼って実行します。Raspberry PiやWindowsのPowerShellで実行するコードではありません。

最初に、画像と出力を置く場所を分ける

場所 何を置くか どう用意するか
/kaggle/input/ Kaggle Datasetとして保存した、読み込み用の画像 自分でDatasetをNotebookのInputへ追加する。この場所へ処理結果を書き込まない
/kaggle/working/ GitHubから取得したコード、取得・整理した学習画像、学習結果、ZIP 以下のセルやスクリプトが作成する。作業環境がリセットされる前に必要なものを退避する

道路モデルでは、画像が次の順に使われます。

順番 入力・処理 その時点で用意されるもの
1 自分でC270評価画像のDatasetをInputへ追加 /kaggle/input/datasets/czh00361/ron-v-c270-evaluation-images に元の評価画像
2 GitHubから処理を取得 /kaggle/working/RON-V/vision/ にスクリプトと設定ファイル
3 Roboflowから学習用の元データを取得 /kaggle/working/ronv_v02_sources/ に元画像と元ラベル
4 元データをRON-V用に整理 /kaggle/working/ronv_road3_v02/ に学習用の画像・ラベル・data.yaml
5 整理した画像で学習 /kaggle/working/runs/ronv_road3_v02/ にモデルと学習成績
6 学習したモデルでInputのC270画像を読む /kaggle/working/runs/ronv_c270_v02/ に枠を描いた画像と検出一覧
7 できたものをZIPへまとめる /kaggle/working/ にダウンロードして保管するZIP

元画像、学習用に整理した画像、検出の枠を描いた出力画像は、それぞれ別のフォルダーに置きます。 C270画像は後の手順7でモデルの動作を見るために使い、道路モデルの学習用フォルダーへコピーしません。

1. Kaggleを準備する

  • NotebookのGPUを有効にする。
  • GitHub・Roboflowから取得するときはInternetを有効にする。初期モデル yolo11n.pt(視界は yolo11n-cls.pt)が未取得の場合、その初回取得にもInternetが必要。
  • Secretsに GITHUB_TOKEN と ROBOFLOW_API_KEY を登録し、このNotebookで使用できるようにする。GitHubのトークンにはRON-Vを読む権限が必要。
  • C270で撮影して保存した評価画像をKaggle Datasetとして用意し、そのDatasetをNotebookのInputへ追加する。すでに保存したDatasetがある場合は、それを追加する。

02-upload-new-dataset.png

自分の記録では、追加したDatasetの元画像を次の場所から読みました。これは自分で作業用フォルダーを作ってコピーする場所ではなく、Inputに追加したDatasetが表示される場所です。

/kaggle/input/datasets/czh00361/ron-v-c270-evaluation-images

新しいDatasetを作る場合は、Input欄の Upload を開いて New Dataset を選びます。保存済みのDatasetをこのNotebookへ追加する場合は、Add Input を使います。

実際のInputパスが違う場合は、手順3でコードを取得した後、Kaggleに置いた次の参照先を合わせます。変更したファイルもNotebookと一緒に保存します。

対象 合わせる場所
道路・煙のC270確認 vision/kaggle_pipeline.py の使用するprofileの eval_source
視界のC270確認 vision/visibility_pipeline_v02.py の EVAL_SOURCE
視界データ作成時の評価画像の除外 vision/datasets_visibility_v02.yaml の evaluation_source

視界はデータ作成前にC270画像のInputを追加します。 この参照先が見つからないと、作成処理は警告を出して除外対象を空として進みます。そのまま「評価画像を除外できた」とは扱いません。除外は、追加する道路画像とC270画像の同じファイル内容を照合する仕組みで、見た目が似た画像まで自動で除外するものではありません。

2. 必要なソフトを入れる

%pip install -q ultralytics==8.4.168 roboflow PyYAML pandas onnx==1.22.0 onnxruntime

これは再実行用にまとめたセルです。当時のPython・PyTorch・GPUの情報は、後半の詳しい記録に残しています。以降も同じNotebookで実行します。作業環境を作り直した場合は、インストールと必要なセルを実行し直します。

3. 学習に使う処理を取得する

以下の長いコードは、GitHubに保存した処理をKaggleへ持ってくるためのものです。同じ取得先・同じ版を使うなら編集せず、全体を1つのCodeセルへ貼って実行します。終わると Source: 11c695... と表示されます。

取得用コードを開く(新しい作業環境で1回実行)
from pathlib import Path
import io
import json
import urllib.request
import zipfile
from kaggle_secrets import UserSecretsClient

SOURCE_REF = '11c69547eeda9b48acd3f8012ec6d03d2cc59fee'
REPO = Path('/kaggle/working/RON-V')
assert not REPO.exists(), '既存のRON-Vを確認し、取得セルの再実行を判断する'
token = UserSecretsClient().get_secret('GITHUB_TOKEN')
request = urllib.request.Request(
    f'https://api.github.com/repos/czh00361/RON-V/zipball/{SOURCE_REF}',
    headers={'Authorization': f'Bearer {token}', 'Accept': 'application/vnd.github+json'},
)
with urllib.request.urlopen(request) as response:
    archive = response.read()
with zipfile.ZipFile(io.BytesIO(archive)) as z:
    root = z.namelist()[0].split('/')[0]
    for entry in z.infolist():
        relative = Path(entry.filename).relative_to(root)
        assert not relative.is_absolute() and '..' not in relative.parts
        target = REPO / relative
        if entry.is_dir():
            target.mkdir(parents=True, exist_ok=True)
        else:
            target.parent.mkdir(parents=True, exist_ok=True)
            target.write_bytes(z.read(entry))
del token, request, archive
(REPO / 'kaggle-source-ref.txt').write_text(SOURCE_REF + '\n')
print('Source:', SOURCE_REF)

すでに /kaggle/working/RON-V がある場合は、上書きを防ぐため停止します。取得できた後は、環境確認と学習データ作成へ進みます。この番号は今回手順を確認したコードの版で、過去の全学習をこの版で行ったと確認した意味ではありません。

取得セルが作成する主なファイルは次です。この段階では学習画像や best.pt はまだ作られません。

取得後のファイル 後で何に使うか
/kaggle/working/RON-V/vision/kaggle_pipeline.py 手順4以降で呼び出す処理の入口
/kaggle/working/RON-V/vision/prepare_v02_dataset.py Roboflowから取得し、学習用の画像とラベルを作る処理
/kaggle/working/RON-V/vision/datasets_v02.yaml 取得する元データ、クラス名、保存先を記した設定。GitHubから取得済みなので、自分で新規作成しない
/kaggle/working/RON-V/kaggle-source-ref.txt 取得したコードの版の記録

4. GPUを確認する(画像はまだ作らない)

!python /kaggle/working/RON-V/vision/kaggle_pipeline.py road-v02 --check

出力に CUDA : True とGPU名が表示され、エラーがなければ次へ進みます。初回は学習データを作る前なので、Dataset OK : False で構いません。このセルは学習画像やモデルを作成しません。

以降は、フォルダーの変化を確認できるよう、保存済みの一括コマンドを段階ごとに分けた再実行手順です。各セルが終わってから、作成されたものを見て次へ進みます。

5. 元画像を取得し、学習用の画像・ラベルを作る

次のセルは学習データの作成だけを行います。モデルの学習はまだ始めません。

!python /kaggle/working/RON-V/vision/kaggle_pipeline.py road-v02 --prepare

このセルが prepare_v02_dataset.py を呼び出し、次の2つの作業を行います。フォルダーを手作業で作ったり、画像を1枚ずつ移したりする必要はありません。

5-1. Roboflowの元データを保存する

datasets_v02.yaml に記録した2つのデータを取得します。

元データ 保存するフォルダー
道路の障害物、Version 3 /kaggle/working/ronv_v02_sources/road_obstacle_v3/
倒木、Version 1 /kaggle/working/ronv_v02_sources/fallen_trees_detection_v1/

それぞれのフォルダーには、取得した元画像、正解の種類・位置を記したラベル、元データの data.yaml が入ります。これが、後の整理処理の入力になります。

5-2. 元データからRON-V用の学習データを作る

続いて、元画像を /kaggle/working/ronv_road3_v02/ へコピーし、対応するラベルを書き出します。元画像は取得元フォルダーにも残ります。

このとき、倒木・路上の落下物・穴の3種類へラベルをそろえ、同一画像の重複を除きます。元データのtrain / valid / testの分割を引き継ぎ、対象のラベルが残る画像を保存します。

以下の表では、すべて /kaggle/working/ronv_road3_v02/ からの相対パスで示します。

作成される場所・ファイル 中身・役割
train/images/ 学習で見せる画像(.jpg、.pngなど)
train/labels/ train画像に対応する正解のラベル(.txt)
valid/images/ と valid/labels/ 学習途中で成績を確認する画像と正解
test/images/ と test/labels/ 学習に使わず、後で性能を確かめるための画像と正解。この道路モデルの手順7では別のC270画像を使う
data.yaml 学習プログラムへ、画像の場所と3種類のクラス名を教える設定
manifest.csv どの元画像をどの出力画像へコピーしたか、クラス数などの記録
build_summary.json 元データごとの処理内容と、保存した画像枚数の集計

画像とラベルは、拡張子以外が同じ名前になります。例えば、次のように対応します。xxxxxxxxxxxxxxxx は名前の形式を示す例で、実際には画像から計算した値が入ります。

train/images/road_obstacle_v3__xxxxxxxxxxxxxxxx.jpg
train/labels/road_obstacle_v3__xxxxxxxxxxxxxxxx.txt

data.yaml もスクリプトが自動で作ります。内容は次の形です。これを見れば、次の学習セルがどの画像を読むか確認できます。

path: /kaggle/working/ronv_road3_v02
train: train/images
val: valid/images
test: test/images
nc: 3
names:
  0: fallen_tree
  1: road_debris
  2: pothole

この段階の確認は、画像とラベルが入り、data.yaml ができたかです。保存済みの構築記録はtrain 1,143枚 / valid 213枚 / test 144枚でした。実行した回の枚数は、ログの images by split と build_summary.json に残ります。

すでに完成した data.yaml がある場合、この入口の処理はデータ作成を省略します。作り直す場合のコマンドは、後半の「3. road-v02 → 再学習するとき」に残しています。

6. 作成した画像とラベルを使って学習する

手順5で作った data.yaml を入力に、初期モデル yolo11n.pt から学習します。以前の道路モデルへ追加学習する設定ではありません。

!python /kaggle/working/RON-V/vision/kaggle_pipeline.py road-v02 --train --epochs 50 --imgsz 640 --batch 16

このセルが読むのは ronv_road3_v02 の画像とラベルです。学習中にvalid画像で成績を確かめ、結果を /kaggle/working/runs/ronv_road3_v02/ へ保存します。学習画像をこのrunsフォルダーへ移す処理ではありません。

この段階で作られるファイル 作られる理由
weights/best.pt 学習中の成績で選んだモデルを保存する
weights/last.pt 最後の学習時点のモデルを保存する
results.csv 学習の各回の成績を記録する
args.yaml その回の学習条件を記録する
グラフや学習画像のプレビュー 成績の推移と、画像・ラベルの状態を見返すため

--epochs 50 は繰り返し回数の上限、--imgsz 640 は処理する画像サイズ、--batch 16 はまとめて処理する枚数です。この段階は時間がかかります。

学習セルが終わったら weights/best.pt と results.csv を確認します。前回の best.pt がすでにある場合は学習を省略するため、ログの [train] も確認します。

道路・煙・視界の保存済みスクリプトはGPU番号0を使います。冠水の復元セルは当時の記録に合わせて番号1を使います。GPU番号と空きメモリは、詳しい記録1の確認セルで調べます。メモリ不足で CUDA out of memory になった場合は、他の処理を止めるかbatchを小さくして、変更後の条件を記録します。

7. 学習したモデルで、InputのC270画像を確認する

ここで、手順1に追加したC270画像を初めてモデルへ渡します。入力は手順6の best.pt と、Inputに置いた元のC270画像です。

!python /kaggle/working/RON-V/vision/kaggle_pipeline.py road-v02 --eval

このセルは追加の学習をせず、C270画像に倒木などがあるかを検出します。結果は /kaggle/working/runs/ronv_c270_v02/ に保存します。

この段階で作られるもの 中身
検出の枠・クラス名を描いた画像 元のC270画像へ、モデルが見つけた対象を描いた出力画像
evaluation_summary.csv 元画像の名前ごとの検出件数とconfidence

Inputの元画像はそのまま残り、枠を描いた画像は別の出力になります。出力画像と元画像を見比べて、誤検出・見逃しを確認します。このCSVは検出の一覧で、正解率を自動計算したものではありません。

Inputパスが見つからないと処理は評価を省略します。出力フォルダーやCSVがない場合は、ログの [eval] とInputパスを確認します。CSVの保存だけが失敗して警告になる場合もあるため、セルが終わったことだけで完了と判断しません。

7-1. testデータで性能を測る場合(再実行用の追記)

学習中のvalid評価、学習後のtest評価、自分のC270画像での確認は、役割が違います。

確認 使う画像 分かること
学習中のvalid valid/images と正解ラベル モデルを選ぶための成績。results.csv に記録される
学習後のtest test/images と正解ラベル 学習・モデル選択に使っていない画像での検出性能
手順7のC270 Inputの自分の撮影画像 実際の撮影画像で何を検出するか。CSVだけでは正解率は分からない

道路・煙の --eval はC270への予測で、test評価ではありません。 testの性能も記録する場合は、次の追加セルを使います。これは過去の道路Notebookから回収したセルではありません。

from pathlib import Path
import json
from ultralytics import YOLO

best = Path('/kaggle/working/runs/ronv_road3_v02/weights/best.pt')
data = Path('/kaggle/working/ronv_road3_v02/data.yaml')
assert best.is_file() and data.is_file(), 'モデルと完成データを確認する'
metrics = YOLO(str(best)).val(
    data=str(data), split='test', imgsz=640, batch=16, device=0,
    project='/kaggle/working/runs', name='ronv_road3_v02_test',
    plots=True,
)
out = Path(metrics.save_dir)
(out / 'test_metrics.json').write_text(
    json.dumps(metrics.results_dict, indent=2, ensure_ascii=False),
    encoding='utf-8',
)
print('test結果:', out)

表示された保存先とJSON・グラフを保管します。testの結果を見ながら何度もモデルを選び直すと、そのtestが選定用になります。次の改善版も公平に比べるなら、選定に使わない評価画像を別に残します。

8. モデルと学習記録をZIPへまとめる

!python /kaggle/working/RON-V/vision/kaggle_pipeline.py road-v02 --export

このセルは、手順6の学習結果と手順5のデータ構築記録をコピーして、次のフォルダーとZIPを作ります。YYYYMMDD_HHMMSS は実行日時が入る部分です。

/kaggle/working/RONV_road-v02_YYYYMMDD_HHMMSS/
/kaggle/working/RONV_road-v02_YYYYMMDD_HHMMSS.zip
ZIPに入るもの コピー元
run/ 以下のモデル・成績・グラフ /kaggle/working/runs/ronv_road3_v02/
data.yaml、manifest.csv、build_summary.json /kaggle/working/ronv_road3_v02/
artifact_manifest.json この保存処理が作る、モデルの照合値と保存情報

このZIPには学習画像・ラベルそのものと、C270の出力画像は入りません。 また、この --export はZIP作成であり、ONNX変換ではありません。再学習用の画像とC270結果は、次の手順で別に保存します。

9. 学習画像・C270結果も保存して、次回に残す

次のセルは、保管するものを分けるために今回追加した例です。学習結果へコードの版とソフトのバージョン一覧を追記してから、3つのZIPを作ります。

import shutil
import subprocess
import sys
from pathlib import Path

run = Path('/kaggle/working/runs/ronv_road3_v02')
dataset = Path('/kaggle/working/ronv_road3_v02')
evaluation = Path('/kaggle/working/runs/ronv_c270_v02')
source = Path('/kaggle/working/RON-V/kaggle-source-ref.txt')
assert (run / 'weights/best.pt').is_file(), '学習結果を確認する'
assert (dataset / 'data.yaml').is_file(), '完成データを確認する'
assert (evaluation / 'evaluation_summary.csv').is_file(), 'C270確認を先に済ませる'
assert source.is_file(), '取得したコードの版の記録を確認する'
shutil.copy2(source, run / 'source-ref.txt')
with (run / 'pip-freeze.txt').open('w') as out:
    subprocess.run([sys.executable, '-m', 'pip', 'freeze'], stdout=out, check=True)

shutil.make_archive(
    '/kaggle/working/RONV_road-v02_training_run',
    'zip', '/kaggle/working/runs', 'ronv_road3_v02',
)
shutil.make_archive(
    '/kaggle/working/RONV_road-v02_training_dataset',
    'zip', '/kaggle/working', 'ronv_road3_v02',
)
shutil.make_archive(
    '/kaggle/working/RONV_road-v02_c270_evaluation',
    'zip', '/kaggle/working/runs', 'ronv_c270_v02',
)
作成するZIP(すべて /kaggle/working/) 次回のために保管する中身
RONV_road-v02_training_run.zip モデル、学習条件・成績、コードの版、ソフトのバージョン
RONV_road-v02_training_dataset.zip train / valid / test の画像とラベル、data.yaml、元画像との対応記録
RONV_road-v02_c270_evaluation.zip 枠を描いたC270画像と検出一覧

手順7の結果がまだない場合は、ZIPを作る前の確認で停止します。先にInputパスを直して評価セルを実行します。手順7-1のtest結果はこの3つのZIPに入らないため、表示されたtest結果フォルダーも別に保存します。

9-1. Outputの「…」からZIPをPCへダウンロードする

01-input-output.png

図:当時保存した画面です。上のInputにはC270評価画像、下のOutputには /kaggle/working に作成したファイルが表示されています。画像中の道路モデルはv01の記録で、この手順ではv02のフォルダー名を使います。右下のファイル行に「…」メニューも写っています。

  1. Notebook右側の Output を開き、/kaggle/working を展開する。
  2. 手順9で作成した RONV_road-v02_training_dataset.zip などのZIPを探す。新しく作ったファイルが見えない場合は、フォルダー横の更新ボタンで表示を更新する。
  3. 対象のファイル行にマウスを合わせ、右端の … を開いて Download を選ぶ。
  4. PCへ保存されたことを確認し、ZIPを開いて画像・ラベル・モデルなど、目的の中身が入っているか確かめる。

ダウンロードするときは、保存したいZIPの行で「…」を開きます。フォルダー全体をまとめて残すために、手順9でZIPを作ってから保存します。

Inputにある既存の画像やモデルをPCへ保存するときも、フォルダーを展開して対象のファイルの … を使います。学習データのフォルダー全体を残す場合は、手順9で作ったZIPを保存すると、画像とラベルを一緒に保管できます。

9-2. 次回のInputとして使うなら、ZIPをDatasetへ保管する

PCへダウンロードしたZIPを、Kaggleの Upload → New Dataset から保管する方法も使いました。当時は、冠水・煙・視界のバックアップZIPをPrivate Datasetへ登録しました。

03-upload-backup-zips.png

図:Dataset Title を入力し、Visibility を Private にして、保管するZIPを指定します。画像はタイトル未入力の途中の状態で、この後タイトルを入力して Create で作成します。画像に写っているZIP名は当時の保存名で、今回の手順9のZIP名とは異なります。

次回は、そのDatasetを Add Input でNotebookへ追加して、保存したデータやモデルを読み出します。Inputのままでは作業用の保存先と違うため、再学習で使うときは以下の復元先へコピー・展開します。

生成したZIPと実行したNotebook(.ipynb)をPCなどへ保存します。C270の元画像は、Inputへ追加したKaggle Dataset側で引き続き保管します。学習用の元データを取得し直せない場合に備えるなら、ronv_v02_sources も保管します。

次回、完成した学習データを復元するときは、画像だけでなくラベルと data.yaml も戻し、元の /kaggle/working/ronv_road3_v02/ に配置します。学習済みモデルを復元するときは、/kaggle/working/runs/ronv_road3_v02/weights/best.pt に戻します。

同じ条件で学習を最初からやり直す場合は、後半の「3. road-v02 → 再学習するとき」を参照します。--force-train は前の学習結果を削除するので、先に保存を済ませます。学習データを含むZIPを公開する場合は、元データの再配布条件を確認します。

9-3. 保存ZIPから次回の作業へ戻る(再実行用の追記)

次回は、ZIPを保管したDatasetを Add Input で追加します。KaggleがZIPを展開して表示する場合もあるため、Inputを開き、ZIPのままか、フォルダーになっているかを最初に確認します。

戻すもの 元の手順9のZIP 復元先
完成データ RONV_road-v02_training_dataset.zip /kaggle/working/ronv_road3_v02/
学習結果 RONV_road-v02_training_run.zip /kaggle/working/runs/ronv_road3_v02/
C270確認結果 RONV_road-v02_c270_evaluation.zip 見返すための記録。新しいモデルの確認では手順7を実行し直す

完成データと学習結果のZIPは、どちらも中の先頭フォルダーが ronv_road3_v02 ですが、展開先が違います。画像・ラベルは working 直下、モデル・成績は working/runs の下です。手順8の日時付きZIPは画像・ラベルを含まないため、完成データZIPの代わりにはなりません。

次のセルは、この記事の手順9で作ったZIPが、InputにZIPのまま残っている場合だけの復元例です。Input内から保存名で探し、既存の復元先があれば停止します。

from pathlib import Path, PurePosixPath
import zipfile

# 学習を最初からやり直す場合はFalse。
# 前のモデルをそのまま再評価・変換する場合はTrue。
RESTORE_RUN = False
work = Path('/kaggle/working')
folder = 'ronv_road3_v02'
jobs = [('RONV_road-v02_training_dataset.zip', work)]
if RESTORE_RUN:
    jobs.append(('RONV_road-v02_training_run.zip', work / 'runs'))

archives = []
for filename, parent in jobs:
    matches = list(Path('/kaggle/input').rglob(filename))
    assert len(matches) == 1, f'{filename}: Inputで1つ見つかるか確認する {matches}'
    assert not (parent / folder).exists(), f'復元先が既にある: {parent / folder}'
    with zipfile.ZipFile(matches[0]) as z:
        assert z.infolist(), '空のZIP'
        for entry in z.infolist():
            p = PurePosixPath(entry.filename)
            assert not p.is_absolute() and '..' not in p.parts
            assert p.parts and p.parts[0] == folder, 'この記事と違うZIP構成'
    archives.append((matches[0], parent))

for archive, parent in archives:
    parent.mkdir(parents=True, exist_ok=True)
    with zipfile.ZipFile(archive) as z:
        z.extractall(parent)
    print('復元:', parent / folder)

Inputで既に展開されている場合は、上のセルではなく、画像・ラベル・data.yaml が入った ronv_road3_v02 フォルダーを表の復元先へコピーします。フォルダーを二重に入れず、復元後の data.yaml が /kaggle/working/ronv_road3_v02/data.yaml にあるか確認します。

Inputが展開済みフォルダーの場合のコピー例

下の saved に、Inputで確認した完成データのフォルダーパスを入れます。指定するのは、その直下に data.yaml / train / valid / test があるフォルダーです。この例は完成データだけを戻します。

from pathlib import Path
import shutil

saved = Path('/kaggle/input/ここをInputに表示された完成データのフォルダーへ変更')
destination = Path('/kaggle/working/ronv_road3_v02')
assert (saved / 'data.yaml').is_file(), 'コピー元の階層を確認する'
assert not destination.exists(), '既存の完成データがあるので停止する'
shutil.copytree(saved, destination)
print('コピー:', destination)

学習結果も戻す場合は、weights/best.pt が入ったrunフォルダーを、/kaggle/working/runs/ronv_road3_v02 へ同様にコピーします。データのフォルダーとrunのフォルダーを取り違えないようにします。

復元後は、data.yaml の path と train / val / test の参照先を確認し、各画像フォルダーと同名のラベルが実際にあることを確認します。data.yaml だけがある状態でも、入口の処理は「既存データ」として作成を省略します。

  • データを使って学習し直す:完成データを戻し、手順6へ進む。古い best.pt を戻すと通常の学習は省略される。
  • 前のモデルをそのまま使う:完成データと学習結果を戻し、手順7の再評価や詳しい記録7のONNX変換へ進む。
  • 途中の学習を続ける:last.pt などからの再開が別途必要。この記事の通常コマンドや --force-train は、その途中再開ではない。

冠水の過去ZIPは中の構成が未確認なので、この道路用セルへ名前だけ置き換えません。詳しい記録6の探し方で中身を確認し、データは ronv_flood_v05、v04のモデルは runs/ronv_flood_v04/weights/best.pt へ戻します。

次へ進む前の確認

起きたこと 次に確認するもの
GitHub取得が認証エラー Secretsの登録・Notebookへの使用許可、トークンのRON-Vへの読取権限
Roboflow取得が失敗 キーの使用許可、対象workspace / project / versionへのアクセス
作成処理を省略したが画像がない data.yaml だけ復元していないか。視界は .ready だけで完了と判断しない
学習したつもりだが成績が変わらない 既存 best.pt により学習を省略したログがないか
C270確認の出力がない Inputの画像パスと [eval] の省略・CSV保存の警告
冠水のデータが見つからない 元NotebookのOutput・Inputとバックアップ。v02の取得だけでv04/v05へは進めない

煙・視界・冠水では、どのフォルダーを使うか

以下はすべて /kaggle/working/ からの相対パスです。モデルごとに画像を整理する場所と学習結果の場所を分けます。

モデル 取得した元データ 学習用に整理した画像の場所 学習したモデル
道路 road-v02 ronv_v02_sources/ ronv_road3_v02/ runs/ronv_road3_v02/weights/best.pt
煙 smoke-v01 ronv_smoke_sources/rf100_smoke_v1/ ronv_smoke_v01/ runs/ronv_smoke_v01/weights/best.pt
視界 visibility-v02 ronv_visibility_sources/ と ronv_visibility_v02_sources/ 基本データの ronv_visibility_v01/ を作り、道路画像を追加して ronv_visibility_v02/ を作る runs/ronv_visibility_v02/weights/best.pt
冠水 flood-v05 初期取得元は次節を参照。v04/v05の全採用画像との対応・作成セルは未回収 完成データを ronv_flood_v05/ に復元して使う runs/ronv_flood_v05/weights/best.pt。追加学習の前にv04のモデルも必要

煙は道路と同じく train / valid / test の下に images / labels を作り、data.yaml を使います。

視界は画像全体の分類なので、正解を画像の置き場所で表します。例えば ronv_visibility_v02/train/clear/ と ronv_visibility_v02/train/low_visibility/ です。val と test にも同じ2種類のフォルダーを作り、画像を入れます。道路のような画像ごとの位置ラベルは作りません。データ作成が完了すると manifest.csv、build_summary.json、.ready ができます。これらも保存します。

冠水は、完成データとv04のモデルを復元してv05を追加学習する流れです。元画像から完成データを作り直す場合は、選別・分割・重点学習用ファイルの作成記録が必要です。次の節で工程を説明し、後半の詳しい記録に復元先と学習セルを残しています。

学習後、詳しい記録の「7. ONNXへ変換する」のセルを実行すると、best.pt を入力にONNXを作り、ronv-runtime/models/road-v02/road-v02.onnx のようにモデルごとのフォルダーへコピーします。同じ場所に変換条件と照合値を記した export.json も作ります。元の best.pt は学習結果側に残し、再学習用として保管します。

ほかのモデル・ONNX変換はどこを見るか

やりたいこと 詳しい記録の場所
煙を検出するモデルを学習する 4. smoke-v01
視界をclear / low_visibilityに分類するモデルを学習する 5. visibility-v02
冠水モデルを再学習する 6. flood-v05。完成した学習データと、追加学習元のモデルが必要
学習したモデルをPiで使うONNXへ変換する 7. ONNX変換。変換したいモデルを選ぶ。道路1つから実行できる
次回に必要なデータ・モデル・環境の記録を保管する 8. 保存
採用版と同じファイルかを確認する 9. 照合値と回収項目

冠水モデルは、v04で基本を学習し、v05で誤検出を減らした

冠水では、道路が水につかった画像を正例にし、濡れただけの道路や反射など、冠水と間違えやすい画像を非冠水の例として使いました。目的は、道路画像から冠水候補を見つけることです。水深や通行可否を判定するモデルではありません。

v04の前段:元データの取得から学習用フォルダーを作るまで

ここは、元データの取得 → 画像の選別 → 保存 → 分割 → v04用の学習データ作成の順に読みます。初期の取得処理はGitHubの設定・スクリプトで確認でき、v04完成時の枚数とフォルダーは成功ログで確認できます。途中の選別セル全文はまだ確認できていないため、確認できた保存先と、元Notebookで確認する項目を分けて記します。

1. 元データを取得する

保存済みの vision/datasets_flood_v01.yaml と vision/datasets_flood_v02.yaml には、次の取得元が記録されています。Roboflowから画像だけでなく、正解ラベルと設定もまとめて取得する構成です。

用途 workspace / project / version 取得元の記録
初期の冠水画像 wo-q0mut / flood-detection-3susv-wbjhi / 1 datasets_flood_v01.yaml とv02設定で有効。v01設定の記録では982画像
非冠水の候補にする道路画像 laasya-wdzur / road-obstacle-detection-6zwj5 / 3 v02設定の road_obstacle_v3_negative が有効
別の道路水面データの候補 ff-vzbf7 / road-flood-water / 7 設定では enabled: false。通常の取得処理には入らない

取得元と保存先を確認できる初期の入口は、次のコマンドです。新しい作業環境でv02データがない場合、元データの取得とv02用の整理を行います。

!python /kaggle/working/RON-V/vision/kaggle_pipeline.py flood-v02 --prepare

このセルは prepare_flood_v02_dataset.py を呼び、元画像・元ラベルを次の場所へ取得します。

/kaggle/working/ronv_flood_v02_sources/flood_detection_wo_v1/
/kaggle/working/ronv_flood_v02_sources/road_obstacle_v3_negative/

各元データのフォルダーには、Roboflowから取得した画像・ラベル・data.yaml が入ります。さらに、v02用に整理した画像とラベルを /kaggle/working/ronv_flood_v02/ へ作成します。このコマンドだけでv04の45枚・377枚まで選別されるわけではありません。 既存のv02の data.yaml がある場合、入口の処理は取得・整理を省略します。

上の取得元は初期設定で確認できるものです。v04に採用した45枚・377枚の全ファイルが、どの取得元に対応するかは、選別一覧と元Notebookで照合します。

2. 道路冠水の正例と、非冠水の例を選ぶ

取得した元画像をそのまま全部使うのではなく、RON-Vの目的である道路冠水に合うかを確認してから学習用へ残します。

選別するもの 確認する内容 v04開始時に残った元画像
冠水の正例 道路・車両に関係する冠水画像か、正解ラベルが冠水領域を指しているか 45枚
非冠水の例 通常道路、濡れた路面、反射などで、floodの正解を付ける対象がないか 377枚
重複・似た画像 同じ画像や似た場面が重複していないか、後の分割で学習側と評価側へ混ざらないか 選別・分割の記録で確認する

ここで重要なのは、非冠水画像を「ラベルを空にした画像」とだけ考えないことです。画像を見て冠水の対象がないことを確認してから、非冠水として扱います。冠水が写っている画像のラベルを消して非冠水へ変更しません。

45枚・377枚という結果はログで確認できますが、どの画像を採用・除外したか、面積や類似度などの当時の判定条件は未確認です。この表は選別の観点を説明するもので、同じ画像を選び直せる元セルの代わりではありません。

3. 元画像を残し、選別した画像とラベルの対応を保存する

取得元のフォルダーを残したまま、学習に使う画像と対応ラベルを作業用のデータへコピーします。こうすると、後から元画像と整理後の画像を見比べられます。

保存するもの 何を後で確認するためか
取得した元画像・元ラベル・元の data.yaml 元データに戻り、選別とラベル変換を確認するため
選別した45枚・377枚の一覧 どの画像を採用したかを再現するため。元画像のパスと、正例/非冠水の区別が必要
整理後の画像とラベル v04の学習に渡すため。同じ名前の画像と .txt を対応させる

選別直後の一時フォルダー名や一覧ファイル名は、確認できたログには残っていません。確認できている最終保存先は /kaggle/working/ronv_flood_v04/ です。 保存済みNotebookのこのフォルダーと、選別・コピーを行ったセルを確認します。

4. train / valid / testへ分割し、どの画像を入れたか固定する

選別した元画像を、学習用・学習途中の確認用・学習後の確認用へ分けます。v04のログに残っている元画像の分割は次です。

分割 冠水 非冠水 用途
train 31 263 学習に使う
valid 7 56 学習中に成績を確認する
test 7 58 学習に使わず、学習後に性能を確認する

この分割をv05でも維持しました。seedは 20261009 ですが、元画像の一覧・順序・分割方法が分からなければ、seedだけで同じ分割を戻せるとは限りません。再現には、各画像をどの分割へ入れたかの記録が必要です。

C270評価画像9枚は、この分割に入れず、別のInput Datasetとして残します。

5. v04用の画像・ラベル・data.yamlを作る

分割後、学習プログラムが読めるフォルダーへ画像とラベルを配置します。次のパスは /kaggle/working/ronv_flood_v04/ からの相対パスです。

作成する場所・ファイル 中身
train/images/ 冠水の学習用186ファイルと、非冠水263ファイル。合計449ファイル
train/labels/ 同名の正解ラベル。冠水のクラスIDは0、非冠水は空のラベル
valid/images/ と valid/labels/ 元の冠水7枚・非冠水56枚の画像とラベル。合計63枚
test/images/ と test/labels/ 元の冠水7枚・非冠水58枚の画像とラベル。合計65枚
data.yaml 上記フォルダーの場所と、1クラスfloodを指定する設定

trainの冠水元画像31枚を、6倍の186ファイルとして使った記録です。元画像の分割を決めてからtrain側だけを増やし、valid / testの画像をtrainへ混ぜません。 この6倍分を実際にどう複製・加工したかは、元セルで確認する項目です。

v04用の data.yaml が指定する内容は、次の形になります。これは保存先とクラスを説明する設定例で、元セルそのものの転載ではありません。

path: /kaggle/working/ronv_flood_v04
train: train/images
val: valid/images
test: test/images
nc: 1
names:
  0: flood

画像とラベルがそろい、この設定が完成してから、後半の「flood-v04も学習し直す場合」のセルを実行します。学習結果はデータとは別の /kaggle/working/runs/ronv_flood_v04/ に保存されます。

再学習のためには、完成した ronv_flood_v04 の画像・ラベル・data.yaml に加え、元画像の取得条件、選別一覧、分割記録、複製・加工のセルも残します。完成データがあれば学習をやり直せ、これらの記録もあれば元画像からの作り直しを追えるようになります。

v04からv05へ進む流れ

上の準備で学習用の正例45枚・非冠水377枚の分割が決まり、train側を重点的に使う構成ができた後、次の順で学習と追加学習を行いました。

順番 何をしたか 次に使うデータ・ファイル
1 道路冠水の正例45枚と、非冠水377枚を用意した 元画像と、冠水の位置を記した正解ラベル
2 画像をtrain / valid / testに分け、学習用の正例を重点的に使う構成にした /kaggle/working/ronv_flood_v04/ の画像・ラベル・data.yaml
3 yolo11n.pt から基本の冠水モデルを学習した /kaggle/working/runs/ronv_flood_v04/weights/best.pt
4 v04で非冠水画像を検出し、間違えやすい画像を選んだ hard negative候補38枚。そのうちtrain側28枚を重点学習用に使った
5 同じtrain / valid / testの分割を保ち、trainへ重点学習分の98ファイルを追加した /kaggle/working/ronv_flood_v05/ の画像・ラベル・data.yaml
6 v04の best.pt を読み、v05のデータで追加学習した /kaggle/working/runs/ronv_flood_v05/weights/best.pt

hard negativeは「冠水ではないのに、モデルが冠水と間違えやすい画像」です。 それを冠水の正例として追加するのではなく、非冠水として重点的に学習させました。ここがv04からv05への主な変更です。

画像枚数は、元画像と学習用ファイルを分けて読む

用途 元の冠水画像 元の非冠水画像 v04で使うファイル数 v05で使うファイル数
train:学習に使う 31 263 正例186 + 非冠水263 = 449 正例186 + 非冠水263 + 重点学習分98 = 547
valid:学習途中の成績を確認する 7 56 63 63
test:学習後の性能を確認する 7 58 65 65

trainの正例31枚は、学習用に6倍の186ファイルとして使った記録です。独立した新しい冠水写真を186枚集めたという意味ではありません。v05の追加98ファイルも、train側の非冠水28枚を重点的に使うために作ったものです。具体的な複製・加工方法は、元セルの確認が必要です。

valid / testへ入れた画像をtrainへ戻さず、別に用意したC270評価9枚も学習に使っていません。

後からやり直すとき、何が残っていればよいか

残っているもの できること・次に必要なもの
v05の完成データ(画像・ラベル・data.yaml)とv04の best.pt 後半の「6. flood-v05」の学習セルで、記録した条件の追加学習をやり直せる
v04の完成データと yolo11n.pt 基本モデルのv04を学習し直せる。v05へ進むには、v05の完成データか、重点学習用データを作る元セルも必要
元画像はあるが、完成データがない 選別した45枚の一覧、train / valid / testの分割記録、ラベル、追加98ファイルの作成セルを確認して、学習用フォルダーを作り直す
ONNXや実機用runtime ZIPだけ 実機用モデルの保管記録。学習用の画像・ラベルが残っているかは別に確認する

不足しているのは、学習コマンド全体ではなく、元画像から採用版の学習用フォルダーを作るための記録です。 data.yaml は画像の場所とクラスを指定する設定なので、これだけでは元画像や正解ラベルは戻りません。

再学習したいときは、まずKaggleの元Notebookと保存したDataset / ZIPを開き、ronv_flood_v05 の完成データと、runs/ronv_flood_v04/weights/best.pt が残っているか確認します。確認用セルと、保管する中身は後半の「6. flood-v05」にまとめています。

用語はこれだけ押さえる

用語 この作業での意味
学習 画像と正解を使って、見分け方を調整する作業
評価 学習に使わなかった画像で、当たり・見逃し・誤検出を確認する作業
best.pt(weight) 学習の結果を保存したモデルファイル
ONNX 学習済みモデルをPiで動かすために、この構成で使うファイル形式
Dataset 画像と、正解を記したデータのまとまり
SHA / SHA256 コードの版やファイルを照合するための番号。認証用の秘密ではない

詳しい学習記録

以下は、採用モデルの条件・コマンド・照合値を後から確認するための記録です。必要な項目だけ開いて参照します。保存済みスクリプトの呼出し、ログから復元したコード、今回追加したコードを区別して残します。

記録の確かさと再現できる範囲

記録の確かさと再現できる範囲

#22本文 は検出対象とAI構築の考え方を説明する記事です。この補足は、自分がKaggleで何を実行したかを確認し、後から再学習するための備忘録です。

対象は road-v02 / flood-v05 / smoke-v01 / visibility-v02 の採用構成です。失敗した試行の羅列は省きます。

記録の確かさを次のように区別します。

表記 意味
GitHubの保存済みコマンド リポジトリの手順と実装で確認できた呼出し。実際のNotebook入力全文とは限らない
実行ログから復元 成功時のtrainerログにある条件を、再実行できるPythonへ書き直したもの
再実行用の追記 今回追加した環境確認・保存・変換用セル。過去に同じセルを実行したという意味ではない

冠水のデータ作成セル全文は未回収です。 flood-v04/v05 の学習条件はログから復元できますが、最終教師データの生成を新規runtimeで完全に再現するには、元Notebookのセルか保存済みデータセットが必要です。枚数だけを合わせた別データを採用版と同じものとは扱いません。

1. 学習時に使った環境と必要なもの

1. 学習時に使った環境と必要なもの

確認できた実行環境は次です。

対象ログ Python Ultralytics PyTorch GPU
2026-10-03の学習環境・visibility-v02 3.12.13 8.4.168 2.10.0+cu128 Tesla T4
2026-10-09 flood-v04/v05 3.13.15 8.4.168 2.11.0+cu128 Tesla T4、device=1

Kaggle NotebookはGPUを有効にし、データの取得時はInternetも有効にします。Kaggle Secretsには GITHUB_TOKEN と ROBOFLOW_API_KEY を登録し、Notebookから使用できるようにします。キーをコードや記事に直書きしません。

実画像評価用のKaggle Datasetは次のパスで使用しました。

/kaggle/input/datasets/czh00361/ron-v-c270-evaluation-images

このパスの czh00361 は、当時使用したKaggle Datasetの所有者名です。別のDatasetを使う場合は、KaggleのInput欄に表示される実際のパスへ、設定・スクリプト中の参照先も合わせます。記事の文字だけを変更しても処理側の参照先は変わりません。評価用画像は学習へ入れません。

packageを用意する(再実行用の追記)

以下はNotebookのPythonセルです。

%pip install -q ultralytics==8.4.168 roboflow PyYAML pandas onnx==1.22.0 onnxruntime

Kaggleが提供するPyTorchを無条件に置き換えず、現在のPython / CUDAとの組合せを確認します。Ultralytics以外の依存packageも、実行後にバージョン一覧を保存します。

import sys
import torch
import ultralytics

print('Python:', sys.version)
print('Ultralytics:', ultralytics.__version__)
print('PyTorch:', torch.__version__)
print('CUDA available:', torch.cuda.is_available())
assert torch.cuda.is_available(), 'KaggleのGPU設定を確認する'
for i in range(torch.cuda.device_count()):
    free, total = torch.cuda.mem_get_info(i)
    print(i, torch.cuda.get_device_name(i), 'free GiB:', round(free / 2**30, 2))
2. GitHubの処理をKaggleへ取得する

2. GitHubの処理をKaggleへ取得する

再現用に確認したソースはcommit 11c69547eeda9b48acd3f8012ec6d03d2cc59fee です。SOURCE_REF はこのコードの版を指定する値で、認証情報ではありません。GitHubの取得URLにある czh00361 はRON-Vの所有者名なので、この取得先を使うときは変更しません。これは今回手順を照合したソースで、すべての過去学習がこのcommitで行われたと確認した意味ではありません。

以下は今回追記した取得セルです。privateリポジトリもtokenをURLへ埋め込まず取得します。すでに別の作業ツリーがあれば上書きせず止まります。

from pathlib import Path
import io
import json
import urllib.request
import zipfile
from kaggle_secrets import UserSecretsClient

SOURCE_REF = '11c69547eeda9b48acd3f8012ec6d03d2cc59fee'
REPO = Path('/kaggle/working/RON-V')
assert not REPO.exists(), '既存のRON-Vを確認し、取得セルの再実行を判断する'
token = UserSecretsClient().get_secret('GITHUB_TOKEN')
request = urllib.request.Request(
    f'https://api.github.com/repos/czh00361/RON-V/zipball/{SOURCE_REF}',
    headers={'Authorization': f'Bearer {token}', 'Accept': 'application/vnd.github+json'},
)
with urllib.request.urlopen(request) as response:
    archive = response.read()
with zipfile.ZipFile(io.BytesIO(archive)) as z:
    root = z.namelist()[0].split('/')[0]
    for entry in z.infolist():
        relative = Path(entry.filename).relative_to(root)
        assert not relative.is_absolute() and '..' not in relative.parts
        target = REPO / relative
        if entry.is_dir():
            target.mkdir(parents=True, exist_ok=True)
        else:
            target.parent.mkdir(parents=True, exist_ok=True)
            target.write_bytes(z.read(entry))
del token, request, archive
(REPO / 'kaggle-source-ref.txt').write_text(SOURCE_REF + '\n')
print('Source:', SOURCE_REF)

以降の !python ... はNotebookから実行するコマンドです。通常のターミナルなら先頭の ! を外します。

3. road-v02 — データ作成・学習・実画像評価

3. road-v02 — データ作成・学習・実画像評価

使用データ

workspace / project version 使用クラス
laasya-wdzur / road-obstacle-detection-6zwj5 3 fallen_tree / road_debris / Pothole - v1 raw
laasya-wdzur / fallen-trees-detection-v1-f7jnw 1 fallen_tree

vision/datasets_v02.yaml が有効なソースを定義します。候補 ha_fallen_tree は enabled: false なので、この構成では使いません。

生成先は /kaggle/working/ronv_road3_v02。クラス順は次です。

0: fallen_tree
1: road_debris
2: pothole

polygonはBBoxへ変換し、重複画像を別splitへ移さないように構築します。構築時の記録はtrain 1,143 / valid 213 / test 144、計1,500画像です。

GitHubの保存済みコマンド

!python /kaggle/working/RON-V/vision/kaggle_pipeline.py road-v02 --check
!python /kaggle/working/RON-V/vision/kaggle_pipeline.py road-v02 --train --eval --export --epochs 50 --imgsz 640 --batch 16

--train は必要ならデータを作り、best.pt がなければ学習します。初期モデルは yolo11n.pt、device=0、workers=2です。前のroadモデルから継続する設定ではありません。

Dataset: /kaggle/working/ronv_road3_v02/data.yaml
Weight : /kaggle/working/runs/ronv_road3_v02/weights/best.pt
C270   : /kaggle/working/runs/ronv_c270_v02/evaluation_summary.csv

--export は学習結果・manifest・SHA256をZIPへ保存するオプションです。このスクリプトではONNX変換を意味しません。ONNXへの変換は後のセルで行います。

再学習するとき

既存の best.pt がある場合、単に --train を付けても学習をスキップします。GitHubの強制再実行用コマンドは次です。

!python /kaggle/working/RON-V/vision/kaggle_pipeline.py road-v02 --prepare --force-prepare
!python /kaggle/working/RON-V/vision/kaggle_pipeline.py road-v02 --train --eval --export --force-train --epochs 50 --imgsz 640 --batch 16

--force-train は既存runを削除します。先にrun全体とweightを保存してから使います。元データの追加だけなら、新しいprofile・出力先を用意して既存採用版を残す方が比較しやすくなります。

4. smoke-v01 — 1クラスの煙検出

4. smoke-v01 — 1クラスの煙検出

保存済み設定 vision/datasets_smoke_v01.yaml は roboflow-100 / smoke-uvylj Version 1を取得し、smoke の1クラスへ構築します。

GitHubの保存済みコマンド

!python /kaggle/working/RON-V/vision/kaggle_pipeline.py smoke-v01 --train --eval --export --epochs 50 --imgsz 640 --batch 16

初期モデルは yolo11n.pt、device=0、workers=2です。既存モデルを単にONNXへ変換しただけという扱いではありません。

Dataset: /kaggle/working/ronv_smoke_v01/data.yaml
Weight : /kaggle/working/runs/ronv_smoke_v01/weights/best.pt

記録済みvalidationはPrecision 0.978 / Recall 0.912 / mAP50 0.970 / mAP50-95 0.721。Pi側の初期confidenceは0.20です。

5. visibility-v02 — clear / low_visibilityの画像分類

5. visibility-v02 — clear / low_visibilityの画像分類

vision/datasets_visibility_v01.yaml の元データは arulraj-hoa0k / phd-project-p0ymv Version 5です。

元クラス 変換先
Fog low_visibility
Normal / Cloudy / Sunny clear

v02では道路データ laasya-wdzur / road-obstacle-detection-6zwj5 Version 3の画像を clear として追加します。追加上限はtrain 150 / val 30 / test 20。C270評価画像のハッシュを除外します。

保存済みスクリプトを使う再実行コマンド

!python /kaggle/working/RON-V/vision/visibility_pipeline_v02.py --epochs 30 --imgsz 224 --batch 32
!python /kaggle/working/RON-V/vision/evaluate_visibility_v02.py

1つ目がデータ構築、学習、test評価、C270評価、結果ZIPを作成します。2つ目はtestの混同行列とクラス別のPrecision / Recall / F1を追加します。

出力 保存先(/kaggle/working/ から)
test評価 runs/ronv_visibility_v02_test/
C270画像ごとの分類一覧 ronv_visibility_c270_v02/evaluation_summary.csv
test予測・混同行列・数値 ronv_visibility_v02_metrics/ の test_predictions.csv / confusion_matrix.csv / metrics.json

視界のC270確認は分類のCSVを作り、道路のような検出枠付き画像は作りません。結果ZIPには完成データの画像を含まないため、ronv_visibility_v02/ 全体(.ready と構築記録を含む)と学習run、上の評価結果も別に保管します。

成功ログには /usr/bin/python3 /kaggle/working/RON-V/vision/prepare_visibility_v02_dataset.py の呼出しが残っています。初期モデルは yolo11n-cls.pt、device=0、workers=2です。

train: clear=1440, low_visibility=675
val  : clear=128,  low_visibility=42
test : clear=122,  low_visibility=38

weightは /kaggle/working/runs/ronv_visibility_v02/weights/best.pt。test 160枚のaccuracyは0.9812、low_visibilityのPrecision / Recall / F1は0.9487 / 0.9737 / 0.9610でした。

--force-train は既存runを削除してやり直します。データを変更した場合は、データ構築スクリプトを再実行して .ready を含む状態を更新してから学習します。現行prepareスクリプトはv02の生成先を作り直します。

6. flood-v05 — 保存データの確認・復元から追加学習まで

6. flood-v05 — 保存データの確認・復元から追加学習まで

6-1. Kaggleで探すもの

元Notebookを開き、Inputへ追加したDatasetと、保存したOutputの両方を確認します。新しく開始した作業環境では、以前の /kaggle/working がそのまま残っているとは限りません。保存したNotebookのOutputや、別に保管したDataset / ZIPも探します。

成功ログには次の保存先が記録されています。

保存記録のあるもの 保存先
v04の学習用データ /kaggle/working/ronv_flood_v04/
v04の学習結果 /kaggle/working/runs/ronv_flood_v04/
v04のFULL ZIP /kaggle/working/RONV_flood-v04_20261009_132310.zip
v05の学習用データ /kaggle/working/ronv_flood_v05/
v05の学習結果 /kaggle/working/runs/ronv_flood_v05/
v05の実機用ZIP /kaggle/working/RONV_flood-v05_runtime.zip
v05の閾値比較結果 /kaggle/working/flood-v05-threshold-sweep.csv

これは当時の出力先の記録です。今のInputに追加されたDatasetの名前や、PCに保存したZIPの中身まで確認済みという意味ではありません。特に、名前がFULL ZIPでも、学習画像とラベルが入っているかは実際に開いて確認します。

読み込まれている冠水ファイルを探す(再確認用に追加したセル)

次のセルは、現在のInputとWorkingに見える冠水関連の設定・モデル・ZIP・CSVを表示するだけです。画像の追加・削除・再学習は行いません。Inputへ保存データを追加してから実行します。

from pathlib import Path

image_suffixes = {'.jpg', '.jpeg', '.png', '.bmp', '.webp'}
patterns = ('data.yaml', 'best.pt', '*.zip', '*.csv')
for root in (Path('/kaggle/input'), Path('/kaggle/working')):
    print('\nSEARCH:', root)
    found = False
    for pattern in patterns:
        for path in sorted(root.rglob(pattern)):
            if 'flood' not in str(path).lower():
                continue
            found = True
            print(path)
            if path.name == 'data.yaml':
                for split in ('train', 'valid', 'val', 'test'):
                    images = path.parent / split / 'images'
                    labels = path.parent / split / 'labels'
                    if images.is_dir():
                        image_count = sum(p.suffix.lower() in image_suffixes for p in images.iterdir() if p.is_file())
                        label_count = len(list(labels.glob('*.txt'))) if labels.is_dir() else 0
                        print(f'  {split}: images={image_count}, label_files={label_count}')
    if not found:
        print('冠水の保存データをInputへ追加するか、元Notebookの保存済みOutputを確認する')

このセルで見つからなくても、データを失ったとは判断しません。現在のNotebookへ追加されていないDatasetや、PC側のバックアップは検索対象に入っていません。

6-2. 完成データとモデルを作業用フォルダーへ戻す

復元したv05のデータでは、次の対応を確認します。パスは /kaggle/working/ronv_flood_v05/ からの相対パスです。

必要な場所・ファイル 中身
train/images/ と train/labels/ 学習用画像と、同じファイル名の正解ラベル。冠水画像にはfloodの枠、非冠水画像には冠水の枠を付けない
valid/images/ と valid/labels/ 学習途中で成績を確認する画像とラベル
test/images/ と test/labels/ 学習に使わず性能を確認する画像とラベル
data.yaml これらの場所と、1クラス {0: flood} を指定する設定
manifest.csv、build_summary.jsonなど(保存されていれば) 元画像、分割、追加したファイルなどを確認する記録

v05を当時と同じ初期モデルから学習し直すには、v04のモデルも別に戻します。

/kaggle/working/runs/ronv_flood_v04/weights/best.pt

画像を戻すだけでは正解の位置が分からず、モデルだけを戻しても学習用画像は増えません。両方を用意して、次の前提確認と学習セルへ進みます。

6-3. 保存済み処理でできる範囲

現行 kaggle_pipeline.py のprofileは road-v02 / smoke-v01 / flood-v01 / flood-v02 です。flood-v05 を引数に渡しても動きません。また、datasets_flood_v02.yaml の road_flood_water_v7 は無効であり、v02用コマンドからv05の最終データは生成できません。

最終冠水データの作成はKaggle側のセルで行いました。以下の学習呼出しは成功ログから復元したものです。元セルそのものの転載ではありません。

再学習の前提として、次を復元します。

必要な保存物 復元先
flood-v05の画像、ラベル、data.yaml(manifestなどの記録もあれば復元) /kaggle/working/ronv_flood_v05
追加学習元flood-v04のbest.pt /kaggle/working/runs/ronv_flood_v04/weights/best.pt
別保存したC270評価9枚 所定のKaggle Inputパス

元Notebookのデータ作成セル、入力データの保存名、hard negativeの選択manifestは、今回確認できた記録では未回収です。新規runtimeから生成し直す場合は、この部分を先に回収します。保存済みの完成データがあれば、以降の学習は実行できます。

ログで確認したデータ構成

split 元の正例 元のnegative 学習側での加工・複製
train 31 263 正例を6倍の186ファイル、hard negative追加98ファイル
valid 7 56 元の分割を保持
test 7 58 元の分割を保持

元の正例は45枚、negativeは377枚。hard negative候補38枚からtrain側28枚だけを使用しました。学習ファイルは計547枚、backgroundは361枚です。seedは20261009。C270画像と自宅内ベンチ画像は学習に使っていません。

復元後の前提確認(再実行用の追記)

from pathlib import Path
import yaml

FLOOD_DATA = Path('/kaggle/working/ronv_flood_v05/data.yaml')
FLOOD_BASE = Path('/kaggle/working/runs/ronv_flood_v04/weights/best.pt')
assert FLOOD_DATA.is_file(), '最終教師データを復元する'
assert FLOOD_BASE.is_file(), 'flood-v04の初期weightを復元する'
cfg = yaml.safe_load(FLOOD_DATA.read_text())
assert cfg['names'] == {0: 'flood'} or cfg['names'] == ['flood'], cfg['names']
root = FLOOD_DATA.parent
assert Path(cfg['path']).resolve() == root.resolve(), 'data.yamlのpathを復元先へ合わせる'
suffixes = {'.jpg', '.jpeg', '.png', '.bmp', '.webp'}
for key, count in [('train', 547), ('val', 63), ('test', 65)]:
    images = root / cfg[key]
    assert images.is_dir(), f'画像フォルダーがない: {images}'
    assert images.name == 'images', f'復元したフォルダー構成を確認: {images}'
    files = [p for p in images.iterdir() if p.is_file() and p.suffix.lower() in suffixes]
    labels = images.parent / 'labels'
    missing = [p.name for p in files if not (labels / (p.stem + '.txt')).is_file()]
    assert not missing, f'対応ラベルがない: {missing[:10]}'
    assert len(files) == count, (key, len(files), count)
    print(key, '画像:', len(files), '対応ラベル: OK')
print('完成データと追加学習元:', FLOOD_DATA, FLOOD_BASE)

data.yaml のpathが前のruntimeを指していれば復元先に合わせます。このセルはログのv05の枚数と画像・ラベルの対応を確認します。非冠水の空ラベルもファイルとして残します。枚数が合うことだけでは、当時と同じ画像や正しいラベルであることまでは確認できません。保存したmanifestや照合値とも見比べます。

学習セル(成功ログから復元)

ログではGPU0の空きが0.77GiB、GPU1が約14GiBだったためdevice=1でした。詳しい記録1の確認セルで現在のGPU番号を確認します。GPUが1台なら通常は番号0なので、以下の device=1 を合わせ、その変更も記録します。

v04/v05のセルは同じrun名に結果を書きます。以前の結果がある場合は、先にrun全体をZIPへ保存します。別条件で試す場合は name を変え、後の評価・変換でもその新しいrunを参照します。

from ultralytics import YOLO

model = YOLO('/kaggle/working/runs/ronv_flood_v04/weights/best.pt')
model.train(
    data='/kaggle/working/ronv_flood_v05/data.yaml',
    epochs=30, imgsz=640, batch=16, device=1, workers=2,
    project='/kaggle/working/runs', name='ronv_flood_v05',
    exist_ok=True, optimizer='AdamW', lr0=0.0005, lrf=0.05,
    patience=8, seed=20261009, deterministic=True,
    degrees=4.0, translate=0.08, scale=0.3,
    fliplr=0.5, flipud=0.0, mosaic=0.7, mixup=0.0,
    hsv_h=0.015, hsv_s=0.45, hsv_v=0.3,
    close_mosaic=10, plots=True, save=True,
)

最大30 epochの指定に対し、実際はepoch 17がbest、epoch 25でEarlyStoppingしました。

Precision=1.000  Recall=0.866  mAP50=0.875  mAP50-95=0.673
best.pt: /kaggle/working/runs/ronv_flood_v05/weights/best.pt

test 65枚はconfidence 0.20〜0.60でTP=7 / FN=0 / FP=0 / TN=58。0.65からFN=1が出た記録があり、Pi側は0.50を採用しました。これは画像単位の候補有無です。

flood-v04も学習し直す場合(成功ログから復元)

v04の完成データが復元できる場合に限り、初期weightから作り直せます。v05だけを追加学習する場合、このセルは不要です。

from ultralytics import YOLO

YOLO('yolo11n.pt').train(
    data='/kaggle/working/ronv_flood_v04/data.yaml',
    epochs=60, imgsz=640, batch=16, device=1, workers=2,
    project='/kaggle/working/runs', name='ronv_flood_v04',
    exist_ok=True, patience=12, seed=20261009,
    degrees=4.0, translate=0.08, scale=0.35,
    fliplr=0.5, flipud=0.0, mosaic=1.0, mixup=0.05,
    hsv_h=0.015, hsv_s=0.5, hsv_v=0.35,
    close_mosaic=10, plots=True, save=True,
)
7. ONNXへ変換する

7. ONNXへ変換する

以下は再実行用に整理した変換セルです。最初の SELECTED_PROFILES で変換対象を選びます。初期値は道路1つです。4つをまとめて変換するときだけ、4つの best.pt を用意し、コメントの4モデルの行へ変更します。入力・出力の確認セルと保存セルも、同じ選択を使います。

既存の同名ONNXは置き換わるので、以前の版を残す場合は先にZIPを保存します。flood-v05の成功ログではONNX 1.22.0、opset 18、入力 [1,3,640,640]、出力 [1,5,8400] を確認しました。simplify等の元セル指定はログだけでは確定できないため、ここではPiの入力・出力に合わせた条件を明示します。

from pathlib import Path
import hashlib
import json
import shutil
import onnx
from ultralytics import YOLO

EXPORTS = {
    'road-v02': ('ronv_road3_v02', 640, ['fallen_tree', 'road_debris', 'pothole']),
    'flood-v05': ('ronv_flood_v05', 640, ['flood']),
    'smoke-v01': ('ronv_smoke_v01', 640, ['smoke']),
    'visibility-v02': ('ronv_visibility_v02', 224, ['clear', 'low_visibility']),
}
SELECTED_PROFILES = ['road-v02']
# SELECTED_PROFILES = ['road-v02', 'flood-v05', 'smoke-v01', 'visibility-v02']
assert SELECTED_PROFILES and len(set(SELECTED_PROFILES)) == len(SELECTED_PROFILES)
assert all(profile in EXPORTS for profile in SELECTED_PROFILES)
models = {}
for profile in SELECTED_PROFILES:
    run, size, expected_names = EXPORTS[profile]
    best = Path('/kaggle/working/runs') / run / 'weights/best.pt'
    assert best.is_file(), best
    model = YOLO(str(best))
    names = [model.names[i] for i in range(len(model.names))]
    assert names == expected_names, (profile, names)
    models[profile] = model

# 選んだ全モデルの存在とクラス順を確認してから変換する。
for profile in SELECTED_PROFILES:
    run, size, expected_names = EXPORTS[profile]
    model = models[profile]
    names = [model.names[i] for i in range(len(model.names))]
    exported = Path(model.export(
        format='onnx', imgsz=size, batch=1, device='cpu',
        opset=18, dynamic=False, simplify=False, nms=False, half=False,
    ))
    onnx.checker.check_model(onnx.load(str(exported)))
    target = Path('/kaggle/working/ronv-runtime/models') / profile
    target.mkdir(parents=True, exist_ok=True)
    destination = target / f'{profile}.onnx'
    shutil.copy2(exported, destination)
    digest = hashlib.sha256(destination.read_bytes()).hexdigest()
    (target / 'export.json').write_text(json.dumps({
        'profile': profile, 'classes': names, 'input_size': size,
        'sha256': digest, 'opset': 18, 'simplify': False, 'nms': False,
    }, indent=2))
    print(profile, digest)

Pi側は外部NMSを行う実装なので、検出用ONNXへNMSを組み込みません。再変換したファイルのSHA256が採用版と異なっていても、それだけで性能の違いは判断できません。

直前の変換セルを実行した同じNotebookで、選んだモデルの入力・出力を確認します(再実行用の追記)。ONNXの形式とshapeを確認する段階であり、モデルの性能やPi上の動作を確認した段階ではありません。

import onnxruntime as ort

EXPECTED = {
    'road-v02': ([1, 3, 640, 640], [1, 7, 8400]),
    'flood-v05': ([1, 3, 640, 640], [1, 5, 8400]),
    'smoke-v01': ([1, 3, 640, 640], [1, 5, 8400]),
    'visibility-v02': ([1, 3, 224, 224], [1, 2]),
}
for profile in SELECTED_PROFILES:
    shapes = EXPECTED[profile]
    path = f'/kaggle/working/ronv-runtime/models/{profile}/{profile}.onnx'
    session = ort.InferenceSession(path, providers=['CPUExecutionProvider'])
    actual = (session.get_inputs()[0].shape, session.get_outputs()[0].shape)
    assert actual == shapes, (profile, actual)
    print(profile, actual)

shapeが合っても、RGB順、0〜1への正規化、検出のletterbox、分類のアスペクト比維持+center crop、クラス順をPi側と合わせる必要があります。実装は app/vision/road_onnx.py と app/vision/multi_onnx.py を参照します。

flood-v05では、採用時にtest 65枚とC270 9枚の合計74枚をPyTorch / ONNXで比較し、候補有無の判定不一致0でした。上の再変換セルを今回GPUで実行して同じ結果を得たわけではありません。新しいweightとONNXでも、採用前に候補有無・confidence・BBox・視界top-1を比較します。

8. 消える前に保存する(再実行用の追記)

8. 消える前に保存する(再実行用の追記)

Kaggleの /kaggle/working は永続保管先ではありません。学習結果ZIPだけでなく、再学習に必要なデータとNotebookを保存します。特にflood-v05はデータ作成セルの回収と完成データの保管が必要です。

  • run全体:args.yaml / results.csv / weights / plots。
  • データの設定、split、manifest、build_summary。
  • データを再生成できない場合は、画像とラベルを含む完成データ。
  • best.pt、追加学習元weight、ONNX、SHA256。道路・煙・v04の初期weight yolo11n.pt と視界の yolo11n-cls.pt も、同じ初期モデルから学習するために保管する。
  • 実行したNotebookの .ipynb、ソースcommit、package一覧。

環境記録とモデルZIPを保存する例です。直前のONNX変換・shape確認を終えてから実行します。このZIPには、選んだモデルのONNX・変換記録と、コード・ソフトの版の記録を入れます。教師データ、.pt、Pi側のアプリ・設定は含めません。 道路だけを選んだZIPは、4モデル一式の保存物ではありません。

from pathlib import Path
import json
import subprocess
import sys
import zipfile

runtime = Path('/kaggle/working/ronv-runtime')
source = Path('/kaggle/working/RON-V/kaggle-source-ref.txt')
assert source.is_file(), '取得したコードの版の記録を確認する'
files = [source]
for profile in SELECTED_PROFILES:
    folder = runtime / 'models' / profile
    files.extend([folder / f'{profile}.onnx', folder / 'export.json'])
assert all(p.is_file() for p in files), '選んだモデルの変換を先に済ませる'
freeze = subprocess.check_output([sys.executable, '-m', 'pip', 'freeze'], text=True)
archive = Path('/kaggle/working/RONV_models_runtime.zip')
with zipfile.ZipFile(archive, 'w', compression=zipfile.ZIP_DEFLATED) as z:
    for p in files:
        name = 'source-ref.txt' if p == source else str(p.relative_to(runtime))
        z.write(p, name)
    z.writestr('pip-freeze.txt', freeze)
    z.writestr('selected-profiles.json', json.dumps(SELECTED_PROFILES, indent=2))
print('保存:', archive, '対象:', SELECTED_PROFILES)

冠水の教師データを保管する例です。保存先の配布・公開範囲は元データの条件に合わせます。

import shutil
from pathlib import Path

assert Path('/kaggle/working/ronv_flood_v05/data.yaml').is_file()
assert Path('/kaggle/working/runs').is_dir()
shutil.make_archive(
    '/kaggle/working/RONV_flood-v05_training_dataset',
    'zip', '/kaggle/working', 'ronv_flood_v05',
)
shutil.make_archive(
    '/kaggle/working/RONV_training_runs', 'zip', '/kaggle/working/runs',
)

Notebookを保存し、生成ZIPをPCかprivate Kaggle Datasetへ退避してからruntimeを終了します。保存後は、PC側でZIPが開け、必要な画像・ラベル・モデルが入っているかも確認します。冠水v05では、今回使ったv04の best.pt も一緒に残します。

保存物の役割を最後に確認します。

保存物 次回できること
完成データの画像・ラベル・設定 + 学習条件・コード・初期weight 同じ入力と条件で学習をやり直す
best.pt + 評価画像 保存モデルを再評価し、ONNXへ変換する。この記事の道路・煙の入口を使う場合は完成データも戻す
ONNX + Pi側のコード・設定 Piで推論するために使う。学習画像や元の best.pt の代わりにはならない
Notebook・manifest・評価結果 どの画像と条件を使い、何を確認したか思い出す

SHA256は同じファイルかを確認するための値で、再学習の出力が同じバイト列になる保証ではありません。

9. 採用版の照合値と、残っている回収項目

9. 採用版の照合値と、残っている回収項目

成功ログにあるflood-v05の照合値です。

best.pt SHA256:
b7132ec7f6050575ff0cf66dace55ef63d9e7110bbefc7ed5e85bfa82af9c6f8

ONNX SHA256:
77ba3a4359a690387bee06aee9ae85fa4c227cb7e517952e70ec33fc0e526dea

RUNTIME ZIP:
/kaggle/working/RONV_flood-v05_runtime.zip

閾値比較CSV:
/kaggle/working/flood-v05-threshold-sweep.csv

道路・煙・視界は、GitHubと設定した取得元へアクセスでき、必要な環境と入力をそろえれば、保存済み処理からデータ作成・学習を進められます。採用時の完成データやモデルと同じものかは、保存した記録で別に照合します。冠水を完成データなしで再構築するために、次を元Notebookから回収します。

  1. flood-v04/v05の元データ取得・data.yaml生成セル全文。
  2. 入力Dataset / ZIPの保存名とversion、元画像の出典。
  3. split manifest、hard negativeの候補選択・追加98ファイルの生成処理。
  4. 閾値比較・PyTorch / ONNX比較セル全文と評価CSV。

この不足がある状態で「4モデルをコマンドだけで完全再現できる」とはしません。ONNX本体の公開判断も、元モデルと各データの利用条件を確認してから行います。

参照先

参照先

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?