《机器学习实战》之逻辑回归--基于Python3--01
Posted 华少的知识宝典
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《机器学习实战》之逻辑回归--基于Python3--01相关的知识,希望对你有一定的参考价值。
其函数图像如下图。可以看到当坐标轴的横坐标的尺度足够大时,在x=0处sigmoid函数看起来很像阶跃函数。并且由函数图像可知,sigmod函数能够满足逻辑回归的基本要求,并且当g(z)>0.5时可将其近似认为g(z)=1,而当g(z)≤0.5时,认为g(z)=0。g(z)的输出,在某种程度上表示一个分类问题在给定x的条件下等于0或者1的概率。
令导数为0,可求出x=2即取得函数f(x)的极大值。极大值等于f(2)=4。
但是真实环境中的函数不会像上面这么简单,就算求出了函数的导数,也很难精确计算出函数的极值。此时我们就可以用迭代的方法来做。就像爬坡一样,一点一点逼近极值。这种寻找最佳拟合参数的方法,就是最优化算法。爬坡这个动作用数学公式表达即为:
import numpy as np
"""
函数说明: 加载并解析数据
Parameters:
无
Returns:
dataMat - 数据列表。注意:这里为 list类型
labelMat - 标签列表。注意:这里为 list类型
"""
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])]) # 这里将 X0 的值设为了 1.0,读取到的文件的每行前两个值分别为 X1 和 X2
labelMat.append(int(lineArr[2])) # 读取到的文件的每行的第三个值为 类别标签
return dataMat, labelMat
"""
函数说明: sigmoid函数
Parameters:
inX - 输入数据
Returns:
计算得到的sigmoid函数值
"""
def sigmoid(inX):
return 1.0 / (1 + np.exp(-inX))
"""
函数说明: 梯度上升法
Parameters:
dataMatIn - 数据集,是一个2维NumPy数组, 每列分别代表每个不同的特征, 每行则代表每个训练样本。
classLabels - 数据类别标签。它是一个 1*100 的行向量. 为了便于矩阵计算,
需要将该 行向量 转换为 列向量, 做法是将原向量转置, 再将它赋值给labelMat
Returns:
weights - 返回权重 数组,即最优的参数
"""
def gradAscent(dataMatIn, classLabels):
dataMatrix = np.mat(dataMatIn) # 将 NumPy数组 转化为 矩阵
labelMat = np.mat(classLabels).transpose() # 将数据转化为 矩阵,然后再 转置
m, n = np.shape(dataMatrix) # 得到 dataMatrix 矩阵的 大小(m行,n列)。即:m个样本数, n个特征
alpha = 0.001 # 步长
maxCycles = 500 # 迭代次数
weights = np.ones((n, 1)) # 回归系数。这里生成了一个长度与 特征数 相同(n行,1列)的 矩阵,且 里面的 所有值都为 1
for k in range(maxCycles):
# 每一行 特征值 乘以 一个回归系数,然后把所有的值相加,最后将各自的总和带入Sigmoid函数中,得到一个矩阵
h = sigmoid(dataMatrix*weights) # 注意:m*n矩阵 乘以 n*1 矩阵 = m*1矩阵。所以这里的 h 不是 一个数,而是 一个列向量,其元素的个数等于样本数
error = (labelMat - h) # 计算 误差。这里得到的结果还是一个矩阵
weights = weights + alpha * dataMatrix.transpose()* error # 根据误差 更新回归系数的向量
return weights # 返回回归系数
# 这里关于 for 循环里的 详细解说,可以参考这篇博客(主要在其3.4部分的公式演绎):
# https://blog.csdn.net/achuo/article/details/51160101
if __name__ == "__main__":
dataMat, labelMat = loadDataSet()
weights = gradAscent(dataMat, labelMat)
print(weights)
3.2 梯度上升算法
"""
函数说明: 梯度上升法
Parameters:
dataMatIn - 数据集,是一个2维NumPy数组, 每列分别代表每个不同的特征, 每行则代表每个训练样本。
classLabels - 数据类别标签。它是一个 1*100 的行向量. 为了便于矩阵计算,
需要将该 行向量 转换为 列向量, 做法是将原向量转置, 再将它赋值给labelMat
Returns:
weights - 返回权重 数组,即最优的参数
"""
def gradAscent(dataMatIn, classLabels):
dataMatrix = np.mat(dataMatIn) # 将 NumPy数组 转化为 矩阵
labelMat = np.mat(classLabels).transpose() # 将数据转化为 矩阵,然后再 转置
m, n = np.shape(dataMatrix) # 得到 dataMatrix 矩阵的 大小(m行,n列)。即:m个样本数, n个特征
alpha = 0.001 # 步长
maxCycles = 500 # 迭代次数
weights = np.ones((n, 1)) # 回归系数。这里生成了一个长度与 特征数 相同(n行,1列)的 矩阵,且 里面的 所有值都为 1
for k in range(maxCycles):
# 每一行 特征值 乘以 一个回归系数,然后把所有的值相加,最后将各自的总和带入Sigmoid函数中,得到一个矩阵
h = sigmoid(dataMatrix*weights) # 注意:m*n矩阵 乘以 n*1 矩阵 = m*1矩阵。所以这里的 h 不是 一个数,而是 一个列向量,其元素的个数等于样本数
error = (labelMat - h) # 计算 误差。这里得到的结果还是一个矩阵
weights = weights + alpha * dataMatrix.transpose()* error # 根据误差 更新回归系数的向量
return weights # 返回回归系数
if __name__ == "__main__":
dataMat, labelMat = loadDataSet()
weights = gradAscent(dataMat, labelMat)
print(weights)
3.3 画出决策边界
"""
函数说明: 画出 数据集 和 Logistic回归最佳拟合直线
Parameters:
dataMat - 数据
labelMat - 样本的类别标签
weights - 回归系数
Returns:
无
"""
def plotBestFit(dataMat, labelMat, weights):
import matplotlib.pyplot as plt
dataArr = np.array(dataMat) # 将 dataMat 转换为 array 数组
n = np.shape(dataArr)[0] # 返回数据的个数。这里取里面标签为0处的数据,即 行数
# 正样本(分类为1)和负样本(分类为0)
xcord1 = []; ycord1 = []
xcord2 = []; ycord2 = []
for i in range(n):
if int(labelMat[i]) == 1: # 数据的 类别标签 为 1,那么就将其 放到 正样本 里面
xcord1.append(dataArr[i, 1]); ycord1.append(dataArr[i, 2]) # 取当前遍历到的第i行数据,取其前两个数据(也就是x和y,第三个数据为类别标签,在loadDataSet函数里就已经将其抽取到labelMat里了)
else: # 数据的 类别标签 不为 1,那么就将其 放到 负样本 里面
xcord2.append(dataArr[i, 1]); ycord2.append(dataArr[i, 2])
fig = plt.figure() # 创建 绘图 的 对象
ax = fig.add_subplot(111) # 添加子图,1行1列,在第1个位置
# 绘制 数据集
ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')
ax.scatter(xcord2, ycord2, s=30, c='green')
# 绘制 Logistic回归最佳拟合直线
x = np.arange(-3.0, 3.0, 0.1) # 为 x 取点。-3.0到3.0之间,步长为0.1
y = (-weights[0]-weights[1]*x)/weights[2] # 计算 y。w0+w1*x+w2*y=0 => y = (-w0-w1*x)/w2
# 如果这里出现报错:
# x and y must have same first dimension, but have shapes (60,) and (1, 60)
# 这是说:x和y的第一维度必须相同,但一个是60*1,一个是1*60。所以将y转置一下就好了。
y = y.transpose() # 将y转置
ax.plot(x, y) # 绘制出 拟合直线
plt.xlabel('X1'); plt.ylabel('X2') # 给坐标轴添加标签
plt.show()
if __name__ == "__main__":
dataMat, labelMat = loadDataSet()
weights = gradAscent(dataMat, labelMat)
plotBestFit(dataArr,labelMat,weights)
4.1 随机梯度上升
"""
函数说明: 随机梯度上升法
Parameters:
dataMatIn - 数据集,是一个2维NumPy数组, 每列分别代表每个不同的特征, 每行则代表每个训练样本。
classLabels - 数据类别标签。它是一个 1*100 的行向量。
Returns:
weights - 返回权重 数组,即最优的参数
"""
def stocGradAscent0(dataMatrix, classLabels):
m, n = np.shape(dataMatrix) # 得到 dataMatrix 矩阵的 大小(m行,n列)。即:m个样本数, n个特征
alpha = 0.01 # 步长
weights = np.ones(n) # 初始化长度为n的数组, 元素全部为 1
for i in range(m):
# sum(dataMatrix[i]*weights)是求 f(x)的值,f(x) = a1*x1+b2*x2+..+nn*xn,
# 此处求出的 h 是一个具体的数值,而不是一个矩阵
h = sigmoid(sum(dataMatrix[i]*weights))
error = classLabels[i] - h # 计算真实类别与预测类别之间的差值,即 误差
weights = weights + alpha * error * dataMatrix[i] # 根据误差调整回归系数
return weights
梯度上升算法的变量h和误差error都是向量,而随机梯度上升都是数值;
随机梯度上升没有矩阵的转换过程,所有变量的数据类型都是NumPy数组。
if __name__ == "__main__":
dataMat, labelMat = loadDataSet()
weights = stocGradAscent0(np.array(dataMat), labelMat) # 这里 注意 第一个参数的类型
plotBestFit(dataArr,labelMat,weights)
4.2 再次改进
"""
函数说明: 改进的 随机梯度上升法
Parameters:
Parameters:
dataMatIn - 数据集,是一个2维NumPy数组, 每列分别代表每个不同的特征, 每行则代表每个训练样本。
classLabels - 数据类别标签。它是一个 1*100 的行向量。
numIter - 迭代次数,整数
Returns:
weights - 返回权重 数组,即最优的参数
"""
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
m, n = np.shape(dataMatrix)
weights = np.ones(n)
for j in range(numIter):
dataIndex = list(range(m)) # 产生一个0至m的列表
for i in range(m):
alpha = 4/(1.0+j+i)+0.0001 # 每次迭代时都调整 步长。随着i和j的不断增大,alpha的值会不断减少,但是不为0,因为里面有一个常数项
randIndex = int(np.random.uniform(0, len(dataIndex))) # 随机产生一个 0~len()之间的一个 整数值,用于后面 通过随机选取样本来更新回归系数
# sum(dataMatrix[i]*weights)是求 f(x)的值,f(x) = a1*x1+b2*x2+..+nn*xn,
# 此处求出的 h 是一个具体的数值,而不是一个矩阵
h = sigmoid(sum(dataMatrix[randIndex]*weights))
error = classLabels[randIndex] - h
weights = weights + alpha * error * dataMatrix[randIndex]
del(dataIndex[randIndex]) # 使用完选出的值后,将其从列表中删掉。因为dataIndex列表是从0到m-1的数,所以每个数刚好就等于其索引值
return weights
if __name__ == "__main__":
dataMat, labelMat = loadDataSet()
weights = stocGradAscent1(np.array(dataMat), labelMat) # 这里 注意 第一个参数的类型
plotBestFit(dataMat,labelMat,weights)
梯度下降中 迭代更新的方式:
批量梯度下降(batch gradient descent):
也就是是梯度下降法最原始的形式,对全部的训练数据求得误差后再对θ进行更新。
优点是每步都趋向全局最优解;
缺点是对于大量数据,由于每步要计算整体数据,训练过程慢;
随机梯度下降(stochastic gradient descent):
每一步随机选择一个样本对θ进行更新。
优点是训练速度快;
缺点是每次的前进方向不好确定,容易陷入局部最优;
微型批量梯度下降(mini-batch gradient descent):
每步选择一小批数据进行批量梯度下降更新θ,
属于批量梯度下降和随机梯度下降的一种折中。非常适合并行处理。
以上是关于《机器学习实战》之逻辑回归--基于Python3--01的主要内容,如果未能解决你的问题,请参考以下文章
阿旭机器学习实战33中文文本分类之情感分析--朴素贝叶斯KNN逻辑回归