@@ -488,6 +488,77 @@ public TFOutput ClipByAverageNorm (TFOutput x, TFOutput clip_norm, string operNa
488488 }
489489 }
490490
491+ /// <summary>
492+ /// Computes sigmoid cross entropy given `logits`.
493+ /// </summary>
494+ ///
495+ /// <remarks>
496+ /// Measures the probability error in discrete classification tasks in which each
497+ /// class is independent and not mutually exclusive.For instance, one could
498+ /// perform multilabel classification where a picture can contain both an elephant
499+ /// and a dog at the same time.
500+ /// </remarks>
501+ ///
502+ public TFOutput SigmoidCrossEntropyWithLogits ( TFOutput labels , TFOutput logits , string operName = null )
503+ {
504+ // https://github.com/tensorflow/tensorflow/blob/r1.3/tensorflow/python/ops/nn_impl.py#L100
505+
506+ var scopeName = this . MakeName ( "logistic_loss" , operName ) ;
507+ using ( var newScope = this . WithScope ( scopeName ) ) {
508+ // Note: The following lines have not been ported from the original TF implementation since
509+ // TensorFlowSharp API should guarantee that logits and labels are of type TFOutput by design:
510+ //
511+ // logits = ops.convert_to_tensor(logits, name: "logits");
512+ // labels = ops.convert_to_tensor(labels, name: "labels");
513+ // try
514+ // {
515+ // labels.get_shape().merge_with(logits.get_shape())
516+ // }
517+ // catch
518+ // {
519+ // throw new ArgumentException("logits and labels must have the same shape ({logits.get_shape()} vs {labels.get_shape()})");
520+ // }
521+
522+ // The logistic loss formula from above is
523+ // x - x * z + log(1 + exp(-x))
524+ // For x < 0, a more numerically stable formula is
525+ // -x * z + log(1 + exp(x))
526+ // Note that these two expressions can be combined into the following:
527+ // max(x, 0) - x * z + log(1 + exp(-abs(x)))
528+ // To allow computing gradients at zero, we define custom versions of max and
529+ // abs functions.
530+ TFOutput zeros = this . ZerosLike ( logits ) ;
531+ TFOutput cond = this . GreaterEqual ( logits , zeros ) ;
532+ TFOutput relu_logits = this . Where ( cond , logits , zeros ) ;
533+ TFOutput neg_abs_logits = this . Where ( cond , this . Neg ( logits ) , logits ) ;
534+ return this . Add (
535+ this . Sub ( relu_logits , this . Mul ( logits , labels ) ) ,
536+ this . Log1p ( this . Exp ( neg_abs_logits ) ) ,
537+ operName : operName ) ;
538+ }
539+ }
540+
541+ /// <summary>
542+ /// Return elements from x or y depending on condition.
543+ /// </summary>
544+ ///
545+ /// <param name="condition">LabeledTensor of type `bool`.</param>
546+ /// <param name="x">LabeledTensor for values where condition is true.</param>
547+ /// <param name="y">LabeledTensor for values where condition is false.</param>
548+ /// <param name="name">Optional op name.</param>
549+ ///
550+ /// <returns>The labeled tensor with values according to condition.</returns>
551+ ///
552+ public TFOutput Where ( TFOutput condition , TFOutput ? x , TFOutput ? y , string name = null )
553+ {
554+ // https://github.com/tensorflow/tensorflow/blob/d4ce3b4681b3a550c095b2cd18a79494d1cc4039/tensorflow/python/ops/array_ops.py#L2342
555+ if ( x == null && y == null )
556+ return this . Where ( input : condition , operName : name ) ;
557+ else if ( x != null && y != null )
558+ return this . Select ( condition : condition , t : x . Value , e : y . Value , operName : name ) ;
559+ throw new ArgumentException ( "x and y must both be non-None or both be None." ) ;
560+ }
561+
491562 /// <summary>
492563 /// Stacks a list of rank-`R` tensors into one rank-`(R+1)` tensor.
493564 /// </summary>
0 commit comments