大叔学ML第二:线性回归
基本形式
线性回归非常直观简洁,是一种常用的回归模型,大叔总结如下:
设有样本\(X\)形如:
x_1^{(1)} & x_2^{(1)} & \cdots &x_n^{(1)}\\
x_1^{(2)} & x_2^{(2)} & \cdots & x_n^{(2)}\\
\vdots & \vdots & \vdots & \vdots\\
x_1^{(m)} & x_2^{(m)} & \cdots & x_n^{(m)}\\
\end{pmatrix} \]
对应的标记\(\vec{y}\)形如:
y^{(1)} \\
y^{(2)} \\
\vdots \\
y^{(m)} \\
\end{pmatrix}\]
其中,矩阵\(X\)的每一行表示一个样本,一共有m个样本;每列表示样本的一个属性,共有n个属性。设假设函数
\]
设\(x_0=1\),则(1)式重新写为
\]
定义代价函数(均方误差)
\]
即:
\]
这里的分母乘以2并没有意义,只是为了求导后正好约掉。另外,其实求绝对值之和更直观,但是计算不方便,求平方后再求和效果是一样的,而且计算非常容易。我们的目标是根据样本数据求出使得代价函数取值最小的参数\(\vec\theta\),均方误差越小,说明以\(\vec\theta\)为参数的线性函数拟合样本的能力越强
求解参数\(\vec\theta\)
梯度下降法
关于梯度下降法可参考 大叔学ML第一:梯度下降
由于代价函数是一个凸函数,可以用梯度下降法找到最小值。由于用到梯度,首先对\(\theta_0\)、\(\theta_1\)、\(\theta_2\)直到\(\theta_n\)求偏导:
- \(\frac{\partial}{\partial\theta_0}j(\theta_0,\theta_1\dots \theta_n) = \frac{1}{m}\sum_{k=1}^m(\theta_0x_0^{(k)} + \theta_1x_1^{(k)} + \dots+ \theta_nx_n^{(k)} - y^{(k)})x_0^{(k)}\)
- \(\frac{\partial}{\partial\theta_1}j(\theta_0,\theta_1\dots \theta_n) = \frac{1}{m}\sum_{k=1}^m(\theta_0x_0^{(k)} + \theta_1x_1^{(k)} + \dots+ \theta_nx_n^{(k)}- y^{(k)})x_1^{(k)}\)
- \(\dots\)
- \(\frac{\partial}{\partial\theta_n}j(\theta_0,\theta_1\dots \theta_n) = \frac{1}{m}\sum_{k=1}^m(\theta_0x_0^{(k)} + \theta_1x_1^{(k)} + \dots+ \theta_nx_n^{(k)}- y^{(k)})x_n^{(k)}\)
可归纳为:\(\frac{\partial}{\partial\theta_n}j(\theta_0,\theta_1\dots \theta_n) = \frac{1}{m}\sum_{k=1}^m(\theta_0x_0^{(k)} + \theta_1x_1^{(k)} + \dots+ \theta_nx_n^{(k)}- y^{(k)})x_n^{(k)}\tag{4}\)
万事俱备,现在可以编程了。创建一组测试数据,每组数据包括3个属性,我们来编码拟合出一个线性函数:
import numpy as np
def gradient(X, Y, m, theta):
''' 求theta位置的梯度.
Args:
X: 样本
Y: 样本标记
m: 样本数
theta: 欲求梯度的位置
Returns:
gi: theta处函数的梯度值
'''
theta_size = np.size(theta)
g = np.zeros(theta_size)
for i in range(theta_size):
gi = 0 #第i个theta分量对应的偏导
for j in range(m):
gi += ((np.dot(X[j], theta) - Y[j]) * X[j, i])
gi = gi / m
g[i] = gi
return g
def gradient_descent(X, Y, step = 0.02, threshold = 0.01):
''' 梯度下降法求使代价函数最小的 theta
Args:
X: 样本
Y: 样本标记
step:步长
threshold:梯度模长阈值,低于此值时停止迭代
Returns:
theta: 使代价函数取最小值的theta
'''
theta = np.random.rand(4)
grad = gradient(X, Y, np.size(X, 0), theta)
norm = np.linalg.norm(grad)
while(norm > threshold):
theta -= step * grad
grad = gradient(X, Y, np.size(X, 0), theta)
norm = np.linalg.norm(grad)
return theta
''' 以下是测试数据 '''
# 测试用线性函数
def linear_function(x1, x2, x3):
result = 1 + 2 * x1 + 3 * x2 + 4 * x3
result = result + np.random.rand() # 噪音
return result
# 计算函数值
def calculate(X):
rowsnumber = np.size(X, axis = 0)
Y = [linear_function (X[i, 0], X[i, 1], X[i, 2]) for i in range(0, rowsnumber)]
return Y
if __name__ == "__main__":
row_count = 500
X = np.random.randint(0, 10, (row_count, 3)) # 随机产生row_count个样本
Y = calculate(X) # 计算标记
X0 = np.ones((row_count, 1))
X = np.hstack((X0, X)) # 补充一列1
theta = gradient_descent(X, Y)
print('theta is ', theta)
运行结果:theta is [1.41206515 2.00558441 3.0013728 4.00684577]
上面的迭代方法被称为批量梯度下降法,参考式(4),计算梯度时用到了所有的样本。梯度下降法还有个简化的版本,叫做随机梯度下降法,每次计算梯度时只随机使用一个样本,而不是所有样本,这样可以加快计算速度。将式(4)修改为:
\]
其中:\(1 \leq k \leq m\)
将上面Python代码中的方法gradient
替换一下:
def gradient_sgd(X, Y, m, theta):
''' 求theta位置的梯度.
Args:
X: 样本
Y: 样本标记
m: 样本数
theta: 欲求梯度的位置
Returns:
gi: theta处函数的梯度值
'''
theta_size = np.size(theta)
g = np.zeros(theta_size)
for i in range(theta_size):
random_Index = np.random.randint(1, m + 1)
gi = ((np.dot(X[random_Index], theta) - Y[random_Index]) * X[random_Index, i])
g[i] = gi
return g
运行结果:
theta is [1.43718942 2.00043557 3.00620849 4.00674728]
感觉像是飞起来。随机梯度下降法肯定没有批量梯度下降法准确,所有还有第三种下降法,叫做小批量梯度下降法,介于批量梯度下降法和随机梯度下降法之间,每次计算梯度使用随机的一小批样本,此处不再code说明。
正规方程导法
因为代价函数是个凸函数,那么我们可以对代价函数求导,让其导数等于0的点即为最小值点。
为方便计算,我们在前面增加了一个值恒等于1的\(x_0\),这样就把线性函数的偏置项去掉了,参考式(2),重新定义矩阵\(X\)为:
x_0^{(1)} & x_1^{(1)} & x_2^{(1)} & \cdots &x_n^{(1)}\\
x_0^{(2)} &x_1^{(2)} & x_2^{(2)} & \cdots & x_n^{(2)}\\
\vdots & \vdots & \vdots & \vdots & \vdots\\
x_0^{(m)} & x_1^{(m)} & x_2^{(m)} & \cdots & x_n^{(m)}\\
\end{pmatrix}\]
代价函数式(3)等价于:
\]
化简式(6):
J(\vec\theta)&=\frac{1}{2m}||X\vec\theta - \vec{y}||^2 \\
&=\frac{1}{2m}(X\vec\theta - \vec{y})^T(X\vec\theta - \vec{y}) \\
&=\frac{1}{2m}(\vec\theta^TX^T - \vec{y}^T)(X\vec\theta - \vec{y}) \\
&=\frac{1}{2m}(\vec\theta^TX^TX\vec\theta - \vec\theta^TX^T\vec{y}- \vec{y}^TX\vec\theta + \vec{y}^T\vec{y})\\
&=\frac{1}{2m}(\vec\theta^TX^TX\vec\theta - 2\vec{y}^TX\vec\theta + \vec{y}^T\vec{y})\\
\end{align}\]
对\(\vec\theta\)求导:
\]
令其等于0,得:$$\vec\theta=(XTX){-1}X^T\vec{y}\tag{7}$$
将上面的Python代码改为:
# 测试用线性函数
def linear_function(x1, x2, x3):
result = 1 + 2 * x1 + 3 * x2 + 4 * x3
result = result + np.random.rand() # 噪音
return result
# 计算函数值
def calculate(X):
rowsnumber = np.size(X, axis = 0)
Y = [linear_function (X[i, 0], X[i, 1], X[i, 2]) for i in range(0, rowsnumber)]
return Y
if __name__ == "__main__":
row_count = 500
X = np.random.randint(0, 10, (row_count, 3)) # 随机产生row_count个样本
Y = calculate(X) # 计算标记
X0 = np.ones((row_count, 1))
X = np.hstack((X0, X)) # 补充一列1
theta = np.dot(np.dot(np.linalg.pinv(np.dot(X.T, X)), X.T), np.array(Y).T)
print('theta is ', theta)
运行结果:theta is [1.49522638 1.99801209 2.99704438 4.00427252]
和梯度下降法比较,光速的感觉,那为什么还要用梯度下降法呢?这是因为求矩阵的逆算法复杂度较高,达爷的建议是:如果样本的属性超过一万个,考虑使用梯度下降法。
调用函数库
其实我们也可以直接调用类库的,有很多类库可以做回归算法,比如:
import numpy as np
from sklearn import linear_model
# 测试用线性函数
def linear_function(x1, x2, x3):
result = 1 + 2 * x1 + 3 * x2 + 4 * x3
result = result + np.random.rand() # 噪音
return result
# 计算函数值
def calculate(X):
rowsnumber = np.size(X, axis = 0)
Y = [linear_function (X[i, 0], X[i, 1], X[i, 2]) for i in range(0, rowsnumber)]
return Y
if __name__ == "__main__":
row_count = 500
X = np.random.randint(0, 10, (row_count, 3)) # 随机产生row_count个样本
Y = calculate(X) # 计算标记
regr = linear_model.LinearRegression()
regr.fit(X, np.array(Y).T)
a, b = regr.coef_, regr.intercept_
print(a)
print(b)
运行结果:
[2.00384674 2.99234723 3.99603084]
1.5344826581936104
和我们自己算的差不多吧。还有很多其他的类库可以调用,大叔没有一一去找。可能通常只要调用类库就足够了,不需要我们自己写,不过还是知道原理比较好,遇到问题才好对症下药。
我是这样理解的:我们能够调用到的常见的(广义)线性回归库,其实内部都是用直接求导法实现的(没有看过源码,猜测是直接求导,如果是梯度下降,不太可能自动算出步长),如果样本的属性比较少,比如少于一万个,调用类库就好,类库肯定比我们大部分人自己写的强,但是当样本属性非常多时,用直接求导法求解速度太慢,这时才需要我们自己写梯度下降代码。
大叔学ML第二:线性回归的更多相关文章
- 大叔学ML第四:线性回归正则化
目录 基本形式 梯度下降法中应用正则化项 正规方程中应用正则化项 小试牛刀 调用类库 扩展 正则:正则是一个汉语词汇,拼音为zhèng zé,基本意思是正其礼仪法则:正规:常规:正宗等.出自<楚 ...
- 大叔学ML第五:逻辑回归
目录 基本形式 代价函数 用梯度下降法求\(\vec\theta\) 扩展 基本形式 逻辑回归是最常用的分类模型,在线性回归基础之上扩展而来,是一种广义线性回归.下面举例说明什么是逻辑回归:假设我们有 ...
- 大叔学ML第三:多项式回归
目录 基本形式 小试牛刀 再试牛刀 调用类库 基本形式 上文中,大叔说道了线性回归,线性回归是个非常直观又简单的模型,但是很多时候,数据的分布并不是线性的,如: 如果我们想用高次多项式拟合上面的数据应 ...
- 大叔学ML第一:梯度下降
目录 原理 实践一:求\(y = x^2 - 4x + 1\)的最小值 实践二:求\(z = x^2 + y^2 + 5\)的最小值 问答时间 原理 梯度下降是一个很常见的通过迭代求解函数极值的方法, ...
- [老老实实学WCF] 第二篇 配置WCF
老老实实学WCF 第二篇 配置WCF 在上一篇中,我们在一个控制台应用程序中编写了一个简单的WCF服务并承载了它.先回顾一下服务端的代码: using System; using System.Col ...
- 跟我学SpringCloud | 第二篇:注册中心Eureka
Eureka是Netflix开源的一款提供服务注册和发现的产品,它提供了完整的Service Registry和Service Discovery实现.也是springcloud体系中最重要最核心的组 ...
- ml的线性回归应用(python语言)
线性回归的模型是:y=theta0*x+theta1 其中theta0,theta1是我们希望得到的系数和截距. 下面是代码实例: 1. 用自定义数据来看看格式: # -*- coding:utf ...
- 简单学C——第二天
控制结构(-) 相信大家对流程图肯定很熟悉.下面我将介绍的正是关于此方面的,c语言中,控制结构大体分为选择结构和循环结构. 一.选择结构: 先贴出一般用于选择结构的语 ...
- (转)[老老实实学WCF] 第二篇 配置WCF
第二篇 配置WCF 在上一篇中,我们在一个控制台应用程序中编写了一个简单的WCF服务并承载了它.先回顾一下服务端的代码: using System; using System.Collections. ...
随机推荐
- python note 13 内置函数
1. lst = ["白蛇传","骷髅叹","庄周闲游"] it = lst.__iter__() print(it.__next__()) ...
- redis常用服务安装部署
常用服务安装部署 学了前面的Linux基础,想必童鞋们是不是更感兴趣了?接下来就学习常用服务部署吧! 安装环境: centos7 + vmware + xshell 即将登场的是: mysql(m ...
- RNAseq测序reads定位
RNAseq测序reads定位 发表评论 3,210 A+ 所属分类:Transcriptomics 收 藏 获得RNA-seq的原始数据后,首先需要将所有测序读段通过序列映射(mapping) ...
- Cannot retrieve metalink for repository: epel/x86_64. Please verify its path and try again
虚拟机恢复快照后,使用yum安装软件,提示下面的信息,开始以为是yum源的问题或者DNS的问题,但是无果,最后再看一下服务器的时间,坑了,还原快照,时间变成以前的了. [root@localhost ...
- 判断作业完成之 读取log 脚本
tail 用于读取文件末尾 -n 后边加行数 -f 为持续追踪,实时输出 NUMECA 输出最后两行 ...done in 727.84-sec (STARTING NUMECA FLOW SOLVE ...
- python3 tkinter添加图片和文本
在前面一篇文章基础上,使用tkinter添加图片和文本.在开始之前,我们需要安装Pillow图片库. 一.Pillow的安装 1.方法一:需要下载exe文件,根据下面图片下载和安装 下载完 ...
- python3中 tkinter模块创建window窗体、添加按钮、事务处理、创建菜单等的使用
开始接触桌面图形界面编程,你可以到安装路径 \lib\tkinter 打开__init__.py 文件了解tkinter 1 tkinter 模块创建窗体,代码如下截图: 运行结果,如有右图显 ...
- monkey测试样例
我们通过在CMD窗口中执行: adb shell monkey {+命令参数}来进行Monkey测试了.首先,我们准备了一个有bug的项目CityWeather:通过测试这个项目(源码在附件文件夹中) ...
- easyui_validatebox常用验证
$.extend($.fn.validatebox.defaults.rules, { idcard: {// 验证身份证 validator: function (value) { return / ...
- 使用rar把程序打包成一个exe
根目录--全部文件--右键添加到压缩文件 常规--创建自解压压缩文件 高级--自解压选项 解压路径--Finger(自己写)--在"Program Files"中创建 设置--解压 ...