다운로드
작성자: admin 작성일시: 2018-12-03 14:38:03 조회수: 2204 다운로드: 68
카테고리: 기타 태그목록:

PyTorch 패키지 소개

PyTorch는 Tensorflow와 함께 딥러닝 구현에 가장 많이 사용되는 Graph 기반의 GPU 연산을 지원하는 패키지이다. 사용 방법이 비교적 쉬워 교육 및 연구에 많이 사용되고 있다.

설치

PyTorch는 Conda를 이용해 설치하는 것을 추천한다.

conda install PyTorch-cpu torchvision-cpu -c PyTorch

torch라는 이름으로 임포트한다.

In [1]:
import torch
import numpy as np 
import pandas as pd 
torch.__version__
Out:
'1.0.0'

텐서 자료형

PyTorch의 텐서(Tensor) 자료형은 NumPy의 배열(ndarray)과 유사한 자료형이다. 텐서 자료형을 사용하는 방법도 NumPy와 비슷하다. 텐서 자료형 데이터를 만드는 방법은 3 가지가 있다.

  • 리스트나 NumPy 배열을 텐서로 변환
  • 0 또는 1 등의 특정한 값을 가진 텐서를 생성
  • 랜덤한 값을 가지는 텐서를 생성

배열과 리스트를 텐서 자료형으로 변환

리스트를 텐서 자료형으로 바꾸러면 torch.tensor() 또는 torch.as_tensor(), torch.from_numpy() 명령을 사용한다.

  • torch.tensor(): 값 복사(value copy)를 사용하여 새로운 텐서 자료형 인스턴스를 생성한다.
  • torch.as_tensor(): 리스트나 ndarray 객체를 받는다. 값 참조(refernce)를 사용하여 텐서 자료형 뷰(view)를 만든다.
  • torch.from_numpy(): ndarray 객체를 받는다. 값 참조(refernce)를 사용하여 텐서 자료형 뷰(view)를 만든다.
In [2]:
li = np.array([[1, 2], [3, 4]])

li_tensor = torch.tensor(li)
li_as_tensor = torch.as_tensor(li)

print(li_tensor)
print(li_as_tensor)
tensor([[1, 2],
        [3, 4]])
tensor([[1, 2],
        [3, 4]])

torch.tensor() 명령으로 텐서를 만들거나 torch.as_tensor()에 ndarray가 아닌 리스트를 넣었을 때는 이러한 일이 발생하지 않는다.

NumPy 배열을 텐서로 바꿀 때는 torch.from_numpy() 함수를 쓸 수 있다.

In [3]:
arr = np.array([[1, 2], [3, 4]])

arr_tensor = torch.tensor(arr)
arr_as_tensor = torch.from_numpy(arr)
arr_from_numpy = torch.from_numpy(arr)

print(arr_tensor, arr_tensor.dtype)
print(arr_as_tensor, arr_as_tensor.dtype)
print(arr_from_numpy, arr_from_numpy.dtype)
tensor([[1, 2],
        [3, 4]]) torch.int64
tensor([[1, 2],
        [3, 4]]) torch.int64
tensor([[1, 2],
        [3, 4]]) torch.int64

반대로 텐서를 NumPy 배열로 바꿀 때는 torch.numpy() 메서드를 사용한다.

In [4]:
print(arr_tensor.numpy())
print(arr_as_tensor.numpy())
print(arr_from_numpy.numpy())
[[1 2]
 [3 4]]
[[1 2]
 [3 4]]
[[1 2]
 [3 4]]

torch.as_tensor()torch.from_numpy()는 원래의 ndarray 객체를 참조하므로 원래 ndarray 객체의 값을 바꾸면 텐서 자료형의 값도 바뀌고 반대로 텐서 자료형에서 원소의 값을 바꾸면 원래 ndarray 객체의 값도 바뀐다.

In [5]:
arr_as_tensor[0, 0] = 1000
arr
Out:
array([[1000,    2],
       [   3,    4]])
In [6]:
arr_from_numpy  # 같은 객체를 참조한다.
Out:
tensor([[1000,    2],
        [   3,    4]])
In [7]:
arr[0, 1] = 2000
arr_as_tensor
Out:
tensor([[1000, 2000],
        [   3,    4]])

랜덤한 값을 가지는 텐서 생성

랜덤 값으로 채워진 텐서를 생성하는 명령은 다음과 같다.

  • torch.rand() : 0과 1사이의 숫자를 균등하게 생성
  • torch.rand_like() : 사이즈를 튜플로 입력하지 않고 기존의 텐서로 정의
  • torch.randn() : 평균이 0이고 표준편차가 1인 가우시안 정규분포를 이용해 생성
  • torch.randn_like() : 사이즈를 튜플로 입력하지 않고 기존의 텐서로 정의
  • torch.randint() : 주어진 범위 내의 정수를 균등하게 생성, 자료형은 torch.float32
  • torch.randint_like() : 사이즈를 튜플로 입력하지 않고 기존의 텐서로 정의
  • torch.randperm() : 주어진 범위 내의 정수를 랜덤하게 생성

랜덤 생성에 사용되는 시드(seed)는 torch.manual_seed() 명령으로 설정한다.

In [8]:
torch.manual_seed(0)

a = torch.rand(5)
b = torch.randn(5)
c = torch.randint(10, size=(5,))
d = torch.randperm(5)

print(a)
print(b)
print(c)
print(d)
tensor([0.4963, 0.7682, 0.0885, 0.1320, 0.3074])
tensor([ 0.5507,  0.2704,  0.6472,  0.2490, -0.3354])
tensor([8, 4, 3, 6, 9])
tensor([1, 3, 4, 2, 0])

특정한 값으로 초기화를 하지 않는 행렬을 만들 때는 torch.empty() 함수를 사용할 수 있다.

In [9]:
torch.empty(3, 4)
Out:
tensor([[1.1421e+03, 3.0625e-41, 3.3631e-44, 0.0000e+00],
        [       nan, 2.7478e-06, 1.1578e+27, 1.1362e+30],
        [7.1547e+22, 4.5828e+30, 1.2121e+04, 7.1846e+22]])

특정한 값의 텐서 생성하기

다음은 특정한 값으로 채워진 텐서를 생성하는 명령들이다.

  • torch.arange() : 주어진 범위 내의 정수를 순서대로 생성
  • torch.ones() : 주어진 사이즈의 1로 이루어진 텐서 생성
  • torch.zeros() : 주어진 사이즈의 0으로 이루어진 텐서 생성
  • torch.ones_like() : 사이즈를 튜플로 입력하지 않고 기존의 텐서로 정의
  • torch.zeros_like() : 사이즈를 튜플로 입력하지 않고 기존의 텐서로 정의
  • torch.linspace() : 시작점과 끝점을 주어진 갯수만큼 균등하게 나눈 간격점을 행벡터로 출력
  • torch.logspace() : 시작점과 끝점을 주어진 갯수만큼 로그간격으로 나눈 간격점을 행벡터로 출력
In [10]:
torch.arange(1, 10)
Out:
tensor([1, 2, 3, 4, 5, 6, 7, 8, 9])
In [11]:
torch.ones((2, 5))
Out:
tensor([[1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1.]])
In [12]:
torch.zeros((3, 5))
Out:
tensor([[0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]])
In [13]:
torch.linspace(0, 10, 5)
Out:
tensor([ 0.0000,  2.5000,  5.0000,  7.5000, 10.0000])

텐서 자료형 변환

텐서 내부의 데이터를 변환 할 때는 .type() 메서드를 사용한다. 다음 표는 PyTorch가 지원하는 자료형을 정리한 것이다. GPU tensor는 GPU 연산이 가능한 자료형을 뜻한다. GPU에 관련된 내용에서 자세히 설명한다.

Data type dtype CPU tensor GPU tensor
32-bit floating point torch.float32 또는 torch.float torch.FloatTensor torch.cuda.FloatTensor
64-bit floating point torch.float64 또는 torch.double torch.DoubleTensor torch.cuda.DoubleTensor
16-bit floating point torch.float16 또는 torch.half torch.HalfTensor torch.cuda.HalfTensor
8-bit integer (unsigned) torch.uint8 torch.ByteTensor torch.cuda.ByteTensor
8-bit integer (signed) torch.int8 torch.CharTensor torch.cuda.CharTensor
16-bit integer (signed) torch.int16 또는 torch.short torch.ShortTensor torch.cuda.ShortTensor
32-bit integer (signed) torch.int32 또는 torch.int torch.IntTensor torch.cuda.IntTensor
64-bit integer (signed) torch.int64 또는 torch.long torch.LongTensor torch.cuda.LongTensor
표 54.1: PyTorch 자료형
In [14]:
arr_tensor.dtype
Out:
torch.int64
In [15]:
arr_tensor.type(dtype=torch.int32)
Out:
tensor([[1, 2],
        [3, 4]], dtype=torch.int32)

텐서 형상 변환

텐서의 형상을 변환(reshape)하려면 .view() 함수를 사용한다.

In [16]:
t1 = torch.ones(4, 3)
t2 = t1.view(3, 4)
t3 = t1.view(12)

print(t1)
print(t2)
print(t3)
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]])
tensor([[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]])
tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])

텐서의 차원을 늘리거나 줄일 때도 .view()함수를 쓴다.

In [17]:
t1.view(1, 3, 4)
Out:
tensor([[[1., 1., 1., 1.],
         [1., 1., 1., 1.],
         [1., 1., 1., 1.]]])

squeeze() 명령이나 unsqueeze()를 사용해도 차원을 변환할 수 있다. squeeze()함수는 차원의 원소가 1인 차원을 없애주고, unsqueeze()함수는 인수로 받은 위치에 새로운 차원을 삽입한다.

In [18]:
t4 = torch.rand(1, 3, 3)
t4.shape
Out:
torch.Size([1, 3, 3])
In [19]:
t4.squeeze()
Out:
tensor([[0.9527, 0.0362, 0.1852],
        [0.3734, 0.3051, 0.9320],
        [0.1759, 0.2698, 0.1507]])
In [20]:
t5 = torch.rand(3, 3)
t5.shape
Out:
torch.Size([3, 3])
In [21]:
t5.unsqueeze(0).shape
Out:
torch.Size([1, 3, 3])
In [22]:
t5.unsqueeze(1).shape
Out:
torch.Size([3, 1, 3])

복수의 텐서를 결합

복수의 텐서를 결합할 때는 torch.cat()을 사용한다.

In [23]:
a = torch.ones(2, 3)
b = torch.zeros(3, 3)

torch.cat([a, b], dim=0)
Out:
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]])

텐서 분할

텐서를 여러개로 나눌 때는 torch.chunk(), torch.split() 명령을 사용한다.

In [24]:
c = torch.rand(3, 6)
c1, c2, c3 = torch.chunk(c, 3, dim=1)
print(c)
print(c1)
print(c2)
print(c3)
tensor([[0.1387, 0.2422, 0.8155, 0.7932, 0.2783, 0.4820],
        [0.8198, 0.9971, 0.6984, 0.5675, 0.8352, 0.2056],
        [0.5932, 0.1123, 0.1535, 0.2417, 0.7262, 0.7011]])
tensor([[0.1387, 0.2422],
        [0.8198, 0.9971],
        [0.5932, 0.1123]])
tensor([[0.8155, 0.7932],
        [0.6984, 0.5675],
        [0.1535, 0.2417]])
tensor([[0.2783, 0.4820],
        [0.8352, 0.2056],
        [0.7262, 0.7011]])
In [25]:
c1, c2 = torch.split(c, 3, dim=1)
print(c1)
print(c2)
tensor([[0.1387, 0.2422, 0.8155],
        [0.8198, 0.9971, 0.6984],
        [0.5932, 0.1123, 0.1535]])
tensor([[0.7932, 0.2783, 0.4820],
        [0.5675, 0.8352, 0.2056],
        [0.2417, 0.7262, 0.7011]])

텐서 연산

텐서 연산은 파이썬에서 사용하는 연산 기호를 사용하거나 torch의 함수를 사용한다.

In [26]:
x = torch.arange(0, 5)
z = torch.arange(1, 6)

print(x + z)
print(torch.add(x, z))
print(x.add(z))
tensor([1, 3, 5, 7, 9])
tensor([1, 3, 5, 7, 9])
tensor([1, 3, 5, 7, 9])

인플레이스 연산

명령어 뒤에 _ 를 붙이면 자기 자신의 값을 바꾸는 인플레이스(inplace) 명령이 된다. 인플레이스 명령은 연산 결과를 반환하면서 동시에 자기 자신의 데이터를 고친다.

In [27]:
x = torch.arange(0, 5)
z = torch.arange(1, 6)

print(x)
print(x.add_(z))
print(x)
tensor([0, 1, 2, 3, 4])
tensor([1, 3, 5, 7, 9])
tensor([1, 3, 5, 7, 9])

1개의 원소를 가진 Tensor를 Python의 Scalar로 만들 때는 .item()함수를 사용한다.

In [28]:
scl = torch.tensor(1)

print(scl.item())
1

GPU 사용

이전 데이터 타입에서 보았듯이 GPU 연산을 하기위해선 텐서를 GPU 연산이 가능한 자료형으로 변환하면 된다. 이에 앞서 본인의 환경에 GPU가 있어야 한다. 사용하는 환경이 GPU를 사용할 수 있는지 확인 하려면 torch.cuda.is_available()을 실행 시켜 보면 된다. 만약 GPU를 사용할 준비가 되었다면, True를 반환 할 것이다.

In [29]:
torch.cuda.is_available()
Out:
True

GPU 연산이 가능한 Tensor를 만드는 것은 device 인수에 GPU 디바이스 객체를 입력하거나 문자열을 입력하면 된다. 디바이스 객체는 torch.device("cuda:0")로 생성할 수 있다.

In [30]:
device = torch.device("cuda:0")
device
Out:
device(type='cuda', index=0)
In [31]:
ts = torch.rand(3,3,device=device)
ts
Out:
tensor([[0.0840, 0.2796, 0.5388],
        [0.5165, 0.8742, 0.3991],
        [0.4241, 0.4993, 0.0417]], device='cuda:0')
In [32]:
ts = torch.rand(3,3,device="cuda:0")
ts
Out:
tensor([[0.4189, 0.6343, 0.2984],
        [0.4794, 0.7766, 0.9346],
        [0.0996, 0.8428, 0.0675]], device='cuda:0')

기존에 있는 Tensor를 GPU 연산이 가능한 자료형으로 바꿀 때는= .cuda() 메서드를 사용한다.

In [33]:
cp = torch.rand(3, 3)
cp
Out:
tensor([[0.2038, 0.6511, 0.7745],
        [0.4369, 0.5191, 0.6159],
        [0.8102, 0.9801, 0.1147]])
In [34]:
cp.cuda()
Out:
tensor([[0.2038, 0.6511, 0.7745],
        [0.4369, 0.5191, 0.6159],
        [0.8102, 0.9801, 0.1147]], device='cuda:0')

autograd

autograd는 PyTorch에서 핵심적인 기능을 담당하는 하부 패키지이다. autograd는 텐서의 연산에 대해 자동으로 미분값을 구해주는 기능을 한다. 텐서 자료를 생성할 때, requires_grad인수를 True로 설정하거나 .requires_grad_(True)를 실행하면 그 텐서에 행해지는 모든 연산에 대한 미분값을 계산한다. 계산을 멈추고 싶으면 .detach()함수를 이용하면 된다.. 예제를 통해 알아 보도록 하자. requires_grad인수를 True로 설정하여 Tensor를 생성했다.

In [35]:
x = torch.rand(2, 2, requires_grad=True)
print(x)
tensor([[0.3168, 0.6965],
        [0.9143, 0.9351]], requires_grad=True)

다음으로 이 x에 연산을 수행한다. 다음 코드의 y는 연산의 결과이므로 미분 함수를 가진다. grad_fn속성을 출력해 미분 함수를 확인 할 수 있다.

In [36]:
y = torch.sum(x * 3)
print(y, y.grad_fn)
tensor(8.5879, grad_fn=) 

y.backward() 함수를 실행하면 x의 미분값이 자동으로 갱신된다. x의 grad속성을 확인하면 미분값이 들어 있는 것을 확인 할 수 있다. y를 구하기 위한 x의 연산을 수식으로 쓰면 다음과 같다.

$$ y = \displaystyle\sum_{i=1}^4 3 \times x_i $$

이를 $x_i$에 대해 미분 하면 미분 함수는 다음과 같다.

$$ \dfrac{\partial y}{\partial x_i} = 3 $$

실제 미분값과 같은지 확인해보자.

In [37]:
print(x.grad)
None
In [38]:
y.backward(retain_graph=True)
In [39]:
x.grad
Out:
tensor([[3., 3.],
        [3., 3.]])

backward()함수는 자동으로 미분값을 계산해 requires_grad인수가 True로 설정된 변수의 grad속성의 값을 갱신한다. retain_graph 미분을 연산하기 위해서 사용했던 임시 그래프를 유지 할 것인가를 설정하는 것이다. 기본값은 False로 설정되어 있지만 동일한 연산에 대해 여러번 미분을 계산하기 위해서는 True로 설정되어 있어야한다. 그리고 미분값을 그대로 출력받아 사용하고 싶은 경우에는 torch.autograd.grad()함수에 출력값과 입력값을 입력하면 미분값을 출력한다.

In [40]:
torch.autograd.grad(y, x)
Out:
(tensor([[3., 3.],
         [3., 3.]]),)

상황에 따라 특정 연산에는 미분값을 계산하고 싶지 않은 경우에는 .detach()함수를 사용한다. 예를 들어, 이전 코드의 결과 값 y에 로지스틱 함수 연산을 수행하고 이에 대한 미분 값을 계산 하고 싶지 않은 경우에 다음처럼 할 수 있다.

In [41]:
y_1 = y.detach()
torch.sigmoid(y_1)
Out:
tensor(0.9998)

PyTorch를 이용한 선형 회귀 구현

지금까지 배운 내용으로 선형회귀를 구현한다.

In [42]:
from sklearn.datasets import load_boston
boston = load_boston()
df = pd.DataFrame(boston.data, columns=boston.feature_names)
df['const'] = np.ones(df.shape[0])
df.tail()
Out:
CRIM ZN INDUS CHAS NOX RM AGE DIS RAD TAX PTRATIO B LSTAT const
501 0.06263 0.0 11.93 0.0 0.573 6.593 69.1 2.4786 1.0 273.0 21.0 391.99 9.67 1.0
502 0.04527 0.0 11.93 0.0 0.573 6.120 76.7 2.2875 1.0 273.0 21.0 396.90 9.08 1.0
503 0.06076 0.0 11.93 0.0 0.573 6.976 91.0 2.1675 1.0 273.0 21.0 396.90 5.64 1.0
504 0.10959 0.0 11.93 0.0 0.573 6.794 89.3 2.3889 1.0 273.0 21.0 393.45 6.48 1.0
505 0.04741 0.0 11.93 0.0 0.573 6.030 80.8 2.5050 1.0 273.0 21.0 396.90 7.88 1.0
In [43]:
n, m = df.shape

X = torch.tensor(df.values)
y = torch.tensor(boston.target).view(-1, 1)

XT = torch.transpose(X, 0, 1)
w = torch.matmul(torch.matmul(torch.inverse(torch.matmul(XT, X)), XT), y)
y_pred = torch.matmul(X, w)


print("예측한 집값 :", y_pred[19], "실제 집값 :", boston.target[19])
예측한 집값 : tensor([18.4078], dtype=torch.float64) 실제 집값 : 18.2

PyTorch를 이용한 퍼셉트론 구현

이번에는 붓꽃 분류 문제를 해결하는 퍼셉트론 모형을 구현한다.

In [44]:
from sklearn.datasets import load_iris

iris = load_iris()

idx = np.in1d(iris.target, [0, 2])
x = torch.from_numpy(iris.data[idx, 0:2]).type(torch.float64)
y = torch.from_numpy((iris.target[idx] - 1.0)
                     [:, np.newaxis]).type(torch.float64)

모형에 사용하는 변수를 requires_grad인수를 True로 하여 생성한다.

In [45]:
w = torch.rand((2, 1), dtype=torch.float64, requires_grad=True)
b = torch.rand((1, 1), dtype=torch.float64, requires_grad=True)

생성한 변수에 직접 연산을 수행해서는 안된다. 방금 예 처럼 "w" 라는 변수명으로 변수를 할당하고 이 변수를 연산 할 때 파이썬에서 하듯이

w = 0.001 * w

이렇게 해서는 안된다. 변수에 연산의 결과 값이 들어가면서 이 후 해당 변수는 이전에 생성한 변수가 아니고, 따라서 더이상 requires_grad가 True로 설정되어 있지 않다. 변수에 연산, 할당을 하고 싶을 때는 변수의 data속성을 불러와 변경하면 된다.

In [46]:
w.data *= 0.001
b.data *= 0.001

