본문 바로가기

AI/딥러닝

[밑바닥부터 시작하는 딥러닝] 3장 신경망

728x90
반응형

입력층(0층), 은닉층(1층), 두번째 은닉층(2층), 출력층(3층)
 
 

활성화 함수

a = b + w1x1 + w2x2
y = h(a)

1단계. 가중치가 곱해진 입력 신호의 총합 계산
2단계. 합을 활성화 함수에 입력해 결과 도출
 
b - 편향

 
 

계단함수

입력이 0 넘으면 1 출력
그 외에는 0 출력

import numpy as np
import matplotlib.pylab as plt

def step_function(x):
  return np.array(x>0, dtype=int)

x=np.arange(-5.0,5.0,0.1)
y=step_function(x)
plt.plot(x,y)
plt.ylim(-0.1,1.1)
plt.show()

 

 

시그모이드 함수

연속적인 실수

def sigmoid(x):
  return 1/(1+np.exp(-x))

 
<공통점>
입력이 중요하면 큰 값을 출력, 입력이 중요하지 않으면 작은 값 출력
출력 0~1 사이

비선형 함수

직선 1개로는 그릴 수 없는 함수
층을 쌓고 싶다면 활성화 함수로 '비선형 함수' 사용
 
선형함수)
f(x)=ax + b
층을 깊게 해도 '은닉층이 없는 네트워크'와 똑같은 기능
 

ReLu 함수

입력이 0을 넘으면 입력 그대로 출력
0 이하면 0 출력

def relu(x):
  return np.maximum(0,x)

 
 

<다차원 배열의 계산>
 

배열의 차원수 - np.dim() 함수 사용
배열의 형상 - 인스턴스 변수(클래스에 정의된 변수) shape

 
행렬의 곱 - 대응하는 차원의 원수 수를 일치시켜야함

 
 

3층 신경망

가중치 오른쪽 아래 인덱스 번호 - 다음층 번호, 앞 층 번호 순서
 

<각층의 신호 전달 구현>

 

1층의 가중치 부분 간소화

        A = (a1,a2,a3)
B=(x1,x2)
                                                     W=((w11,w21,w31),(w12,w22,w32))


 
 

입력층에서 1층으로의 신호 전달

 

X = np.array([1.0, 0.5])
W1 = np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]])
B1 = np.array([0.1,0.2,0.3])

A1 = np.dot(X,W1)+B1

1층에서 2층으로의 신호 전달

Z1=sigmoid(A1)
W2 = np.array([[0.1, 0.4],[0.2, 0.5],[0.3, 0.6]])
B2 = np.array([0.1, 0.2

A2 = np.dot(Z1, W2)+B2
Z2 = sigmoid(A2)

2층에서 출력층으로의 신호 전달

def identity_function(x):
    return x
W3 = np.array([[0.1, 0.3],[0.2,0.4]])
B3 = np.array([0.1, 0.2])

A3 = np.dot(Z2, W3)+B3
Y = identity_function(A3) #Y=A3

 
 
<구현 정리>

import numpy as np
def sigmoid(x):
  return 1/(1+np.exp(-x))

def identity_function(x):
  return x

def init_network():
  network={}
  network['W1']=np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]])
  network['b1']=np.array([0.1,0.2,0.3])
  network['W2']=np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]])
  network['b2']=np.array([0.1,0.2])
  network['W3']=np.array([[0.1,0.3],[0.2,0.4]])
  network['b3']=np.array([0.1,0.2])

  return network

def forward(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=identity_function(a3)

  return y

network=init_network()
x=np.array([1.0,0.5])
y=forward(network, x)
print(y) #[0.31682708 0.69627909]

init_network() -  가중치와 편향을 초기화 → 딕셔너리 변수인 network 에 저장
network - 각 층에 필요한 매개변수 (가중치와 편향)를 저장
forward() - 입력 신호를 출력으로 변환하는 처리 과정을 구현 
 
 

<출력층 설계>

회귀 - 항등 함수

분류 - 소프트 맥스 함수

a=np.array([0.3,2.9,4.0])
exp_a=np.exp(a)
sum_exp_a=np.sum(exp_a)
y=exp_a/sum_exp_a
def softmax(a):
  exp_a=np.exp(a)
  sum_exp_a=np.sum(exp_a)
  y=exp_a/sum_exp_a
  return y

 
#주의점
소프트맥스 함수 - 지수 함수 사용 
→ 오버플로 문제, 큰 값끼리 나눗셈을 해서 결과 수치 '불안정'해짐
 
오버플로 막을 목적 - 입력 신호 중 최대값을 이용

a=np.array([1010,1000,990])
print(np.exp(a)/np.sum(np.exp(a))) #너무 큰 숫자 nan 출력 [ 1.34985881 18.17414537 54.59815003]
c=np.max(a)
print(a-c) #74.1221542101633
print(np.exp(a-c)/np.sum(np.exp(a-c))) #[0.01821127 0.24519181 0.73659691]

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

소프트맥스 함수 출력의 총합 1
→ 출력을 '확률'로 해석 가능
ex) y[0] 확률 0.018 (1.8%), y[1] 확률 0.245 (24.5%) y[2] 확률 0.737 (73.7%)
 
