[NIPA] 데이터 분석 믹스앤 매치 아카데미 - 활용오프라인 - 웹/앱 서비스 트랙

2020. 12. 2. 04:15통계 & 데이터분석/데이터분석

728x90


NIPA 활용과정 앱 데이터 분석을 통한 평점 예측 실습

 

Kaggle에서 제공하는 구글 앱 데이터를 바탕으로 앱 평점을 예측해보는 머신러닝 실습을 수행해 보겠습니다.

데이터 출처: https://www.kaggle.com/gauthamp10/google-playstore-apps

이번 실습은 다음과 같은 과정으로 진행합니다.

  1. 데이터 읽기: 구글 앱 데이터를 불러오고 Dataframe 구조를 확인

  2. 데이터 정제: 비어 있는 데이터 또는 쓸모 없는 데이터를 삭제

  3. 데이터 시각화: 변수 시각화를 통하여 분포 파악

  4. 데이터 전처리: 머신러닝 모델에 필요한 입력값 형식으로 데이터 처리

  5. 머신러닝 모델 수행: 회귀 모델을 사용하여 학습 수행, 평가 및 예측 수행

이번 실습에서의 최소한의 학습 목표 데이터를 바탕으로 머신러닝의 결과물을 수행하는 전체적인 과정을 이해하기입니다. 더욱 확실하게 목표를 이루기 위해서 학생들에게 기존 데이터에서 랜덤하게 뽑은 테스트 데이터를 바탕으로 배웠던 과정을 수행하여 결과물을 제출하게 합니다. 따라서 모든 학생이 위 목표를 달성 할 수 있는 방향으로 지도 부탁드립니다.

최소한의 학습 목표를 만족할 수 있는 학생이라면 성능 지표를 높일 수 있는 방법을 찾는 것 두 번째 목표가 될 것입니다. 성능 지표를 높일 방법으로 본 실습에서는 크게 2가지 방식이 있습니다.

  1. EDA 과정에서의 데이터 정제 및 특성 엔지니어링을 통한 성능 향상 방식: EDA 과정은 데이터를 이해하는데 있어서 매우 중요한 과정이라 생각합니다. 사실 디테일하게 다루려면 온종일 할 수 있는 분량의 콘텐츠를 제작할 수 있지만, 본 실습을 수행하는데 많은 시간이 주어지지 않는다는 한계가 있기에 최소한의 EDA 과정만 다루게 됩니다. 따라서 변수마다 추가적인 이상치 처리 및 특성 엔지니어링을 통하여 성능을 높일 방법은 다양하게 있다 생각하며, 이 부분은 강의자께서도 여러 가지로 시도해보시면서 학생들에게 인사이트를 주면 좋을 거 같습니다.
  1. 머신러닝 모델을 통한 성능 향상 방식: EDA를 통한 데이터에 대한 이해 이후로 머신러닝 특징에 맞추어 모델을 선택하고 다양한 튜닝 방법을 통하여 성능을 높일 수 있습니다. 하지만 본 과정을 수강하는 학생들은 머신러닝 모델별로의 심도 있는 이해는 못 하는 단계라고 생각하기에 이 방식을 통한 성능 향상은 구현하기는 쉽지 않아 보입니다.
!pip install --upgrade pip
!pip install -U pandas
!pip install -U numpy
!pip install -U scipy
!pip install -U scikit-learn
!pip install xgboost
!pip install lightgbm

1. 데이터 읽기

이 파트에서는 데이터를 읽는 방법을 익히고 어떠한 변수가 있는지 확인하는 방법을 수행하는 것을 목표로 합니다. head, info 등의 함수에 대해서 설명해주시고 강의자분이 또 다른 방법들을 추가하여 설명하셔도 될 거 같습니다.

 

pandas를 사용하여 Google-Playstore.csv 데이터를 읽고 dataframe 형태로 저장해 봅시다.

from google.colab import drive
drive.mount('/content/drive')

 

import os
import numpy as np 
import pandas as pd 
import matplotlib.pyplot as plt
import seaborn as sns

1.1 디렉토리 체크

작업을 하기 전에 현재 디렉토리가 어떻게 설정되어있는지 확인해줍니다.

current_path = os.chdir("/content/drive/MyDrive/Colab Notebooks/강의자료")
print(os.listdir(current_path))
print(os.getcwd()) #=> get current working directory

1.2 Google-Playstore.csv 데이터 불러오기

