diff --git a/src/TensorFlowNET.Core/Keras/ArgsDefinition/Activation/ELUArgs.cs b/src/TensorFlowNET.Core/Keras/ArgsDefinition/Activation/ELUArgs.cs
new file mode 100644
index 00000000..23552316
--- /dev/null
+++ b/src/TensorFlowNET.Core/Keras/ArgsDefinition/Activation/ELUArgs.cs
@@ -0,0 +1,9 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tensorflow.Keras.ArgsDefinition {
+ public class ELUArgs : LayerArgs {
+ public float Alpha { get; set; } = 0.1f;
+ }
+}
diff --git a/src/TensorFlowNET.Core/Keras/ArgsDefinition/Cropping/Cropping2DArgs.cs b/src/TensorFlowNET.Core/Keras/ArgsDefinition/Cropping/Cropping2DArgs.cs
new file mode 100644
index 00000000..16705063
--- /dev/null
+++ b/src/TensorFlowNET.Core/Keras/ArgsDefinition/Cropping/Cropping2DArgs.cs
@@ -0,0 +1,16 @@
+using Tensorflow.NumPy;
+
+namespace Tensorflow.Keras.ArgsDefinition {
+ public class Cropping2DArgs : LayerArgs {
+ ///
+ /// channel last: (b, h, w, c)
+ /// channels_first: (b, c, h, w)
+ ///
+ public enum DataFormat { channels_first = 0, channels_last = 1 }
+ ///
+ /// Accept: int[1][2], int[1][1], int[2][2]
+ ///
+ public NDArray cropping { get; set; }
+ public DataFormat data_format { get; set; } = DataFormat.channels_last;
+ }
+}
diff --git a/src/TensorFlowNET.Core/Keras/ArgsDefinition/Cropping/Cropping3DArgs.cs b/src/TensorFlowNET.Core/Keras/ArgsDefinition/Cropping/Cropping3DArgs.cs
new file mode 100644
index 00000000..9da2adc7
--- /dev/null
+++ b/src/TensorFlowNET.Core/Keras/ArgsDefinition/Cropping/Cropping3DArgs.cs
@@ -0,0 +1,16 @@
+using Tensorflow.NumPy;
+
+namespace Tensorflow.Keras.ArgsDefinition {
+ public class Cropping3DArgs : LayerArgs {
+ ///
+ /// channel last: (b, h, w, c)
+ /// channels_first: (b, c, h, w)
+ ///
+ public enum DataFormat { channels_first = 0, channels_last = 1 }
+ ///
+ /// Accept: int[1][3], int[1][1], int[3][2]
+ ///
+ public NDArray cropping { get; set; }
+ public DataFormat data_format { get; set; } = DataFormat.channels_last;
+ }
+}
diff --git a/src/TensorFlowNET.Core/Keras/ArgsDefinition/Cropping/CroppingArgs.cs b/src/TensorFlowNET.Core/Keras/ArgsDefinition/Cropping/CroppingArgs.cs
new file mode 100644
index 00000000..9d23acd4
--- /dev/null
+++ b/src/TensorFlowNET.Core/Keras/ArgsDefinition/Cropping/CroppingArgs.cs
@@ -0,0 +1,10 @@
+using Tensorflow.NumPy;
+
+namespace Tensorflow.Keras.ArgsDefinition {
+ public class CroppingArgs : LayerArgs {
+ ///
+ /// Accept length 1 or 2
+ ///
+ public NDArray cropping { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/TensorFlowNET.Core/Keras/ArgsDefinition/Reshaping/PermuteArgs.cs b/src/TensorFlowNET.Core/Keras/ArgsDefinition/Reshaping/PermuteArgs.cs
new file mode 100644
index 00000000..2686f6cd
--- /dev/null
+++ b/src/TensorFlowNET.Core/Keras/ArgsDefinition/Reshaping/PermuteArgs.cs
@@ -0,0 +1,5 @@
+namespace Tensorflow.Keras.ArgsDefinition {
+ public class PermuteArgs : LayerArgs {
+ public int[] dims { get; set; }
+ }
+}
diff --git a/src/TensorFlowNET.Keras/Layers/Activation/ELU.cs b/src/TensorFlowNET.Keras/Layers/Activation/ELU.cs
new file mode 100644
index 00000000..a38260e4
--- /dev/null
+++ b/src/TensorFlowNET.Keras/Layers/Activation/ELU.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Tensorflow.Keras.ArgsDefinition;
+using Tensorflow.Keras.Engine;
+using static Tensorflow.Binding;
+
+namespace Tensorflow.Keras.Layers {
+ ///
+ /// ELU Layer:
+ /// x = 0 when x > 0, x = alpha( e^x-1 ) elsewhere
+ ///
+ public class ELU : Layer {
+ ELUArgs args;
+ float alpha => args.Alpha;
+ public ELU ( ELUArgs args ) : base(args) {
+ this.args = args;
+ }
+ protected override void build ( Tensors inputs ) {
+ if ( alpha < 0f ) {
+ throw new ValueError("Alpha must be a number greater than 0.");
+ }
+ built = true;
+ }
+ protected override Tensors Call ( Tensors inputs, Tensor state = null, bool? training = null ) {
+ Tensor output = inputs;
+ if ( alpha != 1f ) {
+ output = tf.where(output > 0f, output, alpha * (tf.exp(output) - 1f));
+ }
+ return output;
+ }
+
+ public override Shape ComputeOutputShape ( Shape input_shape ) {
+ return input_shape;
+ }
+ }
+}
diff --git a/src/TensorFlowNET.Keras/Layers/Activation/SELU.cs b/src/TensorFlowNET.Keras/Layers/Activation/SELU.cs
new file mode 100644
index 00000000..8069244b
--- /dev/null
+++ b/src/TensorFlowNET.Keras/Layers/Activation/SELU.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Tensorflow.Keras.ArgsDefinition;
+using Tensorflow.Keras.Engine;
+using static Tensorflow.Binding;
+
+namespace Tensorflow.Keras.Layers {
+ ///
+ /// SELU Layer:
+ /// similar to ELU, but has pre-defined alpha and scale
+ ///
+ public class SELU : Layer {
+ protected const float alpha = 1.67326324f, scale = 1.05070098f;
+ public SELU ( LayerArgs args ) : base(args) {
+ // SELU has no arguments
+ }
+ protected override void build ( Tensors inputs ) {
+ if ( alpha < 0f ) {
+ throw new ValueError("Alpha must be a number greater than 0.");
+ }
+ built = true;
+ }
+ protected override Tensors Call ( Tensors inputs, Tensor state = null, bool? training = null ) {
+ Tensor output = inputs;
+ return tf.where(output > 0f, scale * output, scale * alpha * (tf.exp(output) - 1f));
+ }
+ public override Shape ComputeOutputShape ( Shape input_shape ) {
+ return input_shape;
+ }
+ }
+}
diff --git a/src/TensorFlowNET.Keras/Layers/Cropping/Cropping1D.cs b/src/TensorFlowNET.Keras/Layers/Cropping/Cropping1D.cs
new file mode 100644
index 00000000..cf71e184
--- /dev/null
+++ b/src/TensorFlowNET.Keras/Layers/Cropping/Cropping1D.cs
@@ -0,0 +1,50 @@
+using Tensorflow.Keras.ArgsDefinition;
+using Tensorflow.Keras.Engine;
+
+namespace Tensorflow.Keras.Layers {
+ public class Cropping1D : Layer {
+ CroppingArgs args;
+ public Cropping1D ( CroppingArgs args ) : base(args) {
+ this.args = args;
+ }
+
+ protected override void build ( Tensors inputs ) {
+ if ( args.cropping.rank != 1 ) {
+ // throw an ValueError exception
+ throw new ValueError("");
+ }
+ else if ( args.cropping.shape[0] > 2 || args.cropping.shape[0] < 1 ) {
+ throw new ValueError("The `cropping` argument must be a tuple of 2 integers.");
+ }
+ built = true;
+ }
+
+ protected override Tensors Call ( Tensors inputs, Tensor state = null, bool? training = null ) {
+ Tensor output = inputs;
+ if ( output.rank != 3 ) {
+ // throw an ValueError exception
+ throw new ValueError("Expected dim=3, found dim=" + output.rank);
+ }
+ if ( args.cropping.shape[0] == 1 ) {
+ int crop_start = args.cropping[0];
+ output = output[new Slice(), new Slice(crop_start, ( int ) output.shape[1] - crop_start), new Slice()];
+ }
+ else {
+ int crop_start = args.cropping[0], crop_end = args.cropping[1];
+ output = output[new Slice(), new Slice(crop_start, ( int ) (output.shape[1]) - crop_end), new Slice()];
+ }
+ return output;
+ }
+
+ public override Shape ComputeOutputShape ( Shape input_shape ) {
+ if ( args.cropping.shape[0] == 1 ) {
+ int crop = args.cropping[0];
+ return new Shape(( int ) (input_shape[0]), ( int ) (input_shape[1] - crop * 2), ( int ) (input_shape[2]));
+ }
+ else {
+ int crop_start = args.cropping[0], crop_end = args.cropping[1];
+ return new Shape(( int ) (input_shape[0]), ( int ) (input_shape[1] - crop_start - crop_end), ( int ) (input_shape[2]));
+ }
+ }
+ }
+}
diff --git a/src/TensorFlowNET.Keras/Layers/Cropping/Cropping2D.cs b/src/TensorFlowNET.Keras/Layers/Cropping/Cropping2D.cs
new file mode 100644
index 00000000..340ba42d
--- /dev/null
+++ b/src/TensorFlowNET.Keras/Layers/Cropping/Cropping2D.cs
@@ -0,0 +1,113 @@
+using Tensorflow.Keras.ArgsDefinition;
+using Tensorflow.Keras.Engine;
+
+namespace Tensorflow.Keras.Layers {
+ ///
+ /// Crop the input along axis 1 and 2.
+ /// For example:
+ /// shape (1, 5, 5, 5) -- crop2D ((1, 2), (1, 3)) --> shape (1, 2, 1, 5)
+ ///
+ public class Cropping2D : Layer {
+ Cropping2DArgs args;
+ public Cropping2D ( Cropping2DArgs args ) : base(args) {
+ this.args = args;
+ }
+ protected override void build ( Tensors inputs ) {
+ built = true;
+ }
+ protected override Tensors Call ( Tensors inputs, Tensor state = null, bool? training = null ) {
+ Tensor output = inputs;
+ if ( output.rank != 4 ) {
+ // throw an ValueError exception
+ throw new ValueError("Expected dim=4, found dim=" + output.rank);
+ }
+ if ( args.cropping.shape == new Shape(1) ) {
+ int crop = args.cropping[0];
+ if ( args.data_format == Cropping2DArgs.DataFormat.channels_last ) {
+ output = output[new Slice(),
+ new Slice(crop, ( int ) output.shape[1] - crop),
+ new Slice(crop, ( int ) output.shape[2] - crop),
+ new Slice()];
+ }
+ else {
+ output = output[new Slice(),
+ new Slice(),
+ new Slice(crop, ( int ) output.shape[2] - crop),
+ new Slice(crop, ( int ) output.shape[3] - crop)];
+ }
+ }
+ // a tuple of 2 integers
+ else if ( args.cropping.shape == new Shape(2) ) {
+ int crop_1 = args.cropping[0];
+ int crop_2 = args.cropping[1];
+ if ( args.data_format == Cropping2DArgs.DataFormat.channels_last ) {
+ output = output[new Slice(),
+ new Slice(crop_1, ( int ) output.shape[1] - crop_1),
+ new Slice(crop_2, ( int ) output.shape[2] - crop_2),
+ new Slice()];
+ }
+ else {
+ output = output[new Slice(),
+ new Slice(),
+ new Slice(crop_1, ( int ) output.shape[2] - crop_1),
+ new Slice(crop_2, ( int ) output.shape[3] - crop_2)];
+ }
+ }
+ else if ( args.cropping.shape[0] == 2 && args.cropping.shape[1] == 2 ) {
+ int x_start = args.cropping[0, 0], x_end = args.cropping[0, 1];
+ int y_start = args.cropping[1, 0], y_end = args.cropping[1, 1];
+ if ( args.data_format == Cropping2DArgs.DataFormat.channels_last ) {
+ output = output[new Slice(),
+ new Slice(x_start, ( int ) output.shape[1] - x_end),
+ new Slice(y_start, ( int ) output.shape[2] - y_end),
+ new Slice()];
+ }
+ else {
+ output = output[new Slice(),
+ new Slice(),
+ new Slice(x_start, ( int ) output.shape[2] - x_end),
+ new Slice(y_start, ( int ) output.shape[3] - y_end)
+ ];
+ }
+ }
+ return output;
+ }
+
+ public override Shape ComputeOutputShape ( Shape input_shape ) {
+ if ( args.cropping.shape == new Shape(1) ) {
+ int crop = args.cropping[0];
+ if ( args.data_format == Cropping2DArgs.DataFormat.channels_last ) {
+ return new Shape(( int ) input_shape[0], ( int ) input_shape[1] - crop * 2, ( int ) input_shape[2] - crop * 2, ( int ) input_shape[3]);
+ }
+ else {
+ return new Shape(( int ) input_shape[0], ( int ) input_shape[1], ( int ) input_shape[2] - crop * 2, ( int ) input_shape[3] - crop * 2);
+ }
+ }
+ // a tuple of 2 integers
+ else if ( args.cropping.shape == new Shape(2) ) {
+ int crop_1 = args.cropping[0], crop_2 = args.cropping[1];
+ if ( args.data_format == Cropping2DArgs.DataFormat.channels_last ) {
+ return new Shape(( int ) input_shape[0], ( int ) input_shape[1] - crop_1 * 2, ( int ) input_shape[2] - crop_2 * 2, ( int ) input_shape[3]);
+ }
+ else {
+ return new Shape(( int ) input_shape[0], ( int ) input_shape[1], ( int ) input_shape[2] - crop_1 * 2, ( int ) input_shape[3] - crop_2 * 2);
+ }
+ }
+ else if ( args.cropping.shape == new Shape(2, 2) ) {
+ int crop_1_start = args.cropping[0, 0], crop_1_end = args.cropping[0, 1];
+ int crop_2_start = args.cropping[1, 0], crop_2_end = args.cropping[1, 1];
+ if ( args.data_format == Cropping2DArgs.DataFormat.channels_last ) {
+ return new Shape(( int ) input_shape[0], ( int ) input_shape[1] - crop_1_start - crop_1_end,
+ ( int ) input_shape[2] - crop_2_start - crop_2_end, ( int ) input_shape[3]);
+ }
+ else {
+ return new Shape(( int ) input_shape[0], ( int ) input_shape[1],
+ ( int ) input_shape[2] - crop_1_start - crop_1_end, ( int ) input_shape[3] - crop_2_start - crop_2_end);
+ }
+ }
+ else {
+ throw new ValueError();
+ }
+ }
+ }
+}
diff --git a/src/TensorFlowNET.Keras/Layers/Cropping/Cropping3D.cs b/src/TensorFlowNET.Keras/Layers/Cropping/Cropping3D.cs
new file mode 100644
index 00000000..df102c1f
--- /dev/null
+++ b/src/TensorFlowNET.Keras/Layers/Cropping/Cropping3D.cs
@@ -0,0 +1,123 @@
+using Tensorflow.Keras.ArgsDefinition;
+using Tensorflow.Keras.Engine;
+
+namespace Tensorflow.Keras.Layers {
+ ///
+ /// Similar to copping 2D
+ ///
+ public class Cropping3D : Layer {
+ Cropping3DArgs args;
+ public Cropping3D ( Cropping3DArgs args ) : base(args) {
+ this.args = args;
+ }
+
+ protected override void build ( Tensors inputs ) {
+ built = true;
+ }
+
+ protected override Tensors Call ( Tensors inputs, Tensor state = null, bool? training = null ) {
+ Tensor output = inputs;
+ if ( output.rank != 5 ) {
+ // throw an ValueError exception
+ throw new ValueError("Expected dim=5, found dim=" + output.rank);
+ }
+
+ if ( args.cropping.shape == new Shape(1) ) {
+ int crop = args.cropping[0];
+ if ( args.data_format == Cropping3DArgs.DataFormat.channels_last ) {
+ output = output[new Slice(),
+ new Slice(crop, ( int ) output.shape[1] - crop),
+ new Slice(crop, ( int ) output.shape[2] - crop),
+ new Slice(crop, ( int ) output.shape[3] - crop),
+ new Slice()];
+ }
+ else {
+ output = output[new Slice(),
+ new Slice(),
+ new Slice(crop, ( int ) output.shape[2] - crop),
+ new Slice(crop, ( int ) output.shape[3] - crop),
+ new Slice(crop, ( int ) output.shape[4] - crop)];
+ }
+
+ }
+ // int[1][3] equivalent to a tuple of 3 integers
+ else if ( args.cropping.shape == new Shape(3) ) {
+ var crop_1 = args.cropping[0];
+ var crop_2 = args.cropping[1];
+ var crop_3 = args.cropping[2];
+ if ( args.data_format == Cropping3DArgs.DataFormat.channels_last ) {
+ output = output[new Slice(),
+ new Slice(crop_1, ( int ) output.shape[1] - crop_1),
+ new Slice(crop_2, ( int ) output.shape[2] - crop_2),
+ new Slice(crop_3, ( int ) output.shape[3] - crop_3),
+ new Slice()];
+ }
+ else {
+ output = output[new Slice(),
+ new Slice(),
+ new Slice(crop_1, ( int ) output.shape[2] - crop_1),
+ new Slice(crop_2, ( int ) output.shape[3] - crop_2),
+ new Slice(crop_3, ( int ) output.shape[4] - crop_3)];
+ }
+ }
+ else if ( args.cropping.shape[0] == 3 && args.cropping.shape[1] == 2 ) {
+ int x = args.cropping[0, 0], x_end = args.cropping[0, 1];
+ int y = args.cropping[1, 0], y_end = args.cropping[1, 1];
+ int z = args.cropping[2, 0], z_end = args.cropping[2, 1];
+ if ( args.data_format == Cropping3DArgs.DataFormat.channels_last ) {
+ output = output[new Slice(),
+ new Slice(x, ( int ) output.shape[1] - x_end),
+ new Slice(y, ( int ) output.shape[2] - y_end),
+ new Slice(z, ( int ) output.shape[3] - z_end),
+ new Slice()];
+ }
+ else {
+ output = output[new Slice(),
+ new Slice(),
+ new Slice(x, ( int ) output.shape[2] - x_end),
+ new Slice(y, ( int ) output.shape[3] - y_end),
+ new Slice(z, ( int ) output.shape[4] - z_end)
+ ];
+ }
+ }
+ return output;
+ }
+ public override Shape ComputeOutputShape ( Shape input_shape ) {
+ if ( args.cropping.shape == new Shape(1) ) {
+ int crop = args.cropping[0];
+ if ( args.data_format == Cropping3DArgs.DataFormat.channels_last ) {
+ return new Shape(( int ) input_shape[0], ( int ) input_shape[1] - crop * 2, ( int ) input_shape[2] - crop * 2, ( int ) input_shape[3] - crop * 2, ( int ) input_shape[4]);
+ }
+ else {
+ return new Shape(( int ) input_shape[0], ( int ) input_shape[1], ( int ) input_shape[2] - crop * 2, ( int ) input_shape[3] - crop * 2, ( int ) input_shape[4] - crop * 2);
+ }
+ }
+ // int[1][3] equivalent to a tuple of 3 integers
+ else if ( args.cropping.shape == new Shape(3) ) {
+ var crop_start_1 = args.cropping[0];
+ var crop_start_2 = args.cropping[1];
+ var crop_start_3 = args.cropping[2];
+ if ( args.data_format == Cropping3DArgs.DataFormat.channels_last ) {
+ return new Shape(( int ) input_shape[0], ( int ) input_shape[1] - crop_start_1 * 2, ( int ) input_shape[2] - crop_start_2 * 2, ( int ) input_shape[3] - crop_start_3 * 2, ( int ) input_shape[4]);
+ }
+ else {
+ return new Shape(( int ) input_shape[0], ( int ) input_shape[1], ( int ) input_shape[2] - crop_start_1 * 2, ( int ) input_shape[3] - crop_start_2 * 2, ( int ) input_shape[4] - crop_start_3 * 2);
+ }
+ }
+ else if ( args.cropping.shape == new Shape(3, 2) ) {
+ int x = args.cropping[0, 0], x_end = args.cropping[0, 1];
+ int y = args.cropping[1, 0], y_end = args.cropping[1, 1];
+ int z = args.cropping[2, 0], z_end = args.cropping[2, 1];
+ if ( args.data_format == Cropping3DArgs.DataFormat.channels_last ) {
+ return new Shape(( int ) input_shape[0], ( int ) input_shape[1] - x - x_end, ( int ) input_shape[2] - y - y_end, ( int ) input_shape[3] - z - z_end, ( int ) input_shape[4]);
+ }
+ else {
+ return new Shape(( int ) input_shape[0], ( int ) input_shape[1], ( int ) input_shape[2] - x - x_end, ( int ) input_shape[3] - y - y_end, ( int ) input_shape[4] - z - z_end);
+ }
+ }
+ else {
+ throw new ValueError();
+ }
+ }
+ }
+}
diff --git a/src/TensorFlowNET.Keras/Layers/LayersApi.Cropping.cs b/src/TensorFlowNET.Keras/Layers/LayersApi.Cropping.cs
new file mode 100644
index 00000000..f4d2230c
--- /dev/null
+++ b/src/TensorFlowNET.Keras/Layers/LayersApi.Cropping.cs
@@ -0,0 +1,36 @@
+using Tensorflow.NumPy;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Tensorflow.Keras.ArgsDefinition;
+
+namespace Tensorflow.Keras.Layers {
+ public partial class LayersApi {
+ ///
+ /// Cropping layer for 1D input
+ ///
+ /// cropping size
+ public Cropping1D Cropping1D ( NDArray cropping )
+ => new Cropping1D(new CroppingArgs {
+ cropping = cropping
+ });
+
+ ///
+ /// Cropping layer for 2D input
+ ///
+ public Cropping2D Cropping2D ( NDArray cropping, Cropping2DArgs.DataFormat data_format = Cropping2DArgs.DataFormat.channels_last )
+ => new Cropping2D(new Cropping2DArgs {
+ cropping = cropping,
+ data_format = data_format
+ });
+
+ ///
+ /// Cropping layer for 3D input
+ ///
+ public Cropping3D Cropping3D ( NDArray cropping, Cropping3DArgs.DataFormat data_format = Cropping3DArgs.DataFormat.channels_last )
+ => new Cropping3D(new Cropping3DArgs {
+ cropping = cropping,
+ data_format = data_format
+ });
+ }
+}
diff --git a/src/TensorFlowNET.Keras/Layers/LayersApi.Reshaping.cs b/src/TensorFlowNET.Keras/Layers/LayersApi.Reshaping.cs
index 71f9ef3b..5cfec89e 100644
--- a/src/TensorFlowNET.Keras/Layers/LayersApi.Reshaping.cs
+++ b/src/TensorFlowNET.Keras/Layers/LayersApi.Reshaping.cs
@@ -4,52 +4,54 @@ using System.Collections.Generic;
using System.Text;
using Tensorflow.Keras.ArgsDefinition;
-namespace Tensorflow.Keras.Layers
-{
- public partial class LayersApi
- {
- ///
- /// Zero-padding layer for 2D input (e.g. picture).
- ///
- ///
- ///
- public ZeroPadding2D ZeroPadding2D(NDArray padding)
- => new ZeroPadding2D(new ZeroPadding2DArgs
- {
- Padding = padding
- });
+namespace Tensorflow.Keras.Layers {
+ public partial class LayersApi {
+ ///
+ /// Zero-padding layer for 2D input (e.g. picture).
+ ///
+ ///
+ ///
+ public ZeroPadding2D ZeroPadding2D ( NDArray padding )
+ => new ZeroPadding2D(new ZeroPadding2DArgs {
+ Padding = padding
+ });
- ///
- /// Upsampling layer for 2D inputs.
- /// Repeats the rows and columns of the data by size[0] and size[1] respectively.
- ///
- ///
- ///
- ///
- ///
- public UpSampling2D UpSampling2D(Shape size = null,
- string data_format = null,
- string interpolation = "nearest")
- => new UpSampling2D(new UpSampling2DArgs
- {
- Size = size ?? (2, 2)
- });
+ ///
+ /// Upsampling layer for 2D inputs.
+ /// Repeats the rows and columns of the data by size[0] and size[1] respectively.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public UpSampling2D UpSampling2D ( Shape size = null,
+ string data_format = null,
+ string interpolation = "nearest" )
+ => new UpSampling2D(new UpSampling2DArgs {
+ Size = size ?? (2, 2)
+ });
- ///
- /// Layer that reshapes inputs into the given shape.
- ///
- ///
- ///
- public Reshape Reshape(Shape target_shape)
- => new Reshape(new ReshapeArgs
- {
- TargetShape = target_shape
- });
+ ///
+ /// Permutes the dimensions of the input according to a given pattern.
+ ///
+ public Permute Permute ( int[] dims )
+ => new Permute(new PermuteArgs {
+ dims = dims
+ });
- public Reshape Reshape(object[] target_shape)
- => new Reshape(new ReshapeArgs
- {
- TargetShapeObjects = target_shape
+ ///
+ /// Layer that reshapes inputs into the given shape.
+ ///
+ ///
+ ///
+ public Reshape Reshape ( Shape target_shape )
+ => new Reshape(new ReshapeArgs {
+ TargetShape = target_shape
});
- }
+
+ public Reshape Reshape ( object[] target_shape )
+ => new Reshape(new ReshapeArgs {
+ TargetShapeObjects = target_shape
+ });
+ }
}
diff --git a/src/TensorFlowNET.Keras/Layers/Reshaping/Permute.cs b/src/TensorFlowNET.Keras/Layers/Reshaping/Permute.cs
new file mode 100644
index 00000000..08089900
--- /dev/null
+++ b/src/TensorFlowNET.Keras/Layers/Reshaping/Permute.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Tensorflow.Keras.Engine;
+using Tensorflow.Keras.Utils;
+using static Tensorflow.Binding;
+using Tensorflow.Keras.ArgsDefinition;
+
+namespace Tensorflow.Keras.Layers {
+ public class Permute : Layer {
+ int[] dims, permute;
+ public Permute ( PermuteArgs args ) : base(args) {
+ this.dims = args.dims;
+ }
+ protected override void build ( Tensors inputs ) {
+ var rank = inputs.rank;
+ if ( dims.Length != rank - 1 ) {
+ throw new ValueError("Dimensions must match.");
+ }
+ permute = new int[inputs.rank];
+ dims.CopyTo(permute, 1);
+ built = true;
+ }
+ protected override Tensors Call ( Tensors inputs, Tensor state = null, bool? training = null ) {
+ Tensor outputs = inputs;
+ return tf.transpose(outputs, new Axis(permute));
+ }
+ public override Shape ComputeOutputShape ( Shape input_shape ) {
+ Shape output_shape = new Shape(input_shape.dims);
+ for ( int i = 0; i < dims.Length; i += 1 ) {
+ var d = dims[i];
+ var target_dim = input_shape[d];
+ output_shape[i + 1] = target_dim;
+ }
+ return output_shape;
+ }
+ }
+}
diff --git a/test/TensorFlowNET.Keras.UnitTest/Layers/Layers.Cropping.Test.cs b/test/TensorFlowNET.Keras.UnitTest/Layers/Layers.Cropping.Test.cs
new file mode 100644
index 00000000..b99a9abb
--- /dev/null
+++ b/test/TensorFlowNET.Keras.UnitTest/Layers/Layers.Cropping.Test.cs
@@ -0,0 +1,39 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Tensorflow;
+using Tensorflow.NumPy;
+using static Tensorflow.Binding;
+using static Tensorflow.KerasApi;
+
+namespace TensorFlowNET.Keras.UnitTest {
+ [TestClass]
+ public class LayersCroppingTest : EagerModeTestBase {
+ [TestMethod]
+ public void Cropping1D () {
+ Shape input_shape = (1, 5, 2);
+ var x = tf.zeros(input_shape);
+ var cropping_1d = keras.layers.Cropping1D(new[] { 1, 2 });
+ var y = cropping_1d.Apply(x);
+ Assert.AreEqual((1, 2, 2), y.shape);
+ }
+
+ [TestMethod]
+ public void Cropping2D () {
+ Shape input_shape = (1, 5, 6, 1);
+ NDArray cropping = new NDArray(new[,] { { 1, 2 }, { 1, 3 } });
+ var x = tf.zeros(input_shape);
+ var cropping_2d = keras.layers.Cropping2D(cropping);
+ var y = cropping_2d.Apply(x);
+ Assert.AreEqual((1, 2, 2, 1), y.shape);
+ }
+
+ [TestMethod]
+ public void Cropping3D () {
+ Shape input_shape = new Shape(1, 5, 6, 7, 1);
+ NDArray cropping = new NDArray(new[,] { { 1, 2 }, { 1, 3 }, { 1, 4 } });
+ var x = tf.zeros(input_shape);
+ var cropping_3d = keras.layers.Cropping3D(cropping);
+ var y = cropping_3d.Apply(x);
+ Assert.AreEqual(new Shape(1, 2, 2, 2, 1), y.shape);
+ }
+ }
+}
diff --git a/test/TensorFlowNET.Keras.UnitTest/Layers/Layers.Reshaping.Test.cs b/test/TensorFlowNET.Keras.UnitTest/Layers/Layers.Reshaping.Test.cs
index 70d56aa7..a79c517b 100644
--- a/test/TensorFlowNET.Keras.UnitTest/Layers/Layers.Reshaping.Test.cs
+++ b/test/TensorFlowNET.Keras.UnitTest/Layers/Layers.Reshaping.Test.cs
@@ -4,37 +4,40 @@ using Tensorflow.NumPy;
using static Tensorflow.Binding;
using static Tensorflow.KerasApi;
-namespace TensorFlowNET.Keras.UnitTest
-{
- [TestClass]
- public class LayersReshapingTest : EagerModeTestBase
- {
- [TestMethod]
- public void ZeroPadding2D()
- {
- Shape input_shape = (1, 1, 2, 2);
- var x = np.arange(input_shape.size).reshape(input_shape);
- var zero_padding_2d = keras.layers.ZeroPadding2D(new[,] { { 1, 0 }, { 1, 0 } });
- var y = zero_padding_2d.Apply(x);
- Assert.AreEqual((1, 2, 3, 2), y.shape);
- }
+namespace TensorFlowNET.Keras.UnitTest {
+ [TestClass]
+ public class LayersReshapingTest : EagerModeTestBase {
+ [TestMethod]
+ public void ZeroPadding2D () {
+ Shape input_shape = (1, 1, 2, 2);
+ var x = np.arange(input_shape.size).reshape(input_shape);
+ var zero_padding_2d = keras.layers.ZeroPadding2D(new[,] { { 1, 0 }, { 1, 0 } });
+ var y = zero_padding_2d.Apply(x);
+ Assert.AreEqual((1, 2, 3, 2), y.shape);
+ }
- [TestMethod]
- public void UpSampling2D()
- {
- Shape input_shape = (2, 2, 1, 3);
- var x = np.arange(input_shape.size).reshape(input_shape);
- var y = keras.layers.UpSampling2D(size: (1, 2)).Apply(x);
- Assert.AreEqual((2, 2, 2, 3), y.shape);
- }
+ [TestMethod]
+ public void UpSampling2D () {
+ Shape input_shape = (2, 2, 1, 3);
+ var x = np.arange(input_shape.size).reshape(input_shape);
+ var y = keras.layers.UpSampling2D(size: (1, 2)).Apply(x);
+ Assert.AreEqual((2, 2, 2, 3), y.shape);
+ }
- [TestMethod]
- public void Reshape()
- {
- var inputs = tf.zeros((10, 5, 20));
- var outputs = keras.layers.LeakyReLU().Apply(inputs);
- outputs = keras.layers.Reshape((20, 5)).Apply(outputs);
- Assert.AreEqual((10, 20, 5), outputs.shape);
- }
- }
+ [TestMethod]
+ public void Reshape () {
+ var inputs = tf.zeros((10, 5, 20));
+ var outputs = keras.layers.LeakyReLU().Apply(inputs);
+ outputs = keras.layers.Reshape((20, 5)).Apply(outputs);
+ Assert.AreEqual((10, 20, 5), outputs.shape);
+ }
+
+ [TestMethod]
+ public void Permute () {
+ var inputs = tf.zeros((2, 3, 4, 5));
+ var outputs = keras.layers.Permute(new int[] { 3, 2, 1 }).Apply(inputs);
+ Assert.AreEqual((2, 5, 4, 3), outputs.shape);
+ }
+
+ }
}