本博客记录《机器学习实战》(MachineLearningInAction)的学习过程,包括算法介绍和python实现。
逻辑回归
对于一个数据集中的样本,将其每个特征乘上一个对应的系数,然后输入sigmoid函数中把结果映射到0-1区间内,用这个结果作为分类依据,这种方式称为逻辑回归。
sigmoid函数
sigmoid函数公式及图像如下:
可以看出在x为0时,函数值为0.5,x>0时逐渐趋向1,x<0时逐渐趋向0。这个性质使得sigmoid函数可以把实数域内的数据映射到0-1范围内,从而完成分类的任务。
回归参数确定
逻辑回归中最重要的一部就是参数的确定,如何选择最优的参数使得模型分类时能够达到更高的准确率。这里用到的寻找最优参数的方法是一种最优化方法——梯度上升法。
在二分类问题中,把样本数据的每个特征乘以回归系数
θ
并求和,将结果输入到sigmoid函数中,把得到的结果作为分到1类的概率。这样,结果大于0.5的样本就分到1类,小于0.5的样本就分到0类。构造预测函数如下:
这个 hθ(x) 就代表了样本分到1类的概率, θTx 代表参数与输入样本的特征向量x的加权和,于是有:
综合起来写就是对于样本x,在参数为
θ
时,分到y类的概率为:
假设输入的样本特征为
x1...xn
,对应的分类结果为
y1...yn
,那么最优的参数应该使
L(θ)
最大:
这里用到了最大似然法,引用百度百科的定义:
最大似然法(Maximum Likelihood,ML)也称为最大概似估计,也叫极大似然估计,是一种具有理论性的点估计法,此方法的基本思想是:当从模型总体随机抽取n组样本观测值后,最合理的参数估计量应该使得从模型中抽取该n组样本观测值的概率最大
L(θ) 表示模型分类结果与n个样本数据都符合的概率,称为似然函数,根据最大似然法,我们以 L(θ) 的值最大作为标准确定模型参数 θ 。为了找到函数的最大值,采用的方法就是梯度上升法。
所谓梯度就是指函数值在任意点递增最快的方向,用
∇
表示。梯度上升法是一种参数更新的方法,初始时给参数
θ
赋初值,计算
L(θ)
在参数点的梯度,把梯度乘以步长后加到参数上来更新参数,这样参数的每次更新都会朝着使得
L(θ)
增加最快的方向进行,参数更新公式如下:
对于每一个参数:
为了便于计算,将
L(θ)
取对数,即改为求
l(θ)=logL(θ)
的最大值:
结果中的 xji 表示第i个样本的第j个特征,这样就得出了更新参数的方法,更新第j个参数时,遍历每个样本计算出类标签 yi 与当前模型的分类结果 hθ(xi) 之差,即分类误差,乘以该样本中的第j个特征值,最后求和,再乘以步长,就得到参数更新量。
当然,在python中通过矩阵运算可以使这个运算过程看上去简单地多,通过矩阵乘法就能够优雅地计算加权和。需要注意的是,在特征与参数一一对应的情况下,我们还需要增加一个参数作为常数项,这时候,通常的做法是在数据集上增加一个值为1的特征,这个特征与常数项参数对应,以方便进行矩阵运算,代码如下:
# 梯度上升算法
def gradAscent(dataMat, classLabels):
# 把输入数据转换为矩阵类型
dataMatrix = mat(dataMat)
labelMatrix = mat(classLabels).transpose()
m, n = shape(dataMatrix)
# 步长
alpha = 0.001
# 迭代次数
maxCycles = 500
# 参数矩阵
weights = ones((n, 1))
for k in range(maxCycles):
# 计算实际分类结果
h = sigmoid(dataMatrix * weights)
# 计算误差
error = (labelMatrix - h)
# 更新参数
weights = weights + alpha * dataMatrix.transpose() * error
return weights
画出决策边界
在具有两个特征的一组数据集中,通过梯度上升方法计算出分类边界,通过python中的matplotlib画出边界:
可以看出分类的效果还是比较理想的。
随机梯度上升算法
上述梯度算法的缺点显而易见,在每次迭代更新参数时,都要遍历整个数据集,在数据集很大时,这个时间开销是无法接受的。一种改进的算法——随机梯度上升法,可以在不失太多精度的情况下极大地减小计算量。
在梯度上升法中,每次迭代都遍历整个数据集来更新参数,这使得在每次迭代中参数都朝着全局最优的方向更新。随机梯度上升算法则是每次迭代时只选取一个样本进行参数的更新,可以想象成梯度上升法在样本集只有一个样本情况下的特例,这时候每一次的更新会朝着使得这个样本的正确率增加最快的方向,即对于某个样本的局部最优方向进行。即使这样,在经过多次迭代之后也能够达到跟梯度上升法相近的准确率,但是计算量却大大降低。
另外一点可以优化的是步长的大小,大的步长使得参数能够更快的达到最优值,却可能在最优值附近不断徘徊;小的步长能够更精确地达到最优值,却使得参数更新的更慢。这时候可以取这两者的长处,使用动态步长的方法:在初始时给定较大的步长,在迭代的过程中,随着参数不断趋于稳定,逐渐减小步长。这种方法使得开始时参数能够较快地接近最优值,同时又能够增加参数更新的精确度。下面给出优化的随机梯度上升法的python代码:
# 随机梯度上升法
def stocGradAscent1(dataMatrix, classLabel, numIter=150):
m, n = shape(dataMatrix)
weights = ones(n)
for j in range(numIter):
dataIndex = list(range(m))
for i in range(m):
alpha = 4 / (1.0 + j + i) + 0.01
# 随机选取样本
randIndex = int(random.uniform(0, len(dataIndex)))
h = sigmoid(sum(dataMatrix[dataIndex[randIndex]] * weights))
error = classLabel[dataIndex[randIndex]] - h
weights += alpha * error * dataMatrix[dataIndex[randIndex]]
# 删除使用过的样本
del(dataIndex[randIndex])
return weights
最后,有了最佳参数的模型,就可以用来分类了:
# 分类函数
def classifyVec(inX, weights):
prob = sigmoid(sum(inX * weights))
if prob > 0.5:
return 1
else:
return 0
总结
逻辑回归是在线性回归的基础上增加了一个非线性函数sigmoid,并通过最优化方法(常用的是梯度上升算法)来得到最佳拟合参数。而随机梯度上升算法则是梯度上升的一种优化,减小了时间复杂度。
在计算参数更新方法时,似然函数的选择使用了最大似然法,使得从模型中抽取出样本集的概率最大。对似然函数求取每个参数方向上的梯度,并经过一些数学推导得到了参数更新的方法。
多数情况下,逻辑回归可以用来估计事件的概率,例如某广告被点击的概率,某个人患某种疾病的概率等。