dev_AI_framework

C++ 백엔드 행렬 기본 연산과 함께 노드 정보 저장

명징직조지훈 2024. 9. 5. 12:46

기존에 구현한 기본 행렬 연산 ( 덧셈, 곱셈 ) 의 구현 내용 확인

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <vector>

namespace py = pybind11;

// matrix_add
py::array_t<double> matrix_add(py::array_t<double> A, py::array_t<double> B) {
    // Numpy 배열의 버퍼 정보 가져오기
    py::buffer_info bufA = A.request(), bufB = B.request();

    // 입력 배열이 2D인지 확인
    if (bufA.ndim != 2 || bufB.ndim != 2) {
        throw std::runtime_error("Input should be 2-D NumPy arrays");
    }

    // 행과 열의 크기 확인
    if (bufA.shape[0] != bufB.shape[0] || bufA.shape[1] != bufB.shape[1]) {
        throw std::runtime_error("Input matrices must have the same shape");
    }

    // 행과 열의 크기
    size_t rows = bufA.shape[0];
    size_t cols = bufA.shape[1];

    // 결과 배열 생성
    py::array_t<double> result = py::array_t<double>({rows, cols});
    py::buffer_info bufResult = result.request();

    double* ptrA = static_cast<double*>(bufA.ptr);
    double* ptrB = static_cast<double*>(bufB.ptr);
    double* ptrResult = static_cast<double*>(bufResult.ptr);

    // 행렬 덧셈
    for (size_t i = 0; i < rows; ++i) {
        for (size_t j = 0; j < cols; ++j) {
            ptrResult[i * cols + j] = ptrA[i * cols + j] + ptrB[i * cols + j];
        }
    }

    return result;
}

py::array_t<double> matrix_multiply(py::array_t<double> A, py::array_t<double> B) {
    // Numpy 배열의 버퍼 정보 가져오기
    py::buffer_info bufA = A.request(), bufB = B.request();

    // 입력 배열이 2D인지 확인
    if (bufA.ndim != 2 || bufB.ndim != 2) {
        throw std::runtime_error("Input should be 2-D NumPy arrays");
    }

    // 행렬 곱셈이 가능한지 확인
    if (bufA.shape[1] != bufB.shape[0]) {
        throw std::runtime_error("Inner matrix dimensions must agree");
    }

    // 행과 열의 크기
    size_t rows = bufA.shape[0];
    size_t cols = bufB.shape[1];
    size_t inner_dim = bufA.shape[1];

    // 결과 배열 생성
    py::array_t<double> result = py::array_t<double>({rows, cols});
    py::buffer_info bufResult = result.request();

    double* ptrA = static_cast<double*>(bufA.ptr);
    double* ptrB = static_cast<double*>(bufB.ptr);
    double* ptrResult = static_cast<double*>(bufResult.ptr);

    // 행렬 곱셈
    for (size_t i = 0; i < rows; ++i) {
        for (size_t j = 0; j < cols; ++j) {
            ptrResult[i * cols + j] = 0;
            for (size_t k = 0; k < inner_dim; ++k) {
                ptrResult[i * cols + j] += ptrA[i * inner_dim + k] * ptrB[k * cols + j];
            }
        }
    }

    return result;
}

numpy_array 값을 입력 받아 

단순히 연산 수행 후 결과 값만을 전달

 

개별 노드에 필요한 정보들도 같이 전달 할 수 있을 것 여기서 하는 이유는 연산과정에서 개별 정보에 접근할 수 있기 때문에 호출을 최소화하는 목적

 

#ifndef NODE_H
#define NODE_H

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <vector>
#include <memory>
#include <string>

namespace py = pybind11;

class Node {
public:
    std::string operation;
    double input_a;
    double input_b;
    double output;
    std::vector<std::shared_ptr<Node>> parents;  // 부모 노드들
    std::vector<std::shared_ptr<Node>> children; // 자식 노드들

    Node(const std::string& op, double a, double b, double out)
        : operation(op), input_a(a), input_b(b), output(out) {}

    // 부모 노드를 추가하는 함수
    void add_parent(std::shared_ptr<Node> parent) {
        parents.push_back(parent);
    }

    // 자식 노드를 추가하는 함수
    void add_child(std::shared_ptr<Node> child) {
        children.push_back(child);
    }
};

#endif // NODE_H
#ifndef NODE_H
#define NODE_H

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <vector>
#include <memory>
#include <string>

namespace py = pybind11;

class Node {
public:
    std::string operation;
    double input_a;
    double input_b;
    double output;
    std::vector<std::shared_ptr<Node>> parents;  // 부모 노드들
    std::vector<std::shared_ptr<Node>> children; // 자식 노드들

    Node(const std::string& op, double a, double b, double out)
        : operation(op), input_a(a), input_b(b), output(out) {}

    // 부모 노드를 추가하는 함수
    void add_parent(std::shared_ptr<Node> parent) {
        parents.push_back(parent);
    }

    // 자식 노드를 추가하는 함수
    void add_child(std::shared_ptr<Node> child) {
        children.push_back(child);
    }
};

#endif // NODE_H

node 클래스 정의와 python binding 확인, 출력 내용

Sum Node - Operation: sum, Result: 19.0
Mul Node - Operations: multiply, Input_a: 1.0, Input_b: 5.0, Result: 5.0
Mul Node - Operations: multiply, Input_a: 2.0, Input_b: 7.0, Result: 14.0
Sum Node - Operation: sum, Result: 22.0
Mul Node - Operations: multiply, Input_a: 1.0, Input_b: 6.0, Result: 6.0
Mul Node - Operations: multiply, Input_a: 2.0, Input_b: 8.0, Result: 16.0
Sum Node - Operation: sum, Result: 43.0
Mul Node - Operations: multiply, Input_a: 3.0, Input_b: 5.0, Result: 15.0
Mul Node - Operations: multiply, Input_a: 4.0, Input_b: 7.0, Result: 28.0
Sum Node - Operation: sum, Result: 50.0
Mul Node - Operations: multiply, Input_a: 3.0, Input_b: 6.0, Result: 18.0
Mul Node - Operations: multiply, Input_a: 4.0, Input_b: 8.0, Result: 32.0

 

node_list 에 sum_node 리스트가 저장되어 있음, 행렬의 각 원소 요소,

행렬을 표현하기 위한 부모 노드를 더 추가해야할지...

역전파에 이 값이 그대로 사용될 수 있으므로 이후 처리는 안해줘도 괜찮겠다.