# Google-Playstore.csv 데이터를 pandas를 사용하여 dataframe 형태로 불러옵니다.
df_origin = pd.read_csv('Google-Playstore.csv')

1.3 dataframe 및 요약본 출력해보기

# 5개의 데이터 샘플을 출력합니다.
df_origin.head() #앞 5개
df_origin.tail() #뒤 5개
df_origin[0:5] #슬라이싱으로 원하는 부분 불러오기

 

# dataframe의 정보를 요약해서 출력합니다.
df_origin.info()
# row 개수와 개수가 다른 변수들이 있음 => 비어있음 => 나중에 처리

 

# 수치형 변수의 데이터 정보를 요약하여 출력합니다.
df_origin.describe() # => float 타입의 데이터들만 요약할 수 있으므로 이 변수들만 있음


2. 데이터 정제

일반적으로 각 변수의 분포를 살펴보며 데이터 정제를 수행하는 것이 바르다고 생각하지만, 학생들이 EDA 방법론에 대한 지식이 부족하다고 생각합니다. 따라서 이번 과정에서는 결측값과 이상치에 대한 개념을 소개하고 이를 처리하는 방식에 대해서 알아가는 파트라 생각하시면 될 거 같습니다.

여기서 다루지 않은 결측값 처리 방식 등에 대해서도 간단하게 소개해주시면 될 거 같습니다. (시간이 있다면요)

데이터 정제에서는 결측값(missing value) 또는 이상치(outlier)를 처리합니다.

일반적으로는 각 변수에 대해서 자세히 살펴보면서 결측값과 이상치를 제거를 수행합니다.

이번 파트에서는 간단한 결측값 처리 방식인 삭제 방식을 수행해 보고 다음 과정에서 이어서 데이터 정제를 수행해보겠습니다.

 

2.1. 변수 삭제

이번 실습에서는 23개의 변수 중에 12개의 App Name, App Id, Developer Id, Developer Website, Developer Email, Currency, Size, Released, Privacy Policy, Price, Minimum Installs, Maximum Installs 을 삭제하여 진행합니다.

나머지 11개의 변수들은 이후 실습시간에 모두 사용하여 모델링을 하도록 합니다

# drop 함수를 사용하여 변수들을 삭제합니다.
df_clean = df_origin.drop(columns = [
    'App Name', 'App Id', 'Developer Id', 'Developer Website', 'Developer Email', 'Currency', 'Size',
    'Released', 'Privacy Policy','Price', 'Minimum Installs', 'Minimum Android', 'Maximum Installs'], axis=1)
df_clean

 

2.2. 결측값 확인

아래 코드를 수행하여 각 변수별로 결측값이 몇개가 있는지 확인합니다.

# 결측값 정보를 출력합니다.
# df_clean.isnull()
df_clean.isnull().sum()

# 'Rating'에 있는 결측값 데이터에서 'Rating Count' 변수의 데이터를 출력합니다.
# (1) df_clean['Rating'].isnull()
# (2) df_clean[df_clean['Rating'].isnull()]
# (3) df_clean[df_clean['Rating'].isnull()]['Rating Count']
# (4)
df_clean[df_clean['Rating'].isnull()]['Rating Count'].unique()

 

2.3. 결측값 처리

결측값 처리 방법 → 분포 파악 & 도메인 지식 활용


  1. 삭제
  2. 평균값 처리
  3. 중간값 처리
  4. 도메인 지식으로 특정값으로 처리

등등

# dropna를 사용하여 결측값이 있는 샘플들을 삭제합니다.
df_clean = df_clean.dropna()

# 결측값을 정보를 출력합니다.
df_clean.isnull().sum() #=>개수확인

3. 데이터 시각화 & 추가 데이터 정제

가장 시간이 많이 들어갈 파트라 생각합니다. 가장 기본적으로 numerical 데이터와 object 데이터의 차이점에 대해서 알려주시고 각 데이터 형태에 따라 시각화 방식으로 어떤 것이 있는지 알려주시는 방식으로 지도하시면 될 거 같습니다.

본 실습에서는 각 변수에 대한 분포만을 시각화 하였지만, 여유가 있다면 변수 간의 시각화도 하면 좋을 거 같습니다. 특히 label인 Rating 데이터와의 시각화 방식을 통하여 거시적인 경향성을 볼 수 있다는 점을 알려주시면 좋을 거 같습니다.

3.1. Rating & Rating Count에서 0인 데이터가 있기에 이들은 추가 데이터 정제를 수행하여 삭제합니다.

3.2. Installs 데이터는 문자형 데이터이기에 수치형 데이터로 변환합니다.

3.3. Last Updated 데이터에는 너무나 다양한 시간 정보가 하나로 저장되어 있기에 의미 있는 변수로 사용하기 위하여 올해 업데이트가 되었는지를 알 수 있는 Last update since 2020를 추가합니다.

 

각 변수 분포를 알아보기 위하여 시각화를 수행하겠습니다.

시각화를 통해서 변수 내에 결측값 또는 이상치 처리가 필요하다 생각되면 이를 수행하고 또한 변수의 문자형 데이터를 숫자형 데이터로 변환하는 특성 엔지니어링(Feature engineering) 또한 수행해보겠습니다.

 

3.1. Rating & Rating Count 시각화

# 분포를 도수분포표로 확인합니다.
df_clean['Rating'].value_counts()

# 분포를 막대 그래프를 사용하여 출력합니다.
df_clean['Rating'].value_counts().plot(kind='bar')

# Rating 값이 0인 데이터의 개수를 출력합니다.
len(df_clean[df_clean['Rating'] == 0])

# 163968
# 데이터 확인
df_clean['Rating Count'].value_counts()

# 'Rating Count' 값이 10보다 큰 경우 부터 의미 있는 데이터로 생각하고 10보다 작은 데이터는 삭제합니다.
df_clean = df_clean[df_clean['Rating Count'] > 10]
# 그래프 사이즈 설정
fig, ax = plt.subplots(figsize=(10, 6))  #이렇게 하면 매 셀마다 설정해줘야 함 => 커스터마이징 https://matplotlib.org/tutorials/introductory/customizing.html

# 추가 정제한 분포를 막대 그래프를 사용하여 출력합니다.
df_clean['Rating'].value_counts().sort_index(ascending=False).plot(kind='bar')

# 추가 정제한 분포를 도수분포표로 확인합니다.
df_clean['Rating'].value_counts().sort_index(ascending=False)

# 그래프 사이즈 설정
fig, ax = plt.subplots(figsize=(10, 6))

# pd.cut을 사용하여 데이터를 분리하고 그 분포를 막대 그래프로 출력합니다.
pd.cut(df_clean['Rating Count'], np.arange(10,410,20)).value_counts().sort_index(ascending=True).plot(kind='bar')

 

3.2. Installs 시각화

# 분포를 막대 그래프를 사용하여 출력합니다.
df_clean['Installs'].value_counts().plot(kind='bar')

# 분포를 도수분포표로 확인합니다.
df_clean['Installs'].value_counts().sort_index(ascending=True)

'''
0+                    14
1+                    15
1,000+             61563
1,000,000+         20348
1,000,000,000+        41
10+                  261
10,000+           107995
10,000,000+         4327
100+                7526
100,000+           58985
100,000,000+         388
5+                     8
5,000+             45549
5,000,000+          4386
5,000,000,000+        13
50+                  620
50,000+            37535
50,000,000+          598
500+               10074
500,000+           15335
500,000,000+          48
Name: Installs, dtype: int64
'''
# 문자열 데이터를 숫치 자료형으로 변환합니다.
# + 삭제, 쉼표 삭제, int형 변화
for element in set(df_clean['Installs']):
    # (1) element.split('+')
    # (2) element.split('+')[0]
    # (3) element.split('+')[0].replace(',', '')
    # (4) int(element.split('+')[0].replace(',', '')))
    # (5) 
    df_clean = df_clean.replace(element,int(element.split('+')[0].replace(',', '')))
# 변환된 분포를 막대 그래프를 사용하여 출력합니다.
df_clean['Installs'].value_counts().sort_index(ascending=True).plot(kind='bar')

# 변환된 분포를 도수분포표로 확인합니다.
df_clean['Installs'].value_counts().sort_index(ascending=True)

'''
0                 14
1                 15
5                  8
10               261
50               620
100             7526
500            10074
1000           61563
5000           45549
10000         107995
50000          37535
100000         58985
500000         15335
1000000        20348
5000000         4386
10000000        4327
50000000         598
100000000        388
500000000         48
1000000000        41
5000000000        13
Name: Installs, dtype: int64
'''

