基本介绍:
在推荐系统领域,协同过滤(Collaborative Filtering)是一种常见且有效的推荐算法。其中,基于用户的协同过滤(UserCF)是一种流行的方法,它通过分析用户之间的行为和偏好来进行推荐。
UserCF设计基本流程:
-
数据准备
UserCF算法的实施需要一定的数据支持。首先,我们需要获得用户对项目(或物品)的评分数据,可以是用户对电影的评分、用户对商品的购买行为等。其次,我们需要构建用户-项目的评分矩阵,矩阵的行表示用户,列表示项目,矩阵中的每个元素表示用户对项目的评分。 -
相似度计算
在UserCF算法中,我们需要计算用户之间的相似度。常用的相似度计算方法包括余弦相似度、皮尔逊相关系数等。 -
用户相似度矩阵构建
根据相似度计算的结果,我们可以构建用户相似度矩阵。该矩阵的行和列分别表示用户,矩阵中的每个元素表示对应用户之间的相似度。我们可以使用一个二维数组或字典来表示该矩阵,其中数组或字典的键可以是用户的唯一标识符。 -
候选项目生成
在UserCF算法中,我们需要为目标用户生成一组候选项目。这些候选项目是由与目标用户相似的其他用户喜欢的项目组成的。具体而言,对于目标用户,我们可以通过相似度矩阵找到与其相似度最高的K个用户,然后将这些用户喜欢的项目加入候选项目列表。 -
推荐列表生成
基于候选项目列表,我们可以生成最终的推荐列表。一种常用的方法是根据候选项目的流行度进行排序,选择热门度较高的项目作为推荐结果。另一种方法是根据候选项目与目标用户的相似度加权进行排序,选择与目标用户更为相似的项目作为推荐结果。
简单实现:
根据用户对于景点的评分,给用户推荐景点(python)
# 用字典的形式表示测试数据data,其中键是用户id,值是另一个字典,该字典的键是景点id,值是对该景点评分
data = {
"user1": {"spot1": 5, "spot2": 3, "spot3": 2},
"user2": {"spot1": 5, "spot2": 1, "spot3": 2, "spot4": 4, "spot5": 5},
"user3": {"spot1": 1, "spot4": 4, "spot6": 5},
"user4": {"spot1": 1, "spot2": 2, "spot3": 5},
"user5": {"spot1": 5, "spot2": 3, "spot7": 4}
}
# 基于皮尔相关系数计算两个用户之间相似度
def cal_similarity(user1, user2):
# 1.获取两个用户都评论的景点,避免不必要计算
common_spots = set(data[user1]).intersection(data[user2])
n = len(common_spots)
if n == 0:
return 0
# 2.计算用户对景点评分和
''' sum1 = 0
for spot in common_spots:
sum1 += data[user1][spot] '''
sum1 = sum(data[user1][spot] for spot in common_spots)
sum2 = sum(data[user2][spot] for spot in common_spots)
# 3.计算评分平方和
sumsq1 = sum(pow(data[user1][spot], 2) for spot in common_spots)
sumsq2 = sum(pow(data[user2][spot], 2) for spot in common_spots)
# 4.乘积和
pSum = sum(data[user1][spot] * data[user2][spot] for spot in common_spots)
# 4.计算系数
num = pSum - (sum1 * sum2) / n
den = ((sumsq1 - pow(sum1, 2) / n) * (sumsq2 - pow(sum2, 2) / n)) ** 0.5
if den == 0:
return 0
r = round(num / den, 2)
return r
# 计算与目标用户的n个相似度最高的用户
def nearst_user(username, n=1):
similarities = []
for user in data:
if user != username:
sim = cal_similarity(user, username)
similarities.append((user, sim))
# 使用第二个字段(相似度) 从大到小排序
similarities.sort(key=lambda x: x[1], reverse=True)
# 返回前n个人
return similarities[:n]
# 根据这些相似用户给目标用户进行推荐
def recommend(username, n=1):
recommendations = {}
users = nearst_user(username, n)
print(users)
for user, _ in users:
for spot in data[user]:
if spot not in data[username] or data[username][spot] == 0:
if spot not in recommendations:
recommendations[spot] = 0
# recommendations => 景点:评分
recommendations[spot] = data[user][spot]
# 根据推荐分数排序
re_spots = sorted(recommendations.items(), key=lambda x: x[1], reverse=True)
return re_spots[:n]
# user1 = 'user1'
# user2 = 'user2'
# similarity = cal_similarity(user1, user2)
# print(f'用户{user1}和用户{user2}的相似度为: {similarity}')
print(recommend('user1', 4))
Java版本:
public class UserCF {
private static Map<String, Map<String, Integer>> data = new HashMap<>();
public static void main(String[] args) {
// 初始化数据
initData();
// 计算相似用户
List<Map.Entry<String, Double>> similarUsers = nearestUser("user1", 2);
System.out.println(similarUsers);
// 生成推荐列表
List<Map.Entry<String, Integer>> recommendations = recommend("user1", 3);
System.out.println(recommendations);
}
private static void initData() {
Map<String, Integer> user1 = new HashMap<>();
user1.put("spot1", 5);
user1.put("spot2", 3);
user1.put("spot3", 2);
Map<String, Integer> user2 = new HashMap<>();
user2.put("spot1", 5);
user2.put("spot2", 1);
user2.put("spot3", 2);
user2.put("spot4", 4);
user2.put("spot5", 5);
Map<String, Integer> user3 = new HashMap<>();
user3.put("spot1", 1);
user3.put("spot4", 4);
user3.put("spot6", 5);
Map<String, Integer> user4 = new HashMap<>();
user4.put("spot1", 1);
user4.put("spot2", 2);
user4.put("spot3", 5);
Map<String, Integer> user5 = new HashMap<>();
user5.put("spot1", 5);
user5.put("spot2", 3);
user5.put("spot7", 4);
data.put("user1", user1);
data.put("user2", user2);
data.put("user3", user3);
data.put("user4", user4);
data.put("user5", user5);
}
public static double calSimilarity(String user1, String user2) {
Set<String> commonSpots = new HashSet<>(data.get(user1).keySet());
commonSpots.retainAll(data.get(user2).keySet());
int n = commonSpots.size();
if (n == 0) {
return 0;
}
int sum1 = commonSpots.stream().mapToInt(spot -> data.get(user1).get(spot)).sum();
int sum2 = commonSpots.stream().mapToInt(spot -> data.get(user2).get(spot)).sum();
double sumsq1 = commonSpots.stream().mapToDouble(spot -> Math.pow(data.get(user1).get(spot), 2)).sum();
double sumsq2 = commonSpots.stream().mapToDouble(spot -> Math.pow(data.get(user2).get(spot), 2)).sum();
int pSum = commonSpots.stream().mapToInt(spot -> data.get(user1).get(spot) * data.get(user2).get(spot)).sum();
double num = pSum - (sum1 * sum2) / (double) n;
double den = Math.sqrt((sumsq1 - Math.pow(sum1, 2) / (double) n) * (sumsq2 - Math.pow(sum2, 2) / (double) n));
if (den == 0) {
return 0;
}
return Math.round(num / den * 100) / 100.0;
}
public static List<Map.Entry<String, Double>> nearestUser(String username, int n) {
List<Map.Entry<String, Double>> similarities = new ArrayList<>();
for (String user : data.keySet()) {
if (!user.equals(username)) {
double sim = calSimilarity(user, username);
similarities.add(new AbstractMap.SimpleEntry<>(user, sim));
}
}
similarities.sort((a, b) -> Double.compare(b.getValue(), a.getValue()));
return similarities.subList(0, Math.min(n, similarities.size()));
}
public static List<Map.Entry<String, Integer>> recommend(String username, int n) {
Map<String, Integer> recommendations = new HashMap<>();
List<Map.Entry<String, Double>> similarUsers = nearestUser(username, n);
for (Map.Entry<String, Double> similarUser : similarUsers) {
String user = similarUser.getKey();
for (Map.Entry<String, Integer> entry : data.get(user).entrySet()) {
String spot = entry.getKey();
int rating = entry.getValue();
if (!data.get(username).containsKey(spot) || data.get(username).get(spot) == 0) {
recommendations.put(spot, recommendations.getOrDefault(spot, 0) + rating);
}
}
}
List<Map.Entry<String, Integer>> sortedRecommendations = new ArrayList<>(recommendations.entrySet());
sortedRecommendations.sort((a, b) -> Integer.compare(b.getValue(), a.getValue()));
return sortedRecommendations.subList(0, Math.min(n, sortedRecommendations.size()));
}
}