대부분의 머신러닝은 데이터 조작, 모델 생성, 모델 parameters 업데이트, 그리고 훈련된 모델 저장 등의 순서로 진행됩니다. 머신러닝을 위해서 Pytorch, tensorflow, keras 등 많은 라이브러리들이 사용되지만, 저는 PyTorch를 많이 사용하고 있습니다. PyTorch를 사용하기 위해 필수 지식들을 소개합니다. 해당 내용은 PyTorch tutorial에 제공되어 있습니다.
머신러닝 혹은 딥러닝을 공부하기 위해서는 Tensor의 개념을 알아두는 것이 좋습니다. Tensors란 배열 혹은 행렬과 매우 비슷한 데이터 구조를 의미합니다. PyTorch에서는 tensors를 사용해서 모델의 parameters, inputs, outputs을 인코딩합니다.
또한, Tensors는 Numpy의 배열과 비슷하지만 GPUs 혹은 다른 하드웨어 엑셀레이터에서 실행될 수 있다는 점에서 차이가 있습니다. 사실 tensors는 Numpy 배열과 메모리를 공유해서 사용하기도 하고, Automatic differentiation(자동 미분)에 최적화 되어있습니다.
tensors와 Numpy를 비교하기 위해서 라이브러리들을 받아와줍니다.
import torch
import numpy as np
Tensors는 다양한 방법으로 초기화 될 수 있습니다. 다음을 통해 살펴보도록 하죠!
torch.tensor(data)를 사용해서 Tensors 생성할 수 있습니다. 데이터 타입은 자동으로 맞춰집니다.
data = [[1,2], [3,4]]
x_data = torch.tensor(data)
x_data, x_data.shape
#output:
(tensor([[1, 2],
[3, 4]]),
torch.Size([2, 2]))
Tensors는 또한 torch.from_numpy를 사용해서 Numpy 배열을 통해 생성될 수 있습니다.
np_array = np.array(data)
x_np = torch.from_numpy(np_array)
np_array, np_array.shape
#output:
(array([[1, 2],
[3, 4]]),
(2, 2))
x_np, x_np.shape
#output:
(tensor([[1, 2],
[3, 4]]),
torch.Size([2, 2]))
위에서 생성한 tensor를 사용해서 새로운 tensor를 만들 수 있습니다. 새로운 Tensor는 명시적으로 override 하지 않는 한 텐서의 속성(모양, 데이터유형)을 그대로 유지합니다.
x_ones = torch.ones_like(x_np) # x_np의 데이터 타입, shape 그대로 유지
print(f"Ones Tensor: \n {x_ones} \n")
x_rand = torch.rand_like(x_np, dtype=torch.float) # x_np의 shape 그대로 유지
print(f"Random Tensor: \n {x_rand} \n")
#output:
Ones Tensor:
tensor([[1, 1],
[1, 1]])
Random Tensor:
tensor([[0.1663, 0.6293],
[0.5899, 0.5046]])
shape은 텐서의 dimensions tuple 입니다. shape 사이즈를 지정하여 output tensor의 차원을 결정할 수 있습니다.
shape = (2,3,) # same as shape = (2,3)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)
print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")
output:
Random Tensor:
tensor([[0.1422, 0.2771, 0.9069],
[0.8241, 0.7967, 0.5327]])
Ones Tensor:
tensor([[1., 1., 1.],
[1., 1., 1.]])
Zeros Tensor:
tensor([[0., 0., 0.],
[0., 0., 0.]])
Tensor의 shape, datatype과, 그리고 어디에 저장되어있는지 확인할수 있습니다.
tensor = torch.rand(3,4,)
print(f"shape of tensor: {tensor.shape}")
print(f"datatype of tensor: {tensor.dtype}")
print(f"device tensor is stored on: {tensor.device}")
output:
Shape of tensor: torch.Size([3, 4])
Datatype of tensor: torch.float32
Device tensor is stored on: cpu
arithmetic, linear algebra, matrix 연산들은 GPU에서 실행되는 것이 훨씬 빠릅니다. Tensor는 디폴트로 CPU에 생성되지만, GPU로 실행하기 위해 .to 메소드를 통해 GPU로 옮길 수 있습니다.
if torch.cuda.is_available():
tensor = tensor.to("cuda")
tensor = torch.ones(4,4)
print(f"{tensor} \n")
print(f"First row: {tensor[0]}")
print(f"First column: {tensor[:, 0]}")
print(f"Last column: {tensor[..., -1]}")
tensor[:,1] =0
print(tensor)
output:
tensor([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
First row: tensor([1., 1., 1., 1.])
First column: tensor([1., 1., 1., 1.])
Last column: tensor([1., 1., 1., 1.])
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]])
torch.cat을 사용해서 tensor를 join 시킬 수 있습니다.
t1 = torch.cat([tensor, tensor, tensor], dim=1)
print(t1)
#output: 4*4 사이즈의 텐서가 3번 join됨
tensor([[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.]])
두개 tensors를 가지고 행렬 연산을 할 수 있습니다.
y1 = tensor @ tensor.T
y2 = tensor.matmul(tensor.T)
y3 = tensor.rand_like(y1)
torch.matmul(tensor, tensor.T, out=y3)
y1, y2, y3
#output:
(tensor([[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.]]),
tensor([[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.]]),
tensor([[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.]]))
z1 = tensor * tensor
z2 = tensor.mul(tensor)
z3 = torch.rand_like(tensor)
torch.mul(tensor, tensor, out=z3)
z1, z2, z3
#output:
(tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]]),
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]]),
tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]]))
item()을 사용해서, 모든 텐서의 values를 하나로 집합시킬 수 있습니다.
agg = tensor.sum()
agg_item = agg.item()
print(agg, agg_item, type(agg_item))
#output:
tensor(12.) 12.0 <class 'float'>
_ 접미사를 사용해서 결과를 바로 저장할 수 있습니다 (e.g., x.copy(y), x.t()).
print(f"{tensor} \n")
tensor.add_(5)
print(tensor)
#output:
tensor([[6., 5., 6., 6.],
[6., 5., 6., 6.],
[6., 5., 6., 6.],
[6., 5., 6., 6.]])
tensor([[11., 10., 11., 11.],
[11., 10., 11., 11.],
[11., 10., 11., 11.],
[11., 10., 11., 11.]])