public override void DoSum(Volume <double> result) { var batchsize = this.Shape.GetDimension(3); var channel = this.Shape.GetDimension(2); var height = this.Shape.GetDimension(1); var width = this.Shape.GetDimension(0); var resultWIsOne = result.Shape.GetDimension(0) == 1; var resultHIsOne = result.Shape.GetDimension(1) == 1; var resultCIsOne = result.Shape.GetDimension(2) == 1; var resultNIsOne = result.Shape.GetDimension(3) == 1; for (int n = 0; n < batchsize; n++) { for (int c = 0; c < channel; c++) { for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { var val = this.Get(w, h, c, n); var resultW = resultWIsOne ? 0 : w; var resultH = resultHIsOne ? 0 : h; var resultC = resultCIsOne ? 0 : c; var resultN = resultNIsOne ? 0 : n; var current = result.Get(resultW, resultH, resultC, resultN); result.Set(resultW, resultH, resultC, resultN, current + val); } } } } }
public override void Sum(Volume <double> result) { var batchSize = this.Shape.Dimensions[3]; var channel = this.Shape.Dimensions[2]; var height = this.Shape.Dimensions[1]; var width = this.Shape.Dimensions[0]; var resultWIsOne = result.Shape.Dimensions[0] == 1; var resultHIsOne = result.Shape.Dimensions[1] == 1; var resultCIsOne = result.Shape.Dimensions[2] == 1; var resultNIsOne = result.Shape.Dimensions[3] == 1; for (var n = 0; n < batchSize; n++) { for (var c = 0; c < channel; c++) { for (var h = 0; h < height; h++) { for (var w = 0; w < width; w++) { var val = this.Get(w, h, c, n); var resultW = resultWIsOne ? 0 : w; var resultH = resultHIsOne ? 0 : h; var resultC = resultCIsOne ? 0 : c; var resultN = resultNIsOne ? 0 : n; var current = result.Get(resultW, resultH, resultC, resultN); result.Set(resultW, resultH, resultC, resultN, current + val); } } } } }
public override void ConvolutionGradient(Volume <double> filters, Volume <double> outputGradients, Volume <double> filterGradient, int pad, int stride, Volume <double> inputGradient) { inputGradient.Clear(); // zero out gradient wrt bottom data, we're about to fill it var batchSize = this.Shape.Dimensions[3]; var inputWidth = this.Shape.Dimensions[0]; var inputHeight = this.Shape.Dimensions[1]; var outputWidth = outputGradients.Shape.Dimensions[0]; var outputHeight = outputGradients.Shape.Dimensions[1]; var outputDepth = outputGradients.Shape.Dimensions[2]; var filterWidth = filters.Shape.Dimensions[0]; var filterHeight = filters.Shape.Dimensions[1]; var filterDepth = filters.Shape.Dimensions[2]; for (var n = 0; n < batchSize; n++) { for (var depth = 0; depth < outputDepth; depth++) { var y = -pad; for (var ay = 0; ay < outputHeight; y += stride, ay++) { var x = -pad; for (var ax = 0; ax < outputWidth; x += stride, ax++) { // convolve centered at this particular location var chainGradient = outputGradients.Get(ax, ay, depth, n); // gradient from above, from chain rule for (var fy = 0; fy < filterHeight; fy++) { var oy = y + fy; // coordinates in the original input array coordinates for (var fx = 0; fx < filterWidth; fx++) { var ox = x + fx; if (oy >= 0 && oy < inputHeight && ox >= 0 && ox < inputWidth) { for (var fd = 0; fd < filterDepth; fd++) { filterGradient.Set(fx, fy, fd, depth, filterGradient.Get(fx, fy, fd, depth) + Get(ox, oy, fd, n) * chainGradient); inputGradient.Set(ox, oy, fd, n, inputGradient.Get(ox, oy, fd, n) + filters.Get(fx, fy, fd, depth) * chainGradient); } } } } } } } } }
public override void PoolGradient(Volume <double> input, Volume <double> outputGradient, int windowWidth, int windowHeight, int horizontalPad, int verticalPad, int horizontalStride, int verticalStride, Volume <double> inputGradient) { var inputWidth = input.Shape.Dimensions[0]; var inputHeight = input.Shape.Dimensions[1]; var outputWidth = outputGradient.Shape.Dimensions[0]; var outputHeight = outputGradient.Shape.Dimensions[1]; var outputDepth = outputGradient.Shape.Dimensions[2]; var batchSize = outputGradient.Shape.Dimensions[3]; for (var n = 0; n < batchSize; n++) { for (var depth = 0; depth < outputDepth; depth++) { var x = -horizontalPad; for (var ax = 0; ax < outputWidth; x += verticalStride, ax++) { var y = -verticalPad; for (var ay = 0; ay < outputHeight; y += horizontalStride, ay++) { var a = double.MinValue; int winx = -1, winy = -1; for (var fx = 0; fx < windowWidth; fx++) { for (var fy = 0; fy < windowHeight; fy++) { var oy = y + fy; var ox = x + fx; if (oy < 0 || oy >= inputHeight || ox < 0 || ox >= inputWidth) { continue; } var v = input.Get(ox, oy, depth, n); // perform max pooling and store pointers to where // the max came from. This will speed up backprop // and can help make nice visualizations in future if (v > a) { a = v; winx = ox; winy = oy; } } } var chainGradient = outputGradient.Get(ax, ay, depth, n); inputGradient.Storage.Set(winx, winy, depth, n, chainGradient); } } } } }
public override void Concat(Volume <double> right, Volume <double> result) { var batchSize = Math.Max(this.Shape.Dimensions[3], right.Shape.Dimensions[3]); if (this.Shape.TotalLength > 1 && right.Shape.TotalLength > 1) { var left = this.ReShape(new Shape(1, 1, -1, batchSize)); right = right.ReShape(new Shape(1, 1, -1, batchSize)); var elementPerBatch = result.Shape.TotalLength / batchSize; var threshold = left.Shape.Dimensions[2]; for (var n = 0; n < batchSize; n++) { for (var i = 0; i < elementPerBatch; i++) { result.Set(0, 0, i, n, i < threshold ? left.Get(0, 0, i, n) : right.Get(0, 0, i - threshold, n)); } } } else if (this.Shape.TotalLength == 1 && right.Shape.TotalLength > 1) { // Left volume is actually a scalar => broadcast its value right = right.ReShape(new Shape(1, 1, -1, batchSize)); var elementPerBatch = result.Shape.TotalLength / batchSize; const int threshold = 1; for (var n = 0; n < batchSize; n++) { for (var i = 0; i < elementPerBatch; i++) { result.Set(0, 0, i, n, i < threshold ? this.Get(0) : right.Get(0, 0, i - threshold, n)); } } } else { // Right volume is actually a scalar => broadcast its value var left = this.ReShape(new Shape(1, 1, -1, batchSize)); var elementPerBatch = result.Shape.TotalLength / batchSize; var threshold = left.Shape.Dimensions[2]; for (var n = 0; n < batchSize; n++) { for (var i = 0; i < elementPerBatch; i++) { result.Set(0, 0, i, n, i < threshold ? left.Get(0, 0, i, n) : right.Get(0)); } } } }
public override void MatMultiply(Volume <double> right, Volume <double> result) { if (this.Shape.Dimensions[2] != 1 || right.Shape.Dimensions[2] != 1) { throw new ArgumentException($"Left and right volumes should be [w, h, 1, b]. left = {this.Shape} right = {right.Shape}"); } var broadCastLeft = this.Shape.Dimensions[3] == 1; var broadCastRight = right.Shape.Dimensions[3] == 1; if (this.Shape.Dimensions[3] != right.Shape.Dimensions[3] && !(broadCastLeft || broadCastRight)) { throw new ArgumentException($"Left and right volumes should have the same batch size. left = {this.Shape.Dimensions[3]} right = {right.Shape.Dimensions[3]}"); } var expectedShape = ComputeMatMultiplyShape(this.Shape, right.Shape); if (!result.Shape.Equals(expectedShape)) { throw new ArgumentException($"Result shape should be {expectedShape} but is {result.Shape}"); } for (var n = 0; n < this.Shape.Dimensions[3]; n++) { for (var i = 0; i < expectedShape.Dimensions[0]; i++) { for (var j = 0; j < expectedShape.Dimensions[1]; j++) { var cell = 0.0; for (var k = 0; k < this.Shape.Dimensions[0]; k++) { cell += this.Get(k, j, 0, broadCastLeft ? 0 : n) * right.Get(i, k, 0, broadCastRight ? 0 : n); } result.Set(i, j, 0, n, cell); } } } }