최근 컴퓨터 비전에 대해 공부중이지만 딥러닝에 대해 모르는 부분이 많아 이 부분에 대해 자세히 공부해보려한다.
딥러닝은 심층신경망 기법을 통해서 모델을 만든다.
인간의 뇌의 학습과 기억의 메커니즘을 모방하여 만든 기법이다.
Input → Dendrites(수상 돌기) → Artificial Neuron → Activation Function → Output
딥러닝은 연결을 통해 학습을 강화한다.
학습 데이터의 예측 오류를 최소화 할 수 있도록 최적화 하는 알고리즘을 통해 학습
입력 데이터가 주어졌을 때 답을 유추해 줄 수 있는 최적의 함수를 찾는 것
최적의 가중치 W값을 학습을 통해서 찾아냄. 딥러닝이 학습하는 것은 바로 가중치 W값
기존의 머신러닝은 feature들을 프로그래머들이나 모델을 만드는 사람들이 직접 뽑아야 했지만 딥러닝에서는 스스로 Feature Extraction을 생성한다.
가장 단순한 형태의 신경망.
Hidden Layer가 없이 Single Layer로 구성됨. 입력 피처들과 가중치, Activation, 출력 값으로 구성
n은 입력 Feature 개수
Activation Function
예측 값 = ~= 실제 값
값과 실제 값의 차이가 최소가 되는 weight 값을 찾는 과정이 퍼셉트론 학습하는 과정임. 퍼셉트론이 학습하는 것은 최적은 W벡터[W0,W1,W2,W3,..,Wn]의 값을 찾는 것임
경사하강법을 통해 최적의 W벡터 값을 찾는다.
위의 사진처럼 실제값과 예측 값 차이를 최소화하기 위해 반복해서 W벡터 값을 변경한다.
예측 출력과 실제값의 차이를 줄이는 방향성으로 W를 계속 변경 (중요) → 경사하강법
회귀는 여러 개의 독립변수와 한 개의 종속변수 간의 상관관계를 모델링하는 기법을 통칭한다.
머신러닝 회귀 예측의 핵심은 주어진 피처와 결정 값 데이터 기반에서 학습을 통해 최적의 회귀 계수를 찾아내는 것이다.
주택 가격이 주택의 크기로만 결정 되는 단순 선형 회귀로 가정하면 다음과 같이 주택 가격은 주택 크기에 대해 선형(직선 형태)의 관계로 표현할 수 있다.
RSS는 오류 값의 제곱을 구해서 더하는 방식.
일반적으로 미분 등의 계산을 편리하게 하기 위해서 RSS 방식으로 오류 합을 구한다. 즉 이다.
회귀에서 이 MSE는 비용(Cost)이며 W 변수(회귀 계수)로 구성되는 MSE를 비용 함수라고 한다. 비용 함수를 손실함수(loss function)라고도 한다.
많은 W 파라미터가 있는 경우에 경사 하강법은 보다 간단하고 직관적인 비용함수 최소화 솔루션을 제공
경사 하강법의 사전적 의미인 ‘점진적인 하강’ 이라는 뜻에서도 알 수 있듯이, ‘점진적으로’ 반복적인 계산을 통해 W 파라미터 값을 업데이트하면서 오류 값이 최소가 되는 W 파라미터를 구하는 방식이다.
비용 함수가 다음 그림과 같은 포물선 형태의 2차 함수라면 경사 하강법은 최초 w에서부터 미분을 적용한 뒤 미분 값이 계속 감소하는 방향으로 순차적으로 w를 업데이트 한다.
마침내 더 이상 미분된 1차 함수의 기울기가 감소하지 않는 지점을 비용 함수가 최소인 지점으로 간주하고 그때의 w를 반환한다.
가중치/절편 W 값은 손실 함수의 편미분 값을 Update하면서 계속 갱신함.
Update는 기존 W 값에 손실 함수 편미분 값을 감소 시키는 방식을 적용하되 편미분 값을 그냥 감소 시키지 않고 일정한 계수를 곱해서 감소 시키며, 이를 학습률 이라고 함.
학습률 앞에 부호가 - or +일 수 있는데 이건 예측값과 실제값의 빼기 순서에 따라 달라질 수 있다.
정해진 Iteration 수만큼 손실함수 편미분을 구하고 Weight, Bias Update 수행
# gradient_descent()함수에서 반복적으로 호출되면서 update될 weight/bias 값을 계산하는 함수.
# rm은 RM(방 개수), lstat(하위계층 비율), target은 PRICE임. 전체 array가 다 입력됨.
# 반환 값은 weight와 bias가 update되어야 할 값과 Mean Squared Error 값을 loss로 반환.
def get_update_weights_value(bias, w1, w2, rm, lstat, target, learning_rate=0.01):
# 데이터 건수
N = len(target)
# 예측 값.
predicted = w1 * rm + w2*lstat + bias
# 실제값과 예측값의 차이
diff = target - predicted
# bias 를 array 기반으로 구하기 위해서 설정.
bias_factors = np.ones((N,))
# weight와 bias를 얼마나 update할 것인지를 계산.
w1_update = -(2/N)*learning_rate*(np.dot(rm.T, diff))
w2_update = -(2/N)*learning_rate*(np.dot(lstat.T, diff))
bias_update = -(2/N)*learning_rate*(np.dot(bias_factors.T, diff))
# Mean Squared Error값을 계산.
mse_loss = np.mean(np.square(diff))
# weight와 bias가 update되어야 할 값과 Mean Squared Error 값을 반환.
return bias_update, w1_update, w2_update, mse_loss
# RM, LSTAT feature array와 PRICE target array를 입력 받아서 iter_epochs수만큼 반복적으로 Weight와 Bias를 update적용.
def gradient_descent(features, target, iter_epochs=1000, verbose=True):
# w1, w2는 numpy array 연산을 위해 1차원 array로 변환하되 초기 값은 0으로 설정
# bias도 1차원 array로 변환하되 초기 값은 1로 설정.
w1 = np.zeros((1,))
w2 = np.zeros((1,))
bias = np.zeros((1, ))
print('최초 w1, w2, bias:', w1, w2, bias)
# learning_rate와 RM, LSTAT 피처 지정. 호출 시 numpy array형태로 RM과 LSTAT으로 된 2차원 feature가 입력됨.
learning_rate = 0.01
rm = features[:, 0]
lstat = features[:, 1]
# iter_epochs 수만큼 반복하면서 weight와 bias update 수행.
for i in range(iter_epochs):
# weight/bias update 값 계산
bias_update, w1_update, w2_update, loss = get_update_weights_value(bias, w1, w2, rm, lstat, target, learning_rate)
# weight/bias의 update 적용.
w1 = w1 - w1_update
w2 = w2 - w2_update
bias = bias - bias_update
if verbose:
print('Epoch:', i+1,'/', iter_epochs)
print('w1:', w1, 'w2:', w2, 'bias:', bias, 'loss:', loss)
return w1, w2, bias
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
scaled_features = scaler.fit_transform(bostonDF[['RM', 'LSTAT']])
w1, w2, bias = gradient_descent(scaled_features, bostonDF['PRICE'].values, iter_epochs=5000, verbose=True)
print('##### 최종 w1, w2, bias #######')
print(w1, w2, bias)
predicted = scaled_features[:, 0]*w1 + scaled_features[:, 1]*w2 + bias
bostonDF['PREDICTED_PRICE'] = predicted
bostonDF.head(10)
전체 학습 데이터가 400건이라 한다면, 첫번째 iteration에서 임의의 뭉치 100건에 대하여 GD계산 및 Weight Update를 한다. 두번째는 랜덤으로 100건을 가져오지만 운이 좋지 않으면 학습된 100건을 가져올 수 있다. 이런 문제점을 해결하기위해 밑에 방식처럼 전체ㅐ 학습 데이터의 순차적인 Mini-Batch를 사용한다.
def get_update_weights_value_sgd(bias, w1, w2, rm_sgd, lstat_sgd, target_sgd, learning_rate=0.01):
# 데이터 건수
N = target_sgd.shape[0]
# 예측 값.
predicted_sgd = w1 * rm_sgd + w2*lstat_sgd + bias
# 실제값과 예측값의 차이
diff_sgd = target_sgd - predicted_sgd
# bias 를 array 기반으로 구하기 위해서 설정.
bias_factors = np.ones((N,))
# weight와 bias를 얼마나 update할 것인지를 계산.
w1_update = -(2/N)*learning_rate*(np.dot(rm_sgd.T, diff_sgd))
w2_update = -(2/N)*learning_rate*(np.dot(lstat_sgd.T, diff_sgd))
bias_update = -(2/N)*learning_rate*(np.dot(bias_factors.T, diff_sgd))
# Mean Squared Error값을 계산.
#mse_loss = np.mean(np.square(diff))
# weight와 bias가 update되어야 할 값 반환
return bias_update, w1_update, w2_update
print(bostonDF['PRICE'].values.shape)
print(np.random.choice(bostonDF['PRICE'].values.shape[0], 1))
print(np.random.choice(506, 1))
# RM, LSTAT feature array와 PRICE target array를 입력 받아서 iter_epochs수만큼 반복적으로 Weight와 Bias를 update적용.
def st_gradient_descent(features, target, iter_epochs=1000, verbose=True):
# w1, w2는 numpy array 연산을 위해 1차원 array로 변환하되 초기 값은 0으로 설정
# bias도 1차원 array로 변환하되 초기 값은 1로 설정.
np.random.seed = 2021
w1 = np.zeros((1,))
w2 = np.zeros((1,))
bias = np.zeros((1, ))
print('최초 w1, w2, bias:', w1, w2, bias)
# learning_rate와 RM, LSTAT 피처 지정. 호출 시 numpy array형태로 RM과 LSTAT으로 된 2차원 feature가 입력됨.
learning_rate = 0.01
rm = features[:, 0]
lstat = features[:, 1]
# iter_epochs 수만큼 반복하면서 weight와 bias update 수행.
for i in range(iter_epochs):
# iteration 시마다 stochastic gradient descent 를 수행할 데이터를 한개만 추출. 추출할 데이터의 인덱스를 random.choice() 로 선택.
stochastic_index = np.random.choice(target.shape[0], 1)
rm_sgd = rm[stochastic_index]
lstat_sgd = lstat[stochastic_index]
target_sgd = target[stochastic_index]
# SGD 기반으로 Weight/Bias의 Update를 구함.
bias_update, w1_update, w2_update = get_update_weights_value_sgd(bias, w1, w2, rm_sgd, lstat_sgd, target_sgd, learning_rate)
# SGD로 구한 weight/bias의 update 적용.
w1 = w1 - w1_update
w2 = w2 - w2_update
bias = bias - bias_update
if verbose:
print('Epoch:', i+1,'/', iter_epochs)
# Loss는 전체 학습 데이터 기반으로 구해야 함.
predicted = w1 * rm + w2*lstat + bias
diff = target - predicted
mse_loss = np.mean(np.square(diff))
print('w1:', w1, 'w2:', w2, 'bias:', bias, 'loss:', mse_loss)
return w1, w2, bias
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
scaled_features = scaler.fit_transform(bostonDF[['RM', 'LSTAT']])
w1, w2, bias = st_gradient_descent(scaled_features, bostonDF['PRICE'].values, iter_epochs=5000, verbose=True)
print('##### 최종 w1, w2, bias #######')
print(w1, w2, bias)
predicted = scaled_features[:, 0]*w1 + scaled_features[:, 1]*w2 + bias
bostonDF['PREDICTED_PRICE_SGD'] = predicted
bostonDF.head(10)
def get_update_weights_value_batch(bias, w1, w2, rm_batch, lstat_batch, target_batch, learning_rate=0.01):
# 데이터 건수
N = target_batch.shape[0]
# 예측 값.
predicted_batch = w1 * rm_batch+ w2 * lstat_batch + bias
# 실제값과 예측값의 차이
diff_batch = target_batch - predicted_batch
# bias 를 array 기반으로 구하기 위해서 설정.
bias_factors = np.ones((N,))
# weight와 bias를 얼마나 update할 것인지를 계산.
w1_update = -(2/N)*learning_rate*(np.dot(rm_batch.T, diff_batch))
w2_update = -(2/N)*learning_rate*(np.dot(lstat_batch.T, diff_batch))
bias_update = -(2/N)*learning_rate*(np.dot(bias_factors.T, diff_batch))
# Mean Squared Error값을 계산.
#mse_loss = np.mean(np.square(diff))
# weight와 bias가 update되어야 할 값 반환
return bias_update, w1_update, w2_update
# batch_gradient_descent()는 인자로 batch_size(배치 크기)를 입력 받음.
def batch_random_gradient_descent(features, target, iter_epochs=1000, batch_size=30, verbose=True):
# w1, w2는 numpy array 연산을 위해 1차원 array로 변환하되 초기 값은 0으로 설정
# bias도 1차원 array로 변환하되 초기 값은 1로 설정.
np.random.seed = 2021
w1 = np.zeros((1,))
w2 = np.zeros((1,))
bias = np.zeros((1, ))
print('최초 w1, w2, bias:', w1, w2, bias)
# learning_rate와 RM, LSTAT 피처 지정. 호출 시 numpy array형태로 RM과 LSTAT으로 된 2차원 feature가 입력됨.
learning_rate = 0.01
rm = features[:, 0]
lstat = features[:, 1]
# iter_epochs 수만큼 반복하면서 weight와 bias update 수행.
for i in range(iter_epochs):
# batch_size 갯수만큼 데이터를 임의로 선택.
batch_indexes = np.random.choice(target.shape[0], batch_size)
rm_batch = rm[batch_indexes]
lstat_batch = lstat[batch_indexes]
target_batch = target[batch_indexes]
# Batch GD 기반으로 Weight/Bias의 Update를 구함.
bias_update, w1_update, w2_update = get_update_weights_value_batch(bias, w1, w2, rm_batch, lstat_batch, target_batch, learning_rate)
# Batch GD로 구한 weight/bias의 update 적용.
w1 = w1 - w1_update
w2 = w2 - w2_update
bias = bias - bias_update
if verbose:
print('Epoch:', i+1,'/', iter_epochs)
# Loss는 전체 학습 데이터 기반으로 구해야 함.
predicted = w1 * rm + w2*lstat + bias
diff = target - predicted
mse_loss = np.mean(np.square(diff))
print('w1:', w1, 'w2:', w2, 'bias:', bias, 'loss:', mse_loss)
return w1, w2, bias
predicted = scaled_features[:, 0]*w1 + scaled_features[:, 1]*w2 + bias
bostonDF['PREDICTED_PRICE_BATCH_RANDOM'] = predicted
bostonDF.head(10)
# batch_gradient_descent()는 인자로 batch_size(배치 크기)를 입력 받음.
def batch_gradient_descent(features, target, iter_epochs=1000, batch_size=30, verbose=True):
# w1, w2는 numpy array 연산을 위해 1차원 array로 변환하되 초기 값은 0으로 설정
# bias도 1차원 array로 변환하되 초기 값은 1로 설정.
np.random.seed = 2021
w1 = np.zeros((1,))
w2 = np.zeros((1,))
bias = np.zeros((1, ))
print('최초 w1, w2, bias:', w1, w2, bias)
# learning_rate와 RM, LSTAT 피처 지정. 호출 시 numpy array형태로 RM과 LSTAT으로 된 2차원 feature가 입력됨.
learning_rate = 0.01
rm = features[:, 0]
lstat = features[:, 1]
# iter_epochs 수만큼 반복하면서 weight와 bias update 수행.
for i in range(iter_epochs):
# batch_size 만큼 데이터를 가져와서 weight/bias update를 수행하는 로직을 전체 데이터 건수만큼 반복
for batch_step in range(0, target.shape[0], batch_size):
# batch_size만큼 순차적인 데이터를 가져옴.
rm_batch = rm[batch_step:batch_step + batch_size]
lstat_batch = lstat[batch_step:batch_step + batch_size]
target_batch = target[batch_step:batch_step + batch_size]
bias_update, w1_update, w2_update = get_update_weights_value_batch(bias, w1, w2, rm_batch, lstat_batch, target_batch, learning_rate)
# Batch GD로 구한 weight/bias의 update 적용.
w1 = w1 - w1_update
w2 = w2 - w2_update
bias = bias - bias_update
if verbose:
print('Epoch:', i+1,'/', iter_epochs, 'batch step:', batch_step)
# Loss는 전체 학습 데이터 기반으로 구해야 함.
predicted = w1 * rm + w2*lstat + bias
diff = target - predicted
mse_loss = np.mean(np.square(diff))
print('w1:', w1, 'w2:', w2, 'bias:', bias, 'loss:', mse_loss)
return w1, w2, bias
from tensorflow.keras.layers import Dense
from tensorflow.keras.models import Sequential
from tensorflow.keras.optimizers import Adam
model = Sequential([
# 단 하나의 units 설정. input_shape는 2차원, 회귀이므로 activation은 설정하지 않음.
# weight와 bias 초기화는 kernel_inbitializer와 bias_initializer를 이용.
Dense(1, input_shape=(2, ), activation=None, kernel_initializer='zeros', bias_initializer='ones')
])
# Adam optimizer를 이용하고 Loss 함수는 Mean Squared Error, 성능 측정 역시 MSE를 이용하여 학습 수행.
model.compile(optimizer=Adam(learning_rate=0.01), loss='mse', metrics=['mse'])
# Keras는 반드시 Batch GD를 적용함. batch_size가 None이면 32를 할당.
model.fit(scaled_features, bostonDF['PRICE'].values, batch_size=30, epochs=1000)
predicted = model.predict(scaled_features)
bostonDF['KERAS_PREDICTED_PRICE_BATCH'] = predicted
bostonDF.head(10)
매우 많은 파라미터를 가진 복잡한 함수를 가졌을때 Local Minimum에 빠질 수 있음.
자료 출처: 딥러닝 CNN 완벽가이드 (권철민)