본문 바로가기

deeplearning

5장. 오차 역전파 ( 계산 그래프, 연쇄법칙, 역전파 )

728x90
반응형

오차 역전파를 이용한 3 신경망 학습시키는 방법

 

4장에서는 순전파를 배웠고

가중치를 갱신하기 위한 기울기를 구하기 위해 수치미분을 배웠다.

순전파 + 가중치 갱신 ( 수치미분을 이용해서 )

 

 

역전파 ?

신경망 학습 처리에서 최소화되는 함수의 경사를

효율적으로 계산하기 위한 방법으로 "오류 역전파" 있다.

 

-함수의 경사(기울기) 계산하는 방법?

 

1. 수치미분     <---너무 성능이 느리다

2. 오류 역전파

 

-순전파 : 입력층 ---> 은닉층 ---> 출력층

-역전파:   입력층 <--- 은닉층 <--- 출력층

 

여기서 역전파를 시키는 것이 "오류(오차)" 입니다.

출력층부터 차례대로 역방향으로 따라 올라가 층에 있는

노드의 오차를 계산할 있다.

 

노드의 오차를 계산하면 오차를 사용해서 함수의 경사(기울기)

계산할 있다.

 

" 전파된 오차를 이용해서 가중치를 조정한다"

"오류(오차) 역전파"

 

 

 

 

계산 그래프(p.148)

"순전파와 역전파에 계산 과정을 그래프로 나타내는 방법"

 

계산 그래프의 장점이 무엇인가?

국소적 계산 있다.

국소적 계산이란 전체에 어떤일이 벌어지든 상관없이

자신과 관련된 정보만으로 다음 결과를 출력할 있다는 것이다.

 

 

400원이라는 숫자가 어떻게 계산되었느냐와는 상관없이

사과가 어떻게 200원이 되었는가만 신경쓰면 된다는 것이

국소적 계산이다.

 

 

 

계산 그래프로 문제를 해결하는가?

전체가 아무리 복잡해도 노드에서 단순한 계산에 집중하여

문제를 단순화 시킬 있다.

 

 

실제로 계산 그래프를 사용하는 가장 이유는?

역전파를 통해서 미분을 효율적으로 계산할 있는 점에 있다.

사과값이 '아주조금' 올랐을 '지불금액' 얼마나 증가하는지 알고 싶을

것이다.

 

 

          ∂ 지불금액

         -----------------

            ∂ 사과값

 

지불금액을 사과값으로 편미분하면 있다.

계산 그래프 역전파를 이용하면 되는데

사과값이 1원이 오르면 (사과2개에 대한) 최종 금액은 2.2 원이 오른다.

 

증가한 지불금액은 1+부가세 인데

역전파 시키기 위해 "노드의 국소적 미분" 한다.

 

사과가격이 오르면 최증금액에 어떤 영향을 끼치는가를 알고 싶다.

이는

'사과 가격에 대한 지불 금액의 미분' 구하는 문제에 해당.

 

  지불금액              L

-----------------       =      --------------

  사과값                x

 

 

 

 

연쇄법칙

덧셈노드의 역전파

곱셈노드의 역전파

 

 

문제209. 곱셈계층을 파이썬으로 구현하시오(p161)

 

class MuLayer:

    def __init__(self):

        self.x = None

        self.y = None

       

    def forward(self, x, y):

        self.x = y

        self.y = y

        out = x*y

       

        return out

   

    def backward(self, dout):

        dx = dout*self.y   # x y를 바꾼다.

        dy = dout * self.x

       

       

        return dx, dy

 

 

 

 

 

문제210.

위에서 만든 곱셈 클래스를 객체화 시켜서 사과 가격의 가격을 구하시오

 

class MulLayer:

    def __init__(self):

        self.x = None

        self.y = None

       

    def forward(self, x, y):  # 순전파

        self.x = y

        self.y = y

        out = x*y       

        return out

   

    def backward(self, dout): # 역전파

        dx = dout*self.y   # x y를 바꾼다.

        dy = dout * self.x               

        return dx, dy

 

 

apple = 100

apple_num = 2

tax = 1.1

   

#계층들

mul_apple_layer = MulLayer()

mul_tax_layer = MulLayer()

 

#순전파

apple_price = mul_apple_layer.forward(apple, apple_num)

price = mul_tax_layer.forward(apple_price, tax)

 

 

print(int(price))

220

 

 

 

 

 

문제211.

덧셈계층을 파이썬으로 구현하시오( 163p)

 

class AddLayer:

    def __init__(self):

        pass

   

    def forward(self, x, y):

        out = x+y

        return out

   

    def backward(self, dout):

        dx = dout * 1   #덧셈노드는 상류값을 여과없이 하류로 흘려보냄

        dy = dout * 1

        return dx, dy

 

 

 

 

 

 

문제212.

위에서 만든 곱셈 클래스와 덧셈클래스를 이용해서

149쪽의 그림 5-3 신경망을 구현하시오

(순전파만)

 

사과가격: 100

사과갯수: 2

가격: 150

갯수: 3

소비세: 1.1

 

 

   

class MulLayer:

    def __init__(self):

        self.x = None

        self.y = None

       

    def forward(self, x, y):  # 순전파

        self.x = x

        self.y = y

        out = x*y       

        return out

   

    def backward(self, dout): # 역전파

        dx = dout*self.y   # x y를 바꾼다.

        dy = dout * self.x               

        return dx, dy

 

 

class AddLayer:

    def __init__(self):

        pass

   

    def forward(self, x, y):

        out = x+y

        return out

   

    def backward(self, dout):

        dx = dout * 1   #덧셈노드는 상류값을 여과없이 하류로 흘려보냄

        dy = dout * 1

        return dx, dy

 

 

apple = 100

apple_num = 2

orange = 150

orange_num = 3

tax = 1.1

     

mul_apple_layer = MulLayer()

mul_orange_layer = MulLayer()

add_apple_orange_layer = AddLayer()

mul_tax_layer = MulLayer()

 

 

apple_price = mul_apple_layer.forward(apple, apple_num)

orange_price = mul_orange_layer.forward(orange, orange_num)

all_price = add_apple_orange_layer.forward(apple_price, orange_price) 

price = mul_tax_layer.forward(all_price, tax)

print(price)

715.0000000000001

 

 

 

 

 

 

문제213.( 164)

이번에는 순전파로 출력한 과일 가격의 총합 신경망의

역전파 구현하시오

 

110.00000000000001

2.2

3.3000000000000003

165.0

650

 

 

class MulLayer:

    def __init__(self):

        self.x = None

        self.y = None

       

    def forward(self, x, y):  # 순전파

        self.x = x

        self.y = y

        out = x*y       

        return out

   

    def backward(self, dout): # 역전파

        dx = dout*self.y   # x y를 바꾼다.

        dy = dout * self.x               

        return dx, dy

 

 

 

class AddLayer:

    def __init__(self):

        pass

   

    def forward(self, x, y):

        out = x+y

        return out

   

    def backward(self, dout):

        dx = dout * 1   #덧셈노드는 상류값을 여과없이 하류로 흘려보냄

        dy = dout * 1

        return dx, dy

 

 

   

apple = 100

apple_num = 2

orange = 150

orange_num = 3

tax = 1.1

     

mul_apple_layer = MulLayer()

mul_orange_layer = MulLayer()

add_apple_orange_layer = AddLayer()

mul_tax_layer = MulLayer()

 

 

apple_price = mul_apple_layer.forward(apple, apple_num)

