아이티윌_데이터 분석 55기/강의내용_딥러닝

#2 2일차_인공신경망의 구조, 실습

ecosso 2026. 7. 24. 16:18

01 인공신경망의 구조

 -01 layer + node

 -02 활성함수

 -03 오차함수

 -04 최적화

 -05 오차 역전파

 -06 기울기 소실

 

02 실습

 -01 ANN - 회귀

 -02 ANN 튜닝 - batch_size, early stopping (적절한 epoch 결정)

 -03 ANN - 분류


01 인공신경망의 구조

 -01 layer + node

 입력층 - 중간층 - 출력층 구조로 구성되어 있으며 각 층마다 여러 노드(뉴런)이 존재한다.

 

> 입력층 : 데이터를 전달받는 층 (노드의 수 = 피쳐 수)

> 출력층 : 최종 예측 결과를 리턴하는 층 (노드의 수 = 출력 결과)

  * 회귀분석 : 1개의 target

  * 분류분석 : 

   ex) 사망여부 -> 1개 target -> 원핫인코딩 (클래스별로 서로 다른 target 생성)* => Y_사망 Y_생존 (출력층 노드 수 2개)

   ex) iris data 품종 -> 1개 target -> 원핫인코딩 => Y_1 Y_2 Y_3 (출력층 노드 수 3개)

> 중간층 : 하나의 층 구성에서부터 층을 추가하여 성능 비교 (층의 수, 노드 수 -> 사용자 결정*)


 -02 활성함수 (사용자 결정*)

신호의 가중합을 다음 층에 전달할지의 여부를 결정한다.

sigmoid (0 : x < 0, 1 : x >=0), 렐루, 스텝 등

가중합의 크기가 0보다 작으면 0, 가중합의 크기가 0보다 크거나 같으면 1이다. 


 -03 오차함수

오차를 계산하여 오차를 줄이는 방향으로 기울기의 최적 구간을 탐색한다. (경사하강법)

* 회귀 분석 : MSE, RMSE, MAE 등

* 분류 분석 : cross entropy, binary cross entropy 등 (사용자 결정*)


 -04 최적화 (사용자 결정*)

전체 데이터 수 = N

batch_size : 한 번 가중치 업데이트 시 사용하는 데이터의 수

iteration(step) : 1 epoch 동안 발생하는 업데이트 수 = N / batch_size

epochs : 전체 데이터를 1회 학습하는 단위

 

ex) N = 100

      batch_size = 1

 

각 epoch마다 100건 중 단 1개의 데이터로 가중치 업데이트 100번 반복

 

batch_size = 100 (full batch = 배치 경사하강법)

각 epoch마다 100건 모두 사용하여 가중치 업데이트 1번 반복

 

batch_size = 10 (mini batch)

각 epoch마다 100/10 = 10개의 데이터로 가중치 업데이트 10번 반복


 -05 오차 역전파

기울기와 오차와의 관계를 통해 기울기의 최적화를 진행한다.

중간층에서는 오차와 기울기의 관계를 파악할 수 없는 문제가 발생하며, 순전파를 통한 출력층의 오차를 이전층으로 분해하여 전달, 중간층에서도 기울기와 오차의 관계를 파악할 수 있다.


 -06 기울기 소실

층이 늘어나면서 기울기가 0이 되는 현상이다.

시그모이드의 편미분값이 1보다 작아서 층마다 미분값이 곱해지는 과정에서 기울기가 0에 가까워진다.

하이퍼볼릭 탄젠트, 렐루, 소프트플러스 등으로 해결한다. (주로 렐루 사용)


02 실습

 -01 ANN - 회귀

1) 데이터 로딩

import pandas as pd
boston = pd.read_csv('boston.csv')

boston.head()
Out[14]: 
      crim    zn  indus  chas    nox  ...  tax  ptratio   black  lstat  medv
0  0.00632  18.0   2.31     0  0.538  ...  296     15.3  396.90   4.98  24.0
1  0.02731   0.0   7.07     0  0.469  ...  242     17.8  396.90   9.14  21.6
2  0.02729   0.0   7.07     0  0.469  ...  242     17.8  392.83   4.03  34.7
3  0.03237   0.0   2.18     0  0.458  ...  222     18.7  394.63   2.94  33.4
4  0.06905   0.0   2.18     0  0.458  ...  222     18.7  396.90   5.33  36.2

[5 rows x 14 columns]


