65.9K
CodeProject 正在变化。 阅读更多。
Home

实现机器学习的分步指南 VIII - 线性回归

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2019年5月28日

CPOL

3分钟阅读

viewsIcon

10593

易于实现的机器学习

引言

变量之间普遍存在关系。实际上,这种关系可以分为两类,即确定关系和不确定关系。确定关系可以用函数表示。确定关系也称为相关关系,可以用回归分析进行研究。

通常,线性回归模型为:

h_{\theta}\left(x\right)=\theta^{T}x

最优 \theta 可以通过最小化损失函数来确定:

J\left(\theta\right)=\sum^{m}_{i=1}\left(h_{\theta}\left(x\right)^{(i)}-y^{(i)}\right)^{2}

回归模型

线性回归包括线性回归、局部加权线性回归、岭回归、Lasso 回归和逐步线性回归。

线性回归

线性回归的参数 \theta 可以通过梯度下降法或**正则表达式**计算。 由于梯度下降法已在逐步实现机器学习 IV - 逻辑回归中介绍,因此我们在本文中介绍使用正则表达式的解法。

首先,计算损失函数的导数:

\begin{align*} \nabla_{\theta}J\left(\theta\right)& =\nabla_{\theta}\frac{1}{2}\left(X\theta-Y\right)^{T}\left(X\theta-Y\right)\\ &= \frac{1}{2}\nabla_{\theta}\left(\theta^{T}X^{T}X\theta-\theta^{T}X^{T}Y-YX\theta+Y^{T}Y\right)\\ &=\frac{1}{2}\nabla_{\theta}tr\left(\theta^{T}X^{T}X\theta-\theta^{T}X^{T}Y-YX\theta+Y^{T}Y\right)\\ &=\frac{1}{2}\nabla_{\theta}\left(tr\theta^{T}X^{T}X\theta-2trYX\theta\right)\\ &=\frac{1}{2}\nabla_{\theta}\left(X^{T}X\theta+X^{T}X\theta-2X^{T}Y\right)\\ &= X^{T}X\theta-X^{T}Y \end{align*}

然后,使导数等于 0,我们可以得到:

X^{T}X\theta=X^{T}Y

最后, \theta 为:

\theta=\left(X^{T}X\right)^{-1}X^{T}Y

其中 X 是训练数据,Y 是相应的标签。线性回归的代码如下所示:

    def standardLinearRegression(self, x, y):
        if self.norm_type == "Standardization":
            x = preProcess.Standardization(x)
        else:
            x = preProcess.Normalization(x)

        xTx = np.dot(x.T, x)
        if np.linalg.det(xTx) == 0:   # calculate the Determinant of xTx
            print("Error: Singluar Matrix !")
            return
        w = np.dot(np.linalg.inv(xTx), np.dot(x.T, y))
        return w

局部加权线性回归

由于线性回归使用最小均方误差 (MMSE) 的无偏估计,因此存在欠拟合。为了解决这个问题,我们为要预测的点周围的点分配权重。 然后,我们对其应用正态回归分析。 局部加权线性回归的损失函数为:

J\left(\theta\right)=\sum^{m}_{i=1}w^{(i)}\left(h_{\theta}\left(x\right)^{(i)}-y^{(i)}\right)^{2}

与线性回归一样,我们计算损失函数的导数并使其等于 0。最优 \theta 为:

\theta=\left(X^{T}WX\right)^{-1}X^{T}WY

局部加权线性回归中的权重类似于 SVM 中的核函数,由下式给出:

w^{(i)}=exp\left(-\frac{\left(x^{(i)}-x\right)^{T}\left(x^{(i)}-x\right)}{2\tau^{2}}\right)

局部加权线性回归的代码如下所示:

    def LWLinearRegression(self, x, y, sample):
        if self.norm_type == "Standardization":
            x = preProcess.Standardization(x)
        else:
            x = preProcess.Normalization(x)

        sample_num = len(x)
        weights = np.eye(sample_num)
        for i in range(sample_num):
            diff = sample - x[i, :]
            weights[i, i] = np.exp(np.dot(diff, diff.T)/(-2 * self.k ** 2))
        xTx = np.dot(x.T, np.dot(weights, x))
        if np.linalg.det(xTx) == 0:
            print("Error: Singluar Matrix !")
            return
        result = np.dot(np.linalg.inv(xTx), np.dot(x.T, np.dot(weights, y)))
        return np.dot(sample.T, result)

岭回归

如果特征维度大于样本数,则输入矩阵不是满秩的,其逆矩阵不存在。 为了解决这个问题,岭回归添加 \lambda I 使矩阵非奇异。 实际上,这等于在岭回归的损失函数上添加 **L2 正则化**,即:

J\left(\theta\right)=\sum^{m}_{i=1}\left(h_{\theta}\left(x\right)^{(i)}-y^{(i)}\right)^{2}+\lambda\left| |\theta| \right|_{2}

与线性回归一样,我们计算损失函数的导数并使其等于 0。最优 \theta 为:

\theta=\left(X^{T}X+\lambda^{2}I\right)^{-1}X^{T}Y

岭回归的代码如下所示:

    def ridgeRegression(self, x, y):
        if self.norm_type == "Standardization":
            x = preProcess.Standardization(x)
        else:
            x = preProcess.Normalization(x)

        feature_dim = len(x[0])
        xTx = np.dot(x.T, x)
        matrix = xTx + np.exp(feature_dim)*self.lamda
        if np.linalg.det(xTx) == 0:
            print("Error: Singluar Matrix !")
            return
        w = np.dot(np.linalg.inv(matrix), np.dot(x.T, y))
        return w

Lasso 回归

与岭回归一样,Lasso 回归在损失函数上添加 **L1 正则化**,即:

J\left(\theta\right)=\sum^{m}_{i=1}\left(h_{\theta}\left(x\right)^{(i)}-y^{(i)}\right)^{2}+\lambda\left| |\theta| \right|_{1}

由于 L1 正则化包含绝对值表达式,因此损失函数在任何地方都不可导。 因此,我们应用**坐标下降法**(CD)。 CD 在每次迭代时沿一个方向获得最小值,即:

\theta^{i+1}_{j}=arg\min\limits_{\theta_{i}} J\left(\theta_{1},\theta_{2}^{(i-1)},..., \theta_{n}^{(i-1)}\right)

我们可以得到 CD 的闭合解,其由下式给出:

\begin{equation} \left\{              \begin{array}{lr}              \theta_{j}=\rho_{j}+\lambda &\ if\ \rho_{i}<-\lambda  \\           \theta_{j}=0 &if\ -\lambda\leq\rho_{i}\leq\lambda \\ \theta_{j}=\rho_{j}-\lambda &\ if\ \rho_{i}>\lambda  \\              \end{array} \right. \end{equation}

其中

\rho_{j}=\sum_{i=1}^{m}x_{j}^{(i)}\left(y^{(i)}-\sum_{k\ne j}^{n}\theta_{k}x_{k}^{(i)}\right)\\=\sum_{i=1}^{m}x_{j}^{(i)}\left(y^{(i)}-\hat y_{pred}^{(i)}+\theta_{j}x^{(i)}_{j}\right)

Lasso 回归的代码如下所示:

    def lassoRegression(self, x, y):
        if self.norm_type == "Standardization":
            x = preProcess.Standardization(x)
        else:
            x = preProcess.Normalization(x)

        y = np.expand_dims(y, axis=1)
        sample_num, feataure_dim = np.shape(x)
        w = np.ones([feataure_dim, 1])
        for i in range(self.iterations):
            for j in range(feataure_dim):
                h = np.dot(x[:, 0:j], w[0:j]) + np.dot(x[:, j+1:], w[j+1:])
                w[j] = np.dot(x[:, j], (y - h))
                if j == 0:
                    w[j] = 0
                else:
                    w[j] = self.softThreshold(w[j])
        return w

逐步线性回归

逐步线性回归与 Lasso 类似,它在每次迭代时应用贪婪算法来获得最小值,而不是 CD。 逐步线性回归在每次迭代时在权重上添加或减少一小部分。 逐步线性回归的代码如下所示:

    def forwardstepRegression(self, x, y):
        if self.norm_type == "Standardization":
            x = preProcess.Standardization(x)
        else:
            x = preProcess.Normalization(x)

        sample_num, feature_dim = np.shape(x)
        w = np.zeros([self.iterations, feature_dim])
        best_w = np.zeros([feature_dim, 1])
        for i in range(self.iterations):
            min_error = np.inf
            for j in range(feature_dim):
                for sign in [-1, 1]:
                    temp_w = best_w
                    temp_w[j] += sign * self.learning_rate
                    y_hat = np.dot(x, temp_w)
                    error = ((y - y_hat) ** 2).sum()           # MSE
                    if error < min_error:                   # save the best parameters
                        min_error = error
                        best_w = temp_w
            w = best_w
        return w

结论与分析

有很多解决方案可以获得线性回归的最优参数。 在本文中,我们只介绍一些基本算法。 最后,让我们将我们的线性回归与 Sklearn 中的线性回归进行比较,检测性能显示如下:

Sklearn 线性回归性能

我们的线性回归性能

性能看起来相似。

本文相关的代码和数据集可以在 MachineLearning 中找到。

历史

  • 2019年5月28日:初始版本
© . All rights reserved.