[D&A 운영진 ML 스터디] 2주차 2차시

권유진·2022년 7월 31일
0

D&A 운영진 스터디

목록 보기
15/17
  • 모델이 어떻게 작동하는지 알면 적절한 모델, 올바른 알고리즘, 하이퍼파라미터 탐색, 디버깅, 에러 예측이 효율적으로 가능
  • 선형 회귀 - 두 가지 방법으로 훈련 가능
    • train set에 가장 잘 맞는 파라미터를 해석적으로 구함
    • 경사 하강법 이용

선형 회귀

  • y^=θ0+θ1x1+θ2x2++θnxn\hat y = \theta_0 + \theta_1 x_1 + \theta_2 x_2 + \cdots + \theta_nx_n
    • θ0,θ1,,θn\theta_0, \theta_1, \cdots, \theta_n: 모델 파라미터(편향, 가중치들)
    • nn: 특성 수, xix_i: ii번째 특성 값
  • y^=hθ(x)=θx\hat y = h_{\theta}(x) = \theta\cdot x
    • 벡터 형태로 간단하게 작성한 식
    • \cdot : dot product, x0x_0: 항상 1(편향을 위해 존재)
    • hθ(x)h_{\theta}(x): 모델 파라미터 θ\theta를 사용한 가설 함수
  • 손실 함수를 최소화하는 식을 찾는 방법
    • 해석적인 방법: 정규방정식 활용
      • θ^=(XTX)1XTy\hat \theta = (X^TX)^{-1}X^Ty
        • y=θ^X(X1)Ty=θ^(X1)TX(X1)Ty=θ^θ^=(X1XT)(X1)Tyθ^=X1(X1)TXTyθ^=(XTX)1XTyy =\hat \theta X\\ (X^{-1})^T y = \hat\theta (X^{-1})^TX\\ (X^{-1})^Ty = \hat \theta\\ \hat \theta = (X^{-1}X^T)(X^{-1})^Ty\\ \hat\theta=X^{-1}(X^{-1})^TX^Ty\\ \hat\theta = (X^TX)^{-1}X^Ty
        • (n+1)(n+1)(n+1) \cdot (n+1) 크기가 되는 XTXX^TX의 역행렬 계산
          • 역행렬 계산 복잡도는 O(n2.4)O(n^{2.4})~O(n3)O(n^3)
            • LinearRegression이 사용하는 방법은 약 O(n2)O(n^2)
    • 수치적인 방법: 경사 하강법
      • 비용 함수를 최소화하기 위해 반복해서 파라미터 조정
        • 짙은 안개 산속에서 길을 잃었을 때, 지면의 기울기만 보고 내려가는 방법
      • 파라미터 벡터 θ\theta에 대해 현재의 gradient 계산, gradient가 감소하는 방향으로 진행
        • θ\theta는 무작위 지점에서 시작
        • 편도함수를 통해 계산
          • θjMSE(θ)=2mΣi=1m(θTx(i)y(i))xj(i)\cfrac{\partial}{\partial \theta_j}MSE(\theta) = \cfrac{2}{m}\Sigma^m_{i=1}(\theta^Tx^{(i)}-y^{(i)})x^{(i)}_j
        • gradient의 반대 방향(아래)를 향해 이동
          • θ(nextstep)=θηθMSE(θ)\theta^{(next\,step)} = \theta - \eta \nabla_{\theta} MSE(\theta)
      • 학습률: 스텝의 크기 - 중요한 파라미터!
        • 너무 작으면 수렴에 많은 반복이 필요, 너무 크면 발산
          • 또한 손실 함수가 non-convex하다면 local minimum에 수렴
        • feature의 스케일이 매우 다르면 손실 함수가 길쭉한 모양이 됨
          • 수렴에 어려움이 있으므로 스케일링을 해줘야 함!
        • 적절한 학습률 찾기 위해 그리드 탐색 사용
          • 반복 횟수 제한 후 실행
            • 반복 횟수를 아주 크게 지정하고 gradient 벡터가 작아지면(벡터의 노름이 ε\varepsilon 보다 작아지면 알고리즘 중지
      • 종류
        • 배치 경사 하강법: 매 스텝 마다 전체 훈련 세트 사용
          • 훈련 세트가 커지면 매우 느려짐
        • 확률적 경사 하강법: 매 스텝마다 1개의 샘플을 무작위로 선택하고 gradient 계산
          • 속도가 빨라지고 메모리 절약
          • 하지만 데이터 1개씩 사용해 업데이트 하므로 매우 불안정
            • 학습이 끝난 곳이 좋은 파라미터이긴 하지만 최적은 아님
            • 하지만 비용 함수가 매우 불규칙할 때 지역 최솟값을 건너뛰어 전역 최솟값을 찾을 가능성 높음
            • annealing 기법, 학습 스케줄: 학습률을 점진적으로 감소시키면서 학습 - 위 문제 해결
          • 훈련 셋을 무작위로 추출하면 학습이 되지 않는 데이터가 있을 수 있음
            • 차례대로 하나씩 선택하는 방법도 존재
        • 미니배치 경사 하강법: 미니 배치 단위의 작은 샘플 세트에 대해 gradient 계산
          • 배치 경사 하강법과 SGD의 장점 합침
            • SGD보다 덜 불규칙적
            • 배치 경사 하강법보다 더 빠르고 메모리 사용 적음
          • GPU를 통해 얻는 성능 향상 존재
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(X,y)
print(lin_reg.intercept_, lin_reg.coef_) # 편향, 계수
lin_reg.predict(X_new)

from sklearn.linear_model import SGDRegressor
sgd_reg = SGDRegressor(max_iter=1000, tol=1e-3, penalty=None, eta0=0.1)
sgd_reg.fit(X, y.ravel())
print(sgd_reg.intercept_, sgd_reg.coef_)
  • LinearRegression 클래스는 scipy.linalg.lstsq() 함수 기반 작동
    • θ^=X+y\hat \theta = X^+y
      • X+X^+: XX의 유사역행렬(무어-펜로즈 역행렬)
        • SVD 사용해 계산
          • XXUΣ+VTU\Sigma^+ V^T로 분해
            • Σ+\Sigma^+: Σ\Sigma를 우선 계산하고 임계값보다 작은 모든 수를 0으로 치환
            • 그 후, 0이 아닌 수를 모두 역수로 치환, 행렬 전치
        • 정규방정식보다 더욱 효율적
        • 역행렬이 생성될 수 없는 경우에는 정규방정식은 작동하지 않지만, 유사역행렬은 항상 구할 수 있다.
  • 선형 회귀 알고리즘 비교
알고리즘훈련 샘플이 많을 때외부 메모리 학습 지원feature가 많을 때하이퍼 파라미터 수스케일 조정 필요sklearn
정규방정식빠름No느림0No-
SVD빠름No느림0NoLinearRegression
배치 경사 하강법느림No빠름2YesSGDRegressor
확률적 경사 하강법빠름Yes빠름2\ge 2YesSGDRegressor
미니배치 경사 하강법빠름Yes빠름2\ge 2YesSGDRegressor

다항 회귀

  • 데이터가 단순한 직선보다 복잡한 형태라면 단순한 선형 모델만으로 파악 어려움
    • 하지만 특성의 거듭제곱을 새로운 특성으로 추가함으로써 선형 모델 훈련: 다항 회귀
    • 1차항 뿐만 아니라 더 큰 차원의 관계도 찾을 수 있다.
    • 하지만 특성 수가 (n+d)!d!n!\cfrac{(n+d)!}{d!n!}로 늘어나므로 주의!
from sklearn.preprocessing import PolynomialFeatures
poly_features = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly_features.fit_transform(X)
  • 고차 다항 회귀는 훨씬 더 훈련 데이터에 잘 맞추려고 할 것
    • 오버피팅
    • 교차 검증을 통해 오버피팅 여부 확인 가능
    • 학습 곡선을 통해 살펴보는 것도 가능

학습 곡선

  • 훈련 셋과 검증 셋의 모델 성능을 훈련 세트 크기의 함수로 나타냄
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=.2)
    train_errors, val_erros = [], []
    for m in range(1, len(X_train)):
        model.fit(X_train[:m], y_train[:m])
        y_train_predict = model.predict(X_train[:m])
        y_val_predict = model.predict(X_val[:m])
        train_errors.append(mean_squared_eror(y_train[:m], y_train_predict))
        val_errors.append(mean_squared_eror(y_val[:m], y_val_predict))
    plt.plot(np.sqrt(train_errors), "r-+", linewidth=2, label="훈련 세트")
    plt.plot(np.sqrt(train_errors), "b-", linewidth=2, label="검증 세트")
    plt.show()
  • 학습 데이터의 수가 적을 때에는 훈련 셋의 성능이 높고 검증 셋의 성능이 낮다.
  • 학습 데이터가 많아질수록 훈련셋의 완벽한 학습이 어려워짐
    • 노이즈가 추가되기 때문
    • 하지만 일정 수준까지 검증 셋의 성능은 좋아짐
  • 다항 회귀를 사용할 경우, 오버 피팅이 될 가능성 존재

