当前位置:   article > 正文

逻辑斯蒂回归(logistic regression)原理小结_简述逻辑斯谛回归的学习策略与算法

简述逻辑斯谛回归的学习策略与算法

本博客中使用到的完整代码请移步至: 我的github:https://github.com/qingyujean/Magic-NLPer,求赞求星求鼓励~~~


适用问题:多类分类

模型特点:特征条件下类别的条件概率分布;对数线性模型

模型类型:判别模型

损失函数:逻辑斯蒂损失

学习策略:极大似然估计,或者正则化的极大似然估计

学习算法:梯度下降法、拟牛顿法

1. 模型函数

    逻辑斯蒂回归是解决分类任务的。逻辑斯蒂回归模型属于“对数线性模型”
    先看看逻辑斯蒂回归与线性回归的联系。首先想到的是线性回归+阈值,可以转化为一个分类任务,以阈值划分区间,落到不同范围则分成不同的类,例如使用“单位阶跃函数”
y = { 0 , i f    z < 0 0.5 , i f    z = 0            其 中 z = X θ 1 , i f    z > 0 y=

{0,ifz<00.5,ifz=0z=XθXθ1,ifz>0
y=0,0.5,1,ifz<0ifz=0z=XθXθifz>0
但这样做有2个问题:

  • 不够健壮,非常容易受到噪声的影响
  • 阶跃函数不连续,不可导,无法使用常用的优化算法(梯度下降、拟牛顿法、最小二乘法等)求解最值点的参数。

    考虑在“线性回归原理小结”中提到的“广义线性模型”单调可微的函数 g ( ⋅ ) g(\cdot) g()可作为“联系函数”,能将线性回归模型的预测值与真实label联系起来,如果真实标记是离散的,就就对应了现在讲的分类任务。

“sigmoid”函数拥有非常好的数学性质:
g ( z ) = 1 1 + e − z g(z)=\frac{1}{1+e^{-z}} g(z)=1+ez1
sigmoid函数曲线

  • 它能将实数范围压缩到(0,1)之间,并在z=0附近变化较快。
  • z → + ∞ z \rightarrow +\infty z+时, g ( z ) → 1 g(z) \rightarrow 1 g(z)1,当 z → − ∞ z \rightarrow -\infty z时, g ( z ) → 0 g(z) \rightarrow 0 g(z)0,且 g ( 0 ) = 0.5 g(0)=0.5 g(0)=0.5,这正好与概率对应上。
  • 单调可微(连续光滑,处处可导),且导数求解简单方便, g ′ ( z ) = g ( z ) ( 1 − g ( z ) ) g^{'} (z)=g(z)(1-g(z)) g(z)=g(z)(1g(z))
  • 函数曲线关于(0,0.5)中心对称。

手动实现sigmoid函数

def sigmoid(z):
    return 1./(1+np.exp(-z))
  • 1
  • 2

    使用“sigmoid”函数代替“单位阶跃函数”,即使用“sigmoid”函数作为联系函数 g ( ⋅ ) g(\cdot) g(),并令 z = x θ z=x\theta z=xθ,则得到了二元逻辑斯蒂回归模型的一般形式:
h θ ( x ) = 1 1 + e − x θ h_\theta (x)=\frac{1}{1+e^{-x\theta}} hθ(x)=1+exθ1
x作为样本输入, h θ ( x ) h_\theta (x) hθ(x)作为模型输出,并将其视作后验概率 p ( y = 1 ∣ x , θ ) p(y=1|x,\theta) p(y=1x,θ)

  • 那么当 x θ > 0 x\theta>0 xθ>0时, h θ > 0.5 h_\theta>0.5 hθ>0.5, y预测为1,且当 h θ → 1 h_\theta \rightarrow 1 hθ1时,y预测分类为1的概率就越大;
  • x θ < 0 x\theta<0 xθ<0时, h θ < 0.5 h_\theta<0.5 hθ<0.5, y预测为0,且当 h θ → 0 h_\theta \rightarrow 0 hθ0时,y预测分类为0的概率就越大;
  • x θ = 0 x\theta=0 xθ=0时, h θ = 0.5 h_\theta=0.5 hθ=0.5时,无法做出预测。
    p ( y = 1 ∣ x , θ ) = h θ ( x ) = 1 1 + e − x θ = e x θ 1 + e x θ p(y=1|x,\theta)=h_\theta (x)=\frac{1}{1+e^{-x\theta}}=\frac{e^{x\theta}}{1+e^{x\theta}} p(y=1x,θ)=hθ(x)=1+exθ1=1+exθexθ
    p ( y = 0 ∣ x , θ ) = 1 − h θ ( x ) = e − x θ 1 + e − x θ = 1 1 + e x θ p(y=0|x,\theta)=1-h_\theta (x)=\frac{e^{-x\theta}}{1+e^{-x\theta}}=\frac{1}{1+e^{x\theta}} p(y=0x,θ)=1hθ(x)=1+exθexθ=1+exθ1

    一个事件的“几率”,表示该事件发生的概率与不发生的概率的比值,即 p 1 − p \frac{p}{1-p} 1pp,而该事件的“对数几率” l o g i t ( p ) = l o g p 1 − p logit(p)=log\frac{p}{1-p} logit(p)=log1pp,而在二元逻辑斯蒂回归中,输出 y = 1 y=1 y=1的对数几率是:
l o g p ( y = 1 ∣ x , θ ) 1 − p ( y = 1 ∣ x , θ ) ) = x θ log\frac{p(y=1|x,\theta)}{1-p(y=1|x,\theta))}=x\theta log1p(y=1x,θ))p(y=1x,θ)=xθ
即在逻辑斯蒂回归中,输出 y = 1 y=1 y=1的对数几率是输入x的线性函数,或者说上式实际上是在用线性回归模型的预测结果去逼近真实label的对数几率,这里也是最能体现“回归”的地方,残留了线性回归的影子。该模型也叫作“对数几率回归”

