Lesson 4.6 逻辑回归的手动实现

news2024/11/18 4:32:24

文章目录

  • 一、逻辑回归损失函数的梯度计算表达式
  • 二、创建分类数据集生成器
    • 1. 手动创建分类数据
    • 2. 创建分类数据生成器
  • 三、逻辑回归的手动实现
  • 四、逻辑回归的分类性能瓶颈与算法评价

  • 讨论完梯度下降的相关内容之后,接下来我们尝试使用梯度下降算法求解逻辑回归损失函数,并且通过一系列实验来观察逻辑回归的模型性能。
# 科学计算模块
import numpy as np
import pandas as pd

# 绘图模块
import matplotlib as mpl
import matplotlib.pyplot as plt

# 自定义模块
from ML_basic_function import *

一、逻辑回归损失函数的梯度计算表达式

  • 对于任何模型的手动实现,首先我们都需要考虑关于其损失函数的梯度计算表达式。并且为了方便代码实现,我们需要尽可能的使用矩阵形式进行表示。
  • 在线性回归中,我们通过矩阵求导运算,可以非常快速的求解出用矩阵形式表示的损失函数梯度表达式,而在逻辑回归中,由于损失函数表达式的不同,这个过程会略微复杂一些。
  • 在 Lesson 4.2 中,我们从极大似然估计和 KL 离散两个角度推导了逻辑回归损失函数,对于二分类为逻辑回归模型来说,假设数据集是 m 行 n 列的一个数据集,则二分类交叉熵损失函数如下: b i n a r y C E ( w ^ ) = − 1 m ∑ i = 1 m [ y ( i ) ⋅ l o g ( p 1 ( x ^ ( i ) ; w ^ ) ) + ( 1 − y ( i ) ) ⋅ l o g ( 1 − p 1 ( x ^ ( i ) ; w ^ ) ) ] binaryCE(\hat w)= -\frac{1}{m}\sum^m_{i=1}[y^{(i)} \cdot log(p_1(\hat x^{(i)};\hat w))+(1-y^{(i)}) \cdot log(1-p_1(\hat x^{(i)};\hat w))] binaryCE(w^)=m1i=1m[y(i)log(p1(x^(i);w^))+(1y(i))log(1p1(x^(i);w^))]
  • 其中 n 为样本数量, x ^ ( i ) \hat x^{(i)} x^(i) 为第 i 条样本(添加了最后全是 1 的列), w ^ \hat w w^ 为带截距项的线性方程系数向量, p 1 p_1 p1 为某条样本在当前参数下逻辑回归输出结果,即预测该样本为 1 的概率, y ( i ) y^{(i)} y(i) 为第 i i i 条样本的真实类别。
  • 对于上述 BCE 损失函数,梯度表达式为: ∇ w ^ B C E ( w ^ ) = ∂ B C E ( w ^ ) ∂ w ^ \nabla \hat w BCE(\hat w) = \frac{\partial BCE(\hat w)}{\partial \hat w} w^BCE(w^)=w^BCE(w^)
  • 其中 w ^ = [ w 1 , w 2 , w 3 , . . . , w n , b ] \hat w=[w_1, w_2, w_3, ..., w_n, b] w^=[w1,w2,w3,...,wn,b],因此上式可进一步展开:
    ∇ w ^ B C E ( w ^ ) = ∂ B C E ( w ^ ) ∂ w ^ = [ ∂ B C E ( w ^ ) ∂ w 1 , ∂ B C E ( w ^ ) ∂ w 2 , . . . , ∂ B C E ( w ^ ) ∂ w n , ∂ B C E ( w ^ ) ∂ b ] T \nabla\hat w BCE(\hat w) = \frac{\partial BCE(\hat w)}{\partial \hat w}= [\frac{\partial BCE(\hat w)}{\partial w_1}, \frac{\partial BCE(\hat w)}{\partial w_2}, ...,\frac{\partial BCE(\hat w)}{\partial w_n}, \frac{\partial BCE(\hat w)}{\partial b}]^T w^BCE(w^)=w^BCE(w^)=[w1BCE(w^),w2BCE(w^),...,wnBCE(w^),bBCE(w^)]T
  • l o g ( p 1 ( x ^ ( i ) ; w ^ ) ) = l o g ( S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ) log(p_1(\hat x^{(i)}; \hat w)) = log(Sigmoid(\hat x^{(i)} \cdot \hat w)) log(p1(x^(i);w^))=log(Sigmoid(x^(i)w^)),我们对带入某条数据后的某个参数进行求导,例如对 w i w_i wi 进行求导,则可得: l o g ( p 1 ( x ^ ( i ) ; w ^ ) ) ′ = l o g ( S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ) ′ = 1 S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ⋅ S i g m o i d ′ ( x ^ ( i ) ⋅ w ^ ) log(p_1(\hat x^{(i)}; \hat w))' = log(Sigmoid(\hat x^{(i)} \cdot \hat w))'=\frac{1}{Sigmoid(\hat x^{(i)} \cdot \hat w)} \cdot Sigmoid'(\hat x^{(i)} \cdot \hat w) log(p1(x^(i);w^))=log(Sigmoid(x^(i)w^))=Sigmoid(x^(i)w^)1Sigmoid(x^(i)w^)
  • 对于 Sigmoid 函数来说,有 S i g m o i d ′ ( x ) = S i g m o i d ( x ) ( 1 − S i g m o i d ( x ) ) Sigmoid'(x) = Sigmoid(x)(1-Sigmoid(x)) Sigmoid(x)=Sigmoid(x)(1Sigmoid(x)),因此上式可进一步得到:
    l o g ( p 1 ( x ^ ( i ) ; w ^ ) ) ′ = ( 1 − S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ) ⋅ ( x ^ ( i ) ⋅ w ^ ) ′ log(p_1(\hat x^{(i)}; \hat w))' = (1-Sigmoid(\hat x^{(i)} \cdot \hat w)) \cdot (\hat x^{(i)} \cdot \hat w)' log(p1(x^(i);w^))=(1Sigmoid(x^(i)w^))(x^(i)w^)
  • 其中 x ^ ( i ) ⋅ w ^ = w 1 x 1 ( i ) + w 2 x 2 ( i ) + . . . + w n x n ( i ) + b \hat x^{(i)} \cdot \hat w=w_1x_1^{(i)}+w_2x_2^{(i)}+...+w_nx_n^{(i)}+b x^(i)w^=w1x1(i)+w2x2(i)+...+wnxn(i)+b,对 w i w_i wi 求导可得: ( x ^ ( i ) ⋅ w ^ ) ′ = x i ( i ) (\hat x^{(i)} \cdot \hat w)' = x_i^{(i)} (x^(i)w^)=xi(i)
  • 当然,对截距求导可得: ( x ^ ( i ) ⋅ w ^ ) ′ = 1 (\hat x^{(i)} \cdot \hat w)' = 1 (x^(i)w^)=1
  • 因此,在 l o g ( p 1 ( x ^ ( i ) ; w ^ ) ) log(p_1(\hat x^{(i)}; \hat w)) log(p1(x^(i);w^)) 中对 w i w_i wi 求导可得: l o g ( p 1 ( x ^ ( i ) ; w ^ ) ) ′ = ( 1 − S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ) ⋅ x i ( i ) log(p_1(\hat x^{(i)}; \hat w))' = (1-Sigmoid(\hat x^{(i)} \cdot \hat w)) \cdot x_i^{(i)} log(p1(x^(i);w^))=(1Sigmoid(x^(i)w^))xi(i)
  • 而在 l o g ( p 1 ( x ^ i ; w ^ ) ) log(p_1(\hat x_i; \hat w)) log(p1(x^i;w^)) 中对 b b b 求导可得: l o g ( p 1 ( x ^ ( i ) ; w ^ ) ) ′ = ( 1 − S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ) ⋅ 1 log(p_1(\hat x^{(i)}; \hat w))' = (1-Sigmoid(\hat x^{(i)} \cdot \hat w)) \cdot 1 log(p1(x^(i);w^))=(1Sigmoid(x^(i)w^))1
  • 类似的,在 ( 1 − y ( i ) ) ⋅ l o g ( 1 − p 1 ( x ^ ( i ) ; w ^ ) ) (1-y^{(i)}) \cdot log(1-p_1(\hat x^{(i)};\hat w)) (1y(i))log(1p1(x^(i);w^)) 中对 w i w_i wi 求导可得:
    ( 1 − y ( i ) ) ⋅ l o g ′ ( 1 − p 1 ( x ^ ( i ) ; w ^ ) ) = ( 1 − y ( i ) ) ⋅ l o g ′ ( 1 − S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ) = ( 1 − y ( i ) ) ⋅ 1 ( 1 − S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ) ⋅ ( − S i g m o i d ′ ( x ^ ( i ) ⋅ w ^ ) ) \begin{aligned}(1-y^{(i)}) \cdot log'(1-p_1(\hat x^{(i)};\hat w)) & = (1-y^{(i)}) \cdot log'(1-Sigmoid(\hat x^{(i)} \cdot \hat w)) \\&=(1-y^{(i)}) \cdot \frac{1}{(1-Sigmoid(\hat x^{(i)} \cdot \hat w))} \cdot (-Sigmoid'(\hat x^{(i)} \cdot \hat w))\end{aligned} (1y(i))log(1p1(x^(i);w^))=(1y(i))log(1Sigmoid(x^(i)w^))=(1y(i))(1Sigmoid(x^(i)w^))1(Sigmoid(x^(i)w^))
  • 同样,由于 S i g m o i d ′ ( x ) = S i g m o i d ( x ) ( 1 − S i g m o i d ( x ) ) Sigmoid'(x) = Sigmoid(x)(1-Sigmoid(x)) Sigmoid(x)=Sigmoid(x)(1Sigmoid(x)),所以上式可得:
    ( 1 − y ( i ) ) ⋅ l o g ′ ( 1 − p 1 ( x ^ ( i ) ; w ^ ) ) = ( 1 − y ( i ) ) ⋅ ( − S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ) ⋅ ( x ^ ( i ) ⋅ w ^ ) ′ = ( 1 − y ( i ) ) ⋅ ( − S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ) ⋅ x i ( i ) \begin{aligned}(1-y^{(i)}) \cdot log'(1-p_1(\hat x^{(i)};\hat w)) & = (1-y^{(i)}) \cdot (-Sigmoid(\hat x^{(i)} \cdot \hat w)) \cdot (\hat x^{(i)} \cdot \hat w)' \\&=(1-y^{(i)}) \cdot (-Sigmoid(\hat x^{(i)} \cdot \hat w)) \cdot x_i^{(i)}\end{aligned} (1y(i))log(1p1(x^(i);w^))=(1y(i))(Sigmoid(x^(i)w^))(x^(i)w^)=(1y(i))(Sigmoid(x^(i)w^))xi(i)
  • 类似的,当对截距项 b b b 求导时: ( 1 − y ( i ) ) ⋅ l o g ′ ( 1 − p 1 ( x ^ ( i ) ; w ^ ) ) = ( 1 − y ( i ) ) ⋅ ( − S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ) ⋅ 1 (1-y^{(i)}) \cdot log'(1-p_1(\hat x^{(i)};\hat w))= (1-y^{(i)}) \cdot (-Sigmoid(\hat x^{(i)} \cdot \hat w)) \cdot 1 (1y(i))log(1p1(x^(i);w^))=(1y(i))(Sigmoid(x^(i)w^))1
  • 将上述结果带入 ∂ B C E ( w ^ ) ∂ w i \frac{\partial BCE(\hat w)}{\partial w_i} wiBCE(w^),当对 w i w_i wi 进行求导时:
    ∂ B C E ( w ^ ) ∂ w i = ( − 1 m ∑ i = 1 m [ y ( i ) ⋅ l o g ( p 1 ( x ^ ( i ) ; w ^ ) ) + ( 1 − y ( i ) ) ⋅ l o g ( 1 − p 1 ( x ^ ( i ) ; w ^ ) ) ] ) ′ = − 1 m ∑ i = 1 m [ y ( i ) ⋅ l o g ′ ( p 1 ( x ^ ( i ) ; w ^ ) ) + ( 1 − y ( i ) ) ⋅ l o g ′ ( 1 − p 1 ( x ^ ( i ) ; w ^ ) ) ] = − 1 m ∑ i = 1 m [ y ( i ) ⋅ ( 1 − S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ) ⋅ x i ( i ) + ( 1 − y ( i ) ) ⋅ ( − S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ) ⋅ x i ( i ) ) ] = − 1 m ∑ i = 1 m [ x i ( i ) y ( i ) − x i ( i ) y ( i ) S i g m o i d ( x ^ ( i ) ⋅ w ^ ) − x i ( i ) S i g m o i d ( x ^ ( i ) ⋅ w ^ ) + x i ( i ) y ( i ) S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ] = 1 m ∑ i = 1 m ( x i ( i ) ( S i g m o i d ( x ^ ( i ) ⋅ w ^ ) − y ( i ) ) ) = 1 m ∑ i = 1 m ( x i ( i ) ( y ^ ( i ) − y ( i ) ) ) \begin{aligned}\frac{\partial BCE(\hat w)}{\partial w_i} &= (-\frac{1}{m}\sum^m_{i=1}[y^{(i)} \cdot log(p_1(\hat x^{(i)};\hat w))+(1-y^{(i)}) \cdot log(1-p_1(\hat x^{(i)};\hat w))])' \\&=-\frac{1}{m}\sum^m_{i=1}[y^{(i)} \cdot log'(p_1(\hat x^{(i)};\hat w))+(1-y^{(i)}) \cdot log'(1-p_1(\hat x^{(i)};\hat w))] \\&=-\frac{1}{m}\sum^m_{i=1}[y^{(i)} \cdot (1-Sigmoid(\hat x^{(i)} \cdot \hat w)) \cdot x_i^{(i)} + (1-y^{(i)}) \cdot (-Sigmoid(\hat x^{(i)} \cdot \hat w)) \cdot x_i^{(i)})] \\&=-\frac{1}{m}\sum^m_{i=1}[ x_i^{(i)}y^{(i)}-x_i^{(i)}y^{(i)}Sigmoid(\hat x^{(i)} \cdot \hat w) - x_i^{(i)}Sigmoid(\hat x^{(i)} \cdot \hat w)+x_i^{(i)}y^{(i)}Sigmoid(\hat x^{(i)} \cdot \hat w)] \\&=\frac{1}{m}\sum^m_{i=1}(x_i^{(i)}(Sigmoid(\hat x^{(i)} \cdot \hat w)-y^{(i)})) \\&=\frac{1}{m}\sum^m_{i=1}(x_i^{(i)}(\hat y^{(i)}-y^{(i)})) \end{aligned} wiBCE(w^)=(m1i=1m[y(i)log(p1(x^(i);w^))+(1y(i))log(1p1(x^(i);w^))])=m1i=1m[y(i)log(p1(x^(i);w^))+(1y(i))log(1p1(x^(i);w^))]=m1i=1m[y(i)(1Sigmoid(x^(i)w^))xi(i)+(1y(i))(Sigmoid(x^(i)w^))xi(i))]=m1i=1m[xi(i)y(i)xi(i)y(i)Sigmoid(x^(i)w^)xi(i)Sigmoid(x^(i)w^)+xi(i)y(i)Sigmoid(x^(i)w^)]=m1i=1m(xi(i)(Sigmoid(x^(i)w^)y(i)))=m1i=1m(xi(i)(y^(i)y(i)))
  • 对截距项 b b b 进行求导时: ∂ B C E ( w ^ ) ∂ b = 1 m ∑ i = 1 m 1 ⋅ ( y ^ ( i ) − y ( i ) ) \frac{\partial BCE(\hat w)}{\partial b} = \frac{1}{m}\sum^m_{i=1}1 \cdot (\hat y^{(i)}-y^{(i)}) bBCE(w^)=m1i=1m1(y^(i)y(i))
  • 其实,根据定义, x ( i ) = [ x 1 ( i ) , x 2 ( i ) , . . . , x n ( i ) , 1 ] x^{(i)}=[x_1^{(i)}, x_2^{(i)}, ..., x_n^{(i)}, 1] x(i)=[x1(i),x2(i),...,xn(i),1] x n + 1 ( i ) = 1 x^{(i)}_{n+1}=1 xn+1(i)=1,1 就是 x ( i ) x^{(i)} x(i) 的第 n + 1 n+1 n+1 个分量: ∇ w ^ B C E ( w ^ ) = ∂ B C E ( w ^ ) ∂ w ^ = [ ∂ B C E ( w ^ ) ∂ w 1 ∂ B C E ( w ^ ) ∂ w 2 . . . ∂ B C E ( w ^ ) ∂ w n ∂ B C E ( w ^ ) ∂ b ] = [ 1 m ∑ i = 1 m ( x 1 ( i ) ( S i g m o i d ( x ^ ( i ) ⋅ w ^ ) − y ( i ) ) ) 1 m ∑ i = 1 m ( x 2 ( i ) ( S i g m o i d ( x ^ ( i ) ⋅ w ^ ) − y ( i ) ) ) . . . 1 m ∑ i = 1 m ( x n ( i ) ( S i g m o i d ( x ^ ( i ) ⋅ w ^ ) − y ( i ) ) ) 1 m ∑ i = 1 m ( 1 ⋅ ( S i g m o i d ( x ^ ( i ) ⋅ w ^ ) − y ( i ) ) ) ] = 1 m [ ∑ i = 1 m x 1 ( i ) S i g m o i d ( x ^ ( i ) ⋅ w ^ ) − ∑ i = 1 m x 1 ( i ) y ( i ) ∑ i = 1 m x 2 ( i ) S i g m o i d ( x ^ ( i ) ⋅ w ^ ) − ∑ i = 1 m x 2 ( i ) y ( i ) . . . ∑ i = 1 m x n ( i ) S i g m o i d ( x ^ ( i ) ⋅ w ^ ) − ∑ i = 1 m x n ( i ) y ( i ) ∑ i = 1 m S i g m o i d ( x ^ ( i ) ⋅ w ^ ) − ∑ i = 1 m y ( i ) ] \begin{aligned}\nabla \hat w BCE(\hat w) &= \frac{\partial BCE(\hat w)}{\partial \hat w}\\& =\left [\begin{array}{cccc}\frac{\partial BCE(\hat w)}{\partial w_1} \\ \frac{\partial BCE(\hat w)}{\partial w_2} \\. \\. \\. \\ \frac{\partial BCE(\hat w)}{\partial w_n} \\ \frac{\partial BCE(\hat w)}{\partial b} \\ \end{array}\right] \\ & =\left [\begin{array}{cccc} \frac{1}{m}\sum^m_{i=1}(x_1^{(i)}(Sigmoid(\hat x^{(i)} \cdot \hat w)-y^{(i)})) \\ \frac{1}{m}\sum^m_{i=1}(x_2^{(i)}(Sigmoid(\hat x^{(i)} \cdot \hat w)-y^{(i)})) \\ . \\ . \\ . \\ \frac{1}{m}\sum^m_{i=1}(x_n^{(i)}(Sigmoid(\hat x^{(i)} \cdot \hat w)-y^{(i)})) \\ \frac{1}{m}\sum^m_{i=1}(1 \cdot (Sigmoid(\hat x^{(i)} \cdot \hat w)-y^{(i)})) \\ \end{array}\right] \\ &= \frac{1}{m} \left [\begin{array}{cccc} \sum^m_{i=1}x_1^{(i)}Sigmoid(\hat x^{(i)} \cdot \hat w)-\sum^m_{i=1}x_1^{(i)}y^{(i)} \\ \sum^m_{i=1}x_2^{(i)}Sigmoid(\hat x^{(i)} \cdot \hat w)-\sum^m_{i=1}x_2^{(i)}y^{(i)} \\ . \\ . \\ . \\ \sum^m_{i=1}x_n^{(i)}Sigmoid(\hat x^{(i)} \cdot \hat w)-\sum^m_{i=1}x_n^{(i)}y^{(i)} \\ \sum^m_{i=1}Sigmoid(\hat x^{(i)} \cdot \hat w)-\sum^m_{i=1}y^{(i)} \\ \end{array}\right]\\ \end{aligned} w^BCE(w^)=w^BCE(w^)= w1BCE(w^)w2BCE(w^)...wnBCE(w^)bBCE(w^) = m1i=1m(x1(i)(Sigmoid(x^(i)w^)y(i)))m1i=1m(x2(i)(Sigmoid(x^(i)w^)y(i)))...m1i=1m(xn(i)(Sigmoid(x^(i)w^)y(i)))m1i=1m(1(Sigmoid(x^(i)w^)y(i))) =m1 i=1mx1(i)Sigmoid(x^(i)w^)i=1mx1(i)y(i)i=1mx2(i)Sigmoid(x^(i)w^)i=1mx2(i)y(i)...i=1mxn(i)Sigmoid(x^(i)w^)i=1mxn(i)y(i)i=1mSigmoid(x^(i)w^)i=1my(i)
  • 我们令 X X X 为添加最后一列全是 1 的特征矩阵, y y y 是标签数组:
    X ^ = [ x ( 1 ) , x ( 2 ) , . . . , x ( m ) ] T = [ x 1 ( 1 ) , x 2 ( 1 ) , . . . , x n ( 1 ) , 1 x 1 ( 2 ) , x 2 ( 2 ) , . . . , x n ( 2 ) , 1 . . . x 1 ( m ) , x 2 ( m ) , . . . , x n ( m ) , 1 ] \begin{aligned} \hat X &= [x^{(1)}, x^{(2)}, ..., x^{(m)}]^T\\ & =\left [\begin{array}{cccc} x^{(1)}_1, x^{(1)}_2, ... ,x^{(1)}_n, 1 \\ x^{(2)}_1, x^{(2)}_2, ... ,x^{(2)}_n, 1 \\ . \\ . \\ . \\ x^{(m)}_1, x^{(m)}_2, ... ,x^{(m)}_n, 1 \\ \end{array}\right] \\ \end{aligned} X^=[x(1),x(2),...,x(m)]T= x1(1),x2(1),...,xn(1),1x1(2),x2(2),...,xn(2),1...x1(m),x2(m),...,xn(m),1 y = [ y ( 1 ) , y ( 2 ) , . . . , y ( m ) ] T y = [y^{(1)}, y^{(2)}, ..., y^{(m)}]^T y=[y(1),y(2),...,y(m)]T
  • 则上式前半部分可简化为:
    [ ∑ i = 1 m x 1 ( i ) S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ∑ i = 1 m x 2 ( i ) S i g m o i d ( x ^ ( i ) ⋅ w ^ ) . . . ∑ i = 1 m x n ( i ) S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ∑ i = 1 m S i g m o i d ( x ^ ( i ) ⋅ w ^ ) ] = [ x 1 ( 1 ) , x 1 ( 2 ) , . . . , x 1 ( m ) x 2 ( 1 ) , x 2 ( 2 ) , . . . , x 2 ( m ) . . . x m ( 1 ) , x m ( 2 ) , . . . , x m ( m ) 1 , 1 , . . . , 1 , 1 ] ⋅ [ S i g m o i d ( x ^ ( 1 ) ⋅ w ^ ) S i g m o i d ( x ^ ( 2 ) ⋅ w ^ ) . . . S i g m o i d ( x ^ ( m ) ⋅ w ^ ) ] = [ x 1 ( 1 ) , x 2 ( 1 ) , . . . , x n ( 1 ) , 1 x 1 ( 2 ) , x 2 ( 2 ) , . . . , x n ( 2 ) , 1 . . . x 1 ( m ) , x 2 ( m ) , . . . , x n ( m ) , 1 ] T ⋅ S i g m o i d ( [ x 1 ( 1 ) , x 2 ( 1 ) , . . . , x n ( 1 ) , 1 x 1 ( 2 ) , x 2 ( 2 ) , . . . , x n ( 2 ) , 1 . . . x 1 ( m ) , x 2 ( m ) , . . . , x n ( m ) , 1 ] ⋅ [ w 1 w 2 . . . w n b ] ) = X ^ T S i g m o i d ( X ^ ⋅ w ^ ) \begin{aligned} \left [\begin{array}{cccc} \sum^m_{i=1}x_1^{(i)}Sigmoid(\hat x^{(i)} \cdot \hat w) \\ \sum^m_{i=1}x_2^{(i)}Sigmoid(\hat x^{(i)} \cdot \hat w) \\ . \\ . \\ . \\ \sum^m_{i=1}x_n^{(i)}Sigmoid(\hat x^{(i)} \cdot \hat w) \\ \sum^m_{i=1}Sigmoid(\hat x^{(i)} \cdot \hat w) \\ \end{array}\right] &=\left [\begin{array}{cccc} x^{(1)}_1, x^{(2)}_1, ... ,x^{(m)}_1 \\ x^{(1)}_2, x^{(2)}_2, ... ,x^{(m)}_2 \\ . \\. \\. \\x^{(1)}_m, x^{(2)}_m, ... ,x^{(m)}_m \\ 1, 1, ... ,1, 1 \\ \end{array}\right] \cdot \left [\begin{array}{cccc} Sigmoid(\hat x^{(1)} \cdot \hat w) \\Sigmoid(\hat x^{(2)} \cdot \hat w) \\. \\. \\. \\ Sigmoid(\hat x^{(m)} \cdot \hat w) \\ \end{array}\right] \\ &=\left [\begin{array}{cccc} x^{(1)}_1, x^{(1)}_2, ... ,x^{(1)}_n, 1 \\ x^{(2)}_1, x^{(2)}_2, ... ,x^{(2)}_n, 1 \\ . \\ . \\ . \\ x^{(m)}_1, x^{(m)}_2, ... ,x^{(m)}_n, 1 \\ \end{array}\right]^T \cdot Sigmoid(\left [\begin{array}{cccc} x^{(1)}_1, x^{(1)}_2, ... ,x^{(1)}_n, 1 \\ x^{(2)}_1, x^{(2)}_2, ... ,x^{(2)}_n, 1 \\ . \\ . \\ . \\ x^{(m)}_1, x^{(m)}_2, ... ,x^{(m)}_n, 1 \\ \end{array}\right] \cdot \left [\begin{array}{cccc} w_1 \\ w_2 \\ . \\ . \\ . \\ w_n \\ b \\ \end{array}\right]) \\ &= \hat X^T Sigmoid(\hat X \cdot \hat w) \end{aligned} i=1mx1(i)Sigmoid(x^(i)w^)i=1mx2(i)Sigmoid(x^(i)w^)...i=1mxn(i)Sigmoid(x^(i)w^)i=1mSigmoid(x^(i)w^) = x1(1),x1(2),...,x1(m)x2(1),x2(2),...,x2(m)...xm(1),xm(2),...,xm(m)1,1,...,1,1 Sigmoid(x^(1)w^)Sigmoid(x^(2)w^)...Sigmoid(x^(m)w^) = x1(1),x2(1),...,xn(1),1x1(2),x2(2),...,xn(2),1...x1(m),x2(m),...,xn(m),1 TSigmoid( x1(1),x2(1),...,xn(1),1x1(2),x2(2),...,xn(2),1...x1(m),x2(m),...,xn(m),1 w1w2...wnb )=X^TSigmoid(X^w^)
  • 同时,后半部分可简化为:

[ ∑ i = 1 m x 1 ( i ) y ( i ) ∑ i = 1 m x 2 ( i ) y ( i ) . . . ∑ i = 1 m x n ( i ) y ( i ) ∑ i = 1 m y ( i ) ] = [ x 1 ( 1 ) , x 2 ( 1 ) , . . . , x n ( 1 ) , 1 x 1 ( 2 ) , x 2 ( 2 ) , . . . , x n ( 2 ) , 1 . . . x 1 ( m ) , x 2 ( m ) , . . . , x n ( m ) , 1 ] T ⋅ [ y ( 1 ) y ( 2 ) . . . y ( m ) ] = X ^ T ⋅ y \begin{aligned} \left [\begin{array}{cccc} \sum^m_{i=1}x_1^{(i)}y^{(i)} \\ \sum^m_{i=1}x_2^{(i)}y^{(i)} \\ . \\ . \\ . \\ \sum^m_{i=1}x_n^{(i)}y^{(i)} \\ \sum^m_{i=1}y^{(i)} \\ \end{array}\right] = \left [\begin{array}{cccc} x^{(1)}_1, x^{(1)}_2, ... ,x^{(1)}_n, 1 \\ x^{(2)}_1, x^{(2)}_2, ... ,x^{(2)}_n, 1 \\ . \\ . \\ . \\ x^{(m)}_1, x^{(m)}_2, ... ,x^{(m)}_n, 1 \\ \end{array}\right]^T \cdot \left [\begin{array}{cccc} y^{(1)} \\ y^{(2)} \\ . \\ . \\ . \\ y^{(m)} \\ \end{array}\right]=\hat X^T \cdot y \end{aligned} i=1mx1(i)y(i)i=1mx2(i)y(i)...i=1mxn(i)y(i)i=1my(i) = x1(1),x2(1),...,xn(1),1x1(2),x2(2),...,xn(2),1...x1(m),x2(m),...,xn(m),1 T y(1)y(2)...y(m) =X^Ty

  • 因此,逻辑回归损失函数的梯度计算公式为:
    ∇ w ^ B C E ( w ^ ) = 1 m X ^ T ( S i g m o i d ( X ^ ⋅ w ^ ) − y ) \nabla \hat w BCE(\hat w)=\frac{1}{m}\hat X^T(Sigmoid(\hat X \cdot \hat w) - y) w^BCE(w^)=m1X^T(Sigmoid(X^w^)y)
  • 并且,据此我们可以定义逻辑回归的梯度计算公式:
def logit_gd(X, w, y):
    """
    逻辑回归梯度计算公式
    """
    m = X.shape[0]
    grad = X.T.dot(sigmoid(X.dot(w)) - y) / m
    return grad
  • 其实,无论是理论推导还是手动实现,对于机器学习的建模流程来说,只要我们采用梯度下降方法进行参数求解,就需要有以下环节:
  • 根据算法原理构建损失函数——根据损失函数构建梯度更新表达式——根据梯度表达式进行参数更新。
  • 在这个过程中,损失函数的构建和梯度表达式的计算其实都是涉及理论部分内容相对较深而实践过程高度重复的,即一旦我们推导出逻辑回归模型的梯度计算公式,在未来长期建模过程中该式都无须更改甚至无须关注。

二、创建分类数据集生成器

1. 手动创建分类数据

  • 为了方便展开逻辑回归和后续分类模型的性能探讨,我们考虑通过创建可自定义分类难度的分类数据生成器来辅助后续实验的开展。
  • 在此前的回归数据生成器定义的过程中,我们曾介绍,对于回归数据集来说,越是服从高阶多项式回归规律、或者干扰项越大,建模难度就越高。
  • 而对于分类问题来说,有一种非常简单的方式去划分数据集的分类难度,那就是:在样本空间(特征空间)中观察不同类别的样本分布,各类别样本点重合度越高、边界约不明显,则分类难度越高。
  • 例如我们通过创建各列都服从高斯分布的分类数据集如下:
