Training Metric Monitoring and Profiling
The MindSpore Transformers dynamic graph (PyNative) provides two types of observability capabilities in the training loop:
Training metric monitoring (
monitor): collects fine-grained metrics such as gradient/norm and loss by micro-step (including the reserved configuration for MoE expert load monitoring). These metrics are currently output only through training logs (logger.info).Performance data collection (
profiler): collects performance data such as operators, communication, memory, and call stacks within a specified step range. The results are flushed to the disk as MindSpore Profiler products for performance analysis and optimization.
Both capabilities are enabled through the training YAML configuration and are independent of each other. They can be used separately. If you have not performed a training task, you are advised to read Quick Start first. For details about the overall structure of the configuration file, see Configuration File Description.
Output Description
Currently, dynamic graph monitoring metrics (in the
monitorsection) are written only to logs. Each record is printed byMonitor._flush_loggerin{ key: value, ... }format to the training log. To visualize the norm/loss curve, you need to parse the logs.
1 Training Metric Monitoring
The monitor section has two sub-configurations: train_state and moe_monitor.
The monitoring is instantiated by MonitorGroup during training initialization based on the configuration. Only the activated sub-monitoring is created: train_state is activated when any norm or loss switch is turned on; moe_monitor is activated when save_tokens_per_expert_interval is a positive integer. Metrics are flushed to logs by MonitorCallback (on_step_end) at the end of each training step.
1.1 train_state (Monitoring norm and loss)
The gradient norms and losses at the local and device levels are collected by micro-step. The four switches are independent of each other and can be combined in any way.
Parameter |
Data Type |
Required/Optional |
Default Value |
Description |
|---|---|---|---|---|
|
Boolean/String/list[str] |
Optional |
|
Specifies whether to monitor the local gradient norm. |
|
Boolean |
Optional |
|
Specifies whether to monitor the local loss of each micro-batch. |
|
Boolean/String/list[str] |
Optional |
|
Specifies whether to monitor the device-level (accumulated) gradient norm. The value meaning is the same as that of |
|
Boolean |
Optional |
|
Specifies whether to monitor the device-level loss (loss after gradient accumulation/all-reduce). |
device_norm forcibly collects local_norm.
In the source code, the accumulated norm of
device_normcomes from theprev_local_normscached duringlocal_normcollection. Therefore, as long asdevice_normis enabled, the framework computes the norm of the required parameters during thelocal_normcollection phase, even iflocal_normitself is disabled. Both of them independently filter the parameters to be printed based on their parameter name filters.
Output Interpretation
local_normis the incremental norm of this step. The framework maintains the accumulated normaccumulatedfor each parameter and performs differentiation by micro-step:micro-step 0:
actual_norm = accumulated;Subsequent steps:
actual_norm = accumulated – prev(previs the value ofaccumulatedin the previous micro-step). In this way, each record reflects the incremental contribution of the current micro-batch to the gradient norm, rather than the total norm up to the current point.
device_normis an accumulated value and directly prints the accumulated norm cached inprev_local_norms.Logs are printed by parameter, for example,
{ "local_norm": decoder.layers.0.self_attention.q_layernorm.weight: 0.000381 }. Before multiple norm records of the same micro-step are printed, a record in the format of{ "step": 4, "micro_step": 2 }is printed as a group marker.The
local_lossrecord is in the format of{ "micro_step": 2, "local_loss": 0.123 }.device_lossis recorded after the optimizer is updated.
Applicable Scenarios
Scenario A (Simplest): Viewing Only the Global Loss Curve
If you are only concerned about the overall convergence and do not need to compute the norm of each parameter, you do not need to enable any norm of train_state. The step-level loss is directly printed to logs by LossCallback of the framework. The monitoring segment can be left empty or only device_loss can be enabled.
monitor:
train_state:
device_loss: True # Print the reduced loss after each step optimizer is updated.
Scenario B: Checking for Gradient Explosion or Norm Exceptions at a Layer
When a loss spike or NaN occurs, or you suspect that the gradient at some layers is abnormal, use local_norm to filter and locate the fault layer by parameter name. The incremental norm semantics (see "Output Interpretation" above) allow you to check which micro-batch and which parameter are used to contribute to the abnormal increment.
monitor:
train_state:
# Only the incremental gradient norm of these types of parameters is monitored to avoid screen flooding by parameter.
local_norm: ["attention", "layernorm"]
local_loss: True # Observe the local loss of each micro-batch to locate abnormal micro-batches.
Note: To monitor all parameters, use
local_norm: True. To monitor a single parameter, use a string, for example,local_norm: "embedding".
Scenario C: Observing Accumulation by Using device_norm and local_norm Together
If you need to view both the incremental value per step and the total accumulated value, enable both of them. Note that device_norm reuses the collection result of local_norm (see the preceding note block).
monitor:
train_state:
local_norm: True # Incremental norm of each micro-step.
device_norm: True # Accumulated norm after gradient accumulation (local_norm collection is forcibly triggered).
device_loss: True
1.2 moe_monitor (MoE Expert Load)
This function is designed to monitor the number of tokens (tokens-per-expert) allocated to each expert at the MoE decoding layer and is used to diagnose uneven expert load. If a few experts are saturated for a long time and the rest are idle, the routing is unbalanced. In this case, you need to adjust the load balancing loss or routing strategy.
Parameter |
Data Type |
Required/Optional |
Default Value |
Description |
|---|---|---|---|---|
|
int / None |
Optional |
|
Prints the micro-step interval recorded by tokens-per-expert. You can set this parameter to |
|
Integer/list[int]/None |
Optional |
|
Specifies the target layer to be monitored. |
Semantic differences between int and list of target_layers
int(for example,3) is expanded torange(3), that is, layers 0, 1, and 2 of the model.
list[int](for example,[0, 2, 4]) is used to match these specified layer IDs.If the layer with the specified ID is not an MoE layer (without
tokens_per_expert), the alarmdecoder.layers.X is not a MoE layeris reported in the framework.
Output Format
MoE monitoring information is printed to logs in the tokens-per-expert JSON format of Megatron (one JSON record per line). The fields are described as follows.
Field |
Description |
|---|---|
|
Global micro-step count ( |
|
Training step ID. |
|
Sequence number of micro-batch in a step. |
|
|
|
Layer ID. |
|
MTP sublayer, which is displayed only for |
|
List of tokens allocated to each expert in the micro-batch (increment obtained based on expert differentiation). |
Configuration Example: MoE Load Imbalance Diagnosis
The configuration of the monitoring is as follows:
monitor:
moe_monitor:
save_tokens_per_expert_interval: 10 # Record every 10 micro-steps (design semantics).
target_layers: [0, 5, 10] # Sample the first, middle, and last layers to check for layer imbalance.
You can also use
target_layers: 4to monitor the first four layers, or omit this field to enable the framework to automatically discover all MoE layers (resulting in a larger data volume). The value ofsave_tokens_per_expert_intervalmust be a positive integer. IfTrueorFalseis used,TypeErrorwill be reported during configuration verification.
1.3 MaxLogits Health Monitoring (callbacks: MaxLogitsMonitor)
During large-scale long-term training, attention logits value overflow and gradient spikes can undermine training stability. MaxLogitsMonitor is a health monitoring callback for the actual values consumed in the dynamic graph. For the source code, see mindformers/pynative/callback/max_logits_monitor.py. Unlike the sub-configurations under the monitor section, it is registered through the callbacks section.
MaxLogitsMonitor collects the maximum attention logit of each layer of the model at each training step, prints the logit by layer to the log, and outputs the mean and maximum values of all layers. Then, it resets the accumulated value within each layer at the end of each step, facilitating the observation of whether the attention value increases abnormally step by step.
Output format (_dump and max_logits_monitor.py:88-108):
One line per layer. The log tag is
max_attention_logit/<param_name>, and the value is a list of items at the layer (four significant digits are retained).Two summary lines:
max_attention_logit/meanandmax_attention_logit/max.The prefix of each line is
step:[Current step/Total steps], which is the same as the printing format ofTrainingStateMonitor.
Application Scenarios
If you suspect that the loss jitter or NaN is caused by attention logits overflow or numerical explosion, use this function to locate the layer and step where the abnormal increase starts.
When the Muon optimizer is used and the QK clip is enabled (in this case, the framework automatically enables tracing, as described in the following scenario), use this function to verify whether the clip takes effect.
Parameter Configuration
Register the callback through the callbacks section.
callbacks:
- type: MaxLogitsMonitor
step_interval: 1 # Number of steps between outputs. The value must be a positive integer.
Parameter |
Data Type |
Required/Optional |
Default Value |
Description |
|---|---|---|---|---|
|
int |
Optional |
|
Number of steps between outputs. The value must be a positive integer. Otherwise, |
Behavior details (on_step_end and max_logits_monitor.py:58-78):
When
step_interval > 1and the current step is not an output step, only the accumulated value is reset and is not printed.In the output step,
get_max_attention_logit()of each layer is collected first. If no data is available, only the value is reset.Regardless of whether the value is printed,
reset_max_attention_logit()is called at the end of each step to clear the value, ensuring that the next step starts from a clean state.
Scenario: Enabling the Automatic Tracking with Muon and QK Clip
model.track_max_attention_logit is used to specify whether to collect the max attention logit on the model side. The framework automatically specifies whether to enable this function in configure_max_logits_tracking (max_logits_monitor.py:120-132).
The optimizer is Muon and
qk_clip_enabledisTrue.MaxLogitsMonitorhas been configured incallbacks.
When either of the preceding condition is met, track_max_attention_logit is set to True. This assembly is triggered during Trainer initialization (pynative/trainer/trainer.py:153), and ensures that a MaxLogitsMonitor is present in ensure_max_logits_reset_callback to handle the reset (trainer.py:536-538).
Therefore, in the Muon + QK clip scenario, even if no MaxLogitsMonitor is explicitly written, the framework automatically adds one for resetting. To view layer-by-layer logs, it is still recommended to explicitly write one and set step_interval.
# Scenario: Explicitly enabling layer-by-layer max logit observation in Muon + QK clip
optimizer:
type: Muon
qk_clip_enabled: True # Enable QK clip. The framework automatically enables track_max_attention_logit based on this setting.
callbacks:
- type: MaxLogitsMonitor
step_interval: 50 # Print the layer-by-layer max logit and mean/max every 50 steps.
Tracking (instead of printing) is automatically enabled.
For Muon + QK clip, only model-side tracking is automatically enabled, and a callback for resetting is added. To view the layer-by-layer numerical values in the logs, you still need to explicitly configure
MaxLogitsMonitor(or accept the step-by-step output of the defaultstep_interval=1).
2 Profile Data Collection
The profiler section controls profile data collection. The collection takes effect only when enable_profiling: True and the current rank hits profiler_rank. The framework uses MindSpore schedule to collect data within the [start_step,end_step] range. (Training outside the range is normal and no data is collected.)
Parameter |
Data Type |
Required/Optional |
Default Value |
Description |
|---|---|---|---|---|
|
Boolean |
Optional |
|
Specifies whether to enable profile data collection. |
|
int |
Optional |
|
Specifies the step at which collection starts (corresponding to |
|
int |
Optional |
|
Specifies the step at which collection ends. The value must be greater than or equal to |
|
String/None |
Optional |
|
Specifies the path for saving the result. The result is saved in |
|
list[int]/None |
Optional |
|
Specifies the list of rank IDs for which collection is enabled. |
|
int |
Optional |
|
Specifies the collection level, which can be |
|
Boolean |
Optional |
|
Specifies whether to enable lightweight mstx dotting (transparently transmitted to |
|
Boolean |
Optional |
|
Specifies whether to collect tensor memory data. |
|
Boolean |
Optional |
|
Specifies whether to collect CPU profiling activities. |
|
Boolean |
Optional |
|
Specifies whether to collect call stack data on the Python side. |
Avoiding the first step for warm-up during range selection
The first step usually includes one-off overheads such as compilation and initialization, and the collected data does not represent the steady state. You are advised to set
start_stepto several steps later (for example,start_step: 5) and collect data for only a few steps (for example, 2 to 3 steps) to avoid warm-up and control the product size and overhead.Overhead trade-off
profile_memory: Trueandwith_stack: Truesignificantly increase the collection overhead and product size, which may slow down the collected steps. Enable this option only when checking memory or call stack issues. It is recommended thatprofile_memorybe disabled when analyzing the time consumption of pure operators.
2.1 Rank Selection in Multi-Device Scenarios
In multi-device training, you do not need to collect data from all devices because the data volume is large and the data is redundant. You can use profiler_rank to specify the rank to be collected.
In single-device tuning, only
[0]is collected.When checking for communication imbalance, you can collect data from different ranks in the same parallel group for comparison (for example,
[0, 1]).For details about the mapping between ranks and parallel dimensions (dp/tp/pp/cp), see Distributed Parallel Training. Select representative ranks based on the mapping.
2.2 Output Directory and Viewing Method
Directory structure:
output_path/rank_x/ifoutput_pathis set;<cwd>/profile/rank_x/ifNoneis used.
2.3 Scenario-based Configuration (Three Levels Based on profiler_level)
Scenario A: Lightweight Operator-Level Analysis (Level 0)
Only the operator time consumption distribution is viewed, and hotspot kernels are located, resulting in the minimum overhead. Disable the memory and call stack.
profiler:
enable_profiling: True
start_step: 5 # Skip the first four warm-up steps.
end_step: 7 # Perform three-step collection.
profiler_rank: [0]
profiler_level: 0
profile_memory: False
profile_cpu: False
with_stack: False
output_path: "./output/profile"
Scenario B: Communication Analysis (Level 1)
If you need to analyze the communication time and the overlap between compute and communication in distributed training, increase the level to Level 1 and perform comparison collection for multiple ranks as required.
profiler:
enable_profiling: True
start_step: 5
end_step: 7
profiler_rank: [0, 1] # Comparison within the same group to observe communication imbalance.
profiler_level: 1
mstx: True # Lightweight dotting, facilitating alignment of key phases on the timeline.
profile_memory: False
with_stack: False
output_path: "./output/profile"
Scenario C: Including memory and call stack (Level 2)
To check the peak graphics memory usage or attribute time consumption to the Python call stack, enable Level 2 and enable memory and call stack collection (which has the highest overhead). In this case, you are advised to collect data for only a single device and a small number of steps):
profiler:
enable_profiling: True
start_step: 5
end_step: 6 # Perform two-step collection to control the size of the product.
profiler_rank: [0]
profiler_level: 2
profile_memory: True # Collect tensor memory.
with_stack: True # Collect the Python call stack.
output_path: "./output/profile"