从疝气病症状预测病马的死亡率
1、实战背景
使用Logistic回归来预测患疝气病的马的存活问题,数据包含了368个样本和28个特征;除了部分指标主观和难以测量外,该数据还存在一个问题,数据集中有30%的值是缺失的。如何处理数据集中的数据缺失问题,然后再利用Logistic回归和随机梯度上升算法来预测病马的生死。
2、准备数据
解决数据缺失问题的可选方法:
1.使用可用特征的均值来填补缺失值;
2.使用特殊值来填补缺失值,如-1;
3.忽略有缺失值的样本;
4.使用相似样本的均值添补缺失值;
5.使用另外的机器学习算法预测缺失值。
预处理数据做两件事:
1.如果测试集中一条数据的特征值已经确实,那么我们选择实数0来替换所有缺失值,因为本文使用Logistic回归。因此这样做不会影响回归系数的值。sigmoid(0)=0.5,即它对结果的预测不具有任何倾向性。
2.如果测试集中一条数据的类别标签已经缺失,那么我们将该类别数据丢弃,因为类别标签与特征不同,很难确定采用某个合适的值来替换。
原始的数据集经过处理,保存为两个文件:horseColicTest.txt和horseColicTraining.txt。
3、使用Python构建Logistic回归分类器
先用自己写的改进的随机梯度上升算法进行预测
错误率高的,耗时久,并且每次运行的错误率也是不同的,错误率高的时候可能达到40%多。因为数据集本身有30%的数据缺失,这个是不能避免的。另一个主要原因是,我们使用的是改进的随机梯度上升算法,因为数据集本身就很小,就几百的数据量。用改进的随机梯度上升算法显然不合适。
再试试梯度上升算法
算法耗时减少了,错误率稳定且较低。
当数据集较小时,我们使用梯度上升算法
当数据集较大时,我们使用改进的随机梯度上升算法
在Sklearn中,可以根据数据情况选择优化算法,比如数据较小的时候,使用liblinear,数据较大时,使用sag和saga。
4、使用Sklearn构建Logistic回归分类器
solver='liblinear’
solver=‘sag’ max_iter=5000
小数据集,sag算法需要迭代上千次才收敛,而liblinear只需要不到10次。根据数据集情况,选择最优化算法。
代码汇总
import numpy as np
import random
from sklearn.linear_model import LogisticRegression
"""
函数说明:sigmoid函数
Parameters:
inX - 数据
Returns:
sigmoid函数
"""
def sigmoid(inX):
return 1.0 / (1 + np.exp(-inX))
"""
函数说明:随机梯度上升算法
"""
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
# 返回dataMatrix的大小。m为行数,n为列数。
m, n = np.shape(dataMatrix)
# 参数初始化
weights = np.ones(n)
for j in range(numIter):
dataIndex = list(range(m))
for i in range(m):
# 降低alpha的大小,每次减小1/(j+i)。
alpha = 4 / (1.0 + j + i) + 0.01
# 随机选取样本
randIndex = int(random.uniform(0, len(dataIndex)))
# 选择随机选取的一个样本,计算h
h = sigmoid(sum(dataMatrix[randIndex] * weights))
# 计算误差
error = classLabels[randIndex] - h
# 更新回归系数
weights = weights + alpha * error * dataMatrix[randIndex]
# 删除已经使用的样本
del (dataIndex[randIndex])
return weights
"""
函数说明:梯度上升算法
Parameters:
dataMatIn - 数据集
classLabels - 数据标签
Returns:
weights.getA() - 求得的权重数组(最优参数)
"""
def gradAscent(dataMatIn, classLabels):
# 转换成numpy的mat
dataMatrix = np.mat(dataMatIn)
# 转换成numpy的mat,并进行转置
labelMat = np.mat(classLabels).transpose()
# 返回dataMatrix的大小。m为行数,n为列数。
m, n = np.shape(dataMatrix)
# 移动步长,也就是学习速率,控制更新的幅度。
alpha = 0.001
# 最大迭代次数
maxCycles = 500
weights = np.ones((n,1))
for k in range(maxCycles):
# 梯度上升矢量化公式
# g(X)=h(theta) = theta * X
h = sigmoid(dataMatrix * weights)
error = labelMat - h
# theta = theta + alpha * X^T(y - g(X))
weights = weights + alpha * dataMatrix.transpose() * error
return weights.getA()
"""
函数说明:使用Python写的Logistic分类器做预测
"""
def colicTest():
# 打开训练集
frTrain = open('horseColicTraining.txt')
# 打开测试集
frTest = open('horseColicTest.txt')
# 训练数据集
trainingSet = []
# 训练集标签
trainingLabels = []
# 遍历所有训练集
for line in frTrain.readlines():
# 去除空格,并以'\t'分割
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[-1]))
# trainWeights = gradAscent(np.array(trainingSet), trainingLabels, 500)
# 使用梯度上升训练
trainWeights = gradAscent(np.array(trainingSet), trainingLabels)
errorCount = 0
numTestVec = 0.0
for line in frTest:
numTestVec += 1.0
currLine = line.strip().split('\t')
lineArr = []
for i in range(len(currLine) - 1):
lineArr.append(float(currLine[i]))
# if int(classifyVector(np.array(lineArr), trainWeights)) != int(currLine[-1]): # 改进的梯度上升算法
if int(classifyVector(np.array(lineArr), trainWeights[:, 0])) != int(currLine[-1]):
errorCount += 1
errorRate = (float(errorCount) / numTestVec) * 100
print("测试集合错误率为: %.2f%%" % errorRate)
"""
函数说明:分类函数
Parameters:
inX - 特征向量
weights - 回归系数
returns:
分类结果
"""
def classifyVector(inX, weights):
prob = sigmoid(sum(inX * weights))
if prob > 0.5:
return 1.0
else:
return 0.0
"""
函数说明:使用sklearn构件Logistic回归分类
"""
def colicSklearn():
# 打开训练集
frTrain = open('horseColicTraining.txt')
# 打开测试集
frTest = open('horseColicTest.txt')
# 训练集和标签
trainingSet = []
trainingLabels = []
# 测试集和标签
testSet = []
testLabels = []
# 遍历训练集
for line in frTrain.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[-1]))
# 遍历测试集
for line in frTest.readlines():
currLine = line.strip().split('\t')
lineArr = []
for i in range(len(currLine) - 1):
lineArr.append(float(currLine[i]))
testSet.append(lineArr)
testLabels.append(float(currLine[-1]))
# sklearn中的分类器
classifier = LogisticRegression(solver='sag', max_iter=5000).fit(trainingSet, trainingLabels)
test_accurcy = classifier.score(testSet, testLabels) * 100
print('正确率:%f%%' % test_accurcy)
if __name__ == '__main__':
colicSklearn()
if __name__ == '__main__':
# 梯度上升算法测试
# colicTest()
# sklearn中的分类器
colicSklearn()
总结
Logistic回归的优缺点
优点:实现简单,易于理解和实现;计算代价不高,速度很快,存储资源低。
缺点:容易欠拟合,分类精度可能不高。