로직 과정 이해
입력을 받고, 해당 입력 차원의 확인
입력 데이터의 height, width, channels
출력의 크기 계산, ( output_height, output_width ), 차원의 수는 동일하다.
출력 배열의 생성, output_height, output_width, channels
풀링 연산 수행
풀링 연산 결과가 저장될 인덱스의 계산,
모드, max, mean 에 따라 미리 값을 지정해준다.
pool_node 의 생성, node 초기화 값 넣기
출력 result, output_height, output_width, input_channels 의 반복문 수행
풀링 사이즈에 따른 반복문 수행,
각 인덱스 값 계산, input_i, input_j, input_index
input_value 에 값 할당, pool_size 를 반복하면서 해당 값의 갱신
노드로 계산 그래프 생성, node_list 반환
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <vector>
#include <limits>
#include "../node/node.h" // Node 클래스가 정의된 헤더 파일 포함
namespace py = pybind11;
std::pair<py::array_t<double>, std::vector<std::shared_ptr<Node>>> pooling2d(
py::array_t<double> input, // 입력 이미지
int pool_height, // 풀링 필터 높이
int pool_width, // 풀링 필터 너비
int stride = 1, // 스트라이드
std::string mode = "max", // 풀링 모드: "max" 또는 "average"
std::vector<std::shared_ptr<Node>> node_list = {}
) {
// 입력값 버퍼 정보 가져오기
py::buffer_info bufInput = input.request();
// 입력 차원 체크
if (bufInput.ndim != 3) {
throw std::runtime_error("Input should be 3-D (height, width, channels) NumPy arrays");
}
int input_height = bufInput.shape[0];
int input_width = bufInput.shape[1];
int input_channels = bufInput.shape[2];
// 출력 크기 계산
int output_height = (input_height - pool_height) / stride + 1;
int output_width = (input_width - pool_width) / stride + 1;
// 결과 배열 생성
py::array_t<double> result = py::array_t<double>({output_height, output_width, input_channels});
py::buffer_info bufResult = result.request();
double* ptrInput = static_cast<double*>(bufInput.ptr);
double* ptrResult = static_cast<double*>(bufResult.ptr);
bool is_new_graph = node_list.empty();
// 풀링 연산
for (int h = 0; h < output_height; ++h) {
for (int w = 0; w < output_width; ++w) {
for (int ch = 0; ch < input_channels; ++ch) {
int result_index = (h * output_width + w) * input_channels + ch;
ptrResult[result_index] = (mode == "max") ? -std::numeric_limits<double>::infinity() : 0.0;
std::shared_ptr<Node> pool_node;
std::shared_ptr<Node> active_node = nullptr;
// 각 풀링 영역에 대한 노드 설정
if (is_new_graph) {
pool_node = std::make_shared<Node>((mode == "max") ? "max_pool" : "avg_pool", 0.0, 0.0, 0.0, 0.0);
} else {
pool_node = node_list[result_index];
pool_node->output = (mode == "max") ? -std::numeric_limits<double>::infinity() : 0.0;
}
// 각 풀링 영역을 탐색하며 연산 수행
for (int i = 0; i < pool_height; ++i) {
for (int j = 0; j < pool_width; ++j) {
int input_i = h * stride + i;
int input_j = w * stride + j;
int input_index = (input_i * input_width + input_j) * input_channels + ch;
double input_value = ptrInput[input_index];
if (mode == "max") {
// 최대값 찾기
if (input_value > ptrResult[result_index]) {
ptrResult[result_index] = input_value;
if (is_new_graph) {
active_node = std::make_shared<Node>("max_pool", input_value, 0.0, input_value, 0.0);
} else {
active_node = node_list[result_index];
active_node->update(input_value, 0.0, input_value, 0.0);
}
}
} else if (mode == "average") {
// 평균값 계산
ptrResult[result_index] += input_value / (pool_height * pool_width);
if (is_new_graph) {
active_node = std::make_shared<Node>("ave_pool", input_value, 1.0 / (pool_height * pool_width), input_value / (pool_height * pool_width), 1.0 / (pool_height * pool_width));
} else {
active_node = node_list[result_index];
active_node->update(input_value, 1.0 / (pool_height * pool_width), input_value / (pool_height * pool_width), 1.0 / (pool_height * pool_width));
}
}
}
}
// active_node가 최종 부모 노드가 되고, pool_node는 자식 노드가 됨
if (active_node != nullptr) {
if (is_new_graph) {
pool_node->add_child(active_node); // pool_node가 자식으로 추가됨
active_node->add_parent(pool_node);
}
active_node->output = ptrResult[result_index];
}
// node_list에 최종 부모 노드를 추가
if (is_new_graph) {
node_list.push_back(active_node);
}
}
}
}
return std::make_pair(result, node_list);
}
'dev_AI_framework' 카테고리의 다른 글
RNN 계산 그래프 해석 (0) | 2024.10.21 |
---|---|
RNN 계산 그래프 구현 (3) | 2024.10.14 |
layer 쌓기 add_layer 에서 가변 인수로의 확장 수행 (0) | 2024.09.30 |
결국 어디에 포커스를 둘 것인가... (0) | 2024.09.30 |
pytorch 가 layer 를 쌓는 과정에 대해, activation layer 의 구현 (0) | 2024.09.30 |