Browse Source

Merge branch 'master' of https://github.com/NiklasGustafsson/TensorFlow.NET

tags/v0.40-tf2.4-tstring
Niklas Gustafsson 4 years ago
parent
commit
e0287459c9
9 changed files with 531 additions and 0 deletions
  1. +34
    -0
      src/TensorFlowNET.Core/Keras/ArgsDefinition/Pooling/Pooling1DArgs.cs
  2. +24
    -0
      src/TensorFlowNET.Keras/Layers/LayersApi.cs
  3. +23
    -0
      src/TensorFlowNET.Keras/Layers/Pooling/GlobalAveragePooling1D.cs
  4. +23
    -0
      src/TensorFlowNET.Keras/Layers/Pooling/GlobalMaxPooling1D.cs
  5. +23
    -0
      src/TensorFlowNET.Keras/Layers/Pooling/GlobalMaxPooling2D.cs
  6. +23
    -0
      src/TensorFlowNET.Keras/Layers/Pooling/GlobalPooling1D.cs
  7. +14
    -0
      src/TensorFlowNET.Keras/Layers/Pooling/MaxPooling1D.cs
  8. +62
    -0
      src/TensorFlowNET.Keras/Layers/Pooling/Pooling1D.cs
  9. +305
    -0
      test/TensorFlowNET.Keras.UnitTest/Layers/PoolingTest.cs

+ 34
- 0
src/TensorFlowNET.Core/Keras/ArgsDefinition/Pooling/Pooling1DArgs.cs View File

@@ -0,0 +1,34 @@
namespace Tensorflow.Keras.ArgsDefinition
{
public class Pooling1DArgs : LayerArgs
{
/// <summary>
/// The pooling function to apply, e.g. `tf.nn.max_pool2d`.
/// </summary>
public IPoolFunction PoolFunction { get; set; }

/// <summary>
/// specifying the size of the pooling window.
/// </summary>
public int PoolSize { get; set; }

/// <summary>
/// specifying the strides of the pooling operation.
/// </summary>
public int Strides {
get { return _strides.HasValue ? _strides.Value : PoolSize; }
set { _strides = value; }
}
private int? _strides = null;

/// <summary>
/// The padding method, either 'valid' or 'same'.
/// </summary>
public string Padding { get; set; } = "valid";

/// <summary>
/// one of `channels_last` (default) or `channels_first`.
/// </summary>
public string DataFormat { get; set; }
}
}

+ 24
- 0
src/TensorFlowNET.Keras/Layers/LayersApi.cs View File

@@ -325,6 +325,16 @@ namespace Tensorflow.Keras.Layers
return input_layer.InboundNodes[0].Outputs; return input_layer.InboundNodes[0].Outputs;
} }


public MaxPooling1D MaxPooling1D(int? pool_size = null,
int? strides = null,
string padding = "valid")
=> new MaxPooling1D(new Pooling1DArgs
{
PoolSize = pool_size ?? 2,
Strides = strides ?? (pool_size ?? 2),
Padding = padding
});

public MaxPooling2D MaxPooling2D(TensorShape pool_size = null, public MaxPooling2D MaxPooling2D(TensorShape pool_size = null,
TensorShape strides = null, TensorShape strides = null,
string padding = "valid") string padding = "valid")
@@ -448,6 +458,20 @@ namespace Tensorflow.Keras.Layers
public GlobalAveragePooling2D GlobalAveragePooling2D() public GlobalAveragePooling2D GlobalAveragePooling2D()
=> new GlobalAveragePooling2D(new Pooling2DArgs { }); => new GlobalAveragePooling2D(new Pooling2DArgs { });


public GlobalAveragePooling1D GlobalAveragePooling1D(string data_format = "channels_last")
=> new GlobalAveragePooling1D(new Pooling1DArgs { DataFormat = data_format });

public GlobalAveragePooling2D GlobalAveragePooling2D(string data_format = "channels_last")
=> new GlobalAveragePooling2D(new Pooling2DArgs { DataFormat = data_format });

public GlobalMaxPooling1D GlobalMaxPooling1D(string data_format = "channels_last")
=> new GlobalMaxPooling1D(new Pooling1DArgs { DataFormat = data_format });

public GlobalMaxPooling2D GlobalMaxPooling2D(string data_format = "channels_last")
=> new GlobalMaxPooling2D(new Pooling2DArgs { DataFormat = data_format });



Activation GetActivationByName(string name) Activation GetActivationByName(string name)
=> name switch => name switch
{ {


+ 23
- 0
src/TensorFlowNET.Keras/Layers/Pooling/GlobalAveragePooling1D.cs View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;
using Tensorflow.Keras.ArgsDefinition;

namespace Tensorflow.Keras.Layers
{
public class GlobalAveragePooling1D : GlobalPooling1D
{
public GlobalAveragePooling1D(Pooling1DArgs args)
: base(args)
{
}

protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null)
{
if (data_format == "channels_last")
return math_ops.reduce_mean(inputs, new int[] { 1 }, false);
else
return math_ops.reduce_mean(inputs, new int[] { 2 }, false);
}
}
}

+ 23
- 0
src/TensorFlowNET.Keras/Layers/Pooling/GlobalMaxPooling1D.cs View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;
using Tensorflow.Keras.ArgsDefinition;

namespace Tensorflow.Keras.Layers
{
public class GlobalMaxPooling1D : GlobalPooling1D
{
public GlobalMaxPooling1D(Pooling1DArgs args)
: base(args)
{
}

protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null)
{
if (data_format == "channels_last")
return math_ops.reduce_max(inputs, new int[] { 1 }, false);
else
return math_ops.reduce_max(inputs, new int[] { 2 }, false);
}
}
}

+ 23
- 0
src/TensorFlowNET.Keras/Layers/Pooling/GlobalMaxPooling2D.cs View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;
using Tensorflow.Keras.ArgsDefinition;

namespace Tensorflow.Keras.Layers
{
public class GlobalMaxPooling2D : GlobalPooling2D
{
public GlobalMaxPooling2D(Pooling2DArgs args)
: base(args)
{
}

protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null)
{
if (data_format == "channels_last")
return math_ops.reduce_max(inputs, new int[] { 1, 2 }, false);
else
return math_ops.reduce_max(inputs, new int[] { 2, 3 }, false);
}
}
}

+ 23
- 0
src/TensorFlowNET.Keras/Layers/Pooling/GlobalPooling1D.cs View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;
using Tensorflow.Keras.ArgsDefinition;
using Tensorflow.Keras.Engine;
using Tensorflow.Keras.Utils;

namespace Tensorflow.Keras.Layers
{
public abstract class GlobalPooling1D : Layer
{
Pooling1DArgs args;
protected string data_format => args.DataFormat;
protected InputSpec input_spec;

public GlobalPooling1D(Pooling1DArgs args) : base(args)
{
this.args = args;
args.DataFormat = conv_utils.normalize_data_format(data_format);
input_spec = new InputSpec(ndim: 3);
}
}
}

+ 14
- 0
src/TensorFlowNET.Keras/Layers/Pooling/MaxPooling1D.cs View File

@@ -0,0 +1,14 @@
using Tensorflow.Keras.ArgsDefinition;
using Tensorflow.Operations;

namespace Tensorflow.Keras.Layers
{
public class MaxPooling1D : Pooling1D
{
public MaxPooling1D(Pooling1DArgs args)
: base(args)
{
args.PoolFunction = new MaxPoolFunction();
}
}
}

+ 62
- 0
src/TensorFlowNET.Keras/Layers/Pooling/Pooling1D.cs View File

