from random import seed
from random import random
import numpy as np
# 네트워크 초기 설정
def initialize_network(n_inputs, n_hidden, n_outputs):
network = list()
hidden_layer = [{'weights':[random() for i in range(n_inputs + 1)]} for i in range(n_hidden)] # 개수는 같게 설정
network.append(hidden_layer)
output_layer = [{'weights':[random() for i in range(n_hidden + 1)]} for i in range(n_outputs)] # 개수는 같게 설정
network.append(output_layer)
return network
seed(1)
network = initialize_network(2, 1, 2)
# (2+1(bias))개의 input node, 1개의 hidden layer, 2개의 ouput node
# w 값은 임의로 설정한다
for layer in network:
print(layer)
# initialize_network function
# 첫번째(input node)가 들어가서 두번째(hidden layer)가 나오고
# 두번째(hidden layer)가 들어가서 세번째(output node)가 나온다
# 결과값은 차례대로
# 1) hidden layer: input node의 [w0, w1, w2], (2 input weights + bias)
# 2) output node: hidden layer의 [w00, w01], [w10, w11] (1 weight + bias)
def activate(weights, inputs):
activation = weights[-1] # weights 벡터의 마지막 값이 bias: 그래서 [-1] 넣는 것. bias는 곱하지 않고 넣어야 하기 때문에 그냥 넣는다
for i in range(len(weights)-1):
activation += inputs[i] * weights[i] # 순전파 진행 : 곱해서 activation list에 추가한다는 뜻
return activation # bias + activation 한 것 = list
def sigmoid(activation):
return 1 / (1 + np.exp(-activation)) # 시그모이드 구현
def forward_propagate(network, row):
inputs = row
for layer in network: # network = U1, U2 전체를 말하는 것, 그 중 layer는 U1, U2 각각을 말하는 것
new_inputs = []
for neuron in layer: # layer의 하나의 열이 neuron
activation = activate(neuron['weights'], inputs) # neuron['weight'] = 하나의 열에서 가중치 각각 말하는 것
neuron['output'] = sigmoid(activation) # 활성함수에 activation 값을 넣어서 출력시킨다
new_inputs.append(neuron['output']) # new_input은 다음 히든층에 들어갈 값
inputs = new_inputs
return inputs
network # 똑같다: forward propagation은 weight에 전혀 영향을 주지 않는다
def sigmoid_derivative(output):
return output * (1 - output) # 시그모이드 미분
def backward_propagate_error(network, expected): # expected = 예측값: NN 통과해서 나온 값, ouput = 실제값: target 변수의 값
for i in reversed(range(len(network))): # network 층 거꾸로 간다
layer = network[i] # 마지막 층부터 고려한다
errors = []
if i != len(network)-1: # 출력단인 경우
for j in range(len(layer)):
error = 0.0
for neuron in network[i + 1]:
error += (neuron['weights'][j] * neuron['delta'])
errors.append(error)
else: # 출력단이 아닌 경우
for j in range(len(layer)):
neuron = layer[j]
errors.append(expected[j] - neuron['output'])
# hidden layer의 error = (가중치 * 에러) * 활성함수 미분(예측값)
# backpropagation에서 hidden -> input 으로 갈 때, (U1으로 미분 과정에서)
# 오류 역전파의 유도 과정에서 결론적으로 다 미분하면 가중치만 남게 되므로
# 가중치를 곱해 주면서 에러를 업데이트 한다
for j in range(len(layer)):
neuron = layer[j]
neuron['delta'] = errors[j] * sigmoid_derivative(neuron['output'])
# output node의 error = (실제값 - 예측값) * 활성함수 미분(예측값)
# output -> hidden으로 갈 때, (U2로 미분 과정에서)
# 결론적으로 Zj만 남게 되므로
# 업데이트 된 에러와 활성함수 미분값을 곱해 delta에 저장한다
expected = [0, 1] # label
backward_propagate_error(network, expected)
for layer in network:
print(layer)
def weights_update(network, row, l_rate): # 한 단계 가중치 업데이트
for i in range(len(network)):
inputs = row[:-1]
if i != 0:
inputs = [neuron['output'] for neuron in network[i - 1]]
for neuron in network[i]:
for j in range(len(inputs)):
neuron['weights'][j] += l_rate * neuron['delta'] * inputs[j] # 가중치 갱신 : w
neuron['weights'][-1] += l_rate * neuron['delta'] # 퍼셉트론 학습 규칙: bias - Yk의 유무의 따라서 달라지기 때문
def train_network(network, train, l_rate, n_epoch, n_outputs): # 전체 학습 과정 - 무수히 가중치 업데이트 과정을 반복
for epoch in range(n_epoch):
sum_error = 0
for row in train: # train data set의 하나하나의 row
outputs = forward_propagate(network, row) # 순전파: 예상값
expected = [0 for i in range(n_outputs)] # label
expected[row[-1]] = 1 # 0 / 1 classification - 값이 있으면 1, 없으면 0으로 채워준다
sum_error += sum([(expected[i] - outputs[i])**2 for i in range(len(expected))]) # 예측값의 오차 합: euclidean distance
backward_propagate_error(network, expected)
weights_update(network, row, l_rate)
if epoch%100==0:
print('>epoch=%d, lrate=%.3f, error=%.3f' % (epoch, l_rate, sum_error))
# 학습한 네트워크로 예측값을 뽑아보자.
def predict(network, row):
outputs = forward_propagate(network, row)
return outputs.index(max(outputs))
# 데이터셋 별로 예측하는 것 = 0 / 1 라벨에 속할 '확률'값
# 이 중에 더 큰 것이 최종적으로 예측한 값
# output의 max의 index를 구하면 된다
# 네트워크가 잘 학습되었는지 확인해보자.
for row in dataset:
prediction = predict(network, row) # 앞서 최적(학습)시킨 네트워크로 잘 학습되었는지 평가
print('실제값=%d, 예측값=%d' % (row[-1], prediction))