1. 파이토치
파이토치PyTorch는 2017년 초에 공개된 딥러닝 프레임워크
로 개발자들과 연구자들이 쉽게 GPU를 활용하여 인공 신경망 모델을 만들고 학습
시킬 수 있게 도와 줍니다. 파이토치의 전신이라고 할 수 있는 토치Torch는 루아 프로그래밍 언어로 되어 있었지만, 파이토치는 파이썬으로 작성되어 파이썬의 언어 특징을 많이 가지고 있습니다. 파이토치는 페이스북의 인공지능 연구팀AI
Research 멤버들이 주로 관리하며, 독자적으로 운영되는 파이토치 포럼은 사람들이 질문을 올리면 프레임워크 개발자를 비롯한 많은 사람이 답을 해주는 등 활발히 교류가 일어나고 있습니다.
2. 다른 프레임워크와의 비교
넘파이 vs 파이토치
넘파이를 사용하는 것과 파이토치 프레임워크를 사용하는 것의 큰 차이점 중 하나는 GPU를 통한 연산 가능 여부
입니다. 넘파이만으로는 GPU로 값들을 보내 연산을 돌리고 다시 받는 것이 불가능합니다. 이에 비해 파이토치는 내부적으로 CUDA, cuDNN이라는 API를 통해 GPU를 연산에 사용할 수 있고, 이로 인해 생기는 연산 속도의 차이는 엄청납니다. CUDA는 엔비디아가 GPU를 통한 연산을 가능하게 만든 API 모델이며, cuDNN은 CUDA를 이용해 딥러닝 연산을 가속해주는 라이브러리입니다.
병렬 연산에서 GPU의 속도는 CPU의 속도보다 월등히 빠르며 이 차이는 지속적으로 벌어지고 있습니다. CUDA와 cuDNN을 둘 다 사용하면 연산 속도가 CPU의 15배 이상이 된다고 알려져 있습니다. CPU로 한 달 걸릴 연산을 이틀이면 할 수 있는 셈입니다. 심층 신경망을 만들 때 함수 및 기울기 계산, 그리고 GPU를 이용한 연산 가속 등의 장점이 있기 때문에 딥러닝 개발 시 프레임워크의 사용은 필수라고 할 수 있습니다.
텐서플로 vs 파이토치
텐서플로와 파이토치는 둘 다 연산에 GPU를 이용하는 프레임워크입니다. 하지만 텐서플로는 연산 그래프를 먼저 만들고 실제 연산할 때 값을 전달하여 결과를 얻는 ‘Define and Run’
방식이고, 파이토치는 그래프를 만듦과 동시에 값이 할당되는 ‘Define by Run’ 방식입니다. 텐서플로의 ‘그래프를 먼저 정의하고 세션에서 실제로 값을 집어넣어 결과를 도출’하는 패러다임은 사람에 따라 직관적으로 받아들이기 어려울 수 있고, 그래프를 정의하는 부분과 이를 돌리는 부분이 분리되기 때문에 전체적으로도 코드 길이가 길어지게 됩니다. 반면 파이토치는 연산 그래프를 정의하는 것과 동시에 값도 초기화되어 연산이 이루어지는 ‘Define by Run’이므로 연산 그래프와 연산을 분리해서 생각할 필요가 없습니다.
또한 연산 속도
에서도 차이가 있습니다. 연산 그래프를 고정해놓고 값만 전달하는 텐서플로가 더 빠른 환경도 있을 수 있겠지만, 텐서플로 깃허브에 올라온 이슈에 따르면 실험에 많이 사용되는 모델로 벤치마킹한 결과 파이토치가 텐서플로보다 2.5배 빠른 결과가 나왔다고 합니다. 이슈를 올린 사람이 실험 환경을 공개하여 다른 사람들이 같은 코드로 실험한 결과 역시 파이토치가 빠르게 나왔습니다. 모델마다, 사용한 함수마다 차이는 있겠지만 파이토치는 전반적으로 속도 면에서 텐서플로보다 빠르거나, 적어도 밀리지는 않는다고 할 수 있습니다.
텐서플로가 1년 정도 먼저 딥러닝이 뜨기 시작할 때 발표되어 사용자가 많은 것은 사실입니다. 이에 비해 파이토치는 뒤늦게 사람들에게 알려지고 있는 상태입니다. 텐서플로는 자체적으로 운영하는 포럼이 없고 구글 그룹도 편리하게 정리되어 있지는 않습니다. 반면 파이토치는 자체 운영 포럼이 있어서 질문을 올리면 파이토치 개발자들이 직접 답변을 달아주기도 합니다. 한국 커뮤니티를 보면 텐서플로는 Tensorflow-KR, 파이토치는 PyTorch-KR 페이스북 그룹이 있으며, 많은 사람이 질문을 올리거나 유용한 팁을 공유하고 있습니다.
3. 파이토치 설치
파이토치 홈페이지에 접속 후 원하는 값을 설정하고 밑에 나타나는 명령어를 아나콘다 프롬프트 창에 입력
저같은 경우에는 CUDA 11.0 버전을 필요로 했기에 파이토치 1.7 버전대를 설치하기 위해 Previous Pytorch Versions에서 제가 원하는 세팅값을 골랐습니다.
파이토치가 잘 설치되었는지 확인해보겠습니다.
4. 텐서 연산
데이터 타입
파이토치의 텐서
- 파이토치의 기본 단위
- 다차원 배열을 처리하기 위한 데이터 구조
- Numpy의 ndarray와 거의 같은 API를 지니고 있다.
- GPU를 사용한 계산도 지원한다.
- 어떤 데이터 형의 텐서이건 torch.tensor라는 함수로 작성할 수 있다.
import torch
torch.__version__
--------------------
'1.7.1'
a = torch.FloatTensor([[1, 2], [3, 4]])
b = torch.LongTensor([[1, 2], [3, 4]])
c = torch.ByteTensor([[1, 2], [3, 4]])
a.shape
a.size()
a.size(-1)
a.dim()
-----------
torch.Size([2, 2])
torch.Size([2, 2])
2
2
# 가장 쉽게 쓸 수 있는 방식, 입력 값과 같은 텐서 새로 만든다 -> 메모리 공유 X
# 메모리 낭비 커질 수 있다
torch.tensor([[1, 2], [3, 4]])
torch.tensor(a, dtype=float64, device=device=torch.device('cpu'))
# 텐서를 넘파이 배열로
a = a.numpy()
# 넘파이 배열을 텐서로
a = torch.from_numpy(a)
a = torch.as_tensor(a)
a = torch.FloatTensor([[1, 2],
[3, 4]])
b = torch.FloatTensor([[2, 2],
[3, 3]])
a+b
-------------------------
tensor([[3., 4.],
[6., 7.]])
a-b
-------------------------
tensor([[-1., 0.],
[ 0., 1.]])
a*b
-------------------------
tensor([[ 2., 4.],
[ 9., 12.]])
a/b
-------------------------
tensor([[0.5000, 1.0000],
[1.0000, 1.3333]])
a==b
-------------------------
tensor([[False, True],
[ True, False]])
a**b
-------------------------
tensor([[ 1., 4.],
[27., 64.]])
a = torch.FloatTensor([[1, 2],
[3, 4]])
b = torch.FloatTensor([[2, 2],
[3, 3]])
# a*b의 결과를 리턴
a.mul(b)
# a*b의 결과를 a에 리턴
a.mul_(b)
# a+b의 결과를 a에 리턴
a.add_(b)
a = torch.FloatTensor([[1, 2],
[3, 4]])
a.sum()
a.mean()
----------
10
2.5
a.sum(dim=0)
a.sum(dim=-1)
--------------
tensor([4., 6.])
tensor([3., 7.])
# 텐서가 갖는 단일 스칼라 값을 리턴해준다
x = torch.tensor([1])
print(x, x.item())
-------------------------
tensor([1]) 1
x = torch.FloatTensor([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]],
[[9, 10],
[11, 12]]])
x.reshape(12)
x.reshape(-1)
------------------------------------
tensor([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.])
tensor([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.])
x.reshape(3, 1, 4)
x.reshape(-1, 1, 4)
------------------------------------
tensor([[[ 1., 2., 3., 4.]],
[[ 5., 6., 7., 8.]],
[[ 9., 10., 11., 12.]]])
tensor([[[ 1., 2., 3., 4.]],
[[ 5., 6., 7., 8.]],
[[ 9., 10., 11., 12.]]])
x.view(-1)
-------------------------------------
tensor([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.])
x.view(1, -1)
-------------------------------------
tensor([[ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.]])
# size: (1, 2, 2)
x = torch.FloatTensor([[[1, 2],
[3, 4]]])
# size: (2, 2)
x = x.squeeze()
# size: (1, 2, 2)
x = x.unsqueeze(0)
# size: (2, 1, 2)
x = x.unsqueeze(1)
# size: (2, 2, 1)
x = x.unsqueeze(-1)
x = torch.FloatTensor([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]],
[[9, 10],
[11, 12]]])
x[0]
x[0, :]
x[0, :, :]
-------------------------------------
tensor([[1., 2.],
[3., 4.]])
tensor([[1., 2.],
[3., 4.]])
tensor([[1., 2.],
[3., 4.]])
x[1:3, :, :]
----------------------------------------
tensor([[[ 5., 6.],
[ 7., 8.]],
[[ 9., 10.],
[11., 12.]]])
x = torch.FloatTensor(10, 4)
# 4개씩 자른다
splits = x.split(4, dim=0)
splits
----------------------------------------------------------
(tensor([[0.0000e+00, 1.0102e-38, 1.8788e+31, 1.7220e+22],
[1.8704e+20, 6.5986e-10, 1.3029e-11, 1.0524e+21],
[2.0314e+20, 6.6766e-07, 1.7000e+22, 1.7567e-04],
[4.1765e-08, 2.4693e-18, 1.8788e+31, 7.9303e+34]]),
tensor([[6.1949e-04, 2.8183e+20, 3.2995e-18, 1.9421e+31],
[2.7491e+20, 6.1949e-04, 7.1856e+22, 4.3605e+27],
[2.3329e-18, 1.9284e+31, 3.2314e-18, 6.6448e+22],
[1.6805e-04, 1.6149e-07, 5.2711e-08, 2.6586e+23]]),
tensor([[2.0891e+20, 2.6965e+23, 4.3125e-08, 2.4286e-18],
[2.6302e+20, 6.1949e-04, 1.0256e-08, 6.5631e-07]]))
# 3개의 덩어리로 나눈다
chunks = x.chunk(3, dim=0)
chunks
---------------------------------------------------------
(tensor([[0.0000e+00, 1.0102e-38, 1.8788e+31, 1.7220e+22],
[1.8704e+20, 6.5986e-10, 1.3029e-11, 1.0524e+21],
[2.0314e+20, 6.6766e-07, 1.7000e+22, 1.7567e-04],
[4.1765e-08, 2.4693e-18, 1.8788e+31, 7.9303e+34]]),
tensor([[6.1949e-04, 2.8183e+20, 3.2995e-18, 1.9421e+31],
[2.7491e+20, 6.1949e-04, 7.1856e+22, 4.3605e+27],
[2.3329e-18, 1.9284e+31, 3.2314e-18, 6.6448e+22],
[1.6805e-04, 1.6149e-07, 5.2711e-08, 2.6586e+23]]),
tensor([[2.0891e+20, 2.6965e+23, 4.3125e-08, 2.4286e-18],
[2.6302e+20, 6.1949e-04, 1.0256e-08, 6.5631e-07]]))
# (3, 2, 2)
x = torch.FloatTensor([[[1, 1],
[2, 2]],
[[3, 3],
[4, 4]],
[[5, 5],
[6, 6]]])
indice = torch.LongTensor([2, 1])
y = x.index_select(dim=0, index=indice)
# (2, 2, 2)
y
tensor([[[5., 5.],
[6., 6.]],
[[3., 3.],
[4., 4.]]])
x = torch.FloatTensor([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
y = torch.FloatTensor([[10, 11, 12],
[13, 14, 15],
[16, 17, 18]])
z = torch.cat([x, y], dim=0)
z
z.size()
-------------------------------------
tensor([[ 1., 2., 3.],
[ 4., 5., 6.],
[ 7., 8., 9.],
[10., 11., 12.],
[13., 14., 15.],
[16., 17., 18.]])
torch.Size([6, 3])
z = torch.cat([x, y], dim=-1)
z
z.size()
------------------------------------
tensor([[ 1., 2., 3., 10., 11., 12.],
[ 4., 5., 6., 13., 14., 15.],
[ 7., 8., 9., 16., 17., 18.]])
torch.Size([3, 6])
z = torch.stack([x, y])
z
z.size()
-----------------------------------
tensor([[[ 1., 2., 3.],
[ 4., 5., 6.],
[ 7., 8., 9.]],
[[10., 11., 12.],
[13., 14., 15.],
[16., 17., 18.]]])
torch.Size([2, 3, 3])
z = torch.stack([x, y], dim=1)
z
z.size()
------------------------------------
tensor([[[ 1., 2., 3.],
[10., 11., 12.]],
[[ 4., 5., 6.],
[13., 14., 15.]],
[[ 7., 8., 9.],
[16., 17., 18.]]])
torch.Size([3, 2, 3])
# (2, 1, 2)
x = torch.FloatTensor([[[1, 2]],
[[3, 4]]])
y = x.expand([2, 3, 2])
y
y.size()
-----------------------------------
tensor([[[1., 2.],
[1., 2.],
[1., 2.]],
[[3., 4.],
[3., 4.],
[3., 4.]]])
torch.Size([2, 3, 2])
x = torch.randperm(10)
x
------------------------
tensor([9, 0, 8, 4, 3, 6, 5, 2, 7, 1])
x = torch.randperm(3**3).reshape(3, 3, -1)
x
------------------------------------------
tensor([[[18, 9, 25],
[ 0, 16, 8],
[24, 20, 14]],
[[ 1, 4, 17],
[ 2, 22, 7],
[ 5, 10, 12]],
[[15, 13, 23],
[ 3, 21, 19],
[26, 6, 11]]])
y = x.argmax(dim=-1)
y
-----------------------------------------
tensor([[2, 1, 0],
[2, 1, 2],
[2, 1, 0]])
torch.ones(2, 3)
torch.zeros(2, 3)
---------------------------------------
tensor([[1., 1., 1.],
[1., 1., 1.]])
tensor([[0., 0., 0.],
[0., 0., 0.]])
x = torch.FloatTensor([[1, 2, 3],
[4, 5, 6]])
torch.ones_like(x)
torch.zeros_like(x)
-----------------------------------------
tensor([[1., 1., 1.],
[1., 1., 1.]])
tensor([[0., 0., 0.],
[0., 0., 0.]])
5. GPU를 이용한 텐서 계산
🔔 같은 디바이스에 있는 텐서끼리만 연산이 가능하다 (RAM에 있는 텐서끼리 또는 GPU RAM에 있는 텐서끼리)
1) Convert to CUDA tensor
import torch
import torch.nn as nn
x = torch.cuda.FloatTensor(2, 2)
x
--------------------------------
tensor([[0., 0.],
[0., 0.]], device='cuda:0')
x = torch.FloatTensor(2, 2)
x
--------------------------------
tensor([[-4.4256e-10, 4.5685e-41],
[-4.4256e-10, 4.5685e-41]])
# move아니고 copy -> x는 여전히 CPU에 있다
x.cuda()
--------------------------------
tensor([[-4.4256e-10, 4.5685e-41],
[-4.4256e-10, 4.5685e-41]], device='cuda:0')
d = torch.device('cuda:0')
x.cuda(device=d)
--------------------------------
tensor([[-4.4256e-10, 4.5685e-41],
[-4.4256e-10, 4.5685e-41]], device='cuda:0')
x.device
----------------------------------
device(type='cpu')
# 조금 더 최근에 나온 방법
x.to(device=d)
----------------------------------
tensor([[0.0000e+00, 0.0000e+00],
[7.0065e-45, 0.0000e+00]], device='cuda:0')
2) Convert to CPU tensor from CUDA tensor
x = torch.cuda.FloatTensor(2, 2)
x = x.cpu()
d = torch.device('cpu')
x = x.to(d)
3) Move model from CPU to GPU
linear = nn.Linear(2, 2)
def print_params(model):
for p in model.parameters():
print(p)
print_params(linear)
-------------------------------------------
Parameter containing:
tensor([[-0.6343, 0.0866],
[-0.2044, -0.0207]], requires_grad=True)
Parameter containing:
tensor([0.1993, 0.5434], requires_grad=True)
linear = linear.cuda()
print_params(linear)
-----------------------------------------------
Parameter containing:
tensor([[ 0.5782, 0.4288],
[-0.0220, -0.1614]], device='cuda:0', requires_grad=True)
Parameter containing:
tensor([0.6881, 0.0907], device='cuda:0', requires_grad=True)
linear = linear.cpu()
print_params(linear)
----------------------------------------------
Parameter containing:
tensor([[ 0.5782, 0.4288],
[-0.0220, -0.1614]], requires_grad=True)
Parameter containing:
tensor([0.6881, 0.0907], requires_grad=True)
d = torch.device('cuda:0')
linear = linear.to(d)
print_params(linear)
--------------------------------------------------
Parameter containing:
tensor([[-0.6343, 0.0866],
[-0.2044, -0.0207]], device='cuda:0', requires_grad=True)
Parameter containing:
tensor([0.1993, 0.5434], device='cuda:0', requires_grad=True)
🔔 앞에서 정의한 텐서나 모델과 같은 장치에 있는 새로운 텐서 또는 모델 만들기
x = torch.cuda.FloatTensor(2, 2)
x.new(2, 2)
----------------------------------
tensor([[2.3694e-38, 1.2422e-01],
[1.4013e-45, 0.0000e+00]], device='cuda:0')
torch.zeros_like(x)
-----------------------------------
tensor([[0., 0.],
[0., 0.]], device='cuda:0')
torch.ones_like(x)
-----------------------------------
tensor([[1., 1.],
[1., 1.]], device='cuda:0')
list(linear.parameters())[0].new(2, 2)
-------------------------------------
tensor([[1., 1.],
[1., 1.]], device='cuda:0')