逻辑回归学习算法,适用于二分类——y的输出值为0或1的问题。比如输入一张图片,输出这张图片上的动物是或不是猫。算法能够输出预测值 y ^ \hat{y} y^ ,可以称之为对实际值y的估计。或者说在输入了 n x n_x nx维度的X后,输出的 y ^ \hat{y} y^是表示y=1的可能性大小,即 y ^ = P ( y = 1 ∣ x ) \hat{y}=P\left( y=1|x \right) y^=P(y=1∣x)。在线性回归中, y ^ = ω T x + b \hat{y}=\omega ^Tx+b y^=ωTx+b(w是特征权重,b是偏差)是训练出一个较好的w和b值,用于拟合真实的输出值y,但在逻辑回归中,y的预测值 y ^ \hat{y} y^由于表示的是概率P,值分布在0和1之间,而 y ^ = ω T x + b \hat{y}=\omega ^Tx+b y^=ωTx+b得出的 y ^ \hat{y} y^值属于实数集R,对于想要的在0和1之间的概率来说它是没有意义的。所以需要找到一个函数,能将 y ^ \hat{y} y^映射到0和1之间。
需要找到一个函数,将线性回归模型的实数值转换为[0,1]之间的输出结果,即输入 z = f ( x ) = w T x + b ∈ R z=f\left( x \right) =w^Tx+b\in R z=f(x)=wTx+b∈R,经过这个函数输出 y ^ ∈ ( 0 , 1 ) \hat{y}\in \left( 0,1 \right) y^∈(0,1)。由广义的线性模型可知,可以找到一个输出 y ^ ∈ ( 0 , 1 ) \hat{y}\in \left( 0,1 \right) y^∈(0,1)的函数,若预测值z大于0判为正例,小于0 判为反例,若处于临界值则可任意判别。可知阶跃函数符合这个性质:
y = { 0 , z < 0 ; 0.5 , z = 0 ; 1 , z > 0 , y=\begin{cases} 0,z<0;\\ 0.5,z=0;\\ 1,z>0,\\ \end{cases} y=⎩⎪⎨⎪⎧0,z<0;0.5,z=0;1,z>0,
但是可以看出,阶跃函数是不连续的,不利于后续w,b值的训练,所以找到了sigmoid函数,又称对数几率函数,输入 z = w T x + b z=w^Tx+b z=wTx+b,输出 g ( z ) = 1 1 + e − z g\left( z \right) =\frac{1}{1+e^{-z}} g(z)=1+e−z1,将z值映射到[0,1]之间,用python画出sigmoid函数图像如下:
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(z):
return 1/(1+np.exp(-z))
x=np.linspace(-10,10,1000)
y=sigmoid(x)
plt.plot(x,y)
plt.show()
z = 0 z=0 z=0 时 g ( z ) = 0.5 g(z)=0.5 g(z)=0.5
z > 0 z>0 z>0 时 g ( z ) > 0.5 g(z)>0.5 g(z)>0.5
z < 0 z<0 z<0 时 g ( z ) < 0.5 g(z)<0.5 g(z)<0.5
我们认为当 g ( z ) > 0.5 g\left( z \right) >0.5 g(z)>0.5时,输出值为1,判断为正例;当 g ( z ) < 0.5 g\left( z \right) <0.5 g(z)<0.5时,输出值为0,判断为反例;当 g ( z ) = 0.5 g\left( z \right) =0.5 g(z)=0.5时任意判别。
由函数的表达式容易分析出:
前面已经了解了逻辑回归的样子,下一步需要做的是训练出w和b值,需要一个损失函数(代价函数)。损失函数顾名思义,代表预测值离真实值差异大小,差异越大,损失也越多,所以为了降低差异,需要最小化损失函数。在线性回归中,损失函数为预测值和真实值的均方误差,利用最小化均方误差进行训练,即求解 w , b : min w , b E ( w , b ) = Σ i = 1 m ( y i − w x i − b ) 2 w,b:\min _{w,b}E_{\left( w,b \right)}=\varSigma _{i=1}^{m}\left( y_i-wx_i-b \right) ^2 w,b:minw,bE(w,b)=Σi=1m(yi−wxi−b)2。但是当在学习逻辑回归参数的时候,会发现我们的优化目标不是凸优化,梯度下降方法只能找到多个局部最优值,而找不到全局最优值,所以虽然均方误差确实是一种不错的损失函数,但不适合逻辑回归。
凸函数,可以找到全局最优值:
非凸函数,只能找到局部最优值:
回想一下,在逻辑回归中,我们约定 y ^ = p ( y = 1 ∣ x ) \hat{y}=p\left( y=1|x \right) y^=p(y=1∣x)。即如果y=1,在给定训练样本x的条件下 y = y ^ y=\hat{y} y=y^;反过来说,如果y = 0,在给定训练样本x条件下y等于1减去 y ^ \hat{y} y^, y = 1 − y ^ y=1-\hat{y} y=1−y^,因此,如果 y ^ \hat{y} y^代表y=1的概率,那么 1 − y ^ 1-\hat{y} 1−y^就是y=0的概率。接下来,分析这两个概率公式:
I F y = 1 : p ( y ∣ x ) = y ^ IF\,\,y=1:p\left( y|x \right) =\hat{y} IFy=1:p(y∣x)=y^
I F y = 0 : p ( y ∣ x ) = 1 − y ^ IF\,\,y=0:p\left( y|x \right) =1-\hat{y} IFy=0:p(y∣x)=1−y^
这两个公式代表了y=0和y=1时的情况,需要注意我们仅讨论二分类问题的损失函数,因此y的取值只是0或者1,所以可以将上述两个概率公式合并成如下公式:
p ( y ∣ x ) = y ^ y ( 1 − y ^ ) 1 − y p\left( y|x \right) =\hat{y}^y\left( 1-\hat{y} \right) ^{1-y} p(y∣x)=y^y(1−y^)1−y
看合并成这种表达形式是否可以呢,分别判断y=0和y=1时是否符合上面两个概率公式,将y=0和y=1带入,容易得出是符合的。所以这样就得出了 p ( y ∣ x ) p\left( y|x \right) p(y∣x)的完整定义。由于 p ( y ∣ x ) p\left( y|x \right) p(y∣x)表示的是概率,在训练学习算法时需要输出值的概率当然是越大越好,即最大化 p ( y ∣ x ) p\left( y|x \right) p(y∣x)。因为log函数是严格单调递增的函数,最大化 p ( y ∣ x ) p\left( y|x \right) p(y∣x)就是最大化 log ( p ( y ∣ x ) ) \log \left( p\left( y|x \right) \right) log(p(y∣x)),即计算 log ( y ^ y ( 1 − y ^ ) 1 − y ) \log \left( \hat{y}^y\left( 1-\hat{y} \right) ^{1-y} \right) log(y^y(1−y^)1−y),通过对数函数化简为: y log y ^ + ( 1 − y ) log ( 1 − y ^ ) y\log \hat{y}+\left( 1-y \right) \log \left( 1-\hat{y} \right) ylogy^+(1−y)log(1−y^)
其中y为真实值, y ^ \hat{y} y^为预测值。将上述函数取负号,得到逻辑回归的损失函数: L ( y ^ , y ) = − ( y log y ^ + ( 1 − y ) log ( 1 − y ^ ) ) L\left( \hat{y},y \right) =-\left( y\log \hat{y}+\left( 1-y \right) \log \left( 1-\hat{y} \right) \right) L(y^,y)=−(ylogy^+(1−y)log(1−y^))
由于取了负号,所以需要最小化损失函数,即最小化 L ( y ^ , y ) L\left( \hat{y},y \right) L(y^,y)。我们通过这个称为 L 的损失函数,来衡量预测输出值和实际值有多接近。
上面我们已经求到了单个训练样本的损失函数表达式,但逻辑回归是很多样本放在一起训练,如何求m个样本的损失函数(代价函数)呢?对比于线性回归,在线性回归中,当有m个样本存在时,我们用的损失函数为: J ( θ ) = 1 m Σ m i = 1 1 2 ( h θ ( x ( i ) ) − y ( i ) ) 2 J_{\left( \theta \right)}=\frac{1}{m}\underset{i=1}{\overset{m}{\varSigma}}\frac{1}{2}\left( h_{\theta}\left( x^{\left( i \right)} \right) -y^{\left( i \right)} \right) ^2 J(θ)=m1i=1Σm21(hθ(x(i))−y(i))2
其中h(x)为预测值,也就是 y ^ \hat{y} y^,可以看出是每个样本预测值和真实值之差的平方的平均值。为了对比方便,我们直接给出逻辑回归中的损失函数为: J ( θ ) = − 1 m Σ m i = 1 [ y ( i ) log h θ ( x ( i ) ) + ( 1 − y ( i ) ) log ( 1 − h θ ( x ( i ) ) ) ] J_{\left( \theta \right)}=-\frac{1}{m}\underset{i=1}{\overset{m}{\varSigma}}\left[ y^{\left( i \right)}\log h_{\theta}\left( x^{\left( i \right)} \right) +\left( 1-y^{\left( i \right)} \right) \log \left( 1-h_{\theta}\left( x^{\left( i \right)} \right) \right) \right] J(θ)=−m1i=1Σm[y(i)loghθ(x(i))+(1−y(i))log(1−hθ(x(i)))]
不难发现,其实就是上述单个样本的损失函数的累加之后除以m,即为m个损失函数的平均值。
那么为什么可以用到均值来表示总体的损失函数呢?推导过程中用到了似然函数的概念,参考《概率论与数理统计》(第四版).高等教育出版社
理解,对于0-1分布的似然函数 P{X=k}=pk(1−p)1−k,k=0,1(0<p<1) P{X=x}=px(1−p)1−x,x=0,1(0<p<1)
0-1分布的分布律为 P { X = k } = p k ( 1 − p ) 1 − k , k = 0 , 1 ( 0 < p < 1 ) P\left\{ X=k \right\} =p^k\left( 1-p \right) ^{1-k},k=0,1\left( 0
当 x 1 , x 2 , … , x n x_1,x_2,…,x_n x1,x2,…,xn是来自与样本 X 1 , X 2 , … , X n X_1,X_2,…,X_n X1,X2,…,Xn的一个样本值, X X X的分布律为 P { X = x } = p x ( 1 − p ) 1 − x , x = 0 , 1 ( 0 < p < 1 ) P\left\{ X=x \right\} =p^x\left( 1-p \right) ^{1-x},x=0,1\left( 0
它的似然函数为 L ( p ) = Π n i = 1 p x i ( 1 − p ) 1 − x i L\left( p \right) =\underset{i=1}{\overset{n}{\varPi}}p^{x_i}\left( 1-p \right) ^{1-x_i} L(p)=i=1Πnpxi(1−p)1−xi
似然函数的对数形式为(取对数后累乘变为对数的累加) log L ( p ) = ( Σ m i = 1 x ( i ) ) log p + ( Σ m i = 1 ( 1 − x ( i ) ) log ( 1 − p ) ) \log L\left( p \right) =\left( \underset{i=1}{\overset{m}{\varSigma}}x^{\left( i \right)} \right) \log p+\left( \underset{i=1}{\overset{m}{\varSigma}}\left( 1-x^{\left( i \right)} \right) \log \left( 1-p \right) \right) logL(p)=(i=1Σmx(i))logp+(i=1Σm(1−x(i))log(1−p))
逻辑回归中sigmoid函数其实就是表示0-1中取1的概率,对应于0-1分布中的概率p;而我们输入的 y i y_i yi为真实值,在机器学习中称为标签,对应于0-1分布中的 x i x_i xi。这样就将逻辑回归和0-1分布对应起来了。我们用逻辑回归来作为分类模型,需要用最大似然估计的方法来评判模型的好坏。让总体分布尽量与样本的分布趋同,就是总体的分布与样本分布具有最大的相似性。所以将对应的表达式代入0-1分布,得到逻辑回归似然函数的对数形式为: log L ( p ) = Σ m i = 1 y ( i ) log h θ ( x ( i ) ) + Σ m i = 1 ( 1 − y ( i ) ) log ( 1 − h θ ( x ( i ) ) ) \log L\left( p \right) =\underset{i=1}{\overset{m}{\varSigma}}y^{\left( i \right)}\log h_{\theta}\left( x^{\left( i \right)} \right) +\underset{i=1}{\overset{m}{\varSigma}}\left( 1-y^{\left( i \right)} \right) \log \left( 1-h_{\theta}\left( x^{\left( i \right)} \right) \right) logL(p)=i=1Σmy(i)loghθ(x(i))+i=1Σm(1−y(i))log(1−hθ(x(i)))
逻辑回归的损失函数为: J ( θ ) = − 1 m Σ m i = 1 [ y ( i ) log h θ ( x ( i ) ) + ( 1 − y ( i ) ) log ( 1 − h θ ( x ( i ) ) ) ] J\left( \theta \right) =-\frac{1}{m}\underset{i=1}{\overset{m}{\varSigma}}\left[ y^{\left( i \right)}\log h_{\theta}\left( x^{\left( i \right)} \right) +\left( 1-y^{\left( i \right)} \right) \log \left( 1-h_{\theta}\left( x^{\left( i \right)} \right) \right) \right] J(θ)=−m1i=1Σm[y(i)loghθ(x(i))+(1−y(i))log(1−hθ(x(i)))]
可以发现,损失函数和似然函数的对数形式很像,只是在前面乘上了 − 1 m -\frac{1}{m} −m1,最大似然估计的方法要求对数似然函数的最大值,损失函数在其前面加上负号,即求损失函数的最小值,最小化损失,符合定义。 − 1 m -\frac{1}{m} −m1是用来对m个样本值的损失函数值取平均,不会影响最小化函数和训练。所以可以看出,逻辑回归中损失函数的求解用到了0-1分布以及似然函数的知识。
梯度下降在训练过程中扮演着怎样的角色呢
在测试集上,通过最小化代价函数(成本函数) J ( w , b ) J\left( w,b \right) J(w,b)来训练参数 w w w和 b b b,如图:
其实梯度下降就是当前函数值朝着最陡峭的坡度下降到最低点。我们将上面下降的曲面想象成一个山谷,如果想要最快又最精准的下降到山谷位置,肯定首先要朝着最低点的方向下降,并且越陡的地方下降的越快,越缓的地方下降的越慢(假如在很缓的地方也下降的很快,很容易越过最低点,不容易收敛)。
那么在函数中某一点的导数就是一个很好的指标用于指明参数下降的方向和大小,导数就是曲线的斜率,斜率越大,导数绝对值越大,下降的也就越快。但是不能仅仅用导数来更新参数,需要乘以一个 α \alpha α学习率(learning rate),来控制步长(step)。 : = := :=表示更新参数。公式如下:
w : = w − α ∂ J ( w , b ) ∂ w w:=w-\alpha \frac{\partial J\left( w,b \right)}{\partial w} w:=w−α∂w∂J(w,b)
b : = b − α ∂ J ( w , b ) ∂ b b:=b-\alpha \frac{\partial J\left( w,b \right)}{\partial b} b:=b−α∂b∂J(w,b)
∂ \partial ∂表示求偏导符号,可以读作round, ∂ J ( w , b ) ∂ w \frac{\partial J\left( w,b \right)}{\partial w} ∂w∂J(w,b)就是损失函数 J ( w , b ) J\left( w,b \right) J(w,b)对 b b b求偏导,表示随着 w w w的改变 J ( w , b ) J\left( w,b \right) J(w,b)改变的快慢。小写字母用在求导数(derivative),即函数只有一个参数, 偏导数符号 ∂ \partial ∂用在求偏导(partial derivative),即函数含有两个以上的参数。
损失函数 J ( w , b ) J\left( w,b \right) J(w,b)对 w w w求偏导,即求 ∂ J ( w , b ) ∂ w \frac{\partial J\left( w,b \right)}{\partial w} ∂w∂J(w,b)是一个难点,当然如果你有较好的微积分数理基础可以来看看如何求的:
所有,如果有n个特征,也就是说:
θ = [ θ 0 θ 1 … θ n ] \theta =\left[ \begin{array}{c} \theta _0\\ \theta _1\\ …\\ \theta _n\\ \end{array} \right] θ=⎣⎢⎢⎡θ0θ1…θn⎦⎥⎥⎤,参数向量 θ \theta θ包括 θ 0 \theta _0 θ0 θ 1 \theta _1 θ1 θ 2 \theta _2 θ2一直到 θ n \theta _n θn,那么将会用到这个式子: θ j : = θ j − α 1 m Σ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) \theta _j:=\theta _j-\alpha \frac{1}{m}\varSigma _{i=1}^{m}\left( h_{\theta}\left( x^{\left( i \right)} \right) -y^{\left( i \right)} \right) x_{j}^{\left( i \right)} θj:=θj−αm1Σi=1m(hθ(x(i))−y(i))xj(i)来同时更新所有的 θ \theta θ的值。
学习率能控制一次迭代下降的大小,需要根据不同问题设定不同的学习率。学习率设定过大,虽然下降的很快,但是很容易跳过最低点,无法收敛;学习率设定过小,迭代一个很小的模型也需要迭代很多次,计算量过大,增加训练难度,所以要找到一个始终的学习率,使得模型在达到最大训练次数之前可以下降到最低点。
向量化其实就是深度学习中代替for循环的一种技巧,在训练逻辑回归过程中,你要输入并计算很多项格式相同的样本训练数据,一般来说会用到for循环进行迭代,但是由于数据量过大,训练速度很慢,所以导入numpy包,它将所有样本同类型的数据组合在一起组成一个向量,运用矩阵运算,计算效率很高,大大加快了训练速度。
不论是在线性回归还是逻辑回归中,都会存在过拟合或者欠拟合的问题。
线性回归欠拟合、正好拟合、过拟合对比图:
逻辑回归欠拟合、正好拟合、过拟合对比图:
当欠拟合发生时,继续训练或者加大训练数据等可以解决。但是当发生过拟合时,有两种解决方式:
逻辑回归通常使用正则化来预防过拟合。
下面介绍正则化的相关概念:
以线性回归为例讲解正则化(参考吴恩达老师机器学习课的笔记(黄海广博士)
逻辑回归中的正则化
import numpy as np
def sigmoid(z):
return 1/(1+np.exp(-z))
def costReg(theta, X, y, learningRate):
theta = np.matrix(theta)
X = np.matrix(X)
y = np.matrix(y)
first = np.multiply(-y, np.log(sigmoid(X*theta.T)))
second = np.multiply((1 - y), np.log(1 - sigmoid(X*theta.T)))
reg = (learningRate / (2 * len(X))* np.sum(np.power(theta[:,1:theta.shape[1]],2)))
return np.sum(first - second) / (len(X)) + reg
import numpy as np
#sigmoid函数
def sigmoid(x):
return 1/(1+np.exp(-x))
#预测函数,调用sigmoid函数,0到0.5返回0,0.5到1返回1,可以看出是分类问题
def predict(x_test, weight):
if sigmoid(weight.T @ x_test) > 0.5: #@表示矩阵的乘法
return 1
return 0
#实现逻辑回归的训练
def fit(x, y):
m, n = x.shape #m为行,n为列
w = np.zeros(n) #一行,n列的0,设置为w的初始值
r = 0.001 #learning rate学习率
cnt = 0
max_cnt = 10000 #设置最大迭代次数
t = 0.01 #设置收敛条件
#利用梯度下降方法求最优解
while cnt<max_cnt: #在到达最大迭代次数之前
cnt+=1
for i in range(m): #m为行,对于每一条元素
diff=(y[i]-sigmoid(w.T @ x[i]))*x[i] #diff=[(y-sigmoid(w.T*x)]*x
w=w+r*diff #更新w值,w=w+(学习率)*diff
if (abs(diff)<t).all(): #表示如果diff中所有的值都小于t,.all()的意思就是所有的都满足
break
return w
if __name__ == '__main__':
#输入训练数据
x_train = np.array([
[1, 2.697, 6.254],
[1, 1.872, 2.014],
[1, 2.312, 0.812],
[1, 1.983, 4.990],
[1, 0.932, 3.920],
[1, 1.321, 5.583],
[1, 2.215, 1.560],
[1, 1.659, 2.932],
[1, 0.865, 7.362],
[1, 1.685, 4.763],
[1, 1.786, 2.523]
])
x_train = np.array([
[25,20,22,23],
[1.862,1.872, 2.014,1.222],
[1.669,2.312, 0.812,1.222],
[24,23,21,23],
[1.649,0.932, 3.920,1.222],
[23,24,21,23],
[1.589,2.215, 1.560,1.222],
[1.666,1.659, 2.932,1.222],
[22,25,23,23],
[1.090,1.685, 4.763,1.222],
[23,24,24,23]
])
y_train = np.array([1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1])
#进行训练,得到权重w
weight = fit(x_train, y_train)
# 调用预测函数,输出x_train的预测结果ans
ans = []
for i in x_train:
tmp = predict(i, weight)
ans.append(tmp)
#计算预测准确率
res = list(ans - y_train)
rate = res.count(0) / len(res)
print("逻辑回归预测结果为:", ans)
print("预测结果准确率为:", "%.2f" % (rate * 100), "%")
import pandas as pd
import numpy as np
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression #逻辑回归,也就是对数几率回归,用于分类
#1.加载数据,良/恶性乳腺癌肿瘤预测》
column_names = ['Sample code number', 'Clump Thickness', 'Uniformity of Cell Size', 'Uniformity of Cell Shape',
'Marginal Adhesion', 'Single Epithelial Cell Size', 'Bare Nuclei', 'Bland Chromatin', 'Normal Nucleoli',
'Mitoses', 'Class']
data = pd.read_csv(
'https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin//breast-cancer-wisconsin.data',
names=column_names)
data = data.replace(to_replace='?', value=np.nan) # 非法字符的替代
data = data.dropna(how='any') # 去掉空值,any:出现空值行则删除
print(data.shape)
print(data.head())
#2.数据分割
X_train,X_test,y_train,y_test = train_test_split(data[column_names[1:10]],data[column_names[10]],test_size=0.25,random_state=5)
#3.数据标准化,保证每个维度的特征数据方差为1,均值为0.使得预测结果不会被某些维度过大的特征值主导
ss = StandardScaler()
X_train = ss.fit_transform(X_train)
X_test = ss.transform(X_test)
#4.调用逻辑回归模型,这里采用l2惩罚项
lr = LogisticRegression(C=1.0, penalty='l2', tol=0.1)
lr.fit(X_train, y_train)
#5.预测
lr_predict = lr.predict(X_test)
print("预测结果为:{}".format(lr.score(X_test,y_test)))
print("预测结果为:{}".format(accuracy_score(y_test,lr_predict)))
参考资料: