YOLO를 실행하는 OS로 LINUX와 Windows가 있는데, 먼저 이미지 전처리에 필요한 yolo mark의 경우 windows환경에서 실행시켜 보도록 하겠습니다.


  • 설치할 것
    • openCV 다운로드 링크에서 3.4.5버전을 다운받았습니다. 가능한 C드라이브에 다운받아주세요.

    • 다운받은 파일을 실행시켜서 install 합니다.

  • 환경 변수 설정 : openCV의 경로를 시스템 환경 변수의 path에 추가합니다.
  • 윈도우 검색창에서 환경 변수 편집을 검색

  • 시스템 환경 변수 path를 편집 : openCV를 다운로드 받은 경로를 추가합니다.

  • 프로젝트 속성 수정 : yolo_mark-master 폴더에 있는 yolo_mark.sln 파일을 실행합니다.
  • 프로젝트를 오른쪽 클릭하여 속성에 들어가 C/C++ -> 일반 -> 추가 포함 디렉터리에 다운로드경로\opencv\build\include를 추가합니다.

  • 링커 -> 일반 -> 추가 라이브러리 디렉터리에 opencv\build\x64\vc15\lib을 추가합니다.


  • Debug모드를 Release모드로 바꿔서 빌드합니다.

  • 만약 dll파일이 없다는 오류가 발생한다면 다운받은 openCV폴더에서 dll을 검색한 후 필요한 dll파일을 복사하여 yolo_mark_master의 x64 -> Release와 Debug 폴더에 붙여넣기를 해줍니다. 그 후 빌드를 하면 x64 -> Release폴더 내에 yolo_mark(Windows 명령어 스크립트(.cmd))파일이 생깁니다.
  • 이 파일을 실행 시키기 전에 학습시키고자하는 이미지 파일들을 Release -> data -> img에 넣어줍니다.
  • obj.names라는 파일에는 우리가 학습시키고 싶은 객체의 레이블 이름을 정해 놓을 수 있습니다. 이 파일을 메모장으로 열어서 제가 직접 입력할 수 있습니다.

  • 줄바꿈을 할때마다 종류가 늘어나는 개념입니다. 이 파일까지 저장을 하셨다면 yolo_mark(Windows 명령어 스크립트(.cmd))파일을 실행시켜봅니다.

  • 단축키는 h를 누르면 설명이 나오니 보고 따라하시면 됩니다. 숫자 0부터 우리가 obj.names에 넣어놓은 이름들이 순서대로 배치됩니다. - ex)0을 누르고 전처리를 한다면 그것은 air에 대한 객체들만 처리해야 할 것입니다.

  • 학습시키는 방법은 yolo를 다운받아서 해야 하므로 yolo_mark는 단순히 이미지에 학습시킬 객체를 지정하는 과정만 도움을 줍니다.


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

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


  •  MNIST 데이터는 많은 예제를 가지고 있습니다. 신경망은 가급적 이처럼 변형에 대해 다양하게 학습하는 것이 좋습니다.
  • 만약 우리가 이러한 변형을 직접 만들어서 예제에 추가해 학습할 수 있을까요? 예를 들어 현재의 예제들을 시계 방향과 반시계 방향으로 10도씩 회전시킴으로써 새로운 예제들을 만들 수 있는 것입니다.
  • 일단 10도와 -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
31
32
33
34
35
36
import scipy.ndimage
# train the neural network
 
# epochs is the number of times the training data set is used for training
epochs = 10
 
