跳至主要内容

用 Python 掌握线性回归要点

了解如何构成一个回归问题,以及线性回归算法在 Python 中如何工作。
· 15分钟 阅读

使用 AI 探索

数据科学领域以前所未有的速度在发展。它将统计学、线性代数、机器学习、数据库等多个学科纳入其中,并以尽可能有意义的方式加以融合。那么,这个领域的核心究竟是什么,让它如此令人着迷?——强大的统计算法

其中一种非常原始的统计算法就是线性回归。尽管历史悠久,但对于像您这样的数据科学入门者来说,它绝不应该被忽视。理解线性回归的工作原理非常重要,因为这有助于解释一整类统计算法——广义线性模型——的演进。此外,它还会帮助您理解典型统计/机器学习算法中的其他要素,例如:代价函数、系数、优化等。

正如本教程标题所示,您将在本教程中详细讲解线性回归。在深入线性回归背后的理论之前,先来明确一下“回归”这个术语。

理解一个回归问题

回归属于监督学习任务,其中用于预测/统计建模的数据集包含连续标签。不过,我们还是更数学化地定义一下回归问题。

请看下面这张图:training set 来源:Andrew Ng 的讲义

在上图中,X 是对应各房屋居住面积的一组取值(也可视为输入值空间),y 是相应房屋的价格,但注意这些值是由 h 预测得到的。h 是将 X 映射到 y 的函数(常称为预测器)。出于历史原因,这个 h 被称为假设函数。请注意,该数据集只有一个特征,即各房屋的居住面积,为了便于理解,可以将其视为玩具数据集。

注意,这里的预测值是连续的。因此,最终目标是:给定训练集,学习一个函数 $h : \mathcal{X} \rightarrow \mathcal{Y}$,使得 h(x) 能“较好地”预测对应的 y 值。还要记住,X 和 Y 的取值域都是实数,可以表示为:$\mathcal{X} = \mathcal{Y} = \mathbb{IR}$,其中 $\mathbb{IR}$ 表示全体实数集。

一对 (x(i), y(i)) 称为一个训练样本。若训练集包含 m 个样本,且数据集中只有一个特征 x,则训练集可定义为 {(x(i), y(i)) ; i = 1,...,m}。

这里稍微用点数学,避免在最简单的地方出错。根据 Han、Kamber 和 Pei 的说法——

“一般而言,这些方法用于从一个或多个预测(自变量)变量来预测响应(因变量)的取值,且这些变量都是数值型。”——数据挖掘:概念与技术(第 3 版)

就是这么简单!

在理解典型回归问题的过程中,您也看到了如何为其定义一个假设函数。很棒!现在,直接深入线性回归的机制。

线性回归——它如何工作?

在细节之前,不妨看看它是何时被发现的?这要追溯到 18 世纪。伟大的卡尔·弗里德里希·高斯首次提出了最朴素形式的统计回归,不过对此也有不少争论,我们不深入了。如果您对高斯和阿德里安-玛丽·勒让德之间的争论感兴趣,可以看这里。

线性回归或许是统计与机器学习中最知名、也最易理解的算法之一。它诞生于统计学领域,被作为理解输入与输出数值变量关系的模型来研究,但随着时间推移,它已成为现代机器学习工具箱中不可或缺的一部分。

让我们用一个玩具数据集。仍用房价预测数据集来探究,这次包含两个特征。任务不变:预测房价。

house price prediction dataset 来源:Andrew Ng 的讲义

如前所述,现在 x 是二维的,意味着数据集包含两个特征。例如,x1(i) 是训练集中第 i 套房的居住面积,x2(i) 是其卧室数量。

要进行回归,您必须决定如何表示 h。作为初始选择,假设您决定用 x 的线性函数来近似 y:

hθ(x) = θ0 + θ1x1 + θ2x2

这里,θi 是将 $\mathcal{X}$ 映射到 $\mathcal{Y}$ 的线性函数空间的参数(也称权重)。更直白地说,这些参数用于将 $\mathcal{X}$ 准确映射到 $\mathcal{Y}$。为简化理解,后续将省略 hθ(x) 中的 θ 下标,直接写作 h(x)。进一步简化记号,我们约定令 x0 = 1(这是截距项),于是

function

其中右侧将 θ 和 x 都视为向量,这里的 n 是输入实例的个数(不包含 x0)。

但此时一个关键问题出现了:如何选择或学习参数 θ?您无法更改输入实例来预测价格,能调的只有这些 θ 参数。

一个显而易见的方法是让 h(x) 尽量接近 y,至少在现有训练样本上如此。为了更形式化地理解这一点,我们尝试定义一个函数:对每组 θ 的取值,衡量各个 h(x(i)) 与对应 y(i) 的接近程度。这个函数应如下所示:

