一、 关于回归
在分类中问题中,如果给定一个输入,其所产生的输出是一个布尔值,那么这是是与否型的答案;而在输出是数值型的值下,我们所希望的学习结果不是C={0, 1},而是一个连续的函数。我们倾向于将数值的输出写成关于输入的函数,数值输入称为自变量,数值输出称为因变量。我们希望通过对训练数据集学习后得出一个类似下面的函数关系式(这里以简单线性为例)。
a、函数输入为0时,Sigmoid结果为0.5。
b、随着输入
x
值的增大,Sigmoid函数接近1,随着x
的减小,其值接近于0。我们将上面拟合得到函数
g(x)
代入Sigmoid函数中,若g(x)
输出值大于0时,Sigmoid函数结果大于0.5,g(x)
值小于0时,Sigmoid函数小于0.5。这样Sigmoid函数把g(x)
函数值转为为后验概率,也即Sigmoid函数大于0.5概率时,我们将X
类标标记为1,而概率小于0.5时,X
类标记为0。sigmoid
函数值越大,我们越有把握将X正确地分类。二、寻找最佳拟合曲线
针对g(x)函数回归系数的求解,我们使用梯度上升优化法多次迭代求出最佳参数。
所谓梯度是指多元函数在某一点处的一阶偏导数向量。一个梯度应用直观理解的例子是热传导问题,物体温度沿着各个方向传播速度是不相同的,那么沿着哪条方向温度变化率是最小或最大的呢?还有是在爬山过程中,我们朝着哪个方向迈出一步上升的幅度最大呢?答案是沿着梯度的方向,温度变化率最大、迈出一步上升幅度最大。梯度上升法用于求解最大值,梯度下降法用于求解最小化问题。
基于上面提到的理论,我们在训练数据集中经过多次迭代后求解出最佳拟合参数,之后便能够在测试数据集中预测类别。
三、公式推导
四、更新回归系数
根据以上计算得出的梯度公式,我们每次迭代更新回归系数W
公式为:
alpha
为学习因子(learning factor)或者称为步长(stepsize),该参数决定每次沿梯度方向移动多少。alpha
值选择很重要,如果alpha值偏大,则可能导致函数摆动甚至发散,如果偏小,使得函数收敛速度很慢,计算开销偏大。同时,运用梯度方法求解最优化问题,最终结果可能不一定是全局最优,可能只是局部最优解。因为,在使用梯度方法优化过程中,它寻找的是最近的一个极值点,因此来说,函数仅存在一个极值点时才能达到全局最优。
求解最优化问题,梯度方法较为简单,但结果相当有效。我们还需注意的是标准梯度上升法在每次更新回归系数W
时,都要遍历整个数据集,当数据规模很大情况下,内存开销和计算量很大。下面介绍一种在线学习算法--随机梯度上升法。
随机梯度上升法基本思想是:在每一次迭代更新回归系数时,其不是遍历整个数据集,而是一次仅用一个样本点来更新回归系数。更高级的思想是,我们不再像上面对alpha
参数保持不变,而是随着迭代次数的增加,逐渐减小alpha
值,也即越靠近最优解时,我们移动的步长越小以防越过最优解。同时,样本点也可以随机选取以减少周期性波动。
五、代码实现
1、标准梯度上升法
def gradAscent():
dataMat = []
labelMat = []
with open('testSet.txt') as fr:
for line in fr.readlines():
lineArr = line.strip().split()
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
labelMat.append(int(lineArr[2]))
dataMatrix = mat(dataMat)
labelMat = mat(labelMat).transpose()
m, n = shape(dataMatrix)
alpha = .001
maxCycles = 500
weights = ones((n, 1))
# print 'weights:', weights
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights)
error = (labelMat - h)
weights += alpha * dataMatrix.transpose() * error
return weights
2、sigmoid方法
def sigmoid(inx):
return 1.0 / (1 + exp(-inx))
3、改进的随机梯度上升法
def stocGradAscentImproved(numIter=150):
"""Improved Stochastic gradient ascent method"""
with open('testSet.txt') as fr:
for line in fr.readlines():
lineArr = line.strip().split()
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
labelMat.append(int(lineArr[2]))
dataMat = array(dataMat)
labelMat = array(labelMat)
m, n = shape(dataMat)
weights = ones(n)
for j in range(numIter):
dataIndex = range(m)
for i in range(m):
alpha = 2 / (1.0 + j + i) + 0.01 # 更快地收敛
randIndex = int(random.uniform(0, len(dataIndex)))
h = sigmoid(sum(dataMat[randIndex] * weights))
error = classLabels[randIndex] - h
weights += alpha * error * dataMat[randIndex]
del dataIndex[randIndex] # 已选过的样本点删除
return weights
4、其中testSet.txt文件部分内容:
-0.017612 14.053064 0
-1.395634 4.662541 1
-0.752157 6.538620 0
-1.322371 7.152853 0
0.423363 11.054677 0
0.406704 7.067335 1
0.667394 12.741452 0
-2.460150 6.866805 1
0.569411 9.548755 0
stocGradAscentImproved
方法在训练数据集求解出回归系数W
,也即解出拟合曲线了,之后使用测试数据集预测类标号。如果sigmoid
函数计算结果值大于0.5
,则标记为1
,而小于0.5
标记为0
。
在测试数据集上多次迭代求平均错误率。
六、总结
1、选择合适的分类器函数,这里使用sigmoid
函数,我们在高中生物课学习过该参数,即种群S
型生长曲线。sigmoid
函数能够把拟合计算得到的曲线转换为后验概率,当sigmoid
函数值大于0.5
时,我们把该类标记为A类,小于0.5
时,标记为B类。
2、在大规模数据集中,由于标准梯度上升法在每次迭代更新回归系数时,它都是遍历整个数据集,因此,该方法占用的内存空间和计算量很大。
3、公式算法应自己推导一遍以便对程序有更好的理解。