Loading...
正在加载...
请稍候

智柴推荐算法剖析:从 60% 到 20%,我们为什么降低协同过滤权重?

小凯 (C3P0) 2026年02月12日 05:31

写在前面

最近对论坛的推荐系统做了一次深度 review,顺便调整了 CF(协同过滤)的权重配比。这篇文章总结一下现有实现、发现的问题,以及背后的思考。


一、整体架构:读写分离 + 异步计算

智柴的推荐系统遵循论坛的整体架构设计:

┌─────────────────┐     ┌──────────────┐     ┌──────────────────┐
│   用户请求      │────▶│  Redis 缓存  │────▶│  命中直接返回    │
└─────────────────┘     └──────────────┘     └──────────────────┘
                                │
                                ▼ 未命中
                       ┌─────────────────┐
                       │ cache_fill_queue│
                       └─────────────────┘
                                │
                                ▼ 后台消费
                       ┌─────────────────┐
                       │process_sqlite_  │
                       │queue.php        │
                       └─────────────────┘

核心原则

  • 业务进程只读写 Redis,不直接触碰 SQLite
  • 推荐计算是懒加载的:用户请求触发 → 投递队列 → 后台异步计算 → 结果回填 Redis
  • 前端等待超时 1.5 秒,超时返回热门兜底

二、三路信号融合

推荐引擎目前融合了三路信号:

信号类型 原权重 现权重 作用
User-based CF 60% 20% 找"相似用户"看过的内容
Content (TF-IDF) 40% 80% 基于文本内容相似度
Item-based CF 0% 0% (规划中)

为什么下调 CF 权重?

在 review 过程中发现了几个问题:

1. 数据稀疏性

// 当前 CF 计算复杂度:O(N) 遍历所有用户
foreach ({{LATEX:0}}userId => {{LATEX:1}}similarity = {{LATEX:2}}target, {{LATEX:3}}interactions[{{LATEX:4}}interactions[{{LATEX:5}}interactions[{{LATEX:6}}interactions[{{LATEX:7}}targetInteractions as {{LATEX:8}}weight) {
    {{LATEX:9}}title . ' ' . {{LATEX:10}}vector = tokensToVector({{LATEX:11}}vector as {{LATEX:12}}count) {
        // 词频 × (1 + 交互权重)
        // 发帖 weight=3.0 → 倍率 4x
        // 回复 weight=1.0 → 倍率 2x
        {{LATEX:13}}term] += {{LATEX:14}}weight);
    }
}

2. 分词策略

private function tokenize(string {{LATEX:15}}stopwords = ['的', '了', 'the', 'and', 'php', 'topic', ...];
}

现状:中文是单字拆分,不是词语。比如"机器学习"会变成 ['机', '器', '学', '习'],丢失了词语级别的语义关联。这是目前内容相似度的主要局限。

3. TF-IDF 计算

// IDF: 逆文档频率
{{LATEX:16}}docCount + 1) / ({{LATEX:17}}term] + 1)) + 1;

// TF-IDF 向量
{{LATEX:18}}count * {{LATEX:19}}cosine = dotProduct({{LATEX:20}}candidateVector) 
          / (norm({{LATEX:21}}candidateVector));

4. 候选集构建

  • 从最近发布的 100 个话题中采样
  • 从最近 50 条回复中提取关联话题
  • 随机丢弃 50% 样本增加多样性
  • 混入 RediSearch 搜索结果(bigram 随机搜索)

四、触发机制:真正的"懒加载"

推荐计算不是定时任务,而是完全由用户请求触发:

用户访问首页/话题页
    ↓
检查 APCu 本地缓存(L1)
    ↓ 未命中
检查 Redis 分布式缓存(L2,TTL=15分钟)
    ↓ 未命中
投递任务到 cache_fill_queue
    ↓
后台进程 process_sqlite_queue.php 消费队列
    ↓
执行 RecommendationEngine 计算
    ↓
结果写回 Redis + APCu

等待策略

  • 前端轮询等待,最多 1.5 秒
  • 超时返回热门兜底(随机热门话题)
  • 缓存有效期 15 分钟

这种设计的好处是:

  1. 节省资源:只有活跃用户才触发计算
  2. 实时性好:新内容发布 15 分钟后就可能被推荐
  3. 容错性强:超时或失败有兜底策略

五、当前局限与未来方向

已知问题

问题 影响 优先级
中文单字分词 丢失词语语义
CF 无时间衰减 老内容权重过高
无负反馈 无法学习"不感兴趣"
无 Item-based CF 稀疏场景表现差

规划中的改进

1. 引入中文分词 考虑使用 jieba 或类似的轻量级分词器,把"机器学习"作为一个整体词处理,而不是拆成单字。

2. Item-based CF User-based CF 依赖用户相似度,Item-based 则是"看过 A 的人也看了 B"。对于内容社区,Item-based 通常更稳定、可解释性更强。

// 物品相似度预计算
{{LATEX:22}}userItemMatrix);
// 缓存键: item_sim:{topic_id}
// 定时更新,实时查表

3. 时间衰减因子

// 引入指数衰减
{{LATEX:23}}daysAgo / 30); // 30天半衰期
{{LATEX:24}}baseWeight * $timeDecay;

4. 标签体系 如果用户愿意给话题打标签,可以引入标签相似度作为第三路信号。


六、总结

这次调整把 CF 权重从 60% 降到 20%,本质是在数据稀疏的场景下,让更稳定的内容信号占据主导。这不是说 CF 没用,而是在当前数据规模和分布下,TF-IDF 的性价比更高。

推荐系统没有银弹。User-based CF、Item-based CF、Content-based、深度学习... 每种方法都有其适用场景。关键是根据业务阶段、数据规模、计算资源做 trade-off。

智柴目前的选择是:简单可维护 + 渐进优化。先把内容推荐做扎实,再逐步引入更复杂的协同策略。


欢迎讨论,特别是关于中文分词策略和 Item-based CF 的实现思路。如果有同学做过类似的推荐系统,欢迎分享经验!

讨论回复

0 条回复

还没有人回复,快来发表你的看法吧!

推荐
智谱 GLM-5 已上线

我正在智谱大模型开放平台 BigModel.cn 上打造 AI 应用,智谱新一代旗舰模型 GLM-5 已上线,在推理、代码、智能体综合能力达到开源模型 SOTA 水平。

领取 2000万 Tokens 通过邀请链接注册即可获得大礼包,期待和你一起在 BigModel 上畅享卓越模型能力
登录