模型调优简介

更新时间:
复制 MD 格式

当您在尝试如 Prompt 工程、插件调用等优化方法后,模型表现仍然不及预期时,请使用阿里云百炼的模型调优。模型调优作为改进模型表现的核心策略,可以很好地提升模型在特定行业/业务的表现,对齐人类偏好,降低输出延迟。模型调优包含模型微调(SFT)、继续预训练(CPT)、模型偏好训练(DPO)三种模型训练方式。

重要

本文档仅适用于华北2(北京)地域。

模型调优介绍

模型调优作为重要的模型效果优化方式,可以:

  • 提升模型在特定行业/业务表现

  • 降低模型输出延迟

  • 抑制模型幻觉

  • 对齐人类的价值观或偏好

  • 使用调优后的轻量级模型替代规模更大的模型

模型在调优过程中,会学习训练数据中的知识、语气、表达习惯、自我认知等业务/场景特征。也由于已经在训练过程中学习到了大量特定行业/场景的样例,训练后模型 One-Shot 或者 Zero-Shot 的 Prompt 效果会比训练前 Few-Shot 效果更好,这样可以节省大量输入 token,从而降低模型输出延迟。

模型调优流程

image

详情参见:

支持的模型

支持的模型

文本生成

模型服务

模型代码

CPT全参训练(cpt)

SFT全参训练(sft)

SFT高效训练(efficient_sft)

DPO全参训练(dpo_full)

DPO高效训练(dpo_lora)

Qwen3.6-Flash-2026-04-16

qwen3.6-flash-2026-04-16

×

支持

×

×

×

Qwen3.5-27B

qwen3.5-27b

×

支持

支持

×

×

Qwen3.5-9B

qwen3.5-9b

×

支持

支持

×

×

Qwen3.5-Flash-2026-02-23

qwen3.5-flash-2026-02-23

×

支持

×

×

×

Qwen3-32B

qwen3-32b

支持

支持

支持

支持

支持

Qwen3-30B-A3B-Instruct-2507

qwen3-30b-a3b-instruct-2507

支持

支持

支持

×

×

Qwen3-14B

qwen3-14b

×

支持

支持

支持

支持

Qwen3-8B

qwen3-8b

×

支持

支持

支持

支持

Qwen3-4B-Instruct-2507

qwen3-4b-instruct-2507

支持

支持

支持

支持

支持

Qwen3-1.7B

qwen3-1.7b

支持

支持

支持

支持

支持

Qwen3-0.6B

qwen3-0.6b

支持

支持

支持

支持

支持

Qwen2.5-72B-Instruct

qwen2.5-72b-instruct

支持

支持

支持

支持

支持

Qwen2.5-32B-Instruct

qwen2.5-32b-instruct

支持

支持

支持

支持

支持

Qwen2.5-14B-Instruct

qwen2.5-14b-instruct

支持

支持

支持

支持

支持

Qwen2.5-7B-Instruct

qwen2.5-7b-instruct

支持

支持

支持

支持

支持

千问-Plus-Character-2025-11-06

qwen-plus-character-2025-11-06

×

支持

支持

支持

支持

-Base表示该模型只完成了预训练,虽然模型内已经存储了海量的知识,但无法正常进行对话。

视觉理解(千问VL)

模型服务

模型代码

CPT全参训练(cpt)

SFT全参训练(sft)

SFT高效训练(efficient_sft)

DPO全参训练(dpo_full)

DPO高效训练(dpo_lora)

Qwen3-VL-8B-Instruct

qwen3-vl-8b-instruct

×

支持

支持

×

×

Qwen3-VL-8B-Thinking

qwen3-vl-8b-thinking

×

支持

支持

×

×

Qwen3-VL-4B-Instruct

qwen3-vl-4b-instruct

×

支持

支持

×

×

Qwen2.5-VL-72B-Instruct

qwen2.5-vl-72b-instruct

×

支持

支持

×

×

Qwen2.5-VL-32B-Instruct

qwen2.5-vl-32b-instruct

×

支持

支持

×

×

Qwen2.5-VL-7B-Instruct

qwen2.5-vl-7b-instruct

×

支持

支持

×

×

-Base表示该模型只完成了预训练,虽然模型内已经存储了海量的知识,但无法正常进行对话。

调优方法对比

特性

CPT(持续预训练)

SFT (监督微调)

DPO (直接偏好优化)

一句话总结

补知识注入领域知识

学做事学会遵循指令

做得更好对齐人类偏好

输入数据

1000万+ Token

无标签的领域文本

1000+ 条

高质量的“问-答”对

100+ 组

同一指令下的“更好-更差”回答对

核心目标

领域适应,学习专业词汇和事实

教会模型对话格式和任务执行能力

使模型输出更符合人类价值观和偏好

学习方式