@@ -0,0 +1,62 @@
/*****************************************************************************
Copyright 2018 The TensorFlow.NET Authors. 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 Tensorflow.Keras.ArgsDefinition;
using Tensorflow.Keras.Engine;
using Tensorflow.Keras.Utils;

namespace Tensorflow.Keras.Layers
{
public class Pooling1D : Layer
{
Pooling1DArgs args;
InputSpec input_spec;

public Pooling1D(Pooling1DArgs args)
: base(args)
{
this.args = args;
args.Padding = conv_utils.normalize_padding(args.Padding);
args.DataFormat = conv_utils.normalize_data_format(args.DataFormat);
input_spec = new InputSpec(ndim: 3);
}

protected override Tensors Call(Tensors inputs, Tensor state = null, bool? training = null)
{
int[] pool_shape;
int[] strides;
if (args.DataFormat == "channels_last")
{
pool_shape = new int[] { 1, args.PoolSize, 1 };
strides = new int[] { 1, args.Strides, 1 };
}
else
{
pool_shape = new int[] { 1, 1, args.PoolSize };
strides = new int[] { 1, 1, args.Strides };
}

var outputs = args.PoolFunction.Apply(
inputs,
ksize: pool_shape,
strides: strides,
padding: args.Padding.ToUpper(),
data_format: conv_utils.convert_data_format(args.DataFormat, 3));

return outputs;
}
}
}

+ 305
- 0
test/TensorFlowNET.Keras.UnitTest/Layers/PoolingTest.cs View File

@@ -0,0 +1,305 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NumSharp;
using System.Linq;
using Tensorflow;
using static Tensorflow.Binding;
using static Tensorflow.KerasApi;

namespace TensorFlowNET.Keras.UnitTest
{
/// <summary>
/// https://www.tensorflow.org/versions/r2.3/api_docs/python/tf/keras/layers
/// </summary>
[TestClass]
public class PoolingTest : EagerModeTestBase
{
private NDArray input_array_1D = np.array(new float[,,]
{
{{1,2,3,3,3},{1,2,3,3,3},{1,2,3,3,3}},
{{4,5,6,3,3},{4,5,6,3,3},{4,5,6,3,3}},
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}},
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}}
});

private NDArray input_array_2D = np.array(new float[,,,]
{{
{{1,2,3,3,3},{1,2,3,3,3},{1,2,3,3,3}},
{{4,5,6,3,3},{4,5,6,3,3},{4,5,6,3,3}},
},{
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}},
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}}
},{
{{1,2,3,3,3},{1,2,3,3,3},{1,2,3,3,3}},
{{4,5,6,3,3},{4,5,6,3,3},{4,5,6,3,3}},
},{
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}},
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}}
}});

[TestMethod]
public void GlobalAverage1DPoolingChannelsLast()
{
var pool = keras.layers.GlobalAveragePooling1D();
var y = pool.Apply(input_array_1D);

Assert.AreEqual(4, y.shape[0]);
Assert.AreEqual(5, y.shape[1]);

var expected = np.array(new float[,]
{
{1,2,3,3,3},
{4,5,6,3,3},
{7,8,9,3,3},
{7,8,9,3,3}
});

Assert.AreEqual(expected, y[0].numpy());
}

[TestMethod]
public void GlobalAverage1DPoolingChannelsFirst()
{
var pool = keras.layers.GlobalAveragePooling1D(data_format: "channels_first");
var y = pool.Apply(input_array_1D);

Assert.AreEqual(4, y.shape[0]);
Assert.AreEqual(3, y.shape[1]);

var expected = np.array(new float[,]
{
{2.4f, 2.4f, 2.4f},
{4.2f, 4.2f, 4.2f},
{6.0f, 6.0f, 6.0f},
{6.0f, 6.0f, 6.0f}
});

Assert.AreEqual(expected, y[0].numpy());
}

[TestMethod]
public void GlobalAverage2DPoolingChannelsLast()
{
var pool = keras.layers.GlobalAveragePooling2D();
var y = pool.Apply(input_array_2D);

Assert.AreEqual(4, y.shape[0]);
Assert.AreEqual(5, y.shape[1]);

var expected = np.array(new float[,]
{
{2.5f, 3.5f, 4.5f, 3.0f, 3.0f},
{7.0f, 8.0f, 9.0f, 3.0f, 3.0f},
{2.5f, 3.5f, 4.5f, 3.0f, 3.0f},
{7.0f, 8.0f, 9.0f, 3.0f, 3.0f}
});

Assert.AreEqual(expected, y[0].numpy());
}

[TestMethod]
public void GlobalAverage2DPoolingChannelsFirst()
{
var pool = keras.layers.GlobalAveragePooling2D(data_format: "channels_first");
var y = pool.Apply(input_array_2D);

Assert.AreEqual(4, y.shape[0]);
Assert.AreEqual(2, y.shape[1]);

var expected = np.array(new float[,]
{
{2.4f, 4.2f},
{6.0f, 6.0f},
{2.4f, 4.2f},
{6.0f, 6.0f}
});

Assert.AreEqual(expected, y[0].numpy());
}

[TestMethod]
public void GlobalMax1DPoolingChannelsLast()
{
var pool = keras.layers.GlobalMaxPooling1D();
var y = pool.Apply(input_array_1D);

Assert.AreEqual(4, y.shape[0]);
Assert.AreEqual(5, y.shape[1]);

var expected = np.array(new float[,]
{
{1,2,3,3,3},
{4,5,6,3,3},
{7,8,9,3,3},
{7,8,9,3,3}
});

Assert.AreEqual(expected, y[0].numpy());
}

[TestMethod]
public void GlobalMax1DPoolingChannelsFirst()
{
var pool = keras.layers.GlobalMaxPooling1D(data_format: "channels_first");
var y = pool.Apply(input_array_1D);

Assert.AreEqual(4, y.shape[0]);
Assert.AreEqual(3, y.shape[1]);

var expected = np.array(new float[,]
{
{3.0f, 3.0f, 3.0f},
{6.0f, 6.0f, 6.0f},
{9.0f, 9.0f, 9.0f},
{9.0f, 9.0f, 9.0f}
});

Assert.AreEqual(expected, y[0].numpy());
}

[TestMethod]
public void GlobalMax2DPoolingChannelsLast()
{
var input_array_2D = np.array(new float[,,,]
{{
{{1,2,3,3,3},{1,2,3,3,3},{1,2,3,9,3}},
{{4,5,6,3,3},{4,5,6,3,3},{4,5,6,3,3}},
},{
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}},
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}}
},{
{{1,2,3,3,3},{1,2,3,3,3},{1,2,3,3,9}},
{{4,5,6,3,3},{4,5,6,3,3},{4,5,6,3,3}},
},{
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}},
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}}
}});

var pool = keras.layers.GlobalMaxPooling2D();
var y = pool.Apply(input_array_2D);

Assert.AreEqual(4, y.shape[0]);
Assert.AreEqual(5, y.shape[1]);

var expected = np.array(new float[,]
{
{4.0f, 5.0f, 6.0f, 9.0f, 3.0f},
{7.0f, 8.0f, 9.0f, 3.0f, 3.0f},
{4.0f, 5.0f, 6.0f, 3.0f, 9.0f},
{7.0f, 8.0f, 9.0f, 3.0f, 3.0f}
});

Assert.AreEqual(expected, y[0].numpy());
}

[TestMethod]
public void GlobalMax2DPoolingChannelsFirst()
{
var input_array_2D = np.array(new float[,,,]
{{
{{1,2,3,3,3},{1,2,3,3,3},{1,2,3,9,3}},
{{4,5,6,3,3},{4,5,6,3,3},{4,5,6,3,3}},
},{
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}},
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}}
},{
{{1,2,3,3,3},{1,2,3,3,3},{1,2,3,3,9}},
{{4,5,6,3,3},{4,5,6,3,3},{4,5,6,3,3}},
},{
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}},
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}}
}});

var pool = keras.layers.GlobalMaxPooling2D(data_format: "channels_first");
var y = pool.Apply(input_array_2D);

Assert.AreEqual(4, y.shape[0]);
Assert.AreEqual(2, y.shape[1]);

var expected = np.array(new float[,]
{
{9.0f, 6.0f},
{9.0f, 9.0f},
{9.0f, 6.0f},
{9.0f, 9.0f}
});

Assert.AreEqual(expected, y[0].numpy());
}

[TestMethod, Ignore("There's an error generated from TF complaining about the shape of the pool. Needs further investigation.")]
public void Max1DPoolingChannelsLast()
{
var x = input_array_1D;
var pool = keras.layers.MaxPooling1D(pool_size:2, strides:1);
var y = pool.Apply(x);

Assert.AreEqual(4, y.shape[0]);
Assert.AreEqual(2, y.shape[1]);
Assert.AreEqual(5, y.shape[2]);

var expected = np.array(new float[,,]
{
{{2.0f, 2.0f, 3.0f, 3.0f, 3.0f},
{ 1.0f, 2.0f, 3.0f, 3.0f, 3.0f}},

{{4.0f, 5.0f, 6.0f, 3.0f, 3.0f},
{4.0f, 5.0f, 6.0f, 3.0f, 3.0f}},

{{7.0f, 8.0f, 9.0f, 3.0f, 3.0f},
{7.0f, 8.0f, 9.0f, 3.0f, 3.0f}},

{{7.0f, 8.0f, 9.0f, 3.0f, 3.0f},
{7.0f, 8.0f, 9.0f, 3.0f, 3.0f}}
});

Assert.AreEqual(expected, y[0].numpy());
}

[TestMethod]
public void Max2DPoolingChannelsLast()
{
var x = np.array(new float[,,,]
{{
{{1,2,3,3,3},{1,2,3,3,3},{1,2,3,9,3}},
{{4,5,6,3,3},{4,5,6,3,3},{4,5,6,3,3}},
},{
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}},
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}}
},{
{{1,2,3,3,3},{1,2,3,3,3},{1,2,3,3,9}},
{{4,5,6,3,3},{4,5,6,3,3},{4,5,6,3,3}},
},{
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}},
{{7,8,9,3,3},{7,8,9,3,3},{7,8,9,3,3}}
}});

var pool = keras.layers.MaxPooling2D(pool_size: 2, strides: 1);
var y = pool.Apply(x);

Assert.AreEqual(4, y.shape[0]);
Assert.AreEqual(1, y.shape[1]);
Assert.AreEqual(2, y.shape[2]);
Assert.AreEqual(5, y.shape[3]);

var expected = np.array(new float[,,,]
{
{{{4.0f, 5.0f, 6.0f, 3.0f, 3.0f},
{4.0f, 5.0f, 6.0f, 9.0f, 3.0f}}},


{{{7.0f, 8.0f, 9.0f, 3.0f, 3.0f},
{7.0f, 8.0f, 9.0f, 3.0f, 3.0f}}},


{{{4.0f, 5.0f, 6.0f, 3.0f, 3.0f},
{4.0f, 5.0f, 6.0f, 3.0f, 9.0f}}},


{{{7.0f, 8.0f, 9.0f, 3.0f, 3.0f},
{7.0f, 8.0f, 9.0f, 3.0f, 3.0f}}}
});

Assert.AreEqual(expected, y[0].numpy());
}
}
}

Loading…
Cancel
Save