ONE - On-device Neural Engine
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
FullyConnectedLayer.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "FullyConnectedLayer.h"
18
19#include "OperationUtils.h"
20
25
26namespace
27{
28
29using namespace onert;
30
31std::unique_ptr<backend::train::Tensor>
32createTransposedTensor(const backend::IPortableTensor *origin_tensor)
33{
34 const auto &origin_shape = origin_tensor->getShape();
35 assert(origin_shape.rank() == 2);
36
37 auto transposed_info = origin_tensor->get_info();
38 auto transposed_shape = ir::Shape{origin_shape.dim(1), origin_shape.dim(0)};
39 transposed_info.shape(transposed_shape);
40
41 return std::make_unique<backend::train::Tensor>(transposed_info);
42}
43
44} // namespace
45
47{
48
50 : cpu::ops::FullyConnectedLayer{}, _grad_weights{nullptr}, _grad_bias{nullptr},
51 _back_prop_input{nullptr}, _back_prop_output{nullptr}, _transposed_weights{nullptr},
52 _transposed_input{nullptr}, _transposed_back_prop_output{nullptr},
53 _act_back_prop_output{nullptr}
54{
55 // DO NOTHING
56}
57
59
61 const IPortableTensor *input, const IPortableTensor *weights, IPortableTensor *output,
62 IPortableTensor *back_prop_input, IPortableTensor *grad_weights, IPortableTensor *grad_bias,
63 const IPortableTensor *back_prop_output, ir::Activation activation,
65{
66 _back_prop_input = back_prop_input;
67 _grad_weights = grad_weights;
68 _grad_bias = grad_bias;
69 _back_prop_output = back_prop_output;
70
71 if (weights_format != ir::FullyConnectedWeightsFormat::Default)
72 throw std::runtime_error{
73 "train FullyConnectedLayer: Weight formats other than default are not supported."};
74
75 if (input->get_info().shape().rank() != 2 || weights->get_info().shape().rank() != 2 ||
76 output->get_info().shape().rank() != 2 || back_prop_input->get_info().shape().rank() != 2 ||
77 grad_weights->get_info().shape().rank() != 2 ||
78 back_prop_output->get_info().shape().rank() != 2)
79 throw std::runtime_error{
80 "train FullyConnectedLayer: Input other ranks than 2 are not supported."};
81
82 _transposed_weights = createTransposedTensor(weights);
83 _transposed_weights->setBuffer(std::make_shared<basic::Allocator>(weights->total_size()));
84
85 _transposed_input = createTransposedTensor(input);
86 _transposed_input->setBuffer(std::make_shared<basic::Allocator>(input->total_size()));
87
88 _transposed_back_prop_output = createTransposedTensor(back_prop_output);
89 _transposed_back_prop_output->setBuffer(
90 std::make_shared<basic::Allocator>(back_prop_output->total_size()));
91
92 if (activation != ir::Activation::NONE)
93 {
94 _act_back_prop_output = std::make_unique<Tensor>(_back_prop_output->get_info());
95 _act_back_prop_output->setBuffer(
96 std::make_shared<basic::Allocator>(_back_prop_output->total_size()));
97 }
98}
99
101
103{
104 const auto data_type = _back_prop_output->data_type();
105 assert(data_type == _input->data_type());
106 switch (data_type)
107 {
108 case OperandType::FLOAT32:
109 {
110 assert(data_type == _grad_weights->data_type());
111 assert(_grad_bias == nullptr || data_type == _grad_bias->data_type());
112 backwardFloat32();
113 break;
114 }
115 default:
116 throw std::runtime_error{"train FullyConnectedLayer: unsupported data type"};
117 }
118}
119
120void FullyConnectedLayer::backwardFloat32()
121{
122 // Calculate gradient for activation
123 const IPortableTensor *backprop_act;
124 try
125 {
126 backprop_act =
127 backpropActivation(_activation, _output, _back_prop_output, _act_back_prop_output.get());
128 }
129 catch (const std::exception &e)
130 {
131 throw std::runtime_error{"train FullyConnectedLayer: " + std::string(e.what())};
132 }
133 assert(backprop_act != nullptr);
134
135 // Initialize TransposeParams
136 nnfw::cker::TransposeParams transpose_param;
137 transpose_param.perm_count = 2;
138 transpose_param.perm[0] = 1;
139 transpose_param.perm[1] = 0;
140
141 // Initialize FullyConnectedParams
143 float output_activation_min = 0;
144 float output_activation_max = 0;
145 CalculateActivationRange(ir::Activation::NONE, &output_activation_min, &output_activation_max);
147 op_params.float_activation_min = output_activation_min;
148 op_params.float_activation_max = output_activation_max;
149 op_params.lhs_cacheable = false;
150 op_params.rhs_cacheable = false;
151
152 // Transpose and compute gradient for input
153 // ∂L/∂X = fc(Incoming gradient, transposed W)
154 auto transposed_weights = _transposed_weights.get();
155 assert(transposed_weights->getShape().rank() == 2);
156 nnfw::cker::Transpose(transpose_param, getShape(_weights), getBuffer<float>(_weights),
157 getShape(transposed_weights), getBuffer<float>(transposed_weights));
158
159 nnfw::cker::FullyConnected(op_params, getShape(backprop_act), getBuffer<float>(backprop_act),
160 getShape(transposed_weights), getBuffer<float>(transposed_weights),
161 getShape(nullptr), nullptr, getShape(_back_prop_input),
162 getBuffer<float>(_back_prop_input));
163
164 // Transpose and compute gradient for weights
165 // ∂L/∂W = fc(transposed incomming gradient, transposed X)
166 auto transposed_input = _transposed_input.get();
167 assert(transposed_input->getShape().rank() == 2);
168 nnfw::cker::Transpose(transpose_param, getShape(_input), getBuffer<float>(_input),
169 getShape(transposed_input), getBuffer<float>(transposed_input));
170
171 auto transposed_back_prop_output = _transposed_back_prop_output.get();
172 assert(transposed_back_prop_output->getShape().rank() == 2);
173 nnfw::cker::Transpose(transpose_param, getShape(backprop_act), getBuffer<float>(backprop_act),
174 getShape(transposed_back_prop_output),
175 getBuffer<float>(transposed_back_prop_output));
176
178 op_params, getShape(transposed_back_prop_output), getBuffer<float>(transposed_back_prop_output),
179 getShape(transposed_input), getBuffer<float>(transposed_input), getShape(nullptr), nullptr,
180 getShape(_grad_weights), getBuffer<float>(_grad_weights));
181
182 // Compute gradient for bias
183 if (_bias)
184 {
185 assert(_grad_bias);
187 getBuffer<float>(backprop_act), getShape(_grad_bias),
188 getBuffer<float>(_grad_bias));
189 }
190}
191
192} // namespace onert::backend::train::ops
A tensor class that is portable for other backends.
size_t total_size() const override final
const ir::OperandInfo & get_info() const
ir::DataType data_type() const override final
ir::Shape getShape() const override final
Get ir::Shape of tensor.
void configureBackward(const IPortableTensor *input, const IPortableTensor *weights, IPortableTensor *output, IPortableTensor *back_prop_input, IPortableTensor *grad_weights, IPortableTensor *grad_bias, const IPortableTensor *back_prop_output, ir::Activation activation, ir::FullyConnectedWeightsFormat weights_format)
const Shape & shape() const
Return tensor shape.
Definition OperandInfo.h:93
void FullyConnectedBiasGrad(const Shape &incomming_shape, const T *incomming_data, const Shape &grad_shape, T *grad_data)
void FullyConnected(const FullyConnectedParams &params, const Shape &input_shape, const float *input_data, const Shape &weights_shape, const float *weights_data, const Shape &, const float *bias_data, const Shape &, float *output_data)
void Transpose(const TransposeParams &unshrunk_params, const Shape &unshrunk_input_shape, const T *input_data, const Shape &unshrunk_output_shape, T *output_data)
Definition Transpose.h:512
const IPortableTensor * backpropActivation(const ir::Activation &activation, const IPortableTensor *output, const IPortableTensor *input_backprop, IPortableTensor *output_backprop)
backpropagate acitvation
nnfw::cker::Shape getShape(const IPortableTensor *tensor)
Get shape of tensor.
FullyConnectedWeightsFormat
FusedActivationFunctionType activation
Definition Types.h:257
int32_t dim(int i) const
Definition Shape.h:85