逻辑回归
Logistic 回归 概述
逻辑回归虽然被称为回归,但其实际上是分类模型,并常用于二分类。其主要思想是:根据现有数据对分类边界线(Decision Boundary)建立回归公式,以此进行分类。
须知概念
Sigmoid 函数
回归 概念
假设现在有一些数据点,我们用一条直线对这些点进行拟合(这条直线称为最佳拟合直线),这个拟合的过程就叫做回归。进而可以得到对这些点的拟合直线方程,那么我们根据这个回归方程,怎么进行分类呢?请看下面。
二值型输出分类函数
我们想要的函数应该是: 能接受所有的输入然后预测出类别。例如,在两个类的情况下,上述函数输出 0 或 1.或许你之前接触过具有这种性质的函数,该函数称为 海维塞得阶跃函数(Heaviside step function)
,或者直接称为 单位阶跃函数
。然而,海维塞得阶跃函数的问题在于: 该函数在跳跃点上从 0 瞬间跳跃到 1,这个瞬间跳跃过程有时很难处理。幸好,另一个函数也有类似的性质(可以输出 0 或者 1 的性质),且数学上更易处理,这就是 Sigmoid 函数。 Sigmoid 函数具体的计算公式如下:
下图给出了 Sigmoid 函数在不同坐标尺度下的两条曲线图。当 x 为 0 时,Sigmoid 函数值为 0.5 。随着 x 的增大,对应的 Sigmoid 值将逼近于 1 ; 而随着 x 的减小, Sigmoid 值将逼近于 0 。如果横坐标刻度足够大, Sigmoid 函数看起来很像一个阶跃函数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d6X8WvR3-1597201450729)(http://data.apachecn.org/img/AiLearning/ml/5.Logistic/LR_3.png)]
因此,为了实现 Logistic 回归分类器,我们可以在每个特征上都乘以一个回归系数(如下公式所示),然后把所有结果值相加,将这个总和代入 Sigmoid 函数中,进而得到一个范围在 0~1 之间的数值。任何大于 0.5 的数据被分入 1 类,小于 0.5 即被归入 0 类。所以,Logistic 回归也是一种概率估计,比如这里Sigmoid 函数得出的值为0.5,可以理解为给定数据和参数,数据被分入 1 类的概率为0.5。想对Sigmoid 函数有更多了解,可以点开此链接跟此函数互动。
基于最优化方法的回归系数确定
Sigmoid 函数的输入记为 z ,由下面公式得到:
如果采用矩阵的写法,上述公式可以写成 ,它表示将这两个数值向量对应元素相乘然后全部加起来即得到 z 值。其中的向量 x 是分类器的输入数据,向量 w 也就是我们要找到的最佳参数(系数),从而使得分类器尽可能地精确。为了寻找该最佳参数,需要用到最优化理论的一些知识。我们这里使用的是——梯度上升法(Gradient Ascent)。
梯度下降法
梯度的介绍
需要一点点向量方面的数学知识
向量 = 值 + 方向
梯度 = 向量
梯度 = 梯度值 + 梯度方向
梯度下降法的思想
要找到某函数的最小值,最好的方法是沿着该函数的梯度反方向探寻。如果梯度记为 ▽ ,则函数 f(x, y) 的梯度由下式表示:
这个梯度意味着要沿 x 的方向移动 - ,沿 y 的方向移动- 。其中,函数f(x, y) 必须要在待计算的点上有定义并且可微。
上图展示的,梯度下降算法到达每个点后都会重新估计移动的方向。从 起点 开始,计算完该点的梯度,函数就根据梯度移动到下一点在这点,梯度再次被重新计算,并沿着新的梯度的反方向移动到 P2 。如此循环迭代,直到满足停止条件。迭代过程中,梯度算子总是保证我们能选取到最佳的移动方向。
上图中的梯度下降算法沿梯度反方向移动了一步。可以看到,梯度算子总是指向函数值增长最快的方向。这里所说的是移动方向,而未提到移动量的大小。该量值称为步长,记作 α 。用向量来表示的话,梯度下降算法的迭代公式如下:
该公式将一直被迭代执行,直至达到某个停止条件为止,比如迭代次数达到某个指定值或者算法达到某个可以允许的误差范围。
Logistic 回归 原理
Logistic 回归 工作原理
每个回归系数初始化为 1
重复 R 次:
计算整个数据集的梯度
使用 步长 x 梯度 更新回归系数的向量
返回回归系数
Logistic 回归 算法特点
优点: 计算代价不高,易于理解和实现。
缺点: 容易欠拟合,分类精度可能不高。
适用数据类型: 数值型和标称型数据。
Logistic 回归 项目案例
项目案例1: 使用 Logistic 回归对鸢尾花进行二分类
项目概述
在鸢尾花数据集中,采用梯度下降法法使用 Logistic 回归进行二分类
收集数据: 使用iris.arff.csv中的鸢尾花数据集
处理数据: 由于是进行二分类,只需选取鸢尾花中的两类数据,并且删除重复的数据
data = pd.read_csv(r"iris.arff.csv", header = 0)
#data
# 删除重复的记录
data.drop_duplicates(inplace = True)
# 名称映射为数字
data["class"] = data["class"].map({"Iris-setosa":0, "Iris-versicolor":1, "Iris-virginica":2})
# 筛选数据,只选择0和1
data = data[data["class"]!=2]
#data
编写逻辑回归类:包含初始化方法,sigmoid函数实现方法,训练方法,预测方法。
class LogisticRegression:
'''使用python语言实现逻辑回归算法。'''
def __init__(self, learning_rate, times):
'''初始化
parameters
-----
learning_rate: float
学习率
times: int
迭代次数
'''
self.learning_rate = learning_rate
self.times = times
def sigmoid(self,z):
'''sigmoid函数实现
Parameters
------
z: float
自变量,值为 z = w.T * x
Return
------
p : float 值为[0,1]之间。
返回样本属于类别为1的概率 yield来作为结果的预测。
当 s >= 0.5(z > 0)时,类别为1,否则判定为类别为0.
'''
return 1.0 / (1.0 + np.exp(-z))
def fit(self,X,y):
'''根据提供的训练数据,对模型型进行训练
Parameters
------
X :类数组类型 形状为:[样本数量,特征数量]
待训练的样本特征属性。
y : 类数组类型 形状为:[样本数量]
每个样本的目标值。(标签)
'''
X = np.asarray(X)
y = np.asarray(y)
# 创建权重向量,初始化为0,长度比特征数多1. (多出来的1是截距)
self.w_ = np.zeros(1 + X.shape[1]) # shape[0]为样本数量,shape[1]为特征数量
# 创建损失列表,保存每次迭代后的损失值
self.loss_ = []
for i in range(self.times):
z = np.dot(X, self.w_[1:]) + self.w_[0]
# 计算概率值(判定为1的概率值)
p = self.sigmoid(z)
# 根据逻辑回归的代价函数(目标函数或叫损失函数)计算损失值
# J(W) = - sum(y^(i) * log(s(z^(i))+(1-y^(i)log(1-s(z^(i))) (i 从1到n ,n 为样本的数量)
cost = - np.sum(y * np.log(p) + (1 - y) * np.log(1 - p))
self.loss_.append(cost)
# 调整权重值,根据公式 权重(j列) = 权重(j列) + 学习率 * sum( y - s(z)) * x(j列)
self.w_[0] += self.learning_rate * np.sum(y - p) * 1
self.w_[1:] += self.learning_rate * np.dot(X.T, y - p)
def predict_proba(self,X):
'''根据参数传递的样本,对样本数据进行预测属于某一个类别的概率
Parameters
-----
X: 类数组类型 形状为[ 样本数量, 特征数量]
待测试的样本特征
Return
-----
result:数组类型
预测结果(概率值)
'''
X = np.asarray(X)
z = np.dot(X, self.w_[1:]) + self.w_[0]
p = self.sigmoid(z)
# 将预测结果转换为二维数组,方便后续拼接
p = p.reshape(-1,1)
# 将两个数组拼接,方向是横向拼接
return np.concatenate([1-p, p], axis=1) #横向拼接 axis=1
def predict(self, X):
'''根据参数预测样本属于哪个类别
Parameters
-----
X: 类数组类型: [样本数量,特征数量].
待测试的样本特征
Returns
-----
result:数组类型
返回属于哪个类别
'''
return np.argmax(self.predict_proba(X), axis=1)
使用逻辑回归算法处理数据
# 传入两类的数据
t1 = data[data["class"]==0]
t2 = data[data["class"]==1]
# 对数据进行洗牌
t1 = t1.sample(len(t1), random_state=0)
t2 = t2.sample(len(t2), random_state=0)
#分类为训练集和测试集
train_X = pd.concat([t1.iloc[:40,:-1], t2.iloc[:40,:-1]], axis=0)
train_y = pd.concat([t1.iloc[:40,-1], t2.iloc[:40,-1]], axis=0)
test_X = pd.concat([t1.iloc[40:,:-1], t2.iloc[40:,:-1]], axis=0)
test_y = pd.concat([t1.iloc[40:,-1], t2.iloc[40:,-1]], axis=0)
# 鸢尾花的特征列都在同一个数量级,我们可以不用标准化处理
lr = LogisticRegression(learning_rate=0.01, times=20)
lr.fit(train_X, train_y)
#预测概率值
# lr.predict_proba(test_X)
result = lr.predict(test_X)
# 计算准确性
accuracy = np.sum(result==test_y) / len(test_y)
display(accuracy)
得到分类准确率为1.0
可视化处理: 绘制预测值和目标函数的损失值
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rcParams['font.family'] = 'SimHei'
mpl.rcParams['axes.unicode_minus'] = False
# 绘制预测值
plt.plot(result, 'ro',ms=15, label="预测值") # ms指定圆圈大小
plt.plot(test_y.values, 'go', label="真实值") # pandas读取时serise类型,我们需要转为ndarray
plt.title('逻辑回归预测')
plt.xlabel('样本序号')
plt.ylabel('预测值')
plt.legend()
plt.show()
# 绘制目标函数的损失值
plt.plot(range(1,lr.times+1), lr.loss_, 'go-')
plt.xlabel('迭代次数')
plt.ylabel('损失值loss')