도서 : 신경망 첫걸음(한빛미디어)

지음 : 타리크라시드(송교석 옮김)


 

  •  기본적인 숫자들을 그래프의 그림형태로 나타내 보겠습니다. MNIST 손글씨 데이터는 책 안의 내용에서 찾을 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy
import matplotlib.pyplot
%matplotlib inline
 
#데이터를 불러오고 그 파일을 읽는다.
data_file = open("mnist_train_100.txt",'r')
data_list = data_file.readlines()
data_file.close()
 
all_values = data_list[0].split(','#split는 구분자로 사용할 기호를 그 매개변수로 가진다. 리스트를 불러와서 쉼표로 구분하여 분리
#asfarray는 문자열을 실수로 변환한 다음에 그 숫자로 구성된 배열을 생성한다.
#reshape((28,28))함수는 784개의 어레이 숫자들을 28 x 28 형태의 정방 행렬로 만들어 준다
image_array = numpy.asfarray(all_values[1:]).reshape((28,28)) #리스트의 원소중 가장 첫 번째 원소는 이 원소들이 무엇을 표현하는 것인지를 알려주는 것이기 때문에 빼준다.
matplotlib.pyplot.imshow(image_array, cmap='Greys', interpolation='None'#imshow를 이용해 어레이를 시각화
cs
  • 결과

  • MNIST 학습 데이터 준비하기     

  • 현재 학습 데이터들의 값은 0~255의 값이므로 이를 255로 나눈 후 0.99를 곱하고 0.01을 더해 0.01 ~ 1.0의 범위로 만들겠습니다.

1
2
scaled_input = (numpy.asfarray(all_values[1:])/255.0*0.99)+0.01
print(scaled_input)
cs

  • 우리가 신경망에 바라는 바는 이미지를 분류해 정확한 레이블을 할당하는 것입니다. 레이블은 0~9까지 10개 숫자 중 하나입니다. 다시 말해 10개의 노드로 구성된 출력 계층이 필요하고, 각 노드는 가능한 결과 값, 즉 각 레이블에 해당할 것입니다.

  • 우리는 0.01~ 0.99사이의 값을 결과로 가지고자 합니다. 각 원소들의 첫 번째 값을 targets의 인덱스로 두고 그 값을 0.99로 정해주면 그 원소에 해당하는 노드의 값을 0.99로 설정해 준 것이 됩니다. 

  • 이를 바탕으로 우리는 0~9까지의 숫자를 구분지어 줄 수 있으며 이전에 만든 인공신경망을 사용해 이를 학습시킬 수 있게 됩니다.

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import numpy
import scipy.special
import matplotlib.pyplot
%matplotlib inline
 
#신경망 클래스의 정의
class neutalNetwork:
    
    #신경망 초기화하기
    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
        #입력, 은닉, 출력 계층의 노드 개수 설정
        self.inodes = inputnodes
        self.hnodes = hiddennodes
        self.onodes = outputnodes
        
        #가중치 행렬 wih와 who -> 정규분포의 중심은 0.0으로 설정, 표준편차는 노드로 들어오는 연결 노드의 개수에 루트를 씌우고 역수를 취함(pow함수)
        #배열 내 가중치는 w_i_j로 표기. 노드 i에서 다음 계층의 j로 연결됨을 의미
        #w11 w21
        #w12 w22 등
        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)
        
        pass
    
    #신경망 학습시키기
    def train(self, inputs_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))
        
        pass
    
    #신경망에 질의하기
    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 = 784 #손글씨 숫자 이미지를 구성하는 픽셀이 28x28의 크기를 가지기 때문
hidden_nodes = 100 #입력 값의 수보다 작은 값을 선택함으로써 신경망이 주요 특징을 찾아낸다, 너무 적은 수의 은닉 계층 노드를 선택한다면 제한적이 될 수도 있다
output_nodes = 10 #0~9까지의 레이블을 구분해야 하므로 출력은 10개면 충분하다.
 
#학습률은 0.3으로 정의
learning_rate = 0.3
#신경망의 인스턴스를 생성
= neutalNetwork(input_nodes,hidden_nodes,output_nodes,learning_rate)
 
