数学判题
本章内容已接入千帆ModelBuilder线上加速营课程,一起来感受模型精调的奇妙之旅吧。
一、数学判题介绍
在线教育中,判定学生数学答案会考察解题步骤、方法和答案的准确性。大模型同样可以扮演数学题目评判专家的角色。通过精确设定评分标准和模型的训练,大模型可以根据设定的要求对学生的数学答案进行准确评判。
利用大模型进行数学题目评判,可以有效地整合到在线教育环境中,不仅能成为教师的强大辅助工具,还能帮助学生了解其解题方法的不足之处,从而提升其数学解题能力,同时大幅减少人力和时间成本。如果您的场景比较类似,可以参考下方的精调流程调试出符合您场景的大模型。如果您想体验训练的效果,可以使用平台预置的数据发起精调,并部署调用进行体验。
二、数学判题的难点问题
对于数学题目评判来说,我们不仅需要大模型能够准确给出答案的评分,还需要它能够指出解题过程中的错误、优点和改进建议。因此,最关键的是大模型能够遵循评判的结构和确保评判的准确性。
如果我们直接调用大模型,要求大模型按Prompt中的格式输出摘要内容。会发现可能存在如下问题:
1、未按指定格式输出的问题
第一个存在的问题是:输出内容较为啰嗦,且每次输出格式不太固定。具体示例如下:
Prompt | Response |
---|---|
参考{题目}来批改{学生作业},需要判断解题结果和过程是正确还是错误,不需要返回题目,不需要解析过程 {题目}:阿斯顿不小心撕开了他的漫画书,需要把所有的页码重新组合起来。每本漫画书有25页,阿斯顿在地板上找到了150页。他把漫画书重新组合起来,并放回漫画盒里。如果盒子里已经有5本完整的漫画书,现在盒子里有多少本漫画书? {学生作业}:150 ÷ 25 = 6 6 + 5 = 11 本。 请判断解题过程和结果是否正确 |
学生的解题步骤完全正确,首先计算了150页可以组成6本完整的漫画书,然后加上原有的5本,得到总数11本。过程和结果均正确。 所以,学生的解题过程和结果都是正确的。 |
大模型虽然很好的完成了这次任务,但输出较为啰嗦,我们判题结果只需要看是"正确"还是"错误"。
2、误判问题
第二个存在的问题是:在针对过程错误、结果正确的Case上,大模型认为过程错误即结果错误,出现了误判。具体示例如下:
Prompt | Response |
---|---|
你是一位资深的数学老师,现在需要批改小学数学作业。接下来你会收到<题目>、<解析>以及<学生作业>,批改需要按以下几点要求严格执行: 1. 需要判断<学生作业>的答案和过程是正确还是错误,并给出判断理由。使用json格式输出{"判断理由": "过程和结果正确或者错误的理由", "过程": "直接输出正确或者错误", "结果": "直接输出正确或者错误"} 2. 在"判断理由"中判断过程时,参考<解析>部分,对学生作业中的解题步骤,一步步分析,仔细思考,判断步骤是否出错 3. 在"判断理由"中判断结果时,不管过程是否正确,只看最终答案是否正确。若过程错误,但答案正确,也判断为"正确" <题目>:学校买来20个足球,比买来篮球的个数多25%.买来篮球多少个? <解析>:我们通过代数方程的形式和基本的代数运算来解决问题,则答案可求 <学生作业>: x = 20 ÷ 1.5 x=16 |
{ "判断理由": "学生的解题步骤不符合题目要求,没有正确理解如何建立方程来求解篮球的数量。此外,学生使用的算术运算错误,未能正确转换足球数量与篮球数量的百分比关系。过程中发现了计算错误,学生的解题方法与正确的解析步骤不一致。最终答案也与正确答案不符。", "过程": "错误", "结果": "错误" } |
预期回答是,过程错误,结果正确。而结果为过程错误,结果错误,因此大模型出现了误判。
当出现一些较难的数学题目时,大模型可能完全出错。具体示例如下:
Prompt | Response |
---|---|
你是一位资深的数学老师,现在需要批改小学数学作业。接下来你会收到<题目>、<解析>以及<学生作业>,批改需要按以下几点要求严格执行: 1. 需要判断<学生作业>的答案和过程是正确还是错误,并给出判断理由。使用json格式输出{"判断理由": "过程和结果正确或者错误的理由", "过程": "直接输出正确或者错误", "结果": "直接输出正确或者错误"} 2. 在"判断理由"中判断过程时,参考<解析>部分,对学生作业中的解题步骤,一步步分析,仔细思考,判断步骤是否出错 3. 在"判断理由"中判断结果时,不管过程是否正确,只看最终答案是否正确。若过程错误,但答案正确,也判断为"正确" <题目>:南希可以呼啦圈玩10分钟。凯西比南希少3分钟呼啦圈。摩根呼啦圈玩的时间是凯西的三倍。摩根呼啦圈能玩多久呢? <学生作业>: 凯西的时间: 10 - 3 = 7分钟 摩根的时间:7*3=21分钟 故答案为:21分钟 |
{ "判断理由": "学生的解题步骤不符合题目要求,未能正确计算凯西和摩根的呼啦圈时间。学生错误地应用了算术运算,未能正确理解题目中的时间比例关系。解题过程中存在计算错误,学生的解题方法与正确的解析步骤不一致。最终答案也与正确答案不符。", "过程": "错误", "结果": "错误" } |
预期回答是,过程正确,结果正确。而结果为过程错误,结果错误,因此大模型出现了误判。
三、难点分析
如果我们直接调用大模型发现上述问题,可以通过调整优化Prompt查看模型效果。如果使用优化后的Prompt发现模型的指令遵循能力进一步增强,并且需要提升模型解析的逻辑,可以使用含该Prompt的问答对通过SFT进一步调优。
- 对于上述问题1未按指定格式输出的问题,我们可以通过调整Prompt,如加上角色定义、明确定义输出结构、加上fewshot示例等强化模型输出,可以很好解决。
- 对于问题2,过程错误结果正确的误判问题上,我们也可以通过调整Prompt提升评估结果准确率,比如在Prompt中让大模型增加COT分析的能力,引导大模型一步步分析。同时,调整Prompt对后续的SFT数据构建也非常重要,影响着SFT的效果。
一个好的Prompt,应该包含以下几点:
1.给大模型设定合适的身份:不同的身份视角往往能激发大模型不同的能力,可以根据场景要求进行设定。如下方的Prompt:“你是一位资深的数学老师,现在需要批改小学数学作业”。
2.清晰、具体的任务描述:避免歧义,简洁易懂,和人交流一样,Prompt是和大模型的交流语言,人的视角如果判断任务要求清晰、具体,到大模型也是一样。如下方的Prompt:“接下来你会收到<题目>、<解析>以及<学生作业>,批改需要按以下几点要求严格执行:”。
3.围栏限制:不仅告诉模型要完成什么任务,也需要在一些具体的任务里,告知模型not do something,反向限制模型的输出。
4.更完整的上下文信息:如果有背景信息,也需要放进Prompt里帮助大模型理解,能更好的处理任务。如下方的Prompt:“你是一位资深的数学老师,现在需要批改小学数学作业。”
5.合理使用符号:清晰地表示输入的不同部分,或对特定内容进行标记加强。通过符号调整输入Prompt结构,让大模型更容易理解。注意符号表达的一致性。如下方的Prompt:“接下来你会收到<题目>、<解析>以及<学生作业>”。
6.明确定义输出结构:如果对输出格式、类型有要求,一定要在Prompt里明确定义,不然大模型能正确输出内容,但不能按业务预期格式,也可能导致在业务对接上不可用。如下方的Prompt:“需要判断<学生作业>的答案和过程是正确还是错误,并给出判断理由。使用json格式输出{"判断理由": "过程和结果正确或者错误的理由", "过程": "直接输出正确或者错误", "结果": "直接输出正确或者错误"}”。
7.Few shot:若任务复杂或描述困难,可以在prompt中给模型提供少量成功执行任务的示例,帮助大模型理解,以及更好的控制输出预期。few shot不止能提升输出质量,也能更好的控制输出格式。如下方的Prompt:“示例:....”。
8.COT(Chain-of-Thought):如果涉及复杂的推理或有多个前后依赖执行步骤,COT往往能起到较好的效果。我们可以prompt中把多步骤分解为更多的中间步骤(think more steps),引导模型一步一步执行。也可以在prompt中简单的加入“请逐步思考”、“一步一步思考”(Let's think step by step)等字样。
根据上述描述,我们将Prompt改写为:
你是一位资深的数学老师,现在需要批改小学数学作业。接下来你会收到<题目>、<解析>以及<学生作业>,批改需要按以下几点要求严格执行:
1. 需要判断<学生作业>的答案和过程是正确还是错误,并给出判断理由。使用json格式输出{"判断理由": "过程和结果正确或者错误的理由", "过程": "直接输出正确或者错误", "结果": "直接输出正确或者错误"}
2. 在"判断理由"中判断过程时,参考<解析>部分,对学生作业中的解题步骤,一步步分析,仔细思考,判断步骤是否出错
3. 在"判断理由"中判断结果时,不管过程是否正确,只看最终答案是否正确。若过程错误,但答案正确,也判断为"正确"
示例:
<题目>: 200 x 199 = ?
<解析>: 这是一个基本的乘法问题,我们可以使用分配律来简化计算。
步骤如下:
将199写成200 - 1,这样我们可以将问题转化为200 x (200 - 1)。易得结果为39800。
所以,200 x 199 = 39800。
<学生作业>:
200 x 199
= 200 * (200 - 1)
= 200 * 200 - 200
= 40000 - 200
= 38800
故答案为:39800
<输出>:
{"判断理由": "学生的解题步骤基本正确,但在最后一步中出现了错误,40000 - 200应该等于39800,而不是38800。过程存在错误,然而,学生的最终答案却是39800,这是正确的。", "过程": "错误", "结果": "正确"}
现在请你判断以下题目:
<题目>: xxxx
<解析>: xxxxx
xxxx
xxxx
<学生作业>: xxxx
xxxx
xxxx
<输出>:
问题1成功解决 |
可以看到,我们通过调整Prompt,加上了角色定义与清晰的任务描述,很好的解决了未按指定格式输出的问题。
问题2成功解决(一) |
问题2成功解决(二) |
可以看到,我们通过调整Prompt,明确定义了输出结构并且通过Few shot与COT工程,提升了评估结果准确率,解决了过程错误结果正确的误判问题。
在完成上述优化后,整体准确率和有效性已经有明显提升。但在批量评估过程中,我们还是能发现有回答不准确的各类问题,具体示例如下:
Prompt | Response |
---|---|
你是一位资深的数学老师,现在需要批改小学数学作业。接下来你会收到<题目>、<解析>以及<学生作业>,批改需要按以下几点要求严格执行: 1. 需要判断<学生作业>的答案和过程是正确还是错误,并给出判断理由。使用json格式输出{"判断理由": "过程和结果正确或者错误的理由", "过程": "直接输出正确或者错误", "结果": "直接输出正确或者错误"} 2. 在"判断理由"中判断过程时,参考<解析>部分,对学生作业中的解题步骤,一步步分析,仔细思考,判断步骤是否出错 3. 在"判断理由"中判断结果时,不管过程是否正确,只看最终答案是否正确。若过程错误,但答案正确,也判断为"正确" 示例: <题目>: 200 x 199 = ? <解析>: 这是一个基本的乘法问题,我们可以使用分配律来简化计算。 步骤如下: 将199写成200 - 1,这样我们可以将问题转化为200 x (200 - 1)。易得结果为39800。 所以,200 x 199 = 39800。 <学生作业>: 200 x 199 = 200 x (200 - 1) = 200 x 200 - 200 = 40000 - 200 = 38800 故答案为:39800 <输出>: {"判断理由": "学生的解题步骤基本正确,但在最后一步中出现了错误,40000 - 200应该等于39800,而不是38800。过程存在错误,然而,学生的最终答案却是39800,这是正确的。", "过程": "错误", "结果": "正确"} 现在请你判断以下题目: <题目>: 凯尔每天去打篮球练习两个小时。在训练中,他花一半的时间进行投篮训练,其余时间用于跑步和举重。如果他跑步的时间是举重时间的两倍,那么他花多少分钟举重呢? <解析>: 凯尔每天练习篮球两个小时,这等于120分钟。他一半的时间用于投篮训练,所以投篮训练是60分钟。剩下的时间用于跑步和举重,也就是60分钟。 设举重时间为x分钟,因为跑步时间是举重时间的两倍,所以跑步时间就是2x分钟。 跑步时间和举重时间的总和是60分钟,所以我们有方程式:x + 2x = 60 合并同类项,得到:3x = 60 解这个方程,得到:x = 60 / 3 x = 20,所以凯尔花了20分钟举重。 <学生作业>: 凯尔每天去打篮球练习两个小时。他花一半的时间投篮,剩下的时间跑步和举重。 跑步的时间是举重时间的两倍。我们要找出凯尔花多少分钟举重。 假设凯尔每天练习的总时间为 T 小时,投篮时间为 S 小时,跑步时间为 R 小时,举重时间为 W 小时。这里 T=2小时。 根据题目,我们可以建立以下方程: 1. 投篮时间是总时间的一半,即 S = T / 2。 2. 跑步和举重的时间总和是 T - S。 3. 跑步的时间是举重时间的两倍,即 R = 2 × W。 4. 跑步和举重的时间总和是 R + W = T - S。 现在我们要来解这个方程组,找出 W 的值,并转换为分钟。 计算结果为:举重时间 W = T/3 - 1/3 小时 所以,凯尔每天花 20T - 20 分钟举重。 由于题目中给出T=2小时,所以凯尔每天花 202 - 20 = 20 分钟举重。 <输出>: |
{ "判断理由": "学生在解题思路上是正确的,他/她通过设立变量并建立方程来解决问题。然而,在将结果转换为分钟时出现了错误。学生计算出举重时间W为T/3减去某个值的小时数,但在最后将小时转换为分钟时,他/她忽略了小时和分钟之间的换算关系(即1小时等于60分钟)。因此,最后的答案应该是错误的。", "过程": "错误", "结果": "错误" } |
经过了Prompt Engineering的模型:预期回答是,过程正确,结果正确。而结果为过程错误,结果错误,因此大模型出现了误判。
Prompt | Response |
---|---|
你是一位资深的数学老师,现在需要批改小学数学作业。接下来你会收到<题目>、<解析>以及<学生作业>,批改需要按以下几点要求严格执行: 1. 需要判断<学生作业>的答案和过程是正确还是错误,并给出判断理由。使用json格式输出{"判断理由": "过程和结果正确或者错误的理由", "过程": "直接输出正确或者错误", "结果": "直接输出正确或者错误"} 2. 在"判断理由"中判断过程时,参考<解析>部分,对学生作业中的解题步骤,一步步分析,仔细思考,判断步骤是否出错 3. 在"判断理由"中判断结果时,不管过程是否正确,只看最终答案是否正确。若过程错误,但答案正确,也判断为"正确" 示例: <题目>: 200 x 199 = ? <解析>: 这是一个基本的乘法问题,我们可以使用分配律来简化计算。 步骤如下: 将199写成200 - 1,这样我们可以将问题转化为200 x (200 - 1)。易得结果为39800。 所以,200 x 199 = 39800。 <学生作业>: 200 x 199 = 200 x (200 - 1) = 200 x 200 - 200 = 40000 - 200 = 38800 故答案为:39800 <输出>: {"判断理由": "学生的解题步骤基本正确,但在最后一步中出现了错误,40000 - 200应该等于39800,而不是38800。过程存在错误,然而,学生的最终答案却是39800,这是正确的。", "过程": "错误", "结果": "正确"} 现在请你判断以下题目: <题目>: 盖尔有两个鱼缸。第一个鱼缸是第二个鱼缸的两倍大。第一个鱼缸里有48加仑的水。她遵循一英寸鱼所需的一加仑水规则。如果她在第二个鱼缸里养两英寸的鱼,在第一个鱼缸里养三英寸的鱼,如果第一个鱼缸的一条鱼吃掉了另一条鱼,那么第一个鱼缸里的鱼比第二个鱼缸里多多少条? <解析>: 要解决这个问题,我们需要遵循几个步骤: 1. 确定两个鱼缸的大小。 2. 根据规则计算每个鱼缸能容纳的鱼的数量。 3. 确定如果第一个鱼缸的一条鱼吃掉了另一条鱼后两个鱼缸的鱼的数量差。 首先,根据题目描述: 第一个鱼缸的容量是第二个鱼缸的两倍,我们用V表示第二个鱼缸的容量,则第一个鱼缸的容量是2V。题目中提到第一个鱼缸有48加仑的水,所以2V = 48加仑,从而可得V(第二个鱼缸的容量)= 48 / 2 = 24加仑。 其次,根据一英寸鱼需要一加仑水的规则: 第一个鱼缸可以容纳48英寸的鱼。 第二个鱼缸可以容纳24英寸的鱼。 然后,考虑到鱼的大小: 第一个鱼缸养的是三英寸的鱼,所以它可以容纳48 / 3 = 16条鱼。 第二个鱼缸养的是两英寸的鱼,所以它可以容纳24 / 2 = 12条鱼。 最后,如果第一个鱼缸的一条鱼吃掉了另一条鱼,那么第一个鱼缸的鱼的数量将减少一条,变为15条。 现在,我们可以得出结论: 第一个鱼缸里的鱼(15条)比第二个鱼缸里的(12条)多3条。 <学生作业>: 首先,我们根据题目描述分析盖尔的两个鱼缸及其鱼的情况。 1. 鱼缸大小与水量关系 第一个鱼缸是第二个鱼缸的两倍大,且已知第一个鱼缸里有48加仑的水。由此我们可以推断出第二个鱼缸有48加仑除以2即24加仑的水。 2. 水量和鱼的关系 根据题目中的“一英寸鱼所需的一加仑水”的规则,我们可以知道每个鱼所需的特定大小的水空间。这是计算每类鱼数量时需要考虑的要素。 3. 鱼的尺寸和数量 在第一个鱼缸里养了三英寸的鱼,在第二个鱼缸里养了两英寸的鱼。这意味着在第一个鱼缸中,每条鱼需要的空间是三英寸鱼的尺寸所对应的水量,在第二个鱼缸中则是两英寸鱼的尺寸所对应的水量。然而,题目中并未给出具体的鱼的数量,因此我们无法仅根据鱼的大小和水量来直接计算每缸的鱼的数量。 4. 第一个鱼缸中鱼的消失情况 题目中提到,第一个鱼缸里的一条鱼吃掉了另一条鱼。但这一情况并不影响我们对两个鱼缸中鱼的数量的比较,因为失去的只是其中的一条鱼,并不改变第一个鱼缸里鱼的总体数量。 综上所述,根据题目给出的信息,我们无法确切地知道第一个鱼缸里的鱼比第二个鱼缸里多多少条,因为题目中没有给出两个鱼缸各自鱼的准确数量。因此,我们无法得出具体的答案。 <输出>: |
{"判断理由": "学生的解题步骤清晰,理解题目中的关系并尝试解决问题。然而,学生在理解“一英寸鱼所需的一加仑水”的规则上出现了偏差。根据这个规则,学生应该能够算出每个鱼缸能养多少条鱼,而不是纠结于鱼的数量未知。最后得出的结论是正确的,即无法从题目给出的信息确切知道第一个鱼缸里的鱼比第二个鱼缸里多多少条,因为未给出具体的鱼的数量。但是,学生的解题过程中在理解和应用规则方面出现了错误。", "过程": "错误", "结果": "正确"} |
经过了Prompt Engineering的模型:预期回答是,过程错误,结果错误。而结果为过程错误,结果正确,因此大模型出现了误判。
由上述示例我们可以看到,我们经过了Prompt Engineering后,模型虽然在按格式输出上得到了很好的表现,但是仍然存在如:过程错误导致结果判错、不能准确分析题目信息等问题。对此,我们考虑到通过SFT解决这些问题。
SFT针对以上问题具有以下功能:
- 任务特定性能提升:预训练语言模型通过大规模的无监督训练学习了语言的统计模式和语义表示。然而,它在特定任务上的性能可能不如在大规模无监督数据上表现出的性能。通过在任务特定的有标签数据上进行SFT,模型可以进一步学习任务相关的特征和模式,从而提高性能。
- 提高领域适应性:预训练语言模型可能在不同领域的数据上表现不一致。通过在特定领域的有标签数据上进行SFT,可以使模型更好地适应该领域的特殊术语、结构和语义,提高在该领域任务上的效果。
- 解决数据稀缺性:某些任务可能受制于数据的稀缺性,很难获得大规模的标签数据。SFT可以通过使用有限的标签数据来训练模型,从而在数据有限的情况下取得较好的性能。
- 防止过拟合:在SFT过程中,通过使用有标签数据进行有监督训练,可以减少模型在特定任务上的过拟合风险。这是因为SFT过程中的有标签数据可以提供更具体的任务信号,有助于约束模型的学习,避免过多地拟合预训练过程中的无监督信号。
就此,我们选择SFT方法进行训练,探索进一步提升大模型效果的方法。
四、模型精调最佳实践
(一)准备数据
1、收集原始数据
(1)根据数据格式准备语料
数据格式:本实验是采用SFT调优方式,对于SFT调优,需要使用Prompt+Response数据格式。准备调优数据:对于模型调优来说,高质量的数据是保证模型效果的最关键步骤,建议您参考以下经验准备调优数据:
- 精标数据与真实性:数据充分过滤、清洗,数据完成去重、错字处理,不缺失核心数据,保证每条数据的格式、内容都符合预期,是精标数据。其中问答对数据集中的问题,都是用户真实的提问数据;数据集中的回答,都是正确有效的标准答案。
- 数据量:按照经验,通常需要上千条的精标数据,才能通过SFT训练出一个效果较好的模型,建议1000条数据起步。
(2)调优数据示例
为保证回答按预期格式输出,数据的Prompt中需要包含:1)任务与角色Prompt;2)判断示例的引导;3)数学题目;4)数学题的解析;5)学生作业。如下:
你是一位资深的数学老师,现在需要批改小学数学作业。接下来你会收到<题目>、<解析>以及<学生作业>,批改需要按以下几点要求严格执行:
1. 需要判断<学生作业>的答案和过程是正确还是错误,并给出判断理由。使用json格式输出{"判断理由": "过程和结果正确或者错误的理由", "过程": "直接输出正确或者错误", "结果": "直接输出正确或者错误"}
2. 在"判断理由"中判断过程时,参考<解析>部分,对学生作业中的解题步骤,一步步分析,仔细思考,判断步骤是否出错
3. 在"判断理由"中判断结果时,不管过程是否正确,只看最终答案是否正确。若过程错误,但答案正确,也判断为"正确"
示例:
<题目>: 200 x 199 = ?
<解析>: 这是一个基本的乘法问题,我们可以使用分配律来简化计算。
步骤如下:
将199写成200 - 1,这样我们可以将问题转化为200 x (200 - 1)。易得结果为39800。
所以,200 x 199 = 39800。
<学生作业>:
200 x 199
= 200 * (200 - 1)
= 200 * 200 - 200
= 40000 - 200
= 38800
故答案为:39800
<输出>:
{"判断理由": "学生的解题步骤基本正确,但在最后一步中出现了错误,40000 - 200应该等于39800,而不是38800。过程存在错误,然而,学生的最终答案却是39800,这是正确的。", "过程": "错误", "结果": "正确"}
现在请你判断以下题目:
<题目>: xxxx
<解析>: xxxx
xxxxx
<学生作业>: xxxx
xxxx
<输出>:
2、分析与处理数据
我们从实际业务场景中得到数据后,需要对样本进行分析和处理。一般包括对原始数据进行清洗、分析数学题目质量和类型的分布、对数据进行扩充。
(1)数据清洗
- 数据去重:检查是否存在重复数据。您可以使用本平台的数据处理-数据清洗功能可过滤重合度高的数据样本。
- 错字处理:数学判题场景,通常是通过扫描卷面或者对卷面进行拍照,然后通过OCR能力将图片转化为文字。因此,我们得到的文字可能会包含一些异常字符,本平台的数据处理-数据清洗功能可以规范化空格、去除乱符等,变成更为干净的数据样本。
- 缺失数据补充:检查数据是否有缺失。对于数学判题的场景,由于我们需要大模型按照固定的格式进行输出且需要保证有准确的解析。
(2)数据质量分布
为保证模型训练后具备更好的判断能力,训练数据集的正负样本需要保证合理的分布:
可以参照线上数据现状来做准备。如从线上数据看,学生的答题正确率在70%,过程准确率在60%。那训练数据集也需要按这个比例来做准备,训练出来的模型才能具备更好的判对错能力。
3.拆分训练集与评估集
经过数据清洗和数据处理后,我们得到一份高质量的数据集。同时为了评估我们调优的模型的质量好坏,需要准备对应分布的评估数据集。我们可以对上述得到的数据集进行拆分,拆分的部分数据集用于评估模型的好坏。针对数学判题的场景,评估数据集需要注意的是:
- 评估集中的题目质量分布要和训练集中大致一致
- 评估集和训练集内容不能重复,避免评估不准确
训练集已预置进平台数据集中,点击数据集可进入通用数据集-预置数据集模块查看。
在这里我们采取随机采样的方法,选择了50条数据作为评估集。
(二)精调模型
经过第一步的数据准备工作,第二步将正式进入模型训练任务。如果您也想复刻本次最佳实践的训练任务,可以点击精调进入SFT模块,一键即可开始训练。
1、基础模型选择
我们认为数学判题场景对模型能力的要求较高,因此我们通过在本平台体验中心对比多个基础模型的效果,最终选择了ERNIE Speed模型。该模型为百度2024年最新发布的自研高性能大语言模型,通用能力优异,适合作为基座模型进行精调,更好地处理特定场景问题,同时具备极佳的推理性能。
2、精调方法选择
根据之前的问题诊断与方案,SFT精调可以解决数学判题中的主要问题。
其中SFT精调又分为全量更新与LoRA两种方式。
- 全量更新:是指在训练过程中对大模型的全部参数进行更新的训练方案,也是目前较为主流的方式。相较于LoRA,全量更新更容易保障效果精度和泛化能力。因此建议您对精度要求较高且数据量充足时,直接选择全量更新。
- LoRA:是指在固定预训练大模型本身的参数的基础上,在保留自注意力模块中原始权重矩阵的基础上,对权重矩阵进行低秩分解,训练过程中只更新低秩部分的参数的训练方法。
LoRA方法在训练时间和内存消耗上比全量更新方法大幅减少,因此它训练成本低,训练时间快,可以低价高效地完成训练。如果您想要快速训练看到实验效果,可以优先尝试使用LoRA的训练方法。
3、精调参数配置
拿到一个训练场景或者任务后,往往比较难判断参数应该如何调整。一般使用默认的参数值进行训练即可,平台中的默认参数是多次实验的经验结晶。
接下来介绍参数配置中有两个较为关键的参数:
- 迭代轮次(Epoch): 控制训练过程中的迭代轮数。轮数增加代表会使用训练集对模型训练一次。
- 学习率(Learning Rate): 是在梯度下降的过程中更新权重时的超参数,过高会导致模型难以收敛,过低则会导致模型收敛速度过慢,平台已给出默认推荐值,也可根据经验调整。
本次也针对Epoch和Learning Rate进行简要的调参实验,详细实验结果可以看效果评估数据。
如果您是模型训练的专家,本平台也提供了训练更多的高级参数供您选择。这里也建议您初期调参时步长可以设定稍大些,因为较小的超参变动对模型效果的影响小,会被随机波动掩盖。
4、精调实验
我们基于ERNIE Speed模型共计做了两次SFT实验,参数和训练方法配置如下:
实验1 | 实验2 | 实验3 | 实验4 | 实验5 | |
---|---|---|---|---|---|
训练方法 | ERNIE Speed LoRA | ERNIE Speed LoRA | ERNIE Speed 全量更新 | ERNIE Speed 全量更新 | ERNIE Speed 全量更新 |
Epoch | 3 | 5 | 3 | 5 | 9 |
Learning Rate | 3e-4 | 3e-4 | 3e-5 | 3e-5 | 3e-5 |
训练后,我们可以在运行详情中的评估报告中查看Loss曲线如下:
实验1 Loss曲线 |
对于实验1,我们使用LoRA训练方法,Epoch等于3,发现Loss曲线接近收敛,可以进一步增加Epoch,观察曲线是否完全收敛。
实验2 Loss曲线 |
对于实验2,我们使用LoRA训练方法,Epoch等于5,发现Loss曲线早以提前收敛,可以判定为曲线过拟合。一般出现过拟合,应该在过拟合之前停止实验。
实验3 Loss曲线 |
对于实验3,我们使用全量更新训练方法,Epoch等于3。该训练方法下Loss曲线有明显收敛,Loss值不断降低。类似该种情况,我们建议增加Epoch数量进行训练,查看Loss指标的变化情况。
实验4 Loss曲线 |
对于实验4,我们使用全量更新训练方法,Epoch增加至5,曲线的收敛迹象还没有过于明显。类似该种情况,我们建议增加Epoch数量进行训练,查看Loss指标的变化情况。
实验5 Loss曲线 |
对于实验5,我们使用全量更新训练方法,Epoch增加至9,我们发现曲线的收敛迹象明显。
因此,我们尝试将Epoch调整为9,Learning Rate保持在3e-5。发现Loss曲线不断降低,接近于0。初步判断该实验效果会更好。
(三)效果评估
这里和准备训练数据集阶段工作类似,需要准备出一定数量的评估数据集,这里需要注意的是:
- 保证评估数据集不在训练集中,在本场景中主要是选择不同的题目。若用训练集数据评估,评估结果一般会表现的非常高。
- 评估数据集中,也需要保证合理题型分布比例,如计算题、应用题等。
在本次评估任务中,我们准备了50条评估测试集。
1、确定评估方法与标准
开始评估前,需要建立对应的评估方法与标准。在本次数学判题场景中,结果相对清晰明确。我们可定义以下评分规则:
- 完全匹配(2分):
- 如果模型的解题过程和结果均与标准答案中描述的过程和结果完全相符,那么模型得2分。
- 部分匹配(1分):
- 如果模型的解题过程或结果中的任一项与标准答案中描述的相符,但另一项不符,那么模型得1分。
- 不匹配(0分):
- 如果模型的解题过程和结果均与标准答案中描述的不符,那么模型得0分。
这种评分方法确保了对模型评判的公正,同时也简化了评分过程。使用这样的规则,可以明确地对模型的理解和解题能力进行评价。
打分方式:打分方式主要包含人工和自动两种。
- 人工打分:人工根据评分标准打分。打分时尽量屏蔽掉预测结果的来源,防止人为引入的偏见。较为耗时,但比较准确。
- 自动打分:将评分标准、场景要求、上下文、大模型的回答拼接成一个prompt,使用EB4等大模型进行自动打分。注意为确保评估结果有效,自动打分结果也需要经过人工复查。
2、评估模型效果
在本次最佳实践中,我们选择使用人工打分的方式以准确衡量模型的优劣。
实验1 (默认参数) | 实验2 | 实验3 | 实验4 | 实验5 | ||
实验配置 | 训练方法 | |||||
Epoch | 3 | 6 | 3 | 5 | 9 | |
Learning Rate | 3e-4 | 3e-4 | 3e-5 | 3e-5 | 3e-5 | |
评估结果 | 人工打分 | 1.81 | 1.93 | 1.90 | 1.93 | 1.96 |
3、模型调用对比示例
以下是在同样Prompt下分别使用ERNIE-Speed、和全量更新的Speed模型(Epoch=9,LR=3e-5)的调用结果对比。
Prompt | Response-ERNIE Speed | Response-ERNIE Speed 全量更新 |
---|---|---|
你是一位资深的数学老师,现在需要批改小学数学作业。接下来你会收到<题目>、<解析>以及<学生作业>,批改需要按以下几点要求严格执行: 1. 需要判断<学生作业>的答案和过程是正确还是错误,并给出判断理由。使用json格式输出{"判断理由": "过程和结果正确或者错误的理由", "过程": "直接输出正确或者错误", "结果": "直接输出正确或者错误"} 2. 在"判断理由"中判断过程时,参考<解析>部分,对学生作业中的解题步骤,一步步分析,仔细思考,判断步骤是否出错 3. 在"判断理由"中判断结果时,不管过程是否正确,只看最终答案是否正确。若过程错误,但答案正确,也判断为"正确" 示例:<题目>: 200 x 199 = ? <解析>: 这是一个基本的乘法问题,我们可以使用分配律来简化计算。 步骤如下: 将199写成200 - 1,这样我们可以将问题转化为200 x (200 - 1)。易得结果为39800。 所以,200 x 199 = 39800。 <学生作业>: 200 x 199 = 200 x (200 - 1) = 200 x 200 - 200 = 40000 - 200 = 38800 故答案为:39800 <输出>: {"判断理由": "学生的解题步骤基本正确,但在最后一步中出现了错误,40000 - 200应该等于39800,而不是38800。过程存在错误,然而,学生的最终答案却是39800,这是正确的。", "过程": "错误", "结果": "正确"} 现在请你判断以下题目: <题目>: 凯尔每天去打篮球练习两个小时。在训练中,他花一半的时间进行投篮训练,其余时间用于跑步和举重。如果他跑步的时间是举重时间的两倍,那么他花多少分钟举重呢? <解析>: 凯尔每天练习篮球两个小时,这等于120分钟。他一半的时间用于投篮训练,所以投篮训练是60分钟。剩下的时间用于跑步和举重,也就是60分钟。 设举重时间为x分钟,因为跑步时间是举重时间的两倍,所以跑步时间就是2x分钟。 跑步时间和举重时间的总和是60分钟,所以我们有方程式:x + 2x = 60 合并同类项,得到:3x = 60 解这个方程,得到:x = 60 / 3 x = 20,所以凯尔花了20分钟举重。 <学生作业>: 凯尔每天去打篮球练习两个小时。他花一半的时间投篮,剩下的时间跑步和举重。 跑步的时间是举重时间的两倍。我们要找出凯尔花多少分钟举重。假设凯尔每天练习的总时间为 T 小时,投篮时间为 S 小时,跑步时间为 R 小时,举重时间为 W 小时。 这里 T=2小时。 根据题目,我们可以建立以下方程: 1. 投篮时间是总时间的一半,即 S = T / 2。 2. 跑步和举重的时间总和是 T - S。 3. 跑步的时间是举重时间的两倍,即 R = 2 × W。 4. 跑步和举重的时间总和是 R + W = T - S。 现在我们要来解这个方程组,找出 W 的值,并转换为分钟。 计算结果为:举重时间 W = T/3 - 1/3 小时 所以,凯尔每天花 20T - 20 分钟举重。 由于题目中给出T=2小时,所以凯尔每天花 202 - 20 = 20 分钟举重。 <输出>: |
{ "判断理由": "学生在解题思路上是正确的,他/她通过设立变量并建立方程来解决问题。然而,在将结果转换为分钟时出现了错误。学生计算出举重时间W为T/3减去某个值的小时数,但在最后将小时转换为分钟时,他/她忽略了小时和分钟之间的换算关系(即1小时等于60分钟)。因此,最后的答案应该是错误的。", "过程": "错误", "结果": "错误" } |
{ "判断理由": "学生在解题过程中使用了方程组的方法,逻辑相对较为复杂且冗长,但虽然方法不同,仍然正确地达到了结论。学生的计算并没有错,也最终得出了正确的答案为20分钟。尽管描述过程中有些拗口和不必要的复杂性,但最终结果和预期相符。", "过程": "正确", "结果": "正确" } |
直接调用大模型把拗口和不必要的描述判定为错误的答案,不符合我们的输出预期。经过SFT后模型输出正确。
Prompt | Response-ERNIE Speed | Response-ERNIE Speed 全量更新 |
---|---|---|
你是一位资深的数学老师,现在需要批改小学数学作业。接下来你会收到<题目>、<解析>以及<学生作业>,批改需要按以下几点要求严格执行: 1. 需要判断<学生作业>的答案和过程是正确还是错误,并给出判断理由。使用json格式输出{"判断理由": "过程和结果正确或者错误的理由", "过程": "直接输出正确或者错误", "结果": "直接输出正确或者错误"} 2. 在"判断理由"中判断过程时,参考<解析>部分,对学生作业中的解题步骤,一步步分析,仔细思考,判断步骤是否出错 3. 在"判断理由"中判断结果时,不管过程是否正确,只看最终答案是否正确。若过程错误,但答案正确,也判断为"正确" 示例: <题目>: 200 x 199 = ? <解析>: 这是一个基本的乘法问题,我们可以使用分配律来简化计算。 步骤如下: 将199写成200 - 1,这样我们可以将问题转化为200 x (200 - 1)。易得结果为39800。 所以,200 x 199 = 39800。 <学生作业>: 200 x 199 = 200 x (200 - 1) = 200 x 200 - 200 = 40000 - 200 = 38800 故答案为:39800 <输出>: {"判断理由": "学生的解题步骤基本正确,但在最后一步中出现了错误,40000 - 200应该等于39800,而不是38800。过程存在错误,然而,学生的最终答案却是39800,这是正确的。", "过程": "错误", "结果": "正确"} 现在请你判断以下题目: <题目>: 盖尔有两个鱼缸。第一个鱼缸是第二个鱼缸的两倍大。第一个鱼缸里有48加仑的水。她遵循一英寸鱼所需的一加仑水规则。如果她在第二个鱼缸里养两英寸的鱼,在第一个鱼缸里养三英寸的鱼,如果第一个鱼缸的一条鱼吃掉了另一条鱼,那么第一个鱼缸里的鱼比第二个鱼缸里多多少条? <解析>: 要解决这个问题,我们需要遵循几个步骤: 1. 确定两个鱼缸的大小。 2. 根据规则计算每个鱼缸能容纳的鱼的数量。 3. 确定如果第一个鱼缸的一条鱼吃掉了另一条鱼后两个鱼缸的鱼的数量差。 首先,根据题目描述:第一个鱼缸的容量是第二个鱼缸的两倍,我们用V表示第二个鱼缸的容量,则第一个鱼缸的容量是2V。题目中提到第一个鱼缸有48加仑的水,所以2V = 48加仑,从而可得V(第二个鱼缸的容量)= 48 / 2 = 24加仑。 其次,根据一英寸鱼需要一加仑水的规则: 第一个鱼缸可以容纳48英寸的鱼。 第二个鱼缸可以容纳24英寸的鱼。 然后,考虑到鱼的大小: 第一个鱼缸养的是三英寸的鱼,所以它可以容纳48 / 3 = 16条鱼。 第二个鱼缸养的是两英寸的鱼,所以它可以容纳24 / 2 = 12条鱼。 最后,如果第一个鱼缸的一条鱼吃掉了另一条鱼,那么第一个鱼缸的鱼的数量将减少一条,变为15条。 现在,我们可以得出结论:第一个鱼缸里的鱼(15条)比第二个鱼缸里的(12条)多3条。 <学生作业>: 首先,我们根据题目描述分析盖尔的两个鱼缸及其鱼的情况。 1. 鱼缸大小与水量关系 第一个鱼缸是第二个鱼缸的两倍大,且已知第一个鱼缸里有48加仑的水。由此我们可以推断出第二个鱼缸有48加仑除以2即24加仑的水。 2. 水量和鱼的关系 根据题目中的“一英寸鱼所需的一加仑水”的规则,我们可以知道每个鱼所需的特定大小的水空间。这是计算每类鱼数量时需要考虑的要素。 3. 鱼的尺寸和数量 在第一个鱼缸里养了三英寸的鱼,在第二个鱼缸里养了两英寸的鱼。这意味着在第一个鱼缸中,每条鱼需要的空间是三英寸鱼的尺寸所对应的水量,在第二个鱼缸中则是两英寸鱼的尺寸所对应的水量。然而,题目中并未给出具体的鱼的数量,因此我们无法仅根据鱼的大小和水量来直接计算每缸的鱼的数量。 4. 第一个鱼缸中鱼的消失情况 题目中提到,第一个鱼缸里的一条鱼吃掉了另一条鱼。但这一情况并不影响我们对两个鱼缸中鱼的数量的比较,因为失去的只是其中的一条鱼,并不改变第一个鱼缸里鱼的总体数量。 综上所述,根据题目给出的信息,我们无法确切地知道第一个鱼缸里的鱼比第二个鱼缸里多多少条,因为题目中没有给出两个鱼缸各自鱼的准确数量。因此,我们无法得出具体的答案。 <输出>: |
{ "判断理由": "学生的解题步骤清晰,理解题目中的关系并尝试解决问题。然而,学生在理解“一英寸鱼所需的一加仑水”的规则上出现了偏差。根据这个规则,学生应该能够算出每个鱼缸能养多少条鱼,而不是纠结于鱼的数量未知。最后得出的结论是正确的,即无法从题目给出的信息确切知道第一个鱼缸里的鱼比第二个鱼缸里多多少条,因为未给出具体的鱼的数量。但是,学生的解题过程中在理解和应用规则方面出现了错误。", "过程": "错误", "结果": "正确" } |
{ "判断理由": "学生在解题步骤中未能正确计算出两个鱼缸中鱼的数量。解析中明确指出使用每加仑水养一英寸鱼的规则对于确定鱼的数量,即第一个鱼缸容纳16条鱼,第二个容纳12条。学生没有进行这一计算,而是错误地提到未能确定的鱼数量。因此,过程出现了误解和计算疏忽。尽管如此,学生的最终答案没有给出具体的额数,因此结果也应判断为错误。", "过程": "错误", "结果": "错误" } |
直接调用大模型把未能输出正确答案判定为了正确,不符合我们的输出预期。经过SFT后模型输出符合预期。
五、最佳实践总结
(一)精调经验总结
按照以上的步骤,就可以完成数学判题的模型精调,收获一个贴合业务场景的定制模型服务。整体来看我们需要做的工作有:
- 训练数据集:质量远比数量重要,数据应尽可能来自于线上真实业务数据,如果数据不足,可考虑通过大模型生成+人工Review的方式。但要保证数据能够覆盖业务的场景类别,尤其是能覆盖线上可能或者已经出现的badcase。
- 评估数据集:要与训练数据集分布类似,但不能与训练数据集一样。为了既要能反映线上高频使用效果,还要可以评测模型泛化能力。
- 模型训练:为保障精度,以场景表现好的模型为基础模型,使用SFT-全量更新方法,优先使用平台默认训练参数进行训练。如果模型没有收敛,但是Loss在下降,可以考虑增加Epoch来提升实验次数。
- 模型评估:基于明确的评估标准和计分方法,可以有效使用大规模参数的模型进行自动打分进行高效评估。
(二)精调收益总结
1、效果
我们以相同评估集、评估标准,使用自动打分和人工打分两种打分方式测试精调模型和ERNIE-Bot 4.0的效果。我们可以惊讶的发现,精调模型效果不仅优于基础模型Ernie-Speed效果,效果分数甚至比Ernie 4.0还要好。
ERINE Speed 全量更新 | ERINE Speed LoRA | ERINE-Speed | ERINE 4.0 | |
人工打分 | 1.96 | 1.93 | 1.75 | 1.90 |
2、费用
由于我们选择更高性价比的模型作为基础模型,通过精调使得模型在特定场景如作文自动点评中效果媲美甚至赶超超大规模参数的模型。因此精调模型的部署成本和调用成本都远比超大规模参数模型要低。
例如,本文选择了Ernie-Speed基础模型,经过调优后按照调用量付费模式,调用价格仅为0.005元/千tokens,ERNIE 4.0直接调用的价格为0.12元/千tokens。因此可看出,经过调优后的推理成本也将能够较大降低。
模型 | 调用成本 |
---|---|
ERNIE 4.0 | 0.12元/千tokens |
Ernie Speed SFT | 0.005元/千tokens |
如果您并发要求高且长期稳定调用,可选择租赁资源池的方式进行部署。在活动时间内2023年09月01日-2024年12月31日还有7折折扣限时优惠,单副本的QPS可根据您业务自行配置,最大可设置30QPS。
模块 | 原价 | 折后价 |
---|---|---|
资源池租赁 | 20元/时*算力单元 | 14元/时*算力单元 |
资源池租赁 | 250元/天*算力单元 | 175元/天*算力单元 |
总结来看,经过数据优化和模型精调,我们得到了一个又好又便宜的模型,赶快体验试试吧! 前往调优体验。