18、推荐系统:从基础到实践

推荐系统:从基础到实践

1. 产品评分预测

在预测产品评分时,我们可以使用用户 - 用户协同过滤的方法。以用户 X、Y 和 Z 为例,假设我们要预测用户 X 对 Disposos’ Diapers 的评分,而用户 Y 和 Z 已经对该产品进行了评分。
首先,我们有如下的基础评分表:
| 顾客 | Snarky’s 薯片 | SoSo 润肤乳 | Duffly 啤酒 | BetterTap 水 | XXLargeLivin’ 足球衫 |
| ---- | ---- | ---- | ---- | ---- | ---- |
| X | 4 | 3 | | | |
| Y | 3.5 | 2.5 | | | |
| Z | 4 | 3.5 | | | |

接着,对评分进行中心化处理:
| 顾客 | Snarky’s 薯片 | SoSo 润肤乳 | Duffly 啤酒 | BetterTap 水 | XXLargeLivin’ 足球衫 |
| ---- | ---- | ---- | ---- | ---- | ---- |
| X | 0.33 | -0.66 | | | |
| Y | 0 | -1 | | | |
| Z | -0.125 | -0.625 | | | |

然后,计算用户 X 与用户 Y、Z 的中心化余弦相似度:

import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

user_x = [0, 0.33, 0, -0.66, 0, 0.33, 0] 
user_y = [0, 0, 0, -1, 0, 0.5, 0.5] 
similarity_xy = cosine_similarity(np.array(user_x).reshape(1, -1), np.array(user_y).reshape(1, -1))

user_z = [0, -0.125, 0, -0.625, 0, 0.375, 0.375] 
similarity_xz = cosine_similarity(np.array(user_x).reshape(1, -1), np.array(user_z).reshape(1, -1))

得到用户 X 与用户 Y 的相似度为 0.42447212,与用户 Z 的相似度为 0.46571861。
最后,根据相似度对用户 Y 和 Z 的评分进行加权平均,计算用户 X 对 Disposos’ Diapers 的预测评分:

rating_prediction = (0.42447212 * 4 + 0.46571861 * 4.5) / (0.42447212 + 0.46571861)
print(rating_prediction)  # 输出 4.26

由此可知,用户 X 对 Disposos’ Diapers 的预期评分为 4.26。

2. 物品 - 物品协同过滤

除了用户 - 用户协同过滤,还有物品 - 物品协同过滤方法,该方法在实践中通常优于用户 - 用户过滤。以用户对歌曲的评分为例,有如下的效用矩阵:
| 实体 | U1 | U2 | U3 | U4 | U5 |
| ---- | ---- | ---- | ---- | ---- | ---- |
| S1 | 2 | 4 | 5 | | |
| S2 | 3 | 3 | | | |
| S3 | 1 | 5 | 4 | | |
| S4 | 4 | 4 | 4 | | |
| S5 | 3 | 5 | | | |

假设我们想知道用户 3 对歌曲 S5 的评分,我们将寻找与 S5 相似的歌曲。
首先,对每首歌曲的评分进行中心化处理,并计算与目标行 S5 的余弦相似度:
| 实体 | U1 | U2 | U3 | U4 | U5 | 中心化余弦相似度 |
| ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| S1 | -1.66 | 0.33 | 1.33 | | | 0.98 |
| S2 | 0 | 0 | 0 | | | 0 |
| S3 | -2.33 | 1.66 | 0.66 | | | 0.72 |
| S4 | 0 | 0 | 0 | | | 0 |
| S5 | -1 |? | 1 | | | 1 |

选择 k = 2 作为最近邻的数量,歌曲 S1 和 S3 与 S5 最相似,用户 3 对 S1 和 S3 的评分分别为 4 和 5。
计算用户 3 对歌曲 S5 的预测评分:

rating_s5 = (0.98 * 4 + 0.72 * 5) / (0.98 + 0.72)
print(rating_s5)  # 输出 4.42

根据物品 - 物品协同过滤,用户 3 对歌曲 S5 的预测评分为 4.42。

3. 用户 - 用户与物品 - 物品协同过滤对比

用户 - 用户协同过滤存在一定的局限性。例如,你和朋友可能有一些共同的兴趣爱好,但也有各自独特的兴趣领域。在用户 - 用户推荐中,即使你们在很多方面相似,但由于某些独特兴趣的存在,你可能会收到一些不符合你兴趣的推荐。而物品 - 物品过滤则可以避免这种情况,为你提供更符合你兴趣的推荐。

4. 基于内容的过滤

基于内容的过滤以 Pandora 音乐为例。创始人 Tim Westergren 发现很多优秀的音乐作品没有得到足够的关注,他认为这是因为音乐没有被推送给足够多的合适人群。于是,他开始思考将每首音乐分解成不同的特征,构建音乐基因组。
他们雇佣了一批音乐家,对超过一百万首音乐的近 400 个不同音乐特征进行了细致编码,每个特征在 0 到 5 分的范围内进行评分。每首三到四分钟的歌曲分类大约需要半小时。这些特征包括主唱声音的沙哑程度、每分钟的节拍数等。
经过近一年的时间,他们的第一个原型完成,完全在 Excel 中使用 VBA 宏构建,返回一个推荐大约需要四分钟。最终,这个系统取得了成功,现在的 Pandora 音乐已经拥有数百万的日活跃用户。
在基于内容的过滤中,歌曲不再被视为一个不可分割的整体,而是成为可以使用余弦相似度进行比较的特征向量。同时,听众的口味也可以表示为向量,以便与歌曲进行比较。

5. 混合系统

在大规模生产环境中,通常会使用混合系统,结合协同过滤和基于内容的过滤。下面是两种过滤方法的优缺点对比:
| 过滤方法 | 优点 | 缺点 |
| ---- | ---- | ---- |
| 协同过滤 | 无需手动创建特征 | 没有大量的物品和用户时效果不佳;物品数量远超过可购买数量时会出现稀疏性问题 |
| 基于内容的过滤 | 不需要大量用户 | 定义合适的特征具有挑战性;缺乏意外发现的惊喜 |

基于内容的过滤在用户基数较小时是更好的选择,随着用户数量的增长,添加协同过滤可以为推荐增加更多的意外惊喜。

6. 构建推荐引擎

我们将使用 GitHub API 创建一个基于协同过滤的推荐引擎,具体步骤如下:
1. 导入所需的库:

import pandas as pd 
import numpy as np 
import requests 
import json 
  1. 在 GitHub 上创建账户并为一些仓库加星标,无需注册开发者计划,从个人资料中获取授权令牌。
  2. 生成 API 使用的令牌:访问 https://github.com/settings/tokens,点击“Generate new token”按钮,选择权限(如 public_repo),复制生成的令牌。
myun = "YOUR_GITHUB_HANDLE" 
mypw = "YOUR_PERSONAL_TOKEN" 
  1. 创建函数获取自己加星标的仓库:
my_starred_repos = [] 
def get_starred_by_me(): 
    resp_list = [] 
    last_resp = '' 
    first_url_to_get = 'https://api.github.com/user/starred' 
    first_url_resp = requests.get(first_url_to_get, auth=(myun, mypw)) 
    last_resp = first_url_resp 
    resp_list.append(json.loads(first_url_resp.text)) 

    while last_resp.links.get('next'): 
        next_url_to_get = last_resp.links['next']['url'] 
        next_url_resp = requests.get(next_url_to_get, auth=(myun, mypw)) 
        last_resp = next_url_resp 
        resp_list.append(json.loads(next_url_resp.text)) 

    for i in resp_list: 
        for j in i: 
            msr = j['html_url'] 
            my_starred_repos.append(msr) 

get_starred_by_me()
  1. 解析出加星标仓库的用户名称:
my_starred_users = [] 
for ln in my_starred_repos: 
    right_split = ln.split('.com/')[1] 
    starred_usr = right_split.split('/')[0] 
    my_starred_users.append(starred_usr) 
  1. 创建函数获取这些用户加星标的仓库:
