西瓜书 3. 线性模型

数学理论

线性回归

  • 线性回归: y = w T x + b y=w^Tx+b y=wTx+b
  • 对数线性回归: ln ⁡ y = w T x + b \ln{y}=w^Tx+b lny=wTx+b
  • 广义线性模型: y = g − 1 ( w T x + b ) y=g^{-1}(w^Tx+b) y=g1(wTx+b)
    • g ( ⋅ ) g(\cdot) g() :联系函数,必须单调可微

对数几率回归

  • 线性回归模型产生的预测值: z = w T x + b z=w^Tx+b z=wTx+b
  • 对数几率函数: y = 1 1 + e − z y=\frac{1}{1+e^{-z}} y=1+ez1
  • 将对数几率函数作为 g − 1 ( ⋅ ) g^{-1}(\cdot) g1()代入广义线性模型: y = 1 1 + e − ( w T x + b ) y=\frac{1}{1+e^{-(w^Tx+b)}} y=1+e(wTx+b)1
  • 等价变换: ln ⁡ y 1 − y = w T x + b \ln{\frac{y}{1-y}}=w^Tx+b ln1yy=wTx+b
    • y y y 视为样本 x x x 作为正例的可能性,则 1 − y 1-y 1y 是其反例可能性
    • 两者的比例 y 1 − y \frac{y}{1-y} 1yy 即为几率
    • ln ⁡ y 1 − y \ln{\frac{y}{1-y}} ln1yy 即为对数几率
  • 等价变换: ln ⁡ p ( y = 1 ∣ x ) p ( y = 0 ∣ x ) = w T x + b \ln{\frac{p(y=1|x)}{p(y=0|x)}}=w^Tx+b lnp(y=0∣x)p(y=1∣x)=wTx+b
    • p ( y = 1 ∣ x ) = e w T x + b 1 + e w T x + b p(y=1|x)=\frac{e^{w^Tx+b}}{1+e^{w^Tx+b}} p(y=1∣x)=1+ewTx+bewTx+b
    • p ( y = 0 ∣ x ) = 1 1 + e w T x + b p(y=0|x)=\frac{1}{1+e^{w^Tx+b}} p(y=0∣x)=1+ewTx+b1
  • 最大似然估计
    • 最大化对数似然函数: l ( w , b ) = ∑ i = 1 m ln ⁡ p ( y i ∣ x i ; w , b ) l(w,b)=\sum\limits_{i=1}^m\ln{p(y_i|x_i;w,b)} l(w,b)=i=1mlnp(yixi;w,b)
      • 令 β = ( w ; b ) , x ^ = ( x ; 1 ) , 则 w T x + b = β T x ^ 令\beta=(w;b),\hat{x}=(x;1),则w^Tx+b=\beta^T\hat{x} β=(w;b),x^=(x;1),wTx+b=βTx^
      • 令 p 1 ( x ^ ; β ) = p ( y = 1 ∣ x ^ ; β ) 令p_1(\hat{x};\beta)=p(y=1|\hat{x};\beta) p1(x^;β)=p(y=1∣x^;β)
      • 令 p 0 ( x ^ ; β ) = p ( y = 0 ∣ x ^ ; β ) = 1 − p 1 ( x ^ ; β ) 令p_0(\hat{x};\beta)=p(y=0|\hat{x};\beta)=1-p_1(\hat{x};\beta) p0(x^;β)=p(y=0∣x^;β)=1p1(x^;β)
    • 等价变换: p ( y i ∣ x i ; w , b ) = y i p 1 ( x ^ i ; β ) + ( 1 − y i ) p 0 ( x ^ i ; β ) {p(y_i|x_i;w,b)}=y_ip_1(\hat{x}_i;\beta)+(1-y_i)p_0(\hat{x}_i;\beta) p(yixi;w,b)=yip1(x^i;β)+(1yi)p0(x^i;β)
    • 等价于最小化凸函数: l ( β ) = ∑ i = 1 m [ − y i β T x ^ i + ln ⁡ ( 1 + e β T x ^ i ) ] l(\beta)=\sum\limits_{i=1}^m[-y_i\beta^T\hat{x}_i+\ln{(1+e^{\beta^T\hat{x}_i})}] l(β)=i=1m[yiβTx^i+ln(1+eβTx^i)]
    • 用梯度下降法或牛顿法可求出: β ∗ = arg min ⁡ β l ( β ) \beta^*=\argmin\limits_{\beta}l(\beta) β=βargminl(β)

线性判别分析LDA(二分类)

  • 给定训练样例集,设法将样例投影到一条直线上, 使得同类样例的投影点尽可能接近、异类样例的投影点尽可能远离;在对新样 本进行分类时,将其投影到同样的这条直线上,再根据投影点的位置来确定新样本的类别
    LDA
  • 符号设定
    • 数据集: D = { ( x i , y i ) } i = 1 m , y i ∈ { 0 , 1 } D=\{(x_i,y_i)\}_{i=1}^m,y_i\in\{0,1\} D={(xi,yi)}i=1m,yi{0,1}
    • i ∈ { 0 , 1 } i\in\{0,1\} i{0,1}类示例的集合: X i X_i Xi
    • i ∈ { 0 , 1 } i\in\{0,1\} i{0,1}类示例的均值向量: μ i \mu_i μi
    • i ∈ { 0 , 1 } i\in\{0,1\} i{0,1}类示例的协方差矩阵: Σ i \Sigma_i Σi
  • 投影到直线 w w w
    • 两类样本中心的投影: w T μ 0 , w T μ 1 w^T\mu_0,w^T\mu_1 wTμ0,wTμ1
    • 两类样本所有投影的协方差: w T Σ 0 w , w T Σ 1 w w^T\Sigma_0w,w^T\Sigma_1w wTΣ0w,wTΣ1w
    • 类内散度矩阵: S w = Σ 0 + Σ 1 = ∑ x ∈ X 0 ( x − μ 0 ) ( x − μ 0 ) T + ∑ x ∈ X 1 ( x − μ 1 ) ( x − μ 1 ) T S_w=\Sigma_0+\Sigma_1=\sum\limits_{x\in X_0}(x-\mu_0)(x-\mu_0)^T+\sum\limits_{x\in X_1}(x-\mu_1)(x-\mu_1)^T Sw=Σ0+Σ1=xX0(xμ0)(xμ0)T+xX1(xμ1)(xμ1)T
    • 类间散度矩阵: S b = ( μ 0 − μ 1 ) ( μ 0 − μ 1 ) T S_b=(\mu_0-\mu_1)(\mu_0-\mu_1)^T Sb=(μ0μ1)(μ0μ1)T
  • 目标函数
    • 同类样例的投影点尽可能接近: w T Σ 0 w + w T Σ 1 w w^T\Sigma_0w+w^T\Sigma_1w wTΣ0w+wTΣ1w尽可能小
    • 异类样例的投影点尽可能远离: ∥ w T μ 0 − w T μ 1 ∥ 2 2 \Vert w^T\mu_0-w^T\mu_1\Vert_2^2 wTμ0wTμ122尽可能大
    • 同时考虑两者:最大化广义瑞利商
      • max ⁡ w J = max ⁡ w ∥ w T μ 0 − w T μ 1 ∥ 2 2 w T Σ 0 w + w T Σ 1 w = max ⁡ w w T ( μ 0 − μ 1 ) ( μ 0 − μ 1 ) T w w T ( Σ 0 + Σ 1 ) w = max ⁡ w w T S b w w T S w w \max\limits_{w}J=\max\limits_{w}\frac{\Vert w^T\mu_0-w^T\mu_1\Vert_2^2}{w^T\Sigma_0w+w^T\Sigma_1w}=\max\limits_{w}\frac{w^T(\mu_0-\mu_1)(\mu_0-\mu_1)^Tw}{w^T(\Sigma_0+\Sigma_1)w}=\max\limits_{w}\frac{w^TS_bw}{w^TS_ww} wmaxJ=wmaxwTΣ0w+wTΣ1wwTμ0wTμ122=wmaxwT(Σ0+Σ1)wwT(μ0μ1)(μ0μ1)Tw=wmaxwTSwwwTSbw
    • 等价于 { min ⁡ w − w T S b w s . t . w T S w w = 1 \left\{ \begin{aligned} \min\limits_{w}-w^TS_bw \\ s.t. \quad w^TS_ww=1 \end{aligned} \right. wminwTSbws.t.wTSww=1
  • 拉格朗日乘子法
    • 拉格朗日函数: L ( w , λ ) = − w T S b w + λ ( w T S w w − 1 ) L(w, λ) = −w^TS_bw + λ(w^TS_ww − 1) L(w,λ)=wTSbw+λ(wTSww1)
    • 解得: S b w = λ S w w S_bw = λS_ww Sbw=λSww
      • 即: ( μ 0 − μ 1 ) ( μ 0 − μ 1 ) T w = λ S w w 即:(\mu_0-\mu_1)(\mu_0-\mu_1)^Tw=λS_ww 即:(μ0μ1)(μ0μ1)Tw=λSww
      • 即: w = ( μ 0 − μ 1 ) T w λ S w − 1 ( μ 0 − μ 1 ) 即:w=\frac{(\mu_0-\mu_1)^Tw}{λ}S_w^{-1}(\mu_0-\mu_1) 即:w=λ(μ0μ1)TwSw1(μ0μ1)
    • 由于广义瑞利商 J J J 的分子分母均为关于 w w w 的二次项,因此不关心 w w w 的大小,只关心其方向。通过调整 w w w 的大小使得标量 ( μ 0 − μ 1 ) T w = λ (\mu_0-\mu_1)^Tw = λ (μ0μ1)Tw=λ,得: w = S w − 1 ( μ 0 − μ 1 ) w=S_w^{-1}(\mu_0-\mu_1) w=Sw1(μ0μ1)
  • 奇异值分解
    • S w = U Σ V T S_w=U\Sigma V^T Sw=UΣVT
    • S w − 1 = U Σ − 1 V T S_w^{-1}=U\Sigma^{-1} V^T Sw1=UΣ1VT

多分类学习

将LDA推广到多分类任务

  • 符号设定
    • 假设存在 N N N 个类,其中第 i i i 类示例数为 m i m_i mi
    • 所有示例的均值向量: μ \mu μ
    • 类内散度矩阵: S w = ∑ i = 1 N S w i S_w=\sum\limits_{i=1}^NS_{w_i} Sw=i=1NSwi
      • S w i = ∑ x ∈ X i ( x i − μ i ) ( x i − μ i ) T S_{w_i}=\sum\limits_{x\in X_i}(x_i-\mu_i)(x_i-\mu_i)^T Swi=xXi(xiμi)(xiμi)T
    • 全局散度矩阵: S t = ∑ i = 1 m ( x i − μ ) ( x i − μ ) T S_t=\sum\limits_{i=1}^m(x_i-\mu)(x_i-\mu)^T St=i=1m(xiμ)(xiμ)T
    • 类间散度矩阵: S b = S t − S w = ∑ i = 1 N m i ( μ i − μ ) ( μ i − μ ) T S_b=S_t-S_w=\sum\limits_{i=1}^Nm_i(\mu_i-\mu)(\mu_i-\mu)^T Sb=StSw=i=1Nmi(μiμ)(μiμ)T
  • 目标函数
    • max ⁡ W ∑ i = 1 N − 1 w i T S b w i ∑ i = 1 N − 1 w i T S w w i = max ⁡ W t r ( W T S b W ) t r ( W T S w W ) \max\limits_{W}\frac{\sum\limits_{i=1}^{N-1} w_i^TS_bw_i}{\sum\limits_{i=1}^{N-1} w_i^TS_ww_i}=\max\limits_{W}\frac{tr(W^TS_bW)}{tr(W^TS_wW)} Wmaxi=1N1wiTSwwii=1N1wiTSbwi=Wmaxtr(WTSwW)tr(WTSbW)
      • 其中 W = ( w 1 , w 2 , . . . , w N − 1 ) ∈ R d × ( N − 1 ) W=(w_1,w_2,...,w_{N-1})\in\mathbb{R}^{d\times(N-1)} W=(w1,w2,...,wN1)Rd×(N1)
      • 等价于: { min ⁡ W − t r ( W T S b W ) s . t . t r ( W T S w W ) = 1 \left\{ \begin{aligned} \min\limits_{W}-tr(W^TS_bW) \\ s.t. \quad tr(W^TS_wW) =1 \end{aligned} \right. Wmintr(WTSbW)s.t.tr(WTSwW)=1
    • 另一种常见的优化目标形式: max ⁡ W ∏ i = 1 N − 1 w i T S b w i ∏ i = 1 N − 1 w i T S w w i = max ⁡ W ∏ i = 1 N − 1 w i T S b w i w i T S w w i \max\limits_{W}\frac{\prod\limits_{i=1}^{N-1} w_i^TS_bw_i}{\prod\limits_{i=1}^{N-1} w_i^TS_ww_i}=\max\limits_{W}\prod\limits_{i=1}^{N-1}\frac{ w_i^TS_bw_i}{w_i^TS_ww_i} Wmaxi=1N1wiTSwwii=1N1wiTSbwi=Wmaxi=1N1wiTSwwiwiTSbwi
  • 拉格朗日乘子法
    • 拉格朗日函数: L ( W , λ ) = − t r ( W T S b W ) + λ [ t r ( W T S w W ) − 1 ] L(W,\lambda)=-tr(W^TS_bW)+\lambda[tr(W^TS_wW) -1] L(W,λ)=tr(WTSbW)+λ[tr(WTSwW)1]
      • 解得: S b W = λ S w W S_bW = λS_wW SbW=λSwW
      • W W W 的闭式解是 S w − 1 S b S_w^{-1}S_b Sw1Sb N − 1 N-1 N1 个最大广义特征值所对应的特征向量组成的矩阵。但此处并不能直观地解释为什么要这么构造,因此改用下面的拉格朗日函数求解。
    • 拉格朗日函数: L ( W , Λ ) = − t r ( W T S b W ) + t r [ Λ ( W T S w W − I ) ] L(W,Λ)=−tr(W^TS_bW)+tr [Λ(W^TS_wW−I)] L(W,Λ)=tr(WTSbW)+tr[Λ(WTSwWI)]
      • 其中: I ∈ R ( N − 1 ) × ( N − 1 ) I\in\mathbb{R}^{(N-1)\times(N-1)} IR(N1)×(N1) 为单位矩阵; Λ = d i a g ( λ 1 , λ 2 , . . . , λ N − 1 ) ∈ R ( N − 1 ) × ( N − 1 ) Λ=diag(\lambda_1,\lambda_2,...,\lambda_{N-1})\in\mathbb{R}^{(N-1)\times(N-1)} Λ=diag(λ1,λ2,...,λN1)R(N1)×(N1) 是由 N − 1 N−1 N1 个拉格朗日乘子构成的对角矩阵
      • 解得: S b W = S w W Λ S_bW = S_wWΛ SbW=SwWΛ
        • 展开得: S b w i = λ i S w w i , i = 1 , 2 , . . . , N − 1 S_bw_i =λ_iS_ww_i, i=1,2,...,N−1 Sbwi=λiSwwi,i=1,2,...,N1
        • λ i λ_i λi对应的是广义特征值, w i w_i wi λ i λ_i λi 所对应的特征向量
      • 代回目标函数:
        • min ⁡ W − t r ( W T S b W ) = max ⁡ W t r ( W T S b W ) = max ⁡ W ∑ i = 1 N − 1 w i T S b w i = max ⁡ W ∑ i = 1 N − 1 λ i w i T S w w i \min\limits_{W}-tr(W^TS_bW)=\max\limits_{W}tr(W^TS_bW)=\max\limits_{W}\sum\limits_{i=1}^{N-1}w_i^TS_bw_i=\max\limits_{W}\sum\limits_{i=1}^{N-1}λ_iw_i^TS_ww_i Wmintr(WTSbW)=Wmaxtr(WTSbW)=Wmaxi=1N1wiTSbwi=Wmaxi=1N1λiwiTSwwi
        • 又: t r ( W T S w W ) = ∑ i = 1 N − 1 w i T S w w i = 1 tr(W^TS_wW) =\sum\limits_{i=1}^{N-1}w_i^TS_ww_i= 1 tr(WTSwW)=i=1N1wiTSwwi=1,故在这 N − 1 N-1 N1 λ i \lambda_i λi 都取最大值时上式取最大值
        • 对于 w i w_i wi 的实际个数及相关证明,见南瓜书

拆解法

  • 将多分类任务拆为若干个二分类任务求解
    • 先对问题进行拆分,然后为拆出的每个二分类任务训练一个分类器
    • 在测试时,对这些分类器的预测结果进行集成以获得最终的多分类结果
    • 考虑 N N N 个类别 C 1 , C 2 , . . . , C N C_1,C_2,...,C_N C1,C2,...,CN,给定数据集 D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x m , y m ) } , y i ∈ { C 1 , C 2 , . . . , C N } D=\{(x_1,y_1),(x_2,y_2),...,(x_m,y_m)\},y_i\in\{C_1,C_2,...,C_N\} D={(x1,y1),(x2,y2),...,(xm,ym)},yi{C1,C2,...,CN}
  • 一对一:One vs One,简称 OvO
    • 将这 N N N 个类别两两配对,从而产生 N ( N − 1 ) / 2 N(N-1)/2 N(N1)/2 个二分类任务
    • OvO 将为区分类别 C i C_i Ci C j C_j Cj,训练一个分类器,该分类器把 D D D 中的 C i C_i Ci 类样例作为正例, C j C_j Cj 类样例作为反例
    • 在测试阶段,新样本将同时提交给所有分类器,将得到 N ( N − 1 ) / 2 N(N-1)/2 N(N1)/2 个分类结果
    • 最终结果通过投票产生:即把被预测得最多的类别作为最终分类结果
  • 一对其余:One vs Rest,简称 OvR
    • OvR每次将一个类的样例作为正例、所有其他类的样例作为反例来训练 N N N 个分类器
    • 在测试时若仅有一个分类器预测为正类,则对应的类别标记作为最终分类结果
    • 若有多个分类器预测为正类,则通常考虑各分类器的预测置信度,选择置信度最大的类别标记作为分类结果
      OvO与OvR示意图
  • 多对多:Many vs Many,简称 MvM
    • MvM 是每次将若干个类作为正类,若干个其他类作为反类
    • OvO 和OvR 是 MvM 的特例
    • MvM 的正、反类构造必须有特殊的设计,不能随意选取

类别不平衡问题

  • 如果不同类别的训练样例数目稍有差别,通常影响不大,但若差别很大,则会对学习过程造成困扰。类别不平衡(clas s-imbal ance)就是指分类任务中不同类别的训练样例数目差别很大的情况
  • 再缩放
    • 几率 y 1 − y \frac{y}{1-y} 1yy 反映了正例可能性与反例可能性之比值
    • 阈值设置为 0.5 0.5 0.5 表明分类器认为真实正、反例可能性相同:分类器决策规则为 若 y 1 − y > 1 则预测为正例 若\frac{y}{1-y}>1则预测为正例 1yy>1则预测为正例
    • 当训练集中正、反例的数目不同时,令 m + m+ m+ 表示正例数目, m − m- m表示反例数目,则观测几率是 m + m − \frac{m^+}{m^-} mm+ ,假设训练集是真实样本总体的无偏采样,因此观测几率就代表了真实几率。于是,只要分类器的预测几率高于观测几率就应判定为正例: 若 y 1 − y > m + m − ,即 y ′ 1 − y ′ = y 1 − y × m − m + > 1 则预测为正例 若\frac{y}{1-y}>\frac{m^+}{m^-},即\frac{y'}{1-y'}=\frac{y}{1-y}\times\frac{m^-}{m^+}>1则预测为正例 1yy>mm+,即1yy=1yy×m+m>1则预测为正例
  • 推断 m + m − \frac{m^+}{m^-} mm+ 的方法
    • 欠采样
      • 去除一些反例使得正、反例数目接近,然后再进行学习
    • 过采样
      • 增加一些正例使得正、反例数目接近,然后再进行学习
    • 阈值异动
      • 直接基于原始训练集进行学习,但在用训练好的分类器进行预测时,将 y ′ 1 − y ′ = y 1 − y × m − m + \frac{y'}{1-y'}=\frac{y}{1-y}\times\frac{m^-}{m^+} 1yy=1yy×m+m 嵌入到其决策过程中
    • 对比
      • 欠采样法的时间开销通常远小于过采样法,因为前者丢弃了很多反例,使得分类器训练集远小于初始训练集,而过采样法增加了很多正例,其训练集大于初始训练集
      • 过采样法不能简单地对初始正例样本进行重复采样,否则会招致严重的过拟合;过采样法的代表性算法 SMOTE 是通过对训练集里的正例进行插值来产生额外的正例
      • 欠采样法若随机丢弃反例,可能丢失一些重要信息;欠采样法的代表性算法EasyEnsemble 是利用集成学习机制,将反例划分为若干个集合供不同学习器使用,这样对每个学习器来看都进行了欠采样,但在全局来看却不会丢失重要信息

习题

西瓜数据集 3.0α

编号色泽根蒂敲声纹理脐部触感密度含糖率好瓜
1青绿蜷缩浊响清晰凹陷硬滑0.6970.46
2乌黑蜷缩沉闷清晰凹陷硬滑0.7740.376
3乌黑蜷缩浊响清晰凹陷硬滑0.6340.264
4青绿蜷缩沉闷清晰凹陷硬滑0.6080.318
5浅白蜷缩浊响清晰凹陷硬滑0.5560.215
6青绿稍蜷浊响清晰稍凹软粘0.4030.237
7乌黑稍蜷浊响稍糊稍凹软粘0.4810.149
8乌黑稍蜷浊响清晰稍凹硬滑0.4370.211
9乌黑稍蜷沉闷稍糊稍凹硬滑0.6660.091
10青绿硬挺清脆清晰平坦软粘0.2430.267
11浅白硬挺清脆模糊平坦硬滑0.2450.057
12浅白蜷缩浊响模糊平坦软粘0.3430.099
13青绿稍蜷浊响稍糊凹陷硬滑0.6390.161
14浅白稍蜷沉闷稍糊凹陷硬滑0.6570.198
15乌黑稍蜷浊响清晰稍凹软粘0.360.37
16浅白蜷缩浊响模糊平坦硬滑0.5930.042
17青绿蜷缩沉闷稍糊稍凹硬滑0.7190.103

3.2.编程实现对率回归,并给出西瓜数据集 3.0α 上的结果

'''
与原书不同,原书中一个样本xi 为列向量,本代码中一个样本xi为行向量
尝试了两种优化方法,梯度下降和牛顿法。两者结果基本相同,不过有时因初始化的原因,
会导致牛顿法中海森矩阵为奇异矩阵,np.linalg.inv(hess)会报错。以后有机会再写拟牛顿法吧。
'''

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from sklearn import linear_model


def sigmoid(x):
    s = 1 / (1 + np.exp(-x))
    return s


def J_cost(X, y, beta):
    '''
    :param X:  sample array, shape(n_samples, n_features)
    :param y: array-like, shape (n_samples,)
    :param beta: the beta in formula 3.27 , shape(n_features + 1, ) or (n_features + 1, 1)
    :return: the result of formula 3.27
    '''
    X_hat = np.c_[X, np.ones((X.shape[0], 1))]
    beta = beta.reshape(-1, 1)
    y = y.reshape(-1, 1)

    Lbeta = -y * np.dot(X_hat, beta) + np.log(1 + np.exp(np.dot(X_hat, beta)))

    return Lbeta.sum()


def gradient(X, y, beta):
    '''
    compute the first derivative of J(i.e. formula 3.27) with respect to beta      i.e. formula 3.30
    ----------------------------------
    :param X: sample array, shape(n_samples, n_features)
    :param y: array-like, shape (n_samples,)
    :param beta: the beta in formula 3.27 , shape(n_features + 1, ) or (n_features + 1, 1)
    :return:
    '''
    X_hat = np.c_[X, np.ones((X.shape[0], 1))]
    beta = beta.reshape(-1, 1)
    y = y.reshape(-1, 1)
    p1 = sigmoid(np.dot(X_hat, beta))

    gra = (-X_hat * (y - p1)).sum(0)

    return gra.reshape(-1, 1)


def hessian(X, y, beta):
    '''
    compute the second derivative of J(i.e. formula 3.27) with respect to beta      i.e. formula 3.31
    ----------------------------------
    :param X: sample array, shape(n_samples, n_features)
    :param y: array-like, shape (n_samples,)
    :param beta: the beta in formula 3.27 , shape(n_features + 1, ) or (n_features + 1, 1)
    :return:
    '''
    X_hat = np.c_[X, np.ones((X.shape[0], 1))]
    beta = beta.reshape(-1, 1)
    y = y.reshape(-1, 1)

    p1 = sigmoid(np.dot(X_hat, beta))

    m, n = X.shape
    P = np.eye(m) * p1 * (1 - p1)

    assert P.shape[0] == P.shape[1]
    return np.dot(np.dot(X_hat.T, P), X_hat)


def update_parameters_gradDesc(X, y, beta, learning_rate, num_iterations, print_cost):
    '''
    update parameters with gradient descent method
    --------------------------------------------
    :param beta:
    :param grad:
    :param learning_rate:
    :return:
    '''
    for i in range(num_iterations):

        grad = gradient(X, y, beta)
        beta = beta - learning_rate * grad

        if (i % 10 == 0) & print_cost:
            print('{}th iteration, cost is {}'.format(i, J_cost(X, y, beta)))

    return beta


def update_parameters_newton(X, y, beta, num_iterations, print_cost):
    '''
    update parameters with Newton method
    :param beta:
    :param grad:
    :param hess:
    :return:
    '''

    for i in range(num_iterations):

        grad = gradient(X, y, beta)
        hess = hessian(X, y, beta)
        beta = beta - np.dot(np.linalg.inv(hess), grad)

        if (i % 10 == 0) & print_cost:
            print('{}th iteration, cost is {}'.format(i, J_cost(X, y, beta)))
    return beta


def initialize_beta(n):
    beta = np.random.randn(n + 1, 1) * 0.5 + 1
    return beta


def logistic_model(X, y, num_iterations=100, learning_rate=1.2, print_cost=False, method='gradDesc'):
    '''
    :param X:
    :param y:~
    :param num_iterations:
    :param learning_rate:
    :param print_cost:
    :param method: str 'gradDesc' or 'Newton'
    :return:
    '''
    m, n = X.shape
    beta = initialize_beta(n)

    if method == 'gradDesc':
        return update_parameters_gradDesc(X, y, beta, learning_rate, num_iterations, print_cost)
    elif method == 'Newton':
        return update_parameters_newton(X, y, beta, num_iterations, print_cost)
    else:
        raise ValueError('Unknown solver %s' % method)


def predict(X, beta):
    X_hat = np.c_[X, np.ones((X.shape[0], 1))]
    p1 = sigmoid(np.dot(X_hat, beta))

    p1[p1 >= 0.5] = 1
    p1[p1 < 0.5] = 0

    return p1


if __name__ == '__main__':
    data_path = r'C:\Users\hanmi\Documents\xiguabook\watermelon3_0_Ch.csv'
    #
    data = pd.read_csv(data_path).values

    is_good = data[:, 9] == '是'
    is_bad = data[:, 9] == '否'

    X = data[:, 7:9].astype(float)
    y = data[:, 9]

    y[y == '是'] = 1
    y[y == '否'] = 0
    y = y.astype(int)

    plt.scatter(data[:, 7][is_good], data[:, 8][is_good], c='k', marker='o')
    plt.scatter(data[:, 7][is_bad], data[:, 8][is_bad], c='r', marker='x')

    plt.xlabel('密度')
    plt.ylabel('含糖量')

    # 可视化模型结果
    beta = logistic_model(X, y, print_cost=True, method='gradDesc', learning_rate=0.3, num_iterations=1000)
    w1, w2, intercept = beta
    x1 = np.linspace(0, 1)
    y1 = -(w1 * x1 + intercept) / w2

    ax1, = plt.plot(x1, y1, label=r'my_logistic_gradDesc')

    lr = linear_model.LogisticRegression(solver='lbfgs', C=1000)  # 注意sklearn的逻辑回归中,C越大表示正则化程度越低。
    lr.fit(X, y)

    lr_beta = np.c_[lr.coef_, lr.intercept_]
    print(J_cost(X, y, lr_beta))

    # 可视化sklearn LogisticRegression 模型结果
    w1_sk, w2_sk = lr.coef_[0, :]

    x2 = np.linspace(0, 1)
    y2 = -(w1_sk * x2 + lr.intercept_) / w2

    ax2, = plt.plot(x2, y2, label=r'sklearn_logistic')

    plt.legend(loc='upper right')
    plt.show()

3.3.选择两个 UCI数据集,比较 10 折交叉验证法和留一法所估计出的对率回归的错误率

import numpy as np
from sklearn import linear_model
from sklearn.model_selection import LeaveOneOut
from sklearn.model_selection import cross_val_score

data_path = r'C:\Users\hanmi\Documents\xiguabook\Transfusion.txt'

data = np.loadtxt(data_path, delimiter=',').astype(int)

X = data[:, :4]
y = data[:, 4]

m, n = X.shape

# normalization
X = (X - X.mean(0)) / X.std(0)

# shuffle
index = np.arange(m)
np.random.shuffle(index)

X = X[index]
y = y[index]

# 使用sklarn 中自带的api先
# k-10 cross validation
lr = linear_model.LogisticRegression(C=2)

score = cross_val_score(lr, X, y, cv=10)

print(score.mean())

# LOO
loo = LeaveOneOut()

accuracy = 0
for train, test in loo.split(X, y):
    lr_ = linear_model.LogisticRegression(C=2)
    X_train = X[train]
    X_test = X[test]
    y_train = y[train]
    y_test = y[test]
    lr_.fit(X_train, y_train)

    accuracy += lr_.score(X_test, y_test)

print(accuracy / m)

# 两者结果几乎一样

# 自己写一个试试
# k-10
# 这里就没考虑最后几个样本了。
num_split = int(m / 10)
score_my = []
for i in range(10):
    lr_ = linear_model.LogisticRegression(C=2)
    test_index = range(i * num_split, (i + 1) * num_split)
    X_test_ = X[test_index]
    y_test_ = y[test_index]

    X_train_ = np.delete(X, test_index, axis=0)
    y_train_ = np.delete(y, test_index, axis=0)

    lr_.fit(X_train_, y_train_)

    score_my.append(lr_.score(X_test_, y_test_))

print(np.mean(score_my))

# LOO
score_my_loo = []
for i in range(m):
    lr_ = linear_model.LogisticRegression(C=2)
    X_test_ = X[i, :]
    y_test_ = y[i]

    X_train_ = np.delete(X, i, axis=0)
    y_train_ = np.delete(y, i, axis=0)

    lr_.fit(X_train_, y_train_)

    score_my_loo.append(int(lr_.predict(X_test_.reshape(1, -1)) == y_test_))

print(np.mean(score_my_loo))

# 结果都是类似

3.4.编程实现线性判别分析,并给出西瓜数据集 3.0a 上的结果

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt


class LDA(object):

    def fit(self, X_, y_, plot_=False):
        pos = y_ == 1
        neg = y_ == 0
        X0 = X_[neg]
        X1 = X_[pos]

        u0 = X0.mean(0, keepdims=True)  # (1, n)
        u1 = X1.mean(0, keepdims=True)

        sw = np.dot((X0 - u0).T, X0 - u0) + np.dot((X1 - u1).T, X1 - u1)
        w = np.dot(np.linalg.inv(sw), (u0 - u1).T).reshape(1, -1)  # (1, n)

        if plot_:
            fig, ax = plt.subplots()
            ax.spines['right'].set_color('none')
            ax.spines['top'].set_color('none')
            ax.spines['left'].set_position(('data', 0))
            ax.spines['bottom'].set_position(('data', 0))

            plt.scatter(X1[:, 0], X1[:, 1], c='k', marker='o', label='good')
            plt.scatter(X0[:, 0], X0[:, 1], c='r', marker='x', label='bad')

            plt.xlabel('密度', labelpad=1)
            plt.ylabel('含糖量')
            plt.legend(loc='upper right')

            x_tmp = np.linspace(-0.05, 0.15)
            y_tmp = x_tmp * w[0, 1] / w[0, 0]
            plt.plot(x_tmp, y_tmp, '#808080', linewidth=1)

            wu = w / np.linalg.norm(w)

            # 正负样板店
            X0_project = np.dot(X0, np.dot(wu.T, wu))
            plt.scatter(X0_project[:, 0], X0_project[:, 1], c='r', s=15)
            for i in range(X0.shape[0]):
                plt.plot([X0[i, 0], X0_project[i, 0]], [X0[i, 1], X0_project[i, 1]], '--r', linewidth=1)

            X1_project = np.dot(X1, np.dot(wu.T, wu))
            plt.scatter(X1_project[:, 0], X1_project[:, 1], c='k', s=15)
            for i in range(X1.shape[0]):
                plt.plot([X1[i, 0], X1_project[i, 0]], [X1[i, 1], X1_project[i, 1]], '--k', linewidth=1)

            # 中心点的投影
            u0_project = np.dot(u0, np.dot(wu.T, wu))
            plt.scatter(u0_project[:, 0], u0_project[:, 1], c='#FF4500', s=60)
            u1_project = np.dot(u1, np.dot(wu.T, wu))
            plt.scatter(u1_project[:, 0], u1_project[:, 1], c='#696969', s=60)

            ax.annotate(r'u0 投影点',
                        xy=(u0_project[:, 0], u0_project[:, 1]),
                        xytext=(u0_project[:, 0] - 0.2, u0_project[:, 1] - 0.1),
                        size=13,
                        va="center", ha="left",
                        arrowprops=dict(arrowstyle="->",
                                        color="k",
                                        )
                        )

            ax.annotate(r'u1 投影点',
                        xy=(u1_project[:, 0], u1_project[:, 1]),
                        xytext=(u1_project[:, 0] - 0.1, u1_project[:, 1] + 0.1),
                        size=13,
                        va="center", ha="left",
                        arrowprops=dict(arrowstyle="->",
                                        color="k",
                                        )
                        )
            plt.axis("equal")  # 两坐标轴的单位刻度长度保存一致
            plt.show()

        self.w = w
        self.u0 = u0
        self.u1 = u1
        return self

    def predict(self, X):
        project = np.dot(X, self.w.T)

        wu0 = np.dot(self.w, self.u0.T)
        wu1 = np.dot(self.w, self.u1.T)

        return (np.abs(project - wu1) < np.abs(project - wu0)).astype(int)


if __name__ == '__main__':
    data_path = r'C:\Users\hanmi\Documents\xiguabook\watermelon3_0_Ch.csv'

    data = pd.read_csv(data_path).values

    X = data[:, 7:9].astype(float)
    y = data[:, 9]

    y[y == '是'] = 1
    y[y == '否'] = 0
    y = y.astype(int)

    lda = LDA()
    lda.fit(X, y, plot_=True)
    print(lda.predict(X))  # 和逻辑回归的结果一致
    print(y)


  • 30
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值