| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent e92aa44 commit f0cecd7
21 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,6 +22,7 @@ limitations under the License. | |||
| 22 | 22 | using System.Diagnostics; | |
| 23 | 23 | using System.Linq; | |
| 24 | 24 | using NumSharp.Utilities; | |
| 25 | + using System.Runtime.CompilerServices; | ||
| 25 | 26 | ||
| 26 | 27 | namespace Tensorflow | |
| 27 | 28 | { | |
@@ -50,7 +51,7 @@ public static void add<T>(this IList<T> list, T element) | |||
| 50 | 51 | => list.Add(element); | |
| 51 | 52 | ||
| 52 | 53 | public static void append<T>(this IList<T> list, T element) | |
| 53 | - => list.Add(element); | ||
| 54 | + => list.Insert(list.Count, element); | ||
| 54 | 55 | ||
| 55 | 56 | public static T[] concat<T>(this IList<T> list1, IList<T> list2) | |
| 56 | 57 | { | |
@@ -407,5 +408,37 @@ public static bool isinstance(object Item1, object tuple) | |||
| 407 | 408 | return true; | |
| 408 | 409 | return false; | |
| 409 | 410 | } | |
| 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 | + } | ||
| 410 | 443 | } | |
| 411 | 444 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -21,7 +21,7 @@ public Flatten(FlattenArgs args) | |||
| 21 | 21 | _channels_first = args.DataFormat == "channels_first"; | |
| 22 | 22 | } | |
| 23 | 23 | ||
| 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) | ||
| 25 | 25 | { | |
| 26 | 26 | if (_channels_first) | |
| 27 | 27 | { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,6 +4,7 @@ | |||
| 4 | 4 | using System.Text; | |
| 5 | 5 | using Tensorflow.Keras.ArgsDefinition; | |
| 6 | 6 | using Tensorflow.Keras.Utils; | |
| 7 | + using static Tensorflow.Binding; | ||
| 7 | 8 | ||
| 8 | 9 | namespace Tensorflow.Keras.Engine | |
| 9 | 10 | { | |
@@ -21,6 +22,11 @@ public class Functional : Model | |||
| 21 | 22 | List<Layer> _input_layers; | |
| 22 | 23 | List<KerasHistory> _input_coordinates; | |
| 23 | 24 | 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; | ||
| 24 | 30 | ||
| 25 | 31 | public Functional(Tensors inputs, Tensors outputs) | |
| 26 | 32 | : base(new ModelArgs | |
@@ -33,6 +39,7 @@ public Functional(Tensors inputs, Tensors outputs) | |||
| 33 | 39 | _output_layers = new List<Layer>(); | |
| 34 | 40 | _input_coordinates = new List<KerasHistory>(); | |
| 35 | 41 | _output_coordinates = new List<KerasHistory>(); | |
| 42 | + tensor_usage_count = new Dictionary<int, int>(); | ||
| 36 | 43 | _init_graph_network(inputs, outputs); | |
| 37 | 44 | } | |
| 38 | 45 | ||
@@ -67,16 +74,253 @@ void _init_graph_network(Tensors inputs, Tensors outputs) | |||
| 67 | 74 | _input_layers.append(layer); | |
| 68 | 75 | _input_coordinates.append(new KerasHistory(layer, node_index, tensor_index, x)); | |
| 69 | 76 | } | |
| 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(); | ||
| 70 | 86 | } | |
| 71 | 87 | ||
| 72 | - protected override Tensors call_fn(Tensors inputs, Tensor state = null, bool is_training = false) | ||
| 88 | + void ComputeTensorUsageCount() | ||
| 73 | 89 | { | |
| 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); | ||
| 75 | 211 | } | |
| 76 | 212 | ||
| 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) | ||
| 78 | 221 | { | |
| 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 | + } | ||
| 79 | 318 | throw new NotImplementedException(""); | |
| 80 | 319 | } | |
| 320 | + | ||
| 321 | + Tensor conform_to_reference_input(Tensor tensor, Tensor ref_input) | ||
| 322 | + { | ||
| 323 | + return tensor; | ||
| 324 | + } | ||
| 81 | 325 | } | |
| 82 | 326 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,10 +9,10 @@ namespace Tensorflow.Keras.Engine | |||
| 9 | 9 | /// </summary> | |
| 10 | 10 | public class KerasHistory | |
| 11 | 11 | { | |
| 12 | - public Layer layer; | ||
| 12 | + Layer layer; | ||
| 13 | 13 | int node_index; | |
| 14 | 14 | int tensor_index; | |
| 15 | - public Tensor tensor; | ||
| 15 | + Tensor tensor; | ||
| 16 | 16 | ||
| 17 | 17 | public KerasHistory(Layer layer, int node_index, int tensor_index, Tensor tensor) | |
| 18 | 18 | { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -46,7 +46,7 @@ public Tensors Apply(Tensors inputs, Tensor state = null, bool is_training = fal | |||
| 46 | 46 | if (!built) | |
| 47 | 47 | MaybeBuild(inputs); | |
| 48 | 48 | ||
| 49 | - outputs = call_fn(inputs, state: state, is_training: is_training); | ||
| 49 | + outputs = CallFn(inputs, state: state, is_training: is_training); | ||
| 50 | 50 | ||
| 51 | 51 | outputs = _set_connectivity_metadata_(inputs, outputs); | |
| 52 | 52 | _handle_activity_regularization(inputs, outputs); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -42,7 +42,7 @@ Tensors FunctionalConstructionCall(Tensors inputs) | |||
| 42 | 42 | if (!dynamic) | |
| 43 | 43 | throw new NotImplementedException(""); | |
| 44 | 44 | ||
| 45 | - outputs = call_fn(inputs); | ||
| 45 | + outputs = CallFn(inputs); | ||
| 46 | 46 | ||
| 47 | 47 | outputs = _set_connectivity_metadata_(inputs, outputs); | |
| 48 | 48 | _handle_activity_regularization(inputs, outputs); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -162,7 +162,7 @@ private Tensor compute_mask(Tensor inputs, Tensor mask = null) | |||
| 162 | 162 | /// <param name="state"></param> | |
| 163 | 163 | /// <param name="is_training"></param> | |
| 164 | 164 | /// <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) | ||
| 166 | 166 | { | |
| 167 | 167 | throw new NotImplementedException(""); | |
| 168 | 168 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments