Skip to content

Commit 355ca3a

Browse files
committed
Support construct graph from proto.
1 parent c71745c commit 355ca3a

30 files changed

Lines changed: 1216 additions & 25 deletions

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ You may obtain a copy of the License at
1414
limitations under the License.
1515
******************************************************************************/
1616

17+
using Google.Protobuf;
1718
using System.Text;
1819

1920
namespace Tensorflow
@@ -45,6 +46,23 @@ internal string as_str(byte[] bytes_or_text, Encoding? encoding = null)
4546
{
4647
return as_text(bytes_or_text, encoding);
4748
}
49+
50+
public ByteString as_bytes(ByteString bytes, Encoding encoding = null)
51+
{
52+
return bytes;
53+
}
54+
public ByteString as_bytes(byte[] bytes, Encoding encoding = null)
55+
{
56+
return ByteString.CopyFrom(bytes);
57+
}
58+
public ByteString as_bytes(string text, Encoding encoding = null)
59+
{
60+
if(encoding is null)
61+
{
62+
encoding = Encoding.UTF8;
63+
}
64+
return ByteString.CopyFrom(encoding.GetBytes(text));
65+
}
4866
}
4967

5068
public bool executing_eagerly()

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,6 @@ public ITensorOrOperation[] import_graph_def(GraphDef graph_def,
5454
Dictionary<string, Tensor> input_map = null,
5555
string[] return_elements = null,
5656
string name = null,
57-
OpList producer_op_list = null) => importer.import_graph_def(graph_def, input_map, return_elements, name, producer_op_list);
57+
OpList producer_op_list = null) => importer.import_graph_def(graph_def, input_map, return_elements, name: name, producer_op_list: producer_op_list);
5858
}
5959
}

src/TensorFlowNET.Core/Contexts/Context.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,12 @@ public bool has_graph_arg(params object[] args)
156156
return has_graph_arg;
157157
}
158158

