Skip to content

Commit f0cecd7

Browse files
committed
Add NetworkNodes, NodesByDepth and Layers to Functional model.
1 parent e92aa44 commit f0cecd7

21 files changed

Lines changed: 332 additions & 28 deletions

src/TensorFlowNET.Core/Binding.Util.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ limitations under the License.
2222
using System.Diagnostics;
2323
using System.Linq;
2424
using NumSharp.Utilities;
25+
using System.Runtime.CompilerServices;
2526

2627
namespace Tensorflow
2728
{
@@ -50,7 +51,7 @@ public static void add<T>(this IList<T> list, T element)
5051
=> list.Add(element);
5152

5253
public static void append<T>(this IList<T> list, T element)
53-
=> list.Add(element);
54+
=> list.Insert(list.Count, element);
5455

5556
public static T[] concat<T>(this IList<T> list1, IList<T> list2)
5657
{
@@ -407,5 +408,37 @@ public static bool isinstance(object Item1, object tuple)
407408
return true;
408409
return false;
409410
}
411+
412+
public static bool issubset<T>(this IEnumerable<T> subset, IEnumerable<T> src)
413+
{
414+
bool issubset = true;
415+
foreach (var element in subset)
416+
{
417+
if (!src.Contains(element))
418+
{
419+
issubset = false;
420+
continue;
421+
}
422+
}
423+
424+
return true;
425+
}
426+
427+
public static TValue SetDefault<TKey, TValue>(this Dictionary<TKey, TValue> dic, TKey key, TValue value)
428+
{
429+
if (dic.ContainsKey(key))
430+
return dic[key];
431+
432+
dic[key] = value;
433+
return value;
434+
}
435+
436+
public static TValue Get<TKey, TValue>(this Dictionary<TKey, TValue> dic, TKey key, TValue value)
437+
{
438+
if (dic.ContainsKey(key))
439+
return dic[key];
440+
441+
return value;
442+
}
410443
}
411444
}

src/TensorFlowNET.Core/Keras/Engine/Flatten.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public Flatten(FlattenArgs args)
2121
_channels_first = args.DataFormat == "channels_first";
2222
}
2323