自监督学习(预测下一个词

监督学习模仿标准答案

直接偏好学习增大好答案概率,降低坏答案概率

模型阶段

通常在 SFT 之前

CPT 之后,DPO 之前

通常在 SFT 之后,作为对齐的最后一步

训练模式对比

全参训练

高效训练 (LoRA,推荐)

适用场景

• 需要模型学习新能力

• 追求全局效果最优

• 优化模型特定场景下的效果

• 对训练时间和成本敏感的场景

训练时间

较长,收敛速度较慢。

较短,收敛速度快。

计费说明

计费方式

按训练的数据量计费

计费公式

模型训练费用 = (训练数据 Token 总数 + 混合训练数据 Token 总数)× 循环次数 × 训练单价(最小计费单位:1 token)

您可以查看模型调优控制台底部的预估训练费用,并单击计算详情,查看训练 Token 总数、循环次数和训练单价

训练单价

以下为预置模型的训练单价,自定义模型的训练单价与对应的预置模型单价相同。

千问

模型服务

模型代码

价格

Qwen3.6-Flash-2026-04-16

qwen3.6-flash-2026-04-16

¥0.05/千Token

Qwen3.5-27B

qwen3.5-27b

¥0.05/千Token

Qwen3.5-9B

qwen3.5-9b

¥0.02/千Token

Qwen3.5-Flash-2026-02-23

qwen3.5-flash-2026-02-23

¥0.05/千Token

Qwen3-32B

qwen3-32b

¥0.04/千Token

Qwen3-30B-A3B-Instruct-2507

qwen3-30b-a3b-instruct-2507

¥0.03/千Token

Qwen3-14B

qwen3-14b

¥0.03/千Token

Qwen3-8B

qwen3-8b

¥0.006/千Token

Qwen3-4B-Instruct-2507

qwen3-4b-instruct-2507

¥0.006/千Token

Qwen3-1.7B

qwen3-1.7b

¥0.0045/千Token

Qwen3-0.6B

qwen3-0.6b

¥0.003/千Token

Qwen2.5-72B-Instruct

qwen2.5-72b-instruct

¥0.15/千Token

Qwen2.5-32B-Instruct

qwen2.5-32b-instruct

¥0.03/千Token

Qwen2.5-14B-Instruct

qwen2.5-14b-instruct

¥0.03/千Token

Qwen2.5-7B-Instruct

qwen2.5-7b-instruct

¥0.006/千Token

千问-Plus-Character-2025-11-06

qwen-plus-character-2025-11-06

¥0.15/千Token

千问VL

模型服务

模型代码

价格

Qwen3-VL-8B-Instruct

qwen3-vl-8b-instruct

¥0.012/千Token

Qwen3-VL-8B-Thinking

qwen3-vl-8b-thinking

¥0.012/千Token

Qwen3-VL-4B-Instruct

qwen3-vl-4b-instruct

¥0.006/千Token

Qwen2.5-VL-72B-Instruct

qwen2.5-vl-72b-instruct

¥0.05/千Token

Qwen2.5-VL-32B-Instruct

qwen2.5-vl-32b-instruct

¥0.02/千Token

Qwen2.5-VL-7B-Instruct

qwen2.5-vl-7b-instruct

¥0.01/千Token

计算图像与视频的Token

图像

计算公式:图像 Token = h_bar * w_bar / token_pixels + 2

  • h_bar、w_bar:缩放后的图像长宽,模型在处理图像前会进行预处理,会将图像缩小至特定像素上限内,像素上限与max_pixelsvl_high_resolution_images参数的取值有关,相关章节:处理高分辨率图像

  • token_pixels:每视觉Token对应的像素值,不同模型情况不同:

    • qwen3.7系列qwen3.6系列qwen3.5系列Qwen3-VLqwen-vl-maxqwen-vl-plus每个Token对应 32x32像素

    • QVQ及其他Qwen2.5-VL模型每个Token对应28x28像素

以下代码演示了模型内部对图像的大致缩放逻辑,可用于估算一张图像的Token,实际计费请以API响应为准。

import math
from PIL import Image  # pip install Pillow

def smart_size(image_path, max_pixels, vl_high_resolution_images):
    """根据模型参数,计算图像缩放后的尺寸,用于估算图像 Token。"""
    image = Image.open(image_path)
    height, width = image.height, image.width

    # Qwen3.6、Qwen3.5、Qwen3-VL 等模型的缩放因子为 32;其他模型为 28
    factor = 32
    h_bar = round(height / factor) * factor
    w_bar = round(width / factor) * factor

    # Token 下限:4 个 Token
    min_pixels = 4 * factor * factor

    # vl_high_resolution_images=True 时,Token 上限固定为 16384,忽略 max_pixels
    if vl_high_resolution_images:
        max_pixels = 16384 * factor * factor

    # 将总像素数约束在 [min_pixels, max_pixels] 范围内
    if h_bar * w_bar > max_pixels:
        beta = math.sqrt((height * width) / max_pixels)
        h_bar = math.floor(height / beta / factor) * factor
        w_bar = math.floor(width / beta / factor) * factor
    elif h_bar * w_bar < min_pixels:
        beta = math.sqrt(min_pixels / (height * width))
        h_bar = math.ceil(height * beta / factor) * factor
        w_bar = math.ceil(width * beta / factor) * factor

    return h_bar, w_bar

if __name__ == "__main__":
    # 注意:max_pixels 和 vl_high_resolution_images 的值需要与调用模型时传入的参数保持一致
    h_bar, w_bar = smart_size("xxx/test.jpg", max_pixels=2560 * 32 * 32, vl_high_resolution_images=False)
    print(f"缩放后的图像尺寸:高度 {h_bar},宽度 {w_bar}")

    # 每张图像额外包含 <vision_bos> 和 <vision_eos> 各 1 个 Token
    token = int(h_bar * w_bar / (32 * 32)) + 2
    print(f"图像的 Token 数:{token}")
视频
  • 视频文件:

    模型处理视频文件时,会先进行抽帧,然后计算所有视频帧的总 Token 数。由于该计算过程较为复杂,可使用以下代码,通过传入视频路径来估算视频消耗的总 Token 数:

    # 使用前安装:pip install opencv-python
    import math
    import os
    import logging
    import cv2
    
    logger = logging.getLogger(__name__)
    
    FRAME_FACTOR = 2
    
    # Qwen3.6、Qwen3.5、Qwen3-VL、qwen-vl-max-0813、qwen-vl-plus-0815、qwen-vl-plus-0710等模型,图像缩放因子为32
    IMAGE_FACTOR = 32
    
    #  其他模型,图像缩放因子为28
    # IMAGE_FACTOR = 28
    
    # 视频帧的最大长宽比
    MAX_RATIO = 200
    # 视频帧的像素下限
    VIDEO_MIN_PIXELS = 4 * 32 * 32
    # 视频帧的像素上限,使用Qwen3-VL-Plus模型,VIDEO_MAX_PIXELS640 * 32 * 32,其他模型为768 * 32 * 32
    VIDEO_MAX_PIXELS = 640 * 32 * 32
    
    # 用户未传入FPS参数,则fps使用默认值
    FPS = 2.0
    # 最少抽取帧数
    FPS_MIN_FRAMES = 4
    # 最大抽取帧数(根据模型选择设置值)
    FPS_MAX_FRAMES = 2000
    
    # 视频输入的最大像素值,使用Qwen3-VL-Plus模型,请将VIDEO_TOTAL_PIXELS设置为131072 * 32 * 32,其他模型设置为65536 * 32 * 32
    VIDEO_TOTAL_PIXELS = int(float(os.environ.get('VIDEO_MAX_PIXELS', 131072 * 32 * 32)))
    
    def round_by_factor(number: int, factor: int) -> int:
        """返回与”number“最接近的整数,该整数可被”factor“整除。"""
        return round(number / factor) * factor
    
    def ceil_by_factor(number: int, factor: int) -> int:
        """返回大于或等于“number”且可被“factor”整除的最小整数。"""
        return math.ceil(number / factor) * factor
    
    def floor_by_factor(number: int, factor: int) -> int:
        """返回小于或等于“number”且可被“factor”整除的最大整数。"""
        return math.floor(number / factor) * factor
    
    def extract_vision_info(conversations):
        vision_infos = []
        if isinstance(conversations[0], dict):
            conversations = [conversations]
        for conversation in conversations:
            for message in conversation:
                if isinstance(message["content"], list):
                    for ele in message["content"]:
                        if (
                            "image" in ele
                            or "image_url" in ele
                            or "video" in ele
                            or ele.get("type","") in ("image", "image_url", "video")
                        ):
                            vision_infos.append(ele)
        return vision_infos
    
    def smart_nframes(ele,total_frames,video_fps):
        """用于计算抽取的视频帧数。
    
        Args:
            ele (dict): 包含视频配置的字典格式
                - fps: fps用于控制提取模型输入帧的数量。
            total_frames (int): 视频的原始总帧数。
            video_fps (int | float): 视频的原始帧率
    
        Raises:
            nframes应该在[FRAME_FACTOR,total_frames]间隔内,否则会报错
    
        Returns:
            用于模型输入的视频帧数。
        """
        assert not ("fps" in ele and "nframes" in ele), "Only accept either `fps` or `nframes`"
        fps = ele.get("fps", FPS)
        min_frames = ceil_by_factor(ele.get("min_frames", FPS_MIN_FRAMES), FRAME_FACTOR)
        max_frames = floor_by_factor(ele.get("max_frames", min(FPS_MAX_FRAMES, total_frames)), FRAME_FACTOR)
        duration = total_frames / video_fps if video_fps != 0 else 0
        if duration-int(duration)>(1/fps):
            total_frames = math.ceil(duration * video_fps)
        else:
            total_frames = math.ceil(int(duration)*video_fps)
        nframes = total_frames / video_fps * fps
        if nframes > total_frames:
            logger.warning(f"smart_nframes: nframes[{nframes}] > total_frames[{total_frames}]")
        nframes = int(min(min(max(nframes, min_frames), max_frames), total_frames))
        if not (FRAME_FACTOR <= nframes and nframes <= total_frames):
            raise ValueError(f"nframes should in interval [{FRAME_FACTOR}, {total_frames}], but got {nframes}.")
    
        return nframes
    
    def get_video(video_path):
        # 获取视频信息
        cap = cv2.VideoCapture(video_path)
    
        frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        # 获取视频高度
        frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    
        video_fps = cap.get(cv2.CAP_PROP_FPS)
        return frame_height, frame_width, total_frames, video_fps
    
    def smart_resize(ele, path, factor=IMAGE_FACTOR):
        # 获取原视频的宽和高
        height, width, total_frames, video_fps = get_video(path)
        # 视频帧的Token下限
        min_pixels = VIDEO_MIN_PIXELS
        total_pixels = VIDEO_TOTAL_PIXELS
        # 抽取的视频帧数
        nframes = smart_nframes(ele, total_frames, video_fps)
        max_pixels = max(min(VIDEO_MAX_PIXELS, total_pixels / nframes * FRAME_FACTOR),int(min_pixels * 1.05))
    
        # 视频的长宽比不应超过200:11:200
        if max(height, width) / min(height, width) > MAX_RATIO:
            raise ValueError(
                f"absolute aspect ratio must be smaller than {MAX_RATIO}, got {max(height, width) / min(height, width)}"
            )
    
        h_bar = max(factor, round_by_factor(height, factor))
        w_bar = max(factor, round_by_factor(width, factor))
        if h_bar * w_bar > max_pixels:
            beta = math.sqrt((height * width) / max_pixels)
            h_bar = floor_by_factor(height / beta, factor)
            w_bar = floor_by_factor(width / beta, factor)
        elif h_bar * w_bar < min_pixels:
            beta = math.sqrt(min_pixels / (height * width))
            h_bar = ceil_by_factor(height * beta, factor)
            w_bar = ceil_by_factor(width * beta, factor)
        return h_bar, w_bar
    
    def token_calculate(video_path, fps):
        # 传入视频路径和fps抽帧参数
        messages = [{"content": [{"video": video_path, "fps": fps}]}]
        vision_infos = extract_vision_info(messages)[0]
    
        resized_height, resized_width = smart_resize(vision_infos, video_path)
    
        height, width, total_frames, video_fps = get_video(video_path)
        num_frames = smart_nframes(vision_infos, total_frames, video_fps)
        print(f"原视频尺寸:{height}*{width}, 输入模型的尺寸:{resized_height}*{resized_width},视频总帧数:{total_frames},fps等于{fps}时,抽取的总帧数:{num_frames}", end=",")
        video_token = int(math.ceil(num_frames / 2) * resized_height / 32 * resized_width / 32)
        video_token += 2   # 系统会自动添加<|vision_bos|>和<|vision_eos|>视觉标记(各计1Token)
        return video_token
    
    video_token = token_calculate("xxx/test.mp4", 1)
    print("视频tokens:", video_token)
  • 图像列表:

    当以图像列表形式传入视频时,表示已预先完成视频抽帧,可使用以下代码,通过传入图像的路径和数量来计算传入图像列表时消耗的Token数:

    # 使用前安装:pip install Pillow
    import math
    import os
    import logging
    from typing import Tuple
    from PIL import Image
    
    logger = logging.getLogger(__name__)
    
    # ==================== 常量定义 ====================
    FRAME_FACTOR = 2
    # Qwen3-VL、qwen-vl-max-0813、qwen-vl-plus-0815、qwen-vl-plus-0710模型,缩放因子为32
    IMAGE_FACTOR = 32
    
    #  其他模型,缩放因子为28
    # IMAGE_FACTOR = 28
    
    # Token计算相关常量
    TOKEN_DIVISOR = 32  # token计算时的除数
    VISION_SPECIAL_TOKENS = 2  # <|vision_bos|>和<|vision_eos|>标记
    
    # 视频帧的最大长宽比
    MAX_RATIO = 200
    # 视频帧的像素下限
    VIDEO_MIN_PIXELS = 4 * 32 * 32
    # 视频帧的像素上限,使用Qwen3-VL-Plus模型,VIDEO_MAX_PIXELS640 * 32 * 32,其他模型为768 * 32 * 32
    VIDEO_MAX_PIXELS = 640 * 32 * 32
    
    # 视频输入的最大像素值,使用Qwen3-VL-Plus模型,请将VIDEO_TOTAL_PIXELS设置为131072 * 32 * 32,其他模型设置为65536 * 32 * 32
    VIDEO_TOTAL_PIXELS = int(float(os.environ.get('VIDEO_MAX_PIXELS', 131072 * 32 * 32)))
    
    def round_by_factor(number: int, factor: int) -> int:
        """返回与”number“最接近的整数,该整数可被”factor“整除。"""
        return round(number / factor) * factor
    
    def ceil_by_factor(number: int, factor: int) -> int:
        """返回大于或等于“number”且可被“factor”整除的最小整数。"""
        return math.ceil(number / factor) * factor
    
    def floor_by_factor(number: int, factor: int) -> int:
        """返回小于或等于“number”且可被“factor”整除的最大整数。"""
        return math.floor(number / factor) * factor
    
    def get_image_size(image_path: str) -> Tuple[int, int]:
        if not os.path.exists(image_path):
            raise FileNotFoundError(f"图像文件不存在: {image_path}")
    
        try:
            image = Image.open(image_path)
            height = image.height
            width = image.width
            image.close()  # 及时关闭文件
            return height, width
        except Exception as e:
            raise ValueError(f"无法读取图像文件 {image_path}: {str(e)}")
    
    def smart_resize(height: int, width: int, nframes: int, factor: int = IMAGE_FACTOR) -> Tuple[int, int]:
        """
        计算图像缩放后的尺寸
    
        Args:
            height: 原始图像高度
            width: 原始图像宽度
            nframes: 视频帧数
            factor: 缩放因子,默认为IMAGE_FACTOR
    
        Returns:
            (resized_height, resized_width) 缩放后的高度和宽度
    
        Raises:
            ValueError: 长宽比超过限制
        """
        # 视频帧的Token下限
        min_pixels = VIDEO_MIN_PIXELS
        total_pixels = VIDEO_TOTAL_PIXELS
        # 抽取的视频帧数
        max_pixels = max(min(VIDEO_MAX_PIXELS, total_pixels / nframes * FRAME_FACTOR), int(min_pixels * 1.05))
    
        # 视频的长宽比不应超过200:11:200
        aspect_ratio = max(height, width) / min(height, width)
        if aspect_ratio > MAX_RATIO:
            raise ValueError(
                f"图像长宽比必须小于 {MAX_RATIO}:1,当前为 {aspect_ratio:.2f}:1"
            )
    
        h_bar = max(factor, round_by_factor(height, factor))
        w_bar = max(factor, round_by_factor(width, factor))
        if h_bar * w_bar > max_pixels:
            beta = math.sqrt((height * width) / max_pixels)
            h_bar = floor_by_factor(height / beta, factor)
            w_bar = floor_by_factor(width / beta, factor)
        elif h_bar * w_bar < min_pixels:
            beta = math.sqrt(min_pixels / (height * width))
            h_bar = ceil_by_factor(height * beta, factor)
            w_bar = ceil_by_factor(width * beta, factor)
        return h_bar, w_bar
    
    def calculate_video_tokens(image_path: str, nframes: int = 1, factor: int = IMAGE_FACTOR, verbose: bool = True) -> int:
        """
    
        Args:
            image_path: 视频帧文件路径
            nframes: 视频帧数,
            factor: 缩放因子,默认为IMAGE_FACTOR
            verbose: 是否打印详细信息
    
        Returns:
            所消耗的token数量
    
        Raises:
            FileNotFoundError: 文件不存在
            ValueError: 文件格式无效或长宽比超限
        """
        # 获取原始图像尺寸(只读取一次)
        height, width = get_image_size(image_path)
    
        # 计算缩放后的尺寸
        resized_height, resized_width = smart_resize(height, width, nframes, factor)
    
        # 计算token数量
        # 公式:⌈帧数/2⌉ × (高度/TOKEN_DIVISOR) × (宽度/TOKEN_DIVISOR) + VISION_SPECIAL_TOKENS
        video_token = int(
            math.ceil(nframes / 2) *
            (resized_height / TOKEN_DIVISOR) *
            (resized_width / TOKEN_DIVISOR)
        )
        # 添加视觉标记token(<|vision_bos|>和<|vision_eos|>)
        video_token += VISION_SPECIAL_TOKENS
    
        if verbose:
            print(f"原视频帧尺寸:{height}×{width},输入模型的尺寸:{resized_height}×{resized_width},", end="")
    
        return video_token
    
    if __name__ == "__main__":
        try:
            video_token = calculate_video_tokens("xxx/test.jpg", nframes=30)
            print(f"视频tokens: {video_token}\n")
        except Exception as e:
            print(f"错误: {str(e)}\n")

模型调优前必读

  • 文本生成模型调优虽然能在特定业务/场景取得非常好的效果,但有以下限制:

    • 耗时较长,包括:拥有一个大规模(最少 0.5亿 token)CPT 数据集、构建一个有效(1000+)SFT 数据集、收集足够的(100+)Bad Case 构建模型部署计费有效 DPO 数据集、模型优化迭代速度慢等。

    • 费用较高,调优后的模型部署后才能使用,模型部署计费较高。

  • 阿里云百炼推荐您在考虑使用文本生成模型调优前先尝试使用Prompt 工程Prompt Engineering)或插件调用Function Calling定制化您的应用,模型调优也通常作为改进模型表现“最后的手段”。因为:

    1. 在许多任务中,模型最初可能表现不佳,但通过应用正确的 Prompt 技巧可以改进结果,不一定需要使用模型调优。

    2. 迭代优化 Prompt、插件,比模型调优的迭代更敏捷、成本更低,因为模型调优的迭代可能需要重新收集数据、清洗优化数据、收集 bad case、发起客户调研等。

    3. 即使最后一定要进行模型调优,最初的 Prompt 工程、插件迭代优化相关工作也不会浪费。您的这些前期工作可以充分地在构建调优数据集时复用(用于构建数据集的输入)。

