상세 컨텐츠

본문 제목

[K-DIGITAL] 미드프로젝트. 영화 별점 데이터 분석(추가) - 분류모델의 성능 향상

PYTHON/K-DIGITAL

by ranlan 2021. 7. 12. 12:22

본문

728x90

멋쟁이사자처럼 X K-DIGITAL Training - 07.12

 

 

[github] https://github.com/ijo0r98/likelion-kdigital/tree/main/mid-project-1

 

ijo0r98/likelion-kdigital

멋쟁이사자처럼 & K-DIGITAL. Contribute to ijo0r98/likelion-kdigital development by creating an account on GitHub.

github.com

 

[이전] 2021.07.10 - [python/k-digital] - [K-DIGITAL] mid-project-1. 영화 별점 데이터 분석

 

[K-DIGITAL] mid-project-1. 영화 별점 데이터 분석

멋쟁이사자처럼 X K-DIGITAL Training - 07.05 [github] https://github.com/ijo0r98/likelion-kdigital/tree/main/mid-project-1 ijo0r98/likelion-kdigital 멋쟁이사자처럼 & K-DIGITAL. Contribute to ijo0r98/..

juran-devblog.tistory.com

 


 

어제 하면서 아쉬웠던 부분들 다 해결해버려따 허허

그리고 어제 하면서 궁금했던 부분들도 강사님께 질문했다.

강사님께 성능에 대해 여쭤보니 좋은 팁을 주셔서 성능을 꽤 많이 올렸다! 발표 전에 완성해서 다행이다.

역시 모르는거 있을땐 바로바로 질문하는게 최고인것 같당

에측 모델이 어느정도 성능이 올라 새로운 영화에 대한 정보가 주어졌을 때 별점을 예측하는 것도 만들어 보았다.

넘 주먹구구식이지만 모 어때 여기까지라도 한데 의미가 있는거지 모~.~

 

발표까지 성공리에 끝낸 기념 미드플젝1 마지막 포스팅 😇

 

 

 

왜 float형으로는 모델 accuracy 측정이 안되는가?

 

y_data

실수형으로 모델을 학습하고 accuray 측정을 할 때 continous is not supported 라며 연속값에 관련된 오류가 발생한다.

그 이유는 멀티 클래스(여러 카테고리)를 인식할 때 카테고리 넘버 형태로 인식하기 때문이라고 한다.

float형은 연속된 변수로 인식하여 0, 1 또는 0, 1, 2처럼 클래스가 정수 형태로 되어있어야 한다 !!

그래서 이전에 내가 아무생각없이 모든 rating에 10을 곱해 int형으로 바꿨을 때 잘 작동되었던 것이다..

이유도 모르고 그냥 이렇게 하니 되네~ 하고 했었는데 이제 정확한 이유를 찾았다.

다른 예로 성별을 female / male 에서 0 / 1로 바꿔주는 것도 비슷한 느낌이지 않을까 싶은데.. 쓰고보니 좀 다른것 같기도 ..

 

 

 

성능 향상을 위한 y 범주 축소

강사님께 데이터가 너무 작기도하고 x 데이터 선택이 잘못되어 성능이 잘 안나오는걸까요? 라고 질문드렸었다.

강사님께서는 y의 범주가 너무 많다고 하셨다.

가뜩이나 이렇게 데이터가 적을때 타켓의 카테고리 수가 너무 많으면 성능이 떨어진다고 한다.

rating의 범위를 나눠 카테고리를 2~3개로 줄여보라고 하신 조언에 따라 조금 수정해보았다.

 

먼저 y의 카테고리 분포 확인

y_data.value_counts()

y_data

카테고리 수 축소

def rating_categorized(r):
    if r >= 3.0:
        return 0
    else:
        return 1
    
y_data3 = y_data.copy()
y_data3.rating = y_data3.rating.apply(rating_categorized)

y_data3

y_data3.value_counts()

모델 학습 및 성능 확인

# train / test
x_train, x_test, y_train, y_test = train_test_split(X_data, y_data3, test_size=0.3, random_state=0)

# create model
model = XGBClassifier(seed = 0, n_jobs = -1, learning_rate = 0.1, n_estimators = 100, max_depth = 3) 

# fit
model.fit(x_train, y_train)

# predict & accuracy
y_pred = model.predict(x_test)
accuracy_score(y_test, y_pred)

>> 0.7435530085959885

 

성능이 완전 대폭 상승하였다 !!!!!!! 🥳

 

이 기세를 몰아 귀찮아서 전처리하지 않았던 장르 데이터도 살짝 건들여보았다.

 

 

 

Feature 추가

rating_usr
movies

df2 = pd.merge(rating_usr, movies[['movieId','genres']], on='movieId')

df

전체 장르 카테고리 리스트 생성 (중복 제거)

# | 기준으로 장르 구분
def genres_split(g):
    return g.split('|')

# 각 영화에 대한 장르 dict 형태로 저장 key(movieId) : value(genres)
genres_list = {}
for i in df2.index:
    genres_list[df2.loc[i]['movieId']] = df2.loc[i]['genres']
    
# dict에 저장된 value(장르) |으로 나눠줌 
for key, value in genres_list.items():
    genres_list[key] = genres_split(value)
    
