Using Logs to Troubleshoot Job Errors
Complete the full lesson to earn 25 points
Work through each section, then tap “Mark as Complete” on the last one.
Lesson: Using Logs to Troubleshoot Model Training Job Errors
Introduction: The Critical Role of Logging in Machine Learning
In the lifecycle of machine learning development, the vast majority of your time is spent not in designing complex architectures, but in debugging failed training jobs. When you submit a training script to a remote cluster, a cloud provider, or even a local container, you are handing off control to an environment you cannot directly observe. If the job fails silently or crashes mid-epoch, you are left with a "black box" scenario. This is where logging becomes your most essential tool.
Logging is the systematic recording of events, errors, performance metrics, and state transitions during the execution of your code. Unlike standard print statements, which are often lost in the buffer or difficult to filter, structured logging allows you to reconstruct the history of a training job. It provides the breadcrumbs necessary to identify whether a failure was caused by a hardware bottleneck, a data pipeline corruption, an incompatible library version, or an exploding gradient. Understanding how to interpret these logs is the difference between a productive engineering workflow and hours of frustrating guesswork.
This lesson explores the strategies for implementing robust logging, interpreting common error patterns, and establishing a workflow for troubleshooting machine learning training jobs. We will look beyond basic debugging and move into the realm of observability, ensuring that your training infrastructure provides actionable insights whenever a job deviates from its expected path.
The Anatomy of Training Logs
To effectively troubleshoot, you must first understand what constitutes a useful log. A training log is more than just a stream of text; it is a time-series record of the internal state of your model. When a job fails, the logs are the primary diagnostic artifact you have.
Standard Output (stdout) vs. Standard Error (stderr)
Most training environments separate output into two streams. stdout is typically used for progress bars, epoch-level metrics, and informational messages. stderr is reserved for warnings and errors. A common mistake is to ignore stderr because the job "seems" to be running, or to have stdout cluttered with debugging information that hides the actual errors.
Structured Logging
Modern machine learning workflows benefit from structured logging, where messages are formatted as JSON or key-value pairs. Instead of logging a plain string like "Loss is 0.5", a structured logger produces:
{"timestamp": "2023-10-27T10:00:00Z", "level": "INFO", "epoch": 5, "loss": 0.5, "event": "training_step"}.
This format is machine-readable, allowing you to use external tools to query logs, build dashboards, and set up alerts for specific error thresholds.
Callout: Logging vs. Monitoring While both are essential, they serve different purposes. Logging provides a granular, event-based history of what happened inside the code, which is vital for post-mortem debugging. Monitoring provides a high-level view of system health, such as GPU temperature or CPU utilization over time, which is better for detecting long-term trends or infrastructure bottlenecks.
Setting Up Effective Logging in Python
Python’s built-in logging module is the industry standard for production-grade scripts. Avoid using print() statements for anything other than quick, temporary local testing. The logging module offers levels of severity (DEBUG, INFO, WARNING, ERROR, CRITICAL), which allow you to filter the noise.
Implementing a Basic Logging Configuration
Below is a template for setting up a logger that outputs to both the console and a file. This is useful because it allows you to see progress in real-time while maintaining a persistent record for after-the-fact analysis.
import logging
import sys
def setup_logger(log_file='training.log'):
logger = logging.getLogger('training_logger')
logger.setLevel(logging.DEBUG)
# Create file handler
file_handler = logging.FileHandler(log_file)
file_handler.setLevel(logging.DEBUG)
# Create console handler
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.INFO)
# Create formatter and add to handlers
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# Add handlers to the logger
logger.addHandler(file_handler)
logger.addHandler(console_handler)
return logger
# Usage in a training script
logger = setup_logger()
logger.info("Starting training job...")
try:
# Training logic here
pass
except Exception as e:
logger.error(f"Training failed with error: {e}", exc_info=True)
Note: The
exc_info=Trueargument in the logger is crucial. It tells the logger to capture the full stack trace of the exception. Without this, you only get the error message, which is often insufficient for debugging complex deep learning models.
Common Error Patterns and How to Interpret Them
When you encounter an error during training, the stack trace is your map. However, stack traces can be intimidating. Learning to identify the "source of truth" in a trace is a critical skill.
1. The Out-of-Memory (OOM) Error
OOM errors are the most common issue in deep learning. They often manifest as a RuntimeError: CUDA out of memory.
- How to spot it: The log will show a sudden termination of the training loop, typically right after a
loss.backward()call. - Troubleshooting: Check the logs for the exact moment the memory usage spiked. You may need to reduce the batch size, use gradient accumulation, or enable mixed-precision training.
- Prevention: Log your GPU memory usage periodically. Many libraries like PyTorch allow you to print
torch.cuda.memory_summary()at the end of each epoch to track memory growth.
2. The Exploding/Vanishing Gradient Error
This error often doesn't crash the script immediately but causes the loss to become NaN (Not a Number).
- How to spot it: The training logs will show the loss value transitioning from a valid number to
NaNorinf. - Troubleshooting: Check your learning rate. If it is too high, the gradients may grow uncontrollably. Consider implementing gradient clipping.
- Log Strategy: Include a check in your training loop that logs an alert if
lossisNaN.
3. Data Pipeline Stalls
Sometimes, a training job hangs indefinitely. The logs show the model is "running," but no progress is made.
- How to spot it: The timestamps in your logs will stop updating, or you will see a large gap between log entries.
- Troubleshooting: This is usually a deadlock in the data loader. It often happens when using multiple workers (
num_workers > 0) in a PyTorchDataLoader. Try settingnum_workers=0as a test; if the problem disappears, you have a multi-processing issue.
Best Practices for Logging Training Jobs
To make your logs useful, you must establish consistent habits. A disorganized set of log files is almost as bad as having no logs at all.
1. Log Hyperparameters at the Start
Always log the configuration of your job (batch size, learning rate, optimizer, model architecture) before the training loop starts. If you look at a log file six months later, you need to know exactly what parameters were used to produce those results.
2. Use Unique Run Identifiers
If you are running multiple experiments, ensure each one creates a unique log file. Use a timestamp or a unique job ID in the filename. This prevents overwriting logs from previous runs and makes it easy to compare results.
3. Log Hardware Utilization
Include periodic logs of system metrics such as GPU utilization, CPU load, and disk I/O. If a job is slow, these logs will tell you if the bottleneck is the GPU (compute-bound) or the data loader (I/O-bound).
4. Implement Health Checks
At the start of your script, log the versions of your core libraries (torch, tensorflow, numpy, cuda). Version mismatches are a frequent cause of "ghost" bugs that appear only on specific training nodes.
Tip: Use a centralized logging service if you are working in a team or a distributed environment. While writing to local files is fine for simple scripts, services like Weights & Biases, MLflow, or cloud-native tools (like AWS CloudWatch or Google Cloud Logging) allow you to aggregate logs from multiple nodes into a single, searchable interface.
Step-by-Step: Debugging a Failed Job
When you receive a notification that a training job has failed, follow this systematic approach to isolate the cause:
- Check the Job Status: Confirm if the process exited with a non-zero exit code. A code like
137usually indicates an Out-of-Memory (OOM) kill by the operating system, whereas other codes might point to a Python exception. - Scan the Tail of the Log: Always look at the last 50 lines of the log file first. The most recent log entries contain the exception that caused the crash.
- Search for "Exception" or "Error": Use a text search tool (like
grepor the search function in your text editor) to look for these keywords throughout the entire log file to ensure you haven't missed earlier warnings that led to the final crash. - Review the Stack Trace: Identify the line of code that triggered the error. Is it in your training script, or is it in a library (like
torchorpandas)? If it is in a library, check if you are passing the expected inputs (e.g., shapes of tensors). - Replicate Locally: Attempt to reproduce the error on a smaller scale. If the job fails after 100 epochs on a cluster, try running it for 5 epochs on your local machine with a subset of the data.
- Verify Environment Parity: Compare the log of the failed job with a known successful job. Look for differences in library versions, file paths, or environment variables.
Comparison of Logging Approaches
Depending on the complexity of your project, you might choose different strategies for managing your logs.
| Approach | Best For | Pros | Cons |
|---|---|---|---|
| Standard Print | Rapid prototyping | Extremely easy to implement | No persistence, hard to filter, lost in production |
| Python Logging | Standard scripts | Flexible, configurable, standard library | Requires setup, can be verbose |
| Experiment Tracking | Research/Production | Automatic logging, visualization, searchable | Requires external dependencies |
| System/Cloud Logs | Distributed training | Centralized, persistent, searchable | Can incur costs, requires configuration |
Common Pitfalls and How to Avoid Them
Even experienced engineers fall into common traps when setting up logging. Avoiding these will save you significant time during the training phase.
Pitfall 1: Buffering Issues
By default, Python may buffer your log output. If your program crashes, the buffered logs might not be written to the disk, meaning you lose the final (and most important) lines of output.
- The Fix: Always set your log handlers to flush immediately or disable buffering, especially in distributed environments where output streams are often redirected.
Pitfall 2: Over-Logging
Logging every single training step in a high-frequency loop can create gigabytes of text files, which slows down the training process and makes the log files impossible to parse.
- The Fix: Log progress at defined intervals (e.g., every 100 steps or once per epoch). Reserve detailed logging for debugging sessions only.
Pitfall 3: Ignoring Warnings
Warnings (e.g., "UserWarning: TypedStorage is deprecated") are often ignored because they don't break the code. However, they can signal that your code is relying on outdated patterns that may break in future library versions.
- The Fix: Treat warnings as "soft errors." Periodically review them and address them to keep your codebase clean and future-proof.
Pitfall 4: Hardcoding Paths
Hardcoding log paths like /home/user/logs/ will cause your code to fail when run on a different machine or in a containerized environment.
- The Fix: Use relative paths or environment variables to define where logs should be stored. Ensure your script checks if the directory exists and creates it if necessary.
Warning: Never log sensitive information. This includes API keys, database credentials, or private user data. If you are using a cloud-based logging service, this information will be stored in plain text and could be accessed by others. Always use environment variables or secret management tools for sensitive configuration.
Advanced Troubleshooting: Analyzing Distributed Training Logs
When you move to multi-GPU or multi-node training, logging becomes exponentially more complex. You are no longer dealing with one log file, but potentially dozens.
The "Stuck Worker" Problem
In distributed training, one node might fail while others continue to run. If you are only looking at the "primary" node's logs, you might not see the error occurring on a worker node.
- Strategy: Implement a logging strategy that aggregates logs from all ranks. Most distributed frameworks (like
torch.distributed) provide utilities to identify which rank is producing which log entry. Always prefix your log lines with the Rank ID, for example:[Rank 0] INFO: Epoch 1 starting....
Network Timeouts
Distributed training relies on nodes communicating with each other. A network glitch can cause a timeout that is difficult to diagnose.
- Strategy: Look for socket errors or connection resets in your logs. If you see these, the issue is likely infrastructure-related (e.g., firewall, network congestion) rather than a bug in your model code.
Summary of Best Practices for Production
- Standardize: Use the
loggingmodule consistently across all your training projects. - Contextualize: Always include timestamps, log levels, and contextual information (e.g., epoch, batch index, worker ID).
- Persist: Ensure logs are written to a persistent location, not just the temporary directory of the training container.
- Monitor: Use tools that can trigger alerts if error rates spike or if a job stops sending heartbeats.
- Review: Periodically audit your logs to identify and resolve recurring warnings or inefficient patterns.
- Clean: Implement a log rotation strategy so that your disk doesn't fill up with old, unused log files.
Key Takeaways
- Logs are your primary diagnostic tool: Treat them as first-class citizens in your development process. A training job without logging is a job you cannot debug.
- Use the
loggingmodule: Move away fromprint()statements immediately. Theloggingmodule provides the structure and severity levels needed for professional-grade debugging. - Capture the stack trace: Always configure your logger to capture
exc_infowhen an exception occurs. Knowing where the crash happened is 90% of the battle. - Identify the failure pattern: Learn to recognize common issues like OOM errors,
NaNlosses, and data pipeline stalls by the patterns they leave in your log files. - Aggregated logging is essential for scale: As you move to multi-GPU or distributed training, ensure your logs are properly tagged with rank IDs and aggregated into a central location.
- Proactive maintenance: Address warnings and "soft errors" before they become critical failures. Keep your environment and library versions consistent across all training runs.
- Security first: Never log sensitive information. Ensure your logging configuration handles secrets safely and complies with your organization's data privacy policies.
By mastering the art of logging, you transition from simply "running code" to "managing experiments." This maturity allows you to iterate faster, maintain more complex models, and ultimately build more reliable machine learning systems. When a job fails—and it will—you will have the confidence to open the logs, find the culprit, and get back to training in minutes rather than hours.
Continue the course
Enjoying the courses?
Everything stays free. Pro shows fewer ads, doubles your daily points limit so you progress twice as fast, and lets you read each lesson on one page.
- ✓ Fewer advertisements
- ✓ 2× daily points limit
- ✓ Distraction-free lessons