调优效果展示

角色扮演

gungun

目标检测

converted-1761653885465

快速开始

使用控制台进行模型调优

调优步骤

控制台截图

步骤一:在模型调优页面点击创建训练任务

image

步骤二:训练配置

  • 训练方式SFT微调训练

  • 选择模型: 千问3-8B

  • 训练方式高效训练

  • 参数配置:保持默认即可,百炼对微调超参提供了推荐配置。

这个组合训练时间短,数据要求低。

步骤三:数据配置

  • 训练集: 在平台上选择构建模型所需的已上传调优数据集。

    数据样例:SFT-ChatML格式示例.jsonl

  • 混合训练: 不开启

  • 验证集:设置为自动切分,分割 10% 作为验证集

image

步骤四:配置模型参数快照(Checkpoint)保存参数

  • 模型名称:保持默认即可

  • 导出数量上限:保持默认即可

  • Checkpoint保存间隔:保持默认即可

说明

在百炼平台上,模型调优完成后可以导出参数快照,导出后才能基于此版本的参数快照在百炼上进行模型部署。

导出的参数快照保存在云存储中,暂不支持访问或下载。

image

步骤五:点击“开始训练”后,等待模型训练完毕。

步骤六:使用阿里云百炼的模型部署功能部署训练好的自定义模型,部署好后就可以对调优好的模型进行评测。模型部署相关信息请参见模型部署

