逻辑回归实战:从疝气病症预测病马的死亡率

为了预测患有疝病的马的生存情况,在研究的数据集包含368个样本和28个特征变量的基础上展开分析。研究中收集了有关马疝病检测的一些关键指标,在某些情况下这些指标具有主观性,在测量上存在一定难度。如疼痛等级评估等为例进行说明即可。The study acknowledges that this dataset also faces a challenge with 30% missing values.为了避免影响后续分析结果,在处理过程中需要注意对缺失值进行合理填充与剔除操作以确保准确性与可靠性.In addressing this challenge, the first step is to preprocess the data.为了提高模型的有效性, 需要在后续步骤中引入相应的统计方法进行补充与修正
1. 准备数据:处理数据中的缺失值
对于存在部分缺失的数据而言,丢弃与重新获取并不明智的做法;下面介绍几种解决数据缺失问题的方法:
-
使用可用特征的均值来填补缺失值
-
使用特殊值来填补缺失值,如-1
-
忽略有缺失值的样本
-
使用相似样本的均值添补缺失值
-
使用另外的机器学习算法预测缺失值
对于该实战中使用的数据集,在预处理阶段需要做两件事:
- 所有缺失的数据点都应由一个实数值替代。
我们采用实数0作为替代值。
这一设置特别适合于Logistic回归分析,并不会改变回归系数。
因为Sigmoid函数在输入为Sigmoid(0)=\ sigmoid(0)= 1/(1+e^{-x})= 1/(1+e^{−x})= 1/(1+e^{-x})= 1/(1+e^{-x})= 1/(1+e^{-x})= 1/(1+e^{-x})= 当x=0时等于Sigmoid(0)=\ sigmoid(0)= \frac{1}{2}.
所以,在预测过程中没有偏向性。
这种替代方法也不会引入误差。
在测试数据集中识别出某条数据的类别标签缺失,则应将其移除。这因而是由于类别标签与特征之间存在显著差异的原因,在无法找到一个合适的替代值来填充的情况下不得不进行处理。
在机器学习领域中解决缺失数据的问题无统一解决方案,
该问题受具体应用场景影响。
原始数据经过预处理后被保存为两个文件:horseColicTest.txt和horseColicTraining.txt。这些文件与原始数据可参考文末提供的链接获取。
2. 用Logistic回归进行分类
使用Logistic回归方法进行分类即对测试集中的每一个样本的特征向量进行处理。然后将这些乘积结果累加,并将其代入Sigmoid函数中计算总和。若计算得到的Sigmoid函数值超过0.5,则判定该样本属于正类。
'''
数据加载
'''
def loadDataSet(filePath):
f = open(filePath)
dataList = []
labelList = []
for line in f.readlines():
currLine = line.strip().split(' ')
lineArr = []
for i in range(21):
lineArr.append(float(currLine[i]))
dataList.append(lineArr)
labelList.append(float(currLine[21]))
return dataList, labelList
'''
sigmoid函数
'''
def sigmoid(inX):
return 1.0 / (1 + np.exp(-inX))
'''
随机梯度下降算法
'''
def stocGradAscent(dataList, labelList, numIter=150):
dataArr = np.array(dataList)
m, n = np.shape(dataArr)
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 # 步长为动态变化
rand = int(np.random.uniform(0, len(dataIndex)))
choseIndex = dataIndex[rand]
h = sigmoid(np.sum(dataArr[choseIndex] * weights))
error = h - labelList[choseIndex]
weights = weights - alpha * error * dataArr[choseIndex]
del (dataIndex[rand])
return weights
接下来涉及的是分类函数这一过程,在其运作中将回归系数与特征向量作为输入参数用于计算相应的Sigmoid值输出结果;若所得Sigmoid值超过0.5,则最终输出结果为1。
'''
进行分类
'''
def classifyVector(inX, weights):
prob = sigmoid(np.sum(inX * weights))
if prob > 0.5:
return 1.0
else:
return 0.0
下一步是在测试集上计算分类错误率。由于程序中存在一定的随机性,在多次运行后会得到略有不同的结果。主要原因在于程序中存在一定程度的随机性。只有在回归系数完全收敛的情况下,算法才会得出稳定的结果。
'''
在测试集上计算分类错误率
'''
def colicTest(trainWeights, testDataList, testLabelList):
errorCount = 0 # 判断错误的数量
testCount = 0 # 测试集总共的数量
testCount = len(testDataList)
for i in range(testCount):
if int(classifyVector(np.array(testDataList[i]), trainWeights)) !=
int(testLabelList[i]):
errorCount += 1
errorRate = float(errorCount)/testCount
return errorRate
最后只要调用如下的主函数即可获得10次运行之后的平均分类错误率
def main():
numTests = 10
errorSum = 0.0
trainDataList, trainLabelList = loadDataSet("horseColicTraining.txt")
testDataList, testLabelList = loadDataSet("horseColicTest.txt")
for i in range(numTests):
trainWeights = stocGradAscent(trainDataList, trainLabelList, 500)
errorSum += colicTest(trainWeights, testDataList, testLabelList)
print("%d次的平均错误率为%f"%(numTests, errorSum/numTests))
在运行该程序后,得到了平均分类错误率的数据(即输出结果)。通过优化stocGradAscent函数中参数numIter的值以及梯度下降算法中的步长设置等手段,则能够有效降低至约20%的平均分类错误率。
10次的平均错误率为0.329851
附:数据集及代码
这些代码和数据集都可以在GitHub上获取
不甘于「本该如此」,「多选参数 」值得关注