y = boston['medv']
X = boston.drop(columns='medv')


2) 스케일링

from sklearn.preprocessing import StandardScaler, MinMaxScaler
m_sc = StandardScaler()
X_sc = m_sc.fit_transform(X)


3) train / test 분리

from sklearn.model_selection import train_test_split
train_x, test_x, train_y, test_y = train_test_split(X_sc, y, random_state=0)


4) 모델링

 (1) seed 고정 (딥러닝은 외부에서 seed를 고정)

import numpy as np
import tensorflow as tf
seed = 0
np.random.seed(seed)
tf.random.set_seed(seed)

 

 (2) 모델 정의

from tensorflow.keras.layers import Input
from keras import Sequential
from keras.layers import Dense

model = Sequential()
model.add(Input(shape = (train_x.shape[1], )))  # 입력층은 Input 함수로 노드 구성 (노드수 = 설명변수 수)
model.add(Dense(6, activation = 'sigmoid'))
model.add(Dense(1))  # 회귀분석 출력층은 노드는 1개로 구성, 활성함수 None

 

* 중간층에만 activation function을 넣어준다.

 

 (3) 오차함수, 최적화 결정

model.compile(optimizer='adam', loss='mse', metrics=['mae'])

 

 (4) 학습

model.fit(train_x, train_y, epochs=500, batch_size = 10)

확률적 경사 하강법을 사용하며,

batch_size = 10 : 전체 데이터의 10%를 랜덤 선택하여 가중치 업데이트에 사용한다.

 배치사이즈가 작아지면 속도는 빨라지나 안정성은 낮아진다. (편차가 커진다.)

epochs = 500 : 위 스텝을 500번 반복한다.

...
Epoch 498/500
38/38 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 10.2966 - mae: 2.3441
Epoch 499/500
38/38 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 10.2831 - mae: 2.3428
Epoch 500/500
38/38 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 10.2696 - mae: 2.3414

Out[42]: <keras.src.callbacks.history.History at 0x20fe69ac6e0>

379건의 10%를 사용하였기 때문에 Epoch 하단에 38이라는 숫자가 기재된다.

 

train_x.shape

train_x.shape
Out[43]: (379, 13)

 

 (5) 평가

model.evaluate(test_x, test_y)[0]     # loss (MSE)

model.evaluate(test_x, test_y)[0]
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - loss: 21.1461 - mae: 3.0215 
Out[45]: 21.14611053466797

 

model.evaluate(test_x, test_y)[1]    # metrics (MAE)

model.evaluate(test_x, test_y)[1]
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - loss: 21.1461 - mae: 3.0215 
Out[46]: 3.0215461254119873

 

=> 500번 epoch train loss : 10.2696, test loss : 21.1461 (과적합 가능성 충분)

 

 (6) 예측

model.predict(test_x)
test_y

test_y
Out[52]: 
329    22.6
371    50.0
219    23.0
403     8.3
78     21.2

49     19.4
498    21.2
309    20.3
124    18.8
306    33.4
Name: medv, Length: 127, dtype: float64

 

 

#

model.add(Dense(52, activation = 'relu'))
model.add(Dense(26, activation = 'relu'))
model.add(Dense(13, activation = 'relu'))

다음과 같이 다층으로 구성된 경우 sigmoid가 적절하지 않다. 이러한 경우일 때는 relu를 사용하는 것이 좋다.


 -02 ANN 튜닝 - batch_size, early stopping (적절한 epoch 결정)

1) 데이터 로딩

import pandas as pd
boston = pd.read_csv('boston.csv')

boston.head()
Out[14]: 
      crim    zn  indus  chas    nox  ...  tax  ptratio   black  lstat  medv
0  0.00632  18.0   2.31     0  0.538  ...  296     15.3  396.90   4.98  24.0
1  0.02731   0.0   7.07     0  0.469  ...  242     17.8  396.90   9.14  21.6
2  0.02729   0.0   7.07     0  0.469  ...  242     17.8  392.83   4.03  34.7
3  0.03237   0.0   2.18     0  0.458  ...  222     18.7  394.63   2.94  33.4
4  0.06905   0.0   2.18     0  0.458  ...  222     18.7  396.90   5.33  36.2

[5 rows x 14 columns]


y = boston['medv']
X = boston.drop(columns='medv')


2) 스케일링

from sklearn.preprocessing import StandardScaler, MinMaxScaler
m_sc = StandardScaler()
X_sc = m_sc.fit_transform(X)


