当前位置: 首页 > news >正文

推荐系统入门最佳实践:Slope One 算法详解与完整实现

手把手教你实现 Slope One 个性化推荐算法

在人人都在谈论“精准推荐”的时代,如何用最简单、最高效的方式,实现对用户的个性化评分预测?本文将带你从零开始,深入浅出地掌握 Slope One 算法的核心原理与完整实现,手把手教你一步步落地。


☕ 请作者喝杯咖啡,持续更新更深入的干货


一、为什么选择 Slope One?

  • 极简公式:只需差值(diff)与频次(freq)两张矩阵,轻松完成预测。
  • 易于实现:几百行 Java 代码就能跑通原型,性能也很可观。
  • 可扩展性强:可与其他协同过滤算法混合使用,适应各种场景。

如果你还在为多种复杂模型纠结,不妨试试这份轻量级“小钢炮”!


二、算法原理一览

  1. 差值矩阵

    • 对于任意两个物品 i i i j j j,计算所有用户对它们评分差的累积之和,再除以用户数,得到平均差值

    diff [ i ] [ j ] = ∑ u ( r u , i − r u , j ) freq [ i ] [ j ] \text{diff}[i][j] = \frac{\sum_{u} (r_{u,i} - r_{u,j})}{\text{freq}[i][j]} diff[i][j]=freq[i][j]u(ru,iru,j)

  2. 频次矩阵

    • 记录有多少用户同时对 i i i j j j 做过评分

    freq [ i ] [ j ] = ∑ u 1 \text{freq}[i][j] = \sum_{u} 1 freq[i][j]=u1

  3. 预测公式

    • 对目标用户 u u u 未评分的物品 j j j,用他已评分物品 i i i 的评分加上平均差值,加权平均后得出预测分

    r ^ u , j = ∑ i ∈ R u ( diff [ i ] [ j ] + r u , i ) × freq [ i ] [ j ] ∑ i ∈ R u freq [ i ] [ j ] \hat r_{u,j} = \frac{\sum_{i \in R_u} (\text{diff}[i][j] + r_{u,i}) \times \text{freq}[i][j]}{\sum_{i \in R_u} \text{freq}[i][j]} r^u,j=iRufreq[i][j]iRu(diff[i][j]+ru,i)×freq[i][j]


三、环境准备

  • JDK 8+
  • IDE:IntelliJ IDEA、Eclipse 均可
  • 数据文件ratings_set1.dat,格式为 userId,itemId,rating

示例内容(保存在项目根目录):

1,101,4.0
1,102,3.0
1,103,5.0
2,101,5.0
2,102,2.0
3,102,4.0
3,103,4.5

四、一步步代码实现

1. 读取数据并构建用户评分矩阵

Map<Integer, Map<Integer, Float>> users = new HashMap<>();
int maxItemId = 0;try (BufferedReader br = new BufferedReader(new FileReader("ratings_set1.dat"))) {String line;while ((line = br.readLine()) != null) {String[] parts = line.split(",");int user = Integer.parseInt(parts[0]);int item = Integer.parseInt(parts[1]);float rating = Float.parseFloat(parts[2]);users.computeIfAbsent(user, k -> new HashMap<>()).put(item, rating);maxItemId = Math.max(maxItemId, item);}
}
  • 解释:用 Map<用户ID, Map<物品ID, 评分>> 存储;同时记录最大物品编号,方便后续分配矩阵大小。

2. 构建 diff 与 freq 矩阵

float[][] diff = new float[maxItemId+1][maxItemId+1];
int[][] freq  = new int[maxItemId+1][maxItemId+1];for (var entry : users.entrySet()) {var ratings = entry.getValue();for (int i : ratings.keySet()) {for (int j : ratings.keySet()) {diff[i][j] += ratings.get(i) - ratings.get(j);freq[i][j] ++;}}
}// 计算平均差值
for (int i = 1; i <= maxItemId; i++) {for (int j = 1; j <= maxItemId; j++) {if (freq[i][j] > 0) {diff[i][j] /= freq[i][j];}}
}
  • 解释:三重循环,先累加差值与计数,再做一次遍历除以频次。

3. 对新用户进行评分预测

