3.5 출력층 설계하기
분류 (classification)
- 데이터가 어느 클래스에 속하느냐는 문제이다
- 소프트맥스 함수를 사용한다
회귀 (regression)
- 입력 데이터에 (연속적인) 수치를 예측하는 문제이다
- 항등 함수를 사용한다
항등함수 (identity function)
- 입력을 그대로 출력한다
소프트맥스 함수 (softmax function)
- 소프트맥스 함수의 식은 아래와 같다
- 하지만 이대로 계산하면 지수함수에서 오버플로가 발생하기 때문에 아래와 같은 식으로 계산해야 오류가 나지 않는다
- 이와 같이 계산하면 오버플로를 막을 수 있다
import numpy as np
def softmax(a):
c=np.max(a)
exp_a=np.exp(a-c)
sum_exp_a=np.sum(exp_a)
y=exp_a/sum_exp_a
return y
a=np.array([0.3,2.9,4.0])
y=softmax(a)
print(y)
print(np.sum(y))
- 출력의 합이 1임으로 소프트맥스 함수의 출력을 확률로 해석할 수 있다
- 소프트맥스 함수를 적용해도 지수함수는 단조 증가 함수이기 때문에 가장 큰 출력을 내는 뉴런의 위치는 달라지지 않는다. 따라서 신경망으로 분류할 때는 출력층의 소프트맥스 함수를 생략해도 된다
- 출력층의 뉴런 수는 풀려는 문제에 맞게 적절히 정해야 한다
3.6 손글씨 숫자 인식
신경망의 순전파 (forward propagation)
- 이미 학습된 매개변수를 사용하여 학습과정을 생략하고, 추론하는 과정
MNIST 데이터셋
- 기계학습 분야에서 아주 유명한 데이터셋으로, 간단한 실험부터 논문으로 발표되는 연구까지 다양한 곳에서 이용하고 있다
https://github.com/WegraLee/deep-learning-from-scratch
GitHub - WegraLee/deep-learning-from-scratch: 『밑바닥부터 시작하는 딥러닝』(한빛미디어, 2017)
『밑바닥부터 시작하는 딥러닝』(한빛미디어, 2017). Contribute to WegraLee/deep-learning-from-scratch development by creating an account on GitHub.
github.com
> 다운로드를 받고 그 안에 dataset파일을 자신의 파이썬 코드와 같은 경로에 있어야 한다
from dataset.mnist import load_mnist
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)
print(x_train.shape)
print(t_train.shape)
print(x_test.shape)
print(t_test.shape)
load_mnist 함수
- load_mnist 함수로 MNIST 데이터셋을 읽는다
- load_mnist 함수는 읽은 MNIST 데이터를 "(훈련 이미지, 훈련 테이블),(시험 이미지, 시험 레이블)" 형식으로 변환한다
- normalize, flatten, one_hot_label 세 가지를 설정할 수 있는 세 인수는 모두 bool 값이다
- normalize는 입력 이미지의 픽셀 값을 0.0~1.0 사이의 값으로 정규화할지 결정한다
- flatten은 입력 이미지를 1차원 배열로 만들지를 결정한다. False이면 입력 이미지를 1x28x28 3차원 배열로 정한다
- one-hot-label은 레이블을 원-핫 인코딩(one-hot-encoding) 형태로 저장할지를 결정한다
> 원-핫 인코딩은 정답을 뜻하는 원소만 1이고 나머지는 모두 0인 배열이다
데이터 확인 및 MNIST 이미지를 화면 표시
from dataset.mnist import load_mnist
from PIL import Image
import numpy as np
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)
print(img.shape)
img=img.reshape(28,28)
print(img.shape)
img_show(img)
- flatten=True로 설정해 읽어 들인 이미지는 1차원 넘파이 배열로 저장되어 있기 때문에 이미지를 표시할 때는 원래 형상인 28x28 크기로 다시 변형해야 한다
- reshape() 메서드에 원하는 형상을 인수로 저장하면 넘파이 배열의 형상을 바꿀 수 있다
- 넘파이로 저장된 이미지 데이터를 PIL용 데이터 객체로 변환해야 하며, 이 변환은 Image.fromarray()가 수행한다
신경망의 추론 처리
- 신경망은 입력층 뉴런을 28*28=784개, 출력층 뉴런을 0~9까지의 숫자를 구분하는 문제이기 때문에 10개로 구상한다
- 은닉층을 총 두 개로, 첫 번째 은닉층에는 50개의 뉴런을, 두 번째 은닉층에는 100개의 뉴런을 배치한다 (50과 100은 임의로 정한 값이다)
from dataset.mnist import load_mnist
from PIL import Image
import numpy as np
import pickle
def get_data():
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False, one_hot_label=False)
return x_test,t_test
def init_network():
with open("sample_weight.pkl",'rb') as f: # 같은 위치에 sample_weight.pkl이 있어야 한다
network=pickle.load(f) # pickle은 파이썬 객체 자체를 파일로 저장된 것이다
return network
def sigmoid(x):
return 1/(1+np.exp(-x))
def softmax(a):
c=np.max(a)
exp_a=np.exp(a-c)
sum_exp_a=np.sum(exp_a)
y=exp_a/sum_exp_a
return y
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)))
- MNIST 데이터셋을 얻고 네트워크를 생성한다
- for문을 돌며 x에 저장된 이미지 데이터를 1장씩 꺼내 predict()함수로 분류한다
- 이미지가 숫자 '0'일 확률이 0.1, '1'일 확률 0.3 ... 식으로 해석하여 np.argmax() 함수로 이 배열에서 값이 가장 큰 원소의 인덱스를 구한다. 즉 예측 결과를 구하는 것이다
- 신경망이 예측한 답변과 정답 레이블을 비교하여 맞힌 숫자를 세고, 전체 이미지 숫자로 나눠 정확도를 구한다
정규화 (normalization)
- normalize를 True로 설정하여 0~255 범위인 각 픽셀의 값을 0.0~1.0 범위로 변환하는 것처럼 데이터를 특정 범위로 변환하는 처리를 정규화라고 한다
전처리 (pre_processing)
- 신경망의 입력 데이터에 특정 변환을 가하는 것을 전처리라고 한다
배치(batch)
- 하나로 묶은 입력 데이터를 배치라고 한다
배치 처리의 이점
- 수치 계산 라이브러리 대부분이 큰 배열을 효율적으로 처리할 수 있도록 고도로 최적화되어 있다
- 커다란 신경망에서는 데이터 전송이 병목으로 작용하는 경우가 자주 있는데, 배치 처리를 함으로써 버스에 주는 부하를 줄인다
> 이러한 이유로 큰 배열을 한꺼번에 계산하는 것이 분할된 작은 배열을 여러 번 계산하는 것보다 빠르다
from dataset.mnist import load_mnist
from PIL import Image
import numpy as np
import pickle
def get_data():
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False, 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 sigmoid(x):
return 1/(1+np.exp(-x))
def softmax(a):
c=np.max(a)
exp_a=np.exp(a-c)
sum_exp_a=np.sum(exp_a)
y=exp_a/sum_exp_a
return y
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()
batch_size=100 # 배치 크기
accuracy_cnt=0
for i in range(0,len(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)))
- range() 함수는 인수가 2개인 range(start, end)처럼 인수를 2개 지정해 호출하면 start에서 end-1까지의 정수로 이뤄진 리스트를 반환하고 range(start,end,step)처럼 인수를 3개 지정하면 start에서 end-1까지 step간격으로 증가하는 리스트를 반환한다
- x[i:i+batch_size]은 입력 데이터의 i번째부터 i+batch_size번째까지의 데이터를 묶는다
- argmax()는 최댓값의 인수값을 가져오지만 axis=1이라는 인수를 추가하는 것으로 100x10의 배열 중 1번째 차원을 구성하는 각 원소에서 최댓값의 인덱스를 찾도록 한 것이다.
- 배치 단위로 분류한 결과를 실제 답과 비교하기 위해 ==연산자를 사용해 넘파이 배열끼리 비교하여 True/False로 구성된 bool 배열을 만들고 True의 갯수를 센다
'학교 공부 > 컴퓨터비전' 카테고리의 다른 글
밑바닥부터 시작하는 딥러닝 Chapter 4 : 신경망 학습 (0) | 2024.05.02 |
---|---|
밑바닥부터 시작하는 딥러닝 Chapter3-1 : 신경망 (0) | 2024.05.01 |
밑바닥부터 시작하는 딥러닝 Chapter2 : 퍼셉트론 (0) | 2024.05.01 |
밑바닥부터 시작하는 딥러닝 Chapter1 : 헬로 파이썬 (0) | 2024.04.29 |