# 모든 장르 카테고리 확인
genres_all = []
for value in genres_list.values():
    length = len(value)
    for i in range(0, length):
        genres_all.append(value[i])

genres_all = set(genres_all)

(하나의 로우안에 여러 장르가 섞여있고 중복도 있어서 처리하는데 시간이 조금 걸렸다. 살짝 필요없는 코드도 섞여있는거 같기도 하고..)

# 데이터프레임으로 바꿔 인덱스 번호 매김
genres_df = pd.DataFrame({'name': list(genres_all)})

genres_df

전체 데이터프레임에 장르 정보 추가

# |로 이어진 genre 리스트에서 배열로 전환
df2['genres_list'] = df2.genres.apply(genres_split)
del df2['genres']

# one-hot vector로 사용될 genre 열들 추가
for g in genres_df.name:
    df2[g] = 0 # default

# 해당되는 장르 1로 변경
for index, genres in enumerate(df2.genres_list):
    for genre in genres:
        df2[genre][index] = 1

# 불필요한 열 삭제        
del df2['genres_list']

df2

links_metadata (links & metadata)와 df2(ratings & moive-id & genre) 연결

# metadata 추가
df2 = pd.merge(df2, links_metadata[['title', 'budget', 'original_language', 'popularity', 'runtime', 'vote_average', 'vote_count', 'movieId']], on='movieId')

df2

 

original_language 전처리

숫자형 카테고리로 바꾸기 위해 카테고리마다 인덱스값(숫자) 부여

# language 종류 확인
langs = df2.original_language.unique()
langs_df = pd.DataFrame({'lan': langs})

# 인덱스 reset
langs_df.reset_index(inplace=True)
langs_df.set_index(langs_df.lan, inplace=True)
del langs_df['lan']

lang_df

문자 -> 숫자로 카테고리 변경

def lang_categorized(lan):
    return langs_df.loc[lan]['index']
    
df2.original_language = df2.original_language.apply(lang_categorized)

df2

 

X 데이터 전처리

# object -> float
df2.popularity = df2.popularity.apply(lambda x: float(x))
df2.budget = df2.budget.apply(lambda x: int(x))

# y 데이터 카테고리
df2.rating = df2.rating.apply(rating_categorized)

# 타겟 데이터
y_data3 = df2['rating']

# feature selection
columns = ['Action', 'Adventure', 'Animation', 'Children', 'Comedy', 'Crime', 'Documentary', 'Drama', 'Fantasy', 'Film-Noir', 'Horror', 'IMAX', 'Musical', 'Mystery', 'Romance', 'Sci-Fi', 'Thriller', 'War', 'Western', 'budget', 'popularity', 'runtime', 'vote_average', 'vote_count','original_language']
X_data2 = df2[columns]

X_data2

 

모델 학습 및 성능 확인

x_train, x_test, y_train, y_test = train_test_split(X_data2, y_data3, test_size=0.3, random_state=10)

params = {'learning_rate': 0.01, 'max_depth': 3, 'subsample': 0.6}

xgb = XGBClassifier(**params)
xgb.fit(x_train, y_train)
accuracy_score(y_test, xgb.predict(x_test))

>> 0.7675438596491229

 

 

 

새로운 영화에 대한 별점 예측해보기

# 별점 예측
def predict_rating(title, genre, runtime, budget, vote_avg, vote_cnt, lang, popularity):
    
    # 새로운 x-data 행 추가
    X_data2.loc['new'] = ""
    X_data2.loc['new']['runtime'] = float(runtime)
    X_data2.loc['new']['budget'] = int(budget)
    X_data2.loc['new']['popularity'] = float(popularity)
    X_data2.loc['new']['vote_average'] = float(vote_avg)
    X_data2.loc['new']['vote_count'] = float(vote_cnt)
    
    # language
    X_data2.loc['new']['original_language'] = lang_categorized(lang)
    
    # genre
    genre = list(gerne.split(' '))
    for g in list(genres_all):
        if g in gerne:
            X_data2.loc['new'][g] = 1
        else:
            X_data2.loc['new'][g] = 0
            
    x_pred = X_data2.tail(1)
    
    X_data2.drop('new')
    
    y_pred = xgb.predict(x_pred)
    return y_pred

데이터 input

title = input('제목을 입력해 주세요: ')
gerne = input('장르를 선택해 주세요(복수 나열 가능) '+ str(genres_all) + '\n: ')
runtime = input('상영 시간을 입력해 주세요: ')
budget = input('예산을 입력해 주세요: ')
vote_avg = input('평균 별점을 입력해 주세요: ')
vote_cnt = input('별점 수를 입력해 주세요: ')
popularity = input('인지도를 입력해 주세요: ')
lang = input('언어를 선택해 주세요 ' + str(langs) + '\n: ')

예측 결과 출력

rate = predict_rating(title, gerne, runtime, budget, vote_avg, vote_cnt, lang, popularity)

if rate == 0:
    print(f"영화 [{title}]의 별점은 3.0 이상으로 예측됩니다.")
else: 
    print(f"영화 [{title}]의 별점은 3.0 미만으로 예측됩니다.")

>> 영화 [titanic]의 별점은 3.0 이상으로 예측됩니다.

 

 

예측 서비스도 완성 !!!!

 

728x90

관련글 더보기

댓글 영역