/// <summary> /// This method accepts the training examples as input and performs the training of the MLP neural network /// </summary> /// <param name="data">training data pairs</param> /// <param name="ctx">training data descriptions</param> /// <returns>IScore</returns> public override IScore Run(double[][] data, IContext ctx) { // Sum for every layer. hidLyrNeuronSum1 = x11*w11+x12*w21+..+x1N*wN1 double[][] hidLyrNeuronSum = new double[m_HiddenLayerNeurons.Length + 1][]; // outputs = ActFnx(hidLyrNeuronSum+Bias) double[][] hidLyrOut = new double[m_HiddenLayerNeurons.Length + 1][]; double[][] trainingData = new double[(int)(data.Length * 0.8)][]; double[][] validationData = new double[(int)(data.Length * 0.2)][]; trainingData = data.Take((int)(data.Length * 0.8)).ToArray(); validationData = data.Skip((int)(data.Length * 0.8)).ToArray(); int numOfInputVectors = trainingData.Length; m_InpDims = ctx.DataDescriptor.Features.Count(); m_OutputLayerNeurons = data[0].Length - m_InpDims; m_Weights = new double[m_HiddenLayerNeurons.Length + 1][, ]; m_Biases = new double[m_HiddenLayerNeurons.Length + 1][]; InitializeWeightsandBiasesinputlayer(m_InpDims); InitializeWeightsandBiaseshiddenlayers(m_HiddenLayerNeurons); InitializeWeightsandBiasesoutputlayer(m_HiddenLayerNeurons); var score = new MLPerceptronAlgorithmScore(); double lastLoss = 0; double lastValidationLoss = 0; #if TESTING string path = Directory.GetCurrentDirectory() + "\\MLPerceptron\\TestFiles\\mnist_performance_params_" + this.TestCaseNumber.ToString() + ".csv"; if (!File.Exists(path)) { File.Create(path).Dispose(); } using (var performanceData = new StreamWriter(path)) #endif { Stopwatch watch = new Stopwatch(); double timeElapsed = 0; #if TESTING performanceData.WriteLine("{0},{1},{2},{3},{4},{5}", "Epoch", "Epoch Loss", "Epoch Accuracy", "Validation Loss", "Validation Accuracy", "Time Elapsed"); #endif for (int i = 0; i < m_Iterations; i++) { watch.Restart(); score.Loss = 0; double batchAccuracy = 0; int miniBatchStartIndex = 0; while (miniBatchStartIndex < numOfInputVectors) { BackPropagationNetwork backPropagation = new BackPropagationNetwork(m_Biases, m_HiddenLayerNeurons, m_OutputLayerNeurons, m_InpDims); for (int inputVectIndx = miniBatchStartIndex; inputVectIndx < m_batchSize + miniBatchStartIndex && inputVectIndx < trainingData.Length; inputVectIndx++) { // Z2 = actFnc(X * W1) CalcFirstHiddenLayer(trainingData[inputVectIndx], m_InpDims, out hidLyrOut[0], out hidLyrNeuronSum[0]); // We use output of first layer as input of second layer. CalcRemainingHiddenLayers(hidLyrOut[0], hidLyrNeuronSum[0], m_InpDims, out hidLyrOut, out hidLyrNeuronSum); // Zk = ak-1 * Wk-1 CalculateResultatOutputlayer(hidLyrOut[m_HiddenLayerNeurons.Length - 1], m_InpDims, m_SoftMax, out hidLyrOut[m_HiddenLayerNeurons.Length], out hidLyrNeuronSum[m_HiddenLayerNeurons.Length]); if (m_SoftMax == true) { backPropagation.CalcOutputErrorSoftMax(hidLyrOut[m_HiddenLayerNeurons.Length], m_HiddenLayerNeurons, trainingData[inputVectIndx], ctx); } else { // BackPropagationNetwork backPropagation = new BackPropagationNetwork(m_HiddenLayerNeurons.Length); backPropagation.CalcOutputError(hidLyrOut[m_HiddenLayerNeurons.Length], m_HiddenLayerNeurons, hidLyrNeuronSum[m_HiddenLayerNeurons.Length], trainingData[inputVectIndx], ctx); } backPropagation.CalcHiddenLayersError(hidLyrOut, m_Weights, m_HiddenLayerNeurons, hidLyrNeuronSum, trainingData[inputVectIndx]); backPropagation.CostFunctionChangeWithBiases(m_Biases, m_HiddenLayerNeurons, m_LearningRate); backPropagation.CostFunctionChangeWithWeights(m_Weights, hidLyrOut, m_HiddenLayerNeurons, m_LearningRate, trainingData[inputVectIndx]); } backPropagation.UpdateBiases(m_Biases, m_HiddenLayerNeurons, m_LearningRate, out m_Biases); backPropagation.UpdateWeights(m_Weights, hidLyrOut, m_HiddenLayerNeurons, m_LearningRate, m_InpDims, out m_Weights); score.Errors = backPropagation.MiniBatchError[m_HiddenLayerNeurons.Length]; batchAccuracy += ((double)backPropagation.TrainingSetAccuracy / m_batchSize); double sum = 0; foreach (var outLyrErr in score.Errors) { sum += outLyrErr; } /* * 1 - mean of errors * score.Loss = 1 - (Math.Abs(sum) / score.Errors.Length); */ score.Loss += Math.Abs(sum); miniBatchStartIndex = miniBatchStartIndex + m_batchSize; } double deltaLoss = lastLoss - score.Loss; double accuracy = ((double)batchAccuracy * m_batchSize) / numOfInputVectors; var result = ((MLPerceptronResult)Predict(validationData, ctx)).results; int accurateResults = 0; double validationSetLoss = 0.0; // Check if the test data has been correctly classified by the neural network for (int j = 0; j < validationData.Length; j++) { accurateResults++; for (int k = 0; k < m_OutputLayerNeurons; k++) { validationSetLoss += Math.Abs(validationData[j][(validationData[j].Length - m_OutputLayerNeurons) + k] - result[j * m_OutputLayerNeurons + k]); //Assert.True(testData[i][(testData[i].Length - numberOfOutputs) + j] == (result[i * numberOfOutputs + j] >= 0.5 ? 1 : 0)); if (validationData[j][(validationData[j].Length - m_OutputLayerNeurons) + k] != (result[j * m_OutputLayerNeurons + k] >= 0.5 ? 1 : 0)) { accurateResults--; break; } } } double deltaValidationLoss = lastValidationLoss - validationSetLoss; double validationAccuracy = (double)accurateResults / validationData.Length; watch.Stop(); timeElapsed += ((double)watch.ElapsedMilliseconds / 1000); // Debug.WriteLine($"Loss: {score.Loss}, Last loss: {lastLoss}, Delta: {deltaLoss}, Accuracy: {accuracy}, ValidationLoss: {validationSetLoss}, Last Validationloss: {lastValidationLoss}, Delta: {deltaValidationLoss}, ValidationAccuracy: {validationAccuracy}, TimeElapsed: {timeElapsed}"); #if TESTING performanceData.WriteLine("{0},{1},{2},{3},{4},{5}", i.ToString(), score.Loss.ToString("F3"), accuracy.ToString("F3"), validationSetLoss.ToString("F3"), validationAccuracy.ToString("F3"), timeElapsed.ToString("F3")); #endif lastLoss = score.Loss; lastValidationLoss = validationSetLoss; } ctx.Score = score; return(ctx.Score); } }