@@ -63,7 +63,7 @@ Import TF.NET and Keras API in your project. | |||
using static Tensorflow.Binding; | |||
using static Tensorflow.KerasApi; | |||
using Tensorflow; | |||
using NumSharp; | |||
using Tensorflow.NumPy; | |||
``` | |||
Linear Regression in `Eager` mode: | |||
@@ -162,10 +162,9 @@ Linear Regression in `Eager` mode: | |||
#r "nuget: TensorFlow.Net" | |||
#r "nuget: TensorFlow.Keras" | |||
#r "nuget: SciSharp.TensorFlow.Redist" | |||
#r "nuget: NumSharp" | |||
open NumSharp | |||
open Tensorflow | |||
open Tensorflow.NumPy | |||
open type Tensorflow.Binding | |||
open type Tensorflow.KerasApi | |||
@@ -203,13 +203,6 @@ namespace Tensorflow | |||
yield return values[i]; | |||
} | |||
public static T New<T>() where T : ITensorFlowObject, new() | |||
{ | |||
var instance = new T(); | |||
instance.__init__(); | |||
return instance; | |||
} | |||
[DebuggerStepThrough] | |||
public static void tf_with(ITensorFlowObject py, Action<ITensorFlowObject> action) | |||
{ | |||
@@ -81,7 +81,7 @@ namespace Tensorflow.Eager | |||
if (ops.gradientFunctions[op_name] == null) | |||
return new Tensor[op_inputs.Length]; | |||
var gradients = ops.gradientFunctions[op_name](new EagerOperation | |||
var op = new EagerOperation | |||
{ | |||
Name = op_name, | |||
NumInputs = op_inputs.Length, | |||
@@ -90,9 +90,9 @@ namespace Tensorflow.Eager | |||
Outputs = op_outputs, | |||
SkipInputIndices = unneeded_gradients, | |||
Attrs = attrs | |||
}, output_grads); | |||
}; | |||
return gradients; | |||
return ops.gradientFunctions[op_name](op, output_grads); | |||
}; | |||
bool CouldForwardprop() | |||
@@ -20,15 +20,8 @@ namespace Tensorflow | |||
{ | |||
public interface ITensorFlowObject : IDisposable | |||
{ | |||
/// <summary> | |||
/// Called when the instance is created. | |||
/// </summary> | |||
void __init__(); | |||
void __enter__(); | |||
void __exit__(); | |||
void __del__(); | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
using static Tensorflow.Binding; | |||
namespace Tensorflow.Keras.ArgsDefinition | |||
{ | |||
public class LayerNormalizationArgs : LayerArgs | |||
{ | |||
public Axis Axis { get; set; } = -1; | |||
public float Epsilon { get; set; } = 1e-3f; | |||
public bool Center { get; set; } = true; | |||
public bool Scale { get; set; } = true; | |||
public IInitializer BetaInitializer { get; set; } = tf.zeros_initializer; | |||
public IInitializer GammaInitializer { get; set; } = tf.ones_initializer; | |||
public IRegularizer BetaRegularizer { get; set; } | |||
public IRegularizer GammaRegularizer { get; set; } | |||
} | |||
} |
@@ -89,8 +89,8 @@ tf.net 0.6x.x aligns with TensorFlow v2.6.x native library.</PackageReleaseNotes | |||
</ItemGroup> | |||
<ItemGroup> | |||
<PackageReference Include="MethodBoundaryAspect.Fody" Version="2.0.139" /> | |||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" /> | |||
<PackageReference Include="MethodBoundaryAspect.Fody" Version="2.0.144" /> | |||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" /> | |||
<PackageReference Include="Protobuf.Text" Version="0.5.0" /> | |||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" /> | |||
</ItemGroup> | |||
@@ -316,7 +316,7 @@ namespace Tensorflow.Keras.Engine | |||
var outputs = node.Layer.Apply(layer_inputs, is_training: training ?? false); | |||
foreach (var output in outputs.Where(x => x != null)) | |||
tf.Logger.Information($"Depth {depth}: {node.Layer}: {node.Layer.Name} {output.shape}"); | |||
// Update tensor_dict for next input | |||
// Update tensor_dict for next or later input | |||
foreach (var (x_id, y) in zip(node.Outputs.Select(x => x.Id), outputs)) | |||
tensor_dict[x_id] = new Queue<Tensor>(Enumerable.Range(0, tensor_usage_count[x_id]).Select(x => y)); | |||
} | |||
@@ -635,6 +635,21 @@ namespace Tensorflow.Keras.Layers | |||
return layer.Apply(inputs); | |||
} | |||
public Layer LayerNormalization(Axis? axis, | |||
float epsilon = 1e-3f, | |||
bool center = true, | |||
bool scale = true, | |||
IInitializer beta_initializer = null, | |||
IInitializer gamma_initializer = null) | |||
=> new LayerNormalization(new LayerNormalizationArgs | |||
{ | |||
Axis = axis ?? -1, | |||
Epsilon = epsilon, | |||
Center = center, | |||
Scale = scale, | |||
BetaInitializer = beta_initializer ?? tf.zeros_initializer | |||
}); | |||
/// <summary> | |||
/// Leaky version of a Rectified Linear Unit. | |||
/// </summary> | |||
@@ -218,7 +218,8 @@ namespace Tensorflow.Keras.Layers | |||
beta, | |||
mean: moving_mean, | |||
variance: moving_variance, | |||
epsilon: epsilon, is_training: true, | |||
epsilon: epsilon, | |||
is_training: true, | |||
data_format: _data_format, | |||
exponential_avg_factor: exponential_avg_factor); | |||
}; | |||
@@ -0,0 +1,145 @@ | |||
/***************************************************************************** | |||
Copyright 2021 Haiping Chen. All Rights Reserved. | |||
Licensed under the Apache License, Version 2.0 (the "License"); | |||
you may not use this file except in compliance with the License. | |||
You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
******************************************************************************/ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using Tensorflow.Keras.ArgsDefinition; | |||
using Tensorflow.Keras.Engine; | |||
using Tensorflow.Keras.Utils; | |||
using static Tensorflow.Binding; | |||
namespace Tensorflow.Keras.Layers | |||
{ | |||
public class LayerNormalization : Layer | |||
{ | |||
LayerNormalizationArgs args; | |||
float epsilon => args.Epsilon; | |||
bool center => args.Center; | |||
bool scale => args.Scale; | |||
bool _fused; | |||
int[] axis; | |||
string _data_format; | |||
Shape kernel_size; | |||
IInitializer beta_initializer => args.BetaInitializer; | |||
IInitializer gamma_initializer => args.GammaInitializer; | |||
IRegularizer gamma_regularizer => args.GammaRegularizer; | |||
IVariableV1 gamma; | |||
IVariableV1 beta; | |||
IVariableV1 moving_mean; | |||
IVariableV1 moving_variance; | |||
public LayerNormalization(LayerNormalizationArgs args) : base(args) | |||
{ | |||
this.args = args; | |||
axis = args.Axis.axis; | |||
} | |||
protected override void build(Tensors inputs) | |||
{ | |||
Shape input_shape = inputs.shape; | |||
var ndims = input_shape.ndim; | |||
foreach (var (idx, x) in enumerate(axis)) | |||
if (x < 0) | |||
axis[idx] = ndims + x; | |||
var axis_to_dim = new Dictionary<int, int>(); | |||
foreach (var x in axis) | |||
axis_to_dim[x] = (int)input_shape[x]; | |||
inputSpec = new InputSpec(ndim: ndims, axes: axis_to_dim); | |||
var param_dtype = DType == TF_DataType.DtInvalid ? TF_DataType.TF_FLOAT : DType; | |||
var param_shape = inputSpec.AllAxisDim; | |||
if (scale) | |||
gamma = add_weight("gamma", | |||
param_shape, | |||
dtype: param_dtype, | |||
initializer: gamma_initializer, | |||
trainable: true); | |||
if (center) | |||
beta = add_weight("beta", | |||
param_shape, | |||
dtype: param_dtype, | |||
initializer: beta_initializer, | |||
trainable: true); | |||
_fused = _fused_can_be_used(ndims); | |||
built = true; | |||
} | |||
bool _fused_can_be_used(int ndims) | |||
{ | |||
var can_use_fused = false; | |||
if (axis.Last() == ndims - 1 && axis.Last() - axis[0] == len(axis) - 1) | |||
can_use_fused = true; | |||
if (epsilon < 1.001e-5 || DType != tf.float32) | |||
can_use_fused = false; | |||
return can_use_fused; | |||
} | |||
public override Shape ComputeOutputShape(Shape input_shape) | |||
{ | |||
return input_shape; | |||
} | |||
protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null) | |||
{ | |||
Tensors outputs = null; | |||
var inputs_dtype = inputs.dtype.as_base_dtype(); | |||
var input_shape = inputs.shape; | |||
var ndims = len(input_shape); | |||
var broadcast_shape = range(ndims).Select(x => 1).ToArray(); | |||
foreach (var dim in axis) | |||
broadcast_shape[dim] = input_shape.as_int_list()[dim]; | |||
if (_fused) | |||
{ | |||
var tensor_shape = tf.shape(inputs); | |||
var pre_dim = tf.constant(1); | |||
var in_dim = tf.constant(1); | |||
foreach (var dim in range(ndims)) | |||
{ | |||
var dim_tensor = tensor_shape[dim]; | |||
if (dim < axis[0]) | |||
pre_dim = pre_dim * dim_tensor; | |||
else | |||
in_dim = in_dim * dim_tensor; | |||
} | |||
inputs = tf.reshape(inputs, new object[] { 1, pre_dim, in_dim, 1 }); | |||
var scale = tf.ones(new Shape((int)pre_dim), dtype: DType); | |||
var offset = tf.zeros(new Shape((int)pre_dim), dtype: DType); | |||
/*outputs = tf.nn.fused_batch_norm( | |||
inputs, | |||
scale: scale, | |||
offset: offset, | |||
epsilon: epsilon, | |||
data_format: "NCHW");*/ | |||
} | |||
else | |||
{ | |||
} | |||
return outputs; | |||
} | |||
} | |||
} |
@@ -60,7 +60,7 @@ Keras is an API designed for human beings, not machines. Keras follows best prac | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="MethodBoundaryAspect.Fody" Version="2.0.139" /> | |||
<PackageReference Include="MethodBoundaryAspect.Fody" Version="2.0.144" /> | |||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> | |||
<PackageReference Include="SciSharp.Keras.HDF5" Version="1.1.10.500" /> | |||
<PackageReference Include="SharpZipLib" Version="1.3.3" /> | |||
@@ -26,7 +26,7 @@ namespace TensorFlowNET.UnitTest | |||
using (var sess = tf.Session()) | |||
{ | |||
var default_graph = tf.peak_default_graph(); | |||
var default_graph = tf.get_default_graph(); | |||
var sess_graph = sess.graph; | |||
Assert.IsNotNull(default_graph); | |||
Assert.IsNotNull(sess_graph); | |||
@@ -49,7 +49,7 @@ namespace TensorFlowNET.UnitTest | |||
//tf.Session created an other graph | |||
using (var sess = tf.Session()) | |||
{ | |||
var default_graph = tf.peak_default_graph(); | |||
var default_graph = tf.get_default_graph(); | |||
var sess_graph = sess.graph; | |||
Assert.IsNotNull(default_graph); | |||
Assert.IsNotNull(sess_graph); | |||
@@ -159,7 +159,8 @@ namespace TensorFlowNET.UnitTest | |||
var math = a1 + a2; | |||
for (int i = 0; i < 100; i++) | |||
{ | |||
using (var sess = tf.Session()) | |||
var graph = tf.get_default_graph(); | |||
using (var sess = tf.Session(graph)) | |||
{ | |||
var result = sess.run(math); | |||
Assert.AreEqual(result[0], 5f); | |||
@@ -171,14 +172,14 @@ namespace TensorFlowNET.UnitTest | |||
[TestMethod] | |||
public void SessionRun_InsideSession() | |||
{ | |||
MultiThreadedUnitTestExecuter.Run(1, Core); | |||
MultiThreadedUnitTestExecuter.Run(8, Core); | |||
//the core method | |||
void Core(int tid) | |||
{ | |||
using (var sess = tf.Session()) | |||
{ | |||
Assert.IsNotNull(tf.peak_default_graph()); | |||
Assert.IsNotNull(tf.get_default_graph()); | |||
//graph is created automatically to perform create these operations | |||
var a1 = tf.constant(new[] { 2f }, shape: new[] { 1 }); | |||
var a2 = tf.constant(new[] { 3f }, shape: new[] { 1 }); | |||
@@ -200,7 +201,7 @@ namespace TensorFlowNET.UnitTest | |||
{ | |||
using (var sess = tf.Session()) | |||
{ | |||
Assert.IsNotNull(tf.peak_default_graph()); | |||
Assert.IsNotNull(tf.get_default_graph()); | |||
//graph is created automatically to perform create these operations | |||
var a1 = tf.constant(new[] { 2f }, shape: new[] { 1 }); | |||
var a2 = tf.constant(new[] { 3f }, shape: new[] { 1 }); | |||
@@ -24,7 +24,7 @@ | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> | |||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" /> | |||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.7" /> | |||
<PackageReference Include="MSTest.TestFramework" Version="2.2.7" /> | |||
<PackageReference Include="coverlet.collector" Version="3.1.0"> | |||
@@ -152,5 +152,14 @@ namespace TensorFlowNET.Keras.UnitTest | |||
var output = layer.Apply(inputs); | |||
Assert.AreEqual((10, 16, 16, 3), output.shape); | |||
} | |||
[TestMethod] | |||
public void LayerNormalization() | |||
{ | |||
var inputs = tf.constant(np.arange(10).reshape((5, 2)) * 10, dtype: tf.float32); | |||
var layer = keras.layers.LayerNormalization(axis: 1); | |||
var output = layer.Apply(inputs); | |||
// Assert.AreEqual((10, 16, 16, 3), output.shape); | |||
} | |||
} | |||
} |
@@ -14,7 +14,7 @@ | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> | |||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" /> | |||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.7" /> | |||
<PackageReference Include="MSTest.TestFramework" Version="2.2.7" /> | |||
<PackageReference Include="coverlet.collector" Version="3.1.0"> | |||
@@ -44,7 +44,7 @@ | |||
<ItemGroup> | |||
<PackageReference Include="FluentAssertions" Version="5.10.3" /> | |||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> | |||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" /> | |||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.7" /> | |||
<PackageReference Include="MSTest.TestFramework" Version="2.2.7" /> | |||
<PackageReference Include="coverlet.collector" Version="3.1.0"> | |||
@@ -47,8 +47,8 @@ | |||
<ItemGroup> | |||
<PackageReference Include="FluentAssertions" Version="5.10.3" /> | |||
<PackageReference Include="MethodBoundaryAspect.Fody" Version="2.0.139" /> | |||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> | |||
<PackageReference Include="MethodBoundaryAspect.Fody" Version="2.0.144" /> | |||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" /> | |||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.7" /> | |||
<PackageReference Include="MSTest.TestFramework" Version="2.2.7" /> | |||
<PackageReference Include="SciSharp.TensorFlow.Redist" Version="2.6.0" /> | |||