본문 바로가기

ML&DL/머신러닝

[머신러닝] 파이썬으로 선형회귀 BGD, SGD, MSGD 구현 + 실습(광고에 의한 수익 예측)

728x90
반응형

선형 회귀에서는 경사하강법을 이용해서 파라미터를 찾는다. 하지만 파라미터를 찾기 위해서는 경사하강법에 의거한 다량의 행렬 연산을 수행해야 한다. 행렬 연산의 크기에 따라 크게 3개의 모델로 나뉜다.

  1. BGD : 전체 데이터 셋을 연산에 포함
  2. SGD : 데이터 하나만 연산에 포함
  3. MSGD : 전체 데이터 셋을 mini batch로 쪼개고 거기에서 랜덤으로 뽑아 연산 

선형 회귀에 대한 개념은 여기에 있다.


여기서 해볼 실습은 kaggle에 있는 Advertising data를 이용해서 광고와 수익 사이의 관계를 예측하는 모델을 만들어볼 것이다. 데이터 셋은 바로 아래에 있다.

Advertising.csv
0.00MB

 

의문이 들 수 있는 점이 어차피 여기서 구현하고자 하는 모델 3개는 이미 지원해주고 있는 클래스여서 굳이 스스로 구현할 필요가 있을까? 물론 성능상 내가 만든 모델이 조금 더 딸리거나 비슷한 것은 사실이다(어차피 간단한 모델이니까...). 하지만 스스로 구현을 하면서 생각보다 많은 시행착오를 겪게 되고 독자들 또한 시행착오를 많이 경험하기를 바란다. 시행착오를 통해 배운 점도 좋았지만 다양한 라이브러리들 또한 사용할 줄 알게 되어서 배운 점이 많았던 것 또한 값진 경험이었다.  

 

먼저 모델을 구현하기에 앞서 알아두어야 할 기술들이 있다.

단, 반드시 필요한 라이브러리들은 pip install을 통해 미리 다운로드 받기를 바란다. 또한 가능하면 노트북(.ipynb)이 아닌 그냥 파이썬 파일에서 실행하길 바란다. 그래야지만 이후에 보게 될 데이터 3차원 분포도를 더 재밌게 즐길 수 있기 때문이다.

 

INDEX

1. 데이터 불러오기

2. 데이터간에 연관성을 보기 위해 seaborn 라이브러리 이용 및 피쳐 선택

3. X(피쳐)와 y(타깃 변수)로의 분리

4. 피쳐 스케일링

5. 메모리 재사용과 사용량 확인

6. 훈련 데이터, 테스 데이터의 분리

 

1. 데이터 불러오기

일단 위의 csv파일을 파이썬 파일과 같은 폴더 안에 저장한다.

이런식으로~

아래와 같은 코드를 기입해 보자.

import pandas as pd # 파이썬의 데이터 조작 및 분석 라이브러리
df = pd.read_csv("Advertising.csv") # 현재 파이썬 파일이 저장되어 있는 폴더 전체에서 해당 csv파일을 찾아서 읽는다
print(df.head()) # 상위 5개의 데이터만 읽어 출력

결과 >>

df는 dataframe의 약자이고 학습에 사용할 데이터 셋이 되는 셈이다.

 

2. 데이터 간에 연관성을 보기 위해 seaborn 라이브러리 이용 및 피쳐 선택

데이터의 개수가 많으면 많을수록, 피쳐의 개수가 많으면 많을수록 우리는 피쳐와 수익 사이의 관계가 어떻게 되는지 감각적으로 알기 힘들다. 수익과 가장 연관성이 큰 데이터를 추출해야지만 수익을 예측하는 모델의 정확도를 늘릴 수 있기 때문에 이것은 중요한 부분이다.

 

따라서 우리가 사용할 수 있는 라이브러리는 seaborn이다.

import numpy as np # 과학 및 수학 연산을 위한 파이썬 패키지로, 다차원 배열과 행렬 연산에 특화됨
import matplotlib.pyplot as plt # 시각화를 위한 라이브러리
import seaborn as sns # 히트 맵 제작 라이브러리
sns.heatmap(df.corr().abs(), cmap='Reds',mask=np.triu(df.corr()),fmt = '.2%', annot=True) # 히트 맵 제작
plt.show() # 보여주기

결과 >>

