기타/기타 잡다한 것들

[신경망] 파이썬으로 인공 신경망 만들기5

제주도소년 2018. 9. 28. 21:12

신경망 학습시키기


신경망 학습의 두 가지 단계

  1. 주어진 학습 데이터에 대해 결과 값을 계산해내는 단계.
  2. 계산한 결과 값을 실제의 값과 비교하고 이 차이를 이용해 가중치를 업데이트 하는 단계

tarin() 함수 코드




입력 계층으로부터의 신호를 최종 출력 계층까지 전파하는 과정은 query() 함수와 동일 하므로 내용은 거의 동일하다.

다만 아래 함수 매개변수를 보면 targets_list 가 추가로 존재한다. 이 매개변수 없이는 신경망을 제대로 학습시킬 수 없다.
1
def train(self, input_list, targets_list):
cs

앞에서 inputs_list를 numpy 배열로 변환했던 것과 동일한 방법으로 targets_list를 변환해줍니다.
1
targets = numpy.array(targets_list, ndmin=2).T
cs


이제 계산 값과 실제 값 간의 오차에 기반해 신경망의 동작에서 핵심이 되는 가중치를 업데이트할 준비가 거의 되었습니다.


단계별로 나누어 보면


우선 오차를 계산해야 합니다. 오차는 학습 데이터에 의해 제공되는 실제 값과 우리가 계산한 결과 값 간의 차이로 정의됩니다.

결국 오차는 ( 실제 값 행렬 - 계산 값 행렬 ) 이라는 연산의 결과 값이 됩니다. 이 연산은 원소 간 연산입니다. 파이썬으로 구현하면 다음과 같습니다.

1
2
# 출력계층의 오차는 (실제 값 - 계산 값)
output_errors = targets - final_outputs
cs


그 다음 은닉 계층의 노드들에 대해 역전파된 오차도 구할 수 있습니다.

1
2
# 은닉 계층의 오차는 가중치에 의해 나뉜 출력 계층의 오차들을 재조합해 계산
hidden_errors = numpy.dot(self.who.T, output_errors)
cs


그러면 각각의 계층에서 가중치를 업데이트하기 위해 필요한 모든 것을 갖추게 되었습니다. 은닉 계층과 최종 계층 간의 가중치는 output_errors를 이용하면 되고, 입력 계층과 은닉 계층 간의 가중치는 방금 구한 hidden_errors를 이용하면 되는 것입니다. 입력 계층과 은닉 계층 사이의 가중치에 대한 코드도 유사합니다. 계층의 이름 정도만 변경해주면 되겠습니다.


1
2
# 은닉 계층과 출력 계층 간의 가중치 업데이트
self.who += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)), numpy.transpose(hidden_outputs))
cs


1
2
# 입력 계층과 은닉 계층간의 가중치 업데이트
self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), numpy.transpose(inputs))
cs



이렇게 간단하게(?) 구현이 가능합니다. 인공 신경망 예제는 아래 책에서 참고해서 적습니다. 내용은 완벽하게 적지는 않았습니다.

책 제목 : 신경망 첫걸음

지은이 : 타리크 라시드

옮긴이 : 송교석


이상으로 인공 신경망 구현을 마쳤습니다. 소스 코드는 아래에 올려두고, 다음에는 MNIST 손글씨 데이터 인식을 할 예정입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import scipy.special
import numpy
# 신경망 클래스의 정의
class neuralNetwork:
    
    # 신경망 초기화 (입력노드 , 은닉노드 , 출력 노드 , 학습률)
    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
        # 입력, 은닉, 출력 계층의 노드 개수 설정
        self.inodes = inputnodes
        self.hnodes = hiddennodes
        self.onodes = outputnodes
        
        # 가중치 행렬 wih 와 who
        # self.wih = (numpy.random.rand(self.hnodes, self.inodes)-0.5)
        # self.who = (numpy.random.rand(self.onodes, self.hnodes)-0.5)
        
        # 더 정교한 가중치 
        self.wih = numpy.random.normal(0.0 , pow(self.hnodes, -0.5),(self.hnodes, self.inodes))
        self.who = numpy.random.normal(0.0 , pow(self.onodes, -0.5),(self.onodes, self.hnodes))
        
        # 학습률
        self.lr = learningrate
        
        # 활성화 함수로는 시그모이드 함수를 이용
        self.activation_function = lambda x: scipy.special.expit(x)
    
    # 신경망 학습
    def train(self, input_list, targets_list):
        # 입력 리스트를 2차원 행렬로 변환
        inputs = numpy.array(inputs_list, ndmin=2).T
        targets = numpy.array(targets_list, ndmin=2).T
        
        # 은닉 계층으로 들어오는 신호를 계산
        hidden_inputs = numpy.dot(self.wih, inputs)    
        # 은닉 계층으로 나가는 신호를 계산
        hidden_outputs = self.activation_function(hidden_inputs)
        # 최종 출력 계층으로 들어오는 신호를 계산
        final_inputs = numpy.dot(self.who, hidden_outputs) 
        # 최종 출력 계층으로 나가는 신호를 계산
        final_outputs = self.activation_function(final_inputs)
        
        # 출력계층의 오차는 (실제 값 - 계산 값)
        output_errors = targets - final_outputs
        # 은닉 계층의 오차는 가중치에 의해 나뉜 출력 계층의 오차들을 재조합해 계산
        hidden_errors = numpy.dot(self.who.T, output_errors)
        
        # 은닉 계층과 출력 계층 간의 가중치 업데이트
        self.who += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)), numpy.transpose(hidden_outputs))      
        # 입력 계층과 은닉 계층간의 가중치 업데이트
        self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), numpy.transpose(inputs))
        
    # 신경망에 질의
    def query(self, inputs_list):
        # 입력 리스트를 2차원 행렬로 변환
        inputs = numpy.array(inputs_list, ndmin=2).T
        
        # 은닉 계층으로 들어오는 신호를 계산
        hidden_inputs = numpy.dot(self.wih, inputs)
        
        # 은닉 계층으로 나가는 신호를 계산
        hidden_outputs = self.activation_function(hidden_inputs)
        
        # 최종 출력 계층으로 들어오는 신호를 계산
        final_inputs = numpy.dot(self.who, hidden_outputs)
        
        # 최종 출력 계층에서 나가는 신호를 계산
        final_outputs = self.activation_function(final_inputs)
        
        return final_outputs
 
# 입력, 은닉, 출력 노드의 수
input_nodes = 3
hidden_nodes = 3
output_nodes = 3
 
# 학습률 0.3
learning_rate = 0.3
 
# 인스턴스 생성
= neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)
 
 
cs