机器学习实战(七)线性回归(Linear Regression)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhq9695/article/details/83375124

目录

0. 前言

1. 假设函数(Hypothesis)

2. 标准线性回归

2.1. 代价函数(Cost Function)

2.2. 梯度下降(Gradient Descent)

2.3. 特征缩放(Feature Scaling)

2.4. 正规方程(Normal Equation)

3. 局部加权线性回归(LWLR)

4. 岭回归

5. lasso

6. 前向逐步线性回归

7. 正则化(Regularization)

8. 实战案例

8.1. 普通线性回归

8.2. 局部加权线性回归

8.3. 岭回归

8.4. 前向逐步线性回归


学习完机器学习实战的线性回归,简单的做个笔记。文中部分描述属于个人消化后的理解,仅供参考。

本篇综合了先前的文章,如有不理解,可参考:

吴恩达机器学习(一)单变量线性回归

吴恩达机器学习(二)多元线性回归

吴恩达机器学习(三)正规方程

吴恩达机器学习(五)正则化

所有代码和数据可以访问 我的 github

如果这篇文章对你有一点小小的帮助,请给个关注喔~我会非常开心的~

0. 前言

线性回归(Linear Regression)是通过线性函数,拟合数据,达到预测的目的。

  • 优点:结果易于理解
  • 缺点:对非线性的数据拟合不好
  • 适用数据类型:数值型和标称型数据(标称型数据将被转换为二值型)

本篇主要介绍一下几种回归:

  • 标准线性回归
  • 局部加权线性回归
  • 岭回归
  • lasso
  • 前向逐步线性回归

在本篇中,参数可表示为 \theta 或者 w ,是一致的。

1. 假设函数(Hypothesis)

假设函数定义如下:

h_{\theta}(x)=\theta_{0}x_0+\theta_{1}x_{1}+...+\theta_{n}x_{n}=\theta^{T}x

通常取 x_0=1 ,表示 \theta_0 为一个截距。

2. 标准线性回归

2.1. 代价函数(Cost Function)

每一次预测的误差为真实值减去预测值,总体的误差可采用误差平方和(error sum of squares)表示:

\sum_{i=1}^{m}(w^Tx^{(i)}-y^{(i)})^2

代价函数可根据误差平方和,采用MSE(mean-square error,均方误差)表示:

J(\theta)=\frac{1}{2m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})^2

其中,乘一个系数并不影响结果。

2.2. 梯度下降(Gradient Descent)

寻找最佳的参数 \theta ,可通过最小化MSE达到。

每一次迭代都将参数往梯度下降方向移动即可达到全局最小值(要求函数为凸函数),梯度下降定义为:

\theta_{j}:=\theta_{j}-\alpha \frac{\partial }{\partial \theta_{j} }J(\theta)\\ \Rightarrow \theta_{j}:=\theta_{j}-\alpha \frac{1}{m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})x_{j}^{(i)}

2.3. 特征缩放(Feature Scaling)

如果 x 的不同维度的取值范围相差甚远,会造成梯度下降速度缓慢,可采用均值归一化:

x_{i}=\frac{x_{i}-\mu_{i}}{s_{i}}

其中,\mu_i 为均值,s_i 为最大值减去最小值。

2.4. 正规方程(Normal Equation)

除了通过梯度下降方法求解参数外,还可通过正规方程直接求解:

\hat{w}=(X^TX)^{-1}X^Ty

当存在矩阵不可逆的情况时(多发生在 n\geqslant m 时),可采用:

  • 删除冗余的特征,如其中两个变量间存在线性关系
  • 对于太多的特征 (m\leq n),删除部分特征,或采用正则化(regularization)

注:矩阵逆运算的时间复杂度通常为 O(n^{3}),所以当 n 较大时,建议使用梯度下降。

3. 局部加权线性回归(LWLR)

局部加权线性回归(Locally Weighted Linear Regression)给待测点附近的每个点赋予一定的权值,采用高斯核函数:

w(i,i)=\exp(-\frac{\left\|x^{(i)}-x\right\|^2}{2\sigma^2})

其中,权值矩阵 W 只含对角线元素,\sigma 为下降到 0 的速率,与 x 越近,权值越大。

对正规方程进行修改,定义为:

\hat{w}=(X^TWX)^{-1}X^TWy

4. 岭回归

前面提到,当 n\geqslant m 时,很有可能 X 不是满秩矩阵,不能进行逆运算,此时可采用岭回归:

\hat{w}=(X^TX+\lambda I)^{-1}X^Ty

其中,I 为 n*n 的单位矩阵,\lambda 为常量。可通过初试设定 \lambda 训练参数 w ,不断测试不同的 \lambda 来得到。

因为单位矩阵 I 是一个主对角线为 1 ,其他为 0 的矩阵,1 就像“岭”一样,这就是岭回归名称的由来。

注:岭回归通过引入 \lambda 来限制所有 w 之和,引入了惩罚项,能够减少不重要的参数,这在统计学中叫做缩减(shrinkage)。

5. lasso

在增加约束的情况下,普通的回归和岭回归都会推导得出以下公式:

\sum_{k=1}^{n}w_k^2\leqslant \lambda

当两个特征相关时,可能会出现一个很大的正系数和一个很大的负系数,可采用 lasso 缩减方法:

\sum_{k=1}^{n}\left|w_k\right|\leqslant \lambda

6. 前向逐步线性回归

前向逐步线性回归是一种贪心算法,一开始所有权重都设定为 1 ,每一次迭代都对一个特征增加或减少很小的值,判断当前误差是否减小。

注:前向逐步线性回归的优点在于可以帮助人们理解模型的改善在哪里,发现哪些参数是对模型有效的,可以尽早停止对无用特征的训练。

7. 正则化(Regularization)

在统计学的缩减中,引入了惩罚项,减少了不重要的参数,同时还可采用正则化(regularization)减少不重要的参数。

对代价函数作如下修改:

J(\theta)=\frac{1}{2m}\sum_{i=1}^{m}(h_{\theta}(x^{(i)})-y^{(i)})^2+\lambda\sum_{i=1}^{n}\theta_{j}^{2}

正则项的目的是使得 \theta_{j} 减小,正则化使得假设函数更加简单,减小发生过拟合概率。

8. 实战案例

8.1. 普通线性回归

# coding:utf-8
from numpy import *
import matplotlib.pyplot as plt

"""
普通线性回归
"""


# 加载数据集
def loadDataSet(fileName):
    numFeat = len(open(fileName).readline().split('\t')) - 1
    dataMat = []
    labelMat = []
    fr = open(fileName)
    for line in fr.readlines():
        lineArr = []
        curLine = line.strip().split('\t')
        for i in range(numFeat):
            lineArr.append(float(curLine[i]))
        dataMat.append(lineArr)
        labelMat.append(float(curLine[-1]))
    return dataMat, labelMat


# 正规方程
def standRegres(xArr, yArr):
    xMat = mat(xArr)
    yMat = mat(yArr).T
    xTx = xMat.T * xMat
    if linalg.det(xTx) == 0.0:
        print("This matrix is singular, cannot do inverse")
        return
    ws = xTx.I * (xMat.T * yMat)
    return ws


# 画数据点和回归直线的图
def drawDataSetAndReg(xMat, yMat, ws):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xMat[:, 1].flatten().A[0], yMat.T[:, 0].flatten().A[0])
    xCopy = xMat.copy()
    xCopy.sort(0)
    yHat = xCopy * ws
    ax.plot(xCopy[:, 1], yHat)
    plt.show()


# 计算预测值和真实值的相关矩阵
def computeCorrcoef(yHat, yMat):
    print(corrcoef(yHat.T, yMat))


if __name__ == '__main__':
    xArr, yArr = loadDataSet('ex0.txt')
    ws = standRegres(xArr, yArr)
    print(ws)
    drawDataSetAndReg(mat(xArr), mat(yArr), ws)
    computeCorrcoef(mat(xArr) * ws, mat(yArr))

8.2. 局部加权线性回归

# coding:utf-8
from numpy import *
import matplotlib.pyplot as plt

"""
局部加权线性回归
"""


# 加载数据集
def loadDataSet(fileName):
    numFeat = len(open(fileName).readline().split('\t')) - 1
    dataMat = []
    labelMat = []
    fr = open(fileName)
    for line in fr.readlines():
        lineArr = []
        curLine = line.strip().split('\t')
        for i in range(numFeat):
            lineArr.append(float(curLine[i]))
        dataMat.append(lineArr)
        labelMat.append(float(curLine[-1]))
    return dataMat, labelMat


# 局部加权线性回归
def lwlr(testPoint, xArr, yArr, k=1.0):
    xMat = mat(xArr)
    yMat = mat(yArr).T
    m = shape(xMat)[0]
    weights = mat(eye((m)))
    for j in range(m):
        diffMat = testPoint - xMat[j, :]
        weights[j, j] = exp(diffMat * diffMat.T / (-2.0 * k ** 2))
    xTx = xMat.T * (weights * xMat)
    if linalg.det(xTx) == 0.0:
        print("This matrix is singular, cannot do inverse")
        return
    ws = xTx.I * (xMat.T * (weights * yMat))
    return testPoint * ws


# 局部加权线性回归测试函数
def lwlrTest(testArr, xArr, yArr, k=1.0):
    m = shape(testArr)[0]
    yHat = zeros(m)
    for i in range(m):
        yHat[i] = lwlr(testArr[i], xArr, yArr, k)
    return yHat


# 画数据点和回归直线的图
def drawDataSetAndReg(xMat, yMat, yHat):
    srtInd = xMat[:, 1].argsort(0)
    xSort = xMat[srtInd][:, 0, :]
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.plot(xSort[:, 1], yHat[srtInd])
    ax.scatter(xMat[:, 1].flatten().A[0], yMat.T[:, 0].flatten().A[0], s=2, c='red')
    plt.show()


