Skip to content

Commit 4e0d60c

Browse files
committed
More code refactoring, ready to remove legacy
1 parent 0929906 commit 4e0d60c

File tree

5 files changed

+117
-50
lines changed

5 files changed

+117
-50
lines changed

NeuralNetwork.NET/Networks/Layers/Cpu/ConvolutionalLayer.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,9 @@ public override unsafe void Backpropagate(in Tensor dy, in Tensor z, ActivationF
109109
/// <inheritdoc/>
110110
public override void ComputeGradient(in Tensor a, in Tensor delta, out Tensor dJdw, out Tensor dJdb)
111111
{
112-
a.Rotate180(InputInfo.Channels, out Tensor a180);
113-
ConvolutionExtensions.ConvoluteGradient(a180, InputInfo, delta, OutputInfo, out Tensor dJdwM);
114-
dJdwM.Reshape(1, Weights.Length, out dJdw);
115-
a180.Free();
112+
Tensor.New(OutputInfo.Channels, KernelInfo.Size, out Tensor dw);
113+
CpuDnn.ConvolutionBackwardFilter(a, InputInfo, delta, OutputInfo, dw);
114+
dw.Reshape(1, Weights.Length, out dJdw);
116115
Tensor.New(1, Biases.Length, out dJdb);
117116
CpuDnn.ConvolutionBackwardBias(delta, OutputInfo, dJdb);
118117
}

NeuralNetwork.NET/cpuDNN/CpuBlas.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,35 @@ void Kernel(int i)
6565
Parallel.For(0, n, Kernel).AssertCompleted();
6666
}
6767

68+
/// <summary>
69+
/// Subtracts two <see cref="Tensor"/> instances, element wise
70+
/// </summary>
71+
/// <param name="x1">The first <see cref="Tensor"/></param>
72+
/// <param name="x2">The second <see cref="Tensor"/></param>
73+
/// <param name="y">The resulting <see cref="Tensor"/> - it can be the same as one of the inputs</param>
74+
internal static unsafe void Subtract(in Tensor x1, in Tensor x2, in Tensor y)
75+
{
76+
int
77+
n = x1.Entities,
78+
l = x1.Length;
79+
if (!x1.MatchShape(x2)) throw new ArgumentException("The two input tensors must be of equal shape");
80+
if (!x1.MatchShape(y)) throw new ArgumentException("The output tensor must have the same shape as the input tensors", nameof(y));
81+
82+
// Subtract in parallel
83+
float* px1 = x1, px2 = x2, py = y;
84+
void Kernel(int i)
85+
{
86+
int offset = i * l;
87+
for (int j = 0; j < l; j++)
88+
{
89+
int position = offset + j;
90+
py[position] = px1[position] - px2[position];
91+
}
92+
}
93+
Parallel.For(0, n, Kernel).AssertCompleted();
94+
}
95+
96+
6897
/// <summary>
6998
/// Compresses a <see cref="Tensor"/> into a row by summing the components column by column
7099
/// </summary>