步骤七:使用阿里云百炼模型评测功能评估自定义模型的训练效果,相关信息请参见模型评测

典型的调优流程

百炼提供的三种调优方式并不互斥,而是递进的、相辅相成的。

CPT(可选)→ SFT → DPO(可选)

  1. CPT (持续预训练)- 补知识 (通用模型知识的“广度”和“浅度”,无法满足专业领域的“深度”和“精度”要求)

    • 金融模型: 学金融术语

    • 医疗模型: 记药品病理

    • 法律模型: 懂法条判例

  2. SFT (监督微调)- 学做事

    • 客服机器人: 学客服流程

    • 代码助手: 学编程范式

    • 工具调用 (Agent): 学使用 MCP

  3. DPO (直接偏好优化)- 做得更好

    • 安全与责任感: 拒有害建议

    • 简洁与有效性: 答干脆利落

    • 客观与中立: 评公正客观

调优数据格式

SFT 训练集

SFT ChatML(Chat Markup Language)格式训练数据,支持多轮对话和多种角色设置。

不支持OpenAI 的nameweight参数,所有的 assistant 输出都会被训练。
# 一行训练数据(json 格式),展开后典型结构如下:
{"messages": [
  {"role": "system", "content": "系统输入1"}, 
  {"role": "user", "content": "用户输入1"}, 
  {"role": "assistant", "content": "期望的模型输出1"}, 
  {"role": "user", "content": "用户输入2"}, 
  {"role": "assistant", "content": "期望的模型输出2"}
  ...
]}