orange_price = mul_orange_layer.forward(orange, orange_num)

all_price = add_apple_orange_layer.forward(apple_price, orange_price) 

price = mul_tax_layer.forward(all_price, tax)

print(price)

 

dprice = 1

dall_price, dtax = mul_tax_layer.backward(dprice)

dapple_price, dorange_price = add_apple_orange_layer.backward(dall_price)

dorange, dorange_num = mul_orange_layer.backward(dorange_price)

dapple, dapple_num = mul_apple_layer.backward(dapple_price)

 

print(dapple_num, dapple, dorange, dorange_num, dtax)

 

   

   

 

 

 

활성화 함수 계층 구현하기

1. Relu 계층을 위한 클래스 생성

"0 보다 값이 입력되면 값을 그대로 출력하고

0 이거나 0보다 작은값이 입력되면 0 출력하는 함수"

-순전파 함수

-역전파 함수

 

 

*Relu 클래스를 이해하려면 알아야할 파이썬 문법

1. copy 의미

2. x[ x < = 0 ] 의미

 

 

 

1. copy 테스트

 

from copy import copy

 

a = [1,2,3]

b = copy(a)

 

print(b)

a[1] = 6

print(a)   # [1,6,3]

print(b)   # [1,2,3]

# 카피시킨것은 원본을 바꿔도 카피했던 그때 그값 그대로 유지함.

 

 

2. x[x<=0] 의미

import numpy as np

x = np.array( [ [1.0, -0.5],[-2.0, 3.0] ])

print(x)

 

mask = (x <= 0)

print(mask)

[[False  True]

 [ True False]]

 

 

out = x.copy()

out[mask]=0

print(out)

[[ 1.  0.]

 [ 0.  3.]]

 

 

 

 

문제214.

166p Relu 클래스를 생성하시오

import numpy as np

from copy import copy

 

class Relu:

   

    def __init__(self):

        self.mask = None

       

       

    def forward(self, x):               

        out = x.copy()

        self.mask = (x <= 0)

        out[self.mask] = 0

        return out

   

   

    def backward(self, dout):

        dout[self.mask] = 0

        dx = dout

        return dx

 

 

x = np.array([ [1.0, -0.5], [-2.0, 3.0] ])

relu = Relu()

x_forward = relu.forward(x)

print ( relu.backward(x_forward))

 

 

 

 

오차역전파를 이용해서 신경망을 학습 시키기

오차역전파의 장점? 성능이 빠르다

오차역전파의 단점? 코드 구현이 조금 어렵다

 

코드 구현을 쉽게 하기 위해서 사용한 방법?

"계산 그래프"

 

덧셈 계층, 곱셈 계층

 

 

 

문제215.

위의 relu 함수의 역전파를 실행하시오

역전파때 사용하는 입력값은 순전파의 결과값을 그대로 사용하시오

 

x = np.array([ [1.0, -0.5], [-2.0, 3.0] ])

relu = Relu()

x_forward = relu.forward(x)

print ( relu.backward(x_forward))

 

 

이런 경우의 예상값은?

x=np.array([[1.0,-0.5],[-2.0,3.0]])

relu=Relu()

result=relu.forward(x)

 

dout = np.array([[2.0, 3.0],[-3.0, -4.0]])

print(relu.backward(dout))

 

설명:

순전파일 사용했던 부울값이 있는 mask 정보를

역전파일 때도 사용하는 이유는

순전파일 전파가 되지 않았으면

역전파일때도 역전파가 되지 않게 하기 위해서이다.

 

x=np.array([[1.0,-0.5],[-2.0,3.0]])

relu=Relu()

result=relu.forward(x)

#여기서 mask값이

[[False  True

True  False]]

인데, mask 그대로 아래의 dout 받아쓴다.

 

dout = np.array([[2.0, 3.0],[-3.0, -4.0]])

print(relu.backward(dout))

# mask 값을 받아쓰기 때문에

[[2.0  0

   0   -4.0]]

이렇게 프린트 된다.

 

 

 

 

 

Sigmoid 계층

 

 

sigmoid 계산 그래프

 

 

 

문제217.

sigmoid 클래스를 객체화 시켜서 아래의 입력값을

순전파로 보낸 결과와 역전파로 나온 결과를 출력하시오

 

import numpy as np

 

class Sigmoid:

    def __init__(self):

        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

   

 

sig = Sigmoid()

x = np.array([[2.0,4.0],[-0.3,0.9]])

dout = np.array([[4.0,2.0],[-0.1,0.8]])

 

print(sig.forward(x))

print(sig.backward(dout))

   

       

[[ 0.88079708  0.98201379] #순전파

 [ 0.42555748  0.7109495 ]]

 

[[ 0.41997434  0.03532541] #역전파

 [-0.02444583  0.16440025]]

 

 

 

 

   

   

    Affine 계층

 

"신경망의 순전파 수행하는 행렬의 내적은

기하학에서는 어파인 변환이라고 합니다.

그래서 신경망에서 입력값과 가중치의 내적의 합에

바이어스를 더하는 층을 Affine 계층이라고 해서

구현합니다"

 

"지금까지의 계산 그래프는 노드 사이에 '스칼라값' 흘렀는데

이에 반해 이번에는 '행렬' 흐르고 있어서

Affine 계층 구현이 필요하다"


 

 

 

문제218.

아래의 행렬의 내적과 바이어스를 더한 값이 무엇인지

파이썬을 구현하시오

 

Y = (X 내적 W) + B

 

X: (1  2)

 

     1  3  5

W:(          )

     2  4  6

 

B: ( 1  2  3 )

 

 

import numpy as np

X = np.array([[1,2]])

W = np.array([[1,3,4],[2,4,6]])

B = np.array([[1,2,3]])

 

Y = np.dot(X,W) + B

 

print(Y)

 

[[ 6 13 19]]

 

 

 

문제219. Affine 클래스를 생성하시오( 176)

import numpy as np

 

class Affine:

    def __init__(self, W, b):

        self.W = W

        self.b = b

        self.x = None

        self.dW = None

        self.db = None

       

    def forward(self, x):

        self.x = x

        out = np.dot(x, self.W) + self.b

        return out

   

    def backward(self, dout):

        dx = np.dot(dout, self.W)

        self.dW = np.dot(self.x.T, dout)

        self.db = np.sum(dout, axit = 0)       

        return dx

 

 

 

 

 

문제220.

위의 Affine 클래스를 객체화 시켜

아래의 입력값 행렬, 가중치 행렬, 바이어스 행렬을

흘려보낸 순전파 결과가 어떻게 되는지 출력하시오

 

import numpy as np

 

class Affine:

    def __init__(self, W, b):

        self.W = W

        self.b = b

        self.x = None

        self.dW = None

        self.db = None

       

    def forward(self, x):

        self.x = x

        out = np.dot(x, self.W) + self.b

        return out

   

    def backward(self, dout):

        dx = np.dot(dout, self.W)

        self.dW = np.dot(self.x.T, dout)

        self.db = np.sum(dout, axis = 0)       

        return dx

 

np.random.seed(1234)    # 랜덤추출 고정시키기

# seed 쓰면 랜덤값이 고정추출된다.

x = np.random.randn(2)   #입력값

W = np.random.randn(2,3)   #가중치

b = np.random.randn(3)   #편향

 

af = Affine(W,b)

 

print(af.forward(x))

[-0.11967613 -1.94645411  0.87973534]

 

 

 

 

 

 

문제221.(점심시간문제)

 아래의 행렬의 수학식을 증명하시오

( 또는 파이썬 가능)

 

 

X내적W  !=  W내적x

↓↓↓↓↓↓↓↓↓↓↓

22(내적)22  !=  22(내적)22


 

import numpy as np

 

np.random.seed(12)

 

a = np.random.randn(2)

b = np.random.randn(2)

 

print(np.dot(a,b))

print(np.dot(b,a))

1.27359572205

1.27359572205

 

 

 

 

문제222.

X(내적)W = (W^T (내적) X^T)^T 라는 것을 파이썬으로 증명하시오

 

import numpy as np

X = np.array([[1,2],[3,4]])

W = np.array([[5,6],[7,8]])

 

print(np.dot(X,W))

print(np.dot(W.T,X.T).T)

[[19 22]

 [43 50]]

[[19 22]

 [43 50]]

 

 

 

 

문제223.

2*3 행렬을 전치시키면 몇행몇열 인지 파이썬으로 구현해보시오

 

np.random.seed(1234)

x = np.random.randn(2,3)

print(x.T.shape)

(3, 2)

 

 

 

 

문제224. 아래 수식을 파이썬으로 구현하시오

***내적의 결과값은 앞식의 + 뒷식의 shape으로 결정된다.

2x3(내적)3x4 = 2x4

              전치

4x3(내적)3x2 = 4x2

                   전치

      2x4

 

import numpy as np

np.random.seed(1234)

a = np.random.randn(2,3)

b = np.random.randn(3,4)

 

c = np.random.randn(4,3)

d = np.random.randn(3,2)

 

print(np.dot(a,b).shape)

print(np.dot(c,d).T.shape)

(2, 4)

(2, 4)

 

 

 

 

 

Affine 계층의 계산 그래프

 

문제225.

문제 220번에서 구성한 Affine 클래스를 객체화 시켜서

forward  backward 구현하시오

 

결과:

dX=

[[22, 28],

       [49, 64]]

 

dW=

[[13, 17, 21],

       [18, 24, 30]]

 

dB=[5, 7, 9]

 

 

 

import numpy as np

 

class Affine:

    def __init__(self, W, B):

        self.W = W

        self.b = B

        self.X = None

        self.dW = None

        self.db = None

       

    def forward(self, x):

        self.x = X

        out = np.dot(x, self.W) + self.b

        return out

   

    def backward(self, dout):

        dx = np.dot(dout, self.W.T)

        self.dW = np.dot(self.x.T, dout)

        self.db = np.sum(dout, axis = 0) #열의  

        return dx, self.dW, self.db

#return 여러값 써서 뽑는 방법

 

 

X = np.array([[1,2],[3,4]])

W = np.array([[1,3,5],[2,4,6]])

B = np.array([[1,1,1],[1,1,1]])

dout = np.array([[1,2,3],[4,5,6]])

 

af = Affine(W,B)

 

print(af.forward(X))

print(af.backward(dout))

 

 

[[ 6 12 18]

 [12 26 40]]

(array([[22, 28],

       [49, 64]]), array([[13, 17, 21],

       [18, 24, 30]]), array([5, 7, 9]))

===================================

다른방법

  def backward(self, dout):

        dx = np.dot(dout, self.W.T)

        self.dW = np.dot(self.x.T, dout)

        self.db = np.sum(dout, axis = 0) #열의  

        return dx

# return 한개 받아서

 

af = Affine(W,B)

 

print("forward: ",af.forward(X))

print("dx: ",af.backward(dout))

print("dw:",af.dW)   #self 붙어있어서 직접 불러오기 가능

print("db: ",af.db)

 

 

 

 

문제226.

Affine 클래스를 이용해서 2 신경망의 순전파를 구현하시오!

 

X = np.array([[1,2]])

W = np.array([[1,3,5],[2,4,6]])

W1 = np.array([[1,4],[2,5],[3,6]])

B1 = np.array([[1,2,3]])

B2 = np.array([[1,2]])

 

 

import numpy as np

class Affine:

    def __init__(self, W, B):

        self.W = W

        self.b = B

        self.X = None

        self.dW = None

        self.db = None

       

    def forward(self, x):

        self.x = X

        out = np.dot(x, self.W) + self.b

        return out

   

    def backward(self, dout):

        dx = np.dot(dout, self.W.T)

        self.dW = np.dot(self.x.T, dout)

        self.db = np.sum(dout, axis = 0) #열의  

        return dx

 

X = np.array([[1,2]])

W1 = np.array([[1,3,5],[2,4,6]])

W2 = np.array([[1,4],[2,5],[3,6]])

B1 = np.array([[1,2,3]])

B2 = np.array([[1,2]])

 

layer_1 = Affine(W1,B1)

layer_2 = Affine(W2,B2)

out1 = layer_1.forward(X)

out2 = layer_2.forward(out1)

print(out1)

print(out2)

[[ 6 13 20]]

[[ 93 211]]

 

 

 

 

문제227.

위의 2 신경망의 은닉층(1) 활성화 함수로 Relu 추가해서

구현하시오

import numpy as np

 

class Affine:

    def __init__(self, W, B):

        self.W = W

        self.b = B

        self.X = None

        self.dW = None

        self.db = None

       

    def forward(self, x):

        self.x = X

        out = np.dot(x, self.W) + self.b

        return out

   

    def backward(self, dout):

        dx = np.dot(dout, self.W.T)

        self.dW = np.dot(self.x.T, dout)

        self.db = np.sum(dout, axis = 0) #열의  

        return dx

   

 

 

class Relu:

    def __init__(self):

        self.mask=None

 

    def forward(self,x):

        self.mask=(x<=0)

        out=x.copy()

        out[self.mask]=0

        return out

 

    def backward(self,dout):

        dout[self.mask]=0

        dx=dout

        return dx

 

 

X = np.array([[1,2]])

W1 = np.array([[1,3,5],[2,4,6]])

W2 = np.array([[1,4],[2,5],[3,6]])

B1 = np.array([[1,2,3]])

B2 = np.array([[1,2]])

 

 

layer_1 = Affine(W1,B1)

layer_2 = Affine(W2,B2)

relu = Relu()

 

out1 = layer_1.forward(X)

rout1 = relu.forward(out1)   # out1 relu 적용

out2 = layer_2.forward(rout1)

print(out2)  # relu 1이상은 그대로 출력

[[ 93 211]]

 

 

 

 

문제228.

위의 2 신경망의 역전파를 Affine 클래스를 이용해서 구현하시오

역전파시 입력할 dY 위의 순전파의 결과인

out2 행렬값을 그대로 사용하시오

 

결과:

[[ 93 211]]

[[12385 16108]]

[[ 937 1241 1545]

 [1874 2482 3090]]

[ 937 1241 1545]

 

 

import numpy as np

 

class Affine:

    def __init__(self, W, B):

        self.W = W

        self.b = B

        self.X = None

        self.dW = None

        self.db = None

       

    def forward(self, x):

        self.x = X

        out = np.dot(x, self.W) + self.b

        return out

   

    def backward(self, dout):

        dx = np.dot(dout, self.W.T)

        self.dW = np.dot(self.x.T, dout)

        self.db = np.sum(dout, axis = 0) #열의  

        return dx

   

 

 

class Relu:

    def __init__(self):

        self.mask=None

 

    def forward(self,x):

        self.mask=(x<=0)

        out=x.copy()

        out[self.mask]=0

        return out

 

    def backward(self,dout):

        dout[self.mask]=0

        dx=dout

        return dx

 

 

