UserCF 还是 ItemCF?
因为小说总量有限,相对于构建一个用户-用户的矩阵,小说-小说的矩阵要小的多的多,所以对于小说网站来说,推荐系统算法的选择应该是 ItemCF
如何来做?
传统推荐系统的做法是打分制,也就是用户对小说进行1-5的评分,但其实对于小说网站来说,我们不需要如此考虑,将问题简化,只关心用户喜欢还是不喜欢这不书即可,也就是将问题简化为二分分类问题。
在这里,我选取了用户付费阅读过的书和加入收藏的书,用来标定用户喜欢这本书,同时对操作时间戳也进行了记录,因为我们应该加上时间维度的衰减才能得到流行度数据,而不是给用户推荐一本很多年前的书。
数据库设计
表1
user_id book_id timestamp
表2
book_i book_j value
我这里用的是 go,而不是 python,为了直观和节约内存,我用了 mysql 来记录数据,而不是直接写入文本并加载到内存,在速度上会有一点损失,不过影响不大
关键代码
以下是计算相似度的关键代码,其他代码请自行 google
for _, items := range userItemMatrix { itemsLen := float64(len(items)) for itemI, timeI := range items { N[itemI]++ for itemJ, timeJ := range items { if itemI == itemJ { continue } var sim float64 diff := math.Abs(float64(timeI)-float64(timeJ)) / 2592000 // dev := (1.0 + math.Pow(diff, 2)) * math.Log(1+float64(len(items))) // sim = 1.0 / dev sim = math.Log(1.0+math.Pow(itemsLen, 2)) / (1.0 + math.Pow(diff, 2)) if _, exist := C[itemI]; exist { C[itemI][itemJ] += sim } else { r := make(map[ITEMID]float64) r[itemJ] = sim C[itemI] = r } } } } simMatrix := make(map[ITEMID]map[ITEMID]float64) var ( minValue float64 = 1.0 maxValue float64 = 1.0 ) for itemI, relatedItems := range C { for itemJ, Cij := range relatedItems { value := Cij / math.Sqrt(N[itemI]*N[itemJ]) if value < minValue { minValue = value } if value > maxValue { maxValue = value } if _, exist := simMatrix[itemI]; exist { simMatrix[itemI][itemJ] = value } else { r := make(map[ITEMID]float64) r[itemJ] = value simMatrix[itemI] = r } } }