[Rails]壁纸推荐网站


简单来说就是一次ITEM CF的尝试。不过壁纸本身的管理也花了不少时间,ITEM CF是最后才做的……

源代码在这里 https://github.com/xnnyygn/get-wallpaper

screenshot-index-2014-12-03

上面第一行是推荐,第二行是最新的,第三行是热度最高的。
所谓ITEM CF推荐就是根据人得到相似的物品,然后推荐给你。
热度最高是按照下载次数来的。
这些都是在网站日常操作中得到数据,然后通过推荐计算得到的。

普通操作有,上传,下载。

screenshot-upload-image

screenshot-download-image

上传时有图片大小限制(1M),分辨率限制(>=800×600)等。
下载时实际是一次裁剪+缩放的过程。

sr_tr

按照原图分辨率和下载分辨率计算分类,然后得到最大区间,缩放。图片操作使用的是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

模型关系图

models

计算分数的代码

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的训练数据不够,不过夜算是一个可以用的推荐数据实验。