# 创建初始标记值
num_inputs = 2               # 数据集特征
num_examples = 500           # 每一类样本数

np.random.seed(24)
data0 = np.random.normal(4, 2, size=(num_examples, num_inputs))
data1 = np.random.normal(-2, 2, size=(num_examples, num_inputs))

data0[:10]
#array([[6.65842435, 2.4599331 ],
#       [3.36743928, 2.01837923],
#       [1.85836749, 1.12257344],
#       [5.1288337 , 4.59144378],
#       [0.74719153, 4.4391304 ],
#       [5.3576096 , 7.77854546],
#       [5.9230768 , 4.20802239],
#       [3.03766937, 5.70045706],
#       [6.90684933, 6.11547487],
#       [4.33112321, 5.03003676]])
  • 值得注意的是,对于 random.normal 函数来说,参数 size 决定接下来数组的基本形状,而在创建任意维度数组时,在均值参数和标准差参数都是标量的情况下,实际上均值和方差约束的是所有数据。
data0.mean()
#4.027189146259833

data0.std()
#2.026404030444323
  • 当然,对于其中的每一列数据来说,其实都是从整体数据中抽样而来,因此各列也都服从均值为 4、标准差为 2 的正态分布。
data0.mean(0)
#array([3.90407794, 4.15030035])

