텐서
텐서(Tensor) 는 배열이나 행렬과 유사한 자료 구조로, 파이토치에선 텐서를 이용해 모델의 입출력, 모델의 매개변수를 부호화하거나 GPU에 텐서를 올려 연산을 가속화한다.

텐서 생성
파이토치의 텐서는 토치나 넘파이의 배열과 거의 동일한 방식으로 생성하고 동작, torch.tensor() 또는 torch.Tensor() 로 생성할 수 있다.
- torch.tensor() : 입력된 데이터를 복사해 텐서로 변환하는 함수. 즉, 데이터를 복사하기 때문에 값이 무조건 존재해야 하며 입력된 데이터의 형식에 가장 적합한 텐서 자료형으로 변환.
- torch.Tensor() : 텐서의 기본형으로 텐서 인스턴스를 생성하는 클래스. 인스턴스를 생성하기 때문에 값을 입력하지 않는 경우 비어 있는 텐서를 생성함.
가능한 자료형이 명확하게 표현되는 클래스 형태의 torch.Tensor() 를 사용하는 것을 권장한다.
torch.tensor() 는 비어 있는 구조로 생성되지 않고 자동으로 자료형을 할당하기 때문에 의도하지 않은 자료형으로 변경될 수 있으므로 사용에 주의가 필요해다.
# 텐서 생성 예제
print(torch.tensor([1, 2, 3]))
print(torch.Tensor([[1, 2, 3], [4, 5, 6]]))
print(torch.LongTensor([1, 2, 3]))
print(torch.FloatTensor([1, 2, 3]))
tensor([1, 2, 3])
tensor([[1., 2., 3.],
[4., 5., 6.]])
tensor([1, 2, 3])
tensor([1., 2., 3.])
텐서 속성
텐서의 속성은 크게 형태(shape) , 자료형(dtype) , 장치(device) 가 있다.
- 형태: 텐서의 차원
- 자료형: 텐서에 할당된 데이터 형식
- 장치: GPU 가속 여부
텐서 연산을 할 때 위 세 가지 속성 중 한 가지라도 다르면 작동하지 않으므로 유의해야 한다.
# 텐서 속성 예제
tensor = torch.rand(1, 2)
print(tensor)
print(tensor.shape)
print(tensor.dtype)
print(tensor.device)
tensor([[0.9727, 0.9215]])
torch.Size([1, 2])
torch.float32
cpu
차원 변환
텐서의 차원을 변환하는 방식은 넘파이에서의 배열의 차원 변환 방법과 동일하다.
# 텐서 차원 변환 예제
tensor = torch.rand(1, 2)
print(tensor)
print(tensor.shape)
tensor = tensor.reshape(2, 1)
print(tensor)
print(tensor.shape)
tensor([[0.8647, 0.7841]])
torch.Size([1, 2])
tensor([[0.8647],
[0.7841]])
torch.Size([2, 1])
자료형 설정
텐서의 자료형 설정도 넘파이와 동일한 방식이지만, 선언 방식에 차이점이 존재한다.
# 텐서 자료형 설정 예제
tensor = torch.rand((3, 3), dtype=torch.float)
print(tensor)
tensor([[0.8647, 0.7841]])
torch.Size([1, 2])
tensor([[0.8647],
[0.7841]])
torch.Size([2, 1])
텐서의 자료형 설정 인수는 torch.*의 형태를 가지며, float로 할당하는 것도 가능하지만,
torch.float와 float는 서로 다른 자료형을 갖는다. (torch.float = 32비트 부동 소수점, float = 64비트 부동 소수점)
float로 할당하는 것은 더 많은 자원을 사용하므로, 텐서의 자료형을 설정할 땐 가급적이면 torch.*의 형태로 할당하는 것을 권장한다.
장치 설정
장치 설정을 정확하게 할당하지 않으면 Runtime Error가 발생하거나, CPU 연산이 되어 학습 시간을 기하급수적으로 늘리기 때문에 텐서의 장치 설정은 매우 중요하다.
torch.cuda.is_available() 를 이용하면 CUDA 사용 여부를 확인할 수 있고, 현재 소스 코드에 활용되는 장치를 통일할 수 있음.
# 텐서 GPU 장치 설정 예제
device = "cuda" if torch.cuda.is_available() else "cpu"
# Mac 환경에서는 CUDA 대신 MPS를 이용해 GPU 가속을 적용함함
# device = "mps" if torch.backends.mps.is_available() and torch.backends.mps.is_built() else "cpu"
cpu = torch.FloatTensor([1, 2, 3])
gpu = torch.cuda.FloatTensor([1, 2, 3])
tensor = torch.rand((1, 1), device=device)
print(device)
print(cpu)
print(gpu)
print(tensor)
cuda
tensor([1., 2., 3.])
tensor([1., 2., 3.], device='cuda:0')
tensor([[0.4928]], device='cuda:0')
장치 변환
CPU 텐서와 GPU 텐서는 상호 간 연산이 불가능하며, 같은 장치끼리만 연산할 수 있다.
넘파이 배열 데이터를 활용하려면 이를 GPU 장치로 변환해야 한다.
# 텐서 장치 변환 예제
cpu = torch.FloatTensor([1, 2, 3])
gpu = cpu.cuda()
gpu2cpu = gpu.cpu()
cpu2gpu = cpu.to("cuda")
print(cpu)
print(gpu)
print(gpu2cpu)
print(cpu2gpu)
tensor([1., 2., 3.])
tensor([1., 2., 3.], device='cuda:0')
tensor([1., 2., 3.])
tensor([1., 2., 3.], device='cuda:0')
넘파이 배열 → 텐서 변환
넘파이나 다른 라이브러리의 데이터를 파이토치에서 활용하려면 이를 텐서의 형태로 변환해야 한다.
넘파일 배열을 텐서로 변환하는 방법은 2가지가 있다.
- torch.tensor / torch.Tensor에 넘파이 배열을 그대로 입력하기
- from_numpy() 를 이용해 변환
# 넘파이 배열의 텐서 변환 예제
ndarray = np.array([1, 2, 3], dtype=np.uint8)
print(torch.tensor(ndarray))
print(torch.Tensor(ndarray))
print(torch.from_numpy(ndarray))
tensor([1, 2, 3], dtype=torch.uint8)
tensor([1., 2., 3.])
tensor([1, 2, 3], dtype=torch.uint8)
텐서 → 넘파이 배열 변환
추론된 결과를 후처리하거나 결괏값을 활용할 때, 텐서를 넘파이 배열로 변환한다. (OpenCV, Pillow 등은 넘파이 배열을 사용)
cpu() 를 이용해 텐서의 장치를 CPU로 변환한 뒤, numpy() 를 통해 이를 변환한다.
텐서는 이때 학습을 위한 데이터 형식으로 모든 연산을 추적하고 기록하며, 이후 역전파 등과 같은 연산이 진행되어 모델 학습이 이루어진다.
# 텐서의 넘파이 배열 변환 예제
tensor = torch.cuda.FloatTensor([1, 2, 3])
ndarray = tensor.detach().cpu().numpy()
print(ndarray)
print(type(ndarray))
[1. 2. 3.]
<class 'numpy.ndarray'>
텐서의 연산
텐서는 수학 연산, 삼각함수, 비트 연산, 비교 연산, 집계 등 다양한 연산을 지원한다.
x = torch.Tensor([[1, 2], [3, 4]])
y = torch.Tensor([[5, 6], [7, 8]])
print(torch.add(x, y)) # 덧셈
print(torch.sub(x, y)) # 뺄셈
print(torch.mul(x, y)) # 곱셈
print(torch.div(x, y)) # 나눗셈
print(torch.mm(x, y)) # 내적
tensor([[ 6., 8.],
[10., 12.]])
tensor([[-4., -4.],
[-4., -4.]])
tensor([[ 5., 12.],
[21., 32.]])
tensor([[0.2000, 0.3333],
[0.4286, 0.5000]])
tensor([[19., 22.],
[43., 50.]])
a = torch.Tensor([0.3, -0.6]) * 2 - 1
print(a)
print(torch.abs(a)) # 절댓값
print(torch.ceil(a)) # 올림
print(torch.floor(a)) # 내림
print(torch.clamp(a, -0.5, 0.5)) # clamp(tensor, min, max), 텐서의 값이 min과 max 사이에 위치하도록 조정함
tensor([-0.4000, -2.2000])
tensor([0.4000, 2.2000])
tensor([-0., -2.])
tensor([-1., -3.])
tensor([-0.4000, -0.5000])
a = torch.Tensor([0.3, -0.6]) * 2 - 1
print(a)
print(torch.min(a)) # 최솟값
print(torch.max(a)) # 최댓값
print(torch.mean(a)) # 평균
print(torch.var(a)) # 분산
print(torch.std(a)) # 표준편차
print(torch.sum(a)) # 합
print(torch.prod(a)) # 텐서 내 값 모두 곱하기
print(torch.unique(torch.Tensor([1, 2, 3, 1, 2, 2]))) # 중복 제거
tensor([-0.4000, -2.2000])
tensor(-2.2000)
tensor(-0.4000)
tensor(-1.3000)
tensor(1.6200)
tensor(1.2728)
tensor(-2.6000)
tensor(0.8800)
tensor([1., 2., 3.])
max와 min은 dim 인자를 줄 경우, argmax와 argmin도 함께 반환한다.
- argmax: 최댓값을 가진 인덱스
- argmin: 최솟값을 가진 인덱스
a = torch.rand(2, 2)
print(a)
print(a.max(dim=0)) # dim=0: 아래 방향 (행 방향)
print(a.max(dim=1)) # dim=1: 위 방향 (열 방향)
tensor([[0.0278, 0.0656],
[0.0544, 0.0233]])
torch.return_types.max(
values=tensor([0.0544, 0.0656]),
indices=tensor([1, 0]))
torch.return_types.max(
values=tensor([0.0656, 0.0544]),
indices=tensor([1, 0]))
텐서의 조작
인덱싱: 넘파이처럼 인덱싱 형태로 텐서를 사용할 수 있다.
a = torch.Tensor([[1, 2], [3, 4]])
print(a)
print(a[0, 0])
print(a[0, 1])
print(a[1, 0])
print(a[1, 1])
print(a[:, 0])
print(a[:, 1])
print(a[0, :])
print(a[1, :])
tensor([[1., 2.],
[3., 4.]])
tensor(1.)
tensor(2.)
tensor(3.)
tensor(4.)
tensor([1., 3.])
tensor([2., 4.])
tensor([1., 2.])
tensor([3., 4.])
view: 텐서의 크기나 모양 변경
- 기본적으로 변경 전과 변경 후에 텐서 안의 원소 개수가 유지되어야 한다.
- -1로 설정하면 계산을 통해 해당 크기 값을 유추한다.
a = torch.Tensor([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
print (a)
print (a.view(10))
print (a.view(5, -1))
tensor([[ 1., 2., 3., 4., 5.],
[ 6., 7., 8., 9., 10.]])
tensor([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])
tensor([[ 1., 2.],
[ 3., 4.],
[ 5., 6.],
[ 7., 8.],
[ 9., 10.]])
item: 텐서에 하나의 값만 존재한다면, 그 값을 얻을 수 있음.
a = torch.rand(1)
print(a)
print(a.item())
print(a.dtype)
tensor([0.7881])
0.7881014943122864
torch.float32
squeeze: 차원을 축소 (제거)
a = torch.Tensor([[[1, 2, 3], [4, 5, 6], [7, 8, 9]]])
print(a)
print(a.shape)
t = a.squeeze()
print(t)
print(t.shape)
tensor([[[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]]])
torch.Size([1, 3, 3])
tensor([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]])
torch.Size([3, 3])
unsqueeze: 차원을 확장 (추가)
a = torch.Tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(a)
print(a.shape)
t = a.unsqueeze(dim=0)
print(t)
print(t.shape)
t = a.unsqueeze(dim=2)
print(t)
print(t.shape)
tensor([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]])
torch.Size([3, 3])
tensor([[[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]]])
torch.Size([1, 3, 3])
tensor([[[1.],
[2.],
[3.]],
[[4.],
[5.],
[6.]],
[[7.],
[8.],
[9.]]])
torch.Size([3, 3, 1])
stack: 새로운 차원으로 주어진 텐서들을 결합함.
tensor([1., 4.])
tensor([2., 5.])
tensor([3., 6.])
tensor([[1., 4.],
[2., 5.],
[3., 6.]])
cat: 주어진 차원을 기준으로 주어진 텐서들을 결합함.
- 넘파이의 stack과 유사하지만, 쌓을 dim이 존재해야 함.
- 해당 차원을 늘려준 후 결합.
a = torch.Tensor([[[1, 2, 3], [4, 5, 6], [7, 8, 9]]])
b = torch.Tensor([[[10, 11, 12], [13, 14, 15], [16, 17, 18]]])
print(a)
print(b)
c = torch.cat((a, b), dim=0)
print(c)
print(c.size())
c = torch.cat((a, b), dim=1)
print(c)
print(c.size())
tensor([[[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]]])
tensor([[[10., 11., 12.],
[13., 14., 15.],
[16., 17., 18.]]])
tensor([[[ 1., 2., 3.],
[ 4., 5., 6.],
[ 7., 8., 9.]],
[[10., 11., 12.],
[13., 14., 15.],
[16., 17., 18.]]])
torch.Size([2, 3, 3])
tensor([[[ 1., 2., 3.],
[ 4., 5., 6.],
[ 7., 8., 9.],
[10., 11., 12.],
[13., 14., 15.],
[16., 17., 18.]]])
torch.Size([1, 6, 3])
chunk: 텐서를 여러 개로 나눔.
a = torch.rand(3, 6)
t1, t2, t3 = torch.chunk(a, 3, dim=1)
print(a)
print(t1)
print(t2)
print(t3)
tensor([[0.3548, 0.1087, 0.8347, 0.2305, 0.3299, 0.9947],
[0.2134, 0.4035, 0.8664, 0.1169, 0.6560, 0.5246],
[0.8009, 0.8986, 0.0624, 0.4194, 0.9665, 0.7996]])
tensor([[0.3548, 0.1087],
[0.2134, 0.4035],
[0.8009, 0.8986]])
tensor([[0.8347, 0.2305],
[0.8664, 0.1169],
[0.0624, 0.4194]])
tensor([[0.3299, 0.9947],
[0.6560, 0.5246],
[0.9665, 0.7996]])
split: chunk와 동일한 기능을 하지만, 입력으로 받는 인자가 다름.
- chunk: n개의 텐서로 나눔
- split: n개의 데이터가 있는 텐서로 나눔.
a = torch.rand(3, 6)
t1, t2 = torch.split(a, 3, dim=1)
print(a)
print(t1)
print(t2)
tensor([[0.8657, 0.8427, 0.5138, 0.4691, 0.0391, 0.5273],
[0.7771, 0.7291, 0.4640, 0.0611, 0.9179, 0.0670],
[0.7253, 0.4829, 0.2022, 0.1030, 0.6004, 0.8482]])
tensor([[0.8657, 0.8427, 0.5138],
[0.7771, 0.7291, 0.4640],
[0.7253, 0.4829, 0.2022]])
tensor([[0.4691, 0.0391, 0.5273],
[0.0611, 0.9179, 0.0670],
[0.1030, 0.6004, 0.8482]])'ML · DL > Pytorch 공부' 카테고리의 다른 글
| [파이토치 기초] 데이터세트 분리 (0) | 2025.01.07 |
|---|---|
| [파이토치 기초] 모듈 (0) | 2025.01.04 |
| [파이토치 기초] 데이터세트 & 데이터로더 (0) | 2024.12.30 |
| [파이토치 기초] 경사 하강법 (0) | 2024.12.26 |
| [파이토치 기초] 손실 함수 (0) | 2024.12.24 |