zimage-lora-builder / index_0226.html
Kotajiro's picture
Rename index.html to index_0226.html
e8a5770 verified
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Z-Image LoRA Command Builder</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;600&family=Syne:wght@400;600;800&display=swap" rel="stylesheet">
<style>
:root {
--bg: #0a0c10; --surface: #111318; --surface2: #181c24; --border: #252a35;
--accent: #00e5a0; --text: #e2e8f0; --text-dim: #64748b; --text-bright: #f8fafc;
--radius: 8px; --mono: 'JetBrains Mono', monospace; --sans: 'Syne', sans-serif;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: var(--bg); color: var(--text); font-family: var(--sans); min-height: 100vh; line-height: 1.6; }
body::before {
content: ''; position: fixed; inset: 0;
background-image: linear-gradient(rgba(0,229,160,0.025) 1px, transparent 1px), linear-gradient(90deg, rgba(0,229,160,0.025) 1px, transparent 1px);
background-size: 40px 40px; pointer-events: none; z-index: 0;
}
.container { max-width: 1000px; margin: 0 auto; padding: 32px 24px; position: relative; z-index: 1; }
header { margin-bottom: 24px; border-left: 3px solid var(--accent); padding-left: 20px; }
header h1 { font-size: 1.8rem; font-weight: 800; color: var(--text-bright); letter-spacing: -0.5px; }
header h1 span { color: var(--accent); }
header p { color: var(--text-dim); font-size: 0.8rem; font-family: var(--mono); margin-top: 4px; }
.section { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px 22px; margin-bottom: 12px; }
.section-title { font-size: 0.6rem; font-family: var(--mono); color: var(--accent); text-transform: uppercase; letter-spacing: 2px; margin-bottom: 14px; display: flex; align-items: center; gap: 8px; }
.section-title::after { content: ''; flex: 1; height: 1px; background: var(--border); }
.grid { display: grid; gap: 12px; }
.g2 { grid-template-columns: 1fr 1fr; }
.g3 { grid-template-columns: 1fr 1fr 1fr; }
.g4 { grid-template-columns: 1fr 1fr 1fr 1fr; }
.span2 { grid-column: span 2; }
@media (max-width: 680px) { .g2,.g3,.g4 { grid-template-columns: 1fr; } .span2 { grid-column: span 1; } }
.field { display: flex; flex-direction: column; gap: 5px; }
label { font-size: 0.7rem; font-family: var(--mono); color: var(--text-dim); display: flex; justify-content: space-between; align-items: baseline; }
label .hint { font-size: 0.6rem; color: rgba(100,116,139,0.5); }
input[type="text"], input[type="number"], select {
background: var(--surface2); border: 1px solid var(--border); border-radius: 6px;
color: var(--text-bright); padding: 8px 11px; font-family: var(--mono); font-size: 0.78rem;
outline: none; transition: border-color 0.15s, box-shadow 0.15s; width: 100%;
}
input:focus, select:focus { border-color: rgba(0,229,160,0.4); box-shadow: 0 0 0 2px rgba(0,229,160,0.07); }
select option { background: #181c24; }
.check-group { display: flex; flex-wrap: wrap; gap: 12px; }
.check-item { display: flex; align-items: center; gap: 6px; cursor: pointer; font-family: var(--mono); font-size: 0.74rem; color: var(--text); white-space: nowrap; }
.check-item input[type="checkbox"] { width: 14px; height: 14px; accent-color: var(--accent); cursor: pointer; }
.os-toggle { display: flex; gap: 2px; background: var(--surface2); border: 1px solid var(--border); border-radius: 6px; padding: 3px; width: fit-content; }
.os-btn { padding: 5px 18px; border: none; border-radius: 4px; background: none; color: var(--text-dim); font-family: var(--mono); font-size: 0.74rem; cursor: pointer; transition: all 0.15s; }
.os-btn.active { background: var(--surface); color: var(--accent); border: 1px solid rgba(0,229,160,0.2); }
/* preset bar */
.preset-bar { display: flex; gap: 10px; margin-bottom: 12px; }
.pbtn-export { flex: 1; padding: 11px 16px; border-radius: var(--radius); font-family: var(--mono); font-size: 0.78rem; font-weight: 600; cursor: pointer; border: none; transition: all 0.15s; display: flex; align-items: center; justify-content: center; gap: 8px; background: rgba(0,229,160,0.12); color: var(--accent); border: 1px solid rgba(0,229,160,0.25); }
.pbtn-export:hover { background: rgba(0,229,160,0.22); }
.pbtn-export.done { background: var(--accent); color: #000; border-color: var(--accent); }
/* drop zone */
.drop-zone { border: 2px dashed var(--border); border-radius: var(--radius); background: var(--surface); display: flex; align-items: center; gap: 16px; cursor: pointer; transition: all 0.2s; position: relative; overflow: hidden; min-height: 56px; padding: 0 20px; margin-bottom: 16px; }
.drop-zone:hover, .drop-zone.drag-over { border-color: var(--accent); background: rgba(0,229,160,0.05); }
.drop-zone.drag-over { box-shadow: 0 0 0 3px rgba(0,229,160,0.12); }
.drop-zone .dz-icon { font-size: 1.4rem; flex-shrink: 0; }
.drop-zone .dz-text { font-family: var(--mono); font-size: 0.75rem; color: var(--text-dim); }
.drop-zone .dz-text span { color: var(--accent); }
.drop-zone input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; }
.drop-zone.loaded { border-color: var(--accent); border-style: solid; }
.drop-zone.loaded .dz-text { color: var(--accent); }
/* output */
.output-wrap { background: #060810; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; margin-bottom: 12px; }
.output-header { display: flex; justify-content: space-between; align-items: center; padding: 9px 16px; background: var(--surface); border-bottom: 1px solid var(--border); }
.output-label { font-size: 0.6rem; font-family: var(--mono); color: var(--text-dim); text-transform: uppercase; letter-spacing: 1.5px; }
.output-label span { color: var(--accent); margin-left: 8px; }
.copy-btn { background: rgba(0,229,160,0.1); border: 1px solid rgba(0,229,160,0.2); color: var(--accent); padding: 4px 14px; border-radius: 4px; font-family: var(--mono); font-size: 0.7rem; cursor: pointer; transition: all 0.15s; }
.copy-btn:hover { background: rgba(0,229,160,0.2); }
.copy-btn.copied { color: #000; background: var(--accent); border-color: var(--accent); }
pre { padding: 20px; font-family: var(--mono); font-size: 0.75rem; line-height: 1.9; color: var(--text); white-space: pre-wrap; word-break: break-all; }
.k { color: #7dd3fc; } .c { color: var(--accent); font-weight: 600; } .ct { color: #f97316; }
/* divider */
.cmd-divider { display: flex; align-items: center; gap: 12px; margin: 20px 0 16px; }
.cmd-divider span { font-family: var(--mono); font-size: 0.65rem; color: var(--text-dim); text-transform: uppercase; letter-spacing: 2px; white-space: nowrap; }
.cmd-divider::before, .cmd-divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }
/* toast */
.toast { position: fixed; bottom: 28px; right: 28px; z-index: 999; background: var(--surface); border: 1px solid var(--accent); border-radius: var(--radius); padding: 12px 20px; font-family: var(--mono); font-size: 0.78rem; color: var(--accent); box-shadow: 0 4px 24px rgba(0,0,0,0.5); transform: translateY(16px); opacity: 0; transition: all 0.22s; pointer-events: none; }
.toast.show { transform: translateY(0); opacity: 1; }
.toast.err { border-color: #f97316; color: #f97316; }
</style>
</head>
<body>
<div class="container">
<header>
<h1>Z-Image <span>LoRA</span> Command Builder</h1>
<p>musubi-tuner (kohya-ss) — zimage_train_network.py</p>
</header>
<!-- PRESET -->
<div class="preset-bar">
<button class="pbtn-export" id="exportBtn" onclick="exportSettings()">⬇ 設定をJSONで保存</button>
</div>
<div class="drop-zone" id="dropZone">
<span class="dz-icon">📂</span>
<span class="dz-text">設定ファイル(JSON)を<span>ドラッグ&ドロップ</span>、またはクリックして読み込む</span>
<input type="file" accept=".json" onchange="loadFile(this.files[0])">
</div>
<!-- OS -->
<div class="section">
<div class="section-title">OS / 改行スタイル</div>
<div class="os-toggle">
<button class="os-btn active" id="btn-win" onclick="setOS('win')">Windows ( ^ )</button>
<button class="os-btn" id="btn-lnx" onclick="setOS('lnx')">Linux / Mac ( \ )</button>
</div>
</div>
<!-- PATHS -->
<div class="section">
<div class="section-title">モデル・パス</div>
<div class="grid g2">
<div class="field span2">
<label>--dit <span class="hint">分割safetensorsは1枚目を指定</span></label>
<input type="text" id="dit" value="path/to/dit_model">
</div>
<div class="field">
<label>--vae</label>
<input type="text" id="vae" value="path/to/vae_model">
</div>
<div class="field">
<label>--text_encoder</label>
<input type="text" id="te" value="path/to/text_encoder">
</div>
<div class="field span2">
<label>--dataset_config <span class="hint">TOML</span></label>
<input type="text" id="ds" value="path/to/dataset_config.toml">
</div>
<div class="field">
<label>--output_dir</label>
<input type="text" id="outdir" value="path/to/output_dir">
</div>
<div class="field">
<label>--output_name</label>
<input type="text" id="outname" value="my_lora">
</div>
</div>
</div>
<!-- LORA -->
<div class="section">
<div class="section-title">LoRAパラメータ</div>
<div class="grid g4">
<div class="field">
<label>--network_module</label>
<select id="netmod">
<option value="networks.lora_zimage" selected>lora_zimage(推奨)</option>
<option value="networks.lora">lora(汎用)</option>
</select>
</div>
<div class="field">
<label>--network_dim <span class="hint">Rank</span></label>
<select id="ndim" onchange="syncAlpha()">
<option value="4">4</option><option value="8">8</option><option value="16">16</option>
<option value="32" selected>32</option><option value="64">64</option><option value="128">128</option>
</select>
</div>
<div class="field">
<label>--network_alpha</label>
<select id="nalpha">
<option value="4">4</option><option value="8">8</option><option value="16">16</option>
<option value="32" selected>32</option><option value="64">64</option><option value="128">128</option>
</select>
</div>
<div class="field">
<label style="opacity:0">-</label>
<label class="check-item" style="padding-top:8px;"><input type="checkbox" id="use_loha"> LoHa/LoKr</label>
</div>
</div>
</div>
<!-- STEPS -->
<div class="section">
<div class="section-title">ステップ・シード</div>
<div class="grid g4">
<div class="field">
<label>--max_train_steps</label>
<input type="number" id="steps" value="3000" min="100" step="100">
</div>
<div class="field">
<label>--save_every_n_steps</label>
<input type="number" id="save_steps" value="200" min="50" step="50">
</div>
<div class="field">
<label>--seed</label>
<input type="number" id="seed" value="42">
</div>
<div class="field">
<label>--max_data_loader_n_workers</label>
<input type="number" id="workers" value="2" min="0" max="8">
</div>
</div>
</div>
<!-- OPTIMIZER -->
<div class="section">
<div class="section-title">オプティマイザ / 学習率</div>
<div class="grid g4">
<div class="field">
<label>--optimizer_type</label>
<select id="opttype" onchange="onOptimizerChange()">
<option value="adamw8bit" selected>adamw8bit</option>
<option value="adamw">adamw</option>
<option value="adafactor">adafactor</option>
<option value="prodigy">prodigy</option>
<option value="came">came</option>
<option value="Lion">Lion</option>
<option value="Lion8bit">Lion8bit</option>
</select>
</div>
<div class="field">
<label>--learning_rate <span class="hint" id="lr-hint"></span></label>
<select id="lr" onchange="toggleLRCustom()">
<option value="1e-4" selected>1e-4</option>
<option value="5e-5">5e-5(安定)</option>
<option value="1e-5">1e-5(Lion推奨)</option>
<option value="2e-4">2e-4(高速)</option>
<option value="1e-3">1e-3</option>
<option value="custom">カスタム入力</option>
</select>
</div>
<div class="field" id="lr_custom_wrap" style="display:none">
<label>カスタム値</label>
<input type="text" id="lr_custom" placeholder="例: 3e-5">
</div>
</div>
</div>
<!-- LR SCHEDULER -->
<div class="section">
<div class="section-title">LRスケジューラ</div>
<div class="grid g4">
<div class="field">
<label>--lr_scheduler</label>
<select id="lr_sched" onchange="onSchedChange()">
<option value="constant" selected>constant</option>
<option value="cosine">cosine</option>
<option value="cosine_with_restarts">cosine_with_restarts</option>
<option value="constant_with_warmup">constant_with_warmup</option>
<option value="linear">linear</option>
<option value="polynomial">polynomial</option>
</select>
</div>
<div class="field" id="warmup_wrap" style="display:none">
<label>--lr_warmup_steps</label>
<input type="number" id="lr_warmup" value="100" min="0" step="10">
</div>
<div class="field" id="restart_wrap" style="display:none">
<label>--lr_scheduler_num_cycles</label>
<input type="number" id="lr_cycles" value="1" min="1">
</div>
<div class="field" id="poly_wrap" style="display:none">
<label>--lr_scheduler_power</label>
<input type="number" id="lr_power" value="1.0" min="0.1" step="0.1">
</div>
</div>
</div>
<!-- TIMESTEP -->
<div class="section">
<div class="section-title">Timestep / Flow</div>
<div class="grid g3">
<div class="field">
<label>--timestep_sampling</label>
<select id="ts">
<option value="shift" selected>shift(Z-Image推奨)</option>
<option value="sigmoid">sigmoid</option>
<option value="uniform">uniform</option>
<option value="logit_normal">logit_normal</option>
</select>
</div>
<div class="field">
<label>--discrete_flow_shift <span class="hint">Z-Image: 2.0</span></label>
<select id="dfs" onchange="toggleDFSCustom()">
<option value="2.0" selected>2.0</option>
<option value="1.0">1.0</option>
<option value="3.0">3.0</option>
<option value="custom">カスタム入力</option>
</select>
</div>
<div class="field" id="dfs_custom_wrap" style="display:none">
<label>カスタム値</label>
<input type="text" id="dfs_custom" placeholder="例: 1.5">
</div>
<div class="field">
<label>--weighting_scheme</label>
<select id="ws">
<option value="none" selected>none</option>
<option value="sigma_sqrt">sigma_sqrt</option>
<option value="logit_normal">logit_normal</option>
<option value="mode">mode</option>
</select>
</div>
</div>
</div>
<!-- PRECISION -->
<div class="section">
<div class="section-title">精度 / アテンション</div>
<div class="grid g2">
<div class="field">
<label>--mixed_precision</label>
<select id="mp">
<option value="bf16" selected>bf16(推奨)</option>
<option value="fp16">fp16</option>
<option value="no">no (fp32)</option>
</select>
</div>
<div class="field">
<label style="margin-bottom:8px;">アテンション</label>
<div class="check-group">
<label class="check-item"><input type="checkbox" id="sdpa" checked> --sdpa</label>
<label class="check-item"><input type="checkbox" id="xf"> --xformers</label>
</div>
</div>
</div>
</div>
<!-- VRAM -->
<div class="section">
<div class="section-title">VRAM最適化</div>
<div class="check-group" style="margin-bottom:14px;">
<label class="check-item"><input type="checkbox" id="gc" checked> --gradient_checkpointing</label>
<label class="check-item"><input type="checkbox" id="pdlw" checked> --persistent_data_loader_workers</label>
<label class="check-item"><input type="checkbox" id="fp8"> --fp8_base</label>
<label class="check-item"><input type="checkbox" id="fp8te"> --fp8_llm</label>
<label class="check-item"><input type="checkbox" id="bswap_patch"> --block_swap_optimizer_patch_params <span style="color:#f97316;font-size:0.65rem;margin-left:4px;">Lion+blocks_to_swap時に必要</span></label>
</div>
<div class="grid g4">
<div class="field">
<label>--blocks_to_swap <span class="hint">0=無効</span></label>
<input type="number" id="bswap" value="0" min="0" max="36">
</div>
</div>
</div>
<!-- ACCELERATE -->
<div class="section">
<div class="section-title">accelerate設定</div>
<div class="grid g4">
<div class="field">
<label>--num_cpu_threads_per_process</label>
<input type="number" id="cpu_t" value="1" min="1">
</div>
<div class="field">
<label>--num_processes <span class="hint">マルチGPU時</span></label>
<input type="number" id="nproc" value="1" min="1">
</div>
</div>
</div>
<!-- CACHE LATENT OPTIONS -->
<div class="section">
<div class="section-title">Latentキャッシュ オプション</div>
<div class="grid g4">
<div class="field">
<label>--batch_size</label>
<input type="number" id="cl_batch" value="1" min="1">
</div>
<div class="field">
<label>--vae_spatial_tile_sample_min_size <span class="hint">VRAM節約: 128程度</span></label>
<input type="number" id="cl_tile" value="" min="64" step="32" placeholder="未指定">
</div>
<div class="field">
<label style="margin-bottom:8px;">その他</label>
<div class="check-group">
<label class="check-item"><input type="checkbox" id="cl_skip"> --skip_existing</label>
</div>
</div>
</div>
</div>
<!-- CACHE TE OPTIONS -->
<div class="section">
<div class="section-title">Text Encoderキャッシュ オプション</div>
<div class="grid g4">
<div class="field">
<label>--batch_size <span class="hint">デフォルト: 16</span></label>
<input type="number" id="te_batch" value="16" min="1">
</div>
<div class="field">
<label style="margin-bottom:8px;">VRAM節約</label>
<div class="check-group">
<label class="check-item"><input type="checkbox" id="te_fp8llm"> --fp8_llm</label>
<label class="check-item"><input type="checkbox" id="te_skip"> --skip_existing</label>
</div>
</div>
</div>
</div>
<!-- OUTPUTS -->
<div class="cmd-divider"><span>① Latentキャッシュ</span></div>
<div class="output-wrap">
<div class="output-header">
<span class="output-label">zimage_cache_latents.py</span>
<button class="copy-btn" id="copybtn-cl" onclick="copyCmd('output-cl','copybtn-cl')">Copy</button>
</div>
<pre id="output-cl"></pre>
</div>
<div class="cmd-divider"><span>② Text Encoderキャッシュ</span></div>
<div class="output-wrap">
<div class="output-header">
<span class="output-label">zimage_cache_text_encoder_outputs.py</span>
<button class="copy-btn" id="copybtn-te" onclick="copyCmd('output-te','copybtn-te')">Copy</button>
</div>
<pre id="output-te"></pre>
</div>
<div class="cmd-divider"><span>③ 学習コマンド</span></div>
<div class="output-wrap">
<div class="output-header">
<span class="output-label">zimage_train_network.py</span>
<button class="copy-btn" id="copybtn" onclick="copyCmd('output','copybtn')">Copy</button>
</div>
<pre id="output"></pre>
</div>
</div><!-- /container -->
<div class="toast" id="toast"></div>
<script>
let OS = 'win';
const TEXT_IDS = ['dit','vae','te','ds','outdir','outname','lr_custom','dfs_custom'];
const SELECT_IDS = ['netmod','ndim','nalpha','opttype','lr','lr_sched','ts','dfs','ws','mp'];
const NUMBER_IDS = ['steps','save_steps','seed','workers','lr_warmup','lr_cycles','lr_power','cpu_t','nproc','bswap','cl_batch','cl_tile','te_batch'];
const CHECK_IDS = ['sdpa','xf','gc','pdlw','fp8','fp8te','bswap_patch','use_loha','cl_skip','te_fp8llm','te_skip'];
function esc(s) { return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
function highlight(raw) {
return esc(raw)
.replace(/(accelerate launch|python)/g, '<span class="c">$1</span>')
.replace(/(--[\w_-]+)/g, '<span class="k">$1</span>')
.replace(/(\^|\\)(?=\n)/g, '<span class="ct">$1</span>');
}
function setOS(os) {
OS = os;
document.getElementById('btn-win').classList.toggle('active', os==='win');
document.getElementById('btn-lnx').classList.toggle('active', os==='lnx');
genAll();
}
function syncAlpha() {
document.getElementById('nalpha').value = document.getElementById('ndim').value;
genAll();
}
function onSchedChange() {
const v = document.getElementById('lr_sched').value;
const needsWarmup = ['constant_with_warmup','cosine_with_restarts','cosine','linear','polynomial'].includes(v);
document.getElementById('warmup_wrap').style.display = needsWarmup ? 'block' : 'none';
document.getElementById('restart_wrap').style.display = v === 'cosine_with_restarts' ? 'block' : 'none';
document.getElementById('poly_wrap').style.display = v === 'polynomial' ? 'block' : 'none';
genAll();
}
function onOptimizerChange() {
const opt = document.getElementById('opttype').value;
const hint = document.getElementById('lr-hint');
if (opt === 'Lion' || opt === 'Lion8bit') {
hint.textContent = 'Lion推奨: 1e-5〜1e-4';
hint.style.color = '#f97316';
} else {
hint.textContent = '';
}
genAll();
}
function toggleLRCustom() {
document.getElementById('lr_custom_wrap').style.display =
document.getElementById('lr').value === 'custom' ? 'block' : 'none';
genAll();
}
function toggleDFSCustom() {
document.getElementById('dfs_custom_wrap').style.display =
document.getElementById('dfs').value === 'custom' ? 'block' : 'none';
genAll();
}
function getLR() {
const v = document.getElementById('lr').value;
return v === 'custom' ? (document.getElementById('lr_custom').value || '1e-4') : v;
}
function getDFS() {
const v = document.getElementById('dfs').value;
return v === 'custom' ? (document.getElementById('dfs_custom').value || '2.0') : v;
}
function genCacheLatent() {
const cont = OS === 'win' ? '^' : '\\';
const C = ' ' + cont;
const ds = document.getElementById('ds').value;
const vae = document.getElementById('vae').value;
const batch = parseInt(document.getElementById('cl_batch').value) || 1;
const tile = document.getElementById('cl_tile').value;
const skip = document.getElementById('cl_skip').checked;
let raw = 'python src/musubi_tuner/zimage_cache_latents.py' + C + '\n';
raw += ' --dataset_config ' + ds + C + '\n';
raw += ' --vae ' + vae;
if (batch !== 1) raw += C + '\n --batch_size ' + batch;
if (tile) raw += C + '\n --vae_spatial_tile_sample_min_size ' + tile;
if (skip) raw += C + '\n --skip_existing';
document.getElementById('output-cl').innerHTML = highlight(raw);
}
function genCacheTE() {
const cont = OS === 'win' ? '^' : '\\';
const C = ' ' + cont;
const ds = document.getElementById('ds').value;
const te = document.getElementById('te').value;
const batch = document.getElementById('te_batch').value;
const fp8 = document.getElementById('te_fp8llm').checked;
const skip = document.getElementById('te_skip').checked;
let raw = 'python src/musubi_tuner/zimage_cache_text_encoder_outputs.py' + C + '\n';
raw += ' --dataset_config ' + ds + C + '\n';
raw += ' --text_encoder ' + te + C + '\n';
raw += ' --batch_size ' + batch;
if (fp8) raw += C + '\n --fp8_llm';
if (skip) raw += C + '\n --skip_existing';
document.getElementById('output-te').innerHTML = highlight(raw);
}
function genTrain() {
const cont = OS === 'win' ? '^' : '\\';
const C = ' ' + cont;
const v = id => document.getElementById(id).value;
const c = id => document.getElementById(id).checked;
const dit=v('dit'), vae=v('vae'), te=v('te'), ds=v('ds');
const outdir=v('outdir'), outname=v('outname');
const netmod=v('netmod'), ndim=v('ndim'), nalpha=v('nalpha');
const steps=v('steps'), saveS=v('save_steps'), seed=v('seed'), workers=v('workers');
const opttype=v('opttype'), mp=v('mp'), ts=v('ts'), ws=v('ws');
const cpuT=v('cpu_t'), nproc=v('nproc'), bswap=parseInt(v('bswap'))||0;
const lr_sched=v('lr_sched'), lr_warmup=v('lr_warmup'), lr_cycles=v('lr_cycles'), lr_power=v('lr_power');
let raw = 'accelerate launch --num_cpu_threads_per_process ' + cpuT + ' --mixed_precision ' + mp;
if (parseInt(nproc) > 1) raw += ' --num_processes ' + nproc;
raw += C + '\n';
raw += ' src/musubi_tuner/zimage_train_network.py' + C + '\n';
raw += ' --dit ' + dit + C + '\n';
raw += ' --vae ' + vae + C + '\n';
raw += ' --text_encoder ' + te + C + '\n';
raw += ' --dataset_config ' + ds + C + '\n';
let attn = [];
if (c('sdpa')) attn.push('--sdpa');
if (c('xf')) attn.push('--xformers');
attn.push('--mixed_precision ' + mp);
raw += ' ' + attn.join(' ') + C + '\n';
raw += ' --timestep_sampling ' + ts + ' --weighting_scheme ' + ws + ' --discrete_flow_shift ' + getDFS() + C + '\n';
raw += ' --optimizer_type ' + opttype + ' --learning_rate ' + getLR();
if (c('gc')) raw += ' --gradient_checkpointing';
raw += C + '\n';
if (lr_sched !== 'constant') {
let sl = ' --lr_scheduler ' + lr_sched;
const nw = ['constant_with_warmup','cosine_with_restarts','cosine','linear','polynomial'].includes(lr_sched);
if (nw && parseInt(lr_warmup) > 0) sl += ' --lr_warmup_steps ' + lr_warmup;
if (lr_sched === 'cosine_with_restarts') sl += ' --lr_scheduler_num_cycles ' + lr_cycles;
if (lr_sched === 'polynomial') sl += ' --lr_scheduler_power ' + lr_power;
raw += sl + C + '\n';
}
raw += ' --max_data_loader_n_workers ' + workers;
if (c('pdlw')) raw += ' --persistent_data_loader_workers';
raw += C + '\n';
raw += ' --network_module ' + netmod + C + '\n';
raw += ' --network_dim ' + ndim + ' --network_alpha ' + nalpha + C + '\n';
raw += ' --max_train_steps ' + steps + ' --save_every_n_steps ' + saveS + ' --seed ' + seed;
if (c('fp8')) raw += C + '\n --fp8_base';
if (c('fp8te')) raw += C + '\n --fp8_llm';
if (bswap > 0) raw += C + '\n --blocks_to_swap ' + bswap;
if (c('bswap_patch')) raw += C + '\n --block_swap_optimizer_patch_params';
raw += C + '\n --output_dir ' + outdir + ' --output_name ' + outname;
document.getElementById('output').innerHTML = highlight(raw);
}
function genAll() {
genCacheLatent();
genCacheTE();
genTrain();
}
function copyCmd(outputId, btnId) {
const text = document.getElementById(outputId).innerText;
navigator.clipboard.writeText(text).then(() => {
const btn = document.getElementById(btnId);
btn.textContent = '✓ Copied';
btn.classList.add('copied');
setTimeout(() => { btn.textContent = 'Copy'; btn.classList.remove('copied'); }, 2000);
});
}
// EXPORT
function exportSettings() {
const data = { _version: 1, os: OS };
TEXT_IDS.forEach(id => { const el = document.getElementById(id); if(el) data[id] = el.value; });
SELECT_IDS.forEach(id => { const el = document.getElementById(id); if(el) data[id] = el.value; });
NUMBER_IDS.forEach(id => { const el = document.getElementById(id); if(el) data[id] = el.value; });
CHECK_IDS.forEach(id => { const el = document.getElementById(id); if(el) data[id] = el.checked; });
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = (document.getElementById('outname').value || 'lora-settings').replace(/[\\/:*?"<>|]/g,'_') + '.json';
a.click();
URL.revokeObjectURL(a.href);
const btn = document.getElementById('exportBtn');
btn.classList.add('done'); btn.textContent = '✓ 保存しました';
setTimeout(() => { btn.classList.remove('done'); btn.textContent = '⬇ 設定をJSONで保存'; }, 2200);
}
// IMPORT
function applySettings(data) {
if (!data || data._version !== 1) { showToast('⚠ 形式が不正なファイルです', true); return; }
if (data.os) setOS(data.os);
TEXT_IDS.forEach(id => { if (data[id] !== undefined) { const el = document.getElementById(id); if(el) el.value = data[id]; } });
SELECT_IDS.forEach(id => {
if (data[id] !== undefined) {
const el = document.getElementById(id); if(!el) return;
const exists = Array.from(el.options).some(o => o.value === String(data[id]));
el.value = exists ? data[id] : el.options[0].value;
}
});
NUMBER_IDS.forEach(id => { if (data[id] !== undefined) { const el = document.getElementById(id); if(el) el.value = data[id]; } });
CHECK_IDS.forEach(id => { if (data[id] !== undefined) { const el = document.getElementById(id); if(el) el.checked = !!data[id]; } });
onSchedChange(); onOptimizerChange(); toggleLRCustom(); toggleDFSCustom();
genAll();
showToast('✓ 設定を読み込みました');
}
function loadFile(file) {
if (!file) return;
if (!file.name.toLowerCase().endsWith('.json') && file.type !== 'application/json') {
showToast('⚠ JSONファイルを選択してください', true); return;
}
const reader = new FileReader();
reader.onload = e => {
try {
applySettings(JSON.parse(e.target.result));
const dz = document.getElementById('dropZone');
dz.classList.add('loaded');
dz.querySelector('.dz-text').innerHTML = '✓ <span>' + file.name + '</span> を読み込みました';
} catch(err) {
showToast('⚠ 読み込みに失敗: ' + err.message, true);
}
};
reader.onerror = () => showToast('⚠ ファイル読み取りエラー', true);
reader.readAsText(file, 'UTF-8');
}
// D&D
const dropZone = document.getElementById('dropZone');
['dragenter','dragover'].forEach(ev => dropZone.addEventListener(ev, e => { e.preventDefault(); dropZone.classList.add('drag-over'); }));
['dragleave','dragend'].forEach(ev => dropZone.addEventListener(ev, () => dropZone.classList.remove('drag-over')));
dropZone.addEventListener('drop', e => { e.preventDefault(); dropZone.classList.remove('drag-over'); if(e.dataTransfer.files[0]) loadFile(e.dataTransfer.files[0]); });
function showToast(msg, isErr) {
const t = document.getElementById('toast');
t.textContent = msg; t.className = 'toast' + (isErr ? ' err' : '');
void t.offsetWidth; t.classList.add('show');
setTimeout(() => t.classList.remove('show'), 2800);
}
// AUTO UPDATE
document.querySelectorAll('input:not([type=file]), select').forEach(el => {
el.addEventListener('change', genAll);
el.addEventListener('input', genAll);
});
// INIT
onSchedChange();
genAll();
</script>
</body>
</html>