public static Map<Integer, Float> predict(Map<Integer, Float> userRatings,float[][] diff, int[][] freq
) {Map<Integer, Float> result = new HashMap<>();for (int j = 1; j < diff.length; j++) {if (userRatings.containsKey(j)) continue;float numerator = 0, denominator = 0;for (var e : userRatings.entrySet()) {int i = e.getKey();float r = e.getValue();if (freq[i][j] > 0) {numerator   += (diff[i][j] + r) * freq[i][j];denominator += freq[i][j];}}if (denominator > 0) {result.put(j, numerator / denominator);}}return result;
}
  • 解释:遍历所有未评分物品,按公式计算加权平均,生成预测值。

4. 整合 Demo 演示

public class SlopeOneDemo {public static void main(String[] args) throws IOException {// 1. 构建 diff/freqSlopeOneBuilder builder = new SlopeOneBuilder("ratings_set1.dat");float[][] diff = builder.getDiff();int[][] freq = builder.getFreq();// 2. 假设新用户已评分Map<Integer, Float> newUser = Map.of(101, 4.0f, 103, 2.0f);// 3. 预测并打印Map<Integer, Float> preds = SlopeOnePredictor.predict(newUser, diff, freq);System.out.println("Predicted ratings:");preds.forEach((item, rate) ->System.out.printf("Item %d -> %.2f%n", item, rate));}
}
  • 一键跑通:从文件读入到预测输出,只需运行 SlopeOneDemo

五、验证结果示例

  • Simple Demo 输出

    i   j   diff    freq
    101 102 -0.50   2
    101 103 -0.50   1
    102 103  0.25   2
    ……
    
  • 预测输出

    Predicted ratings:
    Item 102 -> 3.25
    

六、性能与优化建议

  1. 并行化计算:对大规模数据,可引入多线程分区处理用户集合。
  2. 稀疏存储:对稀疏矩阵使用 HashMap<Integer, Float> 存储,只保存非零值。
  3. 增量更新:新评分进来时,仅更新受影响的行列,无需全量重算。

七、总结

本文通过简单示例、源码剖析、完整 Demo,以及验结果展示,让你从零学会 Slope One 算法的落地实现。

  • 核心思想:利用用户间的评分差异,推断未知评分。
  • 实战价值:入门推荐系统的第一站,也可与更复杂算法结合。

下一步:试着把代码改写为 Python 或 Spark 版,处理百万级用户与物品!


小提示:保持算法简单、可读性高,再迭代优化性能,才能在工程中快速落地。

祝你推荐系统之路畅通无阻!

☕ 请作者喝杯咖啡,持续更新更深入的干货

💡 彩蛋时间:如果你看到了这里,说明你是那种喜欢动手实战的人。那我悄悄分享一个开发圈流传的工具试用入口,貌似跟高效调试很有关系,地址也挺特别的:

🔗 入口

据说注册还能解锁一些隐藏功能,懂的都懂(别外传 😂)

http://www.xdnf.cn/news/986851.html

相关文章:

  • 记录下blog的成长过程
  • 我的世界进阶模组开发教程——制作机械动力附属模组
  • MySQL存储引擎--深度解析
  • Go 语言 JWT 深度集成指南
  • 什么是哈希函数
  • C语言——深入解析字符串函数与其模拟实现
  • const auto 和 auto
  • Bash 脚本中的特殊变量
  • python使用SQLAlchemy 库操作本地的mysql数据库
  • python基本语法元素
  • python-docx 库教程
  • Oracle中10个索引优化
  • 美团NoCode中的Dev Mode 使用指南
  • 在windows中安装或卸载nginx
  • spring boot源码和lib分开打包
  • 遍历 unordered_map
  • GFS 分布式文件系统
  • UE_Event Any Damage和OnTake Any Damage
  • JAVA CAS 详解
  • Docker完整教程 - 从入门到SpringBoot实战
  • JSON5 模块的作用与区别
  • 图标异常问题
  • 【Linux】进程控制(下)---程序替换宝藏岛
  • 如何排查PHP-FPM进程CPU占用100%的间歇性问题 (2025)
  • Unity 服务器交互开发指南
  • 基于RocketMQ源码理解顺序写、刷盘机制与零拷贝
  • 海康对接摄像头
  • Chromium 136 编译指南 Windows篇:获取源代码(五)
  • 基于贝叶斯学习方法的块稀疏信号压缩感知算法
  • Spring核心框架完全指南 - 基础知识全解析