아래 링크가 시작입니다~

2020/02/06 - [분류 전체보기] - 책(밑바닥부터 시작하는 딥러닝 2)

 

1.4.1 스파이럴 데이터셋

- spiral: 나선형의 데이터

 

# import 방식
from google.colab import drive
drive.mount('/content/gdrive')
# %cd Drive/ # 이 부분엔 현재 경로 넣기
from dataset import spiral
import matplotlib.pyplot as plt

x, t = spiral.load_data()
print('x', x.shape)  # (300, 2)
print('t', t.shape)  # (300, 3)

# 데이터점 플롯
N = 100
CLS_NUM = 3
markers = ['o', 'x', '^']
for i in range(CLS_NUM):
    plt.scatter(x[i*N:(i+1)*N, 0], x[i*N:(i+1)*N, 1], s=40, marker=markers[i])
plt.show()

 - x가 입력데이터이고, t가 정답 레이블입니다. 참고로 t는 원핫 벡터입니다.

 

x (300, 2)

t (300, 3)

학습에 이용할 스파이럴 데이터셋(3개의 클래스 각각을 X,▲,●로 표기)

 - 위 그림처럼 입력은 2차원 데이터이고, 분류할 클래스 수는 3개가 있습니다.

 - 보다시피 직선만으로는 클래스를 분리 할수 없으므로 비선형 분리를 학습해야합니다.

 

1.4.2 신경망 구현

  - 은닉층이 하나인 신경망 구현

import sys
import numpy as np
from common.layers import Affine, Sigmoid, SoftmaxWithLoss


class TwoLayerNet:
    def __init__(self, input_size, hidden_size, output_size):
        I, H, O = input_size, hidden_size, output_size

        # 가중치와 편향 초기화
        W1 = 0.01 * np.random.randn(I, H)
        b1 = np.zeros(H)
        W2 = 0.01 * np.random.randn(H, O)
        b2 = np.zeros(O)

        # 계층 생성
        self.layers = [
            Affine(W1, b1),
            Sigmoid(),
            Affine(W2, b2)
        ]
        self.loss_layer = SoftmaxWithLoss()

        # 모든 가중치와 기울기를 리스트에 모은다.
        self.params, self.grads = [], []
        for layer in self.layers:
            self.params += layer.params
            self.grads += layer.grads

- 위 코드 중 __init__(초기화)메서드는 3개의 인수를 받습니다.

  • input_size : 입력층의 뉴런 수

  • hidden_size : 은닉층의 뉴런 수

  • output_size : 출력층의 뉴런 수

- 우선 편향을 영벡터(zero vector)로 초기화합니다. : np.zeros( )

 

- 가중치는 작은 무작위 값으로 초기화 합니다. : 0.01 * np.random.randn( )

     └ 가중치를 작은 무작위 값으로 설정시 학습이 잘 진행될 가능성이 커집니다.

 

- 필요한 계층을 생성해  인스턴스 변수인 layers 리스트에 모아둡니다.

 

- 이 모델에서 사용하는 매개변수들고 기울기들을 하나로 모읍니다.

더보기

▶ Softma with loss 계층은 다른 계층과 다르게 취급하여, layers리스트가 아닌 loss_layer 인스턴스        변수에 별도로 저장합니다.

 

- TwoLayerNet에 3개의 메서드를 구현해 넣습니다.

  • 추론을 수행 : predict( )

  • 순전파 담당 : forward( )

  • 역전파 담당 : backward( )

    def predict(self, x):
        for layer in self.layers:
            x = layer.forward(x)
        return x

    def forward(self, x, t):
        score = self.predict(x)
        loss = self.loss_layer.forward(score, t)
        return loss

    def backward(self, dout=1):
        dout = self.loss_layer.backward(dout)
        for layer in reversed(self.layers):
            dout = layer.backward(dout)
        return dout

- 신경망에서 사용하는 처리 블록들을 '계층' 단위로 미리 구현해놨으므로 forward( )화 backward( )를

  적절하게 호출만 하면 됩니다.

 

1.4.3 학습용 코드

import sys
sys.path.append('..')  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
from common.optimizer import SGD
from dataset import spiral
import matplotlib.pyplot as plt
from two_layer_net import TwoLayerNet


# 하이퍼파라미터 설정
max_epoch = 300
batch_size = 30
hidden_size = 10
learning_rate = 1.0

# 데이터 읽기, 모델과 옵티마이저 생성
x, t = spiral.load_data()
model = TwoLayerNet(input_size=2, hidden_size=hidden_size, output_size=3)
optimizer = SGD(lr=learning_rate)

# 학습에 사용하는 변수
data_size = len(x)
max_iters = data_size // batch_size
total_loss = 0
loss_count = 0
loss_list = []

for epoch in range(max_epoch):
    # 데이터 뒤섞기
    idx = np.random.permutation(data_size)
    x = x[idx]
    t = t[idx]

    for iters in range(max_iters):
        batch_x = x[iters*batch_size:(iters+1)*batch_size]
        batch_t = t[iters*batch_size:(iters+1)*batch_size]

        # 기울기를 구해 매개변수 갱신
        loss = model.forward(batch_x, batch_t)
        model.backward()
        optimizer.update(model.params, model.grads)

        total_loss += loss
        loss_count += 1

        # 정기적으로 학습 경과 출력
        if (iters+1) % 10 == 0:
            avg_loss = total_loss / loss_count
            print('| 에폭 %d |  반복 %d / %d | 손실 %.2f'
                  % (epoch + 1, iters + 1, max_iters, avg_loss))
            loss_list.append(avg_loss)
            total_loss, loss_count = 0, 0

- 우선 하이퍼파라미터(hyperparameter)를 설정합니다.

  • 학습하는 에폭 수 : max_epoch

  • 미니배치 크기 : batch_size

  • 은닉층의 뉴런 수 : hidden_size

  • 학습률 : learning_rate

- 데이터를 읽어 들이고, 신경망(모델)과 옵티마이저를 생성합니다.

    └ 이미 신경망을 TwoLayerNet 클래스로, optimizer를 SGD클래스로 구현해놨습니다.

더보기

▶ 에폭(epoch)은 학습 단위입니다.

- 학습은 미니 배치 방식으로 진행되며 데이터를 무작위로 선택합니다.

 

- 에폭단위로 데이터를 뒤섞고, 뒤섞은 데이터 중 앞에서부터 순서대로 뽑아냅니다.

    └ 데이터 뒤섞기 : np.random.permutation( )메서드

 

- 기울기를 구해 매개변수를 갱신합니다.

 

- 정기적으로 학습 결과를 출력합니다.

# 학습 결과 플롯
plt.plot(np.arange(len(loss_list)), loss_list, label='train')
plt.xlabel('반복 (x10)')
plt.ylabel('손실')
plt.show()

- 손실이 줄어드는 것을 그래프에서 볼수 있습니다.

손실 그래프: x축은 학습의 반복수(눈금 값의 10배), y축은 학습 10번 반복당 손실 평균

- 학습 후 신경망이 어떻게 영역을 분리했는지를 시각화해 봅니다.(이를 결정경계 라고 합니다.)

# 경계 영역 플롯
h = 0.001
x_min, x_max = x[:, 0].min() - .1, x[:, 0].max() + .1
y_min, y_max = x[:, 1].min() - .1, x[:, 1].max() + .1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
X = np.c_[xx.ravel(), yy.ravel()]
score = model.predict(X)
predict_cls = np.argmax(score, axis=1)
Z = predict_cls.reshape(xx.shape)
plt.contourf(xx, yy, Z)
plt.axis('off')

학습 후 신경망의 결정 경계(신경망이 식별하는 클래스별 영역을 색으로 구분)

 

# 데이터점 플롯
x, t = spiral.load_data()
N = 100
CLS_NUM = 3
markers = ['o', 'x', '^']
for i in range(CLS_NUM):
    plt.scatter(x[i*N:(i+1)*N, 0], x[i*N:(i+1)*N, 1], s=40, marker=markers[i])
plt.show()

데이터 플롯

위 그림에서 보듯 학습된 신경망은 '나선형' 패턴을 올바르게 파악했음을 알 수 있습니다.

 

1.4.4 Trainer 클래스

  - Trainer클래스 : 학습을 수행하는 역할

 

Trainer클래스는 trainer.py에 학습용 코드를 맡겼습니다.

Trainer 클래스의 초기화 메서드는 신경망(모델)과 옵티마이저를 인수로 받습니다.

model = TwoLayerNet(...)
optimizer = SGD(lr=1.0)
trainer = Trainer(model, optimizer)

 

Trainer 클래스의 fit( ) 메서드가 받는 인수: '(=XX)'는 기본값을 뜻함

인수 설명
x 입력 데이터
t 정답 레이블
max_epoch(=10) 학습을 수행하는 에폭 수
batch_size(=32) 미니배치 크기
eval_interval(=20) 결과(평균 손실 등)를 출력하는 간격
  eval_interval=20으로 설정하면, 20번째 반복마다 손실의 평균을 구해 화면에 출력한다.
max_grad(=None) 기울기 최대 노름norm
  기울기 노름이 이 값을 넘어서면 기울기를 줄인다. (이를 기울기 클리핑이라 한다.)

Trainer 클래스는 plot( )메서드도 제공합니다. 이 메서드는 fit( )에서 기록한 손실을 그래프로 그려줍니다.

import sys
sys.path.append('..')  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
from common.optimizer import SGD
from common.trainer import Trainer
from dataset import spiral
from two_layer_net import TwoLayerNet


# 하이퍼파라미터 설정
max_epoch = 300
batch_size = 30
hidden_size = 10
learning_rate = 1.0

x, t = spiral.load_data()
model = TwoLayerNet(input_size=2, hidden_size=hidden_size, output_size=3)
optimizer = SGD(lr=learning_rate)

trainer = Trainer(model, optimizer)
trainer.fit(x, t, max_epoch, batch_size, eval_interval=10)
trainer.plot()

학습용 코드를 Trainer 클래스에 맡겼기 때문에 코드가 깔끔해질 수 있습니다.

 

1.5 계산 고속화

신경망에서는 얼마나 빠르게 계산 하느냐가 매우 중요한 주제이므로 신경망 고속화에 도움 되는

'비트 정밀도'와 'GPU'에 관해 나와있습니다.

 

1.5.1 비트정밀도

* numpy의 부동소수점 수는 기본적으로 64 비트데이터 타입을 사용합니다.

import numpy as np
a = np.random.randn(3)
a.dtype

위 코드로 확인 할 수 있습니다.

 

* numpy에서는 추론과 학습을 32비트 부동소수점 수로도 문제없이 수행 할 수 있습니다.

  이에 신경망 계산 시 데이터를 전송하는 '버스 대역폭'이 병목이 되는 경우도 있어서 데이터 타입을 작게하고 계산

  속도 측면에서도 빠르기 때문에 32비트 부동소수점 수를 이 책에서는 우선으로 사용합니다.

b = np.random.randn(3).astype(np.float32)
c = np.random.randn(3).astype('f')
b.dtype, c.dtype

(dtype('float32'), dtype('float32'))

 

위 코드로 부동소수점을 변경할 수 있습니다.