feat: 调优创建支持模型量化配置

新增训练时量化(QLoRA 4/8bit)与训练后导出量化模型开关,支持 bitsandbytes、GPTQ、AWQ、GGUF 四种方法及对应位数、分组、导出格式配置,命令预览与提交参数同步接入,常量补充三类量化选项。
This commit is contained in:
caoxiaozhu
2026-07-13 10:30:52 +08:00
parent 08a567062d
commit 4dde761348
2 changed files with 137 additions and 1 deletions

View File

@@ -76,6 +76,29 @@ export const LR_SCHEDULER_OPTIONS = [
{ label: 'constant', value: 'constant' },
]
/** 训练时量化位数QLoRA */
export const QUANTIZATION_BIT_OPTIONS = [
{ label: '不量化', value: 0 },
{ label: '8 bit', value: 8 },
{ label: '4 bit (QLoRA)', value: 4 },
]
/** 训练后导出量化方法 */
export const QUANT_METHOD_OPTIONS = [
{ label: 'bitsandbytes', value: 'bnb' },
{ label: 'GPTQ', value: 'gptq' },
{ label: 'AWQ', value: 'awq' },
{ label: 'GGUF (llama.cpp)', value: 'gguf' },
]
/** GGUF 量化格式 */
export const GGUF_FORMAT_OPTIONS = [
{ label: 'Q4_K_M推荐', value: 'Q4_K_M' },
{ label: 'Q5_K_M', value: 'Q5_K_M' },
{ label: 'Q8_0', value: 'Q8_0' },
{ label: 'F16不量化', value: 'F16' },
]
/** 训练模板分组(按模型系列) */
export const TEMPLATE_GROUPS: { label: string; options: { label: string; value: string }[] }[] = [
{

View File

@@ -13,7 +13,7 @@ import {
import { getModelList } from '@/api/modules/model'
import { getDatasetList } from '@/api/modules/dataset'
import { getSystemInfo } from '@/api/modules/system'
import { TEMPLATE_GROUPS, LR_SCHEDULER_OPTIONS } from '@/constants'
import { TEMPLATE_GROUPS, LR_SCHEDULER_OPTIONS, QUANTIZATION_BIT_OPTIONS, QUANT_METHOD_OPTIONS, GGUF_FORMAT_OPTIONS } from '@/constants'
import type { ModelItem, DatasetItem, GpuInfo } from '@/types'
const router = useRouter()
@@ -48,6 +48,13 @@ const form = reactive({
lora_alpha: 16, // 修复原项目 lora_alpha 默认值不一致 bug
lora_dropout: 0.1,
lora_rank: 8,
// 量化参数
quantization_bit: 0, // 训练时量化QLoRA0=不量化
export_quantized: false, // 训练后是否导出量化模型
quant_method: 'bnb', // 导出量化方法
quant_bits: 4, // 导出量化位数
quant_group_size: 128, // 分组大小GPTQ/AWQ
export_format: 'Q4_K_M', // GGUF 导出格式
})
const rules: FormRules = {
@@ -95,6 +102,19 @@ const commandPreview = computed(() => {
cmd += ` \\\n --lora_dropout ${form.lora_dropout}`
cmd += ` \\\n --lora_rank ${form.lora_rank}`
}
if (showLoraParams.value && form.quantization_bit) {
cmd += ` \\\n --quantization_bit ${form.quantization_bit}`
}
if (form.export_quantized) {
const method = form.quant_method
const bits = form.quant_bits
cmd += ` \\\n # 训练后导出量化模型:${method} ${bits}bit`
if (method === 'gguf') {
cmd += ` \\\n # export_format=${form.export_format}`
} else if (method === 'gptq' || method === 'awq') {
cmd += ` \\\n # group_size=${form.quant_group_size}`
}
}
return cmd
})
@@ -132,6 +152,12 @@ function resetParams() {
lora_alpha: 16,
lora_dropout: 0.1,
lora_rank: 8,
quantization_bit: 0,
export_quantized: false,
quant_method: 'bnb',
quant_bits: 4,
quant_group_size: 128,
export_format: 'Q4_K_M',
})
}
@@ -230,6 +256,12 @@ async function handleSubmit() {
lora_alpha: form.lora_alpha,
lora_dropout: form.lora_dropout,
lora_rank: form.lora_rank,
quantization_bit: form.train_method === 'lora' ? form.quantization_bit : 0,
export_quantized: form.export_quantized,
quant_method: form.export_quantized ? form.quant_method : '',
quant_bits: form.export_quantized ? form.quant_bits : 0,
quant_group_size: form.export_quantized ? form.quant_group_size : 0,
export_format: form.export_quantized && form.quant_method === 'gguf' ? form.export_format : '',
status: 'pending',
progress: 0,
}
@@ -260,6 +292,12 @@ async function handleSubmit() {
lora_alpha: form.lora_alpha,
lora_dropout: form.lora_dropout,
lora_rank: form.lora_rank,
quantization_bit: form.train_method === 'lora' ? form.quantization_bit : 0,
export_quantized: form.export_quantized,
quant_method: form.export_quantized ? form.quant_method : '',
quant_bits: form.export_quantized ? form.quant_bits : 0,
quant_group_size: form.export_quantized ? form.quant_group_size : 0,
export_format: form.export_quantized && form.quant_method === 'gguf' ? form.export_format : '',
})
ElMessage.success('训练任务已创建并启动')
} catch (e) {
@@ -437,6 +475,74 @@ onMounted(() => {
</el-form-item>
</template>
<!-- 模型量化 -->
<el-divider content-position="left">模型量化</el-divider>
<!-- 训练时量化QLoRA LoRA 训练时可用 -->
<el-form-item v-if="form.train_method === 'lora'" label="训练时量化">
<el-select v-model="form.quantization_bit" style="width: 420px;">
<el-option
v-for="opt in QUANTIZATION_BIT_OPTIONS"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
</el-form-item>
<!-- 训练后导出量化模型 -->
<el-form-item label="导出量化模型">
<el-select v-model="form.export_quantized" style="width: 420px;">
<el-option label="否" :value="false" />
<el-option label="是" :value="true" />
</el-select>
</el-form-item>
<template v-if="form.export_quantized">
<el-form-item label="量化方法">
<el-select v-model="form.quant_method" style="width: 420px;">
<el-option
v-for="opt in QUANT_METHOD_OPTIONS"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
</el-form-item>
<el-form-item label="量化位数">
<el-input-number
v-model="form.quant_bits"
:min="form.quant_method === 'bnb' ? 4 : 2"
:max="form.quant_method === 'bnb' ? 8 : 16"
:step="1"
controls-position="right"
style="width: 200px"
/>
</el-form-item>
<el-form-item v-if="form.quant_method === 'gptq' || form.quant_method === 'awq'" label="分组大小">
<el-input-number
v-model="form.quant_group_size"
:min="32"
:max="1024"
:step="32"
controls-position="right"
style="width: 200px"
/>
</el-form-item>
<el-form-item v-if="form.quant_method === 'gguf'" label="导出格式">
<el-select v-model="form.export_format" style="width: 420px;">
<el-option
v-for="opt in GGUF_FORMAT_OPTIONS"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
</el-form-item>
</template>
<!-- 训练命令预览 -->
<el-divider content-position="left">训练命令预览</el-divider>
<div class="command-preview-wrapper">
@@ -606,6 +712,13 @@ onMounted(() => {
margin-bottom: 22px;
}
.field-tip {
color: #909399;
font-size: 12px;
line-height: 20px;
margin-top: 4px;
}
.form-actions-wrapper {
position: fixed;
bottom: 0;