docs: Major documentation overhaul - restructure folders, update tagline, add Getting Started guide
- Restructure docs/ folder (remove numeric prefixes): - 04_USER_GUIDES -> guides/ - 05_API_REFERENCE -> api/ - 06_PHYSICS -> physics/ - 07_DEVELOPMENT -> development/ - 08_ARCHIVE -> archive/ - 09_DIAGRAMS -> diagrams/ - Replace tagline 'Talk, don't click' with 'LLM-driven optimization framework' in 9 files - Create comprehensive docs/GETTING_STARTED.md: - Prerequisites and quick setup - Project structure overview - First study tutorial (Claude or manual) - Dashboard usage guide - Neural acceleration introduction - Rewrite docs/00_INDEX.md with correct paths and modern structure - Archive obsolete files: - 01_PROTOCOLS.md -> archive/historical/01_PROTOCOLS_legacy.md - 03_GETTING_STARTED.md -> archive/historical/ - ATOMIZER_PODCAST_BRIEFING.md -> archive/marketing/ - Update timestamps to 2026-01-20 across all key files - Update .gitignore to exclude docs/generated/ - Version bump: ATOMIZER_CONTEXT v1.8 -> v2.0
This commit is contained in:
530
docs/guides/PHYSICS_LOSS_GUIDE.md
Normal file
530
docs/guides/PHYSICS_LOSS_GUIDE.md
Normal file
@@ -0,0 +1,530 @@
|
||||
# Physics Loss Functions Guide
|
||||
|
||||
**Selecting and configuring loss functions for AtomizerField training**
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
AtomizerField uses physics-informed loss functions to train neural networks that respect engineering principles. This guide explains each loss function and when to use them.
|
||||
|
||||
---
|
||||
|
||||
## Available Loss Functions
|
||||
|
||||
| Loss Function | Purpose | Best For |
|
||||
|--------------|---------|----------|
|
||||
| **MSE Loss** | Standard L2 error | General training, balanced outputs |
|
||||
| **Relative Loss** | Percentage error | Multi-scale outputs (MPa + mm) |
|
||||
| **Physics-Informed Loss** | Enforce physics | Better generalization, extrapolation |
|
||||
| **Max Error Loss** | Penalize outliers | Safety-critical applications |
|
||||
| **Combined Loss** | Weighted combination | Production models |
|
||||
|
||||
---
|
||||
|
||||
## 1. MSE Loss (Mean Squared Error)
|
||||
|
||||
### Description
|
||||
|
||||
Standard L2 loss that treats all predictions equally.
|
||||
|
||||
```python
|
||||
loss = mean((predicted - target)²)
|
||||
```
|
||||
|
||||
### Implementation
|
||||
|
||||
```python
|
||||
def mse_loss(predicted, target):
|
||||
"""Simple MSE loss"""
|
||||
return torch.mean((predicted - target) ** 2)
|
||||
```
|
||||
|
||||
### When to Use
|
||||
|
||||
- Starting point for new models
|
||||
- When all outputs have similar magnitudes
|
||||
- When you don't have physics constraints
|
||||
|
||||
### Pros & Cons
|
||||
|
||||
| Pros | Cons |
|
||||
|------|------|
|
||||
| Simple and stable | Ignores physics |
|
||||
| Fast computation | Scale-sensitive |
|
||||
| Well-understood | Large errors dominate |
|
||||
|
||||
---
|
||||
|
||||
## 2. Relative Loss
|
||||
|
||||
### Description
|
||||
|
||||
Computes percentage error instead of absolute error. Critical for multi-scale outputs.
|
||||
|
||||
```python
|
||||
loss = mean(|predicted - target| / |target|)
|
||||
```
|
||||
|
||||
### Implementation
|
||||
|
||||
```python
|
||||
def relative_loss(predicted, target, epsilon=1e-8):
|
||||
"""Relative (percentage) loss"""
|
||||
relative_error = torch.abs(predicted - target) / (torch.abs(target) + epsilon)
|
||||
return torch.mean(relative_error)
|
||||
```
|
||||
|
||||
### When to Use
|
||||
|
||||
- Outputs have different scales (stress in MPa, displacement in mm)
|
||||
- Percentage accuracy matters more than absolute accuracy
|
||||
- Training data has wide range of values
|
||||
|
||||
### Pros & Cons
|
||||
|
||||
| Pros | Cons |
|
||||
|------|------|
|
||||
| Scale-independent | Unstable near zero |
|
||||
| Intuitive (% error) | Requires epsilon |
|
||||
| Equal weight to all magnitudes | May overfit small values |
|
||||
|
||||
### Example
|
||||
|
||||
```python
|
||||
# Without relative loss
|
||||
stress_error = |100 MPa - 105 MPa| = 5 MPa
|
||||
displacement_error = |0.01 mm - 0.02 mm| = 0.01 mm
|
||||
# MSE dominated by stress, displacement ignored
|
||||
|
||||
# With relative loss
|
||||
stress_error = |5| / |100| = 5%
|
||||
displacement_error = |0.01| / |0.01| = 100%
|
||||
# Both contribute proportionally
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Physics-Informed Loss
|
||||
|
||||
### Description
|
||||
|
||||
Adds physics constraints as regularization terms. The network learns to satisfy physical laws.
|
||||
|
||||
```python
|
||||
loss = mse_loss + λ₁·equilibrium + λ₂·constitutive + λ₃·boundary
|
||||
```
|
||||
|
||||
### Implementation
|
||||
|
||||
```python
|
||||
def physics_informed_loss(predicted, target, data, config):
|
||||
"""
|
||||
Physics-informed loss with multiple constraint terms.
|
||||
|
||||
Components:
|
||||
1. Data loss (MSE)
|
||||
2. Equilibrium loss (F = ma)
|
||||
3. Constitutive loss (σ = Eε)
|
||||
4. Boundary condition loss (u = 0 at supports)
|
||||
"""
|
||||
|
||||
# Data loss
|
||||
data_loss = mse_loss(predicted, target)
|
||||
|
||||
# Equilibrium loss: sum of forces at each node = 0
|
||||
equilibrium_loss = compute_equilibrium_residual(
|
||||
predicted['displacement'],
|
||||
data.edge_index,
|
||||
data.stiffness
|
||||
)
|
||||
|
||||
# Constitutive loss: stress-strain relationship
|
||||
predicted_stress = compute_stress_from_displacement(
|
||||
predicted['displacement'],
|
||||
data.material,
|
||||
data.strain_operator
|
||||
)
|
||||
constitutive_loss = mse_loss(predicted['stress'], predicted_stress)
|
||||
|
||||
# Boundary condition loss: fixed nodes have zero displacement
|
||||
bc_mask = data.boundary_conditions > 0
|
||||
bc_loss = torch.mean(predicted['displacement'][bc_mask] ** 2)
|
||||
|
||||
# Combine with weights
|
||||
total_loss = (
|
||||
data_loss +
|
||||
config.lambda_equilibrium * equilibrium_loss +
|
||||
config.lambda_constitutive * constitutive_loss +
|
||||
config.lambda_bc * bc_loss
|
||||
)
|
||||
|
||||
return total_loss
|
||||
```
|
||||
|
||||
### Physics Constraints
|
||||
|
||||
#### Equilibrium (Force Balance)
|
||||
|
||||
At each node, the sum of forces must be zero:
|
||||
|
||||
```
|
||||
∑F = 0 at every node
|
||||
```
|
||||
|
||||
```python
|
||||
def equilibrium_residual(displacement, stiffness_matrix):
|
||||
"""
|
||||
Check if Ku = F (stiffness × displacement = force)
|
||||
Residual should be zero for valid solutions.
|
||||
"""
|
||||
internal_forces = stiffness_matrix @ displacement
|
||||
external_forces = get_external_forces()
|
||||
residual = internal_forces - external_forces
|
||||
return torch.mean(residual ** 2)
|
||||
```
|
||||
|
||||
#### Constitutive (Stress-Strain)
|
||||
|
||||
Stress must follow material law:
|
||||
|
||||
```
|
||||
σ = Eε (Hooke's law)
|
||||
```
|
||||
|
||||
```python
|
||||
def constitutive_residual(displacement, stress, material):
|
||||
"""
|
||||
Check if stress follows constitutive law.
|
||||
"""
|
||||
strain = compute_strain(displacement)
|
||||
predicted_stress = material.E * strain
|
||||
residual = stress - predicted_stress
|
||||
return torch.mean(residual ** 2)
|
||||
```
|
||||
|
||||
#### Boundary Conditions
|
||||
|
||||
Fixed nodes must have zero displacement:
|
||||
|
||||
```python
|
||||
def boundary_residual(displacement, bc_mask):
|
||||
"""
|
||||
Fixed nodes should have zero displacement.
|
||||
"""
|
||||
return torch.mean(displacement[bc_mask] ** 2)
|
||||
```
|
||||
|
||||
### When to Use
|
||||
|
||||
- When you need good generalization
|
||||
- When extrapolating beyond training data
|
||||
- When physical correctness is important
|
||||
- When training data is limited
|
||||
|
||||
### Pros & Cons
|
||||
|
||||
| Pros | Cons |
|
||||
|------|------|
|
||||
| Physics consistency | More computation |
|
||||
| Better extrapolation | Requires physics info |
|
||||
| Works with less data | Weight tuning needed |
|
||||
|
||||
### Weight Selection
|
||||
|
||||
| Constraint | Typical λ | Notes |
|
||||
|------------|-----------|-------|
|
||||
| Equilibrium | 0.1 - 0.5 | Most important |
|
||||
| Constitutive | 0.05 - 0.2 | Material law |
|
||||
| Boundary | 0.5 - 1.0 | Hard constraint |
|
||||
|
||||
---
|
||||
|
||||
## 4. Max Error Loss
|
||||
|
||||
### Description
|
||||
|
||||
Penalizes the worst predictions. Critical for safety-critical applications.
|
||||
|
||||
```python
|
||||
loss = max(|predicted - target|)
|
||||
```
|
||||
|
||||
### Implementation
|
||||
|
||||
```python
|
||||
def max_error_loss(predicted, target, percentile=99):
|
||||
"""
|
||||
Penalize worst predictions.
|
||||
Uses percentile to avoid single outlier domination.
|
||||
"""
|
||||
errors = torch.abs(predicted - target)
|
||||
|
||||
# Use percentile instead of max for stability
|
||||
max_error = torch.quantile(errors, percentile / 100.0)
|
||||
|
||||
return max_error
|
||||
```
|
||||
|
||||
### When to Use
|
||||
|
||||
- Safety-critical applications
|
||||
- When outliers are unacceptable
|
||||
- Quality assurance requirements
|
||||
- Certification contexts
|
||||
|
||||
### Pros & Cons
|
||||
|
||||
| Pros | Cons |
|
||||
|------|------|
|
||||
| Controls worst case | Unstable gradients |
|
||||
| Safety-focused | May slow convergence |
|
||||
| Clear metric | Sensitive to outliers |
|
||||
|
||||
---
|
||||
|
||||
## 5. Combined Loss (Production)
|
||||
|
||||
### Description
|
||||
|
||||
Combines multiple loss functions for production models.
|
||||
|
||||
```python
|
||||
loss = α·MSE + β·Relative + γ·Physics + δ·MaxError
|
||||
```
|
||||
|
||||
### Implementation
|
||||
|
||||
```python
|
||||
def combined_loss(predicted, target, data, config):
|
||||
"""
|
||||
Production loss combining multiple objectives.
|
||||
"""
|
||||
losses = {}
|
||||
|
||||
# MSE component
|
||||
losses['mse'] = mse_loss(predicted, target)
|
||||
|
||||
# Relative component
|
||||
losses['relative'] = relative_loss(predicted, target)
|
||||
|
||||
# Physics component
|
||||
losses['physics'] = physics_informed_loss(predicted, target, data, config)
|
||||
|
||||
# Max error component
|
||||
losses['max'] = max_error_loss(predicted, target)
|
||||
|
||||
# Weighted combination
|
||||
total = (
|
||||
config.alpha * losses['mse'] +
|
||||
config.beta * losses['relative'] +
|
||||
config.gamma * losses['physics'] +
|
||||
config.delta * losses['max']
|
||||
)
|
||||
|
||||
return total, losses
|
||||
```
|
||||
|
||||
### Recommended Weights
|
||||
|
||||
| Application | MSE (α) | Relative (β) | Physics (γ) | Max (δ) |
|
||||
|-------------|---------|--------------|-------------|---------|
|
||||
| General | 0.5 | 0.3 | 0.2 | 0.0 |
|
||||
| Multi-scale | 0.2 | 0.5 | 0.2 | 0.1 |
|
||||
| Safety-critical | 0.2 | 0.2 | 0.3 | 0.3 |
|
||||
| Extrapolation | 0.2 | 0.2 | 0.5 | 0.1 |
|
||||
|
||||
---
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
### Basic Training
|
||||
|
||||
```python
|
||||
# config.yaml
|
||||
loss:
|
||||
type: "mse"
|
||||
```
|
||||
|
||||
### Multi-Scale Outputs
|
||||
|
||||
```python
|
||||
# config.yaml
|
||||
loss:
|
||||
type: "combined"
|
||||
weights:
|
||||
mse: 0.2
|
||||
relative: 0.5
|
||||
physics: 0.2
|
||||
max_error: 0.1
|
||||
```
|
||||
|
||||
### Physics-Informed Training
|
||||
|
||||
```python
|
||||
# config.yaml
|
||||
loss:
|
||||
type: "physics_informed"
|
||||
physics_weight: 0.3
|
||||
constraints:
|
||||
equilibrium: 0.3
|
||||
constitutive: 0.1
|
||||
boundary: 0.5
|
||||
```
|
||||
|
||||
### Safety-Critical
|
||||
|
||||
```python
|
||||
# config.yaml
|
||||
loss:
|
||||
type: "combined"
|
||||
weights:
|
||||
mse: 0.2
|
||||
relative: 0.2
|
||||
physics: 0.3
|
||||
max_error: 0.3
|
||||
max_error_percentile: 99
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Training Strategies
|
||||
|
||||
### Curriculum Learning
|
||||
|
||||
Start simple, add complexity:
|
||||
|
||||
```python
|
||||
def get_loss_weights(epoch, total_epochs):
|
||||
"""Gradually increase physics loss weight"""
|
||||
|
||||
progress = epoch / total_epochs
|
||||
|
||||
if progress < 0.3:
|
||||
# Phase 1: Pure MSE
|
||||
return {'mse': 1.0, 'physics': 0.0}
|
||||
elif progress < 0.6:
|
||||
# Phase 2: Add physics
|
||||
physics_weight = (progress - 0.3) / 0.3 * 0.3
|
||||
return {'mse': 1.0 - physics_weight, 'physics': physics_weight}
|
||||
else:
|
||||
# Phase 3: Full physics
|
||||
return {'mse': 0.7, 'physics': 0.3}
|
||||
```
|
||||
|
||||
### Adaptive Weighting
|
||||
|
||||
Adjust weights based on loss magnitudes:
|
||||
|
||||
```python
|
||||
def adaptive_weights(losses):
|
||||
"""Balance losses to similar magnitudes"""
|
||||
|
||||
# Compute inverse of each loss (normalized)
|
||||
total = sum(losses.values())
|
||||
weights = {k: total / (v + 1e-8) for k, v in losses.items()}
|
||||
|
||||
# Normalize to sum to 1
|
||||
weight_sum = sum(weights.values())
|
||||
weights = {k: v / weight_sum for k, v in weights.items()}
|
||||
|
||||
return weights
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Loss Not Decreasing
|
||||
|
||||
**Symptom**: Training loss stays flat.
|
||||
|
||||
**Solutions**:
|
||||
1. Reduce learning rate
|
||||
2. Check data normalization
|
||||
3. Simplify loss (use MSE first)
|
||||
4. Increase model capacity
|
||||
|
||||
### Physics Loss Dominates
|
||||
|
||||
**Symptom**: Physics loss >> data loss.
|
||||
|
||||
**Solutions**:
|
||||
1. Reduce physics weight (λ)
|
||||
2. Use curriculum learning
|
||||
3. Check physics computation
|
||||
4. Normalize constraints
|
||||
|
||||
### Unstable Training
|
||||
|
||||
**Symptom**: Loss oscillates or explodes.
|
||||
|
||||
**Solutions**:
|
||||
1. Use gradient clipping
|
||||
2. Reduce learning rate
|
||||
3. Check for NaN in physics terms
|
||||
4. Add epsilon to divisions
|
||||
|
||||
---
|
||||
|
||||
## Metrics for Evaluation
|
||||
|
||||
### Training Metrics
|
||||
|
||||
```python
|
||||
metrics = {
|
||||
'train_loss': total_loss.item(),
|
||||
'train_mse': losses['mse'].item(),
|
||||
'train_physics': losses['physics'].item(),
|
||||
'train_max': losses['max'].item()
|
||||
}
|
||||
```
|
||||
|
||||
### Validation Metrics
|
||||
|
||||
```python
|
||||
def compute_validation_metrics(model, val_loader):
|
||||
"""Compute physics-aware validation metrics"""
|
||||
|
||||
all_errors = []
|
||||
physics_violations = []
|
||||
|
||||
for batch in val_loader:
|
||||
pred = model(batch)
|
||||
|
||||
# Prediction errors
|
||||
errors = torch.abs(pred - batch.y)
|
||||
all_errors.append(errors)
|
||||
|
||||
# Physics violations
|
||||
violations = compute_physics_residual(pred, batch)
|
||||
physics_violations.append(violations)
|
||||
|
||||
return {
|
||||
'val_mae': torch.cat(all_errors).mean(),
|
||||
'val_max': torch.cat(all_errors).max(),
|
||||
'val_physics_violation': torch.cat(physics_violations).mean(),
|
||||
'val_physics_compliance': (torch.cat(physics_violations) < 0.01).float().mean()
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Situation | Recommended Loss |
|
||||
|-----------|-----------------|
|
||||
| Starting out | MSE |
|
||||
| Multi-scale outputs | Relative + MSE |
|
||||
| Need generalization | Physics-informed |
|
||||
| Safety-critical | Combined with max error |
|
||||
| Limited training data | Physics-informed |
|
||||
| Production deployment | Combined (tuned) |
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Neural Features Complete](NEURAL_FEATURES_COMPLETE.md) - Overview
|
||||
- [GNN Architecture](GNN_ARCHITECTURE.md) - Model details
|
||||
- [Neural Workflow Tutorial](NEURAL_WORKFLOW_TUTORIAL.md) - Training guide
|
||||
Reference in New Issue
Block a user