逻辑回归
概念
假设现在有一些数据点,我们用一条直线(最佳拟合直线)进行拟合,这个拟合的过程就称作回归。
利用Logistic回归进行分类的主要思想是:根据现有数据对分类边界线建立回归公式。
训练分类器就是寻找最佳拟合参数,使用的是最优化算法。
基于Logistic回归和Sigmoid函数的分类
-
优点:计算代价不高,易于理解和实现
-
缺点:容易欠拟合,分类精度可能不高
-
适用数据类型:数值型和标称型数据
Sigmoid函数:

当x为0时,Sigmoid函数值为0.5。随着x的增大,对应的Sigmoid值将逼近于1;而随着x的减小,Sigmoid值将逼近于0。
如果横坐标刻度足够大,Sigmoid函数看起来很像一个阶跃函数。(如上面图2所示)
Logistic回归就是利用Sigmoid函数,将特征数据计算映射到0 ~ 1之间。
大于0.5的归于1类;小于0.5的归于0类。所以Logistic回归也可以被看成是一种概率估计。
基于最优化方法的最佳回归系数确定
Sigmoid函数的输入记为z:
上述公式向量写法:
其中x是输入数据,w是需要找到的最佳参数。
梯度上升法
要找到某函数的最大值,最好的方法就是沿着该函数的梯度方向探寻。
如果梯度记为,则函数f(x,y)的梯度由下式表示:

这个梯度意味着要沿x的方向移动,
沿y的方向移动,
其中,函数f(x, y)必须要在待计算的点上有定义并且可微。
梯度算子总是指向函数增长最快的方向,这里只是移动方向,并没有移动量。该量值称为步长,记为,也称作学习率。
梯度上升算法的迭代公式如下:
该公式将一直被迭代执行,直至达到某个停止条件,例如迭代次数达到某个指定值或算法达到某个可以允许的误差范围。
Note:
梯度上升算法用来求函数的最大值,而梯度下降算法用来求函数的最小值。
梯度下降与梯度上升类似。只是公式中的加法需要变成减法。
使用梯度上升找到最佳参数
import numpy as np
# 数据加载
def loadDataSet():
dataMat = []
labelMat = []
file = open('testSet.txt')
for line in file.readlines():
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(matrix):
return 1.0 / (1 + np.exp(-matrix))
def gradientAscent(dataMatIn, classLabels):
# 转换为Numpy矩阵
dataMatrix = np.mat(dataMatIn)
labelMat = np.mat(classLabels).transpose()
# 算法所需参数
alpha = 0.001
maxCycles = 500
weights = np.ones((dataMatrix.shape[1], 1))
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights)
error = labelMat - h
weights = weights + alpha * dataMatrix.transpose() * error
return weights
# 展示数据集
dataMat, labelMat = loadDataSet()
dataArr = np.array(dataMat)
n = dataArr.shape[0]
x1, y1 = [],[]
x2, y2 = [],[]
# 区分两类数据
for i in range(n):
if int(labelMat[i] == 1):
x1.append(dataArr[i, 1])
y1.append(dataArr[i, 2])
else:
x2.append(dataArr[i, 1])
y2.append(dataArr[i, 2])
# 绘图展示数据集
plt.scatter(x1, y1, s = 20, c = 'blue', marker = 'o')
plt.scatter(x2, y2, s = 60, c = 'green', marker = 'o')
plt.legend(['Class 1', 'Class 0'])
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()

画出决策边界
def plotBestFit(weights):
import matplotlib.pyplot as plt
dataMat, labelMat = loadDataSet()
dataArr = np.array(dataMat)
n = dataArr.shape[0]
x1, y1 = [],[]
x2, y2 = [],[]
# 区分两类数据
for i in range(n):
if int(labelMat[i] == 1):
x1.append(dataArr[i, 1])
y1.append(dataArr[i, 2])
else:
x2.append(dataArr[i, 1])
y2.append(dataArr[i, 2])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(x1, y1, s = 30, c = 'red', marker = 's')
ax.scatter(x2, y2, s = 30, c = 'green')
x = np.arange(-3.0, 3.0, 0.1)
# 最佳拟合直线
y = (-weights[0] - weights[1] * x)/weights[2]
ax.plot(x, y)
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()
weights = gradientAscent(dataMat, labelMat)
plotBestFit(weights.getA())

