Skip to main content

ライフサイクルコールバック

コールバックは createTrainer({ callbacks: { ... } }) の下に渡します。バックエンドの SSE イベントストリームから trainer.wait() 内でディスパッチされて動きます。

シグネチャー

interface 自体は 5 プロパティすべて必須ですが、createTrainercallbacks フィールドは 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、など)。
よくある用途は次のとおりです。自前のパイプライン(PostHog、Datadog)へのメトリックス転送、早期発散の検知、カスタム Early Stopping(指標が悪化したら学習を自動で打ち切るパターン。詳しくは Early Stopping レシピ を参照)の実装。Early Stopping を実装する際は、abortSignal の Abort がローカルの wait() を止めるだけである点に注意してください。バックエンドの GPU を実際に止めるには、その後 trainer.cancel() を呼ぶ必要があります。

onCheckpoint({ step, adapter, job, infer, artifacts })

学習中にバックエンドでアダプターチェックポイントが保存されたときに発火します。adapter{ kind: "checkpoint", jobId, step }infer の詳細は infer ページにあります。要するにチャット形式のリクエストを取り、生の Response を返す関数です。
TypeScript でファインチューニングする意義の多くはここにあります。学習途中のモデルに対して、学習完了を待たずに用意したプロンプトを試せます。

onCompleted({ job, artifacts })

成功時に 1 回発火。artifactsunknown[]: バックエンドが送った生の artifact リスト。スキーマは進化するため SDK で絞り込みません。

onFailed({ job, error })

バックエンドからの失敗報告で 1 回発火。errorstring(バックエンドが送ったメッセージ)であり、Error インスタンスではありません。
onFailedバックエンド側の 失敗専用です。他のコールバック内で投げられた例外は onFailed には届きません。何が起きるかは下記の 例外ハンドリング を参照してください。

振る舞い

順序

各コールバックは次のイベントがディスパッチされる前に await されます。Promise を返してよく(DB への書き込み、Slack への投稿、infer の呼び出しなど)、SDK は次のフレーム処理前にそれを待ちます。同一トレーナーに対する並行ディスパッチはありません。

例外ハンドリング

コールバック内で throw すると、その場でエラーとともに wait() が reject します。SDK はコールバックの失敗とトランスポートの失敗を区別します。throw されたコールバックは SSE 再接続ハンドラーには ルートされない ので、その失敗を黙って飛ばして続行することはありません。 これが重要なのは、コールバックが実行される時点で、その失敗イベントの Last-Event-ID は既に進んでいるためです。もし throw をリトライしてしまうと、再接続はそのイベントの から再開してイベントをスキップし、エラーを握り潰し、(終端の training.completed イベントの場合は)まるで何も生成しなかったかのように空の artifactswait() を解決してしまいます。代わりに reject することで、対処できる場所に失敗が表面化します。 再接続ハンドラーは純粋なトランスポート失敗(接続断や一時的な 5xx)専用です。それらは指数バックオフでリトライされますが、throw されたコールバックはリトライされません。 決定的かつ非致命的なエラーハンドリングが必要なら、コールバック内で catch してください(下の 2 つ目の例を参照)。そうすれば、回復可能な副作用の失敗(不安定な Slack 投稿、メトリクス書き込みなど)で実行全体が中断されることはありません。

最小例: すべてのイベントをログに出す。
コールバック内で catch して回復可能な失敗をローカルに閉じ込め、wait() が reject しないようにする:

型定義

TrainingLogContextCheckpointContextarkor から名前付きでエクスポートされていません。自分のコードで型付きコールバックパラメーターが必要なら、インラインで同じ形を定義してください。

関連項目