<신경망 학습>

: 훈련 데이터로부터 가중치(매개변수)의 최적값을 자동으로 획득하는 것
(c.f. 신경망의 특징은 데이터를 보고 학습할 수 있다는 점이다)

기계학습이란 데이터에서 답을 찾고 데이터에서 패턴을 발견하고 데이터로 이야기를 만드는 것이다.
범용 능력(훈련data가 아닌 데이터로도 문제를 올바르게 풀어내는 능력) 을 획득하는 것이 기계학습의 최종 목표
오버피팅(하나의 data set에만 지나치게 최적화된 상태) 피하기는 기계학습의 중요한 과제

딥러닝 = 종단간 기계학습
:처음부터 끝까지 사람의 개입 없이 데이터(입력)에서 목표 결과(출력)를 얻는다.

만약 5를 인식하고 싶다면 이미지에서 특징(입력 데이터에서 본질적인 데이터를 정확하게 추출할 수 있도록 설계된 변환기)을 추출하고 그 특징의 패턴을 기계학습 기술로 학습하는 방법이 있다.

컴퓨터 비전 분야에서는 SIFT, SURF HOG 등의 특징을 사용하고
이들의 특징을 사용하여 SVM, KNN(이미지 데이터를 벡터로 변환한 분류기법)으로 학습 가능하다.

1. 손실 함수

(loss function): (입력에 대한 모델의 출력과 실제 값 사이의 차이를 측정하여) 모델의 성능을 측정하는 데 사용되는 함수

즉, 신경망이 스스로 오차를 줄일 수 있도록 학습하게 하는 지표이다.
이 손실함수의 결괏값을 가장 작게 만드는 가중치(매개변수)를 찾는 것이 학습의 목표!
모델의 예측값과 실제값 간의 차이를 나타내며, 출력과 실제 값의 차이를 최소화하거나 최적화하기 위해 모델의 매개변수를 조정하는 데 사용됨

즉!! 손실함수로 컴퓨터에게 오차를 알려줘야 함!!

회귀 문제에서는 평균 제곱 오차(Mean Squared Error, MSE)가 일반적이며, 분류 문제에서는 교차 엔트로피(Cross Entropy)가 널리 사용

(1-1) 오차제곱합(SSE)

  • 손실 함수 중 하나로, 예측 값과 실제 값 사이의 차이를 제곱하여 모두 더한 값
  • 예측 값과 실제 값의 차이의 제곱을 최소화하는 것이 목표

오차가 더 작으면 실제값과 추정 값이 비슷하다고 볼 수 있음으로 정답에 더 가깝다

y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
t = [0,0,1,0,0,0,0,0,0,0]

y는 출력값 (softmax 함수에서는 확률로 해석) 으로서 (0,1,2 ...) 2에 해당하는 확률이 0.6

t는 정답 레이블로 정답을 가리키는 위치의 원소는 1, 그 이외는 0으로 표기가 된다. (원핫인코딩)

숫자 '2'가 해당하는 원소의 값이 1이므로 정답이 '2'임을 알 수가 있다.

밑의 코드는 평균 제곱 오차를 파이썬으로 구현한 것이다.

 def mean\_squared\_error(y, t):
     return 0.5\*np.sum((y-t)\*\*2)

정답은 2

SSE 활용 예시

  • 주로 회귀(Regression) 문제에서 사용됩니다. 회귀 모델에서는 입력 변수와 출력 변수 간의 관계를 모델링하는데 사용되며, SSE를 최소화하여 모델을 학습하고 예측 결과를 평가합니다.
  1. 데이터 수집: 주택 가격과 관련된 데이터를 수집합니다. 주택의 크기, 위치 등과 가격 정보가 포함될 수 있습니다.
  2. 모델 정의: 선형 회귀 모델을 선택하고, 주택의 크기를 입력 변수로, 가격을 출력 변수로 정의합니다.
  3. 모델 학습: 주어진 데이터를 사용하여 모델을 학습합니다. 학습하는 동안에는 SSE를 최소화하기 위해 모델의 가중치와 편향을 조정합니다.
  4. 모델 평가: 학습된 모델을 사용하여 새로운 주택의 크기에 대한 가격을 예측합니다. 이때 예측 결과와 실제 가격을 비교하여 SSE를 계산하고, 이를 기반으로 모델의 성능을 평가합니다.

