前言
在完成基于流行度和基于新鲜度的推荐与基于文章相似度的推荐之后,准备使用协同过滤算法进行推荐。不管是基于物品的协同过滤还是基于用户的协同过滤,都需要使用用户对博客文章的浏览记录来进行。而问题在于,我们如果将具体的文章作为物品,过分析这个用户读过的博客文章(训练集),去给这个用户推荐他可能感兴趣的文章,但是我们的浏览记录很少的话,根据读取的文章寻找用户的相似度,用户之间的相似度会很低,不好进行推荐。所以我通过博客文章所属的分类和标签,对文章进行推荐。
但是,在推荐的时候需要遍历所有的用户浏览数据,找到用户对每一个标签或者分类的喜爱程度(这里用看每一个分类下的文章,或属于每个标签的文章的次数来衡量),这是需要对全库进行扫描,找到所有人对应的对每个标签或分类的点击次数。这样在运行过程中动态的进行全库扫描是不现实的。所以我考虑建立定时任务,每过一段时间,对用户的浏览记录进行分析,得出用户对于每个标签或者分类的评分,存到本地的csv文件中,作为训练集,在推荐时,参考这些数据进行推荐即可。
提前物化用户-标签的喜好程度
- 先从用户浏览记录表中,查询某个用户对某个标签的浏览量
/**
* 查询某个用户对应标签下的浏览量
* 结果为userId tagId 以及对应的浏览量
* @param userId
* @return
*/
@Select("SELECT user_id,tag_id,count(tag_id) FROM " +
"article_tag as A , user_browsing_history as B " +
"where user_id = #{userId} and A.article_id = B.article_id " +
"group by user_id,tag_id ")
@Results(id = "UserTag", value = {
@Result(property = "userId", column = "user_id"),
@Result(property = "tagId", column = "tag_id"),
@Result(property = "count", column = "count(tag_id)"),})
List<UserTag> findUserTag(@Param("userId") int userId);
这个sql语句的执行结果为
- 然后遍历所有的用户,将数据抽取到本地即可
/**
* 将用户对每个标签的浏览数据提前物化到数据库,以便在推荐中使用
*/
public void extractUserTagDate() {
File file = new File("src/main/resources/UserBrowsingData/user-tag");
deleteFile(file);
//获取用户总数
int userCount = userDao.getUserCount();
int size = userCount / 100;//每次读取1000个用户
//读取整数用户
for (int i = 0; i <= size; i++) {
List<Integer> userIdList = userDao.findAllUserId(1000, i * 1000);
addUserTagData(userIdList);
}
}
/**
* 将userTag数据写入csv
*
* @param userIdList
*/
private void addUserTagData(List<Integer> userIdList) {
for (Integer userId : userIdList) {
List<UserTag> userTagList = userBrowsingDao.findUserTag(userId);
List<String> writeData = new ArrayList<>();
for (UserTag userTag : userTagList) {
String data = String.valueOf(userTag.getUserId()) + "," + String.valueOf(userTag.getTagId()) + "," + String.valueOf(userTag.getCount());
System.out.println(data);
writeData.add(data);
}
csv.writeCsvFile(USER_TAG_MODEL_FILE_NAME, writeData);
}
}
提前物化用户-分类的喜好程度
上一个类似
/**
* 查询某个用户对应分类下的浏览量
* 结果为userId categoryId 以及对应的浏览量
* @param userId
* @return
*/
@Select("SELECT user_id,category_id,count(category_id) FROM " +
"article_category as A , user_browsing_history as B " +
"where user_id = #{userId} and A.article_id = B.article_id " +
"group by user_id,category_id ")
@Results(id = "UserCategory", value = {
@Result(property = "userId", column = "user_id"),
@Result(property = "categoryId", column = "category_id"),
@Result(property = "count", column = "count(category_id)"),})
List<UserCategory> findUserCategory(@Param("userId") int userId);
/**
* 将用户对每个分类的浏览数据提前物化到数据库,以便在推荐中使用
*/
public void extractUserCategoryDate() {
File file = new File("src/main/resources/UserBrowsingData/user-category");
deleteFile(file);
//获取用户总数
int userCount = userDao.getUserCount();
int size = userCount / 100;//每次读取1000个用户
//读取整数用户
for (int i = 0; i <= size; i++) {
List<Integer> userIdList = userDao.findAllUserId(1000, i * 1000);
addUserCategoryData(userIdList);
}
}
/**
* 将userCategory数据写入csv
*
* @param userIdList
*/
private void addUserCategoryData(List<Integer> userIdList) {
for (Integer userId : userIdList) {
List<UserCategory> userTagList = userBrowsingDao.findUserCategory(userId);
List<String> writeData = new ArrayList<>();
for (UserCategory userCategory : userTagList) {
String data = String.valueOf(userCategory.getUserId()) + "," + String.valueOf(userCategory.getCategoryId()) + "," + String.valueOf(userCategory.getCount());
System.out.println(data);
writeData.add(data);
}
csv.writeCsvFile(USER_CATEGORY_MODEL_FILE_NAME, writeData);
}
}
问题
(1)每一次抽取完数据之后,到下一次抽取数据之前,对用户的推荐结果都是相同的
(2)冷启动问题,当一个新用户进入系统后,由于没有这个用户的浏览记录,所以协同过滤算法也面临着失效