X = np.array([[1,2]])

W1 = np.array([[1,3,5],[2,4,6]])

W2 = np.array([[1,4],[2,5],[3,6]])

B1 = np.array([[1,2,3]])

B2 = np.array([[1,2]])

 

 

layer_1 = Affine(W1,B1)

layer_2 = Affine(W2,B2)

relu = Relu()

 

out1 = layer_1.forward(X)

rout1 = relu.forward(out1) # 은닉 1층 순전파

out2 = layer_2.forward(rout1) # 출력층 순전파

print(out2)  # = dY

 

 

dout_2 = layer_2.backward(out2)

dout2_relu = relu.backward(dout_2)

dout_1 = layer_1.backward(dout2_relu)

 

 

 

print(dout_1)

print(layer_1.dW)

print(layer_1.db)

 

 

 

 

 

 

문제229.(오늘의 마지막 문제)

위의 2 신경망을 3 신경망으로 변경하시오 (순전파)

import numpy as np

 

class Affine:

    def __init__(self, W, B):

        self.W = W

        self.b = B

        self.X = None

        self.dW = None

        self.db = None

       

    def forward(self, x):

        self.x = X

        out = np.dot(x, self.W) + self.b

        return out

   

    def backward(self, dout):

        dx = np.dot(dout, self.W.T)

        self.dW = np.dot(self.x.T, dout)

        self.db = np.sum(dout, axis = 0) # 역전파 시킨 b 열의  

        return dx

   

 

 

class Relu:

    def __init__(self):

        self.mask=None

 

    def forward(self,x):

        self.mask=(x<=0)

        out=x.copy()

        out[self.mask]=0

        return out

 

    def backward(self,dout):

        dout[self.mask]=0

        dx=dout

        return dx

 

 

X = np.array([[1,2]])

W1 = np.array([[1,3,5],[2,4,6]])

W2 = np.array([[1,4],[2,5],[3,6]])

W3 = np.array([[5,6,7],[2,3,4]])

B1 = np.array([[1,2,3]])

B2 = np.array([[1,2]])

B3 = np.array([[1,1,1]])

 

 

layer_1 = Affine(W1,B1)

layer_2 = Affine(W2,B2)

layer_3 = Affine(W3,B3)

relu = Relu()

 

out1 = layer_1.forward(X)  # 입력층->은닉1

rout1 = relu.forward(out1) # 은닉1() relu

out2 = layer_2.forward(rout1) # 은닉1->은닉2

rout2 = relu.forward(out2)    #은닉2() relu

out3 = layer_3.forward(rout2)  #은닉2->출력층

print(out3)  # = dY   #마지막 L  [[ 888 1192 1496]]

 

 

 

 

 

 

 

 

역전파 시킨 코드

X = np.array([[1,2]])

W1 = np.array([[1,3,5],[2,4,6]])

W2 = np.array([[1,4],[2,5],[3,6]])

W3 = np.array([[5,6,7],[2,3,4]])

B1 = np.array([[1,2,3]])

B2 = np.array([[1,2]])

B3 = np.array([[1,1,1]])

 

layer_1 = Affine(W1,B1)

layer_2 = Affine(W2,B2)

layer_3 = Affine(W3,B3)

relu = Relu()

 

out1 = layer_1.forward(X)  # 입력층->은닉1

rout1 = relu1.forward(out1) # 은닉1()relu1

out2 = layer_2.forward(rout1) # 은닉1->은닉2

rout2 = relu2.forward(out2)    #은닉2()relu2

out3 = layer_3.forward(rout2)  #은닉2->출력층

print("dY:",out3)  # = dY   #마지막 L  [[ 888 1192 1496]]

 

 

dout_3 = layer_3.backward(out3)   # 출력층->은닉2

dout2_relu = relu2.backward(dout_3)# 은닉2() relu2

dout_2 = layer_2.backward(dout2_relu) # 은닉2->은닉1

dout1_relu = relu1.backward(dout_2) # 은닉1() relu1

dout_1 = layer_1.backward(dout1_relu) #은닉1->입력

 

 

 

print("역전파값:",dout_1)

print("dW:",layer_1.dW)

print("db:",layer_1.db)

 

__main__:36: VisibleDeprecationWarning: boolean index did not match indexed array along dimension 1; dimension is 3 but corresponding boolean dimension is 2

에러가 떴었는데

relu 하나만 써서 그렇다

mask 값을 기억하기 때문에

층의 relu 다르게 설정해줘야 한다.

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

 

layer_1 = Affine(W1,B1)

layer_2 = Affine(W2,B2)

layer_3 = Affine(W3,B3)

relu1 = Relu()

relu2 = Relu()

 

out1 = layer_1.forward(X)  # 입력층->은닉1

rout1 = relu1.forward(out1) # 은닉1()relu1

out2 = layer_2.forward(rout1) # 은닉1->은닉2

rout2 = relu2.forward(out2)    #은닉2()relu2

out3 = layer_3.forward(rout2)  #은닉2->출력층

print("dY:",out3)  # = dY   #마지막 L  [[ 888 1192 1496]]

 

 

dout_3 = layer_3.backward(out3)   # 출력층->은닉2

dout2_relu = relu2.backward(dout_3)# 은닉2() relu2

dout_2 = layer_2.backward(dout2_relu) # 은닉2->은닉1

dout1_relu = relu1.backward(dout_2) # 은닉1() relu1

dout_1 = layer_1.backward(dout1_relu) #은닉1->입력

 

 

print("역전파값:",dout_1)

print("dW:",layer_1.dW)

print("db:",layer_1.db)

 

dY: [[ 888 1192 1496]]

역전파값: [[1040872 1343296]]

dW: [[ 67408 100808 134208]

 [134816 201616 268416]]

db: [ 67408 100808 134208]

 

 

 

행렬과 신경망:

행렬은 여러개의 연산을 한번에 표기하고 처리할 있는 수학적 툴이다

 

행렬의 요소가 수행하는 반복적인 계산을 하나의 표기법으로

처리하고 연산할 있는 방식을 제공하는 것이

선형대수의 역할이다

 

행렬을 적용할 최종 목적 행렬의 shape 다를 경우 행렬을 전치하여

shape 맟춰주면 된다.

 

 

 

 

입력값을 1차원으로 하면 안되고 2차원으로 해야하는 이유?

x = np.array( [ [ 1 , 2 ] ] )

x=  np.array( [ 1 , 2 ] )

 

뒤이어 연산시킬 가중치값이 2차원이니까 !

 

 

 

Softmax-With-Loss 계층 (291p 자세하게 나옴)

 

 

입력이미지가 Affine 계층가 Relu계층을 통과하며 변환되고, 마지막

Softmax계층에 의해서 10개의 입력이 정규화된다.

그림에서는 숫자 '0' 점수는 5.3이며, 이것이 Softmax계층에 의해서

0.008(0.8%) 변환된다.

'2' 점수는 10.1에서 0.991(99.1%) 변환된다

 

 

문제230.

179페이지에 나오는 softmaxWithLoss 클래스를 생성하시오

import numpy as np

 

