Deep Learning/from scratch I

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

해파리냉채무침 2024. 2. 5. 19:16

y= h(b+w1x1+w2x2)

가중치곱에 편향을 더한후 활성화 함수를 거쳐 output으로 출력됨. 

활성화 함수는 말 그대로 입력신호의 총합이 활성화를 일으키는지를 정하는 역할을 함.

활성화 함수는 비선형함수로 분류됨

 

활성화 함수

1. 시그모이드 함수

http://taewan.kim/post/sigmoid_diff/

신경망에서 자주이용되는 함수이다

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

 

2. 계단함수

0이하는 0, 양수는 1로 나타나는 함수다. 

넘파이 배열에 관하여 다음과 같이 구현함

def step_function(x):
  y = x>0
  return y.astype(np.int)

 return y.astype(np.int)는 boolean 형태로 true false가 나올때, 이것을 1,0으로 변환함

시각화 하면 다음과 같이 나온다

import numpy as np
import matplotlib.pylab as plt

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

시그모이드와 계단함수의 차이점이 있다면, 매끄러움의 차이다

계단함수는 0또는 1의값만 돌려주는 반면, 시그모이드는 0.nn의 실수를 return 한다. 

 

3. ReLU 함수

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

https://www.crocus.co.kr/1504

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

 

출력층 설계하기

일반적으로 회귀에는 항등함수, 분류에는 소프트맥스 함수 사용

항등함수는 입력신호 그대로 출력신호가 됨. 

https://blog.naver.com/PostView.naver?blogId=jghsa6665&logNo=222423644838&parentCategoryNo=&categoryNo=19&viewDate=&isShowPopularPosts=true&from=search

지수 함수/ 지수함수의 k번째 출력합 으로 나타낸다

구현시 지수함수로 인한 overflow 문제가 종종 일어난다. 예를들어 e^100은 0이 40개가 넘는 값이 되고 1000승이 되면 inf로 출력된다. 이를 해결하기 위해 각 실수를 배열에 있는 수 중 가장 큰값을 빼준다

본래 softmax 함수를 계산하면 다음과 같이 출력된다

a = np.array([1010,1000,990])
np.exp(a)/ np.sum(np.exp(a))
array([nan, nan, nan])

overflow 해결을 위한 코드는 다음과 같다

c = np.max(a)
print(a-c)

np.exp(a-c)/ np.sum(np.exp(a-c))
[  0 -10 -20]
array([9.99954600e-01, 4.53978686e-05, 2.06106005e-09])

 

softmax 함수의 출력은 0과 1사이의 실수로 계산되고, 출력의 총합이 1이된다는 특징이 있다. 여기서 softmax 를 적용해도 각 원소의 대소관계는 변하지 않는다. exp(x)가 단조 증가 함수이기 때문이다. 또한 출력이 가장 큰 뉴런의 위치 또한 변하지 않는다. 결과적으로 신경망으로 분류할 땐 softmax 함수를 생략해도 된다.

 

MNIST 데이터셋으로 신경망 구현하기

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 용 데이터 객체로 변환
  pil_img.show()

(x_train,t_train),(x_test,t_test)= load_mnist(flatten= True, normalize=False) #flatten 은 이미지를 1차원 배열로 변환, normalize는 정규화
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)

이미지를 불러오는데 다음과 같이 코드를 구현한다

 

입력층 뉴런을 784개(28*28), 출력층 뉴런을 10(mnist class 갯수 0~9)개로 설정함. 

은닉층은 총 두개, 첫번째 은닉층에는 50개, 두번째 은닉층에는 100개의 뉴런을 배치함.

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 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 파일은 학습된 가중치 매개변수를 읽어준다. 가중치와 편향 매개변수가 딕셔너리 변수로 저장되어 있음

x,t = get_data()
network = init_network()
accuracy_int = 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)))

predict 함수는 각 레이블의 확률을 numpy array로 반환함. argmax로 가장 큰 원소의 인덱스를 구함. 예측과 정답이 맞으면하나씩 count 해줌. 맞는 갯수가 몇개인지 acc 구함. 

 

배치처리 구현: 배치란 하나로 묶은 입력 데이터를 배치라고함

이미지가 다발로 묶여있다고 생각하면 됨.

x,t = get_data()
network = init_network()

batch_size=100 # 배치 크기
accuracy_int = 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)))

 

배치사이즈가 100이므로 x[0:100], x[100:200]..과 같이 100개씩 묶어줌. axis=1을 추가하여 100*10의 배열 중 첫번째 차원을 축으로 하는 최댓값의 인덱스를 찾도록함.