【机器学习14】深度学习推荐系统、降维技术PCA
文章目录
- 一 推荐系统的高级应用与方法
- 1.1 利用特征向量寻找相似物品
- 1.2 协同过滤的局限性与混合方法
- 1.3 协同过滤与基于内容的过滤对比
- 1.4 基于深度学习的推荐系统
- 1.5 大规模推荐系统:召回与排序
- 1.6 TensorFlow实现概览
- 二 降维与主成分分析(PCA)
- 2.1 降维的动机
- 2.2 PCA的应用:数据可视化
- 2.3 主成分分析 (PCA) 算法
- 2.4 PCA与线性回归的区别
- 2.5 数据重建与实践
- 2.6 PCA的应用总结
视频链接
吴恩达机器学习p120-128
一 推荐系统的高级应用与方法
在上一篇文章中,我们学习了协同过滤的基础知识。现在,我们将探讨如何利用其学习成果,并介绍一种更现代的、基于深度学习的推荐系统架构。
1.1 利用特征向量寻找相似物品
![[在此处插入图片1]](https://i-blog.csdnimg.cn/direct/f37fd1a64f714f3ebb16db85e44c1c3a.png)
协同过滤算法的一个强大副产品是,它为每个物品(如电影i)学习到了一个特征向量x^(i)。
- 特征的可解释性:虽然我们可能将这些特征维度想象成“浪漫”、“动作”等,但算法自动学习到的特征通常是抽象的,其具体含义很难直接解释。
- 寻找相似物品:尽管特征难以解释,但它们在向量空间中的位置是有意义的。要找到与物品
i相似的其他物品k,我们可以在特征空间中寻找与x^(i)最接近的向量x^(k)。 - 计算方法:通过计算两个物品特征向量之间的欧氏距离的平方
||x^(k) - x^(i)||^2。这个值最小的物品k,就是与物品i最相似的。
1.2 协同过滤的局限性与混合方法
![[在此处插入图片2]](https://i-blog.csdnimg.cn/direct/7dbc27b56f0548a0a265af1804d0cb5b.png)
传统的协同过滤算法存在一些固有的局限性,最主要的就是冷启动问题(Cold start problem):
- 新物品问题:如何为一个几乎没有用户评分的新物品进行推荐?
- 新用户问题:如何为一个几乎没有评分记录的新用户提供合理的推荐?
解决方案:引入额外信息(side information)来辅助推荐,这通常被称为混合推荐系统。
- 物品的额外信息:电影的类型、主演、制片公司等。
- 用户的额外信息:用户的年龄、性别、地理位置等人口统计学信息,或用户明确表示的偏好。
1.3 协同过滤与基于内容的过滤对比
![[在此处插入图片3]](https://i-blog.csdnimg.cn/direct/50c63fefbbc54db08c080b2d6033934f.png)
让我们再次明确两种核心推荐思想的区别:
- 协同过滤 (Collaborative Filtering):其核心是“协同”。它向你推荐物品,是基于那些与你品味相似的用户(即给出了与你相似评分的用户)也喜欢的物品。它不依赖于物品或用户的固有特征。
- 基于内容的过滤 (Content-based Filtering):其核心是“匹配”。它向你推荐物品,是基于用户特征和物品特征之间的匹配程度。它需要明确的特征作为输入。
1.4 基于深度学习的推荐系统
现代推荐系统通常会结合协同过滤和基于内容的思想,并利用深度学习来学习用户和物品的复杂特征表示。
![[在此处插入图片4]](https://i-blog.csdnimg.cn/direct/284345bacbaf4b02a7581f34c2eae922.png)
1. 定义用户与物品特征
- 我们首先为用户和物品收集丰富的原始特征,形成用户特征向量
x_u和物品特征向量x_m。 - 用户特征示例:年龄、性别、国家、观看过的电影列表、对各类型电影的平均评分等。
- 物品特征示例:年份、类型、影评文本、平均评分等。
- 这些原始特征向量的维度和类型都可以是多样的。
![[在此处插入图片5]](https://i-blog.csdnimg.cn/direct/a1fd0ae7e54c4b63b4b96c0ec9ad4460.png)
2. 学习特征向量(Embedding)
- 之前,我们的预测是基于
w^(j)•x^(i) + b^(j)。现在,我们引入一个更强大的思路。 - 我们不再直接使用原始特征
x_u和x_m,而是分别学习出两个新的、低维度的特征向量(或称嵌入向量, embedding):v_u^(j):代表用户j的偏好,从原始用户特征x_u^(j)计算得出。v_m^(i):代表电影i的属性,从原始电影特征x_m^(i)计算得出。
- 例如,学习到的
v_u可能表示用户对“喜欢”的各种抽象概念的倾向,而v_m表示电影在“浪漫”、“动作”等抽象概念上的得分。
![[在此处插入图片6]](https://i-blog.csdnimg.cn/direct/cb122a25d5ec4119a2c5a9653b1ee27a.png)
3. 神经网络架构
- 我们使用两个独立的神经网络来学习这两个嵌入向量:
- 用户网络 (User network):输入是原始用户特征
x_u,经过多层神经网络处理后,输出一个固定长度(例如32维)的嵌入向量v_u。 - 物品网络 (Movie network):输入是原始物品特征
x_m,经过另一个独立的神经网络处理后,输出一个同样长度(32维)的嵌入向量v_m。
- 用户网络 (User network):输入是原始用户特征
- 预测:用户
j对电影i的评分(或交互概率)可以通过计算两个嵌入向量的点积v_u^(j) • v_m^(i)来预测。对于二元分类问题,这个点积结果会被送入一个Sigmoid函数。
![[在此处插入图片7]](https://i-blog.csdnimg.cn/direct/f004641c7d0540a28c47cbe7af37c87d.png)
4. 整体模型与代价函数
- 整个模型由用户网络、物品网络以及最后的点积预测层组成。
- 代价函数:
J = Σ [ (v_u^(j) • v_m^(i) - y(i,j))² ] + NN regularization term- 代价函数的主体是所有已知评分的预测误差(例如均方误差)。求和
Σ只对那些有评分的(i,j)对进行。 - 同时,还需要加上神经网络的正则化项(如L2正则化)来防止过拟合。
![[在此处插入图片8]](https://i-blog.csdnimg.cn/direct/731b1b9536c6402b82470ca86ab03ab2.png)
5. 寻找相似物品
- 在模型训练完成后,我们得到了每个物品
i的嵌入向量v_m^(i)。 - 与之前的协同过滤一样,我们可以通过计算嵌入向量之间的距离
||v_m^(k) - v_m^(i)||^2来找到与物品i最相似的物品k。 - 由于这些计算不依赖于具体用户,可以预先计算并存储好所有物品之间的相似度,以便快速查询。
1.5 大规模推荐系统:召回与排序
![[在此处插入图片9]](https://i-blog.csdnimg.cn/direct/04521252083b43eea52aeffea075632f.png)
当物品库非常庞大时(例如上百万首歌曲或商品),为用户与每一个物品都计算一次预测得分是不可行的。工业界通常采用一个两阶段的流程。
![[在此处插入图片10]](https://i-blog.csdnimg.cn/direct/563697f57b2e4244a1f89574451edf81.png)
第一阶段:召回 (Retrieval)
- 目标:从海量的物品库中,快速筛选出一个规模较小(例如几百个)的候选物品列表。这个阶段追求的是“快”和“全”,即快速找出所有可能相关的物品,宁可错杀一千,不可放过一个。
- 常用方法:
- 基于相似度的召回:例如,对于用户最近看过的10部电影,分别找出与它们最相似的10部电影。
- 基于规则的召回:例如,找出用户最常看的3个电影类型中,排名最高的10部电影。
- 其他策略:例如,推荐本地区的热门电影。
- 最后,将所有召回策略生成的物品合并,并移除用户已经看过或购买过的物品以及重复项。
![[在此处插入图片11]](https://i-blog.csdnimg.cn/direct/92a9c78fb76e48b89687d5352b989126.png)
第二阶段:排序 (Ranking)
- 目标:对召回阶段生成的几百个候选物品,使用我们训练好的、复杂的深度学习模型进行精准的预测打分。
- 流程:
- 将候选列表中的每一个物品,与当前用户的特征一起,输入到我们的双塔神经网络模型中。
- 模型会为每个候选物品计算出一个精确的预测得分(例如,用户喜欢的概率)。
- 根据这个得分对候选列表进行降序排序。
- 将排序最高的若干个物品展示给用户。
![[在此处插入图片12]](https://i-blog.csdnimg.cn/direct/3ed538107d034aa1ad052ef5634c0f45.png)
召回与排序的权衡
- 召回的物品越多,最终推荐效果可能会越好,但排序阶段的计算成本也越高,导致推荐变慢。
- 为了优化这个权衡,可以进行离线实验,分析增加召回数量(例如从100增加到500)是否能显著提升最终推荐列表的质量(例如,预测
y=1的物品比例是否更高)。
1.6 TensorFlow实现概览
![[在此处插入图片13]](https://i-blog.csdnimg.cn/direct/6a7da16ebc3d46fd86d6c7b8126cf05c.png)
使用TensorFlow中的Keras API可以模块化地、清晰地构建出我们所讨论的用于推荐系统的双塔神经网络模型。下面是一个详细的代码实现与解释。
1. 定义基础网络 (User and Movie Networks)
首先,我们为用户和电影分别定义独立的神经网络。这两个网络的作用是将高维、异构的原始特征,转换为低维、稠密的嵌入向量 v_u 和 v_m。
import tensorflow as tf
from tensorflow import keras# 定义用户网络的结构
user_NN = tf.keras.models.Sequential([tf.keras.layers.Dense(256, activation='relu'),tf.keras.layers.Dense(128, activation='relu'),tf.keras.layers.Dense(32) # 输出32维的用户嵌入向量
])# 定义物品(电影)网络的结构
item_NN = tf.keras.models.Sequential([tf.keras.layers.Dense(256, activation='relu'),tf.keras.layers.Dense(128, activation='relu'),tf.keras.layers.Dense(32) # 输出32维的物品嵌入向量
])
代码解释:
tf.keras.models.Sequential([...]): 我们使用Sequential模型来构建一个线性的层堆栈。这是一个简单快捷的建网方式。tf.keras.layers.Dense(units, activation='relu'):Dense层是全连接层,是标准的神经网络层。- 第一个参数(如
256)是该层中的神经元数量。 activation='relu'指定使用ReLU作为激活函数,它能为模型引入非线性,增强模型的表达能力。
- 第一个参数(如
tf.keras.layers.Dense(32): 网络的最后一层不使用激活函数(或可视为使用线性激活)。它的输出就是我们最终想要的32维嵌入向量v_u或v_m。
2. 构建完整的双塔模型
接下来,我们将上述两个基础网络组合起来,构建一个接收用户和物品两路输入,并计算它们匹配度的完整模型。
# 1. 定义模型的输入层
num_user_features = 100 # 假设用户原始特征有100维
num_item_features = 80 # 假设物品原始特征有80维
input_user = tf.keras.layers.Input(shape=(num_user_features,))
input_item = tf.keras.layers.Input(shape=(num_item_features,))# 2. 将输入连接到对应的基础网络,得到嵌入向量
vu = user_NN(input_user)
vm = item_NN(input_item)# (可选步骤) 对嵌入向量进行L2归一化,这有助于稳定训练
vu = tf.linalg.l2_normalize(vu, axis=1)
vm = tf.linalg.l2_normalize(vm, axis=1)# 3. 计算两个嵌入向量的点积作为预测输出
output = tf.keras.layers.Dot(axes=1)([vu, vm])# 4. 定义完整的模型
model = tf.keras.Model(inputs=[input_user, input_item], outputs=output)# 5. 编译模型
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.01),loss=tf.keras.losses.MeanSquaredError())
代码解释:
tf.keras.layers.Input(shape=...): 我们使用Input层来定义模型期望接收的数据的形状。这里我们定义了两个独立的输入:一个给用户特征,一个给物品特征。vu = user_NN(input_user): 这被称为函数式API的用法。我们将input_user这个“符号张量”作为输入,传递给之前定义好的user_NN网络,其输出vu就是用户嵌入向量。tf.linalg.l2_normalize(...): 这一步对嵌入向量进行L2归一化,使其长度变为1。这样做可以使模型更关注向量的方向(代表偏好或属性的方向)而不是其大小,有时能提升模型性能。tf.keras.layers.Dot(axes=1):Dot层专门用于计算输入的点积。axes=1指定沿着特征维度进行点积运算。我们将vu和vm作为列表传入,该层会计算它们的点积,其结果就是模型的最终预测评分。tf.keras.Model(...): 我们使用Model类来定义一个具有多输入、单输出的复杂模型。inputs参数接收一个输入列表,outputs参数指定模型的最终输出。model.compile(...): 在训练之前,我们必须“编译”模型。optimizer: 指定用于更新模型参数的优化算法,Adam是一个常用且高效的选择。loss: 指定代价函数。对于评分预测这样的回归任务,MeanSquaredError(均方误差)是一个标准的选择。
二 降维与主成分分析(PCA)
现在我们转向另一个重要的无监督学习主题:降维(Dimensionality Reduction)。
2.1 降维的动机
![[在此处插入图片14]](https://i-blog.csdnimg.cn/direct/dc33232bc1204c25acc67dc95ba1d998.png)
![[在此处插入图片15]](https://i-blog.csdnimg.cn/direct/9baa8011b41744d8bfefe1d31abf9f23.png)
在很多数据集中,特征之间存在高度的相关性或冗余。
- 例如,在汽车测量数据中,特征
x₁(车长)和x₂(车宽)都反映了汽车的“大小”。x₁的变化范围远大于x₂。如果目标是减少特征数量,一个简单粗暴的方法是直接丢掉方差较小的特征x₂,只保留x₁。
![[在此处插入图片16]](https://i-blog.csdnimg.cn/direct/3f45b1aec76e46f58632351ede87e3cb.png)
- 然而,直接丢弃特征会损失信息。一个更好的方法是创造一个新的特征
z,它能同时捕捉x₁(长度)和x₂(高度)的信息。例如,我们可以找到一个新的z轴,它代表了汽车的整体“尺寸”。这样,我们就成功地将2个特征压缩为了1个特征。 - 降维的目标:用更少的特征来表示数据,同时尽可能多地保留原始数据中的信息。
2.2 PCA的应用:数据可视化
![[在此处插入图片17]](https://i-blog.csdnimg.cn/direct/b9f34f07b2044feeb10083b3addee763.png)
![[在此处插入图片18]](https://i-blog.csdnimg.cn/direct/3a8b84298f8e41bab3aefa21d362de48.png)
![[在此处插入图片19]](https://i-blog.csdnimg.cn/direct/b8ff1d6ac7a647b2bb9cb6fa8cdc4572.png)
![[在此处插入图片20]](https://i-blog.csdnimg.cn/direct/2240e6f62eab4643bc26f1f5ccee0a49.png)
降维的一个主要应用是数据可视化。
- 人类的视觉系统很难理解超过三维的数据。
- 如果我们的数据集有50个特征,我们无法直接将其画出来。
- 通过使用降维算法(如PCA),我们可以将50个特征压缩为2个新的特征
z₁和z₂。 - 然后,我们可以在一个二维平面上绘制这些新的
(z₁, z₂)坐标,从而观察数据的分布、聚类和异常点,获得对高维数据的直观理解。
2.3 主成分分析 (PCA) 算法
PCA是最常用和最基础的降维算法。
![[在此处插入图片21]](https://i-blog.csdnimg.cn/direct/d199b3a40d404037ac489321e822eed1.png)
1. 数据预处理
- 在运行PCA之前,必须对数据进行预处理。
- 均值归一化 (Mean normalization):将每个特征的均值调整为0。
- 特征缩放 (Feature scaling):将每个特征的取值范围缩放到相似的区间。
![[在此处插入图片22]](https://i-blog.csdnimg.cn/direct/b18a65b6f37a4a768b3d20dc26b4259e.png)
![[在此处插入图片23]](https://i-blog.csdnimg.cn/direct/7afb56cb0c4e441a92fe200b71fd97f1.png)
![[在此处插入图片24]](https://i-blog.csdnimg.cn/direct/aba45fcc7d194195826fb6e5ef445705.png)
2. 寻找主成分
- PCA的核心思想是,寻找一个新的坐标轴(或方向),称为主成分(Principal Component)。
- 这个主成分的方向被选择为能使原始数据投影(project)到该轴上后,投影点的方差最大的方向。
- 最大化方差的意义:方差越大,意味着数据点在投影后散布得越开,这等同于保留了原始数据中最多的信息。如果投影后所有点都挤在一起(方差小),则意味着大量原始信息丢失了。
![[在此处插入图片25]](https://i-blog.csdnimg.cn/direct/34489fffb2154914b224d535149e9d2a.png)
![[在此处插入图片26]](https://i-blog.csdnimg.cn/direct/2658b4a228ea48f391f8597ffbcebe8e.png)
3. 计算新坐标
- 找到了主成分轴(由一个单位向量表示)后,原始数据点
x在这个新轴上的坐标z,可以通过计算x与该单位向量的点积(dot product得到。
![[在此处插入图片27]](https://i-blog.csdnimg.cn/direct/4c4f34ea6f1a461fae22ba3e5e53bafc.png)
4. 多个主成分
- 如果想将数据降到
k维,PCA会找到k个相互正交(perpendicular的主成分。 - 第一个主成分
z₁是最大化投影方差的方向。 - 第二个主成分
z₂是在与z₁正交的所有方向中,最大化投影方差的方向,以此类推。
2.4 PCA与线性回归的区别
![[在此处插入图片28]](https://i-blog.csdnimg.cn/direct/04d66592def542d8a945b95292889b0e.png)
![[在此处插入图片29]](https://i-blog.csdnimg.cn/direct/0c2ff5037c3c4d71996029fa422e7dc1.png)
PCA有时会被误认为是线性回归,但它们是完全不同的算法。
- 线性回归:
- 是一个监督学习算法,有输入
x和输出y。 - 目标是找到一条线,最小化预测值与真实值
y之间的垂直距离(预测误差)。
- 是一个监督学习算法,有输入
- PCA:
- 是一个无监督学习算法,只有输入数据
x,没有y。 - 目标是找到一个低维度的子空间(由主成分轴定义),最小化原始数据点到这个子空间的正交投影距离,其等价于最大化投影后的方差。
- 是一个无监督学习算法,只有输入数据
2.5 数据重建与实践
![[在此处插入图片30]](https://i-blog.csdnimg.cn/direct/a7f1427413ff452d8485b81a81ca5bbd.png)
从低维重建高维数据
- 降维是一个有信息损失的过程,但我们也可以从降维后的坐标
z近似地重建(reconstruct出原始的高维坐标。 - 方法是将降维后的坐标值
z乘以其对应的主成分单位向量。
![[在此处插入图片31]](https://i-blog.csdnimg.cn/direct/a6c405d6a65c407eaa8f297d688b234c.png)
![[在此处插入图片32]](https://i-blog.csdnimg.cn/direct/12fb169ccdc44a9595906d3226c4b16f.png)
![[在此处插入图片33]](https://i-blog.csdnimg.cn/direct/709dadf836ae4fef8fabc1ac01ed4644.png)
Scikit-learn库提供了非常易于使用的PCA实现。下面我们将通过一个具体的例子,来展示如何使用它,并详细解释每一步的含义和结果。
准备工作与数据
首先,导入必要的库并创建示例数据。
import numpy as np
from sklearn.decomposition import PCA# 创建一个2D的示例数据集X,包含6个样本点
X = np.array([[1, 1], [2, 1], [3, 2], [-1, -1], [-2, -1], [-3, -2]])
案例1:将数据从2D降至1D
我们的目标是找到一个最佳的一维直线来表示这6个点。
# 1. 初始化PCA模型,指定要降到的维度 n_components=1
pca_1 = PCA(n_components=1)# 2. 拟合数据:让PCA算法学习数据的结构
pca_1.fit(X)# 3. 查看方差解释比例
print(f"Explained variance ratio (1 component): {pca_1.explained_variance_ratio_}")# 4. 转换数据:将原始数据投影到新的1D主成分轴上
X_trans_1 = pca_1.transform(X)
print("Transformed data (1D):\n", X_trans_1)# 5. (可选) 重建数据:从1D数据近似恢复回2D
X_reconstructed_1 = pca_1.inverse_transform(X_trans_1)
print("Reconstructed data from 1D:\n", X_reconstructed_1)
代码解释与结果分析:
pca_1 = PCA(n_components=1): 创建一个PCA类的实例。n_components=1告诉算法,我们的目标是找到1个主成分,即将数据降到1维。pca_1.fit(X): 这是PCA的核心步骤。.fit()方法会执行以下操作:- 对数据
X进行均值归一化。 - 计算数据的协方差矩阵。
- 找到能最大化数据投影后方差的那个方向(即第一个主成分),并将其存储在对象内部。
- 对数据
pca_1.explained_variance_ratio_: 这是一个非常有用的属性。它返回一个列表,表示每个主成分所能解释的原始数据方差的比例。- 输出
[0.992]意味着我们找到的这一个主成分就捕捉了原始2D数据中99.2%的信息(方差)。这是一个非常高的比例,说明降到1维是合理且有效的。
- 输出
X_trans_1 = pca_1.transform(X):.transform()方法使用已经拟合好的主成分,将原始数据X的每个点都投影到这个新的1D轴上。- 输出
X_trans_1是一个(6, 1)的数组,每一行只有一个数值,这个数值就是原始2D点在新1D坐标系中的坐标。
- 输出
X_reconstructed_1 = pca_1.inverse_transform(X_trans_1):.inverse_transform()是一个逆向操作。它将1D的坐标“投射”回原始的2D空间。- 输出
X_reconstructed_1是一个(6, 2)的数组。注意,它只是原始数据X的一个近似,因为在降维过程中,那0.8%的方差信息已经丢失了。所有重建后的点都精确地落在那条由第一个主成分定义的直线上。
- 输出
案例2:将数据从2D转换到新的2D坐标系
当n_components等于原始数据的维度时,PCA不会减少维度,而是会找到一组新的正交基(坐标系),即所有主成分。
# 1. 初始化PCA模型,n_components=2
pca_2 = PCA(n_components=2)# 2. 拟合数据
pca_2.fit(X)# 3. 查看方差解释比例
print(f"Explained variance ratio (2 components): {pca_2.explained_variance_ratio_}")# 4. 转换数据到新的2D坐标系
X_trans_2 = pca_2.transform(X)
print("Transformed data (2D):\n", X_trans_2)
代码解释与结果分析:
pca_2 = PCA(n_components=2): 创建PCA实例,目标是找到2个主成分。pca_2.explained_variance_ratio_:- 输出
[0.992, 0.008](数值可能略有不同) 意味着:- 第一个主成分(新坐标系的
z₁轴)解释了99.2%的方差。 - 第二个主成分(新坐标系的
z₂轴,与z₁正交)解释了剩余的0.8%的方差。
- 第一个主成分(新坐标系的
- 输出
X_trans_2 = pca_2.transform(X):- 输出
X_trans_2是一个(6, 2)的数组。这不再是降维,而是对原始数据的一次坐标系旋转。 - 第一列是每个点在
z₁轴上的坐标,第二列是每个点在z₂轴上的坐标。因为没有信息丢失,这次的inverse_transform可以完美地恢复出原始数据X。
- 输出
2.6 PCA的应用总结
![[在此处插入图片34]](https://i-blog.csdnimg.cn/direct/066d73479f8a4f1c918c67fda2b725dd.png)
PCA的主要应用场景:
- 数据可视化:将数据降至2维或3维进行绘图,是其最主要和最可靠的应用。
- 数据压缩:将数据降维以减少存储空间或传输成本。
- 加速监督学习:在将数据喂给一个监督学习模型之前,先用PCA进行降维,可以减少模型的训练时间。但这种做法需要谨慎,应优先考虑在原始数据上训练,只有在性能瓶颈确实存在时才尝试使用PCA。