class simpleNet:

 

    #import numpy as np  #Ŭ·¡½º¾È¿¡¼­ ³ÑÆÄÀÌ ºÎ¸£¸é ÀÛµ¿ ¾ÈµÊ. ±×·¡¼­ ¸Ç À§¿¡ µÒ.

   

    def __init__(self):   # ·£´ýÀÇ w°ªÀ» »ý¼ºÇÑ´Ù.

        self.w = np.random.randn(2,3) 

        #return w

   

    def predict(self,x):  # 2Çà3¿­ ·£´ý w°ª°ú x¸¦ ³»Àû½ÃŲ´Ù.

        return np.dot(x, self.w )

   

   

    def softmax(self,x):  # Ãâ·ÂÃþ ÇÔ¼ö  

        c = np.max(x)

        return np.exp(x-c) / np.sum( np.exp(x - c) )

   

   

    def cross_entropy_error(self,y,t):

        import numpy as np

        delta = 1e-7   #log=0 Àº ¿¡·¯³ª¼­ y¿¡ µ¨Å¸¸¦ ´õÇØÁÖ±â À§ÇÔÀÌ´Ù.

        return -np.sum( t * np.log(y + delta ) )

   

   

    def loss(self, x, t):  # ¼ÒÇÁÆ®¸Æ½º¸¦ Åë°ú½ÃŲ È®·ü°ú, ½ÇÁ¦ t°ªÀ»

                            #Å©·Î½º_¿£Æ®·ÎÇÇ_¿ÀÂ÷ÇÔ¼ö¿¡ Åë°ú½ÃŲ´Ù.

        z = self.predict(x)

        y = self.softmax(z)

        loss = self.cross_entropy_error(y,t)       

        return loss

   

   

class SoftmaxWithLoss:

    def __init__(self):

        self.loss = None  #손실

        self.y = None     # softmax 출력

        self.t = None     # 정답 레이블(-핫 벡터)

       

    def forward(self, x, t):

        self.t = t

        self.y = simple.softmax(x)

        self.loss = simple.cross_entropy_error(self.y , self.t)

        return self.loss

   

    def backward(self, dout = 1):

        batch_size = self.t.shape[0]

        dx = (self.y - self.t) / batch_size  # 오차역전파는 빼서 전체오차 역전파한다.

       

        return dx

   

simple = simpleNet()   

soft_loss = SoftmaxWithLoss()

 

 

수치미분 -> 계속 미분해서 오차를 갱신시킨다(몹시 느림)

오차역전파-> 계산그래프로 필요 단계별 역전파 시키고

마지막에 뺄셈 시킨다. (그래서 빠르다!)

y1 -  t1

 

 

 

 

문제231.

위에서 만든 softmaxwithloss 클래스를 객체화 시켜서

아래의 x(입력값), t(타겟값 target value) 입력해서

순전파의 오차율 출력하고 역전파 출력하시오

 

a클래스에서 b클래스꺼 불러오려면

a클래스 안에 b클래스선언을 해줘야

그래서 그냥 softmaxWithLoss 함수에 필요함수 넣어씀.

 

t = np.array([0,0,1,0,0,0,0,0,0,0])   # 숫자2

 

x1 = np.array([0.01,0.01,0.01,0.01,0.01,0.01,0.05,0.3,0.1,0.5])

 

x2 =np.array([0.01,0.01,0.9,0.01,0.01,0.01,0.01,0.01,0.01,0.02])

 

class SoftmaxWithLoss:

 

    def __init__(self):

        self.loss = None #손실

        self.y = None #softmax 출력

        self.t = None

 

    def softmax(self,a):

        import numpy as np

        minus = a - np.max(a)

        return np.exp(minus) / np.sum(np.exp(minus))

 

    def cross_entropy_error(self,y,t):

        import numpy as np

        delta = 1e-7

        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+delta)) / batch_size

 

 

    def forward(self,x,t):

 

        self.t = t

        self.y = self.softmax(x)

        self.loss= self.cross_entropy_error(self.y,self.t)

 

        return self.loss

 

    def backward(self,dout=1):

        batch_size = self.t.shape[0]

        dx = (self.y-self.t) / batch_size

 

        return dx

 

import numpy as np

 

t = np.array([0,0,1,0,0,0,0,0,0,0])   # 숫자2

 

x1 = np.array([0.01,0.01,0.01,0.01,0.01,0.01,0.05,0.3,0.1,0.5])

x2 = np.array([0.01,0.01,0.9,0.01,0.01,0.01,0.01,0.01,0.01,0.02])

 

soft=SoftmaxWithLoss()

print(soft.forward(x1,t))

print(soft.backward())

 

print(soft.forward(x2,t))

print(soft.backward())

 

 

 

 

 

문제232.

아래의 x값을 9개를 t 비교해서 오차율이 아래와 같이 출력되게 하시오

(softmaxWitLoss 클래스를 이용해서)

 

t = [0,0,1,0,0,0,0,0,0,0]    # 숫자2

 

x1 = [0.1,0.05,0.1,0.0,0.05,0.1,0.0,0.1,0.0,0.0] 

x2 = [0.1,0.05,0.2,0.0,0.05,0.1,0.0,0.6,0.0,0.0]

x3 = [0.0,0.05,0.3,0.0,0.05,0.1,0.0,0.6,0.0,0.0]

x4 = [0.0,0.05,0.4,0.0,0.05,0.0,0.0,0.5,0.0,0.0]

x5 = [0.0,0.05,0.5,0.0,0.05,0.0,0.0,0.4,0.0,0.0]

x6 = [0.0,0.05,0.6,0.0,0.05,0.0,0.0,0.3,0.0,0.0]

x7 = [0.0,0.05,0.7,0.0,0.05,0.0,0.0,0.2,0.0,0.0]

x8 = [0.0,0.1,0.8,0.0,0.1,0.0,0.0,0.2,0.0,0.0]

x9 = [0.0,0.05,0.9,0.0,0.05,0.0,0.0,0.0,0.0,0.0]

 

결과 :   

x1오차: 2.25358384929

x2오차: 2.22978102389

x3오차: 2.13182940769

x4오차: 2.01975280051

x5오차: 1.91975287223

x6오차: 1.82254467599

x7오차: 1.72813268631

x8오차: 1.65593082752

x9오차: 1.54773595587

 

 

soft=SoftmaxWithLoss()

 

for idx, xx in enumerate(x):

    result = soft.forward(xx,t)

    print("x"+str(idx+1)+"오차:",result)

또는

for i in range(10):

    print("x%d 오차율:" %(i+1),soft.forward(x[i],t))

 

 

 

   

   

  

    문제233.

어제 마지막 문제였던 문제229 3 신경망의 마지막 층으로

SoftmaxWithLoss 클래스를 추가하시오

 

lastLayer = SoftmaxWithLoss()

 

코드구현:

import numpy as np

 

class Affine:

    def __init__(self, W, B):

        self.W = W

        self.b = B

        self.X = None

        self.dW = None

        self.db = None

       

    def forward(self, x):

        self.x = X

        out = np.dot(x, self.W) + self.b

        return out

   

    def backward(self, dout):

        dx = np.dot(dout, self.W.T)

        self.dW = np.dot(self.x.T, dout)

        self.db = np.sum(dout, axis = 0) # 역전파 시킨 b의 열의 합 

        return dx   

 

 

class Relu:

    def __init__(self):

        self.mask=None

 

    def forward(self,x):

        self.mask=(x<=0)

        out=x.copy()

        out[self.mask]=0

        return out

 

    def backward(self,dout):

        dout[self.mask]=0

        dx=dout

        return dx

 

 

 

