NewsRecommendSystem

Introduction: 个性化新闻推荐系统,A news recommendation system involving collaborative filtering,content-based recommendation and hot news recommendation, can be adapted easily to be put into use in other circumstances.
More: Author   ReportBugs   
Tags:

若有任何有关项目的问题(导入、运行、后期改进、需求等等),欢迎首先在 issue 里提出,我会随时查看并积极回复!谢谢大家!

中文文档 | English Document

说明

本推荐系统使用的推荐算法包括协同过滤(Collaborative Filtering)、基于内容相似度的推荐(Content-based Recommendation)与热点新闻推荐(Hot News Recommendation):

  • 协同过滤的实现依托于 Mahout 的提供库;
  • 基于内容的相似度推荐在原始算法上基于相关论文做了自主的改进;
  • 热点新闻推荐顾名思义是取最近被最多用户浏览过的新闻进行推荐。

推荐算法的具体细节可参考文件[推荐系统介绍.pdf]

主要使用的库(Lib):

  • Ansj:基于内容的推荐部分用以分词,以及其内含的 TFIDF 算法。
  • Quartz:推荐系统定时运行的设定。
  • Mahout:使用内置的协同过滤算法。
  • Jfinal:使用内置的 ActiveRecord 与 Db 工具,对推荐系统中的数据库表做了实体类映射,以简化数据库相关操作。

本推荐系统需要基于【新闻模块】使用,此处对于【新闻模块】的定义是:有规律地进行新闻采集,并通过公共平台对用户进行新闻展示与推送的应用。当然,这是实际应用的需求,如果只是自己做研究或者实验的话,可以只使用测试数据即可。

使用

预备工作

一、数据库配合

(该步骤主要是为了说明推荐系统要求交互的数据。真实建库的话,直接在新建的数据库中运行测试数据,即可完成所有建表工作,附带提供的测试数据。)

本推荐系统目前只支持与 MYSQL 数据库进行交互

本系统需要与五个表进行交互:用户表(users),新闻表(news),新闻模块表(newsmodules),浏览记录表(newslogs),推荐结果表(Recommendations)。

  • 用户表 users

存储用户基本信息的表。要求至少拥有两个字段:用户 id(id:bigint),用户喜好关键词列表(pref_list:json),用户最近登录时间(latest_log_time:timestamp)。

字段名 类型 非空 主键 外键 自增 默认值
id bigint yes yes yes
pref_list text yes {"moduleid1":{},"moduleid2":{},...}
latest_log_time timestamp yes
  • 新闻表 news

存储新闻基本信息的表。要求至少拥有三个字段:新闻 id(id:bigint),新闻文本内容(content:text),所属模块(module_id)。

字段名 类型 非空 主键 外键 自增 默认值
id bigint yes yes yes
title text yes
content text yes
module_id int yes yes
  • 新闻模块表 newsmodules

存储新闻模块信息的表。要求至少拥有两个字段:模块 id(id:int),模块名称(name:text),抓取时间/新闻日期(news_time:timestamp)。

字段名 类型 非空 主键 外键 自增 默认值
id int yes yes yes
name text yes
news_time timestamp yes
  • 浏览记录表 newslogs

存储用户浏览新闻记录的表。要求至少拥有三个字段:记录 id(id:bigint),用户 id(user_id:bigint),新闻 id(news_id:bigint),浏览时间(view_time:timestamp),用户对新闻的偏好程度(prefer_degree[0:仅仅浏览,1:评论,2:收藏])。

字段名 类型 非空 主键 外键 自增 默认值
id bigint yes yes yes
user_id bigint yes yes
news_id bigint yes yes
view_time timestamp yes
prefer_degree int yes
  • 推荐结果表 Recommendations

存储推荐系统为用户生成的推荐结果及用户反馈的表。要求至少拥有五个字段:推荐结果 id(id:bigint),用户 id(user_id:bigint),新闻 id(news_id:bigint),推荐结果生成时间戳(derive_time:timestamp),用户反馈(feedback:bit[0:用户未浏览,1:用户进行了浏览]),结果生成的对应推荐算法(derive_algorithm:int[0:协同过滤,1:基于内容的推荐,2:热点新闻推荐])

字段名 类型 非空 主键 外键 自增 默认值
id bigint yes yes yes
user_id bigint yes yes
news_id bigint yes yes
derive_time timestamp yes
feedback bit 0
derive_algorithm int yes

二、数据库连接配置

在项目根目录下的 res 目录下,修改 dbconfig.properties 文件中有关数据库的配置:

url = jdbc:mysql://[数据库 ip]/[数据库名]?useUnicode=true&characterEncoding=utf8
user = [登录用户名]
password = [登录密码]

注意,数据库的编码设置应为 utf8mb4。(mb4 支持 emoji 字符)

系统启动-Quick Start

完成数据库配置后,依次四个步骤:

1.在 com.qianxinyao.TomNewsRecommender 包下,找到类 Main;

2.选择推荐算法。设置 boolean 类型的 enableCB,enableCF,enableHR 变量,分别代表推荐过程中是否启用协同过滤推荐算法、基于内容的推荐算法、基于热点新闻的推荐算法。若均设为 true,表示三种算法均工作,一起为用户生成推荐结果;

