ONE - On-device Neural Engine
Loading...
Searching...
No Matches
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
46namespace onert
47{
48namespace backend
49{
50namespace train
51{
52namespace ops
53{
54
56 : cpu::ops::FullyConnectedLayer{}, _grad_weights{nullptr}, _grad_bias{nullptr},
57 _back_prop_input{nullptr}, _back_prop_output{nullptr}, _transposed_weights{nullptr},
58 _transposed_input{nullptr}, _transposed_back_prop_output{nullptr},
59 _act_back_prop_output{nullptr}
60{
61 // DO NOTHING
62}
63
65
67 const IPortableTensor *input, const IPortableTensor *weights, IPortableTensor *output,
68 IPortableTensor *back_prop_input, IPortableTensor *grad_weights, IPortableTensor *grad_bias,
69 const IPortableTensor *back_prop_output, ir::Activation activation,
71{
72 _back_prop_input = back_prop_input;
73 _grad_weights = grad_weights;
74 _grad_bias = grad_bias;
75 _back_prop_output = back_prop_output;
76
77 if (weights_format != ir::FullyConnectedWeightsFormat::Default)
78 throw std::runtime_error{
79 "train FullyConnectedLayer: Weight formats other than default are not supported."};
80
81 if (input->get_info().shape().rank() != 2 || weights->get_info().shape().rank() != 2 ||
82 output->get_info().shape().rank() != 2 || back_prop_input->get_info().shape().rank() != 2 ||
83 grad_weights->get_info().shape().rank() != 2 ||
84 back_prop_output->get_info().shape().rank() != 2)
85 throw std::runtime_error{
86 "train FullyConnectedLayer: Input other ranks than 2 are not supported."};
87
88 _transposed_weights = createTransposedTensor(weights);
89 _transposed_weights->setBuffer(std::make_shared<basic::Allocator>(weights->total_size()));
90
91 _transposed_input = createTransposedTensor(input);
92 _transposed_input->setBuffer(std::make_shared<basic::Allocator>(input->total_size()));
93
94 _transposed_back_prop_output = createTransposedTensor(back_prop_output);
95 _transposed_back_prop_output->setBuffer(
96 std::make_shared<basic::Allocator>(back_prop_output->total_size()));
97
98 if (activation != ir::Activation::NONE)
99 {
100 _act_back_prop_output = std::make_unique<Tensor>(_back_prop_output->get_info());
101 _act_back_prop_output->setBuffer(
102 std::make_shared<basic::Allocator>(_back_prop_output->total_size()));
103 }
104}
105
107
109{
110 const auto data_type = _back_prop_output->data_type();
111 assert(data_type == _input->data_type());
112 switch (data_type)
113 {
114 case OperandType::FLOAT32:
115 {
116 assert(data_type == _grad_weights->data_type());
117 assert(_grad_bias == nullptr || data_type == _grad_bias->data_type());
118 backwardFloat32();
119 break;
120 }
121 default:
122 throw std::runtime_error{"train FullyConnectedLayer: unsupported data type"};
123 }
124}
125
126void FullyConnectedLayer::backwardFloat32()
127{
128 // Calculate gradient for activation
129 const IPortableTensor *backprop_act;
130 try
131 {
132 backprop_act =
133 backpropActivation(_activation, _output, _back_prop_output, _act_back_prop_output.get());
134 }
135 catch (const std::exception &e)
136 {
137 throw std::runtime_error{"train FullyConnectedLayer: " + std::string(e.what())};
138 }
139 assert(backprop_act != nullptr);
140
141 // Initialize TransposeParams
142 nnfw::cker::TransposeParams transpose_param;
143 transpose_param.perm_count = 2;
144 transpose_param.perm[0] = 1;
145 transpose_param.perm[1] = 0;
146
147 // Initialize FullyConnectedParams
149 float output_activation_min = 0;
150 float output_activation_max = 0;
151 CalculateActivationRange(ir::Activation::NONE, &output_activation_min, &output_activation_max);
153 op_params.float_activation_min = output_activation_min;
154 op_params.float_activation_max = output_activation_max;
155 op_params.lhs_cacheable = false;
156 op_params.rhs_cacheable = false;
157
158 // Transpose and compute gradient for input
159 // ∂L/∂X = fc(Incoming gradient, transposed W)
160 auto transposed_weights = _transposed_weights.get();
161 assert(transposed_weights->getShape().rank() == 2);
162 nnfw::cker::Transpose(transpose_param, getShape(_weights), getBuffer<float>(_weights),
163 getShape(transposed_weights), getBuffer<float>(transposed_weights));
164
165 nnfw::cker::FullyConnected(op_params, getShape(backprop_act), getBuffer<float>(backprop_act),
166 getShape(transposed_weights), getBuffer<float>(transposed_weights),
167 getShape(nullptr), nullptr, getShape(_back_prop_input),
168 getBuffer<float>(_back_prop_input));
169
170 // Transpose and compute gradient for weights
171 // ∂L/∂W = fc(transposed incomming gradient, transposed X)
172 auto transposed_input = _transposed_input.get();
173 assert(transposed_input->getShape().rank() == 2);
174 nnfw::cker::Transpose(transpose_param, getShape(_input), getBuffer<float>(_input),
175 getShape(transposed_input), getBuffer<float>(transposed_input));
176
177 auto transposed_back_prop_output = _transposed_back_prop_output.get();
178 assert(transposed_back_prop_output->getShape().rank() == 2);
179 nnfw::cker::Transpose(transpose_param, getShape(backprop_act), getBuffer<float>(backprop_act),
180 getShape(transposed_back_prop_output),
181 getBuffer<float>(transposed_back_prop_output));
182
184 op_params, getShape(transposed_back_prop_output), getBuffer<float>(transposed_back_prop_output),
185 getShape(transposed_input), getBuffer<float>(transposed_input), getShape(nullptr), nullptr,
186 getShape(_grad_weights), getBuffer<float>(_grad_weights));
187
188 // Compute gradient for bias
189 if (_bias)
190 {
191 assert(_grad_bias);
193 getBuffer<float>(backprop_act), getShape(_grad_bias),
194 getBuffer<float>(_grad_bias));
195 }
196}
197
198} // namespace ops
199} // namespace train
200} // namespace backend
201} // namespace onert
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:95
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:509
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:86