[LibTorch 입문] 3편: C++에서 텐서 다루기 (기초 연산 실습)

지난 글(2편)에서는 LibTorch 환경을 설정하고 C++에서 간단한 텐서를 생성해 출력하는 "Hello LibTorch" 예제를 실습했습니다. 이제 본격적으로 텐서 연산을 조금 더 다뤄보며, C++에서 PyTorch 텐서와 유사한 감각으로 연산을 하는 방법을 익혀봅시다.

이번 글에서는 다음을 다룹니다.

  • 텐서 생성 방법과 다양한 초기화 방식
  • 기초적인 텐서 연산(덧셈, 곱셈, 행렬 연산 등)
  • 텐서 모양(Shape) 확인 및 변경
  • GPU(CUDA) 사용을 위한 기초 개념 (CUDA 지원 환경일 경우)
  • 작은 예제 코드를 통해 실습

이 과정을 통해 C++에서도 Python PyTorch API와 크게 다르지 않은 직관적인 방식으로 텐서를 다룰 수 있음을 확인할 수 있습니다.

텐서 생성하기

LibTorch에서는 PyTorch와 유사하게 다양한 텐서 초기화 함수를 제공합니다.

예를 들어, 다음과 같이 다양한 텐서를 만들 수 있습니다.

  • 랜덤 텐서 (rand): 균등분포 난수
  • randn: 정규분포 난수
  • zeros: 0으로 초기화된 텐서
  • ones: 1로 초기화된 텐서
  • full: 특정 스칼라값으로 초기화된 텐서
  • arange: 파이썬의 range처럼 연속적인 값으로 구성된 텐서

예제 코드 1: 다양한 초기화

#include <torch/torch.h>
#include <iostream>

int main() {
    auto t1 = torch::zeros({2, 3});
    auto t2 = torch::ones({2, 3});
    auto t3 = torch::arange(0, 6).reshape({2, 3});
    auto t4 = torch::randn({2, 3});

    std::cout << "t1 (zeros):\n" << t1 << "\n\n";
    std::cout << "t2 (ones):\n" << t2 << "\n\n";
    std::cout << "t3 (arange -> reshape):\n" << t3 << "\n\n";
    std::cout << "t4 (randn):\n" << t4 << "\n\n";

    return 0;
}

설명:

  • torch::zeros({2, 3})는 (2x3) 형태의 텐서, 모든 요소가 0
  • torch::ones({2, 3})는 모든 요소가 1인 텐서
  • torch::arange(0, 6)은 [0, 1, 2, 3, 4, 5]로 구성된 1D 텐서를 만든 뒤 .reshape({2, 3})로 2x3 텐서 형태 변경
  • torch::randn({2,3})는 평균 0, 표준편차 1의 정규분포에서 샘플링된 난수 텐서 생성

이 코드를 빌드하고 실행하면 각 텐서의 값이 출력되는 것을 확인할 수 있습니다.

기본 연산하기

텐서 간 산술 연산도 Python PyTorch와 동일하게 +, -, *, / 오퍼레이터나 대응되는 함수들을 사용할 수 있습니다.

예제 코드 2: 기본 연산

#include <torch/torch.h>
#include <iostream>

int main() {
    auto a = torch::ones({2, 2});
    auto b = torch::full({2, 2}, 2.0);

    std::cout << "a:\n" << a << "\n\n";
    std::cout << "b:\n" << b << "\n\n";

    auto c = a + b; // 텐서 덧셈
    auto d = a * b; // 텐서 곱셈
    auto e = b - a; // 텐서 뺄셈
    auto f = a / b; // 텐서 나눗셈

    std::cout << "c = a + b:\n" << c << "\n\n";
    std::cout << "d = a * b:\n" << d << "\n\n";
    std::cout << "e = b - a:\n" << e << "\n\n";
    std::cout << "f = a / b:\n" << f << "\n\n";

    return 0;
}

실행하면 a, b 텐서와 더해진 결과, 곱해진 결과 등을 확인할 수 있습니다.

텐서 모양 조작하기

