本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
RFT 评估
注意
只有当你是 Nova Forge 的客户时,才能在自己的Amazon环境中通过远程奖励功能进行评估。
重要
rl_env配置字段仅用于评估,不用于训练。在训练期间,您可以使用reward_lambda_arn(单回合)或带有rollout.delegate: true(多回合)的 BYOO 基础设施配置奖励函数。
什么是 RFT 评估?
RFT 评估允许您在强化学习训练之前、期间或之后使用自定义奖励函数评估模型的性能。与使用预定义指标的标准评估不同,RFT 评估允许您通过 Lambda 函数定义自己的成功标准,该函数根据您的特定要求对模型输出进行评分。
为什么要使用 RFT 进行评估?
评估对于确定 RL 微调过程是否具有:
-
改进了模型与您的特定用例和人类价值观的一致性
-
维护或改进了模型在关键任务上的能力
-
避免了意想不到的副作用,例如减少事实、增加冗长或降低其他任务的性能
-
符合您的奖励功能所定义的自定义成功标准
何时使用 RFT 评估
在以下场景中使用 RFT 评估:
-
RFT 训练之前:在评估数据集上建立基线指标
-
在 RFT 训练期间:使用中间检查点监控训练进度
-
RFT 训练后:验证最终模型是否符合您的要求
-
比较模型:使用一致的奖励标准评估多个模型版本
注意
当您需要针对特定领域的自定义指标时,请使用 RFT 评估。对于通用评估(准确度、困惑度、BLEU),请使用标准评估方法。
数据格式要求
输入数据结构
RFT 评估输入数据必须遵循 OpenAI 强化微调格式。每个示例都是一个 JSON 对象,其中包含:
-
messages: 一系列与角色的对话回system合user -
可选的其他元数据,例如 reference_answer
数据格式示例
以下示例显示了所需的格式:
{ "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Solve for x. Return only JSON like {\"x\": <number>}. Equation: 2x + 5 = 13" } ] } ], "reference_answer": { "x": 4 } }
目前的局限性
以下限制适用于 RFT 评估:
-
仅限文本:不支持多模式输入(图像、音频、视频)
-
单回合对话:仅支持单用户消息(不支持多回合对话)
-
JSON 格式:输入数据必须采用 JSONL 格式(每行一个 JSON 对象)
-
模型输出:根据指定模型生成的补全进行评估
准备评估配方
示例配方配置
以下示例显示了完整的 RFT 评估方法:
run: name: nova-lite-rft-eval-job model_type: amazon.nova-lite-v1:0:300k model_name_or_path: s3://escrow_bucket/model_location # [MODIFIABLE] S3 path to your model or model identifier replicas: 1 # [MODIFIABLE] For SageMaker Training jobs only; fixed for HyperPod jobs data_s3_path: "" # [REQUIRED FOR HYPERPOD] Leave empty for SageMaker Training jobs output_s3_path: "" # [REQUIRED] Output artifact S3 path for evaluation results evaluation: task: rft_eval # [FIXED] Do not modify strategy: rft_eval # [FIXED] Do not modify metric: all # [FIXED] Do not modify # Inference Configuration inference: max_new_tokens: 8196 # [MODIFIABLE] Maximum tokens to generate top_k: -1 # [MODIFIABLE] Top-k sampling parameter top_p: 1.0 # [MODIFIABLE] Nucleus sampling parameter temperature: 0 # [MODIFIABLE] Sampling temperature (0 = deterministic) top_logprobs: 0 # Evaluation Environment Configuration (NOT used in training) rl_env: reward_lambda_arn: arn:aws:lambda:<region>:<account_id>:function:<reward-function-name>
预设奖励功能
我们已经将开源 verl
概述
这些预设函数为以下各项提供了 out-of-the-box评估功能:
-
prime_code:代码生成和正确性评估
-
prime_math:数学推理和问题解决评估
快速设置
要使用预设奖励功能,请执行以下操作:
-
使用 CLI Amazon 发布 Lambda 层:
aws lambda publish-layer-version \ --layer-name preset-function-layer \ --description "Preset reward function layer with dependencies" \ --zip-file fileb://universal_reward_layer.zip \ --compatible-runtimes python3.9 python3.10 python3.11 python3.12 \ --compatible-architectures x86_64 arm64 -
在Amazon控制台中将该层添加到您的 Lambda 函数( preset-function-layer从自定义层中选择并为 numpy 依赖项添加 AWSSDKPandas-Python 312)
-
导入并在您的 Lambda 代码中使用:
from prime_code import compute_score # For code evaluation from prime_math import compute_score # For math evaluation
prime_code 函数
目的:通过对照测试用例执行代码并测量正确性来评估 Python 代码生成任务。
来自评估的输入数据集格式示例:
{"messages":[{"role":"user","content":"Write a function that returns the sum of two numbers."}],"reference_answer":{"inputs":["3\n5","10\n-2","0\n0"],"outputs":["8","8","0"]}} {"messages":[{"role":"user","content":"Write a function to check if a number is even."}],"reference_answer":{"inputs":["4","7","0","-2"],"outputs":["True","False","True","True"]}}
主要特点:
-
自动从 markdown 代码块中提取代码
-
函数检测和基于调用的测试
-
使用超时保护执行测试用例
-
语法验证和编译检查
-
带有回溯功能的详细错误报告
prime_math 函数
目的:通过符号数学支持评估数学推理和解决问题的能力。
输入格式:
{"messages":[{"role":"user","content":"What is the derivative of x^2 + 3x?."}],"reference_answer":"2*x + 3"}
主要特点:
-
符号数学评估使用 SymPy
-
多种答案格式(LaTeX、纯文本、符号)
-
数学等效性检查
-
表达式规范化和简化
最佳实践
使用预设奖励功能时,请遵循以下最佳实践:
-
在测试用例中使用正确的数据类型(整数与字符串、布尔值与 “True”)
-
在代码问题中提供清晰的函数签名
-
在测试输入中包括边缘情况(零、负数、空输入)
-
在参考答案中统一设置数学表达式的格式
-
部署前使用示例数据测试您的奖励功能
创建你的奖励功能
Lambda ARN
您必须参考以下格式的 Lambda ARN:
"arn:aws:lambda:*:*:function:*SageMaker*"
如果 Lambda 没有此命名方案,则任务将失败并显示以下错误:
[ERROR] Unexpected error: lambda_arn must contain one of: ['SageMaker', 'sagemaker', 'Sagemaker'] when running on SMHP platform (Key: lambda_arn)
Lambda 函数结构
您的 Lambda 函数会接收成批的模型输出并返回奖励分数。以下是一个实现示例:
from typing import List, Any import json import re from dataclasses import asdict, dataclass @dataclass class MetricResult: """Individual metric result.""" name: str value: float type: str @dataclass class RewardOutput: """Reward service output.""" id: str aggregate_reward_score: float metrics_list: List[MetricResult] def lambda_handler(event, context): """ Main lambda handler """ return lambda_grader(event) def lambda_grader(samples: list[dict]) -> list[dict]: """ Core grader function """ scores: List[RewardOutput] = [] for sample in samples: print("Sample: ", json.dumps(sample, indent=2)) # Extract components idx = sample.get("id", "no id") if not idx or idx == "no id": print(f"ID is None/empty for sample: {sample}") ground_truth = sample.get("reference_answer") if "messages" not in sample: print(f"Messages is None/empty for id: {idx}") continue if ground_truth is None: print(f"No answer found in ground truth for id: {idx}") continue # Get model's response (last turn is assistant turn) last_message = sample["messages"][-1] if last_message["role"] != "nova_assistant": print(f"Last message is not from assistant for id: {idx}") continue if "content" not in last_message: print(f"Completion text is empty for id: {idx}") continue model_text = last_message["content"] # --- Actual scoring logic (lexical overlap) --- ground_truth_text = _extract_ground_truth_text(ground_truth) # Calculate main score and individual metrics overlap_score = _lexical_overlap_score(model_text, ground_truth_text) # Create two separate metrics as in the first implementation accuracy_score = overlap_score # Use overlap as accuracy fluency_score = _calculate_fluency(model_text) # New function for fluency # Create individual metrics metrics_list = [ MetricResult(name="accuracy", value=accuracy_score, type="Metric"), MetricResult(name="fluency", value=fluency_score, type="Reward") ] ro = RewardOutput( id=idx, aggregate_reward_score=overlap_score, metrics_list=metrics_list ) print(f"Response for id: {idx} is {ro}") scores.append(ro) # Convert to dict format result = [] for score in scores: result.append({ "id": score.id, "aggregate_reward_score": score.aggregate_reward_score, "metrics_list": [asdict(metric) for metric in score.metrics_list] }) return result def _extract_ground_truth_text(ground_truth: Any) -> str: """ Turn the `ground_truth` field into a plain string. """ if isinstance(ground_truth, str): return ground_truth if isinstance(ground_truth, dict): # Common patterns: { "explanation": "...", "answer": "..." } if "explanation" in ground_truth and isinstance(ground_truth["explanation"], str): return ground_truth["explanation"] if "answer" in ground_truth and isinstance(ground_truth["answer"], str): return ground_truth["answer"] # Fallback: stringify the whole dict return json.dumps(ground_truth, ensure_ascii=False) # Fallback: stringify anything else return str(ground_truth) def _tokenize(text: str) -> List[str]: # Very simple tokenizer: lowercase + alphanumeric word chunks return re.findall(r"\w+", text.lower()) def _lexical_overlap_score(model_text: str, ground_truth_text: str) -> float: """ Simple lexical overlap score in [0, 1]: score = |tokens(model) ∩ tokens(gt)| / |tokens(gt)| """ gt_tokens = _tokenize(ground_truth_text) model_tokens = _tokenize(model_text) if not gt_tokens: return 0.0 gt_set = set(gt_tokens) model_set = set(model_tokens) common = gt_set & model_set return len(common) / len(gt_set) def _calculate_fluency(text: str) -> float: """ Calculate a simple fluency score based on: - Average word length - Text length - Sentence structure Returns a score between 0 and 1. """ # Simple implementation - could be enhanced with more sophisticated NLP words = _tokenize(text) if not words: return 0.0 # Average word length normalized to [0,1] range # Assumption: average English word is ~5 chars, so normalize around that avg_word_len = sum(len(word) for word in words) / len(words) word_len_score = min(avg_word_len / 10, 1.0) # Text length score - favor reasonable length responses ideal_length = 100 # words length_score = min(len(words) / ideal_length, 1.0) # Simple sentence structure check (periods, question marks, etc.) sentence_count = len(re.findall(r'[.!?]+', text)) + 1 sentence_ratio = min(sentence_count / (len(words) / 15), 1.0) # Combine scores fluency_score = (word_len_score + length_score + sentence_ratio) / 3 return fluency_score
Lambda 请求格式
您的 Lambda 函数接收以下格式的数据:
[ { "id": "sample-001", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Do you have a dedicated security team?" } ] }, { "role": "nova_assistant", "content": [ { "type": "text", "text": "As an AI developed by Company, I don't have a dedicated security team in the traditional sense. However, the development and deployment of AI systems like me involve extensive security measures, including data encryption, user privacy protection, and other safeguards to ensure safe and responsible use." } ] } ], "reference_answer": { "compliant": "No", "explanation": "As an AI developed by Company, I do not have a traditional security team. However, the deployment involves stringent safety measures, such as encryption and privacy safeguards." } } ]
注意
消息结构包括嵌套content数组,与输入数据格式相匹配。最后一条带角色的消息nova_assistant包含模型生成的响应。
Lambda 响应格式
您的 Lambda 函数必须以以下格式返回数据:
[ { "id": "sample-001", "aggregate_reward_score": 0.75, "metrics_list": [ { "name": "accuracy", "value": 0.85, "type": "Metric" }, { "name": "fluency", "value": 0.90, "type": "Reward" } ] } ]
响应字段:
-
id: 必须与输入的样本编号相匹配 -
aggregate_reward_score: 总分(通常为 0.0 到 1.0) -
metrics_list: 单个指标的数组,包括:-
name: 指标标识符(例如,“准确性”、“流畅度”) -
value: 指标分数(通常为 0.0 到 1.0) -
type:“指标”(用于报告)或 “奖励”(用于培训)
-
IAM 权限
所需的权限
您的 SageMaker AI 执行角色必须具有调用您的 Lambda 函数的权限。将此策略添加到您的 SageMaker AI 执行角色:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": "arn:aws:lambda:region:account-id:function:function-name" } ] }
Lambda 执行角色
您的 Lambda 函数的执行角色需要基本的 Lambda 执行权限:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:*:*:*" } ] }
其他权限:如果您的 Lambda 函数访问其他Amazon服务(例如,Amazon S3 用于参考数据,DynamoDB 用于日志记录),请将这些权限添加到 Lambda 执行角色。
执行评估作业
-
准备您的数据
-
根据数据格式要求对评估数据进行格式化
-
将你的 JSONL 文件上传到亚马逊 S3:
s3://your-bucket/eval-data/eval_data.jsonl
-
-
配置您的食谱
使用您的配置更新示例配方:
-
设置
model_name_or_path到您的模特位置 -
设置
lambda_arn为你的奖励功能 ARN -
设置
output_s3_path为所需的输出位置 -
根据需要调整
inference参数
将食谱另存为
rft_eval_recipe.yaml -
-
运行评估
使用提供的笔记本执行评估工作:Nova 型号评估笔记本
-
监控进度
通过以下方式监控您的评估工作:
-
SageMaker AI 控制台:检查作业状态和日志
-
CloudWatch 日志:查看详细的执行日志
-
Lambda 日志:调试奖励函数问题
-
了解评估结果
输出格式
评估任务以 JSONL 格式将结果输出到您指定的 Amazon S3 位置。每行包含一个样本的评估结果:
{ "id": "sample-001", "aggregate_reward_score": 0.75, "metrics_list": [ { "name": "accuracy", "value": 0.85, "type": "Metric" }, { "name": "fluency", "value": 0.90, "type": "Reward" } ] }
注意
RFT 评估任务输出与 Lambda 响应格式相同。评估服务无需修改即可通过您的 Lambda 函数的响应,从而确保您的奖励计算与最终结果之间的一致性。
解析 结果
总奖励分数:
-
范围:通常为 0.0(最差)到 1.0(最佳),但取决于您的实现
-
用途:总结整体表现的单个数字
-
用法:比较模型,跟踪与训练相比的改进
个人指标:
-
指标类型:用于分析的信息指标
-
奖励类型:RFT 训练期间使用的指标
-
解释:值越高通常表示性能越好(除非您设计反向指标)
性能基准
什么构成 “良好” 性能取决于您的用例:
分数范围 |
口译 |
Action |
|---|---|---|
0.8-1.0 |
太棒了 |
模型已准备好部署 |
0.6-0.8 |
好 |
稍微的改进可能会有好处 |
0.4-0.6 |
一般 |
需要重大改进 |
0.0-0.4 |
可怜的 |
查看训练数据和奖励功能 |
重要
这些是一般指导方针。根据业务需求、基准模型性能、特定领域的限制以及对进一步培训的成本效益分析来定义自己的阈值。