신경망 Neural Network
퍼셉트론은 가중치를 우리가 적절히 사람이 수동으로 설정해야 하는 단점이 존재.
그러나 신경망은 가중치 매개변수에 대한 적절한 값을 데이터로 부터 자동으로 학습.
신경망은 아래 이미지와 같이 왼쪽부터 입력층(Input), 은닉층(Hidden), 출력층(Output)으로 표현.
- 입력층: 가장 왼쪽 우리가 값을 넣어주는 부분
- 은닉층: 입력층과 출력층 사이 (우리 눈에 보이지 않는 층으로 Black Box라고도 표현)
- 출력층: 가장 오른쪽 결과값

input과 weight를 계산하여 다 더하고 임계값과 비교하여 임계값을 넘으면 Output으로 1을 출력하고 그렇지 않으면 0을 출력한
이를 편향을 명시하여 수식으로 표현하자면 아래와 같음.
### 편향을 명시한 퍼셉트론 수식
y = h(b+ x1*w2 + x2*w2)
a = b+ x1*w2 + x2*w2 일때,
y = h(a)
### 편향을 명시한 퍼셉트론 의사코드
y = h(b + w1*x1 + w2*x2)
if x<=0: h(x) = 0
else x>0: h(x) = 1
활성화 함수(activation function)
'활성화'라는 이름이 말해주듯 활성화 함수는 입력 신호의 총합이 활성화를 일으키는지 정하는 역할.

계단 함수(step function)
활성화 함수는 임계값을 경계로 출력이 바뀌는데, 이런 함수는 계단 함수라고 함.
특정, 임계값을 넘기면 활성화되는 함수.
시그모이드 함수(sigmoid function)
신경마엥서 자주 이용하는 활성화 함수는 시그모이드 함수.


시그모이드 함수에서 exp(-x)는 e^-x를 의미하며 e는 자연상수로 2.7192...의 값을 갖는 실수이다.
계단함수와 달리 시그모이드 함수는 완만하게매끄럽게 변화하는데 이 매끄러움이 신경망 학습에서 중요해서 시그모이드 함수를 사용.
계단 함수와 시그모이드 함수의 구현
두 함수를 직접 구현하면서 비교해보도록 하자.
계단함수 구현
# 계단함수 구현하기
def step_fucntion(x):
y = x > 0 # 배열을 받기 위해서
return y.astype(np.int) # 배열로 받아서 해당 타입은 int로 변환해서 반환
해당 함수의 경우 y는 조건에 따라 True 혹은 False를 가짐
이걸 int로 변환하면 False는 0 True는 1로 반환

시그모이드 함수 구현
# 시그모이드 함수 구현
def sigmoid(x):
return 1 / (1 + np.exp(-x))
x = np.array([-1.0, 1.0, 0.1])
y = sigmoid(x)

계단 함수는 0또는 1만 반환한다.
시그모이드 함수는 계단함수와 달리 매끄럽다. (0.880, 0.233 등의 값을 뱉어낼 수 있다.)
둘다 입력이 작으면 0에 가깝고 입력이 크면 1에 가깝다.
하지만 시그모이드 함수는 입력이 중요하면 큰 값을 출력하고 입력이 중요하지 않으면 작은 값을 출력한다.
공통점이자 중요한 점은 이 두 함수 모두 비선형 함수에 속한다는 점이다.
비선형 함수를 사용해야 하는 이유
신경망에서는 활성화 함수로 반드시 비선형 함수를 사용해야 하는데, 선형 함수를 사용하면 층을 깊게하는 의미가 사라진다.
즉, 선형 함수를 사용하면 은닉층을 사용하는 이점이 전혀 존재하지 않기 때문.
선형 함수를 사용하면 아래와 같은 상황을 맞이하게 됨.
### 선형 함수를 사용했을 때의 문제점.
y = cx
h(x) = y
### 3층 신경망 함수를 사용 예시(즉, a = c^3과 동일)
y(x) = h(h(h(x)))
y = c * c * c * x
ReLU 함수 (Recified Linear Unit)
예전에는 시그모이드 함수 주로 사용했으나 요즘은 ReLU 함수를 주로 사용.
ReLU는 입력이 0이 넘으면 그 입력을 그대로 출력하고 0 이하이면 0을 출력하는 함수.
# RELU 함수 (두 값중 큰 값을 반환)
def relu(x):
return np.maximum(0, x)

