Skip to content

Commit fb738d1

Browse files
committed
CuDnnPoolingLayer backpropagation switched to cuDNN
1 parent 968eba6 commit fb738d1

File tree

2 files changed

+86
-8
lines changed

2 files changed

+86
-8
lines changed

NeuralNetwork.NET.Cuda/Layers/CuDnnPoolingLayer.cs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Alea;
1+
using System;
2+
using Alea;
23
using Alea.cuDNN;
34
using NeuralNetworkNET.Extensions;
45
using NeuralNetworkNET.Cuda.Extensions;
@@ -17,7 +18,7 @@ namespace NeuralNetworkNET.Cuda.Layers
1718
/// A pooling layer running on cuDNN, with a custom pooling mode
1819
/// </summary>
1920
[JsonObject(MemberSerialization.OptIn)]
20-
internal sealed class CuDnnPoolingLayer : PoolingLayer
21+
internal sealed class CuDnnPoolingLayer : PoolingLayer, IDisposable
2122
{
2223
#region cuDNN fields
2324

@@ -41,6 +42,16 @@ internal sealed class CuDnnPoolingLayer : PoolingLayer
4142

4243
#endregion
4344

45+
#region Fields
46+
47+
// A copy of the layer inputs
48+
private Tensor _X;
49+
50+
// A copy of the layer output activity
51+
private Tensor _Z;
52+
53+
#endregion
54+
4455
public CuDnnPoolingLayer(in TensorInfo input, in PoolingInfo operation, ActivationFunctionType activation) : base(input, operation, activation)
4556
{
4657
PoolingDescription.Set2D((PoolingMode)operation.Mode, NanPropagation.PROPAGATE_NAN, operation.WindowHeight, operation.WindowWidth, operation.VerticalPadding, operation.HorizontalPadding, operation.VerticalStride, operation.HorizontalStride);
@@ -49,6 +60,8 @@ public CuDnnPoolingLayer(in TensorInfo input, in PoolingInfo operation, Activati
4960
/// <inheritdoc/>
5061
public override void Forward(in Tensor x, out Tensor z, out Tensor a)
5162
{
63+
_X.TryFree();
64+
x.Duplicate(out _X);
5265
using (DeviceMemory<float>
5366
x_gpu = DnnInstance.Gpu.AllocateDevice(x),
5467
z_gpu = DnnInstance.Gpu.AllocateDevice<float>(x.Entities * OutputInfo.Size))
@@ -58,6 +71,8 @@ public override void Forward(in Tensor x, out Tensor z, out Tensor a)
5871
OutputDescription.Set4D(DataType.FLOAT, TensorFormat.CUDNN_TENSOR_NCHW, x.Entities, OutputInfo.Channels, OutputInfo.Height, OutputInfo.Width);
5972
DnnInstance.PoolingForward(PoolingDescription, 1, InputDescription, x_gpu.Ptr, 0, OutputDescription, z_gpu.Ptr);
6073
z_gpu.CopyToHost(x.Entities, OutputInfo.Size, out z);
74+
_Z.TryFree();
75+
z.Duplicate(out _Z);
6176

6277
// Activation
6378
DnnInstance.ActivationForward(z.Entities, z.Length, z_gpu.Ptr, z_gpu.Ptr, ActivationFunctions.Activation);
@@ -66,7 +81,24 @@ public override void Forward(in Tensor x, out Tensor z, out Tensor a)
6681
}
6782

6883
/// <inheritdoc/>
69-
public override void Backpropagate(in Tensor delta_1, in Tensor z, ActivationFunction activationPrime) => z.UpscalePool2x2(delta_1, InputInfo.Channels);
84+
public override void Backpropagate(in Tensor delta_1, in Tensor z, ActivationFunction activationPrime)
85+
{
86+
using (DeviceMemory<float> dx_gpu = DnnInstance.Gpu.AllocateDevice<float>(z.Size))
87+
{
88+
using (DeviceMemory<float>
89+
x_gpu = DnnInstance.Gpu.AllocateDevice(_X),
90+
y_gpu = DnnInstance.Gpu.AllocateDevice(_Z),
91+
dy_gpu = DnnInstance.Gpu.AllocateDevice(delta_1))
92+
{
93+
DnnInstance.PoolingBackward(PoolingDescription, 1, OutputDescription, y_gpu.Ptr, OutputDescription, dy_gpu.Ptr, InputDescription, x_gpu.Ptr, 0, InputDescription, dx_gpu.Ptr);
94+
}
95+
using (DeviceMemory<float> z_gpu = DnnInstance.Gpu.AllocateDevice(z))
96+
{
97+
DnnInstance.ActivationBackward(z.Entities, z.Length, z_gpu.Ptr, dx_gpu.Ptr, activationPrime);
98+
z_gpu.CopyTo(z);
99+
}
100+
}
101+
}
70102

71103
/// <inheritdoc/>
72104
public override INetworkLayer Clone() => new CuDnnPoolingLayer(InputInfo, OperationInfo, ActivationFunctionType);
@@ -84,5 +116,25 @@ public override void Forward(in Tensor x, out Tensor z, out Tensor a)
84116
if (!stream.TryRead(out PoolingInfo operation)) return null;
85117
return new CuDnnPoolingLayer(input, operation, activation);
86118
}
119+
120+
#region IDisposable
121+
122+
~CuDnnPoolingLayer() => Dispose();
123+
124+
/// <inheritdoc/>
125+
void IDisposable.Dispose()
126+
{
127+
GC.SuppressFinalize(this);
128+
Dispose();
129+
}
130+
131+
// Private Dispose method
132+
private void Dispose()
133+
{
134+
_X.TryFree();
135+
_Z.TryFree();
136+
}
137+
138+
#endregion
87139
}
88140
}

