- 참고 : 핸즈온 머신러닝 2판
모델 훈련
- 직접 계산할 수 있는 공식을 사용하여 훈련 세트에 가장 잘 맞는 모델 파라미터를 해석적으로 구함
- 경사 하강법(GD)라고 불리는 반복적인 최적화 방식을 사용하여 모델 파라미터를 조금씩 바꾸면서 비용 함수를 훈련 세트에 대해 최소화 시킴
선형 회귀
- 선형이지만 특성이 많아지면 고차원이 됨
- 파라미터 기준은 MSE 최소 하는 파라미터 선정
입력 특성의 가중치 합 + 편향(절편, x0) 이라는 상수를 더해 예측을 생성
정규 방정식
- 공식
- 비용함수를 최소하 하는 𝜽 값을 찾기위한 해석적 방법
정규 방정식 예시
import numpy as np
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
plt.plot(X, y, "b.")
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.axis([0, 2, 0, 15])
plt.show()
X_b = np.c_[np.ones((100, 1)), X] # 모든 샘플에 x0 = 1을 추가합니다. (X0 = 1)
theta_best = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y) # np.linalg.inv로 역행렬 계산
>> array([[4.21509616],
[2.77011339]])
# 구한 theta_best를 통해서 예측 수행
X_new = np.array([[0], [2]])
X_new_b = np.c_[np.ones((2, 1)), X_new] # 모든 샘플에 x0 = 1을 추가합니다.
y_predict = X_new_b.dot(theta_best)
y_predict
>> array([[4.21509616],
[9.75532293]])
- 결과 시각화
plt.plot(X_new, y_predict, "r-")
plt.plot(X, y, "b.")
plt.axis([0, 2, 0, 15])
plt.show()
!
sklearn 선형 회귀
- 가중치(coef) , 편향(intercept) 호출 가능
- LinearRegression 클래스는 scipy.linalg.lstsq() 함수(최소 제곱)을 사용
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(X, y)
lin_reg.intercept_, lin_reg.coef_
>> (array([4.21509616]), array([[2.77011339]]))
lin_reg.predict(X_new)
>> array([[4.21509616],
[9.75532293]])
# scipy.linalg.lstsq() or np.linalg.lstsq() 로 표현하면
theta_best_svd, residuals, rank, s = np.linalg.lstsq(X_b, y, rcond=1e-6)
theta_best_svd
>> array([[4.21509616],
[2.77011339]])
- 이때
np.linalg.lstsq()
함수는 유사 역행렬을 계산함np.linalg.pinv()
을 사용해서 유사 역행렬을 직접 계산 가능유사 역행렬
은 역행렬을 계산할수 없을때 역행렬과 유사한 행렬을 계산
# 유사 역행렬 구하는 방법
np.linalg.pinv(X_b).dot(y)
>> array([[4.21509616],
[2.77011339]])
- 유사역행렬 자체는
특이값 분해(SVD)
라 부르는 표준 행렬 분해 기법을 사용해 계산 - 특성이 중복되어 역행렬이 없을때 정규방정식이 작동하지 않지만
유사역행렬
은 항상 존재
경사 하강법(GD)
- 여러 종류의 문제에서 최적의 해법을 찾을 수 있는 일반적인 최적화 알고리즘
- 비용함수 최적화를 위해 반복수행을 통해 파라미터 조정
- 현 상태의 기울기가 감소하는 방향으로 진행하다가 0인 지점이 최솟값
- 스텝의 크기는 학습률 파라미터로 결정됨
- 학습률이 너무 작으면 수렴을 위한 반복이 많아지고 시간소요가 커짐
- 학습률이 너무 크면 큰 값으로 발산할 위험이 존재
학습 스텝은 최솟값에 가까워 질수록 줄어듦
위 그림과 같이 왼쪽에서 시작하면 지역 최솟값에 빠질 위험이 크고, 오른쪽에서 시작하면 평탄한 부분을 지나는 동안 학습이 오래 걸리며 조기 종료되어 전역 최솟값에 이르지 못할수 있음
MSE 비용함수
- 선형 회귀를 위한 MSE 비용 함수는 곡선에서 어떤 두 점을 선택해 선을 그어도 곡선을 가로지르지 않는
볼록함수
- 지역 최솟값 X , 전역 최솟값 0
- 연속된 함수이고 기울기가 변하지 않음
경사 하강법이 전역 최솟값에 가깝게 접근할 수 있다는 것을 보장
특성 스케일
- 왼쪽이 특성 스케일 적용, 오른쪽은 미적용
- 경사 하강법을 사용할 때는 반드시 모든 특성이 같은 스케일을 갖도록 만들어야함, 그렇지 않으면 같은 결과 도달까지 오랜 시간 소요
배치 경사 하강법
- 경사 하강법 구현시 각 모델 파라미터에 대해 비용 함수의 기울기를 계산
- 모델 파라미터가 조금 변경될 때 비용 함수가 얼마나 변경되는지 계산 <- 편도함수
- 비용함수의 MSE 그레이디언트 벡터는 비용함수의 편도함수를 모두 가지고있음
매 경사 하강법 스텝에서 전체 훈련 세트 X에 대해 계산 그래서 배치 경사 하강법
매우 큰 훈련 세트에서는 아주 느림, 특성수에 민감하지 않아서 수백만 특성에서는 정규방적식, SVD 보다 경사 하강법이 빠름
- 위로 향하는 그레이디언트 벡터가 구해지면 반대 방향인 아래로 이동 theta - MSE(theta) 를 의미, 내려가는 스텝의 크기를 결정하기 위해 n : 학습률 사용
- 공식
eta = 0.1 # 학습률 n_iterations = 1000 m = 100 theta = np.random.randn(2,1) # 랜덤 초기화 for iteration in range(n_iterations): gradients = 2/m * X_b.T.dot(X_b.dot(theta) - y) theta = theta - eta * gradients theta >> array([[4.21509616], [2.77011339]])
- 학습률에 따른 경사 하강법 시각화
- 왼쪽의 경우 학습률이 너무 낮고 최적점 도달까지 시간소요 큼
- 가운데는 적절한 학습률 빠르게 최적점 도달
- 오른쪽은 너무 큰 학습률로 최적점에 멀어져 발산
- 공식
적절한 학습률을 찾기 위해서는 그리드 탐색을 사용, 그리드 탐색은 수렴하는데 너무 오래 걸리는 모델을 막기위해 반복 횟수를 제한해야 함
간단한 방법은 반복횟수 크게 지정하고 그레디언트 벡터가 아주 적어지면 중지
확률적 경사 하강법
- 배치 경사 하강법은 매 스텝마다 전체 훈련 세트를 사용해 그레디언트를 계산함, 훈련 세트 크면 느림
- 확률적 경사 하강법은 매 스텝에서 한 개의 샘플을 무작위로 선택하고 그 셈플에 대한 그레이디언트를 계산
- 매 반복마다 계산하는 데이터가 매우 적기 때문에 한 번에 하나의 샘플 처리
- 무작위 적인 특성 때문에 배치 경사 하강법보다 훨씬 불안정
- 비용함수가 최솟값에 다다를 때까지 요동치면서 감소
- 요동을 치기때문에 전역을 지나칠수 있음
점진적으로 학습률을 감소시켜서 요동치는 문제를 해결
시작할때는 학습률을 크게하고 점차 작게 줄여서 알고리즘이 전역으로 수렴하도록 함
학습 스케줄을 사용한 확률적 경사 하강법
n_epochs = 50
t0, t1 = 5, 50 # 학습 스케줄 하이퍼파라미터
def learning_schedule(t):
return t0 / (t + t1)
theta = np.random.randn(2,1) # 랜덤 초기화
for epoch in range(n_epochs):
for i in range(m):
if epoch == 0 and i < 20:
y_predict = X_new_b.dot(theta)
style = "b-" if i > 0 else "r--"
plt.plot(X_new, y_predict, style)
random_index = np.random.randint(m)
xi = X_b[random_index:random_index+1]
yi = y[random_index:random_index+1]
gradients = 2 * xi.T.dot(xi.dot(theta) - yi)
eta = learning_schedule(epoch * m + i)
theta = theta - eta * gradients
theta_path_sgd.append(theta)
plt.plot(X, y, "b.")
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.axis([0, 2, 0, 15])
save_fig("sgd_plot")
plt.show()
theta
>> array([[4.21076011],
[2.74856079]])
- 위에서는 셈플을 무작위로 선택, 셈플마다 뽑힐수도 있고 안뽑힐수도 있음
sklearn SGDRegressor
- 기본값으로 제곱 오차 비용 함수를 최적화 함
from sklearn.linear_model import SGDRegressor
sgd_reg = SGDRegressor(max_iter=1000, tol=1e-3, penalty=None, eta0=0.1, random_state=42)
sgd_reg.fit(X, y.ravel())
sgd_reg.intercept_, sgd_reg.coef_
>> array([4.24365286]), array([2.8250878])
미니배치 경사 하강법
- 각 스텝에서 전체 훈련 세트(배치 경사하강법)이나 하나의 샘플(확률적 경사 하강법)을 기반으로 그레디언트를 계산하는 것이 아니라 미니배치라 부르는 임의의 작은 샘플 세트에 대해 그레디언트를 계산.
- gpu 사용가능
- SGD 보다 덜 불규칙하게 움직임, SGD보다 최솟값에 가까울수 있고 반대로 지역 최솟값이 될수 있음
배치 경사 하강법이 실제로 최솟점에 도달, 확률적과 미니배치는 주변에 수렴
확률적 경사하강법과 미니배치 경사 하강법 둘다 적절한 학습 스케줄을 사용하면 최솟값에 도달
선형회귀를 사용한 알고리즘 정리
다항 회귀
- 다항회귀란 데이터들간의 형태가 비선형 일때 데이터에 각 특성의 제곱을 추가해주어서 특성이 추가된 비선형 데이터를 선형 회귀 모델로 훈련시키는 방법
다항회귀 예시
import numpy as np
import numpy.random as rnd
np.random.seed(42)
m = 100
X = 6 * np.random.rand(m, 1) - 3
y = 0.5 * X**2 + X + 2 + np.random.randn(m, 1)
plt.plot(X, y, "b.")
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.axis([-3, 3, 0, 10])
save_fig("quadratic_data_plot")
plt.show()
- 데이터에 일부 잡음이 포함되어있어서 sklearn의 PolynomialFeatures를 사용해 훈련데이터로 변환
- 훈련 세트에 각 특성을 제곱하여 새로운 특성을 만듬
from sklearn.preprocessing import PolynomialFeatures
poly_features = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly_features.fit_transform(X)
X[0]
>> array([-0.75275929])
X_poly[0]
>> array([-0.75275929, 0.56664654])
x_poly는 원래의 특성 X와 특성의 제곱을 포함
lin_reg = LinearRegression()
lin_reg.fit(X_poly, y)
lin_reg.intercept_, lin_reg.coef_
>> array([1.78134581]), array([[0.93366893, 0.56456263]])
X_new=np.linspace(-3, 3, 100).reshape(100, 1)
X_new_poly = poly_features.transform(X_new)
y_new = lin_reg.predict(X_new_poly)
plt.plot(X, y, "b.")
plt.plot(X_new, y_new, "r-", linewidth=2, label="Predictions")
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.legend(loc="upper left", fontsize=14)
plt.axis([-3, 3, 0, 10])
plt.show()
특성이 여러 개일 때 다항 회귀는 이 특성 사이의 관계를 찾음
PolynomialFeatures가 주어진 차수까지 특성 간의 모든 교차항을 추가함
학습 곡선
- 고차 다항 회귀를 적용하면 보통의 선형 회귀에서보다 더 훈련 데이터에 잘 맞출려고 함
선형모델, 2차 회귀 모델, 300차 다항회귀 모델 비교
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
for style, width, degree in (("g-", 1, 300), ("b--", 2, 2), ("r-+", 2, 1)):
polybig_features = PolynomialFeatures(degree=degree, include_bias=False)
std_scaler = StandardScaler()
lin_reg = LinearRegression()
polynomial_regression = Pipeline([
("poly_features", polybig_features),
("std_scaler", std_scaler),
("lin_reg", lin_reg),
])
polynomial_regression.fit(X, y)
y_newbig = polynomial_regression.predict(X_new)
plt.plot(X_new, y_newbig, style, label=str(degree), linewidth=width)
plt.plot(X, y, "b.", linewidth=3)
plt.legend(loc="upper left")
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.axis([-3, 3, 0, 10])
plt.show()
- 고차 다항 회귀 모델은 심각한 과대적합을 보여줌, 선형 모델은 과소적합
- 위 경우에서 가장 일반화가 잘된 모델은 2차 다항 회귀모델임
모델의 과대적합, 과소적합을 확인하는 방법은 훈련데이터에서의 성능과 교차 검증 성능이 나쁜지 확인하는 교차 검증 , 학습 곡선 등의 방법이 있습니다.
학습 곡선
- 훈련 세트와 검증 세트의 모델 성능을 훈련 세트 크기의 함수로 표현
- 훈련세트에서 크기가 다른 서브 세트를 만들어 모델을 여러 번 훈련 시키면서 그림
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
def plot_learning_curves(model, X, y):
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=10)
train_errors, val_errors = [], []
for m in range(1, len(X_train) + 1):
model.fit(X_train[:m], y_train[:m])
y_train_predict = model.predict(X_train[:m])
y_val_predict = model.predict(X_val)
train_errors.append(mean_squared_error(y_train[:m], y_train_predict))
val_errors.append(mean_squared_error(y_val, y_val_predict))
plt.plot(np.sqrt(train_errors), "r-+", linewidth=2, label="train")
plt.plot(np.sqrt(val_errors), "b-", linewidth=3, label="val")
plt.legend(loc="upper right", fontsize=14) # 책에는 없음
plt.xlabel("Training set size", fontsize=14) # 책에는 없음
plt.ylabel("RMSE", fontsize=14) # 책에는 없음
lin_reg = LinearRegression()
plot_learning_curves(lin_reg, X, y)
plt.axis([0, 80, 0, 3]) # 책에는 없음
plt.show() # 책에는 없음
- 위 모델은 과소 적합을 보여주는 모델
- 처음에는 완벽한 적합을 보여주지만 점점 늘어날수록 곡선이 평탄해질때 까지 오차가 게속 상승함
- 이런 경우에서는 데이터를 더 넣어주는것 보다는 더 복잡한 모델을 사용해야함
같은 데이터에서 다항 회귀 모델 사용시
from sklearn.pipeline import Pipeline
polynomial_regression = Pipeline([
("poly_features", PolynomialFeatures(degree=10, include_bias=False)),
("lin_reg", LinearRegression()),
])
plot_learning_curves(polynomial_regression, X, y)
plt.axis([0, 80, 0, 3]) # 책에는 없음
plt.show() # 책에는 없음
- 이전의 학습 곡선과의 큰 차이를 볼수 있음
- 훈련 데이터의 오차가 선형 회귀 모델보다 낮음
- 두 곡선 사이의 공간이 존재, 훈련 데이터에서의 모델 성능이 검증 데이터에서보다 훨씬 낫다는 뜻 ← 이런 특성은 과대적합 모델의 특징
- 해결방안은 더 많은 훈련 세트를 사용하는 방법 (데이터 추가)
편향/분산 트레이드오프
- 모델의 일반화 오차는 세 가지 다른 종류의 오차의 합으로 표현할 수 있다는 사실
- 편향
- 일반화 오차중 편향은 잘못된 가정으로 인해 발생
- 실제는 2차인데 선형으로 가정하는 경우
- 편향이 큰 모델은 훈련 데이터에 과소적합되기 쉬움
- 분산
- 훈련 데이터에 있는 작은 변동에 모델이 과도하게 민감하기 때문에 발생
- 자유도가 높은 모델(고차원 다항 회귀 모델)이 높은 분산을 가지기 쉬워 훈련데이터에 과대적합하는 경향 발생
- 줄일 수 없는 오차
- 데이터 자체에 있는 잡음 때문에 발생
- 오차를 줄일수 있는 유일한 방법은 데이터에서 잡음을 제거하는 것
- 편향
모델의 복잡도가 커지면 통상적으로 분산이 늘어나고 편향은 줄어듦, 반대로 모델의 복잡도가 줄어들면 편향이 커지고 분산이 작아짐 (그래서 편향 분산 트레이드 오프)
규제가 있는 선형 모델
- 과대적합을 감소시키는 좋은 방법은 모델을 규제하는 것
- 자유도를 줄이면 데이터에 과대적합되기가 더 어려워짐
- 다항 회귀모델을 규제하는 간단한 방법은 다항식의 차수를 감소시키는 것
- 선형 화귀 모델에서는 보통 모델의 가중치를 제한, 각기 다른 방법으로 가중치를 제한하는 ‘릿지’, ‘라쏘’, ‘엘라스틱넷’ 이 존재
릿지 회귀
- 규제가 추가된 선형 회귀
- 학습 알고리즘을 데이터에 맞추는 것뿐만 아니라 모델의 가중치가 가능한 작게 유지되도록 노력
- 규제항은 훈련하는 동안에만 비용 함수에 추가, 모델의 훈련이 끝나면 모델의 성능을 규제가 없는 성능지표로 평가
- alpha : 값이 클수록 강력한 정규화(규제) 설정하여 분산을 줄임, 양수로 설정
- fit_intercept : 모형에 상수항 (절편)이 있는가 없는가를 결정하는 인수 (default : True)
- normalize : 매개변수 무시 여부
- copy_X : X의 복사 여부
- max_iter : 계산에 사용할 작업 수
- tol : 정밀도
- solver : 계산에 사용할 알고리즘 (auto, svd, cholesky, lsqr, sparse_cg, sag, saga)
- auto : 데이터 유형에 따른 자동선택
- svd : x의 특이값 분해를 사용해 릿지 계수 계산, 가장 안정적 , cholesky 방식보다 느리지만 안정적
- cholesky : 표준 scipy.linalg.solve 사용한 행렬 분해
- lsqr : scipy.sparse.linalg.lsqr 사용, 가장 빠름, 최소제곱법 방식 사용해 선형 연립방정식 풀어냄
- sag : SGD 사용
- parse_cg : scipy.sparse.linalg.cg 사용, 대규모 데이터에서 cholesky 보다 적합
- lbfgs : scipy.optimize.minimize.positive 가 True 일때 사용가능
>> svd를 제외한 모든 다른 파라미터는 밀집데이터, 희소데이터 지원,
lsqr, sag, sparse_cg, lbfg는 fit_interceot가 True 인 경우만 희소 입력 지원
- random_state : 난수 seed 설정
일반적으로 훈련하는 동안 사용되는 비용 함수와 테스트에서 사용되는 성능 지표는 다름, 규제를 떠나서 이들이 다른 이유는 훈련에 사용되는 비용 함수는 최적화를 위해 미분 가능해야 하기 때문
반면 테스트에 사용되는 성능지표는 최종 목표에 가능한 가까워야 해서 로그 손실같은 비용함수를 사용해 훈련시킨 분류기를 정밀도/재현율을 사용해 평가하는 것이 좋은 예
- 릿지 비용함수
- α는 모델을 얼마나 많이 규제할지 조절, α = 0 이면 선형회귀와 동일
- α가 너무 커지면 모든 가중치가 거의 0에 가까워지며 결국 데이터의 평균을 지나는 수평선이 됨
- 편향(θ) 는 규제되지 않음, w를 특성의 가중치 벡터(θ_1 --> θ_n)이라고 정의 한다면 규제항은 아래와 같음
- 여기서
가 가중치 벡터의
노름
- 경사하강법에 적용하려면 MSE 그레이디언트 벡터에 αw를 더하면 됨
릿지 회귀는 입력 특성의 스케일에 민감하기 때문에 수행하기 전에 데이터 스케일 맞추는 것이 중요, 규제가 있는 모든 모델은 대부분 마찬가지
선형 데이터에 다른 α를 사용해 릿지 모델 훈련 시각화
- 왼쪽은 평범한 릿지 모델 사용, 오른쪽은 PolynomialFeatures(degree=10) 사용해 먼저 데이터를 확장후 StandardScaler 사용해 스케일을 조정한 뒤 릿지 모델 적용 (릿지 규제를 사용한 다항회귀)
from sklearn.linear_model import Ridge
def plot_model(model_class, polynomial, alphas, **model_kargs):
for alpha, style in zip(alphas, ("b-", "g--", "r:")):
model = model_class(alpha, **model_kargs) if alpha > 0 else LinearRegression()
if polynomial:
model = Pipeline([
("poly_features", PolynomialFeatures(degree=10, include_bias=False)),
("std_scaler", StandardScaler()),
("regul_reg", model),
])
model.fit(X, y)
y_new_regul = model.predict(X_new)
lw = 2 if alpha > 0 else 1
plt.plot(X_new, y_new_regul, style, linewidth=lw, label=r"$\\alpha = {}$".format(alpha))
plt.plot(X, y, "b.", linewidth=3)
plt.legend(loc="upper left", fontsize=15)
plt.xlabel("$x_1$", fontsize=18)
plt.axis([0, 3, 0, 4])
plt.figure(figsize=(8,4))
plt.subplot(121)
plot_model(Ridge, polynomial=False, alphas=(0, 10, 100), random_state=42)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.subplot(122)
plot_model(Ridge, polynomial=True, alphas=(0, 10**-5, 1), random_state=42)
plt.show()
- α를 증가시킬수록 직선에 가까워지는 것을 볼 수 있음
- 즉, 모델의 분산은 줄지만 편향은 커짐
- 선형 회귀와 마찬가지로 릿지 회귀를 계산하기 위해 정규방정식을 사용할 수도 있고 경사 하강법을 사용할 수도 있음
정규방정식을 사용한 ridge 회귀 와 확률적 경사 하강법 비교
- 행렬 분해 공식을 사용해 변형한 방정식 활용
from sklearn.linear_model import Ridge
ridge_reg = Ridge(alpha=1, solver="cholesky", random_state=42)
ridge_reg.fit(X, y)
ridge_reg.predict([[1.5]])
>> array([[1.55071465]])
- SGD 사용
- penalty 매개변수는 사용할 규제를 설정
- L2는 SGD가 비용 함수에 가중치 벡터의 L2 노름의 제곱을 2로 나눈 규제항 추가하게 만듬 → 릿지회귀와 동일
sgd_reg = SGDRegressor(penalty="l2", max_iter=1000, tol=1e-3, random_state=42)
sgd_reg.fit(X, y.ravel())
sgd_reg.predict([[1.5]])
L2 규제 : 가중치의 제곱에 비례하는 비용이 추가됩니다. L2규제는 신경망에서 가중치 감쇠
라쏘 회귀
선형 회귀의 또 다른 규제된 버전
릿지 회귀처럼 비용 함수에 규제항을 더하지만 L2 노름 / 2 로 나는 것 대신 가중치 벡터의 L1 노름을 사용
라쏘 비용함수
from sklearn.linear_model import Lasso plt.figure(figsize=(8,4)) plt.subplot(121) plot_model(Lasso, polynomial=False, alphas=(0, 0.1, 1), random_state=42) plt.ylabel("$y$", rotation=0, fontsize=18) plt.subplot(122) plot_model(Lasso, polynomial=True, alphas=(0, 10**-7, 1), random_state=42) plt.show()
- 이전의 Ridge 모델과 같은 양상이지만 조금 더 작은 α값을 사용
- 라쏘의 중요한 점은 덜 중요한 특성의 가중치를 제거하려고 한다는 점
- 오른쪽 그래프의 점선은 2차방적식 처럼 보이는 선형
- 차수가 높은 다항 특성의 가중치가 모두 0으로 변경, 라쏘 회귀는 자동으로 특성 선택을 하고 희소 모델 생성
라쏘 사용시 경사 하강법이 최적점 근처에서 진동하는 것을 방지하기 위해서는 훈련하는 동안 점진적으로 학습률 감소시켜야 함
- 라쏘의 비용함수는 θ0에서 미분 가능하지 않음, 하지만 θi = 0일때 g(서브그레이디언트 벡터)를 사용하면 경사 하강법을 적용하는 데 문제가 없음
- 경사 하강법을 위해 라쏘 비용 함수에 사용할 수 있는 서브그레이디언트 벡터 공식
sklearn Lasso 예시
from sklearn.linear_model import Lasso
lasso_reg = Lasso(alpha=0.1)
lasso_reg.fit(X, y)
lasso_reg.predict([[1.5]])
>> array([1.53788174])
- Lasso 대신 SGDRegressor(penalty=’l1’)도 가능
엘라스틱넷
- 릿지, 라쏘 두 모델을 절충한 모델
- 규제는 릿지 규제 + 라쏘 규제, 혼합 비율을 r을 이용해 조절
- r=0 이면 릿지, r=1 이면 라쏘와 같음
- 비용함수
선형회귀, 릿지, 라쏘, 엘라스틱넷 ?
- 일반적으로 평범한 선형 회귀는 피해야함, 대부분 규제가 있는 것이 좋음
- 릿지가 기본이 되지만 몇몇 사용되는 핵심 특성이 적다고 판단되면 라쏘나 엘라스틱넷이 좋음
- 라쏘, 엘라스틱넷은 불필요한 특성의 가중치를 0으로 만듬
- 특성 수가 훈련 샘플보다 많거나 특성 몇 개가 강하게 연관되어 있다면 라쏘보다 → 엘라스틱넷
ElasticNet 예제
from sklearn.linear_model import ElasticNet
elastic_net = ElasticNet(alpha=0.1, l1_ratio=0.5, random_state=42)
elastic_net.fit(X, y)
elastic_net.predict([[1.5]])
>> array([1.54333232])
조기 종료
- 경사 하강법과 같은 반복적인 학습 알고리즘을 규제하는 방법은 검증 에러가 최솟값에 도달하면 바로 훈련을 중지하는 것 → 조기종료
- 위의 그림을 통해 학습을 진행하면서 RMSE를 기록
- 기록된 RMSE가 최소일때를 선택 ← 다시 error가 늘어나는것이 점차 과대적합이 되고 있는것을 의미
SGD 나 미니배치 경사 하강법에서는 곡선이 매끄럽지 않아 최솟값 도달이 어려움, 이때는 검증 에러가 일정 시간 동안 최솟값보다 클 때 학습을 멈추고 검증 에러가 최소였을 때의 모델 파라미터로 설정
from copy import deepcopy
poly_scaler = Pipeline([
("poly_features", PolynomialFeatures(degree=90, include_bias=False)),
("std_scaler", StandardScaler())
])
X_train_poly_scaled = poly_scaler.fit_transform(X_train)
X_val_poly_scaled = poly_scaler.transform(X_val)
sgd_reg = SGDRegressor(max_iter=1, tol=-np.infty, warm_start=True,
penalty=None, learning_rate="constant", eta0=0.0005, random_state=42)
minimum_val_error = float("inf")
best_epoch = None
best_model = None
for epoch in range(1000):
sgd_reg.fit(X_train_poly_scaled, y_train) # 중지된 곳에서 다시 시작합니다
y_val_predict = sgd_reg.predict(X_val_poly_scaled)
val_error = mean_squared_error(y_val, y_val_predict)
if val_error < minimum_val_error:
minimum_val_error = val_error
best_epoch = epoch
best_model = deepcopy(sgd_reg)
- 최소일때 epoch, model 기록
- warm_start=True 를 통해서 fit() 메서드 재 호출시 이전 모델 파라미터에서 훈련 이어감
로지스틱 회귀
- 회귀 알고리즘중 일부는 분류에서도 사용이 가능함
- 샘플이 특정 클래스에 속할 확률을 추정하는데 사용
- 추정 확률이 50% 이상이면 모델은 그 샘플이 해당 클래스에 속한다고 판단, 반대면 속하지 않는다고 예측 -> 이진분류(시그모이드)
확률 추정
- 입력 특성의 가중치 합을 계산, 그리고 편향을 더함
- 바로 결과를 출력하지 않고 결과값의 로지스틱을 출력
- 공식 (시그모이드 함수)
t가 0 이상이면
는 0.5 이상 1로 예측 반대는 0
훈련과 비용 함수
훈련의 목적은 양상 샘플(y=1)에 대해 높은 확률을 추정, 음성 샘플(y=0)에 대해서는 낮은 확률을 추정하는 모델의 파라미터 벡터 θ 를 찾는 것
하나의 훈련 샘플에 대한 비용함수
- 비용함수가 t가 0에 가까워 지면 -log(t)가 매우 커지므로 타당, 모델이 양성 샘플을 0에 가가운 확률로 추정하면 비용이 크게 증가
- 반대로 t가 1에 가까우면 -log(t)는 0에 가까워짐
음성확률은 0에 가깝게 추정, 양성 샘플의 확률을 1에 가깝체 추정 하면 비용은 0에 가까워짐
전체 훈련 세트에 대한 비용 함수는 모든 훈련 샘플의 비용을 평균한 것 (로지스틱 회귀 비용 함수)
해당 비용 함수의 최솟값을 계산하는 알려진 해는 존재하지 않음
해당 비용 함수는 볼록 함수이므로 경사하강법이 전역 최솟값을 보장
위의 비용 함수의 j번째 모델 파라미터
에 대해 편미분을 한 로지스틱 비용 함수의 편도함수
- 각 샘플에 대해 예측 오차를 계산하고 j번째 특성값을 곱해서 모든 훈련 샘플에 대해 평균을 계산
결정 경계
- 로지스틱 회귀를 통한 꽃잎의 너비 기반 Iris-Versicolor 종을 감지하는 분류기 예시
from sklearn import datasets
iris = datasets.load_iris()
list(iris.keys())
>> ['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename', 'data_module']
X = iris["data"][:, 3:] # 꽃잎 너비
y = (iris["target"] == 2).astype(int) # Iris virginica이면 1 아니면 0
from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression(random_state=42)
log_reg.fit(X, y)
decision_boundary # 결정경계
>> array([1.66066066])
log_reg.predict([[1.7], [1.5]])
>> array([1, 0])
- Iris-Verginica의 꽃잎 너비는 1.4
2.5cm에 분포, 다른 꽃은 0.11.8cm에 분포 - 약간 겹치는 부분인 1.4~1.8 부분 중에서 그래프의 1.6이 결정경계
- 결정경계를 기준으로 1.6 보다 크면 1, 아니면 0으로 예측
from sklearn.linear_model import LogisticRegression
X = iris["data"][:, (2, 3)] # petal length, petal width
y = (iris["target"] == 2).astype(int)
log_reg = LogisticRegression(solver="lbfgs", C=10**10, random_state=42)
log_reg.fit(X, y)
x0, x1 = np.meshgrid(
np.linspace(2.9, 7, 500).reshape(-1, 1),
np.linspace(0.8, 2.7, 200).reshape(-1, 1),
)
X_new = np.c_[x0.ravel(), x1.ravel()]
y_proba = log_reg.predict_proba(X_new)
plt.figure(figsize=(10, 4))
plt.plot(X[y==0, 0], X[y==0, 1], "bs")
plt.plot(X[y==1, 0], X[y==1, 1], "g^")
zz = y_proba[:, 1].reshape(x0.shape)
contour = plt.contour(x0, x1, zz, cmap=plt.cm.brg)
left_right = np.array([2.9, 7])
boundary = -(log_reg.coef_[0][0] * left_right + log_reg.intercept_[0]) / log_reg.coef_[0][1]
plt.clabel(contour, inline=1, fontsize=12)
plt.plot(left_right, boundary, "k--", linewidth=3)
plt.text(3.5, 1.5, "Not Iris virginica", fontsize=14, color="b", ha="center")
plt.text(6.5, 2.3, "Iris virginica", fontsize=14, color="g", ha="center")
plt.xlabel("꽃잎 길이", fontsize=14)
plt.ylabel("꽃잎 너비", fontsize=14)
plt.axis([2.9, 7, 0.8, 2.7])
plt.show()
- 다른 선형 모델처럼 로지스틱 회귀 모델도 l1, l2 페널티를 사용해 규제설정 가능
- parameter의 c를 통해 c가 높을수록 모델의 규제가 줄어듦
소프트맥스 회귀
로지스틱 회귀 모델은 여러 개의 이진 분류기를 훈련 시켜 연결하지 않고 직접 다중 클래스 지원하도록 일반화 시킨것이 소프트맥스 회귀
샘플 x가 주어지면 먼저 소프트맥스 회귀 모델이 각 클래스 k에 대한 점수
를 계산하는 식은 선형 회귀 예측을 위한 식과 매우 비슷
- 각 클래스는 자신만의 파라미터 벡터 가 있음, 이 벡터들은 파라미터 행렬 에 행으로 저장
샘플 x에 대해 각 클래스의 점수가 계산되면 소프트맥스 함수를 통과시켜 클래스k에 속할 확률
을 추정할 수 있음
소프트맥스 함수
- K는 클래스 수
- s(x)는 샘플 x에 대한 각 클래스의 점수를 담은 벡터
- 는 샘플 x에 대한 각 클래스의 점수가 주어졌을 때 이 샘플이 클래스 k에 속할 추정 확률
추정 확률이 가장 높은 클래스를 선택
소프트맥스 회귀 분류기는 한 번에 하나의 클래스만 예측, 종류가 다른 붓꽃같이 상호 배타적인 클래스에서만 사용 가능
- 각 점수에 지수 함수를 적용한 후 정규화, 일반적으로 이 점수를 로짓 또는 로그-오즈라고 함
훈련 방법
모델이 타깃 클래스에 대해서 높은 확률을 추정하도록 만드는 것이 목적
아래 공식의 크로스 엔트로피 비용 함수를 최소화하는 것은 타깃 클래스에 대해 낮은 확률을 예측하는 모델을 억제하므로 이 목적에 부합함
크로스 엔트로피 : 추정된 클래스의 확률이 타깃 클래스에 얼마나 잘 맞는지 측정하는 용도로 사용
크로스 엔트로피 비용 함수
- 이 식에서는 는 i번째 샘플이 클래스 k에 속할 타깃 확률을 의미, 일반적으로 샘플이 클래스에 속하는지 아닌지에 따라 1, 0 으로 판단
- 두 개의 클래스만이 존재할때는 비용함수는 로지스틱 회귀의 비용 함수와 같음
클래스 k에 대한 크로스 엔트로피의 그레이디언트 벡터
- 비용함수 에 대한 그레이디언트 벡터의 공식임
각 클래스에 대한 그레이디언트 벡터를 계산할 수 있으므로 비용 함수를 최소화하기 위한 파라미터 행렬
를 찾기 위해 경사하강법을 사용할 수 있음
소프트맥스 회귀를 통한 3 class 분류
- sklearn Logistic Regression은 클래스 둘 이상일때 기본적으로 OvA 전량을 사용
- multi_class 매개변수를 "multinomial"로 바꾸면 소프트맥스 회귀를 사용 가능, 소프트 맥스 회귀 사용시 solver 매개변수에 "lbfgs"와 같은 소프트맥스 회귀를 지원하는 알고리즘을 지정
- C를 통해 l2 규제 조절
X = iris["data"][:, (2, 3)] # 꽃잎 길이, 꽃잎 너비
y = iris["target"]
softmax_reg = LogisticRegression(multi_class="multinomial",solver="lbfgs", C=10, random_state=42)
softmax_reg.fit(X, y)
- 결정경계가 맞나는 지점은 모두 33% 추정확률을 가짐
연습문제
1. 수백만 개의 특성을 가진 훈련 세트에서는 어떤 선형 회귀 알고리즘을 사용할 수 있을까요?
보통은 확률적 경사 하강법(SGD), 미니배치 경사 하강법을 사용한다. 훈련 세트의 크기가 메모리에 맞다면 배치 경사 하강법도 가능하다. 하지만 정규방정식은 계산 복잡도가 "특성의 갯수"에 따라 매우 빠르게 증가하기에 사용할 수 없다.
확률적 경사 하강법(SGD) O, 미니배치 경사 하강법 O, 배치 경사 하강법 O ,정규방정식 X
2. 훈련 세트에 있는 특성들이 각기 다른 스케일을 갖고 있다. 이런 데이터에 잘 작동하지 않는 알고리즘은 무엇일까요? 그 이유는 무엇일까요? 이 문제를 어떻게 해결할 수 있을까요?
경사하강법(GD) 는 특성의 스케일이 매우 다르면 수렴하는데 오랜 시간이 소요 <- scale 적용해서 해결
정규방정식, SVD는 스케일 조정 없이도 잘 작동
규제가 있는 모델은 특성의 스케일이 다르면 지역 최적점 수렴, 규제는 가중치가 커지지 못하게 제약을 가하므로 특성값이 작으면 큰 값의 특성에 의해 무시됨
3. 경사 하강법으로 로지스틱 회귀 모델을 훈련시킬 때 지역 최솟값에 갇힐 가능성이 있을까요?
로지스틱 회귀 모델의 비용함수는 볼록함수, 경사 하강법이 훈련될 때 지역 최솟값에 갇힐 가능성 없음
4. 충분히 오랫동안 실행하면 모든 경사 하강법 알고리즘이 같은 모델을 만들어낼까요?
최적화할 함수가 볼록 함수이고 학습률이 너무 크지 않으면 모든 경사하강법이 최적점에 도달
하지만 학습률을 감소시키지 않으면 SGD, 미니배치 GD는 최적점에 도달하지 못함
5. 배치 경사 하강법을 사용하고 에프크마다 검증 오차를 그래프로 나타내봤습니다. 검증 오차가 일정하게 상승되고 있다면 어떤 일이 일어나고 있는 걸까요? 이 문제를 어떻게 해결할 수 있나요?
epoch 마다 검증 에러가 지속적으로 상승한다면 학습률이 너무 높고 알고리즘 발산하는 것
훈련 에러도 올라간다면 학습률을 내려야함
만약 훈련 에러가 올라가지 않는다면 모델이 훈련 세트에 과대적합 되어 있는 것으로 훈련 정지
6. 검증 오차가 상승하면 미니배치 경사 하강법을 즉시 중단하는 것이 좋은 방법인가요?
SGD 나 미니배치 경사 하강법은 매 훈련 반복마다 학습의 진정을 보장하기 어려움
검증에러 올라갈때 바로 정지한다면 최적점 근처에 가지 못할수도 있음
조기종료에 모델의 결과를 계속저장하고 일정시간을 넘을때 저장된 모델중 최고를 불러오는 방법이 가장 좋음
7. 어떤 경사 하강법 알고리즘이 가장 빠르게 최적 솔루션의 주변에 도달할까요? 실제로 수렴하는 것은 어떤 것 인가요? 다른 방법들도 수렴하게 만들 수 있나요?
SGD > 미니배치 GD
SGD는 한 번에 하나의 훈련 샘플만 사용해 훈련 반복이 가장 빠름 그래서 가장 먼저 전역 최적점 근처에 도달 (그다음은 미니배치 GD)
훈련시간이 충분하면 배치 경사 하강법으로 실제 수렴가능
하지만 SGD, 미니배치 GD는 점진적으로 감소 시키지 않으면 최적점 주변에만 수렴
8. 다항 회귀를 사용했을 때 학습 곡선을 보니 훈련 오차와 검증 오차 사이에 간격이 큽니다. 무슨 일이 생긴 걸까요? 이 문제를 해결하는 세 가지 방법은 무엇인가요?
검증오차가 훈련 오차보다 훨씬 더 높으면 훈련 세트에 과대적합 가능성 높음
- 다항 차수를 낮추는 방법 (자유도 줄어듦)
- 모델 규제하는 방법 (자유도 줄어듦)
- 훈련 세트의 크기를 증가
9. 릿지 회귀를 사용했을 때 훈련 오차와 검증 오차가 거의 비슷하고 둘 다 높았습니다. 이 모델에는 높은 편향이 문제인가요, 아니면 높은 분산이 문제인가요? 규제 하이퍼파라미터 α를 증가시켜야 할까요, 아니면 줄여야 할까요?
훈련 에러와 검증 에러가 거의 비슷하고 매우 높다면 훈련 세트에 과소적합 가능성이 큼
높은 편향을 가진 모델로 판단됨, α를 줄여야 함
10. 다음과 같이 사용해야 하는 이유는?
- 평범한 선형 회귀 (아무런 규제가 없음) 대신 릿지 회귀
규제가 있는 모델이 없는 모델보다 일반적으로 성능이 좋음 그래서 선형모델보다 릿지를 사용
- 릿지 회귀 대신 라쏘 회귀
라쏘는 l1 규제를 사용해 가중치를 완전히 0으로 만드는 경향이 있습니다. 이는 희소한 모델을 만드는 위험이 있음 또한 자동으로 특성 선택의 효과를 가지므로 단지 몇 개의 특성만 실제 유용하다고 판단될때 사용하는 것이 좋음 아니라면 릿지
- 라쏘 회귀 대신 엘라스틱넷
라쏘가 몇 개의 특성이 강하게 연관되어 있거나 훈련 샘플보다 특성이 더 많을 때에는 불규칙적인 행동을 함으로 엘라스틱넷이 라쏘보다 일반적으로 선호, 그러나 추가적인 하이퍼파라미터가 생김, 불규칙적인 행동이 없는 라쏘를 원하면 엘라스틱 넷에 l1_ratio를 1에 가깝게 설정
11. 사진을 낮과 밤, 실내와 실외로 분류하려 한다. 두 개의 로지스틱 회귀 분류기를 만들어야 할까, 아니면 하나의 소프트맥스 회귀 분류기를 만들어야 할까?
실외와 실내, 낮과 밤은 배타적인 클래스가 아니므로 (4개 class 조합이 가능)
두 개의 로지스틱 회귀 분류기가 적절함
'Study > Self Education' 카테고리의 다른 글
핸즈온 머신러닝 - 6 (0) | 2024.06.18 |
---|---|
핸즈온 머신러닝 - 5 (0) | 2024.06.17 |
핸즈온 머신러닝 - 3 (1) | 2024.06.15 |
핸즈온 머신러닝 - 2 (0) | 2024.06.13 |
핸즈온 머신러닝 - 1 (1) | 2024.06.13 |