Browse Source

impplementing NB classifier

tags/v0.9
Bo Peng 6 years ago
parent
commit
d9d4fa5059
100 changed files with 2989 additions and 489 deletions
  1. +1
    -8
      .gitignore
  2. +2
    -0
      README.md
  3. +0
    -6
      TensorFlow.NET.sln
  4. +58
    -0
      docs/source/Graph.md
  5. +5
    -1
      docs/source/LinearRegression.md
  6. +7
    -0
      docs/source/LogisticRegression.md
  7. +3
    -0
      docs/source/NearestNeighbor.md
  8. +2
    -0
      docs/source/index.rst
  9. +44
    -0
      src/TensorFlowNET.Core/APIs/keras.layers.cs
  10. +11
    -1
      src/TensorFlowNET.Core/APIs/tf.array.cs
  11. +12
    -0
      src/TensorFlowNET.Core/APIs/tf.control.cs
  12. +10
    -1
      src/TensorFlowNET.Core/APIs/tf.init.cs
  13. +46
    -0
      src/TensorFlowNET.Core/APIs/tf.layers.cs
  14. +231
    -6
      src/TensorFlowNET.Core/APIs/tf.math.cs
  15. +34
    -13
      src/TensorFlowNET.Core/APIs/tf.nn.cs
  16. +3
    -0
      src/TensorFlowNET.Core/APIs/tf.reshape.cs
  17. +86
    -0
      src/TensorFlowNET.Core/Clustering/KMeans.cs
  18. +10
    -0
      src/TensorFlowNET.Core/Framework/common_shapes.py.cs
  19. +1
    -1
      src/TensorFlowNET.Core/Framework/meta_graph.py.cs
  20. +7
    -4
      src/TensorFlowNET.Core/Framework/smart_module.cs
  21. +30
    -0
      src/TensorFlowNET.Core/Gradients/array_grad.py.cs
  22. +23
    -0
      src/TensorFlowNET.Core/Gradients/control_flow_grad.py.cs
  23. +21
    -11
      src/TensorFlowNET.Core/Gradients/gradients_impl.py.cs
  24. +255
    -0
      src/TensorFlowNET.Core/Gradients/math_grad.cs
  25. +0
    -160
      src/TensorFlowNET.Core/Gradients/math_grad.py.cs
  26. +135
    -0
      src/TensorFlowNET.Core/Gradients/nn_grad.py.cs
  27. +68
    -0
      src/TensorFlowNET.Core/Gradients/ops.gradient_function_mapping.cs
  28. +23
    -0
      src/TensorFlowNET.Core/Graphs/FreezeGraph.cs
  29. +4
    -1
      src/TensorFlowNET.Core/Graphs/Graph.Export.cs
  30. +1
    -1
      src/TensorFlowNET.Core/Graphs/Graph.cs
  31. +1
    -1
      src/TensorFlowNET.Core/Graphs/graph_io.py.cs
  32. +6
    -3
      src/TensorFlowNET.Core/Keras/Engine/InputSpec.cs
  33. +6
    -1
      src/TensorFlowNET.Core/Keras/Engine/Model.cs
  34. +31
    -0
      src/TensorFlowNET.Core/Keras/Engine/Network.cs
  35. +37
    -5
      src/TensorFlowNET.Core/Keras/Engine/Sequential.cs
  36. +38
    -6
      src/TensorFlowNET.Core/Keras/Layers/BatchNormalization.cs
  37. +81
    -0
      src/TensorFlowNET.Core/Keras/Layers/Dense.cs
  38. +36
    -0
      src/TensorFlowNET.Core/Keras/Layers/Embedding.cs
  39. +16
    -0
      src/TensorFlowNET.Core/Keras/Layers/IPoolFunction.cs
  40. +56
    -0
      src/TensorFlowNET.Core/Keras/Layers/InputLayer.cs
  41. +49
    -15
      src/TensorFlowNET.Core/Keras/Layers/Layer.cs
  42. +24
    -0
      src/TensorFlowNET.Core/Keras/Layers/MaxPooling2D.cs
  43. +71
    -0
      src/TensorFlowNET.Core/Keras/Layers/Node.cs
  44. +57
    -0
      src/TensorFlowNET.Core/Keras/Layers/Pooling2D.cs
  45. +10
    -1
      src/TensorFlowNET.Core/Keras/Utils/base_layer_utils.cs
  46. +15
    -0
      src/TensorFlowNET.Core/Keras/Utils/conv_utils.cs
  47. +8
    -3
      src/TensorFlowNET.Core/Keras/Utils/tf_utils.cs
  48. +16
    -0
      src/TensorFlowNET.Core/Keras/backend.cs
  49. +23
    -0
      src/TensorFlowNET.Core/Layers/Dense.cs
  50. +16
    -4
      src/TensorFlowNET.Core/Layers/Layer.cs
  51. +0
    -0
      src/TensorFlowNET.Core/Operations/Activation/gen_nn_ops.activations.cs
  52. +16
    -6
      src/TensorFlowNET.Core/Operations/ControlFlows/CondContext.cs
  53. +28
    -0
      src/TensorFlowNET.Core/Operations/ControlFlows/ControlFlowContext.cs
  54. +1
    -0
      src/TensorFlowNET.Core/Operations/ControlFlows/IControlFlowContext.cs
  55. +38
    -0
      src/TensorFlowNET.Core/Operations/Initializers/RandomUniform.cs
  56. +4
    -4
      src/TensorFlowNET.Core/Operations/Initializers/TruncatedNormal.cs
  57. +29
    -0
      src/TensorFlowNET.Core/Operations/NnOps/MaxPoolFunction.cs
  58. +98
    -2
      src/TensorFlowNET.Core/Operations/NnOps/gen_nn_ops.cs
  59. +3
    -3
      src/TensorFlowNET.Core/Operations/OpDefLibrary.cs
  60. +17
    -6
      src/TensorFlowNET.Core/Operations/Operation.Control.cs
  61. +2
    -1
      src/TensorFlowNET.Core/Operations/Operation.Output.cs
  62. +10
    -2
      src/TensorFlowNET.Core/Operations/Operation.cs
  63. +196
    -53
      src/TensorFlowNET.Core/Operations/array_ops.py.cs
  64. +64
    -8
      src/TensorFlowNET.Core/Operations/control_flow_ops.py.cs
  65. +18
    -0
      src/TensorFlowNET.Core/Operations/control_flow_util.py.cs
  66. +66
    -11
      src/TensorFlowNET.Core/Operations/gen_array_ops.cs
  67. +8
    -1
      src/TensorFlowNET.Core/Operations/gen_control_flow_ops.py.cs
  68. +176
    -5
      src/TensorFlowNET.Core/Operations/gen_math_ops.cs
  69. +121
    -17
      src/TensorFlowNET.Core/Operations/math_ops.cs
  70. +1
    -1
      src/TensorFlowNET.Core/Operations/nn_impl.py.cs
  71. +50
    -0
      src/TensorFlowNET.Core/Operations/nn_ops.cs
  72. +6
    -8
      src/TensorFlowNET.Core/Python.cs
  73. +9
    -2
      src/TensorFlowNET.Core/Sessions/BaseSession.cs
  74. +19
    -7
      src/TensorFlowNET.Core/Sessions/_ElementFetchMapper.cs
  75. +27
    -3
      src/TensorFlowNET.Core/Sessions/_FetchHandler.cs
  76. +20
    -7
      src/TensorFlowNET.Core/Sessions/_FetchMapper.cs
  77. +18
    -0
      src/TensorFlowNET.Core/Sessions/_ListFetchMapper.cs
  78. +15
    -10
      src/TensorFlowNET.Core/TensorFlowNET.Core.csproj
  79. +1
    -1
      src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs
  80. +8
    -7
      src/TensorFlowNET.Core/Tensors/Tensor.Operators.cs
  81. +71
    -4
      src/TensorFlowNET.Core/Tensors/Tensor.cs
  82. +6
    -0
      src/TensorFlowNET.Core/Tensors/dtypes.cs
  83. +20
    -3
      src/TensorFlowNET.Core/Tensors/tensor_util.cs
  84. +25
    -0
      src/TensorFlowNET.Core/Train/AdamOptimizer.cs
  85. +1
    -1
      src/TensorFlowNET.Core/Train/GradientDescentOptimizer.cs
  86. +2
    -0
      src/TensorFlowNET.Core/Train/Optimizer.cs
  87. +1
    -1
      src/TensorFlowNET.Core/Train/Saving/Saver.cs
  88. +5
    -1
      src/TensorFlowNET.Core/Train/tf.optimizers.cs
  89. +2
    -1
      src/TensorFlowNET.Core/Variables/RefVariable.Operators.cs
  90. +11
    -1
      src/TensorFlowNET.Core/Variables/gen_state_ops.py.cs
  91. +8
    -0
      src/TensorFlowNET.Core/Variables/state_ops.cs
  92. +1
    -0
      src/TensorFlowNET.Core/Variables/variable_scope.py.cs
  93. +6
    -3
      src/TensorFlowNET.Core/Variables/variables.py.cs
  94. +4
    -0
      src/TensorFlowNET.Core/ops.GraphKeys.cs
  95. +11
    -56
      src/TensorFlowNET.Core/ops.py.cs
  96. +3
    -0
      src/TensorFlowNET.Core/tf.cs
  97. +13
    -0
      tensorflowlib/README.md
  98. +0
    -0
      tensorflowlib/runtimes/linux-x64/native/libtensorflow.so
  99. +0
    -0
      tensorflowlib/runtimes/linux-x64/native/libtensorflow_framework.so
  100. BIN
      tensorflowlib/runtimes/win-x64/native/tensorflow.dll

+ 1
- 8
.gitignore View File

@@ -328,15 +328,8 @@ ASALocalRun/


# MFractors (Xamarin productivity tool) working folder # MFractors (Xamarin productivity tool) working folder
.mfractor/ .mfractor/
/tensorflowlib/win7-x64/native/libtensorflow.dll
/tensorflowlib/osx/native/libtensorflow_framework.dylib
/tensorflowlib/osx/native/libtensorflow.dylib
/tensorflowlib/linux/native/libtensorflow_framework.so
/tensorflowlib/linux/native/libtensorflow.so
/src/TensorFlowNET.Core/tensorflow.dll
/docs/build /docs/build
src/TensorFlowNET.Native/libtensorflow.dll
src/TensorFlowNET.Native/bazel-* src/TensorFlowNET.Native/bazel-*
/src/TensorFlowNET.Native/libtensorflow.lib
src/TensorFlowNET.Native/c_api.h src/TensorFlowNET.Native/c_api.h
/.vscode /.vscode
test/TensorFlowNET.Examples/mnist

+ 2
- 0
README.md View File

@@ -72,6 +72,8 @@ Read the docs & book [The Definitive Guide to Tensorflow.NET](https://tensorflow
* [Basic Operations](test/TensorFlowNET.Examples/BasicOperations.cs) * [Basic Operations](test/TensorFlowNET.Examples/BasicOperations.cs)
* [Image Recognition](test/TensorFlowNET.Examples/ImageRecognition.cs) * [Image Recognition](test/TensorFlowNET.Examples/ImageRecognition.cs)
* [Linear Regression](test/TensorFlowNET.Examples/LinearRegression.cs) * [Linear Regression](test/TensorFlowNET.Examples/LinearRegression.cs)
* [Logistic Regression](test/TensorFlowNET.Examples/LogisticRegression.cs)
* [Nearest Neighbor](test/TensorFlowNET.Examples/NearestNeighbor.cs)
* [Text Classification](test/TensorFlowNET.Examples/TextClassificationWithMovieReviews.cs) * [Text Classification](test/TensorFlowNET.Examples/TextClassificationWithMovieReviews.cs)
* [CNN Text Classification](test/TensorFlowNET.Examples/CnnTextClassification.cs) * [CNN Text Classification](test/TensorFlowNET.Examples/CnnTextClassification.cs)
* [Naive Bayes Classification](test/TensorFlowNET.Examples/NaiveBayesClassifier.cs) * [Naive Bayes Classification](test/TensorFlowNET.Examples/NaiveBayesClassifier.cs)


+ 0
- 6
TensorFlow.NET.sln View File

@@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TensorFlowNET.Core", "src\T
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TensorFlowNET.Visualization", "src\TensorFlowNET.Visualization\TensorFlowNET.Visualization.csproj", "{0254BFF9-453C-4FE0-9609-3644559A79CE}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TensorFlowNET.Visualization", "src\TensorFlowNET.Visualization\TensorFlowNET.Visualization.csproj", "{0254BFF9-453C-4FE0-9609-3644559A79CE}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NumSharp.Core", "..\NumSharp\src\NumSharp.Core\NumSharp.Core.csproj", "{3EEAFB06-BEF0-4261-BAAB-630EABD25290}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -35,10 +33,6 @@ Global
{0254BFF9-453C-4FE0-9609-3644559A79CE}.Debug|Any CPU.Build.0 = Debug|Any CPU {0254BFF9-453C-4FE0-9609-3644559A79CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0254BFF9-453C-4FE0-9609-3644559A79CE}.Release|Any CPU.ActiveCfg = Release|Any CPU {0254BFF9-453C-4FE0-9609-3644559A79CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0254BFF9-453C-4FE0-9609-3644559A79CE}.Release|Any CPU.Build.0 = Release|Any CPU {0254BFF9-453C-4FE0-9609-3644559A79CE}.Release|Any CPU.Build.0 = Release|Any CPU
{3EEAFB06-BEF0-4261-BAAB-630EABD25290}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3EEAFB06-BEF0-4261-BAAB-630EABD25290}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3EEAFB06-BEF0-4261-BAAB-630EABD25290}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3EEAFB06-BEF0-4261-BAAB-630EABD25290}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE


+ 58
- 0
docs/source/Graph.md View File

@@ -21,3 +21,61 @@ A typical graph is looks like below:


![image](../assets/graph_vis_animation.gif) ![image](../assets/graph_vis_animation.gif)




### Save Model

Saving the model means saving all the values of the parameters and the graph.

```python
saver = tf.train.Saver()
saver.save(sess,'./tensorflowModel.ckpt')
```

After saving the model there will be four files:

* tensorflowModel.ckpt.meta:
* tensorflowModel.ckpt.data-00000-of-00001:
* tensorflowModel.ckpt.index
* checkpoint

We also created a protocol buffer file .pbtxt. It is human readable if you want to convert it to binary: `as_text: false`.

* tensorflowModel.pbtxt:

This holds a network of nodes, each representing one operation, connected to each other as inputs and outputs.



### Freezing the Graph

##### *Why we need it?*

When we need to keep all the values of the variables and the Graph structure in a single file we have to freeze the graph.

```csharp
from tensorflow.python.tools import freeze_graph

freeze_graph.freeze_graph(input_graph = 'logistic_regression/tensorflowModel.pbtxt',
input_saver = "",
input_binary = False,
input_checkpoint = 'logistic_regression/tensorflowModel.ckpt',
output_node_names = "Softmax",
restore_op_name = "save/restore_all",
filename_tensor_name = "save/Const:0",
output_graph = 'frozentensorflowModel.pb',
clear_devices = True,
initializer_nodes = "")

```

### Optimizing for Inference

To Reduce the amount of computation needed when the network is used only for inferences we can remove some parts of a graph that are only needed for training.



### Restoring the Model




+ 5
- 1
docs/source/LinearRegression.md View File

@@ -8,6 +8,8 @@ Consider the case of a single variable of interest y and a single predictor vari


We have some data $D=\{x{\tiny i},y{\tiny i}\}$ and we assume a simple linear model of this dataset with Gaussian noise: We have some data $D=\{x{\tiny i},y{\tiny i}\}$ and we assume a simple linear model of this dataset with Gaussian noise:


线性回归是一种线性建模方法,这种方法用来描述自变量与一个或多个因变量的之间的关系。在只有一个因变量y和一个自变量的情况下。自变量还有以下几种叫法:协变量,输入,特征;因变量通常被叫做响应变量,输出,输出结果。
假如我们有数据$D=\{x{\tiny i},y{\tiny i}\}$,并且假设这个数据集是满足高斯分布的线性模型:
```csharp ```csharp
// Prepare training Data // Prepare training Data
var train_X = np.array(3.3f, 4.4f, 5.5f, 6.71f, 6.93f, 4.168f, 9.779f, 6.182f, 7.59f, 2.167f, 7.042f, 10.791f, 5.313f, 7.997f, 5.654f, 9.27f, 3.1f); var train_X = np.array(3.3f, 4.4f, 5.5f, 6.71f, 6.93f, 4.168f, 9.779f, 6.182f, 7.59f, 2.167f, 7.042f, 10.791f, 5.313f, 7.997f, 5.654f, 9.27f, 3.1f);
@@ -18,6 +20,8 @@ var n_samples = train_X.shape[0];


Based on the given data points, we try to plot a line that models the points the best. The red line can be modelled based on the linear equation: $y = wx + b$. The motive of the linear regression algorithm is to find the best values for $w$ and $b$. Before moving on to the algorithm, le's have a look at two important concepts you must know to better understand linear regression. Based on the given data points, we try to plot a line that models the points the best. The red line can be modelled based on the linear equation: $y = wx + b$. The motive of the linear regression algorithm is to find the best values for $w$ and $b$. Before moving on to the algorithm, le's have a look at two important concepts you must know to better understand linear regression.


按照上图根据数据描述的数据点,在这些数据点之间画出一条线,这条线能达到最好模拟点的分布的效果。红色的线能够通过下面呢线性等式来描述:$y = wx + b$。线性回归算法的目标就是找到这条线对应的最好的参数$w$和$b$。在介绍线性回归算法之前,我们先看两个重要的概念,这两个概念有助于你理解线性回归算法。

### Cost Function ### Cost Function


The cost function helps us to figure out the best possible values for $w$ and $b$ which would provide the best fit line for the data points. Since we want the best values for $w$ and $b$, we convert this search problem into a minimization problem where we would like to minimize the error between the predicted value and the actual value. The cost function helps us to figure out the best possible values for $w$ and $b$ which would provide the best fit line for the data points. Since we want the best values for $w$ and $b$, we convert this search problem into a minimization problem where we would like to minimize the error between the predicted value and the actual value.
@@ -65,4 +69,4 @@ When we visualize the graph in TensorBoard:


![linear-regression](_static/linear-regression-tensor-board.png) ![linear-regression](_static/linear-regression-tensor-board.png)


The full example is [here](https://github.com/SciSharp/TensorFlow.NET/blob/master/test/TensorFlowNET.Examples/LinearRegression.cs).
The full example is [here](https://github.com/SciSharp/TensorFlow.NET/blob/master/test/TensorFlowNET.Examples/LinearRegression.cs).

+ 7
- 0
docs/source/LogisticRegression.md View File

@@ -0,0 +1,7 @@
# Chapter. Logistic Regression

### What is logistic regression?



The full example is [here](https://github.com/SciSharp/TensorFlow.NET/blob/master/test/TensorFlowNET.Examples/LogisticRegression.cs).

+ 3
- 0
docs/source/NearestNeighbor.md View File

@@ -0,0 +1,3 @@
# Chapter. Nearest Neighbor

The nearest neighbour algorithm was one of the first algorithms used to solve the travelling salesman problem. In it, the salesman starts at a random city and repeatedly visits the nearest city until all have been visited. It quickly yields a short tour, but usually not the optimal one.

+ 2
- 0
docs/source/index.rst View File

@@ -26,4 +26,6 @@ Welcome to TensorFlow.NET's documentation!
Train Train
EagerMode EagerMode
LinearRegression LinearRegression
LogisticRegression
NearestNeighbor
ImageRecognition ImageRecognition

+ 44
- 0
src/TensorFlowNET.Core/APIs/keras.layers.cs View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tensorflow.Keras;
using Tensorflow.Keras.Engine;
using Tensorflow.Keras.Layers;

namespace Tensorflow
{
public static partial class keras
{
public static class layers
{
public static Embedding Embedding(int input_dim, int output_dim,
IInitializer embeddings_initializer = null,
bool mask_zero = false) => new Embedding(input_dim, output_dim,
embeddings_initializer,
mask_zero);

public static Tensor[] Input(int[] batch_shape = null,
TF_DataType dtype = TF_DataType.DtInvalid,
string name = null,
bool sparse = false,
Tensor tensor = null)
{
var batch_size = batch_shape[0];
var shape = batch_shape.Skip(1).ToArray();

var input_layer = new InputLayer(
input_shape: shape,
batch_size: batch_size,
name: name,
dtype: dtype,
sparse: sparse,
input_tensor: tensor);

var outputs = input_layer.inbound_nodes[0].output_tensors;

return outputs;
}
}
}
}

+ 11
- 1
src/TensorFlowNET.Core/APIs/tf.array.cs View File

@@ -28,7 +28,17 @@ namespace Tensorflow
/// <param name="name"></param> /// <param name="name"></param>
/// <param name="conjugate"></param> /// <param name="conjugate"></param>
/// <returns></returns> /// <returns></returns>
public static Tensor transpose(Tensor a, int[] perm = null, string name = "transpose", bool conjugate = false)
public static Tensor transpose<T1, T2>(T1 a, T2 perm, string name = "transpose", bool conjugate = false)
=> array_ops.transpose(a, perm, name, conjugate); => array_ops.transpose(a, perm, name, conjugate);

public static Tensor squeeze(Tensor input, int[] axis = null, string name = null, int squeeze_dims = -1)
=> gen_array_ops.squeeze(input, axis, name);

public static Tensor one_hot(Tensor indices, int depth,
Tensor on_value = null,
Tensor off_value = null,
TF_DataType dtype = TF_DataType.DtInvalid,
int axis = -1,
string name = null) => array_ops.one_hot(indices, depth, dtype: dtype, axis: axis, name: name);
} }
} }

+ 12
- 0
src/TensorFlowNET.Core/APIs/tf.control.cs View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Tensorflow
{
public static partial class tf
{
public static _ControlDependenciesController control_dependencies(Operation[] control_inputs)
=> ops.control_dependencies(control_inputs);
}
}

+ 10
- 1
src/TensorFlowNET.Core/APIs/tf.init.cs View File

@@ -10,7 +10,8 @@ namespace Tensorflow
public static IInitializer zeros_initializer => new Zeros(); public static IInitializer zeros_initializer => new Zeros();
public static IInitializer ones_initializer => new Ones(); public static IInitializer ones_initializer => new Ones();
public static IInitializer glorot_uniform_initializer => new GlorotUniform(); public static IInitializer glorot_uniform_initializer => new GlorotUniform();
public static IInitializer uniform_initializer => new RandomUniform();

public static variable_scope variable_scope(string name, public static variable_scope variable_scope(string name,
string default_name = null, string default_name = null,
object values = null, object values = null,
@@ -27,5 +28,13 @@ namespace Tensorflow
default_name, default_name,
values, values,
auxiliary_name_scope); auxiliary_name_scope);

public static IInitializer truncated_normal_initializer(float mean = 0.0f,
float stddev = 1.0f,
int? seed = null,
TF_DataType dtype = TF_DataType.DtInvalid) => new TruncatedNormal(mean: mean,
stddev: stddev,
seed: seed,
dtype: dtype);
} }
} }

+ 46
- 0
src/TensorFlowNET.Core/APIs/tf.layers.cs View File

@@ -100,6 +100,52 @@ namespace Tensorflow


return layer.apply(inputs, training: training); return layer.apply(inputs, training: training);
} }

/// <summary>
/// Max pooling layer for 2D inputs (e.g. images).
/// </summary>
/// <param name="inputs">The tensor over which to pool. Must have rank 4.</param>
/// <param name="pool_size"></param>
/// <param name="strides"></param>
/// <param name="padding"></param>
/// <param name="data_format"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor max_pooling2d(Tensor inputs,
int[] pool_size,
int[] strides,
string padding = "valid",
string data_format = "channels_last",
string name = null)
{
var layer = new MaxPooling2D(pool_size: pool_size,
strides: strides,
padding: padding,
data_format: data_format,
name: name);

return layer.apply(inputs);
}

public static Tensor dense(Tensor inputs,
int units,
IActivation activation = null,
bool use_bias = true,
IInitializer kernel_initializer = null,
IInitializer bias_initializer = null,
bool trainable = true,
string name = null,
bool? reuse = null)
{
if (bias_initializer == null)
bias_initializer = tf.zeros_initializer;

var layer = new Dense(units, activation,
use_bias: use_bias,
kernel_initializer: kernel_initializer);

return layer.apply(inputs);
}
} }
} }
} }

