一、基础知识
1 回归的含义
回归即为最佳拟合,用一条直线对这些点进行拟合的过程,逻辑回归过程即为寻找最佳拟合参数的过程,使用的是最优化理论。
2 最优化理论中常用的优化算法
- 梯度下降法和梯度上升法
- 随机梯度下降法
- 批量梯度下降法
- 小批量随机梯度下降法
- 牛顿法和拟牛顿法
- 共轭梯度法
- 拉格朗日乘数法
- 启发式优化算法-智能算法
人工神经网络,模拟退火算法,禁忌搜索算法,粒子群算法,蚁群算法,鱼群算法,布谷鸟算法,遗传算法,免疫算法,进化多目标算法
3 逻辑回归思想和优缺点
Logistic主要用于二分类算法,它利用的是Sigmoid函数阈值在[0,1]这个特性。逻辑回归本质就是最大似然参数估计法。
4 Sigmoid函数
hθ(x)=g(θTx)
z=[θ0θ1θ2...θn][θ0θ1θ2...θn]T=θnTX
g(Z)=1+e−z1
可得:hθ(x)=g(θTx)=1+e−θTx1
Sigmoid函数:
θ是参数列向量(待求解未知量),x是样本列向量(给定的数据集)。g(z)函数实现了任意实数到[0,1]的映射,这样我们的数据集x0,x1,x2...xn,不管是大于1还是小于0,都可以映射到[0,1]区间分类。hθ(x)给出了输出为1的概率,比如hθ(x)=0.7,说明有70%的概率输出为1,输出0的概率是输出1的补集,也就是30%。
如果我们有合适的参数列向量θ和样本列向量X,那么我们对样本X分类就可以通过上述公式计算出一个概率,如果概率大于0.5,可以说样本是正样本,否则样本是负样本。
根据Sigmoid函数特性,做出以下假设:
P(y=1∣x;θ)=hθ(x) P(y=0∣x;θ)=1−hθ(x)
上式即为在已知样本x和参数θ情况下,样本x属于正样本(y=1)和负样本(y=0)的条件概率。理想状态下,根据上述公式,求出各点的概率均为1,也就是分类完全正确。但是考虑实际情况,样本点概率越接近1分类效果越好。比如,一个样本属于正样本的概率为0.99,那么说明这个样本属于正样本。我们可以把上述两个概率公式合二为一:
Cost(hθ(x),y)=hθ(x)y(1−hθ(x))1−y我们对整个表达式求对数:
Cost(hθ(x),y)=yloghθ(x)+(1−y)log(1−hθ(x))给定一个样本,我们可以通过这个函数求出,样本所属类别的概率,而且这个概率越大越好,所以也就是求解这个函数的最大值。既然概率出来了,那么最大似然估计也该出场了。假定样本与样本之间互相独立,那么整个样本集生成的概率即为所有样本生成概率的乘积,再将公式对数化,可得:
其中,m为样本的总数,y(i)表示第i个样本的类别,x(i)表示第i个样本。
综上所述,满足J(θ)的最大值的θ即是我们需要求解的模型。
5 梯度上升法
先举一个简单求极大值的例子:f(x)=−x2+4x
求极值,先求函数导数:f′(x)=−2x+4令导数为0,可求出x=2即取得函数f(x)的极大值。
但是真实环境中函数不会像上面这么简单,就算求出函数导数,也很难精确计算出函数的极值。此时我们就可以用迭代的方法来做,就像爬坡一样,一点一点逼近极值。这种寻找最佳拟合参数的方法,就是最优化算法。爬坡这个动作用数学公式表达即为:xi+1=xi+αxi∂f(xi)其中,α为步长,也就是学习速率,控制更新的幅度,效果如下图:比如从(0,0)开始,迭代路径就是1⟶2⟶3⟶4⟶…⟶n,直到求出的x为函数极大值的近似值,停止迭代。
def Gradient_Ascent_test():
# f(x)的导数
def f_prime(x_old):
return -2 * x_old +4
# 初始值,给一个小于x_new的值
x_old = -1
# 梯度上升算法初始值,即从(0,0)开始
x_new = 0
# 步长,也就是学习效率,控制更新的幅度
alpha = 0.01
# 精度,也就是更新阈值
presision = 0.00000001
while abs(x_new - x_old) > presision:
x_old = x_new
# 上面提到的公式
x_new = x_old + alpha * f_prime(x_old)
# 打印最终求解的极值近似值
print(x_new)
if __name__ == '__main__':
Gradient_Ascent_test()
这一过程就是梯度上升算法。同理,J(θ)函数极值,也可以这样求解,公式可以写为:θj:=θj+αθj∂J(θ)由上小节可知:
Sigmoid函数为:hθ(x)=g(θTx)=1+e−θTx1现在,只要求出J(θ),就可以利用梯度上升算法,求解极大值啦。
θj∂J(θ)=∂g(θTx)∂J(θ)∗∂θTx∂g(θTx)∗∂θj∂θTx
其中:
∂g(θTx)∂J(θ)=y∗g(θTx)1+(y−1)∗1−g(θTx)1
再由:
g′(z)=dzd1+e−z1=(1+e−z)21(e−z)=1+e−z1(1−1+e−z1)=g(z)(1−g(z))
可得:
∂θTx∂g(θTx)=g(θTx)(1−g(θTx))
第三部分:
∂θj∂θTx=∂θj∂J(θ1x1+θ2x2...+θnxn)=xj
综上所述:
θj∂J(θ)=(y−hθ(x))xj
因此,梯度上升迭代公式为:θj:=θj+αi=1∑m(y(i)−hθ(x(i)))xj(i)
二、功能函数
1 显示数据
将第一列数据(X1)看作x轴上的值,第二列数据(X2)看作y轴上的值。而最后一列数据即为分类标签。根据标签的不同,对这些点进行分类。
import matplotlib.pyplot as plt
import numpy as np
def loadDataSet():
# 创建数据集
dataMat = []
# 创建标签列表
labelMat = []
# 打开文件
fr = open('Logistic')
# 逐行读取
for line in fr.readlines():
# 去回车,放入列表
lineArr = line.strip().split()
# 添加数据
dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])
# 添加标签
labelMat.append(int(lineArr[2]))
# 关闭文件
fr.close()
# 返回
return dataMat,labelMat
def plotDataSet():
# 加载数据集
dataMat,labelMat = loadDataSet()
# 转换成numpy的array数组
dataArr = np.array(dataMat)
# 数据个数
n = np.shape(dataMat)[0]
# 正样本
xcord1 = []
ycord1 = []
# 负样本
xcord2 = []
ycord2 = []
# 根据数据集标签进行分类
for i in range(n):
if int(labelMat[i]) == 1:
# 1为正样本
xcord1.append(dataArr[i,1])
ycord1.append(dataArr[i,2])
else:
# 0为负样本
xcord2.append(dataArr[i,1])
ycord2.append(dataArr[i,2])
fig = plt.figure()
# 添加subplot
ax = fig.add_subplot(111)
# 绘制正样本
ax.scatter(xcord1,ycord1,s = 20,c = 'red',marker = 's',alpha=.5)
# 绘制负样本
ax.scatter(xcord2,ycord2,s = 20,c = 'green',alpha=.5)
# 绘制title
plt.title('DataSet')
# 绘制label
plt.xlabel('x')
plt.ylabel('y')
# 显示
plt.show()
if __name__ == '__main__':
plotDataSet()
dataMat,labelMat = loadDataSet()
print(dataMat,labelMat)
从上图可以看出数据的分布情况。假设Sigmoid函数的输入记为z,那么z=w0x0 + w1x1 + w2x2,即可将数据分割开。其中,x0为全是1的向量,x1为数据集的第一列数据,x2为数据集的第二列数据。另z=0,则0=w0 + w1x1 + w2x2。横坐标为x1,纵坐标为x2。这个方程未知的参数为w0,w1,w2,也就是我们需要求的回归系数(最优参数)。
2 梯度上升训练算法
根据梯度上升迭代公式:
θj:=θj+αi=1∑m(y(i)−hθ(x(i)))xj(i)将上述公式矢量化:
θj:=θ+αXT(Y−g(Xθ))
#!/user/bin/env python
# -*- coding:utf-8 -*-
#@Time : 2020/3/16 9:51
#@Author: fangyuan
#@File : Logistic回归梯度上升.py
import numpy as np
def loadDataSet():
# 创建数据集
dataMat = []
# 创建标签列表
labelMat = []
# 打开文件
fr = open('Logistic')
# 逐行读取
for line in fr.readlines():
# 去回车,放入列表
lineArr = line.strip().split()
# 添加数据
dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])
# 添加标签
labelMat.append(int(lineArr[2]))
# 关闭文件
fr.close()
# 返回
return dataMat,labelMat
def sigmoid(inX):
return 1.0/(1+np.exp(-inX))
def gradAscent(dataMatIn,classLabels):
# 转换成numpy的mat
dataMatrix = np.mat(dataMatIn)
# 转换成numpy的mat并进行转置
labelMat = np.mat(classLabels).transpose()
# 返回dataMatrix的大小,m为行数,n为列数
m,n = np.shape(dataMatrix)
# 移动步长,也就是学习速率,控制更新的幅度
alpha = 0.001
# 最大迭代次数
maxCycles = 500
weights = np.ones((n,1))
# 梯度上升矢量化公式
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights)
error = (labelMat - h)
weights = weights + alpha * dataMatrix.transpose() * error
# 将矩阵转化为数组,返回权重数组(最优参数)
return weights.getA()
if __name__ == '__main__':
dataMat,labelMat = loadDataSet()
print(gradAscent(dataMat,labelMat))
已经求解出回归系数[w0,w1,w2]。通过求解出的参数,我们就可以确定不同类别数据之间的分隔线,画出决策边界。
3 绘制决策边界
#!/user/bin/env python
# -*- coding:utf-8 -*-
#@Time : 2020/3/16 10:06
#@Author: fangyuan
#@File : Logistic回归绘制决策边界.py
import numpy as np
import matplotlib.pyplot as plt
def loadDataSet():
# 创建数据集
dataMat = []
# 创建标签列表
labelMat = []
# 打开文件
fr = open('Logistic')
# 逐行读取
for line in fr.readlines():
# 去回车,放入列表
lineArr = line.strip().split()
# 添加数据
dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])
# 添加标签
labelMat.append(int(lineArr[2]))
# 关闭文件
fr.close()
# 返回
return dataMat,labelMat
def sigmoid(inX):
return 1.0/(1+np.exp(-inX))
def gradAscent(dataMatIn,classLabels):
# 转换成numpy的mat
dataMatrix = np.mat(dataMatIn)
# 转换成numpy的mat并进行转置
labelMat = np.mat(classLabels).transpose()
# 返回dataMatrix的大小,m为行数,n为列数
m,n = np.shape(dataMatrix)
# 移动步长,也就是学习速率,控制更新的幅度
alpha = 0.001
# 最大迭代次数
maxCycles = 500
weights = np.ones((n,1))
# 梯度上升矢量化公式
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights)
error = (labelMat - h)
weights = weights + alpha * dataMatrix.transpose() * error
# 将矩阵转化为数组,返回权重数组(最优参数)
return weights.getA()
def plotBestFit(weights):
# 加载数据集
dataMat,labelMat = loadDataSet()
# 转换成numpy的array数组
dataArr = np.array(dataMat)
# 数据个数
n = np.shape(dataMat)[0]
# 正样本
xcord1 = []
ycord1 = []
# 负样本
xcord2 = []
ycord2 = []
# 根据数据集标签进行分类
for i in range(n):
# 1为正样本
if int(labelMat[i]) == 1:
xcord1.append(dataArr[i,1])
ycord1.append(dataArr[i,2])
# 0为负样本
else:
xcord2.append(dataArr[i,1])
ycord2.append(dataArr[i,2])
fig = plt.figure()
# 添加subplot
ax = fig.add_subplot(111)
# 绘制正样本
ax.scatter(xcord1,ycord1,s = 20,c = 'red',marker = 's',alpha =.5)
# 绘制负样本
ax.scatter(xcord2,ycord2,s = 20,c = 'green',alpha=.5)
x = np.arange(-3.0,3.0,0.1)
y = (-weights[0] - weights[1] * x) / weights[2]
ax.plot(x,y)
# 绘制title
plt.title('BestFit')
# 绘制label
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()
if __name__ == '__main__':
dataMat,labelMat = loadDataSet()
weights = gradAscent(dataMat,labelMat)
print(weights)
plotBestFit(weights)
Logistic回归的目的是寻找一个非线性函数Sigmoid的最佳拟合参数,求解过程可以由最优化算法完成。