딥러닝을 하다 보면 텐서 모양(Shape)을 바꾸거나, 차원을 추가/제거하는 일이 빈번합니다. LibTorch는 .reshape(), .view(), .unsqueeze(), .squeeze() 등의 함수를 통해 모양을 쉽게 변경할 수 있습니다.

  • .reshape({new_shape}): 텐서의 원소 수를 유지한 채 새로운 모양으로 변환
  • .view({new_shape}): reshape와 비슷하지만 메모리 연속성(Contiguous) 문제를 유념
  • .unsqueeze(dim): 특정 차원을 추가
  • .squeeze(): 크기가 1인 차원을 제거

예제 코드 3: 모양 조정

#include <torch/torch.h>
#include <iostream>

int main() {
    auto x = torch::arange(0, 12); // [0,1,2,3,4,5,6,7,8,9,10,11]
    std::cout << "x:\n" << x << "\n\n";

    auto x_reshaped = x.reshape({3, 4});
    std::cout << "x_reshaped (3x4):\n" << x_reshaped << "\n\n";

    // 차원 추가: 현재 (3,4) 형태에서 맨 앞에 1차원 추가 -> (1,3,4)
    auto x_unsqueezed = x_reshaped.unsqueeze(0);
    std::cout << "x_unsqueezed (1x3x4):\n" << x_unsqueezed << "\n\n";

    // 다시 squeeze로 크기가 1인 차원 제거하면 (3,4)
    auto x_squeezed = x_unsqueezed.squeeze();
    std::cout << "x_squeezed (3x4):\n" << x_squeezed << "\n\n";

    return 0;
}

출력 결과를 통해 차원 변화 과정을 확인할 수 있습니다.

텐서의 Device(GPU) 설정하기

LibTorch 텐서는 CPU를 기본 장치로 사용합니다. 하지만 CUDA 지원 버전을 사용하고, 시스템에 NVIDIA GPU가 있다면 .to(torch::kCUDA) 메서드를 통해 텐서를 GPU로 옮길 수 있습니다.

예를 들어, 다음과 같이 텐서를 GPU로 옮길 수 있습니다. (GPU 환경 아닐 경우, 이 코드를 실행하면 에러가 날 수 있으니 GPU 환경에서만 시도하세요.)

예제 코드 4: GPU 사용하기(옵션)

#include <torch/torch.h>
#include <iostream>

int main() {
    // GPU가 가능한 환경이면
    if (torch::cuda::is_available()) {
        auto device = torch::Device(torch::kCUDA);
        auto gpu_tensor = torch::ones({2, 2}, device);
        std::cout << "gpu_tensor on CUDA:\n" << gpu_tensor << "\n\n";
    } else {
        std::cout << "CUDA is not available on this system.\n";
    }

    return 0;
}

설명:

  • torch::cuda::is_available()를 통해 CUDA 사용 가능 여부 확인
  • 사용 가능하다면 torch::Device(torch::kCUDA)로 디바이스 객체를 만들고, 텐서 생성 시 device 인자를 넘겨 GPU 상에 텐서를 생성

CPU 상의 텐서를 GPU로 옮기고 싶다면 tensor.to(device)를 사용할 수도 있습니다.

정리

이번 글에서는 다음 내용을 배웠습니다.

  • 다양한 텐서 초기화 방법 (zeros, ones, arange, randn 등)
  • 기본 산술 연산 (+, -, *, /)
  • 텐서 모양 조작 (reshape, unsqueeze, squeeze)
  • GPU 디바이스 사용법 (옵션)

이를 통해 C++ LibTorch에서도 Python PyTorch와 유사한 텐서 조작이 가능함을 확인했습니다. 이제 텐서 연산에 익숙해지면, 다음 글(4편)에서는 Python에서 학습한 모델을 C++에서 불러와 추론하는 과정을 다룰 것입니다. 이를 통해 단순한 텐서 연산에서 한 단계 더 나아가 딥러닝 모델을 실제로 C++ 환경에서 활용하는 방법을 살펴보겠습니다.

참고 자료

반응형