基于内容的推荐算法实战:TF-IDF + 余弦相似度实现电影推荐(附Python代码)
·
基于内容的推荐算法实战:TF-IDF + 余弦相似度实现电影推荐(附Python代码)
当你在深夜打开流媒体平台,系统精准推送给你的那部电影,很可能就是基于内容推荐算法的杰作。这种不依赖其他用户行为、仅通过分析内容特征就能实现个性化推荐的技术,正在重塑我们获取信息的方式。本文将带你从零实现一个基于电影内容的推荐系统,核心工具是自然语言处理领域的经典技术——TF-IDF算法与余弦相似度计算。
1. 理解基于内容推荐的核心逻辑
基于内容的推荐算法(Content-Based Recommendation)本质上是 信息过滤系统 的延伸。与协同过滤不同,它不需要用户-物品评分矩阵,而是通过挖掘内容本身的特征建立推荐逻辑:
- 物品表征 :将每部电影转化为可量化的特征向量(如类型、导演、演员、剧情关键词等)
- 用户画像 :根据用户历史行为(如评分、观看记录)构建兴趣偏好向量
- 推荐生成 :计算候选物品与用户画像的相似度,返回最匹配的结果
这种方法的优势在于:
- 冷启动友好 :新电影只要具备基础元数据即可被推荐
- 可解释性强 :"因为您喜欢科幻片"比"与您相似的用户也喜欢"更直观
- 规避数据稀疏 :不依赖用户群体行为数据
# 示例:电影特征向量空间示意
import pandas as pd
movies = pd.DataFrame({
'title': ['盗梦空间', '星际穿越', '泰坦尼克号'],
'genre_sci-fi': [1, 1, 0],
'genre_romance': [0, 1, 1],
'director_nolan': [1, 1, 0],
'actor_dicaprio': [1, 0, 1]
})
2. 关键算法:TF-IDF的工程实现
TF-IDF(Term Frequency-Inverse Document Frequency)是处理文本特征的黄金标准,它能有效衡量词语在文档中的重要程度:
- 词频(TF) :单词在文档中出现的频率
- 逆文档频率(IDF) :降低常见词权重的调节因子
- 计算公式 :TF-IDF = TF × log(N/df)
(N为总文档数,df为包含该词的文档数)
电影描述的TF-IDF处理流程 :
- 文本预处理:分词、去除停用词、词干提取
- 构建词表:统计所有电影描述中的独特词汇
- 计算TF-IDF矩阵:每个电影对应一个高维稀疏向量
from sklearn.feature_extraction.text import TfidfVectorizer
# 示例电影描述
descriptions = [
"建筑师通过梦境窃取机密 科幻悬疑",
"宇航员穿越虫洞拯救人类 科幻亲情",
"豪华邮轮上的跨越阶级爱情 灾难浪漫"
]
# TF-IDF向量化
tfidf = TfidfVectorizer(token_pattern=r'(?u)\b\w+\b')
tfidf_matrix = tfidf.fit_transform(descriptions)
# 查看特征词
print(tfidf.get_feature_names_out())
# ['人类' '亲情' '窃取' '科幻' '跨越' '阶级' '豪华' '邮轮' '梦境' '灾难' '爱情' '悬疑' '拯救' '建筑师' '浪漫' '宇航员' '虫洞' '机密']
3. 相似度计算的工程细节
余弦相似度衡量的是向量间的夹角而非绝对距离,这对高维稀疏的文本特征尤为重要:
- 计算公式 :cosθ = (A·B) / (||A|| × ||B||)
- 取值范围 :[-1, 1],通常文本处理结果为[0,1]
- 优化技巧 :
- 对稀疏矩阵使用
linear_kernel比cosine_similarity快20倍 - 提前计算并缓存相似度矩阵
- 对稀疏矩阵使用
from sklearn.metrics.pairwise import linear_kernel
# 计算余弦相似度
cosine_sim = linear_kernel(tfidf_matrix, tfidf_matrix)
# 构建相似度映射表
sim_df = pd.DataFrame(cosine_sim,
index=['盗梦空间', '星际穿越', '泰坦尼克号'],
columns=['盗梦空间', '星际穿越', '泰坦尼克号'])
print(sim_df)
4. 完整电影推荐系统实现
我们使用MovieLens数据集中的电影元数据构建端到端推荐流程:
4.1 数据准备与清洗
import pandas as pd
from ast import literal_eval
# 加载数据集
md = pd.read_csv('movies_metadata.csv')
md['genres'] = md['genres'].fillna('[]').apply(literal_eval)
md['genres'] = md['genres'].apply(lambda x: [i['name'] for i in x] if isinstance(x, list) else [])
md['description'] = md['overview'] + ' ' + md['tagline'].fillna('')
# 关键字段处理
md = md[['title', 'genres', 'description']]
md['description'] = md['description'].fillna('')
4.2 特征工程与模型构建
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import linear_kernel
# 创建TF-IDF矩阵
tfidf = TfidfVectorizer(stop_words='english')
tfidf_matrix = tfidf.fit_transform(md['description'])
# 计算相似度矩阵
cosine_sim = linear_kernel(tfidf_matrix, tfidf_matrix)
# 建立标题索引映射
indices = pd.Series(md.index, index=md['title']).drop_duplicates()
4.3 推荐函数实现
def content_recommender(title, cosine_sim=cosine_sim, md=md, indices=indices):
# 获取电影索引
idx = indices[title]
# 获取相似度评分
sim_scores = list(enumerate(cosine_sim[idx]))
# 按评分排序
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
# 取Top10(排除自身)
sim_scores = sim_scores[1:11]
# 获取电影索引
movie_indices = [i[0] for i in sim_scores]
# 返回推荐结果
return md['title'].iloc[movie_indices]
# 示例推荐
print(content_recommender('The Dark Knight'))
5. 性能优化与生产级改进
基础版本在实际业务中可能需要以下增强:
5.1 特征增强策略
# 组合多种特征
md['soup'] = md['genres'].apply(' '.join) + ' ' + md['description']
# 使用CountVectorizer增强类型权重
from sklearn.feature_extraction.text import CountVectorizer
count = CountVectorizer(stop_words='english')
count_matrix = count.fit_transform(md['soup'])
# 混合相似度计算
from sklearn.metrics.pairwise import cosine_similarity
final_sim = cosine_similarity(tfidf_matrix, tfidf_matrix) * 0.5 + \
cosine_similarity(count_matrix, count_matrix) * 0.5
5.2 实时推荐优化
# 使用近似最近邻(ANN)加速查询
from sklearn.neighbors import NearestNeighbors
# 构建ANN索引
knn = NearestNeighbors(metric='cosine', algorithm='brute')
knn.fit(tfidf_matrix)
# 实时查询函数
def realtime_recommend(title, n_recommendations=10):
idx = indices[title]
distances, indices = knn.kneighbors(
tfidf_matrix[idx],
n_neighbors=n_recommendations+1
)
return md['title'].iloc[indices.flatten()[1:]]
5.3 混合推荐架构
# 伪代码:结合协同过滤
def hybrid_recommend(user_id, title):
# 内容相似度权重
content_weight = 0.6
# 获取内容推荐
content_rec = content_recommender(title)
# 获取协同过滤推荐
cf_rec = collaborative_filtering(user_id)
# 混合排序
hybrid_rec = content_weight * content_rec + \
(1-content_weight) * cf_rec
return hybrid_rec.sort_values(ascending=False)[:10]
6. 评估推荐质量的方法论
没有评估的推荐系统就像没有罗盘的航行,我们需要量化推荐效果:
6.1 离线评估指标
| 指标 | 计算公式 | 说明 |
|---|---|---|
| 准确率 | TP/(TP+FP) | 推荐列表中用户真正喜欢的比例 |
| 召回率 | TP/(TP+FN) | 系统找出用户喜欢物品的能力 |
| F1分数 | 2*(P*R)/(P+R) | 准确率与召回率的调和平均 |
| NDCG | ∑(rel_i/log2(i+1)) | 考虑排序位置的加权评分 |
from sklearn.metrics import ndcg_score
# 假设有测试集真实评分
true_relevance = np.array([[3, 2, 1, 0, 0]]) # 用户真实偏好
scores = np.array([[0.9, 0.8, 0.7, 0.6, 0.5]]) # 系统预测评分
print("NDCG得分:", ndcg_score(true_relevance, scores))
6.2 在线A/B测试框架
# 伪代码:推荐系统A/B测试
def ab_test_recommend(user_id, algorithm='A'):
if algorithm == 'A':
return content_recommender(user_id)
else:
return collaborative_filtering(user_id)
# 指标追踪
metrics = {
'click_through_rate': [],
'watch_time': [],
'conversion_rate': []
}
7. 工程实践中的挑战与解决方案
在实际部署推荐系统时,会遇到一些典型问题:
冷启动问题 :
- 新电影:引入混合推荐,初期侧重内容特征
- 新用户:利用人口统计信息或引导选择兴趣标签
数据稀疏性 :
# 使用SVD降维
from sklearn.decomposition import TruncatedSVD
svd = TruncatedSVD(n_components=100)
reduced_matrix = svd.fit_transform(tfidf_matrix)
实时性要求 :
- 增量更新:每隔几小时更新TF-IDF矩阵
- 流处理:使用Spark Streaming处理新加入内容
可解释性增强 :
def explain_recommendation(movie_title, recommended_title):
# 获取共同特征
vec1 = tfidf.transform([md[md['title']==movie_title]['description'].iloc[0]])
vec2 = tfidf.transform([md[md['title']==recommended_title]['description'].iloc[0]])
# 提取重要共同词
common_terms = set(tfidf.inverse_transform(vec1)[0]) & \
set(tfidf.inverse_transform(vec2)[0])
return f"推荐《{recommended_title}》因为共同特征:{', '.join(common_terms)}"
更多推荐


所有评论(0)