基本形式

逻辑回归是最常用的分类模型,在线性回归基础之上扩展而来,是一种广义线性回归。下面举例说明什么是逻辑回归:假设我们有样本如下(是我编程生成的数据):

我们要做的是找到一个决策边界,把两类样本给分开,当有新数据进来时,就判断它在决策边界的哪一边。设边界线为线性函数

\[h_\theta(\vec x) = \theta_0 + \theta_1x_1 + \theta_2x_2 \tag {1}$$取0时的直线,如下图:

![image.png-12.4kB][2]

**我们的目标就是根据已知的样本来确定$\vec\theta$取值**。

上图中,处在边界线左边的为负例(带入(1)式结果小于0),边界线右边的为正例(带入(1)式结果大于0)。**从概率的角度考虑**:把一个点代入(1)式,如果为正,且越大越离边界线远,它是正例的概率就越大,直到接近1;相反,把一个点代入(1)式,如果为负,且越小离边界线越远,它是正例的概率就越小,直到接近0;此外,把一个点代入(1)式,如果结果正好等于0,那么它在边界线上,为正例和为负例的概率都是0.5。

为了用数学的方式精确表达上面的概率论述,前人找到一个好用的函数:
$$s(z) = \frac{1}{1+e^{-z}} \tag{2}\]

这个函数叫做sigmoid(sigmoid:S形状的)函数(下文用S(z)或s(z)表示sigmoid函数),样子如下:

当\(z=0\)时,函数值为0.5,当\(z > 0\)时,函数取值\((0.5, 1)\);当\(z < 0\)时,函数取值\((0, 0.5)\),如果我们把欲判断点代入决策边界(1)式后得到的结果作为sigmoid函数的输入,那么输出就可以表示该点是正例的概率,简直完美。其实S型的函数应该还有别的,为什么前人独爱这个呢?那是因为,这个函求导比较简单,用链式法则可以非常容易算出其导数式为:

\[\frac{d}{z}s(z)=s(z)(1 - s(z)) \tag{3}
\]

simgiod函数求导过程:

\[\begin{align}\frac{d}{z}s(z)&=-\frac{1}{(1+e^{-z})^2}\cdot e^{-z} \cdot -1 \\
&=\frac{e^{-z}}{(1+e^{-z})^2}\\
&=\frac{1+e^{-z}-1}{(1+e^{-z})^2}\\
&=\frac{1+e^{-z}}{(1+e^{-z})^2} - \frac{1}{(1+e^{-z})^2}\\
&=s(z) - s(z)^2\\
&=s(z)(1-s(z))
\end{align}\]

第一步用了链式法则。

代价函数

逻辑回归的代价函数可由极大似然估计法得出。我们暂且不管极大似然估计法的证明,直观地理解非常容易:如果你是一个班级的新老师,发现有个孩子考了95.5分,你肯定会认为这个孩子很可能是学霸,虽然学霸有时也会考低分,学渣有时也考高分,但是发生概率很小。更一般的叙述是:有若干事件A、B和C,已知其发生概率为分别为\(P(A|\theta)\)、\(P(B|\theta)\)和\(P(C|\theta)\),如果我们观察到A、B和C已经发生了,那么我们就认为\(P(ABC|\theta)\)是个尽可能大的值,如果我们要求\(\theta\),那么\(\theta\)应该是使得\(P(ABC|\theta)\)最大的那个值。如果A、B和C互相独立,我们所求的就是使得\(P(A|\theta)P(B|\theta)P(C|\theta)\)最大化的\(\theta\)。

设\(t_\theta(\vec x)=s(h_\theta(\vec x))\)已知:$$P(Y=y) = \begin{cases}

t_\theta(\vec x), &\text{如果 y = 1} \

1 - t_\theta(\vec x), &\text{如果 y = 0}

\end{cases}$$,写到一起:

\[P(Y=y)=t_\theta(\vec x)^y(1-t_\theta(\vec x))^{1-y} \tag{4}$$,根据(4)式写出极大似然函数:
$$l(\vec\theta)=\prod_{i=1}^mt_\theta(\vec x^{(i)})^{y^{(i)}}(1-t_\theta(\vec x^{(i)}))^{1-y^{(i)}} $$,设代价函数$$j(\vec\theta)=-\frac{1}{m}\ln l(\vec\theta)$$ ,最大化$l(\vec\theta)$即最小化$j(\vec\theta)$。求对数是为了方便计算,把乘法转换为加法,把幂运算转换为乘法,单调性不变;前面的负号是为了把求最大值问题转换为求最小值问题,习惯上,应用梯度下降法时都是求最小值,不然叫“梯度上升了法”了,手动滑稽。当然,使用梯度下降法的前提条件是函数是凸的,大叔懒得证明了,这个函数就是个凸函数,不管你们信不信,我反正是信了。进一步对上式化解得:
$$j(\vec\theta)=-\frac{1}{m}\sum_{i=1}^m\left[y^{(i)}\ln t_\theta(\vec x^{(i)}) + (1-y^{(i)})\ln(1-t_\theta(\vec x^{(i)}))\right] \tag{5}\]

用梯度下降法求\(\vec\theta\)

将(5)式对\(\vec\theta\)求偏导:

  • \(\frac{\partial}{\partial\theta_0}j(\vec\theta)=\frac{1}{m}\sum_{i=1}^m(t_\theta(\vec x^{(i)})-y^{(i)})x_0^{(i)}\)
  • \(\frac{\partial}{\partial\theta_1}j(\vec\theta)=\frac{1}{m}\sum_{i=1}^m(t_\theta(\vec x^{(i)})-y^{(i)})x_1^{(i)}\)
  • \(\cdots\)
  • \(\frac{\partial}{\partial\theta_n}j(\vec\theta)=\frac{1}{m}\sum_{i=1}^m(t_\theta(\vec x^{(i)})-y^{(i)})x_n^{(i)}\)

如果读者对矩阵计算非常熟悉的话,应该可以看出,上式可以写成矩阵形式,这样计算更方便:

\[\frac{d}{d\vec\theta}=\frac{1}{m}X^T(T-Y),其中,T=\begin{pmatrix}t_\theta(\vec x^{(1)})\\
t_\theta(\vec x^{(2)})\\
\vdots \\
t_\theta(\vec x^{(m)})
\end{pmatrix}\]

对\(j(\vec\theta)\)求导的过程如下,多次应用链式法则即可:

\[\begin{align}
\frac{\partial}{\partial\theta_n}j(\vec\theta)&=-\frac{1}{m}\sum_{i=1}^m\left[y^{(i)}\frac{1}{t_\theta(\vec x^{(i)})}t_\theta(\vec x)^\prime+(1-y^{(i)})\frac{1}{1-t_\theta(\vec x^{(i)})}t_\theta(\vec x)^\prime\right]\\
&=-\frac{1}{m}\sum_{i=1}^m\left[y^{(i)}\frac{1}{t_\theta(\vec x^{(i)})}t_\theta(\vec x^{(i)})(1-t_\theta(\vec x^{(i)}))h_\theta(\vec x)^\prime+(1-y^{(i)})\frac{1}{1-t_\theta(\vec x^{(i)})}t_\theta(\vec x^{(i)})(t_\theta(\vec x^{(i)}) - 1)h_\theta(\vec x)^\prime\right]\\
&=-\frac{1}{m}\sum_{i=1}^m\left[y^{(i)}(1-t_\theta(\vec x^{(i)}))h_\theta(\vec x)^\prime + (y^{(i)})t_\theta(\vec x^{(i)} - 1)h_\theta(\vec x)^\prime\right]\\
&=-\frac{1}{m}\sum_{i=1}^m\left[y^{(i)}(1-t_\theta(\vec x^{(i)}))x_n^{(i)} + (y^{(i)})t_\theta(\vec x^{(i)} - 1)x_n^{(i)}\right]\\
&=-\frac{1}{m}\sum_{i=1}^m(y^{(i)} - t_\theta(\vec x^{(i)}))x_n^{(i)}\\
&=\frac{1}{m}\sum_{i=1}^m(t_\theta(\vec x^{(i)}) - y^{(i)})x_n^{(i)}
\end{align}\]

  • 步骤1用到到了公式:\(\ln x^\prime = \frac{1}{x}\)
  • 步骤2用到式(3)

有了偏导式后就可以编程了:

import matplotlib.pyplot as plt
import numpy as np def sigmoid(z):
return 1 / (1 + np.e**(-z)) def draw_samples(X, Y, sample_count):
''' 绘制正负例. '''
positiveX1 = []
positiveX2 = [] negativeX1 = []
negativeX2 = [] for i in range(sample_count):
if Y[i, 0] == 1:
positiveX1.append(X[i, 0])
positiveX2.append(X[i, 1])
else:
negativeX1.append(X[i, 0])
negativeX2.append(X[i, 1]) plt.scatter(positiveX1, positiveX2, marker='+')
plt.scatter(negativeX1, negativeX2, marker='.') def draw_border(theta):
'''绘制边界线'''
X = []
Y = []
for x in range(-2, 12):
X.append(x)
Y.append(-theta[0] / theta[2] - theta[1] / theta[2] * x )
plt.plot(X, Y) def create_samples(samples_count):
''' 生成样本数据'''
X = np.empty((samples_count, 2))
Y = np.empty((samples_count, 1)) for i in range(samples_count):
x1 = np.random.randint(0, 10)
x2 = np.random.randint(0, 10)
y = 0
if x1 + x2 - 10 > 0:
y =1
X[i, 0] = x1
X[i, 1] = x2
Y[i, 0] = y noise = np.random.normal(0, 0.1, (samples_count, 2))
X += noise
return X, Y def grad(X, Y, samples_count, theta):
''' 求代价函数在theta处的梯度. '''
T = sigmoid(np.dot(X, theta))
g = np.dot(X.T, (T - Y)) / samples_count
return g def descend(X, Y, samples_count, theta = np.array([[1],[1],[1]]), step = 0.01, threshold = 0.05):
''' 梯度下降.
Args:
X: 样本
Y:样本标记
theta:初始值
step:步长
threshold:阈值
Returns:
theta:求出来的最优theta
'''
g = grad(X, Y, samples_count, theta)
norm = np.linalg.norm(g) while norm > threshold:
g = grad(X, Y, samples_count, theta)
norm = np.linalg.norm(g)
theta = theta - g * step
print(norm) return theta samples_count = 100
X, Y = create_samples(samples_count)
MatrixOnes = np.ones((100, 1))
X_with_noes = np.hstack((MatrixOnes, X)) # 添加等于1的x0
theta = descend(X_with_noes, Y, samples_count) plt.xlabel('$x_1$')
plt.ylabel('$x_2$') draw_samples(X, Y, samples_count)
draw_border(theta.flatten())
plt.show()

运行结果:

上面的代码中,我在样本矩阵中增加了一列为1的元素,这样做是为了计算方便,使得样本矩阵的列数等于欲求\(\vec\theta\)的行数,满足矩阵乘法的要求,加上这一列对结果没有影响,请参考大叔学ML第二:线性回归

扩展

  • 和线性回归一样,逻辑回归也存在过拟合的情况,可以通过添加一范数、二范数正则化来解决
  • 和线性回归一样,逻辑回归的决策边界可以是曲折的多项式,可参考大叔学ML第三:多项式回归,依葫芦画瓢来搞定决曲折的决策边界
  • 和线性回归一样,逻辑回归也可以扩展到多余3维的情况,只是不可以做可视化了,代数原理是一致的
  • 和线性回归不一样,逻辑回归应该没有“正规方程”,大叔昨天按照线性回归的正规方程的推导思路推导并未得出结果,查资料也未果
  • 除了梯度下降法,牛顿法和拟牛顿法也是迭代下降算法,而且下降效率更好,大叔稍后将会写一篇博文介绍之
  • 多分类,多分类非常简单,假如样本数据共有3类,记作A、B和C,先把A分为正例,B和C分为负例,会得到一条决策边界,然后再把B分为正例,C分为正例做同样的操作,最后得到3条决策边界,也就是得到3个不同的概率计算函数,如果有新数据进来,分别带入这三个函数,取概率值最大的那个函数所代表的分类即可。

祝元旦快乐!

大叔学ML第五:逻辑回归的更多相关文章

  1. 大叔学ML第四:线性回归正则化

    目录 基本形式 梯度下降法中应用正则化项 正规方程中应用正则化项 小试牛刀 调用类库 扩展 正则:正则是一个汉语词汇,拼音为zhèng zé,基本意思是正其礼仪法则:正规:常规:正宗等.出自<楚 ...

  2. 大叔学ML第二:线性回归

    目录 基本形式 求解参数\(\vec\theta\) 梯度下降法 正规方程导法 调用函数库 基本形式 线性回归非常直观简洁,是一种常用的回归模型,大叔总结如下: 设有样本\(X\)形如: \[\beg ...

  3. 大叔学ML第三:多项式回归

    目录 基本形式 小试牛刀 再试牛刀 调用类库 基本形式 上文中,大叔说道了线性回归,线性回归是个非常直观又简单的模型,但是很多时候,数据的分布并不是线性的,如: 如果我们想用高次多项式拟合上面的数据应 ...

  4. tensorflow学习笔记五----------逻辑回归

    在逻辑回归中使用mnist数据集.导入相应的包以及数据集. import numpy as np import tensorflow as tf import matplotlib.pyplot as ...

  5. 大叔学ML第一:梯度下降

    目录 原理 实践一:求\(y = x^2 - 4x + 1\)的最小值 实践二:求\(z = x^2 + y^2 + 5\)的最小值 问答时间 原理 梯度下降是一个很常见的通过迭代求解函数极值的方法, ...

  6. Theano3.3-练习之逻辑回归

    是官网上theano的逻辑回归的练习(http://deeplearning.net/tutorial/logreg.html#logreg)的讲解. Classifying MNIST digits ...

  7. Spark ML逻辑回归

    import org.apache.log4j.{Level, Logger} import org.apache.spark.ml.classification.LogisticRegression ...

  8. ML(4)——逻辑回归

    Logistic Regression虽然名字里带“回归”,但是它实际上是一种分类方法,“逻辑”是Logistic的音译,和真正的逻辑没有任何关系. 模型 线性模型 由于逻辑回归是一种分类方法,所以我 ...

  9. 机器学习方法(五):逻辑回归Logistic Regression,Softmax Regression

    欢迎转载,转载请注明:本文出自Bin的专栏blog.csdn.net/xbinworld. 技术交流QQ群:433250724,欢迎对算法.技术.应用感兴趣的同学加入. 前面介绍过线性回归的基本知识, ...

随机推荐

  1. 从xampp到phpmyadmin

    目录 xampp的安装和配置 尝试运行Example11_1(图形化管理工具的可视化界面) 参考博文 xampp的安装和配置 主要参考博文Intellj IDEA 简易教程 1.下载并安装xampp ...

  2. JavaSE基础知识(5)—面向对象(对象数组和对象关联)

    一.对象数组 1.说明 数组的定义类型为对象类型 2.动态初始化 1.声明并开辟空间 Person[] pers = new Person[长度];2.赋值 for(int i=0;i<pers ...

  3. AsyncContext的startAsync()方法开启异步

    Servlet 3.0的异步处理支持特性,使Servlet 线程不再需要一直阻塞,直到业务处理完毕才能再输出响应,最后才结束该 Servlet 线程.在接收到请求之后,Servlet 线程可以将耗时的 ...

  4. HYSBZ 2743 (树状数组) 采花

    题目:这里 题意: 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记, ...

  5. Python之路(第三十八篇) 并发编程:进程同步锁/互斥锁、信号量、事件、队列、生产者消费者模型

    一.进程锁(同步锁/互斥锁) 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理. 例 ...

  6. 如何实现Activiti的分支条件的自定义配置(转)

    如何实现Activiti的分支条件的自定义配置 博客分类: Activiti Java SaaS   一.Activiti的流程分支条件的局限 Activiti的流程分支条件目前是采用脚本判断方式,并 ...

  7. Python3 使用pymysql链接MySQL数据库

    1,pymysql安装 pip install pymysql 2,pymysql使用 import pymysql 3,实例查询 import pymysql #获取一个数据库链接 #格式 主机地址 ...

  8. vue使用中遇到的,以及vue1.0到vue2.0新手踩的坑

    最近再写一个vue的项目,视频中用的是vue1.0,但是现在vue已经2.0,所以踩了很多坑,先记录下来.理解有误再来修改. 路由问题 之前的路由是写在app.vue里边,而2.0的路由直接有个rou ...

  9. C# 使用System.Speech 进行语音播报和识别

    C# 使用System.Speech 进行语音播报和识别 using System.Speech.Synthesis; using System.Speech.Recognition; //语音识别 ...

  10. C++矩阵加速经典题目:Warcraft III 守望者的烦恼 [vijos 1067]

    Warcraft III 守望者的烦恼 背景 守望者-warden,长期在暗夜精灵的的首都艾萨琳内担任视察监狱的任务,监狱是成长条行的,守望者warden拥有一个技能名叫"闪烁", ...