1.3 신경망의 학습
1.3.1 손실함수
- 교차 엔트로피 오차(Cross Entropy error) 이용
- 소프트맥스 함수의 출력(확률)이 교차 엔트로피에 입력
- 확률과 정답 레이블의 오차를 계산 : 손실함수
- softmax with Loss 계층 하나로 구현
1.3.2 미분과 기울기
- 각 원소에 대한 미분을 정리한 것이 기울기 gradient
- 행렬과 기울기의 형상이 같음
- 수학에서의 기울기는 벡터에 대한 미분으로 한정된 것과 달리 딥러닝에서는 행렬이나 텐서에 대해서도 미분을 정의하고 기울기라 부름
1.3.3 연쇄 법칙
: 합성함수에 대한 미분의 법칙
-> 많은 함수를 연결하더라도 그 미분은 개별 함수의 미분들을 이용해 구할 수 있음
1.3.4 계산 그래프
- 각 노드의 역전파
- 덧셈 노드 : 상류로부터의 기울기 그대로 흘림
- 곱셈 노드 : 상류로부터 받은 기울기에 순전파 시의 입력을 서로 바꾼 값을 곱함
- 분기 노드 : 상류에서 온 기울기들의 합
- Repeat 노드 : N개의 기울기를 모두 합함
- Sum 노드 : repeat
-> sum 노드와 repeat 노드는 서로 반대 관계
(sum 노드의 순전파가 repeat노드의 역전파, sum 노드의 역전파가 repeat노드의 순전파)
- MatMul 노드 (행렬 곱)
-> 행렬의 형상을 확인하여 역전파 식 유도
class MatMul:
def __init__(self, W):
self.params = [W]
self.grads = [np.zeros_like(W)]
self.x = None
def forward(self,x):
W, = self.params
out = np.matmul(x, W)
self.x = x
return out
def backward(self, dout):
W, = self.params #매개변수
dx = np.matmul(dout, W.T) #매개변수에 대응하는 기울기
dW = np.matmul(self.x.T, dout)
self.grads[0][...] = dW #메모리 위치 고정
return dx
- 생략(ellipsis) 기호 ... 사용
- a = b -> a가 가리키는 메모리 위치가 b가 가리키는 위치와 같아짐(실제 데이터는 복사x) => 얕은 복사
- a[...] = b -> a의 메모리 위치는 변하지 않고 a가 가리키는 메모리에 b의 원소 복제(실제 데이터가 복제)=> 깊은 복사
-> 생략 기호을 이용함으로써 배열의 메모리 주소가 변하는 일 없이 항상 갚을 덮어씀
=> 기울기를 그룹화하는 작업을 최초에 한 번만 하면 됨
1.3.5 기울기 도출과 역전파 구현
- Sigmoid 계층
#sigmoid 계층
class Sigmoid:
def __init__(self):
self.params, self.grads = [], []
self.out = None
def forward(self, x):
out = 1/(1+np.exp(-x))
self.out = out
return out
def backward(self, dout):
dx = dout *(1.0 - self.out)*self.out
return dx
- Affine 계층
순전파 : y = np.matmul(x, W) +b
#Affilne 계층
class Affine:
def __init__(self, W, b):
self.params = [W, b]
self.grads = [np.zeros_like(W), np.zeros_like(b)]
self.x = None
def forward(self,x):
W, b = self.params
out = np.matmul(x, W) +b
self.x = x
return out
def backward(self,dout):
W, b = self.params
dx = np.matmul(dout, W.T)
dW = np.matmul(self.x.T, dout)
db = np.sum(dout, axis=0) #(N,H)->(H), 열기준으로 합 구함
self.grads[0][...] = dW
self.grads[1][...] = db
return dx
- Softmax with Loss 계층
1.3.6 가중치 갱신
- 신경망 학습
1) 미니배치 : 훈련 데이터 중에서 무작위로 다수의 데이터를 골라낸다.
2) 기울기 계산 : 오차역전파법으로 각 가중치 매개변수에 대한 손실 함수의 기울기를 구한다.
기울기 - 현재의 가중치 매개변수에서 손실을 가장 크게 하는 방향을 가리킴
-> 매개변수를 기울기와 반대 방향으로 갱신 : 경사하강법
3) 매개변수 갱신 : 기울기를 사용하여 가중치 매개변수를 갱신한다.
4) 반복 : 1~3단계를 필요한 만큼 반복한다.
- SGD 확률적경사하강법 구현
#SGD 구현
class SGD:
def __init__(self, lr=0.01):
self.lr = lr #학습률
def update(self, params, grads): #매개변수 갱신 처리
for i in range(len(params)):
params[i] -= self.lr*grads[i]
1.4 신경망으로 문제를 풀다
1.4.1 스파이럴 데이터셋
#스파이럴 데이터셋
from dataset import spiral
import matplotlib.pyplot as plt
x, t = spiral.load_data()
print('x', x.shape) #(300,2) 2차원
print('t', t.shape) #(300,3) 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()
- 입력은 2차원 데이터, 분류할 클래스 3개
- 직선만으로는 클래스들을 분리할 수 없음
- 비선형 학습 해야함
1.4.2 신경망 구현
##신경망 구현
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(0)
#계층 생성
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
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
1.4.3 학습용 코드
- 학습 데이터를 읽어 들여 신경망(모델)과 옵티마이저(최적화기) 생성
##학습용 코드
from common.optimizer import SGD
from dataset import spiral
import matplotlib.pyplot as plt
from two_layer_net import TwoLayerNet
#1. 하이퍼파라미터 설정
max_epoch = 300
batch_size = 30
hidden_size = 10
learning_rate = 1.0
#2. 데이터 읽기, 모델과 옵티마이저 생성
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):
#3. 데이터 뒤섞기
idx = np.random.permutation(data_size)
#데이터 인덱스 뒤섞기 (0부터 data_size-1까지의 무작위 순서를 생성해 반환)
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]
#4. 기울기를 구해 매개변수 갱신
loss = model.forward(batch_x, batch_t)
model.backward()
optimizer.update(model.params, model.grads)
total_loss += loss
loss_count +=1
#5. 정기적으로 학습 경과 출력
if (iters+1) % 10 ==0: #10번째 반복마다
avg_loss = total_loss / loss_count
print('|에폭 %d | 반복 %d / %d |손실 %.2f'
% (epoch+1, iters+1, max_iters, avg_loss))
loss_list.append(avg_loss) #손실의 평균 구해 loss_list 변수에 추가
total_loss, loss_count = 0,0
- 학습을 진행함에 따라 손실 줄어듦
- 비선형 분리 영역 학습함
1.4.4 Trainer 클래스
- 학습을 수행하는 역할을 Trainer라는 클래스로 제공
- Trainer클래스의 초기화 메서드는 신경망과 옵티마이저를 인수로 받음
- 그리고 fit()메서드를 호출해 학습 시작
- plot()메서드도 제공 -> 손실 그래프 그려줌
##Trainer 클래스
from common.trainer import Trainer
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()
1.5 계산 고속화
1.5.1 비트 정밀도
- 넘파이의 부동소수점 수는 기본적으로 64비트
- 신경망에서는 작은 비트 수로도 문제없이 수행 가능 -> 32비트 부동소수점 수 사용
- 학습된 가중치를 저장하는 경우에 한해 16비트 수로 변환
- CPU와 GPU는 연산 자체를 32비트로 수행
1.5.2 GPU(쿠파이)
- 쿠파이는 GPU를 이용해 병렬 계산을 수행해주는 라이브러리
- 엔비디아의 GPU에서만 동작
- 넘파이와 호환되는 API 제공
1.6 정리
- 신경망은 입력층, 은닉층, 출력층을 지난다.
- 완전연결계층에 의해 선형 변환이 이뤄지고, 활성화 함수에 의해 비선형 변환이 이뤄진다.
- 완전연결계층이나 미니배치 처리는 행렬로 모아 한꺼번에 계산할 수 있다.
- 오차역전파법을 사용하여 신경망의 손실에 관한 기울기를 효율적으로 구할 수 있다.
- 신경망이 수행하는 처리는 계산 그래프로 시각화할 수 있으며, 순전파와 역전파를 이해하는 데 도움이 된다.
- 신경망의 구성요소들을 '계층'으로 모듈화해두면, 이를 조립하여 신경망을 쉽게 구성할 수 있다.
- 신경망 고속화에는 GPU를 이용한 병렬 계산과 데이터의 비트 정밀도가 중요하다.
책 참고 : 밑바닥부터 시작하는 딥러닝 (한빛미디어)
'딥러닝 > 밑바닥부터 시작하는 딥러닝 2' 카테고리의 다른 글
6장 : 게이트가 추가된 RNN (0) | 2023.09.26 |
---|---|
5장 : 순환 신경망(RNN) (0) | 2023.09.26 |
4장 : word2vec 속도 개선 (0) | 2023.09.23 |
3장 : word2vec (0) | 2023.09.21 |
2장 : 자연어와 단어의 분산 표현 (2) | 2023.09.21 |