test: more gradient optimizer testspull/1257/head
@@ -1,4 +1,4 @@ | |||||
/***************************************************************************** | |||||
/***************************************************************************** | |||||
Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved. | Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved. | ||||
Licensed under the Apache License, Version 2.0 (the "License"); | Licensed under the Apache License, Version 2.0 (the "License"); | ||||
@@ -135,6 +135,23 @@ namespace Tensorflow | |||||
TF_DataType.TF_QINT32 | TF_DataType.TF_QINT32 | ||||
}; | }; | ||||
private static TOut[,] ConvertArray2D<TIn, TOut>(TIn[,] inputArray, Func<TIn, TOut> converter) | |||||
{ | |||||
var rows = inputArray.GetLength(0); | |||||
var cols = inputArray.GetLength(1); | |||||
var outputArray = new TOut[rows, cols]; | |||||
for (var i = 0; i < rows; i++) | |||||
{ | |||||
for (var j = 0; j < cols; j++) | |||||
{ | |||||
outputArray[i, j] = converter(inputArray[i, j]); | |||||
} | |||||
} | |||||
return outputArray; | |||||
} | |||||
/// <summary> | /// <summary> | ||||
/// Create a TensorProto, invoked in graph mode | /// Create a TensorProto, invoked in graph mode | ||||
/// </summary> | /// </summary> | ||||
@@ -157,19 +174,21 @@ namespace Tensorflow | |||||
else if(origin_dtype != dtype) | else if(origin_dtype != dtype) | ||||
{ | { | ||||
var new_system_dtype = dtype.as_system_dtype(); | var new_system_dtype = dtype.as_system_dtype(); | ||||
if (values is long[] long_values) | |||||
{ | |||||
if (dtype == TF_DataType.TF_INT32) | |||||
values = long_values.Select(x => (int)Convert.ChangeType(x, new_system_dtype)).ToArray(); | |||||
} | |||||
else if (values is double[] double_values) | |||||
values = values switch | |||||
{ | { | ||||
if (dtype == TF_DataType.TF_FLOAT) | |||||
values = double_values.Select(x => (float)Convert.ChangeType(x, new_system_dtype)).ToArray(); | |||||
} | |||||
else | |||||
values = Convert.ChangeType(values, new_system_dtype); | |||||
long[] longValues when dtype == TF_DataType.TF_INT32 => longValues.Select(x => (int)x).ToArray(), | |||||
long[] longValues => values, | |||||
float[] floatValues when dtype == TF_DataType.TF_DOUBLE => floatValues.Select(x => (double)x).ToArray(), | |||||
float[] floatValues => values, | |||||
float[,] float2DValues when dtype == TF_DataType.TF_DOUBLE => ConvertArray2D(float2DValues, Convert.ToDouble), | |||||
float[,] float2DValues => values, | |||||
double[] doubleValues when dtype == TF_DataType.TF_FLOAT => doubleValues.Select(x => (float)x).ToArray(), | |||||
double[] doubleValues => values, | |||||
double[,] double2DValues when dtype == TF_DataType.TF_FLOAT => ConvertArray2D(double2DValues, Convert.ToSingle), | |||||
double[,] double2DValues => values, | |||||
_ => Convert.ChangeType(values, new_system_dtype), | |||||
}; | |||||
dtype = values.GetDataType(); | dtype = values.GetDataType(); | ||||
} | } | ||||
@@ -1,5 +1,6 @@ | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||||
using System; | using System; | ||||
using System.Linq; | |||||
using Tensorflow; | using Tensorflow; | ||||
using Tensorflow.NumPy; | using Tensorflow.NumPy; | ||||
using static Tensorflow.Binding; | using static Tensorflow.Binding; | ||||
@@ -67,6 +68,51 @@ namespace TensorFlowNET.UnitTest.Training | |||||
TestBasic<double>(); | TestBasic<double>(); | ||||
} | } | ||||
private void TestMinimizeResourceVariable<T>() where T : struct | |||||
{ | |||||
var dtype = GetTypeForNumericType<T>(); | |||||
// train.GradientDescentOptimizer is V1 only API. | |||||
tf.Graph().as_default(); | |||||
using (var sess = self.cached_session()) | |||||
{ | |||||
var var0 = tf.Variable(new[,] { { 1.0f, 2.0f } }, dtype: dtype); | |||||
var var1 = tf.Variable(new[] { 3.0 }, dtype: dtype); | |||||
var x = tf.constant(new[,] { { 4.0f }, { 5.0f } }, dtype: dtype); | |||||
var pred = math_ops.matmul(var0, x) + var1; | |||||
var loss = pred * pred; | |||||
var sgd_op = tf.train.GradientDescentOptimizer(1.0f).minimize(loss); | |||||
var global_variables = tf.global_variables_initializer(); | |||||
sess.run(global_variables); | |||||
sess.run(new[] { var0, var1 }); | |||||
// Fetch params to validate initial values | |||||
self.assertAllCloseAccordingToType<T>(new[,] { { 1.0, 2.0 } }, self.evaluate<T[,]>(var0)); | |||||
self.assertAllCloseAccordingToType(new[] { 3.0 }, self.evaluate<T[]>(var1)); | |||||
// Run 1 step of sgd | |||||
sgd_op.run(); | |||||
// Validate updated params | |||||
var np_pred = 1.0 * 4.0 + 2.0 * 5.0 + 3.0; | |||||
var np_grad = 2 * np_pred; | |||||
self.assertAllCloseAccordingToType( | |||||
new[,] { { 1.0 - np_grad * 4.0, 2.0 - np_grad * 5.0 } }, | |||||
self.evaluate<T[,]>(var0)); | |||||
self.assertAllCloseAccordingToType( | |||||
new[] { 3.0 - np_grad }, | |||||
self.evaluate<T[]>(var1)); | |||||
} | |||||
} | |||||
[TestMethod] | |||||
public void TestMinimizeResourceVariable() | |||||
{ | |||||
//TODO: add np.half | |||||
TestMinimizeResourceVariable<float>(); | |||||
TestMinimizeResourceVariable<double>(); | |||||
} | |||||
private void TestTensorLearningRate<T>() where T : struct | private void TestTensorLearningRate<T>() where T : struct | ||||
{ | { | ||||
var dtype = GetTypeForNumericType<T>(); | var dtype = GetTypeForNumericType<T>(); | ||||
@@ -115,5 +161,72 @@ namespace TensorFlowNET.UnitTest.Training | |||||
TestTensorLearningRate<float>(); | TestTensorLearningRate<float>(); | ||||
TestTensorLearningRate<double>(); | TestTensorLearningRate<double>(); | ||||
} | } | ||||
public void TestGradWrtRef<T>() where T : struct | |||||
{ | |||||
var dtype = GetTypeForNumericType<T>(); | |||||
var graph = tf.Graph().as_default(); | |||||
using (var sess = self.cached_session()) | |||||
{ | |||||
var opt = tf.train.GradientDescentOptimizer(3.0f); | |||||
var values = new[] { 1.0, 3.0 }; | |||||
var vars_ = values.Select( | |||||
v => tf.Variable(new[] { v }, dtype: dtype) as IVariableV1 | |||||
).ToList(); | |||||
var grads_and_vars = opt.compute_gradients(tf.add(vars_[0], vars_[1]), vars_); | |||||
sess.run(tf.global_variables_initializer()); | |||||
foreach (var (grad, _) in grads_and_vars) | |||||
self.assertAllCloseAccordingToType(new[] { 1.0 }, self.evaluate<T[]>(grad)); | |||||
} | |||||
} | |||||
[TestMethod] | |||||
public void TestGradWrtRef() | |||||
{ | |||||
TestGradWrtRef<float>(); | |||||
TestGradWrtRef<double>(); | |||||
} | |||||
public void TestWithGlobalStep<T>() where T : struct | |||||
{ | |||||
var dtype = GetTypeForNumericType<T>(); | |||||
tf.Graph().as_default(); | |||||
using (var sess = self.cached_session()) | |||||
{ | |||||
var global_step = tf.Variable(0, trainable: false); | |||||
var var0 = tf.Variable(new[] { 1.0, 2.0 }, dtype: dtype); | |||||
var var1 = tf.Variable(new[] { 3.0, 4.0 }, dtype: dtype); | |||||
var grads0 = tf.constant(new[] { 0.1, 0.1 }, dtype: dtype); | |||||
var grads1 = tf.constant(new[] { 0.01, 0.01 }, dtype: dtype); | |||||
var grads_and_vars = new[] { | |||||
Tuple.Create(grads0, var0 as IVariableV1), | |||||
Tuple.Create(grads1, var1 as IVariableV1) | |||||
}; | |||||
var sgd_op = tf.train.GradientDescentOptimizer(3.0f) | |||||
.apply_gradients(grads_and_vars, global_step: global_step); | |||||
sess.run(tf.global_variables_initializer()); | |||||
// Fetch params to validate initial values | |||||
self.assertAllCloseAccordingToType(new[] { 1.0, 2.0 }, self.evaluate<T[]>(var0)); | |||||
self.assertAllCloseAccordingToType(new[] { 3.0, 4.0 }, self.evaluate<T[]>(var1)); | |||||
// Run 1 step of sgd | |||||
sgd_op.run(); | |||||
// Validate updated params and global_step | |||||
self.assertAllCloseAccordingToType(new[] { 1.0 - 3.0 * 0.1, 2.0 - 3.0 * 0.1 }, self.evaluate<T[]>(var0)); | |||||
self.assertAllCloseAccordingToType(new[] { 3.0 - 3.0 * 0.01, 4.0 - 3.0 * 0.01 }, self.evaluate<T[]>(var1)); | |||||
Assert.AreEqual(1, self.evaluate<int>(global_step)); | |||||
} | |||||
} | |||||
[TestMethod] | |||||
public void TestWithGlobalStep() | |||||
{ | |||||
TestWithGlobalStep<float>(); | |||||
TestWithGlobalStep<double>(); | |||||
} | |||||
} | } | ||||
} | } |
@@ -175,8 +175,8 @@ namespace TensorFlowNET.UnitTest | |||||
return 1; | return 1; | ||||
} | } | ||||
var a = (double)x; | |||||
var b = (double)y; | |||||
var a = Convert.ToDouble(x); | |||||
var b = Convert.ToDouble(y); | |||||
double delta = Math.Abs(a - b); | double delta = Math.Abs(a - b); | ||||
if (delta < _epsilon) | if (delta < _epsilon) | ||||
@@ -187,6 +187,19 @@ namespace TensorFlowNET.UnitTest | |||||
} | } | ||||
} | } | ||||
public void assertAllCloseAccordingToType<T>( | |||||
double[,] expected, | |||||
T[,] given, | |||||
double eps = 1e-6, | |||||
float float_eps = 1e-6f) | |||||
{ | |||||
Assert.AreEqual(expected.GetLength(0), given.GetLength(0)); | |||||
Assert.AreEqual(expected.GetLength(1), given.GetLength(1)); | |||||
var flattenGiven = given.Cast<T>().ToArray(); | |||||
assertAllCloseAccordingToType(expected, flattenGiven, eps, float_eps); | |||||
} | |||||
public void assertAllCloseAccordingToType<T>( | public void assertAllCloseAccordingToType<T>( | ||||
ICollection expected, | ICollection expected, | ||||
ICollection<T> given, | ICollection<T> given, | ||||
@@ -267,21 +280,35 @@ namespace TensorFlowNET.UnitTest | |||||
{ | { | ||||
var sess = tf.get_default_session(); | var sess = tf.get_default_session(); | ||||
var ndarray = tensor.eval(sess); | var ndarray = tensor.eval(sess); | ||||
if (typeof(T) == typeof(double) | |||||
|| typeof(T) == typeof(float) | |||||
|| typeof(T) == typeof(int)) | |||||
if (typeof(T) == typeof(int)) | |||||
{ | |||||
int i = ndarray; | |||||
result = i; | |||||
} | |||||
else if (typeof(T) == typeof(float)) | |||||
{ | |||||
float f = ndarray; | |||||
result = f; | |||||
} | |||||
else if (typeof(T) == typeof(double)) | |||||
{ | { | ||||
result = Convert.ChangeType(ndarray, typeof(T)); | |||||
double d = ndarray; | |||||
result = d; | |||||
} | } | ||||
else if (typeof(T) == typeof(double[])) | |||||
else if ( | |||||
typeof(T) == typeof(double[]) | |||||
|| typeof(T) == typeof(double[,])) | |||||
{ | { | ||||
result = ndarray.ToMultiDimArray<double>(); | result = ndarray.ToMultiDimArray<double>(); | ||||
} | } | ||||
else if (typeof(T) == typeof(float[])) | |||||
else if (typeof(T) == typeof(float[]) | |||||
|| typeof(T) == typeof(float[,])) | |||||
{ | { | ||||
result = ndarray.ToMultiDimArray<float>(); | result = ndarray.ToMultiDimArray<float>(); | ||||
} | } | ||||
else if (typeof(T) == typeof(int[])) | |||||
else if (typeof(T) == typeof(int[]) | |||||
|| typeof(T) == typeof(int[,])) | |||||
{ | { | ||||
result = ndarray.ToMultiDimArray<int>(); | result = ndarray.ToMultiDimArray<int>(); | ||||
} | } | ||||