- Add .gitignore for Python/data/models - Add matplotlib>=3.8.0 for eval plots - Add PretrainConfig, FinetuneConfig, BalabitAdapterConfig, EvalConfig dataclasses
134 lines
5.1 KiB
JavaScript
134 lines
5.1 KiB
JavaScript
/**
|
||
* 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,
|
||
}
|
||
}
|
||
}
|