# 위 분포에서 양쪽 끝의 데이터들이 너무 작기에 이를 추가로 삭제합니다.
# 500000개 초과는 1000000으로 변환
df_clean.loc[df_clean['Installs']>500000,'Installs'] = 1000000
df_clean[df_clean['Installs']>500000]

# 5000개 미만은 1000으로 변환
df_clean.loc[df_clean['Installs']<5000,'Installs'] = 1000
df_clean[df_clean['Installs']<5000]
# 다시 분포를 출력합니다.
df_clean['Installs'].value_counts().sort_index(ascending=True).plot(kind='bar')
df_clean['Installs'].value_counts().sort_index(ascending=True)

 

3.4. Last Updated 시각화

# 분포를 도수분포표로 확인합니다.
df_clean['Last Updated'].value_counts()

'''
2020-04-15 19:45:13    4
2020-07-13 07:38:00    4
2020-07-13 07:39:47    4
2020-07-13 07:38:25    4
2020-07-08 14:07:44    4
                      ..
2019-01-28 20:47:19    1
2019-01-10 15:38:31    1
2017-04-14 15:35:52    1
2020-07-21 13:24:44    1
2020-05-10 10:29:06    1
Name: Last Updated, Length: 373779, dtype: int64
'''
# 날짜와 시간 정보인 Last Updated를 가공하여 새로운 정보를 갖는 데이터를 만들어 저장해봅시다.
# 간단하게 올해 업데이트가 되었는지 안되었는지를 알 수 있는 'Last update since 2020' 변수를 만들어 추가해봅시다.

# 1. df_clean['Last Updated']
# 2. for d in df_clean['Last Updated']:
#       print(d)

#3. for d in df_clean['Last Updated']:
#       print(d.split("-")[0])

#4. [d.split('-')[0] == '2020' for d in df_clean['Last Updated']]
df_clean['Last update since 2020'] = [d.split('-')[0] == '2020' for d in df_clean['Last Updated']]

4. 데이터 전처리

머신러닝 모델을 사용하기 위해서 데이터를 준비하는 파트입니다. 본 실습에서는 특성 엔지니어링 기법 중 더미(dummy) 방식과 학습&테스트 데이터를 분리 하는 과정을 다룹니다.

본 실습에서의 더미에서는 어떠한 데이터를 머신러닝의 feature로 사용할 것인가를 결정하는 중요한 파트라 생각합니다. 0,1만을 사용하는 더미 방식 외에 object 자료형을 변환하는 다양한 기법들이 있고 이를 어떻게 적용하느냐에 따라 성능에 큰 영향을 줄 수 있다는 지도가 있었으면 합니다.

학습, 테스트 분리에서는 왜 데이터를 분리에서 사용하는지에 대한 설명이 해주시면 좋을 거 같습니다.

추가로 데이터 정규화를 수행하는데 StandardScaler 이외의 minmax 정규화 등의 다양한 기법들을 소개해주시고 왜 정규화가 필요한지를 지도하면 좋을 거 같습니다.

앱 평점 예측을 수행하기 위해서 주어진 구글 앱 데이터에 대해서 회귀 모델을 사용할 것입니다.

이번 파트에서는 11개의 변수만을 사용하여 예측을 수행하지만, 나머지 12개의 변수들을 추가하여 입력 데이터를 확장할 수 있습니다.

회귀 모델의 필요한 입력 데이터를 준비 하기위해서 다음과 같은 전처리를 수행하겠습니다.

  1. Object 자료형 -> 숫자 자료형 변환하기
  2. 학습 데이터와 테스트 데이터로 나누기

 

4.1. 더미변수(Dummy Variable)를 활용하여 Object 자료형을 수치형으로 변환


일반적인 머신러닝 모델에서는 수치 자료형만을 입력값으로 사용합니다. 따라서 수치 자료형만을 사용하여 입력값으로 사용할 수 있지만, 신용 예측을 위한 데이터에 존재하는 Content Rating 변수의 데이터들은 중요한 정보일 수 잇는 object 자료형을 사용할 수가 없습니다.

그렇기에 이러한 object 자료형을 수치 자료형으로 변환하는 dummy 방식을 사용하여 다양한 데이터를 수치형 입력으로 사용해 봅시다.

# 2개의 카테고리를 갖는 데이터는 replace를 사용하여 0,1로 변환 합니다.
df_clean = df_clean.replace([True,False],[1,0])

