目录
1. 学习内容
1. 多项式回归
2. sklearn中的pipeline
3. 偏差与方差
4. L1正则与L2正则
2. 多项式回归
2.1 什么是多项式回归
线性回归自身有一个很强的假设,那就是数据存在线性关系。然而,有的时候很多数据未必会满足这种情况,例如图2.1的这种情况。

对于数据并不满足线性关系的情况,往往会考虑采用多项式回归来进行预测。研究一个因变量与一个或多个自变量间多项式的回归分析方法,称为多项式回归。多项式回归是线性回归模型的一种,其回归函数关于回归系数是线性的。其中自变量x和因变量y之间的关系被建模为n次多项式[1]。
2.2 一元多项式回归的实现
2.2.1 手动实现
手动实现多项式回归,首先需要我们根据数据的分布大致判断多项式的最高次数是多少。例如上面的这幅图我们可以大致判断数据应该满足一个二次多项式。然后,我们将数据的1次方到n次方(n为判断的最高次数)全部计算一遍并整理成一个新的数据。最后,我们就可以用线性回归的方式来进行预测。
具体代码如下:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# X是从均匀分布中随机采样得到的
np.random.seed(2020)
X = np.random.uniform(-3, 3, size = 100)
X = X.reshape(-1, 1)
y = 0.5 + X ** 2 + X + 2 + np.random.normal(0, 1, size = 100).reshape(-1, 1)
X_new = np.c_[X, X ** 2]
lr = LinearRegression()
lr.fit(X_new, y)
predictions = lr.predict(X_new)
plt.plot(np.sort(X, axis = 0), predictions[np.argsort(X.flatten())], c = 'r')
plt.scatter(X, y)
plt.show()

2.2.2 调用sklearn中的相关模块
需要说明的是,sklearn中并没有直接根据原数据计算多项式回归的相关类。因为根据上面的介绍,多项式回归需要在原有数据的基础上添加新的特征即各原特征的各次幂。因此,这个过程可以视为一种对数据的预处理。进而,相关的模块实际上是存放在sklearn.preprocessing中的。实际上,最后还是需要利用线性回归模型来解决多项式回归的问题。
具体代码如下:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
# X是从均匀分布中随机采样得到的
np.random.seed(2020)
X = np.random.uniform(-3, 3, size = 100)
X = X.reshape(-1, 1)
y = 0.5 + X ** 2 + X + 2 + np.random.normal(0, 1, size = 100).reshape(-1, 1)
# degree表示最高次幂
poly = PolynomialFeatures(degree = 2)
poly.fit(X)
# 计算特征的各次幂(从0到n)
X_new = poly.transform(X)
lr = LinearRegression()
lr.fit(X_new, y)
predictions = lr.predict(X_new)
plt.plot(np.sort(X, axis = 0), predictions[np.argsort(X.flatten())], c = 'r')
plt.scatter(X, y)
plt.show()

另外,需要说明的是利用PolynomialFeatures生成的数据是包含特征的0次幂的,而2.2.1中手动拼接的数据中不含有0次幂。
2.3 多元多项式回归的实现
当原数据不止有一个特征的时候,就需要用到多元多项式回归。同一元多项式回归,多元多项式回归也需要向原数据中添加新的特征。只不过,添加的不仅仅是各个特征的各次幂,还包括不同特征之间的成绩。具体形式可以通过如下代码来推测:
import numpy as np
from sklearn.preprocessing import PolynomialFeatures
X = np.arange(1, 11).reshape(5, 2)
poly = PolynomialFeatures()
poly.fit(X)
X = poly.transform(X)
print(X.shape)
print(X)
输出结果如下:
(5, 6)
[[ 1. 1. 2. 1. 2. 4.]
[ 1. 3. 4. 9. 12. 16.]
[ 1. 5. 6. 25. 30. 36.]
[ 1. 7. 8. 49. 56. 64.]
[ 1. 9. 10. 81. 90. 100.]]
处理后的数据各列从左到右分别为。
同理,我们在这种情况下可以继续使用线性回归来解决多元多项式回归的问题。
3. sklearn中的pipeline
一个回归或者分类的过程实际上需要很多步骤,以上面的多项式回归为例,数据需要先生成其他新的特征,然后进行正则化,最后送到线性回归模型中进行训练。那么有没有什么方法可以将这三个过程整合到一起,就像定义一个新的类一样,将最原始的数据传入后它可以自动依次进行上面的三个过程,最后直接输出预测结果呢?答案就是使用sklearn中的管道操作pipeline。
管道就是将这些步骤都放在一起。参数传入一个列表,列表中的每个元素是管道中的一个步骤。每个元素是一个元组,元组的第一个元素是名字(字符串),第二个元素是实例化[1]。
上述过程的具体代码如下:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
# X是从均匀分布中随机采样得到的
np.random.seed(2020)
X = np.random.uniform(-3, 3, size = 100)
X = X.reshape(-1, 1)
y = 0.5 + X ** 2 + X + 2 + np.random.normal(0, 1, size = 100).reshape(-1, 1)
poly_reg = Pipeline([
('poly', PolynomialFeatures(degree = 2)),
('std_scale', StandardScaler()),
('lin_reg', LinearRegression())
])
poly_reg.fit(X, y)
predictions = poly_reg.predict(X)
plt.plot(np.sort(X, axis = 0), predictions[np.argsort(X.flatten())], c = 'r')
plt.scatter(X, y)
plt.show()

