Datawhale组队学习第27期:集成学习
本次学习的指导老师萌弟教学视频
本贴为学习记录帖,有任何问题欢迎随时交流~
部分内容可能还不完整,后期随着知识积累逐步完善。
开始时间:2021年7月13日
最新更新:2021年7月15日(Task2回归问题)

一、基本建模步骤

  • 明确项目任务
  • 收集数据集并选取合适的特征
  • 选择度量模型性能的指标
  • 选择具体的模型并进行训练以优化模型
  • 评估模型并调参

二、以Boston房价为例

  • Boston房价预测,显然是一个回归问题。

  • 数据集已有,这里默认选定所有的特征。

  • 度量模型性能的指标(n为样本量),具体可以看sklearn的官方文档

    • MSE均方误差
      M S E ( y , y ^ ) = 1 n ∑ i = 1 n ( y i − y ^ i ) 2 MSE(y,\hat y) = \frac{1}{n}\sum\limits_{i=1}^{n}(y_i - \hat y_i)^2 MSE(y,y^)=n1i=1n(yiy^i)2

    • MAE平均绝对误差
      M A E ( y , y ^ ) = 1 n ∣ y i − y ^ i ∣ MAE(y, \hat y) = \frac{1}{n}|y_i - \hat y_i| MAE(y,y^)=n1yiy^i

    • R-Square
      R 2 ( y , y ^ ) = 1 − ∑ i = 1 n ( y i − y ^ i ) 2 ∑ i = 1 n ( y i − y ˉ i ) 2 R^2(y, \hat y) = 1 - \frac{\sum\limits_{i=1}^{n}(y_i - \hat y_i)^2} {\sum\limits_{i=1}^{n}(y_i - \bar y_i)^2} R2(y,y^)=1i=1n(yiyˉi)2i=1n(yiy^i)2

    • 解释方程得分
      E x p l a t i n e d   V a r i a n c e ( y , y ^ ) = 1 − V a r ( y − y ^ ) V a r ( y ) Explatined \ V ariance(y, \hat y) = 1 - \frac{Var(y-\hat y)}{Var(y)} Explatined Variance(y,y^)=1Var(y)Var(yy^)

  • 选择具体的模型

    • 线性回归(linear_model
    • 多项式回归(PolynomialFeatures
    • 广义可加模型GAM(LinearGAM
    • 回归树(DecisionTreeRegressor
    • 支持向量回归SVR(SVR
  • 评估模型并调参(这部分是后续进行)

三、线性回归模型推导

在前几个月,本人写过一篇关于线性回归的简单应用,文章主要介绍了线性回归在Python中的实现,主要应用的是sklearn包的内容。本次学习中,Datawhale安排的作业中要求使用numpy库实现,因此本文主要给出线性回归模型的相关推导基于numpy实现

1. 数据基础

  • 数据集: D = { ( x 1 , y 1 ) , . . . , ( x N , y N ) } D=\{ (x_1, y_1),...,(x_N,y_N) \} D={ (x1,y1),...,(xN,yN)}
  • 单样本向量: x i ∈ R n x_i \in R^n xiRn表示 x i x_i xi n n n个特征,即 x i = [ x i ( j ) ] n × 1 x_i = [x^{(j)}_i]_{n \times 1} xi=[xi(j)]n×1
  • 所有样本组成的数值矩阵: X = [ x i ( j ) ] n × N X = [x_i^{(j)}]_{n\times N} X=[xi(j)]n×N
  • 参数: w = [ w j ] n × 1 w = [w_j]_{n \times 1} w=[wj]n×1
  • 预测值: Y ^ = [ y ^ i ] N × 1 \hat Y = [\hat y_i]_{N \times 1} Y^=[y^i]N×1
  • 实际值: Y = [ y i ] N × 1 Y = [y_i]_{N \times 1} Y=[yi]N×1

2. 基本模型

y ^ i = w T x i Y ^ = [ y ^ i ] N × 1 = [ w T x i ] N × 1 = X T ⋅ w \begin{aligned} \hat y_i &= w^Tx_i \\ \hat Y &= [\hat y_i]_{N \times 1} = [w^Tx_i]_{N \times 1} = X^T \cdot w \\ \end{aligned} y^iY^=wTxi=[y^i]N×1=[wTxi]N×1=XTw

3. 损失函数

  • 这里的损失函数其实就是均方误差 M S E MSE MSE,回归问题中比较常用。
  • 首先先计算预测值与真实值之差的 L 2 L_2 L2范数,可以理解为两个向量的模长:
    ∣ ∣ Y ^ − Y ∣ ∣ 2 = ( X T ⋅ w − Y ) T ( X T ⋅ w − Y ) = w T X X T w − w T X Y − Y T X T w + Y T Y \begin{aligned} ||\hat Y - Y||_2 &= (X^T \cdot w - Y)^T(X^T \cdot w - Y) \\ &= w^TXX^Tw - w^TXY-Y^TX^Tw+Y^TY \end{aligned} Y^Y2=(XTwY)T(XTwY)=wTXXTwwTXYYTXTw+YTY
  • 实际上,对于我们获取的数据集一般是行代表样本数,列代表特征数,可以将上面的数值矩阵改为 X = [ x i ( j ) ] N × n X = [x_i^{(j)}]_{N \times n} X=[xi(j)]N×n。由于中间项都是常数,转置等于它本身,有 w T X T Y = Y T X w w^T X^TY =Y^TXw wTXTY=YTXw,因此上述等式改为:
    ∣ ∣ Y ^ − Y ∣ ∣ 2 = ( X ⋅ w − Y ) T ( X ⋅ w − Y ) = w T X T X w − 2 w T X T Y + Y T Y \begin{aligned} ||\hat Y - Y||_2 &= (X \cdot w - Y)^T(X \cdot w - Y) \\ &= w^TX^TXw - 2w^TX^TY + Y^TY \end{aligned} Y^Y2=(XwY)T(XwY)=wTXTXw2wTXTY+YTY

4. 优化问题

  • 目标函数:(这里加入 1 2 \frac{1}{2} 21是方便后面消去)
    L ( w ) = 1 2 ∣ ∣ Y ^ − Y ∣ ∣ 2 = w T X T X w − 2 w T X T Y + Y T Y L(w) = \frac{1}{2} ||\hat Y - Y||_2 = w^TX^TXw - 2w^TX^TY + Y^TY L(w)=21Y^Y2=wTXTXw2wTXTY+YTY
  • 优化任务:
    w = argmin w L ( w ) w = \mathop{\text{argmin}}_{w} L(w) w=argminwL(w)

5. 优化算法(梯度下降法)

  • 初始化参数 w ( 0 ) = [ w 0 ( 0 ) , w 1 ( 0 ) , . . . , w n ( n ) ] w^{(0)} = [w_0^{(0)}, w_1^{(0)},..., w_n^{(n)}] w(0)=[w0(0),w1(0),...,wn(n)] ,这里是把截距项也放进去了。

  • 设定学习率 α \alpha α,最大迭代次数 l o o p s loops loops,可接受误差 ϵ \epsilon ϵ

  • 修改数值矩阵 X = [ x i ( j ) ] N × ( n + 1 ) X = [x_i^{(j)}]_{N \times (n+1)} X=[xi(j)]N×(n+1):多出来的一列放于首列,全设置为1

  • 计算当前梯度:

    • w T X T X w w^TX^TXw wTXTXw可以视为 w T A w w^TAw wTAw,并由于 A A A是实对称矩阵,即: X T X = ( X T X ) T X^TX = (X^TX)^T XTX=(XTX)T,可以得到:

    ∂ ( w T X T X w ) ∂ w = ( X T X + X T X ) w = 2 X T X w \frac{\partial (w^TX^TXw)}{\partial w} = (X^TX + X^TX)w = 2X^TXw \\ w(wTXTXw)=(XTX+XTX)w=2XTXw

    • w T X T Y w^TX^TY wTXTY 看成是关于 w T w^T wT的函数,即 f ( w T ) = w T X T Y f(w^T) = w^TX^TY f(wT)=wTXTY,则有:
      ∂ ( 2 w T X T Y ) ∂ w = 2 X T Y \frac{\partial (2w^TX^TY)}{\partial w} = 2X^TY w(2wTXTY)=2XTY

    • 参数梯度为(矩阵运算时一般是采用这个):

    ∂ L ( w ) ∂ w = X T X w − X T Y = X T ( Y ^ − Y ) \frac{\partial L(w)}{\partial w} = X^TXw - X^TY = X^T(\hat Y - Y) wL(w)=XTXwXTY=XT(Y^Y)

    • 数值矩阵 X = [ x i ( j ) ] N × ( n + 1 ) X = [x_i^{(j)}]_{N \times (n+1)} X=[xi(j)]N×(n+1),对 X T Y w X^TYw XTYw X T X X^TX XTX进行分解,每个参数 w j w_j wj的当前梯度为:
      ∂ L ( w ) ∂ w j = ∑ k = 0 n ∑ i = 1 N x i j x i k w k − ∑ i = 1 N x i j ⋅ y i \frac{\partial L(w)}{\partial w_j} = \sum\limits_{k=0}^{n}\sum\limits_{i=1}^{N}x^j_ix_i^kw_k - \sum\limits_{i=1}^{N}x_i^{j}\cdot y_i wjL(w)=k=0ni=1Nxijxikwki=1Nxijyi
  • ∂ L ( w ) ∂ w ≥ ϵ \frac{\partial L(w)}{\partial w} \ge \epsilon wL(w)ϵ,则更新参数,再按上述计算方式更新梯度:
    w ( k + 1 ) : = w ( k ) − α ⋅ ∂ L ( w ) ∂ w w^{(k+1)} := w^{(k)} - \alpha \cdot \frac{\partial L(w)}{\partial w} w(k+1):=w(k)αwL(w)

  • ∂ L ( w ) ∂ w < ϵ \frac{\partial L(w)}{\partial w} < \epsilon wL(w)<ϵ 或超过最大迭代次数时,返回最新更新的参数 w ( k ) w^{(k)} w(k)

6. 代码实现

# 导入包
import numpy as np
import os
from sklearn.datasets import load_boston
import time

os.chdir('D:/NewProject/datawhale/ensemble/task2')


# 定义梯度
def grad(x_, y_, w_):
    g = np.dot(x_.T, (np.dot(x_, w_) - y_)) / len(x_)
    return g


# 定义梯度下降
def sgd(x_, y_t, w_, lr, error, loops):
    num = 0
    g = grad(x_, y_t, w_)
    while num < loops:
        theta_ = w_ - lr * g
        g = grad(x_, y_t, theta_)
        if np.all(np.abs(g) < error):
            print('已找到最优参数!\n总共迭代的次数为{}'.format(num))
            print('最优参数为:{}'.format(np.round(w_, 3)))
            break
        w_ = theta_
        num += 1
    if num == loops:
        print('以达到最大迭代次数!\n最终参数为:{}'.format(np.round(w_, 3)))
    return w_


if __name__ == '__main__':
    # 载入数据集
    data = load_boston()

    # 设定随机种子(后面用于生成随机参数)
    np.random.seed(2021)
    np.set_printoptions(suppress=True)

    # 选取变量
    x = data['data']
    y = data['target']

    # 对数据预处理(标准化)
    x = (x - np.mean(x, axis=0)) / np.std(x, axis=0)

    # 加入偏置项
    x0 = np.ones(len(x)).reshape(-1, 1)
    x = np.concatenate((x0, x), axis=1)     # 合并列

    # 初始化参数
    theta = np.random.randn(len(x[0]))

    # 设定参数
    alpha = 0.01
    err = 0.00001
    max_loops = 100000

    # 求解参数
    start = time.time()
    best_theta = sgd(x, y, theta, alpha, err, max_loops)
    end = time.time()
    print('总共运行时长为:{}s'.format(np.round(end - start, 4)))
  • 结果如下(输出参数保留了三位小数):

四、参考资料

  1. https://github.com/datawhalechina/ensemble-learning
  2. https://www.bilibili.com/video/BV1Mb4y1o7ck?t=470