신경망 학습에 쓰는 데이터는 두 종류:
1. 훈련 데이터: 모델이 학습할 때 쓰는 데이터. 가중치 조정에 사용.
2. 테스트 데이터: 학습 끝난 모델의 성능을 확인하는 데이터. 훈련에 안 썼던 별도의 데이터로, 모델이 새 데이터에서도 잘 작동하는지 봄.
import numpy as np
def mean_squared_error(y, t):
return 0.5 * np.sum((y - t)**2)
y
: 모델 예측값 (예: [0.6, 0.3, 0.1]).t
: 실제값 (예: [1, 0, 0]).(y - t)**2
: 차이를 제곱.np.sum
: 차이 제곱의 합.0.5
: 수치 안정성 위해 곱함.y = np.array([0.6, 0.3, 0.1]) # 예측값
t = np.array([1, 0, 0]) # 실제값
print(mean_squared_error(y, t)) # 출력: 0.125
def cross_entropy_error(y, t):
delta = 0.0000001 # log(0) 방지용
return -np.sum(t * np.log(y + delta))
y
: 예측 확률 (예: [0.6, 0.3, 0.1]).t
: 실제 레이블 (예: [0, 0, 1]).np.log(y + delta)
: 예측값에 로그 취해서 확률 차이 계산. delta
는 0 로그 방지.-np.sum(t * ...)
: 실제값과 로그 곱한 뒤 합산.y = np.array([0.6, 0.3, 0.1]) # 예측 확률
t = np.array([0, 0, 1]) # 실제 레이블
print(cross_entropy_error(y, t)) # 출력: 약 2.302585
from mnist import load_mnist
import numpy as np
(X_train, t_train), (X_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
train_size = X_train.shape[0] # 60,000
batch_size = 10
batch_mask = np.random.choice(train_size, batch_size) # 10개 무작위 선택
x_batch = X_train[batch_mask] # 선택된 데이터
t_batch = t_train[batch_mask] # 선택된 레이블
load_mnist
: MNIST 데이터 불러옴. normalize=True
로 0~1 정규화, one_hot_label=True
로 레이블 one-hot encoding.np.random.choice
: 60,000개 중 10개 무작위 선택.x_batch
, t_batch
: 선택된 데이터와 레이블로 미니배치 구성.미니배치에서는 여러 샘플 손실 값을 평균냄.
def cross_entropy_error(y, t):
if y.ndim == 1: # 1차원 입력 처리
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
return -np.sum(t * np.log(y)) / batch_size
y.ndim == 1
: 단일 샘플 입력이면 2차원으로 변환.batch_size
: 미니배치 크기./ batch_size
: 손실 값 평균 내서 배치 크기에 상관없이 일정.def numerical_diff(f, x):
h = 0.0001
return (f(x + h) - f(x)) / h
f
: 미분할 함수.x
: 미분 지점.h
: 작은 값(0.0001)으로 변화량 계산.(f(x + h) - f(x)) / h
: 함수 변화량 나누기로 기울기 구함.def function_1(x):
return 0.01 * x**2 + 0.1 * x
print(numerical_diff(function_1, 5)) # 출력: 약 0.2
print(numerical_diff(function_1, 10)) # 출력: 약 0.3
def function_2(x):
return x[0]**2 + x[1]**2
def numerical_gradient(f, x):
h = 0.0001
grad = np.zeros_like(x)
for index in range(x.size):
tmp_val = x[index]
x[index] = tmp_val + h
fxh1 = f(x)
x[index] = tmp_val
fxh2 = f(x)
grad[index] = (fxh1 - fxh2) / h
return np.round(grad, 3)
np.zeros_like(x)
: $$ x $$와 같은 크기의 0 벡터.np.round(grad, 3)
: 소수점 3자리로 반올림.x = np.array([3.0, 4.0])
print(numerical_gradient(function_2, x)) # 출력: [6. 8.]
x = np.array([0.0, 2.0])
print(numerical_gradient(function_2, x)) # 출력: [0. 4.]
결과 설명:
: 기울기
→ 방향으로 6, 방향으로 8 증가
: 기울기
→ 는 최솟값 근처라 0, 방향만 변화
def gradient_descent(f, init_x, lr=0.01, step_num=100):
x = init_x
x_history = []
for i in range(step_num):
x_history.append(x.copy())
grad = numerical_gradient(f, x)
x -= lr * grad # 기울기 반대 방향 이동
return x, np.array(x_history)
f
: 최적화할 함수.init_x
: 초기 가중치.lr
: 학습률 (한 번에 얼마나 이동).step_num
: 반복 횟수.x -= lr * grad
: 기울기 반대 방향으로 갱신.init_x = np.array([-3.0, 4.0])
lr = 0.1
step_num = 100
x, x_history = gradient_descent(function_2, init_x, lr, step_num)
print(x) # 출력: [-0.0002, 0.0001]
def sgd_update(params, grad, lr=0.01):
params -= lr * grad
return params
params
는 가중치, grad
는 기울기, lr
은 학습률. 기울기 반대 방향으로 갱신.def momentum_update(params, grad, velocity, lr=0.01, momentum=0.9):
velocity = momentum * velocity - lr * grad
params += velocity
return params, velocity
velocity
로 이전 기울기 정보 유지. 새 기울기와 결합해 갱신.def adagrad_update(params, grad, h, lr=0.01, epsilon=1e-8):
h += grad ** 2
params -= lr * grad / (np.sqrt(h) + epsilon)
return params, h
h
로 기울기 제곱 합 누적. 학습률을 로 나눠 조정.def rmsprop_update(params, grad, h, lr=0.01, rho=0.9, epsilon=1e-8):
h = rho * h + (1 - rho) * (grad ** 2)
params -= lr * grad / (np.sqrt(h) + epsilon)
return params, h
rho
로 최근 기울기 강조. ( h )가 너무 커지지 않음.def adam_update(params, grad, m, v, t, lr=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8):
t += 1
m = beta1 * m + (1 - beta1) * grad
v = beta2 * v + (1 - beta2) * (grad ** 2)
m_hat = m / (1 - beta1 ** t)
v_hat = v / (1 - beta2 ** t)
params -= lr * m_hat / (np.sqrt(v_hat) + epsilon)
return params, m, v, t
m
으로 모멘텀, v
로 학습률 조정. t
로 바이어스 보정.가중치가 기울기 방향으로 얼마나 이동할지 정함
큰 학습률: 빠르게 학습. 근데 최솟값 지나칠 수 있음.
작은 학습률: 안정적. 근데 너무 느림.
예: 학습률 0.01은 천천히, 0.1은 빠르게.
신경망 학습은 4단계로 진행:
1. 미니배치: 훈련 데이터 일부 무작위로 뽑음.
2. 기울기 계산: 뽑은 데이터로 손실 함수 기울기 구함.
3. 가중치 갱신: 기울기 방향으로 가중치 조금 갱신.
4. 반복: 1~3 반복해서 손실 값 줄임.
class simpleNet:
def __init__(self):
self.W = np.random.randn(2, 3) # 2x3 가중치 초기화
def predict(self, x):
return np.dot(x, self.W) # 입력과 가중치 행렬곱
def loss(self, x, t):
z = self.predict(x)
y = softmax(z) # 소프트맥스로 확률
loss = cross_entropy_error(y, t) # 손실 계산
return loss
self.W
: 2x3 랜덤 가중치.predict
: 입력 ( x )와 가중치 ( W ) 행렬곱으로 예측.loss
: 예측값 소프트맥스로 변환 후 CEE로 손실 계산.def softmax(a):
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
net = simpleNet()
x = np.array([0.6, 0.9]) # 입력
p = net.predict(x) # 예측
print(np.argmax(p)) # 출력: 2 (최고 확률 클래스)
t = np.array([0, 0, 1]) # 정답 레이블
print(net.loss(x, t)) # 출력: 약 0.465544
def f(W):
return net.loss(x, t)
dW = numerical_gradient(f, net.W) # 출력: [[-0.541 0.164 0.377], [-0.811 0.246 0.565]]