+ 231
- 6
src/TensorFlowNET.Core/APIs/tf.math.cs View File

@@ -6,21 +6,235 @@ namespace Tensorflow
{ {
public static partial class tf public static partial class tf
{ {
public static Tensor add(Tensor a, Tensor b) => gen_math_ops.add(a, b);
public static Tensor abs(Tensor x, string name = null)
=> math_ops.abs(x, name);


public static Tensor sub(Tensor a, Tensor b) => gen_math_ops.sub(a, b);
/// <summary>
/// Computes acos of x element-wise.
/// </summary>
/// <param name="x"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor acos(Tensor x, string name = null)
=> gen_math_ops.acos(x, name);

/// <summary>
/// Computes asin of x element-wise.
/// </summary>
/// <param name="x"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor asin(Tensor x, string name = null)
=> gen_math_ops.asin(x, name);

public static Tensor add(Tensor a, Tensor b)
=> gen_math_ops.add(a, b);

/// <summary>
/// Computes atan of x element-wise.
/// </summary>
/// <param name="x"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor atan(Tensor x, string name = null)
=> gen_math_ops.atan(x, name);

public static Tensor arg_max(Tensor input, int dimension, TF_DataType output_type = TF_DataType.TF_INT64, string name = null)
=> gen_math_ops.arg_max(input, dimension, output_type: output_type, name: name);

public static Tensor arg_min(Tensor input, int dimension, TF_DataType output_type = TF_DataType.TF_INT64, string name = null)
=> gen_math_ops.arg_min(input, dimension, output_type: output_type, name: name);

/// <summary>
/// Returns element-wise smallest integer not less than x.
/// </summary>
/// <param name="x"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor ceil(Tensor x, string name = null)
=> gen_math_ops.ceil(x, name);

/// <summary>
/// Computes cos of x element-wise.
/// </summary>
/// <param name="x"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor cos(Tensor x, string name = null)
=> gen_math_ops.cos(x, name);

/// <summary>
/// Computes hyperbolic cosine of x element-wise.
/// </summary>
/// <param name="x"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor cosh(Tensor x, string name = null)
=> gen_math_ops.cosh(x, name);

/// <summary>
/// Returns element-wise largest integer not greater than x.
/// </summary>
/// <param name="x"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor floor(Tensor x, string name = null)
=> gen_math_ops.floor(x, name);

/// <summary>
/// Returns the truth value of (x > y) element-wise.
/// </summary>
/// <typeparam name="Tx"></typeparam>
/// <typeparam name="Ty"></typeparam>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor greater<Tx, Ty>(Tx x, Ty y, string name = null)
=> gen_math_ops.greater(x, y, name);

/// <summary>
/// Returns the truth value of (x >= y) element-wise.
/// </summary>
/// <typeparam name="Tx"></typeparam>
/// <typeparam name="Ty"></typeparam>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor greater_equal<Tx, Ty>(Tx x, Ty y, string name = null)
=> gen_math_ops.greater_equal(x, y, name);

/// <summary>
/// Returns the truth value of (x < y) element-wise.
/// </summary>
/// <typeparam name="Tx"></typeparam>
/// <typeparam name="Ty"></typeparam>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor less<Tx, Ty>(Tx x, Ty y, string name = null)
=> gen_math_ops.less(x, y, name);


public static Tensor sqrt(Tensor a, string name = null) => gen_math_ops.sqrt(a, name);
/// <summary>
/// Returns the truth value of (x <= y) element-wise.
/// </summary>
/// <typeparam name="Tx"></typeparam>
/// <typeparam name="Ty"></typeparam>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor less_equal<Tx, Ty>(Tx x, Ty y, string name = null)
=> gen_math_ops.less_equal(x, y, name);

/// <summary>
/// Computes natural logarithm of (1 + x) element-wise.
/// </summary>
/// <param name="x"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor log1p(Tensor x, string name = null)
=> gen_math_ops.log1p(x, name);

/// <summary>
/// Clips tensor values to a specified min and max.
/// </summary>
/// <param name="t"></param>
/// <param name="clip_value_min"></param>
/// <param name="clip_value_max"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor _clip_by_value(Tensor t, Tensor clip_value_min, Tensor clip_value_max, string name = null)
=> gen_math_ops._clip_by_value(t, clip_value_min, clip_value_max);

public static Tensor sub(Tensor a, Tensor b)
=> gen_math_ops.sub(a, b);

public static Tensor sqrt(Tensor a, string name = null)
=> gen_math_ops.sqrt(a, name);


public static Tensor subtract<T>(Tensor x, T[] y, string name = null) where T : struct public static Tensor subtract<T>(Tensor x, T[] y, string name = null) where T : struct
=> gen_math_ops.sub(x, ops.convert_to_tensor(y, dtype: x.dtype.as_base_dtype(), name: "y"), name); => gen_math_ops.sub(x, ops.convert_to_tensor(y, dtype: x.dtype.as_base_dtype(), name: "y"), name);


public static Tensor multiply(Tensor x, Tensor y) => gen_math_ops.mul(x, y);
public static Tensor log(Tensor x, string name = null)
=> gen_math_ops.log(x, name);

public static Tensor equal(Tensor x, Tensor y, string name = null)
=> gen_math_ops.equal(x, y, name);

/// <summary>
/// Computes arctangent of `y/x` element-wise, respecting signs of the arguments.
/// </summary>
/// <param name="y"></param>
/// <param name="x"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor atan2(Tensor y, Tensor x, string name = null)
=> gen_math_ops.atan2(y, x, name);

/// <summary>
/// Computes the maximum of elements across dimensions of a tensor.
/// </summary>
/// <typeparam name="Tx"></typeparam>
/// <typeparam name="Ty"></typeparam>
/// <param name="input"></param>
/// <param name="axis"></param>
/// <param name="keep_dims"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor max<Tx, Ty>(Tx input, Ty axis, bool keep_dims = false, string name = null)
=> gen_math_ops._max(input, axis, keep_dims: keep_dims, name: name);

/// <summary>
/// Computes the minimum of elements across dimensions of a tensor.
/// </summary>
/// <typeparam name="Tx"></typeparam>
/// <typeparam name="Ty"></typeparam>
/// <param name="input"></param>
/// <param name="axis"></param>
/// <param name="keep_dims"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor min<Tx, Ty>(Tx input, Ty axis, bool keep_dims = false, string name = null)
=> gen_math_ops._min(input, axis, keep_dims: keep_dims, name: name);

/// <summary>
/// Returns the max of x and y (i.e. x > y ? x : y) element-wise.
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor maximum<T1, T2>(T1 x, T2 y, string name = null)
=> gen_math_ops.maximum(x, y, name: name);

/// <summary>
/// Returns the min of x and y (i.e. x < y ? x : y) element-wise.
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor minimum<T1, T2>(T1 x, T2 y, string name = null)
=> gen_math_ops.minimum(x, y, name: name);

public static Tensor multiply(Tensor x, Tensor y)
=> gen_math_ops.mul(x, y);

public static Tensor negative(Tensor x, string name = null)
=> gen_math_ops.neg(x, name);


public static Tensor divide<T>(Tensor x, T[] y, string name = null) where T : struct public static Tensor divide<T>(Tensor x, T[] y, string name = null) where T : struct
=> x / ops.convert_to_tensor(y, dtype: x.dtype.as_base_dtype(), name: "y"); => x / ops.convert_to_tensor(y, dtype: x.dtype.as_base_dtype(), name: "y");


public static Tensor pow<T1, T2>(T1 x, T2 y) => gen_math_ops.pow(x, y);
public static Tensor pow<T1, T2>(T1 x, T2 y)
=> gen_math_ops.pow(x, y);


/// <summary> /// <summary>
/// Computes the sum of elements across dimensions of a tensor. /// Computes the sum of elements across dimensions of a tensor.
@@ -28,9 +242,20 @@ namespace Tensorflow
/// <param name="input"></param> /// <param name="input"></param>
/// <param name="axis"></param> /// <param name="axis"></param>
/// <returns></returns> /// <returns></returns>
public static Tensor reduce_sum(Tensor input, int[] axis = null) => math_ops.reduce_sum(input);
public static Tensor reduce_sum(Tensor input, int? axis = null, int? reduction_indices = null)
{
if(!axis.HasValue && reduction_indices.HasValue)
return math_ops.reduce_sum(input, reduction_indices.Value);
return math_ops.reduce_sum(input);
}

public static Tensor reduce_mean(Tensor input_tensor, int[] axis = null, bool keepdims = false, string name = null, int? reduction_indices = null)
=> math_ops.reduce_mean(input_tensor, axis: axis, keepdims: keepdims, name: name, reduction_indices: reduction_indices);


public static Tensor cast(Tensor x, TF_DataType dtype = TF_DataType.DtInvalid, string name = null) public static Tensor cast(Tensor x, TF_DataType dtype = TF_DataType.DtInvalid, string name = null)
=> math_ops.cast(x, dtype, name); => math_ops.cast(x, dtype, name);

public static Tensor argmax(Tensor input, int axis = -1, string name = null, int? dimension = null, TF_DataType output_type = TF_DataType.TF_INT64)
=> gen_math_ops.arg_max(input, axis, name: name, output_type: output_type);
} }
} }

+ 34
- 13
src/TensorFlowNET.Core/APIs/tf.nn.cs View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Tensorflow.Operations;
using Tensorflow.Operations.Activation; using Tensorflow.Operations.Activation;


namespace Tensorflow namespace Tensorflow
@@ -27,19 +28,39 @@ namespace Tensorflow


public static IActivation relu => new relu(); public static IActivation relu => new relu();


public static (Tensor, Tensor, Tensor) fused_batch_norm(Tensor x,
RefVariable scale,
RefVariable offset,
Tensor mean = null,
Tensor variance = null,
float epsilon = 0.001f,
string data_format = "NHWC",
bool is_training = true,
string name = null) => nn_impl.fused_batch_norm(x, scale, offset, mean, variance,
epsilon: epsilon,
data_format: data_format,
is_training: is_training,
name: name);
public static Tensor[] fused_batch_norm(Tensor x,
RefVariable scale,
RefVariable offset,
Tensor mean = null,
Tensor variance = null,
float epsilon = 0.001f,
string data_format = "NHWC",
bool is_training = true,
string name = null) => nn_impl.fused_batch_norm(x, scale, offset, mean, variance,
epsilon: epsilon,
data_format: data_format,
is_training: is_training,
name: name);

public static IPoolFunction max_pool => new MaxPoolFunction();

public static Tensor[] top_k(Tensor input, int k = 1, bool sorted = true, string name = null)
=> gen_nn_ops.top_kv2(input, k: k, sorted: sorted, name: name);

public static Tensor bias_add(Tensor value, RefVariable bias, string data_format = null, string name = null)
{
return Python.with(ops.name_scope(name, "BiasAdd", new { value, bias }), scope =>
{
name = scope;
return gen_nn_ops.bias_add(value, bias, data_format: data_format, name: name);
});
}

public static Tensor softmax(Tensor logits, int axis = -1, string name = null)
=> gen_nn_ops.softmax(logits, name);

public static Tensor softmax_cross_entropy_with_logits_v2(Tensor labels, Tensor logits, int axis = -1, string name = null)
=> nn_ops.softmax_cross_entropy_with_logits_v2_helper(labels, logits, axis: axis, name: name);
} }
} }
} }

+ 3
- 0
src/TensorFlowNET.Core/APIs/tf.reshape.cs View File

@@ -10,5 +10,8 @@ namespace Tensorflow
Tensor shape, Tensor shape,
string name = null) => gen_array_ops.reshape(tensor, shape, name); string name = null) => gen_array_ops.reshape(tensor, shape, name);


public static Tensor reshape(Tensor tensor,
int[] shape,
string name = null) => gen_array_ops.reshape(tensor, shape, name);
} }
} }

+ 86
- 0
src/TensorFlowNET.Core/Clustering/KMeans.cs View File

@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Tensorflow.Clustering
{
/// <summary>
/// Creates the graph for k-means clustering.
/// </summary>
public class KMeans : Python
{
public const string CLUSTERS_VAR_NAME = "clusters";

public const string SQUARED_EUCLIDEAN_DISTANCE = "squared_euclidean";
public const string COSINE_DISTANCE = "cosine";
public const string RANDOM_INIT = "random";
public const string KMEANS_PLUS_PLUS_INIT = "kmeans_plus_plus";
public const string KMC2_INIT = "kmc2";

Tensor[] _inputs;
int _num_clusters;
IInitializer _initial_clusters;
string _distance_metric;
bool _use_mini_batch;
int _mini_batch_steps_per_iteration;
int _random_seed;
int _kmeans_plus_plus_num_retries;
int _kmc2_chain_length;

public KMeans(Tensor inputs,
int num_clusters,
IInitializer initial_clusters = null,
string distance_metric = SQUARED_EUCLIDEAN_DISTANCE,
bool use_mini_batch = false,
int mini_batch_steps_per_iteration = 1,
int random_seed = 0,
int kmeans_plus_plus_num_retries = 2,
int kmc2_chain_length = 200)
{
_inputs = new Tensor[] { inputs };
_num_clusters = num_clusters;
_initial_clusters = initial_clusters;
_distance_metric = distance_metric;
_use_mini_batch = use_mini_batch;
_mini_batch_steps_per_iteration = mini_batch_steps_per_iteration;
_random_seed = random_seed;
_kmeans_plus_plus_num_retries = kmeans_plus_plus_num_retries;
_kmc2_chain_length = kmc2_chain_length;
}

public object training_graph()
{
var initial_clusters = _initial_clusters;
var num_clusters = ops.convert_to_tensor(_num_clusters);
var inputs = _inputs;
_create_variables(num_clusters);

throw new NotImplementedException("KMeans training_graph");
}

private RefVariable[] _create_variables(Tensor num_clusters)
{
var init_value = constant_op.constant(new float[0], dtype: TF_DataType.TF_FLOAT);
var cluster_centers = tf.Variable(init_value, name: CLUSTERS_VAR_NAME, validate_shape: false);
var cluster_centers_initialized = tf.Variable(false, dtype: TF_DataType.TF_BOOL, name: "initialized");
RefVariable update_in_steps = null;
if (_use_mini_batch && _mini_batch_steps_per_iteration > 1)
throw new NotImplementedException("KMeans._create_variables");
else
{
var cluster_centers_updated = cluster_centers;
var cluster_counts = _use_mini_batch ?
tf.Variable(array_ops.ones(new Tensor[] { num_clusters }, dtype: TF_DataType.TF_INT64)) :
null;
return new RefVariable[]
{
cluster_centers,
cluster_centers_initialized,
cluster_counts,
cluster_centers_updated,
update_in_steps
};
}
}
}
}

+ 10
- 0
src/TensorFlowNET.Core/Framework/common_shapes.py.cs View File

@@ -29,5 +29,15 @@ namespace Tensorflow.Framework
{ {
throw new NotFiniteNumberException(); throw new NotFiniteNumberException();
} }

public static int? rank(Tensor tensor)
{
return tensor.rank;
}

public static bool has_fully_defined_shape(Tensor tensor)
{
return tensor.getShape().is_fully_defined();
}
} }
} }

+ 1
- 1
src/TensorFlowNET.Core/Framework/meta_graph.py.cs View File

@@ -184,7 +184,7 @@ namespace Tensorflow


// Adds graph_def or the default. // Adds graph_def or the default.
if (graph_def == null) if (graph_def == null)
meta_graph_def.GraphDef = graph._as_graph_def(add_shapes: true);
meta_graph_def.GraphDef = graph.as_graph_def(add_shapes: true);
else else
meta_graph_def.GraphDef = graph_def; meta_graph_def.GraphDef = graph_def;




+ 7
- 4
src/TensorFlowNET.Core/Framework/smart_module.cs View File

@@ -6,9 +6,9 @@ namespace Tensorflow.Framework
{ {
public class smart_module public class smart_module
{ {
public static object smart_cond(Tensor pred,
Func<(Tensor, Tensor, Tensor)> true_fn = null,
Func<(Tensor, Tensor, Tensor)> false_fn = null,
public static Tensor[] smart_cond<T>(Tensor pred,
Func<T[]> true_fn = null,
Func<T[]> false_fn = null,
string name = null) string name = null)
{ {
return control_flow_ops.cond(pred, return control_flow_ops.cond(pred,
@@ -17,9 +17,12 @@ namespace Tensorflow.Framework
name: name); name: name);
} }


public static bool smart_constant_value(Tensor pred)
public static bool? smart_constant_value(Tensor pred)
{ {
var pred_value = tensor_util.constant_value(pred); var pred_value = tensor_util.constant_value(pred);
if (pred_value is null)
return null;

return pred_value; return pred_value;
} }
} }


+ 30
- 0
src/TensorFlowNET.Core/Gradients/array_grad.py.cs View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Tensorflow.Gradients
{
public class array_grad
{
public static Tensor[] _ReshapeGrad(Operation op, Tensor[] grads)
{
return new Tensor[] { array_ops.reshape(grads[0], array_ops.shape(op.inputs[0])), null };
}

public static Tensor[] _SqueezeGrad(Operation op, Tensor[] grads)
{
return new Tensor[] { _ReshapeToInput(op, grads[0]) };
}

private static Tensor _ReshapeToInput(Operation op, Tensor grad)
{
return array_ops.reshape(grad, array_ops.shape(op.inputs[0]));
}

public static Tensor[] _TransposeGrad(Operation op, Tensor[] grads)
{
var p = op.inputs[1];
return new Tensor[] { array_ops.transpose(grads[0], array_ops.invert_permutation(p)), null };
}
}
}

+ 23
- 0
src/TensorFlowNET.Core/Gradients/control_flow_grad.py.cs View File

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

namespace Tensorflow.Gradients
{
public class control_flow_grad
{
public static Tensor[] _MergeGrad(Operation op, Tensor[] grads)
{
var grad = grads[0];
var _ = grads[1];
var input_op = op.inputs[0].op;
var graph = ops.get_default_graph();
var op_ctxt = control_flow_util.GetOutputContext(input_op);
var pred = (op_ctxt as CondContext).pred;

var results = control_flow_ops._SwitchRefOrTensor(grad, pred, name: "cond_grad");
return new Tensor[] { results.Item1, results.Item2 };
}
}
}

+ 21
- 11
src/TensorFlowNET.Core/Gradients/gradients_impl.py.cs View File

@@ -131,12 +131,23 @@ namespace Tensorflow
// for ops that do not have gradients. // for ops that do not have gradients.
var grad_fn = ops.get_gradient_function(op); var grad_fn = ops.get_gradient_function(op);


foreach(var (i, out_grad) in enumerate(out_grads))
{
if(out_grad == null)
{
if (loop_state != null)
;
else
out_grads[i] = control_flow_ops.ZerosLikeOutsideLoop(op, i);
}
}

with(ops.name_scope(op.name + "_grad"), scope1 => with(ops.name_scope(op.name + "_grad"), scope1 =>
{ {
string name1 = scope1; string name1 = scope1;
if (grad_fn != null) if (grad_fn != null)
{ {
in_grads = _MaybeCompile(grad_scope, op, out_grads[0], null, grad_fn);
in_grads = _MaybeCompile(grad_scope, op, out_grads, null, grad_fn);
_VerifyGeneratedGradients(in_grads, op); _VerifyGeneratedGradients(in_grads, op);
} }


@@ -226,7 +237,7 @@ namespace Tensorflow
$"inputs {op.inputs._inputs.Count()}"); $"inputs {op.inputs._inputs.Count()}");
} }


private static Tensor[] _MaybeCompile(string scope, Operation op, Tensor out_grads, Action func, Func<Operation, Tensor, Tensor[]> grad_fn)
private static Tensor[] _MaybeCompile(string scope, Operation op, Tensor[] out_grads, Action func, Func<Operation, Tensor[], Tensor[]> grad_fn)
{ {
scope = scope.EndsWith("/") ? scope.Substring(0, scope.Length - 1) : scope; scope = scope.EndsWith("/") ? scope.Substring(0, scope.Length - 1) : scope;
return grad_fn(op, out_grads); return grad_fn(op, out_grads);
@@ -240,28 +251,27 @@ namespace Tensorflow
private static Tensor[] _AggregatedGrads(Dictionary<string, Tensor[][]> grads, Operation op, string gradient_uid, object loop_state, int aggregation_method = 0) private static Tensor[] _AggregatedGrads(Dictionary<string, Tensor[][]> grads, Operation op, string gradient_uid, object loop_state, int aggregation_method = 0)
{ {
var out_grads = _GetGrads(grads, op); var out_grads = _GetGrads(grads, op);
for(int i = 0; i < out_grads.Length; i++)
var return_grads = new Tensor[out_grads.Length];

foreach(var (i, out_grad) in enumerate(out_grads))
{ {
var out_grad = out_grads[i];
if(loop_state != null)
if (loop_state != null)
{ {


} }


// Grads have to be Tensors or IndexedSlices

// Aggregate multiple gradients, and convert [] to None. // Aggregate multiple gradients, and convert [] to None.
if(out_grad != null)
if (out_grad != null)
{ {
if(out_grad.Length < 2)
if (out_grad.Length < 2)
{ {
string used = "nop"; string used = "nop";
return new Tensor[] { out_grad[0] };
return_grads[i] = out_grad[0];
} }
} }
} }


return null;
return return_grads;
} }


/// <summary> /// <summary>


+ 255
- 0
src/TensorFlowNET.Core/Gradients/math_grad.cs View File

@@ -0,0 +1,255 @@
//using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Tensorflow.Gradients
{
/// <summary>
/// Gradients for operators defined in math_ops.py.
/// </summary>
public class math_grad : Python
{
public static Tensor[] _AddGrad(Operation op, Tensor[] grads)
{
var x = op.inputs[0];
var y = op.inputs[1];
var grad = grads[0];
if (grad is Tensor && _ShapesFullySpecifiedAndEqual(x, y, grad))
return new Tensor[] { grad, grad };

var sx = array_ops.shape(x);
var sy = array_ops.shape(y);
var (rx, ry) = gen_array_ops.broadcast_gradient_args(sx, sy);

var sum1 = math_ops.reduce_sum(grad, rx);
var r1 = gen_array_ops.reshape(sum1, sx);
var sum2 = math_ops.reduce_sum(grad, ry);
var r2 = gen_array_ops.reshape(sum2, sy);

return new Tensor[] { r1, r2 };
}

public static Tensor[] _IdGrad(Operation op, Tensor[] grads)
{
return new Tensor[] { grads[0] };
}

public static Tensor[] _LogGrad(Operation op, Tensor[] grads)
{
var grad = grads[0];
var x = op.inputs[0];
return with(ops.control_dependencies(new Operation[] { grad }), dp => {
x = math_ops.conj(x);
return new Tensor[] { grad * math_ops.reciprocal(x) };
});
}

public static Tensor[] _MulGrad(Operation op, Tensor[] grads)
{
var x = op.inputs[0];
var y = op.inputs[1];
var grad = grads[0];
if (grad is Tensor &&
_ShapesFullySpecifiedAndEqual(x, y, grad) &&
new TF_DataType[] { tf.int32, tf.float32 }.Contains(grad.dtype))
return new Tensor[] { gen_math_ops.mul(grad, y), gen_math_ops.mul(grad, x) };

var sx = array_ops.shape(x);
var sy = array_ops.shape(y);
var (rx, ry) = gen_array_ops.broadcast_gradient_args(sx, sy);

x = math_ops.conj(x);
y = math_ops.conj(y);

var mul1 = gen_math_ops.mul(grad, y);
var reduce_sum1 = math_ops.reduce_sum(mul1, rx);
var reshape1 = gen_array_ops.reshape(reduce_sum1, sx);

var mul2 = gen_math_ops.mul(x, grad);
var reduce_sum2 = math_ops.reduce_sum(mul2, ry);
var reshape2 = gen_array_ops.reshape(reduce_sum2, sy);

return new Tensor[] { reshape1, reshape2 };
}

public static Tensor[] _MatMulGrad(Operation op, Tensor[] grads)
{
var grad = grads[0];
Tensor grad_a = null, grad_b = null;

var t_a = (bool)op.get_attr("transpose_a");
var t_b = (bool)op.get_attr("transpose_b");
var a = math_ops.conj(op.inputs[0]);
var b = math_ops.conj(op.inputs[1]);
if(!t_a && !t_b)
{
grad_a = gen_math_ops.mat_mul(grad, b, transpose_b: true);
grad_b = gen_math_ops.mat_mul(a, grad, transpose_a: true);
}
else if (!t_a && t_b)
{
grad_a = gen_math_ops.mat_mul(grad, b);
grad_b = gen_math_ops.mat_mul(grad, a, transpose_a: true);
}
else if (t_a && !t_b)
{
grad_a = gen_math_ops.mat_mul(grad, b);
grad_b = gen_math_ops.mat_mul(grad, a, transpose_a: true);
}
else if (t_a && t_b)
{
grad_a = gen_math_ops.mat_mul(b, grad, transpose_a: true, transpose_b: true);
grad_b = gen_math_ops.mat_mul(grad, a, transpose_a: true, transpose_b: true);
}

return new Tensor[] { grad_a, grad_b };
}

public static Tensor[] _MeanGrad(Operation op, Tensor[] grads)
{
var grad = grads[0];
var sum_grad = _SumGrad(op, grads)[0];
var input_shape = op.inputs[0]._shape_tuple();
var output_shape = op.outputs[0]._shape_tuple();

var input_shape_tensor = array_ops.shape(op.inputs[0]);
var output_shape_tensor = array_ops.shape(op.outputs[0]);
var factor = _safe_shape_div(math_ops.reduce_prod(input_shape_tensor), math_ops.reduce_prod(output_shape_tensor));

return new Tensor[] { math_ops.truediv(sum_grad, math_ops.cast(factor, sum_grad.dtype)), null };
}

public static Tensor[] _NegGrad(Operation op, Tensor[] grads)
{
return new Tensor[] { -grads[0] };
}

private static Tensor _safe_shape_div(Tensor x, Tensor y)
{
return math_ops.floordiv(x, gen_math_ops.maximum(y, 1));
}

public static Tensor[] _SubGrad(Operation op, Tensor[] grads)
{
var grad = grads[0];
var x = op.inputs[0];
var y = op.inputs[1];
if (grad is Tensor && _ShapesFullySpecifiedAndEqual(x, y, grad))
return new Tensor[] { grad, -grad };

var sx = array_ops.shape(x);
var sy = array_ops.shape(y);
var (rx, ry) = gen_array_ops.broadcast_gradient_args(sx, sy);

var r1 = gen_array_ops.reshape(math_ops.reduce_sum(grad, rx), sx);
var r2 = gen_array_ops.reshape(-math_ops.reduce_sum(grad, ry), sy);

return new Tensor[] { r1, r2 };
}

public static bool _ShapesFullySpecifiedAndEqual(Tensor x, Tensor y, Tensor grad)
{
var x_shape = x._shape_tuple();
var y_shape = y._shape_tuple();
var grad_shape = grad._shape_tuple();
return Enumerable.SequenceEqual(x_shape, y_shape) &&
Enumerable.SequenceEqual(y_shape, grad_shape) &&
x.NDims != -1 &&
!x_shape.Contains(-1);
}

public static Tensor[] _SumGrad(Operation op, Tensor[] grads)
{
var grad = grads[0];
var input_0_shape = op.inputs[0]._shape_tuple();
Tensor input_shape = null;

if (input_0_shape != null)
{
var axes = tensor_util.constant_value(op.inputs[1]);
if(!(axes is null))
{
var rank = input_0_shape.Length;
if (Enumerable.SequenceEqual(Enumerable.Range(0, rank), axes.Data<int>()))
{
grad = array_ops.reshape(grad, new int[] { 1 });
if (!input_0_shape.Contains(-1))
input_shape = constant_op.constant(input_0_shape);
else
input_shape = array_ops.shape(op.inputs[0]);
return new Tensor[] { gen_array_ops.tile(grad, input_shape), null };
}
}
}

input_shape = array_ops.shape(op.inputs[0]);
ops.colocate_with(input_shape);
var output_shape_kept_dims = math_ops.reduced_shape(input_shape, op.inputs[1]);
var tile_scaling = _safe_shape_div(input_shape, output_shape_kept_dims);
grad = gen_array_ops.reshape(grad, output_shape_kept_dims);

return new Tensor[] { gen_array_ops.tile(grad, tile_scaling), null };
}

public static Tensor[] _RealDivGrad(Operation op, Tensor[] grads)
{
var grad = grads[0];
var x = op.inputs[0];
var y = op.inputs[1];

var sx = array_ops.shape(x);
var sy = array_ops.shape(y);
var (rx, ry) = gen_array_ops.broadcast_gradient_args(sx, sy);
x = math_ops.conj(x);
y = math_ops.conj(y);

var realdiv1 = gen_math_ops.real_div(-x, y);
var realdiv2 = gen_math_ops.real_div(realdiv1, y);
var reduce_sum1 = math_ops.reduce_sum(grad * realdiv2, ry);
var reshape1 = gen_array_ops.reshape(reduce_sum1, sy);
var realdiv3 = gen_math_ops.real_div(grad, y);
var reduce_sum2 = math_ops.reduce_sum(realdiv3, rx);
var reshape2 = gen_array_ops.reshape(reduce_sum2, sx);

return new Tensor[] { reshape2, reshape1 };
}

public static Tensor[] _PowGrad(Operation op, Tensor[] grads)
{
var grad = grads[0];
var x = op.inputs[0];
var y = op.inputs[1];
var z = op.outputs[0];

var sx = array_ops.shape(x);
var sy = array_ops.shape(y);
var (rx, ry) = gen_array_ops.broadcast_gradient_args(sx, sy);
x = math_ops.conj(x);
y = math_ops.conj(y);
z = math_ops.conj(z);
var pow = gen_math_ops.pow(x, y - 1.0f);
var mul = grad * y * pow;
var reduce_sum = math_ops.reduce_sum(mul, rx);
var gx = gen_array_ops.reshape(reduce_sum, sx);

// Avoid false singularity at x = 0
Tensor mask = null;
if (x.dtype.is_complex())
throw new NotImplementedException("x.dtype.is_complex()");
else
mask = x > 0.0f;
var ones = array_ops.ones_like(x);
var safe_x = array_ops.where(mask, x, ones);
var x1 = gen_array_ops.log(safe_x);
var y1 = array_ops.zeros_like(x);
var log_x = array_ops.where(mask, x1, y1);
var mul1 = grad * z * log_x;
var reduce_sum1 = math_ops.reduce_sum(mul1, ry);
var gy = gen_array_ops.reshape(reduce_sum1, sy);

return new Tensor[] { gx, gy };
}
}
}

+ 0
- 160
src/TensorFlowNET.Core/Gradients/math_grad.py.cs View File

@@ -1,160 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Tensorflow
{
/// <summary>
/// Gradients for operators defined in math_ops.py.
/// </summary>
public class math_grad
{
public static (Tensor, Tensor) _AddGrad(Operation op, Tensor grad)
{
var x = op.inputs[0];
var y = op.inputs[1];
if (grad is Tensor && _ShapesFullySpecifiedAndEqual(x, y, grad))
return (grad, grad);

var sx = array_ops.shape(x);
var sy = array_ops.shape(y);
var (rx, ry) = gen_array_ops.broadcast_gradient_args(sx, sy);

var r1 = gen_array_ops.reshape(math_ops.reduce_sum(grad, rx), sx);
var r2 = gen_array_ops.reshape(math_ops.reduce_sum(grad, ry), sy);

return (r1, r2);
}

public static Tensor _IdGrad(Operation op, Tensor grad)
{
return grad;
}

public static (Tensor, Tensor) _MulGrad(Operation op, Tensor grad)
{
var x = op.inputs[0];
var y = op.inputs[1];
if (grad is Tensor && _ShapesFullySpecifiedAndEqual(x, y, grad) &&
new TF_DataType[] { tf.int32, tf.float32 }.Contains(grad.dtype))
return (gen_math_ops.mul(grad, y), gen_math_ops.mul(grad, x));

var sx = array_ops.shape(x);
var sy = array_ops.shape(y);
var (rx, ry) = gen_array_ops.broadcast_gradient_args(sx, sy);

x = math_ops.conj(x);
y = math_ops.conj(y);

var mul1 = gen_math_ops.mul(grad, y);
var mul2 = gen_math_ops.mul(x, grad);
var reduce_sum1 = math_ops.reduce_sum(mul1, rx);
var reduce_sum2 = math_ops.reduce_sum(mul2, ry);
var reshape1 = gen_array_ops.reshape(reduce_sum1, sx);
var reshape2 = gen_array_ops.reshape(reduce_sum2, sy);

return (reshape1, reshape2);
}

public static (Tensor, Tensor) _SubGrad(Operation op, Tensor grad)
{
var x = op.inputs[0];
var y = op.inputs[1];
if (grad is Tensor && _ShapesFullySpecifiedAndEqual(x, y, grad))
return (grad, -grad);

var sx = array_ops.shape(x);
var sy = array_ops.shape(y);
var (rx, ry) = gen_array_ops.broadcast_gradient_args(sx, sy);

var r1 = gen_array_ops.reshape(math_ops.reduce_sum(grad, rx), sx);
var r2 = gen_array_ops.reshape(-math_ops.reduce_sum(grad, ry), sy);

return (r1, r2);
}

public static bool _ShapesFullySpecifiedAndEqual(Tensor x, Tensor y, Tensor grad)
{
return x.NDims == y.NDims && y.NDims == grad.NDims && x.NDims > -1;
}

public static (Tensor, Tensor) _SumGrad(Operation op, Tensor grad)
{
if (op.inputs[0].NDims > -1)
{

}

var input_shape = array_ops.shape(op.inputs[0]);
ops.colocate_with(input_shape);
var output_shape_kept_dims = math_ops.reduced_shape(input_shape, op.inputs[1]);
var tile_scaling = _safe_shape_div(input_shape, output_shape_kept_dims);
grad = gen_array_ops.reshape(grad, output_shape_kept_dims);

return (gen_array_ops.tile(grad, tile_scaling), null);
}

public static Tensor _safe_shape_div(Tensor x, Tensor y)
{
return math_ops.floordiv(x, gen_math_ops.maximum(y, 1));
}

public static (Tensor, Tensor) _RealDivGrad(Operation op, Tensor grad)
{
var x = op.inputs[0];
var y = op.inputs[1];

var sx = array_ops.shape(x);
var sy = array_ops.shape(y);
var (rx, ry) = gen_array_ops.broadcast_gradient_args(sx, sy);
x = math_ops.conj(x);
y = math_ops.conj(y);

var realdiv1 = gen_math_ops.real_div(-x, y);
var realdiv2 = gen_math_ops.real_div(realdiv1, y);
var reduce_sum1 = math_ops.reduce_sum(grad * realdiv2, ry);
var reshape1 = gen_array_ops.reshape(reduce_sum1, sy);
var realdiv3 = gen_math_ops.real_div(grad, y);
var reduce_sum2 = math_ops.reduce_sum(realdiv3, rx);
var reshape2 = gen_array_ops.reshape(reduce_sum2, sx);

return (reshape2, reshape1);
}

public static (Tensor, Tensor) _PowGrad(Operation op, Tensor grad)
{
var x = op.inputs[0];
var y = op.inputs[1];
var z = op.outputs[0];

var sx = array_ops.shape(x);
var sy = array_ops.shape(y);
var (rx, ry) = gen_array_ops.broadcast_gradient_args(sx, sy);
x = math_ops.conj(x);
y = math_ops.conj(y);
z = math_ops.conj(z);
var pow = gen_math_ops.pow(x, y - 1.0f);
var mul = grad * y * pow;
var reduce_sum = math_ops.reduce_sum(mul, rx);
var gx = gen_array_ops.reshape(reduce_sum, sx);

// Avoid false singularity at x = 0
Tensor mask = null;
if (x.dtype.is_complex())
throw new NotImplementedException("x.dtype.is_complex()");
else
mask = x > 0.0f;
var ones = array_ops.ones_like(x);
var safe_x = array_ops.where(mask, x, ones);
var x1 = gen_array_ops.log(safe_x);
var y1 = array_ops.zeros_like(x);
var log_x = array_ops.where(mask, x1, y1);
var mul1 = grad * z * log_x;
var reduce_sum1 = math_ops.reduce_sum(mul1, ry);
var gy = gen_array_ops.reshape(reduce_sum1, sy);

return (gx, gy);
}
}
}

+ 135
- 0
src/TensorFlowNET.Core/Gradients/nn_grad.py.cs View File

@@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tensorflow.Operations;

namespace Tensorflow.Gradients
{
public class nn_grad
{
/// <summary>
/// Return the gradients for the 2 inputs of bias_op.
/// </summary>
/// <param name="op"></param>
/// <param name="grad"></param>
/// <returns></returns>
public static Tensor[] _BiasAddGrad(Operation op, Tensor[] grads)
{
var grad = grads[0];
string data_format = op.get_attr("data_format")?.ToString();
var bias_add_grad = gen_nn_ops.bias_add_grad(out_backprop: grad, data_format: data_format);
return new Tensor[] { grad, bias_add_grad };
}

public static Tensor[] _ReluGrad(Operation op, Tensor[] grads)
{
return new Tensor[] { gen_nn_ops.relu_grad(grads[0], op.outputs[0]) };
}

/// <summary>
/// The derivative of the softmax nonlinearity.
/// </summary>
/// <param name="op"></param>
/// <param name="grads"></param>
/// <returns></returns>
public static Tensor[] _SoftmaxGrad(Operation op, Tensor[] grads)
{
var grad_softmax = grads[0];

var softmax = op.outputs[0];
var mul = grad_softmax * softmax;
var sum_channels = math_ops.reduce_sum(mul, -1, keepdims: true);
var sub = grad_softmax - sum_channels;
return new Tensor[] { sub * softmax };
}

/// <summary>
/// Gradient function for SoftmaxCrossEntropyWithLogits.
/// </summary>
/// <param name="op"></param>
/// <param name="grad_loss"></param>
/// <param name="grad_grad"></param>
/// <returns></returns>
public static Tensor[] _SoftmaxCrossEntropyWithLogitsGrad(Operation op, Tensor[] grads)
{
var grad_loss = grads[0];
var grad_grad = grads[1];
var softmax_grad = op.outputs[1];
var grad = _BroadcastMul(grad_loss, softmax_grad);

var logits = op.inputs[0];
if(grad_grad != null && !IsZero(grad_grad))
{
throw new NotImplementedException("_SoftmaxCrossEntropyWithLogitsGrad");
}

return new Tensor[]
{
grad,
_BroadcastMul(grad_loss, -nn_ops.log_softmax(logits))
};
}

private static bool IsZero(Tensor g)
{
if (new string[] { "ZerosLike", "Zeros" }.Contains(g.op.type))
return true;

throw new NotImplementedException("IsZero");
}

private static Tensor _BroadcastMul(Tensor vec, Tensor mat)
{
vec = array_ops.expand_dims(vec, -1);
return vec * mat;
}

/// <summary>
/// Return the gradients for TopK.
/// </summary>
/// <param name="op"></param>
/// <param name="grads"></param>
/// <returns></returns>
public static Tensor[] _TopKGrad(Operation op, Tensor[] grads)
{
var grad = grads[0];
var _ = grads[1];

var in_shape = array_ops.shape(op.inputs[0]);
var ind_shape = array_ops.shape(op.outputs[1]);

// int32 is not supported on GPU hence up-casting
var cast = math_ops.cast(ind_shape, TF_DataType.TF_INT64);
var size = array_ops.size(ind_shape) - 1;
var ind_lastdim = array_ops.gather(cast, size);

// Flatten indices to 2D.
var stack = array_ops.stack(new object[] { -1L, ind_lastdim });
var ind_2d = array_ops.reshape(op.outputs[1], stack);

var in_lastdim = array_ops.gather(math_ops.cast(in_shape, TF_DataType.TF_INT64),
array_ops.size(in_shape) - 1);
var outerdim = array_ops.shape(ind_2d)[0];

// Compute linear indices(flattened to 1D).
var cast1 = math_ops.cast(outerdim, TF_DataType.TF_INT64);
var range2 = math_ops.range(0L, cast1 * in_lastdim, in_lastdim);
var dim2 = array_ops.expand_dims(range2, -1);
var cast2 = math_ops.cast(dim2, TF_DataType.TF_INT32);
var ind = array_ops.reshape(ind_2d + cast2, new int[] { -1 });

// Substitute grad to appropriate locations and fill the rest with zeros,
// finally reshaping it to the original input shape.
var scatter = gen_array_ops.scatter_nd(array_ops.expand_dims(ind, -1),
array_ops.reshape(grad, new int[] { -1 }),
new Tensor[] { math_ops.reduce_prod(in_shape) });

return new Tensor[]
{
array_ops.reshape(scatter, in_shape),
array_ops.zeros(new int[0], dtype: TF_DataType.TF_INT32)
};
}
}
}

+ 68
- 0
src/TensorFlowNET.Core/Gradients/ops.gradient_function_mapping.cs View File

@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Text;
using Tensorflow.Gradients;

namespace Tensorflow
{
public partial class ops
{
public static Func<Operation, Tensor[], Tensor[]> get_gradient_function(Operation op)
{
if (op.inputs == null) return null;

// map tensorflow\python\ops\math_grad.py
return (oper, out_grads) =>
{
// Console.WriteLine($"get_gradient_function: {oper.type} '{oper.name}'");

switch (oper.type)
{
case "Add":
return math_grad._AddGrad(oper, out_grads);
case "BiasAdd":
return nn_grad._BiasAddGrad(oper, out_grads);
case "Identity":
return math_grad._IdGrad(oper, out_grads);
case "Log":
return math_grad._LogGrad(oper, out_grads);
case "MatMul":
return math_grad._MatMulGrad(oper, out_grads);
case "Merge":
return control_flow_grad._MergeGrad(oper, out_grads);
case "Mul":
return math_grad._MulGrad(oper, out_grads);
case "Mean":
return math_grad._MeanGrad(oper, out_grads);
case "Neg":
return math_grad._NegGrad(oper, out_grads);
case "Sum":
return math_grad._SumGrad(oper, out_grads);
case "Sub":
return math_grad._SubGrad(oper, out_grads);
case "Pow":
return math_grad._PowGrad(oper, out_grads);
case "RealDiv":
return math_grad._RealDivGrad(oper, out_grads);
case "Reshape":
return array_grad._ReshapeGrad(oper, out_grads);
case "Relu":
return nn_grad._ReluGrad(oper, out_grads);
case "Squeeze":
return array_grad._SqueezeGrad(oper, out_grads);
case "Softmax":
return nn_grad._SoftmaxGrad(oper, out_grads);
case "SoftmaxCrossEntropyWithLogits":
return nn_grad._SoftmaxCrossEntropyWithLogitsGrad(oper, out_grads);
case "Transpose":
return array_grad._TransposeGrad(oper, out_grads);
case "TopK":
case "TopKV2":
return nn_grad._TopKGrad(oper, out_grads);
default:
throw new NotImplementedException($"get_gradient_function {oper.type}");
}
};
}
}
}

+ 23
- 0
src/TensorFlowNET.Core/Graphs/FreezeGraph.cs View File

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

namespace Tensorflow
{
public class FreezeGraph
{
public static void freeze_graph(string input_graph,
string input_saver,
bool input_binary,
string input_checkpoint,
string output_node_names,
string restore_op_name,
string filename_tensor_name,
string output_graph,
bool clear_devices,
string initializer_nodes)
{

}
}
}

+ 4
- 1
src/TensorFlowNET.Core/Graphs/Graph.Export.cs View File

@@ -18,7 +18,7 @@ namespace Tensorflow
return buffer; return buffer;
} }


public GraphDef _as_graph_def(bool add_shapes = false)
private GraphDef _as_graph_def(bool add_shapes = false)
{ {
var buffer = ToGraphDef(Status); var buffer = ToGraphDef(Status);
Status.Check(); Status.Check();
@@ -30,5 +30,8 @@ namespace Tensorflow


return def; return def;
} }

public GraphDef as_graph_def(bool add_shapes = false)
=> _as_graph_def(add_shapes);
} }
} }

+ 1
- 1
src/TensorFlowNET.Core/Graphs/Graph.cs View File

@@ -355,7 +355,7 @@ namespace Tensorflow
return _collections.Keys.Where(x => !x.StartsWith("__")).ToArray(); return _collections.Keys.Where(x => !x.StartsWith("__")).ToArray();
} }


public object get_collection(string name, string scope = "")
public object get_collection(string name, string scope = null)
{ {
return _collections.ContainsKey(name) ? _collections[name] : null; return _collections.ContainsKey(name) ? _collections[name] : null;
} }


+ 1
- 1
src/TensorFlowNET.Core/Graphs/graph_io.py.cs View File

@@ -10,7 +10,7 @@ namespace Tensorflow
{ {
public static string write_graph(Graph graph, string logdir, string name, bool as_text = true) public static string write_graph(Graph graph, string logdir, string name, bool as_text = true)
{ {
var graph_def = graph._as_graph_def();
var graph_def = graph.as_graph_def();
string path = Path.Combine(logdir, name); string path = Path.Combine(logdir, name);
if (as_text) if (as_text)
File.WriteAllText(path, graph_def.ToString()); File.WriteAllText(path, graph_def.ToString());


+ 6
- 3
src/TensorFlowNET.Core/Keras/Engine/InputSpec.cs View File

@@ -9,17 +9,20 @@ namespace Tensorflow.Keras.Engine
/// </summary> /// </summary>
public class InputSpec public class InputSpec
{ {
public int ndim;
public int? ndim;
public int? min_ndim;
Dictionary<int, int> axes; Dictionary<int, int> axes;


public InputSpec(TF_DataType dtype = TF_DataType.DtInvalid,
public InputSpec(TF_DataType dtype = TF_DataType.DtInvalid,
int? ndim = null, int? ndim = null,
int? min_ndim = null,
Dictionary<int, int> axes = null) Dictionary<int, int> axes = null)
{ {
this.ndim = ndim.Value;
this.ndim = ndim;
if (axes == null) if (axes == null)
axes = new Dictionary<int, int>(); axes = new Dictionary<int, int>();
this.axes = axes; this.axes = axes;
this.min_ndim = min_ndim;
} }
} }
} }

+ 6
- 1
src/TensorFlowNET.Core/Keras/Engine/Model.cs View File

@@ -4,7 +4,12 @@ using System.Text;


namespace Tensorflow.Keras.Engine namespace Tensorflow.Keras.Engine
{ {
internal class Model : Network
public class Model : Network
{ {
public Model(string name = null)
: base(name: name)
{

}
} }
} }

+ 31
- 0
src/TensorFlowNET.Core/Keras/Engine/Network.cs View File

@@ -1,10 +1,41 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Tensorflow.Keras.Layers;


namespace Tensorflow.Keras.Engine namespace Tensorflow.Keras.Engine
{ {
public class Network : Layer public class Network : Layer
{ {
protected bool _is_compiled;
protected bool _expects_training_arg;
protected bool _compute_output_and_mask_jointly;
/// <summary>
/// All layers in order of horizontal graph traversal.
/// Entries are unique. Includes input and output layers.
/// </summary>
protected List<Layer> _layers;

public Network(string name = null)
: base(name: name)
{
_init_subclassed_network(name);
}

protected virtual void _init_subclassed_network(string name = null)
{
_base_init(name: name);
}

protected virtual void _base_init(string name = null)
{
_init_set_name(name);
trainable = true;
_is_compiled = false;
_expects_training_arg = false;
_compute_output_and_mask_jointly = false;
supports_masking = false;
_layers = new List<Layer>();
}
} }
} }

+ 37
- 5
src/TensorFlowNET.Core/Keras/Engine/Sequential.cs View File

@@ -1,24 +1,56 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Tensorflow.Keras.Layers;


namespace Tensorflow.Keras.Engine namespace Tensorflow.Keras.Engine
{ {
public class Sequential : Network, IPython
public class Sequential : Model, IPython
{ {
public void Dispose()
public Sequential(string name = null)
: base(name: name)
{ {
throw new NotImplementedException();
supports_masking = true;
_compute_output_and_mask_jointly = true;
} }


public void __enter__() public void __enter__()
{ {
throw new NotImplementedException();
}

public void add(Layer layer)
{
built = false;
var set_inputs = false;
if(_layers.Count == 0)
{
var (batch_shape, dtype) = (layer._batch_input_shape, layer._dtype);
if(batch_shape != null)
{
// Instantiate an input layer.
var x = keras.layers.Input(
batch_shape: batch_shape,
dtype: dtype,
name: layer._name + "_input");

// This will build the current layer
// and create the node connecting the current layer
// to the input layer we just created.
layer.__call__(x);
set_inputs = true;
}
}
} }


public void __exit__() public void __exit__()
{ {
throw new NotImplementedException();
}

public void Dispose()
{

} }
} }
} }

+ 38
- 6
src/TensorFlowNET.Core/Keras/Layers/BatchNormalization.cs View File

@@ -3,11 +3,10 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Tensorflow.Keras.Utils; using Tensorflow.Keras.Utils;
using Tensorflow.Layers;


namespace Tensorflow.Keras.Layers namespace Tensorflow.Keras.Layers
{ {
public class BatchNormalization : Layer
public class BatchNormalization : Tensorflow.Layers.Layer
{ {
private bool _USE_V2_BEHAVIOR = true; private bool _USE_V2_BEHAVIOR = true;
private float momentum; private float momentum;
@@ -132,6 +131,7 @@ namespace Tensorflow.Keras.Layers
if (fused) if (fused)
{ {
outputs = _fused_batch_norm(inputs, training: training); outputs = _fused_batch_norm(inputs, training: training);
return outputs;
} }


throw new NotImplementedException("BatchNormalization call"); throw new NotImplementedException("BatchNormalization call");
@@ -142,7 +142,7 @@ namespace Tensorflow.Keras.Layers
var beta = this.beta; var beta = this.beta;
var gamma = this.gamma; var gamma = this.gamma;


Func<(Tensor, Tensor, Tensor)> _fused_batch_norm_training = () =>
Func<Tensor[]> _fused_batch_norm_training = () =>
{ {
return tf.nn.fused_batch_norm( return tf.nn.fused_batch_norm(
inputs, inputs,
@@ -152,7 +152,7 @@ namespace Tensorflow.Keras.Layers
data_format: _data_format); data_format: _data_format);
}; };


Func<(Tensor, Tensor, Tensor)> _fused_batch_norm_inference = () =>
Func<Tensor[]> _fused_batch_norm_inference = () =>
{ {
return tf.nn.fused_batch_norm( return tf.nn.fused_batch_norm(
inputs, inputs,
@@ -165,9 +165,41 @@ namespace Tensorflow.Keras.Layers
data_format: _data_format); data_format: _data_format);
}; };


tf_utils.smart_cond(training, _fused_batch_norm_training, _fused_batch_norm_inference);
var results = tf_utils.smart_cond(training, _fused_batch_norm_training, _fused_batch_norm_inference);
var (output, mean, variance) = (results[0], results[1], results[2]);
var training_value = tf_utils.constant_value(training);


throw new NotImplementedException("_fused_batch_norm");
Tensor momentum_tensor;
if (training_value == null)
{
momentum_tensor = tf_utils.smart_cond(training,
() => new float[] { momentum }, () => new float[] { 1.0f })[0];
}
else
{
momentum_tensor = ops.convert_to_tensor(momentum);
}
if(training_value == null)
{
var mean_update = _assign_moving_average(moving_mean, mean, momentum_tensor);
var variance_update = _assign_moving_average(moving_variance, variance, momentum_tensor);
add_update(new Tensor[] { mean_update }, inputs: true);
add_update(new Tensor[] { variance_update }, inputs: true);
}

return output;
}

public Tensor _assign_moving_average(RefVariable variable, Tensor value, Tensor momentum)
{
return Python.with(ops.name_scope(null, "AssignMovingAvg", new { variable, value, momentum }), scope =>
{
// var cm = ops.colocate_with(variable);
var decay = ops.convert_to_tensor(1.0f - momentum, name: "decay");
var update_delta = (variable - math_ops.cast(value, variable.dtype)) * decay;
return state_ops.assign_sub(variable, update_delta, name: scope);
});
} }
} }
} }

+ 81
- 0
src/TensorFlowNET.Core/Keras/Layers/Dense.cs View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tensorflow.Keras.Engine;
using Tensorflow.Operations.Activation;
using static Tensorflow.tf;

namespace Tensorflow.Keras.Layers
{
public class Dense : Tensorflow.Layers.Layer
{
protected int units;
protected IActivation activation;
protected bool use_bias;
protected IInitializer kernel_initializer;
protected IInitializer bias_initializer;
protected RefVariable kernel;
protected RefVariable bias;

public Dense(int units,
IActivation activation,
bool use_bias = true,
bool trainable = false,
IInitializer kernel_initializer = null,
IInitializer bias_initializer = null) : base(trainable: trainable)
{
this.units = units;
this.activation = activation;
this.use_bias = use_bias;
this.kernel_initializer = kernel_initializer;
this.bias_initializer = bias_initializer;
this.supports_masking = true;
this.input_spec = new InputSpec(min_ndim: 2);
}

protected override void build(TensorShape input_shape)
{
var last_dim = input_shape.Dimensions.Last();
var axes = new Dictionary<int, int>();
axes[-1] = last_dim;
input_spec = new InputSpec(min_ndim: 2, axes: axes);
kernel = add_weight(
"kernel",
shape: new int[] { last_dim, units },
initializer: kernel_initializer,
dtype: _dtype,
trainable: true);
if (use_bias)
bias = add_weight(
"bias",
shape: new int[] { units },
initializer: bias_initializer,
dtype: _dtype,
trainable: true);

built = true;
}

protected override Tensor call(Tensor inputs, Tensor training = null)
{
Tensor outputs = null;
var rank = inputs.rank;
if(rank > 2)
{
throw new NotImplementedException("call rank > 2");
}
else
{
outputs = gen_math_ops.mat_mul(inputs, kernel);
}

if (use_bias)
outputs = nn.bias_add(outputs, bias);
if (activation != null)
return activation.Activate(outputs);

return outputs;
}
}
}

+ 36
- 0
src/TensorFlowNET.Core/Keras/Layers/Embedding.cs View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Tensorflow.Keras.Layers
{
public class Embedding : Layer
{
private int input_dim;
private int output_dim;
private bool mask_zero;
public RefVariable embeddings;
public IInitializer embeddings_initializer;

public Embedding(int input_dim, int output_dim,
IInitializer embeddings_initializer = null,
bool mask_zero = false,
TF_DataType dtype = TF_DataType.TF_FLOAT,
int[] input_shape = null) : base(dtype: dtype, input_shape: input_shape)
{
this.input_dim = input_dim;
this.output_dim = output_dim;
this.embeddings_initializer = embeddings_initializer == null ? tf.uniform_initializer : embeddings_initializer;
this.mask_zero = mask_zero;
supports_masking = mask_zero;
}

protected override void build(TensorShape input_shape)
{
embeddings = add_weight(shape: new int[] { input_dim, output_dim },
initializer: embeddings_initializer,
name: "embeddings");
built = true;
}
}
}

+ 16
- 0
src/TensorFlowNET.Core/Keras/Layers/IPoolFunction.cs View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Tensorflow
{
public interface IPoolFunction
{
Tensor Apply(Tensor value,
int[] ksize,
int[] strides,
string padding,
string data_format = "NHWC",
string name = null);
}
}

+ 56
- 0
src/TensorFlowNET.Core/Keras/Layers/InputLayer.cs View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Tensorflow.Keras.Layers
{
/// <summary>
/// Layer to be used as an entry point into a Network (a graph of layers).
/// </summary>
public class InputLayer : Layer
{
public bool sparse;
public int? batch_size;
public bool is_placeholder;

public InputLayer(int[] input_shape = null,
int? batch_size = null,
TF_DataType dtype = TF_DataType.DtInvalid,
string name = null,
bool sparse = false,
Tensor input_tensor = null)
{
built = true;
this.sparse = sparse;
this.batch_size = batch_size;
this.supports_masking = true;

if (input_tensor == null)
{
var batch_input_shape = new int[] { batch_size.HasValue ? batch_size.Value : -1, -1 };

if (sparse)
{
throw new NotImplementedException("InputLayer sparse is true");
}
else
{
input_tensor = backend.placeholder(
shape: batch_input_shape,
dtype: dtype,
name: name);
}

is_placeholder = true;
_batch_input_shape = batch_input_shape;
}

new Node(this,
inbound_layers: new Layer[0],
node_indices: new int[0],
tensor_indices: new int[0],
input_tensors: new Tensor[] { input_tensor },
output_tensors: new Tensor[] { input_tensor });
}
}
}

src/TensorFlowNET.Core/Keras/Engine/Layer.cs → src/TensorFlowNET.Core/Keras/Layers/Layer.cs View File

@@ -1,9 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using Tensorflow.Keras.Engine;
using Tensorflow.Keras.Utils; using Tensorflow.Keras.Utils;


namespace Tensorflow.Keras.Engine
namespace Tensorflow.Keras.Layers
{ {
/// <summary> /// <summary>
/// Base layer class. /// Base layer class.
@@ -19,7 +21,7 @@ namespace Tensorflow.Keras.Engine
/// </summary> /// </summary>
protected bool built; protected bool built;
protected bool trainable; protected bool trainable;
protected TF_DataType _dtype;
public TF_DataType _dtype;
/// <summary> /// <summary>
/// A stateful layer is a layer whose updates are run during inference too, /// A stateful layer is a layer whose updates are run during inference too,
/// for instance stateful RNNs. /// for instance stateful RNNs.
@@ -31,11 +33,22 @@ namespace Tensorflow.Keras.Engine
protected InputSpec input_spec; protected InputSpec input_spec;
protected bool supports_masking; protected bool supports_masking;
protected List<RefVariable> _trainable_weights; protected List<RefVariable> _trainable_weights;
protected string _name;
public string _name;
protected string _base_name; protected string _base_name;
protected bool _compute_previous_mask; protected bool _compute_previous_mask;
protected List<Operation> _updates;
public int[] _batch_input_shape;


public Layer(bool trainable = true, string name = null, TF_DataType dtype = TF_DataType.DtInvalid)
private List<Node> _inbound_nodes;
public List<Node> inbound_nodes => _inbound_nodes;

private List<Node> _outbound_nodes;
public List<Node> outbound_nodes => _outbound_nodes;

public Layer(bool trainable = true,
string name = null,
TF_DataType dtype = TF_DataType.DtInvalid,
int[] input_shape = null)
{ {
this.trainable = trainable; this.trainable = trainable;
this._dtype = dtype; this._dtype = dtype;
@@ -45,13 +58,22 @@ namespace Tensorflow.Keras.Engine
_init_set_name(name); _init_set_name(name);
_trainable_weights = new List<RefVariable>(); _trainable_weights = new List<RefVariable>();
_compute_previous_mask = false; _compute_previous_mask = false;
_updates = new List<Operation>();

// Manage input shape information if passed.

_batch_input_shape = new int[] { -1, -1 };

_dtype = dtype;

_inbound_nodes = new List<Node>();
} }


public Tensor __call__(Tensor inputs,
public Tensor __call__(Tensor[] inputs,
Tensor training = null, Tensor training = null,
VariableScope scope = null) VariableScope scope = null)
{ {
var input_list = new Tensor[] { inputs };
var input_list = inputs;
Tensor outputs = null; Tensor outputs = null;


// We will attempt to build a TF graph if & only if all inputs are symbolic. // We will attempt to build a TF graph if & only if all inputs are symbolic.
@@ -74,9 +96,9 @@ namespace Tensorflow.Keras.Engine
// Symbolic execution on symbolic tensors. We will attempt to build // Symbolic execution on symbolic tensors. We will attempt to build
// the corresponding TF subgraph inside `backend.get_graph()` // the corresponding TF subgraph inside `backend.get_graph()`
var graph = backend.get_graph(); var graph = backend.get_graph();
outputs = call(inputs, training: training);
_handle_activity_regularization(inputs, outputs);
_set_mask_metadata(inputs, outputs, null);
outputs = call(inputs[0], training: training);
_handle_activity_regularization(inputs[0], outputs);
_set_mask_metadata(inputs[0], outputs, null);
} }
}); });


@@ -103,7 +125,7 @@ namespace Tensorflow.Keras.Engine


protected virtual Tensor call(Tensor inputs, Tensor training = null) protected virtual Tensor call(Tensor inputs, Tensor training = null)
{ {
throw new NotImplementedException("Layer.call");
return inputs;
} }


protected virtual string _name_scope() protected virtual string _name_scope()
@@ -111,15 +133,15 @@ namespace Tensorflow.Keras.Engine
return null; return null;
} }


protected void _maybe_build(Tensor inputs)
protected void _maybe_build(Tensor[] inputs)
{ {
var input_list = new Tensor[] { inputs };
build(inputs.getShape());
var input_list = inputs;
build(input_list[0].getShape());
} }


protected virtual void build(TensorShape input_shape) protected virtual void build(TensorShape input_shape)
{ {
throw new NotImplementedException("Layer.build");
built = true;
} }


protected virtual RefVariable add_weight(string name, protected virtual RefVariable add_weight(string name,
@@ -129,10 +151,16 @@ namespace Tensorflow.Keras.Engine
bool? trainable = null, bool? trainable = null,
Func<string, int[], TF_DataType, IInitializer, bool, RefVariable> getter = null) Func<string, int[], TF_DataType, IInitializer, bool, RefVariable> getter = null)
{ {
if (dtype == TF_DataType.DtInvalid)
dtype = TF_DataType.TF_FLOAT;

if (trainable == null)
trainable = true;

var variable = _add_variable_with_custom_getter(name, var variable = _add_variable_with_custom_getter(name,
shape, shape,
dtype: dtype, dtype: dtype,
getter: getter,
getter: getter == null ? base_layer_utils.make_variable : getter,
overwrite: true, overwrite: true,
initializer: initializer, initializer: initializer,
trainable: trainable.Value); trainable: trainable.Value);
@@ -142,6 +170,12 @@ namespace Tensorflow.Keras.Engine
return variable; return variable;
} }


protected virtual void add_update(Tensor[] updates, bool inputs = false)
{
var updates_op = updates.Select(x => x.op).ToArray();
_updates.AddRange(updates_op);
}

protected virtual void _init_set_name(string name) protected virtual void _init_set_name(string name)
{ {
string base_name = name; string base_name = name;

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

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
using static Tensorflow.tf;

namespace Tensorflow.Keras.Layers
{
public class MaxPooling2D : Pooling2D
{
public MaxPooling2D(
int[] pool_size,
int[] strides,
string padding = "valid",
string data_format = null,
string name = null) : base(nn.max_pool, pool_size,
strides,
padding: padding,
data_format: data_format,
name: name)
{

}
}
}

+ 71
- 0
src/TensorFlowNET.Core/Keras/Layers/Node.cs View File

@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Tensorflow.Keras.Layers
{
/// <summary>
/// A `Node` describes the connectivity between two layers.
/// </summary>
public class Node
{
public InputLayer outbound_layer;
public Layer[] inbound_layers;
public int[] node_indices;
public int[] tensor_indices;
public Tensor[] input_tensors;
public Tensor[] output_tensors;
public int[][] input_shapes;
public int[][] output_shapes;

/// <summary>
///
/// </summary>
/// <param name="outbound_layer">
/// the layer that takes
/// `input_tensors` and turns them into `output_tensors`
/// (the node gets created when the `call`
/// method of the layer was called).
/// </param>
/// <param name="inbound_layers">
/// a list of layers, the same length as `input_tensors`,
/// the layers from where `input_tensors` originate.
/// </param>
/// <param name="node_indices">
/// a list of integers, the same length as `inbound_layers`.
/// `node_indices[i]` is the origin node of `input_tensors[i]`
/// (necessary since each inbound layer might have several nodes,
/// e.g. if the layer is being shared with a different data stream).
/// </param>
/// <param name="tensor_indices"></param>
/// <param name="input_tensors">list of input tensors.</param>
/// <param name="output_tensors">list of output tensors.</param>
public Node(InputLayer outbound_layer,
Layer[] inbound_layers,
int[] node_indices,
int[] tensor_indices,
Tensor[] input_tensors,
Tensor[] output_tensors)
{
this.outbound_layer = outbound_layer;
this.inbound_layers = inbound_layers;
this.node_indices = node_indices;
this.tensor_indices = tensor_indices;
this.input_tensors = input_tensors;
this.output_tensors = output_tensors;

input_shapes = input_tensors.Select(x => x._shape_tuple()).ToArray();
output_shapes = output_tensors.Select(x => x._shape_tuple()).ToArray();

// Add nodes to all layers involved.
foreach (var layer in inbound_layers)
{
if (layer != null)
layer.outbound_nodes.Add(this);
}

outbound_layer.inbound_nodes.Add(this);
}
}
}

+ 57
- 0
src/TensorFlowNET.Core/Keras/Layers/Pooling2D.cs View File

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

namespace Tensorflow.Keras.Layers
{
public class Pooling2D : Tensorflow.Layers.Layer
{
private IPoolFunction pool_function;
private int[] pool_size;
private int[] strides;
private string padding;
private string data_format;
private InputSpec input_spec;

public Pooling2D(IPoolFunction pool_function,
int[] pool_size,
int[] strides,
string padding = "valid",
string data_format = null,
string name = null) : base(name: name)
{
this.pool_function = pool_function;
this.pool_size = conv_utils.normalize_tuple(pool_size, 2, "pool_size");
this.strides = conv_utils.normalize_tuple(strides, 2, "strides");
this.padding = conv_utils.normalize_padding(padding);
this.data_format = conv_utils.normalize_data_format(data_format);
this.input_spec = new InputSpec(ndim: 4);
}

protected override Tensor call(Tensor inputs, Tensor training = null)
{
int[] pool_shape;
if (data_format == "channels_last")
{
pool_shape = new int[] { 1, pool_size[0], pool_size[1], 1 };
strides = new int[] { 1, strides[0], strides[1], 1 };
}
else
{
pool_shape = new int[] { 1, 1, pool_size[0], pool_size[1] };
strides = new int[] { 1, 1, strides[0], strides[1] };
}

var outputs = pool_function.Apply(
inputs,
ksize: pool_shape,
strides: strides,
padding: padding.ToUpper(),
data_format: conv_utils.convert_data_format(data_format, 4));

return outputs;
}
}
}

src/TensorFlowNET.Core/Keras/Engine/base_layer_utils.cs → src/TensorFlowNET.Core/Keras/Utils/base_layer_utils.cs View File

@@ -2,10 +2,19 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;


namespace Tensorflow.Keras.Engine
namespace Tensorflow.Keras.Utils
{ {
public class base_layer_utils public class base_layer_utils
{ {
public static RefVariable make_variable(string name,
int[] shape,
TF_DataType dtype = TF_DataType.TF_FLOAT,
IInitializer initializer = null,
bool trainable = false)
{
throw new NotImplementedException("");
}

/// <summary> /// <summary>
/// Makes a layer name (or arbitrary string) unique within a TensorFlow graph. /// Makes a layer name (or arbitrary string) unique within a TensorFlow graph.
/// </summary> /// </summary>

+ 15
- 0
src/TensorFlowNET.Core/Keras/Utils/conv_utils.cs View File

@@ -29,5 +29,20 @@ namespace Tensorflow.Keras.Utils
else else
throw new ValueError($"Invalid data_format: {data_format}"); throw new ValueError($"Invalid data_format: {data_format}");
} }

public static int[] normalize_tuple(int[] value, int n, string name)
{
return value;
}

public static string normalize_padding(string value)
{
return value.ToLower();
}

public static string normalize_data_format(string value)
{
return value.ToLower();
}
} }
} }

+ 8
- 3
src/TensorFlowNET.Core/Keras/Utils/tf_utils.cs View File

@@ -13,14 +13,19 @@ namespace Tensorflow.Keras.Utils
return tensors.Select(x => is_symbolic_tensor(x)).Count() == tensors.Length; return tensors.Select(x => is_symbolic_tensor(x)).Count() == tensors.Length;
} }


public static bool? constant_value(Tensor pred)
{
return smart_module.smart_constant_value(pred);
}

public static bool is_symbolic_tensor(Tensor tensor) public static bool is_symbolic_tensor(Tensor tensor)
{ {
return true; return true;
} }


public static object smart_cond(Tensor pred,
Func<(Tensor, Tensor, Tensor)> true_fn = null,
Func<(Tensor, Tensor, Tensor)> false_fn = null,
public static Tensor[] smart_cond<T>(Tensor pred,
Func<T[]> true_fn = null,
Func<T[]> false_fn = null,
string name = null) string name = null)
{ {
return smart_module.smart_cond(pred, return smart_module.smart_cond(pred,


+ 16
- 0
src/TensorFlowNET.Core/Keras/backend.cs View File

@@ -11,6 +11,22 @@ namespace Tensorflow.Keras


} }


public static Tensor placeholder(int[] shape = null,
int ndim = -1,
TF_DataType dtype = TF_DataType.DtInvalid,
bool sparse = false,
string name = null)
{
if(sparse)
{
throw new NotImplementedException("placeholder sparse is true");
}
else
{
return gen_array_ops.placeholder(dtype: dtype, shape: new TensorShape(shape), name: name);
}
}

public static Graph get_graph() public static Graph get_graph()
{ {
return ops.get_default_graph(); return ops.get_default_graph();


+ 23
- 0
src/TensorFlowNET.Core/Layers/Dense.cs View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;
using Tensorflow.Operations.Activation;

namespace Tensorflow.Layers
{
public class Dense : Keras.Layers.Dense
{
public Dense(int units,
IActivation activation,
bool use_bias = true,
bool trainable = false,
IInitializer kernel_initializer = null) : base(units,
activation,
use_bias: use_bias,
trainable: trainable,
kernel_initializer: kernel_initializer)
{

}
}
}

+ 16
- 4
src/TensorFlowNET.Core/Layers/Layer.cs View File

@@ -1,11 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using Tensorflow.Keras.Engine;


namespace Tensorflow.Layers namespace Tensorflow.Layers
{ {
public class Layer : Keras.Engine.Layer
public class Layer : Keras.Layers.Layer
{ {
protected Graph _graph; protected Graph _graph;
@@ -52,14 +52,26 @@ namespace Tensorflow.Layers


Python.with(scope_context_manager, scope2 => _current_scope = scope2); Python.with(scope_context_manager, scope2 => _current_scope = scope2);
// Actually call layer // Actually call layer
var outputs = base.__call__(inputs, training: training);
var outputs = base.__call__(new Tensor[] { inputs }, training: training);


// Update global default collections. // Update global default collections.
//_add_elements_to_collection(updates, ops.GraphKeys.UPDATE_OPS);
_add_elements_to_collection(_updates.ToArray(), new string[] { ops.GraphKeys.UPDATE_OPS });


return outputs; return outputs;
} }


protected virtual void _add_elements_to_collection(Operation[] elements, string[] collection_list)
{
foreach(var name in collection_list)
{
var collection = ops.get_collection_ref(name) as List<object>;

foreach (var element in elements)
if (!collection.Contains(element))
collection.Add(element);
}
}

protected virtual RefVariable add_weight(string name, protected virtual RefVariable add_weight(string name,
int[] shape, int[] shape,
TF_DataType dtype = TF_DataType.DtInvalid, TF_DataType dtype = TF_DataType.DtInvalid,


src/TensorFlowNET.Core/Operations/Activation/gen_nn_ops.py.cs → src/TensorFlowNET.Core/Operations/Activation/gen_nn_ops.activations.cs View File


+ 16
- 6
src/TensorFlowNET.Core/Operations/ControlFlows/CondContext.cs View File

@@ -10,22 +10,23 @@ namespace Tensorflow.Operations
public class CondContext : ControlFlowContext public class CondContext : ControlFlowContext
{ {
private string _name; private string _name;

/// <summary> /// <summary>
/// The boolean tensor for the cond predicate /// The boolean tensor for the cond predicate
/// </summary> /// </summary>
private Tensor _pred; private Tensor _pred;
/// <summary>
/// The predicate tensor in this branch
/// </summary>
private Tensor _pivot;
public Tensor pred => _pred;

/// <summary> /// <summary>
/// 0 or 1 representing this branch /// 0 or 1 representing this branch
/// </summary> /// </summary>
private int _branch; private int _branch;

/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
private List<string> _values = new List<string>(); private List<string> _values = new List<string>();

private Dictionary<string, Tensor> _external_values = new Dictionary<string, Tensor>(); private Dictionary<string, Tensor> _external_values = new Dictionary<string, Tensor>();


/// <summary> /// <summary>
@@ -63,14 +64,23 @@ namespace Tensorflow.Operations
} }
} }


public (Tensor, Tensor, Tensor) BuildCondBranch(Func<(Tensor, Tensor, Tensor)> fn)
public (T[], Tensor[]) BuildCondBranch<T>(Func<T[]> fn)
{ {
// Add the subgraph defined by fn() to the graph. // Add the subgraph defined by fn() to the graph.
var pre_summaries = ops.get_collection(ops.GraphKeys._SUMMARY_COLLECTION); var pre_summaries = ops.get_collection(ops.GraphKeys._SUMMARY_COLLECTION);
var original_result = fn(); var original_result = fn();
var post_summaries = ops.get_collection(ops.GraphKeys._SUMMARY_COLLECTION); var post_summaries = ops.get_collection(ops.GraphKeys._SUMMARY_COLLECTION);


return original_result;
switch (original_result)
{
case Tensor[] results:
return (original_result, results);
case float[] fv:
var result = ops.convert_to_tensor(fv[0]);
return (original_result, new Tensor[] { result });
default:
return (original_result, new Tensor[0]);
}
} }
} }
} }

+ 28
- 0
src/TensorFlowNET.Core/Operations/ControlFlows/ControlFlowContext.cs View File

@@ -6,6 +6,11 @@ namespace Tensorflow.Operations
{ {
public abstract class ControlFlowContext : IPython, IControlFlowContext public abstract class ControlFlowContext : IPython, IControlFlowContext
{ {
/// <summary>
/// The predicate tensor in this branch
/// </summary>
protected Tensor _pivot;

protected Stack<IControlFlowContext> _context_stack; protected Stack<IControlFlowContext> _context_stack;
public ControlFlowContext() public ControlFlowContext()
{ {
@@ -28,6 +33,29 @@ namespace Tensorflow.Operations
graph._set_control_flow_context(this); graph._set_control_flow_context(this);
} }


public void AddOp(Operation op)
{
_AddOpInternal(op);
}

protected virtual void _AddOpInternal(Operation op)
{
if(op.inputs.Length == 0)
{
_RemoveExternalControlEdges(op);
op._add_control_input(_pivot.op);
}
else
{

}
}

protected virtual void _RemoveExternalControlEdges(Operation op)
{
var internal_control_inputs = op.control_inputs;
}

public void Exit() public void Exit()
{ {
var graph = ops.get_default_graph(); var graph = ops.get_default_graph();


+ 1
- 0
src/TensorFlowNET.Core/Operations/ControlFlows/IControlFlowContext.cs View File

@@ -6,5 +6,6 @@ namespace Tensorflow
{ {
public interface IControlFlowContext public interface IControlFlowContext
{ {
void AddOp(Operation op);
} }
} }

+ 38
- 0
src/TensorFlowNET.Core/Operations/Initializers/RandomUniform.cs View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Tensorflow.Operations.Initializers
{
public class RandomUniform : IInitializer
{
private int? seed;
private float minval;
private float maxval;
private TF_DataType dtype;

public RandomUniform()
{

}

public Tensor call(TensorShape shape, TF_DataType dtype = TF_DataType.DtInvalid)
{
return random_ops.random_uniform(shape,
minval: minval,
maxval: maxval,
dtype: dtype,
seed: seed);
}

public object get_config()
{
return new {
minval,
maxval,
seed,
dtype
};
}
}
}

+ 4
- 4
src/TensorFlowNET.Core/Operations/Initializers/TruncatedNormal.cs View File

@@ -11,9 +11,9 @@ namespace Tensorflow.Operations.Initializers
private int? seed; private int? seed;
private TF_DataType dtype; private TF_DataType dtype;


public TruncatedNormal(float mean = 0.0f,
float stddev = 1.0f,
int? seed = null,
public TruncatedNormal(float mean = 0.0f,
float stddev = 1.0f,
int? seed = null,
TF_DataType dtype = TF_DataType.TF_FLOAT) TF_DataType dtype = TF_DataType.TF_FLOAT)
{ {
this.mean = mean; this.mean = mean;
@@ -24,7 +24,7 @@ namespace Tensorflow.Operations.Initializers


public Tensor call(TensorShape shape, TF_DataType dtype) public Tensor call(TensorShape shape, TF_DataType dtype)
{ {
throw new NotImplementedException("");
return random_ops.truncated_normal(shape, mean, stddev, dtype : dtype, seed: seed);
} }


public object get_config() public object get_config()


+ 29
- 0
src/TensorFlowNET.Core/Operations/NnOps/MaxPoolFunction.cs View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Tensorflow.Operations
{
public class MaxPoolFunction : Python, IPoolFunction
{
public Tensor Apply(Tensor value,
int[] ksize,
int[] strides,
string padding,
string data_format = "NHWC",
string name = null)
{
return with(ops.name_scope(name, "MaxPool", new { value }), scope => {

value = ops.convert_to_tensor(value, name: "input");
return gen_nn_ops.max_pool(
value,
ksize: ksize,
strides: strides,
padding: padding,
data_format: data_format,
name: name);
});
}
}
}

+ 98
- 2
src/TensorFlowNET.Core/Operations/NnOps/gen_nn_ops.cs View File

@@ -53,7 +53,23 @@ namespace Tensorflow.Operations
return _op.outputs[0]; return _op.outputs[0];
} }


public static (Tensor, Tensor, Tensor) _fused_batch_norm(Tensor x,
public static Tensor bias_add_grad(Tensor out_backprop,
string data_format = "NHWC",
string name = null)
{
if (data_format == null)
data_format = "NHWC";

var _op = _op_def_lib._apply_op_helper("BiasAddGrad", name: name, args: new
{
out_backprop,
data_format
});

return _op.outputs[0];
}

public static Tensor[] _fused_batch_norm(Tensor x,
Tensor scale, Tensor scale,
Tensor offset, Tensor offset,
Tensor mean, Tensor mean,
@@ -75,7 +91,87 @@ namespace Tensorflow.Operations
is_training is_training
}); });


return (_op.outputs[0], _op.outputs[1], _op.outputs[2]);
return _op.outputs;
}

public static Tensor log_softmax(Tensor logits, string name = null)
{
var _op = _op_def_lib._apply_op_helper("LogSoftmax", name: name, args: new
{
logits
});

return _op.outputs[0];
}

public static Tensor max_pool(Tensor input,
int[] ksize,
int[] strides,
string padding,
string data_format = "NHWC",
string name = null)
{
var _op = _op_def_lib._apply_op_helper("MaxPool", name: name, args: new
{
input,
ksize,
strides,
padding,
data_format,
});

return _op.outputs[0];
}

public static Tensor[] top_kv2(Tensor input, int k, bool sorted = true, string name = null)
{
var _op = _op_def_lib._apply_op_helper("TopKV2", name: name, args: new
{
input,
k,
sorted
});

return _op.outputs;
}

public static Tensor relu_grad(Tensor gradients, Tensor features, string name = null)
{
var _op = _op_def_lib._apply_op_helper("ReluGrad", name: name, args: new
{
gradients,
features
});

return _op.outputs[0];
}

public static Tensor softmax(Tensor logits, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Softmax", name: name, args: new
{
logits
});

return _op.outputs[0];
}

/// <summary>
/// Computes softmax cross entropy cost and gradients to backpropagate.
/// </summary>
/// <param name="features"></param>
/// <param name="labels"></param>
/// <param name="name"></param>
/// <returns></returns>
public static (Tensor, Tensor) softmax_cross_entropy_with_logits(Tensor features, Tensor labels, string name = null)
{
var _op = _op_def_lib._apply_op_helper("SoftmaxCrossEntropyWithLogits", name: name, args: new
{
features,
labels
});

return (_op.outputs[0], _op.outputs[1]);
} }
} }
} }

+ 3
- 3
src/TensorFlowNET.Core/Operations/OpDefLibrary.cs View File

@@ -42,7 +42,7 @@ namespace Tensorflow
var attrs = new Dictionary<string, object>(); var attrs = new Dictionary<string, object>();
var inputs = new List<Tensor>(); var inputs = new List<Tensor>();
var input_types = new List<TF_DataType>(); var input_types = new List<TF_DataType>();
dynamic values = null;
object values = null;


return with(ops.name_scope(name), scope => return with(ops.name_scope(name), scope =>
{ {
@@ -116,7 +116,7 @@ namespace Tensorflow
else if (default_type_attr_map.ContainsKey(input_arg.TypeAttr)) else if (default_type_attr_map.ContainsKey(input_arg.TypeAttr))
default_dtype = (DataType)default_type_attr_map[input_arg.TypeAttr]; default_dtype = (DataType)default_type_attr_map[input_arg.TypeAttr];


values = ops.internal_convert_to_tensor(values,
var value = ops.internal_convert_to_tensor(values,
name: input_name, name: input_name,
dtype: dtype.as_tf_dtype(), dtype: dtype.as_tf_dtype(),
as_ref: input_arg.IsRef, as_ref: input_arg.IsRef,
@@ -125,7 +125,7 @@ namespace Tensorflow
//if (!String.IsNullOrEmpty(input_arg.TypeAttr)) //if (!String.IsNullOrEmpty(input_arg.TypeAttr))
//attrs[input_arg.TypeAttr] = values.dtype; //attrs[input_arg.TypeAttr] = values.dtype;


values = new Tensor[] { values };
values = new Tensor[] { value };
} }


if (values is Tensor[] values2) if (values is Tensor[] values2)


+ 17
- 6
src/TensorFlowNET.Core/Operations/Operation.Control.cs View File

@@ -7,7 +7,7 @@ namespace Tensorflow
{ {
public partial class Operation public partial class Operation
{ {
private CondContext _control_flow_context;
private IControlFlowContext _control_flow_context;


/// <summary> /// <summary>
/// Add this op to its control flow context. /// Add this op to its control flow context.
@@ -18,19 +18,30 @@ namespace Tensorflow
{ {


} }

if (_control_flow_context != null)
_control_flow_context.AddOp(this);
}

public void _add_control_input(Operation op)
{
c_api.TF_AddControlInput(_handle, op);
} }


public void _add_control_inputs(Operation[] ops) public void _add_control_inputs(Operation[] ops)
{ {
foreach(var op in ops)
{
c_api.TF_AddControlInput(graph, op);
}
foreach (var op in ops)
_add_control_input(op);
} }


public void _set_control_flow_context(CondContext ctx)
public void _set_control_flow_context(IControlFlowContext ctx)
{ {
_control_flow_context = ctx; _control_flow_context = ctx;
} }

public IControlFlowContext _get_control_flow_context()
{
return _control_flow_context;
}
} }
} }

+ 2
- 1
src/TensorFlowNET.Core/Operations/Operation.Output.cs View File

@@ -1,4 +1,5 @@
using System;
//using Newtonsoft.Json;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;


+ 10
- 2
src/TensorFlowNET.Core/Operations/Operation.cs View File

@@ -1,4 +1,5 @@
using Google.Protobuf.Collections; using Google.Protobuf.Collections;
//using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -102,8 +103,12 @@ namespace Tensorflow
} }
} }


// Dict mapping op name to file and line information for op colocation
// context managers.
_control_flow_context = graph._get_control_flow_context();

// This will be set by self.inputs. // This will be set by self.inputs.
if(op_def == null)
if (op_def == null)
op_def = g.GetOpDef(node_def.Op); op_def = g.GetOpDef(node_def.Op);


var grouped_inputs = _reconstruct_sequence_inputs(op_def, inputs, node_def.Attr); var grouped_inputs = _reconstruct_sequence_inputs(op_def, inputs, node_def.Attr);
@@ -185,7 +190,10 @@ namespace Tensorflow
if (oneof_value == "type") if (oneof_value == "type")
return x.Type; return x.Type;


return x.GetType().GetProperty(oneof_value).GetValue(x);
object result = x.GetType().GetProperty(oneof_value).GetValue(x);
if (result is Google.Protobuf.ByteString byteString)
return byteString.ToStringUtf8();
return result;
} }


public TF_AttrMetadata GetAttributeMetadata(string attr_name, Status s) public TF_AttrMetadata GetAttributeMetadata(string attr_name, Status s)


+ 196
- 53
src/TensorFlowNET.Core/Operations/array_ops.py.cs View File

@@ -7,7 +7,8 @@ namespace Tensorflow
{ {
public class array_ops : Python public class array_ops : Python
{ {
public static Tensor placeholder_with_default<T>(T input, int[] shape, string name = null) => gen_array_ops.placeholder_with_default(input, shape, name);
public static Tensor placeholder_with_default<T>(T input, int[] shape, string name = null)
=> gen_array_ops.placeholder_with_default(input, shape, name);
public static Tensor zeros(Shape shape, TF_DataType dtype = TF_DataType.TF_FLOAT, string name = null) public static Tensor zeros(Shape shape, TF_DataType dtype = TF_DataType.TF_FLOAT, string name = null)
{ {
@@ -41,20 +42,85 @@ namespace Tensorflow
else else
{ {
tShape = constant_op._tensor_shape_tensor_conversion_function(shape.as_shape()); tShape = constant_op._tensor_shape_tensor_conversion_function(shape.as_shape());
var c = constant_op.constant(0);
var c = constant_op.constant(0, dtype: dtype);
return gen_array_ops.fill(tShape, c, name: name); return gen_array_ops.fill(tShape, c, name: name);
} }
} }
public static Tensor expand_dims(Tensor input, int axis = -1, string name = null, int dim = -1) => expand_dims_v2(input, axis, name);
public static Tensor _autopacking_conversion_function(object[] v, TF_DataType dtype = TF_DataType.DtInvalid, string name = null, bool as_ref = false)
{
var inferred_dtype = _get_dtype_from_nested_lists(v);
if (dtype == TF_DataType.DtInvalid)
dtype = inferred_dtype;
private static Tensor expand_dims_v2(Tensor input, int axis, string name = null) => gen_array_ops.expand_dims(input, axis, name);
return _autopacking_helper(v, dtype, name == null ? "packed" : name);
}
public static Tensor rank(Tensor input, string name = null)
private static TF_DataType _get_dtype_from_nested_lists(object[] list_or_tuple)
{ {
return math_ops.rank_internal(input, name, optimize: true);
TF_DataType dtype = TF_DataType.DtInvalid;
foreach(var obj in list_or_tuple)
{
switch (obj)
{
case Tensor t:
dtype = t.dtype.as_base_dtype();
break;
}
if (dtype != TF_DataType.DtInvalid)
break;
}
return dtype;
} }
public static Tensor _autopacking_helper(object[] list_or_tuple, TF_DataType dtype, string name)
{
var must_pack = false;
var converted_elems = new List<object>();
return with(ops.name_scope(name), scope =>
{
foreach (var (i, elem) in enumerate(list_or_tuple))
{
converted_elems.Add(elem);
must_pack = true;
}
if(must_pack)
{
var elems_as_tensors = new List<Tensor>();
foreach (var (i, elem) in enumerate(converted_elems))
{
if (elem is Tensor tensor)
elems_as_tensors.Add(tensor);
else
{
var elem_tensor = constant_op.constant(elem, dtype: dtype, name: i.ToString());
elems_as_tensors.Add(elem_tensor);
}
}
return gen_array_ops.pack(elems_as_tensors.ToArray(), name: scope);
}
else
{
// return converted_elems.ToArray();
throw new NotImplementedException("_autopacking_helper.converted_elems");
}
});
}
public static Tensor expand_dims(Tensor input, int axis = -1, string name = null, int dim = -1)
=> expand_dims_v2(input, axis, name);
private static Tensor expand_dims_v2(Tensor input, int axis, string name = null)
=> gen_array_ops.expand_dims(input, axis, name);
public static Tensor rank(Tensor input, string name = null)
=> math_ops.rank_internal(input, name, optimize: true);
/// <summary> /// <summary>
/// Creates a tensor with all elements set to 1. /// Creates a tensor with all elements set to 1.
/// </summary> /// </summary>
@@ -66,10 +132,8 @@ namespace Tensorflow
public static Tensor ones_like<T>(T tensor, TF_DataType dtype = TF_DataType.DtInvalid, string name = null, bool optimize = true) public static Tensor ones_like<T>(T tensor, TF_DataType dtype = TF_DataType.DtInvalid, string name = null, bool optimize = true)
=> ones_like_impl(tensor, dtype, name, optimize); => ones_like_impl(tensor, dtype, name, optimize);
public static Tensor reshape(Tensor tensor, Tensor shape, string name = null)
{
return gen_array_ops.reshape(tensor, shape, null);
}
public static Tensor reshape<T1, T2>(T1 tensor, T2 shape, string name = null)
=> gen_array_ops.reshape(tensor, shape, null);
private static Tensor ones_like_impl<T>(T tensor, TF_DataType dtype, string name, bool optimize = true) private static Tensor ones_like_impl<T>(T tensor, TF_DataType dtype, string name, bool optimize = true)
{ {
@@ -97,6 +161,18 @@ namespace Tensorflow
}); });
} }
public static Tensor ones(Tensor[] shape, TF_DataType dtype = TF_DataType.TF_FLOAT, string name = null)
{
dtype = dtype.as_base_dtype();
return with(ops.name_scope(name, "ones", new { shape }), scope =>
{
name = scope;
var shape1 = ops.convert_to_tensor(shape, dtype: TF_DataType.TF_INT32);
var output = gen_array_ops.fill(shape1, constant_op.constant(1, dtype: dtype), name: name);
return output;
});
}
public static Tensor ones(int[] dims, TF_DataType dtype = TF_DataType.TF_FLOAT, string name = null) public static Tensor ones(int[] dims, TF_DataType dtype = TF_DataType.TF_FLOAT, string name = null)
{ {
dtype = dtype.as_base_dtype(); dtype = dtype.as_base_dtype();
@@ -109,6 +185,44 @@ namespace Tensorflow
}); });
} }
public static Tensor one_hot(Tensor indices, int depth,
Tensor on_value = null,
Tensor off_value = null,
TF_DataType dtype = TF_DataType.DtInvalid,
int axis = -1,
string name = null)
{
return with(ops.name_scope(name, "one_hot", new { indices, depth, dtype }), scope =>
{
name = scope;
var on_exists = false;
var off_exists = false;
var on_dtype = TF_DataType.DtInvalid;
var off_dtype = TF_DataType.DtInvalid;
if (dtype == TF_DataType.DtInvalid)
dtype = TF_DataType.TF_FLOAT;
if(!on_exists)
{
on_value = ops.convert_to_tensor(1, dtype, name: "on_value");
on_dtype = dtype;
}
if (!off_exists)
{
off_value = ops.convert_to_tensor(0, dtype, name = "off_value");
off_dtype = dtype;
}
return gen_array_ops.one_hot(indices, depth,
on_value: on_value,
off_value: off_value,
axis: axis,
name: name);
});
}
public static Tensor where(Tensor condition, Tensor x = null, Tensor y = null, string name = null) public static Tensor where(Tensor condition, Tensor x = null, Tensor y = null, string name = null)
{ {
if( x == null && y == null) if( x == null && y == null)
@@ -136,14 +250,10 @@ namespace Tensorflow
/// </param> /// </param>
/// <returns>A `Tensor` of type `out_type`.</returns> /// <returns>A `Tensor` of type `out_type`.</returns>
public static Tensor shape(Tensor input, string name = null, TF_DataType out_type = TF_DataType.TF_INT32) public static Tensor shape(Tensor input, string name = null, TF_DataType out_type = TF_DataType.TF_INT32)
{
return shape_internal(input, name, optimize: true, out_type: out_type);
}
=> shape_internal(input, name, optimize: true, out_type: out_type);
public static Tensor size(Tensor input, string name = null, bool optimize = true, TF_DataType out_type = TF_DataType.TF_INT32) public static Tensor size(Tensor input, string name = null, bool optimize = true, TF_DataType out_type = TF_DataType.TF_INT32)
{
return size_internal(input, name, optimize: optimize, out_type: out_type);
}
=> size_internal(input, name, optimize: optimize, out_type: out_type);
private static Tensor shape_internal(Tensor input, string name = null, bool optimize = true, TF_DataType out_type = TF_DataType.TF_INT32) private static Tensor shape_internal(Tensor input, string name = null, bool optimize = true, TF_DataType out_type = TF_DataType.TF_INT32)
{ {
@@ -168,32 +278,21 @@ namespace Tensorflow
private static Tensor size_internal(Tensor input, string name = null, bool optimize = true, TF_DataType out_type = TF_DataType.TF_INT32) private static Tensor size_internal(Tensor input, string name = null, bool optimize = true, TF_DataType out_type = TF_DataType.TF_INT32)
{ {
return with(ops.name_scope(name, "Size", new Tensor[] { input }), scope =>
return with(ops.name_scope(name, "Size", new { input }), scope =>
{ {
name = scope; name = scope;
if (!tf.context.executing_eagerly())
var input_tensor = ops.convert_to_tensor(input);
var input_shape = tensor_util.to_shape(input_tensor.shape);
if (optimize)
{ {
var input_tensor = ops.convert_to_tensor(input);
var input_shape = tensor_util.to_shape(input_tensor.shape);
if (optimize)
if (input_shape.is_fully_defined())
{ {
if (input_shape.is_fully_defined())
{
var nd = np.array(input_tensor.shape, out_type.as_numpy_datatype());
return constant_op.constant(nd, name: name);
}
return constant_op.constant(input_shape.Size, dtype: out_type, name: name);
} }
return gen_array_ops.size(input, name: name, out_type: out_type);
}
else
{
// result = gen_array_ops.shape();
throw new NotImplementedException("array_ops.size_internal");
} }
return null;
return gen_array_ops.size(input, name: name, out_type: out_type);
}); });
} }
@@ -234,8 +333,46 @@ namespace Tensorflow
/// <param name="name"></param> /// <param name="name"></param>
/// <returns></returns> /// <returns></returns>
public static Tensor stop_gradient(Tensor input, string name = null) public static Tensor stop_gradient(Tensor input, string name = null)
=> gen_array_ops.stop_gradient(input, name);
/// <summary>
/// Extracts a strided slice of a tensor (generalized python array indexing).
/// </summary>
/// <param name="input_"></param>
/// <param name="begin"></param>
/// <param name="end"></param>
/// <param name="strides"></param>
/// <param name="begin_mask"></param>
/// <param name="end_mask"></param>
/// <param name="ellipsis_mask"></param>
/// <param name="new_axis_mask"></param>
/// <param name="shrink_axis_mask"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor strided_slice(Tensor input_, Tensor begin, Tensor end,
Tensor strides = null,
int begin_mask = 0,
int end_mask = 0,
int ellipsis_mask = 0,
int new_axis_mask = 0,
int shrink_axis_mask = 0,
string name = null)
{ {
return gen_array_ops.stop_gradient(input, name);
var op = gen_array_ops.strided_slice(
input: input_,
begin: begin,
end: end,
strides: strides,
begin_mask: begin_mask,
end_mask: end_mask,
ellipsis_mask: ellipsis_mask,
new_axis_mask: new_axis_mask,
shrink_axis_mask: shrink_axis_mask,
name: name);
string parent_name = name;
return op;
} }
/// <summary> /// <summary>
@@ -256,14 +393,14 @@ namespace Tensorflow
/// Contains the same data as `input`, but has one or more dimensions of /// Contains the same data as `input`, but has one or more dimensions of
/// size 1 removed.</returns> /// size 1 removed.</returns>
public static Tensor squeeze(Tensor input, int[] axis = null, string name = null, int[] squeeze_dims = null) public static Tensor squeeze(Tensor input, int[] axis = null, string name = null, int[] squeeze_dims = null)
{
return gen_array_ops.squeeze(input, axis, name);
}
=> gen_array_ops.squeeze(input, axis, name);
public static Tensor identity(Tensor input, string name = null) public static Tensor identity(Tensor input, string name = null)
{
return gen_array_ops.identity(input, name);
}
=> gen_array_ops.identity(input, name);
public static Tensor invert_permutation(Tensor x, string name = null)
=> gen_array_ops.invert_permutation(x, name: name);
/// <summary> /// <summary>
/// Computes the shape of a broadcast given symbolic shapes. /// Computes the shape of a broadcast given symbolic shapes.
/// When shape_x and shape_y are Tensors representing shapes(i.e.the result of /// When shape_x and shape_y are Tensors representing shapes(i.e.the result of
@@ -279,27 +416,33 @@ namespace Tensorflow
/// <param name="shape_y"> A rank 1 integer `Tensor`, representing the shape of y.</param> /// <param name="shape_y"> A rank 1 integer `Tensor`, representing the shape of y.</param>
/// <returns> A rank 1 integer `Tensor` representing the broadcasted shape.</returns> /// <returns> A rank 1 integer `Tensor` representing the broadcasted shape.</returns>
public static Tensor broadcast_dynamic_shape(Tensor shape_x, Tensor shape_y) public static Tensor broadcast_dynamic_shape(Tensor shape_x, Tensor shape_y)
{
return gen_array_ops.broadcast_args(shape_x, shape_y);
}
=> gen_array_ops.broadcast_args(shape_x, shape_y);
public static Tensor broadcast_static_shape(Tensor shape_x, Tensor shape_y) public static Tensor broadcast_static_shape(Tensor shape_x, Tensor shape_y)
{
return Framework.common_shapes.broadcast_shape(shape_x, shape_y);
}
=> Framework.common_shapes.broadcast_shape(shape_x, shape_y);
public static Tensor gather(Tensor @params, Tensor indices, string name = null, int axis = 0) public static Tensor gather(Tensor @params, Tensor indices, string name = null, int axis = 0)
{
return gen_array_ops.gather_v2(@params, indices, axis, name: name);
}
=> gen_array_ops.gather_v2(@params, indices, axis, name: name);
public static Tensor transpose(Tensor a, int[] perm = null, string name = "transpose", bool conjugate = false)
public static Tensor transpose<T1, T2>(T1 a, T2 perm, string name = "transpose", bool conjugate = false)
{ {
return with(ops.name_scope(name, "transpose", new { a }), scope => return with(ops.name_scope(name, "transpose", new { a }), scope =>
{ {
name = scope;
return gen_array_ops.transpose(a, perm, name);
return gen_array_ops.transpose(a, perm, name: scope);
}); });
} }
public static Tensor slice<Tb, Ts>(Tensor input, Tb[] begin, Ts[] size, string name = null)
=> gen_array_ops.slice(input, begin, size, name: name);
public static Tensor stack(object values, int axis = 0, string name = "stack")
{
if (axis == 0)
// If the input is a constant list, it can be converted to a constant op
return ops.convert_to_tensor(values, name: name);
throw new NotImplementedException("array_ops.stack");
}
} }
} }

+ 64
- 8
src/TensorFlowNET.Core/Operations/control_flow_ops.py.cs View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Tensorflow.Operations; using Tensorflow.Operations;
using util = Tensorflow.control_flow_util;


namespace Tensorflow namespace Tensorflow
{ {
@@ -137,9 +138,25 @@ namespace Tensorflow
return gen_array_ops.identity(data, name: name); return gen_array_ops.identity(data, name: name);
} }


public static (Tensor, Tensor) cond(Tensor pred,
Func<(Tensor, Tensor, Tensor)> true_fn = null,
Func<(Tensor, Tensor, Tensor)> false_fn = null,
/// <summary>
/// Forwards `data` to an output determined by `pred`.
/// </summary>
/// <param name="data"></param>
/// <param name="pred"></param>
/// <param name="name"></param>
/// <returns></returns>
public static (Tensor, Tensor) _SwitchRefOrTensor(Tensor data, Tensor pred, string name = "Switch")
{
data = ops.convert_to_tensor_or_indexed_slices(data, name: "data");

ops.colocate_with(data, ignore_existing: true);

return @switch(data, pred, name: name);
}

public static Tensor[] cond<T>(Tensor pred,
Func<T[]> true_fn = null,
Func<T[]> false_fn = null,
bool strict = false, bool strict = false,
string name = null) string name = null)
{ {
@@ -158,20 +175,46 @@ namespace Tensorflow
// Build the graph for the true branch in a new context. // Build the graph for the true branch in a new context.
var context_t = new CondContext(pred, pivot_1, branch: 1); var context_t = new CondContext(pred, pivot_1, branch: 1);
context_t.Enter(); context_t.Enter();
var res_t = context_t.BuildCondBranch(true_fn);
var (orig_res_t, res_t) = context_t.BuildCondBranch(true_fn);
context_t.Exit(); context_t.Exit();


// Build the graph for the false branch in a new context. // Build the graph for the false branch in a new context.
var context_f = new CondContext(pred, pivot_2, branch: 0); var context_f = new CondContext(pred, pivot_2, branch: 0);
context_f.Enter(); context_f.Enter();
var res_f = context_f.BuildCondBranch(false_fn);
var (orig_res_f, res_f) = context_f.BuildCondBranch(false_fn);
context_f.Exit(); context_f.Exit();


var res_t_flat = new Tensor[] { res_t.Item1, res_t.Item2, res_t.Item3 };
var res_f_flat = new Tensor[] { res_f.Item1, res_f.Item2, res_f.Item3 };
var res_t_flat = res_t;
var res_f_flat = res_f;

var merges = zip(res_f_flat, res_t_flat)
.Select(pair => merge(new Tensor[] { pair.Item1, pair.Item2 }))
.ToArray();

merges = _convert_flows_to_tensorarrays(orig_res_t, merges);

ops.add_to_collection(ops.GraphKeys.COND_CONTEXT, context_t);
ops.add_to_collection(ops.GraphKeys.COND_CONTEXT, context_f);


return merges;
});
}


return (p_2, p_1);
public static Tensor[] _convert_flows_to_tensorarrays<T>(T[] tensors_or_tensorarrays, Tensor[] tensors_or_flows)
{
// zip(tensors_or_tensorarrays, tensors_or_flows).Select((ta, t_or_flow) => ta).ToArray();
return tensors_or_flows;
}

public static Tensor merge(Tensor[] inputs, string name = null)
{
return with(ops.name_scope(name, "Merge", inputs), scope =>
{
name = scope;
inputs = inputs.Select(inp =>
ops.internal_convert_to_tensor_or_indexed_slices(inp, as_ref: true))
.ToArray();
return gen_control_flow_ops.merge(inputs, name).Item1;
}); });
} }


@@ -200,5 +243,18 @@ namespace Tensorflow
return gen_control_flow_ops.@switch(data, pred, name: name); return gen_control_flow_ops.@switch(data, pred, name: name);
}); });
} }

public static Tensor ZerosLikeOutsideLoop(Operation op, int index)
{
var val = op.outputs[index];
if (!util.IsSwitch(op))
{
if (val.dtype == TF_DataType.TF_RESOURCE)
throw new NotImplementedException("ZerosLikeOutsideLoop");
return array_ops.zeros_like(val, optimize: false);
}

throw new NotImplementedException("ZerosLikeOutsideLoop");
}
} }
} }

+ 18
- 0
src/TensorFlowNET.Core/Operations/control_flow_util.py.cs View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Tensorflow.Operations;


namespace Tensorflow namespace Tensorflow
{ {
@@ -15,5 +16,22 @@ namespace Tensorflow
{ {
return op.type == "Exit" || op.type == "RefExit"; return op.type == "Exit" || op.type == "RefExit";
} }

/// <summary>
/// Return true if `op` is a Switch.
/// </summary>
/// <param name="op"></param>
/// <returns></returns>
public static bool IsSwitch(Operation op)
{
return op.type == "Switch" || op.type == "RefSwitch";
}

public static IControlFlowContext GetOutputContext(Operation op)
{
var ctxt = op._get_control_flow_context();

return ctxt;
}
} }
} }

+ 66
- 11
src/TensorFlowNET.Core/Operations/gen_array_ops.cs View File

@@ -27,16 +27,9 @@ namespace Tensorflow
return _op.outputs[0]; return _op.outputs[0];
} }


public static Tensor greater<Tx, Ty>(Tx x, Ty y, string name = null)
public static Tensor pack(Tensor[] values, int axis = 0, string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("Greater", name: name, args: new { x, y });

return _op.outputs[0];
}

public static Tensor less<Tx, Ty>(Tx x, Ty y, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Less", name: name, args: new { x, y });
var _op = _op_def_lib._apply_op_helper("Pack", name: name, args: new { values, axis });


return _op.outputs[0]; return _op.outputs[0];
} }
@@ -68,6 +61,13 @@ namespace Tensorflow
return _op.outputs[0]; return _op.outputs[0];
} }


public static Tensor invert_permutation(Tensor x, string name = null)
{
var _op = _op_def_lib._apply_op_helper("InvertPermutation", name, new { x });

return _op.outputs[0];
}

public static Tensor log(Tensor x, string name = null) public static Tensor log(Tensor x, string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("Log", name: name, args: new { x }); var _op = _op_def_lib._apply_op_helper("Log", name: name, args: new { x });
@@ -110,7 +110,13 @@ namespace Tensorflow
return (_op.outputs[0], _op.outputs[1]); return (_op.outputs[0], _op.outputs[1]);
} }


public static Tensor reshape(Tensor tensor, Tensor shape, string name = null)
public static Tensor reshape<T1, T2>(T1 tensor, T2 shape, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Reshape", name, new { tensor, shape });
return _op.outputs[0];
}

public static Tensor reshape(Tensor tensor, int[] shape, string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("Reshape", name, new { tensor, shape }); var _op = _op_def_lib._apply_op_helper("Reshape", name, new { tensor, shape });
return _op.outputs[0]; return _op.outputs[0];
@@ -121,6 +127,17 @@ namespace Tensorflow
throw new NotImplementedException("where"); throw new NotImplementedException("where");
} }


public static Tensor one_hot(Tensor indices, int depth,
Tensor on_value = null,
Tensor off_value = null,
TF_DataType dtype = TF_DataType.DtInvalid,
int axis = -1,
string name = null)
{
var _op = _op_def_lib._apply_op_helper("OneHot", name, new { indices, depth, on_value, off_value, axis });
return _op.outputs[0];
}

/// <summary> /// <summary>
/// A placeholder op that passes through `input` when its output is not fed. /// A placeholder op that passes through `input` when its output is not fed.
/// </summary> /// </summary>
@@ -140,6 +157,12 @@ namespace Tensorflow
return _op.outputs[0]; return _op.outputs[0];
} }


public static Tensor scatter_nd(Tensor indices, Tensor updates, Tensor[] shape, string name = null)
{
var _op = _op_def_lib._apply_op_helper("ScatterNd", name, new { indices, updates, shape });
return _op.outputs[0];
}

public static Tensor shape(Tensor input, TF_DataType out_type = TF_DataType.TF_INT32, string name = null) public static Tensor shape(Tensor input, TF_DataType out_type = TF_DataType.TF_INT32, string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("Shape", name, new { input, out_type }); var _op = _op_def_lib._apply_op_helper("Shape", name, new { input, out_type });
@@ -163,7 +186,7 @@ namespace Tensorflow
return _op.outputs[0]; return _op.outputs[0];
} }


public static Tensor transpose(Tensor x, int[] perm, string name = null)
public static Tensor transpose<T1, T2>(T1 x, T2 perm, string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("Transpose", name, new { x, perm }); var _op = _op_def_lib._apply_op_helper("Transpose", name, new { x, perm });
return _op.outputs[0]; return _op.outputs[0];
@@ -174,12 +197,44 @@ namespace Tensorflow
var _op = _op_def_lib._apply_op_helper("ZerosLike", name, new { x }); var _op = _op_def_lib._apply_op_helper("ZerosLike", name, new { x });
return _op.outputs[0]; return _op.outputs[0];
} }

public static Tensor stop_gradient(Tensor x, string name = null) public static Tensor stop_gradient(Tensor x, string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("StopGradient", name, args: new { input = x, name }); var _op = _op_def_lib._apply_op_helper("StopGradient", name, args: new { input = x, name });


return _op.outputs[0]; return _op.outputs[0];
} }

public static Tensor strided_slice(Tensor input, Tensor begin, Tensor end, Tensor strides,
int begin_mask = 0,
int end_mask = 0,
int ellipsis_mask = 0,
int new_axis_mask = 0,
int shrink_axis_mask = 0,
string name = null)
{
var _op = _op_def_lib._apply_op_helper("StridedSlice", name, new
{
input,
begin,
end,
strides,
begin_mask,
end_mask,
ellipsis_mask,
new_axis_mask,
shrink_axis_mask
});

return _op.outputs[0];
}

public static Tensor slice<Tb, Ts>(Tensor input, Tb[] begin, Ts[] size, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Slice", name, new { input, begin, size });
return _op.outputs[0];
}

/// <summary> /// <summary>
/// Removes dimensions of size 1 from the shape of a tensor. /// Removes dimensions of size 1 from the shape of a tensor.
/// Given a tensor `input`, this operation returns a tensor of the same type with /// Given a tensor `input`, this operation returns a tensor of the same type with


+ 8
- 1
src/TensorFlowNET.Core/Operations/gen_control_flow_ops.py.cs View File

@@ -4,7 +4,7 @@ using System.Text;


namespace Tensorflow namespace Tensorflow
{ {
public class gen_control_flow_ops
public class gen_control_flow_ops : Python
{ {
public static OpDefLibrary _op_def_lib = new OpDefLibrary(); public static OpDefLibrary _op_def_lib = new OpDefLibrary();


@@ -21,5 +21,12 @@ namespace Tensorflow


return (_op.outputs[0], _op.outputs[1]); return (_op.outputs[0], _op.outputs[1]);
} }

public static (Tensor, Tensor) merge(Tensor[] inputs, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Merge", name, new { inputs });

return (_op.outputs[0], _op.outputs[1]);
}
} }
} }

+ 176
- 5
src/TensorFlowNET.Core/Operations/gen_math_ops.cs View File

@@ -9,6 +9,30 @@ namespace Tensorflow
public static class gen_math_ops public static class gen_math_ops
{ {
public static OpDefLibrary _op_def_lib = new OpDefLibrary(); public static OpDefLibrary _op_def_lib = new OpDefLibrary();
/// <summary>
/// Returns the index with the largest value across dimensions of a tensor.
/// </summary>
/// <param name="input"></param>
/// <param name="dimension"></param>
/// <param name="output_type"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor arg_max(Tensor input, int dimension, TF_DataType output_type = TF_DataType.TF_INT64, string name = null)
=> _op_def_lib._apply_op_helper("ArgMax", name, args: new { input, dimension, output_type }).outputs[0];
/// <summary>
/// Returns the index with the smallest value across dimensions of a tensor.
/// </summary>
/// <param name="input"></param>
/// <param name="dimension"></param>
/// <param name="output_type"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor arg_min(Tensor input, int dimension, TF_DataType output_type= TF_DataType.TF_INT64, string name= null)
=>_op_def_lib._apply_op_helper("ArgMin", name, args: new { input, dimension, output_type }).outputs[0];
/// <summary> /// <summary>
/// Computes the mean of elements across dimensions of a tensor. /// Computes the mean of elements across dimensions of a tensor.
/// Reduces `input` along the dimensions given in `axis`. Unless /// `keep_dims` is true, the rank of the tensor is reduced by 1 for each entry in /// `axis`. If `keep_dims` is true, the reduced dimensions are retained with length 1. /// Reduces `input` along the dimensions given in `axis`. Unless /// `keep_dims` is true, the rank of the tensor is reduced by 1 for each entry in /// `axis`. If `keep_dims` is true, the reduced dimensions are retained with length 1.
@@ -20,16 +44,30 @@ namespace Tensorflow
/// <param name="keep_dims"> An optional `bool`. Defaults to `False`. If true, retain reduced dimensions with length 1.</param> /// <param name="keep_dims"> An optional `bool`. Defaults to `False`. If true, retain reduced dimensions with length 1.</param>
/// <param name="name"> A name for the operation (optional).</param> /// <param name="name"> A name for the operation (optional).</param>
/// <returns> A `Tensor`. Has the same type as `input`.</returns> /// <returns> A `Tensor`. Has the same type as `input`.</returns>
public static Tensor mean(Tensor input, Tensor axis, bool keep_dims= false, string name = null)
public static Tensor mean<T1, T2>(T1 input, T2 axis, bool keep_dims= false, string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("Mean", name, args: new { input, reduction_indices = axis, keep_dims = keep_dims }); var _op = _op_def_lib._apply_op_helper("Mean", name, args: new { input, reduction_indices = axis, keep_dims = keep_dims });
return _op.outputs[0]; return _op.outputs[0];
} }
public static Tensor mean(Tensor input, int[] axis, bool keep_dims = false, string name = null)
public static Tensor prod<T1, T2>(T1 input, T2 axis, bool keep_dims = false, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Prod", name, args: new { input, reduction_indices = axis, keep_dims });
return _op.outputs[0];
}
public static Tensor acos(Tensor x, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Acos", name, args: new { x });
return _op.outputs[0];
}
public static Tensor asin(Tensor x, string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("Mean", name, args: new { input, reduction_indices = axis, keep_dims = keep_dims, name });
var _op = _op_def_lib._apply_op_helper("Asin", name, args: new { x });
return _op.outputs[0]; return _op.outputs[0];
} }
@@ -41,6 +79,83 @@ namespace Tensorflow
return _op.outputs[0]; return _op.outputs[0];
} }
public static Tensor atan(Tensor x, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Atan", name, args: new { x });
return _op.outputs[0];
}
public static Tensor ceil(Tensor x, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Ceil", name, args: new { x });
return _op.outputs[0];
}
public static Tensor cos(Tensor x, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Cos", name, args: new { x });
return _op.outputs[0];
}
public static Tensor cosh(Tensor x, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Cosh", name, args: new { x });
return _op.outputs[0];
}
public static Tensor floor(Tensor x, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Floor", name, args: new { x });
return _op.outputs[0];
}
public static Tensor _clip_by_value(Tensor t, Tensor clip_value_min, Tensor clip_value_max, string name = null)
{
var _op = _op_def_lib._apply_op_helper("ClipByValue", name, args: new { t, clip_value_min, clip_value_max });
return _op.outputs[0];
}
public static Tensor greater<Tx, Ty>(Tx x, Ty y, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Greater", name: name, args: new { x, y });
return _op.outputs[0];
}
public static Tensor greater_equal<Tx, Ty>(Tx x, Ty y, string name = null)
{
var _op = _op_def_lib._apply_op_helper("GreaterEqual", name: name, args: new { x, y });
return _op.outputs[0];
}
public static Tensor less<Tx, Ty>(Tx x, Ty y, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Less", name: name, args: new { x, y });
return _op.outputs[0];
}
public static Tensor less_equal<Tx, Ty>(Tx x, Ty y, string name = null)
{
var _op = _op_def_lib._apply_op_helper("LessEqual", name: name, args: new { x, y });
return _op.outputs[0];
}
public static Tensor log1p(Tensor x, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Log1p", name, args: new { x });
return _op.outputs[0];
}
public static Tensor squared_difference(Tensor x, Tensor y, string name = null) public static Tensor squared_difference(Tensor x, Tensor y, string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("SquaredDifference", name, args: new { x, y, name }); var _op = _op_def_lib._apply_op_helper("SquaredDifference", name, args: new { x, y, name });
@@ -128,6 +243,27 @@ namespace Tensorflow
return _op.outputs[0]; return _op.outputs[0];
} }
/// <summary>
/// Returns the truth value of (x == y) element-wise.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor equal(Tensor x, Tensor y, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Equal", name, args: new { x, y });
return _op.outputs[0];
}
public static Tensor atan2(Tensor y, Tensor x, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Atan2", name, args: new { y, x });
return _op.outputs[0];
}
public static Tensor mul(Tensor x, Tensor y, string name = null) public static Tensor mul(Tensor x, Tensor y, string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("Mul", name, args: new { x, y }); var _op = _op_def_lib._apply_op_helper("Mul", name, args: new { x, y });
@@ -142,6 +278,13 @@ namespace Tensorflow
return _op.outputs[0]; return _op.outputs[0];
} }
public static Tensor reciprocal(Tensor x, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Reciprocal", name, args: new { x });
return _op.outputs[0];
}
public static Tensor floor_mod(Tensor x, Tensor y, string name = null) public static Tensor floor_mod(Tensor x, Tensor y, string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("FloorMod", name, args: new { x, y }); var _op = _op_def_lib._apply_op_helper("FloorMod", name, args: new { x, y });
@@ -186,13 +329,34 @@ namespace Tensorflow
return _op.outputs[0]; return _op.outputs[0];
} }
public static Tensor _max(Tensor input, int[] axis, bool keep_dims=false, string name = null)
public static Tensor minimum<T1, T2>(T1 x, T2 y, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Minimum", name, args: new { x, y });
return _op.outputs[0];
}
public static Tensor _abs(Tensor x, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Abs", name, new { x });
return _op.outputs[0];
}
public static Tensor _max<Tx, Ty>(Tx input, Ty axis, bool keep_dims=false, string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("Max", name, new { input, reduction_indices = axis, keep_dims }); var _op = _op_def_lib._apply_op_helper("Max", name, new { input, reduction_indices = axis, keep_dims });
return _op.outputs[0]; return _op.outputs[0];
} }
public static Tensor _min<Tx, Ty>(Tx input, Ty axis, bool keep_dims = false, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Min", name, new { input, reduction_indices = axis, keep_dims });
return _op.outputs[0];
}
public static Tensor pow<Tx, Ty>(Tx x, Ty y, string name = null) public static Tensor pow<Tx, Ty>(Tx x, Ty y, string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("Pow", name, args: new { x, y }); var _op = _op_def_lib._apply_op_helper("Pow", name, args: new { x, y });
@@ -200,7 +364,14 @@ namespace Tensorflow
return _op.outputs[0]; return _op.outputs[0];
} }
public static Tensor sum(Tensor input, Tensor axis = null, bool keep_dims = false, string name = null)
public static Tensor _sum(Tensor input, Tensor axis = null, bool keep_dims = false, string name = null)
{
var _op = _op_def_lib._apply_op_helper("Sum", name, args: new { input, reduction_indices = axis, keep_dims });
return _op.outputs[0];
}
public static Tensor _sum(Tensor input, int axis, bool keep_dims = false, string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("Sum", name, args: new { input, reduction_indices = axis, keep_dims }); var _op = _op_def_lib._apply_op_helper("Sum", name, args: new { input, reduction_indices = axis, keep_dims });


src/TensorFlowNET.Core/Operations/math_ops.py.cs → src/TensorFlowNET.Core/Operations/math_ops.cs View File

@@ -2,12 +2,40 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Tensorflow.Framework;


namespace Tensorflow namespace Tensorflow
{ {
/// <summary>
/// python\ops\math_ops.py
/// </summary>
public class math_ops : Python public class math_ops : Python
{ {
public static Tensor add(Tensor x, Tensor y, string name = null) => gen_math_ops.add(x, y, name);
public static Tensor abs(Tensor x, string name = null)
{
return with(ops.name_scope(name, "Abs", new { x }), scope =>
{
x = ops.convert_to_tensor(x, name: "x");
if (x.dtype.is_complex())
throw new NotImplementedException("math_ops.abs for dtype.is_complex");
//return gen_math_ops.complex_abs(x, Tout: x.dtype.real_dtype, name: name);
return gen_math_ops._abs(x, name: name);
});
}

public static Tensor add(Tensor x, Tensor y, string name = null)
=> gen_math_ops.add(x, y, name);

public static Tensor add(Tensor x, string name = null)
{
return with(ops.name_scope(name, "Abs", new { x }), scope =>
{
name = scope;
x = ops.convert_to_tensor(x, name: "x");

return gen_math_ops._abs(x, name: name);
});
}


public static Tensor cast(Tensor x, TF_DataType dtype = TF_DataType.DtInvalid, string name = null) public static Tensor cast(Tensor x, TF_DataType dtype = TF_DataType.DtInvalid, string name = null)
{ {
@@ -17,6 +45,7 @@ namespace Tensorflow


return with(ops.name_scope(name, "Cast", new { x }), scope => return with(ops.name_scope(name, "Cast", new { x }), scope =>
{ {
name = scope;
x = ops.convert_to_tensor(x, name: "x"); x = ops.convert_to_tensor(x, name: "x");
if (x.dtype.as_base_dtype() != base_type) if (x.dtype.as_base_dtype() != base_type)
x = gen_math_ops.cast(x, base_type, name: name); x = gen_math_ops.cast(x, base_type, name: name);
@@ -36,12 +65,44 @@ namespace Tensorflow
/// dimensions.Must be in the range `[-rank(input_tensor), rank(input_tensor))`.</param> /// dimensions.Must be in the range `[-rank(input_tensor), rank(input_tensor))`.</param>
/// <param name="keepdims"> If true, retains reduced dimensions with length 1.</param> /// <param name="keepdims"> If true, retains reduced dimensions with length 1.</param>
/// <param name="name"> A name for the operation (optional).</param> /// <param name="name"> A name for the operation (optional).</param>
public static Tensor reduce_mean(Tensor input_tensor, int[] axis = null, bool keepdims = false, string name = null)
public static Tensor reduce_mean(Tensor input_tensor, int[] axis = null, bool keepdims = false, string name = null, int? reduction_indices = null)
{ {
var r = _ReductionDims(input_tensor, axis); var r = _ReductionDims(input_tensor, axis);
var m = gen_math_ops.mean(input_tensor, (int[]) r, keepdims, name);
return _may_reduce_to_scalar(keepdims,axis, m);
if (axis == null)
{
var m = gen_math_ops.mean(input_tensor, r, keepdims, name);
return _may_reduce_to_scalar(keepdims, axis, m);
}
else
{
var m = gen_math_ops.mean(input_tensor, axis, keepdims, name);
return _may_reduce_to_scalar(keepdims, axis, m);
}
}

/// <summary>
/// Computes the product of elements across dimensions of a tensor.
/// </summary>
/// <param name="input_tensor"></param>
/// <param name="axis"></param>
/// <param name="keepdims"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor reduce_prod(Tensor input_tensor, int[] axis = null, bool keepdims = false, string name = null)
{
var r = _ReductionDims(input_tensor, axis);
if (axis == null)
{
var m = gen_math_ops.prod(input_tensor, r, keepdims, name);
return _may_reduce_to_scalar(keepdims, axis, m);
}
else
{
var m = gen_math_ops.prod(input_tensor, axis, keepdims, name);
return _may_reduce_to_scalar(keepdims, axis, m);
}
} }
/// <summary> /// <summary>
/// Returns (x - y)(x - y) element-wise. /// Returns (x - y)(x - y) element-wise.
/// </summary> /// </summary>
@@ -60,6 +121,11 @@ namespace Tensorflow
return gen_math_ops.square(x, name); return gen_math_ops.square(x, name);
} }


public static Tensor subtract<Tx, Ty>(Tx x, Ty y, string name = null)
{
return gen_math_ops.sub(x, y, name);
}

public static Tensor log(Tensor x, string name = null) public static Tensor log(Tensor x, string name = null)
{ {
return gen_math_ops.log(x, name); return gen_math_ops.log(x, name);
@@ -87,6 +153,16 @@ namespace Tensorflow
return gen_data_flow_ops.dynamic_stitch(a1, a2); return gen_data_flow_ops.dynamic_stitch(a1, a2);
} }


/// <summary>
/// Computes the reciprocal of x element-wise.
/// </summary>
/// <param name="x"></param>
/// <param name="name"></param>
/// <returns></returns>
public static Tensor reciprocal(Tensor x, string name = null)
=> gen_math_ops.reciprocal(x, name: name);

/// <summary> /// <summary>
/// Computes log(sum(exp(elements across dimensions of a tensor))). /// Computes log(sum(exp(elements across dimensions of a tensor))).
/// Reduces `input_tensor` along the dimensions given in `axis`. /// Reduces `input_tensor` along the dimensions given in `axis`.
@@ -129,7 +205,10 @@ namespace Tensorflow


public static Tensor reduce_max(Tensor input_tensor, int[] axis = null, bool keepdims = false, string name = null) public static Tensor reduce_max(Tensor input_tensor, int[] axis = null, bool keepdims = false, string name = null)
{ {
return _may_reduce_to_scalar(keepdims, axis, gen_math_ops._max(input_tensor, (int[])_ReductionDims(input_tensor, axis), keepdims, name));
var r = _ReductionDims(input_tensor, axis);
var max = (axis != null) ? gen_math_ops._max(input_tensor, axis, keepdims, name) :
gen_math_ops._max(input_tensor, r, keepdims, name);
return _may_reduce_to_scalar(keepdims, axis, max);
} }


/// <summary> /// <summary>
@@ -160,20 +239,29 @@ namespace Tensorflow
throw new NotImplementedException(); throw new NotImplementedException();
} }


public static Tensor reduce_sum(Tensor input_tensor, Tensor axis = null, bool keepdims = false)
public static Tensor reduce_sum(Tensor input_tensor, Tensor axis = null, bool keepdims = false, string name = null)
{ {
var r = _ReductionDims(input_tensor, axis); var r = _ReductionDims(input_tensor, axis);
var m = gen_math_ops.sum(input_tensor, r);
return _may_reduce_to_scalar(keepdims, m);
var m = gen_math_ops._sum(input_tensor, r, keep_dims: keepdims, name: name);
return _may_reduce_to_scalar(keepdims, axis, m);
} }


private static Tensor _may_reduce_to_scalar(bool keepdims, Tensor output)
public static Tensor reduce_sum(Tensor input_tensor, int axis, bool keepdims = false, string name = null)
{ {
output.shape = new long[0];
var m = gen_math_ops._sum(input_tensor, axis, keep_dims: keepdims, name: name);
return _may_reduce_to_scalar(keepdims, new int[] { axis }, m);
}

private static Tensor _may_reduce_to_scalar(bool keepdims, Tensor axis, Tensor output)
{
if (!common_shapes.has_fully_defined_shape(output) &&
!keepdims &&
axis == null)
output.shape = new long[0];
return output; return output;
} }


private static Tensor _may_reduce_to_scalar(bool keepdims, int[] axos, Tensor output)
private static Tensor _may_reduce_to_scalar(bool keepdims, int[] axis, Tensor output)
{ {
output.shape = new long[0]; output.shape = new long[0];
return output; return output;
@@ -191,19 +279,20 @@ namespace Tensorflow
return range(0, rank, 1); return range(0, rank, 1);
} }
} }
private static object _ReductionDims(Tensor x, int[] axis)
private static Tensor _ReductionDims(Tensor x, int[] axis)
{ {
if (axis != null) if (axis != null)
{ {
return axis;
// should return axis. or check before.
return null;
} }
else else
{ {
var rank = array_ops.rank(x);
var rank = common_shapes.rank(x);
if (rank != null) if (rank != null)
{ {
return constant_op.constant(np.arange(rank), TF_DataType.TF_INT32);
return constant_op.constant(np.arange(rank.Value), TF_DataType.TF_INT32);
} }
return range(0, rank, 1); return range(0, rank, 1);
} }
@@ -221,7 +310,7 @@ namespace Tensorflow
if (delta == null) if (delta == null)
delta = 1; delta = 1;


return with(ops.name_scope(name, "Range", new object[] { start, limit, delta }), scope =>
return with(ops.name_scope(name, "Range", new { start, limit, delta }), scope =>
{ {
name = scope; name = scope;
var start1 = ops.convert_to_tensor(start, name: "start"); var start1 = ops.convert_to_tensor(start, name: "start");
@@ -298,5 +387,20 @@ namespace Tensorflow
return x; return x;
}); });
} }

public static Tensor truediv(Tensor x, Tensor y, string name = null)
=> _truediv_python3(x, y, name);

public static Tensor _truediv_python3(Tensor x, Tensor y, string name = null)
{
return with(ops.name_scope(name, "truediv", new { x, y }), scope =>
{
name = scope;
var x_dtype = x.dtype.as_base_dtype();
var y_dtype = y.dtype.as_base_dtype();

return gen_math_ops.real_div(x, y, name: name);
});
}
} }
} }

+ 1
- 1
src/TensorFlowNET.Core/Operations/nn_impl.py.cs View File

@@ -46,7 +46,7 @@ namespace Tensorflow
}); });
} }


