概念问题
-
逻辑回归是回归还是分类?
逻辑回归是分类,不要被名字所欺骗。因本篇文章仅讨论二分类问题,故我们将逻辑回归最终得到的预测值看作两个,即是或否(0或1)。
从线性回归开始
为什么从线性回归开始呢?因为二分类问题解的得出与线性回归有很大关系,逻辑回归之所以叫回归因为其与线性回归有着千丝万缕的关系。
有这样一个例子:
我们用numpy包生成一百个随机的x值,并且设定y值与x有一定的线性关系,最后把这样的线性关系绘制出来:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
%matplotlib inline
# 使用numpy生成100个随机点 样本
x_data = np.random.rand(100)
y_data = x_data * 0.1 + 0.2
fig, ax = plt.subplots()
ax.scatter(x_data, y_data)
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.show()
绘制出来的图是这样的:
之后我们用tensorflow(一个深度学习框架)的内置api直接构造一个线性回归的模型,进行预测,设置迭代次数为201次,每当20的倍数时便将学习结果输出、绘制出来,会发现我们最终得到的图与原图相似(即斜率基本一致,偏置值基本一致)。
# 构造一个线性模型
b = tf.Variable(1.1)
k = tf.Variable(0.5)
y = k * x_data + b
# 二次代价函数
loss = tf.reduce_mean( tf.square(y_data - y) )
# 定义一个梯度下降法来进行训练的优化器
optimizer = tf.train.GradientDescentOptimizer(0.2) # 梯度下降 学习率是0.2
# 定义一个最小化代价函数
train = optimizer.minimize(loss)
# 初始化变量
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for step in range(201):
sess.run(train)
if step % 20 == 0:
data1 = np.random.rand(100)
data2 = data1 * sess.run(k) + sess.run(b)
fig, ax = plt.subplots()
ax.scatter(data1,data2)
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.show()
print(step, sess.run([k, b]))
关于线性回归更具体的内容(如损失函数等)在另一篇博客里。
最终的图如下:
且斜率值和偏置值分别问:[0.099538416, 0.20024595]。与初始的基本一致。
(代码看不懂或不了解 tensorflow 没关系,只要明白线性回归就好了)
线性回归的目标便是求得一条曲线,能最大程度拟合我们的数据点(X1、X2轴),而曲线的Y值便是我们的预测值。其实就是个立体的曲线,图例如下:
即线性回归得出的是连续的结果,如果我们仅仅需要得到离散的结果,即是或否,由此便引出了二分类问题,也就是本文要讨论的逻辑回归方法。
问题的提出
现要实现一个简单的逻辑回归:
我们将建立一个逻辑回归模型来预测一个学生是否被大学录取。假设你是一个大学系的管理员,你想根据两次考试的结果来决定每个申请人的录取机会。你有以前的申请人的历史数据,你可以用它作为逻辑回归的训练集。对于每一个培训例子,你有两个考试的申请人的分数和录取决定。为了做到这一点,我们将建立一个分类模型,根据考试成绩估计入学概率。
即要求我们通过一些数据集来训练电脑,能实现输入两门考试成绩从而得到是否录取的结果。
设X1为exam1的成绩,X2为exam2的成绩,X1、X2就是我们的两个特征。
载入数据集
我们首先载入数据集看看数据特征与数据项:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import os
path = 'data' + os.sep + 'LogiReg_data.txt'
pdDate = pd.read_csv(path, header=None, names=['Exam1', 'Exam2', 'Admitted'])
pdDate.head()
结果如图:
可以看到学生有两门成绩,学校是通过两门成绩来决定是否录取。
之后我们可以利用python的绘图包通过散点图的绘制来将数据更直接的表现出来。
positive = pdDate[pdDate['Admitted'] == 1]
negative = pdDate[pdDate['Admitted'] == 0]
fig, ax = plt.subplots(figsize=(10, 6))
ax.scatter(positive['Exam1'], positive['Exam2'], s=30, c='b', marker='o', label='Admitted')
ax.scatter(negative['Exam1'], negative['Exam2'], s=30, c='r', marker='x', label='Not Admitted')
ax.legend()
ax.set_xlabel('Exam 1')
ax.set_ylabel('Exam 2')
结果如图:
初步求解
我们求解的线性关系一定是有参数的,因为我们有两个特征值,而我们需要通过两个特征值求得预测值,但是两个特征值对结果的影响又不尽相同,所以我们需要两个参数来度量两个特征值对结果影响程度,以及一个参数来充当偏置项(曲线会上下浮动,且偏置项对结果作用较小):
θ 1 、 θ 2 、 θ 0 \theta_1、\theta_2、\theta_0 θ1、θ2、θ0
所以我们的预测结果可以表示为:
h θ ( x ) = ( θ 0 θ 1 θ 2 ) × ( 1 x 1 x 2 ) = θ 0 + θ 1 x 1 + θ 2 x 2 h_{\theta}(x)=\begin{array}{ccc} \begin{pmatrix}\theta_{0} & \theta_{1} & \theta_{2}\end{pmatrix} & \times & \begin{pmatrix}1\\ x_{1}\\ x_{2} \end{pmatrix}\end{array}=\theta_{0}+\theta_{1}x_{1}+\theta_{2}x_{2} hθ(x)=(θ0θ1θ2)×⎝⎛1x1x2⎠⎞=θ0+θ1x1+θ2x2
整合之后:
h θ ( x ) = ∑ i = 0 n ( θ i x i ) = θ T x h_{\theta}(x)=\sum_{i=0}^n (\theta_i x_i)=\theta^Tx hθ(x)=i=0∑n(θixi)=θTx
这便是我们构造的预测结果值,但是仅仅有结果值还是不够的,我们需要将预测结果值转化为录取的概率,我们规定:当概率大于0.5则Y可以取1表示录用,小于0.5不录用Y取0,所以我们又需要一个函数来转化预测值为概率,称为sigmoid函数,定义如下:
g ( z ) = 1 1 + e − z g(z) = \frac{1}{1+e^{-z}} g(z)=1+e−z1
我们可以先将sigmoid函数画出来瞧瞧,为什么要用它,代码如下:
def sigmoid(x):
z = 1/(1 + np.exp(-x))
return z
nums = np.linspace(-10, 10, 50)
fig, ax = plt.subplots(figsize=(12,4))
ax.plot(nums, sigmoid(nums), 'b')
结果如图:
可以看到该函数符合我们的需求,其取值位于0到1,定义域为实数集R,可以将我们的预测结果转化为概率!
而所谓的逻辑回归便是将任意的输入值映射到[0,1]区间上,将我们通过线性回归得到的预测值转化为概率,完成分类任务。
初步求解
我们载入数据,大体明白了预测结果长什么样,以及值概率的转化,但我们的模型还完全没有建立起来!
我们知道所谓机器学习便是我们交给机器一堆数据,然后告诉它什么样的学习方式是对的(目标函数),叫它朝着这个方向走,然后还要规定每次走的步长(学习率),一口吃不了大胖子,要一步一步来(每次的迭代)。
如同这样一个山谷,我们要达到山谷的最低点,利用梯度下降的方法,每经过一个数据点,便运行更新函数更新下一步的方向(梯度,求偏导)。
所以我们还要做损失函数(目标函数)、更新函数。
何为损失函数?
我们通过X来估计Y的值,预测值可能符合真实值,也可能不符合真实值,所以我们引入损失函数来度量拟合的程度,损失函数越小代表拟合的越好。
在此处我们暂时将损失函数视为目标函数。
关于梯度下降
梯度下降便是在凸函数中沿着梯度下降的方向不断更新参数,一般情况下我们通过加负号实现。
梯度下降有三种方式实现:
-
1 批量梯度下降法(Batch Gradient Descent)
批量梯度下降法,是梯度下降法最常用的形式,具体做法也就是在更新参数时使用所有的样本来进行更新:
由于我们有m个样本,这里求梯度的时候就用了所有m个样本的梯度数据。 -
2 随机梯度下降法(Stochastic Gradient Descent)
随机梯度下降法,其实和批量梯度下降法原理类似,区别在与求梯度时没有用所有的m个样本的数据,而是仅仅选取一个样本j来求梯度。对应的更新公式是:
随机梯度下降法,和批量梯度下降法是两个极端,一个采用所有数据来梯度下降,一个用一个样本来梯度下降。自然各自的优缺点都非常突出。对于训练速度来说,随机梯度下降法由于每次仅仅采用一个样本来迭代,训练速度很快,而批量梯度下降法在样本量很大的时候,训练速度不能让人满意。对于准确度来说,随机梯度下降法用于仅仅用一个样本决定梯度方向,导致解很有可能不是最优。对于收敛速度来说,由于随机梯度下降法一次迭代一个样本,导致迭代方向变化很大,不能很快的收敛到局部最优解。
那么,有没有一个中庸的办法能够结合两种方法的优点呢?有!这就是小批量梯度下降法。
-
3 小批量梯度下降法(Mini-batch Gradient Descent)
小批量梯度下降法是批量梯度下降法和随机梯度下降法的折衷,也就是对于m个样本,我们采用x个样子来迭代。一般可以取x=10,当然根据样本的数据,可以调整这个x的值。对应的更新公式是:
##逻辑回归
预测函数(完成值到概率的转化):
h θ ( x ) = g ( θ T x ) = 1 1 + e − θ T x . ( 其 中 θ T x 是 我 们 前 面 表 示 的 预 测 值 ) h_{\theta}(x)=g(\theta^Tx)=\frac{1}{1+e^{-\theta^Tx}}.(其中\theta^Tx是我们前面表示的预测值) hθ(x)=g(θTx)=1+e−θTx1.(其中θTx是我们前面表示的预测值)
将分类任务整合进去:
P ( y ∣ x ; θ ) = ( h θ ( x ) ) y ( 1 − h θ ( x ) ) 1 − y P(y|x;\theta)=(h_\theta(x))^y(1-h_{\theta}(x))^{1-y} P(y∣x;θ)=(hθ(x))y(1−hθ(x))1−y
即当我们的y取值为0或1时,可以得到较为精简的式子。
有了概率,我们便可以求似然函数了。
何为似然函数?
官方解释如下:
常说的概率是指给定参数后,预测即将发生的事件的可能性。而似然概率正好与这个过程相反,我们关注的量不再是事件的发生概率,而是已知发生了某些事件,我们希望知道参数应该是多少。
我们的似然函数定义如下:
L ( θ ) = ∏ i = 1 m P ( y i ∣ x i ; θ ) = ∏ i = 1 m ( h θ ( x i ) ) y i ( 1 − h θ ( x i ) ) 1 − y i L(\theta)=\prod_{i=1}^{m}P(y_i|x_i;\theta)=\prod_{i=1}^{m}(h_\theta(x_i))^{y_{i}}(1-h_{\theta}(x_i))^{1-y_i} L(θ)=i=1∏mP(yi∣xi;θ)=i=1∏m(hθ(xi))yi(1−hθ(xi))1−yi
即表示我们预测的参数满足所有样本值这一事件的概率,接下来要做的就是极大似然估计,即令参数的取值无限拟合我们的真实数据。
我们取对数似然,此时应用梯度上升求最大值,再引入目标函数转换为梯度下降求最小值(加负号,除以样本总数,考虑整体样本),求偏导,令其等于零。目标函数如下:
J ( θ ) = − 1 m L ( θ ) J(\theta)=-\frac{1}{m}L(\theta) J(θ)=−m1L(θ)
求偏导过程不再给出,结果如下:
δ δ θ j J ( θ ) = 1 m ∑ i = 1 m ( h θ ( x i ) − y i ) x i j \frac{\delta}{\delta_{\theta_j}}J(\theta)=\frac{1}{m}\sum_{i=1}^m(h_\theta(x_i)-y_i)x_i^j δθjδJ(θ)=m1i=1∑m(hθ(xi)−yi)xij
##代码编写
具体代码将再下一篇中详述