机器学习:线性回归

优点:结果易于理解,计算上不复杂
缺点:对非线性的数据拟合不好

线性回归

用于数值预测,回归方程为
  
  (large y = x_{1}w_{1} + x_{2}w_{2} + ... + x_{n}w_{n} + b)
  
写成矩阵形式
  
  (large y = XW + b)
  
为方便计算,添加 (large x_{0}=1), 取 (large w_{0} = b), 将式子改写为
  
  (large y = XW)
  
采用平方差做损失函数
  
  (large L = sum_{i=1}^{n}(y_{i} -X_{i}W)^2)
  
用矩阵表示为
  
  (large L = (Y - XW)^{T}(Y - XW))
  
  其中 X 是 (m,n) 矩阵,W 是 (n,1) 矩阵,Y 是 (m,1) 矩阵

(small W) 求导
  
  (large frac{partial L}{partial W} = frac{partial (Y - XW)^{T}(Y - XW)}{partial W})
  
     (large = frac{partial (Y^{T} - W^{T}X^{T})(Y - XW)}{partial W})
  
     (large = frac{partial (Y^{T}Y - Y^{T}XW - W^{T}X^{T}Y + W^{T}X^{T}XW)}{partial W})
  
     (large = frac{partial Y^{T}Y}{partial W} - frac{partial Y^{T}XW}{partial W} - frac{partial W^{T}X^{T}Y}{partial W} + frac{partial W^{T}X^{T}XW}{partial W})
   
     (large = 0 - X^{T}Y - X^{T}Y + (X^{T}X + (X^{T}X)^{T})W)
   
     (large = - 2X^{T}Y + (2X^{T}X)W)
   
     (large = - 2X^{T}(Y - XW))
  
另导数为 0 得到最优的 (small W)
  
  (large - 2X^{T}(Y - XW) = 0)
  (large X^{T}Y = X^{T}XW)
  (large (X^{T}X)^{-1}X^{T}Y = (X^{T}X)^{-1}X^{T}XW)
  
  (large W = (X^{T}X)^{-1}X^{T}Y)

代码

def standRegres(xArr, yArr):
    """
    xArr - 样本特征,是 (m,n) 矩阵,每行的第一个值既 X0 固定为 1
    yArr - 样本标签,是 (1,m)
    """
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T

    xTx = xMat.T * xMat
    if np.linalg.det(xTx) == 0.0:
        # 如果行列式等于 0,意味着没有逆矩阵
        return None

    # 也可以用 ws = np.linalg.solve(xTx, xMat.T*yMat)
    w = xTx.I * xMat.T * yMat

    # ws 是 (n,1) 矩阵
    return w

通过相关系统判断效果好坏

# 结果越接近 1 效果越好
np.corrcoef(yEstimate, yActual)

局部加权线性回归 (Locally Weighted Linear Regression,LWLR)

线性回归的一个问题是有可能出现欠拟合现象,就是偏离拟合曲线的点比较多
与此对应的有过拟合问题,就是拟合曲线过于迎合数据点,这意味着曲线被噪点干扰比较大
  
解决欠拟合问题的一个方法是局部加权线性回归
  给待预测点附近的每个点赋予权重,然后在这个子集上基于最小均方差进行普通的回归
  注意:与 kNN 一样,这种算法每次预测均需要事先选取出对应的数据子集
  
LWLR 使用核函数来对附近的点赋予权重,核的类型可以自由选择,常用的是高斯核
  
  (large Weight(i,i) = exp(frac{|x_{i}-x|}{-2k^{2}}))
  
  只含对角元素的矩阵,x 是测试点,xi 与 x 越近权重越大,k 越大权重越大,权值必然小于 1
  k 越大越容易欠拟合,越小越容易过拟合
  
算法如下
  
  (large W = (X^{T}Weight X)^{-1}X{^T}Weight Y)
  
代码

def lwlr(testPoint, xArr, yArr, k=1.0):
    """
    testPoint - 待预测的点 (1,n)
    xArr - 样本特征 (m,n),每个样本的第一个值既 X0 固定为 1
    yArr - 样本标签 (1,m)
    """
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T

    m = np.shape(xMat)[0]

    # eye 是单位矩阵,对角线是 1,其余是 0
    weights = np.mat(np.eye(m))

    # 遍历所有数据
    for j in range(m):
        # 计算权重
        diffMat = testPoint - xMat[j, :]
        weights[j, j] = np.exp(diffMat * diffMat.T / (-2.0 * k ** 2))

    xTx = xMat.T * (weights * xMat)
    if np.linalg.det(xTx) == 0.0:
        # 如果行列式等于 0,意味着没有逆矩阵
        return

    # 得出回归系数 (n,1)
    w = xTx.I * (xMat.T * (weights * yMat))
    return testPoint * w


缩减法

如果数据的特征比样本点还多,则不能再使用前面的方法,因为在计算逆矩阵时会出错
  
岭回归
  岭回归最先用来处理特征数多于样本数的情况
  现在也用于在估计中加入偏差,从而得到更好的估计
  
  为 (small X^TX) 加上 (small r cdot I) 使得矩阵可逆,其中 r 是用户定义的数值,I 是单位矩阵
  回归系数
    
    (large W = (X^{T}X + rI)^{-1}X^TY)
    
  代码

def ridgeRegres(xMat, yMat, lam=0.2):
    """
    xMat - 样本特征 (m,n),每个样本的第一个值既 X0 固定为 1
    yMat - 样本标签 (1,m)
    """
    xTx = xMat.T * xMat

    # 加上 r*I 使得矩阵可逆
    denom = xTx + np.eye(np.shape(xMat)[1]) * lam
    if np.linalg.det(denom) == 0.0:
        return

    w = denom.I * (xMat.T * yMat)
    return w


def ridgeTest(xArr, yArr):
    """
    xArr - 样本特征 (m,n),每个样本的第一个值既 X0 固定为 1
    yArr - 样本标签 (1,m)
    """
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T

    # Y 数据标准化,减去均值
    yMean = np.mean(yMat, 0)
    yMat = yMat - yMean

    # X 数据标准化,减去均值,除以方差
    xMeans = np.mean(xMat, 0)
    xVar = np.var(xMat, 0)
    xMat = (xMat - xMeans) / xVar

    numTestPts = 30
    # 初始化回归系数矩阵,每行是一次迭代产生的回归系数
    wMat = np.zeros((numTestPts, np.shape(xMat)[1]))
    for i in range(numTestPts):
        # 迭代,尝试不同的 r 参数
        ws = ridgeRegres(xMat, yMat, np.exp(i - 10))
        wMat[i, :] = ws.T

    # 返回所有回归系数,为了定量地找到最佳参数值,还需要进行交叉验证
    # 一般讲,r 很小时就和普通回归系数一样,r 很大时回归系数趋向于 0
    return wMat


lasso 方法
  限制所有回归系数的绝对值的和必须小于某个值
    
    (large sum_{k=1}^{n}w_{k}^{2} leqslant lambda)
  
前向逐步回归
  属于一种贪心算法,每步尽可能减少误差
  一开始所有的权重都设为 1,然后每步所做的决策是对某个权重增加或减少一个很小的值
  
  前向逐步回归算法可以得到与 lasso 差不多的效果,但更加简单
  
  代码

def stageWise(xArr, yArr, step=0.01, numIt=100):
    """
    xArr - 样本特征 (m,n),每个样本的第一个值既 X0 固定为 1
    yArr - 样本标签 (1,m)
    step - 步长
    numIt - 迭代次数
    """
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T

    # Y 数据标准化,减去均值
    yMean = np.mean(yMat, 0)
    yMat = yMat - yMean

    # X 数据标准化,减去均值,除以方差
    xMeans = np.mean(xMat, 0)
    xVar = np.var(xMat, 0)
    xMat = (xMat - xMeans) / xVar

    m, n = np.shape(xMat)

    # 初始化回归系数矩阵,每行是一次迭代产生的回归系数
    returnMat = np.zeros((numIt, n))
    ws = np.zeros((n, 1))
    wsMax = ws.copy()

    # 迭代
    for i in range(numIt):
        lowestError = np.inf

        # 每个系数
        for j in range(n):
            # 每个方向
            for sign in [-1, 1]:
                wsTest = ws.copy()

                # 在上一次迭代产生的系数向量的基础上,按指定的步长、指定的方向,调整指定的系数
                wsTest[j] += step * sign

                # 预测结果
                yTest = xMat * wsTest

                # 计算方差
                rssE = ((yMat.A - yTest.A) ** 2).sum()

                # 效果更好则保存该系数
                if rssE < lowestError:
                    lowestError = rssE
                    wsMax = wsTest

        # 得到本次迭代的最佳系数
        ws = wsMax.copy()

        # 保存该最佳系数
        returnMat[i, :] = ws.T

    # 返回结果
    return returnMat


原文地址:https://www.cnblogs.com/moonlight-lin/p/12445390.html