MyCaffe  1.12.2.41
Deep learning software for Windows C# programmers.
EltwiseLayer.cs
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using MyCaffe.basecode;
6using MyCaffe.common;
7using MyCaffe.param;
8
9namespace MyCaffe.layers
10{
22 public class EltwiseLayer<T> : Layer<T>
23 {
25 List<double> m_rgdfCoeffs = new List<double>();
26 Blob<T> m_blobIdx;
27 Blob<T> m_blobSingleSecondary = null;
28 bool m_bStableProdGrad;
29 bool m_bCoeffBlob;
30
45 : base(cuda, log, p)
46 {
48 m_blobIdx = new Blob<T>(cuda, log);
49 m_blobIdx.Name = m_param.name + " idx";
50 }
51
53 protected override void dispose()
54 {
55 m_blobIdx.Dispose();
56
57 if (m_blobSingleSecondary != null)
58 m_blobSingleSecondary.Dispose();
59
60 base.dispose();
61 }
62
64 protected override void setup_internal_blobs(BlobCollection<T> col)
65 {
66 if (col.Count > 0)
67 return;
68
69 col.Add(m_blobIdx);
70 }
71
75 public override int MinBottomBlobs
76 {
77 get { return 2; }
78 }
79
83 public override int ExactNumTopBlobs
84 {
85 get { return 1; }
86 }
87
93 public override void LayerSetUp(BlobCollection<T> colBottom, BlobCollection<T> colTop)
94 {
96 m_param.eltwise_param.coeff.Count == colBottom.Count, "Eltwise layer takes one coefficient per bottom blob.");
97
99 m_param.eltwise_param.coeff.Count > 0), "Eltwise layer only takes coefficients for SUM and SUB operations.");
100
102 m_bCoeffBlob = m_param.eltwise_param.coeff_blob;
103
104 if (m_bCoeffBlob)
105 m_log.CHECK(m_op == EltwiseParameter.EltwiseOp.SUM || m_op == EltwiseParameter.EltwiseOp.SUB, "coeff_blob option only implemented for the SUM and SUB operation.");
106
107 int nCoeffSize = m_param.eltwise_param.coeff.Count;
108 m_log.CHECK(nCoeffSize == 0 || (!m_bCoeffBlob && nCoeffSize == colBottom.Count)
109 || (m_bCoeffBlob && nCoeffSize == colBottom.Count - 1), "Eltwise Layer takes one coefficient per bottom blob.");
110 m_log.CHECK(m_op == EltwiseParameter.EltwiseOp.SUM || m_op == EltwiseParameter.EltwiseOp.SUB ||
111 layer_param.eltwise_param.coeff.Count == 0, "Eltwise layer only takes coefficients for SUM and SUB operations.");
112
113 // Blob-wise coefficients for the elementwise operation.
114 m_rgdfCoeffs = Utility.Create<double>(colBottom.Count, 1.0);
115
116 int nCoeffBlobCount = (m_bCoeffBlob) ? 1 : 0;
117
118 for (int i = 0; i < m_param.eltwise_param.coeff.Count - nCoeffBlobCount; i++)
119 {
120 m_rgdfCoeffs[i] = m_param.eltwise_param.coeff[i];
121 }
122
123 m_bStableProdGrad = m_param.eltwise_param.stable_prod_grad;
124 }
125
131 public override void Reshape(BlobCollection<T> colBottom, BlobCollection<T> colTop)
132 {
134 m_log.CHECK_EQ(colBottom.Count, 2, "Only two inputs allowed when 'allow_single_batch_input' = true.");
135
136 if (colBottom[1].count() == 1)
137 m_log.CHECK_EQ(colBottom.Count, 2, "Only two inputs allowed when colBottom[1].count() == 1.");
138
139 for (int i = 1; i < colBottom.Count; i++)
140 {
141 if (m_bCoeffBlob && i == colBottom.Count - 1)
142 {
143 m_log.CHECK_EQ(i, colBottom[i].shape(0), "Dimensions of coeff blob axis 0 must equal the number of bottom blobs (not including the coeff blob itself).");
144
145 for (int input_axis = 0, coeff_axis = 1; coeff_axis < colBottom[i].num_axes; input_axis++, coeff_axis++)
146 {
147 m_log.CHECK_EQ(colBottom[0].shape(input_axis), colBottom[i].shape(coeff_axis), "Each axis i >= 1 of the coeff blob must match the (i-1)th axis of the input.");
148 }
149 }
150 else
151 {
152 if (colBottom.Count == 2 && colBottom[1].count() == 1)
153 {
154 if (m_blobSingleSecondary == null)
155 {
156 m_blobSingleSecondary = new Blob<T>(m_cuda, m_log);
157 m_blobSingleSecondary.ReshapeLike(colBottom[0]);
158
159 double dfVal = Utility.ConvertVal<T>(colBottom[i].GetData(0));
160 m_blobSingleSecondary.SetData(dfVal);
161 }
162 }
163 else
164 {
166 m_log.CHECK(Utility.Compare<int>(colBottom[i].shape(), colBottom[0].shape(), false), "The bottoms should all be of the same shape.");
167 else
168 {
169 if (m_blobSingleSecondary == null)
170 m_blobSingleSecondary = new Blob<T>(m_cuda, m_log);
171 m_blobSingleSecondary.ReshapeLike(colBottom[0]);
172
173 m_log.CHECK_EQ(colBottom[i].num, 1, "The batch for the second input must be 1.");
174 m_log.CHECK_EQ(colBottom[i].count(1), colBottom[0].count(1), "All shapes other than the first shape must match!");
175 }
176 }
177 }
178 }
179
180 colTop[0].ReshapeLike(colBottom[0]);
181
182 // If max operation, we will initialize the vector index part.
184 m_blobIdx.Reshape(colBottom[0].shape());
185 }
186
192 protected override void forward(BlobCollection<T> colBottom, BlobCollection<T> colTop)
193 {
194 Blob<T> blob = (m_blobSingleSecondary != null) ? m_blobSingleSecondary : colBottom[1];
195 long hMask = 0;
196 int nCount = colTop[0].count();
197 long hTopData = colTop[0].mutable_gpu_data;
198 long hCoeffData = 0;
199 int nCoeffCount = 0;
200
202 {
203 // Copy each colBottom[1] to each batch item in blobSingleSecondary.
204 m_cuda.channel_copyall(blob.count(),
205 blob.num,
206 blob.channels,
207 blob.count(2),
208 colBottom[1].gpu_data,
209 blob.mutable_gpu_data);
210 }
211
212 switch (m_op)
213 {
214 case EltwiseParameter.EltwiseOp.PROD:
215 m_cuda.mul(nCount, colBottom[0].gpu_data, blob.gpu_data, hTopData);
216
217 for (int i = 2; i < colBottom.Count; i++)
218 {
219 m_cuda.mul(nCount, hTopData, colBottom[i].gpu_data, hTopData);
220 }
221 break;
222
224 m_cuda.div(nCount, colBottom[0].gpu_data, blob.gpu_data, hTopData);
225
226 for (int i = 2; i < colBottom.Count; i++)
227 {
228 m_cuda.div(nCount, hTopData, colBottom[i].gpu_data, hTopData);
229 }
230 break;
231
233 if (m_bCoeffBlob)
234 {
235 int nNum = colTop[0].num;
236 int nDim = nCount / nNum;
237 hCoeffData = colBottom[colBottom.Count - 1].gpu_data;
238 nCoeffCount = 1;
239
240 for (int i = 0; i < colBottom.Count - nCoeffCount; i++)
241 {
242 long hBottomData = (i == 0 || colBottom.Count > 3) ? colBottom[i].gpu_data : blob.gpu_data;
243 m_cuda.coeff_sum_fwd(nCount, nDim, i * nNum, m_rgdfCoeffs[i], hCoeffData, hBottomData, hTopData);
244 }
245 }
246 else
247 {
248 m_cuda.set(nCount, hTopData, 0);
249 // TODO(shelhamer) does cuBLAS optimize to sum of coeff = 1?
250 for (int i = 0; i < colBottom.Count; i++)
251 {
252 long hBottomData = (i == 0 || colBottom.Count > 2) ? colBottom[i].gpu_data : blob.gpu_data;
253 m_cuda.axpy(nCount, m_rgdfCoeffs[i], hBottomData, hTopData);
254 }
255 }
256 break;
257
259 if (m_bCoeffBlob)
260 {
261 int nNum = colTop[0].num;
262 int nDim = nCount / nNum;
263 hCoeffData = colBottom[colBottom.Count - 1].gpu_data;
264 nCoeffCount = 1;
265
266 for (int i = 0; i < colBottom.Count - nCoeffCount; i++)
267 {
268 long hBottomData = (i == 0 || colBottom.Count > 3) ? colBottom[i].gpu_data : blob.gpu_data;
269 m_cuda.coeff_sub_fwd(nCount, nDim, i * nNum, m_rgdfCoeffs[i], hCoeffData, hBottomData, hTopData);
270 }
271 }
272 else
273 {
274 m_cuda.scale(nCount, m_rgdfCoeffs[0], colBottom[0].gpu_data, hTopData);
275
276 for (int i = 1; i < colBottom.Count; i++)
277 {
278 long hBottomData = (i == 0 || colBottom.Count > 2) ? colBottom[i].gpu_data : blob.gpu_data;
279 m_cuda.axpy(nCount, -1 * m_rgdfCoeffs[i], hBottomData, hTopData);
280 }
281 }
282 break;
283
285 hMask = m_blobIdx.mutable_gpu_data;
286 m_cuda.max_fwd(nCount, colBottom[0].gpu_data, colBottom[1].gpu_data, 0, hTopData, hMask);
287
288 for (int i = 2; i < colBottom.Count; i++)
289 {
290 m_cuda.max_fwd(nCount, hTopData, colBottom[i].gpu_data, i-1, hTopData, hMask);
291 }
292 break;
293
295 hMask = m_blobIdx.mutable_gpu_data;
296 m_cuda.min_fwd(nCount, colBottom[0].gpu_data, colBottom[1].gpu_data, 0, hTopData, hMask);
297
298 for (int i = 2; i < colBottom.Count; i++)
299 {
300 m_cuda.min_fwd(nCount, hTopData, colBottom[i].gpu_data, i - 1, hTopData, hMask);
301 }
302 break;
303
304 default:
305 m_log.FAIL("Unknown elementwise operation.");
306 break;
307 }
308 }
309
316 protected override void backward(BlobCollection<T> colTop, List<bool> rgbPropagateDown, BlobCollection<T> colBottom)
317 {
318 long hMask = 0;
319 int nCount = colTop[0].count();
320 long hTopData = colTop[0].gpu_data;
321 long hTopDiff = colTop[0].gpu_diff;
322 long hCoeffData = 0;
323 int nNum = colTop[0].num;
324 int nDim = nCount / nNum;
325
326 if (m_bCoeffBlob)
327 hCoeffData = colBottom[colBottom.Count - 1].gpu_data;
328
329 for (int i = 0; i < colBottom.Count; i++)
330 {
331 if (rgbPropagateDown[i])
332 {
333 long hBottomData = colBottom[i].gpu_data;
334 long hBottomDiff = colBottom[i].mutable_gpu_diff;
335
336 if (i == 1 && m_blobSingleSecondary != null)
337 {
338 hBottomData = m_blobSingleSecondary.gpu_data;
339 hBottomDiff = m_blobSingleSecondary.mutable_gpu_diff;
340 }
341
342 switch (m_op)
343 {
344 case EltwiseParameter.EltwiseOp.PROD:
345 if (m_bStableProdGrad)
346 {
347 bool bInitialized = false;
348 for (int j = 0; j < colBottom.Count; j++)
349 {
350 if (i == j)
351 continue;
352
353 if (!bInitialized)
354 {
355 m_cuda.copy(nCount, colBottom[j].gpu_data, hBottomDiff);
356 bInitialized = true;
357 }
358 else
359 {
360 m_cuda.mul(nCount, colBottom[j].gpu_data, hBottomDiff, hBottomDiff);
361 }
362 }
363 }
364 else
365 {
366 m_cuda.div(nCount, hTopData, hBottomData, hBottomDiff);
367 }
368 m_cuda.mul(nCount, hBottomDiff, hTopDiff, hBottomDiff);
369 break;
370
372 m_cuda.mul(nCount, hTopData, hBottomData, hBottomDiff);
373 m_cuda.mul(nCount, hBottomDiff, hTopDiff, hBottomDiff);
374 break;
375
377 if (m_bCoeffBlob)
378 {
379 m_cuda.coeff_sum_bwd(nCount, nDim, i * nNum, m_rgdfCoeffs[i], hCoeffData, hTopDiff, hBottomDiff);
380 }
381 else
382 {
383 if (m_rgdfCoeffs[i] == 1.0)
384 m_cuda.copy(nCount, hTopDiff, hBottomDiff);
385 else
386 m_cuda.scale(nCount, m_rgdfCoeffs[i], hTopDiff, hBottomDiff);
387 }
388 break;
389
391 if (m_bCoeffBlob)
392 {
393 m_cuda.coeff_sub_bwd(nCount, nDim, i * nNum, m_rgdfCoeffs[i], hCoeffData, hTopDiff, hBottomDiff);
394 }
395 else
396 {
397 double dfScale = (i == 0) ? 1 : -1;
398 m_cuda.scale(nCount, dfScale * m_rgdfCoeffs[i], hTopDiff, hBottomDiff);
399 }
400 break;
401
403 hMask = m_blobIdx.gpu_data;
404 m_cuda.max_bwd(nCount, hTopDiff, i, hMask, hBottomDiff);
405 break;
406
408 hMask = m_blobIdx.gpu_data;
409 m_cuda.min_bwd(nCount, hTopDiff, i, hMask, hBottomDiff);
410 break;
411
412 default:
413 m_log.FAIL("Unknown elementwise operation.");
414 break;
415 }
416 }
417 }
418
419 // sum the gradients across channels.
420 if (m_param.eltwise_param.allow_single_batch_input && colBottom[1].num == 1 && m_blobSingleSecondary != null)
421 m_cuda.channel_sum(nCount, 1, nNum, colTop[0].channels * colTop[0].count(2), m_blobSingleSecondary.gpu_diff, colBottom[1].mutable_gpu_diff);
422 }
423 }
424}
The Log class provides general output in text form.
Definition: Log.cs:13
void CHECK(bool b, string str)
Test a flag for true.
Definition: Log.cs:227
void FAIL(string str)
Causes a failure which throws an exception with the desciptive text.
Definition: Log.cs:394
void CHECK_EQ(double df1, double df2, string str)
Test whether one number is equal to another.
Definition: Log.cs:239
The Utility class provides general utility funtions.
Definition: Utility.cs:35
static List< int > Create(int nCount, int nStart, int nInc)
Create a new List and fill it with values starting with start and incrementing by inc.
Definition: Utility.cs:721
The BlobCollection contains a list of Blobs.
void Add(Blob< T > b)
Add a new Blob to the collection.
int Count
Returns the number of items in the collection.
void ReshapeLike(BlobCollection< T > src)
Reshapes all blobs in the collection to the sizes of the source.
The Blob is the main holder of data that moves through the Layers of the Net.
Definition: Blob.cs:25
int channels
DEPRECIATED; legacy shape accessor channels: use shape(1) instead.
Definition: Blob.cs:800
void SetData(T[] rgData, int nCount=-1, bool bSetCount=true)
Sets a number of items within the Blob's data.
Definition: Blob.cs:1922
long mutable_gpu_diff
Returns the diff GPU handle used by the CudaDnn connection.
Definition: Blob.cs:1555
long mutable_gpu_data
Returns the data GPU handle used by the CudaDnn connection.
Definition: Blob.cs:1487
void Reshape(int nNum, int nChannels, int nHeight, int nWidth, bool? bUseHalfSize=null)
DEPRECIATED; use
Definition: Blob.cs:442
int count()
Returns the total number of items in the Blob.
Definition: Blob.cs:739
void ReshapeLike(Blob< T > b, bool? bUseHalfSize=null)
Reshape this Blob to have the same shape as another Blob.
Definition: Blob.cs:648
string Name
Get/set the name of the Blob.
Definition: Blob.cs:2184
long gpu_diff
Returns the diff GPU handle used by the CudaDnn connection.
Definition: Blob.cs:1541
virtual void Dispose(bool bDisposing)
Releases all resources used by the Blob (including both GPU and Host).
Definition: Blob.cs:402
int num
DEPRECIATED; legacy shape accessor num: use shape(0) instead.
Definition: Blob.cs:792
long gpu_data
Returns the data GPU handle used by the CudaDnn connection.
Definition: Blob.cs:1479
The CudaDnn object is the main interface to the Low-Level Cuda C++ DLL.
Definition: CudaDnn.cs:969
The EltwiseLayer computes elementwise oeprations, such as product and sum, along multiple input blobs...
Definition: EltwiseLayer.cs:23
override void LayerSetUp(BlobCollection< T > colBottom, BlobCollection< T > colTop)
Setup the layer.
Definition: EltwiseLayer.cs:93
EltwiseLayer(CudaDnn< T > cuda, Log log, LayerParameter p)
The EltwiseLayer constructor.
Definition: EltwiseLayer.cs:44
override void forward(BlobCollection< T > colBottom, BlobCollection< T > colTop)
The Forward computation.
override int ExactNumTopBlobs
Returns the exact number of required top (output) Blobs: output (result of eltwise operation in input...
Definition: EltwiseLayer.cs:84
override int MinBottomBlobs
Returns the minimum required number of bottom (input) Blobs: input1, input2
Definition: EltwiseLayer.cs:76
override void dispose()
Releases all GPU and host resources used by the Layer.
Definition: EltwiseLayer.cs:53
override void backward(BlobCollection< T > colTop, List< bool > rgbPropagateDown, BlobCollection< T > colBottom)
Computes the error gradient w.r.t. the input.
override void Reshape(BlobCollection< T > colBottom, BlobCollection< T > colTop)
Reshape the bottom (input) and top (output) blobs.
override void setup_internal_blobs(BlobCollection< T > col)
Derivative layers should add all internal blobws to the 'col' provided.
Definition: EltwiseLayer.cs:64
An interface for the units of computation which can be composed into a Net.
Definition: Layer.cs:31
Log m_log
Specifies the Log for output.
Definition: Layer.cs:43
LayerParameter m_param
Specifies the LayerParameter describing the Layer.
Definition: Layer.cs:47
CudaDnn< T > m_cuda
Specifies the CudaDnn connection to Cuda.
Definition: Layer.cs:39
LayerParameter.LayerType m_type
Specifies the Layer type.
Definition: Layer.cs:35
LayerParameter layer_param
Returns the LayerParameter for this Layer.
Definition: Layer.cs:899
Specifies the parameters for the EltwiseLayer.
EltwiseOp
Defines the operation to perform.
bool allow_single_batch_input
Specifies whether to allow single batch input for the second input (default = false).
List< double > coeff
Specifies the blob-wise coefficient for SUM operation.
bool stable_prod_grad
Specifies whether or not to use an asymptotically slower (for > 2 inputs) but stabler method of compu...
EltwiseOp operation
Specifies the element-wise operation.
bool coeff_blob
If true and the EltwiseOp is SUM, the last bottom blob is a singleton coefficient for the first N-1 b...
Specifies the base parameter for all layers.
string name
Specifies the name of this LayerParameter.
EltwiseParameter eltwise_param
Returns the parameter set when initialized with LayerType.ELTWISE
LayerType
Specifies the layer type.
The MyCaffe.basecode contains all generic types used throughout MyCaffe.
Definition: Annotation.cs:12
The MyCaffe.common namespace contains common MyCaffe classes.
Definition: BatchInput.cs:8
The MyCaffe.layers namespace contains all layers that have a solidified code base,...
Definition: LayerFactory.cs:15
The MyCaffe.param namespace contains parameters used to create models.
The MyCaffe namespace contains the main body of MyCaffe code that closesly tracks the C++ Caffe open-...
Definition: Annotation.cs:12