재형이의 성장통 일지
  • 자전거 대여량 예측 - 선형 회귀, 군집 모델 (클러스터링) 실습
    2024년 03월 15일 06시 42분 26초에 업로드 된 글입니다.
    작성자: 재형이
    반응형
     

     

    • 가보자 가보자~

     

     

     

     

     

     


     

     

     

     

     

     

     

    1. 자전거 대여량 예측 - 선형회귀

    • 다음과 같은 항목들로 이루어져 있는 데이터를 사용해볼 것이다
      • 시간 - 시간별 타임 스탬프
      • 계절 - 1 = 봄, 2 = 여름, 3 = 가을, 4 = 겨울
      • 휴일 - whether the day is considered a holiday
      • 주간 - whether the day is neither a weekend nor holiday
      • 날씨 -
        • 1: 맑음, Few clouds, Partly cloudy, Partly cloudy
        • 2: 안개낌 + Cloudy, Mist + Broken clouds, Mist + Few clouds, Mist
        • 3: 약간 눈내림, 약간 비 + 천둥번개 + Scattered 구름, 약간의 비 + Scattered 구름
        • 4: 소나기 + 우박 + 천둥번개 + 안개, 눈 + 짙은 안개
      • 온도 - temperature in Celsius
      • 체감온도 - "feels like" temperature in Celsius
      • 습도 - relative humidity
      • 풍량 - wind speed
      • 미가입 - number of non-registered user rentals initiated
      • 가입 - number of registered user rentals initiated
      • 렌탈수 - number of total rentals (Dependent Variable)
    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    import seaborn as sns
    
    df = pd.read_csv('exercise2.csv')
    df.head()

    • df.shape로 총 행의 갯수와 열의 갯수를 파악해보자

    10886개의 행과 12개의 열로 이루어져있다는 것을 알 수 있다

    • df.describe()로 데이터들이 어떻게 분포되어 있는지 확인해보자

    • 우리가 구하고자 하는 값인 count를 기준으로 시각화해보자

    • 각 항목들 별로 시각화
    nrows, ncols = 2, 5
    fig, axs = plt.subplots(nrows=nrows, ncols=ncols)
    fig.set_size_inches(20,8)
    
    for i in range(nrows):
        for j in range(ncols):
            attr = i * ncols + j + 1
            sns.histplot(x = df.columns[attr], data = df, ax=axs[i][j])

    • 히트맵으로 시각화
    sns.heatmap(df.corr())

    • 이제 데이터를 분할해보고 모델을 불러와서 학습시켜보자
    from sklearn.model_selection import train_test_split
    from sklearn.linear_model import LinearRegression
    from sklearn.metrics import mean_squared_log_error
    
    Y = df['count']
    X = df.drop(['datetime', 'count'], axis=1,inplace=False)
    
    X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.3, random_state=42)
    
    lr_model = LinearRegression()
    lr_model.fit(X_train, y_train)
    y_pred = lr_model.predict(X_test)
    
    mean_squared_log_error(y_test, y_pred)
    
    # 2.2538764933380931e-29
    • 거의 0에 수렴하는 오차값이 나왔다...? 띠용? 성능 무엇?
    • 사실 데이터 전처리를 제대로 하지 않고 학습을 진행했기 때문에 성능이 좋게 나온 것이다...
    • 어떤 값이 영향을 가장 많이 주었는지 살펴보면 회원인지 아닌지가 가장 영향을 많이 준 것을 확인 할 수 있다. 하지만 count = registered + casual 이기 때문에 사실 registered랑 casual은 독립 변수가 아니다. 이 값들 또한 우리가 예측해야할 값들이기 때문에 drop 시켜줘야 한다
    coef = pd.Series(lr_model.coef_, index=X.columns)
    coef_sort = coef.sort_values(ascending=False)
    sns.barplot(x=coef_sort.values, y=coef_sort.index)

    1-1. 코드 개선하기

    • 뿐만 아니라, 계절이 1,2,3,4로 되어 있는데 이런 식으로 모델에게 넣어주면 모델은 봄과 겨울이 1과 4이므로 서로 멀리 떨어져 있는 관계를 가지는구나라고 불필요한 선수지식을 알려주게 된다 → 원핫 인코딩 처리
    • 그리고 단순 Linear Regressor 뿐만 아니라 다양한 모델들을 사용해서 어떤 모델이 가장 성능이 좋은지 찾아보자
    # datetime 또한 사용하기 위해 전처리
    df['datetime'] = df['datetime'].astype('datetime64')
    df['year'] = df['datetime'].dt.year
    df['month'] = df['datetime'].dt.month
    df['day'] = df['datetime'].dt.day
    df['hour'] = df['datetime'].dt.hour
    
    # 불필요한 feature 삭제
    df.drop(['datetime','casual','registered'], axis=1,inplace=True)
    • 그리고 현재 count 분포를 보면 정규분포의 형태가 아니다. 선형회귀는 정규분포의 형태를 나타낼 때 가장 성능이 좋으므로 log를 취해서 비슷하게 만들어주자. 실제 값을 사용해야할 때는 exponential를 취해주어야 한다.
    y_log = np.log1p(Y)
    
    nrows, ncols = 1, 2
    fig, axs = plt.subplots(nrows=nrows, ncols=ncols)
    fig.set_size_inches(10, 4)
    sns.histplot(Y, ax = axs[0])
    sns.histplot(y_log, ax = axs[1])

    • 위에서 말한대로 원핫 인코딩이 필요한 값들은 인코딩 진행 → get_dummies 메서드 활용
    df = pd.get_dummies(df, columns=['year', 'month', 'day', 'hour', 'holiday',
                                                  'workingday','season','weather'])

    • 이제 학습을 시킨 후에 오차값을 비교해보자
    from sklearn.preprocessing import StandardScaler
    from sklearn.ensemble import RandomForestRegressor
    from xgboost import XGBRegressor
    from lightgbm import LGBMRegressor
    
    Y = y_log
    X = df.drop(['count'], axis=1,inplace=False)
    
    sc = StandardScaler()
    scaled_X = sc.fit_transform(X)
    
    X_train, X_test, y_train, y_test = train_test_split(scaled_X, Y, test_size=0.3, random_state=42)
    
    lr_model = LinearRegression()
    rf_model = RandomForestRegressor(random_state = 42)
    xgb_model = XGBRegressor(random_state = 42)
    lgbm_model = LGBMRegressor(random_state = 42)
    
    model_list = [lr_model, rf_model, xgb_model, lgbm_model]
    for model in model_list:
        model.fit(X_train, y_train)
        y_pred = model.predict(X_test)
        m = model.__class__.__name__
        score = mean_squared_log_error(np.expm1(y_test), np.expm1(y_pred))
        print('{0} msle: {1:.3f}'.format(m, score))
    • LinearRegression msle: 0.342
    • RandomForestRegressor msle: 0.125
    • XGBRegressor msle: 0.119
    • LGBMRegressor msle: 0.110
    • 여기서는 LGBMRegressor 이 가장 성능이 좋다는 것을 알 수 있다

    2. 고객 멤버십 분류하기 - 클러스터링 실습

    • 고객들의 연평균 소득(그냥 알아냈다고 생각)과 소비량을 기준으로 클러스터링해서 어느 군집에 속해있느냐에 따라 멤버십을 정해보자
    • 여기서도 사이킷런을 사용한다
    • 데이터는 다음과 같다
    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    import seaborn as sns
    
    df = pd.read_csv('exercise3.csv')
    df.head()
    
    df.shape
    # (200, 5)

    • df.info(): 데이터 값들의 정보를 더 자세히 확인

    • 연평균 소득에 따른 소비 점수를 시각화해서 볼 수 있다
    sns.scatterplot(x= 'Annual Income (k$)',  y = 'Spending Score (1-100)', data = df)

    • 모델을 실제로 학습해보자
    • k 값의 따른 실루엣 점수를 비교해가며 최적의 k 값을 찾아보자
    • 실루엣 점수는 0~1의 값이며 1에 가까울수록 좋다
    from sklearn.preprocessing import StandardScaler
    from sklearn.cluster import KMeans
    from sklearn.metrics import silhouette_samples, silhouette_score
    
    X= df.iloc[:,3:]
    sc = StandardScaler()
    X = sc.fit_transform(X)
    
    silhouette_avg = []
    for k in range(10):
        model = KMeans(n_clusters= k+2, random_state=42)
        y_preds = model.fit_predict(X)
        score = silhouette_score(X, y_preds)
        silhouette_avg.append(score)
        print("군집개수: {0}개, 평균 실루엣 점수: {1:.4f}".format(k+2, score))
        
    # 군집개수: 2개, 평균 실루엣 점수: 0.3147
    # 군집개수: 3개, 평균 실루엣 점수: 0.4666
    # 군집개수: 4개, 평균 실루엣 점수: 0.4939
    # 군집개수: 5개, 평균 실루엣 점수: 0.5547
    # 군집개수: 6개, 평균 실루엣 점수: 0.5399
    # 군집개수: 7개, 평균 실루엣 점수: 0.5263
    # 군집개수: 8개, 평균 실루엣 점수: 0.4558
    # 군집개수: 9개, 평균 실루엣 점수: 0.4553
    # 군집개수: 10개, 평균 실루엣 점수: 0.4476
    # 군집개수: 11개, 평균 실루엣 점수: 0.4385
    • k가 5일 때 가장 좋은 성능을 보여준다는 것을 알 수 있다
    plt.plot(range(2,12), silhouette_avg, 'bo--')
    plt.xlabel('# of clusters')
    plt.ylabel('silhouette_avg')
    plt.show()

    • k가 5일 때, 데이터 분포를 시각화해보자
    model = KMeans(n_clusters= 5, random_state=42)
    y_preds = model.fit_predict(X)
    df['cluster'] = y_preds
    
    sns.scatterplot(x = 'Annual Income (k$)', y = 'Spending Score (1-100)', data = df, hue = 'cluster', palette="deep")

    • 이 결과값을 가지고 다양한 지표를 통해 시각화하면 멤버십을 분류할 때 도움을 받을 수 있다
    • 각 군집에 따른 소비 점수 시각화
    sns.barplot(x = 'cluster', y = 'Spending Score (1-100)', data = df)

    • 각 군집에 따른 연평균 소득
    sns.barplot(x = 'cluster', y = 'Annual Income (k$)', data = df)

    • 각 군집에 따라 성별 분포가 어떻게 되어 있는지 시각화
    sns.countplot(x='cluster', data = df, hue = 'Gender')

    • 각 군집에 따라 나이 분포가 어떻게 되어 있는지 시각화
    sns.swarmplot(x = 'cluster', y='Age', data = df)

     

     

     

     

     

     

     

     

     


     

     

     

     

     

     

     

     

    반응형
    댓글