@@ -488,6 +488,77 @@ public TFOutput ClipByAverageNorm (TFOutput x, TFOutput clip_norm, string operNa
488
488
}
489
489
}
490
490
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
+
491
562
/// <summary>
492
563
/// Stacks a list of rank-`R` tensors into one rank-`(R+1)` tensor.
493
564
/// </summary>
0 commit comments