internal override NNDetailedFeedData FeedForward(NNFeedData input) { float[] output = new float[Neurons]; int[] maxIndices = new int[Neurons]; for (int zIdx = 0; zIdx < PreviousLayer.Depth; zIdx++) { for (int yiIdx = 0, yoIdx = 0; yoIdx < Height; yiIdx += this.Stride, yoIdx++) { for (int xiIdx = 0, xoIdx = 0; xoIdx < Width; xiIdx += this.Stride, xoIdx++) { float max = float.MinValue; int maxIdx = -1; for (int fyIdx = 0; fyIdx < this.FilterSize; fyIdx++) { for (int fxIdx = 0; fxIdx < this.FilterSize; fxIdx++) { int idx = PreviousLayer.ConvertToNeuronIndex(xiIdx + fxIdx, yiIdx + fyIdx, zIdx); if (input[idx] > max) { max = input[idx]; maxIdx = idx; } } } int i = ConvertToNeuronIndex(xoIdx, yoIdx, zIdx); output[i] = max; maxIndices[i] = maxIdx; } } } NNDetailedFeedData feedData = new NNDetailedFeedData(this, output, output); feedData.CustomData[nameof(maxIndices)] = maxIndices; return(feedData); }
internal override void PropagateBackward(NNDetailedBackpropagationData backpropagationData) { float[] lastRawOutput = backpropagationData.FeedForwardData[this].RawOutputData; float[] dE_dz = new float[Neurons]; float[] newWeights = new float[this.weights.Length]; // For each filter for (int fIdx = 0; fIdx < FilterCount; fIdx++) { float dE_db = 0; float[] dE_dw = new float[FilterSize * FilterSize * PreviousLayer.Depth]; for (int yIdx = 0; yIdx < Height; yIdx++) { for (int xIdx = 0; xIdx < Width; xIdx++) { int oIdx = ConvertToNeuronIndex(xIdx, yIdx, fIdx); float do_dz = Activation.Gradient(lastRawOutput[oIdx], lastRawOutput); float dE_do; if (NextLayer == null) { float o = backpropagationData.FeedForwardData[this].OutputData[oIdx]; dE_do = backpropagationData.ErrorGradient(o, oIdx); } else { dE_do = NextLayer.CalculateErrorByOutputGradient(backpropagationData, oIdx); } float dE_dz_tmp = dE_do * do_dz; dE_dz[oIdx] = dE_dz_tmp; for (int fzIdx = 0; fzIdx < PreviousLayer.Depth; fzIdx++) { for (int fyIdx = 0; fyIdx < FilterSize; fyIdx++) { for (int fxIdx = 0; fxIdx < FilterSize; fxIdx++) { float dz_dw = backpropagationData.FeedForwardData[PreviousLayer].OutputData[PreviousLayer.ConvertToNeuronIndex(xIdx * Stride + fxIdx, yIdx * Stride + fyIdx, fzIdx)]; dE_dw[ToWeightIndex(fxIdx, fyIdx, fzIdx, 0)] += dE_dz_tmp * dz_dw; } } } dE_db += dE_do * do_dz; // dz_dw = 1 for bias } } for (int fzIdx = 0; fzIdx < PreviousLayer.Depth; fzIdx++) { for (int fyIdx = 0; fyIdx < FilterSize; fyIdx++) { for (int fxIdx = 0; fxIdx < FilterSize; fxIdx++) { int weightIndex = ToWeightIndex(fxIdx, fyIdx, fzIdx, fIdx); newWeights[weightIndex] = backpropagationData.CalculateNewWeight(this.weights[weightIndex], dE_dw[ToWeightIndex(fxIdx, fyIdx, fzIdx, 0)], this, weightIndex); } } } this.biases[fIdx] += backpropagationData.CalculateNewWeight(this.biases[fIdx], dE_db, this, fIdx); } backpropagationData.dE_dz[this] = dE_dz; backpropagationData.UpdatedWeights[this] = newWeights; }