function

来源:StackOverflow

至于为何取平方项而非绝对值项,可以将平方项视为后续训练回归模型时操作上的优势。若想深入了解,可参考此处。

您刚刚看到了数据科学/机器学习/统计领域中最重要的公式之一。这被称为代价函数。

这是一个关键推导,因为它不仅催生了线性回归的下一步演化(普通最小二乘),也奠定了一整类线性建模算法的基础(还记得之前提到的广义线性模型吧)。

需要注意的是,线性回归通常可分为两种基本形式:

  • 简单线性回归(SLR):只涉及两个变量(您最开始看到的那个)
  • 多元线性回归(MLR):涉及两个以上的变量(您刚看到的那个)

这些概念很直接,但也常引起混淆。

您已经打下了线性回归的基础。接下来将学习如何估计上文中的参数。参数估计本质上就是线性回归的训练。训练线性回归有多种方法,其中最受欢迎的是普通最小二乘(OLS)。因此,用 OLS 训练得到的线性回归模型常被称为普通最小二乘线性回归,或简称最小二乘回归。

注意,在此语境下的参数也称为模型系数。

优化线性回归模型——多种方法:

学习/训练线性回归模型,本质上是用手头的数据来估计表示中使用的系数/参数的取值。

本节将简要介绍几种用于训练线性回归模型的技术。

最小二乘回归:

上一节的结论是要选择 θ 来最小化 J(θ)。为此,使用一种搜索算法:从某个“初始猜测”的 θ 开始,迭代地更新 θ 使 J(θ) 变小,直到收敛到使 J(θ) 最小的 θ。具体地,考虑梯度下降算法,它从某个初始 θ 出发,反复执行如下更新:

function 来源:Andrew Ng 的讲义

(对 j = 0, …, n 的所有取值同时进行此更新。)这里,α 被称为学习率。该算法的直觉是:在 J 降幅最大的方向上不断前进一步。α 控制了每次沿 J 下降方向前进的步长。可以用下图形象表示:

gradient descent 来源:ml-cheatsheet

直观来说,上式表示 J 相对于参数 θj 的微小变化,以及它如何影响 θj 的初始值。但请注意,这里涉及偏导数。完整推导过程超出本教程范围。

只需记住,对于单个训练样本,更新规则为:

function 来源:ml-cheatsheet

该规则称为 LMS 更新规则(LMS 即 “least mean squares”,最小均方),也称为Widrow-Hoff 学习规则。

关于 OLS,总结几点:

“普通最小二乘法试图最小化残差平方和。这意味着,给定穿过数据的回归线,我们计算每个数据点到回归线的距离、将其平方,并把所有平方误差加总。普通最小二乘要最小化的正是这个量。”——Jason Brownlee

用梯度下降进行优化

在前面的训练规则中,您已经看到了如何在此情境下使用梯度下降。本质上,梯度下降是通过迭代地最小化模型在训练数据上的误差来优化系数取值的过程。

更简要地说,它从每个系数的随机初值开始。对每对输入与输出值计算平方误差和。使用学习率作为缩放因子,按使误差减小的方向更新系数。重复这一过程,直到达到最小平方误差和,或不再有进一步改进为止。

这里的 α(学习率)非常重要,因为它决定了每次迭代改进步长的大小。

常见的梯度下降有两种变体:

  • 每一步都查看整个训练集里的每个样本的方法,称为批量梯度下降。
  • 重复遍历训练集,每遇到一个训练样本,就仅根据该样本的误差梯度来更新参数的方法,称为随机梯度下降(也称增量梯度下降)。

本教程关于梯度下降就到这里。接下来看看优化线性回归的另一种方式,即正则化。

正则化

DataCamp 已经有一篇不错的正则化入门文章。在继续阅读本篇之前,您可以先看看。

通常,正则化方法通过惩罚取值极大的特征系数来降低误差。它不仅能降低误差率,还能降低模型复杂度。当您处理特征数量庞大的数据集且基线模型无法区分特征重要性时,这尤为有用(并非数据集中的所有特征都同等重要,对吧?)。

线性回归中常见的两种正则化方式:

Lasso 回归:添加一个等于系数绝对值的惩罚项(也称为L1 正则化)。惩罚项如下所示:penalty term

其中,

  • $\lambda$ 是您加入的常数因子,用于控制误差改进的速度(学习率)
  • 数据集有 (M+1) 个特征,所以上标从 0 到 M。wj 为权重/系数。

Ridge 回归:添加一个等于系数平方的惩罚项(也称为L2 正则化)。惩罚项如下所示:penalty term

学习线性回归还值得花时间吗?

