안녕, 세상!

3-(1) 손글씨 숫자 인식 분석 본문

It공부/Deep learning

3-(1) 손글씨 숫자 인식 분석

dev_Lumin 2020. 6. 16. 23:09

손글씨 숫자 분류의 신경망 구조를 코드로 표현하려고 합니다.

이번엔 이미 학습된 매개변수를 사용해서 가중치 학습 과정은 생략하고, 추론 과정만 구현할 것입니다.

이 추론 과정을 신경망의 순전파(forward propagation) 라고도 합니다.

이 예에서 사용하는 데이터셋은 MNIST라는 손글씨 숫자 이미지 집합입니다.

MNIST는 기계학습 분야에서 유명한 데이터셋으로, 간단한 실험부터 논문으로 발표되는 연구까지 다양한 곳에서 이용하고 있습니다.

손글씨 숫자의 훈련 이미지가 60000장, 시험이미지가 10000장 준비되어 있고 

MNIST의 이미지 데이터는 28X28 크기의 이미지입니다.

이미지의 각 픽셀은 0에서 255까지 값을 취합니다.

각 이미지에는 그 이미지가 실제 의미하는 숫자가 레이블로 붙어 있습니다.

 

훈련 이미지와 시험이미지의 크기를 확인하는 코드는 아래와 같습니다.

 

1
2
3
4
5
6
7
8
9
10
11
import sys, os
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
from dataset.mnist import load_mnist
 
(x_train,t_train),(x_test,t_test)=load_mnist(flatten=True,normalize=False)
 
print(x_train.shape) #(60000,784) 
print(t_train.shape) #(60000,)
print(x_test.shape) #(10000,784)
print(t_test.shape) #(10000,)
 
cs

 부모 디렉터의 파일을 가져올 수 있도록 설정을 하고 dataset이라는 파일에 mnist.py 코드의 load_mnist함수를 임포트하고 load_mnist함수로 MNIST 데이터셋을 읽습니다. mnist.py코드는 나중에 아래서 자세히 볼 것입니다.

load_mnist로 데이터셋을 받을경우 인터넷에서 다운하는 형식으로 코드가 짜여져 있어서 인터넷이 연결된 상태이어야 하고 처음에만 다운로드시간 때문에 시간이 좀 걸립니다.

두번째 부터 불러올때는 로컬에 저장된 파일(pickle 파일)을 읽기 때문에 금방 끝납니다.

 

pickle - 파이썬에 있는 기능으로 프로그램 실행 중에 특정 객체를 파일로 저장하는 기능입니다.

           저장해둔 pickle 파일을 로드하면 실행 당시의 객체를 즉시 보원할 수 있습니다.

 

MNIST 데이터를 '(훈련이미지, 훈련레이블), (시험이미지, 시험레이블)' 형식으로 반환합니다.

훈련이미지의 행렬이 행은 60000개 열을 784개로 이뤄져 있는데 여기서 60000은 앞서 말한 60000개의 다른 이미지 샘플들의 수를 의미한는것이고 784는 load_mnist 함수에서 이미지 데이터 28x28=784를 1차원으로 펴준 상태에서 불러와서 784가 된것 입니다.

 

훈련이미지인 x.train은 다음 그림과 같이 이루어져 있습니다.

 

load_mnist 함수 매개변수(인수)

인수로 flatten, normalize, one_hot_label 세가지가 있습니다.

세가지 인수 모두 bool값입니다.

 

①flatten

- 입력 이미지를 평탄하게, 즉 1차원 배열로 만들지 정합니다.

- True로 설정하면 784개 원소로 이뤄진 1차원배열로 저장합니다.

- False로 설정하면 1x28x28의 3차원 배열로 저장합니다.

 

normalize

- 입력 이미지의 픽셀값을 0.0~1.0 사이의 값을 정규화할지를 정합니다.

- False 로 설정하면 입력 이미지를의 픽셀은 원래 값 그대로 0~255 사이의 값을 유지합니다.

 

③one_hot_label

- 레이블을 '원-핫 인코딩' 형태로 저장할지 정합니다.

- 원-핫 인코딩은 정답을 뜻하는 원소만 1이고(원 핫) 나머지는 모두 0인 배열입니다. 

 

 

 

 

다음은 MNIST 이미지를 화면으로 불러오는 코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import sys, os
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
from dataset.mnist import load_mnist
from PIL import Image  #이미지 관련 함수
 
 
def img_show(img):
    pil_img = Image.fromarray(np.uint8(img))
    pil_img.show()
 
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)
 
img = x_train[0]
label = t_train[0]
print(label)  # 5
 
print(img.shape)  # (784,)
img = img.reshape(2828)  # 형상을 원래 이미지의 크기로 변형
print(img.shape)  # (28, 28)
 
img_show(img)
cs

결과로 손글씨 5가 써져있는 이미지 파일이 열립니다.  

Image.fromarray() 함수 - numpy배열을 Image 객체로 바꿀때 사용하는 함수 

                         

Image.fromarray(np.uint8(img)) 에서 uint8는 넘파이배열의 자료형입니다

