使用Logistic回归估计马疝病的死亡率
(1)问题描述:
**训练集:**包含于文件horseColicTraining.txt中,用于训练得到模型的最佳系数。训练集包含299个样本(299行),每个样本含有21个特征(前21列),这些特征包含医院检测马疝病的指标;最后1列为类别标签,表示病马的死亡情况;部分样本含有缺失值。
**测试集:**包含于文件horseColicTest.txt中,通过预测测试样本中病马的死亡情况,来评估训练模型的优劣。测试集包含69个样本,部分样本部分特征缺失;最后1列为类别标签,用于计算错误率。
训练集与测试集存储形式如下:
(2)模型原理:
logistic回归是一种广义线性回归(generalized linear model),因此与多重线性回归分析有很多相同之处。它们的模型形式基本上相同,都具有 $ wx+b $ ,其中w和b是待求参数,其区别在于他们的因变量不同,多重线性回归直接将w‘x+b作为因变量,即 y = w x + b ,而logistic回归则通过函数L将 w x + b 对应一个隐状态 p = L ( w x + b ) ,然后根据p 与1-p的大小决定因变量的值。如果L是logistic函数,就是logistic回归。
(3)具体实现:
1.准备数据:
对于本次实验所用的数据集,需要进行如下预处理:
所有的缺失值必须用一个实数值来替换,这里选择实数0来替换所有缺失值,恰好能适用于Logistic回归,这样做在更新时不会影响回归系数的值。另外由于sigmoid(0)=0.5,即它对结果的预测不具有任何倾向性,因此上述做法也不会对误差造成任何影响。
测试数据集中发现一条数据的类别标签已经缺失,那么应将这条数据丢弃,这是因为类别标签与特征不同,很难确定采用某个合适的值来替换.
这里的 ‘horseColicTest.txt’ 和 ‘horseColicTraining.txt’ 为已经处理好的数据。
2.加载数据:
''' 数据加载 ''' 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
3.算法训练:
使用网络上改进后的随机梯度下降算法,修正系数:
改进内容为调整过程中步长的动态调整。
''' sigmoid函数 ''' def sigmoid(inX): return 1.0 / (1 + np.exp(-inX)) ''' 随机梯度下降算法 ''' def stoGradAscent1(dataMatrix, classLabels, numIter = 150): m,n = shape(dataMatrix) weights = ones(n) for j in range (numIter): dataIndex = range(m) for i in range(m): alpha = 4 / (1.0 + j + i) + 0.01 randIndex = int(random.uniform(0, len(dataIndex))) h = sigmoid(sum(dataMatrix[randIndex] * weights)) error = classLabels[randIndex] - h weights = weights + alpha * error * dataMatrix[randIndex] del (list(dataIndex)[randIndex]) return weights ''' 随机梯度下降算法 ''' def stocGradAscent0(dataList, labelList): dataArr = np.array(dataList) # 数据转化为矩阵 m, n = np.shape(dataArr) alpha = 0.01 weights = np.ones(n) for i in range(m): h = sigmoid(np.sum(dataArr[i]*weights)) error = (h-labelList[i]) weights = weights-alpha*error*dataArr[i] return weights ''' 改进的随机梯度下降算法 ''' def stocGradAscent1(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
4.使用模型分类:
该函数以回归系数和特征向量作为输入来计算对应的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
5.算法评估:
在测试集上计算分类精度,多次运行得到的结果可能稍有不同,主要是因为其中有随机的成分在里面。只有当梯度下降算法得到的回归系数已经完全收敛,那么结果才是确定的。
''' 在测试集上计算分类精度 ''' def colicTest(trainWeights, testDataList, testLabelList): rightCount = 0 # 判断错误的数量 testCount = len(testDataList) for i in range(testCount): if int(classifyVector(np.array(testDataList[i]), trainWeights))==int(testLabelList[i]): rightCount += 1 acc = float(rightCount)/testCount print("本次的精度为%f" % acc) return acc def main(): numTests = 10 accSum = 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, accSum/numTests))
(4)实验结果:
运行结果如下图所示:
(5)使用Sklearn构建LR分类器
代码如下所示:
import numpy as np from sklearn.linear_model import LogisticRegression def colicSklearn(): #使用np读取数据 trainFiled=np.loadtxt('horseColicTraining.txt',delimiter="\t") trainSet = trainFiled[:,:-1] trainLables=trainFiled[:,-1:] testFiled = np.loadtxt('horseColicTest.txt', delimiter="\t") testSet = testFiled[:, :-1] testLables = testFiled[:, -1:] classifier=LogisticRegression(solver='liblinear',max_iter=10).fit(trainSet,trainLables) test_accurcy=classifier.score(testSet,testLables) *100 print('正确率:%f%%'%test_accurcy) if __name__ == '__main__': colicSklearn()
运行结果:
与之前结果相比有一定提升。
(6)实验小结:
Logistic 回归的目的是寻找一个非线性的函数Sigmoid最佳拟合参数,求解过程可以由最优化算法来实现。在最优化算法中,最常用的是梯度上升算法,而梯度上升算法又可以简化为随机梯度上升算法。
随机梯度上升算法与梯度上升算法的效果相当,但是占用更少的计算资源;此外随机梯度上升是一个在线算法,他可以在新数据到来时就程程参数的更新,而不需要重新读取整个数据集来进行批处理计算。
Logistic回归的优缺点
优点:实现简单,易于理解和实现;计算代价不高,速度很快,存储资源低。
缺点:容易欠拟合,分类精度可能不高。