【算法】协同过滤

一、引言

        协同过滤算法是一种基于用户行为数据的推荐算法,广泛应用于电商、社交网络、内容推荐等领域。

        

二、算法原理

        协同过滤算法主要分为两类:基于用户的协同过滤(User-based CF)和基于物品的协同过滤(Item-based CF)。以下是这两种算法的基本原理:

        基于用户的协同过滤:找到与目标用户兴趣相似的其他用户,推荐那些相似用户评价高但目标用户尚未评价的物品。

        基于物品的协同过滤:分析物品之间的相似性,推荐与用户已喜欢物品相似的其他物品。

三、数据结构

在实现协同过滤算法时,常用的数据结构包括:

        用户-物品评分矩阵:一个稀疏矩阵,行表示用户,列表示物品,矩阵中的值表示用户对物品的评分。

        相似度矩阵:用于存储用户或物品之间的相似度,通常使用余弦相似度、皮尔逊相关系数等计算方法。

四、算法使用场景

        电商推荐:根据用户的历史购买行为推荐商品。

        社交网络:推荐可能认识的人或可能感兴趣的话题。

        内容推荐:推荐用户可能感兴趣的文章、新闻、视频等。

五、算法实现

以基于物品的协同过滤为例,实现步骤包括:

        计算物品相似度:使用余弦相似度或皮尔逊相关系数。

        预测用户对未评分物品的评分:基于相似物品的平均评分。

        推荐:对未评分物品按预测评分排序,推荐评分最高的物品。

import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# 用户-物品评分矩阵
ratings = np.array([[5, 4, 0, 0],
                    [4, 0, 0, 3],
                    [0, 0, 4, 5],
                    [0, 3, 4, 0]])

# 计算用户之间的相似度
user_similarity = cosine_similarity(ratings)

# 推荐函数
def recommend(user_index, ratings, user_similarity, n_recommendations=2):
    similar_users = user_similarity[user_index]
    similar_users_indices = np.argsort(similar_users)[::-1]
    
    recommendations = []
    for similar_user in similar_users_indices:
        if similar_user != user_index:
            for item_index, rating in enumerate(ratings[similar_user]):
                if rating > 0 and ratings[user_index][item_index] == 0:
                    recommendations.append(item_index)
                    if len(recommendations) >= n_recommendations:
                        return recommendations
    return recommendations

# 推荐给用户0的物品
recommended_items = recommend(0, ratings, user_similarity)
print("Recommended items for user 0:", recommended_items)

六、其他同类算法对比

        基于内容的推荐:根据物品的属性和用户偏好进行推荐,不依赖用户间或物品间的直接关联。

        原理:根据物品的特征进行推荐。
        优点:能推荐用户未接触过的物品。
        缺点:需要物品的详细特征,且容易陷入推荐的局限性。

        矩阵分解:如SVD(Singular Value Decomposition),通过降维来发现用户和物品的潜在特征,适用于大规模稀疏数据。

        深度学习模型:如NCF(Neural Collaborative Filtering),利用神经网络学习用户和物品的复杂关系,适用于非线性关系的数据。

        原理:使用深度学习模型(如神经网络)进行推荐。
        优点:能够处理复杂的非线性关系。
        缺点:需要大量数据和计算资源。

        混合推荐(Hybrid Recommendation):

        原理:结合协同过滤和基于内容的推荐。
        优点:综合两者的优点,提升推荐效果。
        缺点:实现复杂度增加。

七、多语言实现

        多语言代码实现:

     Java   

import java.util.*;