편향/분산 트레이드오프

  • 일반화 오차는 세 가지 다른 종류의 오차 합으로 표현할 수 있다.
    • 편향: 잘못된 가정으로 인한 것
      • 편향이 클수록 과소 적합
    • 분산: 훈련 데이터의 작은 변동에 모델이 과도하게 민감하기 때문에 나타남
      • 자유도가 높은 모델의 경우 높은 분산을 가져 과대 적합
    • 줄일 수 없는 오차: 데이터 자체에 있는 잡음으로 인해 발생
      • 잡음 제거를 통해서만 줄일 수 있음
        • 이상치 감지, 제거

규제가 있는 선형 모델

  • 과대 적합을 감소시키는 좋은 방법은 모델을 규제하는 것
    • 자유도를 줄이면 데이터에 과대적합되기 더 어려워짐
    • 다항식의 차수를 감소

릿지 회귀(Ridge Regression) - 티호노프 규제

  • 규제항 αΣi=1nθi2\alpha\Sigma^n_{i=1} \theta_i^2이 비용 함수에 추가
    • 모델의 가중치가 가능한 한 작게 유지되도록 함
    • 훈련 동안에만 비용 함수에 추가
      • 평가 시에는 규제 없는 비용 함수로 평가
    • α\alpha: 모델을 얼마나 규제할 지 조절
      • 0일 시, 선형 회귀와 같음
  • 비용 함수: J(θ)=MSE(θ)+α12Σi=1nθi2J(\theta) = MSE(\theta) + \alpha \cfrac{1}{2}\Sigma^n_{i=1} \theta_i^2
    • 편향(θ0\theta_0)는 규제되지 않음
    • 가중치 벡터 ww의 규제항은 12(w2)\cfrac{1}{2}(||w||^2)
      • 2||\cdot||^2: 가중치 벡터의 l2l_2 norm
  • 스케일에 민감해 모델 학습 이전에 스케일링 필수
  • α\alpha를 증가시킬수록 직선에 가까워짐
    • 모델의 분산은 줄지만 편향은 커짐
  • 동일하게 정규 방정식, 경사 하강법 모두 사용 가능
    • 정규 방정식: θ^=(XTX+αA)1XTy\hat \theta = (X^TX + \alpha A)^{-1}X^Ty
      • Andre-Louis Cholesky가 발견한 행렬 분해