2개의 데이터 쌍에 대한 일차원적인 관계를 보여준다. 색이 짙을수록 연관성이 높은 데이터 쌍이라고 할 수 있는 것이다. 우리의 관심사는 Sales에만 관심 있기 때문에 Sales열에 있는 요소들을 보면 TV와 Radio가 연관성이 높음을 알 수 있다. 

 

즉 모델 학습을 위해서 df에 TV, Radio, Sales만 남기고 싶다. 아래와 같은 코드로 쉽게 해결할 수 있다.

df=df[['TV','Radio','Sales']]
print(df.head())

즉 이제 df에는 1열, 2열, 3열에 각각 Tv, Radio, Sales가 저장되어 있는 것이다.

결과 >>

 

히트 맵 대신에 데이터 사이의 일대일 연관성을 시각적 그래프로 보고 싶다면 히트 맵 코드대신 아래의 코드를 작성하면 된다.

sns.pairplot(df)

결과 >>

 

3. X(피쳐)와 y(타깃 변수)로의 분리

학습을 위한 X와 y의 분리는 다음과 같이 행해질 수 있다.

X,y=df.iloc[:,:-1].values, df.iloc[:,-1].values

X만 대문자인 이유는 X는 1 변수가 아닌 이상 다차원 배열이기 때문이다. 디자인 매트릭스를 대문자로 표시하듯이 여기에서도 똑같은 관례를 따르는 것이다. 반대로 y는 선형 회기의 정의에 입각하면 m(데이터 셋의 개수)*1 배열이기 때문에 소문자로 나타낸다.

 

X=df.iloc[:,:-1].values -> df의 데이터들 중에서 전체 행(:)에서의 가장 마지막 열을 제외한 모든 열 (:-1) 을 X에 저장한다.

y=df.iloc[:,-1].values -> df의 데이터들 중에서 전체 행(:)에서의 가장 마지막 열(-1)만을 y에 저장한다.

 

리스트의 인덱싱과 iloc의 조작은 일맥상통한다고 봐도 무방하다.

 

iloc[ ] -> pandas 데이터 프레임을 반환

iloc[ ].values -> 넘파이 배열을 반환

 

우리가 원하는 것은 행렬 간의 넘파이 연산(왜냐하면 넘파이가 다양한 행렬 연산을 지원해 주기 때문)이기 때문에 .values를 붙이는 것이다. 

 

4. 피쳐 스케일링

데이터 셋을 들여다보면 값들이 그다지 커 보이지는 않지만 막상 행렬 연산을 시켜보면 값들이 너무 커져서 연산 감당이 힘들 때가 존재한다. 그래서 피쳐 스케일링이 필요한 것이다. 우리가 사용할 피쳐 스케일링 라이브러리는 아래와 같이 사용할 수 있다.

from sklearn.preprocessing import MinMaxScaler # 0~1의 범위로 데이터 압축

scaler = MinMaxScaler()
X = scaler.fit_transform(X)
scaler_y = MinMaxScaler()
y = scaler_y.fit_transform(y.reshape(-1, 1)).flatten()
# 변환된 값 = (원래 값 - 최소값) / (최대값 - 최소값)

y를 변환할 때만 reshape(-1, 1)을 해준 이유는 매써드 fit_transform은 열 벡터를 입력받기를 예상하기 때문이다.

 

그래서 초기 y.shape는 (200, )인 행 벡터이지만 y.reshape(-1, 1)를 하면 열 벡터인 (200, 1)로 바뀐다.

5. 메모리 재사용과 사용량 확인

우리가 사용할 데이터 셋은 고작 6KB 밖에 안되지만 데이터 셋의 크기는 언제든지 커질 수 있다. 파이썬에서는 사용했던 변수를 업데이트하면 발생하는 문제가 있다.

import numpy as np

X = np.array([1, 2, 3, 4, 5])
before = id(X)
X = X + 1
print(before == id(X)) # 결과 : False

같은 변수에 다른 값을 할당하는 것만으로도 메모리를 새로 할당한다. 이는 여러 가지 문제점으로 이어질 수 있다. 

먼저 프로그램이 사용하는 메모리가 많아질 위험이 있고 메모리 위치가 바뀌니까 참조 위치가 달라진다.

 

이를 해결하기 위해 아래와 같은 코드를 사용하자

import numpy as np

X = np.array([1, 2, 3, 4, 5])
before = id(X)
X[:] = X + 1
print(before == id(X)) # 결과 : True

