Python

[Python] 추천 시스템(2)

orin602 2025. 3. 1. 18:58

Surprise 추천 시스템

  • Surprise는 Python에서 추천 시스템을 구축하기 위한 라이브러리로, 협업 필터링(Collaborative Filtering) 알고리즘을 구현하는 데 유용하다.
  • Surprise는 다양한 알고리즘을 지원하며, 기본적인 데이터셋 처리, 모델 평가교차 검증 등을 쉽게 할 수 있도록 도와줌
  • Surprise는 기본적인 협업 필터링 방법인 행렬 분해KNN (K-Nearest Neighbors) 기반의 추천을 제공
  • Surprise 추천 시스템의 주요 알고리즘
    • KNN (K-Nearest Neighbors)
      • 유사한 사용자 또는 아이템을 찾아 추천
      • User-based 와 Item-based 방식으로 구분
    • SVD (Singular Vlaue Decomposition)
      • 행렬 분해 기반으로 잠재요인(Latent Factor)을 추출하여 추천
    • NMF (Non-negative Matrix Factorization)
      • 비음수 행렬 분해 방법으로, 비음수의 행렬 요소를 찾기위해 사용
    • Baseline
      • 사용자와 아이템의 평균 평점, 사용자 편향, 아이템 편향 등을 기반으로 예측을 수행.

1. 데이터 준비

# Surprise 라이브러리는 Dataset.load_from_df() 메서드를 사용

import pandas as pd
from surprise import Reader, Dataset, SVD, accuracy
from surprise.model_selection import train_test_split

# 데이터 로드
movies_df = pd.read_csv('movies.csv')
rating_df = pd.read_csv('ratings.csv')
tags_df = pd.read_csv('tags.csv')

 

2. Surprise 데이터셋 변환

# Dataset.load_from_df()를 사용해서 rating_df에서 사용자, 영화, 평점을 Surprise 형식으로 변환
reader = Reader(rating_scale=(1, 5))
data = Dataset.load_from_df(rating_df[['userId', 'movieId', 'rating']], reader)

 

3.. 모델 학습

# 데이터셋 분할(훈련용, 테스트용)
trainset, testset = train_test_split(data, test_size=0.2)

# SVD 모델 학습
svd = SVD()
svd.fit(trainset)

 

4. 추천 제공

# 테스트셋으로 예측 평가
predictions = svd.test(testset)
accuracy.rmse(predictions)

RMSE (Root Mean Squared Error)는 예측값과 실제값의 차이를 평가하는 지표로,

추천 시스템의 정확도를 평가.

>>> 값이 작을수록 모델의 예측 정확도가 높다.

 

5. 결과

# 4. 사용자 x에게 추천할 영화 10개를 예측하기
x = 99  # 예시 사용자 ID

# 영화 목록 불러오기 (movies.csv)
movie_titles = movies_df.set_index('movieId')['title'].to_dict()

# 사용자가 이미 본 영화 제외하기
user_ratings = rating_df[rating_df['userId'] == x]
watched_movie_ids = user_ratings['movieId'].tolist()

# 추천할 영화 목록 만들기
recommendations = []
for movie_id in movies_df['movieId'].unique():
    if movie_id not in watched_movie_ids:
        # 예측 평점 계산
        pred = svd.predict(x, movie_id)
        recommendations.append((movie_id, pred.est))

# 예측된 평점 기준으로 정렬
recommendations.sort(key=lambda x: x[1], reverse=True)

# 상위 10개 추천 영화
top_10_recommendations = recommendations[:10]

# 추천 영화 제목 출력
print(f"사용자 {x}에게 추천할 영화 10개:")
for movie_id, est_rating in top_10_recommendations:
    movie_title = movie_titles[movie_id]
    print(f"영화 제목: {movie_title}, 예측 평점: {est_rating:.2f}")

 

 

 

예측 평점순으로 영화 추천

def get_unseen_surprise(ratings, movies, userId):
    # 입력값으로 들어온 userId에 해당하는 사용자가 평점을 매긴 모든 영화를 리스트로 생성
    seen_movies = ratings[ratings['userId'] == userId]['movieId'].tolist()
    
    # 모든 영화의 movieId를 리스트로 생성
    total_movies = movies['movieId'].tolist()
    
    # 모든 영화의 movieId 중 이미 평점을 매긴 영화의 movieId를 제외한 후 리스트 생성
    unseen_movies = [movie for movie in total_movies if movie not in seen_movies]
    
    print(f"평점 매긴 영화 수: {len(seen_movies)}, 추천 대상 영화 수: {len(unseen_movies)}, 전체 영화 수: {len(total_movies)}")
    
    return unseen_movies

# userId 9에 대해 평점 매기지 않은 영화 리스트 확인
user_id = 9 
unseen_movies = get_unseen_surprise(rating_df, movies_df, user_id)
print('추천 대상 영화 ID:', unseen_movies)

 

def recomm_movie_by_surprise(svd, userId, unseen_movies, movies, top_n=10):
    # 알고리즘 객체의 predict()를 평점 없는 영화에 반복 수행 후 결과를 list로 저장
    predictions = [svd.predict(str(userId), str(movieId)) for movieId in unseen_movies]

    # 예측값(est)을 기준으로 내림차순 정렬
    predictions.sort(key=lambda pred: pred.est, reverse=True)

    # top_n개의 예측된 영화 선택
    top_predictions = predictions[:top_n]

    # top_n으로 추출된 영화의 정보 추출
    top_movie_ids = [int(pred.iid) for pred in top_predictions]
    top_movie_rating = [pred.est for pred in top_predictions]
    top_movie_titles = movies[movies.movieId.isin(top_movie_ids)]['title']

    # (영화 아이디, 제목, 예상 평점) 순서로 목록 생성
    top_movie_preds = [(id, title, rating) for id, title, rating in 
                       zip(top_movie_ids, top_movie_titles, top_movie_rating)]

    # 예상 평점 기준으로 내림차순 정렬
    top_movie_preds.sort(key=lambda x: x[2], reverse=True)

    return top_movie_preds

user_id = 123
unseen_movies = get_unseen_surprise(rating_df, movies_df, user_id)
recommended_movies = recomm_movie_by_surprise(svd, user_id, unseen_movies, movies_df, top_n=10)

# 추천된 영화 출력
print(f"사용자 {user_id}에게 추천할 영화 10개:")
for idx, movie in enumerate(recommended_movies, 1):
    print(f"{idx}. 영화 제목: {movie[1]}, 예상 평점: {movie[2]}")

 

두 함수를 이용해서 예측 평점순 추천영화 출력하기

# 사용자 123이 평가하지 않은 영화 목록 가져오기
unseen_movies = get_unseen_surprise(rating_df, movies_df, 123)

# 추천 영화 목록 가져오기
top_movie_preds = recomm_movie_by_surprise(svd, 123, unseen_movies, movies_df, top_n=10)

# 추천 영화 출력
print()
print("***************Top-10 추천 영화 리스트***************")
for top_movie in top_movie_preds:
    print(top_movie[1], ":", round(top_movie[2], 2))  # 예상 평점을 소수점 둘째 자리까지 출력


 

'Python' 카테고리의 다른 글

[Python] 클래스(Class)  (0) 2025.03.04
[Python] 람다 표현식  (0) 2025.03.02
[Python] 추천 시스템(1)  (0) 2025.03.01
[Python] 함수 사용하기  (0) 2025.02.22
[Python] 2차원 리스트  (0) 2025.02.22