from sklearn.linear_model import Ridge, SGDRegressor
ridge_reg = Ridge(alpha=1, solver="cholesky")
ridge_reg.fit(X,y)

sgd_reg = SGDRegressor(penalty='l2)
sgd_reg.fit(X, y.ravel())

라쏘 회귀(Lasso Regression)

  • 규제항으로 가중치 벡터의 l1l_1 norm 사용
    • J(θ)=MSE(θ)+αΣi=1nθiJ(\theta) = MSE(\theta) + \alpha \Sigma^n_{i=1}|\theta_i|
  • 라쏘 회귀는 덜 중요한 특성의 가중치를 제거하려고 함
    • 자동으로 Feature Selection을 하고 sparse model을 만듦
  • l1l_1의 경우 gradient가 1또는 -1
    • 0이 될 수 없다.
      • θi=0\theta_i = 0일 때, 미분 불가능
        • Subgradient vector gg 사용하면 경사 하강법에 문제 없음
          • θi=0\theta_i=0일 때, gradient = 0
    • 모든 파라미터가 동일하게 감소
  • l2l_2는 전역 최적점에 가까워질수록 gradient가 작아짐
from sklearn.linear_model import Lasso
lasso_reg = Lasso(alpha=0.1)
lasso_reg.fit(X,y)

sgd_reg = SGDRegressor(penalty="l1")
sgd_reg.fit(X,y)

엘라스틱넷(ElasticNet)

  • Ridge와 Lasso를 절충
    • ridge와 lasso의 규제항을 단순히 더해 사용
    • 혼합 비율 rr을 사용해 조절
      • r=0r=0: Ridge와 같아짐
      • r=1r=1: Lasso와 같아짐
    • J(θ)=MSE(θ)+rαΣi=1nθi+1r2αΣi=1nθi2J(\theta) = MSE(\theta) + r \alpha \Sigma^n_{i=1}|\theta_i| + \cfrac{1-r}{2}\alpha \Sigma^n_{i=1} \theta_i^2
  • 일반 선형회귀보다는 규제가 있는 것이 대부분 좋음
    • 기본적으로 Ridge를 사용하지만 사용되는 feature가 몇 개 안된다고 생각되면 Lasso, ElasticNet 사용
    • feature가 훈련 샘플 수보다 많거나 특성 몇 개가 강하게 연관되어 있으며 Lasso는 문제 야기
      • ElasticNet 사용
from sklearn.linear_model import ElasticNet
elastic_net = ElasticNet(alpha=0.1, l1_ratio=0.5)
elastic_net.fit(X,y)
  • l1_ratio가 혼합 비율 rr이다.

조기 종료

  • 검증 에러가 최소값에 도달하면 훈련 중지
    • 과대적합되기 이전에 학습 종료
    • 훌륭한 공짜 점심(Beautiful Free Lunch)
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_trasform(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)

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)
  • warm_start=True 시, fit() 메소드가 호출될 때 이전 파라미터에서 훈련 진행

