| @@ -94,6 +94,99 @@ namespace TensorFlowNET.UnitTest.gradients_test | |||
| } | |||
| } | |||
| [TestMethod] | |||
| public void testSimpleGradients() | |||
| { | |||
| (T, T) evaluateDerivatives<T>(Func<Tensor, Tensor> f, T xval) where T : unmanaged | |||
| { | |||
| var x = tf.constant(xval); | |||
| var y = f(x); | |||
| var g = tf.gradients(y, x); | |||
| using (var session = tf.Session()) | |||
| { | |||
| var result = session.run(new[] { y, g[0] }); | |||
| return (result[0].GetData<T>()[0], result[1].GetData<T>()[0]); | |||
| } | |||
| } | |||
| void assertFloat32Equal(float expected, float actual, string msg) | |||
| { | |||
| float eps = 1e-6f; | |||
| Assert.IsTrue(Math.Abs(expected - actual) < eps * Math.Max(1.0f, Math.Abs(expected)), $"{msg}: expected {expected} vs actual {actual}"); | |||
| } | |||
| void test(string name, Func<Tensor, Tensor> tfF, Func<double, (double, double)> targetF, double[] values) | |||
| { | |||
| foreach (var x in values) | |||
| { | |||
| var (expectedY, expectedDY) = targetF(x); | |||
| { | |||
| var (actualY, actualDY) = evaluateDerivatives(tfF, x); | |||
| Assert.AreEqual(expectedY, actualY, $"value {name}/float64 at {x}"); | |||
| Assert.AreEqual(expectedDY, actualDY, $"derivative {name}/float64 at {x}"); | |||
| } | |||
| { | |||
| var (actualY, actualDY) = evaluateDerivatives(tfF, (float)x); | |||
| assertFloat32Equal((float)expectedY, actualY, $"value {name}/float32 at {x}"); | |||
| assertFloat32Equal((float)expectedDY, actualDY, $"derivative {name}/float32 at {x}"); | |||
| } | |||
| } | |||
| } | |||
| test("tf.exp", | |||
| x => tf.exp(5 * x), | |||
| x => (Math.Exp(5.0 * x), 5.0 * Math.Exp(5.0 * x)), | |||
| new[] { -1.0, 0.0, 1.0, 1.5 }); | |||
| test("tf.log", | |||
| x => tf.log(x), | |||
| x => (Math.Log(x), 1.0 / x), | |||
| new[] { 0.5, 1.0, 1.5, 2.0 }); | |||
| test("tf.sqrt", | |||
| x => tf.sqrt(x), | |||
| x => (Math.Sqrt(x), 0.5 / Math.Sqrt(x)), | |||
| new[] { 0.5, 1.0, 1.1, 1.5, 2.0 }); | |||
| test("tf.sin", | |||
| x => tf.sin(x), | |||
| x => (Math.Sin(x), Math.Cos(x)), | |||
| new[] { -1.0, 0.0, 1.0, 1.5, 2.0 }); | |||
| test("tf.sinh", | |||
| x => tf.sinh(x), | |||
| x => (Math.Sinh(x), Math.Cosh(x)), | |||
| new[] { -1.0, 0.0, 1.0, 1.5, 2.0 }); | |||
| test("tf.cos", | |||
| x => tf.cos(x), | |||
| x => (Math.Cos(x), -Math.Sin(x)), | |||
| new[] { -1.0, 0.0, 1.0, 1.5, 2.0 }); | |||
| test("tf.cosh", | |||
| x => tf.cosh(x), | |||
| x => (Math.Cosh(x), Math.Sinh(x)), | |||
| new[] { -1.0, 0.0, 1.0, 1.5, 2.0 }); | |||
| test("tf.tanh", | |||
| x => tf.tanh(x), | |||
| x => (Math.Tanh(x), 1.0 - Math.Pow(Math.Tanh(x), 2.0)), | |||
| new[] { -1.0, 0.0, 1.0, 1.5, 2.0 }); | |||
| test("tf.maximum", | |||
| x => tf.maximum(x, tf.constant(0.0, dtype: x.dtype)), | |||
| x => (Math.Max(x, 0.0), (x > 0.0) ? 1.0 : 0.0), | |||
| new[] { -1.0, 1.0 }); | |||
| test("tf.minimum", | |||
| x => tf.minimum(x, tf.constant(0.0, dtype: x.dtype)), | |||
| x => (Math.Min(x, 0.0), (x < 0.0) ? 1.0 : 0.0), | |||
| new[] { -1.0, 1.0 }); | |||
| } | |||
| [TestMethod] | |||
| public void testTanhGradient() | |||
| { | |||