| @@ -126,16 +126,18 @@ namespace Tensorflow | |||||
| public Tensor[] fused_batch_norm(Tensor x, | public Tensor[] fused_batch_norm(Tensor x, | ||||
| IVariableV1 scale, | IVariableV1 scale, | ||||
| IVariableV1 offset, | IVariableV1 offset, | ||||
| Tensor mean = null, | |||||
| Tensor variance = null, | |||||
| IVariableV1 mean = null, | |||||
| IVariableV1 variance = null, | |||||
| float epsilon = 0.001f, | float epsilon = 0.001f, | ||||
| string data_format = "NHWC", | string data_format = "NHWC", | ||||
| bool is_training = true, | bool is_training = true, | ||||
| string name = null) => nn_impl.fused_batch_norm(x, scale, offset, mean, variance, | |||||
| string name = null, | |||||
| float exponential_avg_factor = 1.0f) => nn_impl.fused_batch_norm(x, scale, offset, mean, variance, | |||||
| epsilon: epsilon, | epsilon: epsilon, | ||||
| data_format: data_format, | data_format: data_format, | ||||
| is_training: is_training, | is_training: is_training, | ||||
| name: name); | |||||
| name: name, | |||||
| exponential_avg_factor: exponential_avg_factor); | |||||
| public Tensor max_pool(Tensor value, int[] ksize, int[] strides, string padding, string data_format = "NHWC", string name = null) | public Tensor max_pool(Tensor value, int[] ksize, int[] strides, string padding, string data_format = "NHWC", string name = null) | ||||
| => nn_ops.max_pool(value, ksize, strides, padding, data_format: data_format, name: name); | => nn_ops.max_pool(value, ksize, strides, padding, data_format: data_format, name: name); | ||||
| @@ -180,7 +180,7 @@ namespace Tensorflow | |||||
| } | } | ||||
| } | } | ||||
| // [DebuggerStepThrough] | |||||
| [DebuggerStepThrough] | |||||
| public static void tf_with<T>(T py, Action<T> action) where T : ITensorFlowObject | public static void tf_with<T>(T py, Action<T> action) where T : ITensorFlowObject | ||||
| { | { | ||||
| try | try | ||||
| @@ -91,7 +91,7 @@ namespace Tensorflow.Contexts | |||||
| } | } | ||||
| [DebuggerStepThrough] | [DebuggerStepThrough] | ||||
| public Tensor RunInAutoMode(Func<Tensor> graphAction, Func<Tensor> eagerAction, params Tensor[] tensors) | |||||
| public T RunInAutoMode<T>(Func<T> graphAction, Func<T> eagerAction, params Tensor[] tensors) | |||||
| { | { | ||||
| var shouldRunInEager = executing_eagerly() | var shouldRunInEager = executing_eagerly() | ||||
| && tensors.Count(x => x.IsEagerTensor) == tensors.Length; | && tensors.Count(x => x.IsEagerTensor) == tensors.Length; | ||||
| @@ -0,0 +1,15 @@ | |||||
| using System; | |||||
| using Tensorflow.Gradients; | |||||
| using static Tensorflow.Binding; | |||||
| using static Tensorflow.tensorflow; | |||||
| namespace Tensorflow.Eager | |||||
| { | |||||
| public partial class EagerRunner | |||||
| { | |||||
| public bool MustRecordGradient() | |||||
| { | |||||
| return HasGradientTape(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -38,5 +38,7 @@ namespace Tensorflow.Eager | |||||
| Tensor[] inputs, | Tensor[] inputs, | ||||
| object[] attrs, | object[] attrs, | ||||
| Tensor[] results); | Tensor[] results); | ||||
| bool MustRecordGradient(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -52,7 +52,7 @@ namespace Tensorflow.Framework | |||||
| { | { | ||||
| var pred_value = tensor_util.constant_value(pred); | var pred_value = tensor_util.constant_value(pred); | ||||
| if (pred_value is null) | if (pred_value is null) | ||||
| return null; | |||||
| return pred.eval(new Session(pred.graph)); | |||||
| return pred_value; | return pred_value; | ||||
| } | } | ||||
| @@ -269,21 +269,29 @@ namespace Tensorflow.Operations | |||||
| } | } | ||||
| public static Tensor[] fused_batch_norm_grad_v3(FusedBatchNormParams @params) | public static Tensor[] fused_batch_norm_grad_v3(FusedBatchNormParams @params) | ||||
| { | |||||
| var op = tf.OpDefLib._apply_op_helper("FusedBatchNormGradV3", name: @params.Name, args: new | |||||
| { | |||||
| y_backprop = @params.YBackprop, | |||||
| x = @params.X, | |||||
| scale = @params.Scale, | |||||
| reserve_space_1 = @params.ReserveSpace1, | |||||
| reserve_space_2 = @params.ReserveSpace2, | |||||
| reserve_space_3 = @params.ReserveSpace3, | |||||
| epsilon = @params.Epsilon, | |||||
| data_format = @params.DataFormat, | |||||
| is_training = @params.IsTraining | |||||
| }); | |||||
| return op.outputs; | |||||
| } | |||||
| => tf.Context.RunInAutoMode(() | |||||
| => tf.OpDefLib._apply_op_helper("FusedBatchNormGradV3", name: @params.Name, | |||||
| args: new | |||||
| { | |||||
| y_backprop = @params.YBackprop, | |||||
| x = @params.X, | |||||
| scale = @params.Scale, | |||||
| reserve_space_1 = @params.ReserveSpace1, | |||||
| reserve_space_2 = @params.ReserveSpace2, | |||||
| reserve_space_3 = @params.ReserveSpace3, | |||||
| epsilon = @params.Epsilon, | |||||
| data_format = @params.DataFormat, | |||||
| is_training = @params.IsTraining | |||||
| }).outputs, () | |||||
| => tf.Runner.TFE_FastPathExecute(tf.Context, tf.Context.DeviceName, | |||||
| "FusedBatchNormGradV3", @params.Name, | |||||
| null, | |||||
| @params.YBackprop, @params.X, @params.Scale, | |||||
| @params.ReserveSpace1, @params.ReserveSpace2, @params.ReserveSpace3, | |||||
| "epsilon", @params.Epsilon, | |||||
| "data_format", @params.DataFormat, | |||||
| "is_training", @params.IsTraining), | |||||
| @params.YBackprop); | |||||
| public static Tensor[] fused_batch_norm(Tensor x, | public static Tensor[] fused_batch_norm(Tensor x, | ||||
| Tensor scale, | Tensor scale, | ||||
| @@ -313,9 +321,10 @@ namespace Tensorflow.Operations | |||||
| public static Tensor[] fused_batch_norm_v3(Tensor x, | public static Tensor[] fused_batch_norm_v3(Tensor x, | ||||
| Tensor scale, | Tensor scale, | ||||
| Tensor offset, | Tensor offset, | ||||
| Tensor mean, | |||||
| Tensor variance, | |||||
| IVariableV1 mean, | |||||
| IVariableV1 variance, | |||||
| float epsilon = 0.0001f, | float epsilon = 0.0001f, | ||||
| float exponential_avg_factor = 1.0f, | |||||
| string data_format = "NHWC", | string data_format = "NHWC", | ||||
| bool is_training = true, | bool is_training = true, | ||||
| string name = null) | string name = null) | ||||
| @@ -328,9 +337,10 @@ namespace Tensorflow.Operations | |||||
| x, | x, | ||||
| scale, | scale, | ||||
| offset, | offset, | ||||
| mean, | |||||
| variance, | |||||
| mean.AsTensor(), | |||||
| variance.AsTensor(), | |||||
| "epsilon", epsilon, | "epsilon", epsilon, | ||||
| "exponential_avg_factor", exponential_avg_factor, | |||||
| "data_format", data_format, | "data_format", data_format, | ||||
| "is_training", is_training); | "is_training", is_training); | ||||
| @@ -378,14 +388,14 @@ namespace Tensorflow.Operations | |||||
| } | } | ||||
| public static Tensor log_softmax(Tensor logits, string name = null) | public static Tensor log_softmax(Tensor logits, string name = null) | ||||
| { | |||||
| var _op = tf.OpDefLib._apply_op_helper("LogSoftmax", name: name, args: new | |||||
| { | |||||
| logits | |||||
| }); | |||||
| return _op.output; | |||||
| } | |||||
| => tf.Context.RunInAutoMode(() | |||||
| => tf.OpDefLib._apply_op_helper("LogSoftmax", name: name, | |||||
| args: new { logits }).output, () | |||||
| => tf.Runner.TFE_FastPathExecute(tf.Context, tf.Context.DeviceName, | |||||
| "LogSoftmax", name, | |||||
| null, | |||||
| logits).FirstOrDefault(), | |||||
| logits); | |||||
| /// <summary> | /// <summary> | ||||
| /// Says whether the targets are in the top `K` predictions. | /// Says whether the targets are in the top `K` predictions. | ||||
| @@ -560,6 +570,16 @@ namespace Tensorflow.Operations | |||||
| /// <returns></returns> | /// <returns></returns> | ||||
| public static (Tensor, Tensor) softmax_cross_entropy_with_logits(Tensor features, Tensor labels, string name = null) | public static (Tensor, Tensor) softmax_cross_entropy_with_logits(Tensor features, Tensor labels, string name = null) | ||||
| { | { | ||||
| if (tf.executing_eagerly()) | |||||
| { | |||||
| var results = tf.Runner.TFE_FastPathExecute(tf.Context, tf.Context.DeviceName, | |||||
| "SoftmaxCrossEntropyWithLogits", name, | |||||
| null, | |||||
| features, labels); | |||||
| return (results[0], results[1]); | |||||
| } | |||||
| var _op = tf.OpDefLib._apply_op_helper("SoftmaxCrossEntropyWithLogits", name: name, args: new | var _op = tf.OpDefLib._apply_op_helper("SoftmaxCrossEntropyWithLogits", name: name, args: new | ||||
| { | { | ||||
| features, | features, | ||||
| @@ -68,7 +68,7 @@ namespace Tensorflow | |||||
| null, | null, | ||||
| resource, value); | resource, value); | ||||
| return null; | |||||
| return results.Length == 0 ? null : results[0]; | |||||
| } | } | ||||
| var _op = tf.OpDefLib._apply_op_helper("AssignVariableOp", name, new { resource, value }); | var _op = tf.OpDefLib._apply_op_helper("AssignVariableOp", name, new { resource, value }); | ||||
| @@ -99,20 +99,21 @@ namespace Tensorflow | |||||
| public static Tensor[] fused_batch_norm(Tensor x, | public static Tensor[] fused_batch_norm(Tensor x, | ||||
| IVariableV1 scale, | IVariableV1 scale, | ||||
| IVariableV1 offset, | IVariableV1 offset, | ||||
| Tensor mean, | |||||
| Tensor variance, | |||||
| IVariableV1 mean, | |||||
| IVariableV1 variance, | |||||
| float epsilon = 0.001f, | float epsilon = 0.001f, | ||||
| string data_format = "NHWC", | string data_format = "NHWC", | ||||
| bool is_training = true, | bool is_training = true, | ||||
| string name = null) | |||||
| string name = null, | |||||
| float exponential_avg_factor = 1.0f) | |||||
| { | { | ||||
| x = ops.convert_to_tensor(x, name: "input"); | x = ops.convert_to_tensor(x, name: "input"); | ||||
| var scale_tensor = ops.convert_to_tensor(scale, name: "scale"); | var scale_tensor = ops.convert_to_tensor(scale, name: "scale"); | ||||
| var offset_tensor = ops.convert_to_tensor(offset, name: "offset"); | var offset_tensor = ops.convert_to_tensor(offset, name: "offset"); | ||||
| if (mean == null) | |||||
| /*if (mean == null) | |||||
| mean = constant_op.constant(new float[0]); | mean = constant_op.constant(new float[0]); | ||||
| if (variance == null) | if (variance == null) | ||||
| variance = constant_op.constant(new float[0]); | |||||
| variance = constant_op.constant(new float[0]);*/ | |||||
| var min_epsilon = 1.001e-5f; | var min_epsilon = 1.001e-5f; | ||||
| epsilon = epsilon > min_epsilon ? epsilon : min_epsilon; | epsilon = epsilon > min_epsilon ? epsilon : min_epsilon; | ||||
| @@ -122,15 +123,16 @@ namespace Tensorflow | |||||
| mean, | mean, | ||||
| variance, | variance, | ||||
| epsilon, | epsilon, | ||||
| data_format, | |||||
| is_training, | |||||
| name); | |||||
| exponential_avg_factor: exponential_avg_factor, | |||||
| data_format: data_format, | |||||
| is_training: is_training, | |||||
| name: name); | |||||
| var y = results[0]; | var y = results[0]; | ||||
| var batch_mean = results[1]; | |||||
| var batch_var = results[2]; | |||||
| var running_mean = results[1]; | |||||
| var running_var = results[2]; | |||||
| return new[] { y, batch_mean, batch_var }; | |||||
| return new[] { y, running_mean, running_var }; | |||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -255,7 +255,7 @@ namespace Tensorflow | |||||
| // The output cost shape should be the input minus axis. | // The output cost shape should be the input minus axis. | ||||
| var output_shape = array_ops.slice(input_shape, | var output_shape = array_ops.slice(input_shape, | ||||
| new int[] { 0 }, | |||||
| new Tensor[] { constant_op.constant(0) }, | |||||
| new Tensor[] { math_ops.subtract(input_rank, 1) }); | new Tensor[] { math_ops.subtract(input_rank, 1) }); | ||||
| cost = array_ops.reshape(cost, output_shape); | cost = array_ops.reshape(cost, output_shape); | ||||
| @@ -274,36 +274,38 @@ namespace Tensorflow | |||||
| var rank = array_ops.rank(logits); | var rank = array_ops.rank(logits); | ||||
| var last_dim_size = array_ops.slice(array_ops.shape(logits), | var last_dim_size = array_ops.slice(array_ops.shape(logits), | ||||
| new[] { math_ops.subtract(rank, 1) }, | new[] { math_ops.subtract(rank, 1) }, | ||||
| new[] { 1 }); | |||||
| new[] { constant_op.constant(1) }); | |||||
| var ops = array_ops.concat(new[] { new[] { -1 }, (object)last_dim_size }, 0); | var ops = array_ops.concat(new[] { new[] { -1 }, (object)last_dim_size }, 0); | ||||
| var output = array_ops.reshape(logits, ops); | var output = array_ops.reshape(logits, ops); | ||||
| // Set output shape if known. | // Set output shape if known. | ||||
| // if not context.executing_eagerly(): | |||||
| var shape = logits.TensorShape; | |||||
| if (shape != null && shape.ndim > 0) | |||||
| if (!tf.Context.executing_eagerly()) | |||||
| { | { | ||||
| var product = 1; | |||||
| var product_valid = true; | |||||
| foreach (var d in shape.dims.Take(shape.ndim - 1)) | |||||
| var shape = logits.TensorShape; | |||||
| if (shape != null && shape.ndim > 0) | |||||
| { | { | ||||
| if (d == -1) | |||||
| var product = 1; | |||||
| var product_valid = true; | |||||
| foreach (var d in shape.dims.Take(shape.ndim - 1)) | |||||
| { | { | ||||
| product_valid = false; | |||||
| break; | |||||
| if (d == -1) | |||||
| { | |||||
| product_valid = false; | |||||
| break; | |||||
| } | |||||
| else | |||||
| { | |||||
| product *= d; | |||||
| } | |||||
| } | } | ||||
| else | |||||
| if (product_valid) | |||||
| { | { | ||||
| product *= d; | |||||
| var output_shape = new[] { product }; | |||||
| throw new NotImplementedException("_flatten_outer_dims product_valid"); | |||||
| } | } | ||||
| } | } | ||||
| if (product_valid) | |||||
| { | |||||
| var output_shape = new[] { product }; | |||||
| throw new NotImplementedException("_flatten_outer_dims product_valid"); | |||||
| } | |||||
| } | } | ||||
| return output; | return output; | ||||
| @@ -5,7 +5,7 @@ | |||||
| <AssemblyName>TensorFlow.NET</AssemblyName> | <AssemblyName>TensorFlow.NET</AssemblyName> | ||||
| <RootNamespace>Tensorflow</RootNamespace> | <RootNamespace>Tensorflow</RootNamespace> | ||||
| <TargetTensorFlow>2.2.0</TargetTensorFlow> | <TargetTensorFlow>2.2.0</TargetTensorFlow> | ||||
| <Version>0.30.0</Version> | |||||
| <Version>0.31.0</Version> | |||||
| <LangVersion>8.0</LangVersion> | <LangVersion>8.0</LangVersion> | ||||
| <Authors>Haiping Chen, Meinrad Recheis, Eli Belash</Authors> | <Authors>Haiping Chen, Meinrad Recheis, Eli Belash</Authors> | ||||
| <Company>SciSharp STACK</Company> | <Company>SciSharp STACK</Company> | ||||
| @@ -19,7 +19,7 @@ | |||||
| <Description>Google's TensorFlow full binding in .NET Standard. | <Description>Google's TensorFlow full binding in .NET Standard. | ||||
| Building, training and infering deep learning models. | Building, training and infering deep learning models. | ||||
| https://tensorflownet.readthedocs.io</Description> | https://tensorflownet.readthedocs.io</Description> | ||||
| <AssemblyVersion>0.30.0.0</AssemblyVersion> | |||||
| <AssemblyVersion>0.31.0.0</AssemblyVersion> | |||||
| <PackageReleaseNotes>tf.net 0.20.x and above are based on tensorflow native 2.x. | <PackageReleaseNotes>tf.net 0.20.x and above are based on tensorflow native 2.x. | ||||
| * Eager Mode is added finally. | * Eager Mode is added finally. | ||||
| @@ -30,7 +30,7 @@ https://tensorflownet.readthedocs.io</Description> | |||||
| TensorFlow .NET v0.30 is focused on making more Keras API work including: | TensorFlow .NET v0.30 is focused on making more Keras API work including: | ||||
| * tf.keras.datasets | * tf.keras.datasets | ||||
| * Building keras model in subclass, functional and sequential api</PackageReleaseNotes> | * Building keras model in subclass, functional and sequential api</PackageReleaseNotes> | ||||
| <FileVersion>0.30.0.0</FileVersion> | |||||
| <FileVersion>0.31.0.0</FileVersion> | |||||
| <PackageLicenseFile>LICENSE</PackageLicenseFile> | <PackageLicenseFile>LICENSE</PackageLicenseFile> | ||||
| <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance> | ||||
| <SignAssembly>true</SignAssembly> | <SignAssembly>true</SignAssembly> | ||||
| @@ -23,6 +23,7 @@ namespace Tensorflow | |||||
| public Graph graph => items.First().graph; | public Graph graph => items.First().graph; | ||||
| public bool IsEagerTensor => items.First().IsEagerTensor; | public bool IsEagerTensor => items.First().IsEagerTensor; | ||||
| public bool IsList { get; set; } | public bool IsList { get; set; } | ||||
| public int Length => items.Length; | |||||
| public Tensor this[int index] | public Tensor this[int index] | ||||
| { | { | ||||
| @@ -51,12 +51,13 @@ namespace Tensorflow | |||||
| private static NDArray _ConstantValue(Tensor tensor, bool partial) | private static NDArray _ConstantValue(Tensor tensor, bool partial) | ||||
| { | { | ||||
| if (tensor.op.type == "Const") | |||||
| switch (tensor.op.type) | |||||
| { | { | ||||
| return MakeNdarray(tensor.op.get_attr("value") as TensorProto); | |||||
| case "Const": | |||||
| return MakeNdarray(tensor.op.get_attr("value") as TensorProto); | |||||
| default: | |||||
| return null; | |||||
| } | } | ||||
| return null; | |||||
| } | } | ||||
| public static NDArray MakeNdarray(TensorProto tensor) | public static NDArray MakeNdarray(TensorProto tensor) | ||||
| @@ -83,8 +83,11 @@ namespace Tensorflow | |||||
| var assign_op = gen_resource_variable_ops.assign_variable_op( | var assign_op = gen_resource_variable_ops.assign_variable_op( | ||||
| handle, value_tensor, name: name); | handle, value_tensor, name: name); | ||||
| if (read_value) | if (read_value) | ||||
| { | |||||
| return gen_resource_variable_ops.read_variable_op(handle, dtype); | return gen_resource_variable_ops.read_variable_op(handle, dtype); | ||||
| // return _lazy_read(assign_op, value_tensor); | |||||
| // var variable = _lazy_read(assign_op, value_tensor); | |||||
| // return variable; | |||||
| } | |||||
| return assign_op; | return assign_op; | ||||
| } | } | ||||
| @@ -111,7 +114,7 @@ namespace Tensorflow | |||||
| return result; | return result; | ||||
| } | } | ||||
| BaseResourceVariable _lazy_read(Operation op, Tensor value) | |||||
| IVariableV1 _lazy_read(Operation op, Tensor value) | |||||
| { | { | ||||
| variable_accessed(this); | variable_accessed(this); | ||||
| return new _UnreadVariable(handle, _dtype, _shape, _in_graph_mode, _unique_id); | return new _UnreadVariable(handle, _dtype, _shape, _in_graph_mode, _unique_id); | ||||
| @@ -6,7 +6,7 @@ namespace Tensorflow | |||||
| /// Represents a future for a read of a variable. | /// Represents a future for a read of a variable. | ||||
| /// Pretends to be the tensor if anyone looks. | /// Pretends to be the tensor if anyone looks. | ||||
| /// </summary> | /// </summary> | ||||
| public class _UnreadVariable : BaseResourceVariable | |||||
| public class _UnreadVariable : BaseResourceVariable, IVariableV1 | |||||
| { | { | ||||
| public override string Name => _in_graph_mode ? _parent_op.name : "UnreadVariable"; | public override string Name => _in_graph_mode ? _parent_op.name : "UnreadVariable"; | ||||
| @@ -85,10 +85,12 @@ namespace Tensorflow | |||||
| public static Tensor assign_sub(IVariableV1 @ref, | public static Tensor assign_sub(IVariableV1 @ref, | ||||
| Tensor value, | Tensor value, | ||||
| bool use_locking = false, | bool use_locking = false, | ||||
| string name = null) => gen_state_ops.assign_sub(@ref, | |||||
| value, | |||||
| use_locking: use_locking, | |||||
| name: name); | |||||
| string name = null) => @ref.dtype.is_ref_dtype() ? | |||||
| gen_state_ops.assign_sub(@ref, | |||||
| value, | |||||
| use_locking: use_locking, | |||||
| name: name) : | |||||
| @ref.assign(value, name: name) as Tensor; | |||||
| //"""Update 'ref' by adding 'value' to it. | //"""Update 'ref' by adding 'value' to it. | ||||
| // | // | ||||
| @@ -335,6 +335,7 @@ namespace Tensorflow.Keras.Engine | |||||
| var layer_inputs = node.MapArguments(tensor_dict); | var layer_inputs = node.MapArguments(tensor_dict); | ||||
| // Console.WriteLine($"{node.Layer}: {node.Layer.Name}"); | |||||
| var outputs = node.Layer.Apply(layer_inputs, is_training: training); | var outputs = node.Layer.Apply(layer_inputs, is_training: training); | ||||
| // Update tensor_dict for next input | // Update tensor_dict for next input | ||||
| @@ -207,11 +207,11 @@ namespace Tensorflow.Keras.Engine | |||||
| })); | })); | ||||
| } | } | ||||
| protected virtual void add_update(Tensor[] updates, bool inputs = false) | |||||
| /*protected virtual void add_update(Tensor[] updates, bool inputs = false) | |||||
| { | { | ||||
| var updates_op = updates.Select(x => x.op).ToArray(); | var updates_op = updates.Select(x => x.op).ToArray(); | ||||
| this.updates.AddRange(updates_op); | this.updates.AddRange(updates_op); | ||||
| } | |||||
| }*/ | |||||
| // Determine layer name (non-unique). | // Determine layer name (non-unique). | ||||
| protected virtual void _init_set_name(string name, bool zero_based = true) | protected virtual void _init_set_name(string name, bool zero_based = true) | ||||
| @@ -60,7 +60,19 @@ namespace Tensorflow.Keras.Engine | |||||
| Func<Tensor, Tensor, Tensor> metric_obj = null; | Func<Tensor, Tensor, Tensor> metric_obj = null; | ||||
| if (metric == "accuracy" || metric == "acc") | if (metric == "accuracy" || metric == "acc") | ||||
| { | { | ||||
| metric_obj = keras.metrics.sparse_categorical_accuracy; | |||||
| var y_t_rank = y_t.rank; | |||||
| var y_p_rank = y_p.rank; | |||||
| var y_t_last_dim = y_t.shape[^1]; | |||||
| var y_p_last_dim = y_p.shape[^1]; | |||||
| bool is_binary = y_p_last_dim == 1; | |||||
| bool is_sparse_categorical = (y_t_rank < y_p_rank || y_t_last_dim == 1) && y_p_last_dim > 1; | |||||
| if (is_sparse_categorical) | |||||
| metric_obj = keras.metrics.sparse_categorical_accuracy; | |||||
| else | |||||
| metric_obj = keras.metrics.categorical_accuracy; | |||||
| return new MeanMetricWrapper(metric_obj, metric); | return new MeanMetricWrapper(metric_obj, metric); | ||||
| } | } | ||||
| @@ -53,7 +53,6 @@ namespace Tensorflow.Keras.Engine | |||||
| stop_training = false; | stop_training = false; | ||||
| _train_counter.assign(0); | _train_counter.assign(0); | ||||
| bool first_step = true; | |||||
| Console.WriteLine($"Training..."); | Console.WriteLine($"Training..."); | ||||
| foreach (var (epoch, iterator) in data_handler.enumerate_epochs()) | foreach (var (epoch, iterator) in data_handler.enumerate_epochs()) | ||||
| { | { | ||||
| @@ -65,11 +64,6 @@ namespace Tensorflow.Keras.Engine | |||||
| { | { | ||||
| // callbacks.on_train_batch_begin(step) | // callbacks.on_train_batch_begin(step) | ||||
| results = step_function(iterator); | results = step_function(iterator); | ||||
| if (first_step) | |||||
| { | |||||
| Console.WriteLine($"epoch: {epoch}, " + string.Join(", ", results.Select(x => $"{x.Item1}: {(float)x.Item2}"))); | |||||
| first_step = false; | |||||
| } | |||||
| } | } | ||||
| Console.WriteLine($"epoch: {epoch + 1}, " + string.Join(", ", results.Select(x => $"{x.Item1}: {(float)x.Item2}"))); | Console.WriteLine($"epoch: {epoch + 1}, " + string.Join(", ", results.Select(x => $"{x.Item1}: {(float)x.Item2}"))); | ||||
| } | } | ||||
| @@ -0,0 +1,30 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Text; | |||||
| using static Tensorflow.Binding; | |||||
| using static Tensorflow.KerasApi; | |||||
| namespace Tensorflow.Keras.Losses | |||||
| { | |||||
| public class CategoricalCrossentropy : LossFunctionWrapper, ILossFunc | |||||
| { | |||||
| float label_smoothing; | |||||
| public CategoricalCrossentropy(bool from_logits = false, | |||||
| float label_smoothing = 0, | |||||
| string reduction = ReductionV2.AUTO, | |||||
| string name = "categorical_crossentropy") : | |||||
| base(reduction: reduction, | |||||
| name: name, | |||||
| from_logits: from_logits) | |||||
| { | |||||
| this.label_smoothing = label_smoothing; | |||||
| } | |||||
| public override Tensor Apply(Tensor y_true, Tensor y_pred, bool from_logits = false, int axis = -1) | |||||
| { | |||||
| // Try to adjust the shape so that rank of labels = rank of logits - 1. | |||||
| return keras.backend.categorical_crossentropy(y_true, y_pred, from_logits: from_logits); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -11,14 +11,18 @@ namespace Tensorflow.Keras.Losses | |||||
| protected string reduction; | protected string reduction; | ||||
| protected string name; | protected string name; | ||||
| bool _allow_sum_over_batch_size; | bool _allow_sum_over_batch_size; | ||||
| protected bool from_logits = false; | |||||
| string _name_scope; | string _name_scope; | ||||
| public string Reduction => reduction; | public string Reduction => reduction; | ||||
| public Loss(string reduction = ReductionV2.AUTO, string name = null) | |||||
| public Loss(string reduction = ReductionV2.AUTO, | |||||
| string name = null, | |||||
| bool from_logits = false) | |||||
| { | { | ||||
| this.reduction = reduction; | this.reduction = reduction; | ||||
| this.name = name; | this.name = name; | ||||
| this.from_logits = from_logits; | |||||
| _allow_sum_over_batch_size = false; | _allow_sum_over_batch_size = false; | ||||
| } | } | ||||
| @@ -29,7 +33,7 @@ namespace Tensorflow.Keras.Losses | |||||
| public Tensor Call(Tensor y_true, Tensor y_pred) | public Tensor Call(Tensor y_true, Tensor y_pred) | ||||
| { | { | ||||
| var losses = Apply(y_true, y_pred); | |||||
| var losses = Apply(y_true, y_pred, from_logits: from_logits); | |||||
| return losses_utils.compute_weighted_loss(losses, reduction: ReductionV2.SUM_OVER_BATCH_SIZE); | return losses_utils.compute_weighted_loss(losses, reduction: ReductionV2.SUM_OVER_BATCH_SIZE); | ||||
| } | } | ||||
| @@ -1,11 +1,15 @@ | |||||
| namespace Tensorflow.Keras.Losses | |||||
| using Tensorflow.Keras.Utils; | |||||
| namespace Tensorflow.Keras.Losses | |||||
| { | { | ||||
| public class LossFunctionWrapper : Loss | public class LossFunctionWrapper : Loss | ||||
| { | { | ||||
| public LossFunctionWrapper(string reduction = ReductionV2.AUTO, | public LossFunctionWrapper(string reduction = ReductionV2.AUTO, | ||||
| string name = null) | |||||
| string name = null, | |||||
| bool from_logits = false) | |||||
| : base(reduction: reduction, | : base(reduction: reduction, | ||||
| name: name) | |||||
| name: name, | |||||
| from_logits: from_logits) | |||||
| { | { | ||||
| } | } | ||||
| } | } | ||||
| @@ -4,5 +4,8 @@ | |||||
| { | { | ||||
| public ILossFunc SparseCategoricalCrossentropy(bool from_logits = false) | public ILossFunc SparseCategoricalCrossentropy(bool from_logits = false) | ||||
| => new SparseCategoricalCrossentropy(from_logits: from_logits); | => new SparseCategoricalCrossentropy(from_logits: from_logits); | ||||
| public ILossFunc CategoricalCrossentropy(bool from_logits = false) | |||||
| => new CategoricalCrossentropy(from_logits: from_logits); | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,6 +2,12 @@ | |||||
| { | { | ||||
| public class MetricsApi | public class MetricsApi | ||||
| { | { | ||||
| public Tensor categorical_accuracy(Tensor y_true, Tensor y_pred) | |||||
| { | |||||
| var eql = math_ops.equal(math_ops.argmax(y_true, -1), math_ops.argmax(y_pred, -1)); | |||||
| return math_ops.cast(eql, TF_DataType.TF_FLOAT); | |||||
| } | |||||
| /// <summary> | /// <summary> | ||||
| /// Calculates how often predictions matches integer labels. | /// Calculates how often predictions matches integer labels. | ||||
| /// </summary> | /// </summary> | ||||