t=1逻辑或0逻辑运算0+1等于多少少?

前言逻辑回归(Logistic Regression,LR) 可以说是机器学习领域最基础也是最常用的模型, 逻辑回归的原理推导以及扩展应用几乎是算法工程师的必备技能。 医生病理诊断、 银行个人信用评估、 邮箱分类垃圾邮件等, 无不体现逻辑回归精巧而广泛的应用。
《百面机器学习》逻辑回归常用于疾病自动诊断、经济预测、点击率预测等领域。由于其处理速度快且容易并行,逻辑回归适合用来学习需要大规模训练的样本和特征,对于广告十亿量级的特征和亿量级的特征来说,逻辑回归有着天然的优势,因而逻辑回归在工业界获得了广泛的应用。而逻辑回归的缺点是,需要大量的特征组合和离散的工作来增加特征的表达性,模型表达能力弱,比较容易欠拟合。《美团机器学习实践》逻辑回归模型是机器学习领域中最为经典的模型之一,绝大部分分类问题都可以使用逻辑回归来尝试解决。同时,逻辑回归也是面试中经常出现的内容。逻辑回归非常实用,有比较成熟的分布式的解决方案,是线上系统中最为常用的模型,对于大部分问题来讲,它可以是我们的首选。由于简单,它也适用于数据量特别大的情况。1 引入当然除了上图的几个应用场景,任何一个分类问题其实都可以使用逻辑回归来解决。至少,逻辑回归是一个非常靠谱的基准(Baseline)。那接下来看一个具体的样本数据。在这个表格里,有三个特征和一个预测变量叫作逾期,而且逾期类别可以取两个不同的值。所以,这是经典的二分类问题。 我们可以把特征值看作是向量 x , 预测值看作是一个具体的类别值 y 。那究竟如何表示 x 和 y 之间的关系呢? 这里就牵涉到了模型的选择。每一种类型的模型其实可以看作是对这个映射关系种类的假设。比如我们使用逻辑回归,就意味着这样的映射关系是线性的;但如果使用的是神经网络,就意味着这种映射关系是很复杂的非线性关系。1.1 分段函数其实我们可以很自然想到一种“分段函数”,即把一个 y=w^Tx+b 的线性模型的取值做一个分段,当 y 值大于某个门槛时, x 属于一类, y 值小于这个门槛时,则 x 属于另一类。用比较严谨一点方式来描述则是:这里我们先不管什么是对数几率函数,重点看红色的部分,即单位阶跃函数。很明显的可以看出,从几何角度来说,单位阶跃函数不是一个连续的函数。而且它也不具备单调性和可微性,通俗来说就是,没法求导。至于为什么对求导有要求,后面会做说明。总之就是,单位阶跃函数不满足我们理想中的 X \to Y 的映射关系。1.2 逻辑函数那对于逻辑回归,我们又如何去定义此映射关系呢?逻辑回归本身解决的是二分类问题,所以在这里我们就假定 y 的取值为0或者1。当然逻辑回归也可以用于多分类问题中,这个会在之后做详细的介绍。上述所谓的 x 到 y 之间的映射关系也可以通过条件概率 p(y|x)
来表示。由于 y 的取值为0或者1, 只要我们能计算出 p(y=1|x) 和 p(y=0|x) , 即可以对任何一个给定的样本 x 做预测了。比如对于给定的一个样本 x , 假如 p(y=1|x) > p(y=0|x) 分类成类别1了,反之就是类别0。所以,这里的核心问题是如何通过条件概率 p(y|x) 来描述 x 和 y 之间的关系 ,而且上面已经提到,逻辑回归其实是线性模型。问题:那我们是否可以把条件概率表示成这种形式呢?
p(y|x) =w^{T}x + b 。答案是不可以的。这里最明显的问题是定义域和值域的不匹配。我们知道 p(y|x) 是一个条件概率,那它一定是一个合理的概率值,也就是它的取值范围一定会落在0和1之间,因为一个概率值不可能小于0或者大于1的。但是,假如我们使用 w^{T}x+b 的方式来描述,显然会导致它的值域变成负无穷到正无穷区间,这不符合概率的定义。那么我们可不可以把 w^Tx+b 做一些处理,将其映射到(0,1)区间呢?也就是如何把正无穷到负无穷的值域映射到(0, 1)之间呢? 因为这样一来,就可以得到合理的概率值了! 答案就是使用逻辑函数。下图就是逻辑函数的形式:可以看到逻辑函数的定义域是负无穷到正无穷,而值域恰好在0和1之间,那么我们只要把 w^Tx+b 的结果当做逻辑函数的的x,就能得到一个落在0和1之间的y值了。所以我们就可以写出新的条件概率。有了新的条件概率后,对于 y=1 或者是 y=0 的情况,我们都可以写出相应的条件概率,而且,由于两者是互补的,即相加等于1的,我们甚至可以把两个条件概率合并成一个式子。如下:合并之后的式子不难理解。当 y=1 时,后面那一项不起任何作用也就变成了第一个条件概率; 当 y=0 时,前面那一项不起作用,也就等同于第二个条件概率。2 基本概念有了上面的引入部分,那么下面我们可以用正式一点的语言来描述逻辑回归模型。2.1 条件概率逻辑回归是一种分类模型,由条件概率 p(y|x) 表示。在二分类的情况下,变量 y 的取值的0或1。其条件概率分布如下: \begin{aligned} p(y=0|x) &=\frac{e^{-(w\cdot x+b)}}{1+e^{-(w\cdot x+b)}} \\ p(y=1|x) &=1-\frac{e^{-(w\cdot x+b)}}{1+e^{-(w\cdot x+b)}}
= \frac{1}{1+e^{-(w\cdot x+b)}} \end{aligned} 这里, x \in R^n 是输入, y\in \{0,1\} 是输出, w \in R^n 和 b \in R
均是参数。结合上面逻辑函数图像来观察,不难发现逻辑回归的条件概率存在以下特点,即线性函数 w \cdot x + b 的值越接近正无穷,其概率值就越接近1;线性函数的值越接近负无穷,其概率值就越接近0。为了方便后续求解参数,两个条件概率可以合并写作: p(y|x) = p(y=1|x;w)^y[1-p(y=1|x;w)]^{1-y} 2.2 最大似然估计有了 x,y 的条件概率形式以后,我们就可以开始想办法构建目标函数了。对于逻辑回归而言,通常是使用最大似然估计的方法来构造目标函数,得到模型的最大似然之后,我们即可以通过优化的方式来得到最优的参数。最大似然估计(Maximum Likelihood Estimation,MLE)在机器学习建模中有着举足轻重的作用。它可以指引我们去构造模型的目标函数,以及求出使目标函数最大或者最小的参数值。那具体怎么理解最大似然估计呢? 一个比较抽象的解释是:假如有个未知的模型(看作是黑盒子),并且它产生了很多能观测到的样本。这时候,我们便可以通过最大化这些样本的概率反过来求出模型的最优参数,这个过程称之为最大似然估计。也就是说,假设样本服从某个模型的分布,那么可以根据已知的样本,通过最大化出现这些样本的概率,去反推模型的参数。这里可以通过一个例子来说明如何使用最大似然估计。问题:假设有一枚不均匀的硬币,出现正面或者反面的概率是不同的,我们设定出现正面的概率为 \theta ,正面用H来表示,反面用T来表示。当我们投掷了六次硬币之后,得到了如下结果 D=\{H,T,T,H,H,H\} ,假定每次投掷事件都是互相独立的,那么 \theta 是多少呢?如何通过最大似然估计求解参数呢?假设未知参数为 \theta ,已知样本为 D ,最大似然估计通过最大化 p(D|\theta) 来求解未知参数。这里 p(D|\theta) 意味着在给定 \theta 的情况下,样本 D 出现的概率。最大化这个概率,就能求出未知的 \theta 。由于 D=\{H,T,T,H,H,H\} ,且每次投掷事件都是互相独立的,所以有: \begin{aligned} p(D|\theta) &=p(H,T,T,H,H,H|\theta) \\ &=p(H|\theta)·p(T|\theta)·p(T|\theta)·p(H|\theta)·p(H|\theta)·p(H|\theta)\\ &=\theta·(1-\theta)·(1-\theta)·\theta · \theta · \theta\\ &=\theta^4 (1-\theta)^2 \end{aligned} 设 f(\theta) = \theta^4 (1-\theta)^2 ,要求 f(\theta) 的最大值对应的参数,可以通过设 f(\theta) 的导数为0的方式来求解。 \begin{aligned} f(\theta) &= \theta^4 (1-\theta)^2 \\ f'(\theta) &=
4\theta^3 (1-\theta)^2 + \theta^42(1-\theta)(-1) \\ &= \theta^3 (1-\theta) [4(1-\theta)-2\theta] \\ &= \theta^3 (1-\theta) (4 - 6\theta)\\ \end{aligned} 令 f'(\theta)=0 可得 \theta_1=0, \theta_2=1,\theta_3=\frac{2}{3} 。由于 \theta
是一个概率值,而0和1是两个极端,是不满足条件的。所以 \theta=\frac{2}{3} 是最终的答案。这个答案其实和我们观察硬币投掷的结果的频率分布是一致,是一种很直观的结果。那接下来的问题是:对于逻辑回归模型如何构造最大似然?2.3 构造目标函数我们知道如何针对某一对样本写出其条件概率了。那对于最大似然,无非就是把所有的样本全部考虑进来。这里需要注意的一点是,这个总概率是由每一个条件概率的乘积来表示的。 随后,通过最大化这个条件概率来得出最优的参数 w 和 b 。我们只要模仿上面扔硬币的形式,把每个样本的概率做一个连乘即可:对于单个样本的条件概率已经定义过了,这个概率也可以看作是似然概率。下一步得把所有的样本全部考虑进来,这时候我们得到的就是所有样本的似然概率。这里的 \prod 符合代表连乘的意思, argmax 代表取最大值时的参数。比如我们写了这样的一个式子
\hat{x} _ {mle}=argmax f(x) , 它的意思是需要寻找让 f(x) 最大化的 x 值,并把这个值赋给 \hat{x} _ {mle} 。可能有些人可能会疑惑,为什么这里可以使用乘积的方式来表示最大似然呢? 而不是使用之和?其实这并不难理解,这里所使用的是叫做条件独立(conditional independence)的假设,就像之前扔硬币的例子一样,每个样本都是独立的。在这个假设的前提下,可以把条件概率 p(x_1, …, x_n
w)
分解成 p(x_1
w) …p(x_n
w) 。有了目标函数之后,我们的任务就比较明确了,就是需要寻找让目标函数最大化的参数 w 和 b 。 所以,接下来的问题就变成了一个经典的优化问题。对于这个目标函数,首先来做一层简化,就是把乘积的形式改造成加法形式。这对于后续的运算有很大的帮助。 因为很多概率的成绩很容易形成特别小的数,进而产生underflow等现象。我们在程序中可能会看到inf, nan等结果,这时候你需要去查找是否出现了underflow, overflow, 或者除以0的现象。 \begin{aligned} \hat{w}_{mle},\hat{b}_{mle} &= \mathop{\arg\max}_{w,b} \prod_{i=1}^{n} p(y_i|x_i;w,b)\\ &=\mathop{\arg\max}_{w,b} \log \prod_{i=1}^{n} p(y_i|x_i;w,b)\\ &=\mathop{\arg\max}_{w,b}
\sum_{i=1}^{n} \log p(y_i|x_i;w,b)\\ &=\mathop{\arg\min}_{w,b}
-\sum_{i=1}^{n} \log p(y_i|x_i;w,b) \end{aligned} 通过引入 \log ,我们把连乘变成了连加,引入符号,把求最大值改为了求最小值(优化问题通常习惯于求最小值)。到这里,已经不好再继续简化下去了。剩下的工作就是要寻找让目标函数最小化的参数 w,b 。即,通过优化算法去寻找模型的最优参数。一个常见的优化算法是直接令导数为0,就像上面扔硬币的例子一样。但对于逻辑回归而言,用这个令导数为0的的方式会面临诸多问题,如:很难写出参数的表达式、涉及到矩阵求导等等。关于矩阵求导的具体缺陷我在线性回归中讲过:https://zhuanlan.zhihu.com/p/360529347。所以,对于逻辑回归的优化问题我们不得不采用另外一种更通用的方法:迭代式的优化算法。即:梯度下降法。这种算法的核心是通过不断迭代的方式来求解函数的最优解。3 梯度下降法3.1 概念所谓梯度下降,我理解的就是让参数沿着梯度的方向进行更新,直到目标函数收敛。这里的梯度,其实就是参数的导数(当有多个参数时,就是偏导数),沿着梯度的方向进行更新,直白点说就是参数减去参数的导数得到的值,就是新的参数。说起来可能有点难理解,用一个例子说明一下:例:求解函数 f(x) = 4x^2+5x+1 的最小值。对于任何学过高中数学的同学而言,显然可以知道这是一个开口向上的抛物线,直接令 x=-\frac{b}{2a}=-\frac{5}{8}=-0.625 ,代入原式即可求出最小值。那么如果使用所谓的梯度下降法,是怎么做的呢?过程如下图:初始化 x^1 就是随机给 x^1 一个值,这里我们假定 x^1=0 ,t就是迭代的轮次,每一次迭代,都会产生新的 x ,新的 x 是由老的 x 所产生。那么这里的核心其实就是 \nabla f(x) ,这个式子就是指 f(x) 的梯度,在只有一个参数 x 的情况的下,可以直接理解为 f'(x) 。 \eta 指的是学习率,用来控制梯度的大小,可以看做是一个常数,这里我们先设定 \eta=0.1 。所以有: \begin{aligned} f(x) & = 4x^2+5x+1 \\ \nabla f(x) &= f'(x) = 8x+5 \end{aligned} 把已知的 x^1,\eta,\nabla f(x) 代入 x^{t+1}=x^t-\eta \nabla f(x^t) 则有: \begin{aligned} x^1 &= 0\\ x^2 &= x^1 - 0.1 *(8*x^1+5) =0-0.1*5 =-0.5\\ x^3 &= x^2 - 0.1 *(8*x^2+5) = -0.5-0.1*1=-0.6\\ x^4 &= x^3 - 0.1 *(8*x^3+5) = -0.6-0.1*0.2=-0.62\\ x^5 &= x^4 - 0.1 *(8*x^4+5) = -0.62-0.1*0.04=-0.624\\ x^6 &= x^5 - 0.1 *(8*x^5+5) = -0.624-0.1*0.008=-0.6248\\ ... \end{aligned} 通过上面的迭代可以观察到,随着轮次进行, x 的值会收敛于 -0.625 (如果目标函数是凸函数,那么用梯度下降进行优化求解是必定收敛的,感兴趣的同学可以找一找收敛性的证明)。对于梯度下降法来说,有一个重要的参数 \eta 叫作学习率(learning rate), 我们可以把它看作是可调节的参数(也称之为超参数)。学习率对于收敛以及对最终的结果起到很重要的作用,因为它控制着每一次迭代的程度。学习率越大,学习速度会越快; 学习率越小,学习速度越慢。但有一个潜在问题是,一旦学习率大于一定的值之后算法就无法收敛,这是我们不想看到的结果。通过上述的例子,我们已经明白了什么是梯度下降法,接着我们试着通过梯度下降法来求解逻辑回归的最优解。3.2 逻辑回归的梯度下降由于逻辑回归的目标函数 L(\theta) = -\sum_{i=1}^{n} \log p(y_i|x_i;\theta) 其中 p(y|x) = p(y=1|x;w)^y[1-p(y=1|x;w)]^{1-y} 且 \begin{aligned} p(y=0|x) &=\frac{e^{-(w\cdot x+b)}}{1+e^{-(w\cdot x+b)}} \\ p(y=1|x) &=1-\frac{e^{-(w\cdot x+b)}}{1+e^{-(w\cdot x+b)}}
= \frac{1}{1+e^{-(w\cdot x+b)}} \end{aligned} 为了方便起见,我们把 w \cdot x + b
写作 \theta^Tx ,令 h_{\theta} = g(\theta^Tx)=\frac{1}{1+e^{-\theta^Tx}} 其中: g(z) =\frac{1}{1+e^{-z}} 这个 g(z) 就是我们之前提到的逻辑函数,也叫 sigmoid 函数。所以我们可以重写条件概率: \begin{array}{l} P(y=1 \mid x ; \theta)=h_{\theta}(x) \\ P(y=0 \mid x ; \theta)=1-h_{\theta}(x) \end{array} 组合在一起则是: p(y \mid x ; \theta)=\left(h_{\theta}(x)\right)^{y}\left(1-h_{\theta}(x)\right)^{1-y} 再把新的条件概率放入目标函数中则有: \begin{aligned} L(\theta) &=p(\vec{y} \mid X ; \theta) \\ &=\prod_{i=1}^{m} p\left(y^{(i)} \mid x^{(i)} ; \theta\right) \\ &=\prod_{i=1}^{m}\left(h_{\theta}\left(x^{(i)}\right)\right)^{y^{(i)}}\left(1-h_{\theta}\left(x^{(i)}\right)\right)^{1-y^{(i)}} \end{aligned} 然后取对数形式,使得连乘变成连加。注意这里没有负号。 \begin{aligned} \ell(\theta) &=\log L(\theta) \\ &=\sum_{i=1}^{m} y^{(i)} \log h\left(x^{(i)}\right)+\left(1-y^{(i)}\right) \log \left(1-h\left(x^{(i)}\right)\right) \end{aligned} 这个 \ell(\theta) 有时候也会被叫做损失函数,或者是代价函数,其实都是一个意思。由于其形式和交叉熵一致,所以也叫做交叉熵损失函数。(机器学习中一样的公式有各种各样的叫法,理解就好,习惯就好)考虑到使用梯度时需要求导,而目标函数 \ell(\theta)
是一个复合函数。包含了对数形式以及一个逻辑函数。所以我们先把逻辑函数单独拎出来看看其求导的结果: \begin{aligned} g(z) &=\frac{1}{1+e^{-z}}\\ g^{\prime}(z) &=\frac{d}{d z} \frac{1}{1+e^{-z}} \\ &=\frac{1}{\left(1+e^{-z}\right)^{2}}\left(e^{-z}\right) \\ &=\frac{1}{\left(1+e^{-z}\right)} \cdot\left(1-\frac{1}{\left(1+e^{-z}\right)}\right) \\ &=g(z)(1-g(z)) \end{aligned} 对于逻辑回归而言,其梯度下降的参数更新公式为: \theta^{new} = \theta^{old}+\eta \nabla_{\theta}\ell (\theta^{old}) 那么根据 \ell(\theta) = \sum_{i=1}^{m} y^{(i)} \log h\left(x^{(i)}\right)+\left(1-y^{(i)}\right) \log \left(1-h\left(x^{(i)}\right)\right) 可以求出 \begin{aligned} \nabla_{\theta}\ell (\theta)&=\frac{\partial}{\partial \theta_{j}} \ell(\theta)
\\ \frac{\partial}{\partial \theta_{j}} \ell(\theta) &=\left(y \frac{1}{g\left(\theta^{T} x\right)}-(1-y) \frac{1}{1-g\left(\theta^{T} x\right)}\right) \frac{\partial}{\partial \theta_{j}} g\left(\theta^{T} x\right) \\ &=\left(y \frac{1}{g\left(\theta^{T} x\right)}-(1-y) \frac{1}{1-g\left(\theta^{T} x\right)}\right) g\left(\theta^{T} x\right)\left(1-g(\theta^{T} x)\right)
\frac{\partial}{\partial \theta_{j}} \theta^{T} x\\ &=\left(y\left(1-g\left(\theta^{T} x\right)\right)-(1-y) g\left(\theta^{T} x\right) \right) x_{j} \\ &=\left(y-h_{\theta}(x)\right) x_{j} \end{aligned} 所以利用梯度得到的最终的参数更新的公式为: \theta_{j}:=\theta_{j}+\eta\left(y^{(i)}-h_{\theta}\left(x^{(i)}\right)\right) x_{j}^{(i)} 值得注意的是,由于这里没有引入负号,实际上我们的方法是梯度上升,求目标函数的最大值,如果引入负号,要求目标函数的最小值,梯度下降的方法可以写成如下形式:一开始需要随机初始化参数 w 和 b 。 之后就可以通过循环来不断更新参数的值了,直到整个优化过程收敛为止。那么问题来了,梯度下降法过程中,如何判断迭代过程是否已经收敛?一般有两种方法:1,如果在相邻两个时间段损失函数没有任何变化或者变化很小,即可以认为优化过程已收敛。2,如果在相邻两个时间段参数的值没有变化或者变化很小,即可以认为优化过程已收敛。当我们使用梯度下降法来求解逻辑回归的最优解时,不管怎么初始化,结果是一样的。主要的原因是逻辑回归的目标函数为凸函数。如果一个函数为凸函数,它只有全局最优解,所以不管怎么初始化,最后收敛到的结果是同一个点。凸函数一般如下面的左图,没有局部最优解。非凸函数则一般如下面的右图,存在很多局部最优解。如果一个目标函数是凸函数就意味着它的最优解就是全局最优解,如果目标函数不是凸函数,那我们只能保证找到局部最优解。在第存在很多局部最优解的情况下,不同的初始化会带来不一样的最终结果,也可以理解为不同的初始化值有好有坏。那好的初始化可以带来更好的局部最优解。另外,梯度下降法里 \eta_t 代表的是每一时刻的学习率,它是可以随时间而动态变化的。一般情况下,我们以人工的方式来定义学习率的变化。通常使用的标准是随着时间的推移,学习率不断变小。import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 随机生成样本数据。 二分类问题,每一个类别生成5000个样本数据
np.random.seed(12)
num_observations = 5000
x1 = np.random.multivariate_normal([0, 0], [[1, .75],[.75, 1]], num_observations)
x2 = np.random.multivariate_normal([1, 4], [[1, .75],[.75, 1]], num_observations)
X = np.vstack((x1, x2)).astype(np.float32)
y = np.hstack((np.zeros(num_observations), np.ones(num_observations)))
print (X.shape, y.shape)
# 数据的可视化
plt.figure(figsize=(12,8))
plt.scatter(X[:, 0], X[:, 1], c = y, alpha = .4)输出为:# 实现sigmoid函数
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# 计算log likelihood
def log_likelihood(X, y, w, b):
"""
针对于所有的样本数据,计算(负的)log likelihood,也叫做cross-entropy loss
这个值越小越好
X: 训练数据(特征向量), 大小为N * D
y: 训练数据(标签),一维的向量,长度为D
w: 模型的参数, 一维的向量,长度为D
b: 模型的偏移量,标量
"""
# 首先按照标签来提取正样本和负样本的下标
pos, neg = np.where(y==1), np.where(y==0)
# 对于正样本计算 loss, 这里我们使用了matrix operation。 如果把每一个样本都循环一遍效率会很低。
pos_sum = np.sum(np.log(sigmoid(np.dot(X[pos], w)+b)))
# 对于负样本计算 loss
neg_sum = np.sum(np.log(1-sigmoid(np.dot(X[neg], w)+b)))
# 返回cross entropy loss
return -(pos_sum + neg_sum)
# 实现逻辑回归模型
def logistic_regression(X, y, num_steps, learning_rate):
"""
基于梯度下降法实现逻辑回归模型
X: 训练数据(特征向量), 大小为N * D
y: 训练数据(标签),一维的向量,长度为D
num_steps: 梯度下降法的迭代次数
learning_rate: 步长
如果对于这块不是很熟悉,建议再看一下梯度下降法的推导
"""
w, b = np.zeros(X.shape[1]), 0
for step in range(num_steps):
# 预测值与真实值之间的误差。
error = sigmoid(np.dot(X,w)+b) - y
# 对于w, b的梯度计算
grad_w = np.matmul(X.T, error)
grad_b = np.sum(error)
# 对于w, b的梯度更新
w = w - learning_rate * grad_w
b = b - learning_rate * grad_b
# 每隔一段时间,计算一下log likelihood,看看有没有变化
# 正常情况下, 它会慢慢变小,最后收敛
if step % 10000 == 0:
print (f'当前迭代步数为:{step:5},当前目标函数的值为:',log_likelihood(X, y, w, b))
return w,b
w, b = logistic_regression(X, y, num_steps = 100000, learning_rate = 5e-5)
print ("(自己写的)逻辑回归的参数w, b分别为: ", w, b)
from sklearn.linear_model import LogisticRegression
clf = LogisticRegression(fit_intercept=True, C = 1e15)
clf.fit(X, y)
print ("(sklearn)逻辑回归的参数w, b分别为: ", clf.coef_, clf.intercept_, )输出为:3.3 随机梯度下降法以及小批量梯度下降法虽然梯度下降法是一个非常经典的优化算法,但实际工程项目中梯度下降法的应用其实并不是特别多。主要原因是当样本很多的时候,每一次迭代所花费的时间成本是很高的。举个例子,当数据集里有一百万个样本的时候,每一次的参数更新就需要循环所有的样本并把它们的梯度做累加。这样的话,时间复杂度就太高了。为了解决这个时间复杂度问题,我们最常用的算法其实是随机梯度下降法,可以理解成是梯度下降法的一个变种。随机梯度下降法的核心思想是:每一次的迭代更新不再依赖于所有样本的梯度之和,而是仅仅依赖于其中一个样本的梯度。所以这种方法的优势很明显,通过很“便宜”的方式获得梯度,并频繁地对参数迭代更新。这种频繁的更新,可以使得参数在更短的时间内达到收敛的效果。接着具体来理解一下随机梯度下降法的细节。可以看到,随机梯度下降法每次更新参数只根据一个样本进行更新,这显然会使得更新的速度变得很快!但也有自身的缺点。这里最大的问题是梯度的噪声,当我们使用梯度下降法时,梯度的计算是依赖于所有样本的,但随机梯度下降法的梯度计算仅仅依赖于其中的一个样本。这必然会导致计算出来的结果包含大量的噪声,因为我们试图使用一个样本的梯度来替换所有样本的梯度之和。这种噪声会对参数的更新产生负面影响,使得每一次的迭代更新可能不太稳定。在随机梯度下降法的模式下,我们通常会把学习率设置为比较小的值,这样可以有效削弱梯度计算中带来的不稳定性。相比梯度下降法,当我们使用随机梯度下降法的时候可以看到每一次迭代之后的目标函数或者损失函数会有一些波动性。有一些更新会带来目标值的提升,但有些更新反而让目标值变得更差。但只要实现细节合理,大的趋势是沿着好的方向而发展的。梯度下降法和随机梯度下降法分别可以看作是两个极端。前者考虑所有的样本,后者仅仅考虑其中的一个样本。有一个折中的方案,叫做小批量梯度下降法(mini-batch gradient descent)。它不依赖于所有的样本,但也不依赖于仅仅一个样本,而是它从所有样本中随机挑选一部分样本来计算梯度并更新参数。从上述图里可以看得到批量梯度下降法做了一个折中,每次都是随机采样一部分样本然后做梯度的更新。这种做法会让每次的梯度计算既稳定效率也高,相当于结合了两者各自的优势。这里可以根据公式写出随机梯度下降法的代码:# X, y的取值与前面梯度下降法的代码一致
# 实现sigmoid函数
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# 计算log likelihood
def log_likelihood(X, y, w, b):
"""
针对于所有的样本数据,计算(负的)log likelihood,也叫做cross-entropy loss
这个值越小越好
X: 训练数据(特征向量), 大小为N * D
y: 训练数据(标签),一维的向量,长度为D
w: 模型的参数, 一维的向量,长度为D
b: 模型的偏移量,标量
"""
# 首先按照标签来提取正样本和负样本的下标
pos, neg = np.where(y==1), np.where(y==0)
# 对于正样本计算 loss, 这里我们使用了matrix operation。 如果把每一个样本都循环一遍效率会很低。
pos_sum = np.sum(np.log(sigmoid(np.dot(X[pos], w)+b)))
# 对于负样本计算 loss
neg_sum = np.sum(np.log(1-sigmoid(np.dot(X[neg], w)+b)))
# 返回cross entropy loss
return -(pos_sum + neg_sum)
# 实现逻辑回归模型
def logistic_regression_minibatch(X, y, num_steps, learning_rate):
"""
基于梯度下降法实现逻辑回归模型
X: 训练数据(特征向量), 大小为N * D
y: 训练数据(标签),一维的向量,长度为D
num_steps: 梯度下降法的迭代次数
learning_rate: 步长
"""
w, b = np.zeros(X.shape[1]), 0
for step in range(num_steps):
# 随机采样一个batch, batch大小为100
batch_idx = np.random.choice(X.shape[0], 100)
X_batch, y_batch = X[batch_idx], y[batch_idx]
# 计算预测值与实际值之间的误差
error = sigmoid(np.dot(X_batch,w)+b) - y_batch
# 对于w, b的梯度计算
grad_w = np.matmul(X_batch.T, error)
grad_b = np.sum(error)
# 对于w, b的梯度更新
w = w - learning_rate * grad_w
b = b - learning_rate * grad_b
# 每隔一段时间,计算一下log likelihood,看看有没有变化
# 正常情况下, 它会慢慢变小,最后收敛
if step % 50000 == 0:
print (f'当前迭代步数为:{step:6},当前目标函数的值为:',log_likelihood(X_batch, y_batch, w, b))
return w,b
w, b = logistic_regression_minibatch(X, y, num_steps = 500000, learning_rate = 5e-4)
print ("(自己写的)逻辑回归的参数w, b分别为: ", w, b)
# 这里我们直接调用sklearn的模块来训练,看看跟自己手写的有没有区别。如果结果一样就说明是正确的!
from sklearn.linear_model import LogisticRegression
# C设置一个很大的值,意味着不想加入正则项
clf = LogisticRegression(fit_intercept=True, C = 1e15)
clf.fit(X, y)
print ("(sklearn)逻辑回归的参数w, b分别为: ", clf.coef_, clf.intercept_, )可以看到,在迭代的过程中,目标函数的值甚至会变大,这说明我们的梯度受到了噪声的影响。但其实梯度的噪声也有一些好处。特别是对于那些复杂模型的训练,噪声也可以使得我们可以得到更好的局部最优解。因为在一些复杂的模型中,存在一种类型的点叫做鞍点(saddle point),它的梯度为0,但并不是局部最优解,如上面图里绿色和红色的相交点。对于这些点,假如我们使用随机梯度下降法即可以较容易跳出并找到真正的局部最优解,因为随机梯度下降法本身具有噪声。总之,在三者当中最常用的算法是小批量梯度下降法。特别是在深度学习当中,它的应用尤其重要。对于各类梯度下降法做一个总结:在工业界中,最常用的方法为小批量梯度下降法。小批量梯度下降法也有助于更好地利用GPU的计算能力。小批量梯度下降法折中了梯度下降法和随机梯度下降法各自的优缺点,更好地解决梯度噪声的问题,更新更加稳定。随机梯度下降法或者小批量梯度下降法有助于解决鞍点(saddle point)的问题。4 逻辑回归进阶4.1 决策边界逻辑回归本质上是一个线性分类器,这一点可以通过逻辑回归的决策边界来加以证明。所谓决策边界,通俗一点来说就是划定一条线,线的两侧分别是不同类型的样本。如下图:显然,在这条线上的点存在一种性质,即属于1的概率和属于0的概率都是一样的。根据这个性质,我们可以求解逻辑回归的决策边界。首先我们知道样本 x 属于1和0的概率分别是:所以给定一个点 x 属于决策边界,那么就有: \begin{aligned} \frac{1}{1+e^{-(w^Tx+b)}}&=\frac{e^{-(w^Tx+b)}}{1+e^{-(w^Tx+b)}}\\ 1&=e^{-(w^Tx+b)}\\ \log 1&=\log e^{-(w^Tx+b)}\\ 0&=-(w^Tx+b)\\ w^Tx+b&=0 \end{aligned} 所以最后得出来的决策边界就是一个线性的决策边界,这就证明了逻辑回归是一个线性分类器。一般而言,对于任何模型来说,只需要判断其决策边界的方程是什么样的,就能知道这个模型是线性的还是非线性的。4.2 多元逻辑回归实际上除了二分类问题,现实生活中还存在大量的多分类问题。对于逻辑回归而言,其实还存在一个多元逻辑回归的版本,多元逻辑回归可以解决多分类的问题。那具体如何实现多元逻辑回归呢? 一个简单直接且直观的方法就是引入多组参数。上图的表示方法是我们通过二分类问题进行很直观地一种衍生表示,但实际上这是不合理的。因为上面的式子有一个重要的性质没有满足,即sum rule。所谓sum rule,就是指一个属于不同类别的概率,加起来应该等于1。我们可以这么理解,给定一个样本,假设能计算出这个样本分别属于类别1, 类别2,…类别K的概率,而且总共有K个类别,那这时候,这些概率也需要满足一个式子:就是所有条件概率之合等于1。虽然每个条件概率都落在(0, 1)之间,但它们之合并不等于1。这就要求在这个基础上还需要做一些改造,使得所有概率之合等于1。更为严谨一点的写法是,假设离散型随机变量 Y 的取值集合是 \{1,2,...,K\} ,那么多向逻辑回归模型是:\begin{aligned}
P(Y=k|x)&=\frac{e^{\theta^Tx}}{1+\sum_{k=1}^{K} e^{\theta ^Tx}}, \quad k=1,2,...,K-1\\ P(Y=K|x)&=\frac{1}{1+\sum_{k=1}^{K} e^{\theta ^Tx}} \end{aligned} 这里简单解释一下softmax函数。假设我们有一个数组 V ,其长度为 j ,
V_i 表示 V 中的第 i 个元素,那么这个元素的softmax值就是: S_i = \frac{e^{V_i}}{\sum_{i=1}^{j} e^{V_i}} 不难发现,上面的式子和多项逻辑回归一模一样(当 V 中某一个元素为0时)。对于多项逻辑回归而言,使用softmax形式去做分类有两个非常耀眼的优点,一是计算梯度的时候便于求导;二是使得样本属于某一类的概率和属于其他类的概率能够显著区分开来,这也是softmax中max的意义所在。4.3 最大熵模型与逻辑回归最大熵模型由最大熵原理推导实现。最大熵原理认为,学习概率模型时,在所有可能的概率模型(分布)中,熵最大的模型是最好的模型。最大熵模型一般写作如下形式: p_{w}(y \mid x)=\frac{\exp \left(\sum_{i=1}^{n} w_{i} f_{i}(x, y)\right)}{\sum_{y} \exp \left(\sum_{i=1}^{n} w_{i} f_{i}(x, y)\right)} 其中 f_i(x,y) 叫做特征函数,用于描述输入 x 和输出 y 之间的某一个事实。其定义是: f(x,y)=\left\{\begin{array}{cc} 1,& \quad x与y满足某一事实 \\ 0,& \quad 否则 \end{array}\right. 当特征函数为: f_{i}(x)=\left\{\begin{array}{cc} x_{i} & y=1 \\ 0 & y=0 \end{array}\right. 当我们分类的数目为
\{0,1\}
时: \begin{array}{l} p_{w}\left(y_{i}=0 \mid x_{i}\right)=\frac{1}{1+\exp \left(\boldsymbol{w} \boldsymbol{x}_{i}\right)} \\ p_{w}\left(y_{i}=1 \mid x_{i}\right)=\frac{\exp \left(\boldsymbol{w} \boldsymbol{x}_{i}\right)}{1+\exp \left(\boldsymbol{w} \boldsymbol{x}_{i}\right)} \end{array} 可以看到,其实逻辑回归就是最大熵模型的一种特例,它们都有着类似的形式。它们又称为对数线性模型。4.4 神经网络与逻辑回归我们都知道神经网络是深度学习的基础,它的技术推动了整个深度学习的发展,进而推动了过去几年AI的发展。神经网络模型是从结构上模拟了神经元的结构,虽然神经网络优秀,但未必一定要比其他的模型效果更好,这要取决于问题本身。另外,神经网络模型其实90年代就有了,只不过那个时候还没有受到广泛的关注。一个具有单个神经元的神经网络如下图:神经网络无非就是很多神经元的组合。输入信号经过一个一个神经元最终到达输出层。这里神经元起到的作用无非就是把输入信号X转换成输出信号Y, 这个过程其实就是学习映射f的过程。这里提及神经网络的目的是为了揭示神经网络和逻辑回归直接的联系,神经网络具体的知识就不做过多的展开了。那么对于神经网络而言,常用的一个激活函数就是Sigmoid激活函数。我们之前说过神经网络是由很多神经元来构成的,而且一个有趣的点是:通过一个神经元我们可以构造出逻辑回归模型。所以,从这个角度来看,可以认为逻辑回归是神经网络的特例。4.5 其他优化方法我们知道,工业界常用的小批量梯度下降法如下图所示:但实际上小批量梯度下降仍然不够高效,于是:所以有新的FTRL算法:5 逻辑回归常见问题5.1 逻辑回归是一个分类模型,为什么叫做回归?我们知道对逻辑回归而言存在: y = \frac{1}{1+e^{-(w^Tx+b)}} 将上式进行数学变化可以得到: \ln \frac{y}{1-y}=w^Tx+b 若将 y 视为样本 x 作为正例的可能性,则
1-y
是其反例可能性,两者的比值为: \frac{y}{1-y} 这个 \frac{y}{1-y} 又叫做几率(odds),反映了 x 作为正例的相对可能性。对这个几率取对数则得到“对数几率”(log odds, 也叫做logit)
\ln \frac{y}{1-y}所以 y = \frac{1}{1+e^{-(w^Tx+b)}} 这个式子本身相当于是在用线性回归模型的预测结果去逼近真实标记的对数几率。因此这个模型也叫做对数几率回归(Logistic Regression,也叫做Logit Regression)。这就是为什么一个分类模型被叫做回归的原因。5.2 逻辑回归为什么用对数损失函数首先,逻辑回归使用对数损失函数是用极大似然估计推导得到的结果。其次,对于逻辑回归而言,使用对数损失函数方便求导,容易优化,存在一个全局最优解。最后,从信息论的角度来说,对数损失函数亦是交叉熵函数,可以更好地衡量模型与真实数据之间的拟合程度,从而帮助优化算法进行更好地学习。5.3 逻辑回归和线性回归的区别和联系线性回归解决的是回归问题, y 的取值是从负无穷到正无穷,是一个连续值。而逻辑回归解决的是分类问题, y 的取值是离散值,如果是二分类问题, y 的取值是0和1。线性回归得到的结果代入sigmiod函数中输出的值就是逻辑回归的的结果,通常我们会设定一个阈值,如0.5,当sigmoid函数输出的结果大于0.5,则预测为正例,反之为反例。逻辑回归和线性回归在进行参数求解时均使用了极大似然估计的方法来对训练样本进行建模。而且优化目标函数时,也都可以使用梯度下降法来进行优化。5.4 逻辑回归的优缺点优点:形式简单,模型的可解释性非常好。从特征的权重可以看到不同的特征对最后结果的影响,某个特征的权重值比较高,那么这个特征最后对结果的影响会比较大。模型效果不错。在工程上是可以接受的(作为baseline),如果特征工程做的好,效果不会太差,并且特征工程可以大家并行开发,大大加快开发的速度。训练速度较快。分类的时候,计算量仅仅只和特征的数目相关。并且逻辑回归的分布式优化sgd发展比较成熟,训练的速度可以通过堆机器进一步提高,这样我们可以在短时间内迭代好几个版本的模型。资源占用小,尤其是内存。因为只需要存储各个维度的特征值。方便输出结果调整。逻辑回归可以很方便的得到最后的分类结果,因为输出的是每个样本的概率分数,我们可以很容易的对这些概率分数进行cutoff,也就是划分阈值(大于某个阈值的是一类,小于某个阈值的是一类)缺点:准确率并不是很高。因为形式非常的简单(非常类似线性模型),很难去拟合数据的真实分布。容易欠拟合。很难处理数据不平衡的问题。举个例子:如果我们对于一个正负样本非常不平衡的问题比如正负样本比 10000:1.我们把所有样本都预测为正也能使损失函数的值比较小。但是作为一个分类器,它对正负样本的区分能力不会很好。处理非线性数据较麻烦。如果要引入非线性,需要做大量的特征并且对特征进行离散化。逻辑回归本身无法筛选特征。有时候,我们会用树模型来筛选特征,然后再上逻辑回归。5.5 逻辑斯特回归为什么要对特征进行离散化?在工业界,很少直接将连续值作为特征喂给逻辑回归模型,而是将连续特征离散化为一系列0、1特征交给逻辑回归模型,这样做的优势有以下几点:稀疏向量内积乘法运算速度快,计算结果方便存储,容易scalable(扩展)。离散化后的特征对异常数据有很强的鲁棒性:比如一个特征是年龄>30是1,否则0。如果特征没有离散化,一个异常数据“年龄300岁”会给模型造成很大的干扰。逻辑回归属于广义线性模型,表达能力受限;单变量离散化为N个后,每个变量有单独的权重,相当于为模型引入了非线性,能够提升模型表达能力,加大拟合。离散化后可以进行特征交叉,由M+N个变量变为M*N个变量,进一步引入非线性,提升表达能力。特征离散化后,模型会更稳定,比如如果对用户年龄离散化,20-30作为一个区间,不会因为一个用户年龄长了一岁就变成一个完全不同的人。当然处于区间相邻处的样本会刚好相反,所以怎么划分区间是门学问。知乎大神李沐少帅指出,模型是使用离散特征还是连续特征,其实是一个“海量离散特征+简单模型” 同 “少量连续特征+复杂模型”的权衡。既可以离散化用线性模型,也可以用连续特征加深度学习。就看是喜欢折腾特征还是折腾模型了。通常来说,前者容易,而且可以n个人一起并行做,有成功经验;后者目前看很赞,能走多远还须拭目以待。6 参考资料统计学习方法机器学习(周志华)美团机器学习实践百面机器学习逻辑回归面试题汇总机器学习-逻辑回归(非常详细)贪心学院学习资料}

我要回帖

更多关于 逻辑运算0+1等于多少 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信