로지스틱 회귀(Logistic Regression)

  • 어떤 회귀 알고리즘은 분류에서도 사용 가능
  • 로지스틱 회귀는 샘플이 특정 클래스에 속할 확률을 추정하는데에 널리 사용
  • 확률 추정
    • 입력 feature의 가중치 합 계산
    • 바로 결과를 출력하는 것이 아니라 로지스틱 출력
    • p^=hθ(x)=σ(θTx)\hat p = h_{\theta}(x) = \sigma(\theta^T x)
      • σ\sigma: 시그모이드 함수
        • σ(t)=11+et\sigma(t) = \cfrac{1}{1+e^{-t}}
    • y^={0      p^<0.51      p^0.5\hat y = \begin{cases} 0\;\;\; \hat p < 0.5 \\ 1 \;\;\; \hat p \ge 0.5 \end{cases}
  • tt가 음수이면 σ(t)<0.5\sigma (t) < 0.5이므로 로지스틱 회귀 모델은 θTx\theta^T x가 양수이면 양성 클래스, 음수이면 음성 클래스 예측
  • 훈련과 비용 함수
    • 훈련 목적: 양성 샘플에 대해서는 높은 확률을 추정, 음성 샘플에 대해서는 낮을 확률을 추정하는 θ\theta를 찾는 것
      • c(θ)={log(p^)      y=1log(1p^)      y=0c(\theta) = \begin{cases} -\log(\hat p) \;\;\; y=1 \\ -\log(1-\hat p) \;\;\; y=0 \end{cases}
      • tt가 0에 가까워지면 log(t)-\log(t)가 매우 커지므로 타당
      • 반대로 tt가 1에 가까워지면 log(t)-\log(t)가 0에 가까워짐
        • 음성 샘플의 확률을 0에 가깝게 추정하거나 양성 샘플의 확률을 1에 가깝게 추정하면 비용은 0에 가까워짐
    • 로그 손실(Log Loss): 모든 훈련 샘플의 비용을 평균
      • J(θ)=1mΣi=1m[y(i)log(p^(i))+(1y(i))log(1p^(i))]J(\theta) = -\cfrac{1}{m} \Sigma^m_{i=1} [y^{(i)}\log(\hat p^{(i)})+(1-y^{(i)})\log(1-\hat p^{(i)})]
      • 해당 함수의 최솟값을 계산하는 해는 알려지지 않았다. (ex. 정규 방정식)
      • 하지만 볼록 함수이므로 경사 하강법이 전역 최솟값을 찾는 것 보장
from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression(C=0.5)
log_reg.fit(X,y)
  • C: 규제 강도를 조절하는 하이퍼 파라미터
    • alphaalpha의 역수

소프트맥스 회귀(다항 로지스틱 회귀)

  • 로지스틱 회귀 모델을 직접 다중 클래스를 지원하도록 일반화
  • 우선 각 클래스 kk에 대한 점수 계산
    • sk(x)=(θ(k))TXs_k(x) = (\theta^{(k)})^T X
  • 해당 점수에 소프트맥스 함수 적용해 각 클래스 확률 추정
    • p^k=σ(s(x))k=esk(x)Σj=1Kesj(x)\hat p_k = \sigma(s(x))_k = \cfrac{e^{s_k(x)}}{\Sigma^K_{j=1} e^{s_j(x)}}
    • 추정 확률이 가장 높은 클래스를 선택
  • 크로스 엔트로피 함수를 사용
    • 타깃 클래스에 대해 낮은 확률을 예측하는 모델 억제
    • J(Θ)=1mΣi=1mΣk=1Kyk(i)log(p^k(i))J(\Theta) = -\cfrac{1}{m} \Sigma^m_{i=1} \Sigma^K_{k=1} y_k^{(i)} \log(\hat p_k^{(i)})
softmax_reg = LogisticRegression(multi_class='multinomial', solver='lbfgs', C=10)
softmax_reg.fit(X,y)
  • multi_class='multinomial', solver='lbfgs' 시, 소프트맥스 회귀 사용 가능

scikit-learn 모델 코드 정리

from sklearn.linear_model import LinearRegression, Ridge, Lasso, ElasticNet, SGDRegressor, LogisticRegressor

LinearRegression(fit_intercept, positive)
"""
fit_intercept: 편향 여부 (True or False)
positive: 계수를 양수로 고정 (True or False) 
"""

Ridge(alpha, fit_intercept, max_iter, tol, solver, positive, random_state)
"""
alpha: L2 규제 강도 조절 (float 0~inf; default=1)
fit_intercept: 편향 여부 (True or False)
max_iter: gradient solver의 반복 횟수 결정 (int)
tol: solution의 정밀도 (float; default=1e-3)
solver: 계산 방법 ("auto", "svd", "cholesky", "lsqr", "sparse_cg", "sag", "saga", "lbfgs")
positive: 계수를 양수로 고정 (True or False)
"""

Lasso(alpha, fit_intercept, precompute, max_iter, tol, warm_start, positive, random_state, selection)
"""
alpha: L1 규제 강도 조절 (float 0~inf; default=1)
fit_intercept: 편향 여부 (True or False)
precompute: 계산 속도 향상을 위해 Gram matrix 사전 계산
max_iter: gradient solver의 반복 횟수 결정 (int)
tol: solution의 정밀도 (float; default=1e-3)
warm_start: True 시, fit을 한번 더 하면 현재 파라미터를 초기값으로 학습 진행
positive: 계수를 양수로 고정 (True or False)
selection: 계수 업데이트 방법 ("cyclic", "random") - random 시, 순차적으로 반복 학습하지 않고 랜덤으로 계수 업데이트
    -> random일 시, tol이 1e-4보다 높으면 더욱 빠르게 수렴한다.
"""

ElasticNet(alpha, l1_ratio, fit_intercept, precompute, max_iter, tol, warm_start, positive, random_state, selection)
"""
alpha: L1, L2 규제 강도 조절 (float 0~inf; default=1)
l1_ratio: L1, L2 균형 계수
fit_intercept: 편향 여부 (True or False)
precompute: 계산 속도 향상을 위해 Gram matrix 사전 계산
max_iter: gradient solver의 반복 횟수 결정 (int)
tol: solution의 정밀도 (float; default=1e-3)
warm_start: True 시, fit을 한번 더 하면 현재 파라미터를 초기값으로 학습 진행
positive: 계수를 양수로 고정 (True or False)
selection: 계수 업데이트 방법 ("cyclic", "rancom") - random 시, 순차적으로 반복 학습하지 않고 랜덤으로 계수 업데이트
    -> random일 시, tol이 1e-4보다 높으면 더욱 빠르게 수렴한다.
"""

SGDRegressor(loss, penalty, alpha, l1_ratio, fit_intercept, max_iter, tol, shuffle, epsilon, random_state, learning_rate, eta, power_t, early_stopping, validation_fraction, n_iter_no_change, warm_start, average)
"""
loss: 손실 함수 종류 ('squared_error', 'huber', 'epsilon_insensitive', 'squared_epsilon_insensitive')
penalty: 규제 종류 ('l1', 'l2', 'elasticnet'; default:'l2')
alpha: 규제 강도 조절 (float 0~inf; default=1)
l1_ratio: ElasticNet 규제일 시, 균형 계수
fit_intercept: 편향 여부 (True or False)
max_iter: gradient solver의 반복 횟수 결정 (int)
tol: solution의 정밀도 (float; default=1e-3)
shuffle: epoch마다 train data 섞을지 여부
epsilon
learning_rate: 스케쥴링 ('constant', 'optimal', 'invscaling', 'adaptive'; default='invscaling')
eta: 학습률 (default: 0.01)
power_t: 학습률 감소 정도 (default: 0.25)
early_stopping: 조기 종료 여부 (True or False; default=False)
validation_fraction: 조기 종료 시 검증 셋 비율
n_iter_no_change: 성능이 증가하지 않아도 기다릴 반복 횟수 (default=5)
warm_start: True 시, fit을 한번 더 하면 현재 파라미터를 초기값으로 학습 진행
average: 평균 계수값 저장 여부
"""

LogisticRegressor(penalty, dual, tol, C, fit_intercept, intercept_scaling, class_weight, random_state, solver, max_iter, multi_class, warm_start, l1_ratio)
"""
penalty: 규제 종류 ('l1', 'l2', 'elasticnet'; default:'l2')
dual: liblinear일 시, dual 또는 기본 공식 (default: False)
tol: solution의 정밀도 (float; default=1e-3)
C: 규제 강도 역수(alpha 역수) (default=1)
fit_intercept: 편향 여부 (True or False)
intercept_scaling: liblinear일 시 스케일링
class_weight
solver: 최적화 알고리즘 ('newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga')
max_iter: gradient solver의 반복 횟수 결정 (int)
multi_class: 'auto', 'ovr', 'multinomial'
warm_start: True 시, fit을 한번 더 하면 현재 파라미터를 초기값으로 학습 진행
l1_ratio: ElasticNet 규제일 시, 균형 계수
"""

참고
Hands-on Machine Learning with Scikit-Learn, Keras & Tensorflow 2 - 오렐리앙 제롱

profile
데이터사이언스를 공부하는 권유진입니다.

0개의 댓글