当然值得!您已经看到,线性回归优雅地引入了机器学习中一些最关键的概念,如代价函数、优化、变量关系等等。即使您在构建神经网络,这些内容也同样重要。适用性也许在某些地方不同,但总体概念完全一致。若不理解这些基础,您就难以解释为何神经网络表现不佳。

此外,从变量间关系这一简单思想出发,衍生出了如此多的概念,更重要的是,它催生了一整个算法家族——广义线性模型。因此,对于有志于数据科学/机器学习/人工智能的实践者而言,这一算法绝对不容忽视。您现在应该已经明白了!

现在,您将亲自用 Python 实现一个简单的线性回归。这会很有趣!

Python 案例研究

在这个案例中,首先使用 Python 的 Statsmodel 库。它是一个非常流行的库,提供用于许多统计模型估计的类与函数,以及统计检验和统计数据探索。数据使用著名的 波士顿房价数据集。强大的 scikit-learn 自带该数据集,您无需单独下载。

先导入 statsmodels 库和数据集:

import statsmodels.api as sm
from sklearn import datasets

data = datasets.load_boston()

Scikit-learn 提供了该数据集的便捷描述,可通过以下方式查看:

print (data.DESCR)
Boston House Prices dataset
===========================

Notes
------
Data Set Characteristics:  

    :Number of Instances: 506

    :Number of Attributes: 13 numeric/categorical predictive

    :Median Value (attribute 14) is usually the target

    :Attribute Information (in order):
        - CRIM     per capita crime rate by town
        - ZN       proportion of residential land zoned for lots over 25,000 sq.ft.
        - INDUS    proportion of non-retail business acres per town
        - CHAS     Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)
        - NOX      nitric oxides concentration (parts per 10 million)
        - RM       average number of rooms per dwelling
        - AGE      proportion of owner-occupied units built prior to 1940
        - DIS      weighted distances to five Boston employment centres
        - RAD      index of accessibility to radial highways
        - TAX      full-value property-tax rate per $10,000
        - PTRATIO  pupil-teacher ratio by town
        - B        1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town
        - LSTAT    % lower status of the population
        - MEDV     Median value of owner-occupied homes in $1000's

    :Missing Attribute Values: None

    :Creator: Harrison, D. and Rubinfeld, D.L.

This is a copy of UCI ML housing dataset.
http://archive.ics.uci.edu/ml/datasets/Housing


This dataset was taken from the StatLib library which is maintained at Carnegie Mellon University.

The Boston house-price data of Harrison, D. and Rubinfeld, D.L. 'Hedonic
prices and the demand for clean air', J. Environ. Economics & Management,
vol.5, 81-102, 1978.   Used in Belsley, Kuh & Welsch, 'Regression diagnostics
...', Wiley, 1980.   N.B. Various transformations are used in the table on
pages 244-261 of the latter.

The Boston house-price data has been used in many machine learning papers that address regression
problems.   