uint8 - 양수만 표현가능하며 2^8만큼 표현이 가능함 ( 0~255)

이미지의 픽셀이 0~255라서 그에 맞는 자료형으로 img 변수를 받아준 것입니다.

 

넘파이배열변수.reshape() - 행렬의 형상을 설정해주는 함수

 

 

 

신경망의 추론 처리

MNIST 데이터셋을 가지고 추론(분류)을 수행하는 신경망을 다음과 같이 구현합니다.

입력층 뉴런을 784개, 출력층 뉴런은 10개로 구성합니다. (이미지 크기가 28x28=784이고 출력은 0~9 까지 숫자를 구분)

은닉층은 총 두 개로 첫 번째 은닉층은 뉴런 50개, 두번째 은닉층은 뉴런 100개를 배치합니다.

처음에 말했듯이 가중치가 이미 학습된 객체를 가져와서 추론만 합니다.

이미 학습된 객체는 "sample_weight.pkl" 입니다.

파이썬은 보통 객체를 저장할 때 .pkl 이라는 확장자로 저장을 합니다.

"sample_weight.pkl"은 아래 코드가 저장된 파일에 같이 넣어줘야 합니다.

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
import sys, os
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import pickle
from dataset.mnist import load_mnist
from common.functions import sigmoid, softmax
 
 
def get_data():
    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
    return x_test, t_test
 
 
def init_network():
    with open("sample_weight.pkl"'rb') as f:
        network = pickle.load(f)
    return network
 
 
def predict(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']
 
    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    y = softmax(a3)
 
    return y
 
 
x, t = get_data()
network = init_network()
accuracy_cnt = 0
for i in range(len(x)):
    y = predict(network, x[i])
    p= np.argmax(y) # 확률이 가장 높은 원소의 인덱스를 얻는다.
    if p == t[i]:
        accuracy_cnt += 1
 
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))
cs

get_data 함수역할

-  load_mnist 함수로 실험이미지와 실험레이블의 데이터를 반환시킵니다.

 

init_network 함수역할

- 가중치의 값이 다 정해진 "sample_weight.pkl" 의 객체를 불러옵니다.

 

with 함수 - 파일을 열면 닫아야하는 명령을 해야하는데 with 함수를 사용함으로써 열고 닫는것을 자동처리함

 

open() as ~ 함수 - 파일을 불러오는 함수

                          형식: open("불러올 파일", 파일모드) as 불러온파일을 대표할 변수

 

파일모드 

파일모드 설명
'r' 읽기모드 - 파일을 읽기만 할 경우 사용
'w'   쓰기모드 - 파일을 내용을 쓸 경우 사용
(이미 존재하는 파일이면 파일의 내용이 다 사라지고 작성. 없는 파일이면 새로 파일 생성
'x' 쓰기모드 
(이미 존재하는 파일이면 오류를 출력함)
'a' 추가모드 - 파일의 마지막에 새로운 내용 추가
'+' 읽기쓰기모드
't' 텍스트모드 - 텍스트문자 기록에 사용
'b' 바이너리모드 - 바이트단위 데이터기록에 사용

 

pickle 모듈

- 파이썬에서 리스트나 클래스같은 텍스트 이외의 자료형을 파일로 저장하기 위해서 pickle이라는 모듈을 제공합니다.

 

import pickle 을 통하여 모듈 임포트가 필요합니다. 

pickle 모듈을 이용하면 원하는 데이터를 자료형의 변경없이 파일로 저장하여 그대로 로드할 수 있습니다.
(open(‘text.txt’, ‘w’) 방식으로 데이터를 입력하면 string 자료형으로 저장됩니다.)

pickle로 데이터를 저장하거나 불러올때는 파일을 바이트형식으로 읽거나 써야합니다. (wb, rb)

wb로 데이터를 입력하는 경우는 .bin 확장자를 사용하는게 좋습니다.

모든 파이썬 데이터 객체를 저장하고 읽을 수 있습니다.

 

③ predict 함수 역할 

- 가중치의 값을 변수에 저장시키고 입력데이터를 신경망에 대입하여 출력층의 결과를 소프트맥스 함수 형태로 반환함

 

 

from common.functions import sigmoid, softmax

이 부분은 common이라는 파일에 functions.py라는 파이썬 코드의 sigmoid, softmax 라는 함수를 임포트시켜서 사용하겠다는 의미입니다. sigmoid, softmax 함수는 전에 코드를 설명했습니다. 

 

 

 

                         

 

배치 처리

배치 - 하나로 묶은 입력 데이터를 말합니다. 

         원하는 수 만큼 묶어서 한번에 입력층에 넣어서 묶어진 수만큼 출력층에서 출력값을 한번에 받습니다.

배치 처리를 하게 되면 이미지 1장당 처리 시간을 대폭 줄여주기 때문에 컴퓨터로 계산할 때 큰 이점을 줍니다.

배치 처리를 수행함으로써 큰 배열로 이뤄진 계산을 하게 되는데, 컴퓨터에서 큰 배열을 한꺼번에 계산하는 것이 분할된 작은 배열을 여러 번 계산하는 것 보다 빠릅니다.

 

위에서 신경망 추론처리에서 한 코드에서 다른 부분만 다음과 같이 나타냅니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
x, t = get_data()
network = init_network()
 
batch_size = 100 # 배치 크기
accuracy_cnt = 0
 
for i in range(0len(x), batch_size):
    x_batch = x[i:i+batch_size]
    y_batch = predict(network, x_batch)
    p = np.argmax(y_batch, axis=1)
    accuracy_cnt += np.sum(p == t[i:i+batch_size])
 
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))
cs

 

 

