机器学习实战-Logistic回归
一、最优化算法
- 我们将首次接触到最优化算法,假设现在有一些数据点,我们用一条直线对这些点进行拟合(该线称为最佳拟合直线),这个拟合过程就称作回归。
- 利用Logistic回归进行分类的主要思想是:根据现有数据对分类边界线建立回归公式,以此进行分类。这里的“回归” 一词源于最佳拟合,表示要找到最佳拟合参数集。
- 训练分类器时的做法就是寻找最佳拟合参数,使用的是最优化算法。
二、Logistic回归一般过程
收集数据:采用任意方法收集数据。
准备数据:由于需要进行距离计算,因此要求数据类型为数值型。另外,结构化数据格式则最佳。
分析数据:采用任意方法对数据进行分析。
训练算法:大部分时间将用于训练,训练的目的是为了找到最佳的分类回归系数。
测试算法:一旦训练步驟完成,分类将会很快。
使用算法:首先,我们需要输入一些数据,并将其转换成对应的结构化数值;
接着,基于训练好的回归系数就可以对这些数值进行简单的回归计算,判定它们属于
哪个类别,在这之后,我们就可以在输出的类别上做一些其他分析工作。
三、基础描述
1.我们想要的函数就是,能够接受所有的输入然后预测类别。例如,在两个类的情况下,输出0或者1.我们使用的具有这种性质的函数叫做Sigmoid函数,函数公式如下:
该函数在跳跃点上从0瞬间跳跃到1。函数图如下:
- 该函数很像一个阶跃函数。因此,为了实现logistic回归分类器,我们可以在每个特征上都乘以一个回归系数,然后把所有的结果值相加,将这个总和代入sigmoid函数中,进而得到一个范围在0〜1之间的数值。任何大于0.5的数据被分入1类,小于0.5即被归入0类 。所以,logistic回归也可以被看成是一种概率估计。
sigmoid函数的输入记为Z,Z= w0x0 +w1x1 + w2x2+ … + wNxN.它表示将这两个数值向量对应元素相乘然后全部加起来即得到Z值 。其中的向量x是分类器的输入数据,向量w就是我们要找到的最佳参数。
四、梯度上升算法
1.学习的第一个最优化算法是梯度上升算法。梯度上升算法的原理是:要找到某函数的最大值,最好的办法是沿着该函数的梯度方向去探寻。如果梯度记为,那么函数f(x,y)的梯度由下式表示:
关于梯度上升算法中的公式推导,请看这篇博客:http://blog.csdn.net/ariessurfer/article/details/41345115。梯度下降算法和梯度上升算法是一样的,只是公式中的减法要变为加法。梯度上升算法用来求函数的最大值,而梯度下降算法用来求函数的最小值。
2.训练算法:
梯度上升算法的伪代码如下:
每个回归系数初始化为1
重复R次:
计算整个数据集的梯度
使用alpha*gradient更新回归系数的向量
返回回归系数
算法具体实现:
def loadDataSet():
'''
打开测试文本并逐行读取
'''
dataMat = []
labelMat = []
fr = open('testSet.txt')
for line in fr.readlines():
lineArr = line.strip().split()
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) # 将参数X0的值设置为1.0,方便计算
labelMat.append(int(lineArr[2]))
return dataMat, labelMat
def sigmoid(inX):
'''
sigmoid函数
'''
return 1.0 / (1 + exp(-inX))
def gradAscent(dataMatIn, classLabels):
'''
梯度上升算法
:param dataMatIn:2维numpy数组
:param classLabels:
:return:
'''
dataMatrix = mat(dataMatIn)
labelMat = mat(classLabels).transpose() # 将行向量转换成列向量
m, n = shape(dataMatrix) # m=100, n=3
alpha = 0.001
maxCycles = 500
weigths = ones((n, 1)) # 使用ones可以创建任意维度和元素个数的数组,其元素均为1
for k in range(maxCycles):
h = sigmoid(dataMatrix * weigths) # h是一个列向量,列向量个数等于样本个数
error = (labelMat - h)
weigths = weigths + alpha * dataMatrix.transpose() * error
return weigths
五、随机梯度上升算法
梯度上升算法在每次更新回归系数时都需要遍历整个数据集, 该方法在处理100个左右的数
据集时尚可,但如果有数十亿样本和成千上万的特征,那么该方法的计算复杂度就太高了。一种改进方法是一次仅用一个样本点来更新回归系数,该方法称为随机梯度上升算法。由于可以在新样本到来时对分类器进行增量式更新,因而随机梯度上升算法是一个在线学习算法。与 “在线学习”相对应,一次处理所有数据被称作是“批处理” 。
随机梯度上升算法伪代码:
所有回归系数初始化为1
对数据集中每个样本
计算该样本的梯度
使用alpha x gradient^ .新回归系数值
返回回归系数值
以下是随机梯度上升算法的实现代码:
def stocGradAscent0(dataMatrix, classLabels):
'''
随机梯度上升算法
:param dataMatrix:
:param classLabels:
:return:
'''
m, n = shape(dataMatrix)
alpha = 0.01
weights = ones(n)
for i in range(m):
h = sigmoid(sum(dataMatrix[i] * weights)) # 这里的h和error都是数值
error = classLabels[i] - h
weights = weights + alpha * error * dataMatrix[i]
return weights
可以看到,随机梯度上升算法与梯度上升算法在代码上很相似,但也有一些区别:第一,后者的变量h和误差error都是向量,而前者则全是数值;第二,前者没有矩阵的转换过程,所有变量的数据类型都是numpy数组。
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
'''
改进后的随机梯度上升算法
'''
m, n = shape(dataMatrix)
weights = ones(n)
for j in range(numIter):
dataIndex = range(m)
for i in range(m):
alpha = 4 / (1.0 + j + i) + 0.01 # alpha每次迭代时都会调整
randIndex = int(random.uniform(0, len(dataIndex))) # 随机选取更新(随机选择样本来更新回归系数)
h = sigmoid(sum(dataMatrix[randIndex] * weights))
error = classLabels[randIndex] - h
weights = weights + error * alpha *dataMatrix[randIndex]
del (dataIndex[randIndex])
return weights
增加了两处代码来进行改进:
1. 一方面,alpha在每次迭代的时候都会调整,这会缓解数据波动或者高频波动;另外, 虽然alpha会随着迭代次数不断减小,但永远不会减小到0,这是因为alpha表达式中
还存在一个常数项。必须这样做的原因是为了保证在多次迭代之后新数据仍然具有一定的影响。
1. 第二个改进,这里通过随机选取样本来更新回归系数。这种方法将减少周期性的波动.这种方法每次随机从列表中选出一个值,然后从列表中删掉该值( 再进行下一次迭代)。
六、小结
logstic回归的目的是寻找一个非线性函数Sigmoid的最佳拟合参数,求解过程可以由最优化算法来完成。在最优化算法中,最常用的就是梯度上升算法,而梯度上升算法又可以简化为随机梯度上升算法。
随机梯度上升算法与梯度上升算法的效果相当,但占用更少的计算资源。此外,随机梯度上升是一个在线算法,它可以在新数据到来时就完成参数更新,而不需要重新读取整个数据集来进行批处理运算。
机器学习的一个重要问题就是如何处理缺失数据。这个问题没有标准答案,取决于实际应用中的需求。
参考资料:
1. 《机器学习实战》