public static (Tensor, Tensor, Tensor) fused_batch_norm(Tensor x,
public static Tensor[] fused_batch_norm(Tensor x,
RefVariable scale, RefVariable scale,
RefVariable offset, RefVariable offset,
Tensor mean, Tensor mean,


+ 50
- 0
src/TensorFlowNET.Core/Operations/nn_ops.cs View File

@@ -41,5 +41,55 @@ namespace Tensorflow
return gen_nn_ops.bias_add(value, bias_tensor, data_format: data_format, name: name); return gen_nn_ops.bias_add(value, bias_tensor, data_format: data_format, name: name);
}); });
} }

public static Tensor log_softmax(Tensor logits, int axis = -1, string name = null)
{
return _softmax(logits, gen_nn_ops.log_softmax, axis, name);
}

public static Tensor _softmax(Tensor logits, Func<Tensor, string, Tensor> compute_op, int dim = -1, string name = null)
{
logits = ops.convert_to_tensor(logits);

var shape = logits.shape;
bool is_last_dim = dim == -1 || dim == shape.Length - 1;
if (is_last_dim)
return compute_op(logits, name);

throw new NotImplementedException("_softmax helper");
}

public static Tensor softmax_cross_entropy_with_logits_v2_helper(Tensor labels,
Tensor logits,
int axis = -1,
string name = null)
{
return Python.with(ops.name_scope(name, "softmax_cross_entropy_with_logits", new { }), scope =>
{
var precise_logits = logits;
var input_rank = array_ops.rank(precise_logits);
var shape = logits.getShape();

if (axis != -1)
throw new NotImplementedException("softmax_cross_entropy_with_logits_v2_helper axis != -1");

var input_shape = array_ops.shape(precise_logits);

// Do the actual op computation.
// The second output tensor contains the gradients. We use it in
// _CrossEntropyGrad() in nn_grad but not here.

var (cost, unused_backprop) = gen_nn_ops.softmax_cross_entropy_with_logits(precise_logits, labels, name: name);

// The output cost shape should be the input minus axis.
var output_shape = array_ops.slice(input_shape,
new int[] { 0 },
new Tensor[] { math_ops.subtract(input_rank, 1) });

cost = array_ops.reshape(cost, output_shape);

return cost;
});
}
} }
} }

+ 6
- 8
src/TensorFlowNET.Core/Python.cs View File

@@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using System.Text; using System.Text;


namespace Tensorflow namespace Tensorflow
@@ -16,6 +17,11 @@ namespace Tensorflow
Console.WriteLine(obj.ToString()); Console.WriteLine(obj.ToString());
} }


protected IEnumerable<int> range(int end)
{
return Enumerable.Range(0, end);
}

public static T New<T>(object args) where T : IPyClass public static T New<T>(object args) where T : IPyClass
{ {
var instance = Activator.CreateInstance<T>(); var instance = Activator.CreateInstance<T>();
@@ -118,14 +124,6 @@ namespace Tensorflow
{ {
object obj = propertyDescriptor.GetValue(dyn); object obj = propertyDescriptor.GetValue(dyn);
string name = propertyDescriptor.Name; string name = propertyDescriptor.Name;
// avoid .net keyword
switch (name)
{
case "_ref_":
name = "ref";
break;
}

dictionary.Add(name, obj); dictionary.Add(name, obj);
} }
return dictionary; return dictionary;


+ 9
- 2
src/TensorFlowNET.Core/Sessions/BaseSession.cs View File

@@ -186,9 +186,10 @@ namespace Tensorflow
var result = new NDArray[fetch_list.Length]; var result = new NDArray[fetch_list.Length];


for (int i = 0; i < fetch_list.Length; i++) for (int i = 0; i < fetch_list.Length; i++)
{
result[i] = fetchValue(output_values[i]); result[i] = fetchValue(output_values[i]);
}

for (int i = 0; i < feed_dict.Length; i++)
feed_dict[i].Value.Dispose();


return result; return result;
} }
@@ -222,6 +223,12 @@ namespace Tensorflow
ints[i] = *(int*)(offset + (int)(tensor.itemsize * i)); ints[i] = *(int*)(offset + (int)(tensor.itemsize * i));
nd = np.array(ints).reshape(ndims); nd = np.array(ints).reshape(ndims);
break; break;
case TF_DataType.TF_INT64:
var longs = new long[tensor.size];
for (ulong i = 0; i < tensor.size; i++)
longs[i] = *(long*)(offset + (int)(tensor.itemsize * i));
nd = np.array(longs).reshape(ndims);
break;
case TF_DataType.TF_FLOAT: case TF_DataType.TF_FLOAT:
var floats = new float[tensor.size]; var floats = new float[tensor.size];
for (ulong i = 0; i < tensor.size; i++) for (ulong i = 0; i < tensor.size; i++)


+ 19
- 7
src/TensorFlowNET.Core/Sessions/_ElementFetchMapper.cs View File

@@ -10,7 +10,6 @@ namespace Tensorflow
/// </summary> /// </summary>
public class _ElementFetchMapper : _FetchMapper public class _ElementFetchMapper : _FetchMapper
{ {
private List<object> _unique_fetches = new List<object>();
private Func<List<object>, object> _contraction_fn; private Func<List<object>, object> _contraction_fn;


public _ElementFetchMapper(object[] fetches, Func<List<object>, object> contraction_fn) public _ElementFetchMapper(object[] fetches, Func<List<object>, object> contraction_fn)
@@ -32,7 +31,7 @@ namespace Tensorflow
/// </summary> /// </summary>
/// <param name="values"></param> /// <param name="values"></param>
/// <returns></returns> /// <returns></returns>
public NDArray build_results(List<object> values)
public override NDArray build_results(List<object> values)
{ {
NDArray result = null; NDArray result = null;


@@ -44,6 +43,24 @@ namespace Tensorflow
case NDArray value: case NDArray value:
result = value; result = value;
break; break;
case short value:
result = value;
break;
case int value:
result = value;
break;
case long value:
result = value;
break;
case float value:
result = value;
break;
case double value:
result = value;
break;
case string value:
result = value;
break;
default: default:
break; break;
} }
@@ -51,10 +68,5 @@ namespace Tensorflow


return result; return result;
} }

public List<object> unique_fetches()
{
return _unique_fetches;
}
} }
} }

+ 27
- 3
src/TensorFlowNET.Core/Sessions/_FetchHandler.cs View File

