CSDN 个性化推荐系统的设计和演进

个性化推荐项目

  • 个性化推荐的设计和演进
    • 项目概览
    • 项目梳理
      • 依赖管理
      • 实现代码的重构和改进
      • 持续演化

个性化推荐的设计和演进

CSDN 的个性化推荐系统,是从既有的推荐项目中剥离出来的一个子项目,这个项目随后移交到了我们AI组。在近一年的时间内,我们对这个项目进行了多次改进和重构。

项目概览

首先,我希望强调一点,个性化推荐系统的整体设计非常好,我交接时拿到的是一个以解释器模式驱动的spring服务:
在这里插入图片描述

从外部接口看, 它是个相当正统的 http json 服务,各种服务接口以 JSON API 的形式提供,大部分都是 POST 请求。从内部看,它没有使用常规的API/服务/关系型数据库的分层模式,数据持久层使用了hbase,把复杂性封装在应用层。之前构造项目的同事设计了一套非常漂亮的解释器机制,把推荐策略设计成可以通过JSON定制的形式,在运行期解释为策略图(graph of strategy)对象(这一步甚至是热更新的),任务执行器依据策略图访问对应的策略组件——它们通常是 spring service 对象——从而得到最终推送给用户的信息流。
在深入到后面的内容之前,我们先回顾一下这里出现的一些名词:

  • API 这里我特指这些用 spring controller 定义的 http json 服务,它们大多是 POST,有少量是 GET 请求
  • 策略图 strategy graph 是用 JSON 定义的策略,它定义一个推送信道来自哪些召回源,经由哪些过滤器和组合策略,最终成为一个线性的推荐信息流,这些图总是定义为一个有向的树图,可能有多个起源,但是最终汇聚为一个唯一的信息出口。有向图中的节点需要定义一个唯一的 id,其中大部分需要制定自己的 next id,而最后一个节点,显然没有 next。
  • 任务执行器 在项目中被称为 task executor,它从配置中心读取 strategy graph 定义,调用 strategy service 执行,用于响应 http 请求或定时任务
  • 策略组件 这些组件以 spring service 的形式运行在项目中,大致可以分为这样几类:
    • 信道 channel 是信息流的定义单元,不同的业务方在调用时会访问不同的信道,策略执行器根据配置执行对应的策略图,返回其对应的信息流
    • 召回策略 大部分是封装 hbase 查询,但是也有少量调用其他 http 服务或 redis
    • 过滤策略 过滤策略通常是用于将符合条件的信息保留下来,抛弃其它,我也写了几个过滤器组件,并不直接修改信息流,而是用于记录和计算,这个后面讨论。
    • 组合策略 组合策略通常是是一个策略图的最后一个节点。召回(callback)策略和过滤(filter)策略通常是无状态的。例如,多个召回策略的next都指向了同一个过滤器策略id,在实际执行时,它们也会被视作各自独立的路线,因为过滤策略是无状态的,即使调用用一个过滤器,也可以视作几次无状态的函数调用——如果我们实现了有状态的过滤器,那就要小心维护其状态在并发环境中不被破坏。而唯一会合并执行路径的,就是组合策略,执行器会将所有指向同一个组合策略的数据汇总后一次传给组合策略,获取其组合操作的结果,在这些组合策略中我们实现推送流的去重、剪裁等操作。
      形象的看,一个策略可能是如下的结构:
      在这里插入图片描述

在组合策略的后面,仍有可能存在过滤器,但是召回策略,总是在执行图的最前面,也就是树的最末端。
除此之外,我们有一些数据处理任务,用于向hbase写入推送数据,这些因为独立性较强,与服务就不做介绍了。
有了这些概念,我就可以简单回顾一下自这个个性化推荐项目交接以来,我们组所做的工作。

项目梳理

刚写了一本解释器教程的我,接手这个项目的时候,可以说非常的欣喜,从这个项目的设计中读到了很多共鸣之处。但是项目的具体实现,仍然有很多需要修正和改进的地方。

依赖管理

首先,项目的依赖库,有很多没有持续的跟进维护,这里面有不少重要的依赖项目已经因为安全和功能bug,做了升级,但是我们仍在使用陈旧的版本。与此相比,仍然在使用 java 8倒不是主要的问题了。
我跟进IDE的提示,对依赖库做了尽可能的升级,这里面确实遇到了一些兼容问题,为此我修改了一部分调用逻辑,但是总体来说,这个工作非常的值得。我遇到过很多项目,历史遗留的代码哪怕再简单,也没有人愿意下功夫去阅读和梳理,这里面固然有开发人员的惰性,但主要仍然是管理问题。不付出一定工作量去处理这些问题,久而久之,这些版本问题就成了一种迷信,即使其中有问题,也没有人原因冒着哪怕万分之一的风险去修改。我就从个性化推荐的项目依赖中删掉了一个非常古老的依赖,这个代码库在 search.maven.org 上能找到的最近一次更新是20年前。我删掉它以后,一直到现在,没有发生任何问题,也就是说,其实根本就没有用到这个库,但是没有人敢去动它。
这个过程中,一个很大的改动是,我用 Jackson 代替了 fastjson。Fastjson有很多优点,但是和很多 Java 项目一样,个性化推荐中对 fastjson 的使用处于失序状态 ,无论最初定义的版本出于何等动机,后续在也没有人面对那些 cve ,去做过任何升级维护。而 fastjson 社区本身 ,也早就推荐用户改用 fastjson2 了。
所谓“稳定”作为不升级的借口,在我看来是非常难以忍受的。我改用 jackson 也无非是因为这样几点:

  • 我对 Jackson 有足够的了解,如果用 fastjson2,那么jackson用户(实际上就是我)和fastjson(公司既有的java开发人员)都不了解它,而 jackson 至少我可以驾驭
  • Jackson 不是最快的 JSON库,也并非没有缺陷,但是它足够活跃,综合各方面表现,也足够使用,Spring、lombok等常规工具与之也配合良好。
  • Jackson 的使用过程并不是最简洁的,但是足够灵活,我可以针对不同的使用场景,定义不同的ObjectMapper——其实我看到fastjson也有类似的功能,但是这样用的人似乎不多,使用 fastjson,主要还是因为它有足够方便的快捷调用风格,并且这个风格经过了深度的性能优化。

实现代码的重构和改进

除了这种基础工具的改动,最大的问题是,之前最常用的几个组合策略其实始终没有达到设计目标,从上游信息流中抽取信息项的逻辑,实现的非常粗糙。为了最终构造出足够好的用户体验,不得不将策略图配置的非常复杂,几个最主要的信息流都是多层组合策略,通过将组合策略再次作为召回源使用,来实现信息流的随机行为。
因此,我重写了组合策略,包括所有组合策略的公共基类和所有的实现子类。引入了非平均随机概率。使得信息流的构造可以兼顾随机性和排名。并且完整的重写了整个业务逻辑和配置, 去掉了那些不必要的多层组合。
在这个过程中,我遇到了很多细节问题,比如过去代码中对信息项的score值,是按升序排列,分数越小越优先。这个问题其实不影响项目的正常运行,但是对开发人员非常的不友好, 可以说这是我二十三年的开发工作中,第一次遇到推荐系统的 score 居然不是降序,于是我在重写组合策略时,预留了排序方向的设定能力,终于最近一段时间,负责数据分析任务的同事修改了上游的任务,个性化推荐也就迅速切换到了常规的降序排列。
这里面还有一个重要的改动是,因为过滤器逻辑在过去的实现中,存在一些不稳定的因素,导致负反馈等过滤器经常失效。具体来说,最初所有的过滤器都是用可删除的迭代器去遍历信息流。而我经过debug,发现有时候这个迭代器会失效,这是因为在信息传递的过程中,有时会经历几次序列化和反序列化,一些反序列化代码(它们来自第三方代码库)会将list还原为某些不能删除的类型。
我将发现的这些迭代器操作, 都改写成了常规的 stream api 风格,总是通过 filter 生成一个新的 list 传递给下游节点。
相信我,在一个复杂项目中,使用可删除迭代器做 in place 的写操作,并不能带来多少性能优势,相反可能会发生这种极难发现的bug。
对于推荐系统,“不重复推荐”是很常规的能力,过去向某个用户推荐过的内容,会写在hbase里,在推荐过程中再通过过滤器查询和筛选。这里面不仅仅是多了一个hbase写入,为了不阻塞hbase,还经过了一个消息队列和几个异步任务,带来了非常多的不确定性。于是我将其简化为一个可以去重的组合策略,将历史内容缓存到redis中。这样可以有效缩短响应时间,也大大简化了项目结构。
当然,在改动过程中,我们也遇到了很多问题,比如一开始对redis key的有效期设置有问题——redis的 spring库有些很细节的东西,会导致代码中的操作顺序未必会与实际操作执行的顺序一致——这些问题也导致我们经历了一段手忙脚乱的时间。感谢同事们的支持和帮助,最终我们解决了这些问题,现在这些功能运转良好,并且比过去更加容易维护和管理。

