You’re an expert programmer with a detailed understanding of high quality coding practices, a technical focus and numerical computing. You always review the code I give to you before you answer to check if there would be code improvements needed to apply first before proceeding with your answer, and when you make code improvements consider the change weight and how it would effect working with large Tensors<float> or Tensor<Double> for large neural networks in real world application. You will think and plan step by step carefully for best possible answer with best performance and least memory footprint,  use ParallelizationThreshold when preferred or required for best practices. you will produce fully complete and working code. 
Please note this is class library on multiply file and large code bases If you want to know if certain parts method or properties exists ask and I will provide to you the current implementation, this is to help you on producing better quality code.

Programming language : c#
Application Type: class library
Describe the application in full (what does it do?): Neural network library

 public class Trainer<T> where T : unmanaged, INumber<T>, IExponentialFunctions<T>, ITrigonometricFunctions<T>, ILogarithmicFunctions<T>, IFloatingPointIeee754<T>, IRootFunctions<T>, IMinMaxValue<T> { public IRegularization<T>? Regularization { get; set; } public ILearningRateScheduler<T>? LearningRateScheduler { get; set; } public NeuralNetwork<T> Network { get; private set; } public ILossFunction<T> LossFunction { get; private set; } public IOptimizer<T> Optimizer { get; private set; } public ILogger<T> Logger { get; private set; } public int Epochs { get; set; } public int BatchSize { get; set; } public T GradientClippingThreshold { get; set; } = T.CreateChecked(1.0); public bool UseEarlyStopping { get; set; } = false; public int EarlyStoppingPatience { get; set; } = 5; public T BestValidationLoss { get; private set; } public int NoImprovementEpochs { get; private set; } public bool EnableCheckpointing { get; set; } = false; public string CheckpointDirectory { get; set; } = "checkpoints"; public int CheckpointInterval { get; set; } = 1; public event Action<int, int, T>? EpochCompleted; public event Action<int, int, T>? BatchCompleted; public Trainer( NeuralNetwork<T> network, ILossFunction<T> lossFunction, IOptimizer<T> optimizer, ILogger<T> logger, int epochs = 100, int batchSize = 32, IRegularization<T>? regularization = null, ILearningRateScheduler<T>? learningRateScheduler = null) { Network = network; LossFunction = lossFunction; Optimizer = optimizer; Logger = logger; Epochs = epochs; BatchSize = batchSize; BestValidationLoss = T.MaxValue; Regularization = regularization; LearningRateScheduler = learningRateScheduler; } public async Task TrainAsync( Tensor<T> trainingData, Tensor<T> trainingLabels, Tensor<T>? validationData = null, Tensor<T>? validationLabels = null, CancellationToken cancellationToken = default) { try { int numSamples = trainingData.Shape[0]; int totalBatches = (numSamples + BatchSize - 1) / BatchSize; Network.InitializeWeights(); for (int epoch = 0; epoch < Epochs; epoch++) { if (cancellationToken.IsCancellationRequested) { Logger.Log("Training cancelled.", LogLevel.Warning); break; } if (LearningRateScheduler != null) { var newLearningRate = LearningRateScheduler.GetLearningRate(epoch); Optimizer.LearningRate = newLearningRate; Logger.Log($"Adjusted learning rate to {newLearningRate} for epoch {epoch + 1}", LogLevel.Info); } var epochStartTime = DateTime.UtcNow; ShuffleData(ref trainingData, ref trainingLabels); var epochLoss = T.Zero; var batchIndices = Enumerable.Range(0, totalBatches).ToArray(); var batchDurations = new double[totalBatches]; var batchLosses = new T[totalBatches]; ParallelOptions parallelOptions = new ParallelOptions { CancellationToken = cancellationToken, MaxDegreeOfParallelism = totalBatches >= Tensor<T>.ParallelizationThreshold ? Environment.ProcessorCount : 1 }; Parallel.ForEach(batchIndices, parallelOptions, (batchIndex) => { var batchStartTime = DateTime.UtcNow; if (cancellationToken.IsCancellationRequested) { Logger.Log("Training cancelled during batch processing.", LogLevel.Warning); parallelOptions.CancellationToken.ThrowIfCancellationRequested(); } int batchStart = batchIndex * BatchSize; int currentBatchSize = Math.Min(BatchSize, numSamples - batchStart); var batchData = trainingData.Slice(batchStart, currentBatchSize); var batchLabels = trainingLabels.Slice(batchStart, currentBatchSize); var predictions = Network.Forward(batchData); T loss = LossFunction.ComputeLoss(predictions, batchLabels); if (Regularization != null) { foreach (var layer in Network.Layers) { foreach (var param in layer.GetParameters()) { loss = loss + Regularization.ComputePenalty(param); } } } var gradients = LossFunction.ComputeGradient(predictions, batchLabels); Backward(gradients); if (Regularization != null) { foreach (var layer in Network.Layers) { var parameters = layer.GetParameters().ToList(); var gradientsList = layer.GetGradients().ToList(); for (int i = 0; i < parameters.Count; i++) { var regGradient = Regularization.ComputeGradient(parameters[i]); gradientsList[i].AddInPlace(regGradient); } } } ClipGradients(); UpdateWeights(); ClearGradients(); batchLosses[batchIndex] = loss; var batchEndTime = DateTime.UtcNow; batchDurations[batchIndex] = (batchEndTime - batchStartTime).TotalSeconds; Logger.Log($"Epoch {epoch + 1}, Batch {batchIndex + 1}/{totalBatches} completed in {batchDurations[batchIndex]:F2}s. Loss: {loss}", LogLevel.Debug); BatchCompleted?.Invoke(epoch + 1, batchIndex + 1, loss); predictions.Dispose(); gradients.Dispose(); batchData.Dispose(); batchLabels.Dispose(); }); epochLoss = batchLosses.Aggregate(T.Zero, (acc, val) => acc + val) / T.CreateChecked(totalBatches); T validationLoss = T.Zero; if (validationData != null && validationLabels != null) { validationLoss = Evaluate(validationData, validationLabels); Logger.Log($"Epoch {epoch + 1}/{Epochs}, Training Loss: {epochLoss}, Validation Loss: {validationLoss}", LogLevel.Info); if (UseEarlyStopping) { if (validationLoss.CompareTo(BestValidationLoss) < 0) { BestValidationLoss = validationLoss; NoImprovementEpochs = 0; if (EnableCheckpointing) { SaveCheckpoint(epoch + 1, validationLoss); } } else { NoImprovementEpochs++; if (NoImprovementEpochs >= EarlyStoppingPatience) { Logger.Log("Early stopping triggered.", LogLevel.Warning); break; } } } } else { Logger.Log($"Epoch {epoch + 1}/{Epochs}, Training Loss: {epochLoss}", LogLevel.Info); } if (EnableCheckpointing && (epoch + 1) % CheckpointInterval == 0) { SaveCheckpoint(epoch + 1, validationLoss); } EpochCompleted?.Invoke(epoch + 1, Epochs, validationLoss); var epochEndTime = DateTime.UtcNow; var epochDuration = (epochEndTime - epochStartTime).TotalSeconds; Logger.Log($"Epoch {epoch + 1} completed in {epochDuration:F2}s.", LogLevel.Info); } } catch (OperationCanceledException) { Logger.Log("Training operation was cancelled.", LogLevel.Warning); } catch (Exception ex) { Logger.Log($"An exception occurred during training: {ex.Message}", LogLevel.Error); throw; } finally { } await Task.CompletedTask; } private void Backward(Tensor<T> gradients) { var grad = gradients; for (int i = Network.Layers.Count - 1; i >= 0; i--) { grad = Network.Layers[i].Backward(grad); } grad.Dispose(); } private void UpdateWeights() { foreach (var layer in Network.Layers) { layer.UpdateWeights(Optimizer); } } private void ClearGradients() { foreach (var layer in Network.Layers) { layer.ClearGradients(); } } private void ClipGradients() { foreach (var layer in Network.Layers) { foreach (var gradient in layer.GetGradients()) { gradient.ClipInPlace(-GradientClippingThreshold, GradientClippingThreshold); } } } public void ShuffleData(ref Tensor<T> data, ref Tensor<T> labels) { int numSamples = data.Shape[0]; int[] indices = Enumerable.Range(0, numSamples).ToArray(); Random rand = new Random(); for (int i = numSamples - 1; i > 0; i--) { int j = rand.Next(i + 1); int temp = indices[i]; indices[i] = indices[j]; indices[j] = temp; } data = data.Gather(indices); labels = labels.Gather(indices); } public T Evaluate(Tensor<T> data, Tensor<T> labels) { var predictions = Network.Forward(data); T loss = LossFunction.ComputeLoss(predictions, labels); return loss; } public void LoadCheckpoint(string checkpointPath) { Network.Load(checkpointPath); Logger.Log($"Checkpoint loaded from '{checkpointPath}'"); } private void SaveCheckpoint(int epoch, T validationLoss) { if (!Directory.Exists(CheckpointDirectory)) { Directory.CreateDirectory(CheckpointDirectory); } string checkpointPath = Path.Combine(CheckpointDirectory, $"model_epoch_{epoch}.ckpt"); Network.Save(checkpointPath); Logger.Log($"Checkpoint saved at '{checkpointPath}' with Validation Loss: {validationLoss}"); } private void SaveCheckpoint(NeuralNetwork<T> network, int epoch, T validationLoss) { if (!Directory.Exists(CheckpointDirectory)) { Directory.CreateDirectory(CheckpointDirectory); } string checkpointPath = Path.Combine(CheckpointDirectory, $"model_epoch_{epoch}.ckpt"); network.Save(checkpointPath); Logger.Log($"Checkpoint saved at '{checkpointPath}' with Validation Loss: {validationLoss}"); } }

write TrainerFactory<T> class