오차제곱합의 장, 단점

장점:

  • 연속적인 값들에 대한 차이를 측정하기 때문에 회귀 문제에 유용합니다.
  • 미분 가능한 함수이기 때문에 경사 하강법 등 최적화 알고리즘에 적용하기 용이합니다.

단점:

  • 이상치(outliers)에 민감하게 반응합니다. 이상치가 있는 경우 SSE는 크게 증가하여 모델의 성능을 왜곡할 수 있습니다.
  • 예측 값과 실제 값 사이의 차이를 제곱하기 때문에, 오차가 클수록 크게 패널티를 부여하기 때문에 모델의 안정성에 영향을 미칠 수 있습니다.

(1-2) 교차 엔트로피 오차(CEE)

  • 정보이론에서 확률분포사이의 거리를 재는 방법
  • 주어진 데이터와 모델의 예측값 사이의 차이를 계산하는 데 사용
  • 이를 통해 모델이 얼마나 정확한 예측을 하는지를 평가하고, 이를 통해 모델을 학습시킴

방식

  • CEE는 실제값 와 모델의 예측값의 로그 값을 곱하여 모든 데이터에 대해 합산합니다.
  • 이때, 실제값은 0 또는 1의 이진값으로 표현되는 One-hot 벡터로 사용됩니다.

특징

  • CEE는 모델이 출력값과 실제값 사이의 차이를 미분 가능한 연속 함수로 나타내기 때문에, 경사 하강법(gradient descent) 등의 최적화 알고리즘에 적용할 수 있습니다.
  • 두 확률 분포간의 유사성을 측정하는데 유용하며, 특히 분류 문제에서 자주 사용
    • 이진 분류 문제에만 사용할 수 있으며, 다중 클래스 분류에는 확장하기 어렵습니다.

log는 밑이 e인 자연로그
yk는 신경망의 출력
tk는 정답 레이블

신경망 출력이 0.6이라면 교차 엔트로피 오차는 -log0.6으로 결과는 0.51이 된다.
즉, 교차 엔트로피 오차는 정답일 때의 출력이 전체 값을 정하게 된다.

def cross\_entropy\_error(y,t):
delta = 1e-7
    return -np.sum(t\*np.log(y+delta))

정답은 평균 제곱 오차에서와 같이 똑같이 2

식에서 np.log를 계산할 때 아주 작은 값인 delta 를 더한 이유:
np.log() 함수에 0을 입력하면 '마이너스 무한대'를 뜻하는 -inf가 되어
더 이상 계산을 진행할 수가 없다.
이는 절대 0이 되지 않도록(-inf) 하기 위함이다.

(1-3) 미니배치 학습

MNIST 데이터셋은 훈련 데이터가 60,000개 이므로
신경망 학습에서 훈련 데이터로부터 일부만 골라 학습을 수행한다.

그 일부가 바로 미니배치이며
가령 60,000장의 훈련 데이터 중에서
100장을 무작위로 뽑은 학습 방법이 바로 미니배치 학습

import sys, os
sys.path.append(os.pardir)
import numpy as np

from dataset.mnist import load\_mnist
(x\_train, t\_train),(x\_train,t\_test) = \\
load\_mnist(normalie=True, one\_hot\_label=True)
 print(x\_train.shape)
 print(t\_train.shape)

load_mnist는 MNIST 데이터셋을 읽어오는 함수이다.
위 함수는 dataset/mnist.py 파일에 있다.

load_mnist

