Implement a custom training loop in PyTorch that incorporates gradient clipping and mixed-precision training. For dependency-free grading, implement the numerical core with JSON-compatible linear-model weights and batches, preserving the required order: scale gradients, unscale them, clip their norm, then update parameters. Return the final weights and average loss for each epoch using custom_training_loop(weights, batches, learning_rate, max_grad_norm, epochs, loss_scale).
def custom_training_loop(weights, batches, learning_rate, max_grad_norm, epochs, loss_scale):