5일간의 미니프로젝트가 끝나고 다시 수업으로 돌아갈 시간.
에이블스쿨의 3 Step 중 첫 단계의 마지막 교육 과정이다.
딥러닝 기초 나흘, 심화 나흘이 지나고 나면 두 번 연달아 미니프로젝트를 하고,
에이블데이까지 마치면 첫 과정이 끝나는 거지.
자, 딥러닝 가보자.
딥러닝 개요
딥러닝은 기본적으로 인간의 신경망 구조에서 영감을 얻었다.
익히 알려진 대로,
우리 신경망은 자극 - 반응의 과정을 연결해 주는 역할을 한다.
당연히 자극이 다르면 반응이 다른데,
파블로프의 개 실험에서 나온 것처럼 우리는 학습에 의해 이 반응을 조절한다.
곱창을 싫어하는 사람이, 어쩌다 정말 맛있게 한 번 먹고 나면 곱창 보고 침이 고일 수 있는거지.
그것처럼 딥러닝 인공신경망은,
학습에 따라 입력되는 자극(= 데이터)에 따른 연결성의 강도(= 가중치)를 갱신해 나간다.
딥 러닝 기초 코드
딥 러닝의 코드는 기본적으로 머신러닝 코드와 비슷한데,
차이점은 다음과 같다.
1. 전처리 단계 : 반드시 스케일링을 해야 한다.
2. 모델링 단계 : 모델 구조 생성 + 컴파일 → 학습 + 학습곡선 작성
예시 코드를 한 번 보자.
전처리까지는 동일하니 생략하고, 모델링 부분만 확인한다.
1. 모델 구조 생성
from keras.models import Sequential
from keras.layers import Dense, Input
from keras.backend import clear_session
# 데이터의 컬럼 수를 변수에 저장 및 확인
nfeatures = x_train.shape[1]
nfeatures
# # 메모리 정리(필수는 아님!)
clear_session()
# Sequential 타입 모델 선언
model = Sequential([Input(shape=(nfeatures,)), Dense(1)])
# 모델요약
model.summary()
주의할 부분은 모델 구조 선언 부분.
모델의 유형(Sequential)의 매개변수로 리스트 안에 들어간 Input()과 Dense()가 들어간다.
Input의 매개변수로는 shape=(nfeatures,)가 들어간다.
모델에 입력(Input)되는 데이터(x_train)의 모양(shape)을 요구한단거지.
모양이란건 몇개의 feature로 구성되었는지를 물어 보는 것이다.
1차원 형태를 타나내기에 (n,) 형태로 쓴다.
n 자리에 쓰기 위해 x_train의 컬럼 수(x_train.shape[1])를 변수 nfeatures에 저장해놓는 것.
Dense는 하나의 layer를 의미하는 것으로,
위 코드는 Input 이후 바로 Output layer를 구성하므로 Dense(1)로 충분하다.
사실 Input을 따로 생성하지 않고 Dense의 옵션으로 선언할 수도 있다.
model = Sequential(Dense(1, input=(nfeatures,))
이런 식으로.
이렇게 모델의 구조를 만든 후 summary()를 호출하면 모델의 구조 정보가 표시된다.
2. 모델 컴파일 - 학습 - 검증
# 컴파일
model.compile(optimizer='adam', loss='mse')
# 학습
model.fit(x_train, y_train)
# 예측 및 검증
pred = model.predict(x_val)
print('MSE: ',mean_squared_error(y_val, pred))
print('MAE: ',mean_absolute_error(y_val, pred))
print('MAPE: ',mean_absolute_percentage_error(y_val, pred))
나머지는 머신러닝과 똑같고, 컴파일 과정만 주의하면 된다.
model.compile()의 매개변수는 optimizer와 loss가 있다.
선형 회귀 모델의 생김새를 떠올려보자.
y = W1X1 + W2X2 + ... + WnXn + W0
model.fit()의 과정에서 모델은 가중치 Wn과 절편 W0을 조기에 랜덤으로 배정한다.
이 값과 실제값을 비교하여 오차를 계산하는데,
그때 사용할 오차 계산 방식이 loss(손실함수)이다.
손실함수(loss)는
회귀 모델 : mse
분류 모델 : cross entropy
을 사용한다.
오차가 계산되고 나면 어떻게 하냐?
당연히 오차가 줄어들도록 가중치와 절편을 조절한다.
이때 어느 방향으로 조절해야 오차가 줄어드는지를 계산하여
가중치를 업데이트하는 역할을 optimizer가 수행한다.
현재 업계 기준으로는 'adam'을 사용하는 것이 국룰인 듯 하다.
- 학습
딥러닝 과정에서 사람이 설정해 주어야 하는 값,
즉 하이퍼파라미터는 이런 것들이 쓰인다.
<.compile()>
learning rate(Adam()의 매개변수로 쓰인다.)
<.fit()>
epoch
validation split - test_size와 같은 뜻이다.
epoch는 학습 횟수, learning rate는 학습률이다.
이들은 모델의 학습 과정 전반에서 활용되므로 이해하고 있어야 한다.
모델이 처음에는 회귀선을 랜덤하게 그리기 때문에,
몇 회의 학습을 거쳐야 최적화되는지도 랜덤이다.
이때 최적 모델을 도출하기까지의 학습 횟수가 epoch과 learning rate를 알아햐 한다.
다음의 그림을 보자. 왼쪽이 모델이 회귀선을 그리는 모습,
오른쪽이 epoch에 따른 오차율의 변화다.
epoch가 10으로 고정되었을 때.
처음의 가중치(기울기)가 적절하게 설정되어서 5~6회 만에 적절한 성능(최소오차)를 뽑아낸 모습.
첫 기울기가 잘못 설정되어 10번의 학습으로는 최적의 회귀선을 만들지 못한 모습.
이런 경우에는 epoch이 더 많았어야겠지.
한편 learning rate는 모델이 매 학습마다 수정하는 정도의 크기라고 생각하면 편하다.
이를테면, epoch이 20으로 고정일 때 다음의 그림을 보자.
왼쪽은 learning rate = 0.1인 상황이다. 성큼성큼 움직여서 거의 3번만에 회귀선에 근접했다.
오른쪽은 learning rate = 0.001이다. 찔끔찔끔 움직여서 20번을 다 움직여도 모델이 데이터를 설명하지 못한다.
이까지만 보면 epoch는 무조건 많게, learning rate는 무조건 크게 하는 게 좋아 보인다.
하지만 그렇게 간단할 리 없지.
epoch는 많이 수행할수록 시간도 길어지고 메모리도 많이 필요하니,
당연히 적절한 값을 설정해야 한다.
learning rate는 작으면 위에서 본 것처럼 목표 수준에 도달할 수 있고,
너무 크면 오히려 목표 수준을 지나쳐 버리는 문제가 생긴다.
따라서 우리는 이 하이퍼파라미터들을 조절해 주어야 한다.
- 학습곡선
fit()을 다음과 같이 진행할 수 있다.
history = model.fit(x_train, y_train, epochs=20,validation_split=0.2).history
이는 학습 - 조정 - 재학습 - 조정... 의 과정을 거치는
딥러닝 과정을 통해 발생하는 오차를 기록해서 시각화하기 위한 방법이다.
이 과정을 거치면 history는 딕셔너리가 되어, 'loss', 'val_loss'등의 키를 가진다.
이 말은? 데이터프레임처럼 시각화할 수 있단거지.
plt.figure(figsize=(10,6))
plt.plot(history['loss'], label='train_err', marker = '.')
plt.plot(history['val_loss'], label='val_err', marker = '.')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend()
plt.grid()
plt.show()
위와 같이 학습 곡선을 얻음으로써 모델의 성능, 학습의 결과를 평가가능하다.
그렇기에 학습곡선을 볼 줄 알아야 한다.
epoch이 늘수록 오차가 줄고 있는데, 충분히 줄기 전에 학습이 끝난 모습.
학습곡선이 이런 모양이라면 epoch을 늘려야 한다.
train_error가 들쑥날쑥한 모습.
가중치 조절이 세밀하지 않은 것이므로 learning_rate의 보폭을 줄여 준다.
train 성능은 좋은데 val 성능이 나쁜 모습.
과적합되었다고 판단가능하다.
히든 레이어
위에서 모델의 구조를 선언할 때, 매개변수를 리스트 형식으로 입력하는 모습을 확인할 수 있었다.
이것은, 사실 Dense가 한 층이 아니라 여러 층 필요하기 때문이다.
입력층(Inout layer)과 출력층(Output layer)의 사이에 있는 Dense층들을
'히든 레이어'라고 한다.
히든 레이어는 노드의 수와 활성함수(Activation Function)을 매개변수로 취한다.
# 히든 레이어를 포함한 모델 구조 선언
model = Sequential(Input(shape=(nfeatures,)), Dense(3, activation='relu'), Dense(1))
이런식으로 중간에 Dense를 삽입하기만 하면 된다.
이때 활성함수는 히든 레이어인 경우에 보통 relu를 쓴다고 한다.
activation이 없으면 단순 선형 모델과 같아지기 때문에, 딥러닝이 성립하기 위해 반드시 필요하다.
따라서, 우리가 딥러닝 모델의 성능을 위해 동적으로 조절 가능한 요소들은 다음과 같다.
1. epochs
2. learnin_rate
3. 히든 레이어의 수
4. 히든 레이어마다의 노드 수
이것들은 함수화해서 반복시켜 보면 최적값을 찾아낼 수 있겠지.
수업때 나온 것 중 추후에 도움이 될 만한 함수들을 담아 놓자.
# epoch와 learning_rate를 조절해가며 성능을 시각화
def dl_test(ep, lr):
# 선언
model = Sequential([Input(shape=(nfeatures, )), Dense(1)])
# 컴파일
model.compile(optimizer=Adam(learning_rate=lr), loss='mse')
# 학습
history = model.fit(x_train, y_train, epochs=ep, validation_split=.2, verbose=0).history
# 시각화
plt.figure(figsize=(10,10))
plt.plot(history['loss'], marker='.', label='Train_set')
plt.plot(history['val_loss'], marker='.', label='Validation_set')
plt.xlabel('EPOCHS')
plt.ylabel('ERROR')
plt.legend()
plt.show()
# 히든 레이어의 노드 수를 조절하는 함수
def modeling_test1(node) :
# 노드 수를 입력 받아 모델 선언
clear_session()
model = Sequential([Input(shape = (nfeatures,)),
Dense(node, activation = 'relu' ),
Dense(1) ] )
#모델 컴파일
model.compile(optimizer=Adam(learning_rate = 0.01), loss = 'mse')
model.fit(x_train, y_train, epochs = 50, verbose = False)
# 예측
pred = model.predict(x_val)
mae = mean_absolute_error(y_val, pred)
# mae 결과 return
return mae
# 반복실행
nodes = [2, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150]
result = []
for n in nodes :
result.append(modeling_test1(n))
# 성능 시각화
lt.plot(nodes, result)
plt.grid()
plt.show()
# 히든 레이어의 개수를 조절하는 함수
def modeling_test2(layer) :
# 레이어 리스트 만들기
# 레이어 수 만큼 리스트에 레이어 추가
clear_session()
# 첫번째 레이어는 input_shape가 필요.
layer_list = [Input(shape = (nfeatures,)), Dense(10, activation = 'relu' )]
# 주어진 레이어 수에 맞게 레이어 추가
for i in range(2, layer) : # 첫번째 레이어, 아웃풋 레이어는 명시적으로 추가하므로 2부터 시작
layer_list.append(Dense(10 , activation = 'relu' ))
# Output Layer 추가하고 모델 선언
layer_list.append(Dense(1))
model = Sequential(layer_list)
# 레이어 잘 추가된 건지 확인하기 위해 summary 출력
print(model.summary())
model.compile(optimizer=Adam(learning_rate = 0.01), loss = 'mse')
model.fit(x_train, y_train, epochs = 50, verbose = False)
pred = model.predict(x_val)
mae = mean_absolute_error(y_val, pred)
return mae
# 반복 실행
layers = list(range(1,11))
result = []
for l in layers :
result.append(modeling_test2(l))
# 성능 시각화
plt.plot(layers, result)
plt.grid()
plt.show()