티스토리 뷰
신경망 2
출력층 설계하기
항등 함수
소프트맥스 함수
exp(x)는 자연상수 e를 x제곱한 지수 함수 입니다. n은 출력층의 뉴런 수, y는 그중 k번째 출력임을 뜻합니다. 분자는 입력신호 a의 지수 함수, 분모는 모든 입력 신호의 지수 함수의 합으로 구성됩니다.
소프트맥스 함수를 구현해 보겠습니다.
1 2 3 4 5 6 7 8 | import numpy as np def softmax(a): exp_a=np.exp(a) sum_exp_a=np.sum(exp_a) y=exp_a/sum_exp_a return y | cs |
식에 대한 설명을 해 보자면, 첫 번째 변형에서는 C라는 임의의 정수르 ㄹ분자와 분모 양쪽에 곱했습니다. 그 다음 C를 지수 함수 exp()안으로 옮겨 logC로 만듭니다. 마지막으로 logC를 C'라는 새로운 기호로 바꾼 것입니다.
여기서 중요한 점은 소프트맥스의 지수 함수를 계산할 때 어떤 정수를 더하거나 빼도 결과값은 변하지 않는다는 것입니다. C'에는 어떤 값을 대입해도 상관없지만 오버플로우를 막기 위해서는 일반적으로 입력 신호 중 최댓값을 이용하는 것이 일반적입니다. 코드를 한번 보겠습니다.
1 2 3 4 5 6 7 8 | >>> a=np.array([1010,1000,999]) >>> np.exp(a)/np.sum(np.exp(a)) array([nan, nan, nan]) >>> c=np.max(a) >>> a-c array([ 0, -10, -11]) >>> np.exp(a-c)/np.sum(np.exp(a-c)) array([9.99937902e-01, 4.53971105e-05, 1.67006637e-05]) | cs |
위 코드를 이용해 softmax()함수를 다시 짜보시기 바랍니다.
소프트맥스의 특징
출력층의 뉴런 수 정하기
위 그림에서 출력층 뉴런은 위에서부터 차례로 숫자 0~9에 대응하며, 위 그림에서는 y2가 가장 큰 값을 출력했기에 이 신경망이 선택한 클래스는 y2, 즉 입력 이미지를 숫자 2로 판단했음을 의미합니다.
손글씨 숫자 인식
MNIST 데이터셋
MNIST의 이미지 데이터는 28*28 크기의 회색조 이미지(1채널)이며, 각 필셀은 0에서 255까지 값을 취합니다.
아래는 데이터셋을 내려받아 이미지를 넘파이 배열로 변환해주는 코드입니다.
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(28, 28) # 형상을 원래 이미지의 크기로 변형 print(img.shape) # (28, 28) img_show(img) | cs |
Image.fromarray()는 너파이로 저장된 이미지 데이터를 PIL용 데이터 객체로 변환하는 함수입니다.
신경망의 추론 처리
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 | # coding: utf-8 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 |
init_network()에서는 pickle 파일인 sample_weight.pkl에 저장된 학습된 가중치 매개변수를 읽습니다. 이 파일에는 가중치와 편향 매개변수가 딕셔너리 변수로 저장되어 있습니다.
load_mnist()에서 normalize를 True로 설정해 줌으로서 0~255 범위인 각 필셀의 값을 0.0~1.0 범위로 변환해 정규화 시켜줍니다. 그리고 신경마의 입력 데이터에 특정 변환을 가하는 것을 전처리라고 합니다 .
배치 처리
1 2 3 4 5 6 7 8 9 10 11 12 | 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))) | cs |
여기서 argmax()는 최댓값의 인덱스를 가져옵니다. 인수로 준 axis=1은 100*10의 배열 중 1번째 차원을 구성하는 각 원소에서(1번째 차원을 축으로) 최댓값의 인덱스를 찾도록 한 것입니다. 아래는 예시입니다.
1 2 3 4 | >>> import numpy as np >>> x=np.array([[0.1,0.8,0.1],[0.3,0.1,0.6],[0.2,0.5,0.3],[0.8,0.1,0.1]]) >>> print(np.argmax(x,axis=1)) [1 2 1 0] | cs |