PyTorch는 인기 있는 머신러닝 프레임워크로, 데이터 처리, 모델 생성, 최적화, 저장 및 추론에 필요한 작업을 지원합니다. 이번 문서에서는 PyTorch의 기본적인 사용 방법과 텐서(Tensor) 관련 연산을 다룹니다.
1. PyTorch와 텐서 소개
대부분의 머신러닝 워크플로우는 데이터 처리, 모델 구성, 하이퍼파라미터 조정, 학습된 모델 저장 및 추론 과정으로 이루어집니다. 본 문서에서는 PyTorch를 이용한 전체적인 머신러닝 워크플로우를 다루며, FashionMNIST 데이터셋을 활용해 이미지를 분류하는 신경망 모델을 구축합니다.
텐서란?
텐서는 배열과 행렬과 유사한 전문적인 데이터 구조입니다. PyTorch에서는 입력값, 출력값, 그리고 모델 파라미터를 모두 텐서로 표현하며, 이를 통해 GPU나 다른 하드웨어 가속기를 활용할 수 있습니다. 또한 NumPy 배열과 마찬가지로 메모리 공유 기능도 제공됩니다.
2. 환경 설정 및 텐서 초기화
먼저 필요한 라이브러리를 임포트합니다:
import torch
import numpy as np
(1) 직접 데이터로 초기화
데이터로부터 바로 텐서를 만들 수 있습니다.
data = [[1, 2], [3, 4]]
tensor_data = torch.tensor(data)
print(f"직접 데이터로 초기화된 텐서:\n {tensor_data}")
(2) NumPy 배열로부터 초기화
NumPy 배열에서 텐서로 변환할 수도 있습니다. 이 경우 메모리 공유가 가능합니다.
numpy_array = np.array(data)
tensor_from_numpy = torch.from_numpy(numpy_array)
# 원본 NumPy 배열 수정 시 텐서도 함께 변경됨
np.multiply(numpy_array, 2, out=numpy_array)
print(f"변경된 NumPy 배열:\n {numpy_array}")
print(f"변경된 텐서:\n {tensor_from_numpy}")
(3) 다른 텐서로부터 초기화
기존 텐서의 속성을 유지하거나 새로운 데이터형을 지정할 수 있습니다.
ones_tensor = torch.ones_like(tensor_data)
random_tensor = torch.rand_like(tensor_data, dtype=torch.float32)
print(f"1로 채워진 텐서:\n {ones_tensor}")
print(f"랜덤 값으로 채워진 텐서:\n {random_tensor}")
(4) 특정 형태로 초기화
주어진 형태(shape)에 따라 텐서를 생성할 수 있습니다.
shape = (2, 3)
rand_tensor = torch.rand(shape)
zero_tensor = torch.zeros(shape)
one_tensor = torch.ones(shape)
print(f"랜덤 텐서:\n {rand_tensor}")
print(f"0으로 채워진 텐서:\n {zero_tensor}")
print(f"1로 채워진 텐서:\n {one_tensor}")
3. 텐서 속성 확인
텐서의 형상(shape), 데이터형(dtype), 위치(device) 등을 확인할 수 있습니다.
sample_tensor = torch.rand(3, 4)
print(f"텐서의 형상: {sample_tensor.shape}")
print(f"데이터형: {sample_tensor.dtype}")
print(f"저장 위치: {sample_tensor.device}")
4. GPU 활용
PyTorch는 CUDA를 통해 GPU를 활용할 수 있습니다. 다음 코드는 GPU가 사용 가능한 경우 텐서를 GPU로 이동시킵니다.
if torch.cuda.is_available():
sample_tensor = sample_tensor.to('cuda')
print(f"GPU로 이동 후 저장 위치: {sample_tensor.device}")
5. 텐서 인덱싱 및 슬라이싱
NumPy와 유사한 방식으로 인덱싱과 슬라이싱을 수행할 수 있습니다.
matrix_tensor = torch.ones(4, 4)
print(f"첫 번째 행: {matrix_tensor[0]}")
print(f"첫 번째 열: {matrix_tensor[:, 0]}")
print(f"마지막 열: {matrix_tensor[..., -1]}")
# 특정 열 값을 수정
matrix_tensor[:, 1] = 0
print(f"수정된 텐서:\n {matrix_tensor}")
6. 텐서 결합
여러 텐서를 연결하거나 새로운 차원을 추가하여 결합할 수 있습니다.
combined_tensor = torch.cat([matrix_tensor, matrix_tensor, matrix_tensor], dim=1)
print(f"결합된 텐서 (dim=1):\n {combined_tensor}")
stacked_tensor = torch.stack([matrix_tensor, matrix_tensor], dim=0)
print(f"스택된 텐서 (dim=0):\n {stacked_tensor}")
7. 수학 연산
텐서 간의 행렬 곱셈이나 요소별 연산을 수행할 수 있습니다.
# 행렬 곱셈
result_matmul = torch.matmul(matrix_tensor, matrix_tensor.T)
print(f"행렬 곱셈 결과:\n {result_matmul}")
# 요소별 곱셈
elementwise_mul = matrix_tensor * matrix_tensor
print(f"요소별 곱셈 결과:\n {elementwise_mul}")
8. 단일 요소 텐서
모든 요소를 하나의 값으로 집계한 경우 item() 메서드를 통해 Python 스칼라 값으로 변환할 수 있습니다.
sum_tensor = matrix_tensor.sum()
sum_item = sum_tensor.item()
print(f"집계된 값: {sum_item}, 타입: {type(sum_item)}")
9. 원위치(in-place) 연산
원위치 연산은 결과를 원래 변수에 직접 저장하는 방식으로, 일반적으로 _ 접미사를 사용합니다.
inplace_tensor = torch.ones(4, 4)
print(f"원래 텐서:\n {inplace_tensor}")
inplace_tensor.add_(5)
print(f"원위치 연산 후 텐서:\n {inplace_tensor}")
주의: 원위치 연산은 메모리를 절약할 수 있지만, 자동 미분 시 기록을 잃어버릴 수 있으므로 주의해야 합니다.
10. NumPy와의 상호운용성
NumPy 배열과 PyTorch 텐서는 CPU 상에서 동일한 메모리 공간을 공유할 수 있습니다.
# 텐서 → NumPy 배열
tensor_to_numpy = torch.ones(5)
numpy_from_tensor = tensor_to_numpy.numpy()
print(f"NumPy 배열로 변환된 값:\n {numpy_from_tensor}")
# NumPy 배열 → 텐서
numpy_array = np.ones(5)
tensor_from_numpy = torch.from_numpy(numpy_array)
print(f"텐서로 변환된 값:\n {tensor_from_numpy}")