for e in range(epochs):
    # go through all records in the training data set
    for record in training_data_list:
        # split the record by the ',' commas
        all_values = record.split(',')
        # scale and shift the inputs
        inputs = (numpy.asfarray(all_values[1:]) / 255.0 * 0.99+ 0.01
        # create the target output values (all 0.01, except the desired label which is 0.99)
        targets = numpy.zeros(output_nodes) + 0.01
        # all_values[0] is the target label for this record
        targets[int(all_values[0])] = 0.99
        n.train(inputs, targets)
        
        ## create rotated variations
        # rotated anticlockwise by x degrees
        inputs_plusx_img = scipy.ndimage.interpolation.rotate(inputs.reshape(28,28), 10, cval=0.01, order=1, reshape=False)
        n.train(inputs_plusx_img.reshape(784), targets)
        # rotated clockwise by x degrees
        inputs_minusx_img = scipy.ndimage.interpolation.rotate(inputs.reshape(28,28), -10, cval=0.01, order=1, reshape=False)
        n.train(inputs_minusx_img.reshape(784), targets)
        
        # rotated anticlockwise by 10 degrees
        #inputs_plus10_img = scipy.ndimage.interpolation.rotate(inputs.reshape(28,28), 10, cval=0.01, order=1, reshape=False)
        #n.train(inputs_plus10_img.reshape(784), targets)
        # rotated clockwise by 10 degrees
        #inputs_minus10_img = scipy.ndimage.interpolation.rotate(inputs.reshape(28,28), -10, cval=0.01, order=1, reshape=False)
        #n.train(inputs_minus10_img.reshape(784), targets)
        
        pass
    pass
cs
  • 결과적인 그래프를 보도록 하겠습니다.

  • 최종적으로 약 98%의 정확도를 가지게 되었습니다.

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

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


  •  역질의(backwards qyery)
    • 일반적으로 우리는 학습된 신경망에 질문을 던지고 답을 얻습니다. 우리가 해온 예제에서 질문은 사람의 손으로 쓴 숫자의 이미지였으며 답은 0~9 사이의 숫자로 표현되는 레이블이었습니다.
    • 이 과정을 거꾸로 진행한다면 어떻게 될까요? 레이블을 출력 노드에 집어 넣고 이 신호를 이미 학습된 신경망에 거꾸로 전파시켜 입력 노드에서 이미지를 출력하게 하는 겁니다.
    • 우리는 이미 순방향으로의 로지스틱 함수를 알고 있습니다. 그렇다면 그 역방향의 함수는 y = f(x)에서 x = g(y)라는 식으로 바뀌었을 것입니다. -> x = ln[y/(1-y)]
    • 코드는 이 책의 깃허브에서 제공하는 코드를 사용했습니다. -> https://github.com/makeyourownneuralnetwork/makeyourownneuralnetwork/blob/master/part3_neural_network_mnist_backquery.ipynb
    • 레이블 0을 이용해 역질의를 해보도록 하겠습니다.

    • 역질의를 한 결과를 보니 숫자 0과 유사한 모양이 그려지는 것을 볼 수 있었습니다.

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

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


  •  이번에는 MNIST에서 제공되는 손글씨 숫자 이미지가 아닌 우리가 직접 만든 손글씨를 이용해 테스트 데이터를 만들어보겠습니다.
    • 노이즈가 끼거나 흔들린 이미지 등 다양한 시도를 해보겠습니다.

    • 이런식으로 다양한 노이즈와 글자에 대해 실험해보겠습니다.

    • 지금까지 사용했던 MNIST 데이터와 맞춰주기 위해 PNG 이미지를 28x28 픽셀에 맞춰 조정해야 합니다.

 

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
# helper to load data from PNG image files
import imageio
# glob helps select multiple files using patterns
import glob
import numpy
# library for plotting arrays
import matplotlib.pyplot
# ensure the plots are inside this notebook, not an external window
%matplotlib inline
 
 
# our own image test data set
our_own_dataset = []
for image_file_name in glob.glob('test_?.png'):
    print ("loading ... ", image_file_name)
    # use the filename to set the correct label
    label = int(image_file_name[-5:-4])
    # load image data from png files into an array
    img_array = imageio.imread(image_file_name, as_gray=True)
    # reshape from 28x28 to list of 784 values, invert values
    img_data  = 255.0 - img_array.reshape(784)
    # then scale data to range from 0.01 to 1.0
    img_data = (img_data / 255.0 * 0.99+ 0.01
    print(numpy.min(img_data))
    print(numpy.max(img_data))
    # append label and image data  to test data set
    record = numpy.append(label,img_data)
    #print(record)
    our_own_dataset.append(record)
    pass
# test the neural network with our own images
 
# record to test
item = 3
 
# plot image
matplotlib.pyplot.imshow(our_own_dataset[item][1:].reshape(28,28), cmap='Greys', interpolation='None')
 
# correct answer is first value
correct_label = our_own_dataset[item][0]
# data is remaining values
inputs = our_own_dataset[item][1:]
 
# query the network
outputs = n.query(inputs)
print (outputs)
 
# the index of the highest value corresponds to the label
label = numpy.argmax(outputs)
print("network says ", label)
# append correct or incorrect to list
if (label == correct_label):
    print ("match!")
else:
    print ("no match!")
    pass
cs
  • 결과

  • 노이즈에 대해서는 구분을 잘 하지 못하는 것 같았습니다.(마지막 6만 match) 이번에는 정상적인 글씨를 사용해보았습니다.

  • 정답인 결과들만 요약하겠습니다.

  • 결론적으로 굵고 뚜렷하게 쓰인 글씨만 잘 잡아내는 것 같았습니다.

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

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


  •  학습률 변경을 통한 신경망의 개선
    • 우리는 MNIST 데이터에 대해 약 95%의 정확도를 달성했지만 좀 더 개선을 할 여지가 있을지 보겠습니다.
    • 첫 번째로 학습률을 조정해보는 것입니다. 우리는 0.3이라는 한 가지 값으로만 학습률을 정했습니다. 따라서 여러 학습률 값을 실험해보며 성능을 측정해 보겠습니다.

    • 그래프를 보면 0.1~0.3의 학습률을 가질 때 좋은 성능을 보입니다. 0.2의 학습률이 가장 높으므로 0.2로 학습률을 정해놓겠습니다.
  • 여러 번 수행을 통한 신경망의 개선
    • 우리는 데이터 모음에 대해 학습을 여러 번 반복함으로써 신경망의 성능을 개선 할 수 있습니다. 한 번의 수행을 주기(epoch)라고 합니다.
    • 이러한 반복 작업이 가치가 있는 이유는 경사 하강법에 의해 가중치의 값이 업데이트되는 과정에서 더 많은 가능성을 제공해주기 때문입니다.
    • 이전에 만든 인공 신경망 소스에 다음과 같은 코드를 추가하여 주기를 넣어보도록 하겠습니다.

 

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
107
108
109
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.2
#신경망의 인스턴스를 생성
= neutalNetwork(input_nodes,hidden_nodes,output_nodes,learning_rate)
 
#데이터를 불러오고 그 파일을 읽는다.
training_data_file = open("mnist_train.csv",'r')
training_data_list = training_data_file.readlines()
training_data_file.close()
 
epochs = 7
 
#신경망 학습시키기
for e in range(epochs):
    #학습 데이터 모음 내의 모든 레코드 탐색
    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
    pass
cs
    • epochs라는 것을 넣어 train을 여러번 하게 했습니다. 그리고 학습률을 0.2로 바꿨습니다.
    • 그 결과 스코어를 계산해보니

    • 96%정도로 올랐습니다. 주기에 따른 정확도를 그래프로 나타내면 다음과 같습니다.

    • 학습률이 작을수록 더욱 성능이 좋아지는 것을 확인할 수 있었습니다. 이는 학습률이 작을 수록 경사 하강법에서 더 정밀한 최적의 경로를 찾을 수 있다는 말이 됩니다. 물론 이는 과학적인 방식이 아니며 손글씨 인식에만 국한되는 법칙임을 알려드립니다. 제대로 하기 위해서는 각각의 학습률과 주기의 다양한 조합에 대해 여러 번 실험을 함으로써 경사 하강법에 내재되어 있는 임의적 요소의 효과를 최소화해야 합니다.
  • 신경망 구조 변경하기
    • 신경망을 개선하는 또 한 가지 방법은 신경망의 구조를 변경하는 것입니다. 중간에 있는 은닉 계층의 노드의 수를 변경해보겠습니다.

  • 은닉 노드의 개수가 커질수록 성능이 높아지기는 하지만 200이상부터는 거의 변하지 않는 것을 확인할 수 있었습니다.
  • 500개의 은닉 노드에서 학습률 0.1로 5번의 학습을 해보고 정확도를 확인해보겠습니다.
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
#신경망 테스트
 
#신경망의 성능의 지표가 되는 성적표를 아무 값도 가지지 않도록 초기화
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)
 
#스코어 평균
scorecard_array = numpy.asarray(scorecard)
print("performance = ", scorecard_array.sum()/scorecard_array.size)
cs

    • 97.5%의 정확도까지 올라갔습니다! 

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

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


 

  •  기본적인 숫자들을 그래프의 그림형태로 나타내 보겠습니다. 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개의 노드를 가진 신경망을 구현했습니다.
  • 완전히 초기 단계이므로 다음 실습에서 학습하는 과정을 해보도록 하겠습니다.

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

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


  • 입력 값
    • 시그모이드 함수에서 입력 값이 크면 활성화 함수는 평범한 형태를 띠는 것을 볼 수 있습니다.
    • 기울기를 이용해 가중치를 업데이트하므로 평평한 활성화 함수는 문제가 있습니다.
    • 가중치의 변화는 활성화 함수의 기울기에 좌우됩니다. 이는 작은 기울기는 학습 능력이 제한된다는 것을 의미합니다. 이를 일컬어 신경망에 포화(saturation)가 발생했다고 합니다. 포화가 일어나지 않게 하기 위해 우리는 입력 값을 작게 유지해야만합니다.
    • 이 수식은 입력 신호(Oj)에도 영향을 받습니다. 따라서 우리는 가중치를 너무 작게 만들 수 없습니다. 컴퓨터는 매우 작거나 매우 큰 수를 처리할 때 정확도를 잃을 수 있으므로 매우 작은 수도 문제가 될 수 있는 것입니다.
    • 0.0~1.0 사이에 입력 값이 놓이도록 그 크기를 조정하는 것입니다. 때로는 입력 값에 0.01같은 작은 오프셋 값을 더해서 입력 값이 0이 되는 것을 방지하기도 합니다. 입력 값이 0이면 Oj = 0으로 설정되므로 가중치 업데이트 수식에 의한 학습 능력이 죽어버리기 때문입니다.

  • 결과 값
    • 신경망의 결과 값은 마지막 계층의 노드들로부터 출력되는 신호입니다.
    • 우리가 사용하는 시그모이드 함수는 0과 1에 한 없이 가까워지기는 하지만 일치하지는 않는 함수입니다. 만약 도달할 수 없는 값으로 목표 값을 설정한다면, 신경망을 학습하면 할수록 활성화 함수에 의해 만들어질 수 없는 더 큰 결과 값을 만들기 위해 점점 더 큰 가중치를 시도하게 될 것입니다. 그 결과, 결국 신경망을 포화시키게 됩니다.
    • 그러므로 목표 값을 도달 불가능한 값으로 설정하지 않도록 해야하며, 활성화 함수가 출력할 수 있는 값으로 재조정해야 합니다.
  • 임의의 값으로 가중치 초기화
    • 가중치 역시 입력 값, 출력 값과 마찬가지로 초기화 시 큰 값을 피해야 합니다.
    • 큰 값을 가지는 가중치는 신호를 크게 만들어 활성화 함수에 전달하게 되며 이는 우리가 계속 언급하고 있는 포화를 일으켜 가중치를 학습하는 능력을 떨어뜨리게 되기 때문입니다.
    • 가중치의 초기화는 -1.0 ~ 1.0 사이의 임의의 값으로 하는 것이 좋은 선택일 것입니다.
    • 수학자들이 도달한 결론은 노드로 오는 연결 노드의 개수에 루트를 씌운 다음 역수를 취해 얻은 값을 범위로 해서 가중치의 값을 초기화 하면 된다는 것이었습니다. -> ex)노드가 4개의 연결 노드를 가진다면 가중치의 초기값은 ±1/2 가 됩니다.
    • 가중치가 지나치게 큰 값으로 초기화하면 활성화 함수를 편향된 방향으로 편향시키게 될 것이며, 지나치게 큰 가중치는 그 활성화 함수를 포화시킬 것입니다.
    • 노드로 들어오는 연결 노드가 많을수록 더 많은 신호들이 합쳐지게 됩니다. 그러므로 많은 연결 노드를 가지면 가중치의 범위를 줄여야 한다는 것이 경험적으로 옳을 것입니다.
    • 어떤 경우라도 가중치의 초기 값을 같은 상수로 설정하면 안 됩니다. 신경망에 있는 모든 노드들이 같은 신호 값을 받아서 각 출력 노드의 출력 값 역시 동일하게 출력되기 때문입니다. 또한 오차를 역전파함으로써 가중치를 업데이트하는 과정에서 오차는 모두 같은 값으로 나뉘어 전파될 것입니다. 이를 대칭(symmetry)이라고 표현합니다.
    • 가중치가 0이 된다면 입력신호를 죽여버리기 때문에 더욱 좋지 않습니다. 입력 신호에 의해 좌우되는 가중치 업데이트 함수는 모두 0이 되어버립니다. 

+ Recent posts