NeuralNetwork.NET/cpuDNN/CpuDnn{Convolution}.cs

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ public static partial class CpuDnn
1818
/// <param name="wInfo">The kernels volume info (depth and 2D slices size)</param>
1919
/// <param name="b">The bias <see cref="Tensor"/> to sum to the resulting images</param>
2020
/// <param name="y">The resulting convolution <see cref="Tensor"/></param>
21-
/// <exception cref="ArgumentException">The size of the matrix isn't valid, or the kernels list isn't valid</exception>
22-
/// <exception cref="ArgumentOutOfRangeException">The size of the matrix doesn't match the expected values</exception>
21+
/// <exception cref="ArgumentException">The size of one of the input <see cref="Tensor"/> instances isn't valid</exception>
2322
public static unsafe void ConvolutionForward(
2423
in Tensor x, in TensorInfo xInfo,
2524
in Tensor w, in TensorInfo wInfo,
@@ -42,9 +41,9 @@ public static unsafe void ConvolutionForward(
4241
imgSize = xInfo.SliceSize,
4342
imgHeight = xInfo.Height,
4443
imgWidth = xInfo.Width; // Size of an edge of one of the inner images per sample
45-
if (imgSize * xInfo.Channels != l) throw new ArgumentException("Invalid depth parameter for the input matrix", nameof(x));
46-
if (imgSize < kSize) throw new ArgumentOutOfRangeException("Each subdivided matrix must at least have the size of the kernels");
47-
if (xInfo.Channels != wInfo.Channels) throw new InvalidOperationException("The depth of each kernel must be equal to the depth of each input volume");
44+
if (imgSize * xInfo.Channels != l) throw new ArgumentException("Invalid depth parameter for the input tensor", nameof(x));
45+
if (imgSize < kSize) throw new ArgumentException("Each subdivided tensor must at least have the size of the kernels");
46+
if (xInfo.Channels != wInfo.Channels) throw new ArgumentException("The depth of each kernel must be equal to the depth of each input volume");
4847
if (b.Length != nKernels) throw new ArgumentException("The sum vector must be as long as the depth of the input volume");
4948

5049
/* ============================
@@ -115,8 +114,7 @@ void ForwardKernel(int index)
115114
/// <param name="wInfo">The kernels volume info (depth and 2D slices size)</param>
116115
/// <param name="dx">The resulting backpropagated error <see cref="Tensor"/></param>
117116
/// <param name="dxInfo">The info on the layer inputs</param>
118-
/// <exception cref="ArgumentException">The size of the matrix isn't valid, or the kernels list isn't valid</exception>
119-
/// <exception cref="ArgumentOutOfRangeException">The size of the matrix doesn't match the expected values</exception>
117+
/// <exception cref="ArgumentException">The size of one of the input <see cref="Tensor"/> instances isn't valid</exception>
120118
public static unsafe void ConvolutionBackwardData(
121119
in Tensor dy, in TensorInfo dyInfo,
122120
in Tensor w, in TensorInfo wInfo,
@@ -137,8 +135,8 @@ public static unsafe void ConvolutionBackwardData(
137135
imgSize = dyInfo.SliceSize,
138136
imgHeight = dyInfo.Height,
139137
imgWidth = dyInfo.Width;
140-
if (imgSize * dyInfo.Channels != l) throw new ArgumentException("Invalid depth parameter for the input matrix", nameof(dy));
141-
if (imgSize < kSize) throw new ArgumentOutOfRangeException("Each subdivided matrix must at least have the size of the kernels");
138+
if (imgSize * dyInfo.Channels != l) throw new ArgumentException("Invalid depth parameter for the input tensor", nameof(dy));
139+
if (imgSize < kSize) throw new ArgumentException("Each subdivided tensor must at least have the size of the kernels");
142140
if (dyInfo.Channels != nKernels) throw new ArgumentException("The source depth must be equal to the number of kernels");
143141

144142
// Traanspose the layer kernels
@@ -209,17 +207,16 @@ void BackwardsKernel(int index)
209207
/// <summary>
210208
/// Performs a the backward convolution operation for a network layer and computes the gradient with respect to the layer weights
211209
/// </summary>
212-
/// <param name="x">The source matrix, where each row is a sample in the dataset and each one contains a series of images in row-first order</param>
210+
/// <param name="x">The source <see cref="Tensor"/>, where each row is a sample in the dataset and each one contains a series of images in row-first order</param>
213211
/// <param name="xInfo">The source volume info (depth and 2D slices size)</param>
214-
/// <param name="dy">The list of convolution kernels to apply to each image</param>
215-
/// <param name="dyInfo">The kernels volume info (depth and 2D slices size)</param>
216-
/// <param name="result">The resulting convolution volume</param>
217-
/// <exception cref="ArgumentException">The size of the matrix isn't valid, or the kernels list isn't valid</exception>
218-
/// <exception cref="ArgumentOutOfRangeException">The size of the matrix doesn't match the expected values</exception>
219-
public static unsafe void ConvoluteGradient(
212+
/// <param name="dy">The output error <see cref="Tensor"/></param>
213+
/// <param name="dyInfo">The output error volume info (depth and 2D slices size)</param>
214+
/// <param name="dw">The resulting weights gradient</param>
215+
/// <exception cref="ArgumentException">The size of one of the input <see cref="Tensor"/> instances isn't valid</exception>
216+
public static unsafe void ConvolutionBackwardFilter(
220217
in Tensor x, in TensorInfo xInfo,
221218
in Tensor dy, in TensorInfo dyInfo,
222-
out Tensor result)
219+
in Tensor dw)
223220
{
224221
// Checks and local parameters
225222
int
@@ -236,8 +233,8 @@ public static unsafe void ConvoluteGradient(
236233
imgSize = xInfo.SliceSize,
237234
imgHeight = xInfo.Height,
238235
imgWidth = xInfo.Width;
239-
if (imgSize * xInfo.Channels != l) throw new ArgumentException(nameof(x), "Invalid depth parameter for the input matrix");
240-
if (imgSize < kSize) throw new ArgumentOutOfRangeException("Each subdivided matrix must at least have the size of the kernels");
236+
if (imgSize * xInfo.Channels != l) throw new ArgumentException(nameof(x), "Invalid depth parameter for the input tensor");
237+
if (imgSize < kSize) throw new ArgumentOutOfRangeException("Each subdivided tensor must at least have the size of the kernels");
241238
if (nKernels != n) throw new ArgumentException(nameof(dy), "There must be a delta volume for each activation sample");
242239

243240
/* ============================
@@ -255,8 +252,7 @@ public static unsafe void ConvoluteGradient(
255252
iterationsPerSample = xInfo.Channels * kDepth; // Each sample has its own list of 3D gradients, one for each kernel
256253

257254
// Process the valid convolution
258-
Tensor.New(n, finalWidth, out result);
259-
float* psource = x, pkernels = dy, presult = result;
255+
float* px = x, pdy = dy, pdw = dw;
260256
void GradientKernel(int index)
261257
{
262258
// Calculate the current indexes
@@ -287,16 +283,14 @@ void GradientKernel(int index)
287283
kernelRowOffset = kernelBaseOffset + (xEnd - r) * kWidth + highY;
288284
for (int c = j; c <= highY; c++)
289285
{
290-
temp += psource[sourceRowOffset + c] * pkernels[kernelRowOffset - c];
286+
temp += px[sourceRowOffset + c] * pdy[kernelRowOffset - c];
291287
}
292288
}
293-
presult[targetRowOffset + j] = temp;
289+
pdw[targetRowOffset + j] = temp;
294290
}
295291
}
296292
}
297293
Parallel.For(0, n * iterationsPerSample, GradientKernel).AssertCompleted();
298-
299-
// TODO: correct gradient implementation
300294
throw new NotImplementedException("The CPU gradient convolution isn't implemented correctly yet");
301295
}
302296

@@ -306,6 +300,7 @@ void GradientKernel(int index)
306300
/// <param name="dy">The output error <see cref="Tensor"/></param>
307301
/// <param name="dyInfo">The info on the output <see cref="Tensor"/></param>
308302
/// <param name="db">The resulting gradient</param>
303+
/// <exception cref="ArgumentException">The size of one of the input <see cref="Tensor"/> instances isn't valid</exception>
309304
[PublicAPI]
310305
public static unsafe void ConvolutionBackwardBias(in Tensor dy, in TensorInfo dyInfo, in Tensor db)
311306
{
@@ -314,9 +309,9 @@ public static unsafe void ConvolutionBackwardBias(in Tensor dy, in TensorInfo dy
314309
depth = dyInfo.Channels,
315310
h = dy.Entities,
316311
w = dy.Length,
317-
imgSize = w % depth == 0 ? w / depth : throw new ArgumentException(nameof(dy), "Invalid depth parameter for the input matrix"),
312+
imgSize = w % depth == 0 ? w / depth : throw new ArgumentException("Invalid depth parameter for the input tensor", nameof(dy)),
318313
imgAxis = imgSize.IntegerSquare(); // Size of an edge of one of the inner images per sample
319-
if (imgAxis * imgAxis != imgSize) throw new ArgumentOutOfRangeException(nameof(dy), "The size of the input matrix isn't valid");
314+
if (imgAxis * imgAxis != imgSize) throw new ArgumentException("The size of the input tensor isn't valid", nameof(dy));
320315
Tensor.New(h, depth, out Tensor temp);
321316

322317
// Kernel to sum each slice
@@ -328,7 +323,7 @@ void Kernel(int index)
328323
iSample = index / depth, // Sample index
329324
z = index % depth; // 2D slice index
330325

331-
// Reverse the input matrix sequentially
326+
// Reverse the input tensor sequentially
332327
int baseOffset = iSample * w + z * imgSize;
333328
float sum = 0;
334329
for (int i = 0; i < imgSize; i++)
@@ -347,19 +342,19 @@ void Kernel(int index)
347342
/// <summary>
348343
/// Rotates the input volume by 180 degrees
349344
/// </summary>
350-
/// <param name="x">The input matrix to rotate</param>
345+
/// <param name="x">The input <see cref="Tensor"/> to rotate</param>
351346
/// <param name="depth">The number of images per row</param>
352-
/// <param name="y">The rotated input matrix</param>
347+
/// <param name="y">The rotated input <see cref="Tensor"/></param>
353348
private static unsafe void Rotate180(in Tensor x, int depth, out Tensor y)
354349
{
355350
// Checks and local parameters
356-
if (depth < 1) throw new ArgumentOutOfRangeException(nameof(depth), "The number of images per row can't be lower than 1");
351+
if (depth < 1) throw new ArgumentException("The number of images per row can't be lower than 1", nameof(depth));
357352
int
358353
n = x.Entities,
359354
l = x.Length,
360-
imgSize = l % depth == 0 ? l / depth : throw new ArgumentException(nameof(x), "Invalid depth parameter for the input matrix"),
355+
imgSize = l % depth == 0 ? l / depth : throw new ArgumentException("Invalid depth parameter for the input tensor", nameof(x)),
361356
imgAxis = imgSize.IntegerSquare(); // Size of an edge of one of the inner images per sample
362-
if (imgAxis * imgAxis != imgSize) throw new ArgumentOutOfRangeException(nameof(x), "The size of the input matrix isn't valid");
357+
if (imgAxis * imgAxis != imgSize) throw new ArgumentException("The size of the input tensor isn't valid", nameof(x));
363358
int
364359
threshold = imgSize / 2,
365360
edge = imgSize - 1;
@@ -375,7 +370,7 @@ void Kernel(int index)
375370
iSample = index / depth, // Sample index
376371
z = index % depth; // 2D slice index
377372

378-
// Reverse the input matrix sequentially
373+
// Reverse the input tensor sequentially
379374
int baseOffset = iSample * l + z * imgSize;
380375
for (int i = 0; i < threshold; i++)
381376
{

NeuralNetwork.NET/cpuDNN/CpuDnn{Main}.cs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using JetBrains.Annotations;
44
using NeuralNetworkNET.APIs.Structs;
55
using NeuralNetworkNET.Extensions;
6+
using NeuralNetworkNET.Networks.Activations;
67
using NeuralNetworkNET.Networks.Activations.Delegates;
78

89
namespace NeuralNetworkNET.cpuDNN
@@ -40,6 +41,47 @@ void Kernel(int i)
4041
Parallel.For(0, n, Kernel).AssertCompleted();
4142
}
4243

44+
/// <summary>
45+
/// Performs the softmax activation on the input <see cref="Tensor"/> and applies the output normalization
46+
/// </summary>
47+
/// <param name="x">The input <see cref="Tensor"/></param>
48+
/// <param name="y">The output <see cref="Tensor"/></param>
49+
public static unsafe void SoftmaxForward(in Tensor x, in Tensor y)
50+
{
51+
// Setup
52+
if (!x.MatchShape(y)) throw new ArgumentException("The input tensor doesn't have the same shape as the output tensor");
53+
int n = x.Entities, l = x.Length;
54+
Tensor.New(1, n, out Tensor partials);
55+
float* pp = partials, px = x, py = y;
56+
57+
// Activation
58+
void ActivationWithAggregate(int i)
59+
{
60+
int offset = i * l;
61+
float sum = 0;
62+
for (int j = 0; j < l; j++)
63+
{
64+
int target = offset + j;
65+
float value = ActivationFunctions.Softmax(px[target]);
66+
py[target] = value;
67+
sum += value;
68+
}
69+
pp[i] = sum;
70+
}
71+
Parallel.For(0, n, ActivationWithAggregate).AssertCompleted();
72+
73+
// Normalization of the tensor values
74+
void NormalizationKernel(int i)
75+
{
76+
int offset = i * l;
77+
float factor = pp[i];
78+
for (int j = 0; j < l; j++)
79+
py[offset + j] /= factor;
80+
}
81+
Parallel.For(0, n, NormalizationKernel).AssertCompleted();
82+
partials.Free();
83+
}
84+
4385
/// <summary>
4486
/// Executes the backward activation function on the target <see cref="Tensor"/>, with the given error delta
4587
/// </summary>
@@ -83,7 +125,7 @@ void Kernel(int i)
83125
/// <param name="y">The output <see cref="Tensor"/> for the current layer</param>
84126
public static unsafe void FullyConnectedForward(in Tensor x, in Tensor w, in Tensor b, in Tensor y)
85127
{
86-
// Initialize the parameters and the result matrix
128+
// Initialize the parameters and the result tensor
87129
if (x.Length != w.Entities) throw new ArgumentOutOfRangeException("Invalid tensors shapes");
88130
if (!b.MatchShape(1, w.Length)) throw new ArgumentException("Invalid biases shape", nameof(b));
89131
if (!y.MatchShape(x.Entities, w.Length)) throw new ArgumentException("The output tensor doesn't have the right shape", nameof(y));
@@ -128,7 +170,7 @@ public static unsafe void FullyConnectedBackwardData(in Tensor x, in Tensor w, i
128170
Tensor.New(w.Length, w.Entities, out Tensor wt);
129171
CpuBlas.Transpose(w, wt);
130172

131-
// Initialize the parameters and the result matrix
173+
// Initialize the parameters and the result tensor
132174
int
133175
h = dy.Entities,
134176
l = dy.Length,
@@ -149,7 +191,7 @@ void Kernel(int i)
149191
res += pdy[i1 + q] * pwt[i2];
150192
}
151193

152-
// res has now the matrix multiplication result for position [i, j]
194+
// res has now the tensor multiplication result for position [i, j]
153195
int zIndex = i * k + j;
154196
pdx[zIndex] = f_(px[zIndex]) * res;
155197
}
@@ -187,7 +229,7 @@ public static unsafe void FullyConnectedBackwardBias(in Tensor dy, in Tensor db)
187229
l = dy.Length;
188230
float* pdy = dy, pdb = db;
189231

190-
// Compress the matrix
232+
// Compress the tensor
191233
void Kernel(int j)
192234
{
193235
float sum = 0;

0 commit comments

Comments
 (0)