Line data Source code
1 : // SPDX-License-Identifier: Apache-2.0
2 : /**
3 : * Copyright (C) 2024 heka1024 <heka1024@gmail.com>
4 : *
5 : * @file upsample2d_layer.h
6 : * @date 15 June 2024
7 : * @brief It is a implementation of upsample layer for given size and
8 : * interpolation method
9 : * @see https://github.com/nnstreamer/nntrainer
10 : * @author heka1024 <heka1024@gmail.com>
11 : * @bug No known bugs except for NYI items
12 : */
13 :
14 : #include <layer_context.h>
15 : #include <node_exporter.h>
16 : #include <upsample2d_layer.h>
17 :
18 : namespace nntrainer {
19 : static constexpr size_t SINGLE_INOUT_IDX = 0;
20 :
21 23 : Upsample2dLayer::Upsample2dLayer() :
22 : Layer(),
23 : upsample2d_props(props::UpsampleMode(),
24 46 : std::array<props::KernelSize, UPSAMPLE2D_DIM>()) {}
25 :
26 9 : void Upsample2dLayer::finalize(nntrainer::InitLayerContext &context) {
27 9 : std::vector<nntrainer::TensorDim> dim = context.getInputDimensions();
28 :
29 : const auto &kernel_size =
30 : std::get<std::array<props::KernelSize, UPSAMPLE2D_DIM>>(upsample2d_props);
31 :
32 18 : for (unsigned int i = 0; i < dim.size(); ++i) {
33 9 : if (dim[i].getDataLen() == 0) {
34 0 : throw std::invalid_argument("Input dimension is not set");
35 : } else {
36 9 : dim[i].channel(dim[i].channel());
37 9 : dim[i].height(dim[i].height() * kernel_size[0]);
38 9 : dim[i].width(dim[i].width() * kernel_size[1]);
39 : }
40 : }
41 :
42 9 : context.setOutputDimensions(dim);
43 9 : }
44 :
45 25 : void Upsample2dLayer::forwarding(nntrainer::RunLayerContext &context,
46 : bool training) {
47 25 : nntrainer::Tensor &in = context.getInput(SINGLE_INOUT_IDX);
48 25 : nntrainer::Tensor &out = context.getOutput(SINGLE_INOUT_IDX);
49 :
50 : const auto &upsampling_type =
51 25 : std::get<props::UpsampleMode>(upsample2d_props).get();
52 : const auto &kernel_size =
53 : std::get<std::array<props::KernelSize, UPSAMPLE2D_DIM>>(upsample2d_props);
54 :
55 25 : switch (upsampling_type) {
56 : case props::UpsampleModeInfo::Interpolation::nearest:
57 20 : for (int b = 0; b < (int)out.batch(); b++) {
58 20 : for (int c = 0; c < (int)out.channel(); c++) {
59 75 : for (int h = 0; h < (int)out.height(); h++) {
60 550 : for (int w = 0; w < (int)out.width(); w++) {
61 485 : out.setValue(
62 : b, c, h, w,
63 485 : in.getValue(b, c, h / kernel_size[0], w / kernel_size[1]));
64 : }
65 : }
66 : }
67 : }
68 : break;
69 : case props::UpsampleModeInfo::Interpolation::bilinear: {
70 15 : float scale_h = (float)kernel_size[0];
71 15 : float scale_w = (float)kernel_size[1];
72 :
73 30 : for (int b = 0; b < (int)out.batch(); b++) {
74 30 : for (int c = 0; c < (int)out.channel(); c++) {
75 280 : for (int h = 0; h < (int)out.height(); h++) {
76 8750 : for (int w = 0; w < (int)out.width(); w++) {
77 8485 : float x_in = (w + 0.5f) / scale_w - 0.5f;
78 8485 : float y_in = (h + 0.5f) / scale_h - 0.5f;
79 :
80 8485 : if (x_in < 0) {
81 : x_in = 0.0f;
82 : }
83 8485 : if (y_in < 0) {
84 : y_in = 0.0f;
85 : }
86 :
87 8485 : int x0 = static_cast<int>(floor(x_in));
88 8485 : int y0 = static_cast<int>(floor(y_in));
89 8485 : int x1 = std::min(x0 + 1, (int)in.width() - 1);
90 8485 : int y1 = std::min(y0 + 1, (int)in.height() - 1);
91 :
92 8485 : float dx = x_in - x0;
93 8485 : float dy = y_in - y0;
94 :
95 8485 : float top = (1.0f - dx) * in.getValue(b, c, y1, x0) +
96 8485 : dx * in.getValue(b, c, y1, x1);
97 8485 : float bottom = (1.0f - dx) * in.getValue(b, c, y0, x0) +
98 8485 : dx * in.getValue(b, c, y0, x1);
99 8485 : float v = (1.0f - dy) * bottom + dy * top;
100 8485 : out.setValue(b, c, h, w, v);
101 : }
102 : }
103 : }
104 : }
105 : } break;
106 0 : default:
107 0 : throw std::runtime_error("Error: Unknown Upsample Mode Type");
108 : }
109 25 : }
110 :
111 5 : void Upsample2dLayer::calcDerivative(nntrainer::RunLayerContext &context) {
112 : const nntrainer::Tensor &derivative_ =
113 5 : context.getIncomingDerivative(SINGLE_INOUT_IDX);
114 :
115 5 : nntrainer::Tensor &dx = context.getOutgoingDerivative(SINGLE_INOUT_IDX);
116 :
117 : const auto &kernel_size =
118 : std::get<std::array<props::KernelSize, UPSAMPLE2D_DIM>>(upsample2d_props);
119 : const auto &upsampling_type =
120 5 : std::get<props::UpsampleMode>(upsample2d_props).get();
121 :
122 5 : switch (upsampling_type) {
123 : case props::UpsampleModeInfo::Interpolation::nearest: {
124 : float val = 0;
125 4 : for (int b = 0; b < (int)derivative_.batch(); b++) {
126 4 : for (int c = 0; c < (int)derivative_.channel(); c++) {
127 15 : for (int h = 0; h < (int)derivative_.height(); h++) {
128 110 : for (int w = 0; w < (int)derivative_.width(); w++) {
129 97 : if (h % kernel_size[0] == 0 && w % kernel_size[1] == 0) {
130 13 : dx.setValue(b, c, h / kernel_size[0], w / kernel_size[1], 0);
131 : }
132 :
133 97 : val = dx.getValue(b, c, h / kernel_size[0], w / kernel_size[1]) +
134 97 : derivative_.getValue(b, c, h, w);
135 97 : dx.setValue(b, c, h / kernel_size[0], w / kernel_size[1], val);
136 : }
137 : }
138 : }
139 : }
140 : } break;
141 3 : case props::UpsampleModeInfo::Interpolation::bilinear: {
142 3 : dx.setZero();
143 :
144 3 : int input_height = dx.height();
145 3 : int input_width = dx.width();
146 :
147 6 : for (int b = 0; b < (int)derivative_.batch(); b++) {
148 6 : for (int c = 0; c < (int)derivative_.channel(); c++) {
149 56 : for (int h = 0; h < (int)derivative_.height(); h++) {
150 1750 : for (int w = 0; w < (int)derivative_.width(); w++) {
151 1697 : float in_h = (h + 0.5f) / kernel_size[0] - 0.5f;
152 1697 : float in_w = (w + 0.5f) / kernel_size[1] - 0.5f;
153 :
154 1697 : if (in_h < 0) {
155 : in_h = 0.0f;
156 : }
157 1697 : if (in_w < 0) {
158 : in_w = 0.0f;
159 : }
160 :
161 1697 : int y0 = static_cast<int>(floor(in_h));
162 1697 : int x0 = static_cast<int>(floor(in_w));
163 1697 : int y1 = std::min(y0 + 1, input_height - 1);
164 1697 : int x1 = std::min(x0 + 1, input_width - 1);
165 :
166 1697 : float dx_ = (in_w - x0); // Due to name conflict with dx
167 1697 : float dy_ = (in_h - y0);
168 :
169 1697 : float top_left_weight = (1.0 - dy_) * (1.0 - dx_);
170 1697 : float top_right_weight = (1.0 - dy_) * dx_;
171 1697 : float bottom_left_weight = dy_ * (1.0 - dx_);
172 1697 : float bottom_right_weight = dy_ * dx_;
173 :
174 1697 : float grad = derivative_.getValue(b, c, h, w);
175 :
176 1697 : dx.addValue(b, c, y0, x0, top_left_weight * grad, 1.0f);
177 1697 : dx.addValue(b, c, y0, x1, top_right_weight * grad, 1.0f);
178 1697 : dx.addValue(b, c, y1, x0, bottom_left_weight * grad, 1.0f);
179 1697 : dx.addValue(b, c, y1, x1, bottom_right_weight * grad, 1.0f);
180 : }
181 : }
182 : }
183 : }
184 : } break;
185 0 : default:
186 0 : throw std::runtime_error("Error: Unknown Upsample Mode Type");
187 : }
188 5 : }
189 :
190 38 : void Upsample2dLayer::setProperty(const std::vector<std::string> &values) {
191 38 : auto remain_props = loadProperties(values, upsample2d_props);
192 :
193 37 : if (!remain_props.empty()) {
194 : std::string msg = "[Upsample2dLayer] Unknown properties set with count" +
195 2 : std::to_string(values.size());
196 4 : throw exception::not_supported(msg);
197 : }
198 37 : }
199 : } // namespace nntrainer
|