2. 损失函数

    平方损失在逻辑斯蒂回归分类问题中是非凸的,逻辑回归不是连续的,所以这里不再使用平方损失,而是使用极大似然来推导损失函数。
p ( y = 1 ∣ x , θ ) p(y=1|x,\theta) p(y=1x,θ) p ( y = 0 ∣ x , θ ) p(y=0|x,\theta) p(y=0x,θ)的式子结合起来,写成一个式子:
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(yx,θ)=hθ(x)y(1hθ(x))1y
即为一个样本属于其真实label y的概率分布表达式。

似然函数的目标是:令每个样本属于其真实label的概率越大越好,“极大似然法”

似然函数为:
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}^mh_\theta (x^{(i)})^{y^{(i)}} (1-h_\theta (x^{(i)}))^{1-y^{(i)}} L(θ)=i=1mp(y(i)x(i),θ)=i=1mhθ(x(i))y(i)(1hθ(x(i)))1y(i)
L ( θ ) L(\theta) L(θ)越大越好,即求 L ( θ ) L(\theta) L(θ)的极大值,作为优化目标,求解对应的参数,即使用“极大似然估计”

对似然函数取对数可使得表达式更简洁,得到对数似然表达式,然后再取负,可得到负对数似然表达式,那么求极大值就可转化为求极小值。
J ( θ ) = − l n L ( θ ) = − ∑ i = 1 m [ y ( i ) l o g ( h θ ( x ( i ) ) ) + ( 1 − y ( i ) ) l o g ( 1 − h θ ( x ( i ) ) ) ] J(\theta)=-lnL(\theta)=-\sum\limits_{i=1}^{m}[y^{(i)}log(h_{\theta}(x^{(i)}))+ (1-y^{(i)})log(1-h_{\theta}(x^{(i)}))] J(θ)=lnL(θ)=i=1m[y(i)log(hθ(x(i)))+(1y(i))log(1hθ(x(i)))]
此时即得到二元逻辑斯蒂回归的损失函数,问题转化为以负对数似然函数为目标的最优化问题(求解极小值)。
J ( θ ) = − [ (   l o g   ( h θ ( X ) ) T y + (   l o g   ( 1 − h θ ( X ) ) T ( 1 − y ) ] J(\theta) = -[(\,log\,(h_\theta(\pmb{X}))^T\pmb{y}+(\,log\,(1-h_\theta(\pmb{X}))^T(1-\pmb{y})] J(θ)=[(log(hθ(XXX))Tyyy+(log(1hθ(XXX))T(1yyy)]
其中1表示全1向量, h θ ( X ) = 1 1 + e − X θ h_\theta(\pmb{X})=\frac{1}{1+e^{-X\theta}} hθ(XXX)=1+eXθ1

手动实现损失函数的计算

# 计算损失函数
def compute_loss(theta, X, y):
    if np.ndim(theta) == 1:
        theta = theta[:, np.newaxis]
    z_x = X.dot(theta) # h=θ^T dot X=θ0*x0+θ1*x1
    # print(z_x.shape) # (100,3)x(3,1)=>(100,1)
    h_x = sigmoid(z_x) # =>(100,1)

    m = y.size # 100,用于对loss求平均
    J_loss = -1./m * (np.log(h_x).T.dot(y) + np.log(1-h_x).T.dot(1-y)) # =>(1,1)
    if np.isnan(J_loss):
        return np.inf
    return J_loss[0][0]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

加载数据:

data = np.loadtxt(data_dir+'data1.txt', delimiter=',')
print('data shape:', data.shape) # (100,3)
print(data[:5])
  • 1
  • 2
  • 3

输出:

data shape: (100, 3)
[[34.62365962 78.02469282  0.        ]
 [30.28671077 43.89499752  0.        ]
 [35.84740877 72.90219803  0.        ]
 [60.18259939 86.3085521   1.        ]
 [79.03273605 75.34437644  1.        ]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

绘制样本数据点的分布:

# 绘制数据点,便于查看数据分布
def plot_data(data, label_pos, label_neg, axes=None):
    # 获取正负样本的index
    pos = data[:,2] == 1
    neg = data[:,2] == 0

    if axes is None:
        axes = plt.gca()

    axes.scatter(data[pos][:,0], data[pos][:,1], marker='+', c='k', s=60, linewidth=2, label=label_pos)
    axes.scatter(data[neg][:,0], data[neg][:,1], c='y', s=60, linewidth=2, label=label_neg)
    axes.legend()

plot_data(data, 'y=1', 'y=0')
plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

样本数据点的分布图

查看一下 θ = [ 0 , 0 , 0 ] \theta=[0,0,0] θ=[0,0,0]时时的loss:

# 把θ0与x0=[1,1,...,1] 与特征x1,x2,..一起组成一个大X
X = np.c_[np.ones(data.shape[0]), data[:,0:2]]
print(X[:5], X.shape) # (100, 3)
y = np.c_[data[:,2]]
print(y[:5], y.shape) # (100,1)

loss = compute_loss(np.array([[0],[0],[0]]), X, y)
print('\nloss:', loss)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

输出:

[[ 1.         34.62365962 78.02469282]
 [ 1.         30.28671077 43.89499752]
 [ 1.         35.84740877 72.90219803]
 [ 1.         60.18259939 86.3085521 ]
 [ 1.         79.03273605 75.34437644]] (100, 3)
[[0.]
 [0.]
 [0.]
 [1.]
 [1.]] (100, 1)

loss: 0.6931471805599453
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3. 学习算法

    逻辑斯蒂回归学习常采用梯度下降法以及拟牛顿法。这里给出使用梯度下降法对损失函数的优化过程。
δ J ( θ ) δ θ j = ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) \frac{\delta J(\theta)}{\delta\theta_{j}} = \sum_{i=1}^{m} ( h_\theta (x^{(i)})-y^{(i)})x^{(i)}_{j} δθjδJ(θ)=i=1m(hθ(x(i))y(i))xj(i)
其矩阵/向量形式为:
δ J ( θ ) δ θ = X T ( h θ ( X ) − y ) \frac{\delta J(\theta)}{\delta\theta} = \pmb{X^T}(h_\theta(\pmb{X})-\pmb{y}) δθδJ(θ)=XTXTXT(hθ(XXX)yyy)
θ \theta θ的使用梯度下降法的迭代计算公式为:
θ = θ − α X T ( h θ ( X ) − y ) \theta=\theta-\alpha\pmb{X^T}(h_\theta(\pmb{X})-\pmb{y}) θ=θαXTXTXT(hθ(XXX)yyy)

手动实现梯度计算

# 计算梯度
def compute_gradient(theta, X, y):
    if np.ndim(theta) == 1:
        theta = theta[:, np.newaxis]
    # 计算model输出
    z_x = X.dot(theta) # θ^T dot X=θ0*x0+θ1*x1
    # print(z_x.shape) # (100,3)x(3,1)=>(100,1)
    h_x = sigmoid(z_x) # =>(100,1)

    m = y.size # 100 对应loss中求平均时的m

    # 计算梯度并更新参数 X.T.dot(h_x-y):(3,100)x(100,1)=>(3,1)
    grad = 1./m * X.T.dot(h_x-y) # (3,1)
    return grad.flatten() # (3,)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
initial_theta = np.zeros(X.shape[1]) # (3,)
initial_theta = initial_theta[:, np.newaxis] # (3,1)
print(initial_theta.shape) # (3,1)

loss = compute_loss(initial_theta, X, y)
grad = compute_gradient(initial_theta, X, y)

print('loss:', loss)
print('grad:', grad)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

输出:

(3, 1)
loss: 0.6931471805599453
grad: [ -0.1        -12.00921659 -11.26284221]
  • 1
  • 2
  • 3

使用梯度下降求解参数,利用scipy.optimize.minimize()函数,指定最小化的目标即损失函数的计算,然后是梯度的计算,优化参数theta,输入输出(X, y),即可实现最小化损失函数。

from scipy.optimize import minimize
res = minimize(compute_loss, initial_theta, args=(X,y),
               jac=compute_gradient, options={'maxiter':400})
res
  • 1
  • 2
  • 3
  • 4

输出:

 fun: 0.2034977015895099
 hess_inv: array([[ 2.85339493e+03, -2.32908823e+01, -2.27416470e+01],
       [-2.32908823e+01,  2.04489131e-01,  1.72969525e-01],
       [-2.27416470e+01,  1.72969525e-01,  1.96170322e-01]])
      jac: array([-2.68557620e-09,  4.36433485e-07, -1.39671757e-06])
  message: 'Optimization terminated successfully.'
     nfev: 34
      nit: 25
     njev: 30
   status: 0
  success: True
        x: array([-25.16131634,   0.2062316 ,   0.20147143])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
# 查看最后优化得到的参数theta的值
final_updated_theta = res.x
print('theta:', final_updated_theta, final_updated_theta.shape) # (3,)
  • 1
  • 2
  • 3

输出:

theta: [-25.16131634   0.2062316    0.20147143] (3,)
  • 1

根据求解出的参数 θ \theta θ绘制出决策边界:

# 绘制决策边界
# 边界:
x1_min, x1_max = X[:,1].min(), X[:,1].max() # 注意X[0]是常数列[1,1,...,1]
x2_min, x2_max = X[:,2].min(), X[:,2].max()
xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max), np.linspace(x2_min, x2_max)) # 生成网格坐标
# print(xx1.shape, xx2.shape) # (50, 50) (50, 50)
XX = np.c_[np.ones(xx1.ravel().shape[0]), xx1.ravel(), xx2.ravel()]
# print(XX.shape) # (2500, 3)
z_x = XX.dot(final_updated_theta) # (2500, 3)x(3,)=>(2500,)
h_x = sigmoid(z_x)
h_x = h_x.reshape(xx1.shape) # (50,50)
# print(h_x.shape)
plt.contour(xx1, xx2, h_x, [0.5], linewidths=1, colors='b') # 决策边界

plot_data(data, 'y=1', 'y=0') # # 样本数据点
plt.legend(loc=1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

绘制决策边界

4. 加正则项的逻辑斯蒂回归

    为防止模型过拟合,一般可以加入正则化项,常见的有L1正则化L2正则化。例如这里考虑L2正则化,那么其加正则化项的损失函数代数表达式为:
J ( θ ) = − [ (   l o g   ( h θ ( X ) ) T y + (   l o g   ( 1 − h θ ( X ) ) T ( 1 − y ) ] + λ 2 ∣ ∣ θ ∣ ∣ 2 2 J(\theta) = -[(\,log\,(h_\theta(\pmb{X}))^T\pmb{y}+(\,log\,(1-h_\theta(\pmb{X}))^T(1-\pmb{y})]+\frac{\lambda}{2}||\theta||^2_2 J(θ)=[(log(hθ(XXX))Tyyy+(log(1hθ(XXX))T(1yyy)]+2λθ22
即 J ( θ ) = − [ (   l o g   ( h θ ( X ) ) T y + (   l o g   ( 1 − h θ ( X ) ) T ( 1 − y ) ] + λ 2 ∑ j = 1 n θ j 2 即J(\theta)=-[(\,log\,(h_\theta(\pmb{X}))^T\pmb{y}+(\,log\,(1-h_\theta(\pmb{X}))^T(1-\pmb{y})]+\frac{\lambda}{2}\sum_{j=1}^{n}\theta_{j}^{2} J(θ)=[(log(hθ(XXX))Tyyy+(log(1hθ(XXX))T(1yyy)]+2λj=1nθj2
注意:正则化项中 θ \theta θ从下标1开始计算, θ 0 \theta_0 θ0不参与计算。

加正则项的逻辑斯蒂回归的损失函数手动实现

# 计算损失函数
def compute_loss_reg(theta, reg, *args):
    if np.ndim(theta) == 1:
        theta = theta[:, np.newaxis] # (28,)=>(28,1)
    XX, y = args
    z_x = XX.dot(theta) # h=θ^T dot X=θ0*x0+θ1*x1
    # print(z_x.shape) # (118,28)x(28,1)=>(118,1)
    h_x = sigmoid(z_x) # =>(118,1)

    m = y.size # 118 用于求平均损失
    J_loss = -1./m * (np.log(h_x).T.dot(y) + np.log(1-h_x).T.dot(1-y)) # =>(1,1)
    # 注意:正则项的求和中j是从1开始的,而不是0,因为约束的是特征,而\theta_0对应的是x_0=1常数项
    reg_item = reg/(2.*m) * np.sum(np.square(theta[:,0][1:]))
    J_loss = J_loss + reg_item
    if np.isnan(J_loss):
        return np.inf
    return J_loss[0][0]

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

    那么加正则项后的偏导(梯度)代数表达式为:
δ J ( θ ) δ θ j = ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) + λ θ j \frac{\delta J(\theta)}{\delta\theta_{j}} = \sum_{i=1}^{m} ( h_\theta (x^{(i)})-y^{(i)})x^{(i)}_{j}+\lambda\theta_j δθjδJ(θ)=i=1m(hθ(x(i))y(i))xj(i)+λθj
其矩阵/向量形式为:
δ J ( θ ) δ θ = X T ( h θ ( X ) − y ) + λ θ \frac{\delta J(\theta)}{\delta\theta} = \pmb{X^T}(h_\theta(\pmb{X})-\pmb{y})+\lambda\pmb{\theta} δθδJ(θ)=XTXTXT(hθ(XXX)yyy)+λθθθ
加正则项的梯度计算的手动实现

# 计算梯度
def compute_gradient_reg(theta, reg, *args):
    if np.ndim(theta) == 1:
        theta = theta[:, np.newaxis] # (28,)=>(28,1)
    XX, y = args
    # 计算model输出
    z_x = XX.dot(theta) # θ^T dot X=θ0*x0+θ1*x1
    # print(z_x.shape) # (118,28)x(28,1)=>(118,1)
    h_x = sigmoid(z_x) # =>(118,1)

    m = y.size # 118 对应于平均损失中的m

    # 计算梯度并更新参数 X.T.dot(h_x-y):(28,118)x(118,1)=>(28,1)
    grad = 1./m * XX.T.dot(h_x-y) # (28,1)

    reg_item = reg/m * np.r_[ [[0]], theta[1:] ] # (1,1) 行拼接 (27,1)=>(28,1)
    grad = grad + reg_item # (28,1)
    return grad.flatten() # (28,)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

加载数据:

data2 = np.loadtxt(data_dir+'data2.txt', delimiter=',')
print('data shape:', data2.shape) # (100,3)
print(data2[:5])
plot_data(data2, 'y=1', 'y=0')
  • 1
  • 2
  • 3
  • 4

输出:

data shape: (118, 3)
[[ 0.051267  0.69956   1.      ]
 [-0.092742  0.68494   1.      ]
 [-0.21371   0.69225   1.      ]
 [-0.375     0.50219   1.      ]
 [-0.51325   0.46564   1.      ]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

样本数据点的分布图

可见该数据不是直接线性可分的,不能直接使用逻辑斯蒂回归。考虑在“线性回归原理小结”中提到“多项式回归”,这里使用“多项式特征”(sklearn.preprocessing.PolynomialFeatures)对原始数据 X X X进行特征转化,对转化后的数据 X ′ X' X再使用逻辑斯蒂回归。由于数据比之前的复杂,这次便可在学习时加入正则项以避免过拟合。。

X = data2[:, 0:2]
y = np.c_[data2[:,2]]
print(X.shape, y.shape) # (118, 2) (118, 1)

poly = PolynomialFeatures(6) # 最高次项为6次
XX = poly.fit_transform(X) # X是有2个特征,XX有28个特征(含组合特征)
XX.shape # (118, 28)
# 0次项:1个,1次项:2个,2次项:3个(x1^2,x2^2,x1x2),3次项:4个
# 4次项:5个,5次项:6个,6次项:7个,一共28个特征

initial_theta = np.zeros(XX.shape[1]) # (28,)
initial_theta = initial_theta[:, np.newaxis]
print(initial_theta.shape) # (28,1)

loss = compute_loss_reg(initial_theta, 1, XX, y)
grad = compute_gradient_reg(initial_theta, 1, XX, y)

print('loss:', loss)
print('grad:', grad)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

输出:

(28, 1)
loss: 0.6931471805599454
grad: [8.47457627e-03 1.87880932e-02 7.77711864e-05 5.03446395e-02
 1.15013308e-02 3.76648474e-02 1.83559872e-02 7.32393391e-03
 8.19244468e-03 2.34764889e-02 3.93486234e-02 2.23923907e-03
 1.28600503e-02 3.09593720e-03 3.93028171e-02 1.99707467e-02
 4.32983232e-03 3.38643902e-03 5.83822078e-03 4.47629067e-03
 3.10079849e-02 3.10312442e-02 1.09740238e-03 6.31570797e-03
 4.08503006e-04 7.26504316e-03 1.37646175e-03 3.87936363e-02]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

绘制决策边界,并查看不同的正则化项系数,对于决策边界的影响

  • lambda = 0 : 就是没有正则化,此时会容易过拟合
  • lambda = 1 : 合适的正则化项系数
  • lambda = 100 : 正则化项太激进,导致基本没拟合出决策边界
# 预测
def predict(theta, X, threshold=0.5):
    z_x = X.dot(theta) # (b,3)x(3,1)=>(b,1)
    h_x = sigmoid(z_x) # (b,1)
    pred = h_x >= threshold
    return pred.astype('int') # (b,1) b个样本的预测结果


fig, axes = plt.subplots(1, 3, sharey=True, figsize=(17,5))
print(axes.shape) # (3,)

lambda_list = [0., 1., 100.]
for i,C in enumerate(lambda_list):
    # 最小化损失
    res2 = minimize(compute_loss_reg, initial_theta, args=(C,XX,y),
               jac=compute_gradient_reg, options={'maxiter':3000})
    final_updated_theta = res2.x # (28,)
    # print('theta:', final_updated_theta.shape) # (28,)

    # 计算准确率 XX:(118, 28) y:(118, 1) theta:=>(28,1)
    pred = predict(final_updated_theta.reshape(-1,1), XX) # (118,1)
    # print(pred.shape) # (118,1)
    accuracy = 100. * sum(pred.ravel() == y.ravel()) / y.size

    # 绘制原始数据分布
    plot_data(data2, 'y=1', 'y=0', axes=axes.flatten()[i])

    # 绘制决策边界 X:(118,2) y:(118,1)
    # 边界:
    x1_min, x1_max = X[:,0].min(), X[:,0].max() # 注意这里的X不含常数列[1,1,...,1]
    x2_min, x2_max = X[:,1].min(), X[:,1].max()
    xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max), np.linspace(x2_min, x2_max)) # 生成网格坐标
    # print(xx1.shape, xx2.shape) # (50, 50) (50, 50)
    X_ = np.c_[xx1.ravel(), xx2.ravel()]
    # print(X_.shape) # (2500,2) # 没算常数列

    XX_ = poly.fit_transform(X_) # (2500,28)
    # print(XX_.shape) # (2500,28)
    z_x = XX_.dot(final_updated_theta) # (2500, 28)x(28,)=>(2500,)
    h_x = sigmoid(z_x) # (2500,)
    h_x = h_x.reshape(xx1.shape) # (50,50)
    #print(h_x.shape) # (50,50)

    axes.flatten()[i].contour(xx1, xx2, h_x, [0.5], linewidths=1, colors='g') # 决策边界
    # 设置标题
    axes.flatten()[i].set_title('Train accuracy {:.2f}% with Lambda = {}'.format(accuracy, C))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

绘制决策边界

5. 多项逻辑斯蒂回归

    二项逻辑斯蒂回归推广到多项逻辑斯蒂回归,可用于解决多分类问题。假设离散型随机变量y的取值集合为{1,2,…,K},那么多项逻辑斯蒂回归的概率分布表达式如下:
p ( y = k ∣ x , θ ) = e x θ k 1 + ∑ k = 1 K − 1 e x θ k      ,      k = 1 , 2 , . . . , K − 1 p(y=k|x,\theta ) = \frac{e^{x\theta_k}} {1+\sum\limits_{k=1}^{K-1}e^{x\theta_k}}\;\;,\;\;k=1,2,...,K-1 p(y=kx,θ)=1+k=1K1exθkexθk,k=1,2,...,K1
p ( y = K ∣ x , θ ) = 1 1 + ∑ k = 1 K − 1 e x θ k p(y=K|x,\theta ) = \frac{1} {1+\sum\limits_{k=1}^{K-1}e^{x\theta_k}} p(y=Kx,θ)=1+k=1K1exθk1
注意:这里的“多项逻辑斯蒂回归”是解决多分类问题,而前面以及“线性回归原理小结”中提到的“多项式回归”是解决 “非线性特征” 经过特征转化,得到 多元“线性特征” 的问题。

6. 模型综合评价

  • 能以近似概率的形式输出结果;
  • 直接对分类的可能性建模,无需事先假设数据分布,避免了假设分布不准确所带来的问题;
  • 形式简单,易于建模;
  • 可解释性强,可控度高。 θ \theta θ直观的表达了各特征在预测中的重要性;
  • 训练快,feature engineering后效果也不错;
  • 添加feature很简单;
  • 结果是近似概率,可以做ranking model 排序模型。

7. 二分类 vs 多分类

    利用二分类器解决多分类问题,关键在于如何对多分类任务进行拆分,以及如何对多个分类器进行集成。常用的拆分策略:

  • 一对一(One vs. One,简称OvO):将给定标注数据集按K个类别两两配对,产生 K ( K − 1 ) 2 \frac{K(K-1)}{2} 2K(K1)个二分类任务。测试时,新样本同时提交到所有分类器。产生 K ( K − 1 ) 2 \frac{K(K-1)}{2} 2K(K1)分类结果,然后对这些分类结果进行“投票”,产生最终分类结果。
  • 一对其余(One vs. Rest,简称OvR):每次将一个类作为正例,其余其他类都作为负例,形成 N N N个二分类任务。测试时,新样本同时提交到所有分类器。若只有1个分类器预测为正类,则对应类别即为最终分类结果,若有多个分类器预测为正类,则取置信度大的为最终分类结果。
  • 多对多(Many vs. Many,简称MvM):每次将若干类作为正例,若干类作为负例。OvO和OvR都是MvM的特例。

完整代码

完整代码请移步至: 我的github:https://github.com/qingyujean/Magic-NLPer,求赞求星求鼓励~~~

参考

[1] 统计学习方法(第2版) 李航
[2] 机器学习(西瓜书) 周志华
[3] 逻辑回归原理小结   刘建平

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/776271
推荐阅读
相关标签
  

闽ICP备14008679号