diff --git a/docs/第一章 推荐系统基础/1.1 基础推荐算法/1.1.2 协同过滤-UserCF.md b/docs/第一章 推荐系统基础/1.1 基础推荐算法/1.1.2 协同过滤-UserCF.md
new file mode 100644
index 00000000..2a34353e
--- /dev/null
+++ b/docs/第一章 推荐系统基础/1.1 基础推荐算法/1.1.2 协同过滤-UserCF.md
@@ -0,0 +1,345 @@
+# 协同过滤算法
+
+## 基本思想
+
+协同过滤(Collaborative Filtering)推荐算法是最经典、最常用的推荐算法。基本思想是:
+
++ 根据用户之前的喜好以及其他兴趣相近的用户的选择来给用户推荐物品。
+
+ + 基于对用户历史行为数据的挖掘发现用户的喜好偏向, 并预测用户可能喜好的产品进行推荐。
+ + 一般是仅仅基于用户的行为数据(评价、购买、下载等), 而不依赖于项的任何附加信息(物品自身特征)或者用户的任何附加信息(年龄, 性别等)。
++ 目前应用比较广泛的协同过滤算法是基于邻域的方法,主要有:
+ + 基于用户的协同过滤算法(UserCF):给用户推荐和他兴趣相似的其他用户喜欢的产品。
+ + 基于物品的协同过滤算法(ItemCF):给用户推荐和他之前喜欢的物品相似的物品。
+
+不管是 UserCF 还是 ItemCF 算法, 重点是计算用户之间(或物品之间)的相似度。
+
+## 相似性度量方法
+
+1. **杰卡德(Jaccard)相似系数**
+
+ `Jaccard` 系数是衡量两个集合的相似度一种指标,计算公式如下:
+ $$
+ sim_{uv}=\frac{|N(u) \cap N(v)|}{|N(u)| \cup|N(v)|}
+ $$
+
+ + 其中 $N(u)$,$N(v)$ 分别表示用户 $u$ 和用户 $v$ 交互物品的集合。
+
+ + 对于用户 $u$ 和 $v$ ,该公式反映了两个交互物品交集的数量占这两个用户交互物品并集的数量的比例。
+
+ 由于杰卡德相似系数一般无法反映具体用户的评分喜好信息,所以常用来评估用户是否会对某物品进行打分, 而不是预估用户会对某物品打多少分。
+
+2. **余弦相似度**
+ 余弦相似度衡量了两个向量的夹角,夹角越小越相似。余弦相似度的计算如下,其与杰卡德(Jaccard)相似系数只是在分母上存在差异:
+ $$
+ sim_{uv}=\frac{|N(u) \cap N(v)|}{\sqrt{|N(u)|\cdot|N(v)|}}
+ $$
+ 从向量的角度进行描述,令矩阵 $A$ 为用户-物品交互矩阵,矩阵的行表示用户,列表示物品。
+
+ + 设用户和物品数量分别为 $m,n$,交互矩阵$A$就是一个 $m$ 行 $n$ 列的矩阵。
+
+ + 矩阵中的元素均为 $0/1$。若用户 $i$ 对物品 $j$ 存在交互,那么 $A_{i,j}=1$,否则为 $0$ 。
+
+ + 那么,用户之间的相似度可以表示为:
+ $$
+ sim_{uv} = cos(u,v) =\frac{u\cdot v}{|u|\cdot |v|}
+ $$
+
+ + 向量 $u,v$ 在形式都是 one-hot 类型,$u\cdot v$ 表示向量点积。
+
+ 上述用户-物品交互矩阵在现实中是十分稀疏的,为了节省内存,交互矩阵会采用字典进行存储。在 `sklearn` 中,余弦相似度的实现:
+
+ ```python
+ from sklearn.metrics.pairwise import cosine_similarity
+
+ i = [1, 0, 0, 0]
+ j = [1, 0, 1, 0]
+ cosine_similarity([i, j])
+ ```
+
+3. **皮尔逊相关系数**
+
+ 在用户之间的余弦相似度计算时,将用户向量的内积展开为各元素乘积和:
+ $$
+ sim_{uv} = \frac{\sum_i r_{ui}*r_{vi}}{\sqrt{\sum_i r_{ui}^2}\sqrt{\sum_i r_{vi}^2}}
+ $$
+ + 其中,$r_{ui},r_{vi}$ 分别表示用户 $u$ 和用户 $v$ 对物品 $i$ 是否有交互(或具体评分值)。
+
+ 皮尔逊相关系数与余弦相似度的计算公式非常的类似,如下:
+ $$
+ sim(u,v)=\frac{\sum_{i\in I}(r_{ui}-\bar r_u)(r_{vi}-\bar r_v)}{\sqrt{\sum_{i\in I }(r_{ui}-\bar r_u)^2}\sqrt{\sum_{i\in I }(r_{vi}-\bar r_v)^2}}
+ $$
+ + 其中,$r_{ui},r_{vi}$ 分别表示用户 $u$ 和用户 $v$ 对物品 $i$ 是否有交互(或具体评分值);
+ + $\bar r_u, \bar r_v$ 分别表示用户 $u$ 和用户 $v$ 交互的所有物品交互数量或者评分的平均值;
+
+ 相较于余弦相似度,皮尔逊相关系数通过使用用户的平均分对各独立评分进行修正,减小了用户评分偏置的影响。在`scipy`中,皮尔逊相关系数的实现:
+
+ ```python
+ from scipy.stats import pearsonr
+
+ i = [1, 0, 0, 0]
+ j = [1, 0.5, 0.5, 0]
+ pearsonr(i, j)
+ ```
+
+**适用场景**
+
++ $Jaccard$ 相似度表示两个集合的交集元素个数在并集中所占的比例 ,所以适用于隐式反馈数据(0-1)。
++ 余弦相似度在度量文本相似度、用户相似度、物品相似度的时候都较为常用。
++ 皮尔逊相关度,实际上也是一种余弦相似度。不过先对向量做了中心化,范围在 $-1$ 到 $1$。
+ + 相关度量的是两个变量的变化趋势是否一致,两个随机变量是不是同增同减。
+ + 不适合用作计算布尔值向量(0-1)之间相关度。
+
+# 基于用户的协同过滤
+
+## 基本思想
+
+基于用户的协同过滤(UserCF):
+
++ 例如,我们要对用户 $A$ 进行物品推荐,可以先找到和他有相似兴趣的其他用户。
++ 然后,将共同兴趣用户喜欢的,但用户 $A$ 未交互过的物品推荐给 $A$。
+
+
+
+## 计算过程
+
+以下图为例,给用户推荐物品的过程可以形象化为一个猜测用户对物品进行打分的任务,表格里面是5个用户对于5件物品的一个打分情况,就可以理解为用户对物品的喜欢程度。
+
+
+
+UserCF算法的两个步骤:
+
++ 首先,根据前面的这些打分情况(或者说已有的用户向量)计算一下 Alice 和用户1, 2, 3, 4的相似程度, 找出与 Alice 最相似的 n 个用户。
+
++ 根据这 n 个用户对物品 5 的评分情况和与 Alice 的相似程度会猜测出 Alice 对物品5的评分。如果评分比较高的话, 就把物品5推荐给用户 Alice, 否则不推荐。
+
+**具体过程:**
+
+1. 计算用户之间的相似度
+
+ + 根据 1.2 节的几种方法, 我们可以计算出各用户之间的相似程度。对于用户 Alice,选取出与其最相近的 $N$ 个用户。
+
+2. 计算用户对新物品的评分预测
+
+ + 常用的方式之一:利用目标用户与相似用户之间的相似度以及相似用户对物品的评分,来预测目标用户对候选物品的评分估计:
+ $$
+ R_{\mathrm{u}, \mathrm{p}}=\frac{\sum_{\mathrm{s} \in S}\left(w_{\mathrm{u}, \mathrm{s}} \cdot R_{\mathrm{s}, \mathrm{p}}\right)}{\sum_{\mathrm{s} \in S} w_{\mathrm{u}, \mathrm{s}}}
+ $$
+
+ + 其中,权重 $w_{u,s}$ 是用户 $u$ 和用户 $s$ 的相似度, $R_{s,p}$ 是用户 $s$ 对物品 $p$ 的评分。
+
+ + 另一种方式:考虑到用户评分的偏置,即有的用户喜欢打高分, 有的用户喜欢打低分的情况。公式如下:
+ $$
+ R_{\mathrm{u}, \mathrm{p}}=\bar{R}_{u} + \frac{\sum_{\mathrm{s} \in S}\left(w_{\mathrm{u}, \mathrm{s}} \cdot \left(R_{s, p}-\bar{R}_{s}\right)\right)}{\sum_{\mathrm{s} \in S} w_{\mathrm{u}, \mathrm{s}}}
+ $$
+
+ + 其中,$\bar{R}_{s}$ 表示用户 $s$ 对物品的历史平均评分。
+
+3. 对用户进行物品推荐
+
+ + 在获得用户 $u$ 对不同物品的评价预测后, 最终的推荐列表根据预测评分进行排序得到。
+
+**手动计算:**
+
+根据上面的问题, 下面手动计算 Alice 对物品 5 的得分:
+
+
+1. 计算 Alice 与其他用户的相似度(基于皮尔逊相关系数)
+
+ + 手动计算 Alice 与用户 1 之间的相似度:
+
+ >用户向量 $\text {Alice}:(5,3,4,4) , \text{user1}:(3,1,2,3) , \text {user2}:( 4,3,4,3) , \text {user3}:(3,3,1,5) , \text {user4}:(1,5,5,2) $
+ >
+ >+ 计算Alice与user1的余弦相似性:
+ >$$
+ >\operatorname{sim}(\text { Alice, user1 })=\cos (\text { Alice, user } 1)=\frac{15+3+8+12}{\operatorname{sqrt}(25+9+16+16) * \operatorname{sqrt}(9+1+4+9)}=0.975
+ >$$
+ >
+ >+ 计算Alice与user1皮尔逊相关系数:
+ > + $Alice\_ave =4 \quad user1\_ave =2.25 $
+ > + 向量减去均值: $\text {Alice}:(1,-1, 0,0) \quad \text { user1 }:(0.75,-1.25,-0.25,0.75)$
+ >
+ >+ 计算这俩新向量的余弦相似度和上面计算过程一致, 结果是 0.852 。
+ >
+
+ + 基于 sklearn 计算所有用户之间的皮尔逊相关系数。可以看出,与 Alice 相似度最高的用户为用户1和用户2。
+
+
+
+2. **根据相似度用户计算 Alice对物品5的最终得分**
+ 用户1对物品5的评分是3, 用户2对物品5的打分是5, 那么根据上面的计算公式, 可以计算出 Alice 对物品5的最终得分是
+ $$
+ P_{Alice, 物品5}=\bar{R}_{Alice}+\frac{\sum_{k=1}^{2}\left(w_{Alice,user k}\left(R_{userk, 物品5}-\bar{R}_{userk}\right)\right)}{\sum_{k=1}^{2} w_{Alice, userk}}=4+\frac{0.85*(3-2.4)+0.7*(5-3.8)}{0.85+0.7}=4.87
+ $$
+
+ + 同样方式,可以计算用户 Alice 对其他物品的评分预测。
+
+3. **根据用户评分对用户进行推荐**
+
+ + 根据 Alice 的打分对物品排个序从大到小:$$物品1>物品5>物品3=物品4>物品2$$。
+ + 如果要向 Alice 推荐2款产品的话, 我们就可以推荐物品 1 和物品 5 给 Alice。
+
+ 至此, 基于用户的协同过滤算法原理介绍完毕。
+
+## UserCF编程实现
+
+1. 建立实验使用的数据表:
+
+ ```python
+ import numpy as np
+ import pandas as pd
+
+
+ def loadData():
+ users = {'Alice': {'A': 5, 'B': 3, 'C': 4, 'D': 4},
+ 'user1': {'A': 3, 'B': 1, 'C': 2, 'D': 3, 'E': 3},
+ 'user2': {'A': 4, 'B': 3, 'C': 4, 'D': 3, 'E': 5},
+ 'user3': {'A': 3, 'B': 3, 'C': 1, 'D': 5, 'E': 4},
+ 'user4': {'A': 1, 'B': 5, 'C': 5, 'D': 2, 'E': 1}
+ }
+ return users
+ ```
+
+ + 这里使用字典来建立用户-物品的交互表。
+ + 字典`users`的键表示不同用户的名字,值为一个评分字典,评分字典的键值对表示某物品被当前用户的评分。
+ + 由于现实场景中,用户对物品的评分比较稀疏。如果直接使用矩阵进行存储,会存在大量空缺值,故此处使用了字典。
+
+2. 计算用户相似性矩阵
+
+ + 由于训练数据中共包含 5 个用户,所以这里的用户相似度矩阵的维度也为 $5 \times 5$。
+
+ ```python
+ user_data = loadData()
+ similarity_matrix = pd.DataFrame(
+ np.identity(len(user_data)),
+ index=user_data.keys(),
+ columns=user_data.keys(),
+ )
+
+ # 遍历每条用户-物品评分数据
+ for u1, items1 in user_data.items():
+ for u2, items2 in user_data.items():
+ if u1 == u2:
+ continue
+ vec1, vec2 = [], []
+ for item, rating1 in items1.items():
+ rating2 = items2.get(item, -1)
+ if rating2 == -1:
+ continue
+ vec1.append(rating1)
+ vec2.append(rating2)
+ # 计算不同用户之间的皮尔逊相关系数
+ similarity_matrix[u1][u2] = np.corrcoef(vec1, vec2)[0][1]
+
+ print(similarity_matrix)
+ ```
+
+ ```
+ 1 2 3 4 5
+ 1 1.000000 0.852803 0.707107 0.000000 -0.792118
+ 2 0.852803 1.000000 0.467707 0.489956 -0.900149
+ 3 0.707107 0.467707 1.000000 -0.161165 -0.466569
+ 4 0.000000 0.489956 -0.161165 1.000000 -0.641503
+ 5 -0.792118 -0.900149 -0.466569 -0.641503 1.000000
+ ```
+
+3. 计算与 Alice 最相似的 `num` 个用户
+
+ ```python
+ target_user = ' Alice '
+ num = 2
+ # 由于最相似的用户为自己,去除本身
+ sim_users = similarity_matrix[target_user].sort_values(ascending=False)[1:num+1].index.tolist()
+ print(f'与用户{target_user}最相似的{num}个用户为:{sim_users}')
+ ```
+
+ ```
+ 与用户 Alice 最相似的2个用户为:['user1', 'user2']
+ ```
+
+4. 预测用户 Alice 对物品 `E` 的评分
+
+ ```python
+ weighted_scores = 0.
+ corr_values_sum = 0.
+
+ target_item = 'E'
+ # 基于皮尔逊相关系数预测用户评分
+ for user in sim_users:
+ corr_value = similarity_matrix[target_user][user]
+ user_mean_rating = np.mean(list(user_data[user].values()))
+
+ weighted_scores += corr_value * (user_data[user][target_item] - user_mean_rating)
+ corr_values_sum += corr_value
+
+ target_user_mean_rating = np.mean(list(user_data[target_user].values()))
+ target_item_pred = target_user_mean_rating + weighted_scores / corr_values_sum
+ print(f'用户{target_user}对物品{target_item}的预测评分为:{target_item_pred}')
+ ```
+
+ ```
+ 用户 Alice 对物品E的预测评分为:4.871979899370592
+ ```
+
+## UserCF优缺点
+
+User-based算法存在两个重大问题:
+
+
+1. 数据稀疏性
+ + 一个大型的电子商务推荐系统一般有非常多的物品,用户可能买的其中不到1%的物品,不同用户之间买的物品重叠性较低,导致算法无法找到一个用户的邻居,即偏好相似的用户。
+ + 这导致UserCF不适用于那些正反馈获取较困难的应用场景(如酒店预订, 大件物品购买等低频应用)。
+
+1. 算法扩展性
+ + 基于用户的协同过滤需要维护用户相似度矩阵以便快速的找出 $TopN$ 相似用户, 该矩阵的存储开销非常大,存储空间随着用户数量的增加而增加。
+ + 故不适合用户数据量大的情况使用。
+
+由于UserCF技术上的两点缺陷, 导致很多电商平台并没有采用这种算法, 而是采用了ItemCF算法实现最初的推荐系统。
+
+
+
+# 算法评估
+
+由于UserCF和ItemCF结果评估部分是共性知识点, 所以在这里统一标识。
+
+## 召回率
+
+对用户 $u$ 推荐 $N$ 个物品记为 $R(u)$, 令用户 $u$ 在测试集上喜欢的物品集合为$T(u)$, 那么召回率定义为:
+$$
+\operatorname{Recall}=\frac{\sum_{u}|R(u) \cap T(u)|}{\sum_{u}|T(u)|}
+$$
++ 含义:在模型召回预测的物品中,预测准确的物品占用户实际喜欢的物品的比例。
+
+## 精确率
+精确率定义为:
+$$
+\operatorname{Precision}=\frac{\sum_{u} \mid R(u) \cap T(u)|}{\sum_{u}|R(u)|}
+$$
++ 含义:推荐的物品中,对用户准确推荐的物品占总物品的比例。
++ 如要确保召回率高,一般是推荐更多的物品,期望推荐的物品中会涵盖用户喜爱的物品。而实际中,推荐的物品中用户实际喜爱的物品占少数,推荐的精确率就会很低。故同时要确保高召回率和精确率往往是矛盾的,所以实际中需要在二者之间进行权衡。
+
+## 覆盖率
+覆盖率反映了推荐算法发掘长尾的能力, 覆盖率越高, 说明推荐算法越能将长尾中的物品推荐给用户。
+$$
+\text { Coverage }=\frac{\left|\bigcup_{u \in U} R(u)\right|}{|I|}
+$$
+
++ 含义:推荐系统能够推荐出来的物品占总物品集合的比例。
+ + 其中 $|I|$ 表示所有物品的个数;
+ + 系统的用户集合为$U$;
+ + 推荐系统给每个用户推荐一个长度为 $N$ 的物品列表$R(u)$.
+
++ 覆盖率表示最终的推荐列表中包含多大比例的物品。如果所有物品都被给推荐给至少一个用户, 那么覆盖率是100%。
+
+## 新颖度
+用推荐列表中物品的平均流行度度量推荐结果的新颖度。 如果推荐出的物品都很热门, 说明推荐的新颖度较低。 由于物品的流行度分布呈长尾分布, 所以为了流行度的平均值更加稳定, 在计算平均流行度时对每个物品的流行度取对数。
+
+- O’scar Celma 在博士论文 "[Music Recommendation and Discovery in the Long Tail](http://mtg.upf.edu/static/media/PhD_ocelma.pdf) " 中研究了新颖度的评测。
+
+
+
+# 参考资料
+
+* [基于用户的协同过滤来构建推荐系统:https://mp.weixin.qq.com/s/ZtnaQrVIpVOPJpqMdLWOcw](https://mp.weixin.qq.com/s/ZtnaQrVIpVOPJpqMdLWOcw)
+* [协同过滤算法概述:https://chenk.tech/posts/8ad63d9d.html](https://chenk.tech/posts/8ad63d9d.html)
+* B站黑马推荐系统实战课程
diff --git a/docs/第一章 推荐系统基础/1.1 基础推荐算法/1.1.2 协同过滤.md b/docs/第一章 推荐系统基础/1.1 基础推荐算法/1.1.2 协同过滤.md
deleted file mode 100644
index 53c5c78c..00000000
--- a/docs/第一章 推荐系统基础/1.1 基础推荐算法/1.1.2 协同过滤.md
+++ /dev/null
@@ -1,502 +0,0 @@
-### 1. 协同过滤算法
-
-协同过滤(Collaborative Filtering)推荐算法是最经典、最常用的推荐算法。
-
-所谓协同过滤, 基本思想是**根据用户之前的喜好以及其他兴趣相近的用户的选择来给用户推荐物品**(基于对用户历史行为数据的挖掘发现用户的喜好偏向, 并预测用户可能喜好的产品进行推荐),**一般是仅仅基于用户的行为数据(评价、购买、下载等), 而不依赖于项的任何附加信息(物品自身特征)或者用户的任何附加信息(年龄, 性别等)**。目前应用比较广泛的协同过滤算法是基于邻域的方法, 而这种方法主要有下面两种算法:
-
-
-* **基于用户的协同过滤算法(UserCF)**: 给用户推荐和他兴趣相似的其他用户喜欢的产品
-* **基于物品的协同过滤算法(ItemCF)**: 给用户推荐和他之前喜欢的物品相似的物品
-
-不管是UserCF还是ItemCF算法, 非常重要的步骤之一就是计算用户和用户或者物品和物品之间的相似度, 所以下面先整理常用的相似性度量方法, 然后再对每个算法的具体细节进行展开。
-
-
-
-### 2. 相似性度量方法
-
-1. **杰卡德(Jaccard)相似系数**
- 这个是衡量两个集合的相似度一种指标。两个用户$u$和$v$交互商品交集的数量占这两个用户交互商品并集的数量的比例,称为两个集合的杰卡德相似系数,用符号$sim_{uv}$表示,其中$N(u),N(v)$分别表示用户$u$和用户$v$交互商品的集合。
- $$
- sim_{uv}=\frac{|N(u) \cap N(v)|}{\sqrt{|N(u)| \cup|N(v)|}}
- $$
- 由于杰卡德相似系数一般无法反映具体用户的评分喜好信息, 所以常用来评估用户是否会对某商品进行打分, 而不是预估用户会对某商品打多少分。
-
-
-
-2. **余弦相似度**
- 余弦相似度衡量了两个向量的夹角,夹角越小越相似。首先从集合的角度描述余弦相似度,相比于Jaccard公式来说就是分母有差异,不是两个用户交互商品的并集的数量,而是两个用户分别交互的商品数量的乘积,公式如下:
- $$
- sim_{uv}=\frac{|N(u) \cap N(v)|}{\sqrt{|N(u)|\cdot|N(v)|}}
- $$
- 从向量的角度进行描述,令矩阵$A$为用户-商品交互矩阵(因为是TopN推荐并不需要用户对物品的评分,只需要知道用户对商品是否有交互就行),即矩阵的每一行表示一个用户对所有商品的交互情况,有交互的商品值为1没有交互的商品值为0,矩阵的列表示所有商品。若用户和商品数量分别为$m,n$的话,交互矩阵$A$就是一个$m$行$n$列的矩阵。此时用户的相似度可以表示为(其中$u\cdot v$指的是向量点积):
- $$
- sim_{uv} = cos(u,v) =\frac{u\cdot v}{|u|\cdot |v|}
- $$
- 上述用户-商品交互矩阵在现实情况下是非常的稀疏了,为了避免存储这么大的稀疏矩阵,在计算用户相似度的时候一般会采用集合的方式进行计算。理论上向量之间的相似度计算公式都可以用来计算用户之间的相似度,但是会根据实际的情况选择不同的用户相似度度量方法。
-
- 这个在具体实现的时候, 可以使用`cosine_similarity`进行实现:
-
- ```python
- from sklearn.metrics.pairwise import cosine_similarity
- i = [1, 0, 0, 0]
- j = [1, 0.5, 0.5, 0]
- cosine_similarity([i, j])
- ```
-
-
-
-3. **皮尔逊相关系数**
-
- 皮尔逊相关系数的公式与余弦相似度的计算公式非常的类似,首先对于上述的余弦相似度的计算公式写成求和的形式,其中$r_{ui},r_{vi}$分别表示用户$u$和用户$v$对商品$i$是否有交互(或者具体的评分值):
- $$
- sim_{uv} = \frac{\sum_i r_{ui}*r_{vi}}{\sqrt{\sum_i r_{ui}^2}\sqrt{\sum_i r_{vi}^2}}
- $$
- 如下是皮尔逊相关系数计算公式,其中$r_{ui},r_{vi}$分别表示用户$u$和用户$v$对商品$i$是否有交互(或者具体的评分值),$\bar r_u, \bar r_v$分别表示用户$u$和用户$v$交互的所有商品交互数量或者具体评分的平均值。
- $$
- sim(u,v)=\frac{\sum_{i\in I}(r_{ui}-\bar r_u)(r_{vi}-\bar r_v)}{\sqrt{\sum_{i\in I }(r_{ui}-\bar r_u)^2}\sqrt{\sum_{i\in I }(r_{vi}-\bar r_v)^2}}
- $$
- 所以相比余弦相似度,皮尔逊相关系数通过使用用户的平均分对各独立评分进行修正,减小了用户评分偏置的影响。具体实现, 我们也是可以调包, 这个计算方式很多, 下面是其中的一种:
-
- ```python
- from scipy.stats import pearsonr
-
- i = [1, 0, 0, 0]
- j = [1, 0.5, 0.5, 0]
- pearsonr(i, j)
- ```
-
-
-下面是基于用户协同过滤和基于物品协同过滤的原理讲解。
-
-
-
-### 3. 基于用户的协同过滤
-
-基于用户的协同过滤(以下用UserCF表示),思想其实比较简单,**当一个用户A需要个性化推荐的时候, 我们可以先找到和他有相似兴趣的其他用户, 然后把那些用户喜欢的, 而用户A没有听说过的物品推荐给A**。
-
-
-
-**UserCF算法主要包括两个步骤:**
-
-
-1. 找到和目标用户兴趣相似的集合
-2. 找到这个集合中的用户喜欢的, 且目标用户没有听说过的物品推荐给目标用户。
-
-上面的两个步骤中, 第一个步骤里面, 我们会基于前面给出的相似性度量的方法找出与目标用户兴趣相似的用户, 而第二个步骤里面, 如何基于相似用户喜欢的物品来对目标用户进行推荐呢? 这个要依赖于目标用户对相似用户喜欢的物品的一个喜好程度, 那么如何衡量这个程度大小呢? 为了更好理解上面的两个步骤, 下面拿一个具体的例子把两个步骤具体化。
-
-
-
-**以下图为例,此例将会用于本文各种算法中**
-
-
-
-给用户推荐物品的过程可以**形象化为一个猜测用户对商品进行打分的任务**,上面表格里面是5个用户对于5件物品的一个打分情况,就可以理解为用户对物品的喜欢程度
-
-应用UserCF算法的两个步骤:
-
-
-1. 首先根据前面的这些打分情况(或者说已有的用户向量)计算一下Alice和用户1, 2, 3, 4的相似程度, 找出与Alice最相似的n个用户
-2. 根据这n个用户对物品5的评分情况和与Alice的相似程度会猜测出Alice对物品5的评分, 如果评分比较高的话, 就把物品5推荐给用户Alice, 否则不推荐。
-
-关于第一个步骤, 上面已经给出了计算两个用户相似性的方法, 这里不再过多赘述, 这里主要解决第二个问题, 如何产生最终结果的预测。
-
-
-
-**最终结果的预测**
-
-根据上面的几种方法, 我们可以计算出向量之间的相似程度, 也就是可以计算出Alice和其他用户的相近程度, 这时候我们就可以选出与Alice最相近的前n个用户, 基于他们对物品5的评价猜测出Alice的打分值, 那么是怎么计算的呢?
-
-这里常用的方式之一是**利用用户相似度和相似用户的评价加权平均获得用户的评价预测**, 用下面式子表示:
-
-$$
-R_{\mathrm{u}, \mathrm{p}}=\frac{\sum_{\mathrm{s} \in S}\left(w_{\mathrm{u}, \mathrm{s}} \cdot R_{\mathrm{s}, \mathrm{p}}\right)}{\sum_{\mathrm{s} \in S} w_{\mathrm{u}, \mathrm{s}}}
-$$
-这个式子里面, 权重$w_{u,s}$是用户$u$和用户$s$的相似度, $R_{s,p}$是用户$s$对物品$p$的评分。
-
-还有一种方式如下, 这种方式考虑的更加前面, 依然是用户相似度作为权值, 但后面不单纯的是其他用户对物品的评分, 而是**该物品的评分与此用户的所有评分的差值进行加权平均, 这时候考虑到了有的用户内心的评分标准不一的情况**, 即有的用户喜欢打高分, 有的用户喜欢打低分的情况。
-
-$$
-P_{i, j}=\bar{R}_{i}+\frac{\sum_{k=1}^{n}\left(S_{i, k}\left(R_{k, j}-\bar{R}_{k}\right)\right)}{\sum_{k=1}^{n} S_{i, k}}
-$$
-所以这一种计算方式更为推荐。下面的计算将使用这个方式。这里的$S_{i,k}$与上面的$w_{u,s}$的意思是类似的,表示的是用户i和用户k之间的相似度。
-
-在获得用户$u$对不同物品的评价预测后, 最终的推荐列表根据预测评分进行排序得到。 至此,基于用户的协同过滤算法的推荐过程完成。
-
-根据上面的问题, 下面手算一下:
-
-Aim: 猜测Alice对物品5的得分:
-
-
-1. **计算Alice与其他用户的相似度(这里使用皮尔逊相关系数)**
-
-
-
-
- 这里我们使用皮尔逊相关系数, 也就是Alice与用户1的相似度是0.85。同样的方式, 我们就可以计算与其他用户的相似度, 这里可以使用numpy的相似度函数得到用户的相似性矩阵:
-
-
-
- 从这里看出, Alice用户和用户2,用户3,用户4的相似度是0.82, 0.7, 0, -0.79。 所以如果n=2, 找到与Alice最相近的两个用户是用户1, 和Alice的相似度是0.85, 用户2, 和Alice相似度是0.7
-
-2. **根据相似度用户计算Alice对物品5的最终得分**
- 用户1对物品5的评分是3, 用户2对物品5的打分是5, 那么根据上面的计算公式, 可以计算出Alice对物品5的最终得分是
-$$
- P_{Alice, 物品5}=\bar{R}_{Alice}+\frac{\sum_{k=1}^{2}\left(S_{Alice,user k}\left(R_{userk, 物品5}-\bar{R}_{userk}\right)\right)}{\sum_{k=1}^{2} S_{Alice, userk}}=4+\frac{0.85*(3-2.4)+0.7*(5-3.8)}{0.85+0.7}=4.87
-$$
-
-3. **根据用户评分对用户进行推荐**
- 这时候, 我们就得到了Alice对物品5的得分是4.87, 根据Alice的打分对物品排个序从大到小:$$物品1>物品5>物品3=物品4>物品2$$
- 这时候,如果要向Alice推荐2款产品的话, 我们就可以推荐物品1和物品5给Alice
-
- 至此, 基于用户的协同过滤算法原理介绍完毕。
-
-### 4. UserCF编程实现
-
-这里简单的通过编程实现上面的案例,为后面的大作业做一个热身, 梳理一下上面的过程其实就是三步: 计算用户相似性矩阵、得到前n个相似用户、计算最终得分。
-
-所以我们下面的程序也是分为这三步:
-
-1. **首先, 先把数据表给建立起来**
-这里我采用了字典的方式, 之所以没有用pandas, 是因为上面举得这个例子其实是个个例, 在真实情况中, 我们知道, 用户对物品的打分情况并不会这么完整, 会存在大量的空值, 所以矩阵会很稀疏, 这时候用DataFrame, 会有大量的NaN。故这里用字典的形式存储。 用两个字典, 第一个字典是物品-用户的评分映射, 键是物品1-5, 用A-E来表示, 每一个值又是一个字典, 表示的是每个用户对该物品的打分。 第二个字典是用户-物品的评分映射, 键是上面的五个用户, 用1-5表示, 值是该用户对每个物品的打分。
-```python
-# 定义数据集, 也就是那个表格, 注意这里我们采用字典存放数据, 因为实际情况中数据是非常稀疏的, 很少有情况是现在这样
-def loadData():
- items={'A': {1: 5, 2: 3, 3: 4, 4: 3, 5: 1},
- 'B': {1: 3, 2: 1, 3: 3, 4: 3, 5: 5},
- 'C': {1: 4, 2: 2, 3: 4, 4: 1, 5: 5},
- 'D': {1: 4, 2: 3, 3: 3, 4: 5, 5: 2},
- 'E': {2: 3, 3: 5, 4: 4, 5: 1}
- }
- users={1: {'A': 5, 'B': 3, 'C': 4, 'D': 4},
- 2: {'A': 3, 'B': 1, 'C': 2, 'D': 3, 'E': 3},
- 3: {'A': 4, 'B': 3, 'C': 4, 'D': 3, 'E': 5},
- 4: {'A': 3, 'B': 3, 'C': 1, 'D': 5, 'E': 4},
- 5: {'A': 1, 'B': 5, 'C': 5, 'D': 2, 'E': 1}
- }
- return items,users
-
-items, users = loadData()
-item_df = pd.DataFrame(items).T
-user_df = pd.DataFrame(users).T
-```
-
-
-
-2. **计算用户相似性矩阵**
- 这个是一个共现矩阵, 5*5,行代表每个用户, 列代表每个用户, 值代表用户和用户的相关性,这里的思路是这样, 因为要求用户和用户两两的相关性, 所以需要用双层循环遍历用户-物品评分数据, 当不是同一个用户的时候, 我们要去遍历物品-用户评分数据, 在里面去找这两个用户同时对该物品评过分的数据放入到这两个用户向量中。 因为正常情况下会存在很多的NAN, 即可能用户并没有对某个物品进行评分过, 这样的不能当做用户向量的一部分, 没法计算相似性。 还是看代码吧, 感觉不太好描述:
-
-```python
-"""计算用户相似性矩阵"""
-similarity_matrix = pd.DataFrame(np.zeros((len(users), len(users))), index=[1, 2, 3, 4, 5], columns=[1, 2, 3, 4, 5])
-
-# 遍历每条用户-物品评分数据
-for userID in users:
- for otheruserId in users:
- vec_user = []
- vec_otheruser = []
- if userID != otheruserId:
- for itemId in items: # 遍历物品-用户评分数据
- itemRatings = items[itemId] # 这也是个字典 每条数据为所有用户对当前物品的评分
- if userID in itemRatings and otheruserId in itemRatings: # 说明两个用户都对该物品评过分
- vec_user.append(itemRatings[userID])
- vec_otheruser.append(itemRatings[otheruserId])
- # 这里可以获得相似性矩阵(共现矩阵)
- similarity_matrix[userID][otheruserId] = np.corrcoef(np.array(vec_user), np.array(vec_otheruser))[0][1]
- #similarity_matrix[userID][otheruserId] = cosine_similarity(np.array(vec_user), np.array(vec_otheruser))[0][1]
-
-```
-这里的similarity_matrix就是我们的用户相似性矩阵, 长下面这样:
-
-
-
-
-有了相似性矩阵, 我们就可以得到与Alice最相关的前n个用户。
-
-
-
-3. **计算前n个相似的用户**
-
-```python
-"""计算前n个相似的用户"""
-n = 2
-similarity_users = similarity_matrix[1].sort_values(ascending=False)[:n].index.tolist() # [2, 3] 也就是用户1和用户2
-```
-
-
-
-4. **计算最终得分**
- 这里就是上面的那个公式了。
-
-```python
-"""计算最终得分"""
-base_score = np.mean(np.array([value for value in users[1].values()]))
-weighted_scores = 0.
-corr_values_sum = 0.
-for user in similarity_users: # [2, 3]
- corr_value = similarity_matrix[1][user] # 两个用户之间的相似性
- mean_user_score = np.mean(np.array([value for value in users[user].values()])) # 每个用户的打分平均值
- weighted_scores += corr_value * (users[user]['E']-mean_user_score) # 加权分数
- corr_values_sum += corr_value
-final_scores = base_score + weighted_scores / corr_values_sum
-print('用户Alice对物品5的打分: ', final_scores)
-user_df.loc[1]['E'] = final_scores
-user_df
-
-```
-结果如下:
-
-
-
-至此, 我们就用代码完成了上面的小例子, 有了这个评分, 我们其实就可以对该用户做推荐了。 这其实就是微型版的UserCF的工作过程了。
-
-**注意:基于用户协同过滤的完整代码参考源代码文件中的UserCF.py**
-
-
-
-### 5. UserCF优缺点
-
-User-based算法存在两个重大问题:
-
-
-1. 数据稀疏性。
-一个大型的电子商务推荐系统一般有非常多的物品,用户可能买的其中不到1%的物品,不同用户之间买的物品重叠性较低,导致算法无法找到一个用户的邻居,即偏好相似的用户。**这导致UserCF不适用于那些正反馈获取较困难的应用场景**(如酒店预订, 大件商品购买等低频应用)
-
-1. 算法扩展性。
-基于用户的协同过滤需要维护用户相似度矩阵以便快速的找出Topn相似用户, 该矩阵的存储开销非常大,存储空间随着用户数量的增加而增加,**不适合用户数据量大的情况使用**。
-
-由于UserCF技术上的两点缺陷, 导致很多电商平台并没有采用这种算法, 而是采用了ItemCF算法实现最初的推荐系统。
-
-
-
-### 6. 基于物品的协同过滤
-
-基于物品的协同过滤(ItemCF)的基本思想是预先根据所有用户的历史偏好数据计算物品之间的相似性,然后把与用户喜欢的物品相类似的物品推荐给用户。比如物品a和c非常相似,因为喜欢a的用户同时也喜欢c,而用户A喜欢a,所以把c推荐给用户A。**ItemCF算法并不利用物品的内容属性计算物品之间的相似度, 主要通过分析用户的行为记录计算物品之间的相似度, 该算法认为, 物品a和物品c具有很大的相似度是因为喜欢物品a的用户大都喜欢物品c**。
-
-
-
-
-
-**基于物品的协同过滤算法主要分为两步:**
-
-
-* 计算物品之间的相似度
-* 根据物品的相似度和用户的历史行为给用户生成推荐列表(购买了该商品的用户也经常购买的其他商品)
-
-基于物品的协同过滤算法和基于用户的协同过滤算法很像, 所以我们这里直接还是拿上面Alice的那个例子来看。
-
-
-
-如果想知道Alice对物品5打多少分, 基于物品的协同过滤算法会这么做:
-
-
-1. 首先计算一下物品5和物品1, 2, 3, 4之间的相似性(它们也是向量的形式, 每一列的值就是它们的向量表示, 因为ItemCF认为物品a和物品c具有很大的相似度是因为喜欢物品a的用户大都喜欢物品c, 所以就可以基于每个用户对该物品的打分或者说喜欢程度来向量化物品)
-2. 找出与物品5最相近的n个物品
-3. 根据Alice对最相近的n个物品的打分去计算对物品5的打分情况
-
-
-
-**下面我们就可以具体计算一下, 首先是步骤1:**
-
-
-
-由于计算比较麻烦, 这里直接用python计算了:
-
-
-
-根据皮尔逊相关系数, 可以找到与物品5最相似的2个物品是item1和item4(n=2), 下面基于上面的公式计算最终得分:
-
-$$
-P_{Alice, 物品5}=\bar{R}_{物品5}+\frac{\sum_{k=1}^{2}\left(S_{物品5,物品 k}\left(R_{Alice, 物品k}-\bar{R}_{物品k}\right)\right)}{\sum_{k=1}^{2} S_{物品k, 物品5}}=\frac{13}{4}+\frac{0.97*(5-3.2)+0.58*(4-3.4)}{0.97+0.58}=4.6
-$$
-
-
-这时候依然可以向Alice推荐物品5。下面也是简单编程实现一下, 和上面的差不多:
-
-```python
-"""计算物品的相似矩阵"""
-similarity_matrix = pd.DataFrame(np.ones((len(items), len(items))), index=['A', 'B', 'C', 'D', 'E'], columns=['A', 'B', 'C', 'D', 'E'])
-
-# 遍历每条物品-用户评分数据
-for itemId in items:
- for otheritemId in items:
- vec_item = [] # 定义列表, 保存当前两个物品的向量值
- vec_otheritem = []
- #userRagingPairCount = 0 # 两件物品均评过分的用户数
- if itemId != otheritemId: # 物品不同
- for userId in users: # 遍历用户-物品评分数据
- userRatings = users[userId] # 每条数据为该用户对所有物品的评分, 这也是个字典
-
- if itemId in userRatings and otheritemId in userRatings: # 用户对这两个物品都评过分
- #userRagingPairCount += 1
- vec_item.append(userRatings[itemId])
- vec_otheritem.append(userRatings[otheritemId])
-
- # 这里可以获得相似性矩阵(共现矩阵)
- similarity_matrix[itemId][otheritemId] = np.corrcoef(np.array(vec_item), np.array(vec_otheritem))[0][1]
- #similarity_matrix[itemId][otheritemId] = cosine_similarity(np.array(vec_item), np.array(vec_otheritem))[0][1]
-
-```
-这里就是物品的相似度矩阵了, 大概长下面这个样子:
-
-
-
-
-**然后也是得到与物品5相似的前n个物品, 计算出最终得分来。**
-
-```python
-"""得到与物品5相似的前n个物品"""
-n = 2
-similarity_items = similarity_matrix['E'].sort_values(ascending=False)[:n].index.tolist() # ['A', 'D']
-
-"""计算最终得分"""
-base_score = np.mean(np.array([value for value in items['E'].values()]))
-weighted_scores = 0.
-corr_values_sum = 0.
-for item in similarity_items: # ['A', 'D']
- corr_value = similarity_matrix['E'][item] # 两个物品之间的相似性
- mean_item_score = np.mean(np.array([value for value in items[item].values()])) # 每个物品的打分平均值
- weighted_scores += corr_value * (users[1][item]-mean_item_score) # 加权分数
- corr_values_sum += corr_value
-final_scores = base_score + weighted_scores / corr_values_sum
-print('用户Alice对物品5的打分: ', final_scores)
-user_df.loc[1]['E'] = final_scores
-user_df
-
-```
-结果如下:
-
-
-
-**注意:基于商品的协同过滤算法的完整代码参考源代码文件中的ItemCF.py**
-
-
-
-### 7. 算法评估
-
-由于UserCF和ItemCF结果评估部分是共性知识点, 所以在这里统一标识。 这里介绍评测指标:
-
-1. 召回率
-
- 对用户u推荐N个物品记为$R(u)$, 令用户u在测试集上喜欢的物品集合为$T(u)$, 那么召回率定义为:
-$$
- \operatorname{Recall}=\frac{\sum_{u}|R(u) \cap T(u)|}{\sum_{u}|T(u)|}
-$$
- 这个意思就是说, 在用户真实购买或者看过的影片里面, 我模型真正预测出了多少, 这个考察的是模型推荐的一个全面性。
-
-2. 准确率
- 准确率定义为:
-$$
-\operatorname{Precision}=\frac{\sum_{u} \mid R(u) \cap T(u)|}{\sum_{u}|R(u)|}
-$$
- 这个意思再说, 在我推荐的所有物品中, 用户真正看的有多少, 这个考察的是我模型推荐的一个准确性。
- 为了提高准确率, 模型需要把非常有把握的才对用户进行推荐, 所以这时候就减少了推荐的数量, 而这往往就损失了全面性, 真正预测出来的会非常少,所以实际应用中应该综合考虑两者的平衡。
-
-3. 覆盖率
- 覆盖率反映了推荐算法发掘长尾的能力, 覆盖率越高, 说明推荐算法越能将长尾中的物品推荐给用户。
-$$
- \text { Coverage }=\frac{\left|\bigcup_{u \in U} R(u)\right|}{|I|}
-$$
-
-4. 该覆盖率表示最终的推荐列表中包含多大比例的物品。如果所有物品都被给推荐给至少一个用户, 那么覆盖率是100%。
-
-5. 新颖度
- 用推荐列表中物品的平均流行度度量推荐结果的新颖度。 如果推荐出的物品都很热门, 说明推荐的新颖度较低。 由于物品的流行度分布呈长尾分布, 所以为了流行度的平均值更加稳定, 在计算平均流行度时对每个物品的流行度取对数。
-
-
-
-### 8. 协同过滤算法的权重改进
-
-
-
-* 基础算法
- 图1为最简单的计算物品相关度的公式, 分子为同时喜好itemi和itemj的用户数
-* 对热门物品的惩罚
- 图1存在一个问题, 如果 item-j 是很热门的商品,导致很多喜欢 item-i 的用户都喜欢 item-j,这时 $w_{ij}$ 就会非常大。同样,几乎所有的物品都和 item-j 的相关度非常高,这显然是不合理的。所以图2中分母通过引入 $N(j)$ 来对 item-j 的热度进行惩罚。如果物品很热门, 那么$N(j)$就会越大, 对应的权重就会变小。
-* 对热门物品的进一步惩罚
- 如果 item-j 极度热门,上面的算法还是不够的。举个例子,《Harry Potter》非常火,买任何一本书的人都会购买它,即使通过图2的方法对它进行了惩罚,但是《Harry Potter》仍然会获得很高的相似度。这就是推荐系统领域著名的 Harry Potter Problem。
如果需要进一步对热门物品惩罚,可以继续修改公式为如图3所示,通过调节参数 $α$,$α $越大,惩罚力度越大,热门物品的相似度越低,整体结果的平均热门程度越低。
-* 对活跃用户的惩罚
- 同样的,Item-based CF 也需要考虑活跃用户(即一个活跃用户(专门做刷单)可能买了非常多的物品)的影响,活跃用户对物品相似度的贡献应该小于不活跃用户。图4为集合了该权重的算法。
-
-
-
-### 9. 协同过滤算法的问题分析
-
-协同过滤算法存在的问题之一就是**泛化能力弱**, 即协同过滤无法将两个物品相似的信息推广到其他物品的相似性上。 导致的问题是**热门物品具有很强的头部效应, 容易跟大量物品产生相似, 而尾部物品由于特征向量稀疏, 导致很少被推荐**。 比如下面这个例子:
-
-
-
-A, B, C, D是物品, 看右边的物品共现矩阵, 可以发现物品D与A、B、C的相似度比较大, 所以很有可能将D推荐给用过A、B、C的用户。 但是物品D与其他物品相似的原因是因为D是一件热门商品, 系统无法找出A、B、C之间相似性的原因是其特征太稀疏, 缺乏相似性计算的直接数据。 所以这就是协同过滤的天然缺陷:**推荐系统头部效应明显, 处理稀疏向量的能力弱**。
-
-为了解决这个问题, 同时增加模型的泛化能力,2006年,**矩阵分解技术(Matrix Factorization,MF**)被提出, 该方法在协同过滤共现矩阵的基础上, 使用更稠密的隐向量表示用户和物品, 挖掘用户和物品的隐含兴趣和隐含特征, 在一定程度上弥补协同过滤模型处理稀疏矩阵能力不足的问题。 具体细节等后面整理, 这里先铺垫一下。
-
-
-
-### 10. 课后思考
-
-1.**什么时候使用UserCF,什么时候使用ItemCF?为什么?**
-
-答案:
-
-> 1. UserCF
-> 由于是基于用户相似度进行推荐, 所以具备更强的社交特性, 这样的特点非常适于**用户少, 物品多, 时效性较强的场合**, 比如新闻推荐场景, 因为新闻本身兴趣点分散, 相比用户对不同新闻的兴趣偏好, 新闻的及时性,热点性往往更加重要, 所以正好适用于发现热点,跟踪热点的趋势。 另外还具有推荐新信息的能力, 更有可能发现惊喜, 因为看的是人与人的相似性, 推出来的结果可能更有惊喜,可以发现用户潜在但自己尚未察觉的兴趣爱好。
->
-> 对于用户较少, 要求时效性较强的场合, 就可以考虑UserCF。
->
-> 2. ItemCF
-> 这个更适用于兴趣变化较为稳定的应用, 更接近于个性化的推荐, 适合**物品少,用户多,用户兴趣固定持久, 物品更新速度不是太快的场合**, 比如推荐艺术品, 音乐, 电影。
-> 下面是UserCF和ItemCF的优缺点对比: (来自项亮推荐系统实践)
-
-
-
-2.**协同过滤在计算上有什么缺点?有什么比较好的思路可以解决(缓解)?**
-
-答案:
-
-> **较差的稀疏向量处理能力**
->
-> 第一个问题就是**泛化能力弱**, 即协同过滤无法将两个物品相似的信息推广到其他物品的相似性上。 导致的问题是**热门物品具有很强的头部效应, 容易跟大量物品产生相似, 而尾部物品由于特征向量稀疏, 导致很少被推荐**。 比如下面这个例子:
->
-> 
->
-> A, B, C, D是物品, 看右边的物品共现矩阵, 可以发现物品D与A、B、C的相似度比较大, 所以很有可能将D推荐给用过A、B、C的用户。 但是物品D与其他物品相似的原因是因为D是一件热门商品, 系统无法找出A、B、C之间相似性的原因是其特征太稀疏, 缺乏相似性计算的直接数据。 所以这就是协同过滤的天然缺陷:**推荐系统头部效应明显, 处理稀疏向量的能力弱**。
->
-> 为了解决这个问题, 同时增加模型的泛化能力,2006年,**矩阵分解技术(Matrix Factorization,MF**)被提出, 该方法在协同过滤共现矩阵的基础上, 使用更稠密的隐向量表示用户和物品, 挖掘用户和物品的隐含兴趣和隐含特征, 在一定程度上弥补协同过滤模型处理稀疏矩阵能力不足的问题。 具体细节等后面整理, 这里先铺垫一下。
-
-
-
-**3.上面介绍的相似度计算方法有什么优劣之处?**
-
-> cosine相似度还是比较常用的, 一般效果也不会太差, 但是对于评分数据不规范的时候, 也就是说, 存在有的用户喜欢打高分, 有的用户喜欢打低分情况的时候,有的用户喜欢乱打分的情况, 这时候consine相似度算出来的结果可能就不是那么准确了, 比如下面这种情况:
->
->
->
->这时候, 如果用余弦相似度进行计算, 会发现用户d和用户f比较相似, 而实际上, 如果看这个商品喜好的一个趋势的话, 其实d和e比较相近, 只不过e比较喜欢打低分, d比较喜欢打高分。 所以对于这种用户评分偏置的情况, 余弦相似度就不是那么好了, 可以考虑使用下面的皮尔逊相关系数。
-
-
-
-4.**协同过滤还存在其他什么缺陷?有什么比较好的思路可以解决(缓解)?**
-
-答案:
-
-> 协同过滤的特点就是完全没有利用到物品本身或者是用户自身的属性, 仅仅利用了用户与物品的交互信息就可以实现推荐,比较简单高效, 但这也是它的一个短板所在, 由于无法有效的引入用户年龄, 性别,商品描述,商品分类,当前时间,地点等一系列用户特征、物品特征和上下文特征, 这就造成了有效信息的遗漏,不能充分利用其它特征数据。
->
-> 为了解决这个问题, 在推荐模型中引用更多的特征,**推荐系统慢慢的从以协同过滤为核心到了以逻辑回归模型为核心**, 提出了能够综合不同类型特征的机器学习模型。
->
-> 演化图左边的时间线梳理完毕:
->
-> 
-
-
-
-
-
-### 11. 参考资料
-
-* [基于用户的协同过滤来构建推荐系统:https://mp.weixin.qq.com/s/ZtnaQrVIpVOPJpqMdLWOcw](https://mp.weixin.qq.com/s/ZtnaQrVIpVOPJpqMdLWOcw)
-* [协同过滤算法概述:https://chenk.tech/posts/8ad63d9d.html](https://chenk.tech/posts/8ad63d9d.html)
-* B站黑马推荐系统实战课程
-
-
-
-
-
diff --git a/docs/第一章 推荐系统基础/1.1 基础推荐算法/1.1.3 协同过滤-ItemCF.md b/docs/第一章 推荐系统基础/1.1 基础推荐算法/1.1.3 协同过滤-ItemCF.md
new file mode 100644
index 00000000..232055ea
--- /dev/null
+++ b/docs/第一章 推荐系统基础/1.1 基础推荐算法/1.1.3 协同过滤-ItemCF.md
@@ -0,0 +1,269 @@
+# 基于物品的协同过滤
+
+## 基本思想
+
+基于物品的协同过滤(ItemCF):
+
++ 预先根据所有用户的历史行为数据,计算物品之间的相似性。
++ 然后,把与用户喜欢的物品相类似的物品推荐给用户。
+
+举例来说,如果用户 1 喜欢物品 A ,而物品 A 和 C 非常相似,则可以将物品 C 推荐给用户1。ItemCF算法并不利用物品的内容属性计算物品之间的相似度, 主要通过分析用户的行为记录计算物品之间的相似度, 该算法认为, 物品 A 和物品 C 具有很大的相似度是因为喜欢物品 A 的用户极可能喜欢物品 C。
+
+
+
+## 计算过程
+
+基于物品的协同过滤算法和基于用户的协同过滤算法很像, 所以我们这里直接还是拿上面 Alice 的那个例子来看。
+
+
+
+如果想知道 Alice 对物品5打多少分, 基于物品的协同过滤算法会这么做:
+
++ 首先计算一下物品5和物品1, 2, 3, 4之间的相似性。
+
++ 在Alice找出与物品 5 最相近的 n 个物品。
+
++ 根据 Alice 对最相近的 n 个物品的打分去计算对物品 5 的打分情况。
+
+**手动计算:**
+
+1. 手动计算物品之间的相似度
+
+ >物品向量: $物品 1(3,4,3,1) ,物品2(1,3,3,5) ,物品3(2,4,1,5) ,物品4(3,3,5,2) ,物品5(3,5,41)$
+ >
+ >+ 下面计算物品 5 和物品 1 之间的余弦相似性:
+ > $$
+ > \operatorname{sim}(\text { 物品1, 物品5 })=\operatorname{cosine}(\text { 物品1, 物品5 } )=\frac{9+20+12+1}{\operatorname{sqrt}(9+16+9+1)+\operatorname{sqrt}(9+25+16+1)}
+ > $$
+ >
+ >+ 皮尔逊相关系数类似。
+ >
+
+2. 基于 `sklearn` 计算物品之间的皮尔逊相关系数:
+
+
+
+3. 根据皮尔逊相关系数, 可以找到与物品5最相似的2个物品是 item1 和 item4, 下面基于上面的公式计算最终得分:
+
+$$
+P_{Alice, 物品5}=\bar{R}_{物品5}+\frac{\sum_{k=1}^{2}\left(w_{物品5,物品 k}\left(R_{Alice, 物品k}-\bar{R}_{物品k}\right)\right)}{\sum_{k=1}^{2} w_{物品k, 物品5}} \\
+=\frac{13}{4}+\frac{0.97*(5-3.2)+0.58*(4-3.4)}{0.97+0.58}=4.6
+$$
+
+## ItemCF编程实现
+
+1. 构建物品-用户的评分矩阵
+
+ ```python
+ import numpy as np
+ import pandas as pd
+
+
+ def loadData():
+ items = {'A': {'Alice': 5.0, 'user1': 3.0, 'user2': 4.0, 'user3': 3.0, 'user4': 1.0},
+ 'B': {'Alice': 3.0, 'user1': 1.0, 'user2': 3.0, 'user3': 3.0, 'user4': 5.0},
+ 'C': {'Alice': 4.0, 'user1': 2.0, 'user2': 4.0, 'user3': 1.0, 'user4': 5.0},
+ 'D': {'Alice': 4.0, 'user1': 3.0, 'user2': 3.0, 'user3': 5.0, 'user4': 2.0},
+ 'E': {'user1': 3.0, 'user2': 5.0, 'user3': 4.0, 'user4': 1.0}
+ }
+ return items
+ ```
+
+2. 计算物品间的相似度矩阵
+
+ ```python
+ item_data = loadData()
+
+ similarity_matrix = pd.DataFrame(
+ np.identity(len(item_data)),
+ index=item_data.keys(),
+ columns=item_data.keys(),
+ )
+
+ # 遍历每条物品-用户评分数据
+ for i1, users1 in item_data.items():
+ for i2, users2 in item_data.items():
+ if i1 == i2:
+ continue
+ vec1, vec2 = [], []
+ for user, rating1 in users1.items():
+ rating2 = users2.get(user, -1)
+ if rating2 == -1:
+ continue
+ vec1.append(rating1)
+ vec2.append(rating2)
+ similarity_matrix[i1][i2] = np.corrcoef(vec1, vec2)[0][1]
+
+ print(similarity_matrix)
+ ```
+
+ ```
+ A B C D E
+ A 1.000000 -0.476731 -0.123091 0.532181 0.969458
+ B -0.476731 1.000000 0.645497 -0.310087 -0.478091
+ C -0.123091 0.645497 1.000000 -0.720577 -0.427618
+ D 0.532181 -0.310087 -0.720577 1.000000 0.581675
+ E 0.969458 -0.478091 -0.427618 0.581675 1.000000
+ ```
+
+3. 从 Alice 购买过的物品中,选出与物品 `E` 最相似的 `num` 件物品。
+
+ ```python
+ target_user = ' Alice '
+ target_item = 'E'
+ num = 2
+
+ sim_items = []
+ sim_items_list = similarity_matrix[target_item].sort_values(ascending=False).index.tolist()
+ for item in sim_items_list:
+ # 如果target_user对物品item评分过
+ if target_user in item_data[item]:
+ sim_items.append(item)
+ if len(sim_items) == num:
+ break
+ print(f'与物品{target_item}最相似的{num}个物品为:{sim_items}')
+ ```
+
+ ```
+ 与物品E最相似的2个物品为:['A', 'D']
+ ```
+
+4. 预测用户 Alice 对物品 `E` 的评分
+
+ ```python
+ target_user_mean_rating = np.mean(list(item_data[target_item].values()))
+ weighted_scores = 0.
+ corr_values_sum = 0.
+
+ target_item = 'E'
+ for item in sim_items:
+ corr_value = similarity_matrix[target_item][item]
+ user_mean_rating = np.mean(list(item_data[item].values()))
+
+ weighted_scores += corr_value * (item_data[item][target_user] - user_mean_rating)
+ corr_values_sum += corr_value
+
+ target_item_pred = target_user_mean_rating + weighted_scores / corr_values_sum
+ print(f'用户{target_user}对物品{target_item}的预测评分为:{target_item_pred}')
+ ```
+
+ ```
+ 用户 Alice 对物品E的预测评分为:4.6
+ ```
+
+# 协同过滤算法的权重改进
+
+* base 公式
+ $$
+ w_{i j}=\frac{|N(i) \bigcap N(j)|}{|N(i)|}
+ $$
+
+ + 该公式表示同时喜好物品 $i$ 和物品 $j$ 的用户数,占喜爱物品 $i$ 的比例。
+ + 缺点:若物品 $j$ 为热门物品,那么它与任何物品的相似度都很高。
+
+* 对热门物品进行惩罚
+ $$
+ w_{i j}=\frac{|N(i) \cap N(j)|}{\sqrt{|N(i)||N(j)|}}
+ $$
+
+
+ * 根据 base 公式在的问题,对物品 $j$ 进行打压。打压的出发点很简单,就是在分母再除以一个物品 $j$ 被购买的数量。
+ * 此时,若物品 $j$ 为热门物品,那么对应的 $N(j)$ 也会很大,受到的惩罚更多。
+
+* 控制对热门物品的惩罚力度
+ $$
+ w_{i j}=\frac{|N(i) \cap N(j)|}{|N(i)|^{1-\alpha}|N(j)|^{\alpha}}
+ $$
+
+ * 除了第二点提到的办法,在计算物品之间相似度时可以对热门物品进行惩罚外。
+ * 可以在此基础上,进一步引入参数 $\alpha$ ,这样可以通过控制参数 $\alpha$来决定对热门物品的惩罚力度。
+
+* 对活跃用户的惩罚
+
+ * 在计算物品之间的相似度时,可以进一步将用户的活跃度考虑进来。
+ $$
+ w_{i j}=\frac{\sum_{\operatorname{\text {u}\in N(i) \cap N(j)}} \frac{1}{\log 1+|N(u)|}}{|N(i)|^{1-\alpha}|N(j)|^{\alpha}}
+ $$
+
+ + 对于异常活跃的用户,在计算物品之间的相似度时,他的贡献应该小于非活跃用户。
+
+# 协同过滤算法的问题分析
+
+协同过滤算法存在的问题之一就是泛化能力弱:
+
++ 即协同过滤无法将两个物品相似的信息推广到其他物品的相似性上。
++ 导致的问题是**热门物品具有很强的头部效应, 容易跟大量物品产生相似, 而尾部物品由于特征向量稀疏, 导致很少被推荐**。
+
+比如下面这个例子:
+
+
+
++ 左边矩阵中,$A, B, C, D$ 表示的是物品。
++ 可以看出,$D $ 是一件热门物品,其与 $A、B、C$ 的相似度比较大。因此,推荐系统更可能将 $D$ 推荐给用过 $A、B、C$ 的用户。
++ 但是,推荐系统无法找出 $A,B,C$ 之间相似性的原因是交互数据太稀疏, 缺乏相似性计算的直接数据。
+
+所以这就是协同过滤的天然缺陷:**推荐系统头部效应明显, 处理稀疏向量的能力弱**。
+
+为了解决这个问题, 同时增加模型的泛化能力。2006年,**矩阵分解技术(Matrix Factorization, MF**)被提出:
+
++ 该方法在协同过滤共现矩阵的基础上, 使用更稠密的隐向量表示用户和物品, 挖掘用户和物品的隐含兴趣和隐含特征。
++ 在一定程度上弥补协同过滤模型处理稀疏矩阵能力不足的问题。
+
+# 课后思考
+
+1. **什么时候使用UserCF,什么时候使用ItemCF?为什么?**
+
+> (1)UserCF
+>
+> + 由于是基于用户相似度进行推荐, 所以具备更强的社交特性, 这样的特点非常适于**用户少, 物品多, 时效性较强的场合**。
+>
+> + 比如新闻推荐场景, 因为新闻本身兴趣点分散, 相比用户对不同新闻的兴趣偏好, 新闻的及时性,热点性往往更加重要, 所以正好适用于发现热点,跟踪热点的趋势。
+> + 另外还具有推荐新信息的能力, 更有可能发现惊喜, 因为看的是人与人的相似性, 推出来的结果可能更有惊喜,可以发现用户潜在但自己尚未察觉的兴趣爱好。
+>
+> (2)ItemCF
+>
+> + 这个更适用于兴趣变化较为稳定的应用, 更接近于个性化的推荐, 适合**物品少,用户多,用户兴趣固定持久, 物品更新速度不是太快的场合**。
+> + 比如推荐艺术品, 音乐, 电影。
+
+
+
+2.**协同过滤在计算上有什么缺点?有什么比较好的思路可以解决(缓解)?**
+
+> 该问题答案参考上一小节的**协同过滤算法的问题分析**。
+
+
+
+**3.上面介绍的相似度计算方法有什么优劣之处?**
+
+> cosine相似度计算简单方便,一般较为常用。但是,当用户的评分数据存在 bias 时,效果往往不那么好。
+>
+> + 简而言之,就是不同用户评分的偏向不同。部分用户可能乐于给予好评,而部分用户习惯给予差评或者乱评分。
+> + 这个时候,根据cosine 相似度计算出来的推荐结果效果会打折扣。
+>
+> 举例来说明,如下图(`X,Y,Z` 表示物品,`d,e,f`表示用户):
+>
+> 
+>
+> + 如果使用余弦相似度进行计算,用户 d 和 e 之间较为相似。但是实际上,用户 d 和 f 之间应该更加相似。只不过由于 d 倾向于打高分,e 倾向于打低分导致二者之间的余弦相似度更高。
+> + 这种情况下,可以考虑使用皮尔逊相关系数计算用户之间的相似性关系。
+
+
+
+4.**协同过滤还存在其他什么缺陷?有什么比较好的思路可以解决(缓解)?**
+
+> + 协同过滤的优点就是没有使用更多的用户或者物品属性信息,仅利用用户和物品之间的交互信息就能完成推荐,该算法简单高效。
+> + 但这也是协同过滤算法的一个弊端。由于未使用更丰富的用户和物品特征信息,这也导致协同过滤算法的模型表达能力有限。
+> + 对于该问题,逻辑回归模型(LR)可以更好地在推荐模型中引入更多特征信息,提高模型的表达能力。
+
+
+
+# 参考资料
+
+* [基于用户的协同过滤来构建推荐系统:https://mp.weixin.qq.com/s/ZtnaQrVIpVOPJpqMdLWOcw](https://mp.weixin.qq.com/s/ZtnaQrVIpVOPJpqMdLWOcw)
+* [协同过滤算法概述:https://chenk.tech/posts/8ad63d9d.html](https://chenk.tech/posts/8ad63d9d.html)
+* B站黑马推荐系统实战课程
+
+
+
+
+
diff --git a/docs/第一章 推荐系统基础/1.1 基础推荐算法/1.1.3 矩阵分解.md b/docs/第一章 推荐系统基础/1.1 基础推荐算法/1.1.3 矩阵分解.md
deleted file mode 100644
index 126648aa..00000000
--- a/docs/第一章 推荐系统基础/1.1 基础推荐算法/1.1.3 矩阵分解.md
+++ /dev/null
@@ -1,311 +0,0 @@
-### 1. 隐语义模型与矩阵分解
-
-协同过滤算法的特点就是完全没有利用到物品本身或者是用户自身的属性, 仅仅利用了用户与物品的交互信息就可以实现推荐,是一个可解释性很强, 非常直观的模型, 但是也存在一些问题, 第一个就是处理稀疏矩阵的能力比较弱, 所以**为了使得协同过滤更好处理稀疏矩阵问题, 增强泛化能力**, 从协同过滤中衍生出矩阵分解模型(Matrix Factorization,MF)或者叫隐语义模型, 两者差不多说的一个意思, 就是在协同过滤共现矩阵的基础上, 使用更稠密的隐向量表示用户和物品, 挖掘用户和物品的隐含兴趣和隐含特征, 在一定程度上弥补协同过滤模型处理稀疏矩阵能力不足的问题。
-
-
-
-### 2. 隐语义模型
-
-隐语义模型最早在文本领域被提出,用于找到文本的隐含语义。在2006年, 被用于推荐中, **它的核心思想是通过隐含特征(latent factor)联系用户兴趣和物品(item), 基于用户的行为找出潜在的主题和分类, 然后对item进行自动聚类,划分到不同类别/主题(用户的兴趣)**。
-
-这么说可能有点抽象,所以下面拿项亮老师《推荐系统实践》里面的那个例子看一下:
->如果我们知道了用户A和用户B两个用户在豆瓣的读书列表, 从他们的阅读列表可以看出,用户A的兴趣涉及侦探小说、科普图书以及一些计算机技术书, 而用户B的兴趣比较集中在数学和机器学习方面。 那么如何给A和B推荐图书呢?
先说说协同过滤算法, 这样好对比不同:
->* 对于UserCF,首先需要找到和他们看了同样书的其他用户(兴趣相似的用户),然后给他们推荐那些用户喜欢的其他书。
->* 对于ItemCF,需要给他们推荐和他们已经看的书相似的书,比如作者B看了很多关于数据挖掘的书,可以给他推荐机器学习或者模式识别方面的书。
->
->
而如果是隐语义模型的话, 它会先通过一些角度把用户兴趣和这些书归一下类, 当来了用户之后, 首先得到他的兴趣分类, 然后从这个分类中挑选他可能喜欢的书籍。
-
-
-
-这里就看到了隐语义模型和协同过滤的不同, 这里说的角度其实就是这个隐含特征, 比如书籍的话它的内容, 作者, 年份, 主题等都可以算隐含特征,如果这个例子还不是很清晰的话, 那么下面再举个更为具体的例子, 看看是如何通过隐含特征来划分开用户兴趣和物品的。但是在这之前, 相信通过上面这个例子, 我们已经隐隐约约感受到了协同过滤和隐语义模型的区别了, 下面放上王喆老师《深度学习推荐系统》的一个原理图作为对比, 区别简直一目了然:
-
-
-
-
-
-我们下面拿一个音乐评分的例子来具体看一下隐特征矩阵的含义。
-
-假设每个用户都有自己的听歌偏好, 比如A喜欢带有**小清新的**, **吉他伴奏的**, **王菲**的歌曲,如果一首歌正好**是王菲唱的, 并且是吉他伴奏的小清新**, 那么就可以将这首歌推荐给这个用户。 也就是说是**小清新, 吉他伴奏, 王菲**这些元素连接起了用户和歌曲。 当然每个用户对不同的元素偏好不同, 每首歌包含的元素也不一样, 所以我们就希望找到下面的两个矩阵:
-1. **潜在因子—— 用户矩阵Q**
- 这个矩阵表示不同用户对于不同元素的偏好程度, 1代表很喜欢, 0代表不喜欢, 比如下面这样:
-
-
-
-
-
-
-
-
-
+
+## 音乐评分实例
+
+假设每个用户都有自己的听歌偏好, 比如用户 A 喜欢带有**小清新的**, **吉他伴奏的**, **王菲**的歌曲,如果一首歌正好**是王菲唱的, 并且是吉他伴奏的小清新**, 那么就可以将这首歌推荐给这个用户。 也就是说是**小清新, 吉他伴奏, 王菲**这些元素连接起了用户和歌曲。
+
+当然每个用户对不同的元素偏好不同, 每首歌包含的元素也不一样, 所以我们就希望找到下面的两个矩阵:
+
+1. 潜在因子—— 用户矩阵Q
+ 这个矩阵表示不同用户对于不同元素的偏好程度, 1代表很喜欢, 0代表不喜欢, 比如下面这样:
+
+
+
+
+
+
+
+
+