**References**

   - Belsley, Kuh & Welsch, 'Regression diagnostics: Identifying Influential Data and Sources of Collinearity', Wiley, 1980. 244-261.
   - Quinlan, R. (1993). Combining Instance-Based and Model-Based Learning. In Proceedings on the Tenth International Conference of Machine Learning, 236-243, University of Massachusetts, Amherst. Morgan Kaufmann.
   - many more! (see http://archive.ics.uci.edu/ml/datasets/Housing)

在应用线性回归之前,需要先准备数据并分离特征与标签。本例中的标签是 MEDV(房价中位数)。可以通过 feature_names 属性访问数据集的特征。

此处一些 pandas 知识会很有用。若想快速回顾 pandas 基础概念,这份速查表值得一看。

# Pandas 和 NumPy 导入
import numpy as np
import pandas as pd

# 设置特征  
df = pd.DataFrame(data.data, columns=data.feature_names)

# 设置目标
target = pd.DataFrame(data.target, columns=["MEDV"])

此时,在将线性回归应用于数据之前,您需要考虑几个重要因素。您本可以在更早学到这些,但在这个时点学习会更有体感。

  • 线性假设:线性回归最适合用于捕捉输入变量与输出之间的关系。为此,线性回归假设这种关系是线性的(而这并非总是成立)。不过,您总可以对数据做变换以维持线性关系。例如,如果数据呈指数关系,可以应用对数变换来使其线性化。

  • 特征之间的共线性:共线性是衡量数据集中某个特征重要性的数学指标。当特征之间高度相关时,线性回归难以恰当地近似关系,并易于过拟合。因此,最好在应用线性回归前检测并删除高度相关的特征。若想深入了解,欢迎查看这篇优秀的 Kaggle 内核。

现在动手实践。为简化起见,先只取 RM — 平均房间数 这一特征。注意 Statsmodels 默认不添加常数项(回忆 θ0)。先看看不加常数项的回归模型:

X = df["RM"]
y = target["MEDV"]

# 拟合并由模型进行预测
model = sm.OLS(y, X).fit()
predictions = model.predict(X)

# 输出统计结果
model.summary()
regression results

这输出是什么!第一次看到确实有点大而全。我们逐条看最关键的点:

  • 首先注意,您使用的是 OLS 方法来训练线性回归模型。

  • 有一个与 R-Squared(决定系数)对应的数值。R-squared 表示模型“解释的方差百分比”。即误差的方差相较于因变量方差减少的比例。R-squared 取值范围为 0 到 1,常以 0% 到 100% 的百分比表示。R-squared 能帮助您估计因变量随自变量变化而变化的关系,但它并不会告诉您所选模型好坏,也不会说明数据和预测是否有偏。高或低的 R-squared 并不一定是好或坏,因为它不表示模型的可靠性,也不表明您选择的回归是否合适。一个好的模型可能有较低的 R-squared,而一个拟合不佳的模型也可能有较高的 R-squared,反之亦然。

  • 系数(coef)为 3.634,表示当 RM 增加 1 时,MEDV 的预测值增加 3.634。

  • RM 的 95% 置信区间意味着模型以 95% 的置信度预测 RM 的系数位于 3.548 到 3.759 之间。

目前先关注这些要点(也可以忽略警告)。

可以很容易地为线性回归模型添加常数项——通过 X = sm.add_constant(X)(X 为包含输入(自变量)的数据框)。

X = sm.add_constant(X)

model = sm.OLS(y, X).fit()
predictions = model.predict(X)

model.summary()
regression results

可以清楚地看到,加入常数项对系数产生了直接影响。没有常数项时,模型通过原点;现在有了 y 截距 -34.67。同时,RM 预测器的斜率也从 3.634 变为 9.1021(RM 的 coef)。

现在拟合一个包含多个变量的回归模型——在 RM 之外加入 LSTAT(低社会地位人口的百分比)。训练(拟合)过程与之前完全相同:

X = df[["RM", "LSTAT"]]
y = target["MEDV"]

model = sm.OLS(y, X).fit()
predictions = model.predict(X)

model.summary()
regression results

来解读一下:

该模型的 R-squared 更高——0.948,基本意味着该模型解释了因变量 94.8% 的方差。现在来理解 RM 与 LSTAT 两个变量与房价中位数之间的关系。当 RM 增加 1 时,MEDV 增加 4.9069;当 LSTAT 增加 1 时,MEDV下降 0.6557。这表明 RM 与 LSTAT 在预测(或估计)房价中位数方面具有统计显著性。

也可以用通俗的话来解释:

  • 房间数较少的房子更可能价格较低。
  • 在社会地位较低的区域,房价更可能偏低。

现在是不是更有感觉了?

以上是在 Statsmodels 中进行单变量和多变量线性回归的示例。您的作业是用更多特征进行探索和解读。

接下来看看如何用您熟悉的 scikit-learn 实现线性回归。数据集已导入,但需要导入 linear_model 类。

from sklearn import linear_model
X = df
y = target["MEDV"]

lm = linear_model.LinearRegression()
model = lm.fit(X,y)

模型训练完成。该 sklearn 实现同样使用 OLS。我们来对前五个样本的 MEDV 值做些预测。

predictions = lm.predict(X)
print(predictions[0:5])
[30.00821269 25.0298606  30.5702317  28.60814055 27.94288232]

如果您想了解更多模型细节(如 R-squared、系数等),也很容易。

lm.score(X,y)
0.7406077428649427
lm.coef_
array([-1.07170557e-01,  4.63952195e-02,  2.08602395e-02,  2.68856140e+00,
       -1.77957587e+01,  3.80475246e+00,  7.51061703e-04, -1.47575880e+00,
        3.05655038e-01, -1.23293463e-02, -9.53463555e-01,  9.39251272e-03,
       -5.25466633e-01])

总结

太棒了!您已经读到了最后。覆盖最简单也最基础的算法之一并不容易,但您做得很好。您不仅熟悉了简单线性回归,还学习了机器学习中许多基础方面、术语和要素,并完成了一个深入的 Python 案例研究。

本教程也可以作为一种激励,促使您从零实现线性回归。若真要动手,以下是简要步骤:

  • 计算数据的均值与方差
  • 计算协方差
  • 估计系数
  • 进行预测

以下是为编写本教程而参考的部分资料:

如果您想进一步学习线性分类器,请参加 DataCamp 的Python 线性分类器课程。

查看我们的线性回归的正规方程教程。

主题