
1. PyTorch Tensor란?
PyTorch Tensor는 PyTorch에서 데이터를 표현하는 가장 기본적인 자료구조입니다.
쉽게 말하면 NumPy 배열과 비슷하지만, 딥러닝 학습에 필요한 기능이 추가된 다차원 배열입니다.
PyTorch에서 모델의 입력값, 출력값, 가중치, 손실값, 기울기 계산은 대부분 Tensor를 중심으로 동작합니다.
이미지 데이터
텍스트를 숫자로 바꾼 데이터
모델의 가중치
예측 결과
손실값
기울기
이런 것들이 모두 Tensor 형태로 처리됩니다.
PyTorch를 공부할 때 Tensor를 먼저 이해해야 하는 이유는 간단합니다.
PyTorch 모델 학습 = Tensor 연산의 반복
즉, Tensor는 PyTorch 딥러닝의 출발점입니다.
2. Tensor와 NumPy 배열의 차이
PyTorch Tensor는 NumPy 배열과 매우 비슷합니다.
둘 다 숫자 데이터를 다차원 배열 형태로 다룰 수 있습니다.
import numpy as np
import torch
np_arr = np.array([1, 2, 3])
torch_tensor = torch.tensor([1, 2, 3])
print(np_arr)
print(torch_tensor)
하지만 Tensor는 NumPy 배열과 비교했을 때 딥러닝에 필요한 중요한 기능을 제공합니다.
| 구분 | NumPy 배열 | PyTorch Tensor |
|---|---|---|
| 기본 용도 | 수치 계산 | 딥러닝, 수치 계산 |
| GPU 연산 | 기본 지원하지 않음 | CUDA GPU 연산 지원 |
| 자동 미분 | 지원하지 않음 | autograd 지원 |
| 딥러닝 모델 학습 | 직접 구현 필요 | PyTorch 모델과 바로 연동 |
| 주요 객체 | numpy.ndarray |
torch.Tensor |
정리하면 다음과 같습니다.
NumPy 배열은 일반 수치 계산에 많이 사용됩니다.
PyTorch Tensor는 딥러닝 학습과 GPU 연산에 최적화되어 있습니다.
3. Tensor 생성하기
PyTorch를 사용하려면 먼저 torch를 import합니다.
import torch
3.1 리스트로 Tensor 만들기
import torch
x = torch.tensor([1, 2, 3])
print(x)
print(type(x))
출력 예시는 다음과 같습니다.
tensor([1, 2, 3])
<class 'torch.Tensor'>
3.2 2차원 Tensor 만들기
x = torch.tensor([
[1, 2, 3],
[4, 5, 6]
])
print(x)
출력 결과는 다음과 같습니다.
tensor([[1, 2, 3],
[4, 5, 6]])
이 Tensor는 2행 3열 구조입니다.
4. 자주 사용하는 Tensor 생성 함수
PyTorch에서는 다양한 방식으로 Tensor를 만들 수 있습니다.
4.1 0으로 채운 Tensor
x = torch.zeros(3, 4)
print(x)
출력 결과는 다음과 같습니다.
tensor([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
4.2 1로 채운 Tensor
x = torch.ones(2, 3)
print(x)
4.3 랜덤 Tensor
x = torch.rand(2, 3)
print(x)
torch.rand()는 0 이상 1 미만의 랜덤값을 만듭니다.
4.4 정규분포 랜덤 Tensor
x = torch.randn(2, 3)
print(x)
torch.randn()은 평균이 0이고 표준편차가 1인 정규분포 기반 랜덤값을 만듭니다.
4.5 연속된 숫자 Tensor
x = torch.arange(0, 10)
print(x)
출력 결과는 다음과 같습니다.
tensor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
간격을 지정할 수도 있습니다.
x = torch.arange(0, 10, 2)
print(x)
출력 결과는 다음과 같습니다.
tensor([0, 2, 4, 6, 8])
5. Tensor의 기본 속성
Tensor를 다룰 때는 다음 속성을 자주 확인합니다.
shape
dtype
device
requires_grad
예제를 보겠습니다.
x = torch.tensor([
[1, 2, 3],
[4, 5, 6]
])
print("shape:", x.shape)
print("dtype:", x.dtype)
print("device:", x.device)
print("requires_grad:", x.requires_grad)
출력 예시는 다음과 같습니다.
shape: torch.Size([2, 3])
dtype: torch.int64
device: cpu
requires_grad: False
각 속성의 의미는 다음과 같습니다.
| 속성 | 설명 |
|---|---|
shape |
Tensor의 모양입니다. |
dtype |
Tensor 안에 들어있는 데이터 타입입니다. |
device |
Tensor가 CPU에 있는지 GPU에 있는지 나타냅니다. |
requires_grad |
자동 미분을 추적할지 여부입니다. |
6. shape 이해하기
shape는 Tensor의 모양을 나타냅니다.
x = torch.zeros(2, 3)
print(x.shape)
출력 결과는 다음과 같습니다.
torch.Size([2, 3])
이 Tensor는 2행 3열입니다.
딥러닝에서 shape는 매우 중요합니다.
모델에 들어가는 입력 데이터의 shape가 맞지 않으면 오류가 발생합니다.
이미지 분류에서는 보통 다음과 같은 shape를 자주 봅니다.
[batch_size, channels, height, width]
예를 들어 이미지 32장을 한 번에 학습하고, 각 이미지가 RGB 3채널이며 크기가 224x224라면 shape는 다음과 같습니다.
[32, 3, 224, 224]
여기서 의미는 다음과 같습니다.
| 위치 | 의미 |
|---|---|
| 32 | 배치 크기 |
| 3 | 색상 채널 수 |
| 224 | 이미지 높이 |
| 224 | 이미지 너비 |
7. dtype 이해하기
dtype은 Tensor 안의 데이터 타입입니다.
x = torch.tensor([1, 2, 3])
print(x.dtype)
출력 결과는 다음과 같습니다.
torch.int64
실수 Tensor를 만들면 다음과 같습니다.
x = torch.tensor([1.0, 2.0, 3.0])
print(x.dtype)
출력 결과는 다음과 같습니다.
torch.float32
직접 dtype을 지정할 수도 있습니다.
x = torch.tensor([1, 2, 3], dtype=torch.float32)
print(x)
print(x.dtype)
딥러닝에서는 보통 torch.float32를 많이 사용합니다.
분류 문제의 정답 라벨은 torch.long 타입을 요구하는 경우가 많습니다.
예를 들어 nn.CrossEntropyLoss()를 사용할 때 정답 라벨은 클래스 인덱스 형태의 torch.long 타입이어야 합니다.
labels = torch.tensor([0, 1, 2], dtype=torch.long)
8. device 이해하기
device는 Tensor가 어디에서 연산되는지를 나타냅니다.
x = torch.tensor([1, 2, 3])
print(x.device)
출력 결과는 보통 다음과 같습니다.
cpu
GPU를 사용할 수 있다면 다음과 같이 device를 지정합니다.
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
Tensor를 GPU로 이동할 수 있습니다.
x = torch.tensor([1, 2, 3], dtype=torch.float32)
x = x.to(device)
print(x.device)
모델과 데이터는 같은 device에 있어야 합니다.
model = model.to(device)
images = images.to(device)
labels = labels.to(device)
모델은 GPU에 있고 데이터는 CPU에 있으면 연산 오류가 발생합니다.
9. Tensor 연산
Tensor는 사칙연산을 지원합니다.
a = torch.tensor([1, 2, 3])
b = torch.tensor([10, 20, 30])
print(a + b)
print(a - b)
print(a * b)
print(b / a)
출력 결과는 다음과 같습니다.
tensor([11, 22, 33])
tensor([ -9, -18, -27])
tensor([10, 40, 90])
tensor([10., 10., 10.])
스칼라 값과도 연산할 수 있습니다.
x = torch.tensor([1, 2, 3])
print(x + 10)
print(x * 2)
10. 브로드캐스팅
브로드캐스팅은 서로 다른 shape의 Tensor끼리 연산할 때 자동으로 크기를 맞춰주는 기능입니다.
x = torch.tensor([
[1, 2, 3],
[4, 5, 6]
])
y = torch.tensor([10, 20, 30])
print(x + y)
출력 결과는 다음과 같습니다.
tensor([[11, 22, 33],
[14, 25, 36]])
y가 각 행에 자동으로 더해진 것입니다.
브로드캐스팅은 편리하지만 shape를 제대로 이해하지 못하면 의도와 다른 연산이 될 수 있습니다.
따라서 연산 전에 shape를 확인하는 습관이 중요합니다.
11. 인덱싱과 슬라이싱
Tensor도 리스트나 NumPy 배열처럼 인덱싱과 슬라이싱을 할 수 있습니다.
x = torch.tensor([10, 20, 30, 40, 50])
print(x[0])
print(x[2])
print(x[-1])
2차원 Tensor에서는 행과 열 기준으로 접근합니다.
x = torch.tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
print(x[0, 0])
print(x[1, 2])
print(x[:, 0])
출력 결과는 다음과 같습니다.
tensor(1)
tensor(6)
tensor([1, 4, 7])
x[:, 0]은 모든 행의 0번째 열을 가져온다는 의미입니다.
12. Tensor 모양 바꾸기
딥러닝에서는 Tensor의 shape를 바꿔야 하는 경우가 많습니다.
12.1 reshape
x = torch.arange(0, 6)
print(x)
출력 결과는 다음과 같습니다.
tensor([0, 1, 2, 3, 4, 5])
2행 3열로 바꿔보겠습니다.
y = x.reshape(2, 3)
print(y)
출력 결과는 다음과 같습니다.
tensor([[0, 1, 2],
[3, 4, 5]])
12.2 view
view()도 Tensor의 shape를 바꿀 때 사용합니다.
x = torch.arange(0, 6)
y = x.view(2, 3)
print(y)
view()는 메모리 연속성에 영향을 받을 수 있습니다.
처음 공부할 때는 reshape()를 먼저 사용하는 것이 편합니다.
12.3 unsqueeze
unsqueeze()는 특정 위치에 차원을 하나 추가합니다.
x = torch.tensor([1, 2, 3])
print(x.shape)
y = x.unsqueeze(0)
print(y)
print(y.shape)
출력 결과는 다음과 같습니다.
torch.Size([3])
tensor([[1, 2, 3]])
torch.Size([1, 3])
딥러닝에서는 배치 차원을 추가할 때 자주 사용합니다.
예를 들어 이미지 한 장을 모델에 넣으려면 [C, H, W]를 [1, C, H, W]로 바꿔야 하는 경우가 많습니다.
image = image.unsqueeze(0)
12.4 squeeze
squeeze()는 크기가 1인 차원을 제거합니다.
x = torch.zeros(1, 3, 1, 4)
print(x.shape)
y = x.squeeze()
print(y.shape)
출력 예시는 다음과 같습니다.
torch.Size([1, 3, 1, 4])
torch.Size([3, 4])
13. Tensor 합치기
Tensor를 합칠 때는 torch.cat()과 torch.stack()을 자주 사용합니다.
13.1 torch.cat
torch.cat()은 기존 차원 방향으로 Tensor를 이어 붙입니다.
a = torch.tensor([[1, 2], [3, 4]])
b = torch.tensor([[5, 6]])
result = torch.cat([a, b], dim=0)
print(result)
출력 결과는 다음과 같습니다.
tensor([[1, 2],
[3, 4],
[5, 6]])
열 방향으로 붙일 수도 있습니다.
a = torch.tensor([[1, 2], [3, 4]])
b = torch.tensor([[10], [20]])
result = torch.cat([a, b], dim=1)
print(result)
출력 결과는 다음과 같습니다.
tensor([[ 1, 2, 10],
[ 3, 4, 20]])
13.2 torch.stack
torch.stack()은 새로운 차원을 만들어서 Tensor를 쌓습니다.
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
result = torch.stack([a, b], dim=0)
print(result)
print(result.shape)
출력 결과는 다음과 같습니다.
tensor([[1, 2, 3],
[4, 5, 6]])
torch.Size([2, 3])
14. NumPy와 Tensor 변환
PyTorch Tensor와 NumPy 배열은 서로 변환할 수 있습니다.
14.1 NumPy 배열을 Tensor로 변환
import numpy as np
import torch
np_arr = np.array([1, 2, 3])
tensor = torch.from_numpy(np_arr)
print(tensor)
14.2 Tensor를 NumPy 배열로 변환
tensor = torch.tensor([1, 2, 3])
np_arr = tensor.numpy()
print(np_arr)
GPU에 있는 Tensor는 바로 NumPy로 변환할 수 없습니다.
먼저 CPU로 이동해야 합니다.
tensor = tensor.cpu().numpy()
또한 자동 미분을 추적 중인 Tensor는 detach() 후 변환하는 경우가 많습니다.
np_arr = tensor.detach().cpu().numpy()
15. Autograd와 requires_grad
PyTorch Tensor의 중요한 기능 중 하나는 자동 미분입니다.
딥러닝 모델은 손실값을 기준으로 가중치를 업데이트하는데, 이때 기울기 계산이 필요합니다.
PyTorch에서는 requires_grad=True를 설정하면 해당 Tensor에 대한 연산을 추적합니다.
x = torch.tensor(2.0, requires_grad=True)
y = x ** 2
y.backward()
print(x.grad)
출력 결과는 다음과 같습니다.
tensor(4.)
수학적으로 보면 다음과 같습니다.
y = x²
dy/dx = 2x
x = 2일 때 dy/dx = 4
PyTorch가 이 미분값을 자동으로 계산한 것입니다.
16. backward 이해하기
backward()는 계산 그래프를 따라 기울기를 계산합니다.
x = torch.tensor(3.0, requires_grad=True)
y = x * x + 2 * x + 1
y.backward()
print(x.grad)
수식은 다음과 같습니다.
y = x² + 2x + 1
dy/dx = 2x + 2
x = 3일 때 dy/dx = 8
출력 결과는 다음과 같습니다.
tensor(8.)
딥러닝 학습에서는 이 과정이 모델의 모든 가중치에 대해 자동으로 수행됩니다.
17. detach와 no_grad
학습 중에는 기울기 계산이 필요하지만, 예측할 때는 필요하지 않습니다.
17.1 detach
detach()는 Tensor를 계산 그래프에서 분리합니다.
x = torch.tensor(2.0, requires_grad=True)
y = x ** 2
z = y.detach()
print(z.requires_grad)
출력 결과는 다음과 같습니다.
False
17.2 torch.no_grad
모델 예측 시에는 보통 torch.no_grad()를 사용합니다.
model.eval()
with torch.no_grad():
outputs = model(inputs)
이렇게 하면 불필요한 기울기 계산을 하지 않으므로 메모리를 절약할 수 있습니다.
18. Tensor와 딥러닝 모델 입력
PyTorch 모델에 들어가는 입력 데이터는 Tensor입니다.
예를 들어 이미지 분류 모델에서는 입력 shape가 보통 다음과 같습니다.
[batch_size, channels, height, width]
예시:
images = torch.randn(32, 3, 224, 224)
print(images.shape)
출력 결과는 다음과 같습니다.
torch.Size([32, 3, 224, 224])
모델 출력은 보통 다음과 같습니다.
outputs = torch.randn(32, 10)
print(outputs.shape)
출력 결과는 다음과 같습니다.
torch.Size([32, 10])
이 의미는 다음과 같습니다.
32개 이미지에 대해
10개 클래스 점수를 출력
19. CrossEntropyLoss에서 Tensor 형태
분류 문제에서 자주 사용하는 손실 함수는 nn.CrossEntropyLoss()입니다.
이 함수는 보통 다음 형태를 기대합니다.
outputs shape: [batch_size, num_classes]
labels shape : [batch_size]
예시는 다음과 같습니다.
import torch
import torch.nn as nn
outputs = torch.tensor([
[2.0, 0.5, 0.1],
[0.2, 1.5, 0.3]
])
labels = torch.tensor([0, 1], dtype=torch.long)
criterion = nn.CrossEntropyLoss()
loss = criterion(outputs, labels)
print(loss)
여기서 outputs는 각 클래스에 대한 점수이고, labels는 정답 클래스 인덱스입니다.
첫 번째 데이터의 정답: 0번 클래스
두 번째 데이터의 정답: 1번 클래스
주의할 점은 CrossEntropyLoss에 넣는 outputs는 softmax 결과가 아니라 raw score입니다.
PyTorch의 CrossEntropyLoss 내부에서 softmax에 해당하는 처리가 함께 이루어집니다.
20. Tensor에서 자주 나는 오류
20.1 shape 오류
가장 흔한 오류는 shape가 맞지 않는 경우입니다.
RuntimeError: mat1 and mat2 shapes cannot be multiplied
이런 오류가 나면 먼저 shape를 출력해봐야 합니다.
print(x.shape)
print(outputs.shape)
print(labels.shape)
20.2 device 오류
모델은 GPU에 있고 데이터는 CPU에 있으면 오류가 발생합니다.
Expected all tensors to be on the same device
이럴 때는 모델과 데이터를 같은 device로 이동해야 합니다.
model = model.to(device)
inputs = inputs.to(device)
labels = labels.to(device)
20.3 dtype 오류
손실 함수가 기대하는 dtype과 다를 때 오류가 발생할 수 있습니다.
분류 라벨은 보통 torch.long이어야 합니다.
labels = labels.long()
회귀 문제의 입력과 정답은 보통 torch.float32를 사용합니다.
x = x.float()
y = y.float()
21. Tensor 기본 실습 예제
아래 코드는 Tensor의 기본 흐름을 한 번에 연습할 수 있는 예제입니다.
import torch
# 1. Tensor 생성
x = torch.tensor([
[1.0, 2.0],
[3.0, 4.0]
])
# 2. 기본 정보 확인
print("x:")
print(x)
print("shape:", x.shape)
print("dtype:", x.dtype)
print("device:", x.device)
# 3. 연산
y = x * 2 + 1
print("y:")
print(y)
# 4. 평균
mean_value = y.mean()
print("mean:", mean_value)
# 5. shape 변경
z = y.reshape(4)
print("z:")
print(z)
print("z shape:", z.shape)
# 6. GPU 사용 가능 여부 확인
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
z = z.to(device)
print("z device:", z.device)
22. Autograd 기본 실습 예제
import torch
# requires_grad=True 설정
x = torch.tensor(2.0, requires_grad=True)
# 계산식
y = x ** 3 + 2 * x
# 역전파
y.backward()
print("x:", x)
print("y:", y)
print("x.grad:", x.grad)
수식으로 보면 다음과 같습니다.
y = x³ + 2x
dy/dx = 3x² + 2
x = 2일 때 dy/dx = 14
따라서 x.grad는 14가 됩니다.