3) train / test 분리

from sklearn.model_selection import train_test_split
train_x, test_x, train_y, test_y = train_test_split(X_sc, y, random_state=0)


4) 모델링

 (1) seed 고정 (딥러닝은 외부에서 seed를 고정)

import numpy as np
import tensorflow as tf
seed = 0
np.random.seed(seed)
tf.random.set_seed(seed)

 

 (2) 모델 정의

from tensorflow.keras.layers import Input
from keras import Sequential
from keras.layers import Dense

model = Sequential()
model.add(Input(shape = (train_x.shape[1], )))  # 입력층은 Input 함수로 노드 구성 (노드수 = 설명변수 수)
model.add(Dense(6, activation = 'sigmoid'))
model.add(Dense(1))  # 회귀분석 출력층은 노드는 1개로 구성, 활성함수 None

 

* 중간층에만 activation function을 넣어준다.

 

 (3) 오차함수, 최적화 결정

model.compile(optimizer='adam', loss='mse', metrics=['mae'])

 

 (4) early stopping rule

충분한 epoch로 학습, 매 스텝마다 검증 오차를 체크하여 더 이상 개선이 없을 경우 학습을 중단한다.

ex= EarlyStopping(monitor='val_loss', # 모니터링 기준
                              patience=10,          # 10회 동안 개선이 없을 경우 중단

                              restore_best_weights = True)  # 최적 오차 도출

# 멈춘 시점이 아닌 검증오차가 최소였던 지점의 오차 기준으로 최종 모델을 선정

 

 (5) 학습

hist = model.fit(train_x, train_y, 

validation_split = 0.25,  # train 데이터의 25%를 검증용 데이터로 선택

batch_size = 10, # 전체 데이터의 10%f를 랜덤 선택하여 가중치 업데이트에 사용

epochs = 50000, # 충분히 크게 설정

callbacks = [es]) # early stopping rule 추가

...
Epoch 126/50000
29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - loss: 2.3838 - mae: 1.1503 - val_loss: 6.4815 - val_mae: 1.9125
Epoch 127/50000
29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - loss: 2.3591 - mae: 1.1438 - val_loss: 6.3612 - val_mae: 1.9056
Epoch 128/50000
29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - loss: 2.3255 - mae: 1.1320 - val_loss: 6.4057 - val_mae: 1.9069
Epoch 129/50000
29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - loss: 2.3205 - mae: 1.1334 - val_loss: 6.3793 - val_mae: 1.9044
Epoch 130/50000
29/29 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - loss: 2.3036 - mae: 1.1307 - val_loss: 6.4116 - val_mae: 1.9115

 

 (6) 평가

hist.history['loss'] # 매 epoch 마다의 학습오차
hist.history['val_loss'] # 매 epoch 마다의 검증오차

 

import matplotlib.pyplot as plt
plt.plot(hist.history['loss'], label = 'train')
plt.plot(hist.history['val_loss'], label = 'val')
plt.legend()


model.evaluate(train_x, train_y)

model.evaluate(train_x, train_y)
12/12 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 3.5405 - mae: 1.4057 
Out[90]: [3.540541172027588, 1.405723214149475]


model.evaluate(test_x, test_y)

model.evaluate(test_x, test_y)
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 7ms/step - loss: 16.4689 - mae: 2.9572 
Out[91]: [16.468944549560547, 2.957167625427246]

 

## ** 참고 : 위 그래프 출력 시 폰트 에러가 발생하는 경우 -> 기본 폰트 깨짐 현상 -> 폰트 캐시 재생성
import matplotlib
import shutil, os
cache_dir = matplotlib.get_cachedir()
print(ache_dir)
shutil.rmtree(cache_dir)

 

 (7) batch size 튜닝

bs = [10, 20, 50, train_x.shape[0]]
for i in bs :
    seed = 0
    np.random.seed(seed)
    tf.random.set_seed(seed)

    # 모델 정의
    model = Sequential()
    model.add(Input(shape=(train_x.shape[1], ))) 
    model.add(Dense(6, activation='relu'))
    model.add(Dense(1))                                    

    # 오차함수, 최적화 결정
    model.compile(optimizer='adam', loss='mse', metrics=['mae','r2_score'])

    # early stopping rule
    es = EarlyStopping(monitor='val_loss',                 
                       patience=10,                        
                       restore_best_weights = True)         
                                  
    # 학습
    hist = model.fit(train_x, train_y, 
                     validation_split=0.25, verbose= 0, 
                     batch_size = i,        
                     epochs=50000,          
                     callbacks=[es])        

    # 최적 반복수 확인
    be = np.argmin(hist.history['val_loss']) + 1
    print(f'batch_size : {i}, 최적 반복수 : {be}')

    # 평가
    print(model.evaluate(train_x, train_y)[0])
    print(model.evaluate(test_x, test_y)[0])