starred_repos = {k: [] for k in set(my_starred_users)} 
def get_starred_by_user(user_name): 
    starred_resp_list = [] 
    last_resp = '' 
    first_url_to_get = 'https://api.github.com/users/' + user_name + '/starred' 
    first_url_resp = requests.get(first_url_to_get, auth=(myun, mypw)) 
    last_resp = first_url_resp 
    starred_resp_list.append(json.loads(first_url_resp.text)) 

    while last_resp.links.get('next'): 
        next_url_to_get = last_resp.links['next']['url'] 
        next_url_resp = requests.get(next_url_to_get, auth=(myun, mypw)) 
        last_resp = next_url_resp 
        starred_resp_list.append(json.loads(next_url_resp.text)) 

    for i in starred_resp_list: 
        for j in i: 
            sr = j['html_url'] 
            starred_repos.get(user_name).append(sr) 

for usr in list(set(my_starred_users)): 
    try: 
        get_starred_by_user(usr) 
    except: 
        print('failed for user', usr) 
  1. 构建特征集:
repo_vocab = [item for sl in list(starred_repos.values()) for item in sl] 
repo_set = list(set(repo_vocab)) 
  1. 为每个用户创建二进制向量:
all_usr_vector = [] 
for k, v in starred_repos.items(): 
    usr_vector = [] 
    for url in repo_set: 
        if url in v: 
            usr_vector.extend([1]) 
        else: 
            usr_vector.extend([0]) 
    all_usr_vector.append(usr_vector) 

df = pd.DataFrame(all_usr_vector, columns=repo_set, index=starred_repos.keys()) 
  1. 添加自己的行到数据框:
my_repo_comp = [] 
for i in df.columns: 
    if i in my_starred_repos: 
        my_repo_comp.append(1) 
    else: 
        my_repo_comp.append(0) 

mrc = pd.Series(my_repo_comp).to_frame('acombs').T 
mrc.columns = df.columns 
fdf = pd.concat([df, mrc]) 
  1. 计算相似度:
from scipy.stats import pearsonr 
sim_score = {} 
for i in range(len(fdf)): 
    ss = pearsonr(fdf.iloc[-1, :], fdf.iloc[i, :]) 
    sim_score.update({i: ss[0]}) 

sf = pd.Series(sim_score).to_frame('similarity') 
  1. 排序并找到最相似的用户:
sf_sorted = sf.sort_values('similarity', ascending=False) 

通过以上步骤,我们可以找到与自己最相似的用户,并根据他们加星标的仓库生成推荐。

下面是构建推荐引擎的流程图:

graph TD;
    A[导入库] --> B[获取授权令牌];
    B --> C[获取自己加星标的仓库];
    C --> D[解析用户名称];
    D --> E[获取用户加星标的仓库];
    E --> F[构建特征集];
    F --> G[创建二进制向量];
    G --> H[添加自己的行到数据框];
    H --> I[计算相似度];
    I --> J[排序找到最相似的用户];

综上所述,我们介绍了产品评分预测的方法,包括用户 - 用户协同过滤和物品 - 物品协同过滤,以及基于内容的过滤和混合系统。最后,详细阐述了如何使用 GitHub API 构建一个基于协同过滤的推荐引擎。这些方法和技术可以帮助我们更好地理解和实现推荐系统。

推荐系统:从基础到实践

7. 推荐生成与分析

在找到最相似的用户后,我们可以利用他们加星标的仓库来生成推荐。我们选取三个最相似的用户(排除自己),分别是用户 6、用户 42 和用户 116。
首先,创建一个包含自己和这三个最相似用户加星标仓库的 DataFrame:

all_recs = fdf.iloc[[6, 42, 116, fdf.index.get_loc(myun)], :]

然后,查看是否有我们都加星标的仓库:

common_starred = all_recs[(all_recs == 1).all(axis = 1)]
print(common_starred)

结果显示,大家似乎都喜欢 scikit - learn 和机器学习相关的仓库。接着,我们找出自己未加星标但这三个用户都加星标的仓库:

str_recs_tmp = all_recs[all_recs[myun] == 0].copy()
str_recs = str_recs_tmp.iloc[:, :-1].copy()
commonly_starred_not_mine = str_recs[(str_recs == 1).all(axis = 1)]
print(commonly_starred_not_mine)

发现没有特别明显被遗漏的仓库。再看看至少有两个用户加星标的仓库,通过对行求和来筛选:

summed_recs = str_recs.sum(axis = 1).to_frame('total').sort_values(by = 'total', ascending = False)
print(summed_recs)

这里出现了很多优秀的机器学习和人工智能仓库,例如 fuzzywuzzy,这是一个很实用但自己之前未加星标的仓库。

8. 推荐系统的优化方向

为了进一步优化推荐结果,我们可以采取以下两种方法:
- 基于星标数量排序 :可以通过再次调用 GitHub API,利用提供星标数量信息的端点,根据仓库获得的总星标数对推荐进行排序。
- 添加内容过滤层 :这就是前面提到的混合系统中的内容过滤部分。我们需要从自己的仓库中创建一组特征,以反映自己感兴趣的类型。一种方法是对加星标仓库的名称和描述进行分词,生成一组单词特征。例如,会包含 Python、Machine Learning、Data Science 等单词。
以下是一个简单的示例,展示如何创建特征集:

import nltk
from nltk.tokenize import word_tokenize
nltk.download('punkt')

feature_set = []
for repo in my_starred_repos:
    # 假设可以通过 API 获取仓库描述
    description = get_repo_description(repo) 
    tokens = word_tokenize(repo + ' ' + description)
    feature_set.extend(tokens)
feature_set = list(set(feature_set))

在 DataFrame 中,列将是单词特征(n - 元组),行将是通过协同过滤步骤生成的仓库。我们可以再次使用自己的仓库进行比较,运行相似度计算过程。

9. 总结与展望

推荐系统在当今的信息时代中扮演着至关重要的角色,它能够帮助用户从海量的信息中快速找到自己感兴趣的内容。本文介绍了多种推荐系统的方法,包括用户 - 用户协同过滤、物品 - 物品协同过滤、基于内容的过滤以及混合系统,并详细阐述了如何使用 GitHub API 构建一个基于协同过滤的推荐引擎。

不同的推荐方法各有优缺点,用户 - 用户协同过滤简单直接,但在数据稀疏时效果不佳;物品 - 物品协同过滤在实践中表现更好,能减少不相关推荐;基于内容的过滤可以深入挖掘用户的兴趣特征,但特征定义具有挑战性。混合系统结合了协同过滤和基于内容的过滤,能够取长补短,提供更优质的推荐。

在实际应用中,我们可以根据具体的场景和数据特点选择合适的推荐方法,并通过不断优化和改进,提高推荐系统的性能和效果。例如,在构建推荐引擎时,我们可以进一步优化特征提取方法、调整相似度计算方式,或者引入更多的数据来源和特征信息。同时,我们也可以考虑用户的实时反馈,动态调整推荐策略,以提供更加个性化和精准的推荐服务。

以下是推荐系统优化步骤的流程图:

graph TD;
    A[生成初始推荐] --> B[基于星标数量排序];
    A --> C[添加内容过滤层];
    B --> D[优化推荐结果];
    C --> D;
    D --> E[考虑用户反馈];
    E --> F[动态调整推荐策略];

总之,推荐系统的发展前景广阔,随着技术的不断进步和数据的不断丰富,我们有理由相信,未来的推荐系统将能够为用户提供更加智能、高效、个性化的推荐服务。

优化方法 操作步骤
基于星标数量排序 1. 调用 GitHub API 中提供星标数量信息的端点;2. 根据星标数量对推荐仓库进行排序
添加内容过滤层 1. 对自己加星标仓库的名称和描述进行分词;2. 生成单词特征集;3. 在新的 DataFrame 中使用特征集和协同过滤生成的仓库进行相似度计算
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值