public class NeuralNetwork where T : unmanaged, INumber, IExponentialFunctions, ITrigonometricFunctions, ILogarithmicFunctions, IFloatingPointIeee754, IRootFunctions { private readonly List<ILayer> layers; private readonly ILossFunction lossFunction; private readonly IOptimizer optimizer; private readonly IRegularization? regularization; public NeuralNetwork(ILossFunction lossFunction, IOptimizer optimizer, IRegularization? regularization = null) { layers = new List<ILayer>(); this.lossFunction = lossFunction; this.optimizer = optimizer; this.regularization = regularization; } public void AddLayer(ILayer layer) { layers.Add(layer); } public Tensor Predict(Tensor input) { var output = input; foreach (var layer in layers) { output = layer.Forward(output); } return output; } public void Train(Tensor input, Tensor target, int epochs, int batchSize) { int numSamples = input.Shape[0]; for (int epoch = 0; epoch < epochs; epoch++) { for (int batchStart = 0; batchStart < numSamples; batchStart += batchSize) { int currentBatchSize = Math.Min(batchSize, numSamples - batchStart); var batchInput = input.Slice(batchStart, currentBatchSize); var batchTarget = target.Slice(batchStart, currentBatchSize); var output = batchInput; foreach (var layer in layers) { output = layer.Forward(output); } T loss = lossFunction.ComputeLoss(output, batchTarget); if (regularization != null) { foreach (var layer in layers) { foreach (var param in layer.GetParameters()) { loss += regularization.ComputePenalty(param); } } } Console.WriteLine($"Epoch {epoch + 1}, Batch {batchStart / batchSize + 1}, Loss: {loss}"); var gradOutput = lossFunction.ComputeGradient(output, batchTarget); for (int i = layers.Count - 1; i >= 0; i--) { gradOutput = layers[i].Backward(layers[i] == layers[0] ? batchInput : layers[i - 1].Forward(batchInput), gradOutput); } foreach (var layer in layers) { var parameters = layer.GetParameters().ToList(); var gradients = layer.GetGradients().ToList(); for (int i = 0; i < parameters.Count; i++) { if (regularization != null) { var regGrad = regularization.ComputeGradient(parameters[i]); gradients[i].AddInPlace(regGrad); } optimizer.Update(parameters[i], gradients[i]); } } } } } }

public interface ILayer where T : unmanaged, INumber, IExponentialFunctions, ITrigonometricFunctions, ILogarithmicFunctions, IFloatingPointIeee754, IRootFunctions { Tensor Forward(Tensor input); Tensor Backward(Tensor input, Tensor gradOutput); IEnumerable<Tensor> GetParameters(); IEnumerable<Tensor> GetGradients(); }

public class DenseLayer : ILayer where T : unmanaged, INumber, IExponentialFunctions, ITrigonometricFunctions, ILogarithmicFunctions, IFloatingPointIeee754, IRootFunctions { private readonly int inputSize; private readonly int outputSize; private readonly IInitializer weightInitializer; private readonly IInitializer biasInitializer; private readonly IActivationFunction activationFunction; private Tensor weights; private Tensor biases; private Tensor gradWeights; private Tensor gradBiases; private Tensor? lastInput; private Tensor? lastPreActivation; public DenseLayer(int inputSize, int outputSize, IActivationFunction activationFunction, IInitializer? weightInitializer = null, IInitializer? biasInitializer = null) { this.inputSize = inputSize; this.outputSize = outputSize; this.activationFunction = activationFunction; weightInitializer ??= new XavierInitializer(); biasInitializer ??= new RandomUniformInitializer(T.Zero, T.Zero); weights = new Tensor(inputSize, outputSize); biases = new Tensor(1, outputSize); weightInitializer.Initialize(weights); biasInitializer.Initialize(biases); } public Tensor Forward(Tensor input) { lastInput = input; var preActivation = input.MatMul(weights).Add(biases); lastPreActivation = preActivation; return activationFunction.Forward(preActivation); } public Tensor Backward(Tensor input, Tensor gradOutput) { if (lastInput == null || lastPreActivation == null) throw new InvalidOperationException("Forward must be called before Backward."); var gradPreActivation = activationFunction.Backward(lastPreActivation, gradOutput); gradWeights = lastInput.Transpose(0, 1).MatMul(gradPreActivation); gradBiases = gradPreActivation.Sum(axis: 0).ExpandDims(0); var gradInput = gradPreActivation.MatMul(weights.Transpose(0, 1)); return gradInput; } public IEnumerable<Tensor> GetParameters() { yield return weights; yield return biases; } public IEnumerable<Tensor> GetGradients() { yield return gradWeights; yield return gradBiases; } public string Display() { StringBuilder txt = new StringBuilder(); return txt.ToString(); } }

based on best practices and academic knowledge in software engineering and numerical computing implement the following improvements:
NeuralNetwork Class
Properties:
List<ILayer<T>> Layers
Tensor<T> Input
Tensor<T> Output
int InputSize
int OutputSize
int TotalParameters
ILogger Logger
IVisualizer<T> Visualizer
Methods:
void AddLayer(ILayer<T> layer)
void RemoveLayer(int index)
void InitializeWeights(Random rng)
Tensor<T> Forward(Tensor<T> input)
void Backward(Tensor<T> gradients)
void Train(Tensor<T> input, Tensor<T> target, int epochs, int batchSize)
void Save(string filePath)
void Load(string filePath)
void LogMetrics()
void Visualize()
void UpdateWeights(T learningRate)
void SetLearningRate(T learningRate)
void ClearGradients()

Layer Class DenseLayer<T> : ILayer<T>
Properties:
Tensor<T> Weights
Tensor<T> Biases
Tensor<T> Input
Tensor<T> Output
Tensor<T> Gradients
IInitializer<T> weightInitializer;
 IInitializer<T> biasInitializer;
IActivationFunction<T> Activation
string Name
ILogger Logger
int NumInputs
int NumOutputs
int TotalParameters
Methods:
void Initialize(Random rng)
Tensor<T> Forward(Tensor<T> input)
Tensor<T> Backward(Tensor<T> outputGradient)
void UpdateWeights(T learningRate)
void LogDetails()
void Visualize()
void ClearGradients()

Visualization Interfaces and Classes
IVisualizer<T> Interface
Methods:
void VisualizeNetwork(NeuralNetwork<T> network)
void VisualizeLayer(ILayer<T> layer)

ILogger Interface
Methods:
void Log(string message)

only generate new code or code changes, don't generate unchanged code and make it focused on the task at hand and provide explanation so I can avoid any copy paste errors and to avoid human confusion