forked from SciSharp/TensorFlow.NET
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTapeGradientFunctions.cs
More file actions
253 lines (230 loc) · 11.1 KB
/
Copy pathTapeGradientFunctions.cs
File metadata and controls
253 lines (230 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tensorflow.Eager;
using Tensorflow.Gradients;
using Tensorflow.Graphs;
using Tensorflow.NumPy;
using Tensorflow.Operations;
using static Tensorflow.Binding;
using static Tensorflow.tensorflow;
namespace Tensorflow.Functions
{
/// <summary>
/// Caches forward and backward functions compatible with eager gradients.
/// </summary>
public abstract class TapeGradientFunctions
{
protected string FORWARD_FUNCTION_ATTRIBUTE_NAME = "forward_function_name";
protected string BACKWARD_FUNCTION_ATTRIBUTE_NAME = "backward_function_name";
protected string _FORWARD_PREFIX = "__forward_";
protected string _BACKWARD_PREFIX = "__backward_";
protected string _INFERENCE_PREFIX = "__inference_";
protected FuncGraph _func_graph;
protected EagerDefinedFunction _forward;
protected FuncGraph _forward_graph;
protected List<int> _forwardprop_input_indices;
protected List<int> _forwardprop_output_indices;
protected int _num_forwardprop_outputs;
protected int _num_inference_outputs;
protected int _num_outputs;
protected int _num_trainable_inference_outputs;
protected ConcreteFunction _backward;
BackwardFunction _backward_function_wrapper;
public TapeGradientFunctions(FuncGraph func_graph,
bool need_gradients_for_jvps)
{
_func_graph = func_graph;
_forward_graph = null;
_forward = null;
_backward = null;
_num_outputs = func_graph.Outputs.Length;
_forwardprop_output_indices = null;
_num_forwardprop_outputs = 0;
_num_inference_outputs = func_graph.Outputs.Length;
_num_trainable_inference_outputs = func_graph.Outputs.Where(t => backprop_util.IsTrainable(t)).Count();
}
public virtual EagerDefinedFunction Forward(Tensors inference_args, Tensors input_tangents = null)
{
// TODO(Rinne): add input_tangents arg.
if(_forward is null)
{
(_forward, _forward_graph, _backward, _forwardprop_output_indices, _num_forwardprop_outputs)
= ForwardAndBackwardFunctions(inference_args);
}
return _forward;
}
/// <summary>
/// Record the function call operation.
/// </summary>
/// <param name="flat_outputs"></param>
/// <param name="inference_args"></param>
public virtual void Record(Tensors flat_outputs, Tensors inference_args)
{
// TODO(Rinne): add arg `input_tagents`.
var (backward_function, to_record) = _wrap_backward_function(_forward_graph, _backward, flat_outputs);
if(_forwardprop_output_indices is not null && _forwardprop_output_indices.Count > 0)
{
// TODO(Rinne): implement it.
throw new NotImplementedException();
}
tf.Runner.TFE_TapeSetRecordOperation(_forward.Signature.Name, to_record, inference_args, backward_function);
}
/// <summary>
/// Create a backward function given `outputs` from the forward function.
/// </summary>
/// <param name="forward_graph"></param>
/// <param name="backward"></param>
/// <param name="outputs"></param>
/// <returns></returns>
(BackwardFunction, Tensors) _wrap_backward_function(FuncGraph forward_graph, ConcreteFunction backward, Tensors outputs)
{
var capture_mapping = zip(forward_graph.Outputs.Select(t => ops.tensor_id(t)), outputs)
.ToDictionary(x => x.Item1, x => x.Item2);
var captured_inputs = backward.CapturedInputs;
var remapped_captures = captured_inputs.Select(c =>
{
if (capture_mapping.TryGetValue(ops.tensor_id(c), out var value))
{
return value;
}
else
{
return c;
}
}).ToArray();
if(remapped_captures.Where(t => t is not EagerTensor).Any(t => t.graph == forward_graph))
{
var incorrect_mapping = remapped_captures.Where(t => t is not EagerTensor && t.graph != forward_graph);
throw new RuntimeError($"Failed to map all backward graph captures to " +
$"the forward graph. Incorrectly mapped: {string.Join(", ", incorrect_mapping)}");
}
Dictionary<int, Tensor> variant_zeros_like = new Dictionary<int, Tensor>();
var backward_function_inputs = backward.Inputs.Length - backward.CapturedInputs.Length;
var recorded_outputs = new Tensors();
int trainable_recorded_outputs = 0;
var skip_positions = new HashSet<int>();
var relevant_outputs = outputs;
foreach (var (output_index, output) in enumerate(relevant_outputs))
{
if (trainable_recorded_outputs < backward_function_inputs)
recorded_outputs.Add(output);
if (backprop_util.IsTrainable(output))
trainable_recorded_outputs++;
else
skip_positions.Add(output_index);
if (output.dtype == dtypes.variant)
variant_zeros_like[output_index] = default_gradient.zeros_like(output);
}
_backward_function_wrapper = (args, unneeded_gradients) =>
{
if(backward.Outputs is null || backward.Outputs.Length == 0)
{
return backward.FlatStructuredOutputs;
}
var processed_args = new Tensors();
int input_index = 0;
foreach (var (output_index, arg) in enumerate(args))
{
if (skip_positions.Contains(output_index))
continue;
if (arg is null)
{
var input_placeholder = backward.Inputs[input_index];
Tensor variant_arg;
if (input_placeholder.dtype == dtypes.variant)
{
variant_arg = variant_zeros_like[output_index];
}
else
{
var (shape, type) = default_gradient.shape_and_dtype(input_placeholder);
variant_arg = array_ops.zeros(shape, type);
}
processed_args.Add(variant_arg);
}
else
{
processed_args.Add(arg);
}
input_index++;
if (input_index >= backward_function_inputs)
break;
}
tf.Logger.Debug($"Invoke backward function: {backward.Name}");
var gradients = backward.CallFlat(processed_args, remapped_captures);
foreach (var unneeded_gradient_index in unneeded_gradients)
{
var index = Convert.ToInt32(unneeded_gradient_index);
if (gradients.Length <= index)
gradients.Insert(index, null);
}
return gradients;
};
return (_backward_function_wrapper, recorded_outputs);
}
protected (EagerDefinedFunction, FuncGraph, ConcreteFunction, List<int>, int)
BuildFunctionsForOutputs(Tensors outputs, Tensors inference_args)
{
var trainable_outputs = new List<Tensor>();
var trainable_indices = new List<int>();
foreach(var (index, output) in enumerate(outputs))
{
if (backprop_util.IsTrainable(output))
{
trainable_outputs.Add(output);
trainable_indices.Add(index);
}
}
var backwards_graph = new FuncGraph(monomorphic_function_utils._backward_name(_func_graph.Name));
backwards_graph.as_default();
var gradients_wrt_outputs = new List<Tensor>();
foreach (var output in trainable_outputs)
{
var (gradient_shape, gradient_dtype) = default_gradient.shape_and_dtype(output);
var gradient_placeholder = tf.placeholder(gradient_dtype, gradient_shape);
gradients_wrt_outputs.Add(gradient_placeholder);
handle_data_util.copy_handle_data(output, gradient_placeholder);
}
// TODO(Rinne): with ops.device(None)
var gradients_wrt_inputs = gradients_util._GradientsHelper(trainable_outputs.ToArray(),
_func_graph.Inputs,
grad_ys: gradients_wrt_outputs.ToArray(),
src_graph: _func_graph);
var captures_from_forward = backwards_graph.external_captures
.Where(x => x is not EagerTensor && x is not NDArray && x.graph == _func_graph)
.ToArray();
HashSet<Tensor> existing_outputs = new(_func_graph.Outputs);
foreach(var capture in captures_from_forward)
{
if (!existing_outputs.Contains(capture))
{
existing_outputs.Add(capture);
_func_graph.Outputs.Add(capture);
}
}
backwards_graph.Exit();
backwards_graph.Inputs = gradients_wrt_outputs.Concat(backwards_graph.internal_captures).ToArray();
backwards_graph.Outputs.AddRange(gradients_wrt_inputs.Where(x => x is not null));
var (wrapped_forward_function, wrapped_backward_function) =
monomorphic_function_utils._create_forward_backward_with_graph(null, _func_graph, backwards_graph);
//var forward_function_name = $"{_FORWARD_PREFIX}_{_func_graph.FuncName}_{ops.uid()}";
//var backward_function_attr = new Dictionary<string, string>();
//backward_function_attr[FORWARD_FUNCTION_ATTRIBUTE_NAME] = forward_function_name;
//var backward_function = new ConcreteFunction(backwards_graph,
// monomorphic_function_utils._parse_func_attrs(backward_function_attr));
//var forward_function_attr = new Dictionary<string, string>();
//forward_function_attr[BACKWARD_FUNCTION_ATTRIBUTE_NAME] = backward_function.Name;
//var forward_function = new EagerDefinedFunction(forward_function_name, _func_graph,
// _func_graph.Inputs, _func_graph.Outputs,
// monomorphic_function_utils._parse_func_attrs(forward_function_attr));
return (wrapped_forward_function, _func_graph, wrapped_backward_function, null, 0);
}
public virtual (EagerDefinedFunction, FuncGraph, ConcreteFunction, List<int>, int)
ForwardAndBackwardFunctions(Tensors inference_args)
{
throw new NotImplementedException("");
}
}
}