目录
6. 随机梯度下降/上升法(Stochastic gradient descent/ascent)
学习完机器学习实战的逻辑回归,简单的做个笔记。文中部分描述属于个人消化后的理解,仅供参考。
本篇综合了先前的文章,如有不理解,可参考:
所有代码和数据可以访问 我的 github
如果这篇文章对你有一点小小的帮助,请给个关注喔~我会非常开心的~
0. 前言
逻辑回归(Logistic Regression)是一种基于最优化思想的二分类监督学习算法。
其主要思想是根据现有数据对分类边界线建立回归公式,以此分类。
其目的是寻找一个非线性函数 的最佳拟合参数,求解过程可以通过最优化算法完成。
- 优点:计算代价不高,易于理解和实现
- 缺点:容易造成欠拟合,分类精度不高
- 适用数据类型:数值型和标称型数据
1. Sigmoid 函数
在线性回归中,对数据进行拟合,是在每个特征上都乘以一个回归系数,然后把所有结果再相加,即 ,向量化即
。
在逻辑回归中,再将 带入
函数,可得到属于
范围的一个数值,若大于
则属于正类,若小于则反之。
函数的定义如下:
函数的图像如下:
综合上述,整体公式表示为:
2. 梯度上升与梯度下降
梯度:某一函数在该点处的方向导数沿该方向取得最大值,即在该点变化率(斜率)最大。
- 梯度上升法:寻找某函数的最大值,沿着该函数的梯度方向寻找
- 梯度下降法:寻找某函数的最小值,沿着该函数的梯度方向寻找
梯度符号记为 ,
的梯度可表示为,对各个方向求偏导数:
如下图所示,求最小值,可采用梯度下降法:
梯度下降: ,当
在最小值左边时,导数为负数,
向右移;当
在最小值右边时,导数为正数,
向左移。
如下图所示,求最大值,可采用梯度上升法:
梯度上升: ,当
在最小值左边时,导数为正数,
向右移;当
在最小值右边时,导数为负数,
向左移。
3. 梯度下降法(Gradient descent)
梯度下降法的代价函数如下定义:
代价函数是指,函数值越大,付出的代价越大,即精度越差。
第二项是正则化项,是为了防止过拟合而设定,先忽略这项。
第一项是所有样本计算结果的求平均,若 ,则取
,若
,则取
。
由上图易得, 时,若
越接近
代价越大,
时,若
越接近
代价越大。
为了降低代价函数 ,可采取梯度下降法,定义如下:
4. 梯度上升法(Gradient ascent)
梯度上升法求解逻辑回归参数,和梯度下降法求解参数原理是一样的。
只是在梯度上升法中, 定义如下:
与梯度下降法只相差一个负号,所以是求解最大值,采用梯度上升法:
5. 梯度下降/上升法的数学推导
以梯度上升法为例子,不考虑正则化项:
对三项分别计算:
综合三项可得:
综上所述,在梯度上升法中:
6. 随机梯度下降/上升法(Stochastic gradient descent/ascent)
以随机梯度下降法为例子,随机梯度下降法是对梯度下降法的改进。
在梯度下降法中,每次更新参数都需要遍历整个数据集,又称作 Batch gradient descent 。
在随机梯度下降法中,每次更新参数只随机选择一个样本点,通过每次更新参数的迭代而选择不同的样本点。
算法流程如下:
随机梯度下降/上升法是一个在线算法,可以在新数据来临的时候直接更新参数,而不用遍历整个数据集。
7. 实战案例
以下将展示书中案例的代码段,所有代码和数据可以在github中下载:
7.1. 简单案例
# coding:utf-8
from numpy import *
import matplotlib.pyplot as plt
"""
简单案例
"""
# sigmoid函数
def sigmoid(inX):
return 1.0 / (1 + exp(-inX))
# 梯度上升求解J(\theta)
# Batch gradient ascent
def gradAscent(dataMatIn, classLabels):
# 转换为矩阵
dataMatrix = mat(dataMatIn)
# 转换为矩阵后转置,表示为列向量
labelMat = mat(classLabels).transpose()
m, n = shape(dataMatrix)
alpha = 0.001
maxCycles = 500
# \theta 表示为列向量
weights = ones((n, 1))
# 梯度上升迭代
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights)
error = (labelMat - h)
weights = weights + alpha * dataMatrix.transpose() * error
return weights
# 加载数据集
def loadDataSet():
dataMat = []
labelMat = []
fr = open('testSet.txt')
for line in fr.readlines():
# 去除头尾空字符,按照空格分割字符串
lineArr = line.strip().split()
# 添加偏置位 w_0(\theta_0) 相乘的 x_0 = 1.0
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
labelMat.append(int(lineArr[2]))
return dataMat, labelMat
# 画出数据的分界线
def plotBestFit(weights):
dataMat, labelMat = loadDataSet()
dataArr = array(dataMat)
n = shape(dataArr)[0]
xcord1 = []
ycord1 = []
xcord2 = []
ycord2 = []
# 遍历每一条数据,根据类别将x1, x2分别插入不同的List中
for i in range(n):
if int(labelMat[i]) == 1:
xcord1.append(dataArr[i, 1])
ycord1.append(dataArr[i, 2])
else:
xcord2.append(dataArr[i, 1])
ycord2.append(dataArr[i, 2])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')
ax.scatter(xcord2, ycord2, s=30, c='green')
x = array(arange(-3.0, 3.0, 0.1))
# x=x1, y=x2 表示的是 w0x0+w1x1+w2x2 = 0 的直线
y = (-weights[0] - weights[1] * x) / weights[2]
ax.plot(x, y)
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()
if __name__ == '__main__':
dataArr, labelMat = loadDataSet()
weights = gradAscent(dataArr, labelMat)
# getA():
# 将矩阵转换为ndarray
plotBestFit(weights.getA())
7.2. 病马死亡率案例
# coding:utf-8
from numpy import *
"""
病马死亡率案例
"""
# sigmoid函数
def sigmoid(inX):
return 1.0 / (1 + exp(-inX))
# 随机梯度上升
# stochastic gradient ascent
def stocGradAscent0(dataMatrix, classLabels):
m, n = shape(dataMatrix)
alpha = 0.01
weights = 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
# 改进后的随机梯度上升
# 学习率随迭代次数减少
# 随机选择样本
def stocGradAscent1(dataMatrix, classLabels, 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.0001
randIndex = int(random.uniform(0, len(dataIndex)))
h = sigmoid(sum(dataMatrix[dataIndex[randIndex]] * weights))
error = classLabels[dataIndex[randIndex]] - h
weights = weights + alpha * error * dataMatrix[dataIndex[randIndex]]
del (dataIndex[randIndex])
return weights
# 分类函数
def classifyVector(inX, weights):
prob = sigmoid(sum(inX * weights))
if prob > 0.5:
return 1
else:
return 0
# 构件逻辑回归分类器,进行分类测试
def colicTest():
frTrain = open('horseColicTraining.txt')
frTest = open('horseColicTest.txt')
trainingSet = []
trainingLabels = []
# 遍历训练集
for line in frTrain.readlines():
currLine = line.strip().split('\t')
lineArr = []
for i in range(21):
lineArr.append(float(currLine[i]))
# 创建训练集的特征向量和标签
trainingSet.append(lineArr)
trainingLabels.append(float(currLine[21]))
# 随机梯度上升求解参数
trainWeights = stocGradAscent1(array(trainingSet), trainingLabels, 1000)
correctCount = 0
numTestVec = 0.0
# 遍历测试集
for line in frTest.readlines():
numTestVec += 1.0
currLine = line.strip().split('\t')
lineArr = []
for i in range(21):
lineArr.append(float(currLine[i]))
if int(classifyVector(array(lineArr), trainWeights)) == int(currLine[21]):
correctCount += 1
accuracy = (float(correctCount) / numTestVec)
print("the accuracy of this test is: %f" % accuracy)
return accuracy
# 多次测试分类器
def multiTest():
numTests = 10
correctSum = 0.0
for k in range(numTests):
correctSum += colicTest()
print("after %d iterations the average accuracy is: %f" % (numTests, correctSum / float(numTests)))
if __name__ == '__main__':
multiTest()
如果这篇文章对你有一点小小的帮助,请给个关注喔~我会非常开心的~