/// <summary> Using the given <paramref name="trainingSet"/>, trains the neural network with the given <paramref name="batchSize"/> over the given <paramref name="epochs"/>. </summary> /// <param name="random"> The random number generator used to shuffle the <paramref name="trainingSet"/>. </param> /// <param name="trainingSet"> The <see cref="IDataSet"/> against which to train. </param> /// <param name="epochs"> The number of times to go over the <paramref name="trainingSet"/>. </param> /// <param name="batchSize"> The number of <see cref="IDataPoint"/>s to process before applying learning. </param> /// <param name="logger"> <see cref="INetworkLogger"/> used to log the progress of the training. If this is <c>null</c>, no logging will happen. </param> /// <param name="testSet"> Another <see cref="IDataSet"/>, with <see cref="IDataPoint"/>s that are not in the <paramref name="trainingSet"/>. If this is given, the network will be tested against the <paramref name="testSet"/> after every epoch. </param> public void LearnOverData(Random random, IDataSet trainingSet, int epochs, int batchSize, INetworkLogger logger = null, IDataSet testSet = null) { // Create the various required arrays once, then just fill them with data. This cuts down on memory allocations, dramatically speeding up the training. float[] input = new float[inputLayer.Count]; float[] output = new float[outputLayer.Count]; float[] desiredOutput = new float[outputLayer.Count]; float[] error = new float[outputLayer.Count]; // Calculate how many batches are required with the given batch size. int numberOfBatches = trainingSet.Count / batchSize; // Create the change to be made to the network. NetworkChange networkChange = new NetworkChange(this); // Do the given amount of epochs. for (int currentEpoch = 0; currentEpoch < epochs; currentEpoch++) { // For each epoch, shuffle the training data randomly. trainingSet.Shuffle(random); // The percentage of data correctly guessed per batch. float lastBatchPercentage = 0.0f; // Do the given amount of batches. for (int batch = 0; batch < numberOfBatches; batch++) { // How many data were correctly guessed in this batch. int batchGuesses = 0; // Do the calculated number of training data in the batch. for (int dataIndex = 0; dataIndex < batchSize; dataIndex++) { // Get the next data and set the input array to its data. IDataPoint currentData = trainingSet[batch * batchSize + dataIndex]; currentData.GetFloatData(ref input); // Process the data through the network, saving its output. ProcessInput(input, ref output); // Get the output that the network guessed with the most confidence. int bestGuess = output.GetIndexOfHighestValue(); // Create the desired output. desiredOutput.ApplyVectorisedFunction((int g) => g == currentData.Label ? 1 : 0); // If the guess was correct, track it. if (bestGuess == currentData.Label) { batchGuesses++; } // Calculate the error. CalculateError(desiredOutput, output, ref error); // Learn from the output compared to the desired output, save the results in the previous network change. CalculateChanges(networkChange, error); // If a logger has been given, log the state of the network as of this iteration. logger?.Log(currentEpoch, epochs, batch, numberOfBatches, dataIndex, batchSize, lastBatchPercentage); } // Calculate how many of the data in the batch were correctly guessed. lastBatchPercentage = (float)batchGuesses / batchSize; // Apply the changes. ApplyChanges(networkChange, batchSize); networkChange.Reset(); } // If a logger was given and should log output, log the output of the epoch. if (logger != null && (logger.OutputOptions & OutputOptions.WhenFinished) == OutputOptions.WhenFinished) { // Log that the epoch finished. logger.Log($"\nEpoch {currentEpoch} finished"); // If test data was given, test against it and log the output. if (trainingSet != null) { logger.Log($", correctly guessed {PercentageOfCorrectTestData(random, testSet, ref input, ref output):P2} of the test data"); } // Log the full stop and line end. logger.Log(".\n"); } } // If a logger was given, log that the training finished. logger?.Log("Training finished."); }