system/user/assistant 区别请参见概述,训练数据集样例:SFT-ChatML格式示例.jsonlSFT-ChatML格式示例.xlsx(xls、xlsx 格式只支持单轮对话)。

单条训练数据的所有 assistant 行都支持"loss_weight"参数,用于设置该行在训练时的相对重要性。(设置范围0.0 ~ 1.0,数值越大,重要性越高)

该参数属于邀测参数,如需使用,请联系您的商务经理。
 {"role": "assistant", "content": "期望的模型输出1", "loss_weight": 1.0}, 
 {"role": "assistant", "content": "期望的模型输出2", "loss_weight": 0.5}

SFT 思考模型(thinking)

训练数据支持多轮对话和多种角色设置,但只能针对最后的 assistant 输出进行训练,一行训练数据展开后结构如下

思考标签前后的若干个\n必须要保留。
# 一行训练数据(json 格式),展开后典型结构如下:
{"messages": [
  {"role": "system", "content": "系统输入1"}, 
  {"role": "user", "content": "用户输入1"}, 
  {"role": "assistant", "content": "模型输出1"}, --中间的 assistant 输出不应添加 <think> 标签
   ...
  {"role": "user", "content": "用户输入2"}, 
  {"role": "assistant", "content": "<think>\n期望的思考内容2\n</think>\n\n期望的输出2"} --思考内容只能包含在最后一个 assistant 输出中。 
]}

