노션에서 작성한 노트를 HTML로 가져오기 때문에 형식이 어색할 수 있습니다.
!잘 보이지 않으시면 오른쪽 밑의 달 모양 버튼을 클릭해주세요!
신경망:
가중치 매개변수의 적절한 값을 데이터로부터 자동으로 학습하는 능력을 지님
은닉층의 뉴런은 겉으로 드러나 있지 않음
활성화 함수:
입력 신호의 총합을 출력 신호로 변환하는 함수
위의 식은 다음의 두 단계로 나눌 수 있음:
-
-
계단 함수: 임계값을 경계로 출력이 바뀜
단층 네트워크에서 사용
def step_function(x):
if x > 0:
return 1
else:
return 0
------------------------
def step_function_numpy(x):
y = x > 0
return y.astype(np.int)
시그모이드 함수(Sigmoid Function):
def sigmoid(x):
return 1 / (1+np.exp(-x))
계단 함수 vs. 시그모이드 함수
공통점:
비선형 함수
출력이 0에서 1 사이
입력이 작아지면 출력은 0에 가깝고 입력이 커지면 출력이 1에 가까움
차이점:
시그모이드 함수는 부드러운 곡선이며 출력이 연속적으로 변함 + 실수 반환
계단 함수는 0을 경계로 출력이 바뀜 + 정수 반환
비선형 함수: (직선 한 개로 그릴 수 없는 함수 - 계단 함수도 비선형)
신경망에서는 비선형 함수를 활성화 함수로 사용 - 선형은 사용하지 않음
선형 함수은 활성화 함수로 사용할 경우 은닉층이 사라짐
를 활성화 함수로 사용한 3층 네트워크는 이 됨
즉, 층을 쌓는 이점이 사라짐
ReLU 함수(Rectified Linear Unit):
*Rectified: "정류된", 정류 - 전기회로 용어로 -흐름을 차단한다고 생각하면 됨
def relu(x):
return np.maximum(0, x)
행렬: (센솔님의 블로그 중 이산수학 카테고리 참고) - 밑은 Numpy 활용 코드 소개
import numpy as np
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
np.ndim(A) -> A가 몇차원 배열인지
[np.array].shape -> MxN 행렬에서 (M, N)을 반환
np.dot(A, B) -> 곱
표기법: *자료마다 다를 수 있으니 확인 필수
가중치: 예)
은닉층 뉴런: 예)
편향: 예)
신호 전달:
*σ()는 항등 함수로 입력을 그대로 출력함 - 위의 그림에서는 출력층의 활성화 함수로 사용됨
실행 식 예시:
# 위의 그림의 식 구현 #
# 1. 입력층에서 1층(첫 번째 은닉층)으로 온 신호 처리
X = np.array([x1, x2])
W1 = np.array([
[w11, w21, w31],
[w12, w22, w32]
])
B1 = np.array([b1, b2, b3])
A1 = np.dot(X, W1) + B1
Z1 = sigmoid(A1) # 시그모이드 함수는 예시로 사용
# 2. 1층에서 2층으로 온 신호 처리
W2 = np.array([
[w11, w21],
[w12, w22],
[w13, w23],
])
B2 = np.array([b1, b2])
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2) # 시그모이드 함수는 예시로 사용
# 3. 2층에서 출력층으로 온 신호 처리
def identify_function(x):
return x
W3 = np.array([
[w11, w12],
[w12, w22]
])
B3 = np.array([b1, b2])
A3 = np.dot(Z2, W3) + B3
Y = identify_function(A3)
구현 정리
def init_network():
network = {}
network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
network['W1'] = 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['W1'] = np.array([0.1, 0.3, 0.5])
network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
network['W1'] = np.array([0.1, 0.2])
return network
# forward인 이유는 신호가 순방향으로 전달됨을 알리기 위함 <-> backward
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(x, W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(x, W3) + b3
y = identify_function(a3)
return y
network = init_network()
x = np.array([1.0, 0.5])
y = forword(network, x)
print(y) # [0.31682708, 0.69627909]
출력층 설계
출력층의 활성화 함수는 문제의 성질에 따라 달라짐
출력층의 뉴런 수는 문제에 맞게 적절히 정해야 함
- 분류에서는 분류하는 클래스 수가 일반적
항등 함수: 입력을 그대로 출력
소프트맥스 함수:
(exp(x)는 , n은 출력층의 뉴런 수, 는 k번째 출력)
특징:
출력은 항상 0과 1.0 사이의 실수
출력의 총합은 1 = 각 결과를 확률로 해석 가능
신경망으로 분류할 경우 가장 큰 출력을 내는 뉴런을 사용
- 소프트맥스 함수는 입력 간의 대소관계와 출력 간의 대소관계가 일치하므로 생략 가능
- 학습 단계에서는 사용함 | 추론 단계에서는 생략하는 것이 일반적
소프트맥스의 출력은 모든 입력 신호로부터 영향을 받음
*지수함수의 특성상 자칫하면 지수함수의 결과가 무한대가 될 수 있어 결과 수치가 불안정해질 수 있음
위의 문제점을 개선한 수식:
위 식에서 을 통해 소프트맥스의 결과를 전체적으로 줄이는 것 - 각 결과의 비율은 변하지 않음
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
예제: 손글씨 숫자 인식
*학습과정 생략, 추론과정만 구현
- Pickle - 특정 객체를 파일로 저장 - 데이터 준비 1회로 실행하는 동안 계속 사용 가능
- MNIST 데이터셋
- 0~9의 숫자 이미지, 28*28의 회색조 이미지, 각 이미지에는 해당하는 숫자로 레이블이 붙어 있음
- 훈련 이미지 60000장, 테스트 이미지 10000장
- 이 예제는 입력층 뉴런 784(28*28)개, 1층 은닉층 뉴런 50개, 2층 은닉층 뉴런 100개, 출력층 뉴런 10개임
(x_train, t_train), (x_test, t_text) = # (이미지, 레이블)의 형태
load_mnist(flatten=True, normalize=False, one_hot_label=False)
# flatten - 입력 이미지 1차원 배열화
# normalize - 픽셀을 0~1로 정규화(데이터를 특정 범위로 변환하는 처리)
# one_hot_label - 원-핫 인코딩 형태로 저장할지 결정-레이블을 배열화 하여 해당하는 인덱스를 1 나머지 0
# x_train.shape = (60000, 784)
# t_train.shape = (60000,)
# x_test.shape = (10000, 784)
# t_test.shape = (10000,)
def get_data():
(x_train, t_train), (x_test, t_text) =
load_mnist(flatten=True, normalize=False, one_hot_label=False)
return x_test, t_test
def init_network():
# sample_weight에는 학습된 가중치 매개변수가 저장되어 있음
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(x, W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(x, W2) + 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]) # 각 레이블의 확률을 넘파이 배열로 반환 [0.1, 0.3, 0.2...]
p = np.argmax(y) # 가장 확률이 높은 레이블(확률이 아닌 레이블=인덱스) 저장
if p == t[i]:
accuracy_cnt += 1 # 확률적으로 가장 높은 레이블이 답이면 증가
Accuracy = float(accuracy_cnt)/len(x)
# 배치 처리
x, t = get_data()
batch_size = 100
accuracy_cnt = 0
for i in range(0, len(x), batch_size):
x_batch = x[i:i+batch_size) # i에서 i+batch_size까지의 데이터 묶기
y_batch = predict(network, x_batch)
# axis=1은 1번째 차원 기준으로 가장 확률이 높은 레이블들을 1차원 배열로 저장
p = np.argmax(y_batch, axis=1)
# 예
x = np.array([[0.1, 0.8, 0.1], [0.3, 0.1, 0.6], [0.2, 0.5, 0.3]])
y = np.argmax(x, axis=1)
= [1, 2, 1]
# 옳게 분류한 것은 True, 아닌 것은 False인 1차원 배열
accuracy_cnt += np.sum(p == t[i:i+batch_size)
# 예
y = np.array([1, 2, 1])
t = np.array([1, 2, 0])
y == t = [True, True, False]
np.sum(y==t) = 2
Accuracy = float(accuracy_cnt)/len(x)
*전처리: 입력 데이터에 특정 변환을 가하는 것 - 위의 경우 정규화가 전처리 작업
정리:
신경망은 매끄럽게 변화하는 함수(시그모이드)를, 퍼셉트론은 갑자기 변화하는 함수(계단)를 활성화 함수로 함
'데이터 사이언스' 카테고리의 다른 글
<데이터 사이언스/Data Science> 3. 신경망 학습 (0) | 2021.02.13 |
---|---|
<데이터 사이언스/Data Science> 1. 퍼셉트론 (0) | 2021.01.31 |
<데이터 사이언스/Data Science> 0. 노트 공유 (0) | 2021.01.31 |