ライフサイクルコールバック
コールバックはcreateTrainer({ callbacks: { ... } }) の下に渡します。バックエンドの SSE イベントストリームから trainer.wait() 内でディスパッチされて動きます。
シグネチャー
createTrainer の callbacks フィールドは Partial<TrainerCallbacks> 型なので、必要なイベントだけ指定すれば OK です。コールバックの戻り値は破棄されます(unknown | Promise<unknown> という戻り値型は、コールバックから値を return しても TypeScript が文句を言わない、というだけの意味です)。
それぞれのコールバックがいつ発火するか
start() を呼んで wait() を呼ばないと、コールバックは決して動きません。arkor start は両方呼んでくれます。プログラムから呼び出す側も同じことをしてください。
パラメーター
onStarted({ job })
SSE ストリームが training.started を報告したときに発火します。ログ行や「学習開始しました」通知に使ってください。
onLog({ step, loss, evalLoss, learningRate, epoch, samplesPerSecond, job })
学習が進むにつれて繰り返し発火します。各数値フィールドは number | null: バックエンドはあるステップで持っているフィールドだけ埋めます(なので evalLoss は eval 以外のステップで null、learningRate は LR スケジューラ更新の合間に null、など)。
abortSignal の Abort がローカルの wait() を止めるだけである点に注意してください。バックエンドの GPU を実際に止めるには、その後 trainer.cancel() を呼ぶ必要があります。
onCheckpoint({ step, adapter, job, infer, artifacts })
学習中にバックエンドでアダプターチェックポイントが保存されたときに発火します。adapter は { kind: "checkpoint", jobId, step }。infer の詳細は infer ページにあります。要するにチャット形式のリクエストを取り、生の Response を返す関数です。
onCompleted({ job, artifacts })
成功時に 1 回発火。artifacts は unknown[]: バックエンドが送った生の artifact リスト。スキーマは進化するため SDK で絞り込みません。
onFailed({ job, error })
バックエンドからの失敗報告で 1 回発火。error は string(バックエンドが送ったメッセージ)であり、Error インスタンスではありません。
onFailed は バックエンド側の 失敗専用です。他のコールバック内で投げられた例外は onFailed には届きません。何が起きるかは下記の 例外ハンドリング を参照してください。
振る舞い
順序
各コールバックは次のイベントがディスパッチされる前に await されます。Promise を返してよく(DB への書き込み、Slack への投稿、infer の呼び出しなど)、SDK は次のフレーム処理前にそれを待ちます。同一トレーナーに対する並行ディスパッチはありません。
例外ハンドリング
コールバック内で throw すると、その場でエラーとともにwait() が reject します。SDK はコールバックの失敗とトランスポートの失敗を区別します。throw されたコールバックは SSE 再接続ハンドラーには ルートされない ので、その失敗を黙って飛ばして続行することはありません。
これが重要なのは、コールバックが実行される時点で、その失敗イベントの Last-Event-ID は既に進んでいるためです。もし throw をリトライしてしまうと、再接続はそのイベントの 後 から再開してイベントをスキップし、エラーを握り潰し、(終端の training.completed イベントの場合は)まるで何も生成しなかったかのように空の artifacts で wait() を解決してしまいます。代わりに reject することで、対処できる場所に失敗が表面化します。
再接続ハンドラーは純粋なトランスポート失敗(接続断や一時的な 5xx)専用です。それらは指数バックオフでリトライされますが、throw されたコールバックはリトライされません。
決定的かつ非致命的なエラーハンドリングが必要なら、コールバック内で catch してください(下の 2 つ目の例を参照)。そうすれば、回復可能な副作用の失敗(不安定な Slack 投稿、メトリクス書き込みなど)で実行全体が中断されることはありません。
例
最小例: すべてのイベントをログに出す。wait() が reject しないようにする:
型定義
TrainingLogContext と CheckpointContext は arkor から名前付きでエクスポートされていません。自分のコードで型付きコールバックパラメーターが必要なら、インラインで同じ形を定義してください。
関連項目
createTrainer: これらのコールバックを取り付ける関数- 実行ライフサイクル: 概念的な流れ
infer:onCheckpointに渡される関数- トレーナー制御:
abortSignalとcancel() - Early Stopping レシピ