Logistic回归之梯度上升优化算法(二)
有了上一篇的知识储备,这一篇博客我们就开始Python3实战
1、数据准备
数据集:数据集下载
数据集内容比较简单,我们可以简单理解为第一列X,第二列Y,第三列是分类标签。根据标签的不同,对这些数据点进行分类。
import matplotlib.pyplot as plt
import numpy as np
'''
函数说明:加载数据
Parameters:
None
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
'''
函数说明:绘制数据集
Parameters:
None
Returns:
None
'''
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: #1为正样本
xcord1.append(dataMat[i][1])
ycord1.append(dataMat[i][2])
# xcord1.append(dataArr[i, 1]);ycord1.append(dataArr[i, 2])
else: #0为负样本
xcord2.append(dataMat[i][1])
ycord2.append(dataMat[i][2])
# xcord2.append(dataArr[i, 1]);ycord2.append(dataArr[i, 2])
fig = plt.figure()
ax = fig.add_subplot(111) #添加subplot
ax.scatter(xcord1,ycord1,s=20,c='red',marker = 's', alpha=.5,label ='1') #绘制正样本
ax.scatter(xcord2,ycord2,s=20,c='green',marker = 's', alpha=.5,label ='0') #绘制正样本
plt.title('DataSet') #绘制title
plt.xlabel('x'); plt.ylabel('y') #绘制label
plt.legend()
plt.show()
if __name__ == '__main__':
plotDataSet()
运行结果如下:
2、训练算法:使用梯度上升找到最佳参数
代码如下:
import matplotlib.pyplot as plt
import numpy as np
'''
函数说明:加载数据
Parameters:
None
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
'''
函数说明:sigmodi函数
Paremeters:
inX - 数据
Returns:
sigmoid函数
'''
def sigmoid(inX):
return 1.0/(1 + np.exp(-inX))
'''
函数说明:梯度上升算法
Parameters:
dataMatIn - 数据集
classLables - 数据标签
Returns:
weights.getA() - 求得的权重数组(最优参数)
'''
def gradAscent(dataMatIn, classLables):
dataMatrix = np.mat(dataMatIn) #转换成numpy的mat
# print(dataMatrix)
labelMat = np.mat(classLables).transpose() #转换成numpy的mat,并进行转置
# print(labelMat)
m, n =np.shape(dataMatrix)#返回dataMatrix的大小。m为行,n为列
alpha = 0.001 #移动补偿,也就是学习速率,控制更新的幅度
maxCycles = 500 #最大迭代次数
weights = np.ones((n,1))
# print(weights)
for k in range(maxCycles):
h = sigmoid(dataMatrix *weights) #梯度上升矢量公式
# print(h)
#权重系数计算公式
error = labelMat - h
weights = weights + alpha * dataMatrix.transpose()*error
return weights.getA() #将矩阵转换为数组,返回权重数组
if __name__ == '__main__':
np.set_printoptions(suppress=True)
dataMat,labelMat = loadDataSet()
print(gradAscent(dataMat,labelMat))
其中在gradAscent()函数中的循环里有一个训练参数的计算公式,这边我不做推导直接给出。推导网址
运行结果如图所示:
到此我们已经求解出回归系数[w0,w1,w2]。通过求解出的参数,我们可以确定不同类别数据之间的分隔线,画出决策边界。
3、绘制决策边界
代码如下:
import matplotlib.pyplot as plt
import numpy as np
'''
函数说明:加载数据
Parameters:
None
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
'''
函数说明:绘制数据集
Parameters:
None
Returns:
None
'''
def plotDataSet(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: #1为正样本
xcord1.append(dataMat[i][1])
ycord1.append(dataMat[i][2])
# xcord1.append(dataArr[i, 1]);ycord1.append(dataArr[i, 2])
else: #0为负样本
xcord2.append(dataMat[i][1])
ycord2.append(dataMat[i][2])
# xcord2.append(dataArr[i, 1]);ycord2.append(dataArr[i, 2])
fig = plt.figure()
ax = fig.add_subplot(111) #添加subplot
ax.scatter(xcord1,ycord1,s=20,c='red',marker = 's', alpha=.5,label ='1') #绘制正样本
ax.scatter(xcord2,ycord2,s=20,c='green',marker = 's', alpha=.5,label ='0') #绘制正样本
x = np.arange(-3.0,3.0,0.1)
y = (-weights[0] - weights[1] * x) / weights[2]
ax.plot(x,y)
plt.title('DataSet') #绘制title
plt.xlabel('x'); plt.ylabel('y') #绘制label
plt.legend()
plt.show()
'''
函数说明:sigmodi函数
Paremeters:
inX - 数据
Returns:
sigmoid函数
'''
def sigmoid(inX):
return 1.0/(1 + np.exp(-inX))
'''
函数说明:梯度上升算法
Parameters:
dataMatIn - 数据集
classLables - 数据标签
Returns:
weights.getA() - 求得的权重数组(最优参数)
'''
def gradAscent(dataMatIn, classLables):
dataMatrix = np.mat(dataMatIn) #转换成numpy的mat
# print(dataMatrix)
labelMat = np.mat(classLables).transpose() #转换成numpy的mat,并进行转置
# print(labelMat)
m, n =np.shape(dataMatrix)#返回dataMatrix的大小。m为行,n为列
alpha = 0.001 #移动补偿,也就是学习速率,控制更新的幅度
maxCycles = 500 #最大迭代次数
weights = np.ones((n,1))
# print(weights)
for k in range(maxCycles):
h = sigmoid(dataMatrix *weights) #梯度上升矢量公式
# print(h)
error = labelMat - h
weights = weights + alpha * dataMatrix.transpose()*error
return weights.getA() #将矩阵转换为数组,返回权重数组
if __name__ == '__main__':
np.set_printoptions(suppress=True)
dataMat,labelMat = loadDataSet()
weights = gradAscent(dataMat,labelMat)
plotDataSet(weights)
其中绘制的分隔线设置了sigmoid函数为0,回忆上一篇内容,0是两个分类的分解出,因此我们设定,然后接触X2和X1的关系式(及分割线的方程,注意X0=1)。
运行结果如下:
从分类结果可以看出,还有几个点是错的。但是这个方法徐良大量的计算(300次乘法),在下一篇文章会对算法稍作改进。
4、总结
Logistic回归的一般过程:
- 收集数据:采用任一方法收集数据
- 准备数据:需要距离计算,因此要求数据类型为数值型
- 分析数据:采用任意方法对数据进行分析
- 训练算法:大部分时间用于训练,为了找到最佳的分类回归洗漱
- 测试算法:训练步骤完成,分类将会很快。
参考文献:
- 《机器学习实战》第五章内容
- Jack Cui博客:http://cuijiahua.com/blog/2017/11/ml_6_logistic_1.html