机器学习——回归算法
一、线性回归算法概述
1.背景引入
银行贷款,如下图,共有5组数据,每组数据具有两个特征:工资-年龄。通过这两个属性:工资-年龄,预测得到的贷款额。
- 特征
- 工资
- 年龄
有监督的机器学习算法可以分作两个类别:回归和分类
- 通俗的解释
如果背景转换,来到银行是为了得知自己能不能得到一笔贷款,那么这个问题就是属于分类问题了。即最后得到的是一个类别。
我们做下面的规定: x 1 , x 2 x_1,x_2 x1,x2是我们的两个特征(年龄,工资), y y y是银行最终会贷款给我们多少。我们要做的就是找到最合适的一条线(面),来最好的拟合我们的数据点。
设 θ 1 \theta_1 θ1是年龄的 参数, θ 2 \theta_2 θ2是工资的参数;
拟合平面为: h θ ( x ) = θ 0 + θ 1 x 1 + θ 2 x 2 , θ 0 h_\theta (x)=\theta_0+\theta_1x_1+\theta_2 x_2 \,,\theta_0 hθ(x)=θ0+θ1x1+θ2x2,θ0是偏置项
整合得到: h θ ( x ) = ∑ i = 0 n θ i x i = θ T x h_\theta (x)=\sum\limits_{i=0}^n\theta_ix_i=\theta^Tx hθ(x)=i=0∑nθixi=θTx
对于上面的求和项的式子,实际上我们引入了一个 x 0 x_0 x0,但是 x 0 x_0 x0恒等于1,相当于在上面的表格中加入了一列全是1的特征。以方便进行矩阵的计算。
假如我们现在已经得到了 θ 0 , θ 1 和 θ 2 \theta_0,\theta_1和\theta_2 θ0,θ1和θ2的具体值,又有一组待预测的数据:年龄:20,工资:1000,那么代入方程就可以得到银行的贷款金额为: h θ ( x ) = θ 0 + 1000 θ 1 + 20 θ 2 h_\theta (x)=\theta_0+1000\theta_1+20\theta_2 \, hθ(x)=θ0+1000θ1+20θ2
- 误差的分析
真实值和预测值之间必然是存在差异的,用 ε \varepsilon ε来表示误差。
对于每一个样本: y ( i ) = θ T x ( i ) + ε ( i ) y^{(i)}=\theta^Tx^{(i)}+\varepsilon^{(i)} y(i)=θTx(i)+ε(i)
对于这些误差我们有下面的假设:
-
ε ( i ) \varepsilon^{(i)} ε(i)是相互独立并且具有相同分布的,并且服从均值为0,方差为 θ 2 \theta^2 θ2的高斯分布(正态分布)
-
独立:张三和李四前来贷款,他俩的贷款额没有关系
-
同分布:来的是一家银行
-
高斯分布:银行可能多给,也可能少给,但是绝大多数情况下,这个浮动不会太大,极小的情况下浮动会比较大,但符合正常情况
因为是服从均值为0的高斯分布,所以(2)式中的 μ = 0 \mu=0 μ=0,因为我们最后享有求解的参数是 θ \theta θ,所以我们使用 θ \theta θ去表示 ε i \varepsilon_i εi即可。对于代入得到的第三个式子,左边其实是式(1) y ( i ) − θ T x ( i ) y^{(i)}-\theta^Tx^{(i)} y(i)−θTx(i)的表达形式,我们将其写作 P ( y ( i ) ∣ x ( i ) ; θ ) P(y^{(i)}|x^{(i)};\theta) P(y(i)∣x(i);θ)。
如果有概率论与数理统计学习基础,那么我们就可以将代入结果式作为一个似然函数去理解了,我们要求的就是当参数 θ \theta θ取多少时,右边的概率达到最大值,及是要利用极大似然估计进行一个参数估计的过程。
线性回归的内核:极大似然估计的过程
那么我们令 A = m l o g 1 2 π σ , B = 1 σ 2 1 2 ∑ i = 1 m ( y ( i ) − θ T x ( i ) ) 2 A=mlog\frac{1}{\sqrt{2\pi}\sigma},B=\frac{1}{\sigma^2}\frac{1}{2}\sum\limits_{i=1}^{m}(y^{(i)}-\theta^T x^{(i)})^2 A=mlog2πσ1,B=σ2121i=1∑m(y(i)−θTx(i))2,因为A≥0,B≥0,要使似然函数极大化,只能是B越小越好,所以我们要做的就是使B,也就是上面的 J ( θ ) J(\theta) J(θ)达到最小
其实也就是所有的点的误差的和的平方达到最小值。
- 评估方法
当残差的平方和越小越好,所以当 R 2 R^2 R2越接近与1结果越好。
2.梯度下降原理
在上面的线性回归的例子中,我们可以直接通过求导得到 θ \theta θ的值,但面对更复杂的函数时,有时并不是直接求偏导求得最小值这么简单,上述的线性回归其实是一个特例。而常规的求解方法则是利用梯度下降原理求解。
梯度下降法的关键字是梯度,梯度的概念在高等数学中已经有所学习,多元函数的梯度就是对所有的变量求偏导,各个变量偏导数组合成的一个向量。以函数
z
=
f
(
x
,
y
)
z=f(x,y)
z=f(x,y)为例,梯度则为向量:
{
∂
f
∂
x
,
∂
f
∂
y
}
=
f
x
(
x
,
y
)
i
⃗
+
f
y
(
x
,
y
)
j
⃗
上
式
又
记
作
g
r
a
d
(
x
,
y
)
,
就
是
函
数
的
梯
度
。
\{\frac{\partial f}{\partial x},\frac{\partial f}{\partial y}\}=f_x(x,y)\vec{i}+f_y(x,y)\vec{j}\\ 上式又记作grad(x,y),就是函数的梯度。
{∂x∂f,∂y∂f}=fx(x,y)i+fy(x,y)j上式又记作grad(x,y),就是函数的梯度。
在高等数学中有结论:梯度的方向就是函数值变化最快的方向。结论不再予以证明,证明附上链接:
至于梯度下降的过程,我们引用一个例子作为讲解:
我们假设有一个目标函数:
J ( Θ ) = θ 1 2 + θ 2 2 J( Θ ) = θ_1^2 + θ_2^2 J(Θ)=θ12+θ22
现在要通过梯度下降法计算这个函数的最小值。我们通过观察就能发现最小值其实就是 (0,0)点。
但是接下来,我们会从梯度下降算法开始一步步计算到这个最小值
我们假设初始的起点为:
Θ 0 = ( 1 , 3 ) \Theta^0 = (1, 3) Θ0=(1,3)
初始的学习率为:(这里的学习率可以理解成为梯度下山的过程中沿着梯度方向每一步迈多大)
α = 0.1 \alpha = 0.1 α=0.1
函数的梯度为:
$\triangledown J(\Theta ) = \left < 2\theta_{1},2\theta_{2} \right >\$
进行多次迭代:
Θ
0
=
(
1
,
3
)
Θ
1
=
Θ
0
−
α
▽
J
(
Θ
)
=
(
1
,
3
)
−
0.1
∗
(
2
,
6
)
=
(
0.8
,
2.4
)
Θ
2
=
(
0.8
,
2.4
)
−
0.1
∗
(
1.6
,
4.8
)
=
(
0.64
,
1.92
)
Θ
3
=
(
0.5124
,
1.536
)
Θ
4
=
(
0.4096
,
1.228800000000001
)
Θ
⋮
Θ
10
=
(
0.1073741824000003
,
0.32212254720000005
)
⋮
Θ
50
=
(
1.141798154164342
e
−
05
,
3.42539442494306
e
−
05
)
⋮
Θ
100
=
(
1.6296287810675902
e
−
10
,
4.8888886343202771
e
−
10
)
\Theta^0 = (1, 3)\\ \Theta^1 = \Theta^0 - \alpha\triangledown J(\Theta ) = (1,3) - 0.1*(2, 6)=(0.8, 2.4)\\ \Theta^2 = (0.8, 2.4) - 0.1*(1.6, 4.8)=(0.64, 1.92)\\ \Theta^3 =(0.5124, 1.536)\\ \Theta^4 =(0.4096, 1.228800000000001)Θ\\ \vdots\\ \Theta^{10} =(0.1073741824000003, 0.32212254720000005)\\ \vdots\\ \Theta^{50} =(1.141798154164342e^{-05}, 3.42539442494306e^{-05})\\ \vdots\\ \Theta^{100} =(1.6296287810675902e^{-10}, 4.8888886343202771e^{-10})\\
Θ0=(1,3)Θ1=Θ0−α▽J(Θ)=(1,3)−0.1∗(2,6)=(0.8,2.4)Θ2=(0.8,2.4)−0.1∗(1.6,4.8)=(0.64,1.92)Θ3=(0.5124,1.536)Θ4=(0.4096,1.228800000000001)Θ⋮Θ10=(0.1073741824000003,0.32212254720000005)⋮Θ50=(1.141798154164342e−05,3.42539442494306e−05)⋮Θ100=(1.6296287810675902e−10,4.8888886343202771e−10)
我们发现,已经基本靠近函数的最小值点.(详细其他附上参考博客链接:)
而梯度下降的概念就是沿着梯度的反方向,函数值变化得最快且向最小值的方向靠拢。梯度下降就是在做下面的过程:
通常的迭代次数可以达到1w~10w次
下面给出三种梯度下降的方法:
- 批量梯度下降
- 随机梯度下降
- 小批量梯度下降
3.学习率对结果的影响
学习率可以理解为在梯度下山的过程中,沿着梯度的方向每一次迈出多大的步长,可以由上面具体的例子理解一下。学习率是不能过大也不能过小的。
学习率若过大,则迭代不平稳,假如有这样的一条曲线想象一下,下面这个下山的过程,你一步就迈过去了,那么也就错过了我们的目标的最小值。
而假如学习率过小,那么收敛的速度较慢,但不一定是一件坏事,所以通常情况下我们选择一个较小的学习率,选择一个较大的迭代次数。现在我们的措施是在开始的时候选择一个稍稍大一点点的学习率加速下降的过程中,在快要接近谷底的时候将学习率减小,一步步逼近最小值,一般从0.01开始。
二、逻辑回归Logistic regression
在背景引入的时候我们谈到有监督的机器学习可以分为:回归和分类,这两大类。而逻辑回归虽然称作回归,但是逻辑回归其实是一个经典的二分类的算法,在众多的分类算法中,比如支持向量机,神经网络等,如果分类效果差不多我们一般会优先选择逻辑回归。原因就是:算法能简单尽量简单,且逻辑回归容易解释。下面我们做具体介绍逻辑回归算法。
1.Sigmoid函数
Sigmoid函数: g ( z ) = 1 1 + e − z g(z)=\frac{1}{1+e^{-z}} g(z)=1+e−z1,函数的自变量为任意实数,但其值域被限定在[0,1]之中。
解释:将任意的输入映射到了[0,1]之间,我们在线性回归中可以得到一个预测值,再将该值映射到Sigmoid函数中,这样就完成了由值到[0,1]的转换,而概率就是一个[0,1]的数,我们也就实现了值到概率的转换,也就是分类的任务。
我们设预测的函数为:
h θ ( x ) = g ( θ T x ) = 1 1 + e − θ T x h_{\theta}(x)=g(\theta^Tx)=\frac{1}{1+e^{-\theta^Tx}} hθ(x)=g(θTx)=1+e−θTx1
其中的 θ T x \theta^Tx θTx就是相当于上述的线性回归的输出值,也就是相当于背景例子中的$\theta_0+\theta_1x_1+\theta_2 x_2 , 将 这 个 输 出 值 作 为 S i g m o i d 函 数 的 输 入 值 , 我 们 可 以 计 算 出 最 终 映 射 在 0 , 1 区 间 上 的 一 个 结 果 ,将这个输出值作为Sigmoid函数的输入值,我们可以计算出最终映射在0,1区间上的一个结果 ,将这个输出值作为Sigmoid函数的输入值,我们可以计算出最终映射在0,1区间上的一个结果h_\theta(x)$。
至于分类任务,则是我们将最后的标签y=1和y=0的概率分别写作
h
θ
(
x
)
h_\theta(x)
hθ(x)和
1
−
h
θ
(
x
)
1-h_\theta(x)
1−hθ(x),以保证两个分类的概率值和为1,即概率的归一化。为了对这个式子进行简写,我们将其写作:
P
(
y
∣
x
,
θ
)
=
(
h
θ
(
x
)
)
y
(
1
−
h
θ
(
x
)
)
1
−
y
P(y|x,\theta)=(h_\theta(x))^y(1-h_\theta(x))^{1-y}
P(y∣x,θ)=(hθ(x))y(1−hθ(x))1−y
对于上式,二分类体现在即当y=1时,概率为
h
θ
(
x
)
h_\theta(x)
hθ(x),当y=0是,概率为
1
−
h
θ
(
x
)
1-h_\theta(x)
1−hθ(x)。
2.似然函数的构造
而接下来我们要做的任务就是利用极大似然估计求解上式的概率最大值,并且利用梯度上升法求解(因为要求的是最大值,而机器学习构造的函数为凸函数,所以求最大值即是要按照梯度的方向进行),引入一个负号转换成为一个梯度下降问题求解。
3.似然函数求解参数
注意每个 θ j \theta_j θj的值组合起来才是最终的梯度方向。
根据上述的过程,我们最后便得到了梯度方向,下一步我们要做的就是根据梯度方向和学习率对参数进行不断地更新,不断的逼近最终的 θ j \theta_j θj的过程,也就是一个梯度下降求解参数的过程。
最后总结一件事,逻辑回归本质其实就是将线性回归的预测值加入Sigmoid函数以映射到概率区间上,得到一个概率,将所有的样本连乘构造出概率函数,对这个概率函数进行极大似然估计参数,得到最后的参数值。
三、使用Python实现逻辑回归
Logistic Regression
The data
我们将建立一个回归模型来预测一个大学生是否被大学录取。假设你是一个大学的管理员,你想根据两次考试的结果来决定每个申请人的录取机会。你有以前的申请人的历史数据,你可以将它作为逻辑回归的训练集。对于每一个培训的例子,你有两个考试的申请人的分数和录取决定。为做到这一点,我们将建立一个分类模型,根据考试成绩估计入学概率。
# 三大件的导入
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
这里导入了os,数据是放在同一路径的data文件夹下的,为了避免不同的操作系统的路径分隔符不同的问题,使用了os.sep来避免。
具体的os.sep的用法参考链接:https://www.cnblogs.com/bit-ter/p/9900940.html
此外,在使用read_csv函数时我们传入了header=None,是因为我们的csv文件并没有列名,第一列就是直接的数据。假如不做设置的话,文件的第一列也就是我们的数据就被当做默认的列名了,所以设置为None,然后自己设置了列名。
# 数据的导入
import os
path = "data" + os.sep + "LogiReg_data.txt"
pdData = pd.read_csv(path, header=None, names=["Exam_1", "Exam_2", "Admitted"])
pdData.head()
Exam_1 | Exam_2 | Admitted | |
---|---|---|---|
0 | 34.623660 | 78.024693 | 0 |
1 | 30.286711 | 43.894998 | 0 |
2 | 35.847409 | 72.902198 | 0 |
3 | 60.182599 | 86.308552 | 1 |
4 | 79.032736 | 75.344376 | 1 |
#输出一下DateFrame的维度
pdData.shape
#由输出结果可以知道,数据是有100个的,每个数据有三个指标,也就上面所述的:两次考试的成绩和最终是否录取的结果
(100, 3)
# 绘制散点图
# 获得正负指标数据
positive = pdData[pdData["Admitted"] == 1]
negative = pdData[pdData["Admitted"] == 0]
fig, ax=plt.subplots(figsize=(10,5))
# 分别绘制出被录取和未被录取的学生的两次成绩的散点图
ax.scatter(positive["Exam_1"], positive["Exam_2"], s=30, c="b", marker="o", label="Admitted")
ax.scatter(negative["Exam_1"], negative["Exam_2"], s=30, c="r", marker="x", label="Not Admitted")
ax.legend()
ax.set_xlabel("Exam 1 Score")
ax.set_ylabel("Exam 2 Score")
Text(0, 0.5, 'Exam 2 Score')
The logistic regression
目标:建立分类器(求解三个参数 θ 0 , θ 1 , θ 2 \theta_0,\theta_1,\theta_2 θ0,θ1,θ2:两个权重参数,一个偏置量)
设定阈值,根据阈值判断结果
要完成的模块
- Sigmoid:映射到概率的函数
- model:返回预测结果值
- cost:根据参数计算损失
- gradient:计算每个参数的梯度方向
- descent:进行参数更新
- accuracy:计算精度
Sigmoid函数
g ( z ) = 1 1 + e − z g(z)=\frac{1}{1+e^{-z}} g(z)=1+e−z1
def sigmoid(z):
return 1 / (1 + np.exp(-z))
#对sigmoid函数曲线进行展示
#创建一个在-10到10上均匀分布的含有20个数的向量
nums = np.arange(-10, 10, step=1)
fig, ax = plt.subplots(figsize=(8,4))
ax.plot(nums, sigmoid(nums), "r")
[<matplotlib.lines.Line2D at 0x1b203c06730>]
Sigmoid
- g: R → [ 0 , 1 ] R\rightarrow [0,1] R→[0,1]
- g(0) = 0.5
- g( − ∞ -\infty −∞) = 0
- g( ∞ \infty ∞) = 1
# model: 预测函数设计
#返回叉乘函数值,即每个样本会得到一个预测值,返回这个预测值对应的sigmoid值
def model(X, theta):
return sigmoid(np.dot(X, theta.T))
# np.dot就是一个矩阵的乘法
( θ 0 , θ 1 , θ 2 ) × { 1 x 1 x 2 } = θ + θ 1 x 1 + θ 2 x 2 (\theta_0,\theta_1,\theta_2)\times \left\{ \begin{matrix} 1\\ x_1 \\ x_2 \end{matrix} \right\} = \theta + \theta_1 x_1 +\theta_2 x_2 (θ0,θ1,θ2)×⎩⎨⎧1x1x2⎭⎬⎫=θ+θ1x1+θ2x2
# X,y,theta数据的构建
# 数据1的填充 x0特征恒为1
pdData.insert(0, "Ones", 1)
#获取x数据和y数据
orig_data = pdData.values
cols = orig_data.shape[1] #矩阵.shape[0]返回矩阵的行数,矩阵.shape[1]返回矩阵的列数
X = orig_data[:,0:cols-1] #X值是由矩阵的第一列到第三列,所以切片为0:3
y = orig_data[:,cols-1:cols] #y值就是最后一列的值
#构造出一个1行3列的theta参数
theta = np.zeros([1, 3])
下面的两个cell的操作是为了展示一下数据,确保导出的数据是没有问题的
X[:5]
array([[ 1. , 34.62365962, 78.02469282],
[ 1. , 30.28671077, 43.89499752],
[ 1. , 35.84740877, 72.90219803],
[ 1. , 60.18259939, 86.3085521 ],
[ 1. , 79.03273605, 75.34437644]])
y[:5]
array([[0.],
[0.],
[0.],
[1.],
[1.]])
theta
array([[0., 0., 0.]])
X.shape, y.shape, theta.shape
((100, 3), (100, 1), (1, 3))
损失函数
将对似然函数取负号
D
(
h
θ
(
x
)
,
y
)
=
−
y
l
o
g
(
h
θ
(
x
)
)
−
(
1
−
y
)
l
o
g
(
1
−
h
θ
(
x
)
)
D(h_\theta(x),y)\,=\, -ylog(h_\theta(x))-(1-y)log(1-h_\theta(x))
D(hθ(x),y)=−ylog(hθ(x))−(1−y)log(1−hθ(x))
求平均损失
J
(
θ
)
=
1
n
∑
i
=
1
n
D
(
h
θ
(
x
i
)
,
y
i
)
J(\theta)=\frac{1}{n}\sum\limits_{i=1}^nD(h_\theta(x_i),y_i)
J(θ)=n1i=1∑nD(hθ(xi),yi)
# 对于J(\theta)函数的定义,
# 分成左边减去右边,然后除以数据数
def cost(X, y, theta):
left = np.multiply(-y, np.log(model(X, theta)))
right = np.multiply(1 - y, np.log(1 - model(X, theta)))
return np.sum(left - right) / (len(X))
# 计算cost值:
cost(X, y, theta)
0.6931471805599453
计算梯度
∂ J ∂ θ j = − 1 m ∑ i = 1 n ( y i − h θ ( x i ) ) x i j \frac{\partial J}{\partial \theta_j}=-\frac{1}{m}\sum\limits_{i=1}^n(y_i-h_\theta(x_i))x_{ij} ∂θj∂J=−m1i=1∑n(yi−hθ(xi))xij
# 梯度的计算
def gradient(X, y, theta):
grad = np.zeros(theta.shape) #一共有三个参数\theta_0,1,2,所以梯度也是个三维向量, 进行参数占位
error = (model(X, theta)- y).ravel() # 本应该是y_i - model,这里将负号放入各项,计算出差值
for j in range(len(theta.ravel())): #for each parmeter 对每个theta进行求偏导
term = np.multiply(error, X[:,j]) #对特定的第j列
grad[0, j] = np.sum(term) / len(X)
return grad
梯度下降法求解
比较三种不同的梯度下降方式
- 批量梯度下降
- 随机梯度下降
- 小批量梯度下降
#停止策略的编码
STOP_ITER = 0 #迭代次数停止策略:更新了一次参数相当于完成了一次迭代
STOP_COST = 1 #损失值变化停止策略:损失值小到一定了相当于停止
STOP_GRAD = 2 #梯度变化停止策略:梯度变化很小的情况下停止
#threshold:阈值:cost或者是grad
def stopCriterion(type, value, threshold):
#设定三种不同的停止策略
if type == STOP_ITER:
return value > threshold
elif type == STOP_COST:
return abs(value[-1]-value[-2]) < threshold
elif type == STOP_GRAD:
return np.linalg.norm(value) < threshold
# 数据的洗牌:使模型的散化能力更强
import numpy.random
#洗牌
def shuffleData(data):
np.random.shuffle(data)
cols = data.shape[1]
X = data[:, 0:cols-1]
y = data[:, cols-1:]
return X,y
import time
def descent(data, theta, batchSize, stopType, thresh, alpha):
#梯度下降求解
init_time = time.time()
i = 0 # 迭代次数
k = 0 # batch
X, y = shuffleData(data)
grad = np.zeros(theta.shape) # 计算的梯度
costs = [cost(X, y, theta)] # 损失值
while True:
grad = gradient(X[k:k+batchSize], y[k:k+batchSize], theta)
k += batchSize #取batch数量个数据
if k >= n:
k = 0
X, y = shuffleData(data) #重新洗牌
theta = theta - alpha*grad # 参数更新
costs.append(cost(X, y, theta)) # 计算新的损失
i += 1
if stopType == STOP_ITER: value = i
elif stopType == STOP_COST: value = costs
elif stopType == STOP_GRAD: value = grad
if stopCriterion(stopType, value, thresh): break
return theta, i-1, costs, grad, time.time() - init_time
def runExpe(data, theta, batchSize, stopType, thresh, alpha):
#import pdb; pdb.set_trace();
theta, iter, costs, grad, dur = descent(data, theta, batchSize, stopType, thresh, alpha)
name = "Original" if (data[:,1]>2).sum() > 1 else "Scaled"
name += " data - learning rate: {} - ".format(alpha)
if batchSize==n: strDescType = "Gradient"
elif batchSize==1: strDescType = "Stochastic"
else: strDescType = "Mini-batch ({})".format(batchSize)
name += strDescType + " descent - Stop: "
if stopType == STOP_ITER: strStop = "{} iterations".format(thresh)
elif stopType == STOP_COST: strStop = "costs change < {}".format(thresh)
else: strStop = "gradient norm < {}".format(thresh)
name += strStop
print ("***{}\nTheta: {} - Iter: {} - Last cost: {:03.2f} - Duration: {:03.2f}s".format(
name, theta, iter, costs[-1], dur))
fig, ax = plt.subplots(figsize=(12,4))
ax.plot(np.arange(len(costs)), costs, 'r')
ax.set_xlabel('Iterations')
ax.set_ylabel('Cost')
ax.set_title(name.upper() + ' - Error vs. Iteration')
return theta
不同的停止策略
设定迭代次数
#选择的梯度下降方法是基于所有样本的
n=100
runExpe(orig_data, theta, n, STOP_ITER, thresh=5000, alpha=0.000001)
***Original data - learning rate: 1e-06 - Gradient descent - Stop: 5000 iterations
Theta: [[-0.00027127 0.00705232 0.00376711]] - Iter: 5000 - Last cost: 0.63 - Duration: 0.99s
array([[-0.00027127, 0.00705232, 0.00376711]])
根据损失值停止
runExpe(orig_data, theta, n, STOP_COST, thresh=0.000001, alpha=0.001)
***Original data - learning rate: 0.001 - Gradient descent - Stop: costs change < 1e-06
Theta: [[-5.13364014 0.04771429 0.04072397]] - Iter: 109901 - Last cost: 0.38 - Duration: 20.91s
array([[-5.13364014, 0.04771429, 0.04072397]])
根据梯度变化停止
设定阈值0.05,差不多需要迭代40000次
runExpe(orig_data, theta, n, STOP_GRAD, thresh=0.05, alpha=0.001)
***Original data - learning rate: 0.001 - Gradient descent - Stop: gradient norm < 0.05
Theta: [[-2.37033409 0.02721692 0.01899456]] - Iter: 40045 - Last cost: 0.49 - Duration: 7.35s
array([[-2.37033409, 0.02721692, 0.01899456]])
对不同的梯度下降方法进行对比
Stochastic descent 小样本迭代
runExpe(orig_data, theta, 1, STOP_ITER, thresh=5000, alpha=0.001)
***Original data - learning rate: 0.001 - Stochastic descent - Stop: 5000 iterations
Theta: [[-0.37957778 0.07935528 -0.00090398]] - Iter: 5000 - Last cost: 1.50 - Duration: 0.28s
array([[-0.37957778, 0.07935528, -0.00090398]])
上述的结果表明迭代很不稳定,结果不收敛,仍是在Stochastic descent的条件下,我们将学习率进行缩小,即观察同等情况下,不同的学习率对于迭代收敛情况的影响
runExpe(orig_data, theta, 1, STOP_ITER, thresh=15000, alpha=0.000002)
***Original data - learning rate: 2e-06 - Stochastic descent - Stop: 15000 iterations
Theta: [[-0.00202246 0.00993581 0.00085156]] - Iter: 15000 - Last cost: 0.63 - Duration: 0.80s
array([[-0.00202246, 0.00993581, 0.00085156]])
可以看出迭代最后趋于收敛了,但是迭代过程中的稳定性较差,最后的损失为0.63,还是一个比较大的数字的.
Mini-batch descent 小批量梯度下降
runExpe(orig_data, theta, 16, STOP_ITER, thresh=15000, alpha=0.001)
***Original data - learning rate: 0.001 - Mini-batch (16) descent - Stop: 15000 iterations
Theta: [[-1.03621821 0.03476669 0.02040813]] - Iter: 15000 - Last cost: 0.85 - Duration: 1.03s
array([[-1.03621821, 0.03476669, 0.02040813]])
浮动仍然比较大,我们来尝试下对数据进行标准化
将数据按其属性(按列进行)减去其均值,然后除以其方差。最后得到的结果是,对每个属性/每列来说所有数据都聚集在0附近,方差值为1
这里说明一下,当结果不是很好时我们优先考虑对数据进行处理,如果效果仍不乐观,再对模型进行修改
from sklearn import preprocessing as pp
scaled_data = orig_data.copy()
scaled_data[:, 1:3] = pp.scale(orig_data[:, 1:3])
runExpe(scaled_data, theta, n, STOP_ITER, thresh=5000, alpha=0.001)
***Scaled data - learning rate: 0.001 - Gradient descent - Stop: 5000 iterations
Theta: [[0.3080807 0.86494967 0.77367651]] - Iter: 5000 - Last cost: 0.38 - Duration: 0.93s
array([[0.3080807 , 0.86494967, 0.77367651]])
效果好多了,未进行数据标准化的结果是0.61,而这里缩小到了0.38。可见在某些情况下,对数据进行标准化是优化结果的有效方法之一
runExpe(scaled_data, theta, n, STOP_GRAD, thresh=0.02, alpha=0.001)
***Scaled data - learning rate: 0.001 - Gradient descent - Stop: gradient norm < 0.02
Theta: [[1.0707921 2.63030842 2.41079787]] - Iter: 59422 - Last cost: 0.22 - Duration: 10.86s
array([[1.0707921 , 2.63030842, 2.41079787]])
更多的迭代次数会使损失变得更小
theta = runExpe(scaled_data, theta, 1, STOP_GRAD, thresh=0.002/5, alpha=0.001)
***Scaled data - learning rate: 0.001 - Stochastic descent - Stop: gradient norm < 0.0004
Theta: [[1.14848065 2.79371801 2.56573578]] - Iter: 72622 - Last cost: 0.22 - Duration: 4.66s
随机梯度下降快,但是所需的迭代次数也增多,可见还是使用小批量梯度下降比较合适
runExpe(scaled_data, theta, 16, STOP_GRAD, thresh=0.002*2, alpha=0.001)
***Scaled data - learning rate: 0.001 - Mini-batch (16) descent - Stop: gradient norm < 0.004
Theta: [[1.15195395 2.80023506 2.57409275]] - Iter: 699 - Last cost: 0.22 - Duration: 0.07s
array([[1.15195395, 2.80023506, 2.57409275]])
精度计算
#设定阈值
def predict(X, theta):
return [1 if x >= 0.5 else 0 for x in model(X, theta)]
scaled_X = scaled_data[:, :3]
y = scaled_data[:, 3]
predictions = predict(scaled_X, theta)
correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y)]
accuracy = (sum(map(int, correct)) % len(correct))
print ('accuracy = {0}%'.format(accuracy))
accuracy = 89%
a, theta, 16, STOP_GRAD, thresh=0.002*2, alpha=0.001)
***Scaled data - learning rate: 0.001 - Mini-batch (16) descent - Stop: gradient norm < 0.004
Theta: [[1.15195395 2.80023506 2.57409275]] - Iter: 699 - Last cost: 0.22 - Duration: 0.07s
[外链图片转存中...(img-FzlvWhO3-1611678810781)]
array([[1.15195395, 2.80023506, 2.57409275]])
[外链图片转存中...(img-w85ykpfz-1611678810781)]
## 精度计算
```python
#设定阈值
def predict(X, theta):
return [1 if x >= 0.5 else 0 for x in model(X, theta)]
scaled_X = scaled_data[:, :3]
y = scaled_data[:, 3]
predictions = predict(scaled_X, theta)
correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y)]
accuracy = (sum(map(int, correct)) % len(correct))
print ('accuracy = {0}%'.format(accuracy))
accuracy = 89%