| @@ -29,5 +29,10 @@ namespace Tensorflow.Framework | |||||
| { | { | ||||
| throw new NotFiniteNumberException(); | throw new NotFiniteNumberException(); | ||||
| } | } | ||||
| public static int? rank(Tensor tensor) | |||||
| { | |||||
| return tensor.rank; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -57,6 +57,24 @@ namespace Tensorflow | |||||
| return (reshape1, reshape2); | return (reshape1, reshape2); | ||||
| } | } | ||||
| public static (Tensor, Tensor) _MeanGrad(Operation op, Tensor grad) | |||||
| { | |||||
| var sum_grad = _SumGrad(op, grad).Item1; | |||||
| var input_shape = op.inputs[0]._shape_tuple(); | |||||
| var output_shape = op.outputs[0]._shape_tuple(); | |||||
| var input_shape_tensor = array_ops.shape(op.inputs[0]); | |||||
| var output_shape_tensor = array_ops.shape(op.outputs[0]); | |||||
| var factor = _safe_shape_div(math_ops.reduce_prod(input_shape_tensor), math_ops.reduce_prod(output_shape_tensor)); | |||||
| return (math_ops.truediv(sum_grad, math_ops.cast(factor, sum_grad.dtype)), null); | |||||
| } | |||||
| private static Tensor _safe_shape_div(Tensor x, Tensor y) | |||||
| { | |||||
| return math_ops.floordiv(x, gen_math_ops.maximum(y, 1)); | |||||
| } | |||||
| public static (Tensor, Tensor) _SubGrad(Operation op, Tensor grad) | public static (Tensor, Tensor) _SubGrad(Operation op, Tensor grad) | ||||
| { | { | ||||
| var x = op.inputs[0]; | var x = op.inputs[0]; | ||||
| @@ -81,12 +99,25 @@ namespace Tensorflow | |||||
| public static (Tensor, Tensor) _SumGrad(Operation op, Tensor grad) | public static (Tensor, Tensor) _SumGrad(Operation op, Tensor grad) | ||||
| { | { | ||||
| if (op.inputs[0].NDims > -1) | |||||
| { | |||||
| var input_0_shape = op.inputs[0]._shape_tuple(); | |||||
| Tensor input_shape = null; | |||||
| if (input_0_shape != null) | |||||
| { | |||||
| var axes = tensor_util.constant_value(op.inputs[1]); | |||||
| if(!(axes is null)) | |||||
| { | |||||
| var rank = axes.shape.Rank; | |||||
| grad = array_ops.reshape(grad, new int[] { 1 }); | |||||
| if (!input_0_shape.Contains(-1)) | |||||
| input_shape = constant_op.constant(input_0_shape); | |||||
| else | |||||
| input_shape = array_ops.shape(op.inputs[0]); | |||||
| return (gen_array_ops.tile(grad, input_shape), null); | |||||
| } | |||||
| } | } | ||||
| var input_shape = array_ops.shape(op.inputs[0]); | |||||
| input_shape = array_ops.shape(op.inputs[0]); | |||||
| ops.colocate_with(input_shape); | ops.colocate_with(input_shape); | ||||
| var output_shape_kept_dims = math_ops.reduced_shape(input_shape, op.inputs[1]); | var output_shape_kept_dims = math_ops.reduced_shape(input_shape, op.inputs[1]); | ||||
| var tile_scaling = _safe_shape_div(input_shape, output_shape_kept_dims); | var tile_scaling = _safe_shape_div(input_shape, output_shape_kept_dims); | ||||
| @@ -95,11 +126,6 @@ namespace Tensorflow | |||||
| return (gen_array_ops.tile(grad, tile_scaling), null); | return (gen_array_ops.tile(grad, tile_scaling), null); | ||||
| } | } | ||||
| public static Tensor _safe_shape_div(Tensor x, Tensor y) | |||||
| { | |||||
| return math_ops.floordiv(x, gen_math_ops.maximum(y, 1)); | |||||
| } | |||||
| public static (Tensor, Tensor) _RealDivGrad(Operation op, Tensor grad) | public static (Tensor, Tensor) _RealDivGrad(Operation op, Tensor grad) | ||||
| { | { | ||||
| var x = op.inputs[0]; | var x = op.inputs[0]; | ||||
| @@ -63,7 +63,7 @@ namespace Tensorflow.Keras.Layers | |||||
| var rank = inputs.rank; | var rank = inputs.rank; | ||||
| if(rank > 2) | if(rank > 2) | ||||
| { | { | ||||
| throw new NotImplementedException(""); | |||||
| throw new NotImplementedException("call rank > 2"); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -82,7 +82,7 @@ namespace Tensorflow | |||||
| public static Tensor ones_like<T>(T tensor, TF_DataType dtype = TF_DataType.DtInvalid, string name = null, bool optimize = true) | public static Tensor ones_like<T>(T tensor, TF_DataType dtype = TF_DataType.DtInvalid, string name = null, bool optimize = true) | ||||
| => ones_like_impl(tensor, dtype, name, optimize); | => ones_like_impl(tensor, dtype, name, optimize); | ||||
| public static Tensor reshape(Tensor tensor, Tensor shape, string name = null) | |||||
| public static Tensor reshape<T1, T2>(T1 tensor, T2 shape, string name = null) | |||||
| { | { | ||||
| return gen_array_ops.reshape(tensor, shape, null); | return gen_array_ops.reshape(tensor, shape, null); | ||||
| } | } | ||||
| @@ -116,7 +116,7 @@ namespace Tensorflow | |||||
| return (_op.outputs[0], _op.outputs[1]); | return (_op.outputs[0], _op.outputs[1]); | ||||
| } | } | ||||
| public static Tensor reshape(Tensor tensor, Tensor shape, string name = null) | |||||
| public static Tensor reshape<T1, T2>(T1 tensor, T2 shape, string name = null) | |||||
| { | { | ||||
| var _op = _op_def_lib._apply_op_helper("Reshape", name, new { tensor, shape }); | var _op = _op_def_lib._apply_op_helper("Reshape", name, new { tensor, shape }); | ||||
| return _op.outputs[0]; | return _op.outputs[0]; | ||||
| @@ -20,16 +20,16 @@ namespace Tensorflow | |||||
| /// <param name="keep_dims"> An optional `bool`. Defaults to `False`. If true, retain reduced dimensions with length 1.</param> | /// <param name="keep_dims"> An optional `bool`. Defaults to `False`. If true, retain reduced dimensions with length 1.</param> | ||||
| /// <param name="name"> A name for the operation (optional).</param> | /// <param name="name"> A name for the operation (optional).</param> | ||||
| /// <returns> A `Tensor`. Has the same type as `input`.</returns> | /// <returns> A `Tensor`. Has the same type as `input`.</returns> | ||||
| public static Tensor mean(Tensor input, Tensor axis, bool keep_dims= false, string name = null) | |||||
| public static Tensor mean<T1, T2>(T1 input, T2 axis, bool keep_dims= false, string name = null) | |||||
| { | { | ||||
| var _op = _op_def_lib._apply_op_helper("Mean", name, args: new { input, reduction_indices = axis, keep_dims = keep_dims }); | var _op = _op_def_lib._apply_op_helper("Mean", name, args: new { input, reduction_indices = axis, keep_dims = keep_dims }); | ||||
| return _op.outputs[0]; | return _op.outputs[0]; | ||||
| } | } | ||||
| public static Tensor mean(Tensor input, int[] axis, bool keep_dims = false, string name = null) | |||||
| public static Tensor prod<T1, T2>(T1 input, T2 axis, bool keep_dims = false, string name = null) | |||||
| { | { | ||||
| var _op = _op_def_lib._apply_op_helper("Mean", name, args: new { input, reduction_indices = axis, keep_dims = keep_dims, name }); | |||||
| var _op = _op_def_lib._apply_op_helper("Prod", name, args: new { input, reduction_indices = axis, keep_dims }); | |||||
| return _op.outputs[0]; | return _op.outputs[0]; | ||||
| } | } | ||||
| @@ -186,7 +186,7 @@ namespace Tensorflow | |||||
| return _op.outputs[0]; | return _op.outputs[0]; | ||||
| } | } | ||||
| public static Tensor _max(Tensor input, int[] axis, bool keep_dims=false, string name = null) | |||||
| public static Tensor _max<Tx, Ty>(Tx input, Ty axis, bool keep_dims=false, string name = null) | |||||
| { | { | ||||
| var _op = _op_def_lib._apply_op_helper("Max", name, new { input, reduction_indices = axis, keep_dims }); | var _op = _op_def_lib._apply_op_helper("Max", name, new { input, reduction_indices = axis, keep_dims }); | ||||
| @@ -2,6 +2,7 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Text; | using System.Text; | ||||
| using Tensorflow.Framework; | |||||
| namespace Tensorflow | namespace Tensorflow | ||||
| { | { | ||||
| @@ -39,9 +40,41 @@ namespace Tensorflow | |||||
| public static Tensor reduce_mean(Tensor input_tensor, int[] axis = null, bool keepdims = false, string name = null) | public static Tensor reduce_mean(Tensor input_tensor, int[] axis = null, bool keepdims = false, string name = null) | ||||
| { | { | ||||
| var r = _ReductionDims(input_tensor, axis); | var r = _ReductionDims(input_tensor, axis); | ||||
| var m = gen_math_ops.mean(input_tensor, (int[]) r, keepdims, name); | |||||
| return _may_reduce_to_scalar(keepdims,axis, m); | |||||
| if (axis == null) | |||||
| { | |||||
| var m = gen_math_ops.mean(input_tensor, r, keepdims, name); | |||||
| return _may_reduce_to_scalar(keepdims, axis, m); | |||||
| } | |||||
| else | |||||
| { | |||||
| var m = gen_math_ops.mean(input_tensor, axis, keepdims, name); | |||||
| return _may_reduce_to_scalar(keepdims, axis, m); | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// Computes the product of elements across dimensions of a tensor. | |||||
| /// </summary> | |||||
| /// <param name="input_tensor"></param> | |||||
| /// <param name="axis"></param> | |||||
| /// <param name="keepdims"></param> | |||||
| /// <param name="name"></param> | |||||
| /// <returns></returns> | |||||
| public static Tensor reduce_prod(Tensor input_tensor, int[] axis = null, bool keepdims = false, string name = null) | |||||
| { | |||||
| var r = _ReductionDims(input_tensor, axis); | |||||
| if (axis == null) | |||||
| { | |||||
| var m = gen_math_ops.prod(input_tensor, r, keepdims, name); | |||||
| return _may_reduce_to_scalar(keepdims, axis, m); | |||||
| } | |||||
| else | |||||
| { | |||||
| var m = gen_math_ops.prod(input_tensor, axis, keepdims, name); | |||||
| return _may_reduce_to_scalar(keepdims, axis, m); | |||||
| } | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| /// Returns (x - y)(x - y) element-wise. | /// Returns (x - y)(x - y) element-wise. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -134,7 +167,10 @@ namespace Tensorflow | |||||
| public static Tensor reduce_max(Tensor input_tensor, int[] axis = null, bool keepdims = false, string name = null) | public static Tensor reduce_max(Tensor input_tensor, int[] axis = null, bool keepdims = false, string name = null) | ||||
| { | { | ||||
| return _may_reduce_to_scalar(keepdims, axis, gen_math_ops._max(input_tensor, (int[])_ReductionDims(input_tensor, axis), keepdims, name)); | |||||
| var r = _ReductionDims(input_tensor, axis); | |||||
| var max = (axis != null) ? gen_math_ops._max(input_tensor, axis, keepdims, name) : | |||||
| gen_math_ops._max(input_tensor, r, keepdims, name); | |||||
| return _may_reduce_to_scalar(keepdims, axis, max); | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -197,18 +233,19 @@ namespace Tensorflow | |||||
| } | } | ||||
| } | } | ||||
| private static object _ReductionDims(Tensor x, int[] axis) | |||||
| private static Tensor _ReductionDims(Tensor x, int[] axis) | |||||
| { | { | ||||
| if (axis != null) | if (axis != null) | ||||
| { | { | ||||
| return axis; | |||||
| // should return axis. or check before. | |||||
| return null; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| var rank = array_ops.rank(x); | |||||
| var rank = common_shapes.rank(x); | |||||
| if (rank != null) | if (rank != null) | ||||
| { | { | ||||
| return constant_op.constant(np.arange(rank), TF_DataType.TF_INT32); | |||||
| return constant_op.constant(np.arange(rank.Value), TF_DataType.TF_INT32); | |||||
| } | } | ||||
| return range(0, rank, 1); | return range(0, rank, 1); | ||||
| } | } | ||||
| @@ -303,5 +340,20 @@ namespace Tensorflow | |||||
| return x; | return x; | ||||
| }); | }); | ||||
| } | } | ||||
| public static Tensor truediv(Tensor x, Tensor y, string name = null) | |||||
| => _truediv_python3(x, y, name); | |||||
| public static Tensor _truediv_python3(Tensor x, Tensor y, string name = null) | |||||
| { | |||||
| return with(ops.name_scope(name, "truediv", new { x, y }), scope => | |||||
| { | |||||
| name = scope; | |||||
| var x_dtype = x.dtype.as_base_dtype(); | |||||
| var y_dtype = y.dtype.as_base_dtype(); | |||||
| return gen_math_ops.real_div(x, y, name: name); | |||||
| }); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -74,7 +74,8 @@ namespace Tensorflow | |||||
| public int[] _shape_tuple() | public int[] _shape_tuple() | ||||
| { | { | ||||
| return null; | |||||
| if (shape == null) return null; | |||||
| return shape.Select(x => (int)x).ToArray(); | |||||
| } | } | ||||
| public TensorShape getShape() | public TensorShape getShape() | ||||
| @@ -51,6 +51,15 @@ namespace Tensorflow | |||||
| if (tensor.TensorContent.Length > 0) | if (tensor.TensorContent.Length > 0) | ||||
| return np.frombuffer(tensor.TensorContent.ToByteArray(), tensor_dtype) | return np.frombuffer(tensor.TensorContent.ToByteArray(), tensor_dtype) | ||||
| .reshape(shape); | .reshape(shape); | ||||
| else if (tensor.Dtype == DataType.DtHalf || tensor.Dtype == DataType.DtBfloat16) | |||||
| ; | |||||
| else if (tensor.Dtype == DataType.DtFloat) | |||||
| ; | |||||
| else if (new DataType[] { DataType.DtInt32, DataType.DtUint8 }.Contains(tensor.Dtype)) | |||||
| if (tensor.IntVal.Count == 1) | |||||
| return np.repeat(np.array(tensor.IntVal[0]), Convert.ToInt32(num_elements)) | |||||
| .reshape(shape); | |||||
| throw new NotImplementedException("MakeNdarray"); | throw new NotImplementedException("MakeNdarray"); | ||||
| } | } | ||||
| @@ -0,0 +1,25 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| namespace Tensorflow.Train | |||||
| { | |||||
| /// <summary> | |||||
| /// Optimizer that implements the Adam algorithm. | |||||
| /// http://arxiv.org/abs/1412.6980 | |||||
| /// </summary> | |||||
| public class AdamOptimizer : Optimizer | |||||
| { | |||||
| private float _beta1; | |||||
| private float _beta2; | |||||
| private float _epsilon; | |||||
| public AdamOptimizer(float learning_rate, float beta1 = 0.9f, float beta2 = 0.999f, float epsilon = 1e-8f, bool use_locking = false, string name = "Adam") | |||||
| : base(learning_rate, use_locking, name) | |||||
| { | |||||
| _beta1 = beta1; | |||||
| _beta2 = beta2; | |||||
| _epsilon = epsilon; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -2,7 +2,7 @@ | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Text; | using System.Text; | ||||
| namespace Tensorflow | |||||
| namespace Tensorflow.Train | |||||
| { | { | ||||
| public class GradientDescentOptimizer : Optimizer | public class GradientDescentOptimizer : Optimizer | ||||
| { | { | ||||
| @@ -34,6 +34,7 @@ namespace Tensorflow | |||||
| Name = name; | Name = name; | ||||
| _use_locking = use_locking; | _use_locking = use_locking; | ||||
| LearningRate = learning_rate; | |||||
| // Dictionary of slots. | // Dictionary of slots. | ||||
| _slots = new Dictionary<string, object>(); | _slots = new Dictionary<string, object>(); | ||||
| _non_slot_dict = new Dictionary<string, object>(); | _non_slot_dict = new Dictionary<string, object>(); | ||||
| @@ -49,6 +50,7 @@ namespace Tensorflow | |||||
| /// was not `None`, that operation also increments `global_step`. | /// was not `None`, that operation also increments `global_step`. | ||||
| /// </returns> | /// </returns> | ||||
| public Operation minimize(Tensor loss, | public Operation minimize(Tensor loss, | ||||
| RefVariable global_step = null, | |||||
| GateGradientType gate_gradients = GateGradientType.GATE_OP, | GateGradientType gate_gradients = GateGradientType.GATE_OP, | ||||
| bool colocate_gradients_with_ops = false) | bool colocate_gradients_with_ops = false) | ||||
| { | { | ||||
| @@ -2,6 +2,7 @@ | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.IO; | using System.IO; | ||||
| using System.Text; | using System.Text; | ||||
| using Tensorflow.Train; | |||||
| namespace Tensorflow | namespace Tensorflow | ||||
| { | { | ||||
| @@ -11,6 +12,8 @@ namespace Tensorflow | |||||
| { | { | ||||
| public static Optimizer GradientDescentOptimizer(float learning_rate) => new GradientDescentOptimizer(learning_rate); | public static Optimizer GradientDescentOptimizer(float learning_rate) => new GradientDescentOptimizer(learning_rate); | ||||
| public static Optimizer AdamOptimizer(float learning_rate) => new AdamOptimizer(learning_rate); | |||||
| public static Saver Saver() => new Saver(); | public static Saver Saver() => new Saver(); | ||||
| public static string write_graph(Graph graph, string logdir, string name, bool as_text = true) => graph_io.write_graph(graph, logdir, name, as_text); | public static string write_graph(Graph graph, string logdir, string name, bool as_text = true) => graph_io.write_graph(graph, logdir, name, as_text); | ||||
| @@ -349,6 +349,7 @@ namespace Tensorflow | |||||
| { | { | ||||
| if (op.inputs == null) return null; | if (op.inputs == null) return null; | ||||
| // map tensorflow\python\ops\math_grad.py | |||||
| return (oper, out_grads) => | return (oper, out_grads) => | ||||
| { | { | ||||
| // Console.WriteLine($"get_gradient_function: {oper.type} '{oper.name}'"); | // Console.WriteLine($"get_gradient_function: {oper.type} '{oper.name}'"); | ||||
| @@ -364,6 +365,9 @@ namespace Tensorflow | |||||
| case "Mul": | case "Mul": | ||||
| var mul = math_grad._MulGrad(oper, out_grads); | var mul = math_grad._MulGrad(oper, out_grads); | ||||
| return new Tensor[] { mul.Item1, mul.Item2 }; | return new Tensor[] { mul.Item1, mul.Item2 }; | ||||
| case "Mean": | |||||
| var mean = math_grad._MeanGrad(oper, out_grads); | |||||
| return new Tensor[] { mean.Item1, mean.Item2 }; | |||||
| case "Sum": | case "Sum": | ||||
| var sum = math_grad._SumGrad(oper, out_grads); | var sum = math_grad._SumGrad(oper, out_grads); | ||||
| return new Tensor[] { sum.Item1, sum.Item2 }; | return new Tensor[] { sum.Item1, sum.Item2 }; | ||||
| @@ -119,10 +119,11 @@ namespace TensorFlowNET.Examples.TextClassification | |||||
| var y_one_hot = tf.one_hot(y, num_class); | var y_one_hot = tf.one_hot(y, num_class); | ||||
| loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits: logits, labels: y_one_hot)); | loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits: logits, labels: y_one_hot)); | ||||
| var update_ops = tf.get_collection(ops.GraphKeys.UPDATE_OPS) as List<Operation>; | |||||
| with(tf.control_dependencies(update_ops.ToArray()), delegate | |||||
| var update_ops = tf.get_collection(ops.GraphKeys.UPDATE_OPS) as List<object>; | |||||
| with(tf.control_dependencies(update_ops.Select(x => (Operation)x).ToArray()), delegate | |||||
| { | { | ||||
| var adam = tf.train.AdamOptimizer(learning_rate); | |||||
| adam.minimize(loss, global_step: global_step); | |||||
| }); | }); | ||||
| }); | }); | ||||
| } | } | ||||