定义
回归:假设现有一些数据点,用一条直线对这些点进行拟合(最佳拟合直线),拟合的过程就称作回归。
利用logistic回归进行分类的主要思想是:根据现有数据对分类边界线建立回归公式,以此进行分类
logistic回归主要是进行二分类预测,也即是对于0~1之间的概率值,当概率大于0.5预测为1,小于0.5预测为0.
理论基础
- sigmoid方程
- 在一个二分类问题中:
- 能接受所有的输入然后预测出类别预测出类别。
- The function spit out: 0 or 1.
- 海维塞得阶跃函数(Heaviside step function),或者直接称为单位阶跃函数。
- 海维塞得阶跃函数的问题在于: 该函数在跳跃点上从0瞬间跳跃到1,这个瞬间跳跃过程有时很难处理。
- Sigmoid函数也有类似的性质(可以输出0或者1的性质),且数学上更易处理
- 为了实现 Logistic 回归分类器,可以在每个特征上都乘以一个回归系数(如下公式所示),然后把所有结果值相加,将这个总和代入Sigmoid函数中,进而得到一个范围在 0~1 之间的数值。任何大于0.5的数据被分入1 类,小于
0.5即被归入0类。Logistic回归也可以被看成是一种概率估计。
z = ω 0 x 0 + ω 1 x 1 + ω 2 x 2 + . . . + ω n x n z = \omega_0x_0+\omega_1x_1+\omega_2x_2+...+\omega_nx_n z=ω0x0+ω1x1+ω2x2+...+ωnxn
- 使用最优化方法查找回归系数
为了寻找最佳的回归系数 ω \omega ω,这里使用梯度上升法。
梯度上升法基于的思想是:要找到某函数的最大值,最好的方法是沿着该函数的梯度方向探寻。如果梯度记为∇,则函数f(x,y)的梯度由下式表示:
Δ
f
(
x
,
y
)
=
[
∂
f
(
x
,
y
)
∂
x
∂
f
(
x
,
y
)
∂
y
]
\Delta f(x,y) = \left[ \begin{matrix} \frac {\partial f(x,y)}{\partial x} \\ \frac {\partial f(x,y)}{\partial y}\end{matrix} \right]
Δf(x,y)=[∂x∂f(x,y)∂y∂f(x,y)]
这个梯度意味着要沿x的方向移动 ∂ f ( x , y ) ∂ y \frac {\partial f(x,y)}{\partial y} ∂y∂f(x,y)
沿y的方向移动 ∂ f ( x , y ) ∂ x \frac {\partial f(x,y)}{\partial x} ∂x∂f(x,y)
其中,函数f (x,y)必须要在待计算的点上有定义并且可微。
梯度上升算法到达每个点后都会重新估计移动的方向。从P0开始,计算完该点的梯度,函数就根据梯度移动到下一点P1。在P1点,梯度再次被重新计算,并沿新的梯度方向移动到P2。如此循环迭代,直到满足停止条件。迭代的过程中,梯度算子总是保证我们能选取到最佳的移动方向。
梯度算子总是指向函数值增长最快的方向。这里所说的是移动方向,而未提到移动量的大小。该量值称为步长,记做α。用向量来表示的话,梯度上升算法的迭代公式如下:
ω
:
=
ω
+
α
Δ
ω
f
(
ω
)
\omega := \omega + \alpha \Delta_\omega f(\omega)
ω:=ω+αΔωf(ω)
该公式将一直被迭代执行,直至达到某个停止条件为止,比如迭代次数达到某个指定值或算法达到某个可以允许的误差范围。
注:一般我们听到的都是梯度下降法,两着并没有什么区别,只是迭代公式符号发生改变
ω
:
=
ω
−
α
Δ
ω
f
(
ω
)
\omega := \omega - \alpha \Delta_\omega f(\omega)
ω:=ω−αΔωf(ω)
实例
- 训练梯度上升算法
(1)假设有100个样本点,每个样本有两个特征:x1和x2,此外为方便考虑,额外添加一个x0 = 1,将线性函数 z = ω T x + b z = \omega^Tx+b z=ωTx+b转为 z = ω T x z = \omega^Tx z=ωTx
#梯度上升法更新最优拟合参数
#@dataMatIn:数据集
#@classLabels:数据标签
def gradAscent(dataMatIn,classLabels):
#将数据集列表转为Numpy矩阵
dataMatrix = np.mat(dataMatIn)
#将数据集标签列表转为Numpy矩阵,并转置
labelMat = np.mat(classLabels).transpose()
labelMat = np.array(list(map(float,labelMat)))
#获取数据集矩阵的行数和列数
col,row = np.shape(dataMatrix)
#学习步长
alpha = 0.001
#最大迭代数
maxCycles = 500
#初始化权值参数向量每个维度为1.0
weights = np.ones((row,1))
#循环迭代次数
for k in range(maxCycles):
#求当前的digmoid函数预测概率
h = sigmoid(dataMatrix*weights)
#计算真实类别和预测类别的差值
#对logistic回归函数的对数释然函数的参数项求偏导
error = (labelMat - h)
#更新权值参数
weights = weights + alpha*dataMatrix.transpose()*error
return weights
(2)绘制决策边界
- 随机梯度上升
- 梯度上升法每次更新回归系数都需要遍历整个数据集(最大化所有训练样本的似然函数,求得全局最优解),当样本数量较小时,该方法尚可,但是当样本数据集非常大且特征非常多时,那么批量梯度下降法的计算复杂度就会特别高(迭代速度相当慢)。
- 随机梯度上升:一种改进的方法是一次仅用一个样本点来更新回归系数(最优化每条样本的似然函数,虽然不是每次迭代得到的似然函数都是向着全局,但大体方向是对的、最终结果往往在全局最优附近、适合于大规模训练样本)。
- 在新样本到来时对分类器进行增量式更新,随机梯度上升法是一个在线学习算法
#随机梯度上升算法
def stoGradAscent(dataMatrix,classLabels):
#为方便计算,转为Numpy数组
dataMat = np.array(dataMatrix)
#将数据集标签列表转为Numpy矩阵,并转置
labelMat = np.mat(classLabels).transpose()
labelMat = np.array(list(map(float,labelMat)))
#获取数据集的行和列
col,row = np.shape(dataMat)
#初始化权值向量各个参数为1.0
weights = np.ones(row)
#设置步长为0.01
alpha = 0.01
#循环col次,每次选取数据集一个样本更新参数
for i in range(col):
# 求当前样本的digmoid函数值
h = sigmoid(dataMat[i] + weights)
# 计算当前样本的残差(代替梯度)
error = (labelMat[i] - h)
# 更新权值参数
weights = weights + alpha * dataMat[i] * error
return weights
- 回归概率预测
def classifyVector(inX,weights):
#计算logistic回归预测概率
prob = sigmoid(np.sum(inX*weights))
#大于0.5预测为1
if prob > 0.5:
return 1.0
else:
return 0.0
- 病马数据预测
def colicTest():
#打开训练数据集
frTrain = open('horseColicTraining.txt')
#打开测试数据集
frTest = open('horseColicTest.txt')
#新建两个空列表,用于保存训练数据集和标签
trainingSet = []
trainingLabels = []
for line in frTrain.readlines():
#对当前行进行特征分割
currLine = line.strip().split()
#新建列表存储每个样本的特征向量
lineArr = []
#遍历每个样本的特征
for i in range(21):
#将每个样本的特征存入lineArr列表
lineArr.append(float(currLine[i]))
#将该样本标签存入标签列表
trainingLabels.append(currLine[21])
#将该列表的特征向量添加到数据集列表
trainingSet.append(lineArr)
#调用梯度上升法更新logistic回归的权值参数
trainWeights = stoGradAscent(trainingSet,trainingLabels)
#统计测试数据集预测样本数量和样本总数
errorCount = 0
numTestVec = 0.0
#遍历测试数据集的每个样本
for line in frTest.readlines():
#样本总数加1
numTestVec += 1.0
#对当前行进行处理,分割出各个特征集样本标签
currLine = line.strip().split()
#新建特征向量
lineArr = []
#将各个特征构成特征向量
for i in range(21):
lineArr.append(float(currLine[i]))
#利用分类预测函数对样本进行预测,并于样本标签进行比较
if classifyVector(lineArr,trainWeights) != currLine[21]:
#如果预测错误,错误加1
errorCount += 1
#计算测试集总的预测错误率
errorRate = (float(errorCount)/numTestVec)
print('the error rate of this test is :',errorRate)
return errorRate
#多次测试算法求取误差平均值
def multTest():
#设置测试次数为10次,并统计错误率总和
numTests = 10
errorRateSum = 0.0
for k in range(numTests):
errorRateSum += colicTest()
print('after ',numTests,' iterations the average error rate is : ',errorRateSum/float(numTests))
总结
- logistic回归的目的是寻找一个非线性函数sigmoid的最佳拟合参数,从而来相对准确的预测分类结果
- 为了找出最佳的函数拟合参数,最常用的优化算法为梯度上升法,为了节省计算资源,通常选择随机梯度上升法来迭代更新拟合参数。
- 随机梯度上升法是一种在线学习算法,它可以在新数据到来时完成参数的更新,而不需要重新读取整个数据集来进行批处理运算
- logistic回归算法,其具有计算代价不高,易于理解和实现等优点;logistic回归算法容易出现欠拟合,以及分类精度不太高的缺点。
- logistic回归和朴素贝叶斯一样,都是基于概率分类:将x分类
到使P(Y|x)最大的那个Y类,x服从logitic分布
sklearn中的logistic
from sklearn import linear_model
from sklearn import metrics
from sklearn.metrics import accuracy_score
# 加载数据
def loadData(filename):
fr = open(filename)
dataSet = []
Labels = []
for line in fr.readlines():
currLine = line.strip().split('\t')
dataSet.append([float(lineArr) for lineArr in currLine[:-1]])
Labels.append(float(currLine[-1]))
return dataSet,Labels
if __name__ == '__main__':
trainingSet,trainingLabels = loadData('horseColicTraining.txt')
testSet,testLabels = loadData('horseColicTest.txt')
#训练一个logistic回归模型,设置迭代次数为1000(默认为100,会导致结果不收敛,从而触发警告)
logistic_model = linear_model.LogisticRegression(max_iter=1000)
logistic_model = logistic_model.fit(trainingSet,trainingLabels)
predicted=logistic_model.predict(testSet)
deathCount = 0
for i in predicted:
if i == 0:
deathCount += 1
deathRate = float(deathCount)/len(predicted)
score = logistic_model.score(testSet,testLabels)
print('The accuracy of logistic is :',score)
print('the death rate of this test is :', deathRate)