티스토리 뷰

Study/AI

[ML] Board Game Review Prediction

생각많은 소심남 2019. 2. 7. 22:37

 이번 포스트에서는 Board Game Geek 이란 사이트에서 제공되는 보드 게임별 리뷰 점수 정보를 활용해서 리뷰 점수를 예측하는 것을 해보고자 한다. 물론 정보를 가지고 있는 데이터를 활용해서 예측을 할 것이기 때문에 Supervised Learning이 될 것이고, 이 중에서 Linear Regression와 Random Forest Regression를 사용해보려고 한다. 참고로 Random Forest Regression은 Linear Regression으로 해결할 수 없는 Non-Linear Relation에 대한 예측을 하기 위해 사용된다.

 참고로 해당 데이터는 오픈소스로 공개되어 있는 Scrappers를 활용해서 데이터를 긁어왔으며, 데이터에 대한 자세한 정보는 다음 링크를 참조하면 좋을 것 같다. (MIT라이선스로 되어 있어, 자유롭게 사용이 가능하다.)

games.csv

사용된 라이브러리와 각각의 버전은 다음과 같다.

이 중 몇가지 라이브러리는 alias를 해주거나 필요한 함수만 import 시켜준다.

  1. # import libraries
  2. import matplotlib.pyplot as plt
  3. import pandas as pd
  4. import seaborn as sns
  5. from sklearn.model_selection import  train_test_split

그리고 앞에서 언급한 데이터를 pandas를 활용해서 가져온다. 그러면 해당 csv파일은 pandas의 DataFrame format에 맞춰서 저장이 된다. (참고로 위의 csv파일은 코드와 동일한 경로에 위치시켜야 한다. 아니면 FILE PATH에 원하는 경로를 넣어준다.)

  1. # Load the data
  2. FILE_PATH = './games.csv'
  3. games = pd.read_csv(FILE_PATH)

 사실 데이터가 정상적인지 여부는 직접 csv 파일을 열어서 확인해볼 수도 있지만, 앞에서 가져온 DataFrame에서 column이 정확히 있는지, 그리고 전체 shape가 0이 아닌지만 확인해보면 된다.

  1. # Print the names of the columns & shapes in games
  2. print(games.columns)
  3. print(games.shape)

 여기까지 해보면 전체 데이터가 81312개 정도 되고, column이 20개 정도로 구성된 데이터라는 것을 확인할 수 있다. 여기서 유심히 봐야 할 것은 어떤 데이터 필드를 가지고 있는지다. 여기서 예측하는데 필요한 데이터 필드만 뽑아서 Regression해야 한다.

이중에서 average rating 데이터를 사용하면 좋을거 같은데, 일단 전체 데이터중에서 해당 데이터의 분포를 살펴보면 다음과 같다.

이렇게 보면 0점대에 몰려있는 게임들도 있지만, 대부분은 평균 7 정도의 정규분포를 띄는 것을 확인할 수 있다. 그런데 위의 결과만 놓고 보면 0주위의 결과가 regression시 영향을 줄 수 있을거 같다. 실제로 다음과 같이 average_rating이 0인 데이터 한개를 뽑아보고, 아닌 데이터도 뽑아보면 좋을거 같다.

  1. # Print the first row of all the games with zero scores
  2. print(games[games['average_rating'] == 0].iloc[0])
  3.  
  4. # Print the first row of games with scores grater than 0
  5. print(games[games['average_rating'] > 0].iloc[0])

이러면 이상한 데이터가 하나 나온다.