system/user/assistant 区别请参见概述,训练数据集样例:SFT- 深度思考内容示例.jsonl

也可以在训练样本中设置模型不输出<think>标签, 如果使用这种输出方式,模型训练完成后不建议再开启思考模式进行调用

{"role": "assistant", "content": "期望的模型输出2"}  --告诉模型不开启思考

单条训练数据最后的 assistant 行支持"loss_weight"参数,用于设置该条数据在训练时的相对重要性。(设置范围0.0 ~ 1.0,数值越大,重要性越高)

该参数属于邀测参数,如需使用,请联系您的商务经理。
 {"role": "assistant", "content": "<think>\n期望的思考内容2\n</think>\n\n期望的输出2", "loss_weight": 1.0}

SFT 视觉理解(千问VL)

不支持OpenAI 的nameweight参数,所有的 assistant 输出都会被训练。

system/user/assistant 区别请参见概述。ChatML 格式训练数据样例:

如需传入 system 消息,对应的 content 必须使用数组格式 [{"text":"..."}],不能使用字符串格式 "content":"字符串"
说明

如果训练思考模型(Thinking),也需要遵循SFT 思考模型(thinking)的数据格式要求。

# 一行训练数据(json 格式),展开后典型结构如下:
{"messages": [
  {"role": "system", "content": [{"text": "系统输入"}]},
  {"role": "user", "content": [{"text": "用户输入1"}, {"image": "图像文件名1.jpg", "resized_width": 200, "resized_height": 200}]},
  {"role": "assistant", "content": [{"text": "期望的模型输出1"}]},
  {"role": "user", "content": [{"text": "用户输入2"}, {"video": "视频文件名1.mp4", "fps": 3.0, "resized_width": 200, "resized_height": 200, "video_start": 0.0, "video_end": 3.0}]},
  {"role": "assistant", "content": [{"text": "期望的模型输出2"}]},
  {"role": "user", "content": [{"text": "用户输入2"}, {"video": ["0.jpg", "1.jpg", "2.jpg", "3.jpg"], "sample_fps": 5.0, "resized_width": 200, "resized_height": 200}]},
  {"role": "assistant", "content": [{"text": "期望的模型输出2"}]},
  ...
]}

