一些最优化方法:
梯度算子总是指向函数值增长最快的方向。迭代公式如下:
公式一直被迭代执行,直至达到某个停止条件为止,比如迭代次数达到某个指定值或算法达到某个可以允许的误差范围。
梯度下降算法和梯度上升算法一样,只是公式中的加法要变成减法。因此,对应的公式如下:
梯度上升算法用来求函数的最大值,而梯度下降算法用来求函数的最小值。
梯度上升算法在每次更新回归系数时都需要遍历整个数据集,该方法在处理100个左右的数据集时尚可,但如果有数十亿样本和成千上万的特征,那么该方法的计算复杂度就太高了。一种改进方法是一次仅用一个样本点来更新回归系数,该方法称为随机梯度上升算法。由于可以在新样本到来时对分类器进行增量式更新,因而随机梯度上升算法是一个在线学习算法。与”在线学习“相对应,一次处理所有数据被称作是“批处理”。
在上述的数据集中,100个样本点中的每个样本点包含两个特征X 1 X 2,利用梯度上升法找其最佳回归系数,也就是拟合出Logistic回归模型的最佳参数。
1、代码实现
import matplotlib.pyplot as plt
import numpy as np
'''
函数说明:加载数据
Parameters:
None
Returns:
dataMat - 数据列表
labelMat - 标签列表
'''
def loadDataSet():
dataMat = [] # 创建数据列表
labelMat = [] # 创建标签列表
fr = open('testSet.txt') # 打开文件
for line in fr.readlines():
lineArr = line.strip().split() # 去回车,放入列表
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) # 添加数据
labelMat.append(int(lineArr[2])) # 添加标签
fr.close() # 关闭文件
return dataMat, labelMat
'''
函数说明:sigmodi函数
Paremeters:
inX - 数据
Returns:
sigmoid函数
'''
def sigmoid(inX):
return 1.0/(1 + np.exp(-inX))
'''
函数说明:梯度上升算法
Parameters:
dataMatIn - 数据集
classLables - 数据标签
Returns:
weights.getA() - 求得的权重数组(最优参数)
'''
def gradAscent(dataMatIn, classLables):
dataMatrix = np.mat(dataMatIn) #转换成numpy的mat
labelMat = np.mat(classLables).transpose() #转换成numpy的mat,并进行转置
m, n =np.shape(dataMatrix)#返回dataMatrix的大小。m为行,n为列
alpha = 0.001 #移动补偿,也就是学习速率,控制更新的幅度
maxCycles = 500 #最大迭代次数
weights = np.ones((n,1))
for k in range(maxCycles):
h = sigmoid(dataMatrix *weights) #梯度上升矢量公式
#权重系数计算公式
error = labelMat - h
weights = weights + alpha * dataMatrix.transpose()*error
return weights.getA() #将矩阵转换为数组,返回权重数组
2、测试代码
import logRegres
import matplotlib.pyplot as plt
from numpy import *
if __name__ == '__main__':
dataArr, labelMat = logRegres.loadDataSet()
print(logRegres.gradAscent(dataArr, labelMat))
3、结果
[[ 4.12414349]
[ 0.48007329]
[-0.6168482 ]]
已经解出了一组回归系数,确定了不同类别数据之间的分割线,那么下面就画出这条分割线,使优化过程更便于理解
1、代码实现
'''
函数说明:绘制数据集和Logistic回归最佳拟合直线的函数
Parameters:
None
Returns:
None
'''
def plotDataSet(weights):
dataMat, labelMat = loadDataSet() # 加载数据集
dataArr = np.array(dataMat) # 转换成numpy的array数组
n = np.shape(dataMat)[0] # 数据个数,即行数
xcord1 = [] ; ycord1 = [] # 正样本
xcord2 = [] ; ycord2 = [] # 负样本
for i in range(n):
if int(labelMat[i]) == 1: #1为正样本
xcord1.append(dataMat[i][1])
ycord1.append(dataMat[i][2])
else: #0为负样本
xcord2.append(dataMat[i][1])
ycord2.append(dataMat[i][2])
fig = plt.figure()
ax = fig.add_subplot(111) #添加subplot
ax.scatter(xcord1,ycord1,s=20,c='red',marker = 's', alpha=.5,label ='1') #绘制正样本
ax.scatter(xcord2,ycord2,s=20,c='green',marker = 's', alpha=.5,label ='0') #绘制正样本
x = np.arange(-3.0,3.0,0.1)
y = (-weights[0] - weights[1] * x) / weights[2]
ax.plot(x,y)
plt.title('DataSet') #绘制title
plt.xlabel('x'); plt.ylabel('y') #绘制label
plt.legend()
plt.show()
2、测试代码
if __name__ == '__main__':
dataArr, labelMat = logRegres.loadDataSet()
wigets = logRegres.gradAscent(dataArr, labelMat)
logRegres.plotBestFit(wigets.getA())
3、结果
根据分类的结果来看,分类结果错分了几个点,效果已经很不错。但是尽管例子简单且数据量(100个样本点)很小,却需要大量的计算(300次乘法),但真实情况下,数据集是非常巨大的,为此应对大数据集如果采用此算法计算成本太大。将对该算法进行改进,以计算更大的数据集与特征。
1、代码实现
#随机梯度上升法
def stocGradAscent0(dataMatrix, classLabels):
m,n = shape(dataMatrix)
alpha = 0.01
weights = ones(n) #initialize to all ones
for i in range(m):
h = sigmoid(sum(dataMatrix[i]*weights))
error = classLabels[i] - h
weights = weights + alpha * error * dataMatrix[i]
return weights
从程序代码看出,随机梯度上升算法与梯度上升算法很相似,但也存在区别:
· 后者的变量 h 和 误差 error 都是向量,而前者则是数值;
· 前者没有矩阵的转换过程,所有变量的数据类型都是 numpy 数组。
2、测试代码
if __name__ == '__main__':
dataArr, labelMat = logRegres.loadDataSet()
wigets = logRegres.stocGradAscent0(array(dataArr), labelMat)
logRegres.plotBestFit(wigets)
3、结果
由图看出,此次拟合的曲线错分了1/3的样本,效果相比于梯度上升算法欠佳
数据集存在的问题:部分数据缺失。
方法:
预处理:
使用Logistic回归方法进行分类时,我们需要把测试集上的每个特征向量乘以最优化方法得到的回归系数,再将该乘积的结果求和,最后给Sigmoid函数即可。
如果对应的sigmoid值大于0.5,就预测类别标签为1,否则为0。
import numpy as np
# 使用逻辑回归来预测病马的死亡率
def sigmoid(inX):
"""
sigmoid函数
"""
if inX > 0:
return 1.0 / (1 + np.exp(-inX))
else:
return np.exp(inX) / (1 + np.exp(inX))
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
m, n = np.shape(dataMatrix)
weights = np.ones(n)
for j in range(numIter):
dataIndex = list(range(m))
for i in range(m):
alpha = 4 / (1.0 + j + i) + 0.01
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
def classifyVector(inX, weights):
prob = sigmoid(sum(inX * weights))
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('\t')
lineArr = []
for i in range(21):
lineArr.append(float(currLine[i]))
trainingSet.append(lineArr)
trainingLabels.append(float(currLine[21]))
trainWeights = stocGradAscent1(np.array(trainingSet), trainingLabels, 500)
errorCount = 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(np.array(lineArr), trainWeights)) != int(currLine[21]):
errorCount += 1
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)))
def main():
multiTest()
if __name__ == '__main__':
main()