위에 보면 Looney Leo란 게임은 published 되지 않은 상태인데도, average_rating이 0으로 매겨져 있고, 단순히 wisher 하나만 체크되어 있다. 반면 밑에 있는 Twilight Struggle이란 게임은 모든 수치가 정상적으로 나와있다. 결국 우리가 사용하고 있는 데이터 중에서도 필요없는 데이터는 배제시켜야 한다. 일단 필요없는 데이터의 기준은 user review가 있는지 여부를 따르는 것으로 해보자. 그리고 추가로 값이 비어있는 데이터도 제거해보자. 이건 pandas에서 제공하는 dropna를 사용하면 된다. (물론 데이터를 함부로 제거하거나 그러는 것도 좋지는 않다. 이부분은 직접 csv를 열고 어떤 데이터를 빼면 좋을지, 또는 어떤 데이터가 의미있는지 판단한 후에 결정하는 것이 좋다.)

  1. # Remove any rows without user reviews
  2. games = games[games['users_rated'] > 0]
  3.  
  4. # Remove any rows with missing values
  5. games = games.dropna(axis=0)
  6.  
  7. # Make a histogram of all the average ratings
  8. plt.hist(games['average_rating'])
  9. plt.show()

그러면 아래와 같이 결과가 딱 정규분포를 띄게끔 나오는 것을 확인할 수 있다.

이제 불필요한 데이터도 제거했으니, 각 column별 상관관계를 파악해서 어떤 열이 average_rating에 영향을 많이 끼치는지 확인해볼 필요가 있다. 이때 사용하는 것이 correlation matrix다. correlation matrix는 간단히 말해 각 항목간 상관관계를 0에서 1사이의 값으로 정의한 정보이다. 보통은 행렬형식으로 나오는데, 앞에서 import 시킨 seaborn의 heatmap을 사용하면 해당 행렬을 시각화시킬 수 있다.

  1. # Correlation matrix
  2. corrmat = games.corr()
  3. fig = plt.figure(figsize=(12, 9))
  4.  
  5. # Plot using seaborn
  6. sns.heatmap(corrmat, vmax=.8, square=True)
  7. plt.show()

이 heatmap을 통해서 보면 column중 서로 어떤 관계가 있는지를 알 수 있는데, 잘 보면 id는 average_rating에 대해서는 다른 변수들에 비해 correlation이 크고, average weight도 역시 다른 변수에 비해서는 average_rating의 correlation이 큰 것을 확인할 수 있다. 그런데 사실 id란 값은 영화의 종류를 구별하기 위한 일종의 라벨일뿐 Regression을 하는데 필요한 정보가 아니다. 또한 포함되어 있는 정보중 bayes_average_rating 같은 경우는 average_rating에 bayes rule을 가한 정보로써 어떻게 보면 우리가 예측해야 하는 average_rating과 의미가 비슷하다. 이런 정보가 실질적으로 regression에 포함되면, 결과가 우리가 예측한 것과 다르게 나올 수 있다. 그렇기 때문에 해당 정보를 빼줘야 한다.

 그렇게 하기 위해서 필요없는 데이터를 뺀 새로운 column층을 만들고 이에 대한 Dataframe을 다시 정리해본다.

  1. # Get all the columns from the dataframe
  2. columns = games.columns.tolist()
  3.  
  4. # Filter the columns to remove data we do not want
  5. columns = [for c in columns if c not in ["bayes_average_rating", "average_rating", "type", "name", "id"]]
  6.  
  7. # Store the variable we`ll be predicting on
  8. target = "average_rating"

이렇게 하면 columns에는 위에서 지정한 필드가 아닌 것들만 남게 되고, target에는 우리가 예측해야 할 정보인 average_rating만 있게 된다. (Dataframe이 아닌 title만!!)

여기까지가 우리가 소위 data preprocessing이라고 한 부분을 수행한 것이다. 이제 본격적으로 Regression을 해볼건데, 보통 ML을 다뤄본 사람이라면 알다시피 학습전에는 학습에 필요한 데이터와 예측한 결과와 비교할 데이터를 구별해야 한다. 앞에서 라이브러리 import시 나왔던 train_test_split을 활용하면 된다.

  1. # Generate training and test datasets
  2. train_X, test_X, train_Y, test_Y = train_test_split(games[columns], games[target], train_size=0.8, test_size=0.2, random_state=1)
  3. # Print shapes
  4. print(train_X.shape)
  5. print(test_X.shape)

