函数调用是现代语言模型的一项强大特性,使其能够与外部工具、API和服务无缝交互。然而,确保你的AI助手能够以正确的参数进行正确的函数调用并非易事。本文将深入探讨如何通过数据合成生成高质量的训练数据,从而对小型语言模型(SLM)进行微调,以实现强大的函数调用能力。通过结合Azure Machine Learning (AML) pipeline,可以更高效地实现这一目标。

为什么选择微调SLM来实现函数调用?

在资源受限的边缘设备上部署AI能力,或者追求更低的运营成本、更高的隐私合规性时,微调SLM来实现函数调用功能是一个极具吸引力的选择。大型语言模型(LLM)虽然功能强大,但它们对计算资源和能耗的要求也相当高,这使得它们在智能手机、物联网设备等边缘设备上的部署变得困难。此外,将敏感数据发送到外部服务器进行处理,也存在潜在的隐私风险。

相比之下,微调SLM优势明显:

  • 边缘部署: 经过微调的SLM可以将函数调用能力带到资源有限的边缘设备上,例如智能手机、物联网设备和离线场景。
  • 更低的运营成本: 运行较小的模型所需的计算资源和功耗更少,从而降低了基础设施成本。
  • 隐私和合规性: 在本地处理数据消除了将敏感信息发送到外部服务器的需要,从而解决了隐私问题。

例如,在自动驾驶场景中,车载系统需要根据用户语音指令实时调整车内温度。如果使用LLM,不仅会增加延迟,还需要持续的网络连接。但如果使用经过数据合成微调的SLM,就能在本地快速响应用户指令,调用相应的API调整温度,无需依赖网络,保障用户隐私。

数据合成:解决训练数据稀缺的关键

数据合成微调SLM以实现函数调用能力的过程中扮演着至关重要的角色。传统的训练数据创建方式,包括设计清晰的函数定义、编写数千条真实的用户查询、手动将每条查询映射到正确的函数调用以及验证每个映射的正确性,都非常耗时且容易产生狭隘、同质化的数据集。

数据合成提供了一种强大的替代方案:

  • 创建多样化的查询模式: 确保广泛覆盖不同类型的查询,从而提高模型的泛化能力。
  • 自动映射查询到函数调用: 简化了数据标注过程,极大地提高了效率。
  • 快速生成大量训练数据: 可以在很短的时间内生成数千个训练样本,满足SLM微调的需求。
  • 自动验证语法和语义的正确性: 确保训练数据的质量,避免模型学习到错误的模式。

然而,在使用数据合成时,需要注意一些潜在的限制。文章中提到,Azure OpenAI Service模型由于许可限制,不能用于数据合成。因此,选择具有更宽松许可证的模型,例如Phi-3.5 MoE instruct模型,至关重要。

从中等规模LLM获取高质量合成数据:多阶段验证的重要性

文章指出,在使用Phi-3.5 MoE等中等规模LLM进行数据合成时,多阶段验证对于获得高质量数据至关重要。这是因为生成的数据可能包含格式错误、语义不对齐和参数错误等问题。

为了解决这些问题,文章借鉴了APIGen论文中的方法,采用三层验证流程:

  1. 格式检查: 确保数据符合所需的结构标准,例如正确的JSON结构、必要的字段和类型匹配。
  2. 函数执行: 验证API调用是否正确执行,确保参数值的有效性。
  3. 语义验证: 确认API响应在语义上与预期的结果对齐,确保函数调用能够满足用户查询的意图。

由于项目采用Python编写数据合成代码,而目标API使用Java on Android Automotive OS编写,集成成本较高,因此文章中最终采用了两阶段验证流程:格式检查和语义验证。

  1. 格式检查 (format_checker.py):
    • 确保所有必需的参数都存在。
    • 验证参数是否与其期望的类型相匹配。
    • 验证枚举值是否有效。
    • 验证数值是否在定义的范围内。
    • 检查是否满足条件要求。
  2. 语义检查 (semantic_checker.py):
    • 将查询和函数调用发送给模型。
    • 要求模型判断函数调用是否正确地满足了查询的意图。
    • 使用确定性方法(temperature = 0)进行一致性评估。

例如,如果用户查询是“将温度设置为72度”,而合成数据中的函数调用为{"function_name": "adjust_temperature", "arguments": {"temperature": "abc"}},则格式检查会发现温度参数的类型错误,将其过滤掉。如果函数调用为{"function_name": "play_audio_track", "arguments": {"service": "StreamX", "media_type": "playlist", "title": "Rock Favorites"}},则语义检查会发现函数调用与用户查询的意图不符,也会将其过滤掉。

数据合成代码库关键部分:生成、验证和拆分

