RFT 评测
注意
仅当您为 Amazon Nova Forge 客户时,方可在自有 AWS 环境中通过远程奖励函数开展评测。
重要
rl_env 配置字段仅用于评测,不用于训练。训练期间,需要使用 reward_lambda_arn(单轮)或带有 rollout.delegate: true(多轮)的 BYOO 基础设施来配置奖励函数。
什么是 RFT 评测?
RFT 评测支持您在强化学习微调的训练前、训练中及训练后,通过自定义奖励函数评测模型性能。与采用预定义指标的标准评测不同,RFT 评测支持通过 Lambda 函数自定义成功标准,并依据您的具体需求对模型输出进行评分。
为什么要使用 RFT 进行评测?
评测对于判断强化微调过程是否实现以下目标至关重要:
-
提升模型与您特定使用案例及人类价值偏好的对齐程度
-
维持或提升模型在核心任务上的能力
-
避免产生非预期副作用,例如事实准确性下降、内容冗余度增加或其他任务性能退化
-
满足由奖励函数定义的自定义成功标准
何时使用 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 SageMaker 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>
预设奖励函数
两个预设的奖励函数(prime_code 和 prime_math)可用作 Lambda 层,以便与 RFT Lambda 函数集成。
概述
这些预设函数提供开箱即用的评测能力,适用于:
-
prime_code:代码生成和正确性评测
-
prime_math:数学推理和问题求解评测
快速设置
要使用预设奖励函数,请执行以下操作:
-
从 nova-custom-eval-sdk 版本
页面下载 Lambda 层 -
使用 AWS CLI 发布 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 -
在 AWS 管理控制台中将该层添加到 Lambda 函数(从自定义层中选择 preset-function-layer,并添加 AWSSDKPandas-Python312 以获取 numpy 依赖项)
-
在 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:必须与输入样本 ID 匹配 -
aggregate_reward_score:总体得分(通常为 0.0 到 1.0) -
metrics_list:各项指标组成的数组,包含以下字段:-
name:指标标识符(例如 accuracy、fluency) -
value:指标得分(通常为 0.0 到 1.0) -
type:Metric(用于报告)或 Reward(用于训练)
-
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 函数访问其他 AWS 服务(例如用于参考数据的 Amazon S3、用于日志记录的 DynamoDB),请将这些权限添加到 Lambda 执行角色。
执行评测作业
-
准备数据
-
根据数据格式要求对评测数据进行格式化
-
将 JSONL 文件上传到 Amazon 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 Logs:查看详细执行日志
-
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 训练过程中使用的指标
-
结果解读:数值越高通常表示性能越好(除非设计了反向指标)
性能基准
何种表现为“良好”取决于实际使用案例:
分数区间 |
评估结果 |
处理建议 |
|---|---|---|
0.8 – 1.0 |
优秀 |
模型可直接部署 |
0.6 – 0.8 |
良好 |
可进行小幅优化 |
0.4 – 0.6 |
一般 |
需大幅优化提升 |
0.0 – 0.4 |
较差 |
检查训练数据和奖励函数 |
重要
以上仅为通用准则。请根据业务需求、基线模型性能、领域特定约束以及进一步训练的成本效益分析,自行定义阈值。