-
PS:实验环境的代码运行很坑,读入数据的时候相对路径要加一个work/
- 紫色字为实验界面提供的学习内容
-
目录
介绍:逻辑回归,就是在线性回归基础上融入一个激活函数加以改进,激活函数取:
实验介绍
1.实验内容
本实验介绍如何使用逻辑回归解决一个实际问题:预测病马死亡率。
2. 实验目标
通过本实验进一步掌握逻辑回归的原理,掌握如何使用逻辑回归解决实际问题,掌握实际世界中机器问题的解决流程。
3. 实验要点
- 逻辑回归
- 数据预处理
- 梯度下降
- sklearn
介绍:逻辑回归,就是在线性回归基础上融入一个激活函数加以改进,激活函数取:
实验步骤
定义函数 | 函数作用 | 传入参数 |
sigmoid(inX) | 将inX收束在0~1之间 | inX:被线性回归处理后的数据 |
stocGradAscent1(dataMatrix, classLabels, numIter=150) | 随机梯度上升,每次用一个样本更新回归系数 | dataMatrix:数据数组 classLabels:数据标签,即y numIter=150 迭代次数,默认150次 |
colicTest() | 用训练集数据随机梯度上升训练weights,并对预测集及测试,显示错误率 | |
classifyVector(inX, weights) | 得到对单个标签的训练分类结果 | inX:被线性回归处理后的数据 weights:这里的weights是经过训练集随机梯度上升训练后的回归系数矩阵 |
import numpy as np
import random
"""
函数说明:sigmoid函数
Parameters:
inX - 数据
Returns:
sigmoid函数
"""
def sigmoid(inX):
return 1.0 / (1 + np.exp(-inX))
"""
函数说明:改进的随机梯度上升算法
Parameters:
dataMatrix - 数据数组
classLabels - 数据标签
numIter - 迭代次数
Returns:
weights - 求得的回归系数数组(最优参数)
"""
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
### Start Code Here ###
#实现随机梯度上升算法
m,n = np.shape(dataMatrix) #n表示数据条数,m表示数据特征数
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 #降低alpha的大小,每次减小1/(j+i)。
randIndex = int(random.uniform(0,len(dataIndex))) #随机选取样本
h = sigmoid(sum(dataMatrix[randIndex]*weights)) #选择随机选取的一个样本,计算h
error = classLabels[randIndex] - h #计算误差
weights = weights + alpha * error * dataMatrix[randIndex] #更新回归系数
del(dataIndex[randIndex]) #删除已经使用的样本
return weights
### End Code Here ###
"""
函数说明:使用Python写的Logistic分类器做预测
Parameters:
无
Returns:
无
"""
def colicTest():
frTrain = open('lr_horse/horseColicTraining.txt') #打开训练集
frTest = open('lr_horse/horseColicTest.txt') #打开测试集
trainingSet = []; trainingLabels = []
### Start Code Here ###
#逐行读取文件
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]))
trainWeights = stocGradAscent1(np.array(trainingSet), trainingLabels, 500) #使用改进的随即上升梯度训练
### End Code Here ###
#计算错误率
errorCount = 0; numTestVec = 0.0
for line in frTest.readlines():
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]):
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
if __name__ == '__main__':
colicTest()
错误率并不低,并且每次运行的错误率也是不同的,错误率高的时候可能达到40%多。为啥这样?首先,因为数据集本身有30%的数据缺失,这个是不能避免的。另一个主要原因是,我们使用的是改进的随机梯度上升算法,因为数据集本身就很小,就几百的数据量。用改进的随机梯度上升算法显然不合适。让我们再试试梯度上升算法,看看它的效果如何,编写以下代码:
def gradAscent(dataMatIn, classLabels):
dataMatrix = np.mat(dataMatIn) #转换成numpy的mat
labelMat = np.mat(classLabels).transpose() #转换成numpy的mat,并进行转置
m, n = np.shape(dataMatrix) #返回dataMatrix的大小。m为行数,n为列数。
alpha = 0.01 #移动步长,也就是学习速率,控制更新的幅度。
maxCycles = 500 #最大迭代次数
weights = np.ones((n,1))
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights) #梯度上升矢量化公式
error = labelMat - h
weights = weights + alpha * dataMatrix.transpose() * error
return weights.getA() #将矩阵转换为数组,并返回
- 当数据集较小时,我们使用梯度上升算法
- 当数据集较大时,我们使用改进的随机梯度上升算法
随机梯度上升算法在每次训练时随机抽取训练数据对回归系数进行调优,梯度上升算法会使用全体数据对相关系数进行优化,每一次更新都要遍历一次训练集。随机梯度上升具有更优的时间复杂度,并且能够提高样本的收敛速度,但梯度上升算法可以获得全局最优解,随机梯度上升容易陷入局部最优解。
对应的,在Sklearn中,我们就可以根据数据情况选择优化算法,比如数据较小的时候,我们使用liblinear,数据较大时,我们使用sag和saga。
实验中有详述Sklearn的Logistic回归分类器及其各参数。
编写Sklearn分类器的代码
# -*- coding:UTF-8 -*-
from sklearn.linear_model import LogisticRegression
"""
函数说明:使用Sklearn构建Logistic回归分类器
Parameters:
无
Returns:
无
"""
def colicSklearn():
frTrain = open('lr_horse/horseColicTraining.txt') #打开训练集
frTest = open('lr_horse/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]))
### Start Code Here ###
#创建LogisticRegression对象
classifier = LogisticRegression(solver='liblinear',max_iter=10).fit(trainingSet, trainingLabels)
test_accurcy = classifier.score(testSet, testLabels) * 100
#调用fit函数进行模型训练,设置迭代次数为1000
#调用score函数计算模型在测试集上的正确率
### End Code Here ###
print('正确率:%f%%' % test_accurcy)
if __name__ == '__main__':
colicSklearn()
可以看到,正确率又高一些了。更改solver参数,比如设置为sag,使用随机平均梯度下降算法,看一看效果。你会发现,有警告了。
def colicSklearn():
frTrain = open('lr_horse/horseColicTraining.txt') #打开训练集
frTest = open('lr_horse/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]))
### Start Code Here ###
#创建LogisticRegression对象
classifier = LogisticRegression(solver='sag',max_iter=10).fit(trainingSet, trainingLabels)
test_accurcy = classifier.score(testSet, testLabels) * 100
#调用fit函数进行模型训练
#调用score函数计算模型在测试集上的正确率
### End Code Here ###
print('正确率:%f%%' % test_accurcy)
if __name__ == '__main__':
colicSklearn()
更改max_iter=5000,再运行代码:
# -*- coding:UTF-8 -*-
from sklearn.linear_model import LogisticRegression
"""
函数说明:使用Sklearn构建Logistic回归分类器
Parameters:
无
Returns:
无
"""
def colicSklearn():
frTrain = open('lr_horse/horseColicTraining.txt') #打开训练集
frTest = open('lr_horse/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]))
### Start Code Here ###
#创建LogisticRegression对象
classifier = LogisticRegression(solver='sag',max_iter=5000).fit(trainingSet, trainingLabels)
test_accurcy = classifier.score(testSet, testLabels) * 100
#调用fit函数进行模型训练,设置迭代次数为5000
#调用score函数计算模型在测试集上的正确率
### End Code Here ###
print('正确率:%f%%' % test_accurcy)
if __name__ == '__main__':
colicSklearn()
实验总结
1. Logistic回归的一般过程
收集数据:采用任意方法收集数据。
准备数据:由于需要进行距离计算,因此要求数据类型为数值型。另外,结构化数据格式则最佳。
分析数据:采用任意方法对数据进行分析。
训练算法:大部分时间将用于训练,训练的目的是为了找到最佳的分类回归系数。
测试算法:一旦训练步骤完成,分类将会很快。
使用算法:首先,我们需要输入一些数据,并将其转换成对应的结构化数值;接着,基于训练好的回归系数,就可以对这些数值进行简单的回归计算,判定它们属于哪个类别;在这之后,我们就可以在输出的类别上做一些其他分析工作。
2. Logistic回归的优点:
- 实现简单,易于理解和实现;
- 计算代价不高,速度很快;
- 存储资源低。
- logistic回归模型直接对分类的可能性进行分类,无需事先假设数据满足某种分类模型
- logistic回归可以处理稀疏高维特征(非0值很少的情况)
- 不仅可以预测出样本类别,还可以得到预测得到预测为某类的近似概率
3. Logistic回归的点:
- 容易欠拟合;
- 分类精度可能不高;
4. 其他:
Logistic回归的目的是寻找一个非线性函数Sigmoid的最佳拟合参数,求解过程可以由最优化算法完成。改进的一些最优化算法,比如sag。它可以在新数据到来时就完成参数更新,而不需要重新读取整个数据集来进行批量处理。机器学习的一个重要问题就是如何处理缺失数据。这个问题没有标准答案,取决于实际应用中的需求。现有一些解决方案,每种方案都各有优缺点。我们需要根据数据的情况,这是Sklearn的参数,以期达到更好的分类效果。
给出对数似然函数:
logistic回归的对数似然函数是一个取对数后的交叉熵损失函数的相反数,我们最大化这个似然函数函数就是最小化损失函数。