內容為本推薦系統 (Content-Based)
內容為本推薦的核心是:計算物品之間的相似度,然後推薦與用戶過去喜歡的物品最相似的物品。
相似度計算方法
在開始實作之前,我們需要了解兩種最常見的相似度計算方法:
1. 餘弦相似度 (Cosine Similarity)
餘弦相似度測量兩個向量之間的「角度」(方向是否一致),而不是距離。
$$\text{Cosine Similarity}(A, B) = \frac{A \cdot B}{||A|| \times ||B||}$$
- 範圍:-1(完全相反)到 1(完全相同)
- 0 代表不相關
- 不受向量長度影響(適合文字或類別資料)
2. 皮爾森相關係數 (Pearson Correlation)
測量兩個變數之間的線性相關程度。
$$\rho = \frac{\text{cov}(X, Y)}{\sigma_X \sigma_Y}$$
- 範圍:-1 到 1
- 適合用來比較兩個使用者的評分模式
實作內容為本推薦
import pandas as pd
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.preprocessing import MultiLabelBinarizer
# === 1. 建立電影特徵向量(類型 + 電影 ID 作為特徵) ===
# 先確保我們有完整的電影資料
movies = pd.read_csv('https://files.grouplens.org/datasets/movielens/ml-latest-small/movies.csv')
ratings = pd.read_csv('https://files.grouplens.org/datasets/movielens/ml-latest-small/ratings.csv')
# 解析類型
movies['genres_list'] = movies['genres'].str.split('|')
# One-Hot 編碼
mlb = MultiLabelBinarizer()
genre_matrix = mlb.fit_transform(movies['genres_list'])
genre_df = pd.DataFrame(genre_matrix, columns=mlb.classes_, index=movies['movieId'])
print(f"電影特徵向量維度: {genre_df.shape}")
print(genre_df.head())
計算電影相似度矩陣
# 計算所有電影之間的餘弦相似度
movie_similarity = cosine_similarity(genre_df)
# 轉換為 DataFrame
movie_similarity_df = pd.DataFrame(
movie_similarity,
index=genre_df.index,
columns=genre_df.index
)
print(f"相似度矩陣大小: {movie_similarity_df.shape}")
print(movie_similarity_df.iloc[:5, :5])
根據電影 ID 推薦相似電影
def recommend_content_based(movie_id, n_recommendations=5):
"""根據電影 ID 推薦最相似的 N 部電影"""
if movie_id not in movie_similarity_df.index:
return []
# 取得該電影與所有電影的相似度
similarity_scores = movie_similarity_df[movie_id]
# 排序(由高到低),排除自己
similar_movies = similarity_scores.sort_values(ascending=False)
similar_movies = similar_movies.drop(movie_id)
# 取前 N 部
top_movies = similar_movies.head(n_recommendations)
# 回傳電影資訊
results = []
for mid, score in top_movies.items():
movie_info = movies[movies['movieId'] == mid].iloc[0]
results.append({
'movieId': mid,
'title': movie_info['title'],
'genres': movie_info['genres'],
'similarity_score': round(score, 4)
})
return results
# 測試:推薦與「Toy Story (1995)」相似的電影
# Toy Story 的 movieId = 1
toy_story_recs = recommend_content_based(1, n_recommendations=10)
print("=== 與「Toy Story」相似的電影 ===")
for rec in toy_story_recs:
print(f"{rec['title']:50s} 相似度: {rec['similarity_score']:.4f} 類型: {rec['genres']}")
為特定用戶做內容為本推薦
def recommend_for_user_content_based(user_id, n_recommendations=10):
"""根據用戶評分過的電影,為用戶推薦最相似的未看過電影"""
# 取得用戶評分過的電影
user_ratings = ratings[ratings['userId'] == user_id]
rated_movie_ids = user_ratings['movieId'].tolist()
if len(rated_movie_ids) == 0:
return []
# 找出用戶評分最高的幾部電影(作為「喜歡」的代表)
favorite_movies = user_ratings.sort_values('rating', ascending=False)
top_rated = favorite_movies.head(5)
print(f"用戶 {user_id} 最喜歡的電影:")
for _, row in top_rated.iterrows():
movie_info = movies[movies['movieId'] == row['movieId']].iloc[0]
print(f" {movie_info['title']} (評分: {row['rating']})")
# 為每一部喜歡的電影找相似電影
candidate_scores = {}
for _, row in top_rated.iterrows():
movie_id = row['movieId']
weight = row['rating'] / 5.0 # 高分電影權重更高
if movie_id in movie_similarity_df.index:
similar = movie_similarity_df[movie_id].sort_values(ascending=False)
similar = similar.drop(movie_id)
for similar_id, score in similar.items():
if similar_id not in rated_movie_ids: # 排除已看過的
candidate_scores[similar_id] = candidate_scores.get(similar_id, 0) + score * weight
# 排序取推薦
sorted_candidates = sorted(candidate_scores.items(), key=lambda x: x[1], reverse=True)
top_candidates = sorted_candidates[:n_recommendations]
results = []
for movie_id, score in top_candidates:
movie_info = movies[movies['movieId'] == movie_id].iloc[0]
results.append({
'title': movie_info['title'],
'genres': movie_info['genres'],
'score': round(score, 4)
})
return results
# 測試
user_id = 1
recommendations = recommend_for_user_content_based(user_id)
print("\n=== 內容為本推薦結果 ===")
for i, rec in enumerate(recommendations, 1):
print(f"{i}. {rec['title']:50s} 分數: {rec['score']:.4f} 類型: {rec['genres']}")
使用 Vibe Coding 做內容推薦
🔥 【內容推薦詠唱範例】
「我有一個 products.csv 包含商品 ID、名稱、類別、價格區間、品牌。請幫我:1. 將類別與品牌進行 One-Hot 編碼,價格進行標準化。2. 使用餘弦相似度計算所有商品之間的相似度。3. 寫一個函式 recommend_similar(product_id, n=5):回傳最相似的 N 個商品。4. 將相似度矩陣儲存為 similarity.pkl 方便之後載入。5. 寫一個 FastAPI 端點 GET /similar/{product_id} 回傳推薦結果。」
本日總結
在本章中,你學到了:
- ✅ 餘弦相似度:測量兩個特徵向量的相似程度
- ✅ 電影特徵向量:將類別資料轉換為機器可讀的向量
- ✅ 相似度矩陣:計算所有電影之間的相似度
- ✅ 內容為本推薦:根據用戶喜歡的電影,推薦相似的電影
- ✅ 權重調整:高分電影的推薦權重更高
下一章,我們將實作協同過濾推薦系統!