1. 学习环境
windows10 、Anaconda(向初学者推荐这个工具) + Pycharm2018 、python 3.7。
2. Logistic回归概述
2.1 Logistic回归的一般过程
(1)收集数据:采用任意方法收集数据。
(2)准备数据:由于需要进行距离计算,因此要求数据类型为数值型。另外,结构化数据
格式则最佳。
(3)分析数据:采用任意方法对数据进行分析。
(4)训练算法:大部分时间将用于训练,训练的目的是为了找到最佳的分类回归系数。
(5)测试算法:一旦训练步驟完成,分类将会很快。
(6)使用算法:首先,我们需要输入一些数据,并将其转换成对应的结构化数值;
接着,基于训练好的回归系数就可以对这些数值进行简单的回归计算,判定它们属于哪个类别.,在这之后,我们就可以在输出的类别上做一些其他分析工作。
2.2 Logistic回归优缺点
优点:计算代价不高,易于理解和实现。
缺点:容易欠拟合,分类精度可能不高。
适用数据类型:数值型和标称型数据。
3.基于最优化方法的最佳回归系数确定
3.1 训练算法:使用梯度上升找到最佳参数
from numpy import *
import numpy
#LogistiC回归梯度上升优化算法
def loadDataSet():
dataMat = []; labelMat = []
fr = open('C:/Code_C/pycharm_workspace/MLIA_chapter05_LogisticRegression/testSet.txt')
#关于readlines()方法:1、一次性读取整个文件。 2、自动将文件内容分析成一个行的列表。
for line in fr.readlines():
#将文本中的每行最后一个换行符去掉 并且把每行中的字符一个个分开,变成list
lineArr = line.strip().split()
#从文件读写出来的是字符串,把它转换为 float 类型
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
# labelMat 为类别标签
labelMat.append(int(lineArr[2]))
return dataMat, labelMat
def sigmoid(inX):
return 1.0/(1+numpy.exp(-inX))
def gradAscent(dataMatIn, classLabels):
#dataMatIn 每列分别代表每个不同的特征,每行则代表每个训练样本
dataMatrix = mat(dataMatIn)
#transpose()函数的作用就是调换数组的行列值的索引值,类似于求矩阵的转置
#第二个参数(classLabels)是类别标签,它是一个1*100的行向量。为了便于矩阵运算,需要将该行向量转换为列
#向量,做法是将原向量转置,再将它赋值给1labelMat
labelMat = mat(classLabels).transpose()
#得到矩阵大小
m, n = shape(dataMatrix)
#再设置一些梯度上升算法所需的参数。
#变量alpha 是向目标移动的步长
alpha = 0.001
#maxCycles是迭代次数
maxCycles = 500
#onesones((n, 1)) 创建 n 行 1列 以1 填充的矩阵
#初始化回归系数为 1
weights = ones((n, 1))
for k in range(maxCycles):
# dataMatrix * weights 的乘积维数为 m*1
#定性地说,这里是在计算真实类别与预测类别的差值,接下来就是按照该差值的方向调整回归系数。
h = sigmoid(dataMatrix * weights)
error = (labelMat - h)
weights = weights + alpha * dataMatrix.transpose() *error
return weights
dataMat, labelMat = loadDataSet()
print(gradAscent(dataMat, labelMat))
3.2 分析数据:画出决策边界
画出数据集和Logstic回归最佳拟合直线的函数
#分析数据:画出决策边界
def plotBestFit(weights):
import matplotlib.pyplot as plt
dataMat, labelMat = loadDataSet()
dataArr = array(dataMat)
#获取特征矩阵的行数 shape(dataArr) 类型为元组()tuple
n = shape(dataArr)[0]
print(type(shape(dataArr)))
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 = 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()
dataArr, labelMat = loadDataSet()
weights = gradAscent(dataArr, labelMat)
plotBestFit(weights.getA())
测试图片:
这个分类结果相当不错,从图上看只错分了两到四个点。但是,尽管例子简单且数据集很小,这个方法却需要大量的计算(300次乘法)。因此下一节将对该算法稍作改进,从而使它可以用在真实数据集上。
3.3 训练算法:随机梯度上升
#改进随机梯度上升算法
def stocGradAscent1(dataMatrix, classLabels, numIter = 150):
m, n = shape(dataMatrix)
# 创建一个 1 行 n 列的数组(权重数组)
weights = ones(n)
dataIndex = range(m)
for j in range(m):
for i in range(m):
# 变量alpha 是向目标移动的步长
alpha = 4/(1.0 + j +i)+0.01
#uniform() 方法将随机生成下一个实数,它在 [x, y] 范围内。
#此处通过随机选取样本来更新回归系数
randIndex = int(random.uniform(0,len(dataIndex)))
h = sigmoid(sum(dataMatrix[randIndex] * weights))
error = classLabels[randIndex] - h
weights = weights + alpha * error * dataMatrix[randIndex]
return weights
dataArr, labelMat = loadDataSet()
weights = stocGradAscent1(array(dataArr), labelMat)
plotBestFit(weights)
测试结果:
程序运行之后应该能看到类似图上一个结果图。该分隔线达到了与stocGradAscent()差不多的效果,但是所使用的计算量更少。默认迭代次数是150,可以通过stocGradAscent1()的第3个参数来对此进行修改,例如:
weights = stocGradAscent1(array(dataArr), labelMat, 500)
4.示例:从疝气病症预测病马的死亡率
4.1 示例算法概述
(1)收集数据:给定数据文件。
(2)准备数据:用python解析文本文件并填充缺失值。
(3)分析数据:可视化并观察数据。
(4)训练算法:使用优化算法,找到最佳的系数。
(5)测试算法:为了量化回归的敢果,需要观察错误率。根据错误率决定是否回退到训练阶段,通过改变迭代的次数和步长等 参数来得到更好的回归系数。
(6)使用算法:实现一个简单的命令行程序来收集马的症状并输出预测结果并非难事,这可以做为留给读者的一道习题。
4.2 准备数据:处理数据中的缺失值
常用方法:
使用可用特征的均值来填补缺失值;
使用特殊值来±真补缺失值,如-1;
忽略有缺失值的样本;
使用相似样本的均值添补缺失值;
使用另外的机器学习算法预测缺失值。
4.5 测试算法:用Logistic 回归进行分类
原始的数据集经过预处理之后保存成两个文件:horseColicTest. txt 和 horseColicTraining. txt
#Logistic回归分类函数
def classifyVector(inX, weights):
prob = sigmoid(sum(inX*weights))
if prob > 0.5:return 1.0
else: return 0
def colicTest():
#打开测试集和训练集
frTrain = open('C:\\Code_C\\pycharm_workspace\\MLIA_chapter05_LogisticRegression\\horseColicTraining.txt')
frTest = open('C:\\Code_C\\pycharm_workspace\\MLIA_chapter05_LogisticRegression\\horseColicTest.txt')
trainingSet = []; trainingLabels = []
#readlines()方法读取整个文件所有行,保存在一个列表(list)变量中,
# 每行作为一个元素,但读取大文件会比较占内存。
for line in frTrain.readlines():
#strip()函数删除每行末尾换行符
#split() 按横向制表符(\t)拆分列表(数据集每一行)
currLine = line.strip().split('\t')
lineArr = []
for i in range(21):
#将训练数据集中前21个特征值调用append()函数存到traninSet列表中
lineArr.append(float(currLine[i]))
trainingSet.append(lineArr)
#将训练数据集中钱最后一个目标特征值调用append()函数存到trainingLabels列表中
trainingLabels.append(float(currLine[21]))
#调用stocGradAscent1()找出最佳系数
trainWeights = stocGradAscent1(array(trainingSet),trainingLabels,500)
errorCount = 0; numTestVec = 0.0
#读取测试集
# readlines()方法读取整个文件所有行,保存在一个列表(list)变量中,
# 每行作为一个元素,但读取大文件会比较占内存。
for line in frTest.readlines():
#记录测试集的样本数量
numTestVec +=1.0
# strip()函数删除每行末尾换行符
# split() 按横向制表符(\t)拆分列表(数据集每一行)
currLine = line.strip().split('\t')
lineArr = []
for i in range(21):
# 将测试数据集中前21个特征值调用append()函数存到lineArr列表中
lineArr.append(float(currLine[i]))
#调用classifyVector()函数计算测试集中每个样本的sigmoid值大于0.5就预测类别标签为1 ,否则为0。
#把 lineArr 转换为 numpy中的数组
#trainWeights 为用训练集算出的最佳回归系数
if int(classifyVector(array(lineArr), array(trainWeights))) != int(currLine[21]):
#如果根据最佳回归系数算出的类别不等于测试集中正确的类别,错误记录数errorCount 加1
errorCount +=1
#计算错误率,把 errorCount 转换为 float 类型,错误率是小数,如果不转换为float 算出来错误率一直为 0
errorRate = (float(errorCount)) / numTestVec
#输出错误率
print("the error rate of this test is: %f" %errorRate)
# 返回错误率
return errorRate
def multiTest():
numTests = 10; errorSum = 0.0
#迭代10次 算错误率取平均值
for k in range(numTests):
#错误率累加
errorSum += colicTest()
#输出迭代次数 并且输出迭代10次后的错误率平均值
print("after %d iterations the average error rate is: %f"\
% (numTests, errorSum/float(numTests)))
multiTest()
测试结果:
从上面的结果可以看到,10次迭代之后的平均错误率为35 %。事实上,这个结果并不差,因为有3 0 %的数据缺失。当然,如果调整colicTest()中的迭代次数和 stocGradAscent1()中的步长,平均错误率可以降到20%左右。第7章中我们还会再次使用到这个数据集。
5.小结
Logistic回归的目的是寻找一个非线性函数Sigmoid的最佳拟合参数,求解过程可以由最优化算法来完成。在最优化算法中,最常用的就是梯度上升算法,而梯度上升算法又可以简化为随机梯度上升算法。
随机梯度上升算法与梯度上升算法的效果相当,但占用更少的计算资源。此外,随机梯度上升是一个在线算法,它可以在新数据到来时就完成参数更新,而不需要重新读取整个数据集来进行批处理运算。
机器学习的一个重要问题就是如何处理缺失数据。这个问题没有标准答案,取决于实际应用中的需求。现有一些解决方案,每种方案都各有优缺点。
下一章将介绍与Logistic回归类似的另一种分类算法:支持向量机,它被认为是目前最好的现成的算法之一。