# object 자료형 데이터의 변수를 정리합니다.
chr_features = ['Content Rating']

# 수치 자료형 데이터의 변수를 정리합니다.
num_features=['Installs', 'Free','Last update since 2020', 'Ad Supported','In App Purchases', 'Editors Choice']

# 더미를 기법을 사용하여 변환합니다.
for variable in chr_features:
    
    # pandas의 더미 방식을 사용하여 object 자료형 데이터를 변환한 dataframe을 생성합니다.
    dummies = pd.get_dummies(df_clean[chr_features])
    # 기존 수치형 데이터에 더미로 새로 생성된 데이터를 추가합니다.
    df_dummy= pd.concat([df_clean[num_features], dummies],axis=1)
df_dummy.columns
'''
Index(['Installs', 'Free', 'Last update since 2020', 'Ad Supported',
       'In App Purchases', 'Editors Choice', 'Content Rating_Adults only 18+',
       'Content Rating_Everyone', 'Content Rating_Everyone 10+',
       'Content Rating_Mature 17+', 'Content Rating_Teen',
       'Content Rating_Unrated'],
      dtype='object')
'''
# 더미로 구한 데이터를 numpy 형태로 머신러닝 입력값인 x에 저장합니다.
x = df_dummy.to_numpy()
print(np.shape(x))
'''
(375629, 12)
'''
# 예측해야 할 변수 `Rating`만을 선택하여 numpy 형태로 y에 저장합니다.
y = df_clean['Rating']
#dimension check
print(np.shape(y.to_numpy()))
y = y.to_numpy()
#y = y.to_numpy().ravel() # 1 차원 벡터 형태로 출력하기 위해 ravel 사용 => 이미 1차원이므로 딱히 필요 없음

 

4.2. 학습, 테스트 데이터 분리


머신러닝의 성능을 평가 하기 위해서는 전체 데이터를 학습에 사용하지 않고 학습용 데이터와 테스트용 데이터를 나누어 사용합니다.

 

<Train set과 Test set을 나누는 이유>

우리는 새로운 데이터에 이 모델이 얼마나 잘 들어맞는지 알고 싶습니다. 그런데 가지고 있는 데이터를 모두 모델 만드는데 써버리면, 그 모델이 새로운 데이터에 얼마나 잘 들어맞는지 모르게 됩니다. 그래서 보유한 데이터에서 test set을 따로 떼어놓고 training set으로만 모델을 만든 다음, test set으로 새로운 데이터에 얼마나 잘 들어맞는지 확인합니다

from sklearn.model_selection import train_test_split

# sklearn에서 제공하는 train_test_split을 사용하여 손 쉽게 분리 할 수 있습니다.
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.3, random_state = 0)

 

4.3. 데이터 정규화

정규화 이유

1. 정규화하지 않으면 스케일이 큰 변수가 영향을 크게 받아 다른 변수를 잡아먹는다

2. 단위가 모두 다르기 때문에 모든 변수를 같은 스케일로 맞춤

    a. min-max

    b. standardization → 표준정규분포로 만들기

from sklearn.preprocessing import StandardScaler #사이킷런

# 정규화를 위해서 StandardScaler 불러오기
sc = StandardScaler()

# x_train에 있는 데이터에 맞춰 정규화를 진행
x_train = sc.fit_transform(x_train)
x_test = sc.transform(x_test)

5. 머신러닝 모델 수행

머신러닝 모델의 경우에는 단순 선형 회귀, 다항 회귀, 트리모델 간의 장단점을 위주로 설명해주셨으면 좋겠습니다. 각 모델의 원리와 파라미터와 특징에 대해서 설명하게 되면 너무나 많은 힘이 들기에 간단하게 sklearn에서의 사용법과 평가 방식에 힘을 들여 설명해주시면 좋을 거 같습니다.

평가 방식으로는 RMSE와 R2를 사용하기에 이에 대한 설명을 꼭 해주셨으면 좋겠습니다.

단순선형회귀

from sklearn.linear_model import LinearRegression

# 선형 회귀 모델 class를 불러옵니다.
model = LinearRegression()

# fit 함수를 사용하여 학습을 수행합니다.
model.fit(x_train, y_train)


# -- 평가 -- #
'''
결정계수(R^2)
'''
from sklearn.metrics import r2_score, mean_squared_error

# 학습 데이터의 예측값을 저장합니다.
pred_train = model.predict(x_train)

