指令微调在渠道业务测试用例大模型中的应用实践
本文探讨了利用指令微调技术优化开源大模型在软件测试领域的应用。针对人工编写测试数据成本高、质量不一的问题,采用 Self-Instruct 方法自动生成多样化的指令微调数据集。通过构建种子指令、调用大模型生成新任务及输入输出对,最终使用 LLaMA Factory 框架对 Qwen 等模型进行微调。实验表明,该方法能有效提升模型在测试用例生成及相关知识问答上的表现,为垂直领域大模型的低成本构建提供了可行方案。

本文探讨了利用指令微调技术优化开源大模型在软件测试领域的应用。针对人工编写测试数据成本高、质量不一的问题,采用 Self-Instruct 方法自动生成多样化的指令微调数据集。通过构建种子指令、调用大模型生成新任务及输入输出对,最终使用 LLaMA Factory 框架对 Qwen 等模型进行微调。实验表明,该方法能有效提升模型在测试用例生成及相关知识问答上的表现,为垂直领域大模型的低成本构建提供了可行方案。

近年来,大语言模型(LLM)在自然语言处理领域取得了显著进展。以 GPT、Qwen 等为代表的大模型展示了在各种语言任务中的强大能力。然而,通用大模型在特定垂直领域的表现往往难以满足专业需求。我们旨在基于开源大模型,定制一个能够生成高质量渠道业务测试用例的专用大模型。
为了实现这一目标,首先需要对开源大模型进行指令微调(Instruction Tuning),优化其在软件测试方面的表现。指令微调是一种重要的方法,通过向模型提供精确的指令或任务描述,使其能够在执行特定任务时表现得更加出色。
但在实际应用中,指令微调面临以下主要挑战:
因此,经过调研,我们选择使用 Self-Instruct 方法生成大量的指令微调数据,用于微调开源大模型,以解决数据稀缺和质量问题。
Self-Instruct 是一个半自动的过程,从有限少量的手动编写的种子任务开始,利用大模型以引导的方式生成新的大量任务,用于指令微调。完整的流程可以分为 6 步,具体步骤如下:

在实践过程中,为了更快地验证 Self-Instruct 的可行性,我们对流程进行了简化。取消了复杂的分类任务识别步骤,并简化了过滤及处理环节,以提高迭代速度。
我们的目标是生成专注于软件测试领域的指令微调数据集。在 Self-Instruct 的基础上,我们做了一定的拓展,使生成的数据严格限定在软件测试范畴内。
指令微调数据集有多种格式,我们选择生成 Alpaca 格式的数据。该格式包含三个关键字段:instruction(指令)、input(输入上下文)、output(期望输出)。
| { "instruction": "请编写一个测试用例,以验证登录功能的正确性。", "input": "登录表单包括用户名和密码字段,以及一个提交按钮。正确的用户名和密码为 'user123' 和 'password123'。", "output": "测试用例:\n1. 打开登录页面。\n2. 在用户名字段中输入 'user123'。\n3. 在密码字段中输入 'password123'。\n4. 点击提交按钮。\n5. 验证页面跳转到用户主页,并显示欢迎信息。\n预期结果:\n用户成功登录,页面跳转至用户主页,显示 '欢迎,user123'。" } | { "instruction": "解释回归测试的意义及其重要性。", "input": "", "output": "回归测试是一种软件测试方法,用于确认软件的新版本没有引入新的错误,且先前修复的问题未再次出现。其主要目的是确保代码更改(如功能增强、错误修复或性能优化)不会影响现有功能的正常运行。回归测试的主要重要性体现在以下几点:\n1. 质量保证:通过重复测试,确保软件的稳定性和可靠性。\n2. 降低风险:在发布新版本前发现潜在问题,避免生产环境中出现重大故障。\n3. 节省成本:尽早发现并解决问题,减少后续的维护和修复成本。\n4. 提高用户满意度:确保新版本的功能和性能提升,减少用户对软件的不满。\n总之,回归测试是软件开发过程中不可或缺的一部分,有助于保持软件的高质量和稳定性。" } |
第一步,我们人工编写了一部分测试相关的指令数据,称为种子指令。这些指令被用来指导整个生成过程,决定了后续生成数据的分布和方向。
以下是部分种子指令示例:
| {"id": 1, "question": "什么是软件测试?为什么它对软件开发过程至关重要?"} | {"id": 2, "question": "请解释黑盒测试和白盒测试之间的区别。"} | {"id": 3, "question": "什么是回归测试?在什么情况下需要进行回归测试?"} | {"id": 4, "question": "请描述静态测试和动态测试的不同点。"} | {"id": 5, "question": "单元测试、集成测试、系统测试和验收测试有什么区别?"} | {"id": 6, "question": "什么是测试用例?请提供一个测试用例的例子。"} | {"id": 7, "question": "测试人员在进行性能测试时需要关注哪些关键指标?"} | {"id": 8, "question": "请解释什么是缺陷生命周期,以及它通常包括哪些状态。"} | {"id": 9, "question": "什么是负载测试和压力测试?它们之间的区别是什么?"} | {"id": 10, "question": "请解释什么是边界值分析,并给出一个应用示例。"} |
第二步,是将人工编写的指令和大模型已经生成的指令作为上下文,与提示词模板组合后,调用大模型,使其生成新的指令。为了使生成的指令具有多样性,共抽取 8 个指令作为上下文示例。在这 8 条指令中,有 6 条来自人工编写的指令,有 2 条来自大语言模型生成的指令。实际比例可以根据生成效果进行调整。
# 读取种子数据
with open("./data/seed/seed_question_%s.jsonl" % domain, "r", encoding="utf-8") as file:
data = [json.loads(line) for line in file]
# 随机抽取 6 个问题
random_questions = random.sample(data, 6)
# 读取大模型生成的数据,随机抽取 2 个
if os.path.exists(generate_tasks_file) and os.path.getsize(generate_tasks_file) != 0:
with open(generate_tasks_file, 'r', encoding='utf-8') as file:
data = [json.loads(line) for line in file]
random_questions.extend(random.sample(data, 2))
num_example = str(len(random_questions))
# 组合 llm 提示模板及上下文数据
question_prompt = question_prompt.replace('[domain]', domain).replace('[续写数量]', num_per_generate).replace('[例子数量]', num_example)
example = '\n'.join(['问题%s.' % (index + 1) + question_dict['question'] for index, question_dict in enumerate(random_questions)])
prompt = question_prompt.replace('[例子生成]', example)
res = chat(prompt)
# 调用大模型,生成指令数据
generate_questions_base = decode_res(res)
# 校验及处理大模型生成的字符串格式
# 最后将生成的指令数据保存
为了使生成的指令专注于软件测试领域,指令生成的提示模板部分编写如下:
question_prompt = """
你是一个[domain]领域的专家,被要求提供[续写数量]个多样化的指令,我会给你八个例子,你再续写[续写数量]个,指令都属于[domain]。
下面是[例子数量]个例子:
[例子生成]
下面请续写[续写数量]个指令,以 json 字符串数组的格式返回,不要多余的其他文字,不要序号,返回格式如下:
["指令内容","指令内容"]
"""
调用大模型选用了 OpenAI API 的 gpt-turbo-3.5 模型进行初期探索。Self-Instruct 生成的数据质量依赖于调用的大模型,所以在后续的工作中可将其更换为更强的 gpt4 或其他大模型以提升数据质量。
调用大模型方法定义:
def chat(question_input):
client = OpenAI(api_key=api_key, base_url=api_base)
msg = [{"role": "user", "content": question_input}]
completion = client.chat.completions.create(
model="gpt-3.5-turbo", messages=msg
)
return completion.choices[0].message.content
生成的部分指令数据如下:
| {"id": 80, "question": "什么是边界测试?为什么在软件测试中执行边界测试很重要?"} | {"id": 81, "question": "什么是易用性测试?它如何帮助确保软件易用性和用户满意度?"} | {"id": 82, "question": "请解释一下什么是性能测试,它如何帮助评估软件的性能表现?"} | {"id": 83, "question": "什么是安全性测试?为什么软件安全性是至关重要的?"} | {"id": 84, "question": "什么是质量保证(QA)和质量控制(QC)?它们之间有何不同?"} | {"id": 85, "question": "什么是无缺陷测试?它如何帮助提高软件的质量?"} | {"id": 86, "question": "什么是回归测试套件?它的作用是什么?"} | {"id": 87, "question": "什么是冲刺演示测试?为什么在敏捷开发中执行冲刺演示测试很重要?"} | {"id": 88, "question": "什么是边界值分析法,它在软件测试中的作用是什么?"} | {"id": 89, "question": "请解释一下什么是黑盒测试和白盒测试,它们之间有什么区别?"} | {"id": 90, "question": "在软件测试中,什么是冒烟测试,它的主要目的是什么?"} |
第三步是输入输出生成。将上一步生成的指令进行过滤后,与生成输入输出的提示词组合在一起,交给大模型生成对应的输入输出。
原始 Self-Instruct 中的过滤方法比较复杂,涉及语义相似度计算等,我们在这里简单处理,直接遍历生成的指令,判断指令是否已存在,已存在的将其过滤掉,以避免冗余数据。
以下是部分代码实现:
for i, question in tqdm(enumerate(question_list)):
if question in existing_questions:
continue # 如果指令已存在,跳过
question_input = answer_prompt + question
try:
result = chat(question_input) # 调用大模型,生成输入输出
except Exception as e:
print('异常:', e)
continue # 如果有异常,跳过
generate_base = decode_res(result) # 校验及处理大模型生成的字符串格式
# 拼接完整的 Alpaca 数据
output = {'instruction': question, 'input': generate_base.input_data, 'output': generate_base.output_data}
# 将生成的数据保存
write_output(output_filename, output)
将生成的输入输出与指令组合起来,就生成了 Alpaca 数据集。以下是部分生成的数据示例:
| {"instruction": "什么是边界测试?为什么在软件测试中执行边界测试很重要?", "input": "", "output": "边界测试是在输入值的边界条件上进行测试的一种方法。它涉及检查输入的最大和最小值,以及接受的范围边界。在软件测试中执行边界测试很重要,因为边界处的错误往往最容易出现。通过测试边界条件,可以发现潜在的边界问题,确保软件在极端情况下也能正常工作,提高软件的健壮性和质量。"} | {"instruction": "什么是易用性测试?它如何帮助确保软件易用性和用户满意度?", "input": "", "output": "易用性测试是评估软件界面、功能和交互设计是否符合用户需求、易于理解和操作的过程。它通过模拟用户场景和行为,检查软件的可用性和用户体验,以发现并解决潜在的用户界面问题。\n\n易用性测试有助于确保软件易用性和用户满意度的几个方面:\n1. 发现问题:通过模拟真实用户行为,易用性测试可以发现用户在使用软件时可能遇到的困难和挑战,如不直观的界面设计、操作流程复杂等。\n2. 优化设计:根据测试结果,开发团队可以对软件界面和功能进行调整和优化,以提高用户体验和满意度。\n3. 提高用户参与度:易用性测试有助于增强用户对软件的信任感和使用意愿,从而提高用户参与度和留存率。\n4. 降低支持成本:通过改善软件的易用性,减少了用户因使用问题而产生的支持和培训成本,提高了软件的整体经济效益。\n\n总之,易用性测试是确保软件能够满足用户需求、提高用户体验和满意度的关键步骤之一。"} | {"instruction": "请解释一下什么是性能测试,它如何帮助评估软件的性能表现?", "input": "", "output": "性能测试是评估软件系统在各种条件下的响应速度、吞吐量、稳定性和可扩展性的过程。它通过模拟真实环境中的用户活动来检查系统在负载下的表现。性能测试有助于识别系统的瓶颈和性能瓶颈,并提供数据以优化系统性能和资源利用率。通过性能测试,开发人员和测试人员可以确保软件在生产环境中能够承受预期的工作负载,并且能够满足用户的性能需求和期望。"} | {"instruction": "什么是安全性测试?为什么软件安全性是至关重要的?", "input": "", "output": "安全性测试是评估软件系统在保护数据、功能和服务免受未经授权访问、修改或损坏的能力。它涉及检查软件是否易受黑客、病毒、恶意软件等攻击,并确保符合安全标准和法规。\n\n软件安全性至关重要,因为:\n1. 保护数据:避免敏感信息泄露,如个人身份信息、财务数据等,维护用户隐私。\n2. 维护信任:用户只信任能够确保其数据安全的软件,安全漏洞可能导致信任丧失,损害品牌声誉。\n3. 遵守法规:许多行业都有严格的安全法规要求,如 GDPR、HIPAA 等,违反可能导致法律责任和罚款。\n4. 防止服务中断:安全漏洞可能导致系统故障、服务中断,影响业务连续性和用户体验。\n5. 减少损失:避免数据被盗、篡改、破坏等可能带来的经济损失和声誉损害。"} | {"instruction": "什么是质量保证(QA)和质量控制(QC)?它们之间有何不同?", "input": "", "output": "质量保证(QA)是一系列过程和活动,旨在确保产品或服务符合预期质量标准。它涉及制定质量标准、制定流程规范、培训人员等。质量控制(QC)是针对已生产的产品或服务进行检查和测试,以确保其符合质量标准。QA 注重预防,QC 注重检测与纠正。QA 是过程导向的,而 QC 是产品导向的。"} |
对生成的数据集进行简单评估,生成的大多数指令是有意义的,生成的大多数输入输出也是正确的,可以为训练模型提供有效的指导。此外,我们还引入了人工抽检机制,对生成的数据进行二次审核,进一步保证了数据质量。
通过对微调大模型所需软硬件依赖进行调研,对所需资源进行了配置。以下是部分主要配置建议:
微调大模型选用了 LLaMA Factory,一款开源的大模型微调框架。它支持 WebUI,支持众多模型,集成了业界广泛使用的微调方法(如 LoRA、QLoRA 等),大大降低了微调门槛。
下载开源大模型,将生成的 Alpaca 指令数据集导入 LLaMA Factory,通过 Web 页面即可对开源大模型进行指令微调。配置文件通常包括学习率、批次大小、训练轮数等参数。对于 RTX 4090 这样的消费级显卡,建议使用 QLoRA 量化微调技术,可以在显存受限的情况下训练较大的模型。
我们使用生成的数据集对多个开源大模型进行了指令微调,包括 Qwen、LLaMA 等。微调后的模型在测试领域方面表现均有显著提升,特别是在理解测试术语、生成结构化测试用例以及回答测试相关问题时,准确性明显优于基座模型。

为了验证微调效果,我们采用了定量和定性相结合的方法进行评估:
初步结果显示,微调后的模型在特定领域的知识问答准确率提升了约 30%,测试用例生成的可用性也得到了显著提高。未来计划引入 RLHF(人类反馈强化学习)进一步对齐人类偏好。
通过对开源大模型进行指令微调,使其在测试领域的表现更加出色,为后续定制渠道业务测试用例大模型的工作奠定了坚实的基础。Self-Instruct 生成指令微调数据集是很重要的一环,其提供了一种低成本、高效率生成多样化指令微调数据的方法,使得成本有限的情况下,也可以微调出效果较好的模型。

本文详细阐述了从数据生成到模型微调的全流程,为其他希望在垂直领域构建大模型的应用提供了参考路径。随着技术的演进,结合 RAG(检索增强生成)和 Agent 技术,未来的测试大模型将更加智能化,能够自主规划测试策略并执行复杂任务。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online