batch_size : 10, 최적 반복수 : 1673
12/12 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 8.2063 - mae: 2.0459 - r2_score: 0.9038 
8.206317901611328
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - loss: 21.7978 - mae: 3.0250 - r2_score: 0.7332 
21.797828674316406
batch_size : 20, 최적 반복수 : 974
12/12 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 8.5905 - mae: 2.1381 - r2_score: 0.8993 
8.590462684631348
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 6ms/step - loss: 20.6176 - mae: 3.0851 - r2_score: 0.7476 
20.617616653442383
batch_size : 50, 최적 반복수 : 2302
12/12 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 8.3724 - mae: 2.1259 - r2_score: 0.9019 
8.372426986694336
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - loss: 20.5260 - mae: 3.0028 - r2_score: 0.7488 
20.525999069213867
batch_size : 379, 최적 반복수 : 5407
12/12 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - loss: 13.3568 - mae: 2.6968 - r2_score: 0.8434
13.356831550598145
4/4 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - loss: 25.5050 - mae: 3.6179 - r2_score: 0.6878 
25.504959106445312

 -03 ANN - 분류

회귀분석과는 다르게 출력층에서 반드시 가중합을 학습된 Y의 형태 (0 or 1)로 변환하는 장치 필요 --> 활성함수 (sigmoid)

출력층의 유닛(노드)의 수가 1개가 아닐 수 있음*

 

 (1) 데이터 로딩
cancer = pd.read_csv('cancer.csv')
y = cancer['diagnosis']
X = cancer.drop(columns=['id','diagnosis'])

 (2) 라벨링
y = (y == 'Malignant').astype('int')                # 출력층 노드 수 1개로 설정 필요
y2 = pd.get_dummies(y, prefix='Y').astype('int')    # 출력층 노드 수 2개로 설정 필요

 (3) 스케일링
from sklearn.preprocessing import MinMaxScaler
m_sc = MinMaxScaler()
X_sc = m_sc.fit_transform(X)

 (4)train / test 분리
from sklearn.model_selection import train_test_split
train_x, test_x, train_y, test_y = train_test_split(X_sc, y2, random_state=0)

 (5) 모델링
# 1) seed 고정
import numpy as np
import tensorflow as tf
seed = 0
np.random.seed(seed)
tf.random.set_seed(seed)

# 2) 모델 정의
from tensorflow.keras.layers import Input
from keras import Sequential
from keras.layers import Dense

model = Sequential()
model.add(Input(shape=(train_x.shape[1], )))           
model.add(Dense(15, activation='relu'))
model.add(Dense(2, activation='sigmoid'))                                    

# 3) 오차함수, 최적화 결정
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

# 4) early stopping rule
from tensorflow.keras.callbacks import EarlyStopping
es = EarlyStopping(monitor='val_loss',                  
                   patience=10,                         
                   restore_best_weights = True)         
                                                       
# 5) 학습
hist = model.fit(train_x, train_y, 
                 validation_split=0.25,   
                 batch_size = 10,        
                 epochs=50000,            
                 callbacks=[es])          

# 6) 평가
hist.history['loss']                      # 매 epoch 마다의 학습오차
hist.history['val_loss']                  # 매 epoch 마다의 검증오차
hist.history['accuracy']                  # 매 epoch 마다의 학습 정확도
hist.history['val_accuracy']              # 매 epoch 마다의 검증 정확도

import matplotlib.pyplot as plt
fig, ax = plt.subplots(1,2)
ax[0].plot(hist.history['loss'], label = 'train')
ax[0].plot(hist.history['val_loss'], label = 'val')
ax[0].legend()

ax[1].plot(hist.history['accuracy'], label = 'train')
ax[1].plot(hist.history['val_accuracy'], label = 'val')
ax[1].legend()

model.evaluate(train_x, train_y)          # accuracy: 0.9883 - loss: 0.0310 
model.evaluate(test_x, test_y)            # accuracy: 0.9441 - loss: 0.2931