class SoftmaxWithLoss:

 

    def __init__(self):

        self.loss = None #손실

        self.y = None #softmax 출력

        self.t = None

 

    def softmax(self,a):

        import numpy as np

        minus = a - np.max(a)

        return np.exp(minus) / np.sum(np.exp(minus))

 

    def cross_entropy_error(self,y,t):

        import numpy as np

        delta = 1e-7

        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+delta)) / batch_size

 

 

    def forward(self,x,t):

 

        self.t = t

        self.y = self.softmax(x)

        self.loss= self.cross_entropy_error(self.y,self.t)

 

        return self.loss

 

    def backward(self,dout=1):

        batch_size = self.t.shape[0]

        dx = (self.y-self.t) / batch_size

        return dx

 

 

X = np.array([[1,2]])

W1 = np.array([[1,3,5],[2,4,6]])

W2 = np.array([[1,4],[2,5],[3,6]])

W3 = np.array([[5,6,7],[2,3,4]])

B1 = np.array([[1,2,3]])

B2 = np.array([[1,2]])

B3 = np.array([[1,1,1]])

t = np.array([[0,0,1]])

 

layer_1 = Affine(W1,B1)

layer_2 = Affine(W2,B2)

layer_3 = Affine(W3,B3)

relu = Relu()

lastLayer = SoftmaxWithLoss()

 

out1 = layer_1.forward(X)  # 입력층->은닉1

rout1 = relu.forward(out1) # 은닉1 () relu

out2 = layer_2.forward(rout1) # 은닉1->은닉2

rout2 = relu.forward(out2)    #은닉2 () relu

out3 = layer_3.forward(rout2)  #은닉2->출력층

#print(out3)  # = dY   #마지막 L  [[ 888 1192 1496]]

print("순전파 출력값:",lastLayer.forward(out3,t))

순전파 출력값: -9.99999950584e-08

 

 

 

 

 

문제234.

이번에는 문제233 코드의 역전파 코드를 구현하시오

( dout=1 이라고 임시로 일단 넣기)

dx = ?  # 역전파

dw1 = ?

db = ?

역전파값: [[  2.63076288e-130   3.39453274e-130]]

 

dW: [[  1.69726637e-131   2.54589956e-131   3.39453274e-131]

 [  3.39453274e-131   5.09179911e-131   6.78906549e-131]]

 

db: [  1.69726637e-131   2.54589956e-131   3.39453274e-131]

 

코드구현:

X = np.array([[1,2]])

W1 = np.array([[1,3,5],[2,4,6]])

W2 = np.array([[1,4],[2,5],[3,6]])

W3 = np.array([[5,6,7],[2,3,4]])

B1 = np.array([[1,2,3]])

B2 = np.array([[1,2]])

B3 = np.array([[1,1,1]])

t = np.array([[0,0,1]])

 

layer_1 = Affine(W1,B1)

layer_2 = Affine(W2,B2)

layer_3 = Affine(W3,B3)

relu1 = Relu()

relu2 = Relu()

lastLayer = SoftmaxWithLoss()

 

out1 = layer_1.forward(X)  # 입력층->은닉1

rout1 = relu1.forward(out1) # 은닉1 () relu

out2 = layer_2.forward(rout1) # 은닉1->은닉2

rout2 = relu2.forward(out2)    #은닉2 () relu

out3 = layer_3.forward(rout2)  #은닉2->출력층

#print(out3)  # = dY   #마지막 L  [[ 888 1192 1496]]

out_soft = lastLayer.forward(out3,t)

 

dout_soft = lastLayer.backward(dout=1)

dout_3 = layer_3.backward(dout_soft)   # 출력층->은닉2

dout2_relu = relu2.backward(dout_3)# 은닉2() relu2

dout_2 = layer_2.backward(dout2_relu) # 은닉2->은닉1

dout1_relu = relu1.backward(dout_2) # 은닉1 () relu1

dout_1 = layer_1.backward(dout1_relu) #은닉1->입력층

 

print("역전파값:",dout_1)

print("dW:",layer_1.dW)

