2 * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "FullyConnectedLayer.h"
19 #include "OperationUtils.h"
21 #include <cker/operation/FullyConnected.h>
22 #include <cker/operation/Transpose.h>
23 #include <cker/train/operation/FullyConnected.h>
24 #include <cker/train/operation/ReLU.h>
29 using namespace onert;
31 std::unique_ptr<backend::train::Tensor>
32 createTransposedTensor(const backend::IPortableTensor *origin_tensor)
34 const auto &origin_shape = origin_tensor->getShape();
35 assert(origin_shape.rank() == 2);
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);
41 return std::make_unique<backend::train::Tensor>(transposed_info, origin_tensor->layout());
55 FullyConnectedLayer::FullyConnectedLayer()
56 : cpu::ops::FullyConnectedLayer{}, _grad_weights{nullptr}, _grad_bias{nullptr},
57 _deriv_input{nullptr}, _deriv_output{nullptr}, _transposed_weights{nullptr},
58 _transposed_input{nullptr}, _transposed_deriv_output{nullptr}, _act_deriv_output{nullptr}
63 FullyConnectedLayer::~FullyConnectedLayer() = default;
65 void FullyConnectedLayer::configure(const IPortableTensor *input, const IPortableTensor *weights,
66 const IPortableTensor *bias, IPortableTensor *output,
67 IPortableTensor *deriv_input, IPortableTensor *grad_weights,
68 IPortableTensor *grad_bias, const IPortableTensor *deriv_output,
69 ir::Activation activation,
70 ir::FullyConnectedWeightsFormat weights_format,
71 const std::shared_ptr<train::ExternalContext> &external_context)
73 cpu::ops::FullyConnectedLayer::configure(input, weights, bias, activation, weights_format, output,
76 _deriv_input = deriv_input;
77 _grad_weights = grad_weights;
78 _grad_bias = grad_bias;
79 _deriv_output = deriv_output;
81 if (weights_format != ir::FullyConnectedWeightsFormat::Default)
82 throw std::runtime_error{
83 "train FullyConnectedLayer: Weight formats other than default are not supported."};
85 if (input->get_info().shape().rank() != 2 || weights->get_info().shape().rank() != 2 ||
86 output->get_info().shape().rank() != 2 || deriv_input->get_info().shape().rank() != 2 ||
87 grad_weights->get_info().shape().rank() != 2 || deriv_output->get_info().shape().rank() != 2)
88 throw std::runtime_error{
89 "train FullyConnectedLayer: Input other ranks than 2 are not supported."};
91 _transposed_weights = createTransposedTensor(weights);
92 _transposed_weights->setBuffer(std::make_shared<basic::Allocator>(weights->total_size()));
94 _transposed_input = createTransposedTensor(input);
95 _transposed_input->setBuffer(std::make_shared<basic::Allocator>(input->total_size()));
97 _transposed_deriv_output = createTransposedTensor(deriv_output);
98 _transposed_deriv_output->setBuffer(
99 std::make_shared<basic::Allocator>(deriv_output->total_size()));
101 if (activation != ir::Activation::NONE)
104 std::make_unique<Tensor>(_deriv_output->get_info(), _deriv_output->layout());
105 _act_deriv_output->setBuffer(std::make_shared<basic::Allocator>(_deriv_output->total_size()));
109 void FullyConnectedLayer::forward(bool) { cpu::ops::FullyConnectedLayer::run(); }
111 void FullyConnectedLayer::backward()
113 const auto data_type = _deriv_output->data_type();
114 assert(data_type == _input->data_type());
117 case OperandType::FLOAT32:
119 assert(data_type == _grad_weights->data_type());
120 assert(data_type == _grad_bias->data_type());
125 throw std::runtime_error{"train FullyConnectedLayer: unsupported data type"};
129 void FullyConnectedLayer::backwardFloat32()
131 // Calculate gradient for activation
132 const IPortableTensor *backprop_act;
135 case ir::Activation::NONE:
136 backprop_act = _deriv_output;
138 case ir::Activation::RELU:
139 nnfw::cker::train::ReLUGrad(getShape(_output), getBuffer<float>(_output),
140 getShape(_deriv_output), getBuffer<float>(_deriv_output),
141 getShape(_act_deriv_output.get()),
142 getBuffer<float>(_act_deriv_output.get()));
143 backprop_act = _act_deriv_output.get();
146 throw std::runtime_error("train FullyConnectedLayer: Unsupported activation type yet");
149 // Initialize TransposeParams
150 nnfw::cker::TransposeParams transpose_param;
151 transpose_param.perm_count = 2;
152 transpose_param.perm[0] = 1;
153 transpose_param.perm[1] = 0;
155 // Initialize FullyConnectedParams
156 nnfw::cker::FullyConnectedParams op_params;
157 float output_activation_min = 0;
158 float output_activation_max = 0;
159 CalculateActivationRange(ir::Activation::NONE, &output_activation_min, &output_activation_max);
160 op_params.activation = nnfw::cker::FusedActivationFunctionType::kNone;
161 op_params.float_activation_min = output_activation_min;
162 op_params.float_activation_max = output_activation_max;
163 op_params.lhs_cacheable = false;
164 op_params.rhs_cacheable = false;
166 // Transpose and compute gradient for input
167 // ∂L/∂X = fc(Incoming gradient, transposed W)
168 auto transposed_weights = _transposed_weights.get();
169 assert(transposed_weights->getShape().rank() == 2);
170 nnfw::cker::Transpose(transpose_param, getShape(_weights), getBuffer<float>(_weights),
171 getShape(transposed_weights), getBuffer<float>(transposed_weights));
173 nnfw::cker::FullyConnected(op_params, getShape(backprop_act), getBuffer<float>(backprop_act),
174 getShape(transposed_weights), getBuffer<float>(transposed_weights),
175 getShape(nullptr), nullptr, getShape(_deriv_input),
176 getBuffer<float>(_deriv_input));
178 // Transpose and compute gradient for weights
179 // ∂L/∂W = fc(transposed incomming gradient, transposed X)
180 auto transposed_input = _transposed_input.get();
181 assert(transposed_input->getShape().rank() == 2);
182 nnfw::cker::Transpose(transpose_param, getShape(_input), getBuffer<float>(_input),
183 getShape(transposed_input), getBuffer<float>(transposed_input));
185 auto transposed_deriv_output = _transposed_deriv_output.get();
186 assert(transposed_deriv_output->getShape().rank() == 2);
187 nnfw::cker::Transpose(transpose_param, getShape(backprop_act), getBuffer<float>(backprop_act),
188 getShape(transposed_deriv_output),
189 getBuffer<float>(transposed_deriv_output));
191 nnfw::cker::FullyConnected(op_params, getShape(transposed_deriv_output),
192 getBuffer<float>(transposed_deriv_output), getShape(transposed_input),
193 getBuffer<float>(transposed_input), getShape(nullptr), nullptr,
194 getShape(_grad_weights), getBuffer<float>(_grad_weights));
196 // Compute gradient for bias
200 nnfw::cker::train::FullyConnectedBiasGrad(getShape(backprop_act),
201 getBuffer<float>(backprop_act), getShape(_grad_bias),
202 getBuffer<float>(_grad_bias));
208 } // namespace backend