Logistic回归
1.什么是回归
将离散的数据用一条线来拟合,这个过程叫做回归

2.那什么是Logistic回归
简单来说就是根据现有数据对分类边界线建立回归公式,以此进行分类。因此Logistic regression虽然被称为回归,但实际上是分类模型。它是一种用于解决二分类问题的机器学习方法,用于估计某种事物的可能性 ,比如你能不能追到女朋友,会不会中彩票之类,这种结果是1 or 0的问题。
3.Logisitc回归的一般过程
(1)收集数据:任意方法收集数据
(2)准备数据:由于需要进行距离的计算,因此要求数据类型 为数值型。当然结构化数据最好啦
(3)分析数据:不择一切手段对数据进行分析
(4)训练算法:训练目的就是为了找到最佳的分类回归系数
(5)测试算法:看看能不能用
(6)使用算法:输入数据并转化成结构化数据;利用训练好的回归系数对这些数值进行简单的回归计算,判断它们属于哪个类别
4. 绘制数据集
既然Logistic回归要收集准备数据,那我们不妨将数据绘制出来,更直观一点
代码如下
import matplotlib.pyplot as plt
import numpy as np
"""
函数说明:加载数据函数
Parameters:
dataMat 数据列表
labelMat 标签列表
Returns:
dataMat 数据列表
labelMat 标签列表
"""
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
"""
函数说明:绘制函数
"""
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.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.title('数据集') #绘制title
plt.xlabel('x')
plt.ylabel('y') #绘制label
plt.show() #显示
"""
函数说明:主函数
"""
if __name__ == '__main__':
plotDataSet()

数据集 testSet.txt
-0.017612 14.053064 0
-1.395634 4.662541 1
-0.752157 6.538620 0
-1.322371 7.152853 0
0.423363 11.054677 0
0.406704 7.067335 1
0.667394 12.741452 0
-2.460150 6.866805 1
0.569411 9.548755 0
-0.026632 10.427743 0
0.850433 6.920334 1
1.347183 13.175500 0
1.176813 3.167020 1
-1.781871 9.097953 0
-0.566606 5.749003 1
0.931635 1.589505 1
-0.024205 6.151823 1
-0.036453 2.690988 1
-0.196949 0.444165 1
1.014459 5.754399 1
1.985298 3.230619 1
-1.693453 -0.557540 1
-0.576525 11.778922 0
-0.346811 -1.678730 1
-2.124484 2.672471 1
1.217916 9.597015 0
-0.733928 9.098687 0
-3.642001 -1.618087 1
0.315985 3.523953 1
1.416614 9.619232 0
-0.386323 3.989286 1
0.556921 8.294984 1
1.224863 11.587360 0
-1.347803 -2.406051 1
1.196604 4.951851 1
0.275221 9.543647 0
0.470575 9.332488 0
-1.889567 9.542662 0
-1.527893 12.150579 0
-1.185247 11.309318 0
-0.445678 3.297303 1
1.042222 6.105155 1
-0.618787 10.320986 0
1.152083 0.548467 1
0.828534 2.676045 1
-1.237728 10.549033 0
-0.683565 -2.166125 1
0.229456 5.921938 1
-0.959885 11.555336 0
0.492911 10.993324 0
0.184992 8.721488 0
-0.355715 10.325976 0
-0.397822 8.058397 0
0.824839 13.730343 0
1.507278 5.027866 1
0.099671 6.835839 1
-0.344008 10.717485 0
1.785928 7.718645 1
-0.918801 11.560217 0
-0.364009 4.747300 1
-0.841722 4.119083 1
0.490426 1.960539 1
-0.007194 9.075792 0
0.356107 12.447863 0
0.342578 12.281162 0
-0.810823 -1.466018 1
2.530777 6.476801 1
1.296683 11.607559 0
0.475487 12.040035 0
-0.783277 11.009725 0
0.074798 11.023650 0
-1.337472 0.468339 1
-0.102781 13.763651 0
-0.147324 2.874846 1
0.518389 9.887035 0
1.015399 7.571882 0
-1.658086 -0.027255 1
1.319944 2.171228 1
2.056216 5.019981 1
-0.851633 4.375691 1
-1.510047 6.061992 0
-1.076637 -3.181888 1
1.821096 10.283990 0
3.010150 8.401766 1
-1.099458 1.688274 1
-0.834872 -1.733869 1
-0.846637 3.849075 1
1.400102 12.628781 0
1.752842 5.468166 1
0.078557 0.059736 1
0.089392 -0.715300 1
1.825662 12.693808 0
0.197445 9.744638 0
0.126117 0.922311 1
-0.679797 1.220530 1
0.677983 2.556666 1
0.761349 10.693862 0
-2.168791 0.143632 1
1.388610 9.341997 0
0.317029 14.739025 0
我对代码详细解释
ax.scatter(xcord1, ycord1, s=20, c='red', marker='s', alpha=.5)s 大小 c 颜色 marker 形状 alpha透明度
对于scatter函数各类参数详见plt.title('数据集') #绘制titleMatplotlib默认是不支持显示中文字符的,因此在最前边设置这两条属性即可
plt.rcParams['font.sans-serif']=['SimHei'] plt.rcParams['axes.unicode_minus'] = False
5.基于Logistic回归和Sigmoid函数的分类
- Logisic回归
优点:计算代价不高,易于理解和实现
缺点:容易欠拟合,分类精度可能不高
- Sigmoid函数
通过上面我们对Logistic回归的解释,我们在选择分类器函数时,由两个条件必须满足
(1)取值在0-1之间
(2)对一个事件发生的概率,50%是其结果的分水岭,选择函数应该关于0.5中心对称
对于好多好多的预测问题我们只想要得到0和1的答案,but好多输出结果不尽人意,也许是0.6.因此我们用这个函数曲线进行预测,当输入的数据待入Sigmoid函数,得到一个在光滑曲线上的某个点,如果该点大于0.5,认为事件的概率为1,若该点小于0.5,认为概率为0.
6. 寻找最佳参数
线性回归模型