다차원 배열의 계산
인공지능과 관련한 학습을 진행한다면 반드시 선형대수(수학 이론)에 대한 부분과 python 언어를 활용한 넘파이를 학습하는 것이 매우 도움이 됨.
행렬과 넘파이에 대한 설명은 컴퓨터공학 수업에서 학습하여 생략.
3층 신경망 구현
신경망을 구현하기 위해서는 구현 방법에 대해서 정리해야 함.
3층 신경망은 입력층(0층), 은닉층(1층), 출력층(2층)으로 구성되었음.
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# 항등함수
def identity_function(x):
return x
# 3.4.3 구현 정리
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) # [ 0.31682708 0.69627909]
print(y)
순전파: 입력층에서 출력층으로.
출력층 설계
신경망은 분류와 회귀에 모두 이용할 수 있음. 어떤 문제냐에 따라서 사용하는 활성화 함수가 달라짐.
일반적으로 회귀에는 항등 함수를 분류에는 소프트맥스 함수를 사용.
분류는 데이터가 어느 클래스에 속하느냐의 문제. (사진 속 인물의 성별을 분류)
회귀는 입력 데이터에서 연속적인 수치를 예측하는 문제. (사진 속 인물의 무게를 예측)
출력층의 활성화 함수는 풀고자 하는 문제의 성질에 맞게 정하는데 아래는 일반적으로
- 회귀
- 항등 함수
- 2클래스 분류 (0/1)
- 시그모이드 함수
- 다중 클래스 분류 (multiple)
- 소프트맥스 함수
항등함수의 표현
항등함수에 대한 표현

소프트맥스 함수
소프트맥스 함수의 수식

우리가 실제로 계산할 때는 컴퓨터의 오버플로우 문제르 인하여 아래처럼 약간의 변형을 가하여 구현
아래의 수식이 의미하는 바는 함수를 계산할 때 어떤 정수를 더하거나 빼도 결과를 바뀌지 않는 점에 주목함.
C 언어에서는 오버플로우를 막을 목적으로 사용하므로 입력 신호 중 최댓값을 이용하는게 일반적임.
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
소프트맥스 함수는 0과 1사이의 실수로 표현됨.
따라서, 소프트맥스 합수의 총합은 1이며 출력 총합이 1이 된다는 점은 소프트 맥스 함수의 중요한 성질
> 즉, 이는 확률로 변환할 수 있다는 의미.
소프트맥스 함수의 특성항 각 원소의 대소 관계는 변하지 않음. 왜냐하면 exp(x)가 단조 증가이기 때문
입력부 사이의 대소관계가 결과값 사이의 대소관계로 그대로 이어짐.
신경망을 이용한 분류에서는 일반적으로 가장 큰 출력을 내는 뉴런에 해당하는 클래스로만 인식
또한 소프트맥스 함수를 적용해도 가장 큰 출력의 위치는 달라지지 않음.
즉, 출력부에 소프트맥스 함수를 생략할 수 있음.
이로 인하여 지수함수 계산에 드는 자원 낭비를 줄일 수 있음.
기계학습의 문제 풀이는 학습과 추론의 두 단계를 거쳐 이루어짐.
손글씨 숫자 인식
딥러닝에 전처리 활발히 사용.
앞의 예에서는 각 픽셀을 255로 나누는 단순 정규화 수행하였으나, 현업에서는 데이터 전체의 분포를 고려해 전처리 수행
전체 평균과 표준 편차를 이용해, 데이터들이 0을 중심으로 분포하도록 이동하거나 데이터의 범위를 제한하는 정규화 수행.
전체 데이터를 균일하게 분포시키는 데이터 백색화 기법도 있음.
위에꺼 분석하려면 형상을 파악해야 함.
import sys
import os
import pickle
import numpy as np
sys.path.append(os.pardir) # 부모 디렉터리의 파일을 가져올 수 있도록 설정
from mnist import load_mnist
from common.functions import sigmoid, softmax
# 3.6.1 MNIST 데이터셋
'''
0~9 숫자 이미지로 구성.
훈련 이미지 60000장, 시험 이미지 10000장
28*28 크기의 회색조 이미지이며 각 픽셀은 0~255의 값을 가짐
레이블은 정답에 해당하는 숫자
'''아하
# 처음 한 번은 몇 분 정도 걸림
# 이미지를 numpy 배열로 저장
# flatten : 입력 이미지를 평탄화(1차원 배열로)
# normalize : 입력 이미지를 정규화(0.0~1.0 사이의 값으로)
# one_hot_label : 레이블을 원-핫 인코딩 형태로 저장
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True,
normalize=False)
# 각 데이터의 형상 출력
print(x_train.shape) # (60000, 784)
print(t_train.shape) # (60000,)
print(x_test.shape) # (10000, 784)
print(t_test.shape) # (10000,)
# 3.6.2 신경망의 추론 처리
'''
입력층 784개, 출력층 10개,
은닉층 50개, 100개로 구성(임의)
'''
def get_data():
(x_train, t_train), (x_test, t_test) = \
load_mnist(flatten=True, normalize=True, one_hot_label=False)
return x_test, t_test
def init_network():
with open("python/ch3.신경망/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))) # Accuracy:0.9352
'''
# 3.6.3 배치 처리
batch_size = 100
for i in range(0, len(x), batch_size):
x_batch = x[i:i+batch_size] # 0~99, 100~=199 이렇게 묶어서 처리함.
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))) # Accuracy:0.9352
배치란?
컴퓨터로 계산할 때 큰 이점을 줌. 이미지 1장당 처리 시간을 대폭 줄여주는 것.
수치 계산 라이브러리 대부분이 큰 배열 효율적으로 처리하도록 고도화되어 있음.
커다란 신경망에서는 데이터 전송이 병목으로 작용하는 경우가 자주 있어서 배치 처리를 통해 버스에 주는 부하를 줄임.
정확히는 느린 I/O를 통해 데이터를 읽는 횟수가 줄어 빠른 CPU나 GPU로 순수 계산을 수행하는 비율이 높아짐.
컴퓨터에서는 작은 배열을 여러 번 계산하는 것보다 큰 배열을 한꺼번에 계산하는게 더 빠름.
정리
- 신경망에서는 활성화 함수로 시그모이드 함수와 ReLU 함수 같은 매끄럽게 변화하는 함수 이용
- 넘파이의 다차원 배열을 잘 사용하면 신경망을 효율적으로 구현
- 기계학습 문제는 회귀와 분류로 나눌 수 있음
- 출력층의 활성화 함수로는 회귀에서는 주로 항등 함수, 분류에서는 주로 소프트맥스 함수 이용
- 분류에서는 출력층의 누런 수를 분류하려는 클래스 수와 같게 설정
- 입력 데이터를 묶는 것을 배치라 함. 배치 단위 수행시 더 빨ㄹ ㅣ수행 가능
'AI > DeepLearning' 카테고리의 다른 글
| 활성화 함수 계층 기본 구현 정리 (0) | 2025.11.09 |
|---|---|
| 경사하강법 (Gradient Descent) (1) | 2025.11.09 |
| 오차역전파 (0) | 2025.11.05 |
| 신경망 학습 (Neural Network Training) (0) | 2024.04.23 |
| 퍼셉트론 Perceptron (0) | 2024.04.17 |