코드를 돌린 결과 훈련 데이터는 60,000개이고 입력 데이터는 784열인 이미지 데이터임을 알 수 있다. 정답 레이블을 10줄짜리 데이터이며 x_train, t_train의 모습은 각각(60000,784)와 (60000,10)이 된다.

만약 훈련 데이터에서 무작위로 10장만 빼내려면 np.random.choice()함수를 쓰면 된다.

train\_size = x\_train.shape\[0\]
batch\_size = 10
batch\_mask = np.random.choice(train\_size, batch\_size)
x\_batch = x\_train\[batch\_mask\]
t\_batch = t\_train\[batch\_mask\]

0~60000 미만의 수 중에서 무작위로 10개 골라내기 위해서는
np.random.choice(60000,10) 사용한다.

(1-4) 교차엔트로피 오차 구현

미니배치 같은 배치 데이터를 지원하는
교차 엔트로피 오차를 구현하기 위해서는
교차 엔트로피 오차(데이터를 하나씩 처리하는 구현)를 조금만 바꿔주면 된다.

def cross\_entropy\_error(y, t):

    if y.ndim ==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+le-7))/batch\_size

위 코드는 데이터가 하나인 경우와 데이터가 배치로 묶여 입력될 경우 모두를 처리할 수 있도록 구현 되었다.

이 코드에서 y는 신경망의 출력, t는 정답 레이블이다.
y가 1차원이라면,
즉 데이터 하나당 교차 엔트로피 오차를 구하는 경우는
reshape 함수로 데이터의 형상을 바꿔준다.
그리고 배치의 크기로 나눠 정규화하고
이미지 1장당 평균의 교차 엔트로피 오차를 계산한다.

(1-5) 왜 손실 함수를 설정하는가?

손실함수를 사용하는 이유는 정확도를 끌어내는 매개변수 값을 찾는 것이 우리의 목표이기 때문이다.

정확도라는 지표를 두고 손실 함수의 값을 사용하는 이유는 미분의 역할에서 주목하게 되며 다음 장에서 설명하게 된다.

정확도를 지표로 삼아서는 안되는 이유는 미분 값이 대부분의 장소에서 0이 되어 매개변수를 갱신할 수 없기 때문이다.

2. 수치 미분

(2-1) 미분

함수의 이름은 수치 미분에서 따온 numerical_diff(f,x)로 한다.

def numerical\_diff(f, x):

    h = 1e-4 # 0.0001

    return (f(x+h) - f(x-h)) / (2\*h)

(2-2) 수치 미분

def function\_1(x):

    return 0.01\*x\*\*2 + 0.1\*x

위 코드는 y=0.01x^2+0.1x를 나타낸 파이썬 코드이다.

계산된 미분 값은 x에 대한 f(x)의 변화량, 즉 함수의 기울기에 해당한다. 그리고 x가 5일때와 10일 때의 진정한 미분은 차례로 0.2와 0.3이다.

(2-3) 편미분

: 변수가 여럿인 함수에 대한 미분
앞에와 달리 변수가 2개라는 점에 주의해야 한다.

def function\_2(x):

return x\[0\]\*\*2 + x\[1\]\*\*2

    #또는 return np.sum(x\*\*2)

인수 x는 넘파이 배열이라고 가정하며 넘파이 배열의 각 원소를 제곱하고 그 합을 구할 간단한 형태로 구현할 수 있다.

3. 기울기

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

여기서 numerical_gradient(f,x) 함수의 구현은 복잡해 보이지만, 동작 방식은 변수가 하나일 때의 수치 미분과 거의 같다. f는 함수, x는 넘파이 배열이므로 넘파이 배열 x의 각 원소에 대해서 수치 미분을 구한다.

기울기는 각 지접에서 낮아지는 방향을 가리킨다.
즉, 기울기가 가리키는 쪽은 각 장소에서 함수의 출력 값을 가장 크게 줄이는 방향이다!