이 방법을 이용하면 같은 변수명에 대해서는 추가적인 메모리를 할당할 필요가 없으니 효율적이게 메모리를 관리할 수 있는 것이다.

pip install memory_profiler

메모리의 사용량을 보고 싶으면 memory_profiler을 설치한 다음에 관찰하고자 하는 함수 정의 바로 위에 @profile을 적으면 된다.

<메모리 재사용이 얼마나 효과적인지 모려면 아래 더 보기 클릭>

더보기

한눈에  메모리 재사용의 효과를 봐보자. 코드마다 출력 화면이다.

import numpy as np
from memory_profiler import profile

@profile()
def func():
    X = np.arange(100000000).reshape((10000, -1))
    X = np.dot(X, np.zeros(100000000).reshape((10000, -1)))

func()
메모리 재사용을 하지 않았을 때

import numpy as np
from memory_profiler import profile

@profile()
def func():
    X = np.arange(100000000).reshape((10000, -1))
    X[:] = np.dot(X, np.zeros(100000000).reshape((10000, -1)))

func()
메모리를 재사용 했을 때

육안으로도 어마어마한 차이가 보인다!!!

6. 훈련 데이터, 테스트 데이터의 분리

훈련을 위한 데이터와 훈련 데이터로 학습을 마친 모델의 정확도를 실험하기 위해 테스트 데이터를 분리시켜야 한다. 주로 비율은 원본 데이터 셋에서 8:2 정도로 나눈다.

 

애초에 나누는 이유는 무엇일까? 그냥 전체 데이터를 전부 학습에 이용하면 안 될까? 물론 데이터를 많이 학습시켜 주는 방향이어서 좋겠지만 테스트 데이터는 훈련 데이터와 독립적이어야 한다. 그래야만 학습에 한 번도 활용되지 않은 데이터를 모델이 맞닥뜨렸을 때에 대한 양질의 시험(테스트)을 할 수 있다. 분리를 위해 아래의 코드를 사용하자. 

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

 

INDEX

1. 배치 경사하강법(Batch Gradient Descent / BGD)

2. 확률적 경사하강법(Stochastic Gradient Descent / SGD)

3. 미니 배치 경사하강법(Mini Batch Gradient Descent / MSGD)

1. 배치 경사하강법(Batch Gradient Descent / BGD)

class BGDLinearRegression:
    def __init__(self, lr=0.005, n_iters=10000): # 하이퍼 파라미터인 학습률과 반복 횟수(사용자 기호에 따라 변경 가능)
        self.lr = lr
        self.n_iters = n_iters
        self.bias = None
        self.param = None

    def fit(self, X, y):
        """
        피쳐(X)와 타겟 변수(y)를 이용해서 학습하는 매서드.
        학습 데이터인 X를 전부 사용해서 경사하강법을 수행함.
        :param X(피쳐):
        :param y(타겟 변수):
        """
        n_case, n_features = X.shape # 행과 열의 정보를 뽑음
        self.param = np.zeros(n_features) # 피쳐에 대응하는 파라미터 값을 임의의 0으로 초기화
        self.bias = 0 # 파라미터인 bias도 임의의 0으로 초기화
        y_pred = np.zeros(n_case) # 이 코드가 없으면 y_pred[:] 사용 불가능하기 때문에 존재, 메모리 in-place를 위해.
        for _ in range(self.n_iters): # 경사하강법으로 계산
            y_pred[:] = np.dot(X, self.param) + self.bias  # 매 업데이트마다 [:]를 해줘야 메모리 in-place 발생, 즉 메모리 절약
            dw = (1 / n_case) * np.dot(X.T, (y_pred - y))
            db = (1 / n_case) * np.sum(y_pred - y)
            self.param = self.param - self.lr * dw
            self.bias = self.bias - self.lr * db

    def predict(self, X): # k*2 행렬의 데이터를 받았을 때 k*1차원의 예측값을 반환
        return np.dot(X, self.param) + self.bias

    def score(self, X, y_true):  # R2 방식으로 모델의 정확도 계산
        y_pred = self.predict(X)
        mean_true = y_true.mean()
        tss = sum((y_true - mean_true) ** 2)

        rss = sum((y_true - y_pred) ** 2)
        r2 = 1 - (rss / tss)

        return r2

2. 확률적 경사하강법(Stochastic Gradient Descent / SGD)