print("db:",layer_1.db

 

 

 

 

 

 

 

OrderDict() 딕셔너리의 이해

orderDict 그냥 Dictionary 와는 다르게 입력된 데이터뿐만 아니라

입력된 순서까지 같아야 동일한 것으로 판단한다.

 

예제:

# 일반 딕셔너리 예제

d1 = {}

d1['a'] = 'A'

d1['b'] = 'B'

d1['c'] = 'C'

d1['d'] = 'D'

d1['e'] = 'E'

 

d2 = {}

d2['e'] = 'E'

d2['d'] = 'D'

d2['c'] = 'C'

d2['b'] = 'B'

d2['a'] = 'A'

# 순서만 반대로 했음

print(d1 == d2)

True

 

 

그러나

 

import collections

print('OrderDict:')  #딕셔너리 선언

 

d1 = collections.OrderedDict()

d1['a'] = 'A'

d1['b'] = 'B'

d1['c'] = 'C'

d1['d'] = 'D'

d1['e'] = 'E'

 

d2 = collections.OrderedDict()

d2['e'] = 'E'

d2['d'] = 'D'

d2['c'] = 'C'

d2['b'] = 'B'

d2['a'] = 'A'

# 순서만 반대로 했음

print(d1==d2)

print(d1)

print(d2)

OrderDict:

False

OrderedDict([('a', 'A'), ('b', 'B'), ('c', 'C'), ('d', 'D'), ('e', 'E')])

OrderedDict([('e', 'E'), ('d', 'D'), ('c', 'C'), ('b', 'B'), ('a', 'A')])

 

orderDict 함수 만든 dictionary 순서까지 같아야 같은 데이터로 인식한다.

순전파 순서의 반대로 역전파가 되어야하기 때문에

orderedDict함수를 사용해야 한다.

 

 

순전파:

입력값 ---> Affine 1 ---> Relu1 ---> Affine 2 ---> Relu2 --->

Affine 3 ---> lastLayer(= SoftmaxWithLoss) ---> 오차

 

 

역전파:

1 ---> lastLayer ---> Affine 3 ---> Relu2 ---> Affine 2 ---> Relu1 --->

Affine 1

 

 

 

순전파의 순서를 반대로 (reverse) 해서 역전파 있도록

orderedDict 함수를 신경망 코드에 사용해야 한다.

 

 

 

문제235.

orderedDict 함수를 이용해서 문제 234 코드의 순전파코드를 구현하시오.

 

 

X = np.array([[1, 2]])

W1 = np.array([[1, 3, 5], [2, 4, 6]])

B1 = np.array([[1, 2, 3]])

W2 = np.array([[1, 4], [2, 5], [3, 6]])

B2 = np.array([[1, 2]])

W3 = np.array([[5,6,7],[2,3,4]])

B3 = np.array([[1,1,1]])

t=np.array([[0,0,1]])

 

# # 층들

# afi1 = Affine(W1, B1)

# afi2 = Affine(W2, B2)

# afi3 = Affine(W3, B3)

# relu1 = Relu()

# relu2 = Relu()

# lastLayer=SoftmaxWithLoss()

# 위와 같이 지정값들을 orderedDict 쓰면 필요없어짐

#딕셔너리에 넣으니까

 

# orderedDict 함수이용 코드

 

layers = OrderedDict()

layers['Affine1'] = Affine(W1,B1)   # orderedDict 밸류값으로 넣기

layers['Relu1'] = Relu()

layers['Affine2'] = Affine(W2,B2)

layers['Relu2'] = Relu()

layers['Affine3'] = Affine(W3,B3)

 

lastLayer = SoftmaxWithLoss()

out = X    # x out 이라고 변수지정

for layer  in  layers.values():

    out = layer.forward(out)

    print (out)

#

# # 순전파

# out_afi1 = afi1.forward(X)  # 은직 1 affine 순전파

# out_relu1 = relu1.forward(out_afi1)  # 은닉 1 relu 순전파

# out_afi2 = afi2.forward(out_relu1)  #은닉 2 affine 순전파

# out_relu2= relu2.forward(out_afi2)  #은닉 2 relu 순전파

# out_afi3 = afi3.forward(out_relu2) # 출력층 affine 순전파

# out_soft=lastLayer.forward(out_afi3,t) # 출력층과 오차함수

#for layer in layers.values 통해 순전파를 해결한다.

 

 

 

 

 

문제236.

위의 3 신경망의 역전파 코드를 구현하시오

(183페이지 참고)

(준하코드로)

 

 

문제237.

위의 2 신경망을 활성화 함수로 relu 사용하고 있고

최종 정확도는 아래와 같다.

그런데 현업에서는 sigmoid 함수보다 relu 함수를 선호한다고 하는데

이유를 테스트로 확인해보시오

 

from common.layers import *

패키지에 Relu, Sigmoid 함수 내장되어 있다.

 

1. Relu 최종 정확도

train acc, test acc | 0.978933333333, 0.9686

 

2. sigmoid 최종 정확도

train acc, test acc | 0.946983333333, 0.9444

 

sigmoid 정확도가 떨어지는 있다.

 

 

 

Sigmoid 보다 Relu 선호하는 이유가 무엇이냐

vanishing gradient 현상때문에 Relu 함수를 써야 합니다.

*vanishing gradient 딥부분까지 업데이트가 안됨. underfitting 발생

(시그모이드의 saddle point 같은 부분때문에 학습이 멈춤)

 

 

 

문제238(오늘의 마지막 문제)

2 신경망     VS     3 신경망의 정확도 차이는 발생하는가?

층을 많이 쌓으면 쌓을수록 정확도가 높아지는가?

 

 

2 신경망(Relu 사용)

정확도 : train acc, test acc | 0.978933333333, 0.9686

 

3 신경망(Relu 사용)

정확도 : ?

 

은닉2층의 노드수는 임의로 정하세요~~30

 

코드구현

 

# coding: utf-8

 

import sys, os

sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정

import numpy as np

from common.layers import *

from common.gradient import numerical_gradient

from collections import OrderedDict

import matplotlib.pyplot as plt

from dataset.mnist import load_mnist

 

 

class TwoLayerNet:

    def __init__(self, input_size, hidden_size, hidden_size2, 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, hidden_size2)

        self.params['b2'] = np.zeros(hidden_size2)

        self.params['W3'] = weight_init_std * np.random.randn(hidden_size2, output_size)

        self.params['b3'] = np.zeros(output_size)

       

 

 

        # 계층 생성

 

        self.layers = OrderedDict()

        self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])

        self.layers['Relu1'] = Relu()

        self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])

        self.layers['Relu2'] = Relu()

        self.layers['Affine3'] = Affine(self.params['W3'], self.params['b3'])

               

        self.lastLayer = SoftmaxWithLoss()

 

 

    def predict(self, x):

        for layer in self.layers.values():

            x = layer.forward(x)

 

        return x

 

 

    # x : 입력 데이터, t : 정답 레이블

 

    def loss(self, x, t):

        y = self.predict(x)

        return self.lastLayer.forward(y, t)

 

 

    def accuracy(self, x, t):

        y = self.predict(x)  #순전파 태움

        y = np.argmax(y, axis=1) #행의 최대값 컴온

        if t.ndim != 1: t = np.argmax(t, axis=1)  #1차원 아니면 행의 최대값 갖고와라

        accuracy = np.sum(y == t) / float(x.shape[0]) # 일치하는거 accuracy로 변수지정

        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'])

        grads['W3'] = numerical_gradient(loss_W, self.params['W3'])

        grads['b3'] = numerical_gradient(loss_W, self.params['b3'])

       

        return grads

 

 

    def gradient(self, x, t):

        # forward

        self.loss(x, t)

        # backward

        dout = 1

        dout = self.lastLayer.backward(dout)

        layers = list(self.layers.values())

        layers.reverse()

 

        for layer in layers:

            dout = layer.backward(dout)

 

 

        # 결과 저장

 

        grads = {}

        grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db  # w b를 뽑아서 기울기 구함

        grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

        grads['W3'], grads['b3'] = self.layers['Affine3'].dW, self.layers['Affine3'].db

 

        return grads

 

# 데이터 읽기

 

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

 

network = TwoLayerNet(input_size=784, hidden_size=50, hidden_size2=30,output_size=10)

 

 

# 하이퍼파라미터

 

iters_num = 10000  # 반복 횟수를 적절히 설정한다.

train_size = x_train.shape[0] # 60000

batch_size = 100  # 미니배치 크기

learning_rate = 0.1

 

train_loss_list = []

train_acc_list = []

test_acc_list = []

 

 

 

# 1에폭당 반복 수

 

iter_per_epoch = max(train_size / batch_size, 1)

 

 

 

for i in range(iters_num): # 10000

 

    # 미니배치 획득  # 랜덤으로 100개씩 뽑아서 10000번을 수행하니까 백만번

    batch_mask = np.random.choice(train_size, batch_size) # 100 씩 뽑아서 10000번 백만번

    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', 'W3', 'b3'):

        network.params[key] -= learning_rate * grad[key] #기울기빼면서 w갱신

 

    # 학습 경과 기록

 

    loss = network.loss(x_batch, t_batch)

    train_loss_list.append(loss) # cost 점점 줄어드는것을 보려고

 

    # 1에폭당 정확도 계산 # 여기는 훈련이 아니라 1에폭 되었을때 정확도만 체크

 

    if i % iter_per_epoch == 0: # 600 번마다 정확도 쌓는다.

 

        train_acc = network.accuracy(x_train, t_train)

 

        test_acc = network.accuracy(x_test, t_test)

 

        train_acc_list.append(train_acc) # 10000/600   16 # 정확도가 점점 올라감

 

        test_acc_list.append(test_acc)  # 10000/600 16 # 정확도가 점점 올라감

 

        print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))

 

 

 

# 그래프 그리기

 

markers = {'train': 'o', 'test': 's'}

x = np.arange(len(train_acc_list))

plt.plot(x, train_acc_list, label='train acc')

plt.plot(x, test_acc_list, label='test acc', linestyle='--')

plt.xlabel("epochs")

plt.ylabel("accuracy")

plt.ylim(0, 1.0)

plt.legend(loc='lower right')

plt.show()

 

3 신경망 relu 정확도:

train acc, test acc | 0.983283333333, 0.9692

 

 

 

수치미분을 이용한 기울기:

-3 신경망을 학습 시켜야 한다고 하면 구해야 하는 기울기

W1,W2,W3,b1,b2,b3 대하여 각각 기울기를 구해야 한다.

 

                           E(오차함수)

W1 = W1 - 기울기(----------------)

                                 W1

 

W1 해도 벌써 784x100개의 W1 있으므로

수치미분을 이용해서 기울기를 구하려면 시간이 상당히 걸린다.

 

 

오차 역전파를 이용한 기울기:

---> W1 ---> Relu1 ---> W2 ---> Relu2 ---> W3 ---> softmax---> 비용함수

                                                                               

backward  backward  backward  backward backward  backward  

 

 

  k        m        g        y        t         E (E 오차함수)

