implement_RNN (vector-to-sequence) 오류 수정
2023.03.05 - [분류 전체보기] - implement_RNN (vector-to-sequence) 문제 해결
implement_RNN (vector-to-sequence) 문제 해결
2023.03.05 - [분류 전체보기] - implement_RNN (vector-to-sequence) 2개의 가중치, bias 값 추가 implement_RNN (vector-to-sequence) 2개의 가중치, bias 값 추가 2023.03.02 - [분류 전체보기] - implement_RNN (vector-to-sequence) 2개의
teach-meaning.tistory.com
RNN 계산 과정,
#초기 학습, 임의의 가중치가 생성된다.
def delta_to_sequence_cal_rnn_test(self, input, target):
# 타겟값의 저장
self.target = target
# 입력 데이터의 저장
self.input = input
# 입력 데이터 크기에 맞는 가중치 값이 생성되어야 한다.
# 입력 (3,1) 데이터에 대한 연산 결과 (1,1)를 예측한다고 가정,
# 입력값에 사용되는 가중치
# (3,1) 크기의 입력 데이터에 대해 (1,1) 의 행렬곱 연산 결과를 얻기 위해, (1,3) 크기의 가중치 행렬이 필요하다
self.input_w = np.random.rand(target.shape[0], input.shape[1])
# 이전 출력에 대한 가중치
# (1,1) 노드 출력에 대해 (1,1) 의 행렬곱 연산 결과를 얻기 위한, (1,1) 크기의 가중치 행렬
self.before_w = np.random.rand(target.shape[0], target.shape[0])
# bias 값 생성, (1,1) 행렬에 더해지기 위한 (1,1) 크기의 bias 값 생성
self.bias = np.random.rand(target.shape[0], target.shape[0])
# 이전 노드의 출력, 첫 부분의 경우 이 값이 0이다. 행렬의 크기는 (1,1)
before = np.zeros((target.shape[0], target.shape[0]))
#self.node_output.append(before)
# (3, n) 크기의 입력 데이터에서 n 번의 반복을 수행하게 됨
for i in range(input.shape[0]):
# 노드 입력 데이터의 numpy 행렬 생성
input_data = input[i].reshape(input.shape[1],-1)
# RNN 노드 입력 값 계산, 이전 노드 출력 @ 가중치 + 입력 데이터 @ 가중치
node_input = (self.before_w @ before) + (self.input_w @ input_data) + self.bias
self.node_input.append(node_input)
# 노드 출력 계산, 활성화 함수 연산을 수행한다.
node_output = self.activation.non(node_input)
self.node_output.append(node_output)
# 타입 스텝의 출력이 다음 노드의 입력이 된다.
before = node_output
self.predict = node_output
self.cost_cal()
return self.predict
1 : 학습에 입력값과 예측값이 입력된다
2 : 임의의 두 가중치, before_w, input_w 와 bias 가 입력 데이터 크기에 맞게 생성된다.
3 : 입력 데이터 크기에 맞게 0으로 초기화된 초기 노드 출력이 생성된다. 여기서 해당 입력은 node_input에 저장될 필요가 없으므로 삭제해준다.
4 : 입력 데이터 크기에 맞게 반복이 수행된다.
5 : 입력 데이터를 행렬곱을 위한 numpy 행렬로 수정해준다. 그 크기에 맞게 수정
6 : 활성화 함수 연산 전의 노드 입력값을 계산한다. 노드간 가중치 @ 이전 노드 출력 + 입력 가중치 @ 데이터 입력 + 편향
7 : 활성화 함수 연산 후의 노드 출력을 계산한다.
8 : 해당 노드 출력은 다음 노드의 입력이다.
9 : 5~8의 과정이 반복되고 마지막 노드의 출력이 예측값이 된다.
10 : 예측값을 통한 비용 함수값을 계산한다.
rnn.delta_to_sequence_cal_rnn_test(input, target)
>>>
(array([[1.57795929]]), 1.867960879028231)
print(rnn.input_w, rnn.before_w, rnn.bias)
>>>
[[0.53680354]] [[0.45702764]] [[0.84214604]]
계산한 예측값과 비용 함수값,
각 가중치와 편향값의 확인,
각 노드별 변화량, delta 값의 계산
def cal_delta(self):
self.delta = []
#마지막 노드의 변화량에 대한 오차 함수의 변화량 계산
#delta = (self.cost.diff_error_squared_sum() * self.activation.non_diff(self.predict))
delta = (self.cost.diff_error_squared_sum() * self.activation.non_diff(self.node_output[len(self.node_output) - 1]))
self.delta.append(delta)
# n-1 개의 delta 값을 계산하기 위한 반복
for i in range(input.shape[0] - 1, 0, -1):
# 노드 출력의 변화에 대한 비용 함수의 변화량 계산
delta = self.before_w * delta
# 활성화 함수에 따른 변화량 계산, 해당 값이 노드 변화에 대한 비용 함수의 변화량이 된다.
delta = delta * self.activation.non_diff(self.node_input[i])
# 노드별 delta 값의 저장
self.delta.append(delta)
1 : delta 배열의 초기화
2 : 초기 출력의 경우 노드 출력(예측값)에 대한 비용 함수의 미분 함수의 값 계산
비용 함수의 미분값, 예측값의 변화에 따른 비용 함수의 변화량이다.
활성화 함수의 미분값에는 활성화 함수 연산 전의 노드 입력값을 넣어준다. 하지만 값을 그대로 출력하는 활성화 함수는 이 미분값이 항상 1이므로 크게 상관은 없다.
3 : delta 값을 역전파 과정을 통해 추가한다.
4 :
이전 노드 출력에 대한 노드 입력의 변화량은 before_w 의 값이다.
해당 함수를 통해 입력 데이터 크기에 맞는 각 노드별 delta 값(노드입력 변화에 따른 비용함수의 변화량) 이 계산된다.
rnn.cal_delta()
rnn.delta
>>>
[1.932853268630721,
array([[0.88336736]]),
array([[0.4037233]]),
array([[0.1845127]]),
array([[0.08432741]]),
array([[0.03853995]]),
array([[0.01761382]]),
array([[0.00805]]),
array([[0.00367907]]),
array([[0.00168144]]),
array([[0.00076846]]),
array([[0.00035121]]),
array([[0.00016051]]),
array([[7.33585774e-05]]),
array([[3.35268972e-05]]),
array([[1.53227186e-05]]),
array([[7.00290587e-06]]),
array([[3.20052152e-06]]),
array([[1.46272678e-06]]),
array([[6.68506565e-07]]),
array([[3.05525975e-07]]),
array([[1.39633814e-07]]),
array([[6.38165121e-08]]),
array([[2.91659097e-08]]),
array([[1.33296268e-08]]),
array([[6.09200782e-09]]),
array([[2.78421594e-09]]),
array([[1.27246363e-09]]),
array([[5.81551045e-10]]),
array([[2.65784899e-10]]),
array([[1.21471044e-10]]),
array([[5.55156243e-11]]),
array([[2.53721746e-11]]),
array([[1.1595785e-11]]),
array([[5.2995942e-12]]),
array([[2.42206101e-12]]),
array([[1.10694882e-12]]),
array([[5.05906203e-13]]),
array([[2.31213116e-13]]),
array([[1.05670784e-13]]),
array([[4.82944686e-14]]),
array([[2.20719069e-14]]),
array([[1.00874714e-14]]),
array([[4.61025322e-15]]),
array([[2.10701313e-15]]),
array([[9.62963232e-16]]),
array([[4.4010081e-16]]),
array([[2.01138233e-16]]),
array([[9.19257313e-17]]),
array([[4.20125997e-17]])]
delta 값은 마지막 노드부터 저장되는데 이전 델타값으로 갈수록, 값이 급격하게 감소하거나 증가하는데, 이는 before_w 의 반복된 곱셈 연산으로 인한 것이다.
delta 값을 계산하는 과정에서 실수가 있어 이를 수정
def cal_delta(self):
#마지막 노드의 변화량에 대한 오차 함수의 변화량 계산
delta = (self.cost.diff_error_squared_sum() * self.activation.sigmoid_diff(self.predict))
self.delta.append(delta)
# n-1 개의 delta 값을 계산하기 위한 반복
for i in range(input.shape[0] - 1, 0, -1):
# 노드 출력의 변화에 대한 비용 함수의 변화량 계산
delta = self.before_w @ delta
# 활성화 함수에 따른 변화량 계산, 해당 값이 노드 변화에 대한 비용 함수의 변화량이 된다.
#
delta = delta * self.activation.non_diff(self.node_input[i])
# 노드별 delta 값의 저장
self.delta.append(delta)
활성화 함수 미분값에 계산에 있어 입력값을 이전에는 node_output 값을 넣었는데 활성화 함수의 계산 값인 node_input 값이 들어가야 한다.
이후 가중치 업데이트 함수
def update_weight(self, learning_rate):
# 원활한 연산을 위해 마지막 노드부터 거꾸로 계산된 delta 값을 뒤집어준다.
self.delta = self.delta[::-1]
result = 0
# 해당 delta 값과의 데이터 입력값을 통해 input_w 의 가중치 변화량을 계산할 수 있다
for i in range(self.input.shape[0]):
result = result + self.delta[i] * self.node_input[i]
print(self.delta[i], self.node_input[i])
self.before_weight_update = result
result = 0
# 이전 노드 출력값을 통해 before_w 의 가중치 변화량 계산
for i in range(self.input.shape[0]):
result = result + self.delta[i] * self.input[i]
self.input_weight_update = result
# bias_update 의 계산
result = 0
for i in range(self.input.shape[0]):
result = result + self.delta[i]
self.bias_update = result
# 가중치 업데이트
self.input_w = self.input_w - (self.input_weight_update * learning_rate)
self.before_w = self.before_w - (self.before_weight_update * learning_rate)
self.bias = self.bias + self.bias_update * learning_rate
1 : 먼저 거꾸로 저장된 delta 값을 뒤집어준다.
2 :
각 가중치 변화량은
input_w_update = input * delta,
before_w_update = before * delta,
bias = 1 * delta
값이 된다.
rnn.update_weight(0.01)
print(rnn.input_weight_update, rnn.before_weight_update, rnn.bias_update)
>>>
[[6.0962773]] [[0.09709869]] [[3.55976362]]
임의의 가중치가 결정되고 학습을 반복하는 함수
def iterations(self, iterations, learning_rate):
# j 번의 학습
for j in range(iterations):
# 초기화
self.node_input = []
self.node_output = []
self.delta = []
# 이전 노드의 출력, 첫 부분의 경우 이 값이 0이다. 행렬의 크기는 (1,1)
before = np.zeros((self.target.shape[0], self.target.shape[0]))
for i in range(input.shape[0]):
# 노드 입력 데이터의 numpy 행렬 생성
input_data = input[i].reshape(input.shape[1],-1)
# RNN 노드 입력 값 계산, 이전 노드 출력 @ 가중치 + 입력 데이터 @ 가중치 + bias
# 업데이트된 가중치, 편향값으로 계산이 이뤄진다.
node_input = (self.before_w @ before) + (self.input_w @ input_data) + self.bias
self.node_input.append(node_input)
# 노드 출력 계산, 활성화 함수 연산을 수행한다.
node_output = self.activation.non(node_input)
self.node_output.append(node_output)
# 순환 노드의 다음 노드 입력값
before = node_output
# 매 학습마다 예측값이 달라짐
self.predict = before
# 예측값에 따른 비용 함숫값 계산
self.cost_result = self.cost_cal()
print(self.cost_cal())
# 각 노드별 비용 함수의 변화량 계산
self.cal_delta()
# 계산한 delta 값을 통해 가중치 값 갱신
self.update_weight(learning_rate)
1 : 노드 입력과 출력, delta 값을 초기화준다.
2 : 업데이트 된 가중치 값을 통해 학습
3 : 비용 함수 계산
4 : 가중치 업데이트
5 : 1~4의 과정을 정해진 학습 횟수, 학습률을 통해 학습한다.
rnn.iterations(100, 0.01)
>>>
1.73179085461459
1.6067607711006486
1.49179330634851
1.3859389478963788
1.2883573833162305
...
0.0036026754102895316
0.0033819166244338727
0.0031747166658001817
0.0029802398639591473
0.0027977023209275484
0.0026263686797589622
print(rnn.predict, rnn.target)
>>>
[[-0.28241821]] [[-0.35489398]]
학습을 반복하면서 target 값에 수렴하는 것을 확인할 수 있다.