Skip to content

Commit f1a80aa

Browse files
committed
Merge branch 'master' into v0.20-tensorflow2.0
2 parents 4e0c062 + 3090e45 commit f1a80aa

80 files changed

Lines changed: 3095 additions & 1030 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
[![Documentation Status](https://readthedocs.org/projects/tensorflownet/badge/?version=latest)](https://tensorflownet.readthedocs.io/en/latest/?badge=latest)
1010
[![Badge](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu/#/en_US)
1111

12-
TF.NET is a member project of [SciSharp STACK](https://github.com/SciSharp). <a href="http://scisharpstack.org"><img src="https://github.com/SciSharp/SciSharp/blob/master/art/scisharp_badge.png" width="200" height="200" align="right" /></a>
12+
TF.NET is a member project of [SciSharp STACK](https://github.com/SciSharp).
1313

1414

1515
![tensors_flowing](docs/assets/tensors_flowing.gif)
@@ -26,6 +26,13 @@ In comparison to other projects, like for instance TensorFlowSharp which only pr
2626

2727
### How to use
2828

29+
| TensorFlow | tf 1.13 | tf 1.14 | tf 1.15 | tf 2.0 |
30+
| ----------- | ------- | ------- | ------- | ------ |
31+
| tf.net 0.12 | | x | | |
32+
| tf.net 0.11 | x | x | | |
33+
| tf.net 0.10 | x | x | | |
34+
| tf.net 0.9 | x | | | |
35+
2936
Install TF.NET and TensorFlow binary through NuGet.
3037
```sh
3138
### install tensorflow C# binding

src/TensorFlowNET.Core/APIs/tf.control_flow.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public Tensor cond(Tensor pred,
3737
public Operation group<T>(T[] inputs, string name = null) where T : ITensorOrOperation
3838
=> control_flow_ops.group(inputs, name: name);
3939

40-
public Tensor while_loop(Func<Tensor, Tensor> cond, Func<Tensor, Tensor> body, Tensor[] loop_vars,
40+
/*public Tensor while_loop(Func<Tensor, Tensor> cond, Func<Tensor, Tensor> body, Tensor[] loop_vars,
4141
TensorShape shape_invariants = null,
4242
int parallel_iterations = 10,
4343
bool back_prop = true,
@@ -52,7 +52,7 @@ public Tensor while_loop(Func<Tensor, Tensor> cond, Func<Tensor, Tensor> body, T
5252
swap_memory: swap_memory,
5353
name: name,
5454
maximum_iterations: maximum_iterations,
55-
return_same_structure: return_same_structure);
55+
return_same_structure: return_same_structure);*/
5656

5757
public _ControlDependenciesController control_dependencies(ITensorOrOperation[] control_inputs)
5858
=> ops.control_dependencies(control_inputs);

src/TensorFlowNET.Core/APIs/tf.layers.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public Tensor conv2d(Tensor inputs,
6363
trainable: trainable,
6464
name: name);
6565

66-
return layer.apply(inputs);
66+
return layer.apply(inputs).Item1;
6767
}
6868

6969
/// <summary>
@@ -117,7 +117,7 @@ public Tensor batch_normalization(Tensor inputs,
117117
trainable: trainable,
118118
name: name);
119119

120-
return layer.apply(inputs, training: training);
120+
return layer.apply(inputs, training: training).Item1;
121121
}
122122

123123
/// <summary>
@@ -143,7 +143,7 @@ public Tensor max_pooling2d(Tensor inputs,
143143
data_format: data_format,
144144
name: name);
145145

146-
return layer.apply(inputs);
146+
return layer.apply(inputs).Item1;
147147
}
148148

149149
/// <summary>
@@ -179,7 +179,7 @@ public Tensor dense(Tensor inputs,
179179
kernel_initializer: kernel_initializer,
180180
trainable: trainable);
181181

182-
return layer.apply(inputs);
182+
return layer.apply(inputs).Item1;
183183
}
184184

185185
/// <summary>

src/TensorFlowNET.Core/APIs/tf.nn.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public Tensor dropout(Tensor x, Tensor keep_prob = null, Tensor noise_shape = nu
7676
/// <param name="swap_memory"></param>
7777
/// <param name="time_major"></param>
7878
/// <returns>A pair (outputs, state)</returns>
79-
public (Tensor, Tensor) dynamic_rnn(RNNCell cell, Tensor inputs,
79+
public (Tensor, Tensor) dynamic_rnn(RnnCell cell, Tensor inputs,
8080
Tensor sequence_length = null, TF_DataType dtype = TF_DataType.DtInvalid,
8181
int? parallel_iterations = null, bool swap_memory = false, bool time_major = false)
8282
=> rnn.dynamic_rnn(cell, inputs, sequence_length: sequence_length, dtype: dtype,
@@ -134,7 +134,7 @@ public Tensor max_pool(Tensor value, int[] ksize, int[] strides, string padding,
134134
=> nn_ops.max_pool(value, ksize, strides, padding, data_format: data_format, name: name);
135135

136136
public Tensor in_top_k(Tensor predictions, Tensor targets, int k, string name = "InTopK")
137-
=> gen_ops.in_top_k(predictions, targets, k, name);
137+
=> nn_ops.in_top_k(predictions, targets, k, name);
138138

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

src/TensorFlowNET.Core/Binding.Util.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@ namespace Tensorflow
3030
/// </summary>
3131
public static partial class Binding
3232
{
33+
public static T2 get<T1, T2>(this Dictionary<T1, T2> dict, T1 key)
34+
=> key == null ?
35+
default(T2) :
36+
(dict.ContainsKey(key) ? dict[key] : default(T2));
37+
38+
public static void add<T>(this IList<T> list, T element)
39+
=> list.Add(element);
40+
41+
public static void append<T>(this IList<T> list, T element)
42+
=> list.Add(element);
43+
44+
public static void extend<T>(this List<T> list, IEnumerable<T> elements)
45+
=> list.AddRange(elements);
46+
3347
private static string _tostring(object obj)
3448
{
3549
switch (obj)
@@ -81,6 +95,9 @@ public static int len(object a)
8195
throw new NotImplementedException("len() not implemented for type: " + a.GetType());
8296
}
8397

98+
public static T[] list<T>(IEnumerable<T> list)
99+
=> list.ToArray();
100+
84101
public static IEnumerable<int> range(int end)
85102
{
86103
return Enumerable.Range(0, end);
@@ -165,6 +182,12 @@ public static float time()
165182
yield return (t1[i], t2[i]);
166183
}
167184

185+
public static IEnumerable<(T1, T2, T3)> zip<T1, T2, T3>(IList<T1> t1, IList<T2> t2, IList<T3> t3)
186+
{
187+
for (int i = 0; i < t1.Count; i++)
188+
yield return (t1[i], t2[i], t3[i]);
189+
}
190+
168191
public static IEnumerable<(T1, T2)> zip<T1, T2>(NDArray t1, NDArray t2)
169192
where T1: unmanaged
170193
where T2: unmanaged
@@ -203,6 +226,7 @@ public static float time()
203226
yield return (i, values[i]);
204227
}
205228

229+
[DebuggerStepThrough]
206230
public static Dictionary<string, object> ConvertToDict(object dyn)
207231
{
208232
var dictionary = new Dictionary<string, object>();
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*****************************************************************************
2+
Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
******************************************************************************/
16+
17+
using System;
18+
using System.Runtime.InteropServices;
19+
20+
namespace Tensorflow
21+
{
22+
public partial class c_api
23+
{
24+
/// <summary>
25+
/// Specify the device for `desc`. Defaults to empty, meaning unconstrained.
26+
/// </summary>
27+
/// <param name="desc"></param>
28+
/// <param name="device"></param>
29+
[DllImport(TensorFlowLibName)]
30+
public static extern void TF_SetDevice(IntPtr desc, string device);
31+
}
32+
}

src/TensorFlowNET.Core/Gradients/control_flow_grad.cs

Lines changed: 71 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,24 @@ public static Tensor[] _SwitchGrad(Operation op, Tensor[] grads)
4545
switch (op_ctxt)
4646
{
4747
case WhileContext cwhile:
48-
throw new NotImplementedException("_SwitchGrad WhileContext");
48+
{
49+
var merge_grad = grad_ctxt.grad_state.switch_map.get(op);
50+
if (merge_grad != null)
51+
{
52+
if (grads[1] != null)
53+
control_flow_ops._AddNextAndBackEdge(merge_grad, grads[1],
54+
enforce_shape_invariant: false);
55+
return new Tensor[] { null, null };
56+
}
57+
else if (grads[0] != null)
58+
{
59+
merge_grad = merge(new[] { grads[0], grads[0] }, name: "b_switch")[0];
60+
grad_ctxt.grad_state.switch_map[op] = merge_grad;
61+
return new Tensor[] { merge_grad, null };
62+
}
63+
else
64+
return new Tensor[] { null, null };
65+
}
4966
case CondContext ccond:
5067
{
5168
var zero_grad = grads[1 - op_ctxt.branch];
@@ -74,7 +91,7 @@ public static Tensor[] _SwitchGrad(Operation op, Tensor[] grads)
7491
/// <param name="inputs"></param>
7592
/// <param name="name"></param>
7693
/// <returns></returns>
77-
internal static Tensor[] merge(Tensor[] inputs, string name = null)
94+
internal static MergeOutput merge(Tensor[] inputs, string name = null)
7895
{
7996
return tf_with(ops.name_scope(name, "Merge", inputs), scope =>
8097
{
@@ -146,7 +163,7 @@ public static Tensor[] _MergeGrad(Operation op, Tensor[] grads)
146163
}
147164

148165
[RegisterGradient("RefMerge")]
149-
public Tensor[] _RefMergeGrad(Operation op, Tensor[] grads)
166+
public static Tensor[] _RefMergeGrad(Operation op, Tensor[] grads)
150167
{
151168
return _MergeGrad(op, grads);
152169
}
@@ -155,43 +172,32 @@ public Tensor[] _RefMergeGrad(Operation op, Tensor[] grads)
155172
/// Gradients for an exit op are calculated using an Enter op.
156173
/// </summary>
157174
[RegisterGradient("Exit")]
158-
public Tensor[] _ExitGrad(Operation op, Tensor[] grads)
175+
public static Tensor[] _ExitGrad(Operation op, Tensor[] grads)
159176
{
160-
throw new NotImplementedException("_ExitGrad");
161-
// graph = ops.get_default_graph()
162-
//# pylint: disable=protected-access
163-
// op_ctxt = op._get_control_flow_context()
164-
// grad_ctxt = graph._get_control_flow_context()
165-
// # pylint: enable=protected-access
166-
// if not grad_ctxt.back_prop:
167-
// # The flag `back_prop` is set by users to suppress gradient
168-
// # computation for this loop. If the attribute `back_prop` is false,
169-
// # no gradient computation.
170-
// return None
177+
var grad = grads[0];
178+
var graph = ops.get_default_graph();
179+
var op_ctxt = op._get_control_flow_context();
180+
var grad_ctxt = graph._get_control_flow_context() as WhileContext;
181+
// The flag `back_prop` is set by users to suppress gradient
182+
// computation for this loop. If the attribute `back_prop` is false,
183+
// no gradient computation.
184+
if (!grad_ctxt.back_prop)
185+
return null;
171186

172-
// if op_ctxt.grad_state:
173-
// raise TypeError("Second-order gradient for while loops not supported.")
187+
if (op_ctxt.grad_state != null)
188+
throw new TypeError("Second-order gradient for while loops not supported.");
174189

175-
// if isinstance(grad, ops.Tensor) :
176-
// grad_ctxt.AddName(grad.name)
177-
// else:
178-
// if not isinstance(grad, (ops.IndexedSlices, sparse_tensor.SparseTensor)):
179-
// raise TypeError("Type %s not supported" % type(grad))
180-
// grad_ctxt.AddName(grad.values.name)
181-
// grad_ctxt.AddName(grad.indices.name)
182-
// dense_shape = grad.dense_shape
183-
// if dense_shape is not None:
184-
// grad_ctxt.AddName(dense_shape.name)
185-
// grad_ctxt.Enter()
186-
// # pylint: disable=protected-access
187-
// result = control_flow_ops._Enter(
188-
// grad, grad_ctxt.name, is_constant=False,
189-
// parallel_iterations=grad_ctxt.parallel_iterations,
190-
// name="b_exit")
191-
// # pylint: enable=protected-access
192-
// grad_ctxt.loop_enters.append(result)
193-
// grad_ctxt.Exit()
194-
// return result
190+
grad_ctxt.AddName(grad.name);
191+
192+
grad_ctxt.Enter();
193+
var result = control_flow_ops._Enter(
194+
grad, grad_ctxt.name, is_constant: false,
195+
parallel_iterations: grad_ctxt.parallel_iterations,
196+
name: "b_exit");
197+
198+
grad_ctxt.loop_enters.append(result);
199+
grad_ctxt.Exit();
200+
return new[] { result };
195201
}
196202

197203
/// <summary>
@@ -200,15 +206,15 @@ public Tensor[] _ExitGrad(Operation op, Tensor[] grads)
200206
/// Note that the backprop next_iteration is added in switch grad.
201207
/// </summary>
202208
[RegisterGradient("NextIteration")]
203-
public Tensor[] _NextIterationGrad(object _, Tensor[] grad)
209+
public static Tensor[] _NextIterationGrad(Operation op, Tensor[] grads)
204210
{
205-
return grad;
211+
return grads;
206212
}
207213

208214
[RegisterGradient("RefNextIteration")]
209-
public Tensor[] _RefNextIterationGrad(object _, Tensor[] grad)
215+
public static Tensor[] _RefNextIterationGrad(Operation op, Tensor[] grads)
210216
{
211-
return grad;
217+
return grads;
212218
}
213219

214220
/// <summary>
@@ -218,33 +224,31 @@ public Tensor[] _RefNextIterationGrad(object _, Tensor[] grad)
218224
/// For loop invariants, we need to add an accumulator loop.
219225
/// </summary>
220226
[RegisterGradient("Enter")]
221-
public Tensor[] _EnterGrad(Tensor op, Tensor[] grad)
227+
public static Tensor[] _EnterGrad(Operation op, Tensor[] grads)
222228
{
223-
throw new NotImplementedException("_EnterGrad");
224-
// graph = ops.get_default_graph()
225-
//# pylint: disable=protected-access
226-
// grad_ctxt = graph._get_control_flow_context()
227-
// # pylint: enable=protected-access
228-
// if not grad_ctxt.back_prop:
229-
// # Skip gradient computation, if the attribute `back_prop` is false.
230-
// return grad
231-
// if grad_ctxt.grad_state is None:
232-
// # Pass the gradient through if we are not in a gradient while context.
233-
// return grad
234-
// if op.get_attr("is_constant"):
235-
// # Add a gradient accumulator for each loop invariant.
236-
// if isinstance(grad, ops.Tensor) :
237-
// result = grad_ctxt.AddBackpropAccumulator(op, grad)
238-
// elif isinstance(grad, ops.IndexedSlices) :
239-
// result = grad_ctxt.AddBackpropIndexedSlicesAccumulator(op, grad)
240-
// else:
241-
// # TODO(yuanbyu, lukasr): Add support for SparseTensor.
242-
// raise TypeError("Type %s not supported" % type(grad))
243-
// else:
244-
// result = exit(grad)
245-
// grad_ctxt.loop_exits.append(result)
246-
// grad_ctxt.ExitResult([result])
247-
// return result
229+
Tensor result = null;
230+
var grad = grads[0];
231+
var graph = ops.get_default_graph();
232+
var grad_ctxt = graph._get_control_flow_context() as WhileContext;
233+
if (!grad_ctxt.back_prop)
234+
// Skip gradient computation, if the attribute `back_prop` is false.
235+
return grads;
236+
if (grad_ctxt.grad_state == null)
237+
// Pass the gradient through if we are not in a gradient while context.
238+
return grads;
239+
if (op.get_attr<bool>("is_constant"))
240+
{
241+
// Add a gradient accumulator for each loop invariant.
242+
result = grad_ctxt.AddBackpropAccumulator(op, grad);
243+
}
244+
else
245+
{
246+
result = control_flow_ops.exit(grad);
247+
grad_ctxt.loop_exits.append(result);
248+
grad_ctxt.ExitResult(new[] { result });
249+
}
250+
251+
return new Tensor[] { result };
248252
}
249253

250254

0 commit comments

Comments
 (0)