原创

AI-无监督学习-第三课


AI-无监督学习-第三课

无监督学习

Unsupervised Learning

聚类算法

Clustering Algorithm

定义

聚类算法查看大量数据点, 并自动找到彼此相关或相似的数据点.

聚类算法对比之前的二元分类监督学习算法

监督学习的训练集上包含输入特征x和标签y(目标输出)的, 而在无监督学习中, 数据集只有特征x, 没有标签y.

在无监督学习中, 要求算法找到数据集中的一些有意义的东西或结构, 而聚类算法在数据中寻找一种特定类型的结构, 查看数据集并尝试把相似或相关的数据分组到同一集群中.

聚类算法的应用有:

  1. 搜集相似的新闻
  2. 市场或用户的划分
  3. 分析DNA数据, 查看不同个体的基因表达数据, 并尝试将它们分组为表现出相似特征的人.
  4. 天文学和太空探索

K-means算法

描述

K-mean算法的第一件事上随机猜测可能要求它找到的两个聚类的中心位置, 如图

image-20230826184937215

如上图所示, 使用红色x和蓝色x表示要查找的两个不同集群的中心(簇质心).

K-means算法会重复做两件不同的事情, 第一步是将数据点分配给簇质心(cluster centroid), 第二步是移动簇质心.

image-20230826185637342

第一步如上图所示, 算法会遍历每个数据点, 并查看数据点上更接近红色x还是蓝色x. 然后把这些点的每一个分配给它更接近的簇质心.

image-20230826185918549

第二步如上图所示, 算法会查看所有的红色(或蓝色)点(颜色代表靠近哪个簇质心)并取它们的平均值, 然后将红色(或蓝色)的簇质心移动到该平均值位置上.

现在红色(或蓝色)簇质心有了新位置, 将再次查看所有的训练示例, 回到第一步, 检查数据点更接近红色或蓝色的簇质心, 然后进行第二步.

image-20230826190747275

如果继续执行这两个步骤, 发现簇质心的位置没有更多的变化, 这意味着K-means算法已经收敛.

公式

如下图所示, 示例中使用的簇质心数量k为2, 数据的特征数量n为2

image-20230826192334770

第一步是随机初始化K个簇质心(本示例中K=2), 簇质心的数据和训练示例数据x是具有相同维度的向量(本示例是两个特征, 是二维向量).

然后K-means算法将重复进行下面两个步骤:

  1. 将训练数据点分配给簇质心, 伪代码如下:

    for i = 1 to m:

    ​ $c^{(i)} = min_{k}||x^{(i)}-\mu_{k}||$

    即遍历数据集, 通过计算L2范数(L2 norm)$c^{(i)}$找到使之最小的k值(簇质心下标), 此时该k值对应于最接近训练示例$x^{(i)}$的簇质心$\mu_{k}$.

    实现该算法时, 使用最小化平方距离会更方便一点, 因为最小平方距离的簇质心与最小距离的簇质心相同, 公式如下

    $c^{(i)} = min_{k}||x^{(i)}-\mu_{k}||^2$

  2. 移动簇质心, 把簇质心的位置更新为分配给该集群k的点的平均值. 要计算分配给集群k的每个数据中所有特征的平均值.

    假如一个集群已分配训练示例$x^{(1)},x^{(5)},x^{(6)},x^{(10)}$, 则平均值计算如下:

    $\frac{1}{4}[x^{(1)}+x^{(5)}+x^{(6)}+x^{(10)}]$

    需要明确的是上面公式是向量加法, 如果有n个特征, 则$x^{(i)}$包含n个数字.

这个算法有个极端情况, 如下图:

image-20230826195138262

如果一个集群分配给它的训练示例为零, 就会发生上图的情况.

如果发生这种情况, 最常见的做法是消除该集群, 最终得到K-1个簇质心.

如果真的需要K个簇质心, 另一种方法是随机重新初始化簇质心, 并希望下一轮至少分配一些数据点(不推荐).

如果是分离不怎么良好的数据集, 如下图:

image-20230826195638139

事实证明, K-means算法也经常用于分离不佳的数据集.

如上图, 人们的身高和体重往往会在数据上连续变化, 没有非常清晰的界限, 假如T恤有三个尺码(S, M, L), 使用三个簇质心, 可以将数据集分为适合三个尺码的.

成本函数

从成本函数的角度理解K-means算法为何会收敛. 如下图

image-20230826201631447

$c^{(i)}$是当前分配给训练数据$x^{(i)}$的集群索引(1到K).

$\mu_{k}$是簇质心.

$\mu_{c^{(i)}}$是指示例数据$x^{(i)}$已分配到的集群的簇质心.

成本函数则如上图所示:

$J(c^{(1)},...,c^{(m)},\mu_{1},...,\mu_{k})=\frac{1}{m}\sum_{i=1}^{m}||x^{(i)}-\mu_{c^{(i)}}||^2$

K-means算法就是最小化该成本函数, 也就是说算法试图找到簇质心的分配, 以最小化示例数据到簇质心的平方距离.

该成本函数在其他文献中还有一个名字叫做失真函数(Distortion).

为何K-means算法能最小化该成本函数呢?

重复步骤的第一步是通过更新$c^{(1)},...,c^{(m)}$和固定$\mu_{k}$尽可能地最小化成本函数. 如果想通过这种方式最小化平方距离, 应该做的是将训练数据$x^{(i)}$分配给最近的簇质心.

重复步骤的第二步是通过固定$c^{(1)},...,c^{(m)}$和更新$\mu_{k}$尽可能地最小化成本函数. 通过给$\mu_{k}$选择分配的示例数据的平均值, 将最小化成本函数, 解释如下图:

image-20230826204220466

如上图所示, 当簇质心处于两个数据的中间位置(平均值)时, 能使平方距离最小.

综上所述, K-means算法在每次迭代中都是在优化成本函数J, 意味着算法能保证收敛. 失真函数应该下降或保持不变, 如果出现上升, 一定是代码有问题, 算法保证了失真函数永远不应该上升. 可以通过判断失真函数是否下降来测试K-means算法是否收敛.

选择簇质心

K-means算法的第一步是选择随机的位置作为簇质心的初始猜测, 那如何进行随机选择呢?

选择K小于训练集数量m, 如果大于, 则没有意义, 如果选择大于, 甚至没有足够的训练数据来让每个簇质心至少有一个训练示例.

为选择簇质心, 最常见的方法是随机选取K个训练示例. 然后将$\mu_{k}$设置为等于这K个训练示例. 但是如何判断该随机的选择是比较好的, 不会出现下图的问题?

image-20230826210543678

比如图中的第二个示例($J_2$)有可能会陷入了局部最小值的情况.

因此需要多次尝试随机初始化, 找到一个更好的(如图中的第一个示例$J_1$)集群.

使用下图的方式进行初始化簇质心:

image-20230826211032902

对K-means算法循环50到1000次的随机初始化:

  1. 选择K个训练示例, 并初始化$\mu_{k}$
  2. 运行K-means算法, 得到$c^{(1)},...,c^{(m)},\mu_{1},...,\mu_{k}$
  3. 计算成本函数(也叫失真函数)

然后选择使成本函数最小的集群.

这样做比只运行一次K-means算法相比, 通常会得到一组更好的集群, 请至少尝试运行50次, 但不要超过1000次, 因为计算耗时, 且收益往往会递减.

簇质心数量

对于很多聚类问题, K的正确值确实是模棱两可的, 因为聚类是无监督学习算法, 所以不会以特定标签的形式给出正确答案来尝试复制.

在许多应用程序中, 数据本身并不能明确指示其中有多少个簇质心.

下图是一个称为Elbow Method(肘法)的选择K值的方法, 但不推荐:

image-20230826212916750

如上图所示, Elbow Method使用各种K值运行K-means算法, 绘制图中的曲线, 找到能让曲线弯曲的K值(如人的肘样子).

该方法的缺点是对于很多应用程序, 绘制的K-J曲线都是平滑的减少(如图中右边的), 没有明确的弯曲的地方来选择K值. 另一个原因是随着K值的增加, 有更多的簇质心几乎总是降低成本函数J.

那该如何选择K值呢? 根据业务需求. 如下图的T恤例子说明

image-20230826213645920

如图中的T恤例子, 如果根据业务需要分为三个尺码(S, M, L), 则选择K=3. 如果需要分为五个尺码(XS, S, M, L, XL), 则选择K=5.

根据有三个尺码或五个尺码, 在T恤的合身性进行权衡, 但制造运输五个尺码的T恤可能会有额外的成本. 这种情况下, 运行K-means算法, 让K=3或5, 然后查看这两个解决方案, 找到合身性良好且成本合理的结果.

在课程提供的编程练习中, 会看到K-means算法在图像压缩方面的应用. 可以看到在图像质量和压缩空间之间进行权衡的最佳K值选择.

异常检测

Anomaly Detection

定义

异常检测算法会查看未标记的正常事件数据集, 从而学会检测不寻常或异常事件.

下图是个异常检测的示例, 用于检测生产的飞机发动机.

image-20230827154632829

搜集的数据集是运行良好的飞机发动机的特征数据.

异常检测算法学习这些示例数据后, 得到飞机发动机通常产生多少热量和多少振动的数据. 如果有一个全新的飞机发动机要下线, 并且有新的数据特征$X_{test}$, 那么通过异常检测算法可以知道这个新的发动机是否和以前制造的发动机相似(特征相近). 如果这台新发动机有什么异常的特征, 应该更仔细地检查它.

如图中所示, 标记为ok的新特征数据是算法认为正常的, 而标记为anomaly的是算法认为异常的.

执行异常检测最常见的方法是称为密度估计(Density estimation)的技术. 如下图

