@@ -35,7 +35,7 @@ TFOutput ReduceDims (TFOutput input, TFOutput? axis = null)
3535 for ( int i = 0 ; i < array . Length ; i ++ )
3636 array [ i ] = i ;
3737
38- return this . Const ( array , TFDataType . Int32 ) ;
38+ return this . Const ( array , TFDataType . Int32 ) ;
3939 }
4040 return Range ( Const ( 0 ) , Const ( shape . Length ) , Const ( 1 ) ) ;
4141 }
@@ -277,70 +277,266 @@ public void GetRandomSeeds (int? operationSeed, out int graphSeed, out int local
277277 localSeed = operationSeed . Value ;
278278 } else {
279279 localSeed = 0 ;
280- }
280+ }
281+ }
282+ }
283+
284+ /// <summary>
285+ /// Computes dropout.
286+ /// </summary>
287+ /// <param name="x">A tensor.</param>
288+ /// <param name="keep_prob">A scalar Tensor with the same type as x. The probability that each element is kept.</param>
289+ /// <param name="noise_shape">A 1-D Tensor of type int32, representing the shape for randomly generated keep/drop flags.</param>
290+ /// <param name="seed">Integer seed used for the random distribution, using the TensorFlow SetRandomSeed .</param>
291+ /// <param name="operName">Operation name, optional.</param>
292+ /// <remarks>
293+ /// With probability keep_prob, outputs the input element scaled up by 1 / keep_prob,
294+ /// otherwise outputs 0. The scaling is so that the expected sum is unchanged.
295+ /// </remarks>
296+ public TFOutput Dropout ( TFOutput x , TFOutput keep_prob , TFShape noise_shape = null , int ? seed = null , string operName = null )
297+ {
298+ var scopeName = MakeName ( "dropout" , operName ) ;
299+
300+ using ( var newScope = WithScope ( scopeName ) ) {
301+ if ( noise_shape == null )
302+ noise_shape = new TFShape ( GetShape ( x ) ) ;
303+
304+ TFOutput shapeTensor = ShapeTensorOutput ( noise_shape ) ;
305+
306+ // uniform [keep_prob, 1.0 + keep_prob)
307+ TFOutput random_tensor = keep_prob ;
308+ random_tensor = Add ( random_tensor , RandomUniform ( shapeTensor , seed : seed , dtype : x . OutputType ) ) ;
309+
310+ // 0. if [keep_prob, 1.0) and 1. if [1.0, 1.0 + keep_prob)
311+ TFOutput binary_tensor = Floor ( random_tensor ) ;
312+ TFOutput ret = Mul ( Div ( x , keep_prob ) , binary_tensor ) ;
313+ SetTensorShape ( ret , GetShape ( x ) ) ;
314+ return ret ;
315+ }
316+ }
317+
318+ /// <summary>
319+ /// Computes dropout.
320+ /// </summary>
321+ /// <param name="x">A tensor.</param>
322+ /// <param name="keep_prob">A scalar Tensor with the same type as x. The probability that each element is kept.</param>
323+ /// <param name="noise_shape">A 1-D Tensor of type int32, representing the shape for randomly generated keep/drop flags.</param>
324+ /// <param name="seed">Integer seed used for the random distribution, using the TensorFlow SetRandomSeed .</param>
325+ /// <param name="operName">Operation name, optional.</param>
326+ /// <remarks>
327+ /// With probability keep_prob, outputs the input element scaled up by 1 / keep_prob,
328+ /// otherwise outputs 0. The scaling is so that the expected sum is unchanged.
329+ /// </remarks>
330+ public TFOutput Dropout ( TFOutput x , double keep_prob , TFShape noise_shape = null , int ? seed = null , string operName = null )
331+ {
332+ if ( keep_prob < 0 || keep_prob >= 1 )
333+ throw new ArgumentOutOfRangeException ( "keep_prob must be a scalar tensor or a float in the range (0, 1], got " + keep_prob ) ;
334+
335+ if ( keep_prob == 1 )
336+ return x ;
337+
338+ var scopeName = MakeName ( "dropout" , operName ) ;
339+ using ( var newScope = WithScope ( scopeName ) ) {
340+ var tkeep_prob = Const ( keep_prob ) ;
341+ return Dropout ( x , tkeep_prob , noise_shape , seed , operName ) ;
342+ }
343+ }
344+
345+
346+
347+ /// <summary>
348+ /// Clips tensor values to a specified min and max.
349+ /// </summary>
350+ /// <remarks>
351+ /// Given a tensor <paramref name="x"/>, this operation returns a tensor of the same type and shape
352+ /// as <paramref name="x"/> with its values clipped to <paramref name="clip_value_min"/> and <paramref name="clip_value_max"/>.
353+ /// Any values less than <paramref name="clib_value_min"/> are set to <paramref name="clip_value_min"/>. Any values greater than
354+ /// <paramref name="clip_value_max"/> are set to <paramref name="clip_value_max"/>.
355+ /// </remarks>
356+ /// <param name="x">The tensor.</param>
357+ /// <param name="clip_value_min">The minimum value to clip by. A 0 - D(scalar) tensor, or a tensor with the same shape as <paramref name="x"/>.</param>
358+ /// <param name="clip_value_max">The minimum value to clip by. A 0 - D(scalar) tensor, or a tensor with the same shape as <paramref name="x"/>.</param>
359+ /// <param name="operName">Operation name, optional.</param>
360+ /// <returns>A clipped <see cref="TFOutput">tensor</see>.</returns>
361+ public TFOutput ClipByValue ( TFOutput x , TFOutput clip_value_min , TFOutput clip_value_max , string operName = null )
362+ {
363+ // https://github.com/tensorflow/tensorflow/blob/r1.2/tensorflow/python/ops/clip_ops.py#L33
364+ var scopeName = MakeName ( "ClipByValue" , operName ) ;
365+ using ( var newScope = WithScope ( scopeName ) ) {
366+ // Go through list of tensors, for each value in each tensor clip
367+ var t_min = Minimum ( x , clip_value_max ) ;
368+ var t_max = Maximum ( t_min , clip_value_min , operName : operName ) ;
369+ return t_max ;
370+ }
371+ }
372+
373+ /// <summary>
374+ /// Clips tensor values to a maximum L2-norm.
375+ /// </summary>
376+ /// <remarks>
377+ /// <para>
378+ /// Given a tensor <paramref name="x"/>, and a maximum clip value <paramref name="clip_norm"/>, this operation normalizes
379+ /// <paramref name="x"/> so that its L2-norm is less than or equal to <paramref name="clip_norm"/>, along the dimensions
380+ /// given in <paramref name="axes"/>. Specifically, in the default case where all dimensions are used for calculation, if
381+ /// the L2-norm of <paramref name="x"/> is already less than or equal to <paramref name="clip_norm"/>, then <paramref name="x"/>
382+ /// is not modified. If the L2-norm is greater than <paramref name="clip_norm"/>, then this operation returns a tensor of
383+ /// the same type and shape as <paramref name="x"/> with its values set to: <c>t* clip_norm / l2norm(t)</c></para>
384+ /// </remarks>
385+ /// <param name="x">The tensor.</param>
386+ /// <param name="clip_norm">The minimum value to clip by. A 0 - D(scalar) tensor, or a tensor with the same shape as <paramref name="x"/>.</param>
387+ /// <param name="axes">The minimum value to clip by. A 0 - D(scalar) tensor, or a tensor with the same shape as <paramref name="x"/>.</param>
388+ /// <param name="operName">Operation name, optional.</param>
389+ /// <returns>A clipped <see cref="TFOutput">tensor</see>.</returns>
390+ public TFOutput ClipByNorm ( TFOutput x , TFOutput clip_norm , TFOutput ? axes = null , string operName = null )
391+ {
392+ // https://github.com/tensorflow/tensorflow/blob/r1.2/tensorflow/python/ops/clip_ops.py#L73
393+ var scopeName = MakeName ( "ClipByNorm" , operName ) ;
394+ using ( var newScope = WithScope ( scopeName ) ) {
395+ // Calculate L2-norm, clip elements by ratio of clip_norm to L2-norm
396+ var l2norm_inv = Rsqrt ( ReduceSum ( Mul ( x , x ) , axes , keep_dims : true ) ) ;
397+ var intermediate = Mul ( x , clip_norm ) ;
398+
399+ var tclip = Identity ( Mul ( intermediate , Minimum ( l2norm_inv , Div ( Const ( new TFTensor ( 1.0 ) ) , clip_norm ) , operName : operName ) ) ) ;
400+
401+ return tclip ;
402+ }
403+ }
404+
405+ /// <summary>
406+ /// Computes the global norm of multiple tensors.
407+ /// </summary>
408+ /// <remarks>
409+ /// <para>
410+ /// Given a tuple or list of tensors <paramref name="tensors"/>, this operation returns the global norm of the elements in all tensors
411+ /// in <paramref name="tensors"/>. The global norm is computed as: <c>global_norm = sqrt(sum([l2norm(t)**2 for t in t_list]))</c>. Any
412+ /// entries in <paramref name="tensors"/> that are of type None are ignored.</para>
413+ /// </remarks>
414+ /// <param name="tensors">The input tensors.</param>
415+ /// <param name="operName">Operation name, optional.</param>
416+ /// <returns>A clipped <see cref="TFOutput">tensor</see>.</returns>
417+ public TFOutput GlobalNorm ( TFOutput [ ] tensors , string operName = null )
418+ {
419+ // https://github.com/tensorflow/tensorflow/blob/r1.2/tensorflow/python/ops/clip_ops.py#L122
420+ var scopeName = MakeName ( "GlobalNorm" , operName ) ;
421+ using ( var newScope = WithScope ( scopeName ) ) {
422+ TFOutput [ ] half_squared_norms = new TFOutput [ tensors . Length ] ;
423+
424+ for ( int i = 0 ; i < half_squared_norms . Length ; i ++ )
425+ half_squared_norms [ i ] = L2Loss ( tensors [ i ] ) ;
426+
427+ TFOutput half_squared_norm = ReduceSum ( Stack ( half_squared_norms ) ) ;
428+ TFOutput norm = Sqrt ( Mul ( half_squared_norm , Const ( 2.0 ) ) , operName : "global_norm" ) ;
429+ return norm ;
430+ }
431+ }
432+
433+ /// <summary>
434+ /// Clips tensor values to a maximum average L2-norm.
435+ /// </summary>
436+ /// <remarks>
437+ /// Given a tensor <paramref name="x"/>, and a maximum clip value <paramref name="clip_norm"/>, this operation
438+ /// normalizes <paramref name="x"/> so that its its average L2-norm is less than or equal to <paramref name="clip_norm"/>.
439+ /// Specifically, if the average L2-norm is already less than or equal to <paramref name="clip_norm"/>, then <paramref name="x"/>
440+ /// is not modified. If the average L2-norm is greater than <paramref name="clip_norm"/>, then this operation returns a tensor of the same
441+ /// type and shape as <paramref name="x"/> with its values set to: <c>t* clip_norm / l2norm_avg(t)</c>. In this case,
442+ /// the average L2-norm of the output tensor is <paramref name="clip_norm"/>.
443+ /// </remarks>
444+ /// <param name="x">The input tensor.</param>
445+ /// <param name="clip_norm">A maximum clipping value.</param>
446+ /// <param name="operName">Name of the oper.</param>
447+ public TFOutput ClipByAverageNorm ( TFOutput x , TFOutput clip_norm , string operName = null )
448+ {
449+ // https://github.com/tensorflow/tensorflow/blob/r1.2/tensorflow/python/ops/clip_ops.py#L251
450+ var scopeName = MakeName ( "ClipByAverageNorm" , operName ) ;
451+ using ( var newScope = WithScope ( scopeName ) ) {
452+ // Calculate L2-norm per element, clip elements by ratio of clip_norm to
453+ // L2-norm per element
454+ TFOutput n_element = Cast ( Size ( x ) , TFDataType . Float ) ;
455+ TFOutput l2norm_inv = Rsqrt ( ReduceSum ( Mul ( x , x ) , Range ( Rank ( x ) ) ) ) ;
456+ TFOutput tclip = Identity ( Mul ( Mul ( x , clip_norm ) , Minimum ( Mul ( l2norm_inv , n_element ) , Div ( Const ( new TFTensor ( 1.0 ) ) , clip_norm ) ) , operName : operName ) ) ;
457+
458+ return tclip ;
459+ }
460+ }
461+
462+
463+
464+
465+
466+
467+
468+
469+
470+
471+ /// <summary>
472+ /// Stacks a list of rank-`R` tensors into one rank-`(R+1)` tensor.
473+ /// </summary>
474+ /// <remarks>
475+ /// Packs the list of tensors in <paramref name="values"/> into a tensor with rank one higher than
476+ /// each tensor in <paramref name="values"/>, by packing them along the <paramref name="axis"/> dimension.
477+ /// Given a list of length <c>N</c> of tensors of shape </c>(A, B, C)</c>: if <c>axis == 0</c> then the
478+ /// <c>output</c> tensor will have the shape <c>(N, A, B, C)</c>; if <c>axis == 1<c> then the <c>output<c>
479+ /// tensor will have the shape <c>(A, N, B, C)<c>; etc.
480+ /// </remarks>
481+ ///
482+ public TFOutput Stack ( TFOutput [ ] values , int ? axis = 0 , string operName = "stack" )
483+ {
484+ // https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/ops/array_ops.py#L804
485+
486+ int ndims = GetTensorNumDims ( values [ 0 ] ) ;
487+
488+ int expanded_num_dims = ndims + 1 ;
489+ if ( axis < - expanded_num_dims || axis >= expanded_num_dims )
490+ throw new InvalidOperationException ( $ "axis = { axis } not in [{ - expanded_num_dims } , { expanded_num_dims } ]") ;
491+
492+ return Pack ( values , axis : axis , operName : operName ) ;
493+ }
494+
495+ /// <summary>
496+ /// Creates a sequence of numbers.
497+ /// </summary>
498+ /// <remarks>
499+ /// Creates a sequence of numbers that begins at `start` and extends by increments of `delta` up to but not including
500+ /// `limit`. The dtype of the resulting tensor is inferred from the inputs unless it is provided explicitly.
501+ /// </remarks>
502+ /// <param name="start">A 0 - D `Tensor` (scalar).Acts as first entry in the range if `limit` is not None; otherwise, acts as range limit and first entry defaults to 0.</param>
503+ /// <param name="limit">A 0 - D `Tensor` (scalar).Upper limit of sequence, exclusive. If None, defaults to the value of `start` while the first entry of the range defaults to 0.</param>
504+ /// <param name="delta">A 0 - D `Tensor` (scalar).Number that increments `start`. Defaults to 1.</param>
505+ /// <param name="dataType">The type of the elements of the resulting tensor.</param>
506+ /// <param name="operName">A name for the operation.Defaults to "range".</param>
507+ public TFOutput Range ( TFOutput start , TFOutput ? limit = null , TFOutput ? delta = null , TFDataType ? dataType = null , string operName = "range" )
508+ {
509+ // https://github.com/tensorflow/tensorflow/blob/r1.2/tensorflow/python/ops/math_ops.py#L1156
510+
511+ if ( limit == null ) {
512+ limit = start ;
513+ start = Cast ( Const ( new TFTensor ( 0.0 ) ) , start . OutputType ) ; // TODO: Maybe add dataType as convenience in Const?
514+ }
515+
516+ if ( delta == null )
517+ delta = Cast ( Const ( new TFTensor ( 1.0 ) ) , start . OutputType ) ;
518+
519+ using ( var newScope = WithScope ( MakeName ( "Range" , operName ) ) ) {
520+ // infer dtype if not explicitly provided
521+ if ( dataType == null ) {
522+ var dtype_hierarchy = new [ ] { TFDataType . Int32 , TFDataType . Int64 , TFDataType . Float , TFDataType . Double } ;
523+ if ( ! dtype_hierarchy . Contains ( start . OutputType )
524+ || ! dtype_hierarchy . Contains ( limit . Value . OutputType )
525+ || ! dtype_hierarchy . Contains ( delta . Value . OutputType ) )
526+ throw new ArgumentException ( "Unexpected type" ) ;
527+
528+ TFDataType [ ] dtypes = new [ ] { start . OutputType , limit . Value . OutputType , delta . Value . OutputType } ;
529+ int imax = dtypes . Select ( x => Array . IndexOf ( dtype_hierarchy , x ) ) . Max ( ) ;
530+ TFDataType inferred_dtype = dtype_hierarchy [ imax ] ;
531+
532+ start = Cast ( start , inferred_dtype ) ;
533+ limit = Cast ( limit . Value , inferred_dtype ) ;
534+ delta = Cast ( delta . Value , inferred_dtype ) ;
535+ }
536+
537+ return Range ( start , limit . Value , delta . Value , operName : operName ) ;
281538 }
282539 }
283540
284- /// <summary>
285- /// Computes dropout.
286- /// </summary>
287- /// <param name="x">A tensor.</param>
288- /// <param name="keep_prob">A scalar Tensor with the same type as x. The probability that each element is kept.</param>
289- /// <param name="noise_shape">A 1-D Tensor of type int32, representing the shape for randomly generated keep/drop flags.</param>
290- /// <param name="seed">Integer seed used for the random distribution, using the TensorFlow SetRandomSeed .</param>
291- /// <param name="operName">Operation name, optional.</param>
292- /// <remarks>
293- /// With probability keep_prob, outputs the input element scaled up by 1 / keep_prob,
294- /// otherwise outputs 0. The scaling is so that the expected sum is unchanged.
295- /// </remarks>
296- public TFOutput Dropout ( TFOutput x , TFOutput keep_prob , TFShape noise_shape = null , int ? seed = null , string operName = null )
297- {
298- var scopeName = MakeName ( "dropout" , operName ) ;
299-
300- using ( var newScope = WithScope ( scopeName ) ) {
301- if ( noise_shape == null )
302- noise_shape = new TFShape ( GetShape ( x ) ) ;
303-
304- TFOutput shapeTensor = ShapeTensorOutput ( noise_shape ) ;
305-
306- // uniform [keep_prob, 1.0 + keep_prob)
307- TFOutput random_tensor = keep_prob ;
308- random_tensor = Add ( random_tensor , RandomUniform ( shapeTensor , seed : seed , dtype : x . OutputType ) ) ;
309-
310- // 0. if [keep_prob, 1.0) and 1. if [1.0, 1.0 + keep_prob)
311- TFOutput binary_tensor = Floor ( random_tensor ) ;
312- TFOutput ret = Mul ( Div ( x , keep_prob ) , binary_tensor ) ;
313- SetTensorShape ( ret , GetShape ( x ) ) ;
314- return ret ;
315- }
316- }
317-
318- /// <summary>
319- /// Computes dropout.
320- /// </summary>
321- /// <param name="x">A tensor.</param>
322- /// <param name="keep_prob">A scalar Tensor with the same type as x. The probability that each element is kept.</param>
323- /// <param name="noise_shape">A 1-D Tensor of type int32, representing the shape for randomly generated keep/drop flags.</param>
324- /// <param name="seed">Integer seed used for the random distribution, using the TensorFlow SetRandomSeed .</param>
325- /// <param name="operName">Operation name, optional.</param>
326- /// <remarks>
327- /// With probability keep_prob, outputs the input element scaled up by 1 / keep_prob,
328- /// otherwise outputs 0. The scaling is so that the expected sum is unchanged.
329- /// </remarks>
330- public TFOutput Dropout ( TFOutput x , double keep_prob , TFShape noise_shape = null , int ? seed = null , string operName = null )
331- {
332- if ( keep_prob < 0 || keep_prob >= 1 )
333- throw new ArgumentOutOfRangeException ( "keep_prob must be a scalar tensor or a float in the range (0, 1], got " + keep_prob ) ;
334-
335- if ( keep_prob == 1 )
336- return x ;
337-
338- var scopeName = MakeName ( "dropout" , operName ) ;
339- using ( var newScope = WithScope ( scopeName ) ) {
340- var tkeep_prob = Const ( keep_prob ) ;
341- return Dropout ( x , tkeep_prob , noise_shape , seed , operName ) ;
342- }
343- }
344- }
345-
346- }
541+ }
542+ }
0 commit comments