(3-1) 경사 하강법

신경망에서 최적의 매개변수 중 최적이란 손실 함수가 최솟값이 될 때의 매개변수 값이다. 하지만 일반적인 손실함수는 복잡하며 최솟값이 되는 곳을 짐작하기 어렵다. 이런 상황에서 기울기를 잘 이용해 함수의 최솟값(또는 가능한 한 작은 값)을 찾으려는 것이 경사법이다.

주의할 점은 각 지점에서 함수의 값을 낮추는 방안을 제시하는 지표가 기울기라는 것이다. 경사법을 수식으로 나타낼 때 갱신하는 양을 학습률이라고 표현한다. 즉, 한 번의 학습으로 얼마만큼 학습해야 할지를 정하는 것이 학습률이다.

from gradient\_2d import numerical\_gradient
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)

(3-2) 신경망에서의 기울기

신경망 학습에서의 기울기는 가중치 매개변수에 대한 손실 함수의 기울기이다. 가중치가 W, 손실 함수가 L인 신경망 경우 편미분을 한다. 그리고 손실 하수 L이 얼마나 변하는지에 대해서 알려주는 것이 w11이다.

기울기를 구하는 코드

class simpleNet:
    def \_\_init\_\_(self):
        self.W = np.random.randn(2,3)
    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

기울기를 구하는 다른 방법

x = np.array(\[0.6, 0.9\])
t = np.array(\[0, 0, 1\])
net = simpleNet()
f = lambda w: net.loss(x, t)
dW = numerical\_gradient(f, net.W)
print(dW)

4. 학습 알고리즘 구현

전체 : 신경망에는 적응 가능한 가중치와 편향이 있고, 이 가중치와 편향을 훈련 데이터에 적응하도록 조정하는 과정을 학습이라 한다

1단계-미니배치
: 훈련 데이터 중 일부를 무작위로 가져온다. 미니배치의 손실 함수 값을 줄이는 것이 목표이다.

2단계-기울기 산출
: 미니배치의 손실 함수 값을 줄이기 위해 각 가주이 매개변수의 기울기를 구한다. 기울기는 손실 함수의 값을 가장 작게 하는 방향을 제시한다.

3단계-매개변수 갱신
: 가중치 매개변수를 기울기 방향으로 아주 조금 갱신한다.

4단계 : 1~3단계를 반복한다.

하강법으로 매개변수를 갱신하는 방법이며 이때 데이터를 미니배치로 무작위로 선정하기 때문에 확률적 경사 하강법이라 부른다.

(4-1) 2층 신경망 클래스 구현

import sys, os
sys.path.append(os.pardir) 
from common.functions import \*
from common.gradient import numerical\_gradient

class TwoLayerNet:
    def \_\_init\_\_(self, input\_size, hidden\_size, output\_size, weight\_init\_std=0.01):
        self.params = {}
        self.params\['W1'\] = weight\_init\_std \* np.random.randn(input\_size, hidden\_size)
        self.params\['b1'\] = np.zeros(hidden\_size)
        self.params\['W2'\] = weight\_init\_std \* np.random.randn(hidden\_size, output\_size)
        self.params\['b2'\] = np.zeros(output\_size)
    def predict(self, x):
        W1, W2 = self.params\['W1'\], self.params\['W2'\]
        b1, b2 = self.params\['b1'\], self.params\['b2'\]
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)
        return y

    # x: 입력 데이터, t: 정답 레이블
    def loss(self, x, t):
        y = self.predict(x)
        return cross\_entropy\_error(y, t)
    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        t = np.argmax(t, axis=1)
        accuracy = np.sum(y == t) / float(x.shape\[0\])
        return accuracy

    # x: 입력 데이터, t: 정답 레이블
    def numerical\_gradient(self, x, t):
        loss\_W = lambda W: self.loss(x, t)
        grads = {}
        grads\['W1'\] = numerical\_gradient(loss\_W, self.params\['W1'\])
        grads\['b1'\] = numerical\_gradient(loss\_W, self.params\['b1'\])
        grads\['W2'\] = numerical\_gradient(loss\_W, self.params\['W2'\])
        grads\['b2'\] = numerical\_gradient(loss\_W, self.params\['b2'\])