image-20230827160019625

使用密度估计的第一件事是为特征x的概率p(x)建立一个模型, 也就是说算法将尝试找出具有高概率的特征值是什么, 以及在数据集中出现可能性较小或概率较低的特征x的值是什么. 如图中的椭圆区域

如果已经为特征x的概率p(x)建立了模型, 当获得新的测试示例$X_{test}$时, 需要做的是计算$X_{test}$的概率$P(X_{test})$. 如果新示例的概率P很小, 小于指定阈值的 $\epsilon$ , 则这新测试示例$X_{test}$的特征值是不寻常的, 即可能是异常情况. 相反, 如果新示例的概率P大于等于指定阈值的 $\epsilon$ , 那这个新测试示例$X_{test}$不是异常情况.

应用

异常检测算法经常用于欺诈检测, 搜集用户在网站和应用上的特征活动数据(如进行了多少交易, 发了多少帖子, 打字速度是多少等等), 可以根据这些数据进行建模检测异常. 在欺诈检测的常见工作流程中, 不会仅仅因为某个账户看起来异常就自动关闭, 可能会要求安全团队进行仔细检查或进行一些额外的安全检查(如要求用户使用手机号码验证身份或进行人脸识别等).

上述类型的欺诈检测既用于查找虚假账户, 也经常使用这种算法来识别金融欺诈(如不寻常的购买行为).

异常检测也经常用于制造业, 如定义里描述的飞机发动机的示例. 制造商经常使用异常检测来查看他们刚刚生产的产品.

异常检测还用于监视集群和数据中心的计算机, 通过捕获机器的内存占用, 每秒访问磁盘次数, CPU负载等等特征数据, 如果某台特定计算机的行为与其他计算机有很大不同, 则可能需要查看该计算机以确认是否有问题.

高斯(正态)分布

Gaussian (Normal) Distribution

如下图所示的高斯分布曲线

image-20230827164009110

假设x是一个数字, 且是随机的, 有时称为随机变量. 随机值x的概率是由均值参数$\mu$和方差$\sigma^2$的高斯分布(正态分布, 钟形分布)给出, 如上图公式

$p(x)=\frac{1}{\sqrt{2\pi}\sigma}e^{\frac{-(x-\mu)^2}{2\sigma^2}}$

高斯分布曲线的中心由$\mu$给出, 曲线的宽度由方差参数$\sigma$(标准差)给出. 这条曲线显示了随机值x分布的概率p(x). 高斯分布曲线的面积等于1.

参数$\mu$和方差参数$\sigma$影响高斯曲线的示例如下图:

image-20230827165203501

如何计算参数$\mu$和方差参数$\sigma$的值呢? 由下面的公式给出

image-20230827165335896

$\mu=\frac{1}{m}\sum_{i=1}^{m}x^{(i)}$

$\sigma^2=\frac{1}{m}\sum_{i=1}^{m}(x^{(i)}-\mu)^2$

其中m是数据量.

可以通过随机值x在高斯分布曲线上的概率p(x)和阈值 $\epsilon$ 来判断x是否异常.

构建算法

如下图所示的密度估计方法示例

image-20230827171659987

假定有训练数据集数量为m个, 且每个训练示例x的特征数量有n个, 则n个特征的训练示例x的模型概率p(x)的计算公式为:

$p(x)=p(x_1;\mu_{1},\sigma_{1}^{2})*p(x_2;\mu_{2},\sigma_{2}^{2})p(x_3;\mu_{3},\sigma_{3}^{2})...*p(x_n;\mu_{n},\sigma_{n}^{2})$

在统计学上, 发生$x_1$的事件和发生$x_2$的事件是独立, 因此要计算两个事件同时发生的概率, 则需要两个事件发生概率的乘积. 事实证明, 即使这些特征实际上不是统计独立的, 该算法也能正常工作.

上述的公式可使用连乘符号改写成:

$p(x)=\prod_{j=1}^{n}p(x_{j};\mu_{j},\sigma_{j}^{2})$

其中$\mu_{j}$是特征j在训练数据x上的平均值, 而$\sigma_{j}^{2}$则是特征j在训练数据x上的方差.

image-20230827173124546

如上图所示, 构建算法的完整步骤如下:

  1. 选择能检测出异常示例的n个特征

  2. 通过下面的公式计算特征j的参数$\mu_{j}$和方差$\sigma_{j}^{2}$

    $\mu_{j}=\frac{1}{m}\sum_{i=1}^{m}x_{j}^{(i)}$

    $\sigma_{j}^2=\frac{1}{m}\sum_{i=1}^{m}(x_{j}^{(i)}-\mu_{j})^2$

    在代码中是向量计算, 把n个特征的参数$\mu$和方差$\sigma^{2}$通过上述公式一次计算出来.

  3. 给定一个新的示例$X_{test}$, 计算概率$P(X_{test})$.

    $p(x)=\prod_{j=1}^{n}p(x_{j};\mu_{j},\sigma_{j}^{2})=\prod_{j=1}^{n}\frac{1}{\sqrt{2\pi}\sigma_{j}}exp({\frac{-(x_{j}-\mu_{j})^2}{2\sigma_{j}^2}})$

    根据计算出的概率$P(X_{test})$和阈值 $\epsilon$ 进行比较, 查看概率p(x)是否小于阈值 $\epsilon$ ,如果是, 则标记该新示例为异常情况.

下图是异常检测算法构建步骤的图示

image-20230827174356209

图中参数$\mu_{1}$和方差$\sigma_{1}$是第一个特征的, 参数$\mu_{2}$和方差$\sigma_{2}$是第二个特征的.

图中左边坐标轴是两个特征的数据的展示, 右边的两个坐标轴分别是特征1和特征2的高斯分布曲线, 而左边下方的3D曲线图是融合了特征1和特征2的高斯分布3D曲线.

可以看到第一个示例$X_{test}^{1}$计算出的概率大于阈值 $\epsilon$, 且在高斯分布3D曲线中处于偏上的山腰部分, 而第二个示例$X_{test}^{2}$计算出的概率小于阈值 $\epsilon$, 且在高斯分布3D曲线中处于山底部分.

开发和评估

开发异常检测系统的使用技巧, 如果能有一种方法来评估该系统, 即使系统正在开发中, 也能够做出决定并快速地改进系统.

在开发学习算法时, 如果有一种评估学习算法的方法来决定以某种方式增加或减少特征, 或选择阈值 $\epsilon$, 将会容易很多. 这种方式被称为实数评估(real-number evaluation).

异常检测算法中经常使用以下方式, 如图:

image-20230827181521839

假设有一些标记数据, 包括正常的和一些在以前观察到的异常示例数据. 标签y=0表示是正常的, 标签y=1表示是异常的.

异常检测算法仍然从训练数据集(未标记数据)中学习, 且把这些训练数据假设是正常的而不是异常的, 此时标签y=0. 在实践中, 异常检测算法的训练集中混入了一些异常数据, 算法依然可以正常工作.

如果有一些已知是异常的数据, 那就可以创建交叉验证集和测试集. 其中交叉验证集和测试集中都包含一些很少的异常示例数据.

使用飞机发动机异常检测的例子说明做法, 如下图

image-20230827182447343

假如有10000个发动机良好的数据和20个发动机缺陷的数据, 把数据如图所示划分为训练集, 交叉验证集和测试集.

在训练集上训练算法, 将高斯分布拟合到训练集中, 然后在交叉验证集上进行验证, 可得知正确检测到了多少个异常数据. 可以使用交叉验证集来调整阈值 $\epsilon$, 或添加减少特征, 取决于算法能够可靠地检测到交叉验证集中的异常数据.

在调整好阈值 $\epsilon$, 或特征后,可以在测试集上对其进行评估, 查看算法检测到了多少异常数据, 以及把正常数据标记为异常数据的数量.

事实证明, 如果在构建一个实用的异常检测系统, 在交叉验证集和测试集中使用少量异常数据来评估算法和调整算法非常有帮助.

当异常数据量非常小时, 可以只使用训练集和交叉验证集, 无测试集. 这种方案的缺点是无法公平地评估在未来示例数据中的效果如何, 因为没有测试集. 这种方案存在高风险, 围绕交叉验证集做出的调整阈值 $\epsilon$, 或添加减少特征的决策可能会过度拟合交叉验证集, 因此有可能在未来真实数据中的表现不如预期的那么好.

在交叉验证集和测试集中评估算法, 如下图

image-20230827184038414

首先, 在训练集上训练拟合示例数据x的模型p(x).

然后在交叉验证集或测试集上, 计算数据x的概率p值, 并和阈值$\epsilon$进行比较, 如果小于阈值$\epsilon$, 则预测标签y=1(异常), 如果大于等于阈值$\epsilon$, 则预测标签y=0(正常).

基于上述的预测标签y的值, 可以查看算法对交叉验证集或测试集预测的标签与交叉验证集或测试集中标签的匹配的准确程度. 使用第二课中学习到的处理高度倾斜数据分布的召回率或F1-score分值评估指标, 评估算法性能.

对比监督学习

异常检测和监督学习的选择方法, 如下图

image-20230827190431190

当正例数量非常少时, 异常检测算法通常是更合适的选择, 同时拥有能为示例x构建模型p的相对大量的反例, 此时正例数据在交叉验证集和测试集中用于参数调整和评估.

相反, 如果有更多的正例和反例, 那么监督学习可能更适用. 即使有20个正例, 也可应用监督学习算法.

异常检测看待数据集的方式和监督学习看待数据的方式截然不同.

如果有许多不同类型的正例(异常), 如飞机发动机有多种不同的故障方式, 此时异常检测更合适. 如果明天有可能会有一种全新的方式让飞机发动机出现问题, 那20个正例无法涵盖飞机发动机出错的所有方式, 这使得.任何算法都很难从一小部分正例中学习异常是什么样的.

