chore: initialize git repo, add matplotlib dep, extend config

- Add .gitignore for Python/data/models
- Add matplotlib>=3.8.0 for eval plots
- Add PretrainConfig, FinetuneConfig, BalabitAdapterConfig, EvalConfig dataclasses
This commit is contained in:
2026-05-10 12:24:07 +08:00
commit 4d414fd180
44 changed files with 9681 additions and 0 deletions

133
static/js/train.js Normal file
View File

@@ -0,0 +1,133 @@
/**
* Train tab component — unified training with [鼠标 | 滚轮] sub-tabs.
*/
import { fetchSSE } from './api.js'
const template = `
<div class="view">
<div class="sub-tabs">
<button class="sub-tab" :class="{active: subTab==='mouse'}" @click="subTab='mouse'">鼠标模型</button>
<button class="sub-tab" :class="{active: subTab==='scroll'}" @click="subTab='scroll'">滚轮模型</button>
</div>
<!-- ════════ 鼠标训练 ════════ -->
<div v-show="subTab==='mouse'">
<div class="form-grid" style="max-width:240px;">
<div class="field">
<label>训练轮数</label>
<input type="number" v-model.number="mouseTrain.epochs" :disabled="mouseTrain.running" min="10" />
</div>
</div>
<button class="btn btn-primary" @click="startMouseTrain" :disabled="mouseTrain.running">
{{ mouseTrain.running ? '训练中…' : '开始训练' }}
</button>
<div class="progress-section" v-if="mouseTrain.running || mouseTrain.done">
<div class="progress-label">
<span>JointCVAE 联合模型</span>
<span>{{ mouseTrain.epoch }} / {{ mouseTrain.total }}</span>
</div>
<div class="progress-track">
<div class="progress-fill" :style="{width: mouseTrainPct+'%'}"></div>
</div>
<div class="loss-display" v-if="mouseTrain.loss !== null">loss = {{ mouseTrain.loss.toFixed(4) }}</div>
</div>
<div v-if="mouseTrain.done" class="msg msg-success">
训练完成!点击分布:μ = {{ mouseTrain.mu.toFixed(1) }} msσ = {{ mouseTrain.sigma.toFixed(1) }} ms
</div>
<div v-if="mouseTrain.error" class="msg msg-error">{{ mouseTrain.error }}</div>
</div>
<!-- ════════ 滚轮训练 ════════ -->
<div v-show="subTab==='scroll'">
<div class="form-grid" style="max-width:240px;">
<div class="field">
<label>训练轮数</label>
<input type="number" v-model.number="scrollTrain.epochs" :disabled="scrollTrain.running" min="10" />
</div>
</div>
<button class="btn btn-primary" @click="startScrollTrain" :disabled="scrollTrain.running">
{{ scrollTrain.running ? '训练中…' : '开始训练' }}
</button>
<div class="progress-section" v-if="scrollTrain.running || scrollTrain.done">
<div class="progress-label">
<span>ScrollCVAE 滚轮模型</span>
<span>{{ scrollTrain.epoch }} / {{ scrollTrain.total }}</span>
</div>
<div class="progress-track">
<div class="progress-fill" :style="{width: scrollTrainPct+'%'}"></div>
</div>
<div class="loss-display" v-if="scrollTrain.loss !== null">loss = {{ scrollTrain.loss.toFixed(4) }}</div>
</div>
<div v-if="scrollTrain.done" class="msg msg-success">滚轮模型训练完成!</div>
<div v-if="scrollTrain.error" class="msg msg-error">{{ scrollTrain.error }}</div>
</div>
</div>
`
export const TrainView = {
template,
emits: ['status-changed'],
setup(props, { emit }) {
const { ref, reactive, computed } = Vue
const subTab = ref('mouse')
// ── 鼠标训练 ──
const mouseTrain = reactive({
epochs: 100, running: false, done: false,
epoch: 0, total: 0, loss: null, mu: 0, sigma: 0, error: '',
})
const mouseTrainPct = computed(() => {
if (!mouseTrain.total) return 0
return Math.round(mouseTrain.epoch / mouseTrain.total * 100)
})
function startMouseTrain() {
mouseTrain.running = true; mouseTrain.done = false; mouseTrain.error = ''
mouseTrain.epoch = 0; mouseTrain.total = mouseTrain.epochs; mouseTrain.loss = null
fetchSSE('/api/train', { epochs: mouseTrain.epochs }, (msg) => {
if (msg.error) { mouseTrain.error = msg.error; mouseTrain.running = false; return }
if (msg.done) {
mouseTrain.mu = msg.mu || 0; mouseTrain.sigma = msg.sigma || 0
mouseTrain.running = false; mouseTrain.done = true; emit('status-changed'); return
}
mouseTrain.epoch = msg.epoch; mouseTrain.total = msg.total; mouseTrain.loss = msg.loss
})
}
// ── 滚轮训练 ──
const scrollTrain = reactive({
epochs: 100, running: false, done: false,
epoch: 0, total: 0, loss: null, error: '',
})
const scrollTrainPct = computed(() => {
if (!scrollTrain.total) return 0
return Math.round(scrollTrain.epoch / scrollTrain.total * 100)
})
function startScrollTrain() {
scrollTrain.running = true; scrollTrain.done = false; scrollTrain.error = ''
scrollTrain.epoch = 0; scrollTrain.total = scrollTrain.epochs; scrollTrain.loss = null
fetchSSE('/api/scroll/train', { epochs: scrollTrain.epochs }, (msg) => {
if (msg.error) { scrollTrain.error = msg.error; scrollTrain.running = false; return }
if (msg.done) { scrollTrain.running = false; scrollTrain.done = true; emit('status-changed'); return }
scrollTrain.epoch = msg.epoch; scrollTrain.total = msg.total; scrollTrain.loss = msg.loss
})
}
return {
subTab, mouseTrain, scrollTrain,
mouseTrainPct, scrollTrainPct,
startMouseTrain, startScrollTrain,
}
}
}