多元线性回归
上图为已知数据信息,每一列为特征向量。分别设为x1,x2,x3...xn等等
每一行为训练示例
- xj:特征列表
- n:特征的总数 = 4
- 上标i代表第i个1训练示例 第i个训练示例的所有特征的向量 对应写成后边形式 多称为行向量
- 第三列的第二个值 楼层列的第二个特征值
将数据代入式子的结果如下图
上面的箭头提醒我们它是一个向量
拓展后如下图,点积
这种具有多个输入特征的线性回归模型的名称是多元线性回归(multiple linear regression)
向量化
用了四个方法来说明运算
右下角 np.dot() 直接运算较好 对于两个行向量,dot是对应元素相乘,二维以上则是矩阵乘法
最快的原因是可以在任何计算机上运行,如果是在GPU,一个图形处理器单元也会更好
以下是向量化实现的内部逻辑
左边是没有向量化的运算,运算是一步一步的,有关于时间戳
右边是NumPy中的这个功能是在计算机硬件中通过向量化实现 不需要格外的步骤 最后将其加起来就可以了
以下是一个具体的例子:(查找相关文章后做的总结)
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循环这个控制结构本身。事实上,“慢”往往是全方位的。
举例来说,要计算一组向量,首先就要存储它。
- 存储:
- 对C/C++来说,就存在“数组”里;而它的数组,就是赤裸裸的一片连续内存区域;区域中每若干个字节就存储了一个数值数据。这种结构CPU处理起来最为方便快捷,且cache友好(若cache不友好就可能慢数倍甚至数十倍)。
- Java等其它语言就要稍逊一筹。因为它的“数组”是“真正的数组”;相对于“连续内存区域”,“真正的数组”就不得不在每次访问时检查数组下标有无越界。这个检查开销不大,但也不小。好处是不用像C/C++那样,整天担心缓冲区溢出了。
- 为了迁就初学者,它去掉了“变量声明”以及“数据类型”——于是它的用户再也用不着、也没法写 int xxx了。随便什么数据,咱想存就存,乌拉!
python快的是编程速度,运行速度可以各种优化,这才是胶水的魅力。如果单单用python搞科学计算的会权衡编程和运行时间,对这个语言也是一种侮辱
用于多元线性回归的梯度下降法
单特征与多特征对比
特征缩放 Feature scaling
举例子说明w1和w2得出的值需要互换
导致反复横跳的根本原因是横纵坐标需要的最合适步长不一样,为什么不一样呢?因为横纵坐标超别太大(0到1 和 10到100 的差别)
下面的图是从新标度,使得x,y的范围在都在0到1中间,得出更接近于圆,梯度下降可以找到一条更直接的通往全局最小值的路径
归一化 (0 到 1)
举例说明限制范围从0到1中间
Mean normalization平均归一化(-1 到 1)
- 算出u1值,u2值(u代表平均值,即所有训练特征该列值的平均)
- 根据式子带入数值
Z-score normalization
- 算出标准差 平均值
- 根据公式计算
如果你的值在-1到1是图中看起来很空,那就把值再缩小一些
所有的一切都是为了需要,不必局限于-1到1什么的,两个值的数量级与范围相对一致
判断梯度下降是否收敛
learning curve学习曲线
如果随着迭代次数变多,J(向量w,b)增大,那就是学习率取值错误或代码错误
学习率太小或太大同时可以收敛,但迭代次数变多
学习率的选择
迭代大起大落:不正常,可能学习率过大或代码书写错误
在足够小的学习率alpha下,成本函数J应该每次迭代都是减小的趋势
下面举一个例子:教授说明大概每次取前面的3倍,直到成本函数尽可能小的情况下alpha尽量大
特征工程
根据房子已知的两个特征,frontage和depth列出式子,预测房子价格
通过两个特征,创建新特征 area 是所谓特征工程的例子 通过转换或结合原有特征来设计新功能
多项式回归(引入2次方、3次方)
x的范围过大的情况下,如果想要运用梯度下降,那么需要
- 提前使用特征缩放去将特征转换为可比较的值范围
- 使用平方根的方法确定曲线一直向上扬
相关Lab为Lab4