3.选择推荐对象。推荐对象分为三种:全体用户,活跃用户(最近一段时间有登录行为)与自定义用户(自己指定的用户),若选择自定义用户,需要构建包含目标用户 id(long)的 List

4.选择系统运行方式。运行方式分为两种:一次运行和定时运行。一次运行即只为用户进行一次推荐生成,生成结束后则系统停止,若要再生成推荐,需要重新启动系统。而定时运行则可以定时为用户生成推荐结果,若不强制停止系统,则系统会一直运行下去。(定时运行时间在 paraConfig.properties 文件中设定)

以下是示例代码:

package com.qianxinyao.TomNewsRecommender;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

/**
 * @author bluemapleman
 * @email tomqianmaple@gmail.com
 * @github https://github.com/bluemapleman
 * @date 2016 年 10 月 20 日
 * 推荐系统入口类,在此启动推荐系统。
 */
public class Main
{

    public static final Logger logger = Logger.getLogger(Main.class);

    /**
     * 推荐系统运行入口
     * @param args
     */
    public static void main(String[] args)
    {
        //选择要在推荐系统中运行的推荐算法
        boolean enableCF=true,enableCB=false,enableHR=false;

        List<Long> userList=new ArrayList<Long>();
        userList.add(1L);
        userList.add(2L);
        userList.add(3L);

        //为指定用户执行一次推荐
        new JobSetter(enableCF,enableCB,enableHR).executeInstantJobForCertainUsers(userList);
        //定时执行推荐
//      new JobSetter(enableCF,enableCB,enableHR).executeQuartzJob(forActiveUsers);
    }
}

日常使用

系统运行的各类参数都可以在根目录下 src/main/res 目录下的 paraConfig.properties 文件中进行配置。默认配置是推荐配置。

若需要推荐系统能在每次生成有效的推荐,只要【新闻模块】保持以一定频率抓取一定量的新闻并入库 news 表。(最好与推荐系统定时推荐的频率相同,并在推荐系统运行之前完成一次抓取,推荐每天抓取一次新闻,并进行一次推荐生成。)

注意:入库的新闻要标注 module_id,详情可参见数据库表与 com.qianxinyao.TomNewsRecommender 包下的 NewsScraper 类中的代码。

测试数据

在 Mysql 数据库中运行 data.sql 中的 sql 语句,可生成数据库结构与测试数据。

测试数据中包含以下几个部分:

  • users 表:7 个测试用户
  • news 表:306 个 2017-12-12 日从网易首页抓取的测试新闻
  • newsmodules 表:17 个测试模块
  • newslogs:测试推荐算法效果用的 9 条浏览记录

要查看推荐系统在测试数据上运行的效果,只需在 Main 类下执行:

//在测试数据上运行
new TestDataRunner().runTestData();

预期的推荐生成结果如下:

  • 若对测试数据进行一次协同过滤,将生成 0 条推荐。
  • 若对测试数据进行一次基于内容的推荐,将为用户 1(id=1)推荐 85,87,89,104 这四条新闻(有重复标题的新闻,新闻标题中的“合同”关键词匹配上了用户的喜好关键词),为用户 2 推荐 89 新闻(重复标题的新闻),推荐用户 3 推荐 87,85,100 这三条新闻(新闻标题中的“合同”关键词匹配上了用户的喜好关键词)。
  • 若对测试数据进行一次基于热点新闻的推荐,将分别为用户 1 推荐 103,104,为用户 2 推荐 100,104,为用户 3 推荐 100,101,因为最近被浏览得最多的新闻就是这三个拥有浏览记录的用户看过的那些新闻(100,101,102,103,104)。

额外说明

1.com.qianxinyao.TomNewsRecommender 下的 NewsScraper 类是抓取网易的测试新闻时用的类,大家也可以用这个类继续采集新闻。该类默认对网易新闻首页的所有新闻进行一次抓取入库。

2.协同过滤的效果目前不太稳定/可控,因为采用的是 Mahout 内置的协同过滤工具。一般来说,新闻模块的活跃用户越多,则协同过滤效果越好,也越明显。若有需求,我会在后期自己实现能稳定生成指定数量的推荐结果的协同过滤算法。

3.一般当协同过滤与基于内容的推荐算法生成的推荐数目不足时,可以用基于热点新闻的推荐进行数量补充。

更新日志

欢迎大家踊跃提出自己对该推荐系统的任何想法和建议!

版本 日期 特性
V1.0.0 2018/10/04 规整 README.md 说明文档,使之更加易懂理解。
V1.0.1 开发中 1. 自主实现 Java 版本的 TFIDF 算法,或者整理一份训练语料库,以达到更好的关键词提取效果;
2.改善基于内容的推荐算法的匹配程度计算方法,使之更加高效。(若用户的偏好列表中有数千喜好关键词,若将一千条最近的新闻与该用户做匹配,后台计算出推荐结果列表只需要100ms,不考虑网络传输等其他时间因素)
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools