Machine Learning(监督机器学习回归和分类Week2)

97 阅读7分钟

多元线性回归

905347a2d13f9a521bcbfeebc98d39a.jpg 上图为已知数据信息,每一列为特征向量。分别设为x1,x2,x3...xn等等

每一行为训练示例

  1. xj:特征列表
  2. n:特征的总数 = 4
  3. 上标i代表第i个1训练示例 第i个训练示例的所有特征的向量 对应写成后边形式 多称为行向量
  4. 第三列的第二个值 楼层列的第二个特征值

将数据代入式子的结果如下图 93c37ed5a5968caf3ee7d9f7a140760.jpg

上面的箭头提醒我们它是一个向量

拓展后如下图,点积 f09ccdcc51fb79e77198aa96ae6c8df.jpg

这种具有多个输入特征的线性回归模型的名称是多元线性回归(multiple linear regression)

向量化

7ac9f87e321ee63e74ae4bff4b8d73c.jpg

用了四个方法来说明运算

右下角 np.dot() 直接运算较好 对于两个行向量,dot是对应元素相乘,二维以上则是矩阵乘法

最快的原因是可以在任何计算机上运行,如果是在GPU,一个图形处理器单元也会更好

以下是向量化实现的内部逻辑

左边是没有向量化的运算,运算是一步一步的,有关于时间戳

右边是NumPy中的这个功能是在计算机硬件中通过向量化实现 不需要格外的步骤 最后将其加起来就可以了 94d16720b3ea868e1c8b0e53442a3bc.jpg

10c06222b8443047bf950468f19f38e.jpg

以下是一个具体的例子:(查找相关文章后做的总结)

numpy是C写的。

python是解释执行的,为了方便初学者,对外提供“傻瓜”界面,有一些 费时间 的工作

举例来说,执行 x = 1234+5678 ,对编译型语言,是从内存读入两个short int到寄存器,然后读入加法指令,通知CPU内部的加法器动作,最后把加法器输出存储到x对应的内存单元(实质上,最后这个动作几乎总会被自动优化为“把加法器输出暂存到寄存器而不是内存单元,因为访问内存的时间消耗常常是访问寄存器的几十倍”)。一共2~4条指令(视不同CPU指令集而定)。

而换了解释性语言,情况就大大不同了。

它得先把“x = 1234+5678”当成字符串,逐个字符比对以分析语法结构——不计空格这也是11个字符,至少要做11个循环;每个循环至少需要执行的指令有:取数据(如读'x'这个字符)、比较数据、根据比较结果跳转(可能还得跳转回来)、累加循环计数器、检查循环计数器是否到达终值、根据比较结果跳转。这就是至少6条指令,其中包含一次内存读取、至少两次分支指令(现代CPU有分支预测,若命中无额外消耗,否则……)。总计66条指令,比编译型慢至少17倍(假设每条指令执行时间相同。但事实上,访存/跳转类指令消耗的时间常常是加法指令的十倍甚至百倍)。

这还只是读入源码的消耗,尚未计入“语法分析”这个大头;加上后,起码指令数多数百倍(消耗时间嘛……我猜起码得多数千倍吧)。

不过,python比起其它解释性语言还是强很多的。因为它可以事先把文本代码编译成“字节码”(存储于扩展名为pyc的文件里),从而直接处理整型的“指令代码”,不再需要从头开始分析文本。

但是,从“字节码”翻译到实际CPU代码这步,仍然是省不下的。

这个消耗,可看作“利用虚拟机”执行异构CPU上的程序。有人证明过,哪怕优化到极致,这也需要10倍的性能消耗。

这个消耗也有办法缩减。这就是JIT技术(Just-in-time compilation:就是在第一遍执行一段代码前,先执行编译动作,然后执行编译后的代码。)

这里面的佼佼者是Java。它甚至能根据上次运行结果实时profile,然后花大力气优化关键代码,从而得到比C更快的执行速度。

不过,理想很丰满,现实很骨感。虽然局部热点的确可能更快,但Java的整体效率仍然比C/C++差上很多——这个原因就比较复杂了。

和C/C++/Java那种投入海量资源经过千锤百炼的编译器不同,python的JIT甚至可称得上“蹩脚”。

加加减减,仅一个循环,慢上十几甚至几十倍还是很正常的。

以上讨论,仅仅考虑了for循环这个控制结构本身。事实上,“慢”往往是全方位的。

举例来说,要计算一组向量,首先就要存储它。

  1. 存储:
    1. 对C/C++来说,就存在“数组”里;而它的数组,就是赤裸裸的一片连续内存区域;区域中每若干个字节就存储了一个数值数据。这种结构CPU处理起来最为方便快捷,且cache友好(若cache不友好就可能慢数倍甚至数十倍)。
    2. Java等其它语言就要稍逊一筹。因为它的“数组”是“真正的数组”;相对于“连续内存区域”,“真正的数组”就不得不在每次访问时检查数组下标有无越界。这个检查开销不大,但也不小。好处是不用像C/C++那样,整天担心缓冲区溢出了。
    3. 为了迁就初学者,它去掉了“变量声明”以及“数据类型”——于是它的用户再也用不着、也没法写 int xxx了。随便什么数据,咱想存就存,乌拉!

python快的是编程速度,运行速度可以各种优化,这才是胶水的魅力。如果单单用python搞科学计算的会权衡编程和运行时间,对这个语言也是一种侮辱

用于多元线性回归的梯度下降法

ecfce839846a209312a7f31447a4a2c.jpg

单特征与多特征对比 956679905b4507e938937a13b182bf9.jpg

特征缩放 Feature scaling

举例子说明w1和w2得出的值需要互换

df34c5e85bd70fb8f42c64dcfd6919b.jpg

导致反复横跳的根本原因是横纵坐标需要的最合适步长不一样,为什么不一样呢?因为横纵坐标超别太大(0到1 和 10到100 的差别)

下面的图是从新标度,使得x,y的范围在都在0到1中间,得出更接近于圆,梯度下降可以找到一条更直接的通往全局最小值的路径

d2610595bd92f6c4dd3e440dc59105b.jpg

归一化 (0 到 1)

举例说明限制范围从0到1中间

cd0d1fdaf49bfc8baded051a7637e52.jpg

Mean normalization平均归一化(-1 到 1)

  1. 算出u1值,u2值(u代表平均值,即所有训练特征该列值的平均)
  2. 根据式子带入数值

480db5b179bbca99b8551884bbbc225.jpg

Z-score normalization

  1. 算出标准差 平均值
  2. 根据公式计算

8e8bd49f27203f9c889df641d50df76.jpg

如果你的值在-1到1是图中看起来很空,那就把值再缩小一些

所有的一切都是为了需要,不必局限于-1到1什么的,两个值的数量级与范围相对一致

判断梯度下降是否收敛

learning curve学习曲线

dad1747e6b5e716e81ed16d26e736fa.jpg

如果随着迭代次数变多,J(向量w,b)增大,那就是学习率取值错误或代码错误

学习率太小或太大同时可以收敛,但迭代次数变多

学习率的选择

迭代大起大落:不正常,可能学习率过大或代码书写错误

c80eda7dd4f729b239662a917471eb8.jpg

在足够小的学习率alpha下,成本函数J应该每次迭代都是减小的趋势

下面举一个例子:教授说明大概每次取前面的3倍,直到成本函数尽可能小的情况下alpha尽量大

c9d9a4f730e04b26798dde488276351.jpg

特征工程

9eb301647c99b752e8916749e3350c7.jpg

根据房子已知的两个特征,frontage和depth列出式子,预测房子价格

通过两个特征,创建新特征 area 是所谓特征工程的例子 通过转换或结合原有特征来设计新功能

多项式回归(引入2次方、3次方)

75bf7291a90bbd000715ffe80d6306c.jpg

x的范围过大的情况下,如果想要运用梯度下降,那么需要

  1. 提前使用特征缩放去将特征转换为可比较的值范围
  2. 使用平方根的方法确定曲线一直向上扬

84772a962009e42347f2259ac4f761b.jpg

相关Lab为Lab4

image.png