range() 함수 - 범위를 지정해주는 함수

위의 코드에 i에는 0,100,200,300....9900  가 들어가면서 반복문이 실행됩니다.

 

np.argmax() 함수 - 해당 넘파이배열 중 가장 큰 원소의 인덱스 숫자를 찾는 함수

위의 코드 변수 p에는 출력값 배치들 각각의 최댓값의 인덱스들을 구성한 넘파이배열이 들어가고

이 인덱스들의 숫자는 결국 해당 이미지의 숫자 레이블을 의미하는것과 다름이 없으니

np.sum(p==t[i:i+batch_size]를 통해 p와 t[i:i+batch_size] 가 같으면 True(1) 이므로

1 아니면 0으로 이뤄진 100개의 원소들을 np.sum으로 합쳐서 합친 값이 0~100 까지 나올 수 있고 이를통해

정확도 확률을 구할 수 있습니다.

 

 

 

 

 

dataset.mnist의 load_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
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
110
111
112
113
try:
    import urllib.request
except ImportError:
    raise ImportError('You should use Python 3.x')
import os.path
import gzip
import pickle
import os
import numpy as np
 
 
url_base = 'http://yann.lecun.com/exdb/mnist/'
key_file = {
    'train_img':'train-images-idx3-ubyte.gz',
    'train_label':'train-labels-idx1-ubyte.gz',
    'test_img':'t10k-images-idx3-ubyte.gz',
    'test_label':'t10k-labels-idx1-ubyte.gz'
}
 
dataset_dir = os.path.dirname(os.path.abspath(__file__))
save_file = dataset_dir + "/mnist.pkl"
 
train_num = 60000
test_num = 10000
img_dim = (12828)
img_size = 784
 
 
def _download(file_name):
    file_path = dataset_dir + "/" + file_name
    
    if os.path.exists(file_path):
        return
 
    print("Downloading " + file_name + " ... ")
    urllib.request.urlretrieve(url_base + file_name, file_path)
    print("Done")
    
def download_mnist():
    for v in key_file.values():
       _download(v)
        
def _load_label(file_name):
    file_path = dataset_dir + "/" + file_name
    
    print("Converting " + file_name + " to NumPy Array ...")
    with gzip.open(file_path, 'rb') as f:
            labels = np.frombuffer(f.read(), np.uint8, offset=8)
    print("Done")
    
    return labels
 
def _load_img(file_name):
    file_path = dataset_dir + "/" + file_name
    
    print("Converting " + file_name + " to NumPy Array ...")    
    with gzip.open(file_path, 'rb') as f:
            data = np.frombuffer(f.read(), np.uint8, offset=16)
    data = data.reshape(-1, img_size)
    print("Done")
    
    return data
    
def _convert_numpy():
    dataset = {}
    dataset['train_img'=  _load_img(key_file['train_img'])
    dataset['train_label'= _load_label(key_file['train_label'])    
    dataset['test_img'= _load_img(key_file['test_img'])
    dataset['test_label'= _load_label(key_file['test_label'])
    
    return dataset
 
def init_mnist():
    download_mnist()
    dataset = _convert_numpy()
    print("Creating pickle file ...")
    with open(save_file, 'wb') as f:
        pickle.dump(dataset, f, -1)
    print("Done!")
 
def _change_one_hot_label(X):
    T = np.zeros((X.size, 10))
    for idx, row in enumerate(T):
        row[X[idx]] = 1
        
    return T
    
 
def load_mnist(normalize=True, flatten=True, one_hot_label=False):
    if not os.path.exists(save_file):
        init_mnist()
        
    with open(save_file, 'rb') as f:
        dataset = pickle.load(f)
    
    if normalize:
        for key in ('train_img''test_img'):
            dataset[key] = dataset[key].astype(np.float32)
            dataset[key] /= 255.0
            
    if one_hot_label:
        dataset['train_label'= _change_one_hot_label(dataset['train_label'])
        dataset['test_label'= _change_one_hot_label(dataset['test_label'])    
    
    if not flatten:
         for key in ('train_img''test_img'):
            dataset[key] = dataset[key].reshape(-112828)
 
    return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label']) 
 
 
if __name__ == '__main__':
    init_mnist()
cs

 

 

 

 

 

 

 

 

 

 

'It공부 > Deep learning' 카테고리의 다른 글

5. 오차역전파법  (0) 2020.06.19
4. 신경망 학습  (0) 2020.06.17
3. 신경망  (0) 2020.06.16
2. 퍼셉트론  (0) 2020.06.15
1. Python 심화( Numpy, Matplotlib)  (1) 2020.06.13
Comments