다음으로 SGD 최적화를 수행한다. .backward()를 사용하면 미분값이 계속 누적된다. 우리는 학습한 뒤 가중치를 수정하고 수정한 가중치에 대한 미분값으로 다시 학습하는 것이기 때문에 가중치를 수정한 뒤에 .grad_zero_()함수를 호출하여 변수에 할당된 미분값을 0으로 만들어준다.

In [47]:
learning_rate = 0.0001
epochs = 300

for epoch in range(epochs):
    z = (x.mm(w) + b).tanh()
    loss_vec = torch.mul(-y, z)
    loss = torch.sum(torch.max(
        torch.cat([torch.zeros_like(loss_vec), loss_vec], dim=-1), dim=-1)[0], dim=-1)
    loss.backward()
    w.data -= w.grad * learning_rate
    b.data -= b.grad * learning_rate
    if epoch % 40 == 0:
        print("{:3} - loss : {}".format(epoch, loss.item()))
    w.grad.zero_()
    b.grad.zero_()
  0 - loss : 0.24654596750657434
 40 - loss : 2.6319448802334113
 80 - loss : 0.012970540791669012
120 - loss : 0.012273524681832315
160 - loss : 0.011576751868071094
200 - loss : 0.011479927252553142
240 - loss : 0.01161257142689302
280 - loss : 0.010917003205824494
In [48]:
from sklearn.metrics import confusion_matrix

confusion_matrix(y, [-1 if p < 0 else 1 for p in z.detach().numpy()])
Out:
array([[49,  1],
       [ 1, 49]])

PyTorch를 이용한 신경망 구현

PyTorch 다양한 신경망 모형을 쉽게 구현 할 수 있도록 torch.nn.module을 제공한다. 이를 이용해 간단한 신경망 모형을 구현한다. PyTorch에서는 샘플데이터를 제공하지 않으므로 샘플데이터는 Keras를 이용해 MNIST 숫자 데이터를 불러오겠다.

In [49]:
from keras.datasets import fashion_mnist, mnist

import torch

(X_train0, y_train), (X_test0, y_test) = mnist.load_data()
Using TensorFlow backend.
In [50]:
print(X_train0.shape, X_train0.dtype)
print(y_train.shape, y_train.dtype)
print(X_test0.shape, X_test0.dtype)
print(y_test.shape, y_test.dtype)
(60000, 28, 28) uint8
(60000,) uint8
(10000, 28, 28) uint8
(10000,) uint8

이전에 Keras에서 했던 방식과 같이 전처리를 진행한다. 다만 여기서는 정답데이터에 대한 원핫인코딩작업을 하지 않는다. 이유는 바로 다음에 설명할 것이다. 전처리를 수행했으면 PyTorch가 사용할 수 있는 텐서로 변환하고 각각 알맞는 데이터 타입으로 변환한다. 이미지데이터에 대해서는 float 타입, 출력데이터에 대해서는 int64 타입으로 변환한다. 이는 추후 사용할 PyTorch의 함수가 해당 데이터 타입을 지원하기 때문이다.

In [51]:
X_train = X_train0.reshape(60000, 784).astype('float64') / 255.0
X_test = X_test0.reshape(10000, 784).astype('float64') / 255.0

X_train, y_train, X_test, y_test = map(
    torch.tensor, (X_train, y_train, X_test, y_test))

X_train, y_train, X_test, y_test = X_train.float(
), y_train.long(), X_test.float(), y_test.long()

torch.nn.Sequential을 이용하면 손쉽게 모델을 구성할 수 있다. 이전 Keras의 방식과 유사하다. 다만 명령어가 조금씩 다를 뿐이다. 이전에 Keras로 구현한 것과 동일한 구조로 구성한다. torch.nn.Linear는 레이어 간의 선형결합을 의미하고 이를 활성화함수에 통과시키면 이전에 배웠던 MLP(multi layer perceptron)구조가 된다. 28x28크기의 이미지를 1차원 벡터로 만들어 입력하고 15개 노드의 은닉층을 거쳐 10개 노드를 출력한다. 이 모형을 GPU를 사용해 학습시키고 싶다면 모형과 사용할 데이터를 GPU연산 가능 자료형으로 변환하면 된다.

In [52]:
from torch import nn

model = nn.Sequential(
    nn.Linear(784, 15),
    nn.Sigmoid(),
    nn.Linear(15, 10),
    nn.Sigmoid(),
).cuda()

