@@ -91,6 +91,9 @@ namespace Tensorflow.Eager | |||
Tensor[] op_outputs) | |||
=> (output_grads, unneeded_gradients) => | |||
{ | |||
if (ops.gradientFunctions[op_name] == null) | |||
return new Tensor[op_inputs.Length]; | |||
var gradients = ops.gradientFunctions[op_name](new EagerOperation | |||
{ | |||
Name = op_name, | |||
@@ -15,5 +15,7 @@ namespace Tensorflow.Gradients | |||
public TapeTensor[] output_tensor_info { get; set; } | |||
public long[] input_tensor_id { get; set; } | |||
public BackwardFunction backward_function { get; set; } | |||
public override string ToString() | |||
=> $"{op_type}, inputs: {string.Join(",", input_tensor_id)}"; | |||
} | |||
} |
@@ -29,12 +29,13 @@ namespace Tensorflow.Gradients | |||
tensor_tape_, | |||
state.op_tape); | |||
while (op_stack.Count > 0) | |||
while (!op_stack.empty()) | |||
{ | |||
var op = op_stack.Dequeue(); | |||
if (!state.op_tape.find(op, out var trace)) | |||
continue; | |||
Console.WriteLine($"ComputeGradient: {state.op_tape[op].op_type}"); | |||
state.op_tape.erase(op); | |||
var out_gradients = new List<Tensor>(trace.output_tensor_info.Length); | |||
@@ -103,7 +104,7 @@ namespace Tensorflow.Gradients | |||
} | |||
else | |||
{ | |||
throw new NotImplementedException(""); | |||
in_gradients = new Tensor[trace.input_tensor_id.Length]; | |||
} | |||
for (int i = 0; i < in_gradients.Length; ++i) | |||
@@ -113,17 +114,18 @@ namespace Tensorflow.Gradients | |||
{ | |||
var unaggregated_grads = gradients[id]; | |||
unaggregated_grads.Add(in_gradients[i]); | |||
if(unaggregated_grads.Count > kMinAggregateCount) | |||
if (unaggregated_grads.Count > kMinAggregateCount) | |||
{ | |||
if(!gradients_size.ContainsKey(id)) | |||
if (!gradients_size.find(id, out var size)) | |||
{ | |||
size = (long)unaggregated_grads[0].size; | |||
gradients_size.emplace(id, size); | |||
} | |||
else | |||
{ | |||
if (unaggregated_grads.Count * size * 4 > kMinAggregateBytes) | |||
{ | |||
throw new NotImplementedException(""); | |||
} | |||
throw new NotImplementedException(""); | |||
} | |||
} | |||
@@ -0,0 +1,16 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace Tensorflow.Keras.ArgsDefinition | |||
{ | |||
public class RMSpropArgs | |||
{ | |||
public float LearningRate { get; set; } = 0.001f; | |||
public float RHO { get; set; } = 0.9f; | |||
public float Momentum { get; set; } = 0.0f; | |||
public float Epsilon { get; set; } = 1e-7f; | |||
public bool Centered { get; set; } = false; | |||
public string Name { get; set; } = "RMSprop"; | |||
} | |||
} |
@@ -23,8 +23,6 @@ namespace Tensorflow.Keras.Engine | |||
List<KerasHistory> _input_coordinates; | |||
List<KerasHistory> _output_coordinates; | |||
public string[] NetworkNodes { get; set; } | |||
public Dictionary<int, List<Node>> NodesByDepth { get; set; } | |||
public List<Layer> Layers => _layers; | |||
Dictionary<int, int> tensor_usage_count; | |||
public Dictionary<int, int> TensorUsageCount => tensor_usage_count; | |||
@@ -43,9 +41,10 @@ namespace Tensorflow.Keras.Engine | |||
} | |||
} | |||
public Functional(Tensors inputs, Tensors outputs) | |||
public Functional(Tensors inputs, Tensors outputs, string name = null) | |||
: base(new ModelArgs | |||
{ | |||
Name = name, | |||
Inputs = inputs, | |||
Outputs = outputs | |||
}) | |||
@@ -10,7 +10,7 @@ namespace Tensorflow.Keras.Engine | |||
/// <summary> | |||
/// `Model` groups layers into an object with training and inference features. | |||
/// </summary> | |||
public class Model : Layer | |||
public partial class Model : Layer | |||
{ | |||
#pragma warning disable CS0169 // The field 'Model._cloning' is never used | |||
bool _cloning; | |||
@@ -33,12 +33,20 @@ namespace Tensorflow.Keras.Engine | |||
} | |||
public void compile(ILossFunc loss, OptimizerV2 optimizer, string[] metrics) | |||
{ | |||
} | |||
public void compile(string optimizerName, string lossName) | |||
{ | |||
switch (optimizerName) | |||
{ | |||
case "rmsprop": | |||
optimizer = new RMSprop(); | |||
optimizer = new RMSprop(new RMSpropArgs | |||
{ | |||
}); | |||
break; | |||
} | |||
@@ -30,7 +30,7 @@ namespace Tensorflow.Keras.Engine | |||
/// Each time the output of a layer is used by another layer, | |||
/// a node is added to `layer._outbound_nodes`. | |||
/// </summary> | |||
public class Node | |||
public partial class Node | |||
{ | |||
NodeArgs args; | |||
@@ -39,8 +39,8 @@ namespace Tensorflow | |||
/// <param name="input"></param> | |||
/// <param name="output"></param> | |||
/// <returns></returns> | |||
public Functional Model(Tensors inputs, Tensors outputs) | |||
=> new Functional(inputs, outputs); | |||
public Functional Model(Tensors inputs, Tensors outputs, string name = null) | |||
=> new Functional(inputs, outputs, name: name); | |||
/// <summary> | |||
/// Instantiate a Keras tensor. | |||
@@ -1,6 +1,7 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using Tensorflow.Keras.ArgsDefinition; | |||
namespace Tensorflow.Keras.Optimizers | |||
{ | |||
@@ -29,5 +30,31 @@ namespace Tensorflow.Keras.Optimizers | |||
epsilon: epsilon, | |||
amsgrad: amsgrad, | |||
name: name); | |||
/// <summary> | |||
/// Construct a new RMSprop optimizer. | |||
/// </summary> | |||
/// <param name="learning_rate"></param> | |||
/// <param name="rho"></param> | |||
/// <param name="momentum"></param> | |||
/// <param name="epsilon"></param> | |||
/// <param name="centered"></param> | |||
/// <param name="name"></param> | |||
/// <returns></returns> | |||
public OptimizerV2 RMSprop(float learning_rate = 0.001f, | |||
float rho = 0.9f, | |||
float momentum = 0.0f, | |||
float epsilon = 1e-7f, | |||
bool centered = false, | |||
string name = "RMSprop") | |||
=> new RMSprop(new RMSpropArgs | |||
{ | |||
LearningRate = learning_rate, | |||
RHO = rho, | |||
Momentum = momentum, | |||
Epsilon = epsilon, | |||
Centered = centered, | |||
Name = name | |||
}); | |||
} | |||
} |
@@ -1,6 +1,7 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using Tensorflow.Keras.ArgsDefinition; | |||
namespace Tensorflow.Keras.Optimizers | |||
{ | |||
@@ -9,6 +10,11 @@ namespace Tensorflow.Keras.Optimizers | |||
/// </summary> | |||
public class RMSprop : OptimizerV2 | |||
{ | |||
RMSpropArgs args; | |||
public RMSprop(RMSpropArgs args) | |||
{ | |||
this.args = args; | |||
} | |||
} | |||
} |
@@ -0,0 +1,193 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using static Tensorflow.Binding; | |||
using Tensorflow.Keras.Engine; | |||
using NumSharp; | |||
using System.Security.Cryptography; | |||
namespace Tensorflow.Keras.Utils | |||
{ | |||
internal class layer_utils | |||
{ | |||
public static void print_summary(Model model, int line_length = -1, float[] positions = null) | |||
{ | |||
bool sequential_like = model is Sequential; | |||
// || model.IsGraphNetwork; | |||
if (!sequential_like) | |||
{ | |||
sequential_like = true; | |||
var nodes = new List<Node>(); | |||
foreach (var v in model.NodesByDepth) | |||
{ | |||
// if the model has multiple nodes | |||
// or if the nodes have multiple inbound_layers | |||
// the model is no longer sequential | |||
if (v.Value.Count > 1 || (v.Value.Count == 1 && v.Value[0].KerasInputs.Count > 1)) | |||
{ | |||
sequential_like = false; | |||
break; | |||
} | |||
nodes.AddRange(v.Value); | |||
} | |||
if (sequential_like) | |||
{ | |||
// search for shared layers | |||
foreach(var layer in model.Layers) | |||
{ | |||
var flag = false; | |||
foreach(var node in layer.InboundNodes) | |||
{ | |||
if(nodes.Contains(node)) | |||
{ | |||
if (flag) | |||
{ | |||
sequential_like = false; | |||
break; | |||
} | |||
else | |||
flag = true; | |||
} | |||
} | |||
if (!sequential_like) | |||
break; | |||
} | |||
} | |||
} | |||
string[] to_display; | |||
var relevant_nodes = new List<Node>(); | |||
if (sequential_like) | |||
{ | |||
if (line_length < 0) | |||
line_length = 65; | |||
if (positions == null) | |||
positions = new[] { 0.45f, 0.85f, 1.0f }; | |||
if (positions[^1] <= 1) | |||
positions = positions.Select(p => line_length * p).ToArray(); | |||
to_display = new[] { "Layer (type)", "Output Shape", "Param #" }; | |||
} | |||
else | |||
{ | |||
if (line_length < 0) | |||
line_length = 98; | |||
if (positions == null) | |||
positions = new[] { 0.33f, 0.55f, 0.67f, 1.0f }; | |||
if (positions[^1] <= 1) | |||
positions = positions.Select(p => line_length * p).ToArray(); | |||
to_display = new[] { "Layer (type)", "Output Shape", "Param #", "Connected to" }; | |||
foreach (var v in model.NodesByDepth) | |||
relevant_nodes.AddRange(v.Value); | |||
} | |||
int[] positions_int = positions.Select(x => Convert.ToInt32(x)).ToArray(); | |||
print($"Model: {model.Name}"); | |||
print(string.Join("", range(line_length).Select(x => "_"))); | |||
print_row(to_display, positions_int); | |||
print(string.Join("", range(line_length).Select(x => "="))); | |||
foreach(var (i, layer) in enumerate(model.Layers)) | |||
{ | |||
if (sequential_like) | |||
print_layer_summary(layer, positions_int); | |||
else | |||
print_layer_summary_with_connections(layer, positions_int, relevant_nodes); | |||
if(i == model.Layers.Count - 1) | |||
print(string.Join("", range(line_length).Select(x => "="))); | |||
else | |||
print(string.Join("", range(line_length).Select(x => "_"))); | |||
} | |||
var trainable_count = count_params(model, model.trainable_variables); | |||
var non_trainable_count = count_params(model, model.non_trainable_variables); | |||
print($"Total params: {trainable_count + non_trainable_count}"); | |||
print($"Trainable params: {trainable_count}"); | |||
print($"Non-trainable params: {non_trainable_count}"); | |||
print(string.Join("", range(line_length).Select(x => "_"))); | |||
} | |||
static void print_row(string[] fields, int[] positions) | |||
{ | |||
var line = ""; | |||
foreach(var i in range(fields.Length)) | |||
{ | |||
if (i > 0) | |||
line = line[0..^1] + " "; | |||
line += fields[i]; | |||
line = string.Join("", line.Take(positions[i])); | |||
line += string.Join("", range(positions[i] - len(line)).Select(x => " ")); | |||
} | |||
print(line); | |||
} | |||
/// <summary> | |||
/// Prints a summary for a single layer. | |||
/// </summary> | |||
/// <param name="layer"></param> | |||
static void print_layer_summary(Layer layer, int[] positions) | |||
{ | |||
var name = layer.Name; | |||
var fields = new string[] | |||
{ | |||
$"{name} ({layer.GetType().Name})", | |||
$"{layer.output_shape}", | |||
$"{layer.count_params()}" | |||
}; | |||
print_row(fields, positions); | |||
} | |||
static void print_layer_summary_with_connections(Layer layer, int[] positions, List<Node> relevant_nodes) | |||
{ | |||
var connections = new List<string>(); | |||
foreach (var node in layer.InboundNodes) | |||
{ | |||
if (!relevant_nodes.Contains(node)) | |||
continue; | |||
foreach (var (inbound_layer, node_index, tensor_index, _) in node.iterate_inbound()) | |||
connections.append($"{inbound_layer.Name}[{node_index}][{tensor_index}]"); | |||
} | |||
var name = layer.Name; | |||
string first_connection = ""; | |||
if (connections.Count > 0) | |||
first_connection = connections[0]; | |||
var fields = new string[] | |||
{ | |||
$"{name}({layer.GetType().Name})", | |||
$"{layer.output_shape}", | |||
$"{layer.count_params()}", | |||
first_connection | |||
}; | |||
print_row(fields, positions); | |||
if(connections.Count > 1) | |||
{ | |||
foreach(var i in range(1, connections.Count)) | |||
{ | |||
fields = new string[] { "", "", "", connections[i] }; | |||
print_row(fields, positions); | |||
} | |||
} | |||
} | |||
public static int count_params(Layer layer, List<IVariableV1> weights) | |||
{ | |||
var weight_shapes = weights.Select(x => x.shape).ToArray(); | |||
var total = weight_shapes.Select(p => (int)np.prod(p.dims)).Sum(); | |||
return total; | |||
} | |||
} | |||
} |