未来的异常可能看起来与我们迄今为止看到的任何异常示例都不一样, 此时异常检测更合适. 异常检测会把与任何正常情况有很大差异的数据标记为异常.

相比之下, 监督学习有不同的看待问题的方式. 当应用监督学习时, 希望会有足够多的正例让算法了解正例是什么样子的. 使用监督学习, 前提是假设未来的正例很可能与训练集中的正例相似.

常见的对比用例如下图

image-20230827192039056

异常检测监督学习
欺诈检测垃圾邮件分类
制造业, 找到从前未出现的缺陷制造业, 找到已知的, 且从前已出现的缺陷
监控数据中心的机器天气预测
疾病分类

总结: 异常检测试图找到全新的异常, 这些异常可能与之前见过的任何异常都不一样.

监督学习会查看正例, 并尝试确定未来示例是否与之前看到的正例相似.

特征选择

在构建异常检测算法时, 选择合适的特征非常重要. 在监督学习中, 如果没有完全正确的特征或者有一些与问题无关的额外特征, 通常结果是没问题的; 而对于异常检测, 只从未标记的数据中学习, 更难找出检测出异常的相关特征.

可以帮助异常检测算法的一个做法是确保提供的特征数据或多或少是高斯分布的, 如果特征数据不是高斯分布的, 可以改变特征数据, 让它更符合高斯分布特征.

常用的将特征数据转换为高斯分布的做法如下图

image-20230827195033576

使用plt.hist功能展示特征使用, 尝试使用log函数, 使用平方根函数等等, 将特征数据转换为符合高斯分布特征的.

异常检测算法使用高斯分布特征的数据构建模型P时, 能更好的拟合数据.

注意: 无论对训练集应用何种转换, 请记住对交叉验证集和测试集也应用相同的转换.

除了确保数据近似为高斯分布外, 还可以执行异常检测的错误分析. 可以尝试查看算法在出错时做得不好的地方, 然后进行改进. 如下图

image-20230827195841427

在构建模型P的过程中, 可能遇到最常见的问题是, 数据x的概率P值对于正常示例和异常示例都很大. 如图中左边坐标轴的蓝色x数据(异常), 这个数据看起来与训练集中的其他正常示例非常相似, 此时算法无法将此示例标记为异常.

此时, 尝试查看此示例, 并弄清楚是什么特征让该示例被认为是异常的, 如果能识别出一些新特征(图中的$x_2$特征), 那有助于将这个示例与正常示例区分开来, 添加此特征, 可以帮助提高算法的性能. 如图中右边的坐标轴, 在选择了新特征后, 蓝色x异常示例和正常示例区分开了.

总结: 训练模型, 然后查看算法未能检测到交叉验证集中的哪些异常示例, 然后查看这些示例, 找到新的特征并创建, 从而允许算法进行该异常示例的识别, 该异常示例在新特征上采用较大或较小的值, 因此可以成功地将该示例标记为异常.

下图是个数据中心监控计算机的示例, 说明组合旧特征来创建新特征:

image-20230827201328037

如果发现有一台计算机运行异常, 但CPU负载和网络流量都没有那么异常, 但不寻常的是这台计算机有非常高的CPU负载, 同时具有非常低的网络流量.

如果是运行流式传输视频的数据中心, 则计算机可能具有高CPU负载和高网络流量, 或低CPU负载且没有网络流量.

这种情况下, 可能会创建一个新特征$x_5$, 表示CPU负载和网络流量的比值, 使用这个新特征, 异常检测算法能检测到上述的计算机是异常的.

根据数据x的概率p来进行特征选择, 即概率p在正常示例中仍然很大, 而在交叉验证集中的异常示例中会变小.

代码

K-means算法

import numpy as np
from utils import *
# 找到最近的簇质心
def find_closest_centroids(X, centroids):
    """
    给每个示例数据找到最近的簇质心
    参数X是示例数据
    centroids是K个簇质心
    """
    # 簇质心的数量
    K = centroids.shape[0]
    # 返回值,下标数组,下标对应簇质心在centroids的下标
    idx = np.zeros(X.shape[0], dtype=int)
    # 遍历所有的示例数据
    m = X.shape[0]
    for i in range(m):
        # 距离数组,计算每个示例数据到每个簇质心的距离
        distance = []
        for j in range(K):
            # 使用np.linalg.norm求解L2范数
            norm_ij = np.linalg.norm(X[i] - centroids[j])
            # 记录到距离数组中
            distance.append(norm_ij)
        # 使用np的argmin方法找到最短距离的下标
        idx[i] = np.argmin(distance)
    return idx

# 重新计算簇质心
def compute_centroids(X, idx, K):
    """
    函数返回新的簇质心数组
    参数X表示训练数据
    idx表示训练数据最近的簇质心下标
    K表示簇质心的数量
    """
    # 示例数据量m和特征数量n
    m, n = X.shape
    # 初始化新的簇质心数组
    centroids = np.zeros((K, n))
    # 遍历所有的簇质心,k是簇质心下标
    for k in range(K):
        # 找到在idx中存储的簇质心下标对应的数据点
        points = X[idx == k]
        # 使用np.mean计算平均值并赋值给新的簇质心数组
        centroids[k] = np.mean(points, axis=0)
    return centroids

# 运行K-means算法
def run_kMeans(X, initial_centroids, max_iters=10):
    """
    返回新的簇质心数组和数据集对应的最近簇质心下标数组idx
    参数X是示例数据
    initial_centroids是初始的簇质心数组
    max_iters是运行K-means算法的迭代次数
    """
    m, n = X.shape
    K = initial_centroids.shape[0]
    centroids = initial_centroids
    previous_centroids = centroids    
    idx = np.zeros(m)
    # 运行K-means算法
    for i in range(max_iters):       
        # 输出运行进度
        print("K-Means iteration %d/%d" % (i, max_iters-1))   
        # 第一步,找到最近的簇质心下标数组idx
        idx = find_closest_centroids(X, centroids)       
        # 第二步,计算新的簇质心数组
        centroids = compute_centroids(X, idx, K)
    return centroids, idx

# 随机初始化簇质心数组
def kMeans_init_centroids(X, K):
    """
    随机初始化簇质心数组
    参数X是示例数据
    参数K是簇质心数量
    """
    # 随机选择m个簇质心下标,permutation是重新排列
    randidx = np.random.permutation(X.shape[0])
    # 使用K个簇质心
    centroids = X[randidx[:K]]
    return centroids

异常检测算法

import numpy as np
from utils import *

# 计算高斯分布需要的平均值mu和方差var
def estimate_gaussian(X): 
    """
    参数X是训练数据
    返回平均值mu数组和方差var数组
    """
    m, n = X.shape
    # 计算平均值mu数组
    mu = 1/m * np.sum(X, axis=0)
    # 计算方差var数组
    var = 1/m * np.sum((X-mu)2, axis=0)

    return mu, var

# 选择阈值epsilon
def select_threshold(y_val, p_val): 
    """
    参数y_val是真实数据(交叉验证集)
    参数p_val是算法预测的概率
    返回选择好的阈值epsilon和F1分值
    """
    best_epsilon = 0
    best_F1 = 0
    F1 = 0
    # 执行的迭代的步长,循环次数是1000次
    step_size = (max(p_val) - min(p_val)) / 1000
    # 开始循环
    for epsilon in np.arange(min(p_val), max(p_val), step_size):
        # 每次使用概率p_val和epsilon进行比较,得出结果
        predictions = (p_val < epsilon)
        # 计算预测为1和实际上为1的数量,即True Positive
        tp = sum((predictions==1) & (y_val==1))
        # 计算预测为1和实际上为0的数量,即False Positive
        fp = sum((predictions==1) & (y_val==0))
        # 计算预测为0和实际上为1的数量,即False Negative
        fn = sum((predictions==0) & (y_val==1))
        # 计算精准度
        prec = tp / (tp + fp)
        # 计算召回率
        rec = tp / (tp + fn)
        # 计算F1分值
        F1 = 2 * prec * rec / (prec + rec)
        # 找到最适合的F1分值和阈值epsilon
        if F1 > best_F1:
            best_F1 = F1
            best_epsilon = epsilon
    return best_epsilon, best_F1

推荐系统

Recommender System

以下面的电影推荐系统为例进行说明:

image-20230829174008611

使用 $n_u$ 表示用户数量, 使用 $n_m$ 表示电影数量, 使用 $r(i,j)$ 表示用户j是否对电影i评分, 如果评分了取值为1, 否则为0; 使用 $y^{(i,j)}$ 表示用户j对电影i的评分.

先假设拥有每个电影的全部特征, 开发一个电影推荐系统. 在典型的推荐系统中, 有一定数量的用户以及一定数量的项目(item, 这里是电影), 推荐系统努力向用户推荐其感兴趣的任何东西(如产品, 网站, 媒体文章等).

使用这个推荐系统框架, 解决此推荐问题的一种方法是查看用户未评价的电影, 并尝试预测用户如何评价这些电影, 这样就可以尝试向用户推荐他们更有可能将其评为5星的电影.

公式

如下图所示, 如果电影有两个特征, 使用线性回归模型预测用户对电影的评分

image-20230829180529713

如图所示, 使用 n 表示特征数量(目前为2), 可以通过线性回归模型来预测用户j对电影i的评分: $w^{(j)} \cdot x^{(i)} + b^{(j)}$, 如图所示, 第1个用户对电影3的评分计算为4.95.

成本函数

image-20230829181028000

如图所示, 和线性回归模型一样, 使用最小化成本函数来训练参数w和b.

