《机器学习实战》
回归
概念:假设有一些数据点,我们用一条直线对这些点进行拟合,这个拟合过程成为回归。这条线称之为 最佳拟合直线。
主要思想:根据现有数据对分类边界线建立回归公式,以此进行分类。
“回归”一词源于最佳拟合,表示要找到最佳拟合参数集。
Sigmoid函数
f(x) = 1/(1 + e(-x))
当 x = 0时, f(x) = 0; 随着 x 的增大,f(x) 逼近于1;随着 x 的减小,f(x) 逼近于0。
2.Logistic回归分类器原理:
我们可以在每个特征上都乘以一个回归系数,然后把所有的结果值相加,将这个总和代入Sigmoid函数中,进而得到一个0~1之间的数值。 任何大于0.5的数据被分为1类,小于0.5则被归为0类。 所以,Logistic回归也可以被看成是一种概率估计。
3.基于最优化方法的最佳回归系数确定
Sigmoid函数 输入记为Z,
Z = wOx0 + w1x1 + w2x2 + ... + wNxN
可以写成 Z = wTx, 表示将这两个数值向量对应元素相乘然后全部加起来。 其中,x是分类器的输入参数,w就是最佳回归系数。
1 2
| def Sigmoid(inX): return 1.0/(1+math.exp(-inX))
|
梯度上升算法
我们需要寻找合适的参数 w 使得 wTx = 0 成为很好的分类判定边界。
思想:
要找到某函数的最大值,最好的方法是沿着该函数的梯度方向探寻。
对于Logistic回归而言,损失函数为非凸函数。重新定义损失函数后得到梯度上升算法迭代公式。
梯度上升算法伪代码
每个回归系数初始化为1
重复r次:
计算整个数据集的梯度
使用 alpha * gradient 更新回归系数
返回回归系数
1 2 3 4 5 6
| weights = np.ones((n,1)) for k in range(maxCycles): h = sigmoid(np.dot(dataMatrix,weights)) error = labelMat - h weights = weights + alpha*dataMatrix.transpose()*error return weights
|
Logistic回归 梯度上升优化算法
1.读取数据并提取对应的类别标签
1 2 3 4 5 6 7
| def loadDataSet(): dataMat = []; labelMat = [] for line in open('data/testSet.txt').readlines(): lineArr = line.strip().split() dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) labelMat.append(int(lineArr[2])) return dataMat,labelMat
|
- sigmoid函数和梯度上升的程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def sigmoid(inX): return 1.0/(1+math.exp(-inX)) def gradAscent(dataMatIn, classLabels): dataMatrix = np.mat(dataMatIn) labelMat = np.mat(classLabels).transpose() m,n = np.shape(dataMatIn) alpha = 0.001 maxCycles = 500 weights = np.ones((n,1)) for k in range(maxCycles): h = sigmoid(np.dot(dataMatrix,weights)) error = labelMat - h weights = weights + alpha*dataMatrix.transpose()*error return weights
|
得到的结果为:
1 2 3 4
| ➜ logistic git:(master) ✗ python logistic.py [[ 4.12414349] [ 0.48007329] [-0.6168482 ]]
|
3.分析数据:画出决策边界
对于hθ(x)=g(θTx)而言,θTx > 0,y = 1;θTx < 0, y = 0.所以我们认为θTx = 0是一个决策边界。
hθ(x)=g(θ0+θ1X1+θ2X2)
-> θ0+θ1X1+θ2X2 = 0
-> x2 = (-θ0-θ1X1)/θ2
-> y = (-θ0-θ1X)/θ2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| def plotBestFit(weights,dataMat,labelMat): dataArr = np.mat(dataMat) weights = weights.getA() n = np.shape(dataArr)[0] xcord1 = []; ycord1 = [] xcord2 = []; ycord2 = [] 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 = 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()
|
随机梯度上升
梯度上升算法在每次更新回归系数时都需要遍历整个数据集,计算复杂度较高。
改进方法是 一次仅用一个样本点来更新回归系数。该方法称为随机梯度上升算法。
伪代码:
所有回归系数初始化为1
对数据集的每个样本:
计算该样本的梯度
使用 alpha * gradient 更新回归系数
返回回归系数
1 2 3 4 5 6 7 8 9
| def socGradAscent0(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 * dataMatrix[i] * error return weights
|
判断一个优化算法是否可靠的方式是看它是否收敛,也就是说参数是否达到了稳定值。
通过改变该算法的迭代次数可以看出:某些特征的回归系数会有周期性波动。
这是因为数据集中存在一些不能正确分类的样本点,在每次迭代时会引发系数的剧烈波动。
改进的随机梯度上升算法
为了解决随机梯度上升的问题,对算法进行了一些改进。
- alpha 在每次迭代时进行调整(随着迭代次数不断减小)
- 随机选取样本来更新回归系数
- 增加迭代次数
1 2 3 4 5 6 7 8 9 10 11 12 13
| def socGradAscent1(dataMatrix, classLabels, numIter = 2): m,n = np.shape(dataMatrix) weights = np.ones(n) for j in range(numIter): dataIndex = list(range(m)) for i in range(m): alpha = 0.04/(1.0+j+i) + 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] del(dataIndex[randIndex]) return weights
|
示例: 从疝气病症预测病马的死亡率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| # -*- coding: utf-8 -*- # !/usr/bin/env python import logistic import numpy as np TRAIN_TIMES = 500 def classifyVector(inX, weights): prob = logistic.socSigmoid(sum(inX * weights)) if prob >= 0.5: return 1.0 else: return 0.0 def colicTest(): trainingSet = []; trainingLabels = [] for line in open('data/horseColicTraining.txt').readlines(): currLine = line.strip().split('\t') lineArr = [] for i in range(len(currLine)-1): lineArr.append(float(currLine[i])) trainingSet.append(lineArr) trainingLabels.append(float(currLine[len(currLine)-1])) weights = logistic.socGradAscent1(np.array(trainingSet),trainingLabels,TRAIN_TIMES) # print(weights) errorCount = 0; numTestVec = 0.0 for line in open('data/horseColicTest.txt').readlines(): numTestVec += 1.0 currLine = line.strip().split('\t') lineArr = [] for i in range(len(currLine)-1): lineArr.append(float(currLine[i])) if int(classifyVector(lineArr,weights) != int(currLine[len(currLine)-1])): errorCount += 1 # print(errorCount,numTestVec) errorRate = float(errorCount)/numTestVec print('the error rate of this test is :%f' % errorRate) return errorRate def multiTest(): numTests = 10; errorSum = 0.0 for k in range(numTests): errorSum += colicTest() print('after %d iterations the average error rate is :%f' % (numTests,errorSum/float(numTests))) if __name__ == '__main__': multiTest()
|