data0.std(0)
#array([2.05961619, 1.98501764])
  • 另外,如果希望对二维数组中的每一列进行均值和标准差进行单独设置,则可以将均值参数和标准差参数也设置为 array_like 的参数。
a = np.random.normal([0, 1], [1, 2], size=(num_examples, num_inputs))
a[:10]
#array([[ 0.96604603,  4.53246719],
#       [ 0.57852053, -1.68056849],
#       [-0.54625943,  2.08126231],
#       [ 1.17934488, -3.87849635],
#       [-0.56712052,  1.31693223],
#       [ 0.23258861,  4.09162486],
#       [ 1.0491485 , -0.24942084],
#       [-0.03904771,  0.77754885],
#       [-1.15375907, -2.14538106],
#       [ 0.40636534,  3.74512035]])

a.mean(0)
#array([-0.04871211,  0.9915323 ])

a.std(0)
#array([0.97824254, 1.90314288])
  • 同时,我们令 data0 的标签为 label0,data1 标签为 label1。
label0 = np.zeros(500)
label1 = np.ones(500)
  • 一般来说,用于表示分类数据集类别的数字可以自行定义,对于二分类问题,一般会用 0/1 来表示两个类别,更进一步的,我们往往会令有待重点识别的类为 1,其他类别为 0。
  • 最终我们将两类数据和两类数据对应的标签分别合并为 features 和 labels,即数据集的整体特征矩阵和标签数组。
features = np.concatenate((data0, data1), 0)
labels = np.concatenate((label0, label1), 0)
  • 由于数据集只包含两个特征,因此我们可以通过绘制样本空间的样本点的分布来进行观察,其中每个点代表每一条样本,而每个点的不同着色则代表不同类别。
plt.scatter(features[:, 0], features[:, 1], c = labels)

在这里插入图片描述

  • 而对于上述两个类别来说,很明显,第一类(data0,列均值为 4)位于样本空间的右上角,而第二类(data0,列均值为 -2)则位于样本空间的左下角。
  • 我们可以从另一个角度来理解两类数据的分布,首先,均值实际上也就代表着样本分布的中心,因此第一类数据的中心点为 (4,4),而第二类的数据的中心点为 (-2,-2)。
  • 此外,方差(及标准差)其实表示数据的离散程度,方差越大、数据距离中心点的离散程度就越大。
a1 = np.random.normal(0, 1, size=(100, 2))
a2 = np.random.normal(0, 5, size=(100, 2))
plt.scatter(a1[:, 0], a1[:, 1])
plt.scatter(a2[:, 0], a2[:, 1])

在这里插入图片描述

  • 因此,对于上述创建数据集过程而言,如果我们缩短中心点之间的距离、增加每一个点簇的方差,则能够进一步加深两个点簇彼此交错的情况。
np.random.seed(24)
data0 = np.random.normal(2, 4, size=(num_examples, num_inputs))
data1 = np.random.normal(-2, 4, size=(num_examples, num_inputs))
​
features = np.concatenate((data0, data1), 0)
labels = np.concatenate((label0, label1), 0)
​
plt.scatter(features[:, 0], features[:, 1], c = labels)

在这里插入图片描述

  • 能够看出,此时两个点簇(两类数据)在样本空间中的边界非常模糊。
  • 反之,如果提高中心点间距、减少每个点簇方差,则会减少各点簇彼此交错的情况,并且能够呈现出一条更加清晰的边界。
data0 = np.random.normal(5, 2, size=(num_examples, num_inputs))
data1 = np.random.normal(-5, 2, size=(num_examples, num_inputs))

features = np.concatenate((data0, data1), 0)
labels = np.concatenate((label0, label1), 0)

plt.scatter(features[:, 0], features[:, 1], c = labels)

在这里插入图片描述

  • 对于上述分布的分类数据集来说,我们甚至能够清楚的在样本空间中找出一条线性边界对两类数据进行切分。而对于分类数据来说,边界越清晰,彼此交错的情况越少、则分类越简单。

2. 创建分类数据生成器

  • 在有了上述基础只是铺垫之后,接下来我们考虑创建分类数据生成器函数,并且拥有以下两点额外功能:能够创建多分类数据集,并且可以通过参数手动调整数据分类难度。
def arrayGenCla(num_examples = 500, num_inputs = 2, num_class = 3, deg_dispersion = [4, 2], bias = False):
    """分类数据集创建函数。
    
    :param num_examples: 每个类别的数据数量
    :param num_inputs: 数据集特征数量
    :param num_class:数据集标签类别总数
    :param deg_dispersion:数据分布离散程度参数,需要输入一个列表,其中第一个参数表示每个类别数组均值的参考、第二个参数表示随机数组标准差。
    :param bias:建立模型逻辑回归模型时是否带入截距,为True时将添加一列取值全为1的列
    :return: 生成的特征张量和标签张量,其中特征张量是浮点型二维数组,标签张量是长正型二维数组。
    """
    
    cluster_l = np.empty([num_examples, 1])                            # 每一类标签数组的形状
    mean_ = deg_dispersion[0]                                        # 每一类特征数组的均值的参考值
    std_ = deg_dispersion[1]                                         # 每一类特征数组的方差
    lf = []                                                          # 用于存储每一类特征的列表容器
    ll = []                                                          # 用于存储每一类标签的列表容器
    k = mean_ * (num_class-1) / 2                                    # 每一类特征均值的惩罚因子
    
    for i in range(num_class):
        data_temp = np.random.normal(i*mean_-k, std_, size=(num_examples, num_inputs))     # 生成每一类特征
        lf.append(data_temp)                                                               # 将每一类特征添加到lf中
        labels_temp = np.full_like(cluster_l, i)                                           # 生成某一类的标签
        ll.append(labels_temp)                                                             # 将每一类标签添加到ll中
        
    features = np.concatenate(lf)
    labels = np.concatenate(ll)
    
    if bias == True:
        features = np.concatenate((features, np.ones(labels.shape)), 1)    # 在特征张量中添加一列全是1的列
    return features, labels
  • 其中,需要注意的是关于离散程度参数的理解和使用。
  • 参数 deg_dispersion 是一个包含两个元素的 list,其中第二个元素为每一类数据的方差,而第一个元素则是每一类数据的均值的一个参考值(mean_),而最终每一类数据的均值则由 mean_、总类别数 num_class 和当前类别标签三者共同决定。
  • 这里如此进行设计的一个重要原因是,我们希望无论最终创建几个类别的数据,最终各类中心点的均值基本是围绕原点分布的。
  • 即假设总共创建两个类别的数据,并且假设 mean_ 取值为 2,i 为每一类的编号 (0,1),则相比采用 i*mean_ 创建中心点分别为 (0, 0)、(2, 2) 的两个簇,我们更希望创建中心点为 (-1, -1)、(1, 1) 为中心点的两个簇。
  • 为了满足该要求,我们需要在原始 i*mean_ 的基础上进行修改。
  • 这里我们创建了一个惩罚因子k=mean_ * (num_class-1) / 2,用于修正 i*mean_。
  • 在进行每个簇中心点计算时,计算公式为 imean_-k,当总类别为 2、mean_=2 时,每个中心点计算公式为 i2_-1,i 为 0 时,第一类中心点计算结果为 (-1,-1),当 i 为 1 时,第二类中心点计算结果为 (1, 1),能够满足上述中心点围绕原点分布的基本要求。
  • 接下来,测试上述函数性能。
# 设置随机数种子
np.random.seed(24)

# 创建数据
f, l = arrayGenCla(num_class = 2, deg_dispersion = [6, 2], bias = True)          # 离散程度较小

# 绘图展示
plt.scatter(f[:, 0], f[:, 1], c = l)

在这里插入图片描述

# 设置随机数种子
np.random.seed(24)

# 创建数据
f1, l1 = arrayGenCla(num_class = 2, deg_dispersion = [6, 4], bias = True)        # 离散程度较大

# 绘制图像查看
plt.scatter(f1[:, 0], f1[:, 1], c = l1)

在这里插入图片描述

f[:10]
#array([[-0.34157565, -4.5400669 ,  1.        ],
#       [-3.63256072, -4.98162077,  1.        ],
#       [-5.14163251, -5.87742656,  1.        ],
#       [-1.8711663 , -2.40855622,  1.        ],
#       [-6.25280847, -2.5608696 ,  1.        ],
#       [-1.6423904 ,  0.77854546,  1.        ],
#       [-1.0769232 , -2.79197761,  1.        ],
#       [-3.96233063, -1.29954294,  1.        ],
#       [-0.09315067, -0.88452513,  1.        ],
#       [-2.66887679, -1.96996324,  1.        ]])
  • 至此,分类数据生成器创建完毕。
  • 需要说明的是,由于目前各列仍然是服从高斯分布,因此上述数据生成器在每一个特征的基本分布上保有较好的数据特性。

三、逻辑回归的手动实现

  • 接下来,尝试进行逻辑回归的手动实现。其基本建模流程依然遵照机器学习的一般流程,可以通过如下方式来执行。
  • 数据准备
  • 此处直接利用上述数据f和l进行模型模型训练,在无其他特殊要求情况下,我们对数据集进行训练集和测试集的切分,并进行数据归一化处理。
# 设置随机数种子
np.random.seed(24)# 数据切分
Xtrain, Xtest, ytrain, ytest = array_split(f, l)

Xtrain
#array([[-1.8548796 , -2.70483481,  1.        ],
#       [-1.87637683, -2.41926232,  1.        ],
#       [ 1.94989069,  4.23222036,  1.        ],
#       ...,
#       [ 2.87532044,  0.3972172 ,  1.        ],
#       [ 3.40818137,  3.98428395,  1.        ],
#       [-0.25339125, -8.61214233,  1.        ]])

Xtest[:10]
#array([[ 3.89897105,  4.36406577,  1.        ],
#       [-1.34183767, -5.4001881 ,  1.        ],
#       [ 2.51468003,  2.5656945 ,  1.        ],
#       [ 6.44964778,  2.68891653,  1.        ],
#       [ 2.09992517,  1.45376719,  1.        ],
#       [ 3.29152071, -1.84196332,  1.        ],
#       [-0.30325369, -4.82751704,  1.        ],
#       [ 3.6890838 ,  3.12407552,  1.        ],
#       [ 2.18449546,  2.28340341,  1.        ],
#       [ 2.26674673,  1.52043045,  1.        ]])
  • 数据归一化过程。
mean_ = Xtrain[:, :-1].mean(axis=0)
std_ = Xtrain[:, :-1].std(axis=0)
​
Xtrain[:, :-1] = (Xtrain[:, :-1] - mean_) / std_
Xtest[:, :-1] = (Xtest[:, :-1] - mean_) / std_

Xtrain
#array([[-0.54046519, -0.81824522,  1.        ],
#       [-0.54654998, -0.73750376,  1.        ],
#       [ 0.53647537,  1.14310619,  1.        ],
#       ...,
#       [ 0.79841834,  0.05881485,  1.        ],
#       [ 0.94924468,  1.07300578,  1.        ],
#       [-0.08716377, -2.48845047,  1.        ]])

Xtest[:10]
#array([[ 1.08816274,  1.18038356,  1.        ],
#       [-0.39524863, -1.58031715,  1.        ],
#       [ 0.69633905,  0.67192025,  1.        ],
#       [ 1.81013201,  0.70675949,  1.        ],
#       [ 0.57894265,  0.35753899,  1.        ],
#       [ 0.91622387, -0.57428086,  1.        ],
#       [-0.10127734, -1.41840274,  1.        ],
#       [ 1.02875413,  0.82979436,  1.        ],
#       [ 0.60288027,  0.59210656,  1.        ],
#       [ 0.6261615 ,  0.37638705,  1.        ]])
  • 初始化参数与核心参数设置
  • 接下来定义参数初始值及核心参数。
# 设置随机数种子
np.random.seed(24)# 参数初始值
n = f.shape[1]
w = np.random.randn(n, 1)# 核心参数
batch_size = 50
num_epoch = 200
lr_init = 0.2

w
#array([[ 1.32921217],
#       [-0.77003345],
#       [-0.31628036]])
  • 并且,由于要执行学习率调度,因此需要定义匿名函数。
lr_lambda = lambda epoch: 0.95 ** epoch
  • 模型训练
  • 然后,带入训练数据进行模型训练。
for i in range(num_epoch):
    w = sgd_cal(Xtrain, w, ytrain, logit_gd, batch_size=batch_size, epoch=1, lr=lr_init*lr_lambda(i))

w
#array([[3.1894049 ],
#       [2.30553244],
#       [0.1691708 ]])
  • 模型结果测试
  • 模型训练完成后,测试模型效果。首先,对于逻辑回归来说,在给出线性方程参数后,模型输出的概率结果为:
yhat = sigmoid(Xtrain.dot(w))
yhat[:10]
#array([[0.03103592],
#       [0.03646214],
#       [0.9891821 ],
#       [0.08059581],
#       [0.0043602 ],
#       [0.05225521],
#       [0.96094581],
#       [0.00100699],
#       [0.60388201],
#       [0.99752817]])
  • 然后我们利用 logit_cla 函数将其转化为分类结果。
logit_cla(yhat, thr=0.5)[:10]
#array([[0.],
#       [0.],
#       [1.],
#       [0.],
#       [0.],
#       [0.],
#       [1.],
#       [0.],
#       [1.],
#       [1.]])
  • 至此我们即得出了模型在训练数据集上的预测结果。
  • 当然,我们还可以利用其与训练集的真是标签进行比较,来计算模型在训练集上的预测准确率。
(logit_cla(yhat, thr=0.5) == ytrain).mean()
#0.9871428571428571
  • 注意,此处我们借助了隐式转化,将布尔型对象转化为浮点型对象。当然,我们也可以将上述准确率计算过程封装为一个函数
def logit_acc(X, w, y, thr=0.5):
    yhat = sigmoid(X.dot(w))
    y_cal = logit_cla(yhat, thr=thr)
    return (y_cal == y).mean()

# 训练集准确率
logit_acc(Xtrain, w, ytrain, thr=0.5)
#0.9871428571428571

# 测试集准确率
logit_acc(Xtest, w, ytest, thr=0.5)
#0.98
  • 至此,我们即完成了整个逻辑回归手动建模过程。
  • 模型准确率变化函数
  • 更进一步的,我们可以借助 Lesson 4.5 节中观察损失函数伴随迭代不断变化的过程中的代码思路,来观察伴随迭代过程模型准确率逐渐提升的过程。
# 设置随机数种子
np.random.seed(24)# 参数初始值
n = f.shape[1]
w = np.random.randn(n, 1)# 记录迭代过程模型准确率计算结果
train_acc = []
test_acc = []

w
#array([[ 1.32921217],
#       [-0.77003345],
#       [-0.31628036]])

for i in range(num_epoch):
    w = sgd_cal(Xtrain, w, ytrain, logit_gd, batch_size=batch_size, epoch=1, lr=lr_init*lr_lambda(i))
    train_acc.append(logit_acc(Xtrain, w, ytrain, thr=0.5))
    test_acc.append(logit_acc(Xtest, w, ytest, thr=0.5))

# 观察计算结果
plt.plot(list(range(num_epoch)), np.array(train_acc).flatten(), label='train_acc')
plt.plot(list(range(num_epoch)), np.array(test_acc).flatten(), label='test_acc')
plt.xlabel('epochs')
plt.ylabel('Accuracy')
plt.legend(loc = 4)

在这里插入图片描述

  • 能够发现,由于数据集本身性质较好,即数据集中各类别边界较为明显,因此整体迭代收敛速度较快,并且模型最终准确率较高。

四、逻辑回归的分类性能瓶颈与算法评价

  • 尽管上述建模结果较好,但是像上述数据集一般、不同类别数据边界如此明显的数据集却并不常见。
  • 一种更加常见的情况是不同类别的数据集彼此之间存在一定的交叉、数据集之间的边界并不明显。因此,我们尝试带入更加复杂的数据集,即带入 f1、l1 带入进行建模,来测试逻辑回归模型性能。
# 数据分布
plt.scatter(f1[:, 0], f1[:, 1], c = l1)

在这里插入图片描述

# 设置随机数种子
np.random.seed(24)   

# 数据切分与归一化
Xtrain, Xtest, ytrain, ytest = array_split(f1, l1)

mean_ = Xtrain[:, :-1].mean(axis=0)
std_ = Xtrain[:, :-1].std(axis=0)

Xtrain[:, :-1] = (Xtrain[:, :-1] - mean_) / std_
Xtest[:, :-1] = (Xtest[:, :-1] - mean_) / std_

Xtrain
#array([[-0.13869677, -0.53505829,  1.        ],
#       [-0.14755539, -0.41940515,  1.        ],
#       [ 0.19293434,  1.05939775,  1.        ],
#       ...,
#       [ 0.574287  , -0.49372856,  1.        ],
#       [ 0.79386924,  0.95898674,  1.        ],
#       [ 0.52124727, -2.92744086,  1.        ]])

# 设置随机数种子
np.random.seed(24)  

# 核心参数
batch_size = 50
num_epoch = 200
lr_init = 0.2

# 参数初始值
n = f1.shape[1]
w = np.random.randn(n, 1)

# 记录迭代过程模型准确率计算结果
train_acc = []
test_acc = []

w
#array([[ 1.32921217],
#       [-0.77003345],
#       [-0.31628036]])

for i in range(num_epoch):
    w = sgd_cal(Xtrain, w, ytrain, logit_gd, batch_size=batch_size, epoch=1, lr=lr_init*lr_lambda(i))
    train_acc.append(logit_acc(Xtrain, w, ytrain, thr=0.5))
    test_acc.append(logit_acc(Xtest, w, ytest, thr=0.5))

# 观察计算结果
plt.plot(list(range(num_epoch)), np.array(train_acc).flatten(), label='train_acc')
plt.plot(list(range(num_epoch)), np.array(test_acc).flatten(), label='test_acc')
plt.xlabel('epochs')
plt.ylabel('Accuracy')
plt.legend(loc = 4)

在这里插入图片描述

  • 能够看出,模型准确率大幅下降。
  • 当然,随着数据集中不同簇彼此交错情况越多、边界越模糊,最终模型准确率还将进一步下降。
  • 我们将发现对于逻辑回归模型来说,对于二维样本空间的数据来说,只能构建线性决策边界来对数据类别进行划分,因此如果我们单独只看模型对于复杂数据的分类判别能力,那么在所有分类模型中,逻辑回归其实并不属于性能最强的第一梯队。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/192189.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Microsoft Office( Word、Excel、PowerPoint)的安装

1 下载Office Tool Plus 官网地址:点击下载 2 点击Office Tool Plus.exe 3 接受软件许可条款和Microsoft服务协议 4 点击安装Office 5 选择Office 专业增强版 2019 6 确认安装Office 专业增强版 2019 7 关闭安全中心警报 8 等待安装完成 9 点击关闭 10 点击返回…

VMware创建Ubuntu系统

Ubuntu镜像可以去各个镜像网站下载 官网:https://ubuntu.com/ 中文官网:https://cn.ubuntu.com/ 镜像下载 使用Ubuntu在开发的时候下载LTS版本,也可以下载其他版本以及历史版本 这里面使用的镜像是ubuntu-16.04.7-desktop-amd64.iso版本&a…

1月书单 2月新书预告 | 百万册畅销书全新续作来啦

幸福而忙碌的 1 月一溜烟就跑走了,我们已经来到 2 月的第 1 天。最近,公司楼下售卖咖啡的地方,总是排着长长的队……想必不少小伙伴还在找状态。 图灵君先带大家回忆一下 1 月我们上新了哪些书。 01 《这才是服务设计》 作者:[德…

章节9 软件安装

9-Linux软件安装 01 软件为什么需要安装 Windows软件安装流程 安装检查释放文件复制可执行文件DLL动态链接库/安装服务注册表开始菜单和快捷方式 Windows安装文件 Windows可执行文件 Linux可执行程序 /bin /sbin /usr/bin /usr/sbin 02 脚本和程序的区别 脚本和程序的…

录屏可以录声音吗?录制带声音的视频,这个方法轻松解决

​很多小伙伴对电脑屏幕进行录制的时候,却发现自己辛苦录制的视频没有录到声音。录屏可以录声音吗?当然可以!只需要使用录屏软件进行录制就可以解决。还不知道录屏可以录声音的小伙伴快来看看吧。 一、录屏可以录声音吗? 很多小伙…

MySQL-如何分库分表?一看就懂

一、为什么要分库分表 如果一个网站业务快速发展,那这个网站流量也会增加,数据的压力也会随之而来,比如电商系统来说双十一大促对订单数据压力很大,Tps十几万并发量,如果传统的架构(一主多从)&…

环境监测看板系统能轻松掌握周边环境的数据

在工业、医疗、养殖和生活等许多地方,温湿度是一个重要的测量参数。季节的变化对环境温湿度影响很大,不同的产品和场地对温湿度都有要求,温湿度环境会影响到产品的性能及寿命,加速产品的老化。以前传统的温湿度检测模式是以人为基…

Pyside2项目实战,从0开始写一个GUI可视化项目:总览

前言 最近使用 Pyside2 编写了几个GUI工具,发现效果出奇的好。遂产生了分享它的念头。 接下来如果不出意外,大概没有意外,我会开始写这个专栏,介绍从零开始去编写一个实用的GUI工具。 这是Pyside2第一篇:《总览》 本文…

数学建模学习笔记(18):三维图形的绘制

三维图形的绘制生成绘图所需的横纵坐标三维网格图的绘制基本语法实战案例两种变形函数三维曲面图的绘制两种变形函数绘图效果的优化其他补充内容生成绘图所需的横纵坐标 [X,Y]meshgrid(xgv,ygv) % 函数解释:X和Y是用于接收返回值的变量,是两个大小相等的…

Python的基础语法

字面量 字面量是在代码中,被写下来的固定的值,称之为字面量 常见的字面量类型 字符串又称为文本是由任意数量的字符如中文、英文、各类符号、数字组成。所以叫做字符串常量。 注释 注释 在程序代码中对程序代码进行解释说明的文字 作用 注释不是程…

Java中XML如何转为BEAN

在网络通讯中&#xff0c;对方传的数据经常是XML格式包装的数据集合。在Java开发中&#xff0c;我们如何将XML转成Java实体类呢&#xff1f; 对方发送的报文 <ns2:response xmlns:ns2"http://service.zxl.cn/test/xml"><school><location>长江边…

Pointnet Tutorial

目录 .1 intro-点云特性 1.1 点云特性 1.2 motivation 1.3 pointnet的处理 .2 Pointnet 2.1 contribution 2.2 solution 2.3 backbone 2.4 代码部分 2.5 小结 .4 应用 References .1 intro-点云特性 Q1&#xff1a;什么是点云&#xff1f; 简单来说就是一堆三维点的集…

【异常】前端提示 Support for the experimental syntax ‘jsx‘ isn‘t currently enabled

一、报错内容 17:33:41 - Building for production... 17:34:13 ERROR Failed to compile with 5 errors5:34:09 PM 17:34:13 17:34:13 error in ./src/layout/components/Sidebar/Item.vue?vue&typescript&langjs& 17:34:13 17:34:13 Syntax Error…

万应低代码1月重点更新内容速递

速览版 详情版 一、低代码开发能力提升 01 工作流 &#xff08;1&#xff09; 优化流程推演 在流程发起时&#xff0c;对流程各节点审批人、抄送人进行实时推演。 【使用场景】 ● 以“请假”场景为例&#xff0c;A 角色的固定审批人为“甲、乙、丙” 3 人&#xff0c;在某…

windows10操作系统 显卡MX150 安装CUDA+cuDNN+pytorch

本人水平有限&#xff0c;如有问题请多指正笔记本系统&#xff1a;Windows 10 64位显卡&#xff1a;NVIDIA GeForce MX150显卡驱动程序版本&#xff1a;512.78显卡驱动程序支持CUDA版本&#xff1a;11.6.134及以下安装CUDA&#xff1a;11.6.0安装cuDNN&#xff1a;8.7.0一、安装…

路由器刷机解决学校无线上网问题(小米4A千兆版)

一、准备工作 1、路由器正常联网状态&#xff0c;&#xff08;正常联网状态指的是电脑能够通过路由器上网&#xff09; 2、电脑网线插入路由lan口&#xff0c;IP地址设置为自动获取 3、启动window10的Telent功能 控制面板 -> 程序和功能 -> 左侧“启动或关闭Windows功能…

FormArray在动态表格中的应用

如下图&#xff0c;是这次想要实现的功能。 一个表格行&#xff0c;点击新增按钮就增加一行&#xff0c;点击后面的删除按钮就可以删除对应的行&#xff0c;其中有部分字段需要添加非空校验。要想实现这个功能&#xff0c;需要应用到FormArray的知识。 步骤: 1. 声明一个Form…

打开IE浏览器自动跳转到Edge的解决方法

场景Windows 10中&#xff0c;打开IE浏览器自动跳转到Edge&#xff0c;但是某些网站还是只能在IE中正常访问&#xff0c;Edge访问会出问题。解决方案Edge中点击右上角的三个点&#xff0c;然后点击设置在“默认浏览器”中&#xff0c;修改“让 Internet Explorer 在 Microsoft …

Linux-文件和目录常用命令

1.查看目录内容1.1 终端使用技巧自动补全在敲出 文件/目录/命令的前几个字母之后&#xff0c;按下tab键如果输入的没有歧义&#xff0c;系统会自动补全如果还存在其他文件/目录/命令,再按一下tab键,系统会提示可能存在的命令曾经使用过的命令按上/下光标键可以再曾经使用过的命…

GeoServer系列-安装SqlServer插件

GeoServer 安装包中是不含sqlserver插件的&#xff0c;所以无法创建sqlserver的数据存储&#xff0c;要想支持发布sqlserver的空间表到geoserver就需要添加插件 1&#xff0c;下载插件 官网下载地址&#xff1a;https://geoserver.org/download/ 根据自己的版本下载指定的插件…