用户j的成本函数公式如下

$min J(w^{(j)},b^{(j)}) = \frac{1}{2\cancel{m^{(j)}}} \sum_{i;r(i,j)=1}(w^{(j)} \cdot x^{(i)}+b^{(j)}-y^{(i,j)})^2+\frac{\lambda}{2\cancel{m^{(j)}}}\sum_{k=1}^{n}(w_{k}^{(j)})^{2}$

使用 $n_u$ 表示用户数量, 使用 $n_m$ 表示电影数量, 使用 $r(i,j)$ 表示用户j是否对电影i评分, 如果评分了取值为1, 否则为0; 使用 $y^{(i,j)}$ 表示用户j对电影i的评分.

其中$m^{(j)}$表示用户j已经评分的电影数量, $m^{(j)}$ 因为是在这个表达式中是常量, 且消除后计算成本函数J更方便.

由上面的用户j的成本函数的成本函数, 可得出用户从1到$n_u$的总成本函数:

image-20230829182708716

协同过滤算法

Collaborative Filtering

如何得到特征X呢? 如下图的示例, 假设已经得知参数w和b的值, 来学习特征x

image-20230829184433173

如图所示, 如果已经得知参数w和b的值, 可以通过用户的评分和线性函数来预测特征x的值, 这也是一个线性回归模型, 如下图

image-20230829184617540

给定参数w和b, 通过最小化成本函数J来学习特征x, 公式如图所示.

需要注意的是正则化项是关于$x^{(j)}$的.

结合学习参数w和b的成本函数, 可得到协同过滤算法的公式:

image-20230829185037076

可得知除去两个成本函数的正则化项, 第一项计算的是相同的数据.

那么如何最小化这个成本函数呢? 同样使用梯度下降算法, 如下图

image-20230829185330025

注意此时特征x也作为和w, b一样的参数进行梯度下降来学习.

协同过滤是指因为多个用户协同评价同一部电影, 从而了解到这部电影的特征, 而这反过来又允许预测尚未评价同一部电影的其他用户将来如何评价该电影.

协同过滤是从多个用户收集数据, 用户之间的协作可帮助预测未来其他用户的评分.

二进制标签

如何将协同过滤算法应用到数据都是二进制标签的应用中呢? 使用逻辑回归, 如下图

二进制标签中值的含义:

image-20230829191216756

标签值1表示出现了项目时进行了相应的行为, 而标签值0表示出现了项目时没有进行相应的行为, 标签值?表示项目没有出现.

image-20230829191442944

如上图所示, 对于二进制标签, 通过使用逻辑函数进行预测. 对应的成本函数如下图

image-20230829191728974

和以前学习逻辑回归一样, 只是这里把特征x也看作一个参数进行训练.

均值归一化

Mean Normalization

如下图所示, 不进行均值归一化, 则新用户Eve对电影评分则不够合理

image-20230829193231686

Eve是一个全新的用户, 还未对电影做任何评分.

如果使用图中的成本函数进行学习得到用户Eve的参数w和b, 参数w和b的值可能为0, 因为使用了正则化项会使参数w变小, 且新用户Eve还未对任何电影进行评分, 所以该新用户的参数w和b不会影响第一项, 即Eve的电影评级不会在第一项的平方差成本函数中起作用, 最小化成本函数意味着让新用户的参数w和b尽可能小.

因此没有使用均值归一化, 那会预测新用户对所有电影的评级都为0, 这不合理.

image-20230829194126985

如上图, 是使用了均值归一化的情况.

通过对每一行电影的评分取平均值, 可得到图中的$\mu$向量, 通过做矩阵减法, 将数据转换为图中右侧的样子, 即原数据的每个值减去对应行i的平均值$\mu_{i}$. 注意在进行预测评分时, 需要再加回对应行i的平均值$\mu_{i}$.

通过算法学习, 虽然得到的参数w和b的值也都是0, 但是预测的值不再是0, 而是其他用户对同一电影的评级的平均值, 这样更加的合理.

使用均值归一化, 不仅能让算法运行更快一点, 也确实使算法对于没有评价电影或评价电影数量较少的用户表现得更好, 而且预测变得更加合理.

可以使用另一种替代方法, 即使用每一列的评分进行均值归一化, 但在这个应用中, 均值归一化行比均值归一化列更为合理. 如果有一部没有人评价过的全新电影, 那可能一开始就不应该将该电影展示给太多用户, 因为算法对该电影还不太了解(协同过滤).

TensorFlow实现

Auto Diff

自动求导, 也叫Auto Grad

以前的线性回归使用TensorFlow的Auto Diff功能实现, 如下图

image-20230829201036050

上图所示的线性回归方程 $f(x)=w \cdot x$, 标签y的值为1, 学习率为0.01, 代码如下:

# 初始化参数w的值
w = tf.Variable(3.0)
# 训练示例数据
x = 1.0
# 目标值y
y = 1.0
# 学习率
alpha = 0.01
# 迭代次数
iterations = 30
# 开始迭代
for iter in range(iterations):
    # 使用tf.GradientTape()可让TensorFlow记录计算过程,从而自动计算导数
    with tf.GradientTape() as tape:
        fwb = w*x
        costJ = (fwb - y)2
    # 计算成本函数J对参数w的导数
    [djdw] = tape.gradient(costJ, [w])
    # 此时w的更新必须调用assign_add进行更新
    w.assign_add(-alpha * djdw)

协同过滤求导

使用TensorFlow进行协同过滤算法的求导, 并使用优化算法Adam, 如下图

image-20230829201938047

代码如下:

# 创建Adam算法优化器
optimizer = keras.optimizers.Adam(learning_rate=le-1)
# 迭代次数
iterations = 200
# 开始迭代
for iter in range(iterations):
    # 使用TensorFlow的GradientTape记录成本函数J的计算步骤,用于自动求导
    with tf.GradientTape() as tape:
        # 计算成本函数,X是训练数据,参数W,参数b,均值归一化的目标值Ynorm
        # R是是否对电影评分的二进制标签数据
        # num_users用户数量,num_movies电影数量,lambda正则化项参数
        cost_value = cofiCostFuncV(X, W, b, Ynorm, R, num_users, num_movies, lambda)
    # 运行梯度下降,并返回成本函数关于X,W,b的导数
    grads = tape.gradient(cost_value, [X, W, b])
    # 使用Adam算法进行优化
    optimizer.apply_gradient(zip(grads, [X, W, b]))

为何不使用神经网络? 因为协同过滤算法不能很好地适合TensorFlow的密集层或其他标准神经网络层类型.

寻找相关项

如何找到相关项(电影的相关电影列表)? 如下图

image-20230829203957062

如果使用协同过滤算法, 找到了项目i的相关的特征$x^{(i)}$(算法找到的特征很难解释).

如果要找到相似的项目, 即是找到项目k的特征$x^{(k)}$近似于项目i的特征$x^{(i)}$, 也就是向量$x^{(k)}$到向量$x^{(i)}$的最小距离, 公式如下

$min(distance)=\sum_{l=1}^{n}(x_{l}^{(k)}-x_{l}^{(i)})^2$

协同过滤算法的局限性

image-20230829204812656

协同过滤算法有冷启动问题

例如, 如果有一个新电影, 但几乎没有人评价过, 如果很少有用户评价过, 那么如何对新电影进行排名?

同样对于只评价了几个电影的新用户, 如何确保向他们展示合理的东西?

更好的方法是向对极少数电影评分的用户展示让他们感兴趣的东西.

上面的问题称为冷启动问题, 当有新的项目, 很少用户评价过时, 或者一个新用户评价的项目很少时, 那么对该项目或该用户的协同过滤结果可能不会非常准确.

协同过滤算法有边界信息问题

一些边界信息(如浏览器, 地域, 设备, 年龄, 偏好等)可以惊人地与用户的偏好相关联. 这可能是算法找不到的特征.

基于内容过滤算法

Content-based filtering

在基于内容过滤中, 将开发一种算法来学习匹配用户和项目. 拥有了用户特征和项目特征(用户特征数量可以和项目特征数量不一致), 找到一种方法来查找用户和项目之间的良好匹配.

做法是计算出向量 $v_u^{(j)}$ (代表用户j)和 $v_m^{(i)}$ (代表电影i), 这两个向量具有相同的维度, 然后对它们进行点积以尝试找到良好的匹配, 如下图

image-20230831164817722

如何计算所需的向量 $v_u^{(j)}$ 和 $v_m^{(i)}$ 呢? 使用神经网络.

神经网络

使用用户神经网络和电影神经网络分别计算向量 $v_u^{(j)}$ 和 $v_m^{(i)}$ , 如下图

image-20230831170205213

给定用户的特征$x_u$通过构建神经网络计算向量 $v_u$

同样的给定电影的特征 $x_m$通过构建神经网络计算向量 $v_m$

注意特征$x_u$和$x_m$的特征数量可以不同(如上图的左右神经网络第一层神经元数量不同), 当输出的对应的向量 $v_u$和向量 $v_m$必须具有相同的维度(如上图的左右神经网络的输出层的神经元数量相同).

然后通过向量的点积 $v_u \cdot v_m$ 来进行预测, 如果是预测二进制标签数据, 则使用函数$g(v_u \cdot v_m)$来计算$y^{(i,j)}$.

那么如何训练用户网络和电影网络呢? 同样使用成本函数, 如下图

image-20230831171039313

使用图中的成本函数J进行神经网络参数的训练, 同样也可以添加正则化项.

需要明确的是, 用户网络和电影网络没有单独的训练程序, 图中的成本函数J是用于训练用户网络和电影网络中所有参数的成本函数.

训练好参数后, 如何找到相似的电影呢? 如下图

image-20230831171525028

同协同过滤算法一样, 计算电影k的向量 $v_m^{(k)}$与电影i的向量 $v_m^{(k)}$平方距离最小值, 就能找到电影i相似的电影k.

这个相似的步骤可以提前预先计算. 可以在一夜之间运行一个服务器来遍历所有电影的列表, 并为每部电影找到与之相似的电影, 在第二天用户访问网站时进行展示.

该方法有个局限性, 如果有大量的不同的电影信息, 那么运行该计算的成本可能非常高. 如何解决呢?

高效推荐

使用两个步骤: 检索(Retrieval)和排名(Ranking)来优化推荐系统.

在检索步骤中生成大量可能的项目候选者列表, 即使包含用户不太可能喜欢的项目, 那么在排名步骤中进行微调并选择最好的项目推荐给用户.

检索(Retrieval)

image-20230831173636630

检索步骤是生成大量的貌似合理的候选项目, 可使用如下方法(不止这些)生成:

  1. 找到用户最近观看的10部电影, 找到这10部电影相似的(计算向量平方距离)电影.
  2. 找到用户最常看的3种电影类型, 找到3中电影类型中的Top10电影.
  3. 在这个国家的Top20电影.

上述步骤中如果包含一些用户根本不喜欢的选项也是可以的, 检索的目的是确保广泛的覆盖范围, 以便有足够多的好的电影值得推荐.

最后, 将在检索步骤中检索的所有项目, 组合到一个列表中, 删除重复的, 删除用户已经购买或用户已经看过的.

排名(Ranking)

image-20230831175044467

在排名步骤中, 获取在检索步骤中检索到的项目列表, 并使用学习模型对这些项目进行排名. 即把用户特征$x_u$和电影特征$x_m$输入到神经网络中, 并为每个用户对每个电影的评分进行预测. 然后基于此评分, 给出向用户展示的项目列表.

如果已经预先计算了所有电影向量 $v_m$, 那么所做的就是对用户这部分的神经网络做一次推理, 计算出向量 $v_u$.

那么在检索步骤中要检索多少项目才合适呢?

检索数量

image-20230831180241757

在检索步骤中, 检索更多的项目往往带来更好的性能, 但算法运行会变慢.

需要进行分析或优化当检索多少项目时在算法性能和运行速度上的权衡.

建议进行离线实验, 看看检索额外项目中有多少会产生更相关的推荐. 如果额外项目的预测评分较高, 且当前算法只是检索了100件项目, 那么需要检索更多的项目, 即使会减慢算法的运行速度.

通过单独的检索和排名步骤, 能让推荐系统提供快速和准确的结果. 因为检索步骤试图剪掉了很多不值得做的项目, 然后排名步骤对用户实际可能喜欢的项目进行了更仔细的预测.

推荐系统伦理

尽管推荐系统对一些企业非常有利可图, 但一些用例让人们和整个社会变得更糟糕.

吴恩达希望, 当你使用推荐系统或其他学习算法时, 能让整个社会和人们过得更好.

推荐系统的几种用例

image-20230831182136872

设计推荐系统时, 选择推荐系统的目标以及选择并决定向用户推荐什么.

  1. 可以向用户推荐最有可能被用户评为5星的电影
  2. 可以向用户推荐他们最有可能购买的产品
  3. 可以向用户推荐最有可能被点击的广告, 很多公司会做的可能是尝试展示最有可能被点击的广告以及广告商出价最高的广告, 对于许多广告模型而言, 公司的收入取决于广告点击量和广告商的行为. 虽然是一种利润最大化的策略, 但是此类广告可能会带来一些负面影响.
  4. 可以向用户推荐能产生最大利润的产品, 这种产品对公司来说更有利可图, 因为可以更便宜的购买并以更高的价格出售, 那么该产品在推荐的排名中会更高. 许多公司面临着最大化利润的压力, 这似乎不是一件不合理的事情, 但需要公司对用户公示推荐标准.
  5. 可以向用户推荐能导致用户最长观看时间的内容, 靠广告收入的网站往往有动力让您在网站上停留更长时间.

虽然前两个看着无害, 但第三, 第四和第五, 可能根本不会造成任何伤害, 或者它们也可能是推荐系统有问题的用例.

好坏用例对比

image-20230831183918443

上图中左边是好的示例, 右边是坏的示例.

如果有一家非常好的旅游公司, 可以向用户提供良好的旅行体验. 一个好的旅游业务, 往往会有更多的利润, 然后公司可以对广告出价更高, 有能力支付更多费用获得用户. 因为有能力出价更高, 在线广告网站会更频繁地展示其广告, 并将更多的用户吸引到这家好公司.

这是一个良性循环, 服务的用户越多, 业务利润越高, 可为广告出价更高, 获得的流量也更多.

发薪日贷款行业往往向低收入个人收取极高的利率, 在发薪日贷款业务做得好的方法之一是真正有效地从客户那里榨取每一分钱.

如果有一家非常善于开发客户的发薪日贷款公司, 真正做到压榨客户每一分钱, 那么这家公司的利润会更高, 因此能给广告出价更高, 以此获得更多的流量, 这使得他们能够压榨更多的客户获取更高的利润. 这也是一个正反馈循环, 但这个循环可能会导致最具剥削性, 最有害的发薪日贷款公司获得更多流量, 与对社会有益的效果相反.

这是推荐系统阶段非常难解决的问题, 一种改进建议是拒绝设置来自剥削性企业的广告, 那么如何定义剥削性业务也是一个非常困难的问题.

当构建推荐系统时, 每个从事技术工作的人都应该问自己这样的问题, 这样就有希望从更多的人在更多的公开讨论和辩论中获得多种意见, 并提出设计选择, 让推荐系统做更有益的事情.

一些问题的解决案例

image-20230831190144370

最大化用户在网站的停留时间, 可能会导致网站放大阴谋论, 仇恨和毒性内容, 因为这些类型的内容非常吸引人, 并导致人们在上面花费大量时间, 结果对个人和整个社会都是有害的.

对这种情况的不完善的一种改进是尝试过滤掉有问题的内容, 如仇恨言论, 欺诈, 诈骗, 阴谋论, 某些类型的暴力内容. 同样的, 关于究竟应该过滤掉什么内容却很棘手.

另一个例子是很多用户没有意识到许多应用程序和网站正在努力最大化利润, 而不一定是用户对推荐的内容的兴趣. 如果可能的话, 鼓励公司对用户保持透明, 让用户知道公司决定向他们推荐什么内容的标准.

总结

推荐系统是非常强大的技术, 非常有利可图的技术, 但有一些问题的用例.

如果构建推荐系统或使用其他机器学习技术时, 吴恩达希望不仅要考虑可以创造的利润, 还要考虑可能造成的危害, 并邀请不同的观点进行讨论和辩论.

请只建造和做您真正相信可以让社会变得更好的事情.

TensorFlow实现

只展示关键代码, 其余代码会随后给出

image-20230831192014848

# 创建用户神经网络层
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),
])
# 创建项目神经网络层
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),
])
# 告诉keras如何提供用户特征
input_user = tf.keras.layers.Input(shape=(num_user_features))
# 调用模型,并获取模型的输出层向量
vu = user_NN(input_user)
# 进行归一化处理,将向量vu标准化为长度1
vu = tf.linalg.l2_normalize(vu, axis=1)

# 告诉keras如何提供项目特征
input_item = tf.keras.layers.Input(shape=(num_item_features))
# 调用模型,并获取模型的输出层向量
vm = item_NN(input_item)
# 进行归一化处理,将向量vm标准化为长度1
vm = tf.linalg.l2_normalize(vm, axis=1)

# 进行点击,计算输出
output = tf.keras.layers.Dot(axis=1)([vu, vm])

# 组合输入输出
model = Model([input_user, input_num], output)

# 成本函数,均值平方差函数
cost_fn = tf.keras.losses.MeanSquaredError()

PCA算法

描述

PCA算法是能让特征数量减少到两个或三个特征, 以便可以对数据进行绘图和可视化.

如下图的汽车的两个特征转换为一个特征的例子:

image-20230831211043892

PCA算法是找到一个或多个新轴, 当使用新轴测量数据坐标时, 最终仍会得到有关汽车的非常有用的信息.

上图的示例就是把汽车长度特征x1和汽车高度特征x2减少到一个特征(图中的z轴)size.

在实践中, PCA通常用于减少非常大量的特征(数百,数千), 减少到可能只有2个或3个特征, 以便可以在二维或三维视图中可视化数据.

如下图的3D(3个特征)可以看到数据在一个2D平面上:

image-20230831211303642

因此可以将特征从3个减少到2个, 从而生成了右边的2D坐标轴.

每当得到一个数据集时, 经常做的一件事是将数据可视化, 这样有助于了解数据是什么样的. 可视化数据集有时会帮助弄清楚数据集中发生的一些有趣的事情.

原理

注意: 在进行PCA算法减少特征时, 需要先进行均值归一化. 如果特征值的数量级不同, 则需要首先执行特征缩放(这样数据点距离不会太远), 再应用PCA算法.

image-20230831214122987

PCA算法说明如下图

image-20230831214947678

PCA算法要做的是, 选择一个轴而不是原来的两个轴来捕获这5个示例的特征数据.

如图所示, 捕获数据点在z轴的投影(project)距离, 即作垂直线到z轴,相交点(投影点)到原点origin的距离.

图中的z轴不是最好的选择, 但是这5个点分散, 因此可以捕获原始数据集中的大量变化或大量差异. 这5个点分散, 因此5个点的差异或变化大, 即数据在z轴上的投影大.

下图是个差的z轴的例子

image-20230831215337744

