밑딥 데이터셋 다운로드 : github.com/WegraLee/deep-learning-from-scratch
앞 장에서 배운 퍼셉트론으로 복잡한 함수도 표현가능
신경망의 성질 : 가중치 매개변수의 적절한 값을 데이터로부터 자동으로 학습하는 능력
3.1 퍼셉트론에서 신경망으로
3.1.1 신경망의 예
가장 왼쪽 줄을 입력층, 맨 오른쪽 줄 출력층, 중간 층을 은닉층이라고 함
* 해당 책에서는 위의 신경망 구조를 '2층 신경망이라고 표현'
가중치를 갖는 층이 두 개이기 때문 / 문헌에 따라서는 같은 구조를 '3층 신경망'이라고도 표현
3.1.3 활성화 함수의 등장
활성화 함수 - 입력 신호의 총합이 활성화를 일으키는 지 정하는 역할, 입력을 출력으로 변환
a = 입력총합 / h() = 활성화 함수 / y = 출력값
* 일반적으로 단층 퍼셉트론은 단층 네트워크에서 계단 함수 사용한 모델을 가리키고,
다층 퍼셉트론은 신경망(여러 층으로 구성되고 시그모이드 함수 등의 매끈한 활성화 함수사용)을 가리킴
3.2 활성화 함수
계단 함수 - 임계값을 경계로 출력이 바뀌는 함수
퍼셉트론에서는 활성화 함수로 계단 함수를 이용한다고 할 수 있음
3.2.1 시그모이드 함수 (sigmoid function)
exp(-x)는 e^-x를 뜻하며, e는 자연상수로 2.7182...의 값을 갖는 실수
다른 함수와 마찬가지로 입력을 주면 출력을 돌려주는 변환기 역할
*계단함수 및 시그모이드함수 구현 생략
*시그모이드 함수
exp = exponentially(기하급수적으로)
x의 값이 커질수록 1보다 아주 작은값만큼의 큰 값이 분모의 값이 됨 (0.999....같은)
즉, 전체 값이 1에 가까워짐 / 반대의 경우도 마찬가지로 전체 값이 0에 가까워짐
이런 점을 활용한 함수가 sigmoid() 함수
3.2.5 시그모이드 함수와 계단 함수 비교
- 가장 큰 차이는 '매끄러움'의 차이
- 계단 함수는 0과 1만 반환하는 반면, 시그모이드 함수는 실수를 반환
- 공통점으로는 입력이 중요하면 큰 값, 중요하지 않으면 작은 값 출력
- 출력이 0에서 1 사이
3.2.6 비선형 함수
선형 함수 - 이 변환기에 무언가 입력했을 대 출력이 입력의 상수배만큼 변하는 함수 (f(x) = ax + b)
비선형 함수 - 직선 1개로는 그릴 수 없는 함수
신경망에서는 활성화 함수로 비선형 함수를 사용해야함
- 선형함수를 이용하면 신경망의 층을 깊게하는 의미가 없어지기 때문
선형함수의 문제는 층을 아무리 깊게해도 '은닉층이 없는 네트워크'로도 똑같은 기능을 할 수 있다는 것
3.2.7 ReLU 함수
- 입력이 0을 넘으면 그대로, 0 이하면 0을 출력하는 함수
- numpy로 구현시, maximum함수 사용하면 구현 가능
3.3 다차원 배열의 계산
숫자가 N차원으로 나열하는 것을 통틀어 다차원 배열이라고 함.
가로방향을 행(row)이라고 하며, 세로방향을 열(column)이라고 함.
1차원 배열일때도 shape은 tuple형태로 반환 ( (,10)과 같은 형태)
#2차원 배열 만들기
B = np.array([[1,2,3], [4,5,6], [7,8,9]])
3.3.2 행렬의 곱
왼쪽 행렬의 행(가로)과 오른쪽 행렬의 열(세로)을 원소별로 곱
numpy의 dot함수 이용하면 쉽게 곱셈가능
행렬의 곱에서 주의해야 할 점
- 행렬의 형상(shape)에 주의 해야 함
- 대응하는 차원의 원소 수를 일치시켜야 함 (그렇지 않으면 오류 출력)
- 서로 차원이 다를 때에도 원칙은 똑같이 적용됨
3.3.3 신경망에서의 행렬 곱
편향과 활성화 함수를 생략한 신경망
X, W, Y의 형상을 주의해서 보기 (대응하는 차원의 원소 수가 같아야함!)
3.4 3층 신경망 구현하기
3.4.2 각 층의 신호 전달 구현하기
- 편향을 뜻하는 뉴런인 (1)이 추가
- 가중치를 곱한 신호 두 개와 편향을 합친 수식 도출 가능
- numpy를 사용해서 구현하기
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])
print(W1.shape) # (2,3)
print(X.shape) # (2, )
print(B1.shape) # (3, )
A1 = np.dot(X, W1) + B1
1층의 활성화 함수에서의 처리
가중치 합을 a로 표기, 활성화 함수 h()로 변환된 신호를 z로 표기
z가 같은 과정 거쳐 2층으로 전달됨
3.4.3 구현 정리
3.5 출력층 설계하기
머신러닝(기계학습) 문제는 크게 분류와 회귀로 나뉨
일반적으로 회귀 문제의 경우 항등 함수, 분류에는 소프트맥스 함수 사용
항등함수는 입력을 그대로 출력
소프트맥스 함수의 분자는 입력신호의 지수함수, 분모는 모든 입력신호의 지수 함수의 합으로 구성
소프트맥스는 출력노드가 모든 입력노드의 영향을 받음
3.5.2 소프트맥스 함수 구현 시 주의점
softmax()는 오버플로 문제가 있음 - 너무 큰 값을 출력하여 표현할 수 없는 문제
softmax의 지수함수 계산 시 어떤 정수를 더하거나 빼도 결과는 바뀌지 않음
이를 이용하여 분자, 분모 각각에 정수를 더하여 문제 해결
정수 (C')는 보통 maximum값 이용
3.5.3 소프트맥스 함수의 특징
a = np.array([0.3, 2.9, 4.0])
y = softmax(a)
print(y)
>>> [0.1821127, 0.24519181, 0.73659691]
np.sum(y)
>>> 1.0
소프트맥스 함수의 출력은 0에서 1.0 사이의 실수
또한, 출력 총합이 1
이 성질로 인해 소프트맥스 함수의 출력을 '확률'로 해석가능
소프트맥스 적용해도 각 원소의 대소관계는 변하지 않음 (지수함수가 단조증가함수이므로)
결과적으로 소프트맥스 함수는 생략 가능함 (추론단계에서)
신경망 학습 시에는 출력층에서 소프트맥스 함수 사용함.
3.5.4 출력층의 뉴런 수 정하기
출력층의 뉴런 수는 풀려는 문제에 맞게 적절히 정해야 함
분류에서는 분류하고 싶은 클래스 수로 설정하는 것이 일반적
3.6 손글씨 숫자 인식
이미 학습된 매개변수 사용하여 추론과정만 구현 - 신경망의 순전파(forward propagation)
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)
load_mnist함수의 파라미터
- normalize: 입력 이미지의 픽셀값을 0.0~1.0 사이의 값으로 정규화할지
- flatten: 입력 이미지를 1차원 배열로 만들지
- one-hot-label: 원-핫 인코딩 형태로 저장할지
3.6.2 신경망의 추론 처리
- 입력층 뉴런 : 784개 (이미지크기가 28*28=784이기 때문)
- 출력층 뉴런 : 10개 (0 ~ 9까지, 클래스 수가 10개이기 때문)
- 은닉층 뉴런 : 각 50개 / 100개 (임의로 정한 값)
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))) # 정확도 파악
정확도를 통해 올바르게 분류한 비율 알아보기
* 데이터 전처리 (pre-processing)
위의 예시에서는 normalize = True설정
0 ~ 255 범위인 각 픽셀의 값을 0.0~1.0 범위로 변환 (정규화)
PCA 변환 뒤 분산을 1로 조정 (백색화, whitening)
3.6.3 배치 처리
배치처리 - 이미지 여러 개를 한꺼번에 묶어 넘기는 것
배치(batch) - 하나로 묶은 입력 데이터
* 배치처리를 할 때에도 원소별 수를 맞춰주어야 한다
이미지 100장을 한꺼번에 입력하는 경우 구현
x, t = get_data()
network = init_network()
batch_size = 100 #배치 크기 = 100
accuracy_cnt = 0
for i in range(0, len(x), batch_size): #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]) # 100장씩 실제 답과 비교
print("Accuracy: "+str(float(accuracy_cnt) / len(x)))
이번 장에서 배운 내용
- 신경망에서는 활성화 함수로 시그모이드 함수와 ReLU함수 같은 매끄럽게 변화하는 함수 이용
- 넘파이의 다차원 배열 잘 사용하면 신경망 효율적으로 구현가능
- 기계학습 문제는 크게 회귀와 분류로 나눌 수 있음
- 출력층 활성화 함수로 회귀에서는 주로 항등함수, 분류에서는 주로 소프트맥스 함수 이용
- 분류에서는 출력층의 뉴런 수를 분류하려는 클래스 수와 같게 설정
- 입력데이터를 묶은 것을 배치라 하며, 추론처리를 배치단위로 진행하면 빠른 결과
- 벡터의 스칼라배들이 합으로 연결된 형태 [본문으로]
'Minding's Reading > 밑바닥부터 시작하는 딥러닝' 카테고리의 다른 글
[밑바닥부터 시작하는 딥러닝] CH. 7 합성곱 신경망(CNN) (0) | 2021.05.09 |
---|---|
[밑바닥부터 시작하는 딥러닝] CH.6 학습 관련 기술들 (0) | 2021.05.09 |
[밑바닥부터 시작하는 딥러닝] CH.5 오차역전파법 (2) | 2021.05.09 |
[밑바닥부터 시작하는 딥러닝] CH.4 신경망 학습 (0) | 2021.04.06 |
[밑바닥부터 시작하는 딥러닝] CH.2 퍼셉트론 (2) | 2021.03.14 |