return grads

(4-2) 미니배치 학습 구현

미니배치 학습이란 훈련 데이터 중 일부를 무작위로 꺼내고, 그 미니배치에 대해서 경사법으로 매개변수를 갱신하는 것이다. TwoLayerNet으로 학습을 수행해본다.

from dataset.mnist import load\_mnist
from two\_layer\_net import TwoLayerNet

(x\_train, t\_train), (x\_test, t\_test) = load\_mnist(normalize=True, one\_hot\_label=True)
network = TwoLayerNet(input\_size=784, hidden\_size=50, output\_size=10)
iters\_num = 10000  
train\_size = x\_train.shape\[0\]
batch\_size = 100
learning\_rate = 0.1
train\_loss\_list = \[\]
train\_acc\_list = \[\]
test\_acc\_list = \[\]
iter\_per\_epoch = max(train\_size / batch\_size, 1)

for i in range(iters\_num):
    batch\_mask = np.random.choice(train\_size, batch\_size)
    x\_batch = x\_train\[batch\_mask\]
    t\_batch = t\_train\[batch\_mask\]
    #grad = network.numerical\_gradient(x\_batch, t\_batch)
    grad = network.gradient(x\_batch, t\_batch)

    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params\[key\] -= learning\_rate \* grad\[key\]
    loss = network.loss(x\_batch, t\_batch)
    train\_loss\_list.append(loss)

위 코드를 통해 신경망의 가중치 매개변수가 학습 횟수가 늘어가면서 손실 함수의 값이 줄어드는 것을 확인할 수 있다. 즉, 신경망의 가중치 매개변수가 서서히 데이터에 적응하고 있음을 의미한다. 신경망이 학습하고 있다는 것이다.

데이터를 반복해서 학습함으로서 최적 가중치 매개변수로 서서히 다가서고 있다.

(4-3) 시험 데이터로 평가하기

신경망 학습의 목표는 범용적인 능력을 익히는 것이다. 오버피팅 일으키지 않는지 확인해야한다. 아래는 평가하기 위한 코드이다.

from dataset.mnist import load\_mnist
from two\_layer\_net import TwoLayerNet

(x\_train, t\_train), (x\_test, t\_test) = load\_mnist(normalize=True, one\_hot\_label=True)
network = TwoLayerNet(input\_size=784, hidden\_size=50, output\_size=10)
iters\_num = 10000 
train\_size = x\_train.shape\[0\]
batch\_size = 100
learning\_rate = 0.1
train\_loss\_list = \[\]
train\_acc\_list = \[\]
test\_acc\_list = \[\]
iter\_per\_epoch = max(train\_size / batch\_size, 1)

for i in range(iters\_num):
    batch\_mask = np.random.choice(train\_size, batch\_size)
    x\_batch = x\_train\[batch\_mask\]
    t\_batch = t\_train\[batch\_mask\]
    #grad = network.numerical\_gradient(x\_batch, t\_batch)
    grad = network.gradient(x\_batch, t\_batch)

    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params\[key\] -= learning\_rate \* grad\[key\]
    loss = network.loss(x\_batch, t\_batch)
    train\_loss\_list.append(loss)

    if i % iter\_per\_epoch == 0:
        train\_acc = network.accuracy(x\_train, t\_train)
        test\_acc = network.accuracy(x\_test, t\_test)
        train\_acc\_list.append(train\_acc)
        test\_acc\_list.append(test\_acc)
        print("train acc, test acc | " + str(train\_acc) + ", " + str(test\_acc))
profile
Fantivation

0개의 댓글