train_test_split은 위와 같이 총 4개의 값을 return해주고, 어느정도의 비율로 나눌지도 정의해준다. (이 비율은 0에서 1사이의 실수값을 넣어주면 된다. 참고로 train_size와 test_size는 서로 상관관계이므로 하나의 값만 지정해주면 나머지 값은 이에 맞춰서 자동으로 계산해 넣어준다.) 위의 코드에서는 training set은 80%, test set은 20%의 비율로 나눈 것이다.

 이제 생성된 데이터를 바탕으로 Linear Regression을 수행하고, 해당 과정이 얼마나 잘 맞추는지를 판단하는 척도를 세워야 한다. 다양한 척도들이 있지만, 여기서는 흔히 사용하는 mean_squared_error를 가지고 정확성을 판단해보고자 한다. 

  1. # Import linear regression model
  2. from sklearn.linear_model import LinearRegression
  3. from sklearn.metrics import mean_squared_error
  4.  
  5. # Initialize the model class
  6. LR = LinearRegression()
  7.  
  8. # Fit the model the training data
  9. LR.fit(train_X, train_Y)

 sklearn에는 Regression을 위한 다양한 model이 있는데, 이중 LinearRegression을 가지고 위와 같이 학습시켰다. 그리고 정확히 prediction이 되었는지를 확인해보고, 이를 test set 값과 비교해서 정확성을 측정하였다.

  1. # Generate prediction for the test set
  2. predictions = LR.predict(test_X)
  3.  
  4. # Compute error between our test prediction and actual values
  5. mean_squared_error(predictions, test_Y)

이러면 정확성(혹은 error)가 약 2.087874198531843정도가 나온다. 이정도도 나쁜 결과는 아닌데, 앞의 heatmap에서도 확인한 것처럼 average_rating에 영향을 주는 column이 여러개 인것은 확인했다. 이 말은 한가지 변수이외에도 결과에 영향을 주는 변수들이 많다는 것이다. 그렇기 때문에 이 케이스는 Linear Regression보다는 앞에서 잠깐 소개했던 RandomForestRegression을 해보면 조금더 좋은 결과를 얻을 수 있다.

 사실 이름이 다를 뿐 앞의 Linear Regression의 코드와 거의 동일하다. 여기서도 fit을 수행해서 model을 학습시킨후 결과를 predict 하고, 그후 mean_squared_error를 측정하는 것이다. 다만, Random Forest 기법을 사용하는 만큼, 몇 개의 leaf에서 자를 것(min_samples_leaf)인지, 몇개 가지를 생성할 것인지(n_estimators)에 대한 기준값을 정해줘야 한다.

  1. # Import the random forest model
  2. from sklearn.ensemble import RandomForestRegressor
  3.  
  4. # Initialize the model
  5. RFR = RandomForestRegressor(n_estimators=100, min_samples_leaf=10, random_state=1)
  6.  
  7. # Fit the model the training data
  8. RFR.fit(train_X, train_Y)

역시 동일하게 prediction한 결과를 바탕으로 정확성을 측정했다.

  1. # Generate prediction for the test set
  2. predictions = RFR.predict(test_X)
  3.  
  4. # Compute error between our test prediction and actual values
  5. mean_squared_error(predictions, test_Y)

 이렇게 하면 결과는 약 1.4575588917181705정도가 나오고, 이 결과가 error이기 때문에 앞에서 구한 Linear Regression에 비해서는 결과가 좋게 나온 것을 확인할 수 있다.

 물론 데이터 하나하나만 놓고 봤을 때는 Linear Regression이 더 좋은 결과를 나타낼 수 도 있긴 하다. 나도 임의로 데이터를 넣고 해봤을 때는 Random Forest Regression보다는 Linear Regression이 실제 결과에 근접하게 나오기도 했다.

 100% 예측하는 모델을 생성하기는 어렵지만, 이렇게 여러개의 변수를 바탕으로 Regression을 해보는 방법을 통해 예측 모델을 만드는 방법을 다뤄보았다.


예제 코드 : https://github.com/goodboychan/ML_Example/blob/master/BoardGameReview

댓글