if __name__ == '__main__':
    xArr, yArr = loadDataSet('ex0.txt')
    yHat = lwlrTest(xArr, xArr, yArr, 0.1)
    drawDataSetAndReg(mat(xArr), mat(yArr), yHat)
    yHat = lwlrTest(xArr, xArr, yArr, 0.01)
    drawDataSetAndReg(mat(xArr), mat(yArr), yHat)
    yHat = lwlrTest(xArr, xArr, yArr, 0.003)
    drawDataSetAndReg(mat(xArr), mat(yArr), yHat)

8.3. 岭回归

# coding:utf-8
from numpy import *
import matplotlib.pyplot as plt

"""
岭回归
"""


# 加载数据集
def loadDataSet(fileName):
    numFeat = len(open(fileName).readline().split('\t')) - 1
    dataMat = []
    labelMat = []
    fr = open(fileName)
    for line in fr.readlines():
        lineArr = []
        curLine = line.strip().split('\t')
        for i in range(numFeat):
            lineArr.append(float(curLine[i]))
        dataMat.append(lineArr)
        labelMat.append(float(curLine[-1]))
    return dataMat, labelMat


# 岭回归
def ridgeRegres(xMat, yMat, lam=0.2):
    xTx = xMat.T * xMat
    denom = xTx + eye(shape(xMat)[1]) * lam
    if linalg.det(denom) == 0.0:
        print("This matrix is singular, cannot do inverse")
        return
    ws = denom.I * (xMat.T * yMat)
    return ws


# 岭回归测试函数
def ridgeTest(xArr, yArr):
    xMat = mat(xArr)
    yMat = mat(yArr).T
    # y进行均值化
    yMean = mean(yMat, 0)
    yMat = yMat - yMean
    # x进行均值归一化
    xMeans = mean(xMat, 0)
    xVar = var(xMat, 0)
    xMat = (xMat - xMeans) / xVar
    # 测试lambda的次数
    numTestPts = 30
    wMat = zeros((numTestPts, shape(xMat)[1]))
    for i in range(numTestPts):
        ws = ridgeRegres(xMat, yMat, exp(i - 10))
        wMat[i, :] = ws.T
    return wMat

# 画lambda
# ax.plot(X) X为m*n时
# 横坐标为 m,[0, m-1]
# 纵坐标 [:, 1] 为第一条直线, [:, 2]为第二条直线....总共n条直线
def drawLambda(ridgeWeights):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.plot(ridgeWeights)
    plt.show()


if __name__ == '__main__':
    abX, abY = loadDataSet('abalone.txt')
    ridgeWeights = ridgeTest(abX, abY)
    print(ridgeWeights)
    drawLambda(ridgeWeights)

8.4. 前向逐步线性回归

# coding:utf-8
from numpy import *
import matplotlib.pyplot as plt

"""
前向逐步回归
"""


# 加载数据集
def loadDataSet(fileName):
    numFeat = len(open(fileName).readline().split('\t')) - 1
    dataMat = []
    labelMat = []
    fr = open(fileName)
    for line in fr.readlines():
        lineArr = []
        curLine = line.strip().split('\t')
        for i in range(numFeat):
            lineArr.append(float(curLine[i]))
        dataMat.append(lineArr)
        labelMat.append(float(curLine[-1]))
    return dataMat, labelMat


# 均值归一化
def regularize(xMat):
    inMat = xMat.copy()
    inMeans = mean(inMat, 0)
    inVar = var(inMat, 0)
    inMat = (inMat - inMeans) / inVar
    return inMat


# 计算平方误差
def rssError(yArr, yHatArr):
    return ((yArr - yHatArr) ** 2).sum()


# 前向逐步回归
def stageWise(xArr, yArr, eps=0.01, numIt=100):
    xMat = mat(xArr)
    yMat = mat(yArr).T
    # y均值化
    yMean = mean(yMat, 0)
    yMat = yMat - yMean
    # x均值归一化
    xMat = regularize(xMat)
    m, n = shape(xMat)
    returnMat = zeros((numIt, n))
    ws = zeros((n, 1))
    wsTest = ws.copy()
    wsMax = ws.copy()
    for i in range(numIt):
        print(ws.T)
        lowestError = inf
        for j in range(n):
            for sign in [-1, 1]:
                wsTest = ws.copy()
                # eps为学习率,步长
                wsTest[j] += eps * sign
                yTest = xMat * wsTest
                rssE = rssError(yMat.A, yTest.A)
                if rssE < lowestError:
                    lowestError = rssE
                    wsMax = wsTest
        ws = wsMax.copy()
        returnMat[i, :] = ws.T
    return returnMat


if __name__ == '__main__':
    xArr, yArr = loadDataSet('abalone.txt')
    wsMat = stageWise(xArr, yArr, 0.01, 200)
    print(wsMat)

如果这篇文章对你有一点小小的帮助,请给个关注喔~我会非常开心的~

猜你喜欢

转载自blog.csdn.net/zhq9695/article/details/83375124