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' },
|
{ 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 }[] }[] = [
|
export const TEMPLATE_GROUPS: { label: string; options: { label: string; value: string }[] }[] = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
import { getModelList } from '@/api/modules/model'
|
import { getModelList } from '@/api/modules/model'
|
||||||
import { getDatasetList } from '@/api/modules/dataset'
|
import { getDatasetList } from '@/api/modules/dataset'
|
||||||
import { getSystemInfo } from '@/api/modules/system'
|
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'
|
import type { ModelItem, DatasetItem, GpuInfo } from '@/types'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -48,6 +48,13 @@ const form = reactive({
|
|||||||
lora_alpha: 16, // 修复原项目 lora_alpha 默认值不一致 bug
|
lora_alpha: 16, // 修复原项目 lora_alpha 默认值不一致 bug
|
||||||
lora_dropout: 0.1,
|
lora_dropout: 0.1,
|
||||||
lora_rank: 8,
|
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 = {
|
const rules: FormRules = {
|
||||||
@@ -95,6 +102,19 @@ const commandPreview = computed(() => {
|
|||||||
cmd += ` \\\n --lora_dropout ${form.lora_dropout}`
|
cmd += ` \\\n --lora_dropout ${form.lora_dropout}`
|
||||||
cmd += ` \\\n --lora_rank ${form.lora_rank}`
|
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
|
return cmd
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -132,6 +152,12 @@ function resetParams() {
|
|||||||
lora_alpha: 16,
|
lora_alpha: 16,
|
||||||
lora_dropout: 0.1,
|
lora_dropout: 0.1,
|
||||||
lora_rank: 8,
|
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_alpha: form.lora_alpha,
|
||||||
lora_dropout: form.lora_dropout,
|
lora_dropout: form.lora_dropout,
|
||||||
lora_rank: form.lora_rank,
|
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',
|
status: 'pending',
|
||||||
progress: 0,
|
progress: 0,
|
||||||
}
|
}
|
||||||
@@ -260,6 +292,12 @@ async function handleSubmit() {
|
|||||||
lora_alpha: form.lora_alpha,
|
lora_alpha: form.lora_alpha,
|
||||||
lora_dropout: form.lora_dropout,
|
lora_dropout: form.lora_dropout,
|
||||||
lora_rank: form.lora_rank,
|
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('训练任务已创建并启动')
|
ElMessage.success('训练任务已创建并启动')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -437,6 +475,74 @@ onMounted(() => {
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</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>
|
<el-divider content-position="left">训练命令预览</el-divider>
|
||||||
<div class="command-preview-wrapper">
|
<div class="command-preview-wrapper">
|
||||||
@@ -606,6 +712,13 @@ onMounted(() => {
|
|||||||
margin-bottom: 22px;
|
margin-bottom: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.field-tip {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 20px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.form-actions-wrapper {
|
.form-actions-wrapper {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user