forked from graphcore/tutorials
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathproduct.cpp
More file actions
102 lines (86 loc) · 3.21 KB
/
Copy pathproduct.cpp
File metadata and controls
102 lines (86 loc) · 3.21 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
// Copyright (c) 2020 Graphcore Ltd. All rights reserved.
#include <poplar/Graph.hpp>
#include <poplin/MatMul.hpp>
#include <popops/ElementWise.hpp>
#include <poputil/exceptions.hpp>
/// Check the Targeting the IPU from TensorFlow document for
/// the API level required for the version of the Poplar SDK that you are using.
extern "C" {
int32_t custom_op_api_level = 5;
}
/// Set the properties of the forward op.
extern "C"
void Build_metadata(
std::vector<std::int64_t>& allocating_indices,
std::vector<std::int64_t>& replica_identical_output_indices,
std::map<std::int64_t, std::int64_t>& input_to_output_tensor_aliasing,
bool& is_elementwise,
bool& is_stateless,
bool& is_hashable,
std::uint32_t num_inputs) {
// The forward op is just a function of its inputs (no internal state)
// so it can be marked as stateless.
is_stateless = true;
}
/// Define the forward op
extern "C" poplar::program::Program Build(
poplar::Graph& graph, const std::vector<poplar::Tensor>& inputs,
std::vector<poplar::Tensor>& outputs, const std::string& debug_prefix) {
if (inputs.size() != 2) {
throw poputil::poplibs_error("product requires 2 inputs.");
}
auto input = inputs[0];
auto weights = inputs[1];
if (input.rank() != 2 && weights.rank() != 2) {
throw poputil::poplibs_error("Both inputs must be matrices.");
}
if (input.dim(1) != weights.dim(0)) {
throw poputil::poplibs_error("Product shapes incompatible.");
}
poplar::program::Sequence prog;
auto result = poplin::matMul(graph, input, weights, prog,
debug_prefix + "/product");
outputs.push_back(result);
return prog;
}
/// The gradient op requires its own metadata. Since it does not have any
/// internal state we can mark the op as stateless.
/// For stateless ops only one instance of the op is compiled even when
/// we ask for the gradient multiple times (e.g. we use tf.gradients() in
/// the python code).
extern "C"
void Build_grad_metadata(
std::vector<std::int64_t>& allocating_indices,
std::vector<std::int64_t>& replica_identical_output_indices,
std::map<std::int64_t, std::int64_t>& input_to_output_tensor_aliasing,
bool& is_elementwise,
bool& is_stateless,
bool& is_hashable,
std::uint32_t num_inputs) {
is_stateless = true;
}
/// Define the gradient op.
extern "C"
poplar::program::Program Build_grad(
poplar::Graph& graph, int input_grad_index,
const std::vector<poplar::Tensor>& gradients,
const std::vector<poplar::Tensor>& fwd_inputs,
const std::vector<poplar::Tensor>& fwd_outputs,
std::vector<poplar::Tensor>& outputs,
const std::string& debug_prefix) {
poplar::program::Sequence prog;
auto inputsTransposed = fwd_inputs[0].dimShuffle({1, 0});
auto weightsTransposed = fwd_inputs[1].dimShuffle({1, 0});
auto gradOfLossWrtWeights =
poplin::matMul(graph, inputsTransposed, gradients[0],
prog, debug_prefix + "/dLdW");
auto gradOfLossWrtInput =
popops::mul(graph,
gradients[0].broadcast(fwd_inputs[1].dim(0), 1),
weightsTransposed.broadcast(gradients[0].dim(0), 0),
prog,
debug_prefix + "/dLdX");
outputs.push_back(gradOfLossWrtInput);
outputs.push_back(gradOfLossWrtWeights);
return prog;
}