| @@ -153,6 +153,15 @@ namespace Tensorflow | |||||
| public static Tensor less<Tx, Ty>(Tx x, Ty y, string name = null) | public static Tensor less<Tx, Ty>(Tx x, Ty y, string name = null) | ||||
| => gen_math_ops.less(x, y, name); | => gen_math_ops.less(x, y, name); | ||||
| /// <summary> | |||||
| /// Computes the log of the absolute value of `Gamma(x)` element-wise. | |||||
| /// </summary> | |||||
| /// <param name="x">A `Tensor`. Must be one of the following types: `bfloat16`, `half`, `float32`, `float64`.</param> | |||||
| /// <param name="name">A name for the operation (optional).</param> | |||||
| /// <returns>A `Tensor`. Has the same type as `x`.</returns> | |||||
| public static Tensor lgamma(Tensor x, string name = null) | |||||
| => gen_math_ops.lgamma(x, name: name); | |||||
| /// <summary> | /// <summary> | ||||
| /// Returns the truth value of (x <= y) element-wise. | /// Returns the truth value of (x <= y) element-wise. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -18,6 +18,41 @@ namespace Tensorflow | |||||
| { | { | ||||
| public static partial class tf | public static partial class tf | ||||
| { | { | ||||
| public static Tensor convert_to_tensor(object value, string name = null) => ops.convert_to_tensor(value, name: name); | |||||
| public static Tensor convert_to_tensor(object value, | |||||
| string name = null) => ops.convert_to_tensor(value, name: name); | |||||
| 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) => 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); | |||||
| public static Tensor strided_slice<T>(Tensor input, T[] begin, T[] end, T[] 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) => 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); | |||||
| } | } | ||||
| } | } | ||||
| @@ -196,6 +196,67 @@ namespace Tensorflow.Gradients | |||||
| return new Tensor[] { _ReshapeToInput(op, grads[0]) }; | return new Tensor[] { _ReshapeToInput(op, grads[0]) }; | ||||
| } | } | ||||
| /// <summary> | |||||
| /// Gradient for StridedSlice op. | |||||
| /// </summary> | |||||
| /// <param name="op"></param> | |||||
| /// <param name="grads"></param> | |||||
| /// <returns></returns> | |||||
| [RegisterGradient("StridedSlice")] | |||||
| public static Tensor[] _StridedSliceGrad(Operation op, Tensor[] grads) | |||||
| { | |||||
| var grad = grads[0]; | |||||
| var begin = op.inputs[1]; | |||||
| var end = op.inputs[2]; | |||||
| var strides = op.inputs[3]; | |||||
| var x = array_ops.shape(op.inputs[0], out_type: begin.dtype); | |||||
| return new Tensor[] | |||||
| { | |||||
| gen_array_ops.strided_slice_grad( | |||||
| x, | |||||
| begin, | |||||
| end, | |||||
| strides, | |||||
| grad, | |||||
| begin_mask: (int)op.get_attr("begin_mask"), | |||||
| end_mask: (int)op.get_attr("end_mask"), | |||||
| ellipsis_mask: (int)op.get_attr("ellipsis_mask"), | |||||
| new_axis_mask: (int)op.get_attr("new_axis_mask"), | |||||
| shrink_axis_mask: (int)op.get_attr("shrink_axis_mask")), | |||||
| null, | |||||
| null, | |||||
| null | |||||
| }; | |||||
| } | |||||
| [RegisterGradient("StridedSliceGrad")] | |||||
| public static Tensor[] _StridedSliceGradGrad(Operation op, Tensor[] grads) | |||||
| { | |||||
| var grad = grads[0]; | |||||
| var begin = op.inputs[1]; | |||||
| var end = op.inputs[2]; | |||||
| var strides = op.inputs[3]; | |||||
| return new Tensor[] | |||||
| { | |||||
| null, | |||||
| null, | |||||
| null, | |||||
| gen_array_ops.strided_slice( | |||||
| grad, | |||||
| begin, | |||||
| end, | |||||
| strides, | |||||
| begin_mask: (int)op.get_attr("begin_mask"), | |||||
| end_mask: (int)op.get_attr("end_mask"), | |||||
| ellipsis_mask: (int)op.get_attr("ellipsis_mask"), | |||||
| new_axis_mask: (int)op.get_attr("new_axis_mask"), | |||||
| shrink_axis_mask: (int)op.get_attr("shrink_axis_mask")) | |||||
| }; | |||||
| } | |||||
| private static Tensor _ReshapeToInput(Operation op, Tensor grad) | private static Tensor _ReshapeToInput(Operation op, Tensor grad) | ||||
| { | { | ||||
| return array_ops.reshape(grad, array_ops.shape(op.inputs[0])); | return array_ops.reshape(grad, array_ops.shape(op.inputs[0])); | ||||
| @@ -92,6 +92,17 @@ namespace Tensorflow.Gradients | |||||
| return new Tensor[] { grads[0] }; | return new Tensor[] { grads[0] }; | ||||
| } | } | ||||
| [RegisterGradient("Lgamma")] | |||||
| public static Tensor[] _LgammaGrad(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.digamma(x) }; | |||||
| }); | |||||
| } | |||||
| [RegisterGradient("Log")] | [RegisterGradient("Log")] | ||||
| public static Tensor[] _LogGrad(Operation op, Tensor[] grads) | public static Tensor[] _LogGrad(Operation op, Tensor[] grads) | ||||
| { | { | ||||
| @@ -431,6 +442,19 @@ namespace Tensorflow.Gradients | |||||
| }); | }); | ||||
| } | } | ||||
| [RegisterGradient("Tanh")] | |||||
| public static Tensor[] _TanhGrad(Operation op, Tensor[] grads) | |||||
| { | |||||
| var grad = grads[0]; | |||||
| var y = op.outputs[0]; | |||||
| return with(ops.control_dependencies(grads), delegate | |||||
| { | |||||
| y = math_ops.conj(y); | |||||
| return new Tensor[] { gen_math_ops.tanh_grad(y, grad) }; | |||||
| }); | |||||
| } | |||||
| [RegisterGradient("Pow")] | [RegisterGradient("Pow")] | ||||
| public static Tensor[] _PowGrad(Operation op, Tensor[] grads) | public static Tensor[] _PowGrad(Operation op, Tensor[] grads) | ||||
| { | { | ||||
| @@ -381,6 +381,71 @@ namespace Tensorflow | |||||
| return _op.outputs[0]; | return _op.outputs[0]; | ||||
| } | } | ||||
| public static Tensor strided_slice<T>(Tensor input, T[] begin, T[] end, T[] 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]; | |||||
| } | |||||
| /// <summary> | |||||
| /// Returns the gradient of `StridedSlice`. | |||||
| /// | |||||
| /// Since `StridedSlice` cuts out pieces of its `input` which is size | |||||
| /// `shape`, its gradient will have the same shape (which is passed here | |||||
| /// as `shape`). The gradient will be zero in any element that the slice | |||||
| /// does not select. | |||||
| /// </summary> | |||||
| /// <param name="shape">Must be one of the following types: `int32`, `int64`.</param> | |||||
| /// <param name="begin">Must have the same type as `shape`.</param> | |||||
| /// <param name="end">Must have the same type as `shape`.</param> | |||||
| /// <param name="strides">Must have the same type as `shape`.</param> | |||||
| /// <param name="dy">A `Tensor`.</param> | |||||
| /// <param name="begin_mask">An optional `int`. Defaults to `0`.</param> | |||||
| /// <param name="end_mask">An optional `int`. Defaults to `0`.</param> | |||||
| /// <param name="ellipsis_mask">An optional `int`. Defaults to `0`.</param> | |||||
| /// <param name="new_axis_mask">An optional `int`. Defaults to `0`.</param> | |||||
| /// <param name="shrink_axis_mask">An optional `int`. Defaults to `0`.</param> | |||||
| /// <param name="name">A name for the operation (optional).</param> | |||||
| /// <returns>A `Tensor`. Has the same type as `dy`.</returns> | |||||
| public static Tensor strided_slice_grad(Tensor shape, Tensor begin, Tensor end, Tensor strides, Tensor dy, | |||||
| 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("StridedSliceGrad", name: name, args: new | |||||
| { | |||||
| shape, | |||||
| begin, | |||||
| end, | |||||
| strides, | |||||
| dy, | |||||
| begin_mask, | |||||
| end_mask, | |||||
| ellipsis_mask, | |||||
| new_axis_mask, | |||||
| shrink_axis_mask | |||||
| }); | |||||
| return op.output; | |||||
| } | |||||
| public static Tensor slice<Tb, Ts>(Tensor input, Tb[] begin, Ts[] size, string name = null) | 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 }); | var _op = _op_def_lib._apply_op_helper("Slice", name, new { input, begin, size }); | ||||
| @@ -62,6 +62,15 @@ namespace Tensorflow | |||||
| public static Tensor arg_min(Tensor input, int dimension, TF_DataType output_type= TF_DataType.TF_INT64, string name= null) | 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]; | =>_op_def_lib._apply_op_helper("ArgMin", name, args: new { input, dimension, output_type }).outputs[0]; | ||||
| /// <summary> | |||||
| /// Computes Psi, the derivative of Lgamma (the log of the absolute value of | |||||
| /// `Gamma(x)`), element-wise. | |||||
| /// </summary> | |||||
| /// <param name="x"></param> | |||||
| /// <param name="name"></param> | |||||
| /// <returns></returns> | |||||
| public static Tensor digamma(Tensor x, string name = null) | |||||
| => _op_def_lib._apply_op_helper("Digamma", name, args: new { x }).output; | |||||
| /// <summary> | /// <summary> | ||||
| /// Returns 0 if the denominator is zero. | /// Returns 0 if the denominator is zero. | ||||
| @@ -250,6 +259,16 @@ namespace Tensorflow | |||||
| return _op.outputs[0]; | return _op.outputs[0]; | ||||
| } | } | ||||
| /// <summary> | |||||
| /// Computes the gradient for the tanh of `x` wrt its input. | |||||
| /// </summary> | |||||
| /// <param name="y"></param> | |||||
| /// <param name="dy"></param> | |||||
| /// <param name="name"></param> | |||||
| /// <returns></returns> | |||||
| public static Tensor tanh_grad(Tensor y, Tensor dy, string name = "TanhGrad") | |||||
| => _op_def_lib._apply_op_helper("TanhGrad", name: name, args: new { y, dy }).output; | |||||
| public static Tensor floor(Tensor x, string name = null) | public static Tensor floor(Tensor x, string name = null) | ||||
| { | { | ||||
| var _op = _op_def_lib._apply_op_helper("Floor", name, args: new { x }); | var _op = _op_def_lib._apply_op_helper("Floor", name, args: new { x }); | ||||
| @@ -271,6 +290,24 @@ namespace Tensorflow | |||||
| return _op.outputs[0]; | return _op.outputs[0]; | ||||
| } | } | ||||
| /// <summary> | |||||
| /// Computes the log of the absolute value of `Gamma(x)` element-wise. | |||||
| /// </summary> | |||||
| /// <param name="x"> | |||||
| /// A `Tensor`. Must be one of the following types: `bfloat16`, `half`, `float32`, `float64`. | |||||
| /// </param> | |||||
| /// <param name="name"> | |||||
| /// </param> | |||||
| /// <returns> | |||||
| /// The Operation can be fetched from the resulting Tensor, by fetching the Operation property from the result. | |||||
| /// </returns> | |||||
| public static Tensor lgamma(Tensor x, string name = null) | |||||
| { | |||||
| var op = _op_def_lib._apply_op_helper("Lgamma", name: name, args: new { x }); | |||||
| return op.output; | |||||
| } | |||||
| public static Tensor greater_equal<Tx, Ty>(Tx x, Ty y, string name = null) | 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 }); | var _op = _op_def_lib._apply_op_helper("GreaterEqual", name: name, args: new { x, y }); | ||||
| @@ -80,6 +80,16 @@ namespace Tensorflow | |||||
| }); | }); | ||||
| } | } | ||||
| /// <summary> | |||||
| /// Computes Psi, the derivative of Lgamma (the log of the absolute value of | |||||
| /// `Gamma(x)`), element-wise. | |||||
| /// </summary> | |||||
| /// <param name="x"></param> | |||||
| /// <param name="name"></param> | |||||
| /// <returns></returns> | |||||
| public static Tensor digamma(Tensor x, string name = null) | |||||
| => gen_math_ops.digamma(x, name: name); | |||||
| /// <summary> | /// <summary> | ||||
| /// Divide two values using Python 2 semantics. Used for Tensor.__div__. | /// Divide two values using Python 2 semantics. Used for Tensor.__div__. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -234,6 +244,9 @@ namespace Tensorflow | |||||
| return gen_math_ops.log(x, name); | return gen_math_ops.log(x, name); | ||||
| } | } | ||||
| public static Tensor lgamma(Tensor x, string name = null) | |||||
| => gen_math_ops.lgamma(x, name: name); | |||||
| /// <summary> | /// <summary> | ||||
| /// Helper function for reduction ops. | /// Helper function for reduction ops. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -5,7 +5,7 @@ | |||||
| <AssemblyName>TensorFlow.NET</AssemblyName> | <AssemblyName>TensorFlow.NET</AssemblyName> | ||||
| <RootNamespace>Tensorflow</RootNamespace> | <RootNamespace>Tensorflow</RootNamespace> | ||||
| <TargetTensorFlow>1.14.0</TargetTensorFlow> | <TargetTensorFlow>1.14.0</TargetTensorFlow> | ||||
| <Version>0.10.0-rc</Version> | |||||
| <Version>0.10.0</Version> | |||||
| <Authors>Haiping Chen, Meinrad Recheis</Authors> | <Authors>Haiping Chen, Meinrad Recheis</Authors> | ||||
| <Company>SciSharp STACK</Company> | <Company>SciSharp STACK</Company> | ||||
| <GeneratePackageOnBuild>true</GeneratePackageOnBuild> | <GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||||
| @@ -25,7 +25,8 @@ Learn more about .NET AI: https://medium.com/scisharp</Description> | |||||
| 2. Take dependency on SciSharp.TensorFlow.Redist. | 2. Take dependency on SciSharp.TensorFlow.Redist. | ||||
| 3. Create Tensor from array without copying. | 3. Create Tensor from array without copying. | ||||
| 4. Fix path issue of Transfer Learning example on Linux. | 4. Fix path issue of Transfer Learning example on Linux. | ||||
| 5. Add back gen_ops.cs.</PackageReleaseNotes> | |||||
| 5. Add back gen_ops.cs. | |||||
| 5. Add StridedSliceGrad.</PackageReleaseNotes> | |||||
| <LangVersion>7.2</LangVersion> | <LangVersion>7.2</LangVersion> | ||||
| <FileVersion>0.10.0.0</FileVersion> | <FileVersion>0.10.0.0</FileVersion> | ||||
| <PackageLicenseFile>LICENSE</PackageLicenseFile> | <PackageLicenseFile>LICENSE</PackageLicenseFile> | ||||
| @@ -59,7 +60,7 @@ Learn more about .NET AI: https://medium.com/scisharp</Description> | |||||
| <ItemGroup> | <ItemGroup> | ||||
| <PackageReference Include="Google.Protobuf" Version="3.9.0" /> | <PackageReference Include="Google.Protobuf" Version="3.9.0" /> | ||||
| <PackageReference Include="NumSharp" Version="0.10.4" /> | |||||
| <PackageReference Include="NumSharp" Version="0.10.5" /> | |||||
| <PackageReference Include="SciSharp.TensorFlow.Redist" Version="1.14.0" /> | <PackageReference Include="SciSharp.TensorFlow.Redist" Version="1.14.0" /> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| @@ -1,4 +1,6 @@ | |||||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||
| using NumSharp; | |||||
| using System.Linq; | |||||
| using Tensorflow; | using Tensorflow; | ||||
| namespace TensorFlowNET.UnitTest | namespace TensorFlowNET.UnitTest | ||||
| @@ -25,5 +27,42 @@ namespace TensorFlowNET.UnitTest | |||||
| Assert.AreEqual(g[0].name, "gradients/Fill:0"); | Assert.AreEqual(g[0].name, "gradients/Fill:0"); | ||||
| Assert.AreEqual(g[1].name, "gradients/Fill:0"); | Assert.AreEqual(g[1].name, "gradients/Fill:0"); | ||||
| } | } | ||||
| [TestMethod] | |||||
| public void StridedSlice() | |||||
| { | |||||
| var graph = tf.Graph().as_default(); | |||||
| var t = tf.constant(np.array(new int[,,] | |||||
| { | |||||
| { | |||||
| { 11, 12, 13 }, | |||||
| { 21, 22, 23 } | |||||
| }, | |||||
| { | |||||
| { 31, 32, 33 }, | |||||
| { 41, 42, 43 } | |||||
| }, | |||||
| { | |||||
| { 51, 52, 53 }, | |||||
| { 61, 62, 63 } | |||||
| } | |||||
| })); | |||||
| var slice = tf.strided_slice(t, | |||||
| begin: new[] { 0, 0, 0 }, | |||||
| end: new[] { 3, 2, 3 }, | |||||
| strides: new[] { 2, 2, 2 }); | |||||
| var y = slice + slice; | |||||
| var g = tf.gradients(y, new Tensor[] { slice, slice }); | |||||
| var r = slice.eval(); | |||||
| Assert.IsTrue(Enumerable.SequenceEqual(r.shape, new[] { 2, 1, 2 })); | |||||
| Assert.IsTrue(Enumerable.SequenceEqual(r[0].GetData<int>(), new[] { 11, 13 })); | |||||
| Assert.IsTrue(Enumerable.SequenceEqual(r[1].GetData<int>(), new[] { 51, 53 })); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||