Unit/NeuralNetwork.NET.Cuda.Unit/CuDnnLayersTest.cs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,15 +236,41 @@ public void PoolingForward()
236236
}
237237

238238
[TestMethod]
239-
public void PoolingBackward()
239+
public unsafe void PoolingBackward()
240240
{
241-
float[,]
242-
delta_1 = WeightsProvider.NewFullyConnectedWeights(TensorInfo.CreateLinear(400), 29 * 29 * 3, WeightsInitializationMode.GlorotNormal).AsMatrix(400, 29 * 29 * 3),
243-
z = WeightsProvider.NewFullyConnectedWeights(TensorInfo.CreateLinear(400), 58 * 58 * 3, WeightsInitializationMode.GlorotNormal).AsMatrix(400, 58 * 58 * 3);
241+
// Setup
242+
Tensor.New(400, 58 * 58 * 3, out Tensor x);
243+
KerasWeightsProvider.FillWithHeEtAlUniform(x, 10);
244244
PoolingLayer
245245
cpu = new PoolingLayer(new TensorInfo(58, 58, 3), PoolingInfo.Default, ActivationFunctionType.LeakyReLU),
246246
gpu = new CuDnnPoolingLayer(cpu.InputInfo, PoolingInfo.Default, ActivationFunctionType.LeakyReLU);
247-
TestBackward(cpu, gpu, delta_1, z);
247+
gpu.Forward(x, out Tensor z, out Tensor a);
248+
a.Free();
249+
x.Duplicate(out Tensor x2);
250+
Tensor.New(z.Entities, z.Length, out Tensor delta);
251+
KerasWeightsProvider.FillWithHeEtAlUniform(delta, 10);
252+
253+
// Backward
254+
cpu.Backpropagate(delta, x, ActivationFunctions.LeakyReLUPrime);
255+
gpu.Backpropagate(delta, x2, ActivationFunctions.LeakyReLUPrime);
256+
bool valid = true;
257+
float* px = (float*)x.Ptr.ToPointer(), px2 = (float*)x2.Ptr.ToPointer();
258+
int count = 0;
259+
for (int i = 0; i < x.Size; i++)
260+
{
261+
if (px[i].EqualsWithDelta(px2[i], 1e-5f)) continue;
262+
if (px[i].EqualsWithDelta(px2[i] * 100f, 1e-5f)) count++; // The cuDNN pooling backwards method returns a value scaled by 0.01 from time to time for some reason (less than 2% anyways)
263+
else
264+
{
265+
valid = false;
266+
break;
267+
}
268+
}
269+
Assert.IsTrue(valid && count * 100f / x.Size < 2);
270+
x.Free();
271+
x2.Free();
272+
z.Free();
273+
delta.Free();
248274
}
249275

250276
#endregion

0 commit comments

Comments
 (0)