冷啟動問題與混合式推薦
什麼是冷啟動問題?
冷啟動 (Cold Start) 是推薦系統實務上最棘手的問題。它發生在以下三種情境:
1. 新使用者冷啟動
一個全新的使用者剛註冊,系統完全不知道他的喜好。此時:
- ❌ 協同過濾無法使用(沒有歷史評分)
- ❌ 無法計算相似使用者
- ✅ 只能依靠內容為本推薦或熱門推薦
2. 新物品冷啟動
一部新電影上架,還沒有任何人評分過:
- ❌ 協同過濾無法使用(沒有評分資料)
- ✅ 內容為本可以根據電影類型進行推薦
- ✅ 可以先用內容特徵計算相似度
3. 系統冷啟動
全新平台,完全沒有任何使用者或評分資料:
- ❌ 所有協同過濾方法都無法使用
- ✅ 只能依靠物品本身的特徵(內容為本)
- ✅ 或使用專家標籤、外部資料
解決冷啟動的方法
方法 1:熱門推薦 (Popularity Baseline)
最簡單的冷啟動解決方案:推薦平台上最受歡迎的物品。
def popular_recommendations(n_recommendations=10):
"""推薦平台上最受歡迎的電影(簡單但有效)"""
# 計算每部電影的評分次數與平均評分
movie_stats = ratings.groupby('movieId').agg(
rating_count=('rating', 'count'),
avg_rating=('rating', 'mean')
).reset_index()
# 至少被評分過 10 次
popular_movies = movie_stats[movie_stats['rating_count'] >= 10].copy()
# 綜合分數:評分次數 × 平均評分(加權熱門度)
popular_movies['popularity_score'] = (
popular_movies['rating_count'] * popular_movies['avg_rating']
)
# 排序
popular_movies = popular_movies.sort_values(
'popularity_score', ascending=False
)
top_movies = popular_movies.head(n_recommendations)
results = []
for _, row in top_movies.iterrows():
movie_info = movies[movies['movieId'] == row['movieId']].iloc[0]
results.append({
'title': movie_info['title'],
'genres': movie_info['genres'],
'rating_count': row['rating_count'],
'avg_rating': round(row['avg_rating'], 2)
})
return results
print("=== 熱門推薦(新使用者預設)===")
popular = popular_recommendations(10)
for i, rec in enumerate(popular, 1):
print(f"{i}. {rec['title']:50s} 平均: {rec['avg_rating']:.2f} 評分次數: {rec['rating_count']}")
方法 2:漸進式收集使用者偏好
新使用者註冊時,可以請他們先評分一些電影:
def onboarding_recommendations():
"""新使用者引導流程:選一些多樣化的電影請使用者評分"""
# 從每個類型中選出最受歡迎的電影
diverse_movies = []
for genre in ['Action', 'Comedy', 'Drama', 'Sci-Fi', 'Romance', 'Thriller']:
# 找該類型的電影
genre_movies = movies[movies['genres'].str.contains(genre)]
# 與評分統計結合
movie_stats = ratings.groupby('movieId').agg(
rating_count=('rating', 'count'),
avg_rating=('rating', 'mean')
)
genre_movies = genre_movies.merge(
movie_stats, on='movieId', how='inner'
)
genre_movies = genre_movies[
genre_movies['rating_count'] >= 20
].sort_values('avg_rating', ascending=False)
if len(genre_movies) > 0:
diverse_movies.append(genre_movies.iloc[0])
print("=== 新使用者引導:請評分這些電影 ===")
for i, movie in enumerate(diverse_movies, 1):
print(f"{i}. {movie['title']:45s} 類型: {movie['genres']}")
return diverse_movies
onboarding_movies = onboarding_recommendations()
混合式推薦系統
實務上最強大的推薦系統,都是混合式的。我們將結合三種方法:
def hybrid_recommend(user_id, n_recommendations=10, alpha=0.3, beta=0.3, gamma=0.4):
"""
混合式推薦:結合 Content-based + User-based CF + Popularity
Parameters:
alpha: Content-based 權重
beta: 協同過濾權重
gamma: 熱門度權重
"""
user_rated = ratings[ratings['userId'] == user_id]
if len(user_rated) == 0:
# 新使用者:只使用熱門推薦
return popular_recommendations(n_recommendations)
# 1. Content-based 分數
cb_scores = {}
favorite_movies = user_rated.sort_values('rating', ascending=False).head(3)
for _, row in favorite_movies.iterrows():
movie_id = row['movieId']
if movie_id in movie_similarity_df.index:
similar = movie_similarity_df[movie_id].head(20)
for sim_id, score in similar.items():
if sim_id not in user_rated['movieId'].tolist():
cb_scores[sim_id] = cb_scores.get(sim_id, 0) + score * alpha
# 2. User-based CF 分數
ub_scores = {}
if user_id in user_similarity_df.index:
similar_users = user_similarity_df[user_id].drop(user_id).head(10)
for sim_uid, sim_score in similar_users.items():
if sim_score <= 0:
continue
liked = ratings[(ratings['userId'] == sim_uid) & (ratings['rating'] >= 4)]
for _, row in liked.iterrows():
mid = row['movieId']
if mid not in user_rated['movieId'].tolist():
ub_scores[mid] = ub_scores.get(mid, 0) + sim_score * beta
# 3. 熱門度分數
pop_scores = {}
movie_popularity = ratings.groupby('movieId').agg(
count=('rating', 'count'),
avg=('rating', 'mean')
)
movie_popularity['pop_score'] = movie_popularity['count'] * movie_popularity['avg']
pop_max = movie_popularity['pop_score'].max()
for mid in movies['movieId'].tolist():
if mid not in user_rated['movieId'].tolist() and mid in movie_popularity.index:
pop_scores[mid] = (movie_popularity.loc[mid, 'pop_score'] / pop_max) * gamma
# 合併所有分數
final_scores = {}
all_candidates = set(list(cb_scores.keys()) + list(ub_scores.keys()) + list(pop_scores.keys()))
for mid in all_candidates:
final_scores[mid] = cb_scores.get(mid, 0) + ub_scores.get(mid, 0) + pop_scores.get(mid, 0)
# 排序推薦
sorted_candidates = sorted(final_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'],
'hybrid_score': round(score, 4),
'cb_score': round(cb_scores.get(movie_id, 0), 4),
'ub_score': round(ub_scores.get(movie_id, 0), 4),
'pop_score': round(pop_scores.get(movie_id, 0), 4),
})
return results
print("\n=== 混合式推薦結果 ===")
hybrid_results = hybrid_recommend(1, 10)
for i, rec in enumerate(hybrid_results, 1):
print(f"{i}. {rec['title']:45s} 總分: {rec['hybrid_score']:.4f} "
f"(內容: {rec['cb_score']:.2f} + 協同: {rec['ub_score']:.2f} + 熱門: {rec['pop_score']:.2f})")
使用 Vibe Coding 建混合式推薦
🔥 【混合推薦詠唱範例】
「請幫我建立一個混合式推薦系統:1. Content-based:使用物品的類別與標籤計算相似度。2. Collaborative:使用 User-based CF。3. Popularity:計算每個物品的熱門度分數。4. 加權混合:alpha=0.2, beta=0.5, gamma=0.3。5. 自動調整權重:如果使用者是新使用者(少於 5 筆評分),增加 gamma(熱門度權重)。6. 輸出每個推薦的分數組成。」
本日總結
在本章中,你學到了:
- ✅ 三種冷啟動問題:新使用者、新物品、新系統
- ✅ 熱門推薦:用評分次數與平均評分計算熱門度
- ✅ 漸進式引導:新使用者註冊時收集偏好
- ✅ 混合式推薦:結合內容為本、協同過濾與熱門推薦
- ✅ 權重調整:根據使用者狀態動態調整混合權重
下一章,我們將把推薦系統包裝成 API 並評估推薦品質!