tune のチュートリアル
このチュートリアルでは、小さなチューニングループを実行します。候補空間を定義し、計時サンプルから候補をスコア付けし、実用的な閾値で勝者を選び、パレートフロント上のトレードオフを確認し、大きな空間を再現可能なランダム部分集合に絞り込みます。例が決定的になるよう計時値は数値で与えていますが、実際には runner の観測から得られます。
クイックスタート
moon add Luna-Flow/mare_mark@0.3.0
import {
"Luna-Flow/mare_mark/tune",
}
test "pick a block size" {
let samples = [
("block-16", [5.1, 5.0, 5.3]),
("block-32", [4.2, 4.4, 4.1]),
("block-64", [4.3, 4.2, 4.3]),
]
let scores = samples.map(entry => @tune.score_samples(entry.0, entry.1, 0.0))
inspect(@tune.select_best(scores, 0.0, true).unwrap().candidate_id, content="block-32")
}
日常的な作業
同率の 2 つの候補のうち安価な方を選ぶ
block-64 は block-32 から 3 % 以内にあり、必要なワークスペースは 4 分の 1 です。閾値 5 % で、ワークスペースを副次的な指標とすると、こちらが勝ちます:
test "ties go to the smaller workspace" {
let scores = [
@tune.score_samples("block-32", [4.2, 4.4, 4.1], 65536.0),
@tune.score_samples("block-64", [4.3, 4.2, 4.3], 16384.0),
]
inspect(@tune.select_best(scores, 5.0, true).unwrap().candidate_id, content="block-64")
}
トレードオフを示す
test "time versus memory" {
let front = @tune.pareto_frontier([
@tune.score_samples("tiny", [9.0], 1024.0),
@tune.score_samples("small", [5.0], 4096.0),
@tune.score_samples("medium", [5.5], 8192.0),
@tune.score_samples("large", [4.0], 65536.0),
])
debug_inspect(front.map(s => s.candidate_id), content="[\"large\", \"small\", \"tiny\"]")
}
medium は small より遅く、かつ大きいため、フロント上にはありません。
制約付きで空間を探索する
test "exhaustive search with constraints" {
let space = @tune.CandidateSpace::new(
() => [(16, 16), (32, 32), (64, 64), (128, 128)],
tile => tile.0.to_string() + "x" + tile.1.to_string(),
tile => tile.0 * tile.1 * 8 <= 65536,
_ => [],
)
let measured : Map[String, Array[Double]] = Map([
("16x16", [6.0, 6.1]), ("32x32", [4.1, 4.0]), ("64x64", [3.9, 4.0]),
])
let result = @tune.exhaustive_scores(space, 4, tile => {
let id = tile.0.to_string() + "x" + tile.1.to_string()
@tune.score_samples(id, measured.get(id).unwrap_or([]), 0.0)
})
inspect(result.policy, content="global:64x64")
inspect(result.build_events[3].reason, content="constraint")
}
128×128 のタイルは 128 KiB を必要とし、計測される前に却下されます。
大きな空間から再現可能にサンプリングする
test "a seeded subset" {
let all = Array::makei(100, i => "candidate-" + i.to_string())
let first_ten = @tune.seeded_order(all, 2026UL, id => id)[:10].to_owned()
let again = @tune.seeded_order(all.rev(), 2026UL, id => id)[:10].to_owned()
inspect(first_ten == again, content="true")
}
同じシードからは、空間がどの順序で列挙されていても同じ 10 個の候補が得られます。シードを結果とともに記録してください。
さらに先へ
- 最終候補を確認する。 上位のいくつかを
@tune.confirmation_count(base, relative_iqr, budget)個の新しいサンプルで再計測し、それに基づいて改めて選択します。最初の選択が楽観的になる理由は tune の設計で説明しています。 - 形状をホールドアウトする。 いくつかの形状で選び、その後
ShapeHoldoutで勝者を検査します。 - ランナーで計測する。 各候補を
@runner.Implementationに包み、ブロックを共有するよう 1 つのケースで実行し、候補ごとの確認的な中央値をscore_samplesに渡します。 - 実例。
tune_gemmはこれらすべてをブロック化された行列積に適用しています。
よくある落とし穴
- 探索データだけで選択する。 勝者の呪いにより、実際より良く見えてしまいます。
- ノイズの多いデータでゼロの閾値を使う。 同率がノイズによって決まってしまいます。
- 一意でない ID。 選択と順序付けは ID が一意であることを前提とします。