可以看到数据在z轴的投影点挤在一起了, 这些点比较集中, 因此它们的差异或变化较小, 意味着选择这样的z抽, 在原始数据集中捕获的信息较少.

下图是更好的z轴选择

image-20230831215834211

图中的z轴在PCA算法中称为主成分(principal component), 当数据投影到上图中的z轴时, 会得到最大的方差.

深层的工作原理, 如下图所示

image-20230831220555584

如图所示, 假设PCA算法已经找到z轴, 紫色的小箭头是长度为1的z轴的单位向量, 指向PCA算法选择z轴方向.

长度为1的向量的值是(0.71, 0.71), 而原始数据x的特征值(坐标)为(2, 3). 将示例数据投影到z轴的做法是 原始数据向量单位向量 进行点积, 如图中所示, 得到3.55的值, 该值代表着投影点到z轴坐标原点的距离是3.55.

更多的主成分轴, 如下图所示

image-20230831220941775

图中的z1轴为第一主成分轴, 而与z1轴垂直(90度)的z2轴是第二主成分轴. 如果要选择第三个轴, 那么第三个轴同样要和第一轴, 第二轴相互垂直(90度).

PCA算法和线性回归的不同, 如下图

PCA不是线性回归, 是一种完全不同的算法.

线性回归是一种监督学习算法, 有训练数据x和标签y. 使用线性回归, 试图拟合一条直线, 以便预测值尽可能接近真实标签y的值. 即最小化数据点到直线的y轴距离长度. 因为线性回归成本函数总是试图最小化曲线与标签y之间的距离.

PCA算法没有标签y, 只有未标记的数据x1和x2, 不会尝试拟合一条线以使用x1预测x2. PCA算法试图找到一个z轴, 让数据投影到z轴时, 数据与z轴垂直的小线段为最小.

PCA算法可以有很多特征, 所有特征都被平等对待, 只是想找到一个z轴, 当把数据投影到z轴时, 要尽可能地多保留原始数据的差异.

两个算法的目的截然不同, 使用线性回归来预测目标输出y, 而PCA试图获取大量特征并平等对待它们且减少表示数据时所需要的轴数.

PCA中的重建, 如下图所示

image-20230831223032598

PCA算法的重建(reconstruction)就是试图从z轴的数据还原回原来的数据信息. 事实证明, 没有足够的信息来准确返回特征数据, 但是可以尝试近似值.

重建过程就是用z轴的数据(3.55)乘以单位向量长度(0.71, 0.71)得到(2.52, 2.52). 原始点和投影点之间的区别是原始数据到z轴的垂直线段长度. 只用一个数字就可以得到原始数据坐标的合理近似值.

代码

算法步骤, 如图所示

image-20230831225245114

当特征值的取值范围非常不同时, 需要在执行PCA算法前进行特征缩放.

然后执行下面的步骤:

  1. 运行PCA算法(fit方法)拟合数据以获得两个或三个新轴. fit函数会自动进行均值归一化.
  2. 检查每个新轴或每个主成分在多大程度上解释了数据差异.
  3. 最后进行变换, 需要将数据投影到新轴上.

具体的代码如下图

image-20230831225355272

上图是讲二维的数据投影到一维的z轴上, 可以看到z轴的数据有99.2%解释.

# 原始二维数据
X = np.array([[1,1],[2,1],[3,2],[-1,-1],[-2,-1],[-3,-2]])
# 使用PCA算法减少到一维,n_components指明坐标轴数量
pca_1 = PCA(n_components=1)
# 调用fit方法拟合原始数据
pca_1.fit(X)
# 解释原始数据在新轴上有多大的比值
pca_1.explained_variance_radio_
# 转换二维原始数据为一维数据
X_trans_1 = pca_1.transform(X)
# 反向转换,将一维数据转换为二维数据
X_reduced_1 = pca.inverse_transform(X_trans_1)

如果二维数据使用PCA算法生成新二维数据, 如下图

image-20230831230132983

应用PCA算法的建议

image-20230831230910073

PCA算法经常用于可视化, 可将数据维度减少到2或3, 以便进行绘制.

PCA算法曾经用于数据压缩, 但随着带宽的增大, 可以传输更多数据, 因此该应用很少.

PCA算法曾用于加速监督学习模型的训练, 将大量特征减少, 能提高模型训练速度. 而现代机器学习算法, 实际上并没有那么多特征, 更常见的是获取高维数据集, 并将其输入到神经网络中.

今天使用PCA算法最常见的是可视化数据.

代码

协同过滤算法

成本函数

# 成本函数的实现
def cofi_cost_func_v(X, W, b, Y, R, lambda_):
    """
    返回值是成本函数
    X是电影数据二维数组
    W是用户参数二维数组,b是用户参数二维数组,每个小数组长度为1
    Y是用户电影评分数据二维数组,R是用户是否对电影评分的二进制数据二维数组
    lambda_是正则化项
    """
    # 使用TensorFlow的函数能快速完成计算
    j = (tf.linalg.matmul(X, tf.transpose(W)) + b - Y)*R
    J = 0.5 * tf.reduce_sum(j2) + (lambda_/2) * (tf.reduce_sum(X2) + tf.reduce_sum(W2))
    return J

训练模型

# 加载评分和是否评分数据
Y, R = load_ratings_small()
# 把自己的评分数据整合进Y中
Y    = np.c_[my_ratings, Y]
# 把自己的是否评分数据整合进R中
R    = np.c_[(my_ratings != 0).astype(int), R]
# 数据正则化
Ynorm, Ymean = normalizeRatings(Y, R)
# 电影数量和用户数量
num_movies, num_users = Y.shape
# 特征数量
num_features = 100

# 随机种子,用于持久化结果
tf.random.set_seed(1234)
# 初始化参数W,X,b
W = tf.Variable(tf.random.normal((num_users, num_features),dtype=tf.float64), name='W')
X = tf.Variable(tf.random.normal((num_movies, num_features),dtype=tf.float64), name='X')
b = tf.Variable(tf.random.normal((1, num_users), dtype=tf.float64), name='b')

# Adam优化算法
# 如果是Mac Book的M芯片,使用keras.optimizers.legacy.Adam
# 如果是其他,则使用keras.optimizers.Adam
optimizer = keras.optimizers.legacy.Adam(learning_rate=1e-1)

# 迭代次数
iterations = 200
# 正则化项的lambda值
lambda_ = 1
# 开始训练
for iter in range(iterations):
    # 使用TensorFlow的自动求导功能
    with tf.GradientTape() as tape:
        # 计算成本函数的值
        cost_value = cofi_cost_func_v(X, W, b, Ynorm, R, lambda_)
    # 自动求导
    grads = tape.gradient(cost_value, [X,W,b])
    # 应用优化算法
    optimizer.apply_gradients(zip(grads, [X,W,b]))

# 开始预测,整合参数W,X,b
p = np.matmul(X.numpy(), np.transpose(W.numpy())) + b.numpy()
# 进行预测并存储预测结果
pm = p + Ymean
# 获取预测结果
my_predictions = pm[:,0]
# 进行排序
ix = tf.argsort(my_predictions, direction='DESCENDING')

基于内容过滤算法

目前因为第三方库的问题, 代码暂时没有.

强化学习

Reinforcement Learning

描述

下图是个自动直升机飞行的强化学习的例子

image-20230902164326441

如何使用强化学习让直升机自行飞行呢?

任务是给出直升机的位置来决定如何移动控制杆. 在强化学习中, 把直升机的位置, 方向已经速度等信息称为状态(state-s). 因此任务是找到一个函数, 将直升机的状态s映射到动作(action)a, 以保持直升机在空中和飞行时平衡且不会坠毁.

解决上述任务的一种方法是使用监督学习, 可以使用监督学习来训练神经网络, 直接学习状态(数据x)到动作(标签y)的映射, 但事实证明, 当直升机在空中移动时, 究竟应该采取什么正确的行动是非常模糊的, 比如是向左倾斜一点还是更多, 或者稍微增加直升机压力还是增加更多, 想要得到x和理想动作y的数据集是非常困难的.

对于控制机器人的这类任务, 监督学习效果不佳, 改用强化学习.

强化学习的一个关键输入是一个叫做奖励或奖励函数(reward function)的东西, 它会告诉直升机什么时候做得好, 什么时候做得不好.

强化学习强大的原因是必须告诉它做什么, 而不是如何做, 指定奖励函数而不是最佳操作可以在设计系统时更加灵活.

如图中所示的, 当直升机飞行良好时, 给予正奖励, 飞行不好时, 给予负奖励.

强化学习的应用

  1. 控制机器人(Controlling robots)
  2. 工厂优化(factory optimization)
  3. 金融股票交易(financial stock trading)
  4. 玩游戏, 包括视频游戏(playing games, including video games)

强化学习的关键思想是不是告诉算法每个输入x的正确输出y是什么, 而是指定一个奖励函数, 告诉它什么时候做得好, 什么时候做得不好, 算法的工作是自动找出如何选择好的动作(action).

火星探测器

下面的关于强化学习的讨论, 基于下图中的火星探测器的例子

image-20230902165912957

如图中所示, 火星车只能在6个方格中向左或向右移动, 火星车最初位置为4.

火星车当前的位置在强化学习中叫做状态(state). 状态1的奖励是100, 而状态6的奖励是40, 中间其他状态的奖励为0. 状态1和状态6有时称为终端状态, 到达终端状态后, 在该状态下获得奖励, 不会再进行下一步.

每一步中, 火星车都可以选择两个动作(action): 向左走和向右走. 那么火星车应该如何做呢?

如图中的3个做法: 一个是一直向左走, 到达状态1, 得到奖励100. 一个是一直向右走, 到达状态6, 得到奖励40. 一个是向右走到状态5, 然后向左走一直走到状态1, 得到奖励100.