回看前面的Sigmoid函数,可以知道z = 0是两个分类的分界处。因此,我们设定
解出关系式,就可以得到代码中的计算式子y = (-weights[0] - weights[1] * x)/weights[2]
随机梯度上升
梯度上升算法每次遍历整个数据集,计算复杂度较高。
随机梯度上升每次仅用一个样本点。
def stocGradAscent(dataMatrix, classLabels):
m, n = np.shape(dataMatrix)
alpha = 0.01
weights = np.ones(n)
for i in range(m):
h = sigmoid(sum(dataMatrix[i] * weights))
error = classLabels[i] - h
weights = weights + alpha * error * dataMatrix[I]
return weights
dataArr, labelMat = loadDataSet()
weights = stocGradAscent(np.array(dataArr), labelMat)
plotBestFit(weights)

只遍历一次完整数据集的随机梯度上升算法效果如上图所示。
下面将使用随机梯度算法在整个数据集上迭代200次,并展示weights的变化情况,以及最后weights的拟合情况
# 遍历完整数据集200次,并记录每次完整遍历后的weights
def stocGradAscent1(dataMatrix, classLabels):
m, n = np.shape(dataMatrix)
alpha = 0.05
weights = np.ones(n)
weightsHist = []
for i in range(200):
for j in range(m):
h = sigmoid(sum(dataMatrix[j] * weights))
error = classLabels[j] - h
weights = weights + alpha * error * dataMatrix[j]
weightsHist.append(weights)
return weightsHist
dataMat, labelMat = loadDataSet()
dataArr = np.array(dataMat)
weightsHist = stocGradAscent1(dataArr, labelMat)
weightsArr = np.array(weightsHist)
# 绘图展示参数变化
fig = plt.figure(figsize=(8, 10))
ax = fig.add_subplot(311)
type1 = ax.plot(weightsArr[:,0])
plt.ylabel('X0')
ax = fig.add_subplot(312)
type1 = ax.plot(weightsArr[:,1])
plt.ylabel('X1')
ax = fig.add_subplot(313)
type1 = ax.plot(weightsArr[:,2])
plt.xlabel('iteration')
plt.ylabel('X2')
plt.show()
# 展示遍历200次后的分类效果
plotBestFit(weightsArr[-1])


前面展示了随机梯度上升算法在200次迭代过程中回归系数的变化情况,以及最后一次计算出的参数对数据分类的效果。
我们可以看到最后一次计算出的参数对数据的分类情况跟使用梯度上升算法的效果几乎是一样的。
# 改进的随机梯度上升算法
def stocGradAscent2(dataMatrix, classLabels, maxIter = 200):
m, n = dataMatrix.shape
weights = np.ones(n)
weightsHistory = np.zeros((maxIter * m, n))
for j in range(maxIter):
dataIndex = list(range(m))
for i in range(m):
# 每次迭代都调整alpha
alpha = 4 / (1.0 + i + j) + 0.001
# 随机选取样本
randIndex = int(np.random.uniform(0, len(dataIndex)))
h = sigmoid(sum(dataMatrix[randIndex] * weights))
error = classLabels[randIndex] - h
weights = weights + alpha * error * dataMatrix[randIndex]
weightsHistory[j * m + i, :] = weights
# 删除已被选择的样本index,避免重复选择
del(dataIndex[randIndex])
return weightsHistory
dataMat, labelMat = loadDataSet()
dataArr = np.array(dataMat)
weightsHist = stocGradAscent2(dataArr, labelMat)
# 绘图展示参数变化
fig = plt.figure(figsize=(8, 10))
ax = fig.add_subplot(311)
type1 = ax.plot(weightsHist[:,0])
plt.ylabel('X0')
ax = fig.add_subplot(312)
type1 = ax.plot(weightsHist[:,1])
plt.ylabel('X1')
ax = fig.add_subplot(313)
type1 = ax.plot(weightsHist[:,2])
plt.xlabel('iteration')
plt.ylabel('X2')
plt.show()
plotBestFit(weightsHist[-1])


小结
Logistic回归的目的是寻找一个非线性函数Sigmoid的最佳拟合参数。
求解过程可以用梯度上升算法完成。
随机梯度上升和梯度上升的效果相当,但占用更少的计算资源。而且,随机梯度上升是一个在线算法,可以在新数据到来时就完成参数更新,而不需要重新读取整个数据集。
都看到最后了,要不~点个赞?加波关注?