Golang · 2018年8月20日 0

小说智能推荐系统实战

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
			}

		}
	}