简单来说就是一次ITEM CF的尝试。不过壁纸本身的管理也花了不少时间,ITEM CF是最后才做的……
源代码在这里 https://github.com/xnnyygn/get-wallpaper
上面第一行是推荐,第二行是最新的,第三行是热度最高的。
所谓ITEM CF推荐就是根据人得到相似的物品,然后推荐给你。
热度最高是按照下载次数来的。
这些都是在网站日常操作中得到数据,然后通过推荐计算得到的。
普通操作有,上传,下载。
上传时有图片大小限制(1M),分辨率限制(>=800×600)等。
下载时实际是一次裁剪+缩放的过程。
按照原图分辨率和下载分辨率计算分类,然后得到最大区间,缩放。图片操作使用的是ImageMagick。命令是
convert #{original_file_path} -crop #{crop_bound[0]}x#{crop_bound[1]}+#{crop_bound[2]} +#{crop_bound[3]} -resize #{target_width}x#{target_height} #{target_file_path}
图片上传时其实有一次获取分辨率的过程,同样使用ImageMagick,命令是
identify -format '%w %h' #{wallpaper_tmpfile.path}
首页左上角其实有一个随机图片。里面用的不是ORDER BY RAND()之类的,因为考虑到数据库的语法不同,所以用了一个变通的方法,最近100张中随机20张。其中随机过程是由ruby完成的。
@wallpapers = Wallpaper.find( Wallpaper.order(created_at: :desc).limit(100).pluck(:id).sample(20) )
图片的存储采用“有用时再生成”的方式,一开始只存储原图,显示缩略图时生成一次,中等缩略图又有一次,下载时再生成。如果有重复使用,那么就只需要一次。图片定期删除,这样可以保证存储不会爆满。如果能根据访问预测图片也可以,不过那个不是本次项目的初衷啦。
推荐是本项目的核心。本次使用mahout作为推荐核心。mahout是Java库,所以使用Java Web作为推荐中心的框架。和Rails的网站使用HTTP通信。
具体来说Rails使用faraday访问推荐中心。推荐中心定时访问Rails网站刷新推荐数据。推荐数据一般1分钟刷新一次。因为是异步刷新,所以问题不大。
mahout本身的功能,本项目用的是第一个。
- User and Item based recommenders
- Matrix factorization based recommenders
- K-Means, Fuzzy K-Means clustering
- Latent Dirichlet Allocation
- Singular Value Decomposition
- Logistic regression classifier
- (Complementary) Naive Bayes classifier
- Random forest classifier
- High performance java collections
- A vibrant community
推荐的核心代码
DataModel dataModel = new FileDataModel(new File(filePath)); ItemSimilarity similarity = new PearsonCorrelationSimilarity(dataModel); Recommender recommender = new GenericItemBasedRecommender(dataModel, similarity); recommender.recommend(user_id = 123, max = 10);
推荐数据,列分别为用户ID,物品ID,分数,时间戳
2,65,5,1417522483 2,23,3,1417442145 2,25,3,1417440697 1,23,3,1417514439 1,65,3,1417514442 3,23,5,1417519760 3,27,3,1417519018 3,61,3,1417519021 3,62,2,1417519028 3,65,5,1417521646 3,36,5,1417521640 3,49,3,1417521614 3,50,3,1417521617 1,36,3,1417521664 1,25,5,1417521667
模型关系图
计算分数的代码
def preferences data = '' WallpaperStat.all.each do |ws| score = 0 score += 2 if ws.download_count > 0 score += 3 if ws.favorite if score == 0 next end data << ws.user_id.to_s << ',' data << ws.wallpaper_id.to_s << ',' data << score.to_s << ',' data << ws.updated_at.to_time.to_i.to_s << "\n" end render text: data end
计算过程稍微简化了,实际可能还要考虑别的因素。
实际ITEM CF需要更多的数据才能显示出其特别的地方,所以本项目可能对ITEM CF的训练数据不够,不过夜算是一个可以用的推荐数据实验。