feat: 调优创建支持模型量化配置
新增训练时量化(QLoRA 4/8bit)与训练后导出量化模型开关,支持 bitsandbytes、GPTQ、AWQ、GGUF 四种方法及对应位数、分组、导出格式配置,命令预览与提交参数同步接入,常量补充三类量化选项。
This commit is contained in:
@@ -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 }[] }[] = [
|
||||
{
|
||||
|
||||
@@ -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, // 训练时量化(QLoRA):0=不量化
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user