티스토리 뷰
항상 강조하지만, 기본적으로 CRISP-DM이 깔고 들어가야 한다.
모델 튜닝을 해서, 성능을 올려서, 프로젝트의 시작과 끝에서 진짜로 비즈니스 문제가 해결되었는가? 이걸 보아야 한다.
그게 실제 성과 지표다.
성능 1%를 올리는 게 비즈니스 문제 해결에 의미가 없다면, 그저 헛고생이 된다.
머신러닝에서
모든 셀은 값이 되어야 하고, -> Nan 처리(삭제, 채우기)
그 값은 모두 숫자여야 한다. -> 가변수화(범주-> 수치형으로)
값의 범위가 일치해야 한다. -> 스케일링(minmax,robust)
이건 딥러닝도 크게 다르지 않다.
knn과 svm은
svm은 요즘에 딥러닝과 접목해서 핫하다.
커널 변형을 시킨다.
딥러닝은 기본적으로 스케일링을 요구한다.
shape의 복습
정형 데이터에서 행은 내가 다루는 데이터가 몇 건 있느냐의 설명이다.
1차,2차원 데이터는 그렇다.
모든 모델의 성능은 오차(error)를 통해 계산된다.
모델링은 train에서 발생된 error를 최소화 하는 모델 생성이고,
모델 튜닝은 validation error를 최소화 하는 모델을 선정하는 것이다.
가중치(파라미터)의 의미
선형 회귀에서 타깃값= 2*온도 +10 라는게 있다면,
쉽게 이야기 했을 때, 온도가 1 올라가면 y=12가 발생한다는 의미가 된다.
온도라는 정보가 들어가면, 기울기(가중치, 여기서는 2) + 편향(10)이 발생해서, y=12라는 최종적인 함수가 된다.
이게 바로 머신러닝의 기본 원리이다.
그러면 만약에 온도와 습도가 같이 더해지면?
여기서 선형 대수학이 들어간다.
2*온도+1.5*습도+20 =y
라면, 여기서 이 함수를 이용해서 결정 경계를 만드는 게 딥러닝이다.
그러므로 딥러닝에서 모델링은, 실제와 오차를 최소화하는 가중치(파라미터)를 찾는다는 것이다.
파라미터를 잘 찾는 게 모델링이다.
기본적인 딥러닝 구조
NaN 조치 -> 가변수화 -> 스케일링(머신러닝은 선택, 딥러닝은 필수)
모델링 : 모델 구조를 만들고, 컴파일. 그리고 학습과 학습 곡선을 그린다.
그리고 예측 및 검증으로 마무리
Colab 사용 방법
ipynb 올리고, 오른쪽 클릭 -> 연결앱 더보기
colaboratory 검색
눌러서 설치
그래서 연결 앱에서 claboratory로 들어간다.
런타임 -> 런타임 유형 변경에서 하드웨어 가속기 GPU, TPU 쓸 수 있다.
도구-> 설정에서 테마를 다 바꿀 수 있다.
colab에서의 자동완성은 ctrl+스페이스바다.
검증함수에 대한 고찰
squared =False면 제곱을 안했기 떄문에 RMSE이다.
1-MAPE라고 하면, 평균 정확도라고 쓰기도 한다.
standard scaler = 평균이 0이고, 표준편차가 1인 분포로 바꿔준다.
이상치가 심할 떄 많이 쓴다.
딥러닝에서는 minmax를 그냥 많이 쓰긴 한다.
keras에서 딥러닝에 필요한 함수 불러오기
from keras.models import Sequential
from keras.layers import Dense
from keras.backend import clear_session
feature= x_train.shape[1] #컬럼 개수
feature
피처의 개수 = 즉, 칼럼의 개수를 불러온다.
clear_session()
model = Sequential( Dense(1, input_shape = (feature,)) )
model.summary()
clear_sesson()은 메모리 정리를 해준다.
seqeuntial 함수 안에 1, input_shape = (features,)를 넣어준다.
이렇게 넣는 이유는, 아웃풋은 1, 그리고 들어가는 뉴런은 3개이기 때문이다.
이 sequential을 여러개 쌓으면 은닉층이 된다.
컴파일
# 컴파일
model.compile(optimizer='adam', loss='mse')
모델을 선언하고, 그 정보를 토대로 컴파일을 한다.
학습, 예측, 평가
model.fit(x_train, y_train)
pre = model.predict(x_t)
print(f'RMSE : {mean_squared_error(y_t, pre, squared=False)}')
print(f'MAE : {mean_absolute_error(y_t, pre)}')
print(f'MAPE : {mean_absolute_percentage_error(y_t, pre)}')
그 이후는 머신러닝과 완전히 똑같다.
딥러닝의 개념 - 가중치
1~3월의 판매량으로 4월의 판매량을 예측하는 방법
가장 쉬운 방법은 평균.
그러나, 1월 *w1 + 2월 * w2 + 3월 *w3 로 표현할 수도 있다.
이때, w1,w2,w3이 얼마나 중요한가?를 생각하는 게 가중치이다.
최적의 가중치를 만드는 방법 -> 조금씩 가중치를 조절하면서, 오차가 줄어드는지를 확인한다.
지정한 횟수 만큼(epoch), 혹은, 더 이상 오차가 줄지 않을 때까지(early stopping).
함수 하나는 가중치 w1과, 편향 b로 만들어진다.
y= w1+b
여러 개의 함수를 표현하려면?
순전파와 역전파가 이루어지는 과정
1. 가중치에 초기값을 할당 - 초기값이 랜덤으로 결정(성능 평가가 달라지는 이유)
2. 각 함수에 대해 결과( ŷ)를 추출.
3. 오차를 계산 : 예시 = mse : 시그마 (y-ŷ)^2/n
- 손실함수, 코스트 함수 등으로도 불린다.
4.오차를 줄이는 방향으로 가중치를 조정한다(여기서 옵티마이저가 쓰인다.)
- 아직까지는 adam이 거의 국룰이다.
- 얼마만큼 조절하는가? learning_rate(하이퍼 파라미터-사람이 개입할 여)
5. 다시 1로 가서 반복(epoch - 역시나 하이퍼 파라미터)
에폭은 전체 훈련 데이터를 한 번 모델에 입력한 것을 의미하며, 반복 횟수는 에폭 당 훈련 이터레이션 수
1~3까지가 순전파(forward propagation). 그리고 4가 역전파(back propagation)이다.
뉴런이 많아지만, 각각의 파라미터를 예측하려고 엄청난 컴퓨팅 파워가 필요하다.
chatgpt의 경우 1750억개
전이학습이 필요하다.
순전파 이후에는 최종적으로는 활성화 함수로 판단을 한다. (sigmoid, Lelu, Leaky Lelu 등)
활성화 함수에서 기울기 소실이 일어나지 않도록 하는 게 중요하다.
시그모이드(sigmoid) 활성화 함수 사용 시
- 시그모이드 함수는 입력값이 매우 작거나 매우 클 때 기울기가 0에 가까워지는 문제가 있습니다.
- 이로 인해 역전파 시 가중치 업데이트가 제대로 이루어지지 않을 수 있습니다.
매우 깊은 신경망(very deep neural network) 사용 시
- 네트워크가 깊어질수록 기울기 값이 레이어를 거치면서 점점 작아지거나 폭발하는 문제가 발생할 수 있습니다.
- 이는 초기 레이어의 가중치 업데이트를 어렵게 만듭니다.
RNN(Recurrent Neural Network)의 긴 시퀀스 입력 시
- RNN은 이전 단계의 은닉 상태를 현재 단계에 전달하는 구조인데, 이 과정에서 기울기 값이 시간 단계가 깊어질수록 점점 작아지는 현상이 발생합니다.
학습 절차를 눈으로 확인하기
import matplotlib.pyplot as plt # Matplotlib를 사용하여 시각화합니다.
import numpy as np
import seaborn as sns
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.callbacks import ModelCheckpoint
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.backend import clear_session
# visualize 함수 정의
def visualize(epoch, learning_rate):
clear_session()
model = Sequential([Dense(1, input_shape=(1,))])
model.compile(loss='mse', optimizer=Adam(learning_rate=learning_rate))
mcp = ModelCheckpoint(filepath='/content/{epoch:d}.h5',
monitor='val_loss', save_best_only=False, save_weights_only=True)
history = model.fit(x_train_s, y_train_s, verbose=0, epochs=epoch, callbacks=[mcp]).history
coef, intercept = [], []
for i in range(epoch):
file = f'/content/{i + 1}.h5'
model.load_weights(file)
coef.append(np.array(model.weights[0])[0, 0])
intercept.append(np.array(model.weights[1])[0])
plt.figure(figsize=(20, 8))
plt.subplot(1, 2, 1)
sns.scatterplot(x=x_train_s.reshape(-1,), y=y_train_s, alpha=.5)
plt.grid()
plt.xlabel('lstat')
for i in range(epoch):
x = np.linspace(0, 1, 10)
y = coef[i] * x + intercept[i]
plt.plot(x, y, 'r--')
v = 1.005
plt.text(v, coef[i] * v + intercept[i], f'ep:{i + 1}', color='r')
plt.subplot(1, 2, 2)
plt.plot(range(1, epoch + 1), history['loss'], label='train_err', marker='.')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend()
plt.grid()
plt.show()
visualize(ep =5 , lr = 0.01)
epoch와 학습률이 변함에 따라서, fit이 어떻게 변하는지에 대한 그래프를 볼 수 있다.
학습률이 클수록 빠르게 수렴. 그러나 정확도가 떨어진다.
학습률이 작으면 느리게 수렴한다.
초기값이 랜덤이므로, 할때마다 시작 지점이 다르다.
가우시안 분포로부터 난수로 뽑는다.
수학적으로 어디에서 뽑아도 성능에 지장이 없다. 옵티마이저 덕분.
딥러닝에서도 역시 너무 많이 학습하면 과적합이 일어난다.
early stopping으로 해결할 수 있다.
딥러닝 전처리 - 스케일링(반드시 해야 한다)
방법 1 : normalization(정규화)
방법 2: 표준화
모든 값을, 평균=0, 표준편차 =1 로 변환
이상치가 심할 때 많이 쓴다.
둘 중에 하나만 하거나, 둘 다 해주는 경우도 있다.
방식은 표준화 후 정규화
프로세스
각 단계(task)는 이전 단계의 output을 input으로 받아 처리 한 후에 다음 단계로 전달한다.
input -> task1->task2->task3 -> output
딥러닝의 구조도 이와 똑같다. input은 train data이고, 각 task가 레이어라고 생각하면 된다.
요즘엔 input을 레이어라고 부르지 않는다.
bottle neck 구조로 은닉층을 만든다.
최종적인 output layer(은닉층에서의 마지막 값)이 아닌 레이어는 전부 은닉층(hidden layer)다.
딥러닝 코드 - Dense
model = Sequential([ Dense(1, input_shape = (3,)) ])
Dense( 노드, input_shape = ( , ))
input_shape는 분석단위에 대한 shape다.
1차원 : (feature 수, ) - 정형 데이터인 경우
2차원 : (rows, columns)
이때는 x_train이
[
[ [1,2,3,4], [5,6,7,8], [9,10,11,12] ] ,
[ [1,2,3,4], [5,6,7,8], [9,10,11,12] ], ....
와 같이, 아예 3*4 형태의 가진다.
예시 : 센서 데이터.
3개의 센서에서 4개의 다른 특성(온도, 습도, 압력, 소음 등)
주식 데이터(시가,저가,고가,종가)
자연어 처리(문장길이가 3, 임베딩 차원이 4)
일때 사용한다.
헷갈리지 말아야 할 것.
- 이미지의 경우, input_shape = (height, width, channels)
- 예를 들어 28x28 그레이스케일 이미지라면 input_shape = (28, 28, 1)
3차원(시퀀스 데이터) :
- input_shape = (timesteps, feature_count)
- 를 들어 10개 timestep, 50개 feature라면 input_shape = (10, 50)
(4,3) 에서 Param = (3,)이면, 4행이므로 파라미터의 개수가 4이다.
기저 벡터를 계산하기 위한 파라미터의 개수이다.
딥러닝 코드 - compile
컴파일 : 기계어로 변환
딥러닝에서의 컴파일은 오차 함수와 옵티마이저를 지정해주는 용도.
keras 기본 코드 로딩
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.metrics import *
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense
from keras.backend import clear_session
from keras.optimizers import Adam
분석단위 shape
feature = x_train.shape[1]
feature
clear_session()
model = Sequential([Dense(1, input_shape=(feature, ))])
model.summary() #요약
features가 3이라는 말은, 다시 한번 강조하지만 3개의 뉴런이 하나의 input으로 들어간다는 말과 똑같다.
model.compile(optimizer=Adam(learning_rate=0.1),loss = 'mse')
his = model.fit(x_train, y_train, epochs = 50, validation_split=0.2).history
learning_rate와 epochs, validation_split까지 지정해줄 수 있다.
validation은 학습을 하는 게 아니라 눈으로 보려고 하는 것.
.history의 로그를 떼서 history라는 이름의 변수에 저장한다.(loss와 validation loss가 저장된다)
# plot 함수로 그리기
def his(his):
plt.figure(figsize=(10,6))
plt.plot(hi['loss'], label='train_error')
plt.plot(his['val_loss'], label='val_error')
plt.ylabel('Loss_of_his')
plt.xlabel('Ep')
plt.legend()
plt.grid()
plt.show()
his(his)
학습률이 클수록 빠르게 수렴. 그러나 정확도가 떨어진다.
학습률이 작으면 느리게 수렴한다고 했다.
그럼 이걸 어떤 걸 보고 조절을 하는가? -> 결국에는 loss를 봐야 한다.
딥러닝의 학습곡선
바람직한 곡선은 epoch가 증가하면서 loss가 큰 폭으로 축소한다.
점차 loss 감소 폭이 줄어들면서 수렴한다.
loss가 줄어들기는 하나, 들쑥날쑥한 경우는 learning rate 줄이기.
val_loss가 줄어들다가 다시 상승(과적합)
Epochs와 learning_rate를 둘 다 조절
은닉층
파라미터를 한 번 더 생각해서 모델 설계.
keras에서 히든 레이어 추가는 리스트 [ ] 로 입력한다.
[ Dense(2, input_shape= (nfeatures,) , activation = 'relu',
Dense(1)]
첫번째 레이어는 input_shape가 무조건 있어야 한다.
마지막 레이어는 Dense (1) 이면, 앞의 것과 알아서 연결해준다.
히든레이어는 활성화 함수가 필요하다. 보통은 relu를 쓴다.
일반적인 히든 레이어라면 거의 국룰로 쓴다.
validation_split은 epoch 마다 섞어서 뗴어낸다.
dense (Dense) | (None, 8) | 104 |
dense_1 (Dense) | (None, 1) | 9 |
이렇게 설계하기 위해서는
[ Dense(8, input_shape=(nfeatures,),activation='relu'),
Dense(1)]
이렇게 넣어야 한다.
즉, 앞이 output의 값이고, 뒤가 (nfeature+1) * output의 개수 = 파라미터의 개수다.
은닉층을 더 추가하려면 이렇게만 하면 된다. input_shape은 첫번째 레이어에만 넣는다.
혹은 model.add를 쓰면 편하다.
[ Dense(8, input_shape =(nfeatures,),activation='relu' ),
Dense(4, activation='relu'),
Dense(1)]
model = Sequential()
model.add(SimpleRNN(8, input_shape=(x_train.shape[1], 1),activation = 'relu')) # 2차원 입력
model.add(SimpleRNN(4,activation = 'relu')) # 2차원 입력
model.add(Dense(1))
노드와 파라미터 개수를 찾는 keras gridsearch 방법이 있다.
라인차트는 x축이 시간축, y축이 값.
라인차트보다 권장하는 건 scatterplot
def visualization_result(y_val,pred):
sns.lineplot(y_val.values, label='valid train')
sns.lineplot(pred.reshape(pred.shape[0],), label='pred')
plt.legend(loc='upper right')
plt.show()
visualization_result(y_val,pred)
def visualization_result(y_val,pred):
sns.scatterplot(x= pred.reshape(-1), y= y_val.values, label='valid train')
plt.xlabel='pred'
plt.ylabel='y_true'
plt.legend(loc='upper right')
plt.show()
visualization_result(y_val,pred)
활성화 함수 (Activation Function)
현재 레이어의 결과값을 다음 레이어로 어떻게 전달할지를 결정 및 변환 해주는 함수이다.
만약에 없다면, 히든 레이어를 아무리 추가해도 그냥 선형회귀 모델이 된다.
y1= ax+b를 전달 -> y2 = a2 * y1 +b2
==> y2 = a2 *(ax+b) +b2 {그냥 선형 모델임 : 단조 증가나 감소를 한다}
XOR 문제
대각선 꼭지점에 위치한 다른 색의 점들을 직선 하나로 분리할 수 없다.
그래서 비선형 모델을 만들고 싶다면, 선형함수를 일부러 비선형 함수로 변환시킨다.
은닉층에서는 선형함수를 비선형 함수로 변환한다.
output layer에서는 결과값을 다른 값으로 변환해주는 역할
주로 분류 모델에서 필요하다.
은닉층 국룰 : sigmoid, tanh, relu
relu는 은닉층에서 activiation으로 쓸 때 경험적으로 좋다.
그 외에는 leaky relu, maxout, elu 등이 있다.
옵티마이저는
히든 레이어의 노드 수
보통은 점차 줄여간다. 그러나 정답은 없다.
늘렸다가 줄일 수도 있다.
rf의 그래프와 똑같다. max_depth의 tree 개수를 늘려가면 과적합이 되듯이, 딥러닝도 똑같다.
layer가 늘어난다고 해서 항성 성능이 늘어나는 건 아니지만, 은닉층이 많아지면 성능이 좋아지는 건 맞다.
대체로는 input의 수가 있으면, 노드 수를 줄여가는 게 원래는 정석이다.
그러나 bottle neck 구조를 가지는 것도 좋다.
한 권위자 할아버지 : 성능이 더이상 좋아지지 않을 떄까지 늘려라.
for loop로 반복 실행
import matplotlib.pyplot as plt # Matplotlib를 사용하여 시각화합니다.
from sklearn.metrics import mean_absolute_error
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.backend import clear_session
def custom_modeling_test(node):
# 노드 수를 입력 받아 모델 선언
clear_session()
model = Sequential([Dense(node, input_shape=(nfeatures,), 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(custom_modeling_test(n))
plt.plot(nodes, result)
plt.grid()
plt.show()
레이어 추가 실험
layer도 list 이므로 추가할 수가 있다.
from sklearn.metrics import mean_absolute_error
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.backend import clear_session
def custom_modeling_test(layer_count):
# 레이어 리스트 만들기
# 레이어 수 만큼 리스트에 레이어 추가
clear_session()
# 첫번째 레이어는 input_shape가 필요.
layer_list = [Dense(10, input_shape=(nfeatures,), activation='relu')]
# 주어진 레이어 수에 맞게 레이어 추가
for i in range(2, layer_count): # 첫번째 레이어, 아웃풋 레이어는 명시적으로 추가하므로 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(custom_modeling_test(l))
plt.plot(layers, result)
plt.grid()
plt.show()
mape가 엄청 큰 것은 분모가 커서 그렇다.
즉, 0에 가까울수록 무한대로 뻗어나가는 문제가 있다.
요약
첫번째 레이어는 인풋을 받고, 아웃풋 레이어의 노드 수는 1이다.
활성화 함수는 히든 레이어에 필요하다.