持续演化

在这些改动的过程中,实际影响最大的是首页推荐流,在终于解决了负反馈失效的问题后,我们简化了负反馈的流程,将其从过去的异步任务访问日志->填充数据->加载过滤的过程,简化为通过个性化推荐的内部API直接记录负反馈。这里面使用 hbase 做了些 OLTP 的工作,虽然 hbase 并不擅长做这样的工作,但是至少我们不必再基于一个漫长的链路来实现负反馈业务,回报是丰厚的。现在负反馈切实的起作用了!
三四月份,我用了一些时间,实现了一些内部的调试器功能,这些功能并不服务于最终用户,但是在我们的开发过程中,这些代码起了重要的作用。我们可以通过这些接口实时查看redis、hbase和服务进程的工作状态,数据的细节。调试器极大的优化了日常的开发工作。
目前,我们正在落实实时正反馈的功能开发,力求将用户的使用体验,更快更有效的体现于内容推荐服务,使个性化推荐系统更智能、精准和友善。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/36991.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Apollo、RocketMQ加载顺序问题

在SpringCloudAlibaba框架中,因Nacos配置中心管理权限过于简单,决定用Apollo代替Nacos配置中心,但在启动时,Nacos、Redis等配置读取正常,RocketMQ由于启动过早,无法从Apollo读取自己的服务地址配置。 报错…

第41节:cesium 闪烁效果-熊出没(含源码+视频)

结果示例: 点的闪烁:1.逐渐放大后消失;2.点闪烁。 图的闪烁:熊出没,含自定义显示文字效果。 完整源码: <template><div class="viewer"><vc-viewer @ready="ready" :logo="false">

科技资讯|苹果Vision Pro手部追踪和手势相关新专利曝光

近日&#xff0c;美国专利商标局正式授予苹果一项与 Apple Vision Pro 主要功能相关的专利&#xff1a;手部追踪和手指手势。 苹果专利指出&#xff0c;沉浸感的质量取决于几个重要因素。例如&#xff0c;显示器的特性&#xff0c;如图像质量、帧率、像素分辨率、高动态范围 …

信号波形时序图常用工具推荐

Refer: 下载&#xff1a;画时序图的四大神器_可编程器件-面包板社区 (eet-china.com) 软件工程师&#xff0c;习惯使用StarUML画Timing Diagram, 硬件工程师建议使用一下软件。 1、AndyTiming 免费的&#xff0c;这个有一个知乎的博文可以参考 https://zhuanlan.zhihu.com/p…

如何成为微软MVP?

对一个普通的开发人员来说&#xff0c;最大的认可就是得到微软官方的MVP 认证了&#xff0c;是一份对技术人的荣誉证书。 微软的MVP是相对公平公正的&#xff0c;只要你热爱技术&#xff0c;热爱分享&#xff0c;在一定的领域里有足够的深度&#xff0c;就会得到微软官方的认证…

MURF2080CT-ASEMI快恢复二极管对管MURF2080CT

编辑&#xff1a;ll MURF2080CT-ASEMI快恢复二极管对管MURF2080CT 型号&#xff1a;MURF2080CT 品牌&#xff1a;ASEMI 封装&#xff1a;TO-220F 恢复时间&#xff1a;75ns 正向电流&#xff1a;20A 反向耐压&#xff1a;800V 芯片个数&#xff1a;2 引脚数量&#xf…

〖码银送书第三期〗《Python机器学习:基于PyTorch和Scikit-Learn》

前言 近年来&#xff0c;机器学习方法凭借其理解海量数据和自主决策的能力&#xff0c;已在医疗保健、 机器人、生物学、物理学、大众消费和互联网服务等行业得到了广泛的应用。自从AlexNet模型在2012年ImageNet大赛被提出以来&#xff0c;机器学习和深度学习迅猛发展&#xf…

WebDAV之π-Disk派盘 + PDF Expert

PDF Expert 支持WebDAV方式连接π-Disk派盘。 PDF Expert是一款macOS上的办公软件,它具有专业的PDF编辑功能,可以快速从邮件、网页支持PDF打开,支持用户进行阅读、批注等功能,用户可以直接在PDF上进行编辑文字图片,表单文档、创建笔记、添加书单等自定义使用,大大提高工…

C国演义 [第五章]

第五章 子集题目理解步骤树形结构递归函数递归结束的条件单层逻辑 代码 子集II题目理解步骤树形结构递归函数递归结束的条件单层逻辑 代码 子集 力扣链接 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的子集&#xff08;幂集&#xff09;。…

HarmonyOS/OpenHarmony应用开发-程序包多HAP机制(上)

一、多HAP机制设计目标 方便开发者模块化的管理应用&#xff0c;好的应用一般都是模块化管理&#xff0c;模块之间属于松耦合关系。多HAP方便了开发者将业务划分成多个模块&#xff0c;每个模块放到独立的HAP中。例如支付类应用&#xff0c;有统一的主界面&#xff0c;主界面管…

Windows mingw64 最简易 安装配置

其实挺简单一件事 很多教程都搞复杂了 自己写一个 只需要两步 1. 下载压缩包并解压 2. 配置环境变量 (1). GitHub 下载地址 Releases niXman/mingw-builds-binaries GitHub 如果GitHub下载太慢可以来这里加速 或者用地址2 GitHub Proxy 代理加速 (ghproxy.com) (2). 下…

学无止境·MySQL⑥(数据库备份和还原)

数据库备份和还原 备份和还原练习1、创建库和表2、使用mysqldump命令备份数据库中的所有表3、备份booksDB数据库中的books表4、使用mysqldump备份booksDB和test数据库5、使用mysqldump备份服务器中的所有数据库6、使用mysql命令还原第二题导出的book表7、进入数据库使用source命…

Rainbond开源

Rainbond的 Gateway API 插件制作实践 Gateway API 作为新一代的流量管理标准&#xff0c;对原有 Ingress 的扩展不规范、移植性差等问题做出了改进。从兼容K8s生态和优化网关体验出发&#xff0c;Rainbond 支持以插件的形式扩展平台网关能力&#xff0c;目前已经有多家社区提供…

领域知识图谱的医生推荐系统:利用BERT+CRF+BiLSTM的医疗实体识别,建立医学知识图谱,建立知识问答系统

项目设计集合&#xff08;人工智能方向&#xff09;&#xff1a;助力新人快速实战掌握技能、自主完成项目设计升级&#xff0c;提升自身的硬实力&#xff08;不仅限NLP、知识图谱、计算机视觉等领域&#xff09;&#xff1a;汇总有意义的项目设计集合&#xff0c;助力新人快速实…

DevOps基础服务1——版本控制gitlab

文章目录 一、基本了解1.1 安装git客户端1.2 git命令1.2.1 本地仓库1.2.2 远程仓库 二、安装gitlab三、功能管理3.1 创建账号3.2 用户注册授权通知功能3.3 创建项目远程库3.4 ssh设置3.5 克隆远程库项目到本地3.6 上传本地项目代码到远程库3.7 授权用户查看项目权限 一、基本了…

electron+vue3全家桶+vite项目搭建【21】自定义无边框窗口拖拽移动

文章目录 引入实现思路实现步骤1.主进程监听窗口移动2.通信工具补充ipc调用3.渲染进程封装通用拖拽组件 测试 引入 如果你尝试过透明窗口&#xff0c;并控制透明部分事件击穿&#xff0c;就会发现使用 drag属性样式去控制窗口拖拽会导致点击事件失效&#xff0c;并且带drag属性…

陌陌聊天数据分析 (一)

陌陌聊天数据分析&#xff08;一&#xff09; 目标 基于Hadoop和Hive实现聊天数据统计分析&#xff0c;构建聊天数据分析报表 需求 统计今日总消息量统计今日每小时消息量&#xff0c;发送和接收用户数量统计今日各地区发送消息数据量统计今日发送消息和接收消息用户数统计…

机器学习 day25(softmax在神经网络模型上的应用,提高数据精度的方法)

输出层采用softmax 在识别手写数字的模型中&#xff0c;预测y只有两个结果&#xff0c;所以输出层采用sigmoid激活函数且只有一个神经元。若预测y有10个结果&#xff08;0-9&#xff09;&#xff0c;该模型的前向传播计算方式与识别数字的模型完全相同&#xff0c;即隐藏层的…

符号化的正确姿势

GUI方式 将 .ips crash report 文件拖放到 Xcode > Window > Devices and Simulators > View Device Logs中, 然后导出 .crash 符号化文件. 使用条件: crash report 对应的 Archive 包是在本机构建的 symbolicatecrash symbolicatecrash 是一个 exec (可执行文件), …

Stepper, Slider 的使用

1. Stepper 步进器的使用 1.1 实现 /// 步进器 /加减控件 struct StepperBootcamp: View {State var stepperValue: Int 10State var widthIncrement: CGFloat 0var body: some View {VStack {Stepper("Stepper: \(stepperValue)", value: $stepperValue).padding…