신경망 학습에서 학습이란 훈련 데이터로부터 가중치 매개변수의 최적값을 자동으로 획득하는 것
손실 함수는 신경망이 학습할 수 있도록 해주는 지표
이 손실 함수의 결과값을 가장 작게 만드는 가중치 매개변수를 찾는 것이 학습의 목표
데이터에서 학습한다는 것은 가중치 매개변수의 값을 데이터를 보고 자동으로 결정한다는 뜻
이전 예제에서는 매개변수가 3개였으나, 실제 신경망에서는 매개변수가 수천에서 수만개이기 때문에 매개변수를 수작업으로 정한다는 것은 불가능
Machine Learning의 중심에는 데이터가 존재
Machine Learning에서는 사람의 개입을 최소화하고 수집한 데이터로부터 패턴을 찾으려 시도
이미지에서 특징(feature)을 추출하고 그 특징의 패턴을 Machine Learning 기술로 학습하는 방법
특징은 입력 데이터(이미지)에서 본질적인 데이터르 ㄹ정확하게 추출할 수 있도록 설계된 변환기
이미지의 특징은 보통 벡터로 기술
이런 특징을 사용하여 이미지 데이터를 벡터로 변환하고, 변환된 벡터를 가지고 지도 학습 방식의 대표 분류 기법인 SVM, KNN 등으로 학습
모아진 데이터로부터 규칙을 찾아내는 역할을 Machine이 담당하지만, 이미지를 벡터로 변환할 때 사용하는 특징은 여전히 '사람'이 설계하는 것임에 주의
신경망은 이미지에 포함된 중요한 특징까지도 기계가 스스로 학습
훈련 데이터만 사용하여 학습하면서 최적의 매개변수 확인
그 다음 시험 데이터를 사용하여 앞서 훈련한 모델의 실력을 평가
범용 능력을 평가하기 위해 훈련/시험 데이터를 구분
범용 능력은 아직 보지 못한 데이터(훈련 데이터에 포함되지 않은 데이터)로도 문제를 올바르게 풀어내는 능력
오버피팅 : 한 데이터셋에만 지나치게 최적화된 상태
손실 함수(loss function) : 신경망이 최적의 매개변수 값을 탐색하기 위해 '하나의 지표'를 기준으로 삼는 것
일반적으로는 오차제곱합과 교차 엔트로피 오차를 사용
오차제곱합
수식
는 신경망의 추력(신경망이 추정한 값)
는 정답 레이블
는 데이터의 차원 수
예제
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
# y는 소프트맥스 함수의 출력 : 확률로 해석
이미지가 '0'일 확률이 0.1, '1'일 확률은 0.05, '2'일 확률은 0.6이라는 의미
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
t는 정답 레이블로, 정답을 가리키는 원소만 1로 표현하고 나머지는 0으로 표기
위 예에서는 숫자 '2'에 해당하는 원소의 값이 1이므로 정답이 '2'임을 알 수 있음
오차제곱합은 각 원소의 출력(추정 값)과 정답 레이블(참 값)의 차( - )를 제곱한 후, 그 총합을 구함
def sum_suqares_error(y, t):
return 0.5 * np.sum((y-t)**2)
함수 사용 예제
첫 번째 예의 손실 함수 쪽 출력이 작으며, 정답 레이블과의 오차도 작은 것
즉, 오차제곱합 기준으로는 첫 번째 추정 결과가 정답에 더 가까울 것으로 판단
import numpy as np
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
# 신경망의 출력 '2'
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
sum_suqares_error(np.array(y), np.array(t))
0.09750000000000003
# 신경망의 출력 '7'
y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
sum_suqares_error(np.array(y), np.array(t))
0.5975
교차 엔트로피 오차
또 다른 손실 함수로서 교차 엔트로피 오차도 자주 이용
수식
는 밑이 인 자연로그
는 신경망의 출력,
는 정답 레이블 : 는 정답에 해당하는 인덱스의 원소만 1이고 나머지는 0(One-hot Encoding)
실질적으로 정답일 때의 추정(가 1일 때의 )의 자연로그를 계산하는 식
예를 들어, 정답 레이블은 '2'가 정답이라고 하고, 이때의 신경망 출력이 0.6이라면 교차 엔트로피 오차는
또한, 같은 조건에서 신경망 출력이 0.1이라면
교차 엔트로피 오차는 정답일 때의 출력이 전체 값을 정하게 됨
교차 엔트로피 구현
def cross_entropy_error(y, t):
delta = 1e-7 # np.log를 계산할 때 np.log 함수에 0을 입력하는 마이너스 무한대인 -inf가 반환되어 더 이상 계산을 진행할 수 없기 때문에 추가
return -np.sum(t * np.log(y + delta))
import numpy as np
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
# 정답일 때의 출력이 0.6
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
cross_entropy_error(np.array(y), np.array(t))
0.510825457099338 # 오차가 더 작은 첫 번째 추정이 정답일 가능성이 높다고 판단
# 정답일 때의 출력이 0.1
y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
cross_entropy_error(np.array(y), np.array(t))
2.302584092994546
훈련 데이터에 대한 손실 함수의 값을 구하고, 그 값을 최대한 줄여주는 매개변수 확인 필요
모든 훈련 데이터를 대상으로 손실 함수 값을 구해야 함
훈련 데이터 모두에 대한 손실 함수의 합을 구하는 방법
손실 함수를 단순히 N개의 데이터로 확장
마지막에 N으로 나누어 정규화(N으로 나눔으로써 '평균 손실 함수'를 구하는 것)
훈련 데이터가 너무 많은 경우 시간이 오래 걸리기 때문에 현실적이지 않아, 데이터 일부를 추려 전체의 '근사치'로 이용 --> 일부를 미니배치라 함
예를 들어, 60,000장의 훈련 데이터 중에서 100장을 무작위로 뽑아 그 100장만을 사용하여 학습
이러한 학습 방법을 미니배치 학습이라고 함
훈련 데이터에서 지정한 수의 데이터를 무작위로 골라내는 코드
from google.colab import drive
drive.mount('/content/drive')
%cd /content/drive/MyDrive/temp/dataset
/content/drive/MyDrive/temp/dataset
#!python3 mnist.py
from mnist import load_mnist
(x_train, t_train), (x_test, t_test) = \
load_mnist(normalize=False, one_hot_label=True)
print(x_train.shape)
(60000, 784)
print(t_train.shape)
(60000, 10)
훈련 데이터는 60,000개
입력 데이터는 784열(원래는 28 x 28) 이미지 데이터
정답 레이블은 10줄짜리 데이터
훈련 데이터에서 무작위로 10장만 빼내려면? np.random.choice() 함수 사용
np.random.choice(60000,10)은 0이상 60,000미만의 수 중에서 무작위로 10개를 골라냄
train_size = x_train.shape[0]
print(train_size)
60000
batch_size = 10
batch_mask = np.random.choice(train_size, batch_size) # 지정한 범위의 수 중에서 무작위로 원하는 개수만 꺼냄
print(batch_mask)
[ 1594 44487 45813 19047 51835 8335 25719 58235 17757 12448]
x_batch = x_train[batch_mask] # batch_mask를 미니배치로 뽑아낼 데이터의 인덱스로 사용
t_batch = t_train[batch_mask] # batch_mask를 미니배치로 뽑아낼 데이터의 인덱스로 사용
이제 무작위로 선택한 인덱스(위 예제에서는 batch_size=10이 인덱스 개수)를 사용해서 미니배치를 계산
데이터가 하나인 경우와 데이터가 배치로 묶여 입력될 경우 모두를 처리할 수 있도록 구현
아래 코드는 정답 레이블이 One-Hot Encoding으로 되어 있을 때
(정답만 1이고, 나머지는 모두 0인 형태)
def cross_entropy_error(y, t):
if y.ndim == 1: # y가 1차원 이라면
# reshape 함수로 (784,)인 경우 (1, 784)
t = t.reshpae(1, t.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
return -np.sum(t * np.log(y + 1e-7)) / batch_size
정답 레이블이 One-Hot Encoding이 아니라 '2'나 '7'등의 숫자 레이블로 주어진 경우 다음과 같이 구현
def cross_entropy_error(y, t):
if y.ndim == 1: # y가 1차원 이라면
# reshape 함수로 (784,)인 경우 (1, 784)
t = t.reshpae(1, t.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size
t * np.log(y)를 np.log(y[np.arange(batch_size), t])로 구현
y[np.arange(batch_size), t]까지 배열 생성
batch_size가 5이면 np.arange(batch_size)는 [0, 1, 2, 3, 4]인 numpy 배열 생성
t는 [2, 7, 0, 9, 4]와 같이 저장되어 있으므로
y[np.arange(batch_size), t]는
결국 [y[0,2], y[1,7], y[2,0], y[3,9], y[4,4]]인 numpy 배열 생성
손실 함수를 사용하는 목적은 높은 '정확도'를 끌어내는 매개변수를 찾는 것
왜 '정확도'라는 지표를 놔두고 '손실 함수의 값'이라는 우회적인 방법을 택할까?
신경망 학습에서는 최적의 매개변수(가중치와 편향)를 탐색할 때 손실 함수의 값을 가능한 작게 하는 매개변수 값을 찾으려고 함
이 때 매개변수의 미분(기울기)을 계산하고, 그 미분 값을 단서로 매개변수의 값을 서서히 갱신하는 과정을 반복
손실 함수의 비분이란 '가중치 매개변수의 값을 아주 조금 변화시켰을 때, 손실 함수가 어떻게 변하는가?'라는 의미
만약, 미분 값이 음수면 그 가중치 매개변수를 양의 방향으로 변화시켜 손실 함수의 값을 줄일 수 있음
반대로, 미분 값이 양수면 가중치 매개변수를 음의 방향으로 변화시켜 손실 함수의 값을 줄일 수 있음 하지만 미분 값이 0이면 가중치 매개변수를 어느 쪽으로 움직여도 손실 함수의 값이 줄어드지 않기 때문에 가중치 매개변수의 갱신이 멈춤
'정확도'를 지표로 삼으면 매개변수의 미분이 대부분의 장소에서 0이 된다?
정확도는 매개변수의 미소한 변화에는 거의 반응을 보이지 않고, 반응이 있더라도 그 값이 불연속적으로 갑자기 변화
이는 '계단 함수'를 활성화 함수로 사용하지 않는 이유와 동일
미분은 '특정 순간'의 변화량을 의미
좌변은 의 에 대한 미분(에 대한 의 변화량)을 나타내는 기호
이 때, 시간을 뜻하는 를 한없이 0에 가깝게 한다는 의미로
사용
'진정한 미분'은 위치의 함수의 기울기(이를 접선이라 함)에 해당
위 그림에서와 같이 수치 미분에는 오차가 포함
이 오차를 줄이기 위해 와 일 때의 함수 f의 차분을 계산하는 방법 사용
이 차분은 를 중심으로 그 전후의 차분을 계산한다는 의미에서 중심 차분 혹은 중앙 차분이라 함
def numerical_diff(f, x):
h = 1e-4 # 0.0001
return (f(x+h) - f(x-h)) / (2*h)
파이썬으로 구현
def function_1(x):
return 0.01*x**2 + 0.1*x
import numpy as np
import matplotlib.pylab as plt
x = np.arange(0.0, 20.0, 0.1) # 0에서 20까지 0.1 간격의 배열 x
y = function_1(x)
plt.xlabel("x")
plt.ylabel("f(x)")
plt.plot(x, y)
plt.show()
일 때와 10일 때 함수의 미분 계산
numerical_diff(function_1, 5)
0.1999999999990898
numerical_diff(function_1, 10)
0.2999999999986347
이렇게 계산한 미분 값이 에 대한 의 변화량
의 해석적 해는
'진정한 미분'은 차례로 0.2와 0.3
앞의 수치 미분 결과를 비교하면 그 오차가 매우 작음
인수들의 제곱 합을 계싼하는 단순한 식이지만, 앞의 예와 달리 변수가 2개라는 점
파이썬으로 구현
def function_2(x):
return x[0]**2 + x[1]**2
# 또는 return np.sum(x**2)
이 함수를 그래프로 그려보면 다음과 같이 3차원으로 표현
def function_2(x, y):
return x**2 + y**2
import numpy as np
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
x = np.arange(-3.0, 3.0, 0.01)
y = np.arange(-3.0, 3.0, 0.01)
plt.scatter(x, y)
plt.grid()
X, Y = np.meshgrid(x, y)
Z = function_2(X, Y)
fig = plt.figure() # plt.figure()는 새로운 figure를 생성해준다.
ax = fig.gca(projection='3d')
# gca()로 현재의 Axes를, gcf()로 현재의 Figure 객체를 구할 수 있다.
# axes는 figure 내에서 축을 가지는 하나의 좌표평면과 같은 개념
surf = ax.plot_surface(X, Y, Z)
# Matplotlib의 축 생성 함수 중 하나에projection = '3d' 인수를 전달하여 3 차원 축을 만들 수 있습니다.
# 3 차원 축이 초기화되면plot_surface()메소드를 사용하여 표면 플롯을 생성 할 수 있습니다.
ax.view_init(elev=30, azim=50)
# view_init(elev=None , azim=None)
# elev : elevation을 약자로 쓴것으로 z plane의 각도를 의미. 입력한 각도대로 위 아래로 변화함.
# azim : azimuth angle 로 x,y plane의 각도를 의미.
plt.show()
이와 같이 변수가 여럿인 함수에 대한 미분이 편미분
와 중 어느 변수에 대한 미분이냐를 구별해야 함
수식으로는 나 으로 표현
편미분은 변수가 하나인 미분과 마찬가지로 특정 장소의 기울기를 계산
여러 변수 중 목표 변수 하나에 초점을 맞추고 다른 변수는 값을 고정
와 의 편미분을 동시에 계산하고 싶다면?
예를 들어, 와 일 때 (, ) 양쪽의 편미분을 묶어서 (, )을 계산하다고 가정
(, )처럼 모든 변수의 편미분을 벡터로 정리한 것을 기울기(gradient)라 함
기울기 구현
def function_2(x):
return x[0]**2 + x[1]**2
# 또는 return np.sum(x**2)
def numerical_gradient(f, x):
h = 1e-4 # 0.0001
grad = np.zeros_like(x) # x와 형상이 같은 배열을 생성
for idx in range(x.size):
tmp_val = x[idx]
x[idx] = tmp_val + h # x+h
fxh1 = f(x) # f(x+h)
x[idx] = tmp_val - h # x-h
fxh2 = f(x)
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = tmp_val
return grad
numerical_gradient(function_2, np.array([3.0, 4.0]))
array([6., 8.]) # 점 (3,4)의 기울기는 (6,8)
numerical_gradient(function_2, np.array([0.0, 2.0]))
array([0., 4.]) # 점 (0,2)의 기울기는 (0,4)
numerical_gradient(function_2, np.array([3.0, 0.0]))
array([6., 0.]) # 점 (3,0)의 기울기는 (6,0)
기울기의 결과에 마이너스를 붙인 벡터를 그려보기
구현 코드
import numpy as np
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
def _numerical_gradient_no_batch(f, x):
h = 1e-4 # 0.0001
grad = np.zeros_like(x)
for idx in range(x.size):
tmp_val = x[idx]
x[idx] = float(tmp_val) + h
fxh1 = f(x) # f(x+h)
x[idx] = tmp_val - h
fxh2 = f(x) # f(x-h)
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = tmp_val
return grad
def numerical_gradient(f, X):
if X.ndim == 1:
return _numerical_gradient_no_batch(f, X)
else:
grad = np.zeros_like(X)
for idx, x in enumerate(X):
grad[idx] = _numerical_gradient_no_batch(f, x)
return grad
def function_2(x):
if x.ndim == 1:
return np.sum(x**2)
else:
return np.sum(x**2, axis=1)
if __name__ == '__main__':
x0 = np.arange(-2, 2.5, 0.25)
x1 = np.arange(-2, 2.5, 0.25)
X, Y = np.meshgrid(x0, x1)
X = X.flatten()
Y = Y.flatten()
grad = numerical_gradient(function_2, np.array([X, Y]).T).T # .T는 배열 전치(Transpose)
[[-4. -3.5 -3. -2.5 -2. -1.5 -1. -0.5 0. 0.5 1. 1.5 2. 2.5
3. 3.5 4. 4.5 -4. -3.5 -3. -2.5 -2. -1.5 -1. -0.5 0. 0.5
1. 1.5 2. 2.5 3. 3.5 4. 4.5 -4. -3.5 -3. -2.5 -2. -1.5
-1. -0.5 0. 0.5 1. 1.5 2. 2.5 3. 3.5 4. 4.5 -4. -3.5
-3. -2.5 -2. -1.5 -1. -0.5 0. 0.5 1. 1.5 2. 2.5 3. 3.5
4. 4.5 -4. -3.5 -3. -2.5 -2. -1.5 -1. -0.5 0. 0.5 1. 1.5
2. 2.5 3. 3.5 4. 4.5 -4. -3.5 -3. -2.5 -2. -1.5 -1. -0.5
0. 0.5 1. 1.5 2. 2.5 3. 3.5 4. 4.5 -4. -3.5 -3. -2.5
-2. -1.5 -1. -0.5 0. 0.5 1. 1.5 2. 2.5 3. 3.5 4. 4.5
-4. -3.5 -3. -2.5 -2. -1.5 -1. -0.5 0. 0.5 1. 1.5 2. 2.5
3. 3.5 4. 4.5 -4. -3.5 -3. -2.5 -2. -1.5 -1. -0.5 0. 0.5
1. 1.5 2. 2.5 3. 3.5 4. 4.5 -4. -3.5 -3. -2.5 -2. -1.5
-1. -0.5 0. 0.5 1. 1.5 2. 2.5 3. 3.5 4. 4.5 -4. -3.5
-3. -2.5 -2. -1.5 -1. -0.5 0. 0.5 1. 1.5 2. 2.5 3. 3.5
4. 4.5 -4. -3.5 -3. -2.5 -2. -1.5 -1. -0.5 0. 0.5 1. 1.5
2. 2.5 3. 3.5 4. 4.5 -4. -3.5 -3. -2.5 -2. -1.5 -1. -0.5
0. 0.5 1. 1.5 2. 2.5 3. 3.5 4. 4.5 -4. -3.5 -3. -2.5
-2. -1.5 -1. -0.5 0. 0.5 1. 1.5 2. 2.5 3. 3.5 4. 4.5
-4. -3.5 -3. -2.5 -2. -1.5 -1. -0.5 0. 0.5 1. 1.5 2. 2.5
3. 3.5 4. 4.5 -4. -3.5 -3. -2.5 -2. -1.5 -1. -0.5 0. 0.5
1. 1.5 2. 2.5 3. 3.5 4. 4.5 -4. -3.5 -3. -2.5 -2. -1.5
-1. -0.5 0. 0.5 1. 1.5 2. 2.5 3. 3.5 4. 4.5 -4. -3.5
-3. -2.5 -2. -1.5 -1. -0.5 0. 0.5 1. 1.5 2. 2.5 3. 3.5
4. 4.5]
[-4. -4. -4. -4. -4. -4. -4. -4. -4. -4. -4. -4. -4. -4.
-4. -4. -4. -4. -3.5 -3.5 -3.5 -3.5 -3.5 -3.5 -3.5 -3.5 -3.5 -3.5
-3.5 -3.5 -3.5 -3.5 -3.5 -3.5 -3.5 -3.5 -3. -3. -3. -3. -3. -3.
-3. -3. -3. -3. -3. -3. -3. -3. -3. -3. -3. -3. -2.5 -2.5
-2.5 -2.5 -2.5 -2.5 -2.5 -2.5 -2.5 -2.5 -2.5 -2.5 -2.5 -2.5 -2.5 -2.5
-2.5 -2.5 -2. -2. -2. -2. -2. -2. -2. -2. -2. -2. -2. -2.
-2. -2. -2. -2. -2. -2. -1.5 -1.5 -1.5 -1.5 -1.5 -1.5 -1.5 -1.5
-1.5 -1.5 -1.5 -1.5 -1.5 -1.5 -1.5 -1.5 -1.5 -1.5 -1. -1. -1. -1.
-1. -1. -1. -1. -1. -1. -1. -1. -1. -1. -1. -1. -1. -1.
-0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5
-0.5 -0.5 -0.5 -0.5 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0.5 0.5 0.5 0.5 0.5 0.5
0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 1. 1.
1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
1. 1. 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5 1.5
1.5 1.5 1.5 1.5 1.5 1.5 2. 2. 2. 2. 2. 2. 2. 2.
2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2.5 2.5 2.5 2.5
2.5 2.5 2.5 2.5 2.5 2.5 2.5 2.5 2.5 2.5 2.5 2.5 2.5 2.5
3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3. 3.
3. 3. 3. 3. 3.5 3.5 3.5 3.5 3.5 3.5 3.5 3.5 3.5 3.5
3.5 3.5 3.5 3.5 3.5 3.5 3.5 3.5 4. 4. 4. 4. 4. 4.
4. 4. 4. 4. 4. 4. 4. 4. 4. 4. 4. 4. 4.5 4.5
4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5 4.5
4.5 4.5]]
plt.figure() # Figure인스턴스를 생성하는데 Figure인스턴스의 역할은 이미지 전체의 영역을 확보하는 것
plt.quiver(X, Y, -grad[0], -grad[1], angles="xy",color="#666666")
# quiver는 2차원 평면 상에서 좌표마다 scaled된 화살표를 그릴 때 사용
plt.xlim([-2, 2])
plt.ylim([-2, 2])
plt.xlabel('x0')
plt.ylabel('x1')
plt.grid()
# https://stackoverflow.com/questions/23141452/difference-between-plt-draw-and-plt-show-in-matplotlib
plt.draw() # display the current figure that you are working on
plt.show() # re-draw the figure. This allows you to work in interactive mode and, should you have changed your data or formatting, allow the graph itself to change.
최적의 매개변수(가중치와 편향)를 학습 시에 찾아야 함
최적이란 손실 함수가 최솟값이 될 때의 매개변수 값
각 지점에서 함수의 값을 낮추는 방안을 제시하는 지표가 기울기
경사법은 현 위치에서 기울어진 방향으로 일정 거리만큼 이동
이동한 곳에서도 마찬가지로 기울기를 구하고, 또 그 기울어진 방향으로 나아가기를 반복
이렇게 해서 함수의 값을 점차 줄이는 것이 경사법
경사법 수식
기호는 갱신하는 양 : 신경망 학습에서는 학습률
매개변수 값을 얼마나 갱신하느냐를 정하는 것이 학습률
경사 하강법 구현
def gradient_descent(f, init_x, lr=0.01, step_num=100):
x = init_x
for i in range(step_num):
grad = numerical_gradient(f, x)
x -= lr * grad
return x
인수 f는 최적화하려는 함수
init_x는 초깃값
lr은 learning rate를 의미하는 학습률
step_num은 경사법에 따른 반복 횟수
함수의 기울기는 numerical_gradient(f, x)로 계산
그 기울기에 학습률을 곱한 값으로 갱신 처리를 step_num만큼 반복
경사법 예상 문제
최종 결과 : [-0.03458765 0.04611686]
import numpy as np
import matplotlib.pylab as plt
#from gradient_2d import numerical_gradient
def _numerical_gradient_no_batch(f, x):
h = 1e-4 # 0.0001
grad = np.zeros_like(x)
for idx in range(x.size):
tmp_val = x[idx]
x[idx] = float(tmp_val) + h
fxh1 = f(x) # f(x+h)
x[idx] = tmp_val - h
fxh2 = f(x) # f(x-h)
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = tmp_val
return grad
def numerical_gradient(f, X):
if X.ndim == 1:
return _numerical_gradient_no_batch(f, X)
else:
grad = np.zeros_like(X)
for idx, x in enumerate(X):
grad[idx] = _numerical_gradient_no_batch(f, x)
return grad
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)
def function_2(x):
return x[0]**2 + x[1]**2
init_x = np.array([-3.0, 4.0])
lr = 0.1
step_num = 20
x, x_history = gradient_descent(function_2, init_x, lr=lr, step_num=step_num)
plt.plot( [-5, 5], [0,0], '--b')
plt.plot( [0,0], [-5, 5], '--b')
plt.plot(x_history[:,0], x_history[:,1], 'o')
plt.xlim(-3.5, 3.5)
plt.ylim(-4.5, 4.5)
plt.xlabel("X0")
plt.ylabel("X1")
plt.show()
학습률이 너무 크거나 작으면 좋은 결과를 얻을 수 없음
학습률이 너무 크면 큰 값으로 발산 : [-2.58983747e+13 -1.29524862e+12]
학습률이 너무 작으면 거의 갱신되지 않고 종료 : [-2.99999999 3.99999998]
학습률 같은 매개변수는 하이퍼파라미터
가중치와 편향 같은 신경망의 매개변수와는 달리, 하이퍼파라미터는 사람이 직접 설정해야 하는 매개변수
신경망에서 기울기는 가중치 매개변수에 대한 손실 함수의 기울기
형상이 2x3, 가중치가 W, 손실 함수가 L인 신경망인 경우
경사는
은 을 조금 변경했을 때 손실 함수 L이 얼마나 변화하느냐를 의미
간단한 신경망을 통한 실제로 기울기를 구하는 코드 구현