点击此处查看更多支持的参数

字段

类型

必填

说明

图片文件

image

str

图片文件路径

resized_width

int

图片目标缩放宽度(像素)

resized_height

int

图片目标缩放高度(像素)

视频文件-视频文件路径模式(仅 qwen3.5 及以后的 VL 模型支持)

样例:阿里云VL_Video.zip

video

str 

视频文件路径模式:{"video": "视频文件名1.mp4"}

resized_width

int

视频目标缩放宽度(像素)

resized_height

int

视频目标缩放高度(像素)

fps

float

训练时的输入频率。如果设置fps=30,实际训练用的视频帧率为 30 帧。

video_start

float

视频截取起始时间(秒)

video_end

float

视频截取结束时间(秒)

视频文件-图片帧列表模式(仅 qwen3.5 及以后的 VL 模型支持)

video

List[str]

图片帧列表模式:{"video": ["0.jpg", "1.jpg", "2.jpg", ...], "sample_fps": 2.0}

sample_fps

float

用于告知图片帧的帧率。

resized_width

int

图片帧缩放宽度(像素)

resized_height

int

图片帧缩放高度(像素)

训练物体定位建议:

  • Qwen2.5-VL:训练的坐标相对于缩放后的图像左上角的绝对值,单位为像素。

  • Qwen3-VL:训练坐标为相对坐标,坐标值会缩放到[0, 999]范围内。

压缩包要求:

  1. 压缩包格式:ZIP。最大支持 2 GB, ZIP 包内文件夹、文件名仅支持 ASCII 字符集中的字母 (a-z, A-Z)、数字 (0-9)、下划线 (_)、连字符 (-)。

  2. 训练文本数据固定为 data.jsonl,并且位于压缩包的根目录下,应确保压缩后打开 zip 文件,直接就能看到 data.jsonl 文件。

  3. 图片单张尺寸的宽度和高度均不得超过 1024px,最大不超过10MB,支持 .bmp.jpeg /.jpg.png.tif /.tiff.webp 格式。

  4. 图片文件的名称不能重复,即使分布在不同的文件夹中。

  5. 压缩包目录结构:

    单层目录(推荐)

    图片文件与 data.jsonl 文件均位于压缩包根目录下。

    Trainingdata_vl.zip
       |--- data.jsonl #注意:外层不能再包裹文件夹
       |--- image1.png
       |--- video1.mp4

    多层目录

    1. data.jsonl 必须在压缩包根目录下。

    2. data.jsonl 内只需要声明图像/视频文件名,不需要声明文件路径。例如:

      正确示例image1.jpg错误示例jpg_folder/image1.jpg

    3. 图像/视频文件名应在压缩包内全局唯一。

    Trainingdata_vl.zip
        |--- data.jsonl #注意:外层不能再包裹文件夹
        |--- jpg_folder
        |   └── image1.jpg
        |--- mp4_folder
            └── video.mp4