在每个步骤中, 机器人都处于某种状态, 称为状态s, 可以选择一个动作a, 并且还有一些奖励R(s), 即从状态s中获得的奖励, 由于采取了动作a, 导致状态变更为s’. 一个步骤的公式为: $(s, a, R(s), s')$.

回报

Return

回报被定义为奖励的总和, 但有一个称为折扣因子的加权.

回报的概念是在快速获得奖励(一般较低)与需要很长时间才能获得奖励(一般较大)之间的权衡.

image-20230902173447567

如上图所示, 计算火星车从状态4一直向左走的回报, 第一次计算时折扣因子为0.9, 第二次计算时折扣因子为0.5.

回报公式为: $Return=R_1 + \gamma R_2 + \gamma^2R_3 + ...$

折扣因子描述了越早获得奖励, 那么总回报值越高的情形, 一般取值为非常接近1的小数, 如0.9, 0.99, 0.999等. 如果使用较低的折扣因子(如0.5), 则非常严重降低了未来的奖励, 因为每增加一个时间步骤, 得到的奖励只能获得早一步奖励的一半.

下图是回报的例子, 说明了回报取决于采取的行动

image-20230902174647697

图中分别计算了一直向左走和一直向右走的回报情况, 通过对比可得知, 如果处于状态2, 3, 4, 那么选择向左走(比向右走的回报大); 如果处于状态5, 那么选择向右走.

强化学习中的回报是系统获得的奖励总和, 由折扣因子加权, 越遥远未来的奖励使用更高幂的折扣因子加权. 当系统中有负奖励时, 那么折扣因子实际上会激励系统将负奖励尽可能推迟到未来, 因为折扣因子是[0, 1]之间的数, 如果是负奖励, 则时间越久, 奖励越小, 损失越小.

策略

Policy

image-20230902175953363

在强化学习中, 算法的目标是提出一个称为策略(Policy)的$\pi$函数, 该函数将任何状态s作为输入, 并将其映射到采取的某个动作a, 公式为: $\pi(s)=a$.

如图中所示的, 如果输入状态2, 通过$\pi$函数可得知下一步的动作是向左走; 如果输入状态5, 通过$\pi$函数可得知下一步的动作是向右走.

image-20230902180350415

强化学习的目标是找到一个策略$\pi$函数告诉在每个状态下采取什么行动能最大化回报.

马尔可夫决策

Markov Decision Process (MDP)

通过上述概念描述一些其他的机器学习的例子, 如下图

image-20230902181125343

上图的这些强化学习应用程序的形式称为马尔可夫决策.

马尔可夫决策指的是未来仅取决于当前的状态, 而不取决于在到达当前状态的之前发生的任何事情, 也就是说, 在马尔可夫决策中, 未来只取决于现在所处的位置, 而不取决于如何到达现在所处的位置的.

另一种思考马尔可夫决策过程的方法如下图

image-20230902182207976

有一个机器人或代理, 要做的是选择行动a, 基于该行动, 世界或环境中会发生某些事情(如位置发生变化, 火星车取样岩石). 选择行动a的方法是基于策略$\pi$函数. 随后基于世界或环境中发生的事情, 可以得到所处的状态s和获得的奖励R.

状态动作值函数

State Action Value Function

状态动作值函数是通常使用大写字母Q来表示的函数. 该函数是指所处的状态s和该状态下可采取的行动a后的回报值的函数. 定义如下图

image-20230902185935835

从状态s开始, 执行一次动作a, 执行一次动作a后, 行为将达到最优, 即随后采取的行动导致最高的回报. 类似贪心算法思想, 使用局部最优达到全局最优. 假如在行动a之后的行动都是最优的(回报最大的), 那么行动a是能使之回报最大的行动, 那么全部行动则是最优的(回报最大的).

如上图所示, 计算Q(2, -->)的过程如下, 因为执行向右走的动作, 因此到达状态3, 而状态3, 通过上面的最优策略表查到, 一直向左走是回报最大的. 因此整个行动的回报值为:

$0(状态2)+0.5\cdot 0(状态3)+0.5^2\cdot 0(状态2)+0.5^3\cdot 100(状态1)=12.5$

计算Q(2, <--)的过程如下, 因为执行向左走的动作, 因此达到状态1, 而状态1为终端状态, 通过上面的最优策略表查到, 此时回报最大. 因此整个行动的回报值为:

$0(状态2)+0.5\cdot 100(状态1)=50$

通过对比可得知, 在状态2应该采取向左走的最优策略.

那么如果通过上述的方式得到策略$\pi$函数呢? 如下图

image-20230902191332465

事实证明, 来自状态s能获得的最佳可能回报是状态s采取行动a的Q函数中的最大值. 状态s采取的行动a可以是多个, 但要使得回报最大, 即$\mathop{max}\limits_{a}Q(s,a)$.

在状态s能执行的最佳可能动作a是让状态s采取行动a后能使得Q函数最大.

有时Q函数在其他文献中写为$Q^{*}$, 称为最优Q函数.

当折扣因子较大时, 更倾向于较长时间能获得的更大奖励(放长线钓大鱼), 当折扣因子较小时, 更倾向于短时间能获得的较小奖励(急功近利).

贝尔曼方程

Bellman Equation

那么如何计算函数Q呢? 使用下图的贝尔曼方程

image-20230902194803462

贝尔曼方程: $Q(s,a)=R(s)+\gamma\cdot \mathop{max}\limits_{a'}Q(s',a')$.

其中s是当前状态, a是当前状态s时可采取的行动, R(s)是当前状态的奖励, s‘是采取行动a后的状态, a’是状态s‘时可采取的行动.

贝尔曼方程的计算Q函数的示例, 如下图

image-20230902195238687

上图展示了使用贝尔曼方程计算Q(2, -->)和Q(4, <--)的过程.

下图是贝尔曼方程的进一步解释

image-20230902195451089

通过贝尔曼方程可得知, 强化学习中的总回报有两个部分, 第一部分是当前状态的回报R(s)(称为立即奖励), 第二部分是折扣因子乘以从下一个状态获得的回报.

随机环境

在某些环境下, 采取行动时, 结果不总是完全可靠的, 比如想让火星车向左行驶, 可能会出现大风或滑坡, 导致火星车向右行驶了.

在随机强化学习问题中, 感兴趣的不是最大化回报(变为随机数了), 感兴趣的事最大化回报的平均值. 如下图

image-20230902202416285

平均值的意思是指如果采用策略进行多次尝试, 会得到不同的回报值, 要对所有的回报值取平均值, 也就是所说的预期收益. 意味着想要最大化平均期望得到的回报值.

随机强化学习算法是选择策略$\pi$函数来最大化回报的平均值或预期总和.

对应的贝尔曼方程则修改为下图所示的

image-20230902202546866

贝尔曼方程: $Q(s,a)=R(s)+\gamma\cdot E[\mathop{max}\limits_{a'}Q(s',a')]$.

其中E是取平均值的运算.

连续状态空间

连续状态强化学习问题, 即连续状态马尔可夫决策过程, 即连续MTP. 其中的状态不仅仅是少数可能的离散值之一, 如火星车的1到6状态, 相反是一个数字向量, 其中任何一个都可以取大量值中的任何一个, 如下图的直升机位置状态的描述, 就有12个.

image-20230903165015790

其中x是南北方向的位置, y是东西方向的位置, z是离地高度, 与之对应的$\dot{x}, \dot{y}, \dot{z}$分别是在x, y, z方向的速度. 而其他的希腊字母代表在方向上的旋转角度和对应的角速度.

月球着陆器

月球着陆器模拟将车辆降落在月球上, 在下面的应用程序中, 将指挥正在快速接近月球表面的月球着陆器, 任务是在适当的时候启动火力推进器, 将其安全降落在着陆台上, 如下图

image-20230903170735054

月球着陆器有三个方向的推进器, 分别是左推进器, 右推进器, 下推进器(主推机器). 因此可以采取的动作a有4个, 一是什么都不做, 二是启动左推进器, 三是启动右推进器, 四是启动主推进器.

状态向量如下图

image-20230903170907369

应用程序是在二维平面上, 因此状态的向量中包含x-水平位置, y-高度位置, $\dot{x}$是水平速度, $\dot{y}$是高度速度, $\theta$是角度, $\dot{\theta}$是角速度, $l$表示左腿是否着地, 取值0和1, $r$表示右腿是否着地, 取值0和1.

奖励函数如下图

image-20230903172038022

奖励函数描述如下:

  • 到达着陆台: 根据到达的情况不同, 奖励在100到140之间
  • 靠近或远离着陆台的额外奖励, 靠近奖励+1, 远离奖励-1
  • 发生撞车则奖励-100
  • 软着陆奖励+100
  • 腿部(左腿或右腿)接地奖励+10
  • 启动主推进器(下推进器)奖励-0.3
  • 启动侧边推进器(左右推进器)奖励-0.03

则月球着陆器的问题描述如下图

image-20230903173033059

目标是学习策略$\pi$函数, 给定一个状态向量s, 选择一个动作a以最大化回报. 问题中的折扣因子$\gamma=0.985$.

DQN算法

使用强化学习算法来控制月球着陆器, 关键思想是要训练一个神经网络来计算Q(s, a)函数, 然后反过来能帮助选择好的动作. 如下图

image-20230903180608360

将训练一个神经网络, 网络的输入是当前状态和当前动作, 并计算Q(s, a).

把状态s和动作a放在一起当作输入x, 状态是之前看到的8个数字向量, 然后有四种可能的动作(nothing, left, main, right), 四个动作可通过one-hot进行编码, 比如动作如果是nothing, 则编码为(0, 0, 0, 0), 动作如果是left, 则编码为(0, 1, 0, 0).

如图所示, 输出层只有一个神经元, 即输出Q(s, a), 也就是y.

强化学习和监督学习不同, 强化学习要做的不是输入状态并输出动作, 而是输入一个动作状态向量并输出Q(s, a).

得到Q(s, a)后就可根据状态s, 计算该状态下每个动作的Q, 以此找到能让Q最大化的动作a.

那么如何训练一个神经网络来输出Q(s, a)呢?

使用贝尔曼方程来创建大量的示例x(状态动作向量)和y(Q函数)的训练集, 然后使用监督学习, 学习从x到y的映射.

那么如何获得x和y的训练集呢? 如下图

image-20230903182307991

如图中所示, 神经网络的工作是输入Q(s, a)中的(s, a), 并尝试准确预测右边的值y.

因为目前还没有好的策略, 所以将随机采取行动.

通过在月球着陆器中尝试不同的事情, 将观察到很多例子, 有了处于某种状态s时采取了一些行动(可好可坏)的示例, 由于处于该状态s, 通过奖励函数能计算出奖励R, 采取的行动的结果是进入了新状态$s'$, 从而得到$(s, a, R(s), s')$数据.

得到的每个$(s, a, R(s), s')$数据都足以创建一个训练示例x和y. 其中s和a用于获得输入x, 而使用$R(s), s'$配合贝尔曼方程从而得到输出y.

那么函数Q从何而来? 最初是完全不知道Q函数是什么, 但完全可以从随机的猜测开始, 随机算法的迭代, 实际的Q函数会越来越好.

完整算法的流程如下

image-20230903184404962

首先, 将采用神经网络并随机初始化神经网络的所有参数, 最初不知道Q函数是什么, 因此假设该初始化的神经网络是对Q函数的随机猜测.

接下来, 将重复以下操作:

  • 在月球着陆器中采取随机行动, 于是得到在某个状态时的$(s, a, R(s), s')$数据.

  • 存储最新的$(s, a, R(s), s')$数据10000个, 这种存储最新示例的技术称为重放缓冲区(replay buffer).

  • 然后开始训练神经网络:

    查看存储的10000个最新$(s, a, R(s), s')$数据, 并创建一个包含10000个示例的训练集. 其中输入$x=(s,a)$和输出$y=R(s)+\gamma \cdot \mathop{max}\limits_{a'}Q(s',a')$.

    由此训练一个新的神经网络, 称为$Q_{new}$, 并让$Q_{new}(s,a)\approx y$.

  • 然后把Q函数设置为刚刚训练好的神经网络$Q_{new}$.

事实证明, 从真正随机猜测Q函数开始运行上述的算法, 然后使用贝尔曼方程尝试改进Q函数的估计值, 通过反复执行此操作, 这将改进对Q函数的猜测.

上述的算法有时称为DQN算法, 即Deep Q-Network, 因为使用深度学习和神经网络来学习Q函数.

神经网络的改进

使用原先的神经网络时, 有四个动作, 则需要使用神经网络做四次推理来计算四个Q值, 以便进行选择最大的Q值, 这是低效的, 每个状态都要进行四次推理.

事实证明, 训练单个神经网络同时输出该状态下的所有动作的Q值则更有效. 改进后的神经网络如下图

image-20230903190012196

改进的神经网络有四个输出神经元, 分别输出Q(s, nothing), Q(s, left), Q(s, main), Q(s, right). 神经网络同时计算处于状态s时所有可能动作的Q值, 这更加有效, 因给定状态s可以只运行一次推理并得到所有可能的Q值, 然后能非常快速的选择最大化的Q.

$\epsilon$贪婪策略

Epsilon Greedy Policy

在算法的描述中有一个步骤是在月球着陆器中采取行动, 当算法运行时, 并不真正知道每种状态下采取的最佳行动是什么(如果知道了, 那么算法已经完成了), 即使没有很好的猜测Q函数, 那么如何在这一步采取行动呢?

做法有两种, 如下图

image-20230903194047445

第一个选择是在状态s的任何时候都选择一个使Q(s, a)最大化的动作a, 事实证明这可能正常工作, 但不是最佳选择(原因后面会解释).

第二个选择是在大多数情况下选择最大化Q(s, a)的动作a, 但有小部分情况会随机选择一个动作a.

为何要偶尔随机选择一个动作?

假设Q(s, a)初始化过程中有些奇怪的原因, 以至于学习算法认为启动主推动器绝不是一个好动作, 即神经网络参数被初始化时, Q(s, main)总是很低. 如果是这种情况, 那么神经网络选择最大化Q(s, a)的动作a时, 可能永远不会尝试选择启动主推动器的动作. 因为算法从来没有尝试启动主推动器, 所以算法永远不知道启动主推动器实际上有时是个好动作.

如果采用第二个选择, 则有很小的概率尝试不同的操作, 这样神经网络可以克服自己先入为主的想法, 即上段所说算法认为启动主推动器是个坏动作, 而实际上启动主推动器有时是个好动作.

这种随机选择动作的想法有时被称为探索(exploration)步骤.

采用最大化Q(s, a)的动作a, 有时称为贪婪(Greedy)行动, 因为试图选择该行动来最大化回报, 其他的文献中有时称为剥削(exploitation)步骤.

第二个选择有一个名称称为$\epsilon$贪婪策略(Epsilon Greedy Policy), 其中$\epsilon$是随机选择动作的概率(注意!!).

有时, 使用强化学习算法的技巧之一是从高的$\epsilon$开始. 开始时, 一次采取很多随机行动, 随后逐渐减少$\epsilon$, 随着时间的推移(算法的迭代), 渐渐不太可能随机采取行动, 而更优先使用改进的猜测Q函数来选择好的行动.

对于强化学习算法, 和监督学习相比, 在参数的选择方面更加挑剔, 在强化学习中, 如果参数设置的不太好, 学习时间会成倍的增加(比监督学习花费更长时间).

因此强化学习算法不如监督学习算法成熟, 在使用强化学习算法调整参数时有时会令人沮丧.

小批量和软更新

小批量Mini-Batch

小批量是一个既可以加速强化学习算法, 又适用于监督学习的想法.

监督学习下的小批量

image-20230903201427895

当训练集的数量达到亿级别时, 每次进行梯度下降都要计算亿次成本函数J值, 这非常慢. 小批量的想法是在每次梯度下降迭代过程中不使用所有的训练示例, 将使用一个较小的训练示例, 这样会让算法更高效.

为什么小批量有效呢?

image-20230903202043977

小批量梯度下降所做的是在算法的每一次迭代中, 只查看数据的某个子集. 运行梯度下降时参数w和b拟合了该子集, 下一次迭代中使用不同的子集进行拟合, 直到拟合完全部的数据集.

小批量梯度下降的直觉, 如下图

image-20230903202617069

使用小批量梯度下降, 会使算法朝着正确的方向前进, 但有可能不是最好的梯度下降方向, 有时甚至会朝着错误的方向偏离全局最小值, 但就平均而言, 小批量梯度下降会趋向于全局最小值. 虽然不可靠且嘈杂, 但每次迭代的计算成本要低得多, 因此当有非常大的训练集时是种更快的算法.

强化学习中的小批量

image-20230903203500788

如果在重返缓冲区存储了10000个最新的数据, 每次训练模型时不是都使用这10000个训练数据, 而是获取其中的子集, 比如1000个数据, 并且使用子集来训练神经网络.

事实证明, 这将使训练模型的每次迭代更加嘈杂但速度更快, 总体上会加速强化学习算法.

软更新Soft Update

软更新能帮助强化学习算法更好地收敛到一个好的解决方案.

强化学习算法中最后一步的设置$Q=Q_{new}$这一步可以使Q发生非常突然的变化. 如果在强化学习算法中训练了一个新的神经网络, 也许这个新的神经网络不是一个很好的神经网络, 甚至比旧的还差, 然后就用一个更糟糕的神经网络覆盖了原来的Q函数. 软更新有助于缓解这种情况.

软更新的方式如下图

image-20230903204519087

在设置$Q=Q_{new}$中, 软更新所做的是如下更新神经网络参数W和B

$W=0.01 \cdot W_{new} + 0.99 \cdot W$

$B=0.01 \cdot B_{new} + 0.99 \cdot B$

这称为软更新, 因为每当训练一个新的神经网络的参数$W_{new}$时, 只会接受一点点的新值. 软更新允许进行神经网络参数W和B的渐进变化影响当前对Q函数的估计.

事实证明, 使用软更新方法可以使强化学习算法更可靠地收敛, 使强化学习算法不太可能发生震荡或转向或具有其他的不良特性.

局限性

image-20230903200608269

让强化学习算法在模拟或视频游戏中工作比在真实机器人中工作要容易得多. 许多开发人员评论说, 即使在模拟中使用强化学习工作后, 要让算法在现实世界或真实的机器人中工作却出人意料具有挑战性.

强化学习的应用远少于监督和非监督学习.

强化学习在未来的应用中具有的潜力非常大. 强化学习仍然是机器学习的主要支柱之一.

课程结语

下图是三个课程的简要总结

image-20230903204808298

I know you're a busy person with many other things going on in your life, so that you took the time to watch the videos, go through the quizzes and labs.

I know you put a lot of time and put a lot of yourself into this class.

I just want to say, thank you very much for having been a student in this class.

I'm very grateful to you and appreciate all the time you spent with me and with the specialization. Thank you.

image-20230903205236276

AI学习
  • 作者:lzlg520
  • 发表时间:2023-09-12 21:23
  • 版权声明:自由转载-非商用-非衍生-保持署名
  • 公众号转载:请在文末添加作者公众号二维码