---      x  ---     x  ---    x  ---    x  ---     x  ---

 w1       k       m       g        y         t

 

                          

 

    

       

 

  E

--------

  w1

   

결국 수치미분과 오차역전파는 같은 결과를 갖는다.

p.154 보면 위의 연쇄 법칙은 합성함수 미분과 같다.

*연쇄법칙: 합성함수의 미분에 대한 성질

*합성함수: 여러 함수로 구성된 함수를 한개의 함수로 표현하는 함수.

 

 

x ---> f(x) ---> g(f(x) ) ---> z( g( f(x) ) ) ---> k(z ( g( f (x) ) ) )

                                                              

      relu1        relu2            softmax                오차함수

 

 

    k(z(g(f(x)))) 미분 ──▶ k(z(g(f(x))))´ * z(g(f(x)))´ * g(f(x))´ * f(x)´   

 

 

 

 k       f       g      z       k

---   =  ---   x ---    x ---   x  ---

 x       x      f       g      z

 

                              

    

   

=============================이하 완성된 코드들

import numpy as np

class Affine: #입력값 내적 및 순전파. 결과값,가중치,편향 역전파

    def __init__(self, W, b):

        self.W = W

        self.b = b

        self.x = None

        self.dW = None

        self.db = None 

 

    def forward(self, x):

        self.x = x

        out = np.dot(x, self.W) + self.b

        return out

 

    def backward(self, dout):

        dx = np.dot(dout, self.W.T)

        self.dW = np.dot(self.x.T, dout)

        self.db = np.sum(dout, axis = 0)

        return dx

 

class Relu: # relu 순전파, 역전파

    def __init__(self):

        self.mask = None 

 

    def forward(self, x):

        self.mask = (x <= 0)

        out = x.copy()

        out[self.mask] = 0

        return out 

 

    def backward(self, dout):

        dout[self.mask] = 0

        dx = dout

        return dx

 

class SoftmaxWithLoss:#소프트맥스, 교차손실함수, 손실값, 역손실값

    def __init__(self):

        self.loss = None

        self.y = None

        self.t = None

 

    def softmax(self, a):

        c = np.max(a)

        minus = a - c

        exp_a = np.exp(minus)

        sum_exp_a = np.sum(exp_a)

        y = exp_a / sum_exp_a

        return y 

 

    def cross_entropy_error(self, y, t):

        delta = 1e-7

        return -np.sum(t * np.log(y + delta))

 

    def forward(self, x, t):

        self.t = t

        self.y = self.softmax(x)

        self.loss = self.cross_entropy_error(self.y, self.t)

        return self.loss 

 

    def backward(self, dout = 1):

        batch_size = self.t.shape[0]

        dx = (self.y - self.t) / batch_size

        return dx

 

 

class Three_Layer:

    def __init__(self, W):

        import collections

        self.W1 = W['W1']

        self.W2 = W['W2']

        self.W3 = W['W3']

        self.b1 = W['b1']

        self.b2 = W['b2']

        self.b3 = W['b3']

        self.layers = collections.OrderedDict()

        self.layers['AF1'] = Affine(self.W1, self.b1)

        self.layers['ReLU1'] = Relu()

        self.layers['AF2'] = Affine(self.W2, self.b2)

        self.layers['ReLu2'] = Relu()

        self.layers['AF3'] = Affine(self.W3, self.b3)

        self.layer_last = SoftmaxWithLoss() 

   

    #순전파       

    def predict(self, x, t): #layers딕셔너리의 밸류값을 for문 돌려서 출력

        for layer in self.layers.values():

            x = layer.forward(x)  #해당 함수의 forward가 돈다.(Affine, Relu)

        x = self.layer_last.forward(x, t) #출력층 함수의 순전파

        return x 

 

    #역전파

    def backword(self, dout = 1 ):

        dout = self.layer_last.backward(dout)

        layers = list(self.layers.values())

        layers.reverse()

        for layer in layers:

            dout = layer.backward(dout) #layer에 들어있는 해당함수의 backward가 돈다

        return dout, self.layers['AF1'].dW, self.layers['AF1'].db

X = np.array([[1,2]])

W = {}

W['W1'] = np.array([[1,3,5],[2,4,6]])

W['W2'] = np.array([[1,4],[2,5],[3,6]])

W['W3'] = np.array([[5,6,7],[2,3,4]])

W['b1'] = np.array([[1,2,3]])

W['b2'] = np.array([[1,2]])

W['b3'] = np.array([[1,1,1]])

 

t = np.array([[0,0,1]])

tl = Three_Layer(W)  # Three_layer 클래스선언

tl_p = tl.predict(X, t)  # Three_layer 클래스를 이용한 순전파

print(tl_p)

print(tl.backword()) #dout=1 정해져 있으니까 그냥 돌리면 됨.

 

============================================

 

#################################################

# 모듈공간

import numpy as np

import deeplearning as dp

import collections

#################################################

 

def Three_layer(what='forward'):

   x = np.array([[1,2]]) # 형식을 2차원으로 해주자.

   w1 = np.array([[1,3,5],[2,4,6]])

   w2 = np.array([[1,4],[2,5],[3,6]])

   w3 = np.array([[5,6,7],[2,3,4]])

   b1 = np.array([[1,2,3]])

   b2 = np.array([[1,2]])

   b3 = np.array([[1,1,1]])

   t = np.array([[0,0,1]])

 

   layers = collections.OrderedDict()

   layers['aff1'] = dp.Affine(w1,b1)

   layers['relu'] = dp.Relu()

   layers['aff2'] = dp.Affine(w2,b2)

   layers['relu'] = dp.Relu()

   layers['aff3'] = dp.Affine(w3,b3)

   layers['last'] = dp.SoftmaxWithLoss()

  

   # 순전파

   for i in layers.values():

      try:

         x = i.forward(x)

      except:

         x = i.forward(x,t)

 

   # 역전파

   layers = list(layers.values())

   layers.reverse()

   dout = 1

   for i in layers:

      dout = i.backward(dout)

 

   if what == 'forward':

      return x

   else:

      return dout

 

print(Three_layer())

print(Three_layer('backward'))

 

 

class Three_Layer_class:

   def __init__(self):

      self.x = np.array([[1,2]]) # 형식을 2차원으로 해주자.

      self.w1 = np.array([[1,3,5],[2,4,6]])

      self.w2 = np.array([[1,4],[2,5],[3,6]])

      self.w3 = np.array([[5,6,7],[2,3,4]])

      self.b1 = np.array([[1,2,3]])

      self.b2 = np.array([[1,2]])

      self.b3 = np.array([[1,1,1]])

      self.t = np.array([[0,0,1]])

 

      self.layers = collections.OrderedDict()

      self.layers['aff1'] = dp.Affine(self.w1,self.b1)

      self.layers['relu'] = dp.Relu()

      self.layers['aff2'] = dp.Affine(self.w2,self.b2)

      self.layers['relu'] = dp.Relu()

      self.layers['aff3'] = dp.Affine(self.w3,self.b3)

      self.layers['last'] = dp.SoftmaxWithLoss()

 

   def forward(self):

      x = self.x

      t = self.t

      for i in self.layers.values():

         try:

            x = i.forward(x)

         except:

            x = i.forward(x,t)

      return x

 

   def backward(self):

      layers = list(self.layers.values())

      layers.reverse()

      dout = 1

 

      for i in layers:

         dout = i.backward(dout)

 

      return dout

 

th3 = Three_Layer_class()

print(th3.forward())

print(th3.backward())

 

 

 

728x90
반응형