数据挖掘竞赛 - 猜你喜欢

datacastle上的一道推荐算法竞赛(这里是地址和数据),由于最近想整理和汇总最常用的推荐算法。因此干脆就把这个竞赛拿出来实战一下。


1. 赛题 & 数据解析

本次比赛是一个名副其实的推荐算法大赛,在本次比赛中,我们提供了一个商品网站中大约16万名用户在四年内对商品的评分数据,每条评分记录都有时间戳(隐匿了具体时间,只保证顺序不变)。评分分为5级,1分最低,5分最高。

这是训练集,其中四列分别代表用户编号,物品编号,评分,时间。

数据挖掘竞赛 - 猜你喜欢_第1张图片


这是测试集,只有两列用户编号,物品编号,需要预测分数:

数据挖掘竞赛 - 猜你喜欢_第2张图片


2. 基于人工规则模型

基于人工规则的模型可以设计出很多,其特点是简单,鲁棒:

# -*- coding: utf-8 -*-
""" 基于各种指标对用户, 物品分类, 然后取均值进行预测

1. 基于用户对物品评分的均值来分类, 然后预测;
    1.1 根据用户对train物品的评分来分类train, test中的用户;
    1.2 由于对用户进行了分类, 那么对于同一种物品, 在不同的簇下得分就不同;
    1.3 从而就可以确定[物品, 簇]的得分;

"""
import numpy as np
import pandas as pd
import data

train = data.load_data("train")
test = data.load_data("test")

# 求平均分
score_mean = train.groupby(["uid"], as_index=False)["score"].agg({"score_mean": "mean"})

score_mean["cluster"] = np.array(score_mean["score_mean"] * 2, np.int)
score_mean = score_mean[["uid", "cluster"]]

# 基于用户, 对train, test添加cluster列
train = pd.merge(train, score_mean, on="uid", how="left")
test = pd.merge(test, score_mean, on="uid", how="left")

# 基于cluster对物品打分
item_mean_score = train.groupby(["iid", "cluster"], as_index=False).mean()
result = pd.merge(test, item_mean_score[["iid", "cluster", "score"]], on=["iid", "cluster"], how="left")
result = result.fillna(3)

# 保存结果
data.save_data(result, "result_rule.csv")

3. 基于协同过滤模型

主要是基于物品的协同过滤算法,这个算法复杂度比较高,需要计算的东西很多。因此一般实际应用都是离线计算。主要注意两点:

1. 多使用字典,利于后期查询;

2. 对于两件不同的物品,可能对某个人而言,只评价过一种物品,另一种物品为空。此时计算距离,认为他们之间的距离为0,而不是两者之差。

这里给出一个之前实现的协同过滤推荐算法:http://blog.csdn.net/zk_j1994/article/details/77062091

# -*- coding: utf-8 -*-
""" 基于物品的协同过滤算法, 使用SVD分解对人进行降维
1. 有一个细节, 计算相似度时, 一个位置为空, 另一个位置有评价分数, 此时相似度视为0;

2. 算法复杂度很高, 需要离线计算;

3. 这里使用一个小数据集来进行示例;

4. 之所以使用SVD来对样本降维, 是由于推荐系统矩阵的稀疏性导致的, 事实上使用SVD不但可以减少
    计算量, 还能提高推荐性能;

"""
import numpy as np

def load_data():
    data = [[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5],
            [0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3],
            [0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0],
            [3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0],
            [5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0],
            [0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0],
            [4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1],
            [0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4],
            [0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2],
            [0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0],
            [1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]]
    return np.array(data)

if __name__ == "__main__":
    data = load_data()
    
    u, sigma, v = np.linalg.svd(data)
    
    sigma_4 = np.mat(np.eye(6) * sigma[0:6])
    
    # 约简后的矩阵
    simpler_data = np.dot(u[0:6, :], data)
                    
    """
    由于行被压缩, 因此计算物品相似度的时候, 计算复杂度大大降低了;
    
    同时, 矩阵越稀疏, 这种先SVD降维, 再计算, 效果就越好
    
    """

4. 基于矩阵分解的模型

使用矩阵分解来对原矩阵进行恢复,但是这个数据量稍微大了点(用户 - 物品矩阵就有9G),我的本本跑不动。这里是一个例子:

# -*- coding: utf-8 -*-  
""" 
基于矩阵分解的推荐算法 
 
1. 使用梯度下降进行迭代更新; 
 
"""  
import numpy as np  
import matplotlib.pyplot as plt  
  
np.random.seed(1)  
  
def load_data():  
    data = [[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5],  
            [0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3],  
            [0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0],  
            [3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0],  
            [5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0],  
            [0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0],  
            [4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1],  
            [0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4],  
            [0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2],  
            [0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0],  
            [1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]]  
    return np.array(data)  
  
def gradAscent(data, K, max_iter, alpha, beta):  
    """ 梯度下降更新P, Q矩阵的元素, 使均方误差最小 """  
    if not isinstance(data, np.matrix):  
        data = np.mat(data)  
          
    # 初始化P, Q矩阵  
    n, m = data.shape  
    P = np.mat(np.random.random((n, K)))  
    Q = np.mat(np.random.random((K, m)))  
    print("\nP = \n{0}".format(P))  
    print("\nQ = \n{0}".format(Q))  
      
    loss_list = []  
    _iter = 0  
    while _iter < max_iter:  
        # 更新P, Q中的每一个元素  
        for i in range(n):  
            for j in range(m):  
                if data[i, j] > 0:  
                    error = (data[i, j] - P[i, :] * Q[:, j])[0, 0]     # (i, j)处的误差  
                    for k in range(K):  
                        P[i, k] = P[i, k] + alpha * (2 * error * Q[k, j] - beta * P[i, k])  
                        Q[k, j] = Q[k, j] + alpha * (2 * error * P[i, k] - beta * Q[k, j])  
          
        # 计算原矩阵和恢复矩阵之间的误差  
        loss = 0  
        for i in range(n):  
            for j in range(m):  
                if data[i, j]> 0:  
                    for k in range(K):  
                        loss += P[i, k] * Q[k, j]  
                    loss = np.sum(abs(data[i, j] - loss))  
          
        loss_list.append(loss)  
        if loss <= 1e-3:  
            break  
        _iter += 1  
    return P, Q, loss_list  
  
def draw_loss(loss):  
    plt.plot(range(len(loss)), loss)  
    plt.show()  
          
if __name__ == "__main__":  
    data = load_data()  
      
    P, Q, loss = gradAscent(data, 5, 20000, 0.0002, 0.02)  
      
    print("\n恢复矩阵 = \n{0}".format(P * Q))  
    draw_loss(loss)  
数据挖掘竞赛 - 猜你喜欢_第3张图片


参考文献

http://blog.csdn.net/nihaoxiaocui/article/details/51974194

http://blog.csdn.net/datacastle/article/details/52190423


你可能感兴趣的:(数据挖掘竞赛,推荐系统)