#데이터를 불러오고 그 파일을 읽는다.
training_data_file = open("mnist_train_100.txt",'r')
training_data_list = training_data_file.readlines()
training_data_file.close()
 
#신경망 학습시키기
 
#학습 데이터 모음 내의 모든 레코드 탐색
for record in training_data_list:
    #레코드를 쉼표에 의해 분리
    all_values = record.split(','#split는 구분자로 사용할 기호를 그 매개변수로 가진다. 리스트를 불러와서 쉼표로 구분하여 분리
    #입력 값의 범위와 값 조정
    inputs = (numpy.asfarray(all_values[1:])/255.0*0.99)+0.01
    #결과 값 생성 (실제 값인 0.99 외에는 모두 0.01)
    targets = numpy.zeros(output_nodes) + 0.01
    #all_values[0]은 이 레코드에 대한 결과 값
    targets[int(all_values[0])] = 0.99
    n.train(inputs,targets)
 
    pass
cs
  • 이 소스를 실행시켰다면 신경망이 학습되었을 것입니다.
  • 이제 테스트를 해보도록 하겠습니다. 학습시킨 100개의 데이터가 아닌 10개의 데이터에서 값을 찾아 신경망에 넣고 정확한 결과가 나오는지 확인합니다.
  • 먼저 데이터를 확인해 보겠습니다.
1
2
3
4
5
6
7
8
9
test_data_file = open("mnist_test_10.txt",'r')
test_data_list = test_data_file.readlines()
test_data_file.close()
 
all_values = test_data_list[0].split(',')
print(all_values[0])
 
image_array = numpy.asfarray(all_values[1:]).reshape((28,28))
matplotlib.pyplot.imshow(image_array, cmap='Greys', interpolation='None')
cs
  • 결과는

  • 그리고 우리가 학습시킨 인공 신경망에 질의를 하면?
1
n.query((numpy.asfarray(all_values[1:])/255.0*0.99)+0.01)
cs

  • 레이블 7에 해당하는 결과 값이 가장 크다는 것을 알 수 있습니다. 학습데이터와는 전혀다른 손글씨를 구분한 것입니다. 
  • 이번에는 10개의 테스트 데이터를 인공 신경망이 잘 구분하는지 확인해 보겠습니다.
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
#신경망 테스트
 
#신경망의 성능의 지표가 되는 성적표를 아무 값도 가지지 않도록 초기화
scorecard = []
 
#테스트 데이터 모음 내의 모든 레코드 탐색
for record in test_data_list:
    #레코드를 쉼표에 의해 분리
    all_values = record.split(',')
    #정답은 첫 번째 값
    correct_label = int(all_values[0])
    print(correct_label,"correct label")
    #입력 값의 범위와 값 조정
    inputs = (numpy.asfarray(all_values[1:])/255.0*0.99)+0.01
    #신경망에 질의
    outputs = n.query(inputs)
    #가장 높은 값의 인덱스는 레이블의 인덱스와 일치
    label = numpy.argmax(outputs)
    print(label, "network's answer")
    #정답 또는 오답을 리스트에 추가
    if(label == correct_label):
        #정답인 경우 성적표에 1을 더함
        scorecard.append(1)
    else:
        #정답이 아닌 경우 성적표에 0을 더함
        scorecard.append(0)
        pass
    pass
 
print(scorecard)
cs

  • 일치하는 값은 10개 중 6개 입니다. 즉, 100개의 데이터로 학습시킨 인공 신경망은 60%의 정확도밖에 보이지 않는다는 말 입니다.
  •  전체 데이터를 이용해 학습 및 테스트하기
    • 이제부터는 좀 더 큰 학습 데이터와 테스트 데이터를 이용해 정확도를 확인해 보도록 하겠습니다.
    • 책의 내용에 있는 깃허브에서 각각의 파일을 다운로드 한 후 폴더에 넣어줍니다.
    • 그 후 파일명만 바꿔주면 됩니다.
1
2
3
#스코어 평균
scorecard_array = numpy.asarray(scorecard)
print("performance = ", scorecard_array.sum()/scorecard_array.size)
cs

  • 약 95%의 정확도를 가질 수 있게 되었습니다. 

도서 : 신경망 첫걸음(한빛미디어)

지음 : 타리크라시드(송교석 옮김)


  •  인공 신경망의 뼈대를 책을 보고 만들어 보았습니다.(책 내용 그대로 입니다.)
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
83
84
85
86
import numpy
import scipy.special
 
#신경망 클래스의 정의
class neutalNetwork:
    
    #신경망 초기화하기
    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
        #입력, 은닉, 출력 계층의 노드 개수 설정
        self.inodes = inputnodes
        self.hnodes = hiddennodes
        self.onodes = outputnodes
        
        #가중치 행렬 wih와 who -> 정규분포의 중심은 0.0으로 설정, 표준편차는 노드로 들어오는 연결 노드의 개수에 루트를 씌우고 역수를 취함(pow함수)
        #배열 내 가중치는 w_i_j로 표기. 노드 i에서 다음 계층의 j로 연결됨을 의미
        #w11 w21
        #w12 w22 등
        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)
        
        pass
    
    #신경망 학습시키기
    def train(self, inputs_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))
        
        pass
    
    #신경망에 질의하기
    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
#신경망의 인스턴스를 생성
= neutalNetwork(input_nodes,hidden_nodes,output_nodes,learning_rate)
 
n.query([1.00.5-1.5])
 
cs
  • 3개의 계층과 3개의 노드를 가진 신경망을 구현했습니다.
  • 완전히 초기 단계이므로 다음 실습에서 학습하는 과정을 해보도록 하겠습니다.

도서 : 신경망 첫걸음(한빛미디어)

지음 : 타리크라시드(송교석 옮김)

 

  • 인간에게는 쉽고 기계에게는 어려운
    1. 인간과 컴퓨터의 차이점 : 컴퓨터는 많은 연산을 매우 빠르게 할 수 있는 쉽게 말하면 계산기 같은 것이지만 인간은 그러한 빠른 계산을 보통은 하지 못한다. 그렇지만 인간은 어떠한 정보를 인지하고 올바른 것을 찾아내는 일을 쉽게 해낸다. 물론 컴퓨터는 그러한 일을 하지 못한다.
    2. 딥러닝은 이러한 컴퓨터의 단점을 장점으로 극복하고자 하는 것이다.
  • 간단한 예측자
    1. 킬로미터를 마일로 바꾸는 공식을 모른다고 가정했을 때, 두 단위의 관계가 선형이라는 것과 오차를 활용하여 정확한 공식을 알아내볼 수 있다.
    2. 마일 = 킬로미터 x c(상수)라는 선형적인 공식을 만들어서 c의 값에 임의의 값인 0.5를 넣는다면
    3. 100킬로미터는 50마일이라는 예측 값을 얻게 된다.
    4. 그러나 100킬로미터는 62.137마일이므로 오차는 12.137이 된다. -> 오차 = 실제 값 - 계산된 값
    5. 여기서 상수값을 조금 더 올려 0.6으로 계산을 해본다면 60마일이 나오므로 오차는 2.137이 된다.
    6. 여전히 오차가 발생했으므로 또다시 0.7을 넣어본다면 70마일, 즉, 오차가 -7.863이 되어버려 다시 c의 값을 줄여야하는 상황에 도달한다. -> 오버슈팅
    7. 여기서 0.6을 조금만 올려 0.61로 계산해보면 61마일, 오차는 1.137
    8. 이런식으로 오차를 비교해가며 임의의 값을 증가시키고, 증가량 또한 조절하면서 오차가 0이 되는 순간을 찾는다면 우리가 원하는 킬로미터를 마일로 바꾸는 공식을 컴퓨터의 계산만으로 찾아낼 수 있는 것이다. -> 이러한 과정을 반복이라고 한다.

 

    • 컴퓨터는 입력 -> 연산 -> 출력 시스템이며 인공 신경망도 마찬가지이다.
    • 어떤 것의 동작 원리를 정확히 파악할 수 없을 때 취할 수 있는 한 방식은 우리가 조정할 수 있는 매개변수 값을 포함하는 모델을 만들어보는 것이다.
    • 모델을 정교화해나가는 좋은 방법은 오차에 기초해 매개변수 값을 조정해나가는 것이다.
  • 분류는 예측과 그다지 다르지 않습니다
    1. 두 가지 부류의 곤충들을 분류하는 하나의 공식이 있다고 가정하자
    2. 애벌레와 무당벌레가 그 두 부류라면 애벌레는 날씬하고 길이가 길며, 무당벌레는 통통하고 길이가 짧다.
    3. 이를 그래프상에 길이(y), 폭(x)으로 나타내면 인간들의 눈으로는 분류된 것이 보이지만 컴퓨터는 하나의 공식으로 분류를 하게된다.
    4. 여기서는 간단히 직선공식의 기울기를 변화시키며 분류시킬 수 있다.
    5. 폭 1.0cm에 길이 3.0cm인 애벌레와 폭 3.0cm에 길이 1.0cm인 무당벌레가 있다고 하면 그래프에 두 개의 점을 찍게 된다.
    6. 여기서 y = Ax의 아주 간단한 직선공식으로 저 둘을 분류할 수 있을까?
    7. A를 0.25라고 한다면 두 벌레가 모두 같은 부류로 분류되므로 좋은 분류자가 될 수 없다.
    8. 여기서 이 공식에 무당벌레의 데이터를 넣어본다면 y = 0.25 * 3.0 = 0.75 인데 실제 길이는 1.0이므로 A는 너무 작은 수라는 것을 할 수 있다.
    9. 그렇지만 저 공식은 무당벌레의 길이를 맞추는 것이 아닌 분류를 하기 위한 것이므로 직선 아래에 값이 있기 위해서는 1.0보다 큰 값이 나와야 한다.
    10. 그래서 x = 3.0일 때, y = 1.1이 되는 것을 목표로 해보자
    11. 오차(E) = 목표값(1.1) - 실제 출력 값(0.75) = 0.35이므로 그래프를 확인해보면
    12. E와 A의 관계를 공식으로 나타낼 수 있을까? 우리는 A의 값을 또 다시 조금씩 증가, 혹은 감소시키며 원하는 목표값에 도달하고자 한다. 그러므로 y = Ax라는 이 공식을 y = (A + B)x 로 바꿀 수 있다.(B는 A의 변화량)
    13. 오차(E)의 공식을 이용하여 Ax - (A + B)x를 계산해보면 E = Bx라는 하나의 공식을 알아낼 수 있다. -> 따라서 B = E / x
    14. 아까 오차와 x값을 넣어보면 B = 0.1167이므로 0.1167만큼 A값을 증가시키면 된다.
    15. 새롭게 만들어진 y = 0.3667 * x 라는 공식에 두 번째 학습 데이터인 x = 1.0, y = 3.0을 대입하면 y = 0.3667이 되어 3.0과 매우 많은 차이가 나게 된다.
    16. 아까 전, 직선 아래에 무당벌레가 들어갔으므로 애벌레는 직선 위쪽에 위치해야 하므로 목표값을 2.9로 정하면 오차(E) = 2.5333이 된다.
    17. 그러나 이런식으로 학습을 한다면 너무 한 쪽에만 편향될 수 있으므로 여기서 변화량을 일부만 업데이트 하도록 하면 편향되는 것을 막을 수 있다.
    18. 이를 학습률(L)이라고 하고 B = L(E/x)로 나타낼 수 있다. 이렇게 하면 더 나은 결과를 얻을 수 있게 된다.

  • 오차를 제거하기 위해서 얼마만큼의 기울기를 조정해야 하는지 오차와 기울기 매개변수 간의 관계를 이해할 수 있다.
  • 그러나 이러한 조정과정은 이전의 학습 데이터는 무시하고 최종 학습 데이터에만 맞춰 업데이트되기때문에 학습률을 도입하여 정도를 조정한다.

+ Recent posts