# 학습 데이터의 성능을 저장합니다.
mse_train = mean_squared_error(y_train, pred_train)
r2_train = r2_score(y_train, pred_train)

# 테스트 데이터의 예측값을 저장합니다.
pred_test = model.predict(x_test)

# 테스트 데이터의 성능을 저장합니다.
mse_test = mean_squared_error(y_test, pred_test)
r2_test = r2_score(y_test, pred_test)

# 성능을 출력합니다.
print('train data RMSE: ', np.sqrt(mse_train))
print('train data R2: ', r2_train)

print('test data RMSE: ', np.sqrt(mse_test))
print('test data R2: ', r2_test)

 

예측

# 0번부터 9번까지 10개를 출력해 보겠습니다.
for i in range(10): 
    
    #1.prediction = model.predict(x_test[i]) => 에러
    #2.prediction = model.predict(x_test[i].reshape(1,-1))
    #3.print(predition)
    #4.print(prediction[0])

    prediction = model.predict(x_test[i].reshape(1,-1))
    print("{} 번째 테스트 데이터의 예측 결과: {}, 실제 데이터: {}".format(i, prediction[0], y_test[i]))
    
 print(model.coef_)

 

다항회귀

from sklearn.preprocessing import PolynomialFeatures

# 우리가 위에서 사용한 모델은 선형회귀 => 직선
# 다항 회귀 모델을 사용하기 위해서는 PolynomialFeatures를 사용하여 고차항 변수를 생성합니다.
# interaction_only=True 를 사용하면 변수 간의 곱을 고려하지 않기에 모델이 간소화 됩니다.
# interaction_only=False 사용 시 모델은 복잡해지고 학습 시간이 매우 증가합니다. => 계량적(?)으로 고려해야한다. interaction Term
poly = PolynomialFeatures(degree=3, interaction_only=True) #데이터가 3차원식으로 생겼을 때 이렇게 쓰면 선형보다 잘 나올 것임

# x_train 데이터를 다항 회귀에 필요한 입력값으로 변환합니다.
x_train_poly = poly.fit_transform(x_train)
x_train_poly


# 선형 회귀 모델을 선언합니다.
# 선형 회귀 모델이지만 다항 회귀도 수행할 수 있습니다.
# n_jobs=-1을 설정하여 모든 프로세서를 사용하여 학습의 속도를 높입니다. #멀티코어 #default = 싱글코어
model_poly = LinearRegression(n_jobs=-1)

# 학습을 수행합니다.
model_poly = model_poly.fit(x_train_poly, y_train)

 

5.3. 의사결정나무 & 랜덤포레스트 & 앙상블기법들

!pip install xgboost
!pip install lightgbm
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor
from xgboost.sklearn import XGBRegressor
from lightgbm import LGBMRegressor

models = []
models.append(('CART', DecisionTreeRegressor())) # 의사결정나무 모델 #CART는 의사결정나무 중 알고리즘 이름
models.append(('RandomForest', RandomForestRegressor(n_jobs=-1))) # 랜덤포레스트 모델
models.append(('XGB', XGBRegressor(n_jobs=-1))) 
models.append(('lightGBM', LGBMRegressor(n_jobs=-1)))

for name, model in models:
    model.fit(x_train, y_train)
    model.predict(x_train)

    pred_train = model.predict(x_train)
    pred_test = model.predict(x_test)

    mse_train = mean_squared_error(y_train, pred_train)
    mse_test = mean_squared_error(y_test, pred_test)

    r2_train = r2_score(y_train, model.predict(x_train))
    r2_test = r2_score(y_test, model.predict(x_test))

    result_1 = "%s - train_RMSE_score : %f, test_RMSE_score : %f" % (name, mse_train, mse_test)
    result_2 = "%s - train_R2_score : %f, test_R2_score : %f" % (name, r2_train , r2_test) 
    print(result_1)
    print(result_2)
    print("="*80)
    
    
    
    
# 각 모델마다 0번부터 9번까지 10개의 예측 결과를 출력해 보겠습니다.

for i in range(len(models)):
    print("{} 모델".format(models[i][0]))
    for j in range(10): 
    
        prediction = models[i][1].predict(x_test[j].reshape(1,-1))
        print("{} 번째 테스트 데이터의 예측 결과: {}, 실제 데이터: {}".format(j, prediction[0], y_test[j]))
    print("="*80)