class UserBasedCF {
private Map<String, Map<String, Integer>> userItemRatings; // 用户-物品评分矩阵
private Map<String, Map<String, Double>> similarityMatrix; // 用户之间的相似度矩阵

// 构造函数,初始化评分矩阵并计算相似度
public UserBasedCF(Map<String, Map<String, Integer>> userItemRatings) {
this.userItemRatings = userItemRatings;
this.similarityMatrix = new HashMap<>();
computeSimilarity();
}

// 计算用户之间的相似度
private void computeSimilarity() {
for (String user1 : userItemRatings.keySet()) {
for (String user2 : userItemRatings.keySet()) {
if (!user1.equals(user2)) {
double similarity = calculateSimilarity(user1, user2); // 计算相似度
similarityMatrix.computeIfAbsent(user1, k -> new HashMap<>()).put(user2, similarity);
}
}
}
}

// 计算两个用户的相似度
private double calculateSimilarity(String user1, String user2) {
Set<String> commonItems = new HashSet<>(userItemRatings.get(user1).keySet());
commonItems.retainAll(userItemRatings.get(user2).keySet()); // 获取共同评分的物品

if (commonItems.isEmpty()) return 0.0; // 如果没有共同物品,则相似度为0

double sum1 = 0.0, sum2 = 0.0, sumProduct = 0.0;
for (String item : commonItems) {
sum1 += userItemRatings.get(user1).get(item); // 评分总和
sum2 += userItemRatings.get(user2).get(item);
sumProduct += userItemRatings.get(user1).get(item) * userItemRatings.get(user2).get(item); // 评分乘积总和
}

return sumProduct / (Math.sqrt(sum1) * Math.sqrt(sum2)); // 返回相似度
}

// 推荐物品给用户
public List<String> recommend(String user, int n) {
Map<String, Double> scores = new HashMap<>(); // 存储推荐物品及其分数
for (String otherUser : similarityMatrix.get(user).keySet()) {
double similarity = similarityMatrix.get(user).get(otherUser);
for (String item : userItemRatings.get(otherUser).keySet()) {
if (!userItemRatings.get(user).containsKey(item)) { // 只推荐用户未评分的物品
scores.put(item, scores.getOrDefault(item, 0.0) + similarity * userItemRatings.get(otherUser).get(item));
}
}
}
return scores.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) // 按分数排序
.limit(n) // 获取前n个物品
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
}

Python

class UserBasedCF:
def __init__(self, user_item_ratings):
"""
初始化协同过滤模型,传入用户评分数据并计算用户之间的相似度。

:param user_item_ratings: 用户对物品的评分字典
"""
self.user_item_ratings = user_item_ratings # 用户评分数据
self.similarity_matrix = {} # 用户相似度矩阵
self.compute_similarity() # 计算相似度

def compute_similarity(self):
"""计算用户之间的相似度"""
for user1 in self.user_item_ratings:
for user2 in self.user_item_ratings:
if user1 != user2:
similarity = self.calculate_similarity(user1, user2) # 计算相似度
self.similarity_matrix.setdefault(user1, {})[user2] = similarity # 更新相似度矩阵

def calculate_similarity(self, user1, user2):
"""计算两个用户的相似度"""
common_items = set(self.user_item_ratings[user1]) & set(self.user_item_ratings[user2]) # 找到共同评分的物品
if not common_items:
return 0.0 # 没有共同物品时相似度为0

# 计算评分总和和评分乘积
sum1 = sum(self.user_item_ratings[user1][item] for item in common_items)
sum2 = sum(self.user_item_ratings[user2][item] for item in common_items)
sum_product = sum(self.user_item_ratings[user1][item] * self.user_item_ratings[user2][item] for item in common_items)

return sum_product / (sum1 * sum2) ** 0.5 # 返回相似度

def recommend(self, user, n):
"""为用户推荐物品"""
scores = {} # 存储推荐物品及其分数
for other_user, similarity in self.similarity_matrix.get(user, {}).items():
for item in self.user_item_ratings[other_user]:
if item not in self.user_item_ratings[user]: # 只推荐用户未评分的物品
scores[item] = scores.get(item, 0) + similarity * self.user_item_ratings[other_user][item]
return sorted(scores, key=scores.get, reverse=True)[:n] # 按得分排序并返回前n个物品

C++

#include <vector>
#include <unordered_map>
#include <algorithm>
#include <cmath>
#include <unordered_set>

class UserBasedCF {
public:
// 构造函数,接收用户物品评分
UserBasedCF(const std::unordered_map<std::string, std::unordered_map<std::string, int>>& ratings)
: userItemRatings(ratings) {
computeSimilarity(); // 计算相似度
}

// 为用户推荐 n 个物品
std::vector<std::string> recommend(const std::string& user, int n) {
std::unordered_map<std::string, double> scores; // 存储推荐分数
for (const auto& other : similarityMatrix[user]) {
const std::string& otherUser = other.first;
double similarity = other.second; // 取得相似度
for (const auto& item : userItemRatings[otherUser]) {
if (userItemRatings[user].count(item.first) == 0) { // 只推荐未评分物品
scores[item.first] += similarity * item.second; // 更新分数
}
}
}
// 按分数排序并返回前 n 个物品
std::vector<std::pair<std::string, double>> sortedScores(scores.begin(), scores.end());
std::sort(sortedScores.begin(), sortedScores.end(), [](const auto& a, const auto& b) { return a.second > b.second; });
std::vector<std::string> recommendations;
for (int i = 0; i < std::min(n, (int)sortedScores.size()); ++i) {
recommendations.push_back(sortedScores[i].first);
}
return recommendations;
}

private:
std::unordered_map<std::string, std::unordered_map<std::string, int>> userItemRatings; // 用户-物品评分
std::unordered_map<std::string, std::unordered_map<std::string, double>> similarityMatrix; // 相似度矩阵

// 计算用户之间的相似度
void computeSimilarity() {
for (const auto& user1 : userItemRatings) {
for (const auto& user2 : userItemRatings) {
if (user1.first != user2.first) { // 确保不比较自身
double similarity = calculateSimilarity(user1.first, user2.first); // 计算相似度
similarityMatrix[user1.first][user2.first] = similarity; // 更新相似度矩阵
}
}
}
}

// 计算两个用户之间的相似度
double calculateSimilarity(const std::string& user1, const std::string& user2) {
std::unordered_set<std::string> commonItems; // 存储共同物品
for (const auto& item : userItemRatings[user1]) {
if (userItemRatings[user2].count(item.first)) {
commonItems.insert(item.first); // 添加共同物品
}
}
if (commonItems.empty()) return 0.0; // 没有共同物品时返回0

double sum1 = 0.0, sum2 = 0.0, sumProduct = 0.0;
for (const auto& item : commonItems) {
sum1 += userItemRatings[user1][item];
sum2 += userItemRatings[user2][item];
sumProduct += userItemRatings[user1][item] * userItemRatings[user2][item]; // 评分乘积
}
return sumProduct / (std::sqrt(sum1) * std::sqrt(sum2)); // 返回相似度
}
};

Go

package main

import (
"math"
"sort"
)

// 定义用户基于协同过滤的结构体
type UserBasedCF struct {
userItemRatings map[string]map[string]int // 用户-物品评分数据
similarityMatrix map[string]map[string]float64 // 用户之间的相似度矩阵
}

// 初始化并计算相似度
func NewUserBasedCF(ratings map[string]map[string]int) *UserBasedCF {
ubcf := &UserBasedCF{
userItemRatings: ratings, // 用户评分数据
similarityMatrix: make(map[string]map[string]float64),
}
ubcf.computeSimilarity() // 计算相似度
return ubcf
}

// 计算用户之间相似度
func (ubcf *UserBasedCF) computeSimilarity() {
for user1 := range ubcf.userItemRatings {
for user2 := range ubcf.userItemRatings {
if user1 != user2 {
similarity := ubcf.calculateSimilarity(user1, user2) // 计算相似度
if ubcf.similarityMatrix[user1] == nil {
ubcf.similarityMatrix[user1] = make(map[string]float64)
}
ubcf.similarityMatrix[user1][user2] = similarity // 更新相似度
}
}
}
}

