![2198176696d1b7e376588c3d1a4e290d.gif](https://i-blog.csdnimg.cn/blog_migrate/938404aa85147556716f4e5f2de3c976.gif)
![9fd188442bbd7ffb55273b1c121ebb63.png](https://i-blog.csdnimg.cn/blog_migrate/2907d16f48231ab71469a2b4675b2a8b.png)
Python3《机器学习实战》学习笔记(七):Logistic回归实战篇之预测病马死亡率:https://blog.csdn.net/c406495762/article/details/77851973
【机器学习实战】【python3版本】【代码讲解】:https://www.bilibili.com/video/BV16t411Q7TM
相关的运行平台和环境
运行平台: win10
python: 3.7.6
Anaconda: 4.8.3
IDE: pycharm community
01 前期知识点Logistic回归一般处理的是二分类问题。Logistic回归进行分类的主要思想是:根据现有数据对分类边界线建立回归公式,以此进行分类。其实,Logistic本质上是一个基于条件概率的判别模型(Discriminative Model)。所以要想了解Logistic回归,我们必须先看一看Sigmoid函数 ,我们也可以称它为Logistic函数。它的公式如下:def Gradient_Ascent_test(): def f_prime(x_old): # f(x)的导数 return -2 * x_old + 4 x_old = -1 # 初始值,给一个小于x_new的值 x_new = 0 # 梯度上升算法初始值,即从(0,0)开始 alpha = 0.01 # 步长,也就是学习速率,控制更新的幅度 presision = 0.00000001 # 精度,也就是更新阈值 while abs(x_new - x_old) > presision: x_old = x_new x_new = x_old + alpha * f_prime(x_old) # 上面提到的公式 print(x_new) # 打印最终求解的极值近似值
这个就是固定的步长,不断地沿着梯度上升的方向去寻找函数最大值,然后更新,直到达到精度要求,我尝试F8不断更新,发现速度还是比较慢的,当我尝试增大步长,发现快了很多,这就是增加了迈步的大小,事实上,太大也会导致振荡,这个都是很常见的问题,废话不多说,运行查看结果:
def loadDataSet(): dataMat = [] # 创建数据列表 labelMat = [] # 创建标签列表 fr = open('testSet.txt') # 打开文件 for line in fr.readlines(): # 逐行读取 lineArr = line.strip().split() # 去回车,放入列表 dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) # 添加数据 labelMat.append(int(lineArr[2])) # 添加标签 fr.close() # 关闭文件 return dataMat, labelMat # 返回
这里都是常规操作,需要注意的是添加了一列1,这是b0,看过吴恩达的机器学习都应该知道,属于偏置项,其他的没什么问题,处理后数据如下:
def plotDataSet(): dataMat, labelMat = loadDataSet() # 加载数据集 dataArr = np.array(dataMat) # 转换成numpy的array数组 n = np.shape(dataMat)[0] # 数据个数 xcord1 = [] ycord1 = [] # 正样本 xcord2 = [] ycord2 = [] # 负样本 for i in range(n): # 根据数据集标签进行分类 if int(labelMat[i]) == 1: xcord1.append(dataArr[i, 1]) ycord1.append(dataArr[i, 2]) # 1为正样本 else: xcord2.append(dataArr[i, 1]) ycord2.append(dataArr[i, 2]) # 0为负样本 fig = plt.figure() ax = fig.add_subplot(111) # 添加subplot ax.scatter(xcord1, ycord1, s=20, c='red', marker='s', alpha=.5) # 绘制正样本 ax.scatter(xcord2, ycord2, s=20, c='green', alpha=.5) # 绘制负样本 plt.title('DataSet') # 绘制title plt.xlabel('X1') plt.ylabel('X2') # 绘制label plt.show() # 显示
由于刚才已经切分好成list,直接转化成np的array数组即可,如下:
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.001 # 移动步长,也就是学习速率,控制更新的幅度。 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() # 将矩阵转换为数组,返回权重数组
这里先将list转化成np的array方便后续处理,核心的是for循环的内容,将特征向量与权重相乘后,经过sigmoid函数,求出值与实际值(这里是二分类的标签值0和1)差值,然后去更新权重。这里需要注意的是返回的时候将矩阵转换成数组,方便下一步直接取数画出决策边界,求出的权重结果如下:
def plotBestFit(weights): dataMat, labelMat = loadDataSet() # 加载数据集 dataArr = np.array(dataMat) # 转换成numpy的array数组 n = np.shape(dataMat)[0] # 数据个数 xcord1 = [] ycord1 = [] # 正样本 xcord2 = [] ycord2 = [] # 负样本 for i in range(n): # 根据数据集标签进行分类 if int(labelMat[i]) == 1: xcord1.append(dataArr[i, 1]) ycord1.append(dataArr[i, 2]) # 1为正样本 else: xcord2.append(dataArr[i, 1]) ycord2.append(dataArr[i, 2]) # 0为负样本 fig = plt.figure() ax = fig.add_subplot(111) # 添加subplot ax.scatter(xcord1, ycord1, s=20, c='red', marker='s', alpha=.5) # 绘制正样本 ax.scatter(xcord2, ycord2, s=20, c='green', alpha=.5) # 绘制负样本 x = np.arange(-3.0, 3.0, 0.1) y = (-weights[0] - weights[1] * x) / weights[2] ax.plot(x, y) plt.title('BestFit') # 绘制title plt.xlabel('X1') plt.ylabel('X2') # 绘制label plt.show()
运行得到结果如下:
# 全批量梯度上升法-每次全部使用样本计算,计算量非常大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)) weights_array = np.array([]) for k in range(maxCycles): h = sigmoid(dataMatrix * weights) # 梯度上升矢量化公式 error = labelMat - h weights = weights + alpha * dataMatrix.transpose() * error weights_array = np.append(weights_array, weights) weights_array = weights_array.reshape(maxCycles, n) return weights.getA(), weights_array # 将矩阵转换为数组,并返回
下面是随机梯度上升法:
# 随机梯度上升法-每次随机取一个不同的样本进行更新回归系数def stocGradAscent1(dataMatrix, classLabels, numIter=150): m, n = np.shape(dataMatrix) # 返回dataMatrix的大小。m为行数,n为列数。 weights = np.ones(n) # 参数初始化 weights_array = np.array([]) # 存储每次更新的回归系数 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] # 更新回归系数 weights_array = np.append(weights_array, weights, axis=0) # 添加回归系数到数组中 del (dataIndex[randIndex]) # 删除已经使用的样本 weights_array = weights_array.reshape(numIter * m, n) # 改变维度 return weights, weights_array # 返回
对比可知,全批量梯度上升法每次更新需要全部数据跑一遍,而批量梯度上升法每次随机抽取样本进行梯度的计算,然后更新,每次不需要计算全部数据,每次取出的样本会删除,再次抽取,多个局部最优确定整体最优,而且步长会随着逼近不断缩小,也是为了避免振荡,下面画出对比图分析,代码如下:
def plotWeights(weights_array1, weights_array2): # 设置汉字格式 font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14) # 将fig画布分隔成1行1列,不共享x轴和y轴,fig画布的大小为(13,8) # 当nrow=3,nclos=2时,代表fig画布被分为六个区域,axs[0][0]表示第一行第一列 fig, axs = plt.subplots(nrows=3, ncols=2, sharex=False, sharey=False, figsize=(20, 10)) x1 = np.arange(0, len(weights_array1), 1) # 绘制w0与迭代次数的关系 axs[0][0].plot(x1, weights_array1[:, 0]) axs0_title_text = axs[0][0].set_title(u'改进的随机梯度上升算法:回归系数与迭代次数关系', FontProperties=font) axs0_ylabel_text = axs[0][0].set_ylabel(u'W0', FontProperties=font) plt.setp(axs0_title_text, size=20, weight='bold', color='black') plt.setp(axs0_ylabel_text, size=20, weight='bold', color='black') # 绘制w1与迭代次数的关系 axs[1][0].plot(x1, weights_array1[:, 1]) axs1_ylabel_text = axs[1][0].set_ylabel(u'W1', FontProperties=font) plt.setp(axs1_ylabel_text, size=20, weight='bold', color='black') # 绘制w2与迭代次数的关系 axs[2][0].plot(x1, weights_array1[:, 2]) axs2_xlabel_text = axs[2][0].set_xlabel(u'迭代次数', FontProperties=font) axs2_ylabel_text = axs[2][0].set_ylabel(u'W1', FontProperties=font) plt.setp(axs2_xlabel_text, size=20, weight='bold', color='black') plt.setp(axs2_ylabel_text, size=20, weight='bold', color='black') x2 = np.arange(0, len(weights_array2), 1) # 绘制w0与迭代次数的关系 axs[0][1].plot(x2, weights_array2[:, 0]) axs0_title_text = axs[0][1].set_title(u'梯度上升算法:回归系数与迭代次数关系', FontProperties=font) axs0_ylabel_text = axs[0][1].set_ylabel(u'W0', FontProperties=font) plt.setp(axs0_title_text, size=20, weight='bold', color='black') plt.setp(axs0_ylabel_text, size=20, weight='bold', color='black') # 绘制w1与迭代次数的关系 axs[1][1].plot(x2, weights_array2[:, 1]) axs1_ylabel_text = axs[1][1].set_ylabel(u'W1', FontProperties=font) plt.setp(axs1_ylabel_text, size=20, weight='bold', color='black') # 绘制w2与迭代次数的关系 axs[2][1].plot(x2, weights_array2[:, 2]) axs2_xlabel_text = axs[2][1].set_xlabel(u'迭代次数', FontProperties=font) axs2_ylabel_text = axs[2][1].set_ylabel(u'W1', FontProperties=font) plt.setp(axs2_xlabel_text, size=20, weight='bold', color='black') plt.setp(axs2_ylabel_text, size=20, weight='bold', color='black') plt.show()
这里画图比较简单,设置汉字格式后,画出权重与迭代次数的关系图,运行看结果:
def colicTest(): frTrain = open('horseColicTraining.txt') # 打开训练集 frTest = open('horseColicTest.txt') # 打开测试集 trainingSet = [] trainingLabels = [] 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) # 使用改进的随即上升梯度训练 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)
368个样本,28个特征,这里存在数据的缺失,处理办法是
使用可用特征的均值来填补缺失值;
使用特殊值来填补缺失值,如-1;
忽略有缺失值的样本;
使用相似样本的均值添补缺失值;
使用另外的机器学习算法预测缺失值。
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])) classifier = LogisticRegression(solver='sag', max_iter=5000).fit(trainingSet, trainingLabels) test_accurcy = classifier.score(testSet, testLabels) * 100 print('正确率:%f%%' % test_accurcy)
这里大致分析一下,主要不同的代码是这两行:
classifier = LogisticRegression(solver='sag', max_iter=5000).fit(trainingSet, trainingLabels) test_accurcy = classifier.score(testSet, testLabels) * 100
这里有一些参数就简单说一下solver,
s
olver:优化算法选择参数,只有五个可选参数,即newton-cg,lbfgs,liblinear,sag,saga。默认为liblinear。solver参数决定了我们对逻辑回归损失函数的优化方法,有四种算法可以选择,分别是:
liblinear:使用了开源的liblinear库实现,内部使用了坐标轴下降法来迭代优化损失函数。
lbfgs:拟牛顿法的一种,利用损失函数二阶导数矩阵即海森矩阵来迭代优化损失函数。
newton-cg:也是牛顿法家族的一种,利用损失函数二阶导数矩阵即海森矩阵来迭代优化损失函数。
sag:即随机平均梯度下降,是梯度下降法的变种,和普通梯度下降法的区别是每次迭代仅仅用一部分的样本来计算梯度,适合于样本数据多的时候。
saga:线性收敛的随机优化算法的的变重。
ending
点击在看,了解更多精彩内容微信公众号:share_mojie摩羯青年![fe02118d6486fcce45d2582d0e70bb52.png](https://i-blog.csdnimg.cn/blog_migrate/1b7c4992697afd8e6fbff6f79f25fb33.jpeg)
![781921986cd7fde9d19007f3b1a9ccd5.png](https://i-blog.csdnimg.cn/blog_migrate/762b069d5c217004c134910a396a5e76.png)
![6d8772700bc9e1d997aec8262226d33d.png](https://i-blog.csdnimg.cn/blog_migrate/1c848750cb07eeaeecdeed77315e191b.png)