|
| 1 | +# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. |
| 2 | +# |
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +# you may not use this file except in compliance with the License. |
| 5 | +# You may obtain a copy of the License at |
| 6 | +# |
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +# |
| 9 | +# Unless required by applicable law or agreed to in writing, software |
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +# See the License for the specific language governing permissions and |
| 13 | +# limitations under the License. |
| 14 | + |
| 15 | +import argparse |
| 16 | +import os |
| 17 | + |
| 18 | +import pandas as pd |
| 19 | +import wandb |
| 20 | + |
| 21 | + |
| 22 | +def parse_args(): |
| 23 | + parser = argparse.ArgumentParser() |
| 24 | + parser.add_argument("--org", type=str, default="nvidia") |
| 25 | + parser.add_argument("--project", type=str, default="nemo-rl") |
| 26 | + parser.add_argument("--uid", type=str, required=True) |
| 27 | + parser.add_argument("--at-step", type=int, default=5) |
| 28 | + parser.add_argument("--average-steps", type=int, default=5) |
| 29 | + return parser.parse_args() |
| 30 | + |
| 31 | + |
| 32 | +def main(): |
| 33 | + args = parse_args() |
| 34 | + assert os.environ.get("WANDB_API_KEY") is not None, "WANDB_API_KEY is not set" |
| 35 | + |
| 36 | + keys = [ |
| 37 | + "train/mean_total_tokens_per_sample", |
| 38 | + "timing/train/total_step_time", |
| 39 | + "timing/train/generation", |
| 40 | + "timing/train/exposed_generation", |
| 41 | + "timing/train/policy_training", |
| 42 | + "timing/train/policy_and_reference_logprobs", |
| 43 | + "timing/train/weight_sync", |
| 44 | + "timing/train/prepare_for_generation/total", |
| 45 | + "timing/train/prepare_for_generation/transfer_and_update_weights", |
| 46 | + "performance/tokens_per_sec_per_gpu", |
| 47 | + "performance/generation_tokens_per_sec_per_gpu", |
| 48 | + "performance/training_worker_group_tokens_per_sec_per_gpu", |
| 49 | + "performance/policy_training_tokens_per_sec_per_gpu", |
| 50 | + "performance/policy_and_reference_logprobs_tokens_per_sec_per_gpu", |
| 51 | + "performance/train_flops_per_gpu", |
| 52 | + "performance/train_fp_utilization", |
| 53 | + "timing/train/checkpointing", |
| 54 | + ] |
| 55 | + api = wandb.Api() |
| 56 | + run = api.run(f"{args.org}/{args.project}/{args.uid}") |
| 57 | + min_step = args.at_step - args.average_steps // 2 |
| 58 | + max_step = args.at_step + args.average_steps // 2 - 1 + (args.average_steps % 2) |
| 59 | + raw_history = run.history() |
| 60 | + |
| 61 | + # Newer wandb versions (or environments without pandas at import time) may |
| 62 | + # return a list of per-step dicts instead of a DataFrame. |
| 63 | + if not isinstance(raw_history, pd.DataFrame): |
| 64 | + raw_history = pd.DataFrame(list(raw_history)) |
| 65 | + missing_keys = [key for key in keys if key not in raw_history.columns] |
| 66 | + if missing_keys: |
| 67 | + print(f"Warning: skipping keys not found in history: {missing_keys}") |
| 68 | + keys = [key for key in keys if key in raw_history.columns] |
| 69 | + history = raw_history.loc[min_step:max_step, keys] |
| 70 | + |
| 71 | + # get average of the history |
| 72 | + average_history = history.mean(axis=0) |
| 73 | + history_at_step = history.loc[args.at_step] |
| 74 | + print("Average history:") |
| 75 | + print(average_history) |
| 76 | + print("History at step:") |
| 77 | + print(history_at_step) |
| 78 | + |
| 79 | + |
| 80 | +if __name__ == "__main__": |
| 81 | + main() |
0 commit comments