| 147 | } |
| 148 | |
| 149 | std::vector< std::vector< std::vector<double> > > NeuralNet::getGradients(const std::vector<double> &input, const std::vector<double> &correctOutput) { |
| 150 | std::vector< std::vector<double> > outputs = feedForward(input); |
| 151 | std::vector< std::vector< std::vector<double> > > weights = getWeights3D(); |
| 152 | std::vector< std::vector<double> > errors; |
| 153 | ActivationFunction hiddenActivationFunctionDerivative = Layer::getDerivedActivationFunctionNames()[getHiddenActivationFunctionName()]; |
| 154 | ActivationFunction outputActivationFunctionDerivative = Layer::getDerivedActivationFunctionNames()[getOutputActivationFunctionName()]; |
| 155 | |
| 156 | // Compute output layer error |
| 157 | std::vector<double> outputNeuronErrors; |
| 158 | std::vector<double> outputLayerOutput = outputs[outputs.size() - 1]; |
| 159 | for(unsigned int neuronIndex = 0; neuronIndex < outputLayerOutput.size(); neuronIndex++) { |
| 160 | double outputNeuronError = (correctOutput[neuronIndex] - outputLayerOutput[neuronIndex]) * outputActivationFunctionDerivative(outputLayerOutput[neuronIndex]); |
| 161 | outputNeuronErrors.push_back(outputNeuronError); |
| 162 | } |
| 163 | errors.push_back(outputNeuronErrors); |
| 164 | |
| 165 | // Compute hidden layer error |
| 166 | for(int layerIndex = net.size() - 2; layerIndex >= 0; layerIndex--) { |
| 167 | std::vector<double> currentLayerError; |
| 168 | const std::vector<double> ¤tHiddenLayerOutput = outputs[layerIndex]; |
| 169 | const std::vector<double> &lastLayerError = errors[errors.size() - 1]; |
| 170 | const std::vector< std::vector<double> > &lastLayerWeights = weights[layerIndex + 1]; |
| 171 | |
| 172 | for(unsigned int neuronIndex = 0; neuronIndex < net[layerIndex].neurons.size(); neuronIndex++) { |
| 173 | double errorsTimesWeights = 0; |
| 174 | for(unsigned int previousNeuronIndex = 0; previousNeuronIndex < lastLayerError.size(); previousNeuronIndex++) { |
| 175 | errorsTimesWeights += lastLayerError[previousNeuronIndex] * lastLayerWeights[previousNeuronIndex][neuronIndex]; |
| 176 | } |
| 177 | double hiddenNeuronError = hiddenActivationFunctionDerivative(currentHiddenLayerOutput[neuronIndex]) * errorsTimesWeights; |
| 178 | currentLayerError.push_back(hiddenNeuronError); |
| 179 | } |
| 180 | errors.push_back(currentLayerError); |
| 181 | } |
| 182 | |
| 183 | // Compute gradients |
| 184 | std::vector< std::vector< std::vector<double> > > gradients(weights.size()); |
| 185 | for(unsigned int errorIndex = 0; errorIndex < errors.size(); errorIndex++) { |
| 186 | int layerIndex = ((int)errors.size() - 1) - errorIndex; |
| 187 | std::vector< std::vector<double> > layerGradient; |
| 188 | |
| 189 | for(unsigned int neuronIndex = 0; neuronIndex < errors[errorIndex].size(); neuronIndex++) { |
| 190 | std::vector<double> neuronGradient; |
| 191 | |
| 192 | if(layerIndex == 0) { |
| 193 | for(unsigned int inputIndex = 0; inputIndex < input.size(); inputIndex++) { |
| 194 | neuronGradient.push_back(errors[errorIndex][neuronIndex] * input[inputIndex]); |
| 195 | } |
| 196 | } else { |
| 197 | for(unsigned int previousOutput = 0; previousOutput < outputs[layerIndex - 1].size(); previousOutput++) { |
| 198 | neuronGradient.push_back(errors[errorIndex][neuronIndex] * outputs[layerIndex - 1][previousOutput]); |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | // Activation weight gradient |
| 203 | neuronGradient.push_back(-errors[errorIndex][neuronIndex]); |
| 204 | |
| 205 | layerGradient.push_back(neuronGradient); |
| 206 | } |
no test coverage detected