// 计算两个用户之间的相似度
func (ubcf *UserBasedCF) calculateSimilarity(user1, user2 string) float64 {
// 获取两个用户的共同物品
commonItems := make(map[string]struct{})
for item := range ubcf.userItemRatings[user1] {
if _, exists := ubcf.userItemRatings[user2][item]; exists {
commonItems[item] = struct{}{} // 添加共同物品
}
}

if len(commonItems) == 0 {
return 0 // 没有共同物品时相似度为0
}

var sum1, sum2, sumProduct float64
for item := range commonItems {
sum1 += float64(ubcf.userItemRatings[user1][item]) // 评分总和
sum2 += float64(ubcf.userItemRatings[user2][item])
sumProduct += float64(ubcf.userItemRatings[user1][item] * ubcf.userItemRatings[user2][item]) // 评分乘积
}

return sumProduct / (math.Sqrt(sum1) * math.Sqrt(sum2)) // 返回相似度
}

// 为用户推荐物品
func (ubcf *UserBasedCF) Recommend(user string, n int) []string {
scores := make(map[string]float64) // 存储推荐物品的分数
for otherUser, similarity := range ubcf.similarityMatrix[user] {
for item, rating := range ubcf.userItemRatings[otherUser] {
if _, exists := ubcf.userItemRatings[user][item]; !exists { // 只推荐未评分物品
scores[item] += similarity * float64(rating) // 更新分数
}
}
}

// 排序并返回前 n 个物品
type scoreItem struct {
Item string
Score float64
}
var scoreItems []scoreItem
for item, score := range scores {
scoreItems = append(scoreItems, scoreItem{Item: item, Score: score})
}
sort.Slice(scoreItems, func(i, j int) bool {
return scoreItems[i].Score > scoreItems[j].Score
})

var recommendations []string
for i := 0; i < n && i < len(scoreItems); i++ {
recommendations = append(recommendations, scoreItems[i].Item) // 添加推荐物品
}
return recommendations
}

八、实际服务应用场景代码框架

        在电影推荐系统中,协同过滤算法可以基于用户对电影的评分历史,推荐用户可能感兴趣的电影。实际服务中,代码框架可能包括:

        数据收集与预处理:收集用户评分数据,清洗和格式化数据。

        模型训练:使用协同过滤算法训练模型。

        推荐服务:基于用户请求,提供实时的推荐结果。

        反馈循环:收集用户对推荐结果的反馈,用于模型迭代和优化。

应用场景:电子商务平台的商品推荐

框架结构

        数据收集:从用户行为中收集数据

        数据预处理:构建用户-物品评分矩阵

        模型训练:使用协同过滤算法进行训练

        推荐生成:生成推荐列表

        前端展示:将推荐结果展示给用户

Python代码框架

from flask import Flask, request, jsonify
from surprise import Dataset, Reader, KNNBasic
from surprise.model_selection import train_test_split
from surprise import accuracy

app = Flask(__name__)

# Load dataset and train model
def train_model():
    data = Dataset.load_builtin('ml-100k')
    reader = Reader(line_format='user item rating timestamp', sep='\t')
    trainset, _ = train_test_split(data, test_size=0.2)
    sim_options = {'name': 'cosine', 'user_based': True}
    model = KNNBasic(sim_options=sim_options)
    model.fit(trainset)
    return model

model = train_model()

@app.route('/recommend', methods=['POST'])
def recommend():
    user_id = request.json.get('user_id')
    # Generate recommendations (dummy logic for demo)
    recommendations = [{'item_id': 'item1', 'score': 5.0}]
    return jsonify(recommendations)

if __name__ == '__main__':
    app.run(debug=True)

        协同过滤推荐系统是一种强大的工具,能够根据用户的历史行为来发现潜在的喜好,并提供个性化的推荐。了解协同过滤的原理、实现和应用场景,多语言的代码实践。在实际应用中,协同过滤算法可以帮助企业提高用户满意度和参与度,增加销售额和市场份额。随着技术的发展,协同过滤算法及其变种在推荐系统领域的应用将更加广泛。

  • 12
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shinelord明

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值