24-
protected override Tensors call_fn(Tensors inputs, Tensor state = null, bool is_training = false)
24+
protected override Tensors CallFn(Tensors inputs, Tensor state = null, bool is_training = false)
2525
{
2626
if (_channels_first)
2727
{

src/TensorFlowNET.Core/Keras/Engine/Functional.cs

Lines changed: 247 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Text;
55
using Tensorflow.Keras.ArgsDefinition;
66
using Tensorflow.Keras.Utils;
7+
using static Tensorflow.Binding;
78

89
namespace Tensorflow.Keras.Engine
910
{
@@ -21,6 +22,11 @@ public class Functional : Model
2122
List<Layer> _input_layers;
2223
List<KerasHistory> _input_coordinates;
2324
List<KerasHistory> _output_coordinates;
25+
public string[] NetworkNodes { get; set; }
26+
public Dictionary<int, List<Node>> NodesByDepth { get; set; }
27+
public List<Layer> Layers { get; set; }
28+
Dictionary<int, int> tensor_usage_count;
29+
public Dictionary<int, int> TensorUsageCount => tensor_usage_count;
2430

2531
public Functional(Tensors inputs, Tensors outputs)
2632
: base(new ModelArgs
@@ -33,6 +39,7 @@ public Functional(Tensors inputs, Tensors outputs)
3339
_output_layers = new List<Layer>();
3440
_input_coordinates = new List<KerasHistory>();
3541
_output_coordinates = new List<KerasHistory>();
42+
tensor_usage_count = new Dictionary<int, int>();
3643
_init_graph_network(inputs, outputs);
3744
}
3845

@@ -67,16 +74,253 @@ void _init_graph_network(Tensors inputs, Tensors outputs)
6774
_input_layers.append(layer);
6875
_input_coordinates.append(new KerasHistory(layer, node_index, tensor_index, x));
6976
}
77+
78+
// Keep track of the network's nodes and layers.
79+
var (nodes, nodes_by_depth, layers, _) = MapGraphNetwork(inputs, outputs);
80+
81+
NetworkNodes = nodes;
82+
NodesByDepth = nodes_by_depth;
83+
Layers = layers;
84+
85+
ComputeTensorUsageCount();
7086
}
7187

72-
protected override Tensors call_fn(Tensors inputs, Tensor state = null, bool is_training = false)
88+
void ComputeTensorUsageCount()
7389
{
74-
return run_internal_graph(inputs, state, is_training);
90+
var available_tensors = inputs.Select(x => x.GetHashCode()).ToList();
91+
var depth_keys = NodesByDepth.Keys.Reverse().Skip(1).ToArray();
92+
foreach(var depth in depth_keys)
93+
{
94+
foreach(var node in NodesByDepth[depth])
95+
{
96+
var input_tensors = node.KerasInputs.Select(x => x.GetHashCode()).ToArray();
97+
if (input_tensors.issubset(available_tensors))
98+
{
99+
foreach (var tensor in node.KerasInputs)
100+
{
101+
if (!tensor_usage_count.ContainsKey(tensor.GetHashCode()))
102+
tensor_usage_count[tensor.GetHashCode()] = 0;
103+
tensor_usage_count[tensor.GetHashCode()] += 1;
104+
}
105+
106+
foreach (var output_tensor in node.Outputs)
107+
available_tensors.Add(output_tensor.GetHashCode());
108+
}
109+
}
110+
}
111+
112+
foreach (var tensor in outputs)
113+
{
114+
if (!tensor_usage_count.ContainsKey(tensor.GetHashCode()))
115+
tensor_usage_count[tensor.GetHashCode()] = 0;
116+
tensor_usage_count[tensor.GetHashCode()] += 1;
117+
}
118+
}
119+
120+
/// <summary>
121+
/// Validates a network's topology and gather its layers and nodes.
122+
/// </summary>
123+
/// <param name="inputs"></param>
124+
/// <param name="outputs"></param>
125+
(string[], Dictionary<int, List<Node>>, List<Layer>, Dictionary<int, List<Layer>>) MapGraphNetwork(Tensors inputs, Tensors outputs)
126+
{
127+
var (nodes_in_decreasing_depth, layer_indices) = BuildMap(outputs);
128+
var network_nodes = nodes_in_decreasing_depth
129+
.Select(node => MakeNodeKey(node.Layer.Name, node.Layer.InboundNodes.IndexOf(node)))
130+
.ToArray();
131+
132+
var nodes_depths = new Dictionary<Node, int>();
133+
var layers_depths = new Dictionary<Layer, int>();
134+
135+
nodes_in_decreasing_depth.Reverse();
136+
foreach (var node in nodes_in_decreasing_depth)
137+
{
138+
// If the depth is not set, the node has no outbound nodes (depth 0).
139+
int depth = nodes_depths.SetDefault(node, 0);
140+
// Update the depth of the corresponding layer
141+
int previous_depth = layers_depths.Get(node.Layer, 0);
142+
// If we've seen this layer before at a higher depth,
143+
// we should use that depth instead of the node depth.
144+
// This is necessary for shared layers that have inputs at different
145+
// depth levels in the graph.
146+
depth = Math.Max(depth, previous_depth);
147+
layers_depths[node.Layer] = depth;
148+
nodes_depths[node] = depth;
149+
150+
// Update the depth of inbound nodes.
151+
// The "depth" of a node is the max of the depths
152+
// of all nodes it is connected to + 1.
153+
foreach(var node_dep in node.ParentNodes)
154+
{
155+
previous_depth = nodes_depths.Get(node_dep, 0);
156+
nodes_depths[node_dep] = Math.Max(depth + 1, previous_depth);
157+
}
158+
}
159+
160+
// Handle inputs that are not connected to outputs.
161+
// We do not error out here because the inputs may be used to compute losses
162+
// and metrics.
163+
foreach(var input_t in inputs)
164+
{
165+
var (input_layer, _, _) = input_t.KerasHistory;
166+
if (!layers_depths.ContainsKey(input_layer))
167+
{
168+
layers_depths[input_layer] = 0;
169+
layer_indices[input_layer] = -1;
170+
nodes_depths[input_layer.InboundNodes[0]] = 0;
171+
network_nodes.add(MakeNodeKey(input_layer.Name, 0));
172+
}
173+
}
174+
175+
// Build a dict {depth: list of nodes with this depth}
176+
var nodes_by_depth = new Dictionary<int, List<Node>>();
177+
foreach (var node in nodes_depths)
178+
{
179+
if (!nodes_by_depth.ContainsKey(node.Value))
180+
nodes_by_depth[node.Value] = new List<Node>();
181+
nodes_by_depth[node.Value].append(node.Key);
182+
}
183+
184+
var layers_by_depth = new Dictionary<int, List<Layer>>();
185+
foreach (var layer in layers_depths)
186+
{
187+
if (!layers_by_depth.ContainsKey(layer.Value))
188+
layers_by_depth[layer.Value] = new List<Layer>();
189+
layers_by_depth[layer.Value].append(layer.Key);
190+
}
191+
192+
// Get sorted list of layer depths.
193+
var depth_keys = layers_by_depth.Keys.Reverse();
194+
195+
// Set self.layers ordered by depth.
196+
var layers = new List<Layer>();
197+
foreach(var depth in depth_keys)
198+
{
199+
var layers_for_depth = layers_by_depth[depth];
200+
201+
// Network.layers needs to have a deterministic order:
202+
// here we order them by traversal order.
203+
layers_for_depth.Reverse();
204+
layers.AddRange(layers_for_depth);
205+
}
206+
207+
// Get sorted list of node depths.
208+
depth_keys = nodes_by_depth.Keys.Reverse();
209+
210+
return (network_nodes, nodes_by_depth, layers, layers_by_depth);
75211
}
76212

77-
Tensors run_internal_graph(Tensors inputs, Tensor state = null, bool is_training = false)
213+
string MakeNodeKey(string layer_name, int node_index)
214+
=> $"{layer_name}_ib-{node_index}";
215+
216+
/// <summary>
217+
/// This method topologically sorts nodes in order from inputs to outputs.
218+
/// </summary>
219+
/// <param name="outputs"></param>
220+
(List<Node>, Dictionary<Layer, int>) BuildMap(Tensors outputs)
78221
{
222+
var finished_nodes = new List<Node>();
223+
var nodes_in_progress = new List<Node>();
224+
var nodes_in_decreasing_depth = new List<Node>();
225+
var layer_indices = new Dictionary<Layer, int>();
226+
foreach (var output in outputs)
227+
BuildMapHelper(output,
228+
finished_nodes,
229+
nodes_in_progress,
230+
nodes_in_decreasing_depth,
231+
layer_indices);
232+
233+
return (nodes_in_decreasing_depth, layer_indices);
234+
}
235+
236+
void BuildMapHelper(Tensor tensor,
237+
List<Node> finished_nodes,
238+
List<Node> nodes_in_progress,
239+
List<Node> nodes_in_decreasing_depth,
240+
Dictionary<Layer, int> layer_indices)
241+
{
242+
var (layer, node_index, _) = tensor.KerasHistory;
243+
var node = layer.InboundNodes[node_index];
244+
245+
// Don't repeat work for shared subgraphs
246+
if (finished_nodes.Contains(node))
247+
return;
248+
249+
// Prevent cycles.
250+
if (nodes_in_progress.Contains(node))
251+
throw new ValueError($"The tensor {tensor.name} at layer {layer.Name} is part of a cycle.");
252+
253+
// Store the traversal order for layer sorting.
254+
if (!layer_indices.ContainsKey(layer))
255+
layer_indices[layer] = layer_indices.Count;
256+
257+
// Propagate to all previous tensors connected to this node.
258+
nodes_in_progress.Add(node);
259+
foreach (var k_tensor in node.KerasInputs)
260+
BuildMapHelper(k_tensor,
261+
finished_nodes,
262+
nodes_in_progress,
263+
nodes_in_decreasing_depth,
264+
layer_indices);
265+
266+
finished_nodes.Add(node);
267+
nodes_in_progress.Remove(node);
268+
nodes_in_decreasing_depth.Insert(nodes_in_decreasing_depth.Count, node);
269+
}
270+
271+
protected override Tensors CallFn(Tensors inputs, Tensor state = null, bool is_training = false)
272+
{
273+
return run_internal_graph(inputs, is_training);
274+
}
275+
276+
Tensors run_internal_graph(Tensors inputs, bool training = false, Tensors mask = null)
277+
{
278+
if (mask != null)
279+
{
280+
Tensor[] masks = new Tensor[inputs.Count()];
281+
foreach (var (i, input_t) in enumerate(inputs))
282+
input_t.KerasMask = masks[i];
283+
}
284+
285+
var tensor_dict = new Dictionary<int, Tensor[]>();
286+
foreach (var (x, y) in zip(this.inputs, inputs))
287+
{
288+
var y1 = conform_to_reference_input(y, x);
289+
var x_id = x.GetHashCode();
290+
tensor_dict[x_id] = Enumerable.Range(0, tensor_usage_count[x_id]).Select(x => y1).ToArray();
291+
}
292+
293+
var depth_keys = NodesByDepth.Keys.Reverse().ToArray();
294+
295+
foreach(var depth in depth_keys)
296+
{
297+
var nodes = NodesByDepth[depth];
298+
foreach(var node in nodes)
299+
{
300+
// Input tensors already exist.
301+
if (node.IsInput)
302+
continue;
303+
304+
var layer_inputs = new Tensors(tensor_dict[node.FlatInputIds[0]]);
305+
tensor_dict[node.FlatInputIds[0]] = new Tensor[0];
306+
307+
var outputs = node.Layer.Apply(layer_inputs, is_training: training);
308+
// Update tensor_dict.
309+
foreach (var (x_id, y) in zip(node.FlatOutputIds, outputs))
310+
tensor_dict[x_id] = Enumerable.Range(0, tensor_usage_count[x_id]).Select(x => y).ToArray();
311+
}
312+
}
313+
314+
foreach(var x in outputs)
315+
{
316+
317+
}
79318
throw new NotImplementedException("");
80319
}
320+
321+
Tensor conform_to_reference_input(Tensor tensor, Tensor ref_input)
322+
{
323+
return tensor;
324+
}
81325
}
82326
}

