推薦系統基礎概念與資料準備

什麼是推薦系統?

推薦系統是一個資訊過濾系統,它的目標是從大量的物品(商品、電影、文章)中,預測使用者最可能感興趣的那些物品,並將它們呈現給使用者。

推薦系統的三大類型

1. 內容為本推薦 (Content-Based Filtering)

核心概念:推薦與用戶過去喜歡的物品「相似」的物品。

想像你最近在 Netflix 上看了一部「科幻片」,推薦系統就會分析這部電影的特徵(類型:科幻、導演、演員、關鍵詞),然後找出其他也具有「科幻」特徵的電影推薦給你。

優點:

  • ✅ 不需要其他用戶的資料
  • ✅ 可以推薦冷門物品(新商品也能被推薦)
  • ✅ 推薦結果容易解釋(「因為您看過⋯」)

缺點:

  • ❌ 推薦結果多樣性低(一直推薦同一類型的東西)
  • ❌ 難以發現用戶跨領域的興趣

2. 協同過濾 (Collaborative Filtering)

核心概念:推薦與你「相似的其他用戶」喜歡的物品。

Netflix 發現「喜歡《全面啟動》的人也喜歡《駭客任務》」,所以當你看了《全面啟動》後,系統就會推薦《駭客任務》給你。

優點:

  • ✅ 可以跨領域推薦(發現用戶意想不到的興趣)
  • ✅ 不需要物品的特徵資料

缺點:

  • ❌ 冷啟動問題 (Cold Start):新物品或新用戶沒有歷史資料,無法推薦
  • ❌ 計算複雜度高(用戶×物品的矩陣可能非常大)

3. 混合式推薦 (Hybrid Approach)

實務上最強大的推薦系統,通常是內容為本 + 協同過濾的混合體。先用內容為本解決冷啟動,再用協同過濾提升推薦品質。

推薦系統的評價指標

| 指標 | 說明 | 適用場景 | |------|------|---------| | Precision@K | 推薦的 K 個物品中,有多少是用戶真正喜歡的 | 精準度重要(如電商首頁推薦) | | Recall@K | 用戶喜歡的所有物品中,有多少被推薦出來了 | 覆蓋率重要(如探索新內容) | | RMSE | 預測評分與真實評分的誤差 | 評分預測任務 | | MAP@K | 平均精準率均值 | 排序品質評估 |

準備資料集:MovieLens

我們將使用經典的 MovieLens 資料集來實作推薦系統。

import pandas as pd
import numpy as np

# 直接從網路載入 MovieLens 資料集
urls = {
    'movies': 'https://files.grouplens.org/datasets/movielens/ml-latest-small/movies.csv',
    'ratings': 'https://files.grouplens.org/datasets/movielens/ml-latest-small/ratings.csv',
}

movies = pd.read_csv(urls['movies'])
ratings = pd.read_csv(urls['ratings'])

print("=== 電影資料 ===")
print(f"電影數量: {len(movies)}")
print(movies.head())
print("\n=== 評分資料 ===")
print(f"評分數量: {len(ratings)}")
print(f"使用者數量: {ratings['userId'].nunique()}")
print(f"電影數量: {ratings['movieId'].nunique()}")
print(ratings.head())

# 檢查資料分布
print("\n=== 評分布 ===")
print(ratings['rating'].value_counts().sort_index())

資料預處理

# 使用者-電影評分矩陣
user_movie_matrix = ratings.pivot(
    index='userId',
    columns='movieId',
    values='rating'
)

print(f"使用者-電影矩陣大小: {user_movie_matrix.shape}")
print("稀疏度: {:.2f}%".format(
    (1 - user_movie_matrix.notna().sum().sum() / 
     (user_movie_matrix.shape[0] * user_movie_matrix.shape[1])) * 100
))

# 電影特徵:從 genres 欄位中解析
movies['genres_list'] = movies['genres'].str.split('|')

# One-Hot Encoding 類別
from sklearn.preprocessing import MultiLabelBinarizer

mlb = MultiLabelBinarizer()
genre_matrix = mlb.fit_transform(movies['genres_list'])
genre_df = pd.DataFrame(
    genre_matrix,
    columns=mlb.classes_,
    index=movies['movieId']
)

print("\n=== 電影類型 One-Hot 編碼 ===")
print(f"類型數量: {genre_df.shape[1]}")
print(genre_df.head())

資料分割(時間序列分割)

推薦系統的資料分割不能使用隨機分割,因為使用者過去的行為才能預測未來:

# 將每個用戶的評分按時間排序,取最後 20% 作為測試集
ratings['timestamp'] = pd.to_datetime(ratings['timestamp'], unit='s')

train_data = []
test_data = []

for user_id, user_ratings in ratings.groupby('userId'):
    user_ratings = user_ratings.sort_values('timestamp')
    split_idx = int(len(user_ratings) * 0.8)
    
    train_data.append(user_ratings.iloc[:split_idx])
    test_data.append(user_ratings.iloc[split_idx:])

train_df = pd.concat(train_data)
test_df = pd.concat(test_data)

print(f"訓練集: {len(train_df)} 筆評分")
print(f"測試集: {len(test_df)} 筆評分")

本日總結

在本章中,你學到了:

  1. 推薦系統三大類型:內容為本、協同過濾、混合式
  2. 評價指標:Precision@K、Recall@K、RMSE、MAP@K
  3. MovieLens 資料集:載入與探索
  4. 資料預處理:評分矩陣與電影特徵編碼
  5. 時間序列分割:依時間順序分割訓練/測試集

下一章,我們將實作內容為本的推薦系統!

會員專屬免費教學

本章節為註冊會員專屬的免費開放內容!請先登入或註冊會員,即可立即解鎖閱讀。

立即登入 / 註冊