| @@ -1,4 +1,44 @@ | |||||
| namespace Tensorflow.Keras | |||||
| using Newtonsoft.Json; | |||||
| using System.Reflection; | |||||
| using System.Runtime.Versioning; | |||||
| using Tensorflow.Keras.Common; | |||||
| namespace Tensorflow.Keras | |||||
| { | { | ||||
| public delegate Tensor Activation(Tensor features, string name = null); | |||||
| [JsonConverter(typeof(CustomizedActivationJsonConverter))] | |||||
| public class Activation | |||||
| { | |||||
| public string Name { get; set; } | |||||
| /// <summary> | |||||
| /// The parameters are `features` and `name`. | |||||
| /// </summary> | |||||
| public Func<Tensor, string, Tensor> ActivationFunction { get; set; } | |||||
| public Tensor Apply(Tensor input, string name = null) => ActivationFunction(input, name); | |||||
| public static implicit operator Activation(Func<Tensor, string, Tensor> func) | |||||
| { | |||||
| return new Activation() | |||||
| { | |||||
| Name = func.GetMethodInfo().Name, | |||||
| ActivationFunction = func | |||||
| }; | |||||
| } | |||||
| } | |||||
| public interface IActivationsApi | |||||
| { | |||||
| Activation GetActivationFromName(string name); | |||||
| Activation Linear { get; } | |||||
| Activation Relu { get; } | |||||
| Activation Sigmoid { get; } | |||||
| Activation Softmax { get; } | |||||
| Activation Tanh { get; } | |||||
| Activation Mish { get; } | |||||
| } | |||||
| } | } | ||||
| @@ -26,27 +26,8 @@ namespace Tensorflow.Keras.ArgsDefinition | |||||
| public Shape DilationRate { get; set; } = (1, 1); | public Shape DilationRate { get; set; } = (1, 1); | ||||
| [JsonProperty("groups")] | [JsonProperty("groups")] | ||||
| public int Groups { get; set; } = 1; | public int Groups { get; set; } = 1; | ||||
| public Activation Activation { get; set; } | |||||
| private string _activationName; | |||||
| [JsonProperty("activation")] | [JsonProperty("activation")] | ||||
| public string ActivationName | |||||
| { | |||||
| get | |||||
| { | |||||
| if (string.IsNullOrEmpty(_activationName)) | |||||
| { | |||||
| return Activation.Method.Name; | |||||
| } | |||||
| else | |||||
| { | |||||
| return _activationName; | |||||
| } | |||||
| } | |||||
| set | |||||
| { | |||||
| _activationName = value; | |||||
| } | |||||
| } | |||||
| public Activation Activation { get; set; } | |||||
| [JsonProperty("use_bias")] | [JsonProperty("use_bias")] | ||||
| public bool UseBias { get; set; } | public bool UseBias { get; set; } | ||||
| [JsonProperty("kernel_initializer")] | [JsonProperty("kernel_initializer")] | ||||
| @@ -18,28 +18,8 @@ namespace Tensorflow.Keras.ArgsDefinition | |||||
| /// <summary> | /// <summary> | ||||
| /// Activation function to use. | /// Activation function to use. | ||||
| /// </summary> | /// </summary> | ||||
| public Activation Activation { get; set; } | |||||
| private string _activationName; | |||||
| [JsonProperty("activation")] | [JsonProperty("activation")] | ||||
| public string ActivationName | |||||
| { | |||||
| get | |||||
| { | |||||
| if (string.IsNullOrEmpty(_activationName)) | |||||
| { | |||||
| return Activation.Method.Name; | |||||
| } | |||||
| else | |||||
| { | |||||
| return _activationName; | |||||
| } | |||||
| } | |||||
| set | |||||
| { | |||||
| _activationName = value; | |||||
| } | |||||
| } | |||||
| public Activation Activation { get; set; } | |||||
| /// <summary> | /// <summary> | ||||
| /// Whether the layer uses a bias vector. | /// Whether the layer uses a bias vector. | ||||
| @@ -35,27 +35,8 @@ namespace Tensorflow.Keras.ArgsDefinition.Core | |||||
| /// <summary> | /// <summary> | ||||
| /// Activation function to use. | /// Activation function to use. | ||||
| /// </summary> | /// </summary> | ||||
| public Activation Activation { get; set; } | |||||
| private string _activationName; | |||||
| [JsonProperty("activation")] | [JsonProperty("activation")] | ||||
| public string ActivationName | |||||
| { | |||||
| get | |||||
| { | |||||
| if (string.IsNullOrEmpty(_activationName)) | |||||
| { | |||||
| return Activation.Method.Name; | |||||
| } | |||||
| else | |||||
| { | |||||
| return _activationName; | |||||
| } | |||||
| } | |||||
| set | |||||
| { | |||||
| _activationName = value; | |||||
| } | |||||
| } | |||||
| public Activation Activation { get; set; } | |||||
| /// <summary> | /// <summary> | ||||
| /// Initializer for the `kernel` weights matrix. | /// Initializer for the `kernel` weights matrix. | ||||
| @@ -4,6 +4,7 @@ using Newtonsoft.Json.Linq; | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Text; | using System.Text; | ||||
| using static Tensorflow.Binding; | |||||
| namespace Tensorflow.Keras.Common | namespace Tensorflow.Keras.Common | ||||
| { | { | ||||
| @@ -31,20 +32,19 @@ namespace Tensorflow.Keras.Common | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| var token = JToken.FromObject((value as Activation)!.GetType().Name); | |||||
| var token = JToken.FromObject(((Activation)value).Name); | |||||
| token.WriteTo(writer); | token.WriteTo(writer); | ||||
| } | } | ||||
| } | } | ||||
| public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) | public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) | ||||
| { | { | ||||
| throw new NotImplementedException(); | |||||
| //var dims = serializer.Deserialize(reader, typeof(string)); | |||||
| //if (dims is null) | |||||
| //{ | |||||
| // throw new ValueError("Cannot deserialize 'null' to `Activation`."); | |||||
| //} | |||||
| //return new Shape((long[])(dims!)); | |||||
| var activationName = serializer.Deserialize<string>(reader); | |||||
| if (tf.keras is null) | |||||
| { | |||||
| throw new RuntimeError("Tensorflow.Keras is not loaded, please install it first."); | |||||
| } | |||||
| return tf.keras.activations.GetActivationFromName(string.IsNullOrEmpty(activationName) ? "linear" : activationName); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -16,6 +16,7 @@ namespace Tensorflow.Keras | |||||
| IInitializersApi initializers { get; } | IInitializersApi initializers { get; } | ||||
| ILayersApi layers { get; } | ILayersApi layers { get; } | ||||
| ILossesApi losses { get; } | ILossesApi losses { get; } | ||||
| IActivationsApi activations { get; } | |||||
| IOptimizerApi optimizers { get; } | IOptimizerApi optimizers { get; } | ||||
| IMetricsApi metrics { get; } | IMetricsApi metrics { get; } | ||||
| IModelsApi models { get; } | IModelsApi models { get; } | ||||
| @@ -6,45 +6,61 @@ using static Tensorflow.Binding; | |||||
| namespace Tensorflow.Keras | namespace Tensorflow.Keras | ||||
| { | { | ||||
| public class Activations | |||||
| public class Activations: IActivationsApi | |||||
| { | { | ||||
| private static Dictionary<string, Activation> _nameActivationMap; | private static Dictionary<string, Activation> _nameActivationMap; | ||||
| private static Dictionary<Activation, string> _activationNameMap; | |||||
| private static Activation _linear = (features, name) => features; | |||||
| private static Activation _relu = (features, name) | |||||
| => tf.Context.ExecuteOp("Relu", name, new ExecuteOpArgs(features)); | |||||
| private static Activation _sigmoid = (features, name) | |||||
| => tf.Context.ExecuteOp("Sigmoid", name, new ExecuteOpArgs(features)); | |||||
| private static Activation _softmax = (features, name) | |||||
| => tf.Context.ExecuteOp("Softmax", name, new ExecuteOpArgs(features)); | |||||
| private static Activation _tanh = (features, name) | |||||
| => tf.Context.ExecuteOp("Tanh", name, new ExecuteOpArgs(features)); | |||||
| private static Activation _mish = (features, name) | |||||
| => features * tf.math.tanh(tf.math.softplus(features)); | |||||
| private static Activation _linear = new Activation() | |||||
| { | |||||
| Name = "linear", | |||||
| ActivationFunction = (features, name) => features | |||||
| }; | |||||
| private static Activation _relu = new Activation() | |||||
| { | |||||
| Name = "relu", | |||||
| ActivationFunction = (features, name) => tf.Context.ExecuteOp("Relu", name, new ExecuteOpArgs(features)) | |||||
| }; | |||||
| private static Activation _sigmoid = new Activation() | |||||
| { | |||||
| Name = "sigmoid", | |||||
| ActivationFunction = (features, name) => tf.Context.ExecuteOp("Sigmoid", name, new ExecuteOpArgs(features)) | |||||
| }; | |||||
| private static Activation _softmax = new Activation() | |||||
| { | |||||
| Name = "softmax", | |||||
| ActivationFunction = (features, name) => tf.Context.ExecuteOp("Softmax", name, new ExecuteOpArgs(features)) | |||||
| }; | |||||
| private static Activation _tanh = new Activation() | |||||
| { | |||||
| Name = "tanh", | |||||
| ActivationFunction = (features, name) => tf.Context.ExecuteOp("Tanh", name, new ExecuteOpArgs(features)) | |||||
| }; | |||||
| private static Activation _mish = new Activation() | |||||
| { | |||||
| Name = "mish", | |||||
| ActivationFunction = (features, name) => features * tf.math.tanh(tf.math.softplus(features)) | |||||
| }; | |||||
| /// <summary> | /// <summary> | ||||
| /// Register the name-activation mapping in this static class. | /// Register the name-activation mapping in this static class. | ||||
| /// </summary> | /// </summary> | ||||
| /// <param name="name"></param> | /// <param name="name"></param> | ||||
| /// <param name="activation"></param> | /// <param name="activation"></param> | ||||
| private static void RegisterActivation(string name, Activation activation) | |||||
| private static void RegisterActivation(Activation activation) | |||||
| { | { | ||||
| _nameActivationMap[name] = activation; | |||||
| _activationNameMap[activation] = name; | |||||
| _nameActivationMap[activation.Name] = activation; | |||||
| } | } | ||||
| static Activations() | static Activations() | ||||
| { | { | ||||
| _nameActivationMap = new Dictionary<string, Activation>(); | _nameActivationMap = new Dictionary<string, Activation>(); | ||||
| _activationNameMap= new Dictionary<Activation, string>(); | |||||
| RegisterActivation("relu", _relu); | |||||
| RegisterActivation("linear", _linear); | |||||
| RegisterActivation("sigmoid", _sigmoid); | |||||
| RegisterActivation("softmax", _softmax); | |||||
| RegisterActivation("tanh", _tanh); | |||||
| RegisterActivation("mish", _mish); | |||||
| RegisterActivation(_relu); | |||||
| RegisterActivation(_linear); | |||||
| RegisterActivation(_sigmoid); | |||||
| RegisterActivation(_softmax); | |||||
| RegisterActivation(_tanh); | |||||
| RegisterActivation(_mish); | |||||
| } | } | ||||
| public Activation Linear => _linear; | public Activation Linear => _linear; | ||||
| @@ -59,7 +75,7 @@ namespace Tensorflow.Keras | |||||
| public Activation Mish => _mish; | public Activation Mish => _mish; | ||||
| public static Activation GetActivationByName(string name) | |||||
| public Activation GetActivationFromName(string name) | |||||
| { | { | ||||
| if (!_nameActivationMap.TryGetValue(name, out var res)) | if (!_nameActivationMap.TryGetValue(name, out var res)) | ||||
| { | { | ||||
| @@ -70,17 +86,5 @@ namespace Tensorflow.Keras | |||||
| return res; | return res; | ||||
| } | } | ||||
| } | } | ||||
| public static string GetNameByActivation(Activation activation) | |||||
| { | |||||
| if(!_activationNameMap.TryGetValue(activation, out var name)) | |||||
| { | |||||
| throw new Exception($"Activation {activation} not found"); | |||||
| } | |||||
| else | |||||
| { | |||||
| return name; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -45,7 +45,7 @@ namespace Tensorflow.Keras | |||||
| public Regularizers regularizers { get; } = new Regularizers(); | public Regularizers regularizers { get; } = new Regularizers(); | ||||
| public ILayersApi layers { get; } = new LayersApi(); | public ILayersApi layers { get; } = new LayersApi(); | ||||
| public ILossesApi losses { get; } = new LossesApi(); | public ILossesApi losses { get; } = new LossesApi(); | ||||
| public Activations activations { get; } = new Activations(); | |||||
| public IActivationsApi activations { get; } = new Activations(); | |||||
| public Preprocessing preprocessing { get; } = new Preprocessing(); | public Preprocessing preprocessing { get; } = new Preprocessing(); | ||||
| ThreadLocal<BackendImpl> _backend = new ThreadLocal<BackendImpl>(() => new BackendImpl()); | ThreadLocal<BackendImpl> _backend = new ThreadLocal<BackendImpl>(() => new BackendImpl()); | ||||
| public BackendImpl backend => _backend.Value; | public BackendImpl backend => _backend.Value; | ||||
| @@ -110,7 +110,7 @@ namespace Tensorflow.Keras.Layers | |||||
| throw new NotImplementedException(""); | throw new NotImplementedException(""); | ||||
| if (activation != null) | if (activation != null) | ||||
| return activation(outputs); | |||||
| return activation.Apply(outputs); | |||||
| return outputs; | return outputs; | ||||
| } | } | ||||
| @@ -117,7 +117,7 @@ namespace Tensorflow.Keras.Layers | |||||
| } | } | ||||
| if (activation != null) | if (activation != null) | ||||
| outputs = activation(outputs); | |||||
| outputs = activation.Apply(outputs); | |||||
| return outputs; | return outputs; | ||||
| } | } | ||||
| @@ -81,7 +81,7 @@ namespace Tensorflow.Keras.Layers | |||||
| if (args.UseBias) | if (args.UseBias) | ||||
| outputs = tf.nn.bias_add(outputs, bias); | outputs = tf.nn.bias_add(outputs, bias); | ||||
| if (args.Activation != null) | if (args.Activation != null) | ||||
| outputs = activation(outputs); | |||||
| outputs = activation.Apply(outputs); | |||||
| return outputs; | return outputs; | ||||
| } | } | ||||
| @@ -193,7 +193,7 @@ namespace Tensorflow.Keras.Layers | |||||
| if (this.bias != null) | if (this.bias != null) | ||||
| ret += this.bias.AsTensor(); | ret += this.bias.AsTensor(); | ||||
| if (this.activation != null) | if (this.activation != null) | ||||
| ret = this.activation(ret); | |||||
| ret = this.activation.Apply(ret); | |||||
| return ret; | return ret; | ||||
| } | } | ||||
| /// <summary> | /// <summary> | ||||
| @@ -109,7 +109,7 @@ namespace Tensorflow.Keras.Layers | |||||
| DilationRate = dilation_rate, | DilationRate = dilation_rate, | ||||
| Groups = groups, | Groups = groups, | ||||
| UseBias = use_bias, | UseBias = use_bias, | ||||
| Activation = Activations.GetActivationByName(activation), | |||||
| Activation = keras.activations.GetActivationFromName(activation), | |||||
| KernelInitializer = GetInitializerByName(kernel_initializer), | KernelInitializer = GetInitializerByName(kernel_initializer), | ||||
| BiasInitializer = GetInitializerByName(bias_initializer) | BiasInitializer = GetInitializerByName(bias_initializer) | ||||
| }); | }); | ||||
| @@ -211,8 +211,7 @@ namespace Tensorflow.Keras.Layers | |||||
| UseBias = use_bias, | UseBias = use_bias, | ||||
| KernelInitializer = GetInitializerByName(kernel_initializer), | KernelInitializer = GetInitializerByName(kernel_initializer), | ||||
| BiasInitializer = GetInitializerByName(bias_initializer), | BiasInitializer = GetInitializerByName(bias_initializer), | ||||
| Activation = Activations.GetActivationByName(activation), | |||||
| ActivationName = activation | |||||
| Activation = keras.activations.GetActivationFromName(activation) | |||||
| }); | }); | ||||
| /// <summary> | /// <summary> | ||||
| @@ -257,7 +256,7 @@ namespace Tensorflow.Keras.Layers | |||||
| UseBias = use_bias, | UseBias = use_bias, | ||||
| KernelInitializer = GetInitializerByName(kernel_initializer), | KernelInitializer = GetInitializerByName(kernel_initializer), | ||||
| BiasInitializer = GetInitializerByName(bias_initializer), | BiasInitializer = GetInitializerByName(bias_initializer), | ||||
| Activation = Activations.GetActivationByName(activation) | |||||
| Activation = keras.activations.GetActivationFromName(activation) | |||||
| }); | }); | ||||
| /// <summary> | /// <summary> | ||||
| @@ -302,8 +301,7 @@ namespace Tensorflow.Keras.Layers | |||||
| => new Dense(new DenseArgs | => new Dense(new DenseArgs | ||||
| { | { | ||||
| Units = units, | Units = units, | ||||
| Activation = Activations.GetActivationByName("linear"), | |||||
| ActivationName = "linear" | |||||
| Activation = keras.activations.GetActivationFromName("linear") | |||||
| }); | }); | ||||
| /// <summary> | /// <summary> | ||||
| @@ -323,8 +321,7 @@ namespace Tensorflow.Keras.Layers | |||||
| => new Dense(new DenseArgs | => new Dense(new DenseArgs | ||||
| { | { | ||||
| Units = units, | Units = units, | ||||
| Activation = Activations.GetActivationByName(activation), | |||||
| ActivationName = activation, | |||||
| Activation = keras.activations.GetActivationFromName(activation), | |||||
| InputShape = input_shape | InputShape = input_shape | ||||
| }); | }); | ||||
| @@ -704,7 +701,7 @@ namespace Tensorflow.Keras.Layers | |||||
| => new SimpleRNN(new SimpleRNNArgs | => new SimpleRNN(new SimpleRNNArgs | ||||
| { | { | ||||
| Units = units, | Units = units, | ||||
| Activation = Activations.GetActivationByName(activation), | |||||
| Activation = keras.activations.GetActivationFromName(activation), | |||||
| KernelInitializer = GetInitializerByName(kernel_initializer), | KernelInitializer = GetInitializerByName(kernel_initializer), | ||||
| RecurrentInitializer = GetInitializerByName(recurrent_initializer), | RecurrentInitializer = GetInitializerByName(recurrent_initializer), | ||||
| BiasInitializer = GetInitializerByName(bias_initializer), | BiasInitializer = GetInitializerByName(bias_initializer), | ||||
| @@ -852,7 +849,6 @@ namespace Tensorflow.Keras.Layers | |||||
| public ILayer GlobalMaxPooling2D(string data_format = "channels_last") | public ILayer GlobalMaxPooling2D(string data_format = "channels_last") | ||||
| => new GlobalMaxPooling2D(new Pooling2DArgs { DataFormat = data_format }); | => new GlobalMaxPooling2D(new Pooling2DArgs { DataFormat = data_format }); | ||||
| Activation GetActivationByName(string name) => Activations.GetActivationByName(name); | |||||
| /// <summary> | /// <summary> | ||||
| /// Get an weights initializer from its name. | /// Get an weights initializer from its name. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -5,6 +5,8 @@ using Tensorflow.Keras.Engine; | |||||
| using static Tensorflow.Binding; | using static Tensorflow.Binding; | ||||
| using static Tensorflow.KerasApi; | using static Tensorflow.KerasApi; | ||||
| using Tensorflow.NumPy; | using Tensorflow.NumPy; | ||||
| using System; | |||||
| using Tensorflow.Keras.Optimizers; | |||||
| namespace TensorFlowNET.Keras.UnitTest; | namespace TensorFlowNET.Keras.UnitTest; | ||||
| @@ -40,7 +42,7 @@ public class GradientTest : EagerModeTestBase | |||||
| } | } | ||||
| [TestMethod] | [TestMethod] | ||||
| public void GetGradient_Test() | |||||
| public void GetGradientTest() | |||||
| { | { | ||||
| var numStates = 3; | var numStates = 3; | ||||
| var numActions = 1; | var numActions = 1; | ||||
| @@ -102,7 +102,7 @@ namespace TensorFlowNET.Keras.UnitTest { | |||||
| public void Mish() | public void Mish() | ||||
| { | { | ||||
| var x = tf.constant(new[] { 1.0, 0.0, 1.0 }, dtype: tf.float32); | var x = tf.constant(new[] { 1.0, 0.0, 1.0 }, dtype: tf.float32); | ||||
| var output = keras.activations.Mish(x); | |||||
| var output = keras.activations.Mish.Apply(x); | |||||
| Assert.AreEqual(new[] { 0.86509836f, 0f, 0.86509836f }, output.numpy()); | Assert.AreEqual(new[] { 0.86509836f, 0f, 0.86509836f }, output.numpy()); | ||||
| } | } | ||||
| } | } | ||||
| @@ -18,7 +18,7 @@ public class SequentialModelSave | |||||
| { | { | ||||
| var inputs = tf.keras.layers.Input((28, 28, 1)); | var inputs = tf.keras.layers.Input((28, 28, 1)); | ||||
| var x = tf.keras.layers.Flatten().Apply(inputs); | var x = tf.keras.layers.Flatten().Apply(inputs); | ||||
| x = tf.keras.layers.Dense(100, activation: tf.nn.relu).Apply(x); | |||||
| x = tf.keras.layers.Dense(100, activation: "relu").Apply(x); | |||||
| x = tf.keras.layers.Dense(units: 10).Apply(x); | x = tf.keras.layers.Dense(units: 10).Apply(x); | ||||
| var outputs = tf.keras.layers.Softmax(axis: 1).Apply(x); | var outputs = tf.keras.layers.Softmax(axis: 1).Apply(x); | ||||
| var model = tf.keras.Model(inputs, outputs); | var model = tf.keras.Model(inputs, outputs); | ||||
| @@ -110,7 +110,7 @@ public class SequentialModelSave | |||||
| tf.keras.layers.Softmax(1) | tf.keras.layers.Softmax(1) | ||||
| }); | }); | ||||
| model.compile(new Adam(0.001f), tf.keras.losses.SparseCategoricalCrossentropy(from_logits: true), new string[] { "accuracy" }); | |||||
| model.compile(tf.keras.optimizers.Adam(), tf.keras.losses.SparseCategoricalCrossentropy(from_logits: true), new string[] { "accuracy" }); | |||||
| var num_epochs = 1; | var num_epochs = 1; | ||||
| var batch_size = 8; | var batch_size = 8; | ||||