今天看《机器学习实战》里面的logistic回归篇,发现里面虽然内容不多,但是作者省略了很多过程,结果自己看得有点懵,后来查了一些资料,然后自己推了推公式,才有了豁然开朗的感觉。这里就记录一下自己的理解,也希望和大家一起分享。
Github: 梯度下降
数学部分
—————————————————————————————————————————————————————————————
1.目标描述
之前我们说过一个物体可以抽象成许多特征的集合,在这里我们用X表示物体:
其中表示特征,
表示每个特征的权重,用一个大家都在用的例子解释就是:
就好比房子的价格,而
就是影响房价的各个因素(房屋面积,房屋朝向,房屋所在地段等),同时
则是每个影响因素的权重,这些所有的影响因素(特征)共同决定了房价。
2.代价函数
既然我们已经确定了目标描述的方法,那么该取什么值,才能最好最贴切地描述物体本身呢?(一般房屋所在地段及房屋面积对房价影响较大,所以其对应的
就会大一些)
为了求解,我们需要
个样本(
),这里的
个样本是已经知道真实值的(即属于已经分好类的)
这里我们需要构建一个代价函数(构建的方法有很多,这里我们用最小二乘法):
其中表示第i个样本,
表示第i个样本的真实描述值。
当我们使最小时,也就是我们估计得到的
与真实
的总误差最小时,即可认为我们选取的
是最合适的!
3.梯度下降
现在我们的目标是使得代价函数最小,即求:
对了,这个时候我们的梯度就出场了:
注:梯度指示了该如何变化(即变化的方向),才能使得
最小。
对于每一个的偏导数均可以表示为:
其中表示第i个样本,
表示第i个样本的真实描述值,
表示第i个样本的第j个特征。
好了,到了这里接下来就容易多了,要确保最小,那么
就应该这么变化:
其中表示每次迭代的步长(即每次变化的幅度),而
则指示了
每次变化的方向。当所有
收敛到某一个值的时候,此时
最小。
4.向量表示法
①梯度下降向量描述:
②对于每一个的偏导数向量描述:
其中
5. Sigmoid函数
这个函数主要用来归一化处理用的,对于像二分类这样的问题(结果只有0和1),我们将的值归一化到0或1,这样更有利于我们分类。
100个样本点的logistic回归
—————————————————————————————————————————————————————————————
#准备数据:这里使用书本里面已经格式化好的数据
#训练算法:利用已有数据,使用梯度下降法寻找最优回归系数
#测试算法:可以采用交叉验证
#使用算法:利用最优回归系数构建的分类器进行样本分类
注:《机器学习实战》里面用的是梯度上升,其实和梯度下降的结果是一致的。用上升还是下降,其实与构造函数的凹凸性有关,不用特别在意,毕竟函数都是自己构造的,你可以保证它的凹凸性。程序里面除了常规的梯度下降法,还包含了随机梯度下降法,这是一种更加高效的求解最优解的方法。
# -*- coding: UTF-8 -*-
import numpy as np
from matplotlib import pyplot as plt
def drawSigmod():
plt.figure()
plt.subplot(111)
X = np.linspace(-60, 60, 256, endpoint=True)
Y = 1 / (1 + np.exp(-X))
plt.plot(X, Y)
plt.ylim(Y.min() - 0.1, Y.max() * 1.1)
plt.show()
def sigmod(inX):
return 1.0 / (1 + np.exp(-inX))
def loadDataSet(path):
# Set: X0 = 1.0
dataSet = []; labelSet = []
with open(path, 'r') as f:
for line in f.xreadlines():
lineArr = line.strip().split()
dataSet.append([1.0, float(lineArr[0]), float(lineArr[1])])
labelSet.append(float(lineArr[2]))
return dataSet, labelSet
def gradDescend(trainMatIn, trainLabelIn):
trainMat = np.mat(trainMatIn)
trainLabel = np.mat(trainLabelIn).transpose()
alpha = 0.001 # alpha为步长
m, n = np.shape(trainMat) # m为样本数量,n为特征个数
loopCount = 500 # 迭代次数
theta = np.ones((n, 1)) # 初始化回归系数
for i in xrange(loopCount):
h = sigmod(trainMat * theta)
error = h - trainLabel
theta -= alpha * trainMat.transpose() * error
return theta
def randGradDescend(trainMatIn, trainLabelIn):
import random
trainMat = np.array(trainMatIn) # list转换为numpy.array
trainLabel = np.array(trainLabelIn)
alpha = 0.01 # 初始化步长
m, n = np.shape(trainMat) # m个样本,n个特征
theta = np.ones(n) # 初始化回归系数
loopCount = 40 # 迭代次数
for j in xrange(loopCount):
for i in xrange(m):
alpha += 4 / (1 + i + j)
randIndex = int(random.uniform(0, m))
h = sigmod(sum(theta * trainMat[randIndex]))
error = h - trainLabel[randIndex]
theta -= alpha * error * trainMat[randIndex]
return theta
def main():
data, label = loadDataSet("testSet.txt")
theta = gradDescend(data, label)
thetaRand = randGradDescend(data, label)
dataArr = np.array(data)
m, n = np.shape(dataArr)
print m, n
print len(label)
xcord1 = []; ycord1 = []
xcord2 = []; ycord2 = []
for i in xrange(m):
if label[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=50, c='red')
ax.scatter(xcord2, ycord2, s=50, c='blue')
x = np.arange(-3.0, 3.0, 0.1)
y1 = (- theta[0] - theta[1] * x) / theta[2]
y2 = (- thetaRand[0] - thetaRand[1] * x) / thetaRand[2]
ax.plot(x, y1, c='green')
ax.plot(x, y2, c= 'green')
plt.xlabel('x1'); plt.ylabel('x2')
plt.show()
if __name__ == "__main__":
main()