diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/BinaryCrossentropy.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/BinaryCrossentropy.java
new file mode 100644
index 00000000000..effdf990f71
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/BinaryCrossentropy.java
@@ -0,0 +1,231 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+
+import static org.tensorflow.framework.utils.CastHelper.cast;
+
+/**
+ * Computes the cross-entropy loss between true labels and predicted labels.
+ *
+ * <p>Use this cross-entropy loss when there are only two label classes (assumed to be 0 and 1). For
+ * each example, there should be a single floating-point value per prediction.
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[][] {{0.f, 1.f}, {0.f, 0.f}});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{0.6f, 0.4f}, {0.4f, 0.6f}});
+ *    BinaryCrossentropy bce = new BinaryCrossentropy(tf);
+ *    Operand&lt;TFloat32&gt; result = bce.call(labels, predictions);
+ *    // produces 0.815
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {1.f, 0.f});
+ *    Operand&lt;TFloat32&gt; result = bce.call(labels, predictions, sampleWeight);
+ *    // produces 0.458f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    BinaryCrossentropy bce = new BinaryCrossentropy(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = bce.call(labels, predictions);
+ *    // produces 1.630f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    BinaryCrossentropy bce = new BinaryCrossentropy(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = bce.call(labels, predictions);
+ *    // produces [0.916f, 0.714f]
+ * </pre>
+ */
+public class BinaryCrossentropy extends Loss {
+  public static final boolean FROM_LOGITS_DEFAULT = false;
+  public static final float LABEL_SMOOTHING_DEFAULT = 0.0f;
+
+  private final boolean fromLogits;
+  private final float labelSmoothing;
+
+  /**
+   * Creates a Binary Crossentropy Loss using {@link Class#getSimpleName()} as the loss name, {@link
+   * #FROM_LOGITS_DEFAULT} for fromLogits, {@link #LABEL_SMOOTHING_DEFAULT} for labelSmoothing and a
+   * Loss Reduction of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public BinaryCrossentropy(Ops tf) {
+    this(tf, null, FROM_LOGITS_DEFAULT, LABEL_SMOOTHING_DEFAULT, REDUCTION_DEFAULT);
+  }
+
+  /**
+   * Creates a Binary Crossentropy loss using {@link Class#getSimpleName()} as the loss name, {@link
+   * #FROM_LOGITS_DEFAULT} for fromLogits, and {@link #LABEL_SMOOTHING_DEFAULT} for labelSmoothing
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public BinaryCrossentropy(Ops tf, Reduction reduction) {
+    this(tf, null, FROM_LOGITS_DEFAULT, LABEL_SMOOTHING_DEFAULT, reduction);
+  }
+
+  /**
+   * Creates a Binary Crossentropy loss using using {@link Class#getSimpleName()} as the loss name,
+   * labelSmoothing of {@link #LABEL_SMOOTHING_DEFAULT}, a reduction of {@link
+   * Loss#REDUCTION_DEFAULT},
+   *
+   * @param tf the TensorFlow Ops
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   */
+  public BinaryCrossentropy(Ops tf, boolean fromLogits) {
+    this(tf, null, fromLogits, LABEL_SMOOTHING_DEFAULT, REDUCTION_DEFAULT);
+  }
+
+  /**
+   * Creates a Binary Crossentropy loss using labelSmoothing of {@link #LABEL_SMOOTHING_DEFAULT} a
+   * reduction of {@link Loss#REDUCTION_DEFAULT}.
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   */
+  public BinaryCrossentropy(Ops tf, String name, boolean fromLogits) {
+    this(tf, name, fromLogits, LABEL_SMOOTHING_DEFAULT, REDUCTION_DEFAULT);
+  }
+
+  /**
+   * Creates a Binary Crossentropy loss using using {@link Class#getSimpleName()} as the loss name,
+   * and a reduction of {@link Loss#REDUCTION_DEFAULT}.
+   *
+   * @param tf the TensorFlow Ops
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   * @param labelSmoothing A number in the range, [0, 1]. When 0, no smoothing occurs. When &gt; 0,
+   *     compute the loss between the predicted labels and a smoothed version of the true labels,
+   *     where the smoothing squeezes the labels towards 0.5. Larger values of labelSmoothing
+   *     correspond to heavier smoothing.
+   */
+  public BinaryCrossentropy(Ops tf, boolean fromLogits, float labelSmoothing) {
+    this(tf, null, fromLogits, labelSmoothing, REDUCTION_DEFAULT);
+  }
+
+  /**
+   * Creates a Binary Crossentropy loss using a reduction of {@link Loss#REDUCTION_DEFAULT}.
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   * @param labelSmoothing A number in the range, [0, 1]. When 0, no smoothing occurs. When &gt; 0,
+   *     compute the loss between the predicted labels and a smoothed version of the true labels,
+   *     where the smoothing squeezes the labels towards 0.5. Larger values of labelSmoothing
+   *     correspond to heavier smoothing.
+   */
+  public BinaryCrossentropy(Ops tf, String name, boolean fromLogits, float labelSmoothing) {
+    this(tf, name, fromLogits, labelSmoothing, REDUCTION_DEFAULT);
+  }
+
+  /**
+   * Creates a Binary Crossentropy loss
+   *
+   * @param tf the TensorFlow Ops
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   * @param labelSmoothing A number in the range, [0, 1]. When 0, no smoothing occurs. When &gt; 0,
+   *     compute the loss between the predicted labels and a smoothed version of the true labels,
+   *     where the smoothing squeezes the labels towards 0.5. Larger values of labelSmoothing
+   *     correspond to heavier smoothing.
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public BinaryCrossentropy(Ops tf, boolean fromLogits, float labelSmoothing, Reduction reduction) {
+    this(tf, null, fromLogits, labelSmoothing, reduction);
+  }
+
+  /**
+   * Creates a Binary Crossentropy loss
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   * @param labelSmoothing A number in the range, [0, 1]. When 0, no smoothing occurs. When &gt; 0,
+   *     compute the loss between the predicted labels and a smoothed version of the true labels,
+   *     where the smoothing squeezes the labels towards 0.5. Larger values of labelSmoothing
+   *     correspond to heavier smoothing.
+   * @param reduction Type of Reduction to apply to the loss.
+   * @throws IllegalArgumentException if labelSmoothing is not in the inclusive range of 0. - 1.
+   */
+  public BinaryCrossentropy(
+      Ops tf, String name, boolean fromLogits, float labelSmoothing, Reduction reduction) {
+    super(tf, name, reduction);
+    if (labelSmoothing < 0 || labelSmoothing > 1)
+      throw new IllegalArgumentException(
+          "labelSmoothing must be >= 0. and <= 1, found " + labelSmoothing);
+    this.fromLogits = fromLogits;
+    this.labelSmoothing = labelSmoothing;
+  }
+
+  /**
+   * Generates an Operand that calculates the loss.
+   *
+   * <p>If run in Graph mode, the computation will throw {@link
+   * org.tensorflow.exceptions.TFInvalidArgumentException} if the predictions values are outside the
+   * range o [0. to 1.]. In Eager Mode, this call will throw {@link IllegalArgumentException}, if
+   * the predictions values are outside the range o [0. to 1.]
+   *
+   * @param labels the truth values or labels
+   * @param predictions the predictions, values must be in the range [0. to 1.] inclusive.
+   * @param sampleWeights Optional SampleWeights acts as a coefficient for the loss. If a scalar is
+   *     provided, then the loss is simply scaled by the given value. If SampleWeights is a tensor
+   *     of size [batch_size], then the total loss for each sample of the batch is rescaled by the
+   *     corresponding element in the SampleWeights vector. If the shape of SampleWeights is
+   *     [batch_size, d0, .. dN-1] (or can be broadcast to this shape), then each loss element of
+   *     predictions is scaled by the corresponding value of SampleWeights. (Note on dN-1: all loss
+   *     functions reduce by 1 dimension, usually axis=-1.)
+   * @param <T> The data type of the predictions, sampleWeights and loss.
+   * @param <U> The data type of the labels.
+   * @return the loss
+   * @throws IllegalArgumentException if the predictions are outside the range [0.-1.].
+   */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+      Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    Operand<T> lPredictions;
+    if (!fromLogits) {
+      // add predictions range check for 0 - 1
+      lPredictions =
+          LossesHelper.rangeCheck(
+              getTF(),
+              "predictions range check [0-1]",
+              predictions,
+              cast(getTF(), getTF().constant(0), predictions.asOutput().dataType()),
+              cast(getTF(), getTF().constant(1), predictions.asOutput().dataType()));
+
+    } else {
+      lPredictions = predictions;
+    }
+
+    Operand<T> losses =
+        Losses.binaryCrossentropy(getTF(), labels, lPredictions, fromLogits, labelSmoothing);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/CategoricalCrossentropy.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/CategoricalCrossentropy.java
new file mode 100644
index 00000000000..7701ebfb806
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/CategoricalCrossentropy.java
@@ -0,0 +1,270 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+
+import static org.tensorflow.framework.utils.CastHelper.cast;
+
+/**
+ * Computes the crossentropy loss between the labels and predictions.
+ *
+ * <p>Use this crossentropy loss function when there are two or more label classes. We expect labels
+ * to be provided in a one_hot representation. If you want to provide labels as integers, please use
+ * {@link SparseCategoricalCrossentropy} loss. There should be <code># classes</code> floating point
+ * values per feature.
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[][] {{0, 1, 0}, {0, 0, 1}});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{0.05f, 0.95f, 0f}, {0.1f, 0.8f, 0.1f}});
+ *    CategoricalCrossentropy cce = new CategoricalCrossentropy(tf);
+ *    Operand&lt;TFloat32&gt; result = cce.call(labels, predictions);
+ *    // produces 1.177
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {0.3f, 0.7f});
+ *    Operand&lt;TFloat32&gt; result = cce.call(labels, predictions, sampleWeight);
+ *    // produces 0.814f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    CategoricalCrossentropy cce = new CategoricalCrossentropy(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = cce.call(labels, predictions);
+ *    // produces 2.354f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    CategoricalCrossentropy cce =
+ *        new CategoricalCrossentropy(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = cce.call(labels, predictions);
+ *    // produces [0.0513f, 2.303f]
+ * </pre>
+ */
+public class CategoricalCrossentropy extends Loss {
+  public static final boolean FROM_LOGITS_DEFAULT = false;
+  public static final float LABEL_SMOOTHING_DEFAULT = 0.0f;
+  public static final int DEFAULT_AXIS = -1;
+
+  private final boolean fromLogits;
+  private final float labelSmoothing;
+  private final int axis;
+
+  /**
+   * Creates a categorical cross entropy Loss using {@link Class#getSimpleName()} as the loss name,
+   * {@link #FROM_LOGITS_DEFAULT} for fromLogits, {@link #LABEL_SMOOTHING_DEFAULT} for
+   * labelSmoothing, a Loss Reduction of {@link Loss#REDUCTION_DEFAULT}, and an axis of {@link
+   * #DEFAULT_AXIS}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public CategoricalCrossentropy(Ops tf) {
+    this(tf, null, FROM_LOGITS_DEFAULT, LABEL_SMOOTHING_DEFAULT, REDUCTION_DEFAULT, DEFAULT_AXIS);
+  }
+
+  /**
+   * Creates a categorical cross entropy Loss using {@link #FROM_LOGITS_DEFAULT} for fromLogits,
+   * {@link #LABEL_SMOOTHING_DEFAULT} for labelSmoothing, a Loss Reduction of {@link
+   * Loss#REDUCTION_DEFAULT}, and an axis of {@link #DEFAULT_AXIS}
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of this loss
+   */
+  public CategoricalCrossentropy(Ops tf, String name) {
+    this(tf, name, FROM_LOGITS_DEFAULT, LABEL_SMOOTHING_DEFAULT, REDUCTION_DEFAULT, DEFAULT_AXIS);
+  }
+
+  /**
+   * Creates a categorical cross entropy Loss using {@link Class#getSimpleName()} as the loss name,
+   * {@link #FROM_LOGITS_DEFAULT} for fromLogits, {@link #LABEL_SMOOTHING_DEFAULT} for
+   * labelSmoothing and an axis of {@link #DEFAULT_AXIS}
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to loss.
+   */
+  public CategoricalCrossentropy(Ops tf, Reduction reduction) {
+    this(tf, null, FROM_LOGITS_DEFAULT, LABEL_SMOOTHING_DEFAULT, reduction, DEFAULT_AXIS);
+  }
+
+  /**
+   * Creates a categorical cross entropy Loss {@link #FROM_LOGITS_DEFAULT} for fromLogits, {@link
+   * #LABEL_SMOOTHING_DEFAULT} for labelSmoothing, and an axis of {@link #DEFAULT_AXIS}
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of this loss
+   * @param reduction Type of Reduction to apply to loss.
+   */
+  public CategoricalCrossentropy(Ops tf, String name, Reduction reduction) {
+    this(tf, name, FROM_LOGITS_DEFAULT, LABEL_SMOOTHING_DEFAULT, reduction, DEFAULT_AXIS);
+  }
+
+  /**
+   * Creates a categorical cross entropy Loss using {@link Class#getSimpleName()} as the loss name,
+   * {@link #LABEL_SMOOTHING_DEFAULT} for labelSmoothing, a Loss Reduction of {@link
+   * Loss#REDUCTION_DEFAULT}, and an axis of {@link #DEFAULT_AXIS}
+   *
+   * @param tf the TensorFlow Ops
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   */
+  public CategoricalCrossentropy(Ops tf, boolean fromLogits) {
+    this(tf, null, fromLogits, LABEL_SMOOTHING_DEFAULT, REDUCTION_DEFAULT, DEFAULT_AXIS);
+  }
+
+  /**
+   * Creates a categorical cross entropy Loss using {@link #LABEL_SMOOTHING_DEFAULT} for
+   * labelSmoothing, a Loss Reduction of {@link Loss#REDUCTION_DEFAULT}, and a channel axis of
+   * {@link #DEFAULT_AXIS}
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of this loss
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   */
+  public CategoricalCrossentropy(Ops tf, String name, boolean fromLogits) {
+    this(tf, name, fromLogits, LABEL_SMOOTHING_DEFAULT, REDUCTION_DEFAULT, DEFAULT_AXIS);
+  }
+
+  /**
+   * Creates a categorical cross entropy Loss using {@link Class#getSimpleName()} as the loss name,
+   * a Loss Reduction of {@link Loss#REDUCTION_DEFAULT}, and a channel axis of {@link #DEFAULT_AXIS}
+   *
+   * @param tf the TensorFlow Ops
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   * @param labelSmoothing Float in <code>[0, 1]</code>. When <code>&gt; 0</code>, label values are smoothed, meaning the
+   *    confidence on label values are relaxed. e.g. <code>labelSmoothing=0.2<code> means that we will use a
+   *    value of </code>0.1<code> for label </code>0<code> and </code>0.9<code> for label </code>1<code>
+   */
+  public CategoricalCrossentropy(Ops tf, boolean fromLogits, float labelSmoothing) {
+    this(tf, null, fromLogits, labelSmoothing, REDUCTION_DEFAULT, DEFAULT_AXIS);
+  }
+
+  /**
+   * Creates a categorical cross entropy Loss using a Loss Reduction of {@link Loss#REDUCTION_DEFAULT},
+   * and a channel axis of {@link #DEFAULT_AXIS}
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of this loss
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   * @param labelSmoothing Float in <code>[0, 1]</code>. When <code>&gt; 0</code>, label values are smoothed, meaning the
+   *    confidence on label values are relaxed. e.g. <code>labelSmoothing=0.2<code> means that we will use a
+   *    value of </code>0.1<code> for label </code>0<code> and </code>0.9<code> for label </code>1<code>
+   */
+  public CategoricalCrossentropy(Ops tf, String name, boolean fromLogits, float labelSmoothing) {
+    this(tf, name, fromLogits, labelSmoothing, REDUCTION_DEFAULT, DEFAULT_AXIS);
+  }
+
+  /**
+   * Creates a categorical cross entropy Loss using {@link Class#getSimpleName()} as the loss name
+   * and a channel axis of {@link #DEFAULT_AXIS}
+   *
+   * @param tf the TensorFlow Ops
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   * @param labelSmoothing Float in <code>[0, 1]</code>. When <code>&gt; 0</code>, label values are smoothed, meaning the
+   *    confidence on label values are relaxed. e.g. <code>x=0.2<code> means that we will use a
+   *    value of </code>0.1<code> for label </code>0<code> and </code>0.9<code> for label </code>1<code>
+   * @param reduction Type of Reduction to apply to loss.
+   */
+  public CategoricalCrossentropy(
+      Ops tf, boolean fromLogits, float labelSmoothing, Reduction reduction) {
+    this(tf, null, fromLogits, labelSmoothing, reduction, DEFAULT_AXIS);
+  }
+
+  /**
+   * Creates a categorical cross entropy Loss
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of this loss
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   * @param labelSmoothing Float in <code>[0, 1]</code>. When <code>&gt; 0</code>, label values are smoothed, meaning the
+   *    confidence on label values are relaxed. e.g. <code>labelSmoothing=0.2<code> means that we will use a
+   *    value of </code>0.1<code> for label </code>0<code> and </code>0.9<code> for label </code>1<code>
+   * @param reduction Type of Reduction to apply to loss.
+   * @param axis The channels axis. <code>axis=-1</code> corresponds to data format `Channels Last'
+   *     and <code>axis=1</code> corresponds to data format 'Channels First'.
+   * @throws IllegalArgumentException if labelSmoothing is not in the inclusive range of 0. - 1.
+   */
+  public CategoricalCrossentropy(
+      Ops tf,
+      String name,
+      boolean fromLogits,
+      float labelSmoothing,
+      Reduction reduction,
+      int axis) {
+    super(tf, name, reduction);
+    if (labelSmoothing < 0 || labelSmoothing > 1)
+      throw new IllegalArgumentException(
+          "labelSmoothing must be >= 0. and <= 1, found " + labelSmoothing);
+    this.fromLogits = fromLogits;
+    this.labelSmoothing = labelSmoothing;
+    this.axis = axis;
+  }
+
+  /**
+   * Generates an Operand that calculates the loss.
+   *
+   * <p>If run in Graph mode, the computation will throw {@link
+   * org.tensorflow.exceptions.TFInvalidArgumentException} if the predictions values are outside the
+   * range o [0. to 1.]. In Eager Mode, this call will throw {@link IllegalArgumentException}, if
+   * the predictions values are outside the range o [0. to 1.]
+   *
+   * @param labels the truth values or labels
+   * @param predictions the predictions, values must be in the range [0. to 1.] inclusive.
+   * @param sampleWeights Optional SampleWeights acts as a coefficient for the loss. If a scalar is
+   *     provided, then the loss is simply scaled by the given value. If SampleWeights is a tensor
+   *     of size [batch_size], then the total loss for each sample of the batch is rescaled by the
+   *     corresponding element in the SampleWeights vector. If the shape of SampleWeights is
+   *     [batch_size, d0, .. dN-1] (or can be broadcast to this shape), then each loss element of
+   *     predictions is scaled by the corresponding value of SampleWeights. (Note on dN-1: all loss
+   *     functions reduce by 1 dimension, usually axis=-1.)
+   * @param <T> The data type of the predictions, sampleWeights and loss.
+   * @param <U> The data type of the labels.
+   * @return the loss
+   * @throws IllegalArgumentException if the predictions are outside the range [0.-1.].
+   */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+      Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    Operand<T> lPredictions;
+    if (!fromLogits) {
+      // add predictions range check for 0 - 1
+      lPredictions =
+          LossesHelper.rangeCheck(
+              getTF(),
+              "predictions range check [0-1]",
+              predictions,
+              cast(getTF(), getTF().constant(0), predictions.asOutput().dataType()),
+              cast(getTF(), getTF().constant(1), predictions.asOutput().dataType()));
+
+    } else {
+      lPredictions = predictions;
+    }
+    Operand<T> losses =
+        Losses.categoricalCrossentropy(
+            getTF(), labels, lPredictions, fromLogits, labelSmoothing, axis);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/CategoricalHinge.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/CategoricalHinge.java
new file mode 100644
index 00000000000..f592c19f8bb
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/CategoricalHinge.java
@@ -0,0 +1,107 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+
+/**
+ * Computes the categorical hinge loss between labels and predictions.
+ *
+ * <p><code>loss = maximum(neg - pos + 1, 0)</code> where <code>neg=maximum((1-labels)*predictions)
+ * </code> and <code>pos=sum(labels*predictions)</code>
+ *
+ * <p><code>labels</code> values are expected to be 0 or 1.</p>
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[][] {{0, 1}, {0, 0}});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{0.6f, 0.4f}, {0.4f, 0.6f}});
+ *    CategoricalHinge categoricalHinge = new CategoricalHinge(tf);
+ *    Operand&lt;TFloat32&gt; result = categoricalHinge.call(labels, predictions);
+ *    // produces 1.4
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {1f, 0.f});
+ *    Operand&lt;TFloat32&gt; result = categoricalHinge.call(labels, predictions, sampleWeight);
+ *    // produces 0.6f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    CategoricalHinge categoricalHinge = new CategoricalHinge(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = categoricalHinge.call(labels, predictions);
+ *    // produces 2.8f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    CategoricalHinge categoricalHinge =
+ *        new CategoricalHinge(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = categoricalHinge.call(labels, predictions);
+ *    // produces [1.2f, 1.6f]
+ * </pre>
+ */
+public class CategoricalHinge extends Loss {
+
+  /**
+   * Creates a Categorical Hinge Loss using {@link Class#getSimpleName()} as the loss name and a
+   * Loss Reduction of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public CategoricalHinge(Ops tf) {
+    super(tf);
+  }
+
+  /**
+   * Creates a Categorical Hinge Loss using {@link Class#getSimpleName()} as the loss name
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public CategoricalHinge(Ops tf, Reduction reduction) {
+    super(tf, null, reduction);
+  }
+
+  /**
+   * Creates a Categorical Hinge
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public CategoricalHinge(Ops tf, String name, Reduction reduction) {
+    super(tf, name, reduction);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+          Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    Operand<T> losses = Losses.categoricalHinge(getTF(), labels, predictions);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/CosineSimilarity.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/CosineSimilarity.java
new file mode 100644
index 00000000000..137c7025c04
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/CosineSimilarity.java
@@ -0,0 +1,179 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+
+/**
+ * Computes the cosine similarity between labels and predictions.
+ *
+ * <p>Note that it is a number between <code>-1</code> and <code>1</code>. When it is a negative number between <code>-1</code> and <code>0</code>, <code>0</code>
+ * indicates orthogonality and values closer to <code>-1</code>indicate greater similarity. The values closer to
+ * <code>1</code> indicate greater dissimilarity. This makes it usable as a loss function in a setting where you
+ * try to maximize the proximity between predictions and targets. If either <code>labels</code> or <code>predictions</code> is
+ * a zero vector, cosine similarity will be <code>0</code> regardless of the proximity between predictions and
+ * targets.
+ *
+ * <p><code>loss = -sum(l2Norm(labels) * l2Norm(predictions))</code>
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[][] {{0.f, 1.f}, {1.f, 1.f}});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{1.f, 0.f}, {1.f, 1.f}});
+ *    CosineSimilarity cosineLoss = new CosineSimilarity(tf);
+ *    Operand&lt;TFloat32&gt; result = cosineLoss.call(labels, predictions);
+ *    // produces -0.5
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {0.8f, 0.2f});
+ *    Operand&lt;TFloat32&gt; result = cosineLoss.call(labels, predictions, sampleWeight);
+ *    // produces -0.0999f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    CosineSimilarity cosineLoss = new CosineSimilarity(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = cosineLoss.call(labels, predictions);
+ *    // produces -0.999f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    CosineSimilarity cosineLoss = new CosineSimilarity(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = cosineLoss.call(labels, predictions);
+ *    // produces [-0.f, -0.999f]
+ * </pre>
+ */
+public class CosineSimilarity extends Loss {
+  public static final int DEFAULT_AXIS = -1;
+  public static final Reduction DEFAULT_REDUCTION = Reduction.AUTO;
+
+  private final int axis;
+
+  /**
+   * Creates a Cosine Similarity Loss using {@link Class#getSimpleName()} as the loss name, an axis
+   * of {@link #DEFAULT_AXIS}, and a Loss Reduction of {@link #DEFAULT_REDUCTION}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public CosineSimilarity(Ops tf) {
+
+    this(tf, null, DEFAULT_AXIS, DEFAULT_REDUCTION);
+  }
+
+  /**
+   * Creates a Cosine Similarity Loss using an axis of {@link #DEFAULT_AXIS}, and a Loss Reduction
+   * of {@link #DEFAULT_REDUCTION}
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   */
+  public CosineSimilarity(Ops tf, String name) {
+
+    this(tf, name, DEFAULT_AXIS, DEFAULT_REDUCTION);
+  }
+
+  /**
+   * Creates a Cosine Similarity Loss using {@link Class#getSimpleName()} as the loss name, and a
+   * Loss Reduction of {@link #DEFAULT_REDUCTION}
+   *
+   * @param tf the TensorFlow Ops
+   * @param axis The dimension along which the cosine similarity is computed.
+   */
+  public CosineSimilarity(Ops tf, int axis) {
+
+    this(tf, null, axis, DEFAULT_REDUCTION);
+  }
+
+  /**
+   * Creates a Cosine Similarity Loss using a Loss Reduction of {@link #DEFAULT_REDUCTION}
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param axis The dimension along which the cosine similarity is computed.
+   */
+  public CosineSimilarity(Ops tf, String name, int axis) {
+
+    this(tf, name, axis, DEFAULT_REDUCTION);
+  }
+
+  /**
+   * Creates a Cosine Similarity Loss using {@link Class#getSimpleName()} as the loss name and an
+   * axis of {@link #DEFAULT_AXIS}
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public CosineSimilarity(Ops tf, Reduction reduction) {
+
+    this(tf, null, DEFAULT_AXIS, reduction);
+  }
+
+  /**
+   * Creates a Cosine Similarity Loss using an axis of {@link #DEFAULT_AXIS}
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public CosineSimilarity(Ops tf, String name, Reduction reduction) {
+
+    this(tf, name, DEFAULT_AXIS, reduction);
+  }
+
+  /**
+   * Creates a Cosine Similarity Loss using {@link Class#getSimpleName()} as the loss name
+   *
+   * @param tf the TensorFlow Ops
+   * @param axis The dimension along which the cosine similarity is computed.
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public CosineSimilarity(Ops tf, int axis, Reduction reduction) {
+
+    this(tf, null, axis, reduction);
+  }
+
+  /**
+   * Creates a Cosine Similarity Loss
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param axis The dimension along which the cosine similarity is computed.
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public CosineSimilarity(Ops tf, String name, int axis, Reduction reduction) {
+    super(tf, name, reduction);
+    this.axis = axis;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+          Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    Operand<T> losses = Losses.cosineSimilarity(getTF(), labels, predictions, axis);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Hinge.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Hinge.java
new file mode 100644
index 00000000000..5fdfd4c9b96
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Hinge.java
@@ -0,0 +1,140 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+import static org.tensorflow.framework.utils.CastHelper.cast;
+
+/**
+ * Computes the hinge loss between labels and predictions.
+ *
+ * <p><code>loss = maximum(1 - labels * predictions, 0)</code></p>.
+ *
+ * <p><code>labels/code> values are expected to be -1 or 1.
+ * If binary (0 or 1) labels are provided, they will be converted to -1 or 1.</p>
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[][] {{0.f, 1.f}, {0.f, 0.f}});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{0.6f, 0.4f}, {0.4f, 0.6f}});
+ *    Hinge hingeLoss = new Hinge(tf);
+ *    Operand&lt;TFloat32&gt; result = hingeLoss.call(labels, predictions);
+ *    // produces 1.3f
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {1.f, 0.f});
+ *    Operand&lt;TFloat32&gt; result = hingeLoss.call(labels, predictions, sampleWeight);
+ *    // produces 0.55f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    Hinge hingeLoss = new Hinge(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = hingeLoss.call(labels, predictions);
+ *    // produces 2.6f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    Hinge hingeLoss = new Hinge(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = hingeLoss.call(labels, predictions);
+ *    // produces [1.1f, 1.5f]
+ * </pre>
+ */
+public class Hinge extends Loss {
+
+  /**
+   * Creates a Hinge Loss using {@link Class#getSimpleName()} as the loss name and a Loss Reduction
+   * of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public Hinge(Ops tf) {
+    this(tf, null, Reduction.AUTO);
+  }
+
+  /**
+   * Creates a Hinge Loss using {@link Class#getSimpleName()} as the loss name
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public Hinge(Ops tf, Reduction reduction) {
+    super(tf, null, reduction);
+  }
+
+  /**
+   * Creates a Hinge
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public Hinge(Ops tf, String name, Reduction reduction) {
+    super(tf, name, reduction);
+  }
+
+  /**
+   * Generates an Operand that calculates the loss.
+   *
+   * <p>If run in Graph mode, the computation will throw {@link
+   * org.tensorflow.exceptions.TFInvalidArgumentException} if the label values are not in the set
+   * [-1., 0., 1.]. In Eager Mode, this call will throw {@link IllegalArgumentException}, if the
+   * label values are not in the set [-1., 0., 1.].
+   *
+   * @param labels the truth values or labels, must be either -1, 0, or 1. Values are expected to be
+   *     -1 or 1. If binary (0 or 1) labels are provided they will be converted  to -1 or 1.
+   * @param predictions the predictions, values must be in the range [0. to 1.] inclusive.
+   * @param sampleWeights Optional sampleWeights acts as a coefficient for the loss. If a scalar is
+   *     provided, then the loss is simply scaled by the given value. If sampleWeights is a tensor
+   *     of size [batch_size], then the total loss for each sample of the batch is rescaled by the
+   *     corresponding element in the SampleWeights vector. If the shape of SampleWeights is
+   *     [batch_size, d0, .. dN-1] (or can be broadcast to this shape), then each loss element of
+   *     predictions is scaled by the corresponding value of SampleWeights. (Note on dN-1: all loss
+   *     functions reduce by 1 dimension, usually axis=-1.)
+   * @param <T> The data type of the predictions, sampleWeights and loss.
+   * @param <U> The data type of the labels.
+   * @return the loss
+   * @throws IllegalArgumentException if the predictions are outside the range [0.-1.].
+   */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+      Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    @SuppressWarnings("unchecked")
+    Operand<T> tLabels = predictions.asOutput().dataType() == labels.asOutput().dataType() ?
+            (Operand<T>)labels :
+            cast(tf,  labels, predictions.asOutput().dataType());
+    tLabels = LossesHelper.valueCheck(
+            getTF(),
+            "labels value check [-1, 0, 1]",
+            tLabels,
+            cast(getTF(), getTF().constant(new int[] { -1, 0, 1}),
+                    predictions.asOutput().dataType()));
+
+    Operand<T> losses = Losses.hinge(getTF(), tLabels, predictions);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Huber.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Huber.java
new file mode 100644
index 00000000000..6d3e3f0c2ac
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Huber.java
@@ -0,0 +1,138 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+
+/**
+ * Computes the Huber loss between labels and predictions.
+ *
+ * <p>For each value x in <code>error = labels - predictions</code>:
+ *
+ * <pre>
+ *     loss = 0.5 * x^2                  if |x| &lt;= d
+ *     loss = 0.5 * d^2 + d * (|x| - d)  if |x| &gt; d
+ * </pre>
+ *
+ * <p>where d is delta.
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[][] {{0.f, 1.f}, {0.f, 0.f}});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{0.6f, 0.4f}, {0.4f, 0.6f}});
+ *    Huber huberLoss = new Huber(tf);
+ *    Operand&lt;TFloat32&gt; result = huberLoss.call(labels, predictions);
+ *    // produces 0.155
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {1.f, 0.f});
+ *    Operand&lt;TFloat32&gt; result = huberLoss.call(labels, predictions, sampleWeight);
+ *    // produces 0.09f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    Huber huberLoss = new Huber(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = huberLoss.call(labels, predictions);
+ *    // produces 0.32f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    Huber huberLoss = new Huber(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = huberLoss.call(labels, predictions);
+ *    // produces [0.18f, 0.13f]
+ * </pre>
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Huber_loss">Huber loss</a>
+ */
+public class Huber extends Loss {
+  public static final float DELTA_DEFAULT = 1.0f;
+
+  private final float delta;
+
+  /**
+   * Creates a Huber Loss using {@link Class#getSimpleName()} as the loss name, {@link
+   * #DELTA_DEFAULT} as the delta and a Loss Reduction of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public Huber(Ops tf) {
+    this(tf, null, DELTA_DEFAULT, Reduction.AUTO);
+  }
+
+  /**
+   * Creates a Huber Loss using {@link #DELTA_DEFAULT} as the delta and a Loss Reduction of {@link
+   * Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public Huber(Ops tf, String name) {
+    this(tf, name, DELTA_DEFAULT, Reduction.AUTO);
+  }
+
+  /**
+   * Creates a Huber Loss using {@link Class#getSimpleName()} as the loss name and and {@link
+   * #DELTA_DEFAULT} as the delta
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public Huber(Ops tf, Reduction reduction) {
+    this(tf, null, DELTA_DEFAULT, reduction);
+  }
+
+  /**
+   * Creates a Huber Loss using {@link #DELTA_DEFAULT} as the delta
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public Huber(Ops tf, String name, Reduction reduction) {
+    this(tf, name, DELTA_DEFAULT, reduction);
+  }
+
+  /**
+   * Creates a Huber Loss
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param delta the point where the Huber loss function changes from quadratic to linear.
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public Huber(Ops tf, String name, float delta, Reduction reduction) {
+    super(tf, name, reduction);
+    this.delta = delta;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+          Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    Operand<T> losses = Losses.huber(getTF(), labels, predictions, delta);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/KLDivergence.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/KLDivergence.java
new file mode 100644
index 00000000000..8cf3db8d518
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/KLDivergence.java
@@ -0,0 +1,107 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+
+/**
+ * Computes Kullback-Leibler divergence loss between labels and predictions.
+ *
+ * <p><code>loss = labels * log(labels / predictions)</code>
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[][] {{0.f, 1.f}, {0.f, 0.f}});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{0.6f, 0.4f}, {0.4f, 0.6f}});
+ *    KLDivergence kld = new KLDivergence(tf);
+ *    Operand&lt;TFloat32&gt; result = kld.call(labels, predictions);
+ *    // produces 0.458
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {0.8f, 0.2f});
+ *    Operand&lt;TFloat32&gt; result = kld.call(labels, predictions, sampleWeight);
+ *    // produces 0.366f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    KLDivergence kld = new KLDivergence(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = kld.call(labels, predictions);
+ *    // produces 0.916f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    KLDivergence kld = new KLDivergence(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = kld.call(labels, predictions);
+ *    // produces [0.916f, -3.08e-06f]
+ * </pre>
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Kullback?Leibler_divergence">Kullback?Leibler
+ *     divergence</a>
+ */
+public class KLDivergence extends Loss {
+
+  /**
+   * Creates a Kullback Leibler Divergence Loss using {@link Class#getSimpleName()} as the loss name
+   * and a Loss Reduction of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public KLDivergence(Ops tf) {
+    super(tf);
+  }
+
+  /**
+   * Creates a Kullback Leibler Divergence Loss Loss using {@link Class#getSimpleName()} as the loss
+   * name
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public KLDivergence(Ops tf, Reduction reduction) {
+    super(tf, null, reduction);
+  }
+
+  /**
+   * Creates a Kullback Leibler Divergence Loss
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public KLDivergence(Ops tf, String name, Reduction reduction) {
+    super(tf, name, reduction);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+          Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    Operand<T> losses = Losses.kullbackLeiblerDivergence(getTF(), labels, predictions);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/LogCosh.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/LogCosh.java
new file mode 100644
index 00000000000..1669669a768
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/LogCosh.java
@@ -0,0 +1,113 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+
+/**
+ * Computes Computes the logarithm of the hyperbolic cosine of the prediction error.
+ *
+ * <p><code>logcosh = log((exp(x) + exp(-x))/2)</code>, where <code>x</code> is the error <code>
+ * predictions - labels</code>.
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[][] {{0.f, 1.f}, {0.f, 0.f}});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{1.f, 1.f}, {0.f, 0.f}});
+ *    LogCosh logcosh = new LogCosh(tf);
+ *    Operand&lt;TFloat32&gt; result = logcosh.call(labels, predictions);
+ *    // produces 0.108
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {0.8f, 0.2f});
+ *    Operand&lt;TFloat32&gt; result = logcosh.call(labels, predictions, sampleWeight);
+ *    // produces 0.087f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    LogCosh logcosh = new LogCosh(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = logcosh.call(labels, predictions);
+ *    // produces 0.217f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    LogCosh logcosh = new LogCosh(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = logcosh.call(labels, predictions);
+ *    // produces [0.217f, 0f]
+ * </pre>
+ */
+public class LogCosh extends Loss {
+
+  /**
+   * Creates a LogCosh Loss using {@link Class#getSimpleName()} as the loss name and a Loss
+   * Reduction of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public LogCosh(Ops tf) {
+    this(tf, null, Reduction.AUTO);
+  }
+
+  /**
+   * Creates a LogCosh Loss using a Loss Reduction of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public LogCosh(Ops tf, String name) {
+    this(tf, name, Reduction.AUTO);
+  }
+
+  /**
+   * Creates a LogCosh Loss using {@link Class#getSimpleName()} as the loss name
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public LogCosh(Ops tf, Reduction reduction) {
+    this(tf, null, reduction);
+  }
+
+  /**
+   * Creates a LogCosh Loss
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public LogCosh(Ops tf, String name, Reduction reduction) {
+    super(tf, name, reduction);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+          Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    Operand<T> losses = Losses.logCosh(getTF(), labels, predictions);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Loss.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Loss.java
new file mode 100644
index 00000000000..ae33d5dfa37
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Loss.java
@@ -0,0 +1,108 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+
+public abstract class Loss {
+  public static final Reduction REDUCTION_DEFAULT = Reduction.AUTO;
+
+  protected final Ops tf;
+  protected final Reduction reduction;
+
+  /**
+   * Creates a Loss using {@link Class#getSimpleName()}  as the name and a Loss Reduction of {@link
+   * Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  protected Loss(Ops tf) {
+    this(tf, null, Reduction.AUTO);
+  }
+
+  /**
+   * Creates a Loss using a Loss Reduction of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of this Loss, if null the name will be {@link Class#getSimpleName()}.
+   */
+  protected Loss(Ops tf, String name) {
+    this(tf, name, Reduction.AUTO);
+  }
+
+  /**
+   * Creates a Loss
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of this loss, if null the name will be {@link Class#getSimpleName()}.
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  protected Loss(Ops tf, String name, Reduction reduction) {
+    this.tf = name != null ? tf.withSubScope(name) : tf.withSubScope(getClass().getSimpleName());
+    this.reduction = reduction;
+  }
+
+  /**
+   * Calculates the loss
+   *
+   * @param labels the truth values or labels
+   * @param predictions the predictions
+   * @param <T> The data type of the predictions and loss.
+   * @param <U> The data type of the labels.
+   * @return the loss
+   */
+  public <T extends TNumber, U extends TNumber> Operand<T> call(Operand<U> labels, Operand<T> predictions) {
+    return call(labels, predictions, null);
+  }
+
+  /**
+   * Generates an Operand that calculates the loss.
+   *
+   * @param labels the truth values or labels
+   * @param predictions the predictions
+   * @param sampleWeights Optional sampleWeights acts as a coefficient for the loss. If a scalar is
+   *     provided, then the loss is simply scaled by the given value. If SampleWeights is a tensor
+   *     of size [batch_size], then the total loss for each sample of the batch is rescaled by the
+   *     corresponding element in the SampleWeights vector. If the shape of SampleWeights is
+   *     [batch_size, d0, .. dN-1] (or can be broadcast to this shape), then each loss element of
+   *     predictions is scaled by the corresponding value of SampleWeights. (Note on dN-1: all loss
+   *     functions reduce by 1 dimension, usually axis=-1.)
+   * @param <T> The data type of the predictions, sampleWeights and loss.
+   * @param <U> The data type of the labels.
+   * @return the loss
+   */
+  public abstract <T extends TNumber, U extends TNumber> Operand<T> call(
+      Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights);
+
+  /**
+   * Gets the TensorFlow Ops
+   *
+   * @return the TensorFlow Ops
+   */
+  public Ops getTF() {
+    return tf;
+  }
+
+  /**
+   * Gets the loss reduction
+   *
+   * @return the loss reduction
+   */
+  public Reduction getReduction() {
+    return reduction;
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Losses.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Losses.java
new file mode 100644
index 00000000000..7a633ede2bf
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Losses.java
@@ -0,0 +1,704 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.DataType;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossTuple;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.op.core.ReduceAll;
+import org.tensorflow.op.core.ReduceMax;
+import org.tensorflow.op.core.ReduceSum;
+import org.tensorflow.op.math.Mean;
+import org.tensorflow.op.math.Softplus;
+import org.tensorflow.types.TBool;
+import org.tensorflow.types.TInt64;
+import org.tensorflow.types.family.TNumber;
+
+import static org.tensorflow.framework.utils.CastHelper.cast;
+
+/** Built-in loss functions. */
+public class Losses {
+
+  /** Default Fuzz factor. */
+  public static final float EPSILON = 1e-7f;
+
+  /**
+   * Calculates the mean absolute error between labels and predictions.
+   *
+   * <p><code>loss = reduceMean(abs(labels - predictions))</code>
+   *
+   * @param tf The TensorFlow Ops
+   * @param labels the labels
+   * @param predictions the predictions
+   * @param <T> the data type of the predictions and result
+   * @param <U> the data type of the labels
+   * @return the mean absolute error
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> meanAbsoluteError(
+      Ops tf, Operand<U> labels, Operand<T> predictions) {
+    Operand<T> tLabels = cast(tf, labels, predictions.asOutput().dataType());
+    LossTuple<T> ops = LossesHelper.squeezeOrExpandDimensions(tf, tLabels, predictions, null);
+    predictions = ops.getTarget();
+    tLabels = ops.getLabels();
+    return tf.math.mean(
+        tf.math.abs(tf.math.sub(tLabels, predictions)), tf.constant(-1), Mean.keepDims(false));
+  }
+
+  /**
+   * Computes the mean squared error between labels and predictions.
+   *
+   * <p><code>loss = reduceMean(square(labels - predictions))</code>
+   *
+   * @param tf The TensorFlow Ops
+   * @param labels the labels
+   * @param predictions the predictions
+   * @param <T> the data type of the predictions and result
+   * @param <U> the data type of the labels
+   * @return the mean squared error
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> meanSquaredError(
+      Ops tf, Operand<U> labels, Operand<T> predictions) {
+    Operand<T> tLabels = cast(tf, labels, predictions.asOutput().dataType());
+    LossTuple<T> ops = LossesHelper.squeezeOrExpandDimensions(tf, tLabels, predictions, null);
+    predictions = ops.getTarget();
+    tLabels = ops.getLabels();
+    return tf.math.mean(tf.math.squaredDifference(predictions, tLabels), tf.constant(-1));
+  }
+
+  /**
+   * Calculates the mean absolute percentage error between labels and predictions.
+   *
+   * <p><code>loss = 100 * reduceMean(abs((labels - predictions) / labels))</code>
+   *
+   * @param tf The TensorFlow Ops
+   * @param labels the labels
+   * @param predictions the predictions
+   * @param <T> the data type of the predictions and result
+   * @param <U> the data type of the labels
+   * @return the mean absolute percentage error
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> meanAbsolutePercentageError(
+      Ops tf, Operand<U> labels, Operand<T> predictions) {
+    DataType<T> dataType = predictions.asOutput().dataType();
+    Operand<T> tLabels = cast(tf, labels, dataType);
+    LossTuple<T> ops = LossesHelper.squeezeOrExpandDimensions(tf, tLabels, predictions, null);
+    predictions = ops.getTarget();
+    tLabels = ops.getLabels();
+    Operand<T> diff =
+        tf.math.abs(
+            tf.math.div(
+                tf.math.sub(tLabels, predictions),
+                tf.math.maximum(tf.math.abs(tLabels), cast(tf, tf.constant(EPSILON), dataType))));
+    return tf.math.mul(cast(tf, tf.constant(100), dataType), tf.math.mean(diff, tf.constant(-1)));
+  }
+
+  /**
+   * Calculates the mean squared logarithmic error between labels and predictions.
+   *
+   * <p><code>loss = reduceMean(square(log(labels + 1) - log(predictions + 1)))</code>
+   *
+   * @param tf The TensorFlow Ops
+   * @param labels the labels
+   * @param predictions the predictions
+   * @param <T> the data type of the predictions and result
+   * @param <U> the data type of the labels
+   * @return the mean squared logarithmic percentage error
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> meanSquaredLogarithmicError(
+      Ops tf, Operand<U> labels, Operand<T> predictions) {
+    DataType<T> dataType = predictions.asOutput().dataType();
+    Operand<T> tLabels = cast(tf, labels, dataType);
+    LossTuple<T> ops = LossesHelper.squeezeOrExpandDimensions(tf, tLabels, predictions, null);
+    predictions = ops.getTarget();
+    tLabels = ops.getLabels();
+
+    Operand<T> epsilonConst = cast(tf, tf.constant(EPSILON), dataType);
+    Operand<T> one = cast(tf, tf.constant(1), dataType);
+
+    Operand<T> firstLog = tf.math.log(tf.math.add(tf.math.maximum(predictions, epsilonConst), one));
+    Operand<T> secondLog = tf.math.log(tf.math.add(tf.math.maximum(tLabels, epsilonConst), one));
+
+    return tf.math.mean(tf.math.squaredDifference(firstLog, secondLog), tf.constant(-1));
+  }
+
+  /**
+   * Computes the binary crossentropy loss between labels and predictions.
+   *
+   * @param tf the TensorFlow Ops
+   * @param labels true targets
+   * @param predictions the predictions
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   * @param labelSmoothing A number in the range [0, 1]. When 0, no smoothing occurs. When &gt; 0,
+   *     compute the loss between the predicted labels and a smoothed version of the true labels,
+   *     where the smoothing squeezes the labels towards 0.5. Larger values of labelSmoothing
+   *     correspond to heavier smoothing.
+   * @param <T> the data type of the predictions and labels
+   * @return the binary crossentropy loss.
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> binaryCrossentropy(
+      Ops tf, Operand<U> labels, Operand<T> predictions, boolean fromLogits, float labelSmoothing) {
+    DataType<T> dataType = predictions.asOutput().dataType();
+    Operand<T> tLabels = cast(tf, labels, dataType);
+    LossTuple<T> ops = LossesHelper.squeezeOrExpandDimensions(tf, tLabels, predictions, null);
+    predictions = ops.getTarget();
+    tLabels = ops.getLabels();
+
+    if (labelSmoothing != 0.0f) {
+      tLabels = smoothBinaryLabels(tf, tLabels, labelSmoothing);
+    }
+    Operand<T> bce = binaryCrossentropyHelper(tf, tLabels, predictions, fromLogits);
+    return tf.math.mean(bce, tf.constant(-1));
+  }
+
+  /**
+   * Computes the unreduced crossentropy loss between labels and predictions.
+   *
+   * @param tf the TensorFlow Ops
+   * @param target the target Operand
+   * @param output the output, either logits or a probability distribution
+   * @param fromLogits whether `output` is expected to be a logits tensor. By default, we consider
+   *     that `output` encodes a probability distribution.
+   * @param <T> the data type of the Operands
+   * @return the binary crossentropy loss.
+   */
+  private static <T extends TNumber> Operand<T> binaryCrossentropyHelper(
+      Ops tf, Operand<T> target, Operand<T> output, boolean fromLogits) {
+    if (fromLogits) return tf.nn.sigmoidCrossEntropyWithLogits(target, output);
+
+    /* TODO - skip this loggic for now. It requires walking back the inputs which is not yet possible
+    if (!(output instanceof Variable) && (!tf.scope().env().isEager())) {
+      // TODO - this does not work
+      // TODO output = backtrackIdentity(output);
+      // TODO if (output.op().type().equals(Sigmoid.OP_NAME)) {
+      // TODO   if (output.op().numInputess() != 1)
+      // TODO     throw new IllegalArgumentException("output can only have 1 output");
+      // TODO   output = output.op().inout(0);
+       // TODO   return tf.nn.sigmoidCrossEntropyWithLogits(target, output);
+      // TODO}
+    }
+    */
+
+    DataType<T> dataType = output.asOutput().dataType();
+    Operand<T> one = cast(tf, tf.constant(1), dataType);
+    Operand<T> epsilonConst = cast(tf, tf.constant(EPSILON), dataType);
+    Operand<T> oneMinusEpsilonConst = tf.math.sub(one, epsilonConst);
+    output = tf.clipByValue(output, epsilonConst, oneMinusEpsilonConst);
+
+    // Compute cross entropy from probabilities.
+    Operand<T> bce = tf.math.mul(target, tf.math.log(tf.math.add(output, epsilonConst)));
+    bce =
+        tf.math.add(
+            bce,
+            tf.math.mul(
+                tf.math.sub(one, target),
+                tf.math.log(tf.math.add(tf.math.sub(one, output), epsilonConst))));
+    return tf.math.neg(bce);
+  }
+
+  /**
+   * Computes the categorical crossentropy loss between labels and predictions.
+   *
+   * @param tf the TensorFlow Ops
+   * @param labels true targets
+   * @param predictions the predictions
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   * @param labelSmoothing Float in <code>[0, 1]</code>. When <code>&gt; 0</code>, label values are smoothed, meaning the
+   *     confidence on label values are relaxed. e.g. <code>labelSmoothing=0.2<code> means that we will use a
+   *     value of </code>0.1<code> for label </code>0<code> and </code>0.9<code> for label </code>1<code>
+   * @param axis the
+   * @param <T> the data type of the predictions and labels
+   * @return the categorical crossentropy loss.
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> categoricalCrossentropy(
+      Ops tf,
+      Operand<U> labels,
+      Operand<T> predictions,
+      boolean fromLogits,
+      float labelSmoothing,
+      int axis) {
+    DataType<T> dataType = predictions.asOutput().dataType();
+    Operand<T> tLabels = cast(tf, labels, dataType);
+    LossTuple<T> ops = LossesHelper.squeezeOrExpandDimensions(tf, tLabels, predictions, null);
+    predictions = ops.getTarget();
+    tLabels = ops.getLabels();
+
+    if (labelSmoothing != 0.0f) {
+      tLabels = smoothCategoricalLabels(tf, tLabels, labelSmoothing);
+    }
+    if (fromLogits) {
+      return tf.nn.softmaxCrossEntropyWithLogits(tLabels, predictions, -1);
+    }
+    /* TODO
+    if (!(predictions instanceof Variable) && (!tf.scope().env().isEager())) {
+
+      // TODO output = backtrackIdentity(output); doesn't seem to work with Java version.
+      if (predictions.op().type().equals("Softmax")) {
+        if (predictions.op().numOutputs() != 1)
+          throw new IllegalArgumentException("output can only have 1 output");
+        predictions = predictions.op().output(0);
+        return tf.nn.softmaxCrossEntropyWithLogits(tLabels, predictions, -1);
+      }
+    }
+    */
+
+    Operand<T> one = cast(tf, tf.constant(1), dataType);
+    Operand<T> epsilonConst = cast(tf, tf.constant(EPSILON), dataType);
+    Operand<T> oneMinusEpsilonConst = tf.math.sub(one, epsilonConst);
+    predictions =
+        tf.math.div(
+            predictions, tf.reduceSum(predictions, tf.constant(axis), ReduceSum.keepDims(true)));
+    predictions = tf.clipByValue(predictions, epsilonConst, oneMinusEpsilonConst);
+
+    // Compute cross entropy from probabilities.
+    Operand<T> cce =
+        tf.reduceSum(
+            tf.math.mul(tLabels, tf.math.log(predictions)),
+            tf.constant(axis),
+            ReduceSum.keepDims(false));
+    return tf.math.neg(cce);
+  }
+
+  /**
+   * Computes the categorical hinge loss between labels and predictions.
+   *
+   * @param tf the TensorFlow Ops
+   * @param labels true targets, values are expected to be 0 or 1.
+   * @param predictions the predictions
+   * @param <T> the data type of the predictions and labels
+   * @return the categorical hinge loss
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> categoricalHinge(
+      Ops tf, Operand<U> labels, Operand<T> predictions) {
+    DataType<T> dataType = predictions.asOutput().dataType();
+    Operand<T> tLabels = cast(tf, labels, dataType);
+    LossTuple<T> lossTuple = LossesHelper.squeezeOrExpandDimensions(tf, tLabels, predictions, null);
+    predictions = lossTuple.getTarget();
+    tLabels = lossTuple.getLabels();
+    Operand<T> one = cast(tf, tf.constant(1), dataType);
+    Operand<T> zero = cast(tf, tf.constant(0), dataType);
+
+    Operand<T> pos =
+        tf.reduceSum(
+            tf.math.mul(tLabels, predictions), tf.constant(-1), ReduceSum.keepDims(Boolean.FALSE));
+    Operand<T> neg =
+        tf.reduceMax(
+            tf.math.mul(tf.math.sub(one, tLabels), predictions),
+            tf.constant(-1),
+            ReduceMax.keepDims(Boolean.FALSE));
+    Operand<T> sub = tf.math.sub(neg, pos);
+    Operand<T> add = tf.math.add(sub, one);
+    return tf.math.maximum(zero, add);
+  }
+
+  /**
+   * Computes the cosine similarity loss between labels and predictions.
+   *
+   * <p>Note that it is a number between <code>-1</code> and <code>1</code>, which is different from
+   * the mathematical definition of cosine similarity where <code>1</code> represents similar
+   * vectors, and <code>0</code> represents dissimilar vectors. In this function, the numbers are
+   * inverted in a range of <code>-1</code> to <code>1</code>. When it is a negative number between
+   * <code>-1</code> and <code>0</code>, <code>0</code> indicates orthogonality and values closer to
+   * <code>-1</code> indicate greater similarity. The values closer to <code>1</code> indicate
+   * greater dissimilarity. This makes it usable as a loss function in a setting where you try to
+   * maximize the proximity between predictions and targets. If either labels or predictions is a
+   * zero vector, cosine similarity will be <code>0</code> regardless of the proximity between
+   * predictions and targets.
+   *
+   * <p><code>loss = -sum(l2Norm(labels) * l2Norm(predictions))</code>
+   *
+   * @param tf the TensorFlow Ops
+   * @param labels true targets
+   * @param predictions the predictions
+   * @param axis Axis along which to determine similarity.
+   * @param <T> the data type of the predictions and labels
+   * @return the cosine similarity loss
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> cosineSimilarity(
+      Ops tf, Operand<U> labels, Operand<T> predictions, int axis) {
+    DataType<T> dataType = predictions.asOutput().dataType();
+    Operand<T> tLabels = cast(tf, labels, dataType);
+    LossTuple<T> lossTuple = LossesHelper.squeezeOrExpandDimensions(tf, tLabels, predictions, null);
+    predictions = lossTuple.getTarget();
+    tLabels = lossTuple.getLabels();
+
+    tLabels = l2Normalize(tf, tLabels, axis);
+    predictions = l2Normalize(tf, predictions, axis);
+    Operand<T> mathMul = tf.math.mul(tLabels, predictions);
+    Operand<T> sum = tf.reduceSum(mathMul, tf.constant(axis), ReduceSum.keepDims(Boolean.FALSE));
+    return tf.math.neg(sum);
+  }
+
+  /**
+   * Computes the hinge loss between labels and predictions
+   *
+   * <p><code>loss = reduceMean(maximum(1 - labels * predictions, 0))</code>
+   *
+   * @param tf the TensorFlow Ops
+   * @param labels true targets, values are expected to be -1 or 1. If binary (0 or 1) labels are
+   *     provided, they will be converted to -1 or 1.
+   * @param predictions the predictions
+   * @param <T> the data type of the predictions and labels
+   * @return the hinge loss
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> hinge(
+      Ops tf, Operand<U> labels, Operand<T> predictions) {
+    DataType<T> dataType = predictions.asOutput().dataType();
+    Operand<T> tLabels = cast(tf, labels, dataType);
+    LossTuple<T> lossTuple = LossesHelper.squeezeOrExpandDimensions(tf, tLabels, predictions, null);
+    predictions = lossTuple.getTarget();
+    tLabels = lossTuple.getLabels();
+    Operand<T> one = cast(tf, tf.constant(1), dataType);
+    Operand<T> zero = cast(tf, tf.constant(0), dataType);
+
+    tLabels = maybeConvertLabels(tf, tLabels);
+
+    return tf.math.mean(
+        tf.math.maximum(tf.math.sub(one, tf.math.mul(tLabels, predictions)), zero),
+        tf.constant(-1));
+  }
+
+  /**
+   * Computes the Huber loss between labels and predictions.
+   *
+   * <p>For each value x in error = labels - predictions:
+   *
+   * <pre>
+   *     loss = 0.5 * x^2                  if |x| &lt;= d
+   *     loss = 0.5 * d^2 + d * (|x| - d)  if |x| &gt; d
+   * </pre>
+   *
+   * <p>where d is delta.
+   *
+   * @param tf the TensorFlow Ops
+   * @param labels true targets
+   * @param predictions the predictions
+   * @param delta the point where the Huber loss function changes from quadratic to linear.
+   * @param <T> the data type of the predictions and labels
+   * @return the Huber loss
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> huber(
+      Ops tf, Operand<U> labels, Operand<T> predictions, float delta) {
+    DataType<T> dataType = predictions.asOutput().dataType();
+    Operand<T> tLabels = cast(tf, labels, dataType);
+    LossTuple<T> lossTuple = LossesHelper.squeezeOrExpandDimensions(tf, tLabels, predictions, null);
+    predictions = lossTuple.getTarget();
+    tLabels = lossTuple.getLabels();
+
+    Operand<T> error = tf.math.sub(predictions, tLabels);
+    Operand<T> deltaConst = cast(tf, tf.constant(delta), dataType);
+    Operand<T> point5 = cast(tf, tf.constant(0.5), dataType);
+    Operand<T> absError = tf.math.abs(error);
+    Operand<T> quadratic = tf.math.minimum(absError, deltaConst);
+    Operand<T> linear = tf.math.sub(absError, quadratic);
+    Operand<T> q2Point5 = tf.math.mul(point5, tf.math.mul(quadratic, quadratic));
+    Operand<T> deltaLinear = tf.math.mul(deltaConst, linear);
+    Operand<T> loss = tf.math.add(q2Point5, deltaLinear);
+    return tf.math.mean(loss, tf.constant(-1));
+  }
+
+  /**
+   * Computes the Kullback-Leibler divergence loss between labels and predictions.
+   *
+   * @param tf the TensorFlow Ops
+   * @param labels true targets
+   * @param predictions the predictions
+   * @param <T> the data type of the predictions and labels
+   * @return the Kullback-Leibler divergence loss
+   * @see <a href="https://en.wikipedia.org/wiki/Kullback?Leibler_divergence">Kullback?Leibler
+   *     divergence</a>
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> kullbackLeiblerDivergence(
+      Ops tf, Operand<U> labels, Operand<T> predictions) {
+    DataType<T> dataType = predictions.asOutput().dataType();
+    Operand<T> tLabels = cast(tf, labels, dataType);
+    LossTuple<T> lossTuple = LossesHelper.squeezeOrExpandDimensions(tf, tLabels, predictions, null);
+    predictions = lossTuple.getTarget();
+    tLabels = lossTuple.getLabels();
+    Operand<T> one = cast(tf, tf.constant(1), dataType);
+    Operand<T> epsilonConst = cast(tf, tf.constant(EPSILON), dataType);
+
+    tLabels = tf.clipByValue(tLabels, epsilonConst, one);
+    predictions = tf.clipByValue(predictions, epsilonConst, one);
+    return tf.reduceSum(
+        tf.math.mul(tLabels, tf.math.log(tf.math.div(tLabels, predictions))), tf.constant(-1));
+  }
+
+  /**
+   * Computes the hyperbolic cosine loss between labels and predictions.
+   *
+   * <p><code>log(cosh(x))</code> is approximately equal to <code>(x ** 2) / 2</code> for small
+   * <code>x</code> and to <code>abs(x) - log(2)</code> for large <code>x</code>. This means that
+   * 'logCosh' works mostly like the mean squared error, but will not be so strongly affected by the
+   * occasional wildly incorrect prediction.
+   *
+   * @param tf the TensorFlow Ops
+   * @param labels true targets
+   * @param predictions the predictions
+   * @param <T> the data type of the predictions and labels
+   * @return the hyperbolic cosine divergence loss
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> logCosh(
+      Ops tf, Operand<U> labels, Operand<T> predictions) {
+    DataType<T> dataType = predictions.asOutput().dataType();
+    Operand<T> tLabels = cast(tf, labels, dataType);
+    LossTuple<T> lossTuple = LossesHelper.squeezeOrExpandDimensions(tf, tLabels, predictions, null);
+    predictions = lossTuple.getTarget();
+    tLabels = lossTuple.getLabels();
+    Operand<T> minusTwo = cast(tf, tf.constant(-2), dataType);
+    Operand<T> two = cast(tf, tf.constant(2), dataType);
+
+    Operand<T> diff = tf.math.sub(predictions, tLabels);
+    Softplus<T> softplus = tf.math.softplus(tf.math.mul(minusTwo, diff));
+    Operand<T> logcosh = tf.math.sub(tf.math.add(diff, softplus), tf.math.log(two));
+    return tf.math.mean(logcosh, tf.constant(-1));
+  }
+
+  /**
+   * Computes the Poisson loss between labels and predictions.
+   *
+   * <p>The Poisson loss is the mean of the elements of the Tensor <code>
+   * predictions - labels * log(predictions)</code>.
+   *
+   * @param tf the TensorFlow Ops
+   * @param labels true targets
+   * @param predictions the predictions
+   * @param <T> the data type of the predictions and labels
+   * @return the Poisson loss
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> poisson(
+      Ops tf, Operand<U> labels, Operand<T> predictions) {
+    DataType<T> dataType = predictions.asOutput().dataType();
+    Operand<T> tLabels = cast(tf, labels, dataType);
+    LossTuple<T> lossTuple = LossesHelper.squeezeOrExpandDimensions(tf, tLabels, predictions, null);
+    predictions = lossTuple.getTarget();
+    tLabels = lossTuple.getLabels();
+    Operand<T> epsilonConst = cast(tf, tf.constant(EPSILON), dataType);
+
+    return tf.math.mean(
+        tf.math.sub(
+            predictions, tf.math.mul(tLabels, tf.math.log(tf.math.add(predictions, epsilonConst)))),
+        tf.constant(-1));
+  }
+
+  /**
+   * Computes the sparse categorical crossentropy loss between labels and predictions.
+   *
+   * @param tf the TensorFlow Ops
+   * @param labels true targets
+   * @param predictions the predictions
+   * @param fromLogits Whether predictions is expected to be logits. By default, it is assumed that
+   *     predictions encodes a probability distribution.
+   * @param axis The dimension along which the entropy is computed.
+   * @param <T> the data type of the predictions and labels
+   * @return the sparse categorical crossentropy loss
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> sparseCategoricalCrossentropy(
+      Ops tf, Operand<U> labels, Operand<T> predictions, boolean fromLogits, int axis) {
+    DataType<T> dataType = predictions.asOutput().dataType();
+    Operand<T> epsilonConst = cast(tf, tf.constant(EPSILON), dataType);
+    Operand<T> one = cast(tf, tf.constant(1), dataType);
+    Operand<T> oneMinusEpsilonConst = tf.math.sub(one, epsilonConst);
+
+    /* TODO need ability to walk back inputs
+    if (!fromLogits && !(predictions instanceof Variable) && (!tf.scope().env().isEager())) {
+      // TODO output = backtrackIdentity(output); doesn't seem to work with Java version.
+      /* TODO
+      if (predictions.op().type().equals(Softmax.OP_NAME)) {
+        // When softmax activation function is used for output operation, we
+        // use logits from the softmax function directly to compute loss in order
+        // to prevent collapsing zero when training.
+        // TODO  if( output.op().numOutputs() != 1)
+        //          throw new IllegalArgumentException("output can only have 1 output");
+        // TODO output = output.op.inputs[0]
+        fromLogits = true;
+      }
+
+    }
+     */
+    if (!fromLogits) {
+
+      predictions = tf.clipByValue(predictions, epsilonConst, oneMinusEpsilonConst);
+      predictions = tf.math.log(predictions);
+    }
+    Shape predictionsShape = predictions.asOutput().shape();
+    int predictionsRank = predictionsShape.numDimensions();
+    axis %= predictionsRank;
+    if (axis < 0) {
+      axis += predictionsRank;
+    }
+    if (axis != predictionsRank - 1) {
+      int[] axisNew = moveAxisToEnd(axis, predictionsRank);
+      predictions = tf.linalg.transpose(predictions, tf.constant(axisNew));
+    }
+
+    Operand<TInt64> iLabels = cast(tf, labels, TInt64.DTYPE);
+
+    // Try to adjust the shape so that rank of labels = rank of logits - 1.
+    Shape labelsShape = labels.asOutput().shape();
+    int labelsRank = labelsShape.numDimensions();
+
+    boolean updateShape = labelsRank != predictionsRank - 1;
+    if (updateShape) { // TODO check to see if this is right
+      Shape newShape = labelsShape.take(labelsRank - 1);
+      iLabels = tf.reshape(iLabels, tf.constant(newShape)); // flatten one dimension
+      predictions =
+          tf.reshape(
+              predictions,
+              tf.constant(
+                  new long[] {-1L, predictionsShape.size(predictionsShape.numDimensions() - 1)}));
+    }
+
+    @SuppressWarnings("unchecked")
+    Operand<T> loss = tf.nn.sparseSoftmaxCrossEntropyWithLogits(iLabels, predictions);
+    if (updateShape && predictionsRank >= 3) {
+      Shape newShape = predictionsShape.take(predictionsShape.numDimensions() - 1);
+      loss = tf.reshape(loss, tf.constant(newShape));
+    }
+    return loss;
+  }
+
+  /**
+   * Computes the squared hinge loss between labels and predictions.
+   *
+   * <p><code>loss = reduceMean(square(maximum(1 - labels * predictions, 0)))</code>
+   *
+   * @param tf the TensorFlow Ops
+   * @param labels true targets, values are expected to be -1 or 1. If binary (0 or 1) labels are *
+   *     provided, they will be converted to -1 or 1.
+   * @param predictions the predictions
+   * @param <T> the data type of the predictions and labels
+   * @return the squared hinge loss
+   */
+  public static <T extends TNumber, U extends TNumber> Operand<T> squaredHinge(
+      Ops tf, Operand<U> labels, Operand<T> predictions) {
+    DataType<T> dataType = predictions.asOutput().dataType();
+    Operand<T> tLabels = cast(tf, labels, dataType);
+    LossTuple<T> lossTuple = LossesHelper.squeezeOrExpandDimensions(tf, tLabels, predictions, null);
+    predictions = lossTuple.getTarget();
+    tLabels = lossTuple.getLabels();
+    Operand<T> one = cast(tf, tf.constant(1), dataType);
+    Operand<T> zero = cast(tf, tf.constant(0), dataType);
+
+    tLabels = maybeConvertLabels(tf, tLabels);
+    return tf.math.mean(
+        tf.math.square(tf.math.maximum(tf.math.sub(one, tf.math.mul(tLabels, predictions)), zero)),
+        tf.constant(-1));
+  }
+
+  /**
+   * Smooths binary labels
+   *
+   * @param tf the TensorFlow Ops
+   * @param labels true targets
+   * @param labelSmoothing A number in the range [0, 1]. When 0, no smoothing occurs. When &gt; 0,
+   *     compute the loss between the predicted labels and a smoothed version of the true labels,
+   *     where the smoothing squeezes the labels towards 0.5. Larger values of labelSmoothing
+   *     correspond to heavier smoothing.
+   * @param <T> the data type of the labels
+   * @return the smoothed binary labels
+   */
+  private static <T extends TNumber> Operand<T> smoothBinaryLabels(
+      Ops tf, Operand<T> labels, float labelSmoothing) {
+    DataType<T> dataType = labels.asOutput().dataType();
+    Operand<T> oneMinusSmoothing = cast(tf, tf.constant(1.f - labelSmoothing), dataType);
+    Operand<T> halfSmoothing = cast(tf, tf.constant(0.5F * labelSmoothing), dataType);
+    return tf.math.add(tf.math.mul(labels, oneMinusSmoothing), halfSmoothing);
+  }
+
+  /**
+   * Smooths categorical labels
+   *
+   * @param tf the TensorFlow Ops
+   * @param labels true targets
+   * @param labelSmoothing Float in <code>[0, 1]</code>. When <code>&gt; 0</code>, label values are smoothed, meaning the
+   *    confidence on label values are relaxed. e.g. <code>labelSmoothing=0.2<code> means that we will use a
+   *    value of </code>0.1<code> for label </code>0<code> and </code>0.9<code> for label </code>1<code>
+   * @param <T> the data type of the labels
+   * @return the smoothed categorical labels
+   */
+  private static <T extends TNumber> Operand<T> smoothCategoricalLabels(
+      Ops tf, Operand<T> labels, float labelSmoothing) {
+    DataType<T> dataType = labels.asOutput().dataType();
+    Operand<T> smoothing = cast(tf, tf.constant(labelSmoothing), dataType);
+    Shape labelsShape = labels.asOutput().shape();
+    int numDims = labelsShape.numDimensions();
+    Operand<T> numClasses = cast(tf, tf.constant(labelsShape.size(numDims - 1)), dataType);
+    Operand<T> oneMinusSmoothing = cast(tf, tf.constant(1.f - labelSmoothing), dataType);
+    return tf.math.add(tf.math.mul(labels, oneMinusSmoothing), tf.math.div(smoothing, numClasses));
+  }
+
+  // TODO this was tf.math.l2_normalize in TF Python
+  /**
+   * Normalizes along dimension axis using an L2 norm.
+   *
+   * @param tf The TensorFlow Ops
+   * @param x the input
+   * @param axis Dimension along which to normalize.
+   * @return the normalized values based on L2 norm
+   */
+  public static <T extends TNumber> Operand<T> l2Normalize(Ops tf, Operand<T> x, int axis) {
+    Operand<T> squareSum =
+        tf.reduceSum(tf.math.square(x), tf.constant(axis), ReduceSum.keepDims(Boolean.TRUE));
+    Operand<T> invNorm =
+        tf.math.rsqrt(
+            tf.math.maximum(squareSum, cast(tf, tf.constant(1e-12F), x.asOutput().dataType())));
+    return tf.math.mul(x, invNorm);
+  }
+
+  /**
+   * Converts binary labels into -1/1.
+   *
+   * @param tf the TensorFlow Ops
+   * @param labels true targets
+   * @param <T> the data type of the labels
+   * @return the labels, possibly converted into -1/1.
+   */
+  private static <T extends TNumber> Operand<T> maybeConvertLabels(Ops tf, Operand<T> labels) {
+    DataType<T> dataType = labels.asOutput().dataType();
+
+    Operand<T> one = cast(tf, tf.constant(1), dataType);
+    Operand<T> zero = cast(tf, tf.constant(0), dataType);
+    Operand<T> two = cast(tf, tf.constant(2), dataType);
+    Operand<TBool> areZeros = tf.math.equal(labels, zero);
+    Operand<TBool> areOnes = tf.math.equal(labels, one);
+    Operand<TBool> isBinary =
+        tf.reduceAll(
+            tf.math.logicalOr(areZeros, areOnes), tf.constant(-1), ReduceAll.keepDims(true));
+    Operand<T> convertBinaryLabels = tf.math.sub(tf.math.mul(two, labels), one);
+    return tf.select(isBinary, convertBinaryLabels, labels);
+  }
+
+  /**
+   * Move the specified axis to end, to be used with transposes
+   *
+   * @param axis the axis to move
+   * @param outputRank the rank of the shape
+   * @return the new dimension array with the axis moved to the end.
+   */
+  private static int[] moveAxisToEnd(int axis, int outputRank) {
+    int[] axisNew = new int[outputRank];
+    for (int i = 0; i < axis; i++) {
+      axisNew[i] = i;
+    }
+    for (int i = axis + 1; i < outputRank; i++) {
+      axisNew[i - 1] = i;
+    }
+    axisNew[outputRank - 1] = axis;
+    return axisNew;
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/MeanAbsoluteError.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/MeanAbsoluteError.java
new file mode 100644
index 00000000000..a2d5d5f8efc
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/MeanAbsoluteError.java
@@ -0,0 +1,103 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+
+/**
+ * Computes the mean of absolute difference between labels and predictions.
+ *
+ * <p><code>loss = abs(labels - predictions)</code>
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[][] {{0.f, 1.f}, {0.f, 0.f}});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{1.f, 1.f}, {1.f, 0.f}});
+ *    MeanAbsoluteError mae = new MeanAbsoluteError(tf);
+ *    Operand&lt;TFloat32&gt; result = mae.call(labels, predictions);
+ *    // produces 0.5f
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {0.7f, 0.3f});
+ *    Operand&lt;TFloat32&gt; result = mae.call(labels, predictions, sampleWeight);
+ *    // produces 0.25f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    MeanAbsoluteError mae = new MeanAbsoluteError(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = mae.call(labels, predictions);
+ *    // produces 1.0f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    MeanAbsoluteError mae = new MeanAbsoluteError(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = mae.call(labels, predictions);
+ *    // produces [0.5f, 0.5f]
+ * </pre>
+ */
+public class MeanAbsoluteError extends Loss {
+
+  /**
+   * Creates a MeanAbsoluteError Loss using {@link Class#getSimpleName()} as the loss name and a
+   * Loss Reduction of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public MeanAbsoluteError(Ops tf) {
+    super(tf);
+  }
+
+  /**
+   * Creates a MeanAbsoluteError Loss using {@link Class#getSimpleName()} as the loss name
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public MeanAbsoluteError(Ops tf, Reduction reduction) {
+    super(tf, null, reduction);
+  }
+
+  /**
+   * Creates a MeanAbsoluteError
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public MeanAbsoluteError(Ops tf, String name, Reduction reduction) {
+    super(tf, name, reduction);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+          Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    Operand<T> losses = Losses.meanAbsoluteError(getTF(), labels, predictions);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/MeanAbsolutePercentageError.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/MeanAbsolutePercentageError.java
new file mode 100644
index 00000000000..49133df610b
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/MeanAbsolutePercentageError.java
@@ -0,0 +1,103 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+
+/**
+ * Computes the mean absolute percentage error between labels and predictions.
+ *
+ * <p><code>loss = 100 * abs(labels - predictions) / labels</code>
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[][] {{2.f, 1.f}, {2.f, 3.f}});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{1.f, 1.f}, {1.f, 0.f}});
+ *    MeanAbsolutePercentageError mape = new MeanAbsolutePercentageError(tf);
+ *    Operand&lt;TFloat32&gt; result = mape.call(labels, predictions);
+ *    // produces 50f
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {0.7f, 0.3f});
+ *    Operand&lt;TFloat32&gt; result = mape.call(labels, predictions, sampleWeight);
+ *    // produces 20f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    MeanAbsolutePercentageError mape = new MeanAbsolutePercentageError(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = mape.call(labels, predictions);
+ *    // produces 100.0f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    MeanAbsolutePercentageError mape = new MeanAbsolutePercentageError(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = mape.call(labels, predictions);
+ *    // produces [25f, 75f]
+ * </pre>
+ */
+public class MeanAbsolutePercentageError extends Loss {
+
+  /**
+   * Creates a MeanAbsolutePercentageError Loss using {@link Class#getSimpleName()} as the loss name
+   * and a Loss Reduction of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public MeanAbsolutePercentageError(Ops tf) {
+    super(tf);
+  }
+
+  /**
+   * Creates a MeanAbsolutePercentageError Loss using {@link Class#getSimpleName()} as the loss name
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public MeanAbsolutePercentageError(Ops tf, Reduction reduction) {
+    super(tf, null, reduction);
+  }
+
+  /**
+   * Creates a MeanAbsolutePercentageError
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public MeanAbsolutePercentageError(Ops tf, String name, Reduction reduction) {
+    super(tf, name, reduction);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+          Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    Operand<T> losses = Losses.meanAbsolutePercentageError(getTF(), labels, predictions);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/MeanSquaredError.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/MeanSquaredError.java
new file mode 100644
index 00000000000..2a6c2be885e
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/MeanSquaredError.java
@@ -0,0 +1,103 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+
+/**
+ * Computes the mean of squares of errors between labels and predictions.
+ *
+ * <p><code>loss = loss = square(labels - predictions)</code>
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[][] {{0.f, 1.f}, {0.f, 0.f}});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{1.f, 1.f}, {1.f, 0.f}});
+ *    MeanSquaredError mse = new MeanSquaredError(tf);
+ *    Operand&lt;TFloat32&gt; result = mse.call(labels, predictions);
+ *    // produces 0.5f
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {0.7f, 0.3f});
+ *    Operand&lt;TFloat32&gt; result = mse.call(labels, predictions, sampleWeight);
+ *    // produces 0.25f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    MeanSquaredError mse = new MeanSquaredError(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = mse.call(labels, predictions);
+ *    // produces 1.0f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    MeanSquaredError mse = new MeanSquaredError(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = mse.call(labels, predictions);
+ *    // produces [0.5f, 0.5f]
+ * </pre>
+ */
+public class MeanSquaredError extends Loss {
+
+  /**
+   * Creates a MeanSquaredError Loss using {@link Class#getSimpleName()} as the loss name and a Loss
+   * Reduction of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public MeanSquaredError(Ops tf) {
+    super(tf);
+  }
+
+  /**
+   * Creates a MeanSquaredError Loss using {@link Class#getSimpleName()} as the loss name
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public MeanSquaredError(Ops tf, Reduction reduction) {
+    super(tf, null, reduction);
+  }
+
+  /**
+   * Creates a MeanSquaredError
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public MeanSquaredError(Ops tf, String name, Reduction reduction) {
+    super(tf, name, reduction);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+          Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    Operand<T> losses = Losses.meanSquaredError(getTF(), labels, predictions);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/MeanSquaredLogarithmicError.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/MeanSquaredLogarithmicError.java
new file mode 100644
index 00000000000..2604e226b81
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/MeanSquaredLogarithmicError.java
@@ -0,0 +1,103 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+
+/**
+ * Computes the mean squared logarithmic errors between labels and predictions.
+ *
+ * <p><code>loss = square(log(labels + 1.) - log(predictions + 1.))</code>
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[][] {{0.f, 1.f}, {0.f, 0.f}});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{1.f, 1.f}, {1.f, 0.f}});
+ *    MeanSquaredLogarithmicError msle = new MeanSquaredLogarithmicError(tf);
+ *    Operand&lt;TFloat32&gt; result = msle.call(labels, predictions);
+ *    // produces 0.240f
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {0.7f, 0.3f});
+ *    Operand&lt;TFloat32&gt; result = msle.call(labels, predictions, sampleWeight);
+ *    // produces 0.120f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    MeanSquaredLogarithmicError msle = new MeanSquaredLogarithmicError(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = msle.call(labels, predictions);
+ *    // produces 0.480f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    MeanSquaredLogarithmicError msle = new MeanSquaredLogarithmicError(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = msle.call(labels, predictions);
+ *    // produces [0.240f, 0.240f]
+ * </pre>
+ */
+public class MeanSquaredLogarithmicError extends Loss {
+
+  /**
+   * Creates a MeanSquaredError Loss using {@link Class#getSimpleName()} as the loss name and a Loss
+   * Reduction of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public MeanSquaredLogarithmicError(Ops tf) {
+    super(tf);
+  }
+
+  /**
+   * Creates a MeanSquaredError Loss using {@link Class#getSimpleName()} as the loss name
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public MeanSquaredLogarithmicError(Ops tf, Reduction reduction) {
+    super(tf, null, reduction);
+  }
+
+  /**
+   * Creates a MeanSquaredError
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public MeanSquaredLogarithmicError(Ops tf, String name, Reduction reduction) {
+    super(tf, name, reduction);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+          Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    Operand<T> losses = Losses.meanSquaredLogarithmicError(getTF(), labels, predictions);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Poisson.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Poisson.java
new file mode 100644
index 00000000000..c43be4f2821
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Poisson.java
@@ -0,0 +1,112 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+
+/**
+ * Computes the Poisson loss between labels and predictions.
+ *
+ * <p><code>loss = predictions - labels * log(predictions)</code>
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[][] {{0.f, 1.f}, {0.f, 0.f}});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{1.f, 1.f}, {0.f, 0.f}});
+ *    Poisson poissonLoss = new Poisson(tf);
+ *    Operand&lt;TFloat32&gt; result = poissonLoss.call(labels, predictions);
+ *    // produces 0.5f
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {0.8f, 0.2f});
+ *    Operand&lt;TFloat32&gt; result = poissonLoss.call(labels, predictions, sampleWeight);
+ *    // produces 0.4f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    Poisson poissonLoss = new Poisson(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = poissonLoss.call(labels, predictions);
+ *    // produces 0.999f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    Poisson poissonLoss = new Poisson(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = poissonLoss.call(labels, predictions);
+ *    // produces [0.999f, 0f]
+ * </pre>
+ */
+public class Poisson extends Loss {
+
+  /**
+   * Creates a Poisson Loss using {@link Class#getSimpleName()} as the loss name and a Loss
+   * Reduction of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public Poisson(Ops tf) {
+    this(tf, null, Reduction.AUTO);
+  }
+
+  /**
+   * Creates a Poisson Loss using a Loss Reduction of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public Poisson(Ops tf, String name) {
+    this(tf, name, Reduction.AUTO);
+  }
+
+  /**
+   * Creates a Poisson Loss using {@link Class#getSimpleName()} as the loss name
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public Poisson(Ops tf, Reduction reduction) {
+    this(tf, null, reduction);
+  }
+
+  /**
+   * Creates a Poisson Loss
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public Poisson(Ops tf, String name, Reduction reduction) {
+    super(tf, name, reduction);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+      Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    Operand<T> losses = Losses.poisson(getTF(), labels, predictions);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Reduction.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Reduction.java
new file mode 100644
index 00000000000..87ea43c6c3a
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/Reduction.java
@@ -0,0 +1,45 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+/**
+ * Type of Loss Reduction
+ *
+ * <p>{@link #AUTO} indicates that the reduction option will be determined by the usage context. For
+ * almost all cases this defaults to {@link #SUM_OVER_BATCH_SIZE}.
+ *
+ * <p>{@link #NONE} Weighted losses with one dimension reduced (axis=-1, or axis specified by loss
+ * function).
+ *
+ * <p>{@link #SUM} Scalar sum of weighted losses.
+ *
+ * <p>{@link #SUM_OVER_BATCH_SIZE} Scalar <code>SUM</code> divided by number of elements in losses.
+ */
+public enum Reduction {
+  AUTO,
+  NONE,
+  SUM,
+  SUM_OVER_BATCH_SIZE;
+
+  /**
+   * Get the Reduction based on name
+   *
+   * @param name the name of the reduction
+   * @return the Reduction
+   */
+  public static Reduction ofName(String name) {
+    return Reduction.valueOf(name.toUpperCase());
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/SparseCategoricalCrossentropy.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/SparseCategoricalCrossentropy.java
new file mode 100644
index 00000000000..5586a4da889
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/SparseCategoricalCrossentropy.java
@@ -0,0 +1,218 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+import static org.tensorflow.framework.utils.CastHelper.cast;
+
+/**
+ * Computes the crossentropy loss between labels and predictions.
+ *
+ * <p>Use this crossentropy loss function when there are two or more label classes. The labels are
+ * expected to be provided as integers. If you want to provide labels using <code>one-hot</code>
+ * representation, please use {@link CategoricalCrossentropy} loss. There should be <code># classes
+ * </code> floating point values per feature for <code>predictions</code> and a single floating
+ * point value per feature for <code>label</code>.
+ *
+ * <p>In the snippet below, there is a single floating point value per example for <code>labels
+ * </code> and <code># classes</code> floating pointing values per example for <code>predictions
+ * </code>. The shape of <code>labels</code> is <code>[batch_size]</code> and the shape of <code>
+ * predictions</code> is <code>[batch_size, num_classes]</code>.
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[] {1, 2});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{0.05f, 0.95f, 0f}, {0.1f, 0.8f, 0.1f}});
+ *    SparseCategoricalCrossentropy sparseCCE = new SparseCategoricalCrossentropy(tf);
+ *    Operand&lt;TFloat32&gt; result = sparseCCE.call(labels, predictions);
+ *    // produces 1.177f
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {0.3f, 0.7f});
+ *    Operand&lt;TFloat32&gt; result = sparseCCE.call(labels, predictions, sampleWeight);
+ *    // produces 0.814f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    SparseCategoricalCrossentropy sparseCCE = new SparseCategoricalCrossentropy(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = sparseCCE.call(labels, predictions);
+ *    // produces 2.354f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    SparseCategoricalCrossentropy sparseCCE = new SparseCategoricalCrossentropy(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = sparseCCE.call(labels, predictions);
+ *    // produces [0.0513f, 2.303f]
+ * </pre>
+ */
+public class SparseCategoricalCrossentropy extends Loss {
+  public static final boolean FROM_LOGITS_DEFAULT = false;
+  public static final int AXIS_DEFAULT = -1;
+
+  private final boolean fromLogits;
+  private final int axis;
+
+  /**
+   * Creates a SparseCategoricalCrossentropy loss using {@link Class#getSimpleName()} as the loss
+   * name, a Loss Reduction of {@link Loss#REDUCTION_DEFAULT}, and fromLogits={@link #FROM_LOGITS_DEFAULT}.
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public SparseCategoricalCrossentropy(Ops tf) {
+    this(tf, null, FROM_LOGITS_DEFAULT, REDUCTION_DEFAULT, AXIS_DEFAULT);
+  }
+
+  /**
+   * Creates a SparseCategoricalCrossentropy loss using a Loss Reduction of {@link Loss#REDUCTION_DEFAULT},
+   * and fromLogits={@link #FROM_LOGITS_DEFAULT}.
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of this loss function
+   */
+  public SparseCategoricalCrossentropy(Ops tf, String name) {
+    this(tf, name, FROM_LOGITS_DEFAULT, REDUCTION_DEFAULT, AXIS_DEFAULT);
+  }
+
+  /**
+   * Creates a SparseCategoricalCrossentropy loss using {@link Class#getSimpleName()} as the loss
+   * name, with Reduction.AUTO and fromLogits={@link #FROM_LOGITS_DEFAULT}.
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to loss.
+   */
+  public SparseCategoricalCrossentropy(Ops tf, Reduction reduction) {
+    this(tf, null, FROM_LOGITS_DEFAULT, reduction, AXIS_DEFAULT);
+  }
+
+  /**
+   * Creates a SparseCategoricalCrossentropy loss with Reduction.AUTO and fromLogits={@link
+   * #FROM_LOGITS_DEFAULT}.
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of this loss function
+   * @param reduction Type of Reduction to apply to loss.
+   */
+  public SparseCategoricalCrossentropy(Ops tf, String name, Reduction reduction) {
+    this(tf, name, FROM_LOGITS_DEFAULT, reduction, AXIS_DEFAULT);
+  }
+
+  /**
+   * Creates a SparseCategoricalCrossentropy using a Loss Reduction of {@link Loss#REDUCTION_DEFAULT}, and
+   * fromLogits={@link #FROM_LOGITS_DEFAULT}.
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of this loss function
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   */
+  public SparseCategoricalCrossentropy(Ops tf, String name, boolean fromLogits) {
+    this(tf, name, fromLogits, REDUCTION_DEFAULT, AXIS_DEFAULT);
+  }
+
+  /**
+   * Creates a SparseCategoricalCrossentropy loss using {@link Class#getSimpleName()} as the loss
+   * name, a Loss Reduction of {@link Loss#REDUCTION_DEFAULT} and fromLogits={@link #FROM_LOGITS_DEFAULT}.
+   *
+   * @param tf the TensorFlow Ops
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   */
+  public SparseCategoricalCrossentropy(Ops tf, boolean fromLogits) {
+    this(tf, null, fromLogits, REDUCTION_DEFAULT, AXIS_DEFAULT);
+  }
+
+  /**
+   * Creates a SparseCategoricalCrossentropy loss using {@link Class#getSimpleName()} as the loss
+   * name,
+   *
+   * @param tf the TensorFlow Ops
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   * @param reduction Type of Reduction to apply to loss.
+   */
+  public SparseCategoricalCrossentropy(Ops tf, boolean fromLogits, Reduction reduction) {
+    this(tf, null, fromLogits, reduction, AXIS_DEFAULT);
+  }
+
+  /**
+   * Creates a SparseCategoricalCrossentropy
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of this loss function
+   * @param fromLogits Whether to interpret predictions as a tensor of logit values
+   * @param reduction Type of Reduction to apply to loss.
+   * @param axis The channels axis. <code>axis=-1</code> corresponds to data format `Channels Last'
+   *     and <code>axis=1</code> corresponds to data format 'Channels First'.
+   */
+  public SparseCategoricalCrossentropy(
+      Ops tf, String name, boolean fromLogits, Reduction reduction, int axis) {
+    super(tf, name, reduction);
+    this.fromLogits = fromLogits;
+    this.axis = axis;
+  }
+
+  /**
+   * Generates an Operand the calculates the loss.
+   *
+   * If run in Graph mode, the computation will throw {@link org.tensorflow.exceptions.TFInvalidArgumentException}
+   * if the predictions values are outside the range o [0. to 1.]. In Eager Mode, this call
+   * will throw {@link IllegalArgumentException}, if the predictions values are outside the range o [0. to 1.]
+   *
+   * @param labels the truth values or labels
+   * @param predictions the predictions, values must be in the range [0. to 1.] inclusive.
+   * @param sampleWeights Optional SampleWeights acts as a coefficient for the loss. If a scalar is
+   *     provided, then the loss is simply scaled by the given value. If SampleWeights is a tensor
+   *     of size [batch_size], then the total loss for each sample of the batch is rescaled by the
+   *     corresponding element in the SampleWeights vector. If the shape of SampleWeights is
+   *     [batch_size, d0, .. dN-1] (or can be broadcast to this shape), then each loss element of
+   *     predictions is scaled by the corresponding value of SampleWeights. (Note on dN-1: all loss
+   *     functions reduce by 1 dimension, usually axis=-1.)
+   * @param <T> The data type of the predictions, sampleWeights and loss.
+   * @param <U> The data type of the labels.
+   * @return the loss
+   * @throws IllegalArgumentException if the predictions are outside the range [0.-1.].
+   */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+      Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    Operand<T> lPredictions;
+    if (!fromLogits) {
+      // add predictions range check for 0 - 1
+      lPredictions =
+              LossesHelper.rangeCheck(
+                      getTF(),
+                      "predictions range check [0-1]",
+                      predictions,
+                      cast(getTF(), getTF().constant(0), predictions.asOutput().dataType()),
+                      cast(getTF(), getTF().constant(1), predictions.asOutput().dataType()));
+
+    } else {
+      lPredictions = predictions;
+    }
+    Operand<T> losses =
+        Losses.sparseCategoricalCrossentropy(getTF(), labels, lPredictions, fromLogits, axis);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/SquaredHinge.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/SquaredHinge.java
new file mode 100644
index 00000000000..182ce592e55
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/SquaredHinge.java
@@ -0,0 +1,140 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.impl.LossesHelper;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TNumber;
+import static org.tensorflow.framework.utils.CastHelper.cast;
+
+/**
+ * Computes the squared hinge loss between labels and predictions.
+ *
+ * <p><code>loss = square(maximum(1 - labels * predictions, 0))</code>
+ *
+ * <p><code>labels</code> values are expected to be -1 or 1. If binary (0 or 1) labels are provided, they will be
+ * converted to -1 or 1.
+ *
+ * <p>Standalone usage:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; labels =
+ *        tf.constant(new float[][] {{0., 1.}, {0., 0.}});
+ *    Operand&lt;TFloat32&gt; predictions =
+ *        tf.constant(new float[][] {{0.6f, 0.4f}, {0.4f, 0.6f}});
+ *    SquaredHinge squaredHinge = new SquaredHinge(tf);
+ *    Operand&lt;TFloat32&gt; result = squaredHinge.call(labels, predictions);
+ *    // produces 1.86f
+ * </pre>
+ *
+ * <p>Calling with sample weight:
+ *
+ * <pre>
+ *    Operand&lt;TFloat32&gt; sampleWeight = tf.constant(new float[] {1.f, 0.f});
+ *    Operand&lt;TFloat32&gt; result = squaredHinge.call(labels, predictions,
+ *                                                  sampleWeight);
+ *    // produces 0.73f
+ * </pre>
+ *
+ * <p>Using <code>SUM</code> reduction type:
+ *
+ * <pre>
+ *    SquaredHinge squaredHinge = new SquaredHinge(tf, Reduction.SUM);
+ *    Operand&lt;TFloat32&gt; result = squaredHinge.call(labels, predictions);
+ *    // produces 3.72f
+ * </pre>
+ *
+ * <p>Using <code>NONE</code> reduction type:
+ *
+ * <pre>
+ *    SquaredHinge squaredHinge = new SquaredHinge(tf, Reduction.NONE);
+ *    Operand&lt;TFloat32&gt; result = squaredHinge.call(labels, predictions);
+ *    // produces [1.46f, 2.26f]
+ * </pre>
+ */
+public class SquaredHinge extends Loss {
+
+  /**
+   * Creates a Squared Hinge Loss using {@link Class#getSimpleName()} as the loss name and a Loss
+   * Reduction of {@link Loss#REDUCTION_DEFAULT}
+   *
+   * @param tf the TensorFlow Ops
+   */
+  public SquaredHinge(Ops tf) {
+    super(tf);
+  }
+
+  /**
+   * Creates a Squared Hinge Loss using {@link Class#getSimpleName()} as the loss name
+   *
+   * @param tf the TensorFlow Ops
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public SquaredHinge(Ops tf, Reduction reduction) {
+    super(tf, null, reduction);
+  }
+
+  /**
+   * Creates a Squared Hinge
+   *
+   * @param tf the TensorFlow Ops
+   * @param name the name of the loss
+   * @param reduction Type of Reduction to apply to the loss.
+   */
+  public SquaredHinge(Ops tf, String name, Reduction reduction) {
+    super(tf, name, reduction);
+  }
+
+  /**
+   * Generates an Operand that calculates the loss.
+   *
+   * <p>If run in Graph mode, the computation will throw {@link
+   * org.tensorflow.exceptions.TFInvalidArgumentException} if the label values are not in the set
+   * [-1., 0., 1.]. In Eager Mode, this call will throw {@link IllegalArgumentException}, if the
+   * label values are not in the set [-1., 0., 1.].
+   *
+   * @param labels the truth values or labels, must be either -1, 0, or 1. Values are expected to be
+   *     -1 or 1. If binary (0 or 1) labels are provided they will be converted  to -1 or 1.
+   * @param predictions the predictions, values must be in the range [0. to 1.] inclusive.
+   * @param sampleWeights Optional SampleWeights acts as a coefficient for the loss. If a scalar is
+   *     provided, then the loss is simply scaled by the given value. If SampleWeights is a tensor
+   *     of size [batch_size], then the total loss for each sample of the batch is rescaled by the
+   *     corresponding element in the SampleWeights vector. If the shape of SampleWeights is
+   *     [batch_size, d0, .. dN-1] (or can be broadcast to this shape), then each loss element of
+   *     predictions is scaled by the corresponding value of SampleWeights. (Note on dN-1: all loss
+   *     functions reduce by 1 dimension, usually axis=-1.)
+   * @param <T> The data type of the predictions, sampleWeights and loss.
+   * @param <U> The data type of the labels.
+   * @return the loss
+   * @throws IllegalArgumentException if the predictions are outside the range [0.-1.].
+   */
+  @Override
+  public <T extends TNumber, U extends TNumber> Operand<T> call(
+      Operand<U> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+    @SuppressWarnings("unchecked")
+    Operand<T> tLabels = predictions.asOutput().dataType() == labels.asOutput().dataType() ?
+            (Operand<T>)labels :
+            cast(tf,  labels, predictions.asOutput().dataType());
+    tLabels = LossesHelper.valueCheck(
+            getTF(),
+            "labels value check [-1, 0, 1]",
+            tLabels,
+            cast(getTF(), getTF().constant(new int[] { -1, 0, 1}),
+                    predictions.asOutput().dataType()));
+    Operand<T> losses = Losses.squaredHinge(getTF(), tLabels, predictions);
+    return LossesHelper.computeWeightedLoss(getTF(), losses, getReduction(), sampleWeights);
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/impl/LossTuple.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/impl/LossTuple.java
new file mode 100644
index 00000000000..2104937a979
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/impl/LossTuple.java
@@ -0,0 +1,67 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses.impl;
+
+import org.tensorflow.Operand;
+import org.tensorflow.types.family.TNumber;
+
+/**
+ * A helper class for loss methods to return  labels, target, and sampleWeights
+ *
+ * @param <T> the data type of the LossTuple entries.
+ */
+public class LossTuple<T extends TNumber> {
+  private final Operand<T> labels;
+  private final Operand<T> target;
+  private final Operand<T> sampleWeights;
+
+  /**
+   * Creates a LossTuple of Operands for labels, target, and sampleWeights
+   *
+   * @param labels the labels
+   * @param target the losses or target
+   */
+  public LossTuple(Operand<T> labels, Operand<T> target) {
+    this(labels, target, null);
+  }
+
+  /**
+   * Creates a LossTuple of Operands for labels, target, and sampleWeights
+   *
+   * @param labels the labels
+   * @param target the losses or target
+   * @param sampleWeights the sample weights
+   */
+  public LossTuple(Operand<T> labels, Operand<T> target, Operand<T> sampleWeights) {
+    this.labels = labels;
+    this.target = target;
+    this.sampleWeights = sampleWeights;
+  }
+
+  /** @return the labels */
+  public Operand<T> getLabels() {
+    return labels;
+  }
+
+  /** @return the target */
+  public Operand<T> getTarget() {
+    return target;
+  }
+
+  /** @return the sampleWeights */
+  public Operand<T> getSampleWeights() {
+    return sampleWeights;
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/impl/LossesHelper.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/impl/LossesHelper.java
new file mode 100644
index 00000000000..463296a1f50
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/losses/impl/LossesHelper.java
@@ -0,0 +1,417 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses.impl;
+
+import org.tensorflow.DataType;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.losses.Reduction;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.op.core.AssertThat;
+import org.tensorflow.op.core.ReduceSum;
+import org.tensorflow.op.core.SetDiff1d;
+import org.tensorflow.op.core.Squeeze;
+import org.tensorflow.types.TBool;
+import org.tensorflow.types.TInt32;
+import org.tensorflow.types.family.TNumber;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.tensorflow.framework.utils.CastHelper.cast;
+
+/**
+ * These are helper methods for Losses and Metrics and will be module private when Java modularity is applied to
+ * TensorFlow Java. These methods should not be used outside of the losses and metrics packages.
+ */
+public class LossesHelper {
+
+  /**
+   * Squeeze or expand last dimension if needed with a sampleWeights of one.
+   *
+   * <ol type="1">
+   *   <li>Squeezes last dim of <code>predictions</code> or <code>labels</code> if their rank
+   *       differs by 1 (using {@link #removeSqueezableDimensions}).
+   *   <li>Squeezes or expands last dim of <code>sampleWeight</code> if its rank differs by 1 from
+   *       the new rank of <code>predictions</code>. If <code>sampleWeight</code> is scalar, it is
+   *       kept scalar./li>
+   * </ol>
+   *
+   * @param tf the TensorFlow Ops
+   * @param predictions Predicted values, a <code>Operand</code> of arbitrary dimensions.
+   * @param labels Optional label <code>Operand</code> whose dimensions match <code>prediction
+   *     </code>.
+   * @return LossTuple of <code>prediction</code>, <code>label</code>,<code>sampleWeight</code> will
+   *     be null. Each of them possibly has the last dimension squeezed, <code>sampleWeight</code>
+   *     could be extended by one dimension. If <code>sampleWeight</code> is null, (prediction,
+   *     label) is returned.
+   */
+  public static <T extends TNumber> LossTuple<T> squeezeOrExpandDimensions(
+      Ops tf, Operand<T> labels, Operand<T> predictions) {
+    return squeezeOrExpandDimensions(tf, labels, predictions, null);
+  }
+
+  /**
+   * Squeeze or expand last dimension if needed.
+   *
+   * <ol type="1">
+   *   <li>Squeezes last dim of <code>predictions</code> or <code>labels</code> if their rank do not
+   *       differ by 1.
+   *   <li>Squeezes or expands last dim of <code>sampleWeight</code> if its rank differs by 1 from
+   *       the new rank of <code>predictions</code>. If <code>sampleWeight</code> is scalar, it is
+   *       kept scalar.
+   * </ol>
+   *
+   * @param tf the TensorFlow Ops
+   * @param predictions Predicted values, a <code>Operand</code> of arbitrary dimensions.
+   * @param labels Optional label <code>Operand</code> whose dimensions match <code>prediction
+   *     </code>.
+   * @param sampleWeights Optional sample weight(s) <code>Operand</code> whose dimensions match<code>
+   *     prediction</code>.
+   * @return LossTuple of <code>prediction<s/code>, <code>labels</code> and <code>sampleWeight</code>.
+   *     Each of them possibly has the last dimension squeezed, <code>sampleWeight</code> could be
+   *     extended by one dimension. If <code>sampleWeight</code> is null, only the possibly shape modified <code>predictions</code> and <code>labels</code> are
+   *     returned.
+   */
+  public static <T extends TNumber> LossTuple<T> squeezeOrExpandDimensions(
+      Ops tf, Operand<T> labels, Operand<T> predictions, Operand<T> sampleWeights) {
+
+    Shape predictionsShape = predictions.asOutput().shape();
+    long predictionsRank = predictionsShape.numDimensions();
+
+    // Default case when no modifications are made.
+    LossTuple<T> lossTuple = new LossTuple<>(labels, predictions, sampleWeights);
+    if (labels != null) {
+      Shape labelsShape = labels.asOutput().shape();
+      long labelsRank = labelsShape.numDimensions();
+      if (labelsRank != Shape.UNKNOWN_SIZE && predictionsRank != Shape.UNKNOWN_SIZE) {
+        // Use static rank for 'label' and 'prediction'.
+        if (predictionsRank - labelsRank != 1 || predictionsShape.size(-1) == 1) {
+          lossTuple = removeSqueezableDimensions(tf, labels, predictions);
+        }
+      } else { // use dynamic rank
+        lossTuple = removeSqueezableDimensions(tf, labels, predictions);
+      }
+    }
+    if (sampleWeights == null) { // nothing more to do.
+      return lossTuple;
+    }
+    Shape weightsShape = sampleWeights.asOutput().shape();
+    long weightsRank = weightsShape.numDimensions();
+    if (weightsRank == 0) { // scalar
+      return new LossTuple<>(lossTuple.getLabels(), lossTuple.getTarget(), sampleWeights);
+    }
+
+    if (predictionsRank != Shape.UNKNOWN_SIZE && weightsRank != Shape.UNKNOWN_SIZE) {
+
+      if (weightsRank - predictionsRank == 1) {
+        sampleWeights = tf.squeeze(sampleWeights);
+      } else if (predictionsRank - weightsRank == 1) {
+        sampleWeights = tf.expandDims(sampleWeights, tf.constant(-1L));
+      }
+      return new LossTuple<>(lossTuple.getLabels(), lossTuple.getTarget(), sampleWeights);
+    }
+    // Use dynamic rank.
+    Operand<TInt32> weightsRankTensor = tf.rank(sampleWeights);
+    Operand<TInt32> rankDiff = tf.math.sub(weightsRankTensor, tf.rank(predictions));
+    sampleWeights =
+        tf.select(
+            tf.math.equal(weightsRankTensor, tf.constant(0)),
+            sampleWeights,
+            maybeAdjustWeights(tf, sampleWeights, rankDiff));
+    return new LossTuple<>(lossTuple.getLabels(), lossTuple.getTarget(), sampleWeights);
+  }
+
+  /**
+   * Squeeze or expand the sampleWeight based on the rank difference
+   *
+   * <p>If the rank difference is +1, squeeze the last dimension of sampleWeight, If the rank
+   * difference is -1, expand the last dimension of sampleWeight. Otherwise, leave the shape of
+   * sampleWeight as is.
+   *
+   * @param tf the TensorFlow Ops
+   * @param sampleWeight the sample weights
+   * @param rankDiff the difference in rank
+   * @param <T> the data type for the Operands.
+   * @return the adjusted sampleWeight
+   */
+  private static <T extends TNumber> Operand<T> maybeAdjustWeights(
+      Ops tf, Operand<T> sampleWeight, Operand<TInt32> rankDiff) {
+    return tf.select(
+        tf.math.equal(rankDiff, tf.constant(1)),
+        tf.squeeze(sampleWeight, Squeeze.axis(Collections.singletonList(-1L))),
+        maybeExpandWeights(tf, sampleWeight, rankDiff));
+  }
+
+  /**
+   * Expand the last dimension of sampleWeight. if the rank difference is -1.
+   *
+   * @param tf the TensorFlow Ops
+   * @param sampleWeight the sample weights
+   * @param rankDiff the difference in rank
+   * @param <T> the data type for the Operands.
+   * @return the adjusted sampleWeight
+   */
+  private static <T extends TNumber> Operand<T> maybeExpandWeights(
+      Ops tf, Operand<T> sampleWeight, Operand<TInt32> rankDiff) {
+    return tf.select(
+        tf.math.equal(rankDiff, tf.constant(-1)),
+        tf.expandDims(sampleWeight, tf.constant(-1)),
+        sampleWeight);
+  }
+
+  /**
+   * Squeeze last dim if ranks differ from expected by exactly 1.
+   *
+   * @param tf the TensorFlowOps
+   * @param labels Label values, a <code>Tensor</code> whose dimensions match <code>predictions
+   *     </code>.
+   * @param predictions Predicted values, a <code>Tensor</code> of arbitrary dimensions.
+   * @return <code>labels</code> and <code>predictions</code>, possibly with last dim squeezed.
+   */
+  public static <T extends TNumber> LossTuple<T> removeSqueezableDimensions(
+      Ops tf, Operand<T> labels, Operand<T> predictions) {
+    return removeSqueezableDimensions(tf, labels, predictions, 0);
+  }
+
+  /**
+   * Squeeze last dim if ranks differ from expected by exactly 1.
+   *
+   * @param tf the TensorFlowOps
+   * @param labels Label values, a <code>Operand</code> whose dimensions match <code>predictions
+   *     </code>.
+   * @param predictions Predicted values, a <code>Tensor</code> of arbitrary dimensions.
+   * @param expectedRankDiff Expected result of <code>rank(predictions) - rank(labels)</code>.
+   * @return <code>labels</code> and <code>predictions</code>, possibly with last dim squeezed.
+   */
+  public static <T extends TNumber> LossTuple<T> removeSqueezableDimensions(
+      Ops tf, Operand<T> labels, Operand<T> predictions, int expectedRankDiff) {
+
+    tf = tf.withSubScope("removeSqueezableDimensions");
+    Shape predictionsShape = predictions.asOutput().shape();
+    int predictionsRank = predictionsShape.numDimensions();
+    Shape labelsShape = labels.asOutput().shape();
+    int labelsRank = labelsShape.numDimensions();
+
+    if (predictionsRank != Shape.UNKNOWN_SIZE || labelsRank != Shape.UNKNOWN_SIZE) {
+      // Use static rank.
+      int rankDiff = predictionsRank - labelsRank;
+      if (rankDiff == expectedRankDiff + 1 && Shape.isCompatible(predictionsShape.size(-1), 1)) {
+        predictions = tf.squeeze(predictions);
+      } else if (rankDiff == expectedRankDiff - 1 && Shape.isCompatible(labelsShape.size(-1), 1)) {
+        labels = tf.squeeze(labels);
+      }
+      return new LossTuple<>(labels, predictions);
+    }
+    // Use dynamic rank.
+
+    // TODO Operand<TInt32> rankDiff = tf.math.sub(tf.rank(predictions), tf.rank(labels));
+    if (predictionsRank == Shape.UNKNOWN_SIZE && Shape.isCompatible(predictionsShape.size(-1), 1)) {
+      /*
+       * TODO, if we ever get a select that does lazy evaluation, but for now do the tf.squeeze
+       * predictions = tf.select( tf.math.equal(tf.constant(expectedRankDiff+1),rankDiff ),
+       * tf.squeeze(predictions, Squeeze.axis(Arrays.asList(-1L))), predictions ); *
+       */
+      predictions = tf.squeeze(predictions, Squeeze.axis(Collections.singletonList(-1L)));
+    }
+    if (labelsRank == Shape.UNKNOWN_SIZE && Shape.isCompatible(labelsShape.size(-1), 1)) {
+      /*
+       * TODO, if we ever get a select that does lazy evaluation labels = tf.select(
+       * tf.math.equal(tf.constant(expectedRankDiff+1),rankDiff ), tf.squeeze(labels,
+       * Squeeze.axis(Arrays.asList(-1L))), predictions ); *
+       */
+      labels = tf.squeeze(labels, Squeeze.axis(Collections.singletonList(-1L)));
+    }
+    return new LossTuple<>(labels, predictions);
+  }
+
+  /**
+   * Computes the weighted loss
+   *
+   * @param tf the TensorFlow Ops
+   * @param loss the unweighted loss
+   * @param reduction the type of reduction
+   * @param sampleWeight the sample weight, if null then this defaults to one.
+   * @param <T> the data type of the loss
+   * @return the weighted loss
+   */
+  public static <T extends TNumber> Operand<T> computeWeightedLoss(
+      Ops tf, Operand<T> loss, Reduction reduction, Operand<T> sampleWeight) {
+    DataType<T> dataType = loss.asOutput().dataType();
+    if (sampleWeight == null) {
+      sampleWeight = cast(tf, tf.constant(1), dataType);
+    }
+    LossTuple<T> result = squeezeOrExpandDimensions(tf, null, loss, sampleWeight);
+    loss = result.getTarget();
+    sampleWeight = result.getSampleWeights();
+
+    Operand<T> weightedLosses = tf.math.mul(loss, cast(tf, sampleWeight, dataType));
+    loss = reduceWeightedLoss(tf, weightedLosses, reduction);
+    return cast(tf, loss, dataType);
+  }
+
+  /**
+   * Reduces the weighted loss based on the reduction type
+   *
+   * @param tf the TensorFlow Ops
+   * @param weightedLoss the weighted loss
+   * @param reduction the type of reduction
+   * @param <T> the data type of the weighted loss
+   * @return the reduced weighted loss
+   */
+  private static <T extends TNumber> Operand<T> reduceWeightedLoss(
+      Ops tf, Operand<T> weightedLoss, Reduction reduction) {
+    Operand<T> loss;
+    if (reduction == Reduction.NONE) {
+      loss = weightedLoss;
+    } else {
+      loss =
+          tf.reduceSum(weightedLoss, allAxes(tf, weightedLoss), ReduceSum.keepDims(Boolean.FALSE));
+      if (reduction == Reduction.AUTO || reduction == Reduction.SUM_OVER_BATCH_SIZE) {
+        loss = safeMean(tf, loss, weightedLoss.asOutput().shape().size());
+      }
+    }
+    return loss;
+  }
+
+  /**
+   * Computes a safe mean of the losses.
+   *
+   * @param tf the TensorFlow Ops
+   * @param losses </code>Operand</code> whose elements contain individual loss measurements.
+   * @param numElements The number of measurable elements in <code>losses</code>.
+   * @param <T> the data type of the losses
+   * @return A scalar representing the mean of <code>losses</code>. If <code>numElements</code> is
+   *     zero, then zero is returned.
+   */
+  public static <T extends TNumber> Operand<T> safeMean(
+      Ops tf, Operand<T> losses, long numElements) {
+    Operand<T> totalLoss = tf.reduceSum(losses, allAxes(tf, losses));
+    return tf.math.divNoNan(
+        totalLoss, cast(tf, tf.constant(numElements), losses.asOutput().dataType()));
+  }
+
+  /**
+   * Gets a Constant integer array representing all the axes of the operand.
+   *
+   * @param tf the TensorFlow Ops
+   * @param op the TensorFlow Ops
+   * @param <T> the type of Operand
+   * @return a Constant that represents all the axes of the operand.
+   */
+  public static <T extends TNumber> Operand<TInt32> allAxes(Ops tf, Operand<T> op) {
+    int rank = op.asOutput().shape().numDimensions();
+    if (rank != Shape.UNKNOWN_SIZE) {
+      int[] axes = new int[rank];
+      for (int i = 0; i < rank; i++) {
+        axes[i] = i;
+      }
+      return tf.constant(axes);
+    } else {
+      return tf.range(tf.constant(0), tf.rank(op), tf.constant(1));
+    }
+  }
+
+  /**
+   * Perform an inclusive range check on the values
+   *
+   * @param tf the TensorFlow Ops
+   * @param prefix A String prefix to include in the error message
+   * @param values the values to check
+   * @param minValue the minimum value
+   * @param maxValue the maximum value
+   * @param <T> the datatype for the values
+   * @return the values possibly with control dependencies if the TensorFlow Ops represents a Graph
+   *     Session
+   * @throws IllegalArgumentException if the TensorFlow Ops represents an Eager Session
+   */
+  public static <T extends TNumber> Operand<T> rangeCheck(
+      Ops tf, String prefix, Operand<T> values, Operand<T> minValue, Operand<T> maxValue) {
+    Operand<TInt32> allDims = allAxes(tf, values);
+    Operand<TBool> cond =
+        tf.math.logicalAnd(
+            tf.reduceAll(tf.math.greaterEqual(values, minValue), allDims),
+            tf.reduceAll(tf.math.lessEqual(values, maxValue), allDims));
+    // Graph and Eager mode need to be handled differently, control dependencies are not allowed in
+    // Eager mode
+    if (tf.scope().env().isGraph()) {
+      AssertThat assertThat =
+          tf.assertThat(
+              cond,
+              Arrays.asList(
+                  tf.constant(prefix),
+                  tf.constant(": values out of range, "),
+                  tf.constant("minimum = "),
+                  minValue,
+                  tf.constant(", maximum = "),
+                  maxValue));
+      Ops ltf =
+          tf.withSubScope("rangeCheck")
+              .withControlDependencies(Collections.singletonList(assertThat));
+      return ltf.identity(values);
+    } else if (!cond.asOutput().data().getBoolean())
+      throw new IllegalArgumentException(String.format("%s : values out of range", prefix));
+    else return values;
+  }
+
+  /**
+   * Checks to see if all the values are in the allowed values set. Running the operand in Graph
+   * mode will throw {@link org.tensorflow.exceptions.TFInvalidArgumentException}, if at least one
+   * value is not in the allowed values set. In Eager mode, this method will throw an {@link
+   * IllegalArgumentException} if at least one value is not in the allowed values set.
+   *
+   * @param tf The TensorFlow Ops
+   * @param prefix A String prefix to include in the error message
+   * @param values the values to check
+   * @param allowedValues the allowed values
+   * @param <T> the data type for values and allowed values
+   * @return the values possibly with control dependencies if the TensorFlow Ops represents a Graph
+   *     Session
+   * @throws IllegalArgumentException if the Session is in Eager mode and at least one value is not
+   *     in the allowed values set
+   */
+  public static <T extends TNumber> Operand<T> valueCheck(
+      Ops tf, String prefix, Operand<T> values, Operand<T> allowedValues) {
+    Operand<T> flatValues =
+        tf.reshape(values, tf.constant(Shape.of(values.asOutput().shape().size())));
+    SetDiff1d<T, TInt32> diff = tf.setDiff1d(flatValues, allowedValues, TInt32.DTYPE);
+    long diffSize = diff.out().asOutput().shape().size();
+
+    if (diffSize != Shape.UNKNOWN_SIZE) {
+      if (diffSize != 0) { // at least 1 value in the diff did not match the allowed values.
+        throw new IllegalArgumentException(String.format("%s : values not in value set,", prefix));
+      } else return values;
+    } else { // use dynamic shape
+      Operand<TBool> cond = tf.math.equal(tf.shape.size(tf.shape(diff.out())), tf.constant(0));
+      // Graph and Eager mode need to be handled differently, control dependencies are not allowed
+      // in Eager mode
+      if (tf.scope().env().isGraph()) {
+        AssertThat assertThat =
+            tf.assertThat(
+                cond,
+                Arrays.asList(
+                    tf.constant(prefix),
+                    tf.constant(": values not in value set, values = "),
+                    values));
+        Ops ltf =
+            tf.withSubScope("valueCheck")
+                .withControlDependencies(Collections.singletonList(assertThat));
+        return ltf.identity(values);
+      } else if (!cond.asOutput().data().getBoolean())
+        throw new IllegalArgumentException(String.format("%s : values not in value set", prefix));
+      else return values;
+    }
+  }
+}
diff --git a/tensorflow-framework/src/main/java/org/tensorflow/framework/utils/CastHelper.java b/tensorflow-framework/src/main/java/org/tensorflow/framework/utils/CastHelper.java
new file mode 100644
index 00000000000..aec75e6078a
--- /dev/null
+++ b/tensorflow-framework/src/main/java/org/tensorflow/framework/utils/CastHelper.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.tensorflow.framework.utils;
+
+import org.tensorflow.DataType;
+import org.tensorflow.Operand;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.family.TType;
+
+/** A helper class for casting an Operand */
+public class CastHelper {
+
+  /**
+   * Casts an operand to the desired type.
+   *
+   * @param tf The TensorFlow Ops
+   * @param value the value to be cast
+   * @param requiredType the required data type
+   * @param <T> the required data type
+   * @param <U> the original data type of the value
+   * @return the value cast to the required data type.
+   */
+  @SuppressWarnings("unchecked")
+  public static <T extends TType, U extends TType> Operand<T> cast(
+          Ops tf, Operand<U> value, DataType<T> requiredType) {
+    return (value.asOutput().dataType() == requiredType)
+        ? (Operand<T>) value
+        : tf.dtypes.cast(value, requiredType);
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/BinaryCrossentropyTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/BinaryCrossentropyTest.java
new file mode 100644
index 00000000000..d2128b80839
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/BinaryCrossentropyTest.java
@@ -0,0 +1,226 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class BinaryCrossentropyTest {
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  /** Test of call method, of class BinaryCrossentropy. */
+  @Test
+  public void testAllCorrectUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        BinaryCrossentropy instance = new BinaryCrossentropy(tf);
+        float[] trueArray = {1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 3)));
+
+        Operand<TFloat32> loss = instance.call(yTrue, yTrue);
+
+        float expected = 0.0f;
+        testSession.evaluate(expected, loss);
+        // Test with logits.
+        float[] logitsArray = {
+          100.0f, -100.0f, -100.0f,
+          -100.0f, 100.0f, -100.0f,
+          -100.0f, -100.0f, 100.0f
+        };
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(3, 3)));
+        instance = new BinaryCrossentropy(tf, true);
+
+        loss = instance.call(yTrue, logits);
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testInvalidPredictionsRange() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Class<? extends Throwable> catchClass =
+            tfMode == TestSession.Mode.EAGER
+                ? IllegalArgumentException.class
+                : org.tensorflow.exceptions.TFInvalidArgumentException.class;
+        assertThrows(
+            catchClass,
+            () -> {
+              Ops tf = testSession.getTF();
+              BinaryCrossentropy instance = new BinaryCrossentropy(tf);
+              float[] trueArray = {1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f};
+              float[] predArray = {2f, 1f, -1f, 0f};
+              Operand<TFloat32> yTrue =
+                  tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 3)));
+              Operand<TFloat32> yPred =
+                  tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 2)));
+
+              Operand<TFloat32> loss = instance.call(yTrue, yPred);
+              testSession.run(loss);
+            });
+      }
+  }
+
+  /** Test of call method, of class BinaryCrossentropy. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        BinaryCrossentropy instance = new BinaryCrossentropy(tf);
+        float[] trueArray = {1f, 0f, 1f, 0f};
+        float[] predArray = {1f, 1f, 1f, 0f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 2)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 2)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 3.83331f;
+        testSession.evaluate(expected, loss);
+
+        // Test with logits.
+        float[] trueArray1 = {1f, 0f, 1f, 0f, 1f, 1f};
+        float[] logitsArray = {
+          100.0f, -100.0f, 100.0f,
+          100.0f, 100.0f, -100.0f
+        };
+        Operand<TFloat32> yTrue1 = tf.reshape(tf.constant(trueArray1), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(2, 3)));
+        instance = new BinaryCrossentropy(tf, true);
+        loss = instance.call(yTrue1, logits);
+        expected = 33.33333f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class BinaryCrossentropy. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        BinaryCrossentropy instance = new BinaryCrossentropy(tf);
+        float[] trueArray = {1f, 0f, 1f, 0f};
+        float[] predArray = {1f, 1f, 1f, 0f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 2)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 2)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 8.816612f;
+        testSession.evaluate(expected, loss);
+
+        // Test with logits.
+        float[] trueArray1 = {1f, 0f, 1f, 0f, 1f, 1f};
+        float[] logitsArray = {
+          100.0f, -100.0f, 100.0f,
+          100.0f, 100.0f, -100.0f
+        };
+        Operand<TFloat32> yTrue1 = tf.reshape(tf.constant(trueArray1), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(2, 3)));
+        instance = new BinaryCrossentropy(tf, true);
+        loss = instance.call(yTrue1, logits, sampleWeight);
+        expected = 76.66667f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        BinaryCrossentropy instance = new BinaryCrossentropy(tf);
+        float[] trueArray = {1f, 0f, 1f, 0f};
+        float[] predArray = {1f, 1f, 1f, 0f};
+        float[] sampleWeightArray = {1.2f, 3.4f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 2)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 2)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleWeightArray), tf.constant(Shape.of(2, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 4.59997f;
+        testSession.evaluate(expected, loss);
+
+        // Test with logits.
+        float[] trueArray1 = {1f, 0f, 1f, 0f, 1f, 1f};
+        float[] logitsArray = {
+          100.0f, -100.0f, 100.0f,
+          100.0f, 100.0f, -100.0f
+        };
+        float[] sampleWeightArray1 = {4f, 3f};
+        Operand<TFloat32> yTrue1 = tf.reshape(tf.constant(trueArray1), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight1 = tf.constant(sampleWeightArray1);
+        instance = new BinaryCrossentropy(tf, true);
+        loss = instance.call(yTrue1, logits, sampleWeight1);
+        expected = 100f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testNoReduction() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+
+        // Test with logits.
+        float[] trueArray = {1f, 0f, 1f, 0f, 1f, 1f};
+        float[] logitsArray = {
+          100.0f, -100.0f, 100.0f,
+          100.0f, 100.0f, -100.0f
+        };
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(2, 3)));
+        BinaryCrossentropy instance =
+            new BinaryCrossentropy(
+                tf, true, BinaryCrossentropy.LABEL_SMOOTHING_DEFAULT, Reduction.NONE);
+        Operand<TFloat32> loss = instance.call(yTrue, logits);
+        Float[] expected = {0.f, 66.666664f};
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testLabelSmoothing() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        float labelSmoothing = 0.1f;
+        float[] trueArray = {1f, 0f, 1f};
+        float[] logitsArray = {100.0f, -100.0f, -100.0f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(1, 3)));
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(1, 3)));
+
+        BinaryCrossentropy instance = new BinaryCrossentropy(tf, true, labelSmoothing);
+        Operand<TFloat32> loss = instance.call(yTrue, logits);
+        float expected = (100.0f + 50.0f * labelSmoothing) / 3.0f;
+        testSession.evaluate(expected, loss);
+      } catch (Exception expected) {
+
+      }
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/CategoricalCrossentropyTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/CategoricalCrossentropyTest.java
new file mode 100644
index 00000000000..13b287de3cd
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/CategoricalCrossentropyTest.java
@@ -0,0 +1,263 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+import org.tensorflow.types.TInt32;
+import org.tensorflow.types.TInt64;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class CategoricalCrossentropyTest {
+
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  /** Test of call method, of class CategoricalCrossentropy. */
+  @Test
+  public void testAllCorrectUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+
+        long[] trueArray = {
+          1L, 0L, 0L,
+          0L, 1L, 0L,
+          0L, 0L, 1L
+        };
+        float[] predArray = {
+          1.F, 0.F, 0.F,
+          0.F, 1.F, 0.F,
+          0.F, 0.F, 1.F
+        };
+        Operand<TInt64> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(3, 3)));
+        CategoricalCrossentropy instance = new CategoricalCrossentropy(tf);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 0F;
+        testSession.evaluate(expected, loss);
+
+        // Test with logits.
+        float[] logitsArray = {
+          10.F, 0.F, 0.F,
+          0.F, 10.F, 0.F,
+          0.F, 0.F, 10.F
+        };
+        yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 3)));
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(3, 3)));
+        instance = new CategoricalCrossentropy(tf, true);
+        loss = instance.call(yTrue, logits);
+        testSession.setEpsilon(1e-3F);
+        testSession.evaluate(0.0F, loss);
+      }
+  }
+
+  @Test
+  public void testInvalidPredictionsRange() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Class<? extends Throwable> catchClass =
+            tfMode == TestSession.Mode.EAGER
+                ? IllegalArgumentException.class
+                : org.tensorflow.exceptions.TFInvalidArgumentException.class;
+        assertThrows(
+            catchClass,
+            () -> {
+              Ops tf = testSession.getTF();
+              CategoricalCrossentropy instance = new CategoricalCrossentropy(tf);
+              float[] trueArray = {
+                1L, 0L, 0L,
+                0L, 1L, 0L,
+                0L, 0L, 1L
+              };
+              float[] predArray = {
+                -1.F, 0.F, 0.F,
+                0.F, 1.F, 0.F,
+                0.F, 0.F, 1.F
+              };
+              Operand<TFloat32> yTrue =
+                  tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 3)));
+              Operand<TFloat32> yPred =
+                  tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 2)));
+
+              Operand<TFloat32> loss = instance.call(yTrue, yPred);
+              testSession.run(loss);
+            });
+      }
+  }
+
+  /** Test of call method, of class CategoricalCrossentropy. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        CategoricalCrossentropy instance = new CategoricalCrossentropy(tf);
+        int[] trueArray = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+        float[] predArray = {
+          .9F, .05F, .05F,
+          .5F, .89F, .6F,
+          .05F, .01F, .94F
+        };
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(3, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 0.32396814F;
+        testSession.evaluate(expected, loss);
+
+        // Test with logits.
+        float[] logitsArray = {
+          8.F, 1.F, 1.F,
+          0.F, 9.F, 1.F,
+          2.F, 3.F, 5.F
+        };
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(3, 3)));
+        instance = new CategoricalCrossentropy(tf, true);
+        loss = instance.call(yTrue, logits);
+        expected = 0.0573755F;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class CategoricalCrossentropy. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+
+        int[] trueArray = {
+          1, 0, 0,
+          0, 1, 0,
+          0, 0, 1
+        };
+        float[] predArray = {
+          .9F, .05F, .05F,
+          .5F, .89F, .6F,
+          .05F, .01F, .94F
+        };
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(3, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3F);
+
+        CategoricalCrossentropy instance = new CategoricalCrossentropy(tf);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = .7451267F;
+        testSession.evaluate(expected, loss);
+
+        // Test with logits.
+        float[] logitsArray = {
+          8.F, 1.F, 1.F,
+          0.F, 9.F, 1.F,
+          2.F, 3.F, 5.F
+        };
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(3, 3)));
+        instance = new CategoricalCrossentropy(tf, true);
+        loss = instance.call(yTrue, logits, sampleWeight);
+        expected = 0.13196386F;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSsampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        CategoricalCrossentropy instance = new CategoricalCrossentropy(tf);
+        float[] sampeWeightArray = {1.2F, 3.4F, 5.6F};
+        int[] trueArray = {
+          1, 0, 0,
+          0, 1, 0,
+          0, 0, 1
+        };
+        float[] predArray = {
+          .9F, .05F, .05F,
+          .5F, .89F, .6F,
+          .05F, .01F, .94F
+        };
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(3, 3)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampeWeightArray), tf.constant(Shape.of(3, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 1.0696F;
+        testSession.evaluate(expected, loss);
+
+        // Test with logits.
+        float[] logitsArray = {
+          8.F, 1.F, 1.F,
+          0.F, 9.F, 1.F,
+          2.F, 3.F, 5.F
+        };
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(3, 3)));
+        instance = new CategoricalCrossentropy(tf, true);
+        loss = instance.call(yTrue, logits, sampleWeight);
+        expected = 0.31829F;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testNoReduction() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+
+        // Test with logits.
+        int[] trueArray = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+        float[] logitsArray = {
+          8.F, 1.F, 1.F,
+          0.F, 9.F, 1.F,
+          2.F, 3.F, 5.F
+        };
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 3)));
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(3, 3)));
+        CategoricalCrossentropy instance =
+            new CategoricalCrossentropy(tf, true, 0.0F, Reduction.NONE);
+        Operand<TFloat32> loss = instance.call(yTrue, logits);
+        Float[] expected = {0.001822F, 0.000459F, 0.169846F};
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testLabelSmoothing() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        float labelSmoothing = 0.1F;
+        int[] trueArray = {1, 0, 0};
+        float[] logitsArray = {100.0F, -100.0F, -100.0F};
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(1, 3)));
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(1, 3)));
+
+        CategoricalCrossentropy instance = new CategoricalCrossentropy(tf, true, labelSmoothing);
+        Operand<TFloat32> loss = instance.call(yTrue, logits);
+        float expected = 400.0F * labelSmoothing / 3.0F;
+        testSession.evaluate(expected, loss);
+      }
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/CategoricalHingeTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/CategoricalHingeTest.java
new file mode 100644
index 00000000000..b0d0442b3c7
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/CategoricalHingeTest.java
@@ -0,0 +1,138 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+import org.tensorflow.types.TInt32;
+
+public class CategoricalHingeTest {
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  /** Test of call method, of class CategoricalHinge. */
+  @Test
+  public void testReductionNone() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        CategoricalHinge instance = new CategoricalHinge(tf, Reduction.NONE);
+        int[] trueArray = {1, 9, 2, -5};
+        float[] predArray = {4f, 8f, 12f, 8f};
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 2)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 2)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        Float[] expected = {0.0f, 65.0f};
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class CategoricalHinge. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        CategoricalHinge instance = new CategoricalHinge(tf);
+        int[] trueArray = {1, 9, 2, -5};
+        float[] predArray = {4f, 8f, 12f, 8f};
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 2)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 2)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 32.5f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class CategoricalHinge. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        CategoricalHinge instance = new CategoricalHinge(tf);
+        int[] trueArray = {1, 9, 2, -5, -2, 6};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 83.95f;
+        testSession.evaluate(expected, loss);
+
+        Operand<TFloat32> loss2 = instance.call(yTrue, yPred, sampleWeight);
+        testSession.evaluate(loss, loss2);
+      }
+  }
+
+  @Test
+  public void testSampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        CategoricalHinge instance = new CategoricalHinge(tf);
+        int[] trueArray = {1, 9, 2, -5, -2, 6};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] weightsNp = {1.2f, 3.4f};
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(weightsNp), tf.constant(Shape.of(2, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 124.1f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testZeroWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        CategoricalHinge instance = new CategoricalHinge(tf);
+        int[] trueArray = {1, 9, 2, -5, -2, 6};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(0f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testTimestepWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        CategoricalHinge instance = new CategoricalHinge(tf);
+        int[] trueArray = {1, 9, 2, -5, -2, 6};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] weightsNp = {3, 6, 5, 0, 4, 2};
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> yPred =
+            tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(weightsNp), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 4.0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/CosineSimilarityTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/CosineSimilarityTest.java
new file mode 100644
index 00000000000..8350d1403ed
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/CosineSimilarityTest.java
@@ -0,0 +1,185 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+
+public class CosineSimilarityTest {
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  /** Test of call method, of class CosineSimilarity. */
+  @Test
+  public void testReductionNone() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        CosineSimilarity instance = new CosineSimilarity(tf, Reduction.NONE);
+        Shape shape = Shape.of(2, 3);
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(shape));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(shape));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        Float[] expected = {-0.720488f, 0.3460499f};
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class CosineSimilarity. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] expectedLoss = {0.720488f, -0.3460499f};
+        CosineSimilarity instance = new CosineSimilarity(tf);
+        Shape shape = Shape.of(2, 3);
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(shape));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(shape));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = -mean(expectedLoss);
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class CosineSimilarity. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] expectedLoss = {0.720488f, -0.3460499f};
+        CosineSimilarity instance = new CosineSimilarity(tf);
+        Shape shape = Shape.of(2, 3);
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(shape));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(shape));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = -mean(mul(expectedLoss, 2.3f));
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] expectedLoss = {0.720488f, -0.3460499f};
+        CosineSimilarity instance = new CosineSimilarity(tf);
+        float[] weightsArray = {1.2f, 3.4f};
+        Shape shape = Shape.of(2, 3);
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(shape));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(shape));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(weightsArray), tf.constant(Shape.of(2, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = -mean(mul(expectedLoss, weightsArray));
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testZeroWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        CosineSimilarity instance = new CosineSimilarity(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Shape shape = Shape.of(2, 3);
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(shape));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(shape));
+        Operand<TFloat32> sampleWeight = tf.constant(0f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testTimestepWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        CosineSimilarity instance = new CosineSimilarity(tf);
+        Shape shape = Shape.of(2, 3, 1);
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(shape));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(shape));
+        float[] weightsArray = {3, 6, 5, 0, 4, 2};
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(weightsArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = -2.0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testAxis() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] expectedLoss = {0.720488f, -0.3460499f};
+        CosineSimilarity instance = new CosineSimilarity(tf, 1);
+        Shape shape = Shape.of(2, 3);
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(shape));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(shape));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = -mean(expectedLoss);
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  private float mean(float[] v) {
+    float sum = 0;
+    for (float value : v) {
+      sum += value;
+    }
+    return sum / v.length;
+  }
+
+  private float[] mul(float[] v, float scalar) {
+    float[] result = new float[v.length];
+    for (int i = 0; i < v.length; i++) {
+      result[i] = v[i] * scalar;
+    }
+    return result;
+  }
+
+  private float[] mul(float[] v, float[] b) {
+    float[] result = new float[v.length];
+    for (int i = 0; i < v.length; i++) {
+      result[i] = v[i] * b[i];
+    }
+    return result;
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/HingeTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/HingeTest.java
new file mode 100644
index 00000000000..4770511207e
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/HingeTest.java
@@ -0,0 +1,149 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class HingeTest {
+
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  /** Test of call method, of class Hinge. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        Hinge instance = new Hinge(tf);
+        float[] trueArray = {0f, 1f, 0f, 1f, 0f, 0f, 1f, 1f};
+        float[] predArray = {-0.3f, 0.2f, -0.1f, 1.6f, -0.25f, -1.f, 0.5f, 0.6f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 0.50625f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testInvalidLabelValue() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Class<? extends Throwable> catchClass =
+            tfMode == TestSession.Mode.EAGER
+                ? IllegalArgumentException.class
+                : org.tensorflow.exceptions.TFInvalidArgumentException.class;
+        assertThrows(
+            catchClass,
+            () -> {
+              Ops tf = testSession.getTF();
+              Hinge instance = new Hinge(tf);
+              float[] trueArray = {2f, 1f, 0f, 1f, 0f, 0f, 1f, 1f};
+              float[] predArray = {-0.3f, 0.2f, -0.1f, 1.6f, -0.25f, -1.f, 0.5f, 0.6f};
+              Operand<TFloat32> yTrue =
+                  tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 4)));
+              Operand<TFloat32> yPred =
+                  tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 4)));
+              Operand<TFloat32> loss = instance.call(yTrue, yPred);
+              testSession.run(loss);
+            });
+      }
+  }
+
+  /** Test of call method, of class Hinge. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        Hinge instance = new Hinge(tf);
+        float[] trueArray = {0f, 1f, 0f, 1f, 0f, 0f, 1f, 1f};
+        float[] predArray = {-0.3f, 0.2f, -0.1f, 1.6f, -0.25f, -1.f, 0.5f, 0.6f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 1.164375f;
+        testSession.evaluate(expected, loss);
+
+        // todo Verify we get the same output when the same input is given
+      }
+  }
+
+  @Test
+  public void testSampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        Hinge instance = new Hinge(tf);
+        float[] sampleArray = {1.2f, 3.4f};
+        float[] trueArray = {0f, 1f, 0f, 1f, 0f, 0f, 1f, 1f};
+        float[] predArray = {-0.3f, 0.2f, -0.1f, 1.6f, -0.25f, -1.f, 0.5f, 0.6f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 1.06125f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testZeroWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        Hinge instance = new Hinge(tf);
+        float[] trueArray = {0f, 1f, 0f, 1f, 0f, 0f, 1f, 1f};
+        float[] predArray = {-0.3f, 0.2f, -0.1f, 1.6f, -0.25f, -1.f, 0.5f, 0.6f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> sampleWeight = tf.constant(0.F);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testTimestepWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        Hinge instance = new Hinge(tf, Reduction.AUTO);
+        float[] sampleArray = {3f, 6f, 5f, 0f, 4f, 2f, 1f, 3f};
+        float[] trueArray = {0f, 1f, 0f, 1f, 0f, 0f, 1f, 1f};
+        float[] predArray = {-0.3f, 0.2f, -0.1f, 1.6f, -0.25f, -1.f, 0.5f, 0.6f};
+        Operand<TFloat32> yTrue =
+            tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 4, 1)));
+        Operand<TFloat32> yPred =
+            tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 4, 1)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+
+        float expected = 2.0125f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/HuberTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/HuberTest.java
new file mode 100644
index 00000000000..d1751f223a1
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/HuberTest.java
@@ -0,0 +1,137 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+
+public class HuberTest {
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  @Test
+  public void testAllCorrect() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        float[] trueArray = {.9f, .2f, .2f, .8f, .4f, .6f};
+
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Huber instance = new Huber(tf);
+        Operand<TFloat32> loss = instance.call(yTrue, yTrue);
+        float expected = 0.0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class Huber. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+
+        float[] trueArray = {.9f, .2f, .2f, .8f, .4f, .6f};
+        float[] predArray = {1.f, 0.f, 1.f, 1.f, 0.f, 0.f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Huber instance = new Huber(tf);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 0.10416666666666669f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class Huber. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        float[] trueArray = {.9f, .2f, .2f, .8f, .4f, .6f};
+        float[] predArray = {1.f, 0.f, 1.f, 1.f, 0.f, 0.f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Huber instance = new Huber(tf);
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0.23958333333333337f;
+        testSession.evaluate(expected, loss);
+
+        // todo Verify we get the same output when the same input is given
+      }
+  }
+
+  @Test
+  public void testSampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        float[] sampleArray = {1.2f, 3.4f};
+        float[] trueArray = {.9f, .2f, .2f, .8f, .4f, .6f};
+        float[] predArray = {1.f, 0.f, 1.f, 1.f, 0.f, 0.f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Huber instance = new Huber(tf);
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0.22766666666666668f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testZeroWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        float[] trueArray = {.9f, .2f, .2f, .8f, .4f, .6f};
+        float[] predArray = {1.f, 0.f, 1.f, 1.f, 0.f, 0.f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Huber instance = new Huber(tf);
+        Operand<TFloat32> sampleWeight = tf.constant(0.F);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testTimestepWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        float[] sampleArray = {3f, 6f, 5f, 0f, 4f, 2f};
+        float[] trueArray = {.9f, .2f, .2f, .8f, .4f, .6f};
+        float[] predArray = {1.f, 0.f, 1.f, 1.f, 0.f, 0.f};
+        Operand<TFloat32> yTrue =
+            tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> yPred =
+            tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3, 1)));
+        Huber instance = new Huber(tf);
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+
+        float expected = .4025f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/KLDivergenceTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/KLDivergenceTest.java
new file mode 100644
index 00000000000..d57b61b18dd
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/KLDivergenceTest.java
@@ -0,0 +1,119 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+
+public class KLDivergenceTest {
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  /** Test of call method, of class KLDivergence. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        KLDivergence instance = new KLDivergence(tf);
+        float[] predArray = {.4f, .9f, .12f, .36f, .3f, .4f};
+        float[] trueArray = {.5f, .8f, .12f, .7f, .43f, .8f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 0.5960738398643668f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class KLDivergence. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        KLDivergence instance = new KLDivergence(tf);
+        float[] predArray = {.4f, .9f, .12f, .36f, .3f, .4f};
+        float[] trueArray = {.5f, .8f, .12f, .7f, .43f, .8f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 1.3709698316880434f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        KLDivergence instance = new KLDivergence(tf);
+        float[] predArray = {.4f, .9f, .12f, .36f, .3f, .4f};
+        float[] trueArray = {.5f, .8f, .12f, .7f, .43f, .8f};
+        float[] sampleArray = {1.2f, 3.4f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 2.0075711736936492f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testZeroWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        KLDivergence instance = new KLDivergence(tf);
+        float[] predArray = {.4f, .9f, .12f, .36f, .3f, .4f};
+        float[] trueArray = {.5f, .8f, .12f, .7f, .43f, .8f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(0.F);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testTimestepWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        KLDivergence instance = new KLDivergence(tf, Reduction.AUTO);
+        float[] predArray = {.4f, .9f, .12f, .36f, .3f, .4f};
+        float[] trueArray = {.5f, .8f, .12f, .7f, .43f, .8f};
+        float[] sampleArray = {3f, 6f, 5f, 0f, 4f, 2f};
+        Operand<TFloat32> yTrue =
+            tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> yPred =
+            tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+
+        float expected = 0.2495994912084345f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/LogCoshTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/LogCoshTest.java
new file mode 100644
index 00000000000..c4347b3fccb
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/LogCoshTest.java
@@ -0,0 +1,119 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+
+public class LogCoshTest {
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  /** Test of call method, of class LogCosh. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        LogCosh instance = new LogCosh(tf);
+        float[] predArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] trueArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 4.829245330860459f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class LogCosh. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        LogCosh instance = new LogCosh(tf);
+        float[] predArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] trueArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 11.107264260979056f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        LogCosh instance = new LogCosh(tf);
+        float[] predArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] trueArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] sampleArray = {1.2f, 3.4f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 12.001114667519486f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testZeroWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        LogCosh instance = new LogCosh(tf);
+        float[] predArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] trueArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(0.F);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testTimestepWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        LogCosh instance = new LogCosh(tf, Reduction.AUTO);
+        float[] predArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] trueArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] sampleArray = {3f, 6f, 5f, 0f, 4f, 2f};
+        Operand<TFloat32> yTrue =
+            tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> yPred =
+            tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+
+        float expected = 11.653484271934046f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/MeanAbsoluteErrorTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/MeanAbsoluteErrorTest.java
new file mode 100644
index 00000000000..3498c6d53aa
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/MeanAbsoluteErrorTest.java
@@ -0,0 +1,194 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+
+public class MeanAbsoluteErrorTest {
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  /** Test of call method, of class MeanAbsoluteError. */
+  @Test
+  public void testAllCorrectUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsoluteError instance = new MeanAbsoluteError(tf);
+        float[] trueArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yTrue);
+        float expected = 0.0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class MeanAbsoluteError. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsoluteError instance = new MeanAbsoluteError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 5.5f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class MeanAbsoluteError. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsoluteError instance = new MeanAbsoluteError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 12.65f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsoluteError instance = new MeanAbsoluteError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] sampleArray = {1.2f, 3.4f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 81.4f / 6f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testZeroWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsoluteError instance = new MeanAbsoluteError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(0.F);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testTimestepWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsoluteError instance = new MeanAbsoluteError(tf, Reduction.AUTO);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] sampleArray = {3f, 6f, 5f, 0f, 4f, 2f};
+        Operand<TFloat32> yTrue =
+            tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> yPred =
+            tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+
+        float expected = 83f / 6f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testInvalidSampleWeight() {
+    for (TestSession.Mode tfMode : tfModes)
+      Assertions.assertThrows(
+          IllegalArgumentException.class,
+          () -> {
+            try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+              Ops tf = testSession.getTF();
+              MeanAbsoluteError instance = new MeanAbsoluteError(tf);
+              float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+              float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+              float[] sampleArray = {3f, 6f, 5f, 0f};
+              Operand<TFloat32> yTrue =
+                  tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3, 1)));
+              Operand<TFloat32> yPred =
+                  tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3, 1)));
+              Operand<TFloat32> sampleWeight =
+                  tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 2)));
+              Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+              float expected = 83f / 6f;
+              testSession.evaluate(expected, loss);
+            }
+          });
+  }
+
+  @Test
+  public void testNoReduction() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsoluteError instance = new MeanAbsoluteError(tf, Reduction.NONE);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        Float[] expected = {10.733333f, 14.566667f};
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSumReduction() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsoluteError instance = new MeanAbsoluteError(tf, Reduction.SUM);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        Float[] expected = {25.29999f};
+        testSession.evaluate(expected, loss);
+      }
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/MeanAbsolutePercentageErrorTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/MeanAbsolutePercentageErrorTest.java
new file mode 100644
index 00000000000..7816a8a288a
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/MeanAbsolutePercentageErrorTest.java
@@ -0,0 +1,167 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+
+public class MeanAbsolutePercentageErrorTest {
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  /** Test of call method, of class MeanAbsolutePercentageError. */
+  @Test
+  public void testAllCorrectUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsolutePercentageError instance = new MeanAbsolutePercentageError(tf);
+        float[] trueArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yTrue);
+        float expected = 0.0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class MeanAbsolutePercentageError. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsolutePercentageError instance = new MeanAbsolutePercentageError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 211.85184f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class MeanAbsolutePercentageError. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsolutePercentageError instance = new MeanAbsolutePercentageError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 487.25922f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsolutePercentageError instance = new MeanAbsolutePercentageError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] sampleArray = {1.2f, 3.4f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 422.8889f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testZeroWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsolutePercentageError instance = new MeanAbsolutePercentageError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(0.F);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testTimestepWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsolutePercentageError instance = new MeanAbsolutePercentageError(tf, Reduction.AUTO);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] sampleArray = {3f, 6f, 5f, 0f, 4f, 2f};
+        Operand<TFloat32> yTrue =
+            tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> yPred =
+            tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 694.4445f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testNoReduction() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsolutePercentageError instance = new MeanAbsolutePercentageError(tf, Reduction.NONE);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        Float[] expected = {621.8518f, 352.66666f};
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSumReduction() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanAbsolutePercentageError instance = new MeanAbsolutePercentageError(tf, Reduction.SUM);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 974.51843f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/MeanSquaredErrorTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/MeanSquaredErrorTest.java
new file mode 100644
index 00000000000..1a971f0492b
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/MeanSquaredErrorTest.java
@@ -0,0 +1,194 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+
+public class MeanSquaredErrorTest {
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  /** Test of call method, of class MeanSquaredError. */
+  @Test
+  public void testAllCorrectUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredError instance = new MeanSquaredError(tf);
+        float[] trueArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yTrue);
+        float expected = 0.0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class MeanSquaredError. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredError instance = new MeanSquaredError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 49.5f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class MeanSquaredError. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredError instance = new MeanSquaredError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 113.85f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredError instance = new MeanSquaredError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] sampleArray = {1.2f, 3.4f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 127.96667f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testZeroWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredError instance = new MeanSquaredError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(0.F);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testTimestepWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredError instance = new MeanSquaredError(tf, Reduction.AUTO);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] sampleArray = {3f, 6f, 5f, 0f, 4f, 2f};
+        Operand<TFloat32> yTrue =
+            tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> yPred =
+            tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+
+        float expected = 97.833336f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testInvalidSampleWeight() {
+    for (TestSession.Mode tfMode : tfModes)
+      Assertions.assertThrows(
+          IllegalArgumentException.class,
+          () -> {
+            try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+              Ops tf = testSession.getTF();
+              MeanSquaredError instance = new MeanSquaredError(tf);
+              float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+              float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+              float[] sampleArray = {3f, 6f, 5f, 0f};
+              Operand<TFloat32> yTrue =
+                  tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3, 1)));
+              Operand<TFloat32> yPred =
+                  tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3, 1)));
+              Operand<TFloat32> sampleWeight =
+                  tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 2)));
+              Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+              float expected = 173.25f;
+              testSession.evaluate(expected, loss);
+            }
+          });
+  }
+
+  @Test
+  public void testNoReduction() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredError instance = new MeanSquaredError(tf, Reduction.NONE);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        Float[] expected = {84.333336f, 143.36665f};
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSumReduction() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredError instance = new MeanSquaredError(tf, Reduction.SUM);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        Float[] expected = {227.69998f};
+        testSession.evaluate(expected, loss);
+      }
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/MeanSquaredLogarithmicErrorTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/MeanSquaredLogarithmicErrorTest.java
new file mode 100644
index 00000000000..558f9c84659
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/MeanSquaredLogarithmicErrorTest.java
@@ -0,0 +1,194 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+
+public class MeanSquaredLogarithmicErrorTest {
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  /** Test of call method, of class MeanSquaredLogarithmicError. */
+  @Test
+  public void testAllCorrectUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredLogarithmicError instance = new MeanSquaredLogarithmicError(tf);
+        float[] trueArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yTrue);
+        float expected = 0.0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class MeanSquaredLogarithmicError. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredLogarithmicError instance = new MeanSquaredLogarithmicError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 1.4370421f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class MeanSquaredLogarithmicError. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredLogarithmicError instance = new MeanSquaredLogarithmicError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 3.3051968f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredLogarithmicError instance = new MeanSquaredLogarithmicError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] sampleArray = {1.2f, 3.4f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 3.7856376f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testZeroWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredLogarithmicError instance = new MeanSquaredLogarithmicError(tf);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(0.F);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testTimestepWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredLogarithmicError instance = new MeanSquaredLogarithmicError(tf, Reduction.AUTO);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] sampleArray = {3f, 6f, 5f, 0f, 4f, 2f};
+        Operand<TFloat32> yTrue =
+            tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> yPred =
+            tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+
+        float expected = 2.647374f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testInvalidSampleWeight() {
+    for (TestSession.Mode tfMode : tfModes)
+      Assertions.assertThrows(
+          IllegalArgumentException.class,
+          () -> {
+            try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+              Ops tf = testSession.getTF();
+              MeanSquaredLogarithmicError instance = new MeanSquaredLogarithmicError(tf);
+              float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+              float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+              float[] sampleArray = {3f, 6f, 5f, 0f};
+              Operand<TFloat32> yTrue =
+                  tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3, 1)));
+              Operand<TFloat32> yPred =
+                  tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3, 1)));
+              Operand<TFloat32> sampleWeight =
+                  tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 2)));
+              Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+              float expected = 83f / 6f;
+              testSession.evaluate(expected, loss);
+            }
+          });
+  }
+
+  @Test
+  public void testNoReduction() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredLogarithmicError instance = new MeanSquaredLogarithmicError(tf, Reduction.NONE);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        Float[] expected = {2.3006392f, 4.3097544f};
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSumReduction() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        MeanSquaredLogarithmicError instance = new MeanSquaredLogarithmicError(tf, Reduction.SUM);
+        float[] trueArray = {1f, 9f, 2f, -5f, -2f, 6f};
+        float[] predArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        Float[] expected = {6.6103935f};
+        testSession.evaluate(expected, loss);
+      }
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/PoissonTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/PoissonTest.java
new file mode 100644
index 00000000000..55c59ca5ac6
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/PoissonTest.java
@@ -0,0 +1,119 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+
+public class PoissonTest {
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  /** Test of call method, of class Poisson. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        Poisson instance = new Poisson(tf);
+        float[] predArray = {1f, 9f, 2f, 5f, 2f, 6f};
+        float[] trueArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = -3.306581945521002f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class Poisson. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        Poisson instance = new Poisson(tf);
+        float[] predArray = {1f, 9f, 2f, 5f, 2f, 6f};
+        float[] trueArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = -7.605138474698304f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        Poisson instance = new Poisson(tf);
+        float[] predArray = {1f, 9f, 2f, 5f, 2f, 6f};
+        float[] trueArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] sampleArray = {1.2f, 3.4f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = -6.147338926788071f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testZeroWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        Poisson instance = new Poisson(tf);
+        float[] predArray = {1f, 9f, 2f, 5f, 2f, 6f};
+        float[] trueArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(0.F);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testTimestepWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        Poisson instance = new Poisson(tf, Reduction.AUTO);
+        float[] predArray = {1f, 9f, 2f, 5f, 2f, 6f};
+        float[] trueArray = {4f, 8f, 12f, 8f, 1f, 3f};
+        float[] sampleArray = {3f, 6f, 5f, 0f, 4f, 2f};
+        Operand<TFloat32> yTrue =
+            tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> yPred =
+            tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 3, 1)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+
+        float expected = -12.263126013890561f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/SparseCategoricalCrossentropyTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/SparseCategoricalCrossentropyTest.java
new file mode 100644
index 00000000000..a6a0ff35c78
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/SparseCategoricalCrossentropyTest.java
@@ -0,0 +1,225 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+import org.tensorflow.types.TInt32;
+import org.tensorflow.types.TInt64;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class SparseCategoricalCrossentropyTest {
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  /** Test of call method, of class SparseCategoricalCrossentropy. */
+  @Test
+  public void testAllCorrectUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        testSession.setEpsilon(1e-3f);
+        Ops tf = testSession.getTF();
+
+        int[] trueArray = {0, 1, 2};
+        float[] predArray = {
+          1.F, 0.F, 0.F,
+          0.F, 1.F, 0.F,
+          0.F, 0.F, 1.F
+        };
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 1)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(3, 3)));
+        SparseCategoricalCrossentropy instance = new SparseCategoricalCrossentropy(tf);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 0.0f;
+        testSession.evaluate(expected, loss);
+
+        // Test with logits.
+        float[] logitsArray = {
+          10.F, 0.F, 0.F,
+          0.F, 10.F, 0.F,
+          0.F, 0.F, 10.F
+        };
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(3, 3)));
+        instance = new SparseCategoricalCrossentropy(tf, true);
+        loss = instance.call(yTrue, logits);
+        testSession.evaluate(0.0f, loss);
+      }
+  }
+
+  @Test
+  public void testInvalidPredictionsRange() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Class<? extends Throwable> catchClass =
+            tfMode == TestSession.Mode.EAGER
+                ? IllegalArgumentException.class
+                : org.tensorflow.exceptions.TFInvalidArgumentException.class;
+        assertThrows(
+            catchClass,
+            () -> {
+              Ops tf = testSession.getTF();
+              SparseCategoricalCrossentropy instance = new SparseCategoricalCrossentropy(tf);
+              int[] trueArray = {0, 1, 2};
+              float[] predArray = {
+                1.9f, .05f, .05f,
+                .5f, .89f, .6f,
+                .05f, .01f, .94f
+              };
+              Operand<TInt32> yTrue =
+                  tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 1)));
+              Operand<TFloat32> yPred =
+                  tf.reshape(tf.constant(predArray), tf.constant(Shape.of(3, 3)));
+              Operand<TFloat32> loss = instance.call(yTrue, yPred);
+              testSession.run(loss);
+            });
+      }
+  }
+
+  /** Test of call method, of class SparseCategoricalCrossentropy. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        SparseCategoricalCrossentropy instance = new SparseCategoricalCrossentropy(tf);
+        int[] trueArray = {0, 1, 2};
+        float[] predArray = {
+          .9f, .05f, .05f,
+          .5f, .89f, .6f,
+          .05f, .01f, .94f
+        };
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 1)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(3, 3)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 0.32396814f;
+        testSession.evaluate(expected, loss);
+
+        // Test with logits.
+        float[] logitsArray = {
+          8.F, 1.F, 1.F,
+          0.F, 9.F, 1.F,
+          2.F, 3.F, 5.F
+        };
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(3, 3)));
+        instance = new SparseCategoricalCrossentropy(tf, true);
+        loss = instance.call(yTrue, logits);
+        expected = 0.05737559f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  /** Test of call method, of class SparseCategoricalCrossentropy. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+
+        int[] trueArray = {0, 1, 2};
+        float[] predArray = {
+          .9f, .05f, .05f,
+          .5f, .89f, .6f,
+          .05f, .01f, .94f
+        };
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 1)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(3, 3)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+
+        SparseCategoricalCrossentropy instance = new SparseCategoricalCrossentropy(tf);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = .7451267f;
+        testSession.evaluate(expected, loss);
+
+        // Test with logits.
+        float[] logitsArray = {
+          8.F, 1.F, 1.F,
+          0.F, 9.F, 1.F,
+          2.F, 3.F, 5.F
+        };
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(3, 3)));
+        instance = new SparseCategoricalCrossentropy(tf, true);
+        loss = instance.call(yTrue, logits, sampleWeight);
+        expected = 0.13196386f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        SparseCategoricalCrossentropy instance = new SparseCategoricalCrossentropy(tf);
+        float[] sampleWeightArray = {1.2f, 3.4f, 5.6f};
+        int[] trueArray = {0, 1, 2};
+        float[] predArray = {
+          .9f, .05f, .05f,
+          .5f, .89f, .6f,
+          .05f, .01f, .94f
+        };
+        Operand<TInt32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 1)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(3, 3)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleWeightArray), tf.constant(Shape.of(3, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 1.0696f;
+        testSession.evaluate(expected, loss);
+
+        // Test with logits.
+        float[] logitsArray = {
+          8.F, 1.F, 1.F,
+          0.F, 9.F, 1.F,
+          2.F, 3.F, 5.F
+        };
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(3, 3)));
+        instance = new SparseCategoricalCrossentropy(tf, true);
+        loss = instance.call(yTrue, logits, sampleWeight);
+        expected = 0.31829f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testNoReduction() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+
+        // Test with logits.
+        long[] trueArray = {0L, 1L, 2L};
+        float[] logitsArray = {
+          8.F, 1.F, 1.F,
+          0.F, 9.F, 1.F,
+          2.F, 3.F, 5.F
+        };
+        Operand<TInt64> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(3, 1)));
+        Operand<TFloat32> logits =
+            tf.reshape(tf.constant(logitsArray), tf.constant(Shape.of(3, 3)));
+        SparseCategoricalCrossentropy instance =
+            new SparseCategoricalCrossentropy(tf, true, Reduction.NONE);
+        Operand<TFloat32> loss = instance.call(yTrue, logits);
+        Float[] expected = {0.001822f, 0.000459f, 0.169846f};
+        testSession.evaluate(expected, loss);
+      }
+  }
+}
diff --git a/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/SquaredHingeTest.java b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/SquaredHingeTest.java
new file mode 100644
index 00000000000..57a012bbe9d
--- /dev/null
+++ b/tensorflow-framework/src/test/java/org/tensorflow/framework/losses/SquaredHingeTest.java
@@ -0,0 +1,146 @@
+/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+=======================================================================*/
+package org.tensorflow.framework.losses;
+
+import org.junit.jupiter.api.Test;
+import org.tensorflow.Operand;
+import org.tensorflow.framework.utils.TestSession;
+import org.tensorflow.ndarray.Shape;
+import org.tensorflow.op.Ops;
+import org.tensorflow.types.TFloat32;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class SquaredHingeTest {
+  private final TestSession.Mode[] tfModes = {TestSession.Mode.EAGER, TestSession.Mode.GRAPH};
+
+  /** Test of call method, of class SquaredHinge. */
+  @Test
+  public void testUnweighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        SquaredHinge instance = new SquaredHinge(tf);
+        float[] trueArray = {0, 1, 0, 1, 0, 0, 1, 1};
+        float[] predArray = {-0.3f, 0.2f, -0.1f, 1.6f, -0.25f, -1.f, 0.5f, 0.6f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred);
+        float expected = 0.364062f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testInvalidLabelValue() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Class<? extends Throwable> catchClass =
+            tfMode == TestSession.Mode.EAGER
+                ? IllegalArgumentException.class
+                : org.tensorflow.exceptions.TFInvalidArgumentException.class;
+        assertThrows(
+            catchClass,
+            () -> {
+              Ops tf = testSession.getTF();
+              SquaredHinge instance = new SquaredHinge(tf);
+              float[] trueArray = {0, 2, 0, 1, 0, 0, 1, 1};
+              float[] predArray = {-0.3f, 0.2f, -0.1f, 1.6f, -0.25f, -1.f, 0.5f, 0.6f};
+              Operand<TFloat32> yTrue =
+                  tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 4)));
+              Operand<TFloat32> yPred =
+                  tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 4)));
+              Operand<TFloat32> loss = instance.call(yTrue, yPred);
+              testSession.run(loss);
+            });
+      }
+  }
+
+  /** Test of call method, of class SquaredHinge. */
+  @Test
+  public void testScalarWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        SquaredHinge instance = new SquaredHinge(tf);
+        float[] trueArray = {0, 1, 0, 1, 0, 0, 1, 1};
+        float[] predArray = {-0.3f, 0.2f, -0.1f, 1.6f, -0.25f, -1.f, 0.5f, 0.6f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> sampleWeight = tf.constant(2.3f);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0.8373437f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testSampleWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        SquaredHinge instance = new SquaredHinge(tf);
+        float[] sampleArray = {1.2f, 3.4f};
+        float[] trueArray = {0, 1, 0, 1, 0, 0, 1, 1};
+        float[] predArray = {-0.3f, 0.2f, -0.1f, 1.6f, -0.25f, -1.f, 0.5f, 0.6f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 1)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0.7043125f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testZeroWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        SquaredHinge instance = new SquaredHinge(tf);
+        float[] trueArray = {0, 1, 0, 1, 0, 0, 1, 1};
+        float[] predArray = {-0.3f, 0.2f, -0.1f, 1.6f, -0.25f, -1.f, 0.5f, 0.6f};
+        Operand<TFloat32> yTrue = tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> yPred = tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> sampleWeight = tf.constant(0.F);
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+        float expected = 0f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+
+  @Test
+  public void testTimestepWeighted() {
+    for (TestSession.Mode tfMode : tfModes)
+      try (TestSession testSession = TestSession.createTestSession(tfMode)) {
+        Ops tf = testSession.getTF();
+        SquaredHinge instance = new SquaredHinge(tf, Reduction.AUTO);
+        float[] trueArray = {0, 1, 0, 1, 0, 0, 1, 1};
+        float[] predArray = {-0.3f, 0.2f, -0.1f, 1.6f, -0.25f, -1.f, 0.5f, 0.6f};
+        Operand<TFloat32> yTrue =
+            tf.reshape(tf.constant(trueArray), tf.constant(Shape.of(2, 4, 1)));
+        Operand<TFloat32> yPred =
+            tf.reshape(tf.constant(predArray), tf.constant(Shape.of(2, 4, 1)));
+        float[] sampleArray = {3f, 6f, 5f, 0f, 4f, 2f, 1f, 3f};
+        Operand<TFloat32> sampleWeight =
+            tf.reshape(tf.constant(sampleArray), tf.constant(Shape.of(2, 4)));
+        Operand<TFloat32> loss = instance.call(yTrue, yPred, sampleWeight);
+
+        float expected = 1.54250000f;
+        testSession.evaluate(expected, loss);
+      }
+  }
+}