回归:假设现在有一些数据点,我们用一条直线对这些点进行拟合(该线称为最佳拟合直线),这个拟合的过程叫做回归。
训练一般是指训练机器学习模型的参数,优化参数。
一、基于Logistic回归和Sigmoid函数的分类
我们需要的分类函数应该是接受所有的输入然后预测出类别,在二分类问题中,函数输出0或者1,为了避免函数在跳跃点上从0瞬间跳跃到1,可以使用sigmoid函数。为了实现Logistic回归分类器,需要在每个特征上都乘以一个回归系数,然后把所有乘积相加,将这个总和带入sigmoid函数中,进而得到一个范围在0~1之间的数值,该数值如果大于0.5,则被归为1类;如果小于0.5,则归为0类。所这方面看,Logistic回归也可以看作是一种概率估计。
二、基于最优化方法的最佳回归系数的确定
Sigmoid函数的输入为x向量和weights回归系数向量对应元素相乘然后全部加起来的值,其中向量x是分类器的输入数据,向量w也就是我们要找的最佳系数,从而使得分类器尽可能的精确。
最优化方法:梯度上升算法
基本思想:要找到某函数的最大值,最好的方法是沿着该函数的梯度(导数或偏导)方向探寻,梯度算子总是指向函数值增长最快的方向。梯度上升算法的迭代公式为:w := w + α*梯度
公式中的α为步长,所以梯度上升算法实际上是在说为了使函数达到最大值,回归系数应最大方向且最大步长迈进。
三、训练算法:使用梯度上升找到最佳参数
梯度上升法的伪代码:
每个回归系数初始化为1
重复R次:
计算整个数据集的梯度
使用alpha*gradient更新回归系数的向量
返回回归系数
Logistic回归梯度上升优化算法:
import numpy
import random
#加载数据
def data_load():
dataMat = []
labelMat = []
fileData = open("testSet.txt")
#逐行遍历读入的文件内容
for line in fileData.readlines():
#将每行的空格去掉,并以tab键分隔
pure_line = line.strip().split()
#特征矩阵第一列置为1,第二列,第三列为分隔后的列表的第一列和第二列
dataMat.append([1.0,float(pure_line[0]),float(pure_line[1])])
#将最后标签列加入标签矩阵中
labelMat.append(int(pure_line[2]))
return dataMat,labelMat
def sigmoid(in_x):
#返回输入值x的sigmoid函数值
return 1.0/(1+numpy.exp(-in_x))
#返回最佳回归系数向量,使用梯度上升优化算法
def log_regress(dataMat,labelMat):
dataMatix = numpy.mat(dataMat)
#将标签向量转置成m*1
labelMatix = numpy.mat(labelMat).transpose()
m,n = numpy.shape(dataMatix)
#设置步长
alpha = 0.001
#初始化回归系数向量
weight = numpy.ones((n,1))
print("梯度上升法中的weight向量:")
print(weight)
print(weight.shape)
#设置最大迭代次数
maxCycles = 500
for i in range(maxCycles):
#计算sigmoid函数与真实值之间的误差
error = (labelMatix - sigmoid(dataMatix*weight))
weight = weight + alpha*dataMatix.transpose()*error
return weight
四、分析数据:画出决策边界
上面确定了一组回归系数,确定了不同类别数据的分割线,画出该分割线
画出数据集和Logistic回归最佳拟合直线的函数:
#画出数据集和最佳拟合直线的函数
def plotBestFit(weights):
import matplotlib.pyplot as plt
dataMat,labelMat = data_load()
dataArr = numpy.array(dataMat)
n = numpy.shape(dataArr)[0]
xcord1 = []
ycord1 = []
xcord2 = []
ycord2 = []
for i in range(n):
#将标签值为1的特征值X1和X2分别存储在列表中
if labelMat[i]==1:
xcord1.append(dataArr[i,1])
ycord1.append(dataArr[i,2])
# 将标签值为0的特征值X1和X2分别存储在列表中
else:
xcord2.append(dataArr[i,1])
ycord2.append(dataArr[i,2])
fig = plt.figure()
#图像分为一行一列并且将图像画在第一块画布上
ax = fig.add_subplot(111)
#绘制数据集上的散点图
#标签为1的点为正方形
ax.scatter(xcord1,ycord1,s=30,c='red',marker='s')
#标签为0的点为原形
ax.scatter(xcord2, ycord2, s=30, c='green')
#画出最佳拟合直线
x = numpy.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')
结果:
五、训练算法:随机梯度上升
梯度上升算法需要在每次更新回归系数时都需要遍历整个数据集,计算复杂度太高。改进的方法是一次仅用一个样本点来更新回归系数,该方法称为随机梯度上升算法,随机梯度上升算法是一个“在线学习”算法,而梯度学习算法为“批处理”算法。
随机梯度上升算法伪代码:
所有回归系数初始化为1
对数据集中的每一个样本
计算该样本的梯度
使用alpha*gradient更新回归系数值
返回回归系数值
随机梯度上升算法:
#随机梯度上升算法,每次更新回归系数时只使用一个样本
def stocGradAscent0(dataMat,labelMat):
m,n = numpy.shape(dataMat)
alpha = 0.01
weights = numpy.ones(n)
print("随机梯度上升中的weights:")
print(weights)
for i in range(m):
h = sigmoid(sum(dataMat[i]*weights))
error = labelMat[i] - h
weights = weights + alpha*error*dataMat[i]
return weights
注:改进的随机梯度算法:
#随机梯度上升算法优化,输入参数中加入了迭代次数
def stocGradAscent1(dataMat,labelMat,numIter=150):
m,n = numpy.shape(dataMat)
weights = numpy.ones(n)
print("随机梯度上升中的weights:")
print(weights)
#在优化的随机梯度上升算法中加入了迭代次数
for j in range(numIter):
#将样本下标值存入列表中,python3.x中range()函数返回的是迭代器
dataIndex = list(range(m))
for i in range(m):
#alpha在每次迭代时都会变换,不断减小,但是不会为0
alpha = 1/(1.0+j+i)+0.01
#使用uniform()函数随机产生下标
randIndex = int(random.uniform(0,len(dataIndex)))
#计算weight
h = sigmoid(sum(dataMat[randIndex]*weights))
error = labelMat[randIndex] - h
weights = weights + alpha*error*dataMat[randIndex]
#删除此次使用的下标
del(dataIndex[randIndex])
return weights
1.alpha在每次迭代时都会调整,会缓解数据波动或高频波动。
2.通过随机选取样本来更新回归系数,将减少周期性的波动。
示例:从疝气病症预测病马的死亡率
这里的数据包含368个样本和28个特征。数据中存在缺失值,在此处缺失值全部填补为0,既可以保留现有数据,也不需要对优化的方法进行修改。
总结:使用Logsitc回归方法进行分类不需要做很多工作,所需的只是把数据集上每个特征向量乘以最优化方法得到的回归系数,再将该乘积结果求和,最后输入Sigmoig函数中即可。如果对应的Sigmoid值大于0.5就预测类别标签为 1,否则为0.
Logistic回归分类函数:
#分类函数,输入参数为输入值(特征向量)和最优回归系数
def classifyVector(in_x,weights):
#计算simoid值,如果值大于0.5,则返回1;如果值小于0.5,则返回0
pro = sigmoid(sum(in_x*weights))
if pro>0.5:
return 1.0
else:
return 0.0
#根据数据集的分类结果计算测试误差
def coicTest():
#打开训练数据文件和测试数据文件
frTrain = open('horseColicTraining.txt')
frTest = open('horseColicTest.txt')
trainSet = []
trainLabels = []
#逐行遍历文件内容
for line in frTrain.readlines():
#将每行去除空格并且以\t分割
currLine = line.strip().split('\t')
lineArr = []
#将每行的20个特征值加入列表中
for i in range(21):
lineArr.append(float(currLine[i]))
#再将整行向量加入训练数据集中
trainSet.append(lineArr)
#将标签加入向量列表中
trainLabels.append(float(currLine[21]))
#向stocGradAscent1()函数传入格式化后的输入参数,获得sigmoig函数的最佳回归系数向量
trainWeights = stocGradAscent1(numpy.array(trainSet),numpy.array(trainLabels),500)
errorCount = 0.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]))
#比较通过分类器分类出来的标签结果是否与样本的真实标签一致,如果不一致,错误数+1
if(int(classifyVector(numpy.array(lineArr),trainWeights))!=currLine[21]):
errorCount += 1
#计算错误率
errorRate = float(errorCount)/numTestVec
print("the error rate of this test is : %f"%errorRate)
return errorRate
#多次调用coicTest()函数,计算错误率的平均值
def multiTest():
numTests = 10
errorSum = 0.0
for k in range(numTests):
errorSum += coicTest()
print("after %d iterations the average error rate is: %f"%(numTests,errorSum/float(numTests)))
注:以上函数需要在main函数中调用,main函数如下:
if __name__ == "__main__":
# dataMatix,labelMatix = data_load()
# # weight_0 = log_regress(dataMatix,labelMatix)
# # weight_1 = stocGradAscent0(numpy.array(dataMatix),labelMatix)
# weight_2 = stocGradAscent1(numpy.array(dataMatix), labelMatix)
# # plotBestFit(weight_0.getA())
# plotBestFit(weight_2)
multiTest()
注释掉的代码为以上函数的测试语句。
总结:Logistic回归的目的是寻找一个非线性函数Sigmoid的最佳拟合参数,求解过程可以由最优化算法来完成。