可以发现,使用了管道后,代码量大幅度缩短。这令我们的代码更加简洁易读。
另外,管道本身也可作为模型传入网格搜索函数中进行调参。具体用法参考[4]。
4. 偏差与方差
4.1 什么是偏差和方差
偏差(bias):偏衡量模型的预测值与实际值之间的偏离关系。例如某模型的准确度为96%,则说明是低偏差;反之,如果准确度只有70%,则说明是高偏差[2]。
方差(variance):描述训练数据在不同迭代阶段的训练模型中,预测值的变化波动情况(或称之为离散情况)。从数学角度看,可以理解为每个预测值与预测均值差的平方和的再求平均数。通常在模型训练中,初始阶段模型复杂度不高,为低方差;随着训练量加大,模型逐步拟合训练数据,复杂度开始变高,此时方差会逐渐变高[2]。
一般,高偏差意味着欠拟合,而高方差则意味着过拟合。
4.2 偏差与方差产生的原因
一个模型有偏差,主要的原因可能是对问题本身的假设是不正确的,或者欠拟合。如:针对非线性的问题使用线性回归,或者采用的特征和标签完全没有关系,如用学生姓名预测考试成绩,就会导致高偏差。
高方差表现为数据的一点点扰动就会较大地影响模型,即模型没有完全学习到问题的本质,而是学习到很多噪音。进一步的原因可能是使用的模型太复杂,如:使用高阶多项式回归,也就是过拟合。
有一些算法天生就是高方差的算法,如kNN算法。非参数学习算法通常都是高方差,因为不对数据进行任何假设。
有一些算法天生就是高偏差算法,如线性回归。参数学习算法通常都是高偏差算法,因为模型的生成依赖于原始数据。
4.3 如何避免高偏差和高方差
首先,需要明确的是偏差和方差是无法消除的,只能是尽可能地减小其影响。
偏差与方差往往是此消彼长的,因此我们需要在二者之间找到平衡。如果要避免高偏差,需要尽量选择正确的模型。而如果要避免高方差,则可以考虑的方面有很多,如降低模型复杂度、减少数据维度、降噪、适当增加样本数(使数据更具有一般性和代表性)、使用验证集以及接下来要介绍的正则化。其中需要注意的是,当样本数增加到一定程度后,高方差的降低程度将会削弱。同时,由于数据规模的增加,计算时间将会加大。因此,用于训练的样本数量应当恰到好处。
5. L1正则与L2正则
5.1 用正则化来降低模型误差
模型误差=偏差+方差 +不可避免的误差(噪音)。在机器学习领域中最重要就是解决过拟合的问题,也就是降低模型的方差。这里介绍用L1正则和L2正则来降低方差,进而降低模型误差。
先来看一个过拟合的实例,如下:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
# X是从均匀分布中随机采样得到的
np.random.seed(2020)
X = np.random.uniform(-3, 3, size = 100)
X = X.reshape(-1, 1)
y = 0.5 + X ** 2 + X + 2 + np.random.normal(0, 1, size = 100).reshape(-1, 1)
poly_reg = Pipeline([
('poly', PolynomialFeatures(degree = 30)),
('std_scale', StandardScaler()),
('lin_reg', LinearRegression())
])
poly_reg.fit(X, y)
predictions = poly_reg.predict(X)
plt.plot(np.sort(X, axis = 0), predictions[np.argsort(X.flatten())], c = 'r')
plt.scatter(X, y)
plt.show()

5.2 L1正则
L1正则化,就是在目标函数中加了L1范数这一项。使用L1正则化的模型也叫做Lasso回归。需要注意的是正则项中不包括对常数也就是截距的正则,因为对截距进行控制并没有什么意义。
L1正则化可以使得参数稀疏化,即得到的参数是一个稀疏矩阵(原因参考[3])。因此,L1正则可以用来进行特征选择。
具体代码如下:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import Lasso
from sklearn.preprocessing import StandardScaler
# X是从均匀分布中随机采样得到的
np.random.seed(2020)
X = np.random.uniform(-3, 3, size = 100)
X = X.reshape(-1, 1)
y = 0.5 + X ** 2 + X + 2 + np.random.normal(0, 1, size = 100).reshape(-1, 1)
def LassoRegression(degree, alpha):
return Pipeline([
('poly', PolynomialFeatures(degree = degree)),
('std_scaler', StandardScaler()),
('lasso_reg', Lasso(alpha = alpha))
])
lasso_reg = LassoRegression(30, 0.0001)
lasso_reg.fit(X, y)
predictions = lasso_reg.predict(X)
plt.plot(np.sort(X, axis = 0), predictions[np.argsort(X.flatten())], c = 'r')
plt.scatter(X, y)
plt.show()

其中越大,参数就越小,过拟合程度就越低,不过相应地欠拟合程度也在提高。因此,
的取值也要适当。
5.3 L2正则
L2正则是在目标函数中加了L2范数的平方这一项(注意不是L2范数)。使用L2正则化的模型也叫做Ridge回归。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.preprocessing import StandardScaler
# X是从均匀分布中随机采样得到的
np.random.seed(2020)
X = np.random.uniform(-3, 3, size = 100)
X = X.reshape(-1, 1)
y = 0.5 + X ** 2 + X + 2 + np.random.normal(0, 1, size = 100).reshape(-1, 1)
def RidgeRegression(degree, alpha):
return Pipeline([
('poly', PolynomialFeatures(degree = degree)),
('std_scaler', StandardScaler()),
('ridge_reg', Ridge(alpha = alpha))
])
ridge_reg = RidgeRegression(30, 0.0001)
ridge_reg.fit(X, y)
predictions = ridge_reg.predict(X)
plt.plot(np.sort(X, axis = 0), predictions[np.argsort(X.flatten())], c = 'r')
plt.scatter(X, y)
plt.show()

6. 参考文献
4. sklearn中Pipeline的使用方法__illusion_的博客-优快云博客_pipeline sklearn