"2번째 원소의 확률이 높으니 답은 2번째 클래스다"
"74% 확률로 두번째 클래스, 25% 확률로 1번째 클래스, 1% 확률로 0번째 클래스다"
 
각 원소의 대소 관계는 변하지 않음
→ y=exp(x) 가 단조 증가 함수

입력 이미지를 2로 판단

뉴런의 회색 농도 - 해당 뉴런의 출력값의 크기
 


<손글씨 숫자 인식>

추론 과정 - 신경망의 순전파
 
MNIST 데이터셋
손글씨 숫자 이미지 집합
28x28 픽셀 사이즈의 손글씨 사진, 훈련 이미지 60,000장 시험 이미지 10,000장
각 픽셀은 0~255까지의 값

MNIST Data set 일부

import sys, os
sys.path.append(os.pardir) #부모 디렉터리로의 접근을 허락  
import numpy as np
from dataset.mnist import load_mnist #dataset 폴더의 mnist라는 파일에서 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)

print(img.shape)
img = img.reshape(28, 28)
print(img.shape)

img_show(img)

sys - 파이썬 인터프리터가 제공하는 변수들과 함수들을 직접 제어할 수 있게 해주는 모듈
os - 환경 변수나 디렉터리, 파일 등의 OS 자원을 제어할 수 있게 해주는 모듈
 
mnist_show.py

(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)

load_mnist 함수

normalize - 입력 이미지의 픽셀값을 0.0~1.0 사이의 값으로 정규화할지를 정함
                   False 설정 - 입력 이미지의 픽셀은 원래 값인 0~255 사이의 값 유지
 
flatten - 입력 이미지를 평탄하게 (1차원 배열)로 만들지를 정함
             False 설정 - 입력 이미지 1 x 28 x 28 의 3차원 배열
             True 설정 - 784개의 원소로 이뤄진 1차원 배열 저장
 
one_hot_label - 원-핫 인코딩 형태로 저장할지를 정함
             [0,0,1,0,0,0] 정답을 뜻하는 원소만 1 (hot), 나머지는 0
             False 설정 - '7'이나 '2' 같은 숫자 형태의 레이블 저장
             True 설정 - 레이블을 원-핫 인코딩하여 저장
 
 

<신경망의 추론 처리>

입력층 뉴런을 784(28x28)개, 출력층 뉴런을 10(0~9)개로 구성
첫번째 은닉층 50개의 뉴런
두번째 은닉층 100개의 뉴런

# coding: utf-8
import sys, os
sys.path.append(os.pardir) 
from dataset.mnist import load_mnist
from common.functions import sigmoid, softmax
import numpy as np
import pickle


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

pickle 파일인 sample_weight.pkl에 저장된 '학습된 가중치 매개변수' 읽음
→ 가중치와 매개변수가 딕셔너리 변수로 저장되어 있음
 
 

<정확도 평가>

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)))

for 문을 돌며 x에 저장된 이미지 데이터 1장씩 꺼냄. → predict() 함수로 분류
predict() - 각 레이블의 확률을 넘파이 배열로 반환
ex) [0.1,0.3] → '0'일 확률 0.1, '1'일 확률 0.3
 
신경망이 예측한 답변과 정답 레이블을 비교하며 맞힌 숫자를 셈 (accuracy_cnt +1) 
→ 전체 이미지 숫자로 나눠 정확도 구함
 
정규화 - 데이터를 특정 범위로 변환하는 처리
전처리 - 신경망의 입력 데이터에 특정 변환을 가함
 
가중치 형상 출력

print(x.shape) =>(10000, 784)

print(x[0].shape) =>(784,)

print(W1.shape) =>(784, 50)

print(W2.shape) =>(50, 100)

print(W3.shape) =>(100, 10)

다차원 배열의 대응하는 차원의 원소 수가 일치함을 확인

 
 

입력 데이터 형상 100x784 , 출력 데이터 형상 100x10

x[0]과 y[0] 에는 0번째 이미지 추론 결과, x[1]과 y[1]에는 1번째 이미지와 결과 저장
 
배치 - 하나로 묶은 입력 데이터 (묶음)
 
 

<데이터를 배치 처리로 구현>

x, y = 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 == y[i:i+batch_size])    

print("Accuracy: " + str(float(accuracy_cnt)/ len(x)))

range() - x[i:i+batch_size] 에서 입력 데이터를 묵음 (i번째부터 i+batch_size 번째까지의 데이터를 묶음)
batch_size가 100 - x[0:100], x[100:200]'
 
 
효율적이고 빠르게 처리 가능

 

728x90
반응형