《Machine Learning in action》,机器学习实战(笔记)之Logistic regression
使用工具
- Python3.7
- pycharm
- anaconda
- jupyter notebook
一、 前言
- 在这里我们将更多的用书中的内容,更多的公式到额推导就不在这里介绍了,但是还是会适当的介绍一些了,因为本身也是刚开始学习的,所以自己对公式的把握也不是那么好,所以这里就不一一详细的介绍了
二、Logistic regression与 The gradient ascent algorithm(梯度上升算法)
- 针对于回归的问题,有线性回归和逻辑回归,但是两者却是有很大的区别,Logistic 回归主要就是做的分类的问题,二线性回归就是针对回归的问题
- Logistic回归是众多分类算法中的一员。通常,Logistic回归用于二分类问题,例如预测明天是否会下雨。当然它也可以用于多分类问题,正对与多分类的问题,也就是将多分类的问题装换为多个二分类进行处理,这样还是会回归到二分类的问题,所以在这里我们主要讲的就是二分类的问题。
1. Logistic regression
-
针对于Logistic 回归,我们可以用下面的例子来解释
-
如图我们的获得了上面的一些数据,现在用通过一条直线对这些数据进行拟合,从而我们将这个过程称为:回归,这一条直线就是我们的最佳拟合直线,如下图所示:
-
Logistic 回归是一种二分类的算法,它利用了Sigmoid函数,将阈值都锁定在0到1之间,Logistic本质上是一个基于条件概率的判别模型(Discriminative Model)。
-
现在我们来了解一些我们的Sigmoid函数,
-
可以观察它的图形
-
当然在这里,Z可以是一个矩阵,我们将我们的数据表示成一个向量,那么自然也会得到我们的Sigmoid矩阵函数,这里的棘突过程就不解释了
-
可以将z表示成
- w表示权重,x表示我们输入的数据值 -
我们一般是将g(z)=0.5 作为我们划分的依据,既是如果只是大于0.5 那么我们就将其划分为1类,相反就是为0类(就是不同的另一类)
-
如何求我们的这些参数(权重等参数),又将是我们面临的问题所以这里就会引入我们的代价函数。
代价函数 Cost Function
- 这里我们使用的交叉熵的代价函数
- 这里不会具体的研究这里公式的由来,只会大致的了解就好了,这里的a就是相当于z
- 而本文所使用的就是利用相反的公式:
- 比较两个公式可以看出,# 只是一个是用数值的形式给出,一个使用向量的形式给出来的,但是其实都是一样的,这里之所以给出不同的,就是方面大家的理解。了解公式的多样性,但是都是表达的差不多意思。只是没有负号 #
- 上面公式,我们所用到的就是梯度下降算法,而在这里我们多采用的就是梯度上升算法。
- 梯度上升法是求函数的局部最大值。因此,对比梯度下降法,其几何意义和很好理解,那就是:算法的迭代过程是一个“上坡”的过程,每一步选择坡度变化率最大的方向往上走,这个方向就是函数在这一点梯度方向(注意不是反方向了)。最后随着迭代的进行,梯度还是不断减小,最后趋近与零。
Gradient ascent algorithm 梯度上升算法
- 利用python代码来实现,梯度上升测试函数
- 我们的函数例子为
- 我们利用梯度上升算法就是求该公式取得极大值是的,x的值,代码如下:
'''
函数说明:梯度上升算法测试函数
求函数f(x) = -x^2 + 4x的极大值
'''
def Gradient_Ascent_test():
# 表示f(x)的导数
def f_prime(x_old):
# 这就是我们所要计算的公式
return -2 * x_old + 4
# 初始值,给一个小于x_new的值
x_old = -1
# 梯度上升算法的初始值,即从(0,0)开始
x_new = 0
# 步长就是学习速率,控制更新的快慢
alpha = 0.01
# 精度,就是更新的阈值
presision = 0.000001
while abs(x_new - x_old) > presision:
x_old = x_new
x_new = x_old + alpha * f_prime(x_old)
print(x_new)
if __name__ == '__main__':
Gradient_Ascent_test()
输出的即结果为:
- 有分析可知,该函数在x=2处,取得极大值,而我们利用梯度上升算法所取得的近视值已经很接近2了,所以我哦们的结果还是很满意的了。
在这里一些梯度上升算法的迭代公式就不在一步步的推导了。
三、python3.7 实战
1. 数据准备
- 数据集已经准备好了,这四一个简单的数据集,没有太多的实际意义,先从简单的入手就可以了。
-[数据集下载地址]
https://github.com/Tupeng2019/Machine_jichu/blob/master/Machine-Learning/Machine-Learning_note/Logistic regression/1_实战准备/testSet.txt - 首先实现数据的可视化:
# -*- coding:UTF-8 -*-
import matplotlib.pyplot as plt
import numpy as np
def loadDataSet():
# 创建数据列表,用来放入我们的数据
dataMat = []
# 该列表就是用来存放我们的标签的
labelMat = []
# 读取我们的数据集txt文件
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
'''
下面就是我们事先数据可视化的步骤
plotDataSet函数就是实现数据集可视化的
'''
def plotDataSet():
# 数据集的实例化,也就是加载数据
dataMat, labelMat = loadDataSet()
# 将数据转换为有序列的numpy中的array数组
dataArr = np.array(dataMat)
# 获取数据的个数,也就是行数
n = np.shape(dataMat)[0]
# 下面两个不同的列表就是分别计算正样本和负样本的
# 正样本
xcord1 = []; ycord1 = []
# 负样本
xcord2 = []; ycord2 = []
# 根据数据集的标签进行分类
for i in range(n):
# 如果标签是1
if int(labelMat[i]) == 1:
# 将为1 的放入我们的正样本中
xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2])
else:
# 其他,既是为0时放入负样本中
xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2])
# 绘制绘图框
fig = plt.figure()
# 添加子图subplot
ax = fig.add_subplot(111)
# 实现正样本的可视化
ax.scatter(xcord1, ycord1, s = 20, c = 'red', marker = 's',alpha=.5)
# 实现负样本的可视化
ax.scatter(xcord2, ycord2, s = 20, c = 'blue',alpha=.5)
# 设置绘图框的title
plt.title('DataSet')
plt.xlabel('x'); plt.ylabel('y')
plt.show()
if __name__ == '__main__':
plotDataSet()
实现结果图:
2. 训练算法
# -*- coding:UTF-8 -*-
import matplotlib.pyplot as plt
import numpy as np
def loadDataSet():
# 创建数据列表,用来放入我们的数据
dataMat = []
# 该列表就是用来存放我们的标签的
labelMat = []
# 读取我们的数据集txt文件
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
# 定义sigmiod函数
def sigmoid(inx):
return 1.0/(1 + np.exp(-inx))
'''
下面就是我们的梯度上升算法的实现
gradAscent
'''
def gradAscent(dataMatIn, classLabels):
# 将数据集转换为numpy的mat
dataMatrix = np.mat(dataMatIn)
# 将标签转换成numpy的mat。并且进行转置
# transpose 就是表示转置
labelMat = np.mat(classLabels).transpose()
# 返回dataMatrix的大小。m为行数,n为列数。
m, n = np.shape(dataMatrix)
# 移动步长,也就是学习速率,控制更新的幅度。
alpha = 0.001
# 最大迭代次数
maxCycles = 500
# 权重的初始化为0
weights = np.ones((n,1))
for k in range(maxCycles):
# h就是代表z的含义,梯度上升矢量化公式
h = sigmoid(dataMatrix * weights)
error = labelMat - h
weights = weights + alpha * dataMatrix.transpose() * error
# 将矩阵转换为数组,返回权重数组
# 这里的最优权重数组就是所求的最佳的解
return weights.getA()
if __name__ =='__main__':
dataMat, labelMat = loadDataSet()
matrix = gradAscent(dataMat,labelMat)
print(matrix)
运行结果:
这里为什么是出现三行数组呢,因为我们的数据集特征是两个,同时一般规定我们默认Xo为1,所以就该出现的是三个权重,Wo,W1,W2。
3. 绘制决策边界 Ploting the decision boundary
# -*- coding:UTF-8 -*-
import matplotlib.pyplot as plt
import numpy as np
def loadDataSet():
# 创建数据列表,用来放入我们的数据
dataMat = []
# 该列表就是用来存放我们的标签的
labelMat = []
# 读取我们的数据集txt文件
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
# 定义sigmiod函数
def sigmoid(inx):
return 1.0/(1 + np.exp(-inx))
'''
下面就是我们的梯度上升算法的实现
gradAscent
'''
def gradAscent(dataMatIn, classLabels):
# 将数据集转换为numpy的mat
dataMatrix = np.mat(dataMatIn)
# 将标签转换成numpy的mat。并且进行转置
# transpose 就是表示转置
labelMat = np.mat(classLabels).transpose()
# 返回dataMatrix的大小。m为行数,n为列数。
m, n = np.shape(dataMatrix)
# 移动步长,也就是学习速率,控制更新的幅度。
alpha = 0.001
# 最大迭代次数
maxCycles = 500
# 权重的初始化为0
weights = np.ones((n,1))
for k in range(maxCycles):
# h就是代表z的含义,梯度上升矢量化公式
h = sigmoid(dataMatrix * weights)
error = labelMat - h
weights = weights + alpha * dataMatrix.transpose() * error
# 将矩阵转换为数组,返回权重数组
# 这里的最优权重数组就是所求的最佳的解
return weights.getA()
def plotBestFit(weights):
# 数据集的实例化,也就是加载数据
dataMat, labelMat = loadDataSet()
# 将数据转换为有序列的numpy中的array数组
dataArr = np.array(dataMat)
# 获取数据的个数,也就是行数
n = np.shape(dataArr)[0]
# 下面两个不同的列表就是分别计算正样本和负样本的
# 正样本
xcord1 = []; ycord1 = []
# 负样本
xcord2 = []; ycord2 = []
# 根据数据集的标签进行分类
for i in range(n):
# 如果标签是1
if int(labelMat[i]) == 1:
# 将为1 的放入我们的正样本中
xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2])
else:
# 其他,既是为0时放入负样本中
xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2])
# 绘制绘图框
fig = plt.figure()
# 添加子图subplot
ax = fig.add_subplot(111)
# 实现正样本的可视化
ax.scatter(xcord1, ycord1, s = 20, c = 'red', marker = 's')
# 实现负样本的可视化
ax.scatter(xcord2, ycord2, s = 20, c = 'blue')
x = np.arange(-3.0, 3.0, 0.1)
y = (-weights[0]- weights[1] *x)/ weights[2]
ax.plot(x,y)
# 设置绘图框的title
plt.title('DataSet')
plt.xlabel('x'); plt.ylabel('y')
plt.show()
if __name__ == '__main__':
dataMat, labelMat = loadDataSet()
weights = gradAscent(dataMat, labelMat)
plotBestFit(weights)
运行结果:
这个分类结果相当不错,从上图可以看出,只分错了几个点而已。但是,尽管例子简单切数据集很小,但是这个方法却需要大量的计算(300次乘法)。因此下篇文章将对改算法稍作改进,从而减少计算量,使其可以应用于大数据集上。
四、总结、
逻辑回归的一般步骤:
- 收集数据:采用任意方法收集数据。
- 准备数据:由于需要进行距离计算,因此要求数据类型为数值型。另外,结构化数据格式则最佳。
- 分析数据:采用任意方法对数据进行分析。
- 训练算法:大部分时间将用于训练,训练的目的是为了找到最佳的分类回归系数。
- 测试算法:一旦训练步骤完成,分类将会很快。
- 使用算法:首先,我们需要输入一些数据,并将其转换成对应的结构化数值;接着,基于训练好的回归系数,就可以对这些数值进行简单的回归计算,判定它们属于哪个类别;在这之后,我们就可以在输出的类别上做一些其他分析工作。