@@ -10,7 +10,7 @@ namespace Tensorflow
/// </summary> /// </summary>
public class _FetchHandler public class _FetchHandler
{ {
private _ElementFetchMapper _fetch_mapper;
private _FetchMapper _fetch_mapper;
private List<Tensor> _fetches = new List<Tensor>(); private List<Tensor> _fetches = new List<Tensor>();
private List<bool> _ops = new List<bool>(); private List<bool> _ops = new List<bool>();
private List<Tensor> _final_fetches = new List<Tensor>(); private List<Tensor> _final_fetches = new List<Tensor>();
@@ -18,7 +18,7 @@ namespace Tensorflow


public _FetchHandler(Graph graph, object fetches, Dictionary<object, object> feeds = null, Action feed_handles = null) public _FetchHandler(Graph graph, object fetches, Dictionary<object, object> feeds = null, Action feed_handles = null)
{ {
_fetch_mapper = new _FetchMapper().for_fetch(fetches);
_fetch_mapper = _FetchMapper.for_fetch(fetches);
foreach(var fetch in _fetch_mapper.unique_fetches()) foreach(var fetch in _fetch_mapper.unique_fetches())
{ {
switch (fetch) switch (fetch)
@@ -58,7 +58,31 @@ namespace Tensorflow
{ {
var value = tensor_values[j]; var value = tensor_values[j];
j += 1; j += 1;
full_values.Add(value);
if (value.ndim == 0)
{
switch (value.dtype.Name)
{
case "Int32":
full_values.Add(value.Data<int>(0));
break;
case "Int64":
full_values.Add(value.Data<long>(0));
break;
case "Single":
full_values.Add(value.Data<float>(0));
break;
case "Double":
full_values.Add(value.Data<double>(0));
break;
case "String":
full_values.Add(value.Data<string>(0));
break;
}
}
else
{
full_values.Add(value[np.arange(0, value.shape[0])]);
}
} }
i += 1; i += 1;
} }


+ 20
- 7
src/TensorFlowNET.Core/Sessions/_FetchMapper.cs View File

@@ -1,4 +1,5 @@
using System;
using NumSharp.Core;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;


@@ -6,14 +7,26 @@ namespace Tensorflow
{ {
public class _FetchMapper public class _FetchMapper
{ {
public _ElementFetchMapper for_fetch(object fetch)
protected List<object> _unique_fetches = new List<object>();

public static _FetchMapper for_fetch(object fetch)
{ {
var fetches = new object[] { fetch };
var fetches = fetch.GetType().IsArray ? (object[])fetch : new object[] { fetch };

if (fetch.GetType().IsArray)
return new _ListFetchMapper(fetches);
else
return new _ElementFetchMapper(fetches, (List<object> fetched_vals) => fetched_vals[0]);
}


return new _ElementFetchMapper(fetches, (List<object> fetched_vals) =>
{
return fetched_vals[0];
});
public virtual NDArray build_results(List<object> values)
{
return values.ToArray();
}

public virtual List<object> unique_fetches()
{
return _unique_fetches;
} }
} }
} }

+ 18
- 0
src/TensorFlowNET.Core/Sessions/_ListFetchMapper.cs View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Tensorflow
{
public class _ListFetchMapper : _FetchMapper
{
private _FetchMapper[] _mappers;

public _ListFetchMapper(object[] fetches)
{
_mappers = fetches.Select(fetch => _FetchMapper.for_fetch(fetch)).ToArray();
_unique_fetches.AddRange(fetches);
}
}
}

+ 15
- 10
src/TensorFlowNET.Core/TensorFlowNET.Core.csproj View File

@@ -4,23 +4,26 @@
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>TensorFlow.NET</AssemblyName> <AssemblyName>TensorFlow.NET</AssemblyName>
<RootNamespace>Tensorflow</RootNamespace> <RootNamespace>Tensorflow</RootNamespace>
<Version>0.4.2</Version>
<Version>0.6.0</Version>
<Authors>Haiping Chen</Authors> <Authors>Haiping Chen</Authors>
<Company>SciSharp STACK</Company> <Company>SciSharp STACK</Company>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Copyright>Apache 2.0</Copyright> <Copyright>Apache 2.0</Copyright>
<RepositoryUrl>https://github.com/SciSharp/TensorFlow.NET</RepositoryUrl> <RepositoryUrl>https://github.com/SciSharp/TensorFlow.NET</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageProjectUrl>https://github.com/SciSharp</PackageProjectUrl> <PackageProjectUrl>https://github.com/SciSharp</PackageProjectUrl>
<PackageIconUrl>https://avatars3.githubusercontent.com/u/44989469?s=200&amp;v=4</PackageIconUrl> <PackageIconUrl>https://avatars3.githubusercontent.com/u/44989469?s=200&amp;v=4</PackageIconUrl>
<PackageTags>TensorFlow, NumSharp, SciSharp, MachineLearning, TensorFlow.NET</PackageTags>
<PackageTags>TensorFlow, NumSharp, SciSharp, MachineLearning, TensorFlow.NET, C#</PackageTags>
<Description>Google's TensorFlow binding in .NET Standard. <Description>Google's TensorFlow binding in .NET Standard.
Docs: https://tensorflownet.readthedocs.io</Description> Docs: https://tensorflownet.readthedocs.io</Description>
<AssemblyVersion>0.4.2.0</AssemblyVersion>
<PackageReleaseNotes>Added ConfigProto to control CPU and GPU resource.
Fixed import name scope issue.</PackageReleaseNotes>
<AssemblyVersion>0.6.0.0</AssemblyVersion>
<PackageReleaseNotes>Changes since v0.5:
Added K-means Clustering.
Added Nearest Neighbor.
Added a lot of APIs to build neural networks model.
Bug fix.</PackageReleaseNotes>
<LangVersion>7.2</LangVersion> <LangVersion>7.2</LangVersion>
<FileVersion>0.4.2.0</FileVersion>
<FileVersion>0.6.0.0</FileVersion>
</PropertyGroup> </PropertyGroup>


<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -44,15 +47,17 @@ Fixed import name scope issue.</PackageReleaseNotes>


<ItemGroup> <ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.7.0" /> <PackageReference Include="Google.Protobuf" Version="3.7.0" />
<PackageReference Include="NumSharp" Version="0.7.4" />
<PackageReference Include="NumSharp" Version="0.8.2" />
</ItemGroup> </ItemGroup>


<ItemGroup> <ItemGroup>
<Content CopyToOutputDirectory="PreserveNewest" Include="./runtimes/win-x64/native/tensorflow.dll" Link="tensorflow.dll" Pack="true" PackagePath="runtimes/win-x64/native/tensorflow.dll" />
<Content CopyToOutputDirectory="PreserveNewest" Include="../../tensorflowlib/runtimes/win-x64/native/tensorflow.dll" Link="tensorflow.dll" Pack="true" PackagePath="../../tensorflowlib/runtimes/win-x64/native/tensorflow.dll" />
<Content CopyToOutputDirectory="PreserveNewest" Include="../../tensorflowlib/runtimes/linux-x64/native/libtensorflow.so" Link="libtensorflow.so" Pack="true" PackagePath="../../tensorflowlib/runtimes/linux-x64/native/libtensorflow.so" />
<Content CopyToOutputDirectory="PreserveNewest" Include="../../tensorflowlib/runtimes/linux-x64/native/libtensorflow_framework.so" Link="libtensorflow_framework.so" Pack="true" PackagePath="../../tensorflowlib/runtimes/linux-x64/native/libtensorflow_framework.so" />
</ItemGroup> </ItemGroup>


<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\NumSharp\src\NumSharp.Core\NumSharp.Core.csproj" />
<Folder Include="Keras\Initializers\" />
</ItemGroup> </ItemGroup>


</Project> </Project>

+ 1
- 1
src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs View File

@@ -111,7 +111,7 @@ namespace Tensorflow
// Free the original buffer and set flag // Free the original buffer and set flag
Deallocator deallocator = (IntPtr values, IntPtr len, ref bool closure) => Deallocator deallocator = (IntPtr values, IntPtr len, ref bool closure) =>
{ {
Marshal.FreeHGlobal(dotHandle);
Marshal.FreeHGlobal(values);
closure = true; closure = true;
}; };




+ 8
- 7
src/TensorFlowNET.Core/Tensors/Tensor.Operators.cs View File

@@ -14,6 +14,7 @@ namespace Tensorflow
public static Tensor operator -(Tensor x, Tensor y) => BinaryOpWrapper("sub", x, y); public static Tensor operator -(Tensor x, Tensor y) => BinaryOpWrapper("sub", x, y);
public static Tensor operator -(Tensor x, int y) => BinaryOpWrapper("sub", x, y); public static Tensor operator -(Tensor x, int y) => BinaryOpWrapper("sub", x, y);
public static Tensor operator -(Tensor x, double y) => BinaryOpWrapper("sub", x, y); public static Tensor operator -(Tensor x, double y) => BinaryOpWrapper("sub", x, y);
public static Tensor operator -(float x, Tensor y) => BinaryOpWrapper("Sub", x, y);


public static Tensor operator *(float x, Tensor y) => BinaryOpWrapper("mul", x, y); public static Tensor operator *(float x, Tensor y) => BinaryOpWrapper("mul", x, y);
public static Tensor operator *(double x, Tensor y) => BinaryOpWrapper("mul", x, y); public static Tensor operator *(double x, Tensor y) => BinaryOpWrapper("mul", x, y);
@@ -26,12 +27,12 @@ namespace Tensorflow


public static Tensor operator %(Tensor x, Tensor y) => BinaryOpWrapper("mod", x, y); public static Tensor operator %(Tensor x, Tensor y) => BinaryOpWrapper("mod", x, y);


public static Tensor operator >(Tensor x, int y) => gen_array_ops.greater(x, y);
public static Tensor operator >(Tensor x, float y) => gen_array_ops.greater(x, y);
public static Tensor operator >(Tensor x, double y) => gen_array_ops.greater(x, y);
public static Tensor operator <(Tensor x, int y) => gen_array_ops.less(x, y);
public static Tensor operator <(Tensor x, float y) => gen_array_ops.less(x, y);
public static Tensor operator <(Tensor x, double y) => gen_array_ops.less(x, y);
public static Tensor operator >(Tensor x, int y) => gen_math_ops.greater(x, y);
public static Tensor operator >(Tensor x, float y) => gen_math_ops.greater(x, y);
public static Tensor operator >(Tensor x, double y) => gen_math_ops.greater(x, y);
public static Tensor operator <(Tensor x, int y) => gen_math_ops.less(x, y);
public static Tensor operator <(Tensor x, float y) => gen_math_ops.less(x, y);
public static Tensor operator <(Tensor x, double y) => gen_math_ops.less(x, y);


private static Tensor BinaryOpWrapper<Tx, Ty>(string name, Tx x, Ty y) private static Tensor BinaryOpWrapper<Tx, Ty>(string name, Tx x, Ty y)
{ {
@@ -48,7 +49,7 @@ namespace Tensorflow
var x1 = ops.convert_to_tensor(x, dtype: dtype, name: "x"); var x1 = ops.convert_to_tensor(x, dtype: dtype, name: "x");
var y1 = ops.convert_to_tensor(y, dtype: dtype, name: "y"); var y1 = ops.convert_to_tensor(y, dtype: dtype, name: "y");


switch (name)
switch (name.ToLower())
{ {
case "add": case "add":
result = gen_math_ops.add(x1, y1, name: scope); result = gen_math_ops.add(x1, y1, name: scope);


+ 71
- 4
src/TensorFlowNET.Core/Tensors/Tensor.cs View File

@@ -1,4 +1,5 @@
using NumSharp.Core;
//using Newtonsoft.Json;
using NumSharp.Core;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -43,6 +44,8 @@ namespace Tensorflow
public IntPtr buffer => _handle == IntPtr.Zero ? IntPtr.Zero : c_api.TF_TensorData(_handle); public IntPtr buffer => _handle == IntPtr.Zero ? IntPtr.Zero : c_api.TF_TensorData(_handle);
public int num_consumers(TF_Output oper_out) => _handle == IntPtr.Zero ? 0 : c_api.TF_OperationOutputNumConsumers(oper_out); public int num_consumers(TF_Output oper_out) => _handle == IntPtr.Zero ? 0 : c_api.TF_OperationOutputNumConsumers(oper_out);


private TF_Output? _tf_output;

public long[] shape public long[] shape
{ {
get get
@@ -74,7 +77,8 @@ namespace Tensorflow


public int[] _shape_tuple() public int[] _shape_tuple()
{ {
return null;
if (shape == null) return null;
return shape.Select(x => (int)x).ToArray();
} }


public TensorShape getShape() public TensorShape getShape()
@@ -122,7 +126,10 @@ namespace Tensorflow


public TF_Output _as_tf_output() public TF_Output _as_tf_output()
{ {
return new TF_Output(op, value_index);
if(!_tf_output.HasValue)
_tf_output = new TF_Output(op, value_index);

return _tf_output.Value;
} }


public T[] Data<T>() public T[] Data<T>()
@@ -161,7 +168,12 @@ namespace Tensorflow
/// <param name="feed_dict">A dictionary that maps `Tensor` objects to feed values.</param> /// <param name="feed_dict">A dictionary that maps `Tensor` objects to feed values.</param>
/// <param name="session">The `Session` to be used to evaluate this tensor.</param> /// <param name="session">The `Session` to be used to evaluate this tensor.</param>
/// <returns></returns> /// <returns></returns>
public NDArray eval(FeedItem[] feed_dict = null, Session session = null)
public NDArray eval(params FeedItem[] feed_dict)
{
return ops._eval_using_default_session(this, feed_dict, graph);
}

public NDArray eval(Session session, FeedItem[] feed_dict = null)
{ {
return ops._eval_using_default_session(this, feed_dict, graph, session); return ops._eval_using_default_session(this, feed_dict, graph, session);
} }
@@ -186,6 +198,61 @@ namespace Tensorflow
} }
} }


public Tensor this[int slice_spec]
{
get
{
var slice_spec_s = new int[] { slice_spec };
var begin = new List<int>();
var end = new List<int>();
var strides = new List<int>();

var index = 0;
var (new_axis_mask, shrink_axis_mask) = (0, 0);
var (begin_mask, end_mask) = (0, 0);
var ellipsis_mask = 0;

foreach(var s in slice_spec_s)
{
{
begin.Add(s);
end.Add(s + 1);
strides.Add(1);
shrink_axis_mask |= (1 << index);
}
index += 1;
}

return with(ops.name_scope(null, "strided_slice", new { begin, end, strides }), scope =>
{
string name = scope;
if(begin != null)
{
var (packed_begin, packed_end, packed_strides) =
(array_ops.stack(begin.ToArray()),
array_ops.stack(end.ToArray()),
array_ops.stack(strides.ToArray()));

return gen_array_ops.strided_slice(
this,
packed_begin,
packed_end,
packed_strides,
begin_mask: begin_mask,
end_mask: end_mask,
shrink_axis_mask: shrink_axis_mask,
new_axis_mask: new_axis_mask,
ellipsis_mask: ellipsis_mask,
name: name);
}

throw new NotImplementedException("");
});
}
}

public override string ToString() public override string ToString()
{ {
if(NDims == 0) if(NDims == 0)


+ 6
- 0
src/TensorFlowNET.Core/Tensors/dtypes.cs View File

@@ -31,9 +31,15 @@ namespace Tensorflow


switch (type.Name) switch (type.Name)
{ {
case "Boolean":
dtype = TF_DataType.TF_BOOL;
break;
case "Int32": case "Int32":
dtype = TF_DataType.TF_INT32; dtype = TF_DataType.TF_INT32;
break; break;
case "Int64":
dtype = TF_DataType.TF_INT64;
break;
case "Single": case "Single":
dtype = TF_DataType.TF_FLOAT; dtype = TF_DataType.TF_FLOAT;
break; break;


+ 20
- 3
src/TensorFlowNET.Core/Tensors/tensor_util.cs View File

@@ -38,7 +38,8 @@ namespace Tensorflow
{ {
return MakeNdarray(tensor.op.get_attr("value") as TensorProto); return MakeNdarray(tensor.op.get_attr("value") as TensorProto);
} }
throw new NotImplementedException("_ConstantValue");

return null;
} }


public static NDArray MakeNdarray(TensorProto tensor) public static NDArray MakeNdarray(TensorProto tensor)
@@ -50,6 +51,15 @@ namespace Tensorflow
if (tensor.TensorContent.Length > 0) if (tensor.TensorContent.Length > 0)
return np.frombuffer(tensor.TensorContent.ToByteArray(), tensor_dtype) return np.frombuffer(tensor.TensorContent.ToByteArray(), tensor_dtype)
.reshape(shape); .reshape(shape);
else if (tensor.Dtype == DataType.DtHalf || tensor.Dtype == DataType.DtBfloat16)
;
else if (tensor.Dtype == DataType.DtFloat)
;
else if (new DataType[] { DataType.DtInt32, DataType.DtUint8 }.Contains(tensor.Dtype))
if (tensor.IntVal.Count == 1)
return np.repeat(np.array(tensor.IntVal[0]), Convert.ToInt32(num_elements))
.reshape(shape);

throw new NotImplementedException("MakeNdarray"); throw new NotImplementedException("MakeNdarray");
} }


@@ -101,6 +111,9 @@ namespace Tensorflow
case int intVal: case int intVal:
nparray = intVal; nparray = intVal;
break; break;
case long intVal:
nparray = intVal;
break;
case int[] intVals: case int[] intVals:
nparray = np.array(intVals); nparray = np.array(intVals);
break; break;
@@ -216,11 +229,15 @@ namespace Tensorflow
switch (nparray.dtype.Name) switch (nparray.dtype.Name)
{ {
case "Bool": case "Bool":
case "Boolean":
tensor_proto.BoolVal.AddRange(proto_values.Data<bool>()); tensor_proto.BoolVal.AddRange(proto_values.Data<bool>());
break; break;
case "Int32": case "Int32":
tensor_proto.IntVal.AddRange(proto_values.Data<int>()); tensor_proto.IntVal.AddRange(proto_values.Data<int>());
break; break;
case "Int64":
tensor_proto.Int64Val.AddRange(proto_values.Data<long>());
break;
case "Single": case "Single":
tensor_proto.FloatVal.AddRange(proto_values.Data<float>()); tensor_proto.FloatVal.AddRange(proto_values.Data<float>());
break; break;
@@ -286,7 +303,7 @@ namespace Tensorflow
default: default:
throw new NotImplementedException("as_shape Not Implemented"); throw new NotImplementedException("as_shape Not Implemented");
} }
dim.Name = $"dim_{i}";
// dim.Name = $"dim_{i}";


shape.Dim.Add(dim); shape.Dim.Add(dim);
} }
@@ -317,7 +334,7 @@ namespace Tensorflow
{ {
var dim = new TensorShapeProto.Types.Dim(); var dim = new TensorShapeProto.Types.Dim();
dim.Size = tshape.Dimensions[i]; dim.Size = tshape.Dimensions[i];
dim.Name = $"dim_{i}";
//dim.Name = $"dim_{i}";


shape.Dim.Add(dim); shape.Dim.Add(dim);
} }


+ 25
- 0
src/TensorFlowNET.Core/Train/AdamOptimizer.cs View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Tensorflow.Train
{
/// <summary>
/// Optimizer that implements the Adam algorithm.
/// http://arxiv.org/abs/1412.6980
/// </summary>
public class AdamOptimizer : Optimizer
{
private float _beta1;
private float _beta2;
private float _epsilon;

public AdamOptimizer(float learning_rate, float beta1 = 0.9f, float beta2 = 0.999f, float epsilon = 1e-8f, bool use_locking = false, string name = "Adam")
: base(learning_rate, use_locking, name)
{
_beta1 = beta1;
_beta2 = beta2;
_epsilon = epsilon;
}
}
}

+ 1
- 1
src/TensorFlowNET.Core/Train/GradientDescentOptimizer.cs View File

@@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;


namespace Tensorflow
namespace Tensorflow.Train
{ {
public class GradientDescentOptimizer : Optimizer public class GradientDescentOptimizer : Optimizer
{ {


+ 2
- 0
src/TensorFlowNET.Core/Train/Optimizer.cs View File

@@ -34,6 +34,7 @@ namespace Tensorflow


Name = name; Name = name;
_use_locking = use_locking; _use_locking = use_locking;
LearningRate = learning_rate;
// Dictionary of slots. // Dictionary of slots.
_slots = new Dictionary<string, object>(); _slots = new Dictionary<string, object>();
_non_slot_dict = new Dictionary<string, object>(); _non_slot_dict = new Dictionary<string, object>();
@@ -49,6 +50,7 @@ namespace Tensorflow
/// was not `None`, that operation also increments `global_step`. /// was not `None`, that operation also increments `global_step`.
/// </returns> /// </returns>
public Operation minimize(Tensor loss, public Operation minimize(Tensor loss,
RefVariable global_step = null,
GateGradientType gate_gradients = GateGradientType.GATE_OP, GateGradientType gate_gradients = GateGradientType.GATE_OP,
bool colocate_gradients_with_ops = false) bool colocate_gradients_with_ops = false)
{ {


+ 1
- 1
src/TensorFlowNET.Core/Train/Saving/Saver.cs View File

@@ -251,7 +251,7 @@ namespace Tensorflow
{ {
return export_meta_graph( return export_meta_graph(
filename: filename, filename: filename,
graph_def: ops.get_default_graph()._as_graph_def(add_shapes: true),
graph_def: ops.get_default_graph().as_graph_def(add_shapes: true),
saver_def: _saver_def, saver_def: _saver_def,
collection_list: collection_list, collection_list: collection_list,
as_text: as_text, as_text: as_text,


+ 5
- 1
src/TensorFlowNET.Core/Train/tf.optimizers.cs View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using Tensorflow.Train;


namespace Tensorflow namespace Tensorflow
{ {
@@ -11,9 +12,12 @@ namespace Tensorflow
{ {
public static Optimizer GradientDescentOptimizer(float learning_rate) => new GradientDescentOptimizer(learning_rate); public static Optimizer GradientDescentOptimizer(float learning_rate) => new GradientDescentOptimizer(learning_rate);


public static Optimizer AdamOptimizer(float learning_rate) => new AdamOptimizer(learning_rate);

public static Saver Saver() => new Saver(); public static Saver Saver() => new Saver();


public static string write_graph(Graph graph, string logdir, string name, bool as_text = true) => graph_io.write_graph(graph, logdir, name, as_text);
public static string write_graph(Graph graph, string logdir, string name, bool as_text = true)
=> graph_io.write_graph(graph, logdir, name, as_text);


public static Saver import_meta_graph(string meta_graph_or_file, public static Saver import_meta_graph(string meta_graph_or_file,
bool clear_devices = false, bool clear_devices = false,


+ 2
- 1
src/TensorFlowNET.Core/Variables/RefVariable.Operators.cs View File

@@ -13,7 +13,8 @@ namespace Tensorflow
public static Tensor operator -(RefVariable x, int y) => op_helper("sub", x, y); public static Tensor operator -(RefVariable x, int y) => op_helper("sub", x, y);
public static Tensor operator -(RefVariable x, float y) => op_helper("sub", x, y); public static Tensor operator -(RefVariable x, float y) => op_helper("sub", x, y);
public static Tensor operator -(RefVariable x, double y) => op_helper("sub", x, y); public static Tensor operator -(RefVariable x, double y) => op_helper("sub", x, y);
public static Tensor operator -(RefVariable x, Tensor y) => op_helper("sub", x, y);

private static Tensor op_helper<T>(string default_name, RefVariable x, T y) private static Tensor op_helper<T>(string default_name, RefVariable x, T y)
{ {
var tensor1 = x.value(); var tensor1 = x.value();


+ 11
- 1
src/TensorFlowNET.Core/Variables/gen_state_ops.py.cs View File

@@ -52,7 +52,7 @@ namespace Tensorflow
bool use_locking = true, bool use_locking = true,
string name = null) string name = null)
{ {
var _op = _op_def_lib._apply_op_helper("Assign", name: name, args: new { _ref_ = tensor, value, validate_shape, use_locking });
var _op = _op_def_lib._apply_op_helper("Assign", name: name, args: new { @ref = tensor, value, validate_shape, use_locking });


var _result = _op.outputs; var _result = _op.outputs;
var _inputs_flat = _op.inputs; var _inputs_flat = _op.inputs;
@@ -66,5 +66,15 @@ namespace Tensorflow


return _result[0]; return _result[0];
} }

public static Tensor assign_sub(RefVariable @ref,
Tensor value,
bool use_locking = false,
string name = null)
{
var _op = _op_def_lib._apply_op_helper("AssignSub", name: name, args: new { @ref, value, use_locking });

return _op.outputs[0];
}
} }
} }

+ 8
- 0
src/TensorFlowNET.Core/Variables/state_ops.cs View File

@@ -24,5 +24,13 @@ namespace Tensorflow
name: name, name: name,
container: container, container: container,
shared_name: shared_name); shared_name: shared_name);

public static Tensor assign_sub(RefVariable @ref,
Tensor value,
bool use_locking = false,
string name = null) => gen_state_ops.assign_sub(@ref,
value,
use_locking: use_locking,
name: name);
} }
} }

+ 1
- 0
src/TensorFlowNET.Core/Variables/variable_scope.py.cs View File

@@ -156,6 +156,7 @@ namespace Tensorflow
{ {
return new RefVariable(initial_value, return new RefVariable(initial_value,
trainable: trainable.Value, trainable: trainable.Value,
validate_shape: validate_shape,
name: name, name: name,
dtype: dtype); dtype: dtype);
} }


+ 6
- 3
src/TensorFlowNET.Core/Variables/variables.py.cs View File

@@ -47,11 +47,11 @@ namespace Tensorflow
/// special tokens filters by prefix. /// special tokens filters by prefix.
/// </param> /// </param>
/// <returns>A list of `Variable` objects.</returns> /// <returns>A list of `Variable` objects.</returns>
public static List<RefVariable> global_variables(string scope = "")
public static List<RefVariable> global_variables(string scope = null)
{ {
var result = ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES, scope); var result = ops.get_collection(ops.GraphKeys.GLOBAL_VARIABLES, scope);


return result as List<RefVariable>;
return result == null ? new List<RefVariable>() : result as List<RefVariable>;
} }


/// <summary> /// <summary>
@@ -62,7 +62,10 @@ namespace Tensorflow
/// <returns>An Op that run the initializers of all the specified variables.</returns> /// <returns>An Op that run the initializers of all the specified variables.</returns>
public static Operation variables_initializer(RefVariable[] var_list, string name = "init") public static Operation variables_initializer(RefVariable[] var_list, string name = "init")
{ {
return control_flow_ops.group(var_list.Select(x => x.initializer).ToArray(), name);
if (var_list.Length > 0)
return control_flow_ops.group(var_list.Select(x => x.initializer).ToArray(), name);
else
return gen_control_flow_ops.no_op(name: name);
} }
} }
} }

+ 4
- 0
src/TensorFlowNET.Core/ops.GraphKeys.cs View File

@@ -47,6 +47,10 @@ namespace Tensorflow


// Used to store v2 summary names. // Used to store v2 summary names.
public static string _SUMMARY_COLLECTION = "_SUMMARY_V2"; public static string _SUMMARY_COLLECTION = "_SUMMARY_V2";

// Key for control flow context.
public static string COND_CONTEXT = "cond_context";
public static string WHILE_CONTEXT = "while_context";
} }
} }
} }

+ 11
- 56
src/TensorFlowNET.Core/ops.py.cs View File

@@ -9,6 +9,7 @@ using Google.Protobuf;
using System.Linq; using System.Linq;
using NumSharp.Core; using NumSharp.Core;
using System.ComponentModel; using System.ComponentModel;
using Tensorflow.Gradients;


namespace Tensorflow namespace Tensorflow
{ {
@@ -40,7 +41,7 @@ namespace Tensorflow
/// list contains the values in the order under which they were /// list contains the values in the order under which they were
/// collected. /// collected.
/// </returns> /// </returns>
public static object get_collection(string key, string scope = "")
public static object get_collection(string key, string scope = null)
{ {
return get_default_graph().get_collection(key, scope); return get_default_graph().get_collection(key, scope);
} }
@@ -345,43 +346,6 @@ namespace Tensorflow
session.run(operation, feed_dict); session.run(operation, feed_dict);
} }


public static Func<Operation, Tensor, Tensor[]> get_gradient_function(Operation op)
{
if (op.inputs == null) return null;

return (oper, out_grads) =>
{
// Console.WriteLine($"get_gradient_function: {oper.type} '{oper.name}'");

switch (oper.type)
{
case "Add":
var add = math_grad._AddGrad(oper, out_grads);
return new Tensor[] { add.Item1, add.Item2 };
case "Identity":
var id = math_grad._IdGrad(oper, out_grads);
return new Tensor[] { id };
case "Mul":
var mul = math_grad._MulGrad(oper, out_grads);
return new Tensor[] { mul.Item1, mul.Item2 };
case "Sum":
var sum = math_grad._SumGrad(oper, out_grads);
return new Tensor[] { sum.Item1, sum.Item2 };
case "Sub":
var sub = math_grad._SubGrad(oper, out_grads);
return new Tensor[] { sub.Item1, sub.Item2 };
case "Pow":
var pow = math_grad._PowGrad(oper, out_grads);
return new Tensor[] { pow.Item1, pow.Item2 };
case "RealDiv":
var realdiv = math_grad._RealDivGrad(oper, out_grads);
return new Tensor[] { realdiv.Item1, realdiv.Item2 };
default:
throw new NotImplementedException($"get_gradient_function {oper.type}");
}
};
}

public static Tensor[] convert_n_to_tensor_or_indexed_slices(Tensor[] values, TF_DataType dtype = TF_DataType.DtInvalid, string name = null) public static Tensor[] convert_n_to_tensor_or_indexed_slices(Tensor[] values, TF_DataType dtype = TF_DataType.DtInvalid, string name = null)
{ {
return internal_convert_n_to_tensor_or_indexed_slices(values, dtype: dtype, name: name); return internal_convert_n_to_tensor_or_indexed_slices(values, dtype: dtype, name: name);
@@ -417,13 +381,13 @@ namespace Tensorflow
return ret.ToArray(); return ret.ToArray();
} }


public static Tensor[] internal_convert_n_to_tensor<T>(T[] values, TF_DataType dtype = TF_DataType.DtInvalid,
public static Tensor[] internal_convert_n_to_tensor(object values, TF_DataType dtype = TF_DataType.DtInvalid,
string name = null, TF_DataType preferred_dtype = TF_DataType.DtInvalid, string name = null, TF_DataType preferred_dtype = TF_DataType.DtInvalid,
bool as_ref = false) bool as_ref = false)
{ {
var ret = new List<Tensor>(); var ret = new List<Tensor>();


foreach((int i, T value) in Python.enumerate(values))
foreach((int i, object value) in enumerate(values as object[]))
{ {
string n = string.IsNullOrEmpty(name) ? "" : $"{name}_{i}"; string n = string.IsNullOrEmpty(name) ? "" : $"{name}_{i}";
ret.Add(internal_convert_to_tensor(value, dtype: dtype, name: n, as_ref: as_ref, preferred_dtype: preferred_dtype)); ret.Add(internal_convert_to_tensor(value, dtype: dtype, name: n, as_ref: as_ref, preferred_dtype: preferred_dtype));
@@ -434,7 +398,8 @@ namespace Tensorflow


public static Tensor internal_convert_to_tensor(object value, TF_DataType dtype = TF_DataType.DtInvalid, public static Tensor internal_convert_to_tensor(object value, TF_DataType dtype = TF_DataType.DtInvalid,
string name = null, TF_DataType preferred_dtype = TF_DataType.DtInvalid, string name = null, TF_DataType preferred_dtype = TF_DataType.DtInvalid,
bool as_ref = false)
bool as_ref = false,
string scope = null)
{ {
if (dtype == TF_DataType.DtInvalid) if (dtype == TF_DataType.DtInvalid)
dtype = preferred_dtype; dtype = preferred_dtype;
@@ -445,24 +410,14 @@ namespace Tensorflow
return constant_op.constant(nd, dtype: dtype, name: name); return constant_op.constant(nd, dtype: dtype, name: name);
case Tensor tensor: case Tensor tensor:
return tensor; return tensor;
case string str:
return constant_op.constant(str, dtype: dtype, name: name);
case string[] strArray:
return constant_op.constant(strArray, dtype: dtype, name: name);
case int intVal:
return constant_op.constant(intVal, dtype: dtype, name: name);
case int[] intArray:
return constant_op.constant(intArray, dtype: dtype, name: name);
case float floatVal:
return constant_op.constant(floatVal, dtype: dtype, name: name);
case float[] floatArray:
return constant_op.constant(floatArray, dtype: dtype, name: name);
case double doubleVal:
return constant_op.constant(doubleVal, dtype: dtype, name: name);
case Tensor[] tensors:
return array_ops._autopacking_helper(tensors, dtype, name);
case RefVariable varVal: case RefVariable varVal:
return varVal._TensorConversionFunction(as_ref: as_ref); return varVal._TensorConversionFunction(as_ref: as_ref);
case object[] objects:
return array_ops._autopacking_conversion_function(objects, dtype: dtype, name: name);
default: default:
throw new NotImplementedException($"internal_convert_to_tensor: Can't convert {value.GetType().Name} to Tensor");
return constant_op.constant(value, dtype: dtype, name: name);
} }
} }




+ 3
- 0
src/TensorFlowNET.Core/tf.cs View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Tensorflow.Eager; using Tensorflow.Eager;
using static Tensorflow.ops;


namespace Tensorflow namespace Tensorflow
{ {
@@ -21,11 +22,13 @@ namespace Tensorflow


public static RefVariable Variable<T>(T data, public static RefVariable Variable<T>(T data,
bool trainable = true, bool trainable = true,
bool validate_shape = true,
string name = null, string name = null,
TF_DataType dtype = TF_DataType.DtInvalid) TF_DataType dtype = TF_DataType.DtInvalid)
{ {
return Tensorflow.variable_scope.default_variable_creator(data, return Tensorflow.variable_scope.default_variable_creator(data,
trainable: trainable, trainable: trainable,
validate_shape: validate_shape,
name: name, name: name,
dtype: TF_DataType.DtInvalid); dtype: TF_DataType.DtInvalid);
} }


+ 13
- 0
tensorflowlib/README.md View File

@@ -1,3 +1,5 @@
TensorFlow.NET pack all required libraries in architecture-specific assemblies folders per NuGet standard.

Here are some pre-built TensorFlow binaries you can use for each platform: Here are some pre-built TensorFlow binaries you can use for each platform:


- Linux - Linux
@@ -6,7 +8,18 @@ Here are some pre-built TensorFlow binaries you can use for each platform:
- Mac: https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-cpu-darwin-x86_64-1.13.1.tar.gz - Mac: https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-cpu-darwin-x86_64-1.13.1.tar.gz
- Windows: https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-cpu-windows-x86_64-1.13.1.zip - Windows: https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-cpu-windows-x86_64-1.13.1.zip


### Run in Linux

`Install-Package TensorFlow.NET`

Download Linux pre-built library and unzip `libtensorflow.so` and `libtensorflow_framework.so` into current running directory.

### Run in Mac OS

### Build from source for Windows

https://www.tensorflow.org/install/source_windows https://www.tensorflow.org/install/source_windows

pacman -S git patch unzip pacman -S git patch unzip


1. Build static library 1. Build static library


+ 0
- 0
tensorflowlib/runtimes/linux-x64/native/libtensorflow.so View File


+ 0
- 0
tensorflowlib/runtimes/linux-x64/native/libtensorflow_framework.so View File


BIN
tensorflowlib/runtimes/win-x64/native/tensorflow.dll View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save