class SGDLinearRegression:
    def __init__(self, lr=0.005, n_iters=10000):
        self.lr = lr
        self.n_iters = n_iters
        self.bias = None
        self.param = None

    def fit(self, X, y):
        n_case, n_features = X.shape
        self.param = np.zeros(n_features)
        self.bias = 0

        for _ in range(self.n_iters):
            idx = random.randint(0, n_case - 1) # 랜덤으로 하나의 훈련 데이터만 추출해서 학습
            X_sample = X[idx, :]
            y_sample = y[idx]
    # 여기에서 y_pred는 다른 모델과는 다르게 불변 타입(스칼라)이므로 y_pred[:]를 사용할 수도 없고 동시에 애초에 메모리를 많이 차지하지도 않아서 y_pred[:]로 쓸 필요도 없다. 
            y_pred = np.dot(X_sample, self.param) + self.bias 

            dw = np.dot(X_sample, (y_pred - y_sample))
            db = y_pred - y_sample
            self.param = self.param - self.lr * dw
            self.bias = self.bias - self.lr * db

    def predict(self, X):
        return np.dot(X, self.param) + self.bias

    def score(self, X, y_true):
        y_pred = self.predict(X)
        mean_true = y_true.mean()
        tss = sum((y_true - mean_true) ** 2)
        rss = sum((y_true - y_pred) ** 2)
        r2 = 1 - (rss / tss)
        return r2

3. 미니 배치 경사하강법(Mini Batch Gradient Descent / MSGD)

class MSGDLinearRegression:
    def __init__(self, lr=0.005, n_iters=10000, batch_size=32):
        self.lr = lr
        self.n_iters = n_iters
        self.batch_size = batch_size
        self.bias = None
        self.param = None

    def fit(self, X, y):
        n_case, n_features = X.shape
        self.param = np.zeros(n_features)
        self.bias = 0
        # 미니 배치를 생성해준다. 다만 마지막 배치는 self.batch_size의 크기가 아닐수도 있다.
        batch_list = [(i, i + self.batch_size) if i < (n_case - self.batch_size) else (i, n_case - 1) for i in range(0, n_case, self.batch_size)]
        for _ in range(self.n_iters):
            idx = random.randint(0, len(batch_list) - 1)
            s, f = batch_list[idx]
            X_sample = X[s:f, :]
            y_sample = y[s:f]
            y_pred = np.dot(X_sample, self.param) + self.bias

            dw = (1 / self.batch_size) * np.dot(X_sample.T, (y_pred - y_sample))
            db = (1 / self.batch_size) * (np.sum(y_pred - y_sample))
            self.param = self.param - self.lr * dw
            self.bias = self.bias - self.lr * db

    def predict(self, X):
        return np.dot(X, self.param) + self.bias

    def score(self, X, y_true):
        y_pred = self.predict(X)
        mean_true = y_true.mean()
        tss = sum((y_true - mean_true) ** 2)
        rss = sum((y_true - y_pred) ** 2)
        r2 = 1 - (rss / tss)
        return r2

 

일단 전체 코드는 다음과 같다.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
import random
import time
from memory_profiler import profile
import seaborn as sns

df = pd.read_csv("Advertising.csv")

sns.heatmap(df.corr().abs(), cmap='Reds', mask=np.triu(df.corr()), fmt='.2%', annot=True)
plt.show()
df = df[['TV', 'Radio', 'Sales']]  # 히트 맵에서 유의미한 피쳐들만 추림
X, y = df.iloc[:, :-1].values, df.iloc[:, -1].values  # 데이터 로드
y_min, y_max = min(y), max(y)  # 피쳐 스케일링 때문에 predict도 스케일링 된 상태로 나오기 때문에 기존 값으로 되돌리기 위한 정보 미리 추출
scaler = MinMaxScaler()  # 전처리(피쳐 스케일링)
X = scaler.fit_transform(X)
scaler_y = MinMaxScaler()
y = scaler_y.fit_transform(y.reshape(-1, 1)).flatten()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)


