Files
ai_mouse/static/js/train.js
Huang Qi 4d414fd180 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
2026-05-10 12:24:07 +08:00

134 lines
5.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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,
}
}
}