文章详细介绍了数据合成代码库的三个核心组件:数据生成、多阶段验证和数据拆分与转换。

  1. 数据生成 (generate_synthetic_pairs.py):

    • 使用种子示例、函数定义和提示模板创建合成的查询-答案对。
    • 加载种子示例和函数定义。
    • 根据函数定义采样一些示例。
    • 创建包含函数定义、示例和指令的提示。
    • 将这些提示发送到语言模型以生成合成数据。
    • 解析和验证模型的响应。
    • 消除重复的查询,确保数据集的多样性。

    数据生成器的提示(prompt)在generator_config.yaml中定义,它指导模型生成多样化的、真实的查询-函数调用对。为了增加数据集的多样性,模型温度被设置为0.7。

  2. 格式验证 (format_checker.py):

    • 确保生成的函数调用符合定义的模式,拒绝不符合约束的示例。
    • 验证函数调用中的参数是否满足类型、范围和枚举值等约束。
  3. 语义验证 (semantic_checker.py):

    • 确保函数调用能够真正解决用户的查询。
    • 使用一个语言模型判断函数调用是否满足用户的意图,从而捕捉格式验证遗漏的细微偏差。
    • 通过提示工程,指导模型判断函数调用与用户查询意图是否匹配。

    例如,语义检查器使用的提示词如下,用于评估函数调用和结果是否准确反映了用户的意图:

    作为数据质量评估员,您必须评估用户查询、相应函数调用及其执行结果之间的一致性。
    这些函数调用和结果由其他模型生成,您的任务是确保这些结果准确反映用户的意图。
    如果不满足以下条件,请勿通过:
    1. 函数调用与查询的目标不一致,或者输入参数看起来不正确。
    2. 函数调用和参数未从可用函数中正确选择。
    3. 函数调用的数量与用户的意图不符。如果查询有多个意图,请将其视为错误的查询。
    给定信息:
    - 所有可用函数:{func_desc}
    - 用户查询:{query}
    - 生成的函数调用:{func_call}
    注意:主要决定因素是函数调用是否准确反映查询的意图和函数描述。
    在思考部分提供你的推理,并决定数据是否通过(回答是或否)。
    如果不通过,请在思考部分简洁地解释原因;否则,请将此部分留空。
    你的回复必须严格遵守以下JSON格式,并且不得包含其他文本。''' {{ "thought": "在此简洁地描述你的推理", "pass": "是" 或 "否" }} '''
    
  4. 数据拆分 (split_data.py):

    • 使用分层抽样机制将数据拆分为训练集和验证集,确保函数类型之间具有均衡的表示。
    • 采用多级分层方法,包括类名(函数名称)和参数(排序后的参数键的组合)。
    • 将类名和参数组合成一个字符串,用于分层拆分,确保分割中不仅平衡了函数本身,还平衡了参数组合的多样性。

    例如,一个分层拆分的例子是,确保play_audio_trackadjust_temperature这两个函数在训练集和验证集中都有相似的比例,并且play_audio_trackservice=MusicBoxservice=StreamX的比例也在两个集合中相似。

  5. 数据格式化 (apply_chat_message_format.py):

    • 将数据转换为适合模型微调的格式,例如聊天消息格式,包含系统、用户和助手角色。
    • 加载系统提示,定义函数调用的行为。
    • 读取包含查询/函数对的JSONL输入数据。
    • 将每对数据转换为具有系统/用户/助手角色的聊天消息格式。
    • 将格式化的数据写入输出文件。

    例如,最终的聊天消息格式如下:

    {
        "messages": [
            { "role": "system", "content": "您是车载助手,拥有一系列工具。..." },
            { "role": "user", "content": "在 MusicBox 上播放歌曲 'Starlight'" },
            { "role": "assistant", "content": "[{\"function_name\": \"play_audio_track\", \"arguments\": {\"service\": \"MusicBox\", \"media_type\": \"track\", \"title\": \"Starlight\"}}]" }
        ]
    }
    

使用Azure Machine Learning Pipeline进行自动化

文章还介绍了如何将所有步骤集成到Azure Machine Learning (AML) pipeline中,实现大规模自动化运行,从而避免手动运行各个Python文件的繁琐操作。

AML Pipeline的优势包括:

  • 自动化: 提交作业后,它会在云中持续运行,无需手动干预。
  • 可扩展性: 能够轻松地并行运行数据合成作业。
  • 监控和可追溯性: 跟踪进度,查看详细日志,并记录所有过程和更改。
  • 可重复性: 维护数据、代码和环境的版本控制,确保结果一致。
  • 集成: 与其他Azure服务无缝连接,实现全面的MLOps解决方案。

可以通过以下步骤设置AML Pipeline:

  1. 使用提供的Dockerfile创建AML自定义环境。
  2. 将种子数据集和函数定义注册为AML数据资产。
  3. 使用单个命令提交pipeline作业。

文章提供了 pipelines/main.yaml 文件,展示了如何在AML pipeline中编排整个过程,包括数据生成、格式验证、语义检查、数据拆分和转换为微调格式。使用Azure Machine Learning (AML) pipeline能够极大提升工作效率。

结论

通过数据合成SLM进行微调,可以低成本地将强大的AI功能部署到边缘设备上,例如实现高效的函数调用。Azure-Samples/function-calling-data-synthesizer中概述的数据合成方法,为大规模创建高质量训练数据提供了一种可行的解决方案。通过将少量样本学习与多阶段验证相结合,该方法可以生成多样化、准确的训练样本,而无需大量的人工工作。与Azure Machine Learning (AML) pipeline的集成使得大规模运行此pipeline变得容易。

如果你希望将函数调用功能部署到边缘设备,或者希望降低AI解决方案的成本和延迟,那么这种合成数据生成方法将为高质量SLM微调提供强大的途径。