class BGDLinearRegression:
    def __init__(self, lr=0.005, n_iters=10000): # 하이퍼 파라미터인 학습률과 반복 횟수(사용자 기호에 따라 변경 가능)
        self.lr = lr
        self.n_iters = n_iters
        self.bias = None
        self.param = None

    @profile()
    def fit(self, X, y):
        """
        피쳐(X)와 타겟 변수(y)를 이용해서 학습하는 매서드.
        학습 데이터인 X를 전부 사용해서 경사하강법을 수행함.
        :param X(피쳐):
        :param y(타겟 변수):
        """
        n_case, n_features = X.shape # 행과 열의 정보를 뽑음
        self.param = np.zeros(n_features) # 피쳐에 대응하는 파라미터 값을 임의의 0으로 초기화
        self.bias = 0 # 파라미터인 bias도 임의의 0으로 초기화
        y_pred = np.zeros(n_case) # 이 코드가 없으면 y_pred[:] 사용 불가능하기 때문에 존재, 메모리 in-place를 위해.
        for _ in range(self.n_iters): # 경사하강법으로 계산
            y_pred[:] = np.dot(X, self.param) + self.bias  # 매 업데이트마다 [:]를 해줘야 메모리 in-place 발생, 즉 메모리 절약
            dw = (1 / n_case) * np.dot(X.T, (y_pred - y))
            db = (1 / n_case) * np.sum(y_pred - y)
            self.param = self.param - self.lr * dw
            self.bias = self.bias - self.lr * db

    def predict(self, X):
        return np.dot(X, self.param) + self.bias

    def score(self, X, y_true):  # R2 방식으로 모델의 정확도 계산
        y_pred = self.predict(X)
        mean_true = y_true.mean()
        tss = sum((y_true - mean_true) ** 2)

        rss = sum((y_true - y_pred) ** 2)
        r2 = 1 - (rss / tss)

        return r2


class SGDLinearRegression:
    def __init__(self, lr=0.005, n_iters=10000):
        self.lr = lr
        self.n_iters = n_iters
        self.bias = None
        self.param = None

    @profile()
    def fit(self, X, y):
        n_case, n_features = X.shape
        self.param = np.zeros(n_features)
        self.bias = 0

        for _ in range(self.n_iters):
            idx = random.randint(0, n_case - 1)
            X_sample = X[idx, :]
            y_sample = y[idx]

            y_pred = np.dot(X_sample, self.param) + self.bias

            dw = np.dot(X_sample, (y_pred - y_sample))
            db = y_pred - y_sample
            self.param = self.param - self.lr * dw
            self.bias = self.bias - self.lr * db

    def predict(self, X):
        return np.dot(X, self.param) + self.bias

    def score(self, X, y_true):
        y_pred = self.predict(X)
        mean_true = y_true.mean()
        tss = sum((y_true - mean_true) ** 2)
        rss = sum((y_true - y_pred) ** 2)
        r2 = 1 - (rss / tss)
        return r2


class MSGDLinearRegression:
    def __init__(self, lr=0.005, n_iters=10000, batch_size=32):
        self.lr = lr
        self.n_iters = n_iters
        self.batch_size = batch_size
        self.bias = None
        self.param = None

	@profile()
    def fit(self, X, y):
        n_case, n_features = X.shape
        self.param = np.zeros(n_features)
        self.bias = 0
        batch_list = [(i, i + self.batch_size) if i < (n_case - self.batch_size) else (i, n_case - 1) for i in range(0, n_case, self.batch_size)]
        for _ in range(self.n_iters):
            idx = random.randint(0, len(batch_list) - 1)
            s, f = batch_list[idx]
            X_sample = X[s:f, :]
            y_sample = y[s:f]
            y_pred = np.dot(X_sample, self.param) + self.bias

            dw = (1 / self.batch_size) * np.dot(X_sample.T, (y_pred - y_sample))
            db = (1 / self.batch_size) * (np.sum(y_pred - y_sample))
            self.param = self.param - self.lr * dw
            self.bias = self.bias - self.lr * db

    def predict(self, X):
        return np.dot(X, self.param) + self.bias

    def score(self, X, y_true):
        y_pred = self.predict(X)
        mean_true = y_true.mean()
        tss = sum((y_true - mean_true) ** 2)
        rss = sum((y_true - y_pred) ** 2)
        r2 = 1 - (rss / tss)
        return r2


# 그려보기
def draw_3D(X, y):
    feature1 = X[:, 0]
    feature2 = X[:, 1]

    fig = plt.figure(figsize=(10, 8))
    ax = fig.add_subplot(111, projection='3d')

    ax.scatter(feature1, feature2, y, c=y, cmap='viridis', edgecolors='k', s=50)
    ax.set_xlabel('TV')
    ax.set_ylabel('Radio')
    ax.set_zlabel('Sales')
    ax.set_title('3D Scatter Plot of Two Features with Target Variable')
    plt.show()


reg = BGDLinearRegression()
Sreg = SGDLinearRegression()
mreg = MSGDLinearRegression()
# 학습에 걸리는 시간 측정
t1 = time.time()
reg.fit(X_train, y_train)
t1 = time.time() - t1

t2 = time.time()
Sreg.fit(X_train, y_train)
t2 = time.time() - t2

t3 = time.time()
mreg.fit(X_train, y_train)
t3 = time.time() - t3

sc_reg = reg.score(X_test, y_test)
sc_sreg = Sreg.score(X_test, y_test)
sc_mreg = mreg.score(X_test, y_test)
print(f"reg time  : {t1:.5f}")
print(f"Sreg time : {t2:.5f}")
print(f"mreg time : {t3:.5f}")
print("reg score  : ", sc_reg)
print("Sreg score : ", sc_sreg)
print("mreg score : ", sc_mreg)

# score이 가장 높았던 모델 선택
models = [reg, Sreg, mreg]
accuracies = [sc_reg, sc_sreg, sc_mreg]
best_model, best_accuracy = max(zip(models, accuracies), key=lambda x: x[1])
# 예측 후 역변환
new_data = scaler.transform([list(map(int, input("수익 예측을 위한 TV, Radio 값 각각 입력 : ").split()))])
prediction_scaled = best_model.predict(new_data)
prediction_original = prediction_scaled * (y_max - y_min) + y_min
print(f"예측 수입 : {prediction_original[0]:.2f}")

draw_3D(X, y)

 

draw_3D의 결과 >>

 

결과는 보다시피 TV와 Radio 데이터들은 Sales에 대해 한 평면 위에 존재한다는 것을 알 수 있고 데이터를 선형적임을 알 수 있다. 


알게 된 점

 

1. 데이터의 크기가 작으면 BGD, SGD, MSGD 간에 별 차이가 없지만(오히려 가장 느려 야한 BGD가 MSGD보다 빠르다) 데이터 셋이 많이 커지면 BGD가 눈에 띄게 느려지는 경향이 보인다. 이를 직접 확인해보고 싶으면 아래의 코드를 추가해 보기를 바란다.

from sklearn.datasets import make_regression
X, y = make_regression(n_samples=10000, n_features=10, noise=20, random_state=42)

 

2. 브로드캐스팅(Broadcasting)이 이런 곳에 사용될 줄 몰랐다. 브로드캐스팅은 넘파이에서 배열 간의 연산을 유연하게 처리하는 방식 중 하나이다. 예시로는 predict 매서드에서

np.dot(X, self.param) + self.bias

np.dot은 행렬을 생산하고 self.bias는 엄연히 스칼라이다. 브로드캐스팅을 처음 배웠을 때는 도대체 어떤 경우에 이것을 사용하지 싶었는데 막상 사용하고 나니까 너무 편했다. self.bias를 같은 크기의 행렬로 변환할 필요도 없이 간단한 연산이 가능해진 셈이다.

 

3. 피쳐 스케일링은 생각보다 중요하다. 데이터 셋이 거대해지고 피쳐들의 값이 커질수록 행렬 연산이 버거워진다. 그래서 피쳐스케일링은 중요한 전처리 과정 중 하나임을 몸소 깨달았다.

 

4. 파라미터를 사실상 drop할 필요는 없다. 초반에 Sales에 연관성이 상대적으로 적다고 드러난 Newspaper을 학습 데이터 셋에서 제거하였다. 하지만 다시 생각해 보면 어차피 타겟 변수와 연관성이 적은 피쳐의 파라미터는 학습에 의해 자동으로 0으로 가까워지지 않을까? 그래서 직접 drop을 하지 않고 학습을 시켜보았다. 결과적으로 score는 별 차이 없었다. 파라미터도 직접 확인해 본 결과 Newspaper 파라미터만 0에 가깝게 책정되었다. 

 

이와 관련해서 추가적인 개념이 over parameterization problem이다. 모델이 커지면 커질 수록 더욱 복잡한 관계까지 학습하려고 하면서 관계가 없거나 잘못된 데이터까지 학습하게 되는 현상이다.  

728x90
반응형