src/TensorFlowNET.Core/Keras/Engine/KerasHistory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ namespace Tensorflow.Keras.Engine
99
/// </summary>
1010
public class KerasHistory
1111
{
12-
public Layer layer;
12+
Layer layer;
1313
int node_index;
1414
int tensor_index;
15-
public Tensor tensor;
15+
Tensor tensor;
1616

1717
public KerasHistory(Layer layer, int node_index, int tensor_index, Tensor tensor)
1818
{

src/TensorFlowNET.Core/Keras/Engine/Layer.Apply.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public Tensors Apply(Tensors inputs, Tensor state = null, bool is_training = fal
4646
if (!built)
4747
MaybeBuild(inputs);
4848

49-
outputs = call_fn(inputs, state: state, is_training: is_training);
49+
outputs = CallFn(inputs, state: state, is_training: is_training);
5050

5151
outputs = _set_connectivity_metadata_(inputs, outputs);
5252
_handle_activity_regularization(inputs, outputs);

src/TensorFlowNET.Core/Keras/Engine/Layer.FunctionalConstructionCall.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Tensors FunctionalConstructionCall(Tensors inputs)
4242
if (!dynamic)
4343
throw new NotImplementedException("");
4444

45-
outputs = call_fn(inputs);
45+
outputs = CallFn(inputs);
4646

4747
outputs = _set_connectivity_metadata_(inputs, outputs);
4848
_handle_activity_regularization(inputs, outputs);

src/TensorFlowNET.Core/Keras/Engine/Layer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ private Tensor compute_mask(Tensor inputs, Tensor mask = null)
162162
/// <param name="state"></param>
163163
/// <param name="is_training"></param>
164164
/// <returns></returns>
165-
protected virtual Tensors call_fn(Tensors inputs, Tensor state = null, bool is_training = false)
165+
protected virtual Tensors CallFn(Tensors inputs, Tensor state = null, bool is_training = false)
166166
{
167167
throw new NotImplementedException("");
168168
}

0 commit comments

Comments
 (0)