PyTorch에서 최적화 알고리즘은 torch.optim에 정의되어 있다. 다음처럼 torch.optim클래스에서 원하는 최적화 방법을 정의한다. 여기서는 SGD방법을 사용한다. 그리고 대부분의 비용함수는 torch.nn클래스에 구현되어 있다. 여기서는 크로스 엔트로피 비용함수(Cross Entropy Loss)를 사용한다. 크로스 엔트로피 비용함수는 신경망 모형을 이용해 분류문제를 풀 때 많이 사용하는 비용함수이다. 이는 추후 신경망 성능개선 다룰 것이다.

In [53]:
from torch import optim

opt = optim.SGD(model.parameters(), lr=0.5)
loss_fn = torch.nn.CrossEntropyLoss()
In [54]:
def accuracy(pred, y):
    preds = torch.argmax(pred, dim=1)
    return (preds == y).float().mean()


epochs = 10
n = X_train.shape[0]
bs = 100

for epoch in range(epochs):
    for i in range((n - 1) // bs + 1):
        start_i = i * bs
        end_i = start_i + bs
        xb = X_train[start_i:end_i].cuda()
        yb = y_train[start_i:end_i].cuda()
        pred = model(xb)
        loss = loss_fn(pred, yb)
        loss.backward()
        opt.step()
        opt.zero_grad()
    acc = accuracy(pred, yb)
    print("{}epoch - cross entropy : {}, accuracy : {}".format(epoch+1,
                                                               loss.detach().item(), acc.detach().item()))
1epoch - cross entropy : 1.7819966077804565, accuracy : 0.8999999761581421
2epoch - cross entropy : 1.6745222806930542, accuracy : 0.9099999666213989
3epoch - cross entropy : 1.6295411586761475, accuracy : 0.9199999570846558
4epoch - cross entropy : 1.6058640480041504, accuracy : 0.9199999570846558
5epoch - cross entropy : 1.5905970335006714, accuracy : 0.9300000071525574
6epoch - cross entropy : 1.5791420936584473, accuracy : 0.9399999976158142
7epoch - cross entropy : 1.5704188346862793, accuracy : 0.9300000071525574
8epoch - cross entropy : 1.5634205341339111, accuracy : 0.9300000071525574
9epoch - cross entropy : 1.5574002265930176, accuracy : 0.9300000071525574
10epoch - cross entropy : 1.5522010326385498, accuracy : 0.9300000071525574

hiddenlayer 패키지를 사용하면 PyTorch로 구현한 모델의 그래프를 시각화 할 수 있다. 설치하는 방법은 다음과 같다.

  • pip install hiddenlayer
In [55]:
import hiddenlayer as hl

hl.build_graph(model=model, args=torch.zeros([10000, 784]).cuda())
Out:
%3 Sequential/Linear[0]/outputs/5 Linear Sequential/Sigmoid[1]/outputs/6 Sigmoid Sequential/Linear[0]/outputs/5->Sequential/Sigmoid[1]/outputs/6 10000x15 Sequential/Sigmoid[1]/outputs/7 Linear Sequential/Sigmoid[1]/outputs/6->Sequential/Sigmoid[1]/outputs/7 10000x15 Sequential/Sigmoid[3]/outputs/8 Sigmoid Sequential/Sigmoid[1]/outputs/7->Sequential/Sigmoid[3]/outputs/8 10000x10

질문/덧글

몇가지 오류가 있는것같아 질문드립니다. leey*** 2019년 3월 25일 2:07 오후

몇가지 오류가 있는것같아 질문드립니다.

1. In [2] Cell은 list를 tensor로 변형하는 과정을 보여주려고 하신것같은데, li가 list가 아닌 array로 되었네요 ㅜㅜ
2. In [3] Cell에서 'arr_as_tensor = torch.from_numpy(arr)'가 아니라 'arr_as_tensor = torch.as_tensor(arr)'를 쓰려고 하셨던것 아닌가요?
3. 'torch.tensor() 명령으로 텐서를 만들거나 torch.as_tensor()에 ndarray가 아닌 리스트를 넣었을 때는 이러한 일이 발생하지 않는다.' 이부분은 '배열과 리스트를 텐서 자료형으로 변환' section에서 제일 마지막 부분에 와야하는것 아닌가요?

항상 좋은 자료 잘 보고있습니다. 감사합니다.