本节主要内容:
- 目的:寻找一个非线性函数Sigmoid的最佳拟合参数,求解过程由优化算法来完成。
- 最优化算法,最常用的是梯度上升算法,而梯度上升算法又可以简化为随机梯度上升法,随机梯度上升法占用的计算资源更少,是一个在线算法,可以在新数据到来时完成参数的更新,而不需要重新读取整个数据集进行批量处理。
- Logistic 回归的一般过程
- 收集数据:采用任意方式收集数据
- 准备数据:由于需要进行距离计算,因此要求数据类型为数值型,另外,结构化数据格式更佳
- 分析数据:采用合适的方式对数据进行分析(比如可视化、数据的简单处理等)
- 训练算法:大部分时间用于训练,训练的目的是为了找到最佳的分类回归系数
- 测试算法:一旦训练完成,分类将会很快
2.使用算法:首先,将输入数据转化为结构化数值;然后,利用训练好的回归系数对输入数据进行回归计算,判定它们属于哪个类别;最后,在输出类别上做一些其他分析工作。
3.基于logistic回归和sigmoid函数的分类
Sigmoid函数的集体计算公式:

Sigmoid函数的曲线图:
图1-1 两种坐标尺度下的sigmoid函数图。上图为-8到8,曲线变化较为平滑;下图坐标尺度较大-60到60,在(0,0.5)处看起来像阶跃函数
为了实现logistic回归分类器,可以在每个特征上乘以一个回归系数,然后把所有的结果值相加,带入到sigmoid函数中,进而得到0-1之间的数值。任意大于0.5的数值被分入1类,小于0.5的被分入0类。
4. 基于最优化方法的最佳回归系数确定
Sigmoid函数的输入记为z,由下面公式得出:
根据向量的写法,上述公式写成
,其中,x是分类器的输入数据,向量w就是我们要找的最佳参数(系数),为了寻找最优化w值,还需要用到最优化理论。
4.1 梯度上升法
梯度上升法思想:要找到某个函数的最大值,最好的方法是沿着该函数的梯度方向寻找。函数f(x,y)的梯度由下式表示:
这个梯度意味着沿x方向移动
,沿着y的方向移动
在梯度上升算法中,梯度算子总是向函数值增长最快的方向,梯度是指最佳的移动方向,而移动量的大小称为步长,记作
,用向量表示,梯度上升算法的迭代公式如下:
是指未更新的参数,
是指更新后参数
4.2 梯度下降法
梯度下降法用来计算函数的最小值,在梯度下降算法中,梯度算子总是向函数值增长最慢的方向。
梯度下降法用来计算函数的最小值,在梯度下降算法中,梯度算子总是向函数值增长最慢的方向。
5. 训练算法:使用梯度上升算法找到最佳参数
给出一个简单的数据集,利用logistic进行回归分类:
图1-2 一个简单那数据集,二分类问题,绿色和红色点分别为一类
图1-2有100个样本点,每个点包含两个数值型特征:X1和X2,下面采用梯度上升法找到logistic回归器在此数据及上的最佳回归系数。
梯度上升法的伪代码:
- 每个回归系数初始化为1
- 重复n次:
- 计算整个数据集的梯度
- 使用 更新回归系数的向量
机器学习实战(logistic回归)
- 返回回归系数
代码实现:
from numpy import *
import matplotlib.pyplot as plt
def loadDataSet():
dataMat = []; labelMat = []
fr = open('testSet.txt') ##打开文本
for line in fr.readlines(): ##逐行读取,每行前两个值为X1和X2,第三个值是数据对应的标签,将X0的值设置为1
lineArr = line.strip().split()
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
labelMat.append(int(lineArr[2]))
return dataMat,labelMat
##定义sigmoid函数
def sigmoid(inX):
return 1.0/(1+exp(-inX))
##梯度下降法
def gradAscent(dataMatIn, classLabels):
dataMatrix = mat(dataMatIn) #原始数据转化为矩阵
labelMat = mat(classLabels).transpose() #标签数据转化为矩阵
m,n = shape(dataMatrix)
alpha = 0.001
maxCycles = 5000 ##迭代次数500
weights = ones((n,1)) ##初始化参数为1
temp= []
for k in range(maxCycles):
h = sigmoid(dataMatrix*weights) #预测值
error = (labelMat - h) #向量
weights = weights + alpha * dataMatrix.transpose()* error #matrix mult
return weights
迭代500次,得到回归系数:
[[4.12414349]
[0.48007329]
[-0.6168482]]
画出决策边界:
上面已经解出一组回归系数,它确定了不同类别数据之间的分割线。下面是画出数据集和logistic回归最佳拟合直线的函数:
##画出数据集和logistic回归最佳拟合直线
def plotBestFit(weights):
import matplotlib.pyplot as plt ##画图工具
dataMat,labelMat=loadDataSet()
dataArr = array(dataMat)
n = shape(dataArr)[0]
xcord1 = []; ycord1 = []
xcord2 = []; ycord2 = []
for i in range(n):
if int(labelMat[i])== 1: ##xcord1标签值为1
xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2])
else:
xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2]) ##xcord2标签值为0
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')
ax.scatter(xcord2, ycord2, s=30, c='green')
x = arange(-3.0, 3.0, 0.1)
y = (-weights[0]-weights[1]*x)/weights[2]##0为两类的分界处,故假定0=w0x0+w1x1+w2x2,x0=1
y = y.transpose()
ax.plot(x, y)
plt.xlabel('X1'); plt.ylabel('X2')
plt.show()
输出结果:
图1-3 梯度上升算法字500次迭代后得到的logistic回归最佳拟合直线
这个分类结果从图上看之分错了2到4个点。尽管数据集很小,但是却需要大量的计算(300次乘法),接下来对算法进行稍微改进。
6. 训练算法:改进的随机梯度上升
梯度上升法在每次更新回归系数时,都需要遍历整个数据集,该方法在处理100个左右的数据集尚可,但如果有数十亿样本和成千上万的特征,那么该方法的计算复杂度就太高了。一种改进方法:一次仅用一个样本点来更新回归系数,该方法称为随机梯度上升算法。由于可以在新样本到来时进行增量式更新,吟哦人随机梯度上升算法是一个在线学子算法。与在线学习相对应,一次处理所有数据称为“批处理”。
改进的随机梯度上升算法:1.步长(学习率)在每次迭代的时候都会减小(不会减小到0),能够缓解数据的波动或高频波动。2. 通过随机选取样本来更新回归系数,每次随机从列表中选出一个值,然后从列表中删除该值(再进行下一次迭代)。
随机梯度上升算法伪代码
- 每个回归系数初始化为1
- 对数据中每个样本:
- 计算该样本的梯度
- 使用 更新回归系数的向量
机器学习实战(logistic回归)
- 返回回归系数
实现代码
##改进的随机梯度上升算法
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
m,n = shape(dataMatrix)
weights = ones(n) #initialize to all ones
for j in range(numIter):
dataIndex = range(m)
for i in range(m):
alpha = 4/(1.0+j+i)+0.0001 #改变步长
randIndex = int(random.uniform(0,len(dataIndex)))#随机选取样本,减少周期性的波动
h = sigmoid(sum(dataMatrix[randIndex]*weights))
error = classLabels[randIndex] - h ##数值型,float64
weights = weights + alpha * error *array(dataMatrix[randIndex]) ##参数更新
del(list(dataIndex)[randIndex]) ##删除该值进行下一次迭代
return weights
输出结果:
这次仅对数据做了150次的遍历,而之前的方法是做了500次的遍历。该分割线和gradAscent()差不多的效果,但是作用计算量更少。
完整代码:
from numpy import *
import matplotlib.pyplot as plt
def loadDataSet():
dataMat = []; labelMat = []
fr = open('testSet.txt') ##打开文本
for line in fr.readlines(): ##逐行读取,每行前两个值为X1和X2,第三个值是数据对应的标签,将X0的值设置为1
lineArr = line.strip().split()
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) ##
labelMat.append(int(lineArr[2]))
return dataMat,labelMat
##定义sigmoid函数
def sigmoid(inX):
return 1.0/(1+exp(-inX))
##梯度下降法
def gradAscent(dataMatIn, classLabels):
dataMatrix = mat(dataMatIn) #原始数据转化为矩阵
labelMat = mat(classLabels).transpose() #标签数据转化为矩阵
m,n = shape(dataMatrix)
alpha = 0.001
maxCycles = 500 ##迭代次数500
weights = ones((n,1)) ##初始化参数为1
temp= []
for k in range(maxCycles):
h = sigmoid(dataMatrix*weights) #预测值
error = (labelMat - h) #向量
weights = weights + alpha * dataMatrix.transpose()* error #matrix mult
return weights
##画出数据集和logistic回归最佳拟合直线
def plotBestFit(weights):
import matplotlib.pyplot as plt ##画图工具
dataMat,labelMat=loadDataSet()
dataArr = array(dataMat)
n = shape(dataArr)[0]
xcord1 = []; ycord1 = []
xcord2 = []; ycord2 = []
for i in range(n):
if int(labelMat[i])== 1: ##xcord1标签值为1
xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2])
else:
xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2]) ##xcord2标签值为0
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')
ax.scatter(xcord2, ycord2, s=30, c='green')
x = arange(-3.0, 3.0, 0.1)
y = (-weights[0]-weights[1]*x)/weights[2]##0为两类的分界处,故假定0=w0x0+w1x1+w2x2,x0=1
y = y.transpose()
ax.plot(x, y)
plt.xlabel('X1'); plt.ylabel('X2')
plt.show()
##随机
def stocGradAscent0(dataMatrix, classLabels):
m,n = shape(dataMatrix)
alpha = 0.01
weights = ones(n) #initialize to all ones
for i in range(m):
h = sigmoid(sum(dataMatrix[i]*weights))
error = classLabels[i] - h ##数值
weights = weights + alpha * error * dataMatrix[i]
return weights
##改进的随机梯度上升算法
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
m,n = shape(dataMatrix)
weights = ones(n) #initialize to all ones
for j in range(numIter):
dataIndex = range(m)
for i in range(m):
alpha = 4/(1.0+j+i)+0.0001 #改变步长
randIndex = int(random.uniform(0,len(dataIndex)))#随机选取样本,减少周期性的波动
h = sigmoid(sum(dataMatrix[randIndex]*weights))
error = classLabels[randIndex] - h ##数值型,float64
weights = weights + alpha * error *array(dataMatrix[randIndex]) ##参数更新
del(list(dataIndex)[randIndex]) ##删除该值进行下一次迭代
return weights
if __name__=='__main__':
dataMat,labelMat = loadDataSet()
# weights = gradAscent(dataMat,labelMat) ##梯度下降算法回归系数
weights1 = stocGradAscent1(dataMat,labelMat) ##改进随机梯度下降算法回归系数
# plotBestFit(weights) ##画出梯度下降算法在数据集上的拟合直线
plotBestFit(weights1) ##画出改进随机梯度下降算法在数据集上的拟合直线
上面是具体的分类任务,下节涉及到的是使用随机梯度上升算法来解决病马的生死生死预测问题