Sigmoid函数

Logistic回归是处理二分类问题,所以输出的标记y={0,1},并且线性回归模型产生的预测值z是一个实值,所以我们将实值z转化成0/1值便可,即带入Sigmoid函数中。即将线性函数的结果映射到Sigmoid函数中将结果约束到0-1范围内。
而对于线性回归方程来说:
x 分类器输入的数据
w 最佳参数
而为了寻找该最佳参数,需要用到最优化理论的一些知识
介绍的第一个最优化算法叫做梯度上升法
- 我们由梯度下降法进行引入

梯度下降法(gradient descent)是一种常用的一阶(first-order)优化方法,是求解无约束优化问题最简单、最经典的方法之一。我们来考虑一个无约束优化问题min f(x) , 其中f(x)为连续可微函数,如果我们能够构造一个序列

并满足:

那么我们就能够不断执行该过程即可收敛到局部极小点,如下。

那么问题就是如何找到下一个点并满足
![[公式]](https://i-blog.csdnimg.cn/blog_migrate/c677f529fe5842890d58dff41fa40cd7.png)

- 而对于梯度上升法来说,只不过是将减号改为加号罢了。
梯度上升法的伪代码:
- 每个回归系数初始化为1
- 重复R次
-计算整个数据集的梯度
-使用alpha*gradient更新回归系数的向量
-返回回归系数
代码实现如下
import numpy as np
"""
函数说明:加载数据函数
Parameters:
dataMat 数据列表
labelMat 标签列表
Returns:
dataMat 数据列表
labelMat 标签列表
"""
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
"""
函数说明:sigmoid函数
Parameters:
inX - 数据
Returns:
sigmoid函数
"""
def sigmoid(inX):
return 1.0 / (1 + np.exp(-inX))
"""
函数说明:梯度上升算法
Parameters:
dataMatIn - 数据集
classLabels - 数据标签
Returns:
weights.getA() - 求得的权重数组(最优参数)
"""
def gradAscent(dataMatIn, classLabels):
dataMatrix = np.mat(dataMatIn) #dataMatIn数据集 转换成numpy的mat
labelMat = np.mat(classLabels).transpose() #classLabels数据标签 转换成numpy的mat,并进行转置(便于计算)
m, n = np.shape(dataMatrix) #返回dataMatrix的大小。m为行数(数据量),n为列数(特征数)
alpha = 0.001 #移动步长,也就是学习速率,控制更新的幅度。
maxCycles = 500 #最大迭代次数
weights = np.ones((n, 1)) #生成一个长度和特征数相同的矩阵,此处n为3
for k in range(maxCycles):
h = sigmoid(dataMatrix * weights) #梯度上升矢量化公式
error = labelMat - h
weights = weights + alpha * dataMatrix.transpose() * error #就是那个梯度算法的公式嘛
return weights.getA() #将矩阵转换为数组,返回权重数组
"""
函数说明:主函数
"""
if __name__ == '__main__':
dataMat, labelMat = loadDataSet()
print(gradAscent(dataMat, labelMat))
根据计算结果,我们很容易得到回归系数[W0,W1,W2]

8.分析数据:使用matplotlib画出决策边界
上面已经解出了便于一组回归系数,确定了不同类别数据之间的分割线。为了我们开展之后的各类分析工作,我们将使用matplotlib画出决策边界,从而使得优化的过程易于理解
代码实现如下
import matplotlib.pyplot as plt
import numpy as np
"""
函数说明:加载数据
Parameters:
dataMat 数据列表
labelMat 标签列表
Returns:
dataMat 数据列表
labelMat 标签列表
"""
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
"""
函数说明:sigmoid函数
Parameters:
inX - 数据
Returns:
sigmoid函数
"""
def sigmoid(inX):
return 1.0 / (1 + np.exp(-inX))
"""
函数说明:梯度上升算法
Parameters:
dataMatIn - 数据集
classLabels - 数据标签
Returns:
weights.getA() - 求得的权重数组(最优参数)
"""
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() # 将矩阵转换为数组,返回权重数组
"""
函数说明:绘制数据集
Parameters:
weights - 权重参数数组
Returns:
无
"""
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()
"""
函数说明:主函数
"""
if __name__ == '__main__':
dataMat, labelMat = loadDataSet()
weights = gradAscent(dataMat, labelMat)
plotBestFit(weights)

这个分类器分类效果不错,从图上看只分错了几个点而已,但是回顾对算法的原理的解析,我们却发现这个这个方法需要相当大量的计算(迭代300次的乘法与加法),因此下一步我们将对其进行改进,从而使它可以运用在我们现实生活中,处理例子复杂数量大的数据集
结构解析
- loadDataSet()函数功能:
打开文本文件testSet.txt并逐行读取。每行前两个值为X1和X2,第三个值是数据对应的标签 - gradAscent()函数
1.两个参数dataMathIn.labelMat
First是dataMathIn,它是一个二维Numpy数组,每 列代表不同的特征,每行代表每个训练样本。我们现在使用的是一个存有100个样本的简单数据集,它包含了两个特征X1和X2,再加上一个0维的特征,因此dataMathln里存放的是一个1003的数组。因此我们使用numpy中的mat函数将数组转化为矩阵便于计算。
Second是类别标签,它是一个1100的行向量。不过为了便于计算,我们的做法是将原向量转置再将其赋值给labelMat
2.梯度上升算法中的参数
alpha是向目标移动的步长
maxCycles是迭代次数
在for循环迭代完成后,将返回一个训练好的回归系数
新手对于代码的详细解释
-
lineArr = line.strip().split()去回车,放入列表
strip()函数用于移除字符串头尾指定的字符(默认为空格或者换行符)或字符序列
split()函数用于拆分字符串,split()不带参数时以空格进行分割 -
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
1.描述
append()函数用于在列表末尾添加新的对象
2.语法
list.append(obj)
list 列表对象
obj 添加到列表末尾的对象
注意:append()函数无返回值,但是会修改原本的列表
3.例子
list = ['hello', 'python', 'world']
list.append(2020)
print('list(updated):',list)

1878