159+
public bool has_function(string name)
160+
{
161+
ensure_initialized();
162+
return c_api.TFE_ContextHasFunction(_handle, name);
163+
}
164+
159165
public void restore_mode()
160166
{
161167
context_switches.Pop();
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Security.Cryptography;
5+
using System.Text;
6+
using Tensorflow.Graphs;
7+
using static Tensorflow.Binding;
8+
using static Tensorflow.CppShapeInferenceResult.Types;
9+
10+
namespace Tensorflow.Framework
11+
{
12+
public class function_def_lib
13+
{
14+
// TODO(Rinne): process signatures and structured outputs.
15+
public static FuncGraph function_def_to_graph(FunctionDef fdef, object? structured_input_signature,
16+
object? structured_outputs, List<TensorShapeProto> input_shapes = null)
17+
{
18+
var func_graph = new FuncGraph(fdef.Signature.Name);
19+
if(input_shapes is null)
20+
{
21+
if(fdef.Attr.TryGetValue("_input_shapes", out var input_shapes_attr))
22+
{
23+
var raw_input_shapes = input_shapes_attr.List.Shape;
24+
input_shapes = new List<TensorShapeProto>();
25+
foreach(var (input_shape, arg_def) in raw_input_shapes.Zip(fdef.Signature.InputArg, (x, y) => (x, y)))
26+
{
27+
if(arg_def.Type == DataType.DtResource && arg_def.HandleData is not null && arg_def.HandleData.Count > 0)
28+
{
29+
input_shapes.Add(null);
30+
}
31+
else
32+
{
33+
input_shapes.Add(input_shape);
34+
}
35+
}
36+
}
37+
}
38+
39+
var (graph_def, nested_to_flat_tensor_name) = function_def_to_graph_def(fdef, input_shapes);
40+
41+
func_graph.as_default();
42+
importer.import_graph_def(graph_def, name: "", validate_colocation_constraints: false);
43+
var input_tensor_names = fdef.Signature.InputArg.Select(x => nested_to_flat_tensor_name[x.Name]);
44+
func_graph.Inputs = new Tensors(input_tensor_names.Select(x => func_graph.get_tensor_by_name(x)));
45+
46+
var output_tensor_names = fdef.Signature.OutputArg.Select(x => nested_to_flat_tensor_name[fdef.Ret[x.Name]]);
47+
func_graph.Outputs = new Tensors(output_tensor_names.Select(x => func_graph.get_tensor_by_name(x)));
48+
// TODO(Rinne): func_graph.ControlOutputs
49+
_set_handle_data(func_graph, fdef);
50+
51+
foreach(var node in graph_def.Node)
52+
{
53+
if(node.Attr.TryGetValue("_output_shapes", out var output_shapes))
54+
{
55+
var op = func_graph.get_operation_by_name(node.Name);
56+
foreach(var (output_index, shape) in enumerate(output_shapes.List.Shape.Take(op.outputs.Length)))
57+
{
58+
op.outputs[output_index].shape = new Shape(shape);
59+
}
60+
}
61+
}
62+
Dictionary<long, string> output_names = new();
63+
foreach(var (ret_arg_def, tensor_name) in zip(fdef.Signature.OutputArg, output_tensor_names))
64+
{
65+
output_names[ops.tensor_id(func_graph.get_tensor_by_name(tensor_name))] = ret_arg_def.Name;
66+
}
67+
// TODO(Rinne): func_graph._output_names = output_names
68+
69+
func_graph.Exit();
70+
return func_graph;
71+
}
72+
73+
public static (GraphDef, Dictionary<string, string>) function_def_to_graph_def(FunctionDef fdef, List<TensorShapeProto> input_shapes)
74+
{
75+
var graph_def = new GraphDef()
76+
{
77+
Versions = new VersionDef()
78+
{
79+
Producer = versions.GRAPH_DEF_VERSION,
80+
MinConsumer = versions.GRAPH_DEF_VERSION_MIN_CONSUMER
81+
}
82+
};
83+
84+
var default_graph = ops.get_default_graph();
85+
86+
if(input_shapes is not null && input_shapes.Count > 0 && input_shapes.Count != fdef.Signature.InputArg.Count)
87+
{
88+
throw new ValueError($"Length of `input_shapes` must match the number " +
89+
$"of `input_arg`s in `fdef`. Got {input_shapes.Count} `input_shapes` and " +
90+
$"{fdef.Signature.InputArg.Count} `input_arg`s.");
91+
}
92+
93+
foreach(var (i, arg_def) in enumerate(fdef.Signature.InputArg))
94+
{
95+
NodeDef node_def = new();
96+
node_def.Name = arg_def.Name;
97+
node_def.Op = "Placeholder";
98+
node_def.Attr["dtype"] = new AttrValue()
99+
{
100+
Type = arg_def.Type
101+
};
102+
if(input_shapes is not null && input_shapes.Count > 0 && input_shapes[i] is not null)
103+
{
104+
var input_shape = input_shapes[i];
105+
// skip the condition that input_shape is not `TensorShapeProto`.
106+
AttrValue shape = new AttrValue()
107+
{
108+
Shape = new TensorShapeProto()
109+
};
110+
shape.Shape = new TensorShapeProto(input_shape);
111+
node_def.Attr["shape"] = shape;
112+
}
113+
if (!fdef.ArgAttr.ContainsKey((uint)i))
114+
{
115+
fdef.ArgAttr[(uint)i] = new FunctionDef.Types.ArgAttrs();
116+
}
117+
var arg_attrs = fdef.ArgAttr[(uint)i].Attr;
118+
foreach(var k in arg_attrs.Keys)
119+
{
120+
if(k == "_output_shapes")
121+
{
122+
if (arg_attrs[k].ValueCase == AttrValue.ValueOneofCase.List)
123+
{
124+
node_def.Attr["shape"].Shape = new TensorShapeProto(arg_attrs[k].List.Shape[0]);
125+
}
126+
else if (arg_attrs[k].ValueCase == AttrValue.ValueOneofCase.Shape)
127+
{
128+
node_def.Attr["shape"].Shape = new TensorShapeProto(arg_attrs[k].Shape);
129+
}
130+
}
131+
else if (k.StartsWith("_"))
132+
{
133+
if (!node_def.Attr.ContainsKey(k))
134+
{
135+
node_def.Attr[k] = new AttrValue();
136+
}
137+
node_def.Attr[k] = new AttrValue(arg_attrs[k]);
138+
}
139+
}
140+
141+
graph_def.Node.Add(node_def);
142+
}
143+
144+
graph_def.Node.AddRange(fdef.NodeDef);
145+
146+
Dictionary<string, string> nested_to_flat_tensor_name = new();
147+
foreach(var arg_def in fdef.Signature.InputArg)
148+
{
149+
nested_to_flat_tensor_name[arg_def.Name] = $"{arg_def.Name}:0";
150+
string control_name = "^" + arg_def.Name;
151+
nested_to_flat_tensor_name[control_name] = control_name;
152+
}
153+
154+
foreach(var node_def in fdef.NodeDef)
155+
{
156+
var graph = default_graph;
157+
// TODO(Rinne): The `Graph` lacks `_functions`, needed to be implemented in the future.
158+
while(graph.OuterGraph is not null)
159+
{
160+
graph = graph.OuterGraph;
161+
}
162+
163+
var op_def = default_graph.GetOpDef(node_def.Op);
164+
165+
foreach(var attr in op_def.Attr)
166+
{
167+
if(attr.Type == "func")
168+
{
169+
var fname = node_def.Attr[attr.Name].Func.Name;
170+
if (!is_function(fname))
171+
{
172+
throw new ValueError($"Function {fname} was not found. Please make sure " +
173+
$"the FunctionDef `fdef` is correct.");
174+
}
175+
}
176+
else if(attr.Type == "list(func)")
177+
{
178+
foreach(var fn in node_def.Attr[attr.Name].List.Func)
179+
{
180+
var fname = fn.Name;
181+
if (!is_function(fname))
182+
{
183+
throw new ValueError($"Function {fname} was not found. Please make " +
184+
$"sure the FunctionDef `fdef` is correct.");
185+
}
186+
}
187+
}
188+
}
189+
190+
int flattened_index = 0;
191+
foreach(var arg_def in op_def.OutputArg)
192+
{
193+
var num_args = _get_num_args(arg_def, node_def);
194+
for(int i = 0; i < num_args; i++)
195+
{
196+
var nested_name = $"{node_def.Name}:{arg_def.Name}:{i}";
197+
var flat_name = $"{node_def.Name}:{flattened_index}";
198+
nested_to_flat_tensor_name[nested_name] = flat_name;
199+
flattened_index++;
200+
}
201+
}
202+
string control_name = "^" + node_def.Name;
203+
nested_to_flat_tensor_name[control_name] = control_name;
204+
}
205+
206+
foreach(var node_def in graph_def.Node)
207+
{
208+
for(int i = 0; i < node_def.Input.Count; i++)
209+
{
210+
node_def.Input[i] = nested_to_flat_tensor_name[node_def.Input[i]];
211+
}
212+
}
213+
214+
return (graph_def, nested_to_flat_tensor_name);
215+
}
216+
217+
private static void _set_handle_data(FuncGraph func_graph, FunctionDef fdef)
218+
{
219+
foreach(var (tensor, arg_def) in zip(func_graph.Inputs, fdef.Signature.InputArg).Concat(zip(func_graph.Outputs, fdef.Signature.OutputArg)))
220+
{
221+
if(arg_def.HandleData is not null && arg_def.HandleData.Count > 0)
222+
{
223+
tensor.shape = Shape.Scalar;
224+
225+
var shape_and_type = arg_def.HandleData[0];
226+
var handle_data = new HandleData();
227+
handle_data.IsSet = true;
228+
handle_data.ShapeAndType.Add(new HandleShapeAndType()
229+
{
230+
Shape = shape_and_type.Shape,
231+
Dtype = shape_and_type.Dtype
232+
});
233+
resource_variable_ops._set_handle_shapes_and_types(tensor, handle_data, true);
234+
}
235+
}
236+
}
237+
238+
private static long _get_num_args(OpDef.Types.ArgDef arg_def, NodeDef node_def)
239+
{
240+
if (!string.IsNullOrEmpty(arg_def.NumberAttr))
241+
{
242+
return node_def.Attr[arg_def.NumberAttr].I;
243+
}
244+
else if(!string.IsNullOrEmpty(arg_def.TypeListAttr))
245+
{
246+
return node_def.Attr[arg_def.TypeListAttr].List.Type.Count;
247+
}
248+
else if(arg_def.TypeAttr is not null || arg_def.Type != DataType.DtInvalid)
249+
{
250+
return 1;
251+
}
252+
else
253+
{
254+
throw new ValueError($"Invalid arg_def:\n\n{arg_def}. Please make sure the " +
255+
$"FunctionDef `fdef` is correct.");
256+
}
257+
}
258+
259+
public static bool is_function(string fname)
260+
{
261+
if (tf.Context.executing_eagerly())
262+
{
263+
return tf.Context.has_function(fname);
264+
}
265+
else
266+
{
267+
var graph = ops.get_default_graph();
268+
while(graph is not null)
269+
{
270+
if (graph.IsFunction(fname))
271+
{
272+
return true;
273+
}
274+
if(graph.OuterGraph is not null)
275+
{
276+
graph = graph.OuterGraph;
277+
}
278+
else
279+
{
280+
return false;
281+
}
282+
}
283+
}
284+
throw new ValueError("Unexpected behavior happened in runtime, please submit an issue to " +
285+
"https://github.com/SciSharp/TensorFlow.NET/issues");
286+
}
287+
}
288+
}

0 commit comments

Comments
 (0)