■ 오차 역전파를 이용한 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
↓↓↓↓↓↓↓↓↓↓↓
2행2열(내적)2행2열 != 2행2열(내적)2행2열
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())
'deeplearning' 카테고리의 다른 글
7장. 합성곱 신경망(CNN) (0) | 2019.03.30 |
---|---|
6장. 신경망을 학습시키는 여러 기술들 소개(경사하강법의 종류, 배치 정규화, 드롭아웃) (0) | 2019.03.27 |
4장. 인공 신경망을 학습시킴(손실(오차)함수, 수치 미분, 경사하강법, 학습 알고리즘 구현) (0) | 2019.03.27 |
3장. 인공 신경망 ( 신경망의 활성화 함수, 3층 신경망 구현 ) (0) | 2019.03.27 |
2장. 퍼셉트론Perceptron (0) | 2019.03.27 |