끄적끄적

2025.08.18 추천시스템 & LLM 1회차 (과제) 본문

[스파르타]내일배움캠프 데이터 분석 트랙/Session

2025.08.18 추천시스템 & LLM 1회차 (과제)

kminx 2025. 8. 18. 17:33

🔥 과제

지금까지 콘텐츠 기반 필터링 프로세스에 대해 코드를 배웠습니다.

아래 스켈레톤 코드를 바탕으로 user_id를 입력 받았을 때 추천 웹툰을 반환하는 함수를 만들어봅시다.

 

조건

  • TF-IDF로 'keyword' 컬럼 기준 웹툰을 벡터화한다.
  • 사용자가 읽은 웹툰들의 평균 벡터를 계산해서 사용자 벡터를 만든다.
  • 사용자 벡터와 전체 웹툰 벡터 간의 유사도를 계산한다.
  • 아직 읽지 않은 웹툰 중에서 상위 5개를 추천한다.

1. TF-IDF로 keyword 컬럼 벡터화를 진행할 것이기에 필요한 라이브러리를 import하고, 사용할 데이터를 불러온다.

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
import pandas as pd

webtoon = pd.read_csv('/content/drive/MyDrive/Colab Notebooks/스파르타/llm세션/1강/cbf_webtoon_data.csv')
user_logs = pd.read_csv('/content/drive/MyDrive/Colab Notebooks/스파르타/llm세션/1강/cbf_user_logs.csv')

 

이때, webtoon과 user_logs는 아래와 같은 데이터이다.

 

2. 각 웹툰에 대한 TF-IDF 벡터를 생성하고, user_id 별로 데이터를 묶어준다.

tfidf = TfidfVectorizer()
tfidf_matrix = tfidf.fit_transform(webtoon['keyword'])
user_read_webtoons = user_logs.groupby('user_id')['webtoon_id'].apply(set).reset_index()

 

3. 입력된 사용자가 본 웹툰 목록을 정의한다.

    이 때, 사용자 정보가 없거나, 사용자의 시청 기록이 없는 경우도 고려한다.

user_data = user_read_webtoons[user_read_webtoons['user_id'] == user_id]
if user_data.empty:
    return print(f"{user_id}의 사용자 정보가 없습니다.")

user_read_webtoonids = list(user_data['webtoon_id'].iloc[0])
if not user_read_webtoonids:
    return print(f"{user_id}의 시청 기록이 없습니다.")

 

4. 사용자가 본 웹툰들의 TF-IDF 평균을 구해 사용자 벡터를 구한다.

user_vector = tfidf_matrix[user_read_webtoonids].mean(axis=0)
user_vec = np.asarray(user_vector)

 

5. 사용자 벡터와 전체 웹툰과의 유사도를 계산한다.

cos_sim = cosine_similarity(user_vec,tfidf_matrix)
recommend_indicies = cos_sim.argsort()[0][::-1]

 

6. 사용자가 이미 본 웹툰을 제외한 상위 N개의 웹툰 목록을 반환한다.

recommend_indicies = [i for i in recommend_indicies if i not in user_read_webtoonids]

top_n_webtoons = recommend_indicies[:top_n]

print(f'{user_id}에게 추천하는 웹툰 Top {top_n}')
recommended_webtoons = webtoon.loc[top_n_webtoons][['webtoon_title', 'main_genre']]

 

 

최종코드

# HINT : 웹툰을 벡터화하는 연산은 추천 함수 안에 들어갈 필요가 없습니다. (모든 사용자가 공통으로 사용하는 값이기 때문에)
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
import pandas as pd

webtoon = pd.read_csv('/content/drive/MyDrive/Colab Notebooks/스파르타/llm세션/1강/cbf_webtoon_data.csv')
user_logs = pd.read_csv('/content/drive/MyDrive/Colab Notebooks/스파르타/llm세션/1강/cbf_user_logs.csv')

# 1. 웹툰 아이템에 대한 TF-IDF 벡터 생성 및 user_id 그룹화
tfidf = TfidfVectorizer()
tfidf_matrix = tfidf.fit_transform(webtoon['keyword'])
user_read_webtoons = user_logs.groupby('user_id')['webtoon_id'].apply(set).reset_index()

# 2. 추천 함수 정의
def cbf_recommend(user_id, top_n=5) :
  # (1) 해당 유저가 본 웹툰 목록 정의
    user_data = user_read_webtoons[user_read_webtoons['user_id'] == user_id]
    if user_data.empty:
        return print(f"{user_id}의 사용자 정보가 없습니다.")

    user_read_webtoonids = list(user_data['webtoon_id'].iloc[0])
    if not user_read_webtoonids:
        return print(f"{user_id}의 시청 기록이 없습니다.")

  # (2) 사용자가 본 웹툰들의 TF-IDF 벡터 평균을 통해 사용자 벡터 구하기
    user_vector = tfidf_matrix[user_read_webtoonids].mean(axis=0)
    user_vec = np.asarray(user_vector)

  # (3) 전체 웹툰과의 유사도 계산
    cos_sim = cosine_similarity(user_vec,tfidf_matrix)
    recommend_indicies = cos_sim.argsort()[0][::-1]

  # (4) 사용자가 이미 본 웹툰 제외하기
    recommend_indicies = [i for i in recommend_indicies if i not in user_read_webtoonids]

  # (5) 유사도 기준으로 top-N 웹툰 추천하기
    top_n_webtoons = recommend_indicies[:top_n]

  # (6) 결과 반환 (DataFrame, webtoon_title과 main_genre 포함)
    print(f'{user_id}에게 추천하는 웹툰 Top {top_n}')
    recommended_webtoons = webtoon.loc[top_n_webtoons][['webtoon_title', 'main_genre']]

    return recommended_webtoons

cbf_recommend('user_1',5)