DPO 数据集

DPO ChatML 格式训练数据,一行训练数据展开后结构如下

system/user/assistant 区别请参见概述,训练数据集样例:DPO ChatML格式样例.jsonl

# 一行训练数据(json 格式),展开后典型结构如下:
{"messages": [
  {"role": "system", "content": "系统输入"},
  {"role": "user", "content": "用户输入1"},
  {"role": "assistant", "content": "模型输出1"},
  {"role": "user", "content": "用户输入2"},
  {"role": "assistant", "content": "模型输出2"},
  {"role": "user", "content": "用户输入3"}
 ],
 "chosen":
   {"role": "assistant", "content": "赞同的模型期望输出3"},
 "rejected":
   {"role": "assistant", "content": "反对的模型期望输出3"}}

模型将 messages 内的所有内容均作为输入,DPO 用于训练模型对用户输入3的正负反馈。

针对深度思考的内容,需要使用<think>标签包裹:

{"role": "assistant", "content": "<think>期望的模型思考内容</think>期望的模型输出"}

单条训练数据的"chosen"模块支持"loss_weight"参数,用于设置该条训练数据在训练中的相对重要性。(设置范围0.0 ~ 1.0,数值越大,重要性越高)

该参数属于邀测参数,如需使用,请联系您的商务经理。
 "chosen":
   {"role": "assistant", "content": "赞同的模型期望输出3", "loss_weight": 1.0},

CPT 训练集

CPT 纯文本格式训练数据,一行训练数据展开后结构如下

{"text":"文本内容"}

训练数据集样例:CPT-文本生成训练集示例.jsonl

数据集构建技巧

数据集的规模要求

对于CPT来说,数据集最少需要五千万Token优质预训练数据;对于 SFT 来说,数据集最少需要上千条优质调优数据;对于 DPO 来说,数据集一般需要上百条人类偏好数据。如果数据调优后的模型评测结果不佳,最简单的改进方法是收集更多数据进行训练。

如果您缺乏数据,建议构建智能体应用,使用知识库索引来增强模型能力。当然在很多复杂的业务场景,可以综合采用模型调优和知识库检索结合的技术方案。

以客服场景为例,可以借助模型调优解决客服回答的语气、表达习惯、自我认知等问题,场景涉及的专业知识可以结合知识库,动态引入到模型上下文中。

阿里云百炼推荐您可以先构建 RAG 应用试运行,在收集到足够的应用数据后再通过模型调优继续提升模型表现。

您也可以采用以下策略扩充数据集:

  1. 让大模型模拟生成特定业务/场景的相关内容,辅助您生成更多用于调优数据。(生成模型建议选取表现优异、规模更大的模型)

  2. 使用阿里云百炼的数据处理功能,对您的数据集进行数据清洗、数据增强。

  3. 通过应用场景收集、网络爬虫、社交媒体和在线论坛、公开数据集、合作伙伴与行业资源、用户贡献等各种方式,人工获取更多数据。

数据的多样性与均衡性

模型调优有不同场景,针对具体业务场景时,专业性更重要;而针对问答场景时通用性更重要。您需要根据模型负责的业务模块或使用场景进行数据用例设计。因此训练效果好坏并不是仅取决于数据量,更需要考虑针对场景的专业性和多样性。

这里以智能 AI 对话场景为例,介绍一个专业、多样的数据集应该包含的各种业务场景:

具体业务

多样化场景/业务

电商客服

活动推送、售前咨询、售中引导、售后服务、售后回访、投诉处理等。

金融服务

贷款咨询、投资理财顾问、信用卡服务、银行账户管理等。

在线医疗

病症咨询、挂号预约、就诊须知、药品信息查询、健康小建议等。

AI 秘书

IT 信息、行政信息、HR 信息、员工福利解答、公司日历查询等。

旅游出行助手

旅行规划、出入境指南、旅行保险咨询、目的地风土人情介绍等。

企业法律顾问

合同审核、知识产权保护、合规性检查、劳动法律答疑、跨境交易咨询、个案法律分析等。

还请特别注意的是各个场景/业务的数据数量应相对均衡,数据比例符合实际场景比例,避免某一类数据过多导致模型偏向于学习该类特征,影响模型的泛化能力。

训练集与验证集拆分

当您使用控制台进行模型调优时,支持

  • 自动将一个完整训练数据集拆分,随机抽取少量数据组成验证集。

  • 选择独立上传数据集。

控制台可以在训练时及时方便地显示验证集 Loss 和 Token Accuracy。

image

常见问题

是否支持调优自己的模型呢?

百炼不支持调优自己的模型,但可以通过我的模型(北京地域)导入safetensor 格式的千问系列开源模型,详情请参考模型导入。模型导入后可以在百炼平台上部署使用。