冷啟動問題與混合式推薦

什麼是冷啟動問題?

冷啟動 (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. 輸出每個推薦的分數組成。」

本日總結

在本章中,你學到了:

  1. 三種冷啟動問題:新使用者、新物品、新系統
  2. 熱門推薦:用評分次數與平均評分計算熱門度
  3. 漸進式引導:新使用者註冊時收集偏好
  4. 混合式推薦:結合內容為本、協同過濾與熱門推薦
  5. 權重調整:根據使用者狀態動態調整混合權重

下一章,我們將把推薦系統包裝成 API 並評估推薦品質!

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!