ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [혼공머] 3-3. 특성 공학과 규제
    Data Science/딥러닝 & 머신러닝 2025. 1. 10. 23:49

     

    1. 배경

    1. 적은 특성 사용 및 고차항 사용

    • 수동으로 고차항을 계속 넣기는 어렵다는 문제
    • 특성이 많을수록 효과가 커진다.

    2. 선형 회귀 모델의 학습

      • 1개의 특성 → 직선
      • 2개의 특성 → 평면
      • 인간은 3차원 공간 이상을 그리거나 상상할 수 없다.선형 회귀
      • 3차원 이상의 고차원 : 매우 복잡한 모델을 표현할 수 있다.

    2. 다중 회귀와 특성 공학

    1. 다중 회귀

    • 여러 개의 특성을 사용한 선형 회귀
    • 선형 회귀와의 차이
      • 선형 회귀 : 1개의 특성, 직선 학습
      • 다중 회귀 : 여러 개의 특성, 평면 학습
    • 다항 회귀와의 차이 *
      • 다항 회귀 : 1개의 독립 변수를 비선형적으로 변형해서 여러 개로 사용
      • ex. y=β_0 + β_1x + β_2x^2 +⋯+ϵ.
      • 다중 회귀 : 여러 독립 변수를 사용
      • ex. y=β_0 + β_1x1 + β_2x2 +⋯+ϵ.

    2. 특성 공학

    • 기존의 특성을 사용해 새로운 특성을 뽑아내는 작업
    • 목적 : 존의 데이터로부터 유용한 정보를 추출하거나 데이터를 변환하여 모델의 성능을 향상시키는 것
    • ex. 기존 변수인 lengh, weight 를 이용해 새로운 변수 length*weight 생성

    3. 데이터 준비

    1. 데이터프레임

    • csv 파일을 이용한다.
    • 순서
      • pandas의 read_csv()
      • to_numpy()

    2. 훈련 데이터와 타겟 데이터 준비

    import pandas as pd
    df = pd.read_csv('<https://bit.ly/perch_csv_data>')
    perch_full = df.to_numpy()
    
    import numpy as np
    perch_weight = np.array(
     [5.9, 32.0, 40.0, 51.5, 70.0, 100.0, 78.0, 80.0, 85.0, 85.0,
     110.0, 115.0, 125.0, 130.0, 120.0, 120.0, 130.0, 135.0, 110.0,
     130.0, 150.0, 145.0, 150.0, 170.0, 225.0, 145.0, 188.0, 180.0,
     197.0, 218.0, 300.0, 260.0, 265.0, 250.0, 250.0, 300.0, 320.0,
     514.0, 556.0, 840.0, 685.0, 700.0, 700.0, 690.0, 900.0, 650.0,
     820.0, 850.0, 900.0, 1015.0, 820.0, 1100.0, 1000.0, 1100.0,
     1000.0, 1000.0]
     )
    

    3. 훈련 세트와 테스트 세트 분할

    from sklearn.model_selection import train_test_split
    train_input, test_input, train_target, test_target = train_test_split(perch_full, perch_weight, random_state=42)

     


    4. 사이킷런의 변환기

    1. 변환기

    • 특성을 만들거나 전처리하기 위한 클래스
      • fit, transform 메서드 제공

    2. PolynomialFeatures

    • 교재에서는 PolynomialFeatures 클래스 사용from sklearn.preprocessing import PolynomialFeatures
      • 순서
        1. fit
        2. transform : 훈련해야 변환이 가능하다.
        또는 fit_transform() 으로 한 번에 변환할 수도 있다.
      • 특성 자체, 특성끼리 곱한 값, 각 특성의 제곱, 1이 된다
      • 1 은 절편 값
    • include_bias = False
      • 사이킷런의 선형 모델은 자동으로 절편 추가해서, 절편값인 1이 따로 필요하지 않다.
      poly = PolynomialFeatures(include_bias=False)
      poly.fit([[2, 3]])
      print(poly.transform([[2, 3]])) # [[2. 3. 4. 6. 9.]]
      
    # 예시 from sklearn.preprocessing import PolynomialFeatures 
    poly = PolynomialFeatures() 
    poly.fit([[2, 3]]) 
    print(poly.transform([[2, 3]])) # [[1. 2. 3. 4. 6. 9.]]

     


    5. 다중 회귀 모델 훈련

    1. train_input, test_input 에 적용

    poly = PolyNomialFeatures(include_bias=False)
    ploy.fit(train)input)
    train_poly = poly.transform(train_input)
    test_poly = poly.transform(test_input)
    print(train_poly.shape) # (42, 9)
    
    • get_features_names_out() 으로 특성의 조합 확인
    poly.get_feature_names_out()
    # array(['x0', 'x1', 'x2', 'x0^2', 'x0 x1', 'x0 x2', 'x1^2', 'x1 x2',
           'x2^2'], dtype=object)
    

    2. 다중 회귀 모델 훈련하기

    • 선형 회귀 모델을 훈련하는 것과 같다.
    from sklearn.linear_model import LinearRegression
    lr = Linear_regression()
    lr.fit(train_poly, train_target)
    

    3. 모델의 정확도

    print(lr.score(train_poly, train_target)) # 0.99
    print(lr.score(test_poly, test_target)) # 0.97
    

    과소적합 문제는 해결되었음!

    4. 특성을 추가한 모델 재훈련

    • 더 높은 제곱의 항 추가
    • PolunomialFeatures 클래스의 degree 매개변수 → 고차항의 최대 차수 지정
    # 5제곱까지 특성 추가
    poly = PolynomialFeatures(degree = 5, include_bias = False)
    poly.fit(train_input)
    train_poly = poly.transform(train_input)
    test_poly = poly.transform(test_input)
    print(train_poly.shape) # (42, 55)
    
    # 모델 훈련과 점수
    lr.fit(train_poly, train_target)
    lr.score(train_poly, train_target) # 0.99
    lr.score(test_poly, test_target) # -144.4
    

    훈련 세트에 너무 과대적합되므로 테스트 세트에서 점수가 음수 나옴.

    → 특성을 다시 줄여야 한다.


    6. 규제

    1. 규제의 정의

    • 머신러닝 모델이 훈련 세트를 과도하게 학습하지 못하도록 훼방하는 것
    • 모델이 훈련 세트에 과대적합되지 않도록 만드는 것
    • 선형 회귀 모델에서는, 기울기 (계수) 작게 만드는 것

    2. 규제 이전에 꼭 해야 하는 것 : 표준화

    • 이유 : 계수값에 차이가 나면 공정하게 제어되지 않을 수 있기 때문이다.
    • mean, std 구해서 하지 말고, 사이킷런의 StandardScaler 클래스를 사용한다.
    • 순서
      1. StandardScaler 클래스의 객체 ss 초기화
      2. PolynomialFeatures 클래스로 만든 train_poly를 사용해 객체 훈련
      3. 훈련 세트로 학습한 변환기를 사용해 테스트 세트까지 반환
    from sklearn.preprocessing import StandardScaler
    ss = StandardScaler()
    ss.fit(train_poly)
    train_scaled = ss.transform(train_poly)
    test_scaled = ss.transform(test_poly)
    
    • 선형 회귀 모델에 규제를 추가한 모델
      • 둘 다 같은 패키지 안에 있다.
      • 릿지 : 계수를 제곱한 값을 기준으로 규제 적용
      • 라쏘 : 계수의 절댓값을 기준으로 규제 적용; 계수 크기를 0으로 만들 수도 있다.
    • 하이퍼파라미터 : 사람이 알려줘야 하는 파라미터

    3. 릿지 회귀

    from sklearn.linear_model import Ridge
    ridge = Ridge()
    ridge.fit(train_scaled, train_target)
    print(ridge.score(train_scaled, train_target)) # 0.98
    print(ridge.score(test_scaled, test_target)) # 0.97
    

    너무 과대적합되지 않아 테스트 성능에서도 좋은 성능을 낸다.

    • alhpa 값의 변경
      • 줄이면 과대적합 가능성 증가 : 규제 강도 감소
      • 높이면 과소적합 가능성 증가 : 규제 강도 증가
    • 적절한 alhpa 값 : 훈련 세트와 테스트 세트의 점수가 가장 가까운 지점
      1. alpha 값 찾기
      import matplotlib.pyplot as plt
      train_score = [] 
      test_score = [] # alpha 바꿀 때마다 score()한 값 저장할 리스트
      
      alpha_list = [0.001, 0.01, 0.1, 1, 10, 100] # alpha 값 리스트
      for alpha in alpha_list:
          ridge = Ridge(alpha=alpha) # 모델 생성
          ridge.fit(train_scaled, train_target) # 훈련
      
          # 훈련 세트와 테스트 세트의 점수 각각 저장
          train_score.append(ridge.score(train_scaled, train_target)) 
          test_score.append(ridge.score(test_scaled, test_target)) 
      
      # 그래프 그리기 : 동일 간격을 위해 지수로 표현 (상용로그)
      plt.plot(np.log10(alpha_list), train_score) # blue
      plt.plot(np.log10(alpha_list), test_score) # oragne
      plt.xlabel('alpha')
      plt.ylabel('R^2')
      

      2. alpha =0.1로 훈련
      ridge = Ridge(alpha=0.1)
      ridge.fit(train_scaled, train_target)
      print(ridge.score(train_scaled, train_target)) # 0.99
      print(ridge.score(test_scaled, test_target)) # 0.98
      
    • 가장 가까운 alhpa = orange 에서 가장 높은 -1 = 0.1

     

    4. 라쏘 회귀

    • 계수의 크기를 0으로 만들 수도 있다.
    from sklearn.linear_model import Lasso
    lasso = Lasso()
    lasso.fit(train_scaled, train_target)
    print(lasso.score(train_scaled, train_target)) # 0.98
    print(lasso.score(test_scaled, test_target)) # 0.97
    
    • 적절한 alpha 값 찾기 : 훈련 세트와 테스트 세트의 점수가 가장 가까운 지점
      1. alpha 값 찾기
      import matplotlib.pyplot as plt
      train_score = [] 
      test_score = [] # alpha 바꿀 때마다 score()한 값 저장할 리스트
      
      alpha_list = [0.001, 0.01, 0.1, 1, 10, 100] # alpha 값 리스트
      for alpha in alpha_list:
          lasso = Lasso(alpha=alpha) # 모델 생성
          lasso.fit(train_scaled, train_target) # 훈련
      
          # 훈련 세트와 테스트 세트의 점수 각각 저장
          train_score.append(lasso.score(train_scaled, train_target)) 
          test_score.append(lasso.score(test_scaled, test_target)) 
      
      # 그래프 그리기 : 동일 간격을 위해 지수로 표현 (log10)
      plt.plot(np.log10(alpha_list), train_score) # blue
      plt.plot(np.log10(alpha_list), test_score) # oragne
      plt.xlabel('alpha')
      plt.ylabel('R^2')
      

      2. alpha =10로 훈련
      lasso = Lasso(alpha=10)
      lasso.fit(train_scaled, train_target)
      print(lasso.score(train_scaled, train_target)) # 0.988
      print(lasso.score(test_scaled, test_target)) # 0.982
      
       

    7. 모델의 과대적합 제어하기 전체 소스 코드

    # 훈련 데이터
    df = pd.read_csv('<https://bit.ly/perch_csv_data>')
    perch_full = df.to_numpy()
    
    # 타겟 데이터
    perch_weight = np.array(
     [5.9, 32.0, 40.0, 51.5, 70.0, 100.0, 78.0, 80.0, 85.0, 85.0,
     110.0, 115.0, 125.0, 130.0, 120.0, 120.0, 130.0, 135.0, 110.0,
     130.0, 150.0, 145.0, 150.0, 170.0, 225.0, 145.0, 188.0, 180.0,
     197.0, 218.0, 300.0, 260.0, 265.0, 250.0, 250.0, 300.0, 320.0,
     514.0, 556.0, 840.0, 685.0, 700.0, 700.0, 690.0, 900.0, 650.0,
     820.0, 850.0, 900.0, 1015.0, 820.0, 1100.0, 1000.0, 1100.0,
     1000.0, 1000.0]
     )
     
     # 테스트 세트 분리
    from sklearn.model_selection import train_test_split
    train_input, test_input, train_target, test_target = train_test_split(perch_full, perch_weight, random_state=42)
    
    # 사이킷런 변환기로 다중 회귀 특성 만들기
    from sklearn.preprocessing import PolynomialFeatures
    poly = PolynomialFeatures(include_bias=False)
    poly.fit(train_input)
    train_poly = poly.transform(train_input)
    test_poly = poly.transform(test_input)		
    
    # 다중회귀 모델 훈련
    from sklearn.linear_model import LinearRegression
    lr = LinearRegression()
    lr.fit(train_poly, train_target)
    print(lr.score(train_poly, train_target))
    print(lr.score(test_poly, test_target))			
    
    # degree로 특성 추가한 다중 회귀 모델 훈련
    poly = PolynomialFeatures(degree = 5, include_bias = False)
    poly.fit(train_input)
    train_poly = poly.transform(train_input)
    test_poly = poly.transform(test_input)
    lr.fit(train_poly, train_target)
    lr.score(train_poly, train_target)
    lr.score(test_poly, test_target)			
    
    # 규제를 위한 표준화 과정
    from sklearn.preprocessing import StandardScaler
    ss = StandardScaler()
    ss.fit(train_poly)
    train_scaled = ss.transform(train_poly)
    test_scaled = ss.transform(test_poly)
    
    # 릿지 회귀
    from sklearn.linear_model import Ridge
    ridge = Ridge()
    ridge.fit(train_scaled, train_target)
    print(ridge.score(train_scaled, train_target))
    print(ridge.score(test_scaled, test_target))
    
    # 최적의 alpha 를 이용한 릿지 회귀
    # 1. alpha 찾기
    import matplotlib.pyplot as plt
    train_score = [] 
    test_score = []
    alpha_list = [0.001, 0.01, 0.1, 1, 10, 100]
    for alpha in alpha_list:
        ridge = Ridge(alpha=alpha)
        ridge.fit(train_scaled, train_target) 
        train_score.append(ridge.score(train_scaled, train_target)) 
        test_score.append(ridge.score(test_scaled, test_target))  
    plt.plot(np.log10(alpha_list), train_score)
    plt.plot(np.log10(alpha_list), test_score)
    plt.xlabel('alpha')
    plt.ylabel('R^2')
    plt.show()
    
    # 2. alpha로 재훈련
    ridge = Ridge(alpha=0.1)
    ridge.fit(train_scaled, train_target)
    print(ridge.score(train_scaled, train_target))
    print(ridge.score(test_scaled, test_target))
    
    ##
    
    # 라쏘 회귀
    from sklearn.linear_model import Lasso
    lasso = Lasso()
    lasso.fit(train_scaled, train_target)
    print(lasso.score(train_scaled, train_target))
    print(lasso.score(test_scaled, test_target))
    
    # 최적의 alpha 를 이용한 라쏘 회귀
    # 1. alpha 찾기
    train_score = [] 
    test_score = [] 
    alpha_list = [0.001, 0.01, 0.1, 1, 10, 100]
    for alpha in alpha_list:
        lasso = Lasso(alpha=alpha, max_iter=10000) 
        lasso.fit(train_scaled, train_target) 
        train_score.append(lasso.score(train_scaled, train_target)) 
        test_score.append(lasso.score(test_scaled, test_target)) 
    plt.plot(np.log10(alpha_list), train_score) # blue
    plt.plot(np.log10(alpha_list), test_score) # oragne
    plt.xlabel('alpha')
    plt.ylabel('R^2')
    
    # 2. alpha로 재훈련
    lasso = Lasso(alpha=10)
    lasso.fit(train_scaled, train_target)
    print(lasso.score(train_scaled, train_target))
    print(lasso.score(test_scaled, test_target))
    
    # 3. 계수 0인 특성의 개수
    print(np.sum(lasso.coef_==0))
Designed by Tistory.