-
2019-07-04 21:27:05
线性回归是最简单的一种机器学习算法,可分为一元线性回归和多元线性回归(也就是特征数量不同),这里先主要介绍一元线性回归。举一个简单的例子,有一组带标签样本(xi,yi),x是特征y是响应变量(标签),认为他们服从线性方程:
y i = a + b x i + c y_i=a+bx_i+c yi=a+bxi+c
其中b是回归系数,a+bxi是回归函数,c则是随机误差。
我们要做的就是估计a、b,使得函数对数据尽可能好地拟合,同时也具有较好的泛化能力。虽然实际上我们可以从理论上推导出a、b的最优值,但是既然是机器学习,就应该把这些工作交给计算机完成。按照机器学习的流程,我们首先为ab定义一个初始点(比如(0,0)),然后定义损失函数,这里我们采取最小二乘法,定义每点的误差再相加,得到的函数称为RSS(Residual Sum of squares):
R S S = ( y 1 − β ^ 0 − β ^ 1 x 1 ) 2 + ( y 2 − β ^ 0 − β ^ 1 x 2 ) 2 + . . . + ( y n − β ^ 0 − β ^ 1 x n ) 2 RSS = (y_1 - \hat\beta_0 - \hat\beta_1 x_1)^2 + (y_2 - \hat\beta_0 - \hat\beta_1 x_2)^2 + ... + (y_n - \hat\beta_0 - \hat\beta_1 x_n)^2 RSS=(y1−β^0−β^1x1)2+(y2−β^0−β^1x2)2+...+(yn−β^0−β^1xn)2
通过RSS进一步构建损失函数MSE(均方误差)。得到损失函数和初始点之后,就可以采取梯度下降法,计算梯度,然后得到新的a、b取值,不断重复这个过程,直到最后MSE小于一定值,即可完成对ab的估计。
针对更一般的多元线性回归,可把多个变量写成矩阵形式,并得到更一般的回归方程:
f ( x i ) = w T x i + b f(x_i) = w^T x_i +b f(xi)=wTxi+b
虽然特征变多了,本质上我们还是估计参数,让MSE变小,所以只是改变了输入格式,整个流程还是一样的。
我们可以扩展一下线性回归,之前假设特征和响应变量之间服从一元一次方程的关系,可是如果遇到一元函数没办法很好拟合数据的情况,就需要多项式回归了。
多项式回归主要是加入了特征的更高次项,相当于增加了模型的自由度,或者说增加了模型的复杂度,使得模型可以更好更灵活地拟合数据(正则化里有讲述相关模型复杂度和特征次数的关系)。
假如现在有一个特征,那么多项式回归方程为:
h ^ = θ 0 + θ 1 x 1 + . . . + θ n x n \hat h = \theta_0 + \theta_1 x^1 + ... + \theta_n x^n h^=θ0+θ1x1+...+θnxn
有了向量形式,即使我们考虑多个特征,也可以很简洁地写出来了:
h ^ = X ⋅ θ 0 \hat h = X·\theta_0 h^=X⋅θ0
事实上,线性回归就是特殊的多项式回归,这很好理解,最后,我想说的是,不考虑过拟合之类的问题,理论上来说,多项式回归应该是可以拟合任何的数据的,因为我们有泰勒定理,任意的函数都可以通过泰勒级数逼近,或许我们构建的多项式过于复杂,有很多项是不需要的(因为多项式回归必须把同一次项的所有情况都要考虑,比如两个特征的三次多项式,只要是特征的幂是三就需要称为多项式的项,而不仅仅是每个特征的一二三次幂),但是我们也要知道,在训练模型的过程中,对数据影响不大的项的参数是会最终趋于零的,所以不考虑计算量、过拟合等问题,哪怕我们只用多项式回归这个方法,也足够拟合任意的数据集了。
想浏览更多关于数学、机器学习、深度学习的内容,可浏览本人博客
更多相关内容 -
多项式回归详解 从零开始 从理论到实践
2021-01-21 22:12:09多项式回归详解 从零开始 从理论到实践一、多项式回归的理解1.1、字面含义1.2、引申1.2.1、多项式回归二、sklearn的使用2.1、方法与属性2.2、实例应用2.2.1、拟合非线性关系2.2.2、处理特征之间的影响 一、多项式...多项式回归详解 从零开始 从理论到实践
一、多项式回归的理解
上一章是对线性关系的建模。比如在线性回归中,会假设建筑物的楼层数和建筑物的高度是一个线性关系,20层楼是10层楼的两倍左右高,10层楼是5层楼的两倍左右高。(即便不是线性关系,但是近似线性关系,误差很小,即可以等同线性关系)
然而在现实生活中,许多关系则几乎完全不是线性的,比如学生学习的时间和考试分数之间的关系,在60分及格分以上,考试每提高10分,所花的时间会越多(因为后面基本都是越来越难的题目,需要花更多时间练习,而不是每道题目难度一样)。
多项式回归是线性回归的一种扩展,它可以使我们对非线性关系进行建模。线性回归使用直线来拟合数据,如一次函数 y = k x + b y=kx+b y=kx+b等。而多项式回归则使用曲线来拟合数据,如二次函数 y = a x 2 + b x + c y=ax^2+bx+c y=ax2+bx+c、三次函数 y = a x 3 + b x 2 + c x + d y=ax^3+bx^2+cx+d y=ax3+bx2+cx+d以及多次函数等来拟合数据。
通过线性回归所不能解决的问题出发,由此来介绍多项式回归。
参考1 一次函数与直线的关系
参考2 线性函数 非线性函数 多项式函数
1.1、字面含义
多项式的理解:
“多项式回归”由“多项式”和“回归”组成。“多项式”(polynomial)的含义:在数学中,多项式是指由若干个单项式(monomial)相加组成的代数式叫做多项式(若有减法:减一个数等于加上它的相反数)。
多项式中的每个单项式叫做多项式的项,这些单项式中的最高项次数,就是这个多项式的次数。单项式由单个或多个变量(一般为单个 x x x)和系数相乘组成,或者不含变量,即不含字母的单个系数(常数)组成,这个不含字母的项叫做常数项。
多项式函数:
形如 f ( x ) = a n x n + a n − 1 x ( n − 1 ) + … + a 2 x 2 + a 1 x + a 0 f(x)=a_nx^n+a_{n-1}x^{(n-1)}+…+a_2x^2+a_1x+a_0 f(x)=anxn+an−1x(n−1)+…+a2x2+a1x+a0的函数,叫做多项式函数,它是由常数与自变量x经过有限次乘法与加法运算得到的。显然,当n=1时,其为一次函数 y = k x + b y=kx+b y=kx+b,当n=2时,其为二次函数。
1.2、引申
1.2.1、多项式回归
有时直线难以拟合全部的数据,需要曲线来适应数据,如二次模型、三次模型等等。
一般情况下,数据的回归函数是未知的,即使已知也难以用一个简单的函数变换转化为线性模型,所以常用的做法是多项式回归(Polynomial Regression),即使用多项式函数拟合数据。次数的选择:
多项式函数有多种,一般来说,需要先观察数据的形状,再去决定选用什么形式的多项式函数来处理问题。比如,从数据的散点图观察,如果有一个“弯”,就可以考虑用二次多项式;有两个“弯”,可以考虑用三次多项式;有三个“弯”,则考虑用四次多项式,以此类推。
当然,如果预先知道数据的属性,则有多少个
虽然真实的回归函数不一定是某个次数的多项式,但只要拟合的好,用适当的多项式来近似模拟真实的回归函数是可行的。多元线性回归比较复杂,如二元二次的线性回归函数,仅二元就很复杂了。
y = β 0 + β 1 x 1 + β 2 x 2 + β 3 x 1 x 2 + β 4 x 1 2 + β 5 x 2 2 y=\beta_0+\beta_1x_1+\beta_2x_2+\beta_3x_1x_2+\beta_4x_1^2+\beta_5x_2^2 y=β0+β1x1+β2x2+β3x1x2+β4x12+β5x22所以主要通过一元多项式回归模型来理解回归算法,它的结构如下:
y = β 0 + β 1 x + . . . + β n x n + ϵ y=\beta_0+\beta_1x+...+\beta_nx^n+\epsilon y=β0+β1x+...+βnxn+ϵ
同样,假设有 p p p 个样本,它的矩阵形式为:
y = X β + ϵ y=X\beta+\epsilon y=Xβ+ϵ
其中: y = [ y 1 y 2 ⋮ y p ] y=\begin{bmatrix} y_1&\\ y_2&\\ {\vdots}&\\ y_p&\\ \end{bmatrix} y=⎣⎢⎢⎢⎡y1y2⋮yp⎦⎥⎥⎥⎤, X = [ 1 x 1 x 1 2 ⋯ x 1 n 1 x 2 x 2 2 ⋯ x 2 n ⋮ ⋮ ⋮ ⋱ ⋮ 1 x p x p 2 ⋯ x p n ] X=\begin{bmatrix} 1&{x_{1}}&{x_{1}^2}&{\cdots}&{x_{1}^n}\\ 1&{x_{2}}&{x_{2}^2}&{\cdots}&{x_{2}^n}\\ {\vdots}&{\vdots}&{\vdots}&{\ddots}&{\vdots}\\ 1&{x_{p}}&{x_{p}^2}&{\cdots}&{x_{p}^n}\\ \end{bmatrix} X=⎣⎢⎢⎢⎡11⋮1x1x2⋮xpx12x22⋮xp2⋯⋯⋱⋯x1nx2n⋮xpn⎦⎥⎥⎥⎤, β = [ β 0 β 1 β 2 ⋮ β n ] \beta=\begin{bmatrix} \beta_0&\\ \beta_1&\\ \beta_2&\\ {\vdots}&\\ \beta_n&\\ \end{bmatrix} β=⎣⎢⎢⎢⎢⎢⎡β0β1β2⋮βn⎦⎥⎥⎥⎥⎥⎤, ϵ = [ ϵ 0 ϵ 1 ϵ 2 ⋮ ϵ p ] \epsilon=\begin{bmatrix} \epsilon_0&\\ \epsilon_1&\\ \epsilon_2&\\ {\vdots}&\\ \epsilon_p&\\ \end{bmatrix} ϵ=⎣⎢⎢⎢⎢⎢⎡ϵ0ϵ1ϵ2⋮ϵp⎦⎥⎥⎥⎥⎥⎤令 x 1 = x x_1=x x1=x, x 2 = x 2 x_2=x^2 x2=x2, x 3 = x 3 x_3=x^3 x3=x3, x 4 = x 4 x_4=x^4 x4=x4
原方程改写为 y = β 0 + β 1 x 1 + β 2 x 2 + β 3 x 3 + β 4 x 4 + β 5 x 5 y=\beta_0+\beta_1x_1+\beta_2x_2+\beta_3x_3+\beta_4x_4+\beta_5x_5 y=β0+β1x1+β2x2+β3x3+β4x4+β5x5
于是有关线性回归的方法都可以使用了,也就是说,线性回归并不“知道” x 2 x^2 x2是 x x x的二次变换,而是把它当做两一个变量来处理,只是为模型添加了特征而没有改变线性回归拟合模型的方式。由一元多项式回归模型的公式可以知道,只有一个自变量 x x x,但是 x x x 的级数(Degree) 不同。
二、sklearn的使用
多项式没有直接生成模型的方法,而是数据预处理效果,即:数据预处理(多项式特征)+线性模型。
该方法在sklearn包中的preprocessing模块中
class sklearn.preprocessing.PolynomialFeatures(degree=2, *, interaction_only=False, include_bias=True, order='C')
2.1、方法与属性
模型类函数的参数:
参数 取值 说明 degree int, default=2 多项式特征的次数 interaction_only bool, default=False 如果为True,只生成交互特征,即不存在自己和自己相结合的特征项,如degree=2的2个特征属性 a a a和 b b b的数据进行多项式化,则没有 a 2 a^2 a2和 b 2 b^2 b2这两个属性,只有 [ 1 , a , b , a b ] [1, a, b, ab] [1,a,b,ab]这四个属性 include_bias bool, default=True 表示有无 β 0 \beta_0 β0项。如果为True,则包含一列为1的偏差项,该列特征在多项式中的幂为0,在模型中充当截距。如interaction_only参数中的[1, a, b, ab]中的1,为False,则只有[a, b, ab]。 order {‘C’, ‘F’}, default=‘C’ 在密集情况下,对输出数组进行控制的指令。'F’指令对于计算而更快,但是会减慢后续的拟合速度 作用(两个):
1 生成多项式
2 生成交互特征。
结果:
生成一个含有新特征的矩阵,该矩阵由指定的少于或者等于degree的特征次数的多项式组合而组成。
例如:
输入为2个特征 [ a , b ] [a, b] [a,b]
生成二次多项式特征,输出为6个特征 [ 1 , a , b , a 2 , a b , b 2 ] [1, a, b, a^2, ab, b^2] [1,a,b,a2,ab,b2]
本质:
不是像线性模型那样,直接构建多项式模型,而是数据预处理效果,即在生成模型前,提前将数据特征转变为多项式特征,所以达到即便是线性模型,还可以生成曲线。模型类的属性:
参数 输出值 说明 PolynomialFeature.powers_ n维数组,形状(i, j) 幂运算数组,根据degree的值而确定行,根据属性个数而确定列。degree=2、3分别对应6行和10行,2、3个属性就对应2列、3列,该矩阵与属性值数组进行计算而得出多项式结果 PolynomialFeature.n_input_features_ int, =j 输入特征的总数,即属性个数,代表幂运算矩阵的列 PolynomialFeature.n_output_features_ int, =i 输出特征的总数,即多项式化后,属性的个数,代表幂运算矩阵的行 举例理解:
from sklearn.preprocessing import PolynomialFeatures import numpy as np
x = np.arange(6).reshape(3, 2) x """ array([[0, 1], [2, 3], [4, 5]]) """
# 数据预处理,将有两列的数组,即有两个特征属性的数组进行多项式化,degree=2,多项式变成6个项 plot = PolynomialFeatures(degree=2)
# 查看该类的幂运算数组 plot.powers_ """ array([[0, 0], [1, 0], [0, 1], [2, 0], [1, 1], [0, 2]], dtype=int64) """
对于上面的幂运算数组:
[ 0 , 0 ] [0, 0] [0,0] 表示 a 0 × b 0 = 1 a^0\times b^0=1 a0×b0=1
[ 1 , 0 ] [1, 0] [1,0] 表示 a 1 × b 0 = a a^1\times b^0=a a1×b0=a
[ 0 , 1 ] [0, 1] [0,1] 表示 a 0 × b 1 = b a^0\times b^1=b a0×b1=b
[ 2 , 0 ] [2, 0] [2,0] 表示 a 2 × b 0 = a 2 a^2\times b^0=a^2 a2×b0=a2
[ 1 , 1 ] [1, 1] [1,1] 表示 a 1 × b 1 = a b a^1\times b^1=ab a1×b1=ab
[ 0 , 2 ] [0, 2] [0,2] 表示 a 0 × b 2 = b 2 a^0\times b^2=b^2 a0×b2=b2# 类属性,查看输入特征与输出特征 plot.n_input_features_ """ 2 """ plot.n_output_features_ """ 6 """
# 幂运算矩阵与x进行运算,生成(i, j)即(6, 2)的数组 x_plot = plot.fit_transform(x) x_plot """ array([[ 1., 0., 1., 0., 0., 1.], [ 1., 2., 3., 4., 6., 9.], [ 1., 4., 5., 16., 20., 25.]]) """
按照幂运算数组的计算规则, x x x 的每一行代表 [ a , b ] [a, b] [a,b] 进行计算:
# x array([[0, 1], [2, 3], [4, 5]]) # 幂运算数组 array([[0, 0], [1, 0], [0, 1], [2, 0], [1, 1], [0, 2]], dtype=int64)
对于第一行 [ 0 , 1 ] [0, 1] [0,1],即:
[ 0 0 × 1 0 , 0 1 × 1 0 , 0 0 × 1 1 , 0 2 × 1 0 , 0 1 + 1 1 , 0 0 + 1 2 ] = [ 1 , 0 , 1 , 0 , 0 , 1 ] [0^0\times1^0, 0^1\times1^0, 0^0\times1^1, 0^2\times1^0, 0^1+1^1, 0^0+1^2 ]=[1, 0, 1, 0, 0, 1] [00×10,01×10,00×11,02×10,01+11,00+12]=[1,0,1,0,0,1]
后面的也类似这样计算。
2.2、实例应用
2.2.1、拟合非线性关系
非线性关系拟合过程的理解
将特征提升到某个幂次(二次方,三次方等)来创建新特征。模型添加的新特征越多,拟合的“线”就越灵活。
以波士顿数据第一个属性为例:
from sklearn.linear_model import LinearRegression from sklearn.datasets import load_boston from sklearn.preprocessing import PolynomialFeatures
# 加载数据集,并只选取 1 个特征 boston =load_boston() features = boston.data[:, 0:1] target = boston.target
# 创建多项式特征 polynomial = PolynomialFeatures(degree=3, include_bias=False) features_polynomial = polynomial.fit_transform(features)
# 创建线性回归对象与实例 linear = LinearRegression() model = linear.fit(features_polynomial, target)
观察第一个样本,手动创建多项式特征
# 由于参数设置degree=3,所以最高维3阶 # 一阶不变 features[0] "array([0.00632])" # 升到二阶 features[0]**2 "array([3.99424e-05])" # 升到三阶 features[0]**3 "array([2.52435968e-07])"
观察PolynomialFeatures函数产生的多项式特征
features_polynomial[0] "array([6.32000000e-03, 3.99424000e-05, 2.52435968e-07])"
结果一样,三个特征 x x x、 x 2 x^2 x2、 x 3 x^3 x3 都包含在特征矩阵中,然后进行线性回归,就构造出了一个多项式回归模型
2.2.2、处理特征之间的影响
有时,对某个特征变量的影响会取决于另一个特征。比如现实生活中冲咖啡的例子,有一组二元特征:是否加糖(sugar)和是否搅拌(stirred)。要预测某咖啡是否是甜的。如果只把糖加入咖啡(sugar=1, stirred=0)不会使咖啡变甜,所有糖沉在咖啡底部,只搅拌咖啡而不加糖(sugar=0, stirred=1)也不会让它变甜。加糖和搅拌对咖啡变甜的影响是相互依赖的。
利用这种交互特征进行建模,可得到二元二次多项式函数,不过剔除了二次项:
y ^ = β ^ 0 + β ^ 1 x 1 + β ^ 2 x 2 + β ^ 3 x 1 x 2 + ϵ \hat y=\hat\beta_0+\hat\beta_1x_1+\hat\beta_2x_2+\hat\beta_3x_1x_2+\epsilon y^=β^0+β^1x1+β^2x2+β^3x1x2+ϵ
其中, x 1 x_1 x1和 x 2 x_2 x2分别是sugar和stirred的值, x 1 x 2 x_1x_2 x1x2代表两者之间的相互作用。在类方法中的参数interaction_only,调整为True就表示生成交互特征。
以波士顿房价的前两个属性为实例:
from sklearn.linear_model import LinearRegression from sklearn.datasets import load_boston from sklearn.preprocessing import PolynomialFeatures
# 加载数据集,并只选取 2 个特征 boston =load_boston() features = boston.data[:, 0:2] target = boston.target
# 创建交互特征 interaction = PolynomialFeatures(degree=3, include_bias=False, interaction_only=True) features_interaction = interaction.fit_transform(features)
# 创建线性回归对象与实例 linear = LinearRegression() model = linear.fit(features_interaction, target)
interaction.powers_ """ array([[1, 0], [0, 1], [1, 1]], dtype=int64) """
第一个样本的交互特征
features_interaction[0] """ array([6.3200e-03, 1.8000e+01, 1.1376e-01]) """
手动计算交互特征
features[0] """ array([6.32e-03, 1.80e+01]) """
import numpy as np interaction_term = np.multiply(features[0, 0], features[0, 1]) np.array([interaction_term]) """ array([0.11376]) """
np.concatenate((features[0], np.array([interaction_term]))) """ array([6.3200e-03, 1.8000e+01, 1.1376e-01]) """
结果相等
2.2.3、多项式回归与不同degree的对比
import numpy as np import matplotlib.pyplot as plt from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression
# 创建训练集和测试集 def f(x): return x * np.sin(x) x_plot = np.linspace(0, 10, 100) x = np.linspace(0, 10, 100) rng = np.random.RandomState(3) rng.shuffle(x) x = np.sort(x[:30]) y = f(x)
# 创建训练集X(30)和测试集X_plot(100) X = x[:, np.newaxis] X_plot = x_plot[:, np.newaxis]
# 设置不同degree的颜色和线的样式 colors = ['m', 'r', 'g'] linestyles = ['-', '--', ':']
fig = plt.figure(figsize=(15, 6)) plt.plot(x_plot, f(x_plot), color='blue', linewidth=1, label='ground truth') plt.scatter(x, y, color='navy', s=10, marker='o', label='training points') for count, degree in enumerate([3, 4, 5]): polynomial = PolynomialFeatures(degree=degree, include_bias=False) train_polynomial = polynomial.fit_transform(X) test_polynomial = polynomial.fit_transform(X_plot) linear = LinearRegression() model = linear.fit(train_polynomial, y) y_plot = model.predict(test_polynomial) plt.plot(x_plot, y_plot, color=colors[count], linewidth=1, linestyle='-', label="degree %d" % degree) #plt.plot(1,2, linestyle='--') plt.legend(loc='lower left') plt.show()
结果展示:
从图像可以看出,degree=3,图像有两个“弯”;degree=4,图像有3个“弯”,接近标准曲线;而degree=4时,图像也只有3个“弯”(已经是标答),且相对于degree=4的曲线偏上一点,也就是更接近标准曲线。可见阶数越高,曲线的弯曲形状越大,越接近函数曲线。
-
多项式回归
2018-10-31 12:24:451.多项式回归简介 2.scikit-learn中的多项式回归于pipeline 3.过拟合与前拟合 4.学习曲线 5.验证数据集与交叉验证 6.偏差方差均衡 7.模型正则化 8.LASSO 9.L1,L2和弹性网络 1.多项式回归简介 考虑下面...- 1.多项式回归简介
- 2.scikit-learn中的多项式回归于pipeline
- 3.过拟合与前拟合
- 4.学习曲线
- 5.验证数据集与交叉验证
- 6.偏差方差均衡
- 7.模型正则化
- 8.LASSO
- 9.L1,L2和弹性网络
1.多项式回归简介
考虑下面的数据,虽然我们可以使用线性回归来拟合这些数据,但是这些数据更像是一条二次曲线,相应的方程是y=ax2+bx+c,这是式子虽然可以理解为二次方程,但是我们呢可以从另外一个角度来理解这个式子:
如果将x2理解为一个特征,将x理解为另外一个特征,换句话说,本来我们的样本只有一个特征x,现在我们把他看成有两个特征的一个数据集。多了一个特征x2,那么从这个角度来看,这个式子依旧是一个线性回归的式子,但是从x的角度来看,他就是一个二次的方程
1-1
以上这样的方式,就是所谓的多项式回归
相当于我们为样本多添加了一些特征,这些特征是原来样本的多项式项,增加了这些特征之后,我们们可以使用线性回归的思路更好的我们的数据
2.编程实验多项式回归
1.模拟多项式回归的数据集
import numpy as np import matplotlib.pyplot as plt 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) plt.scatter(x,y)
<matplotlib.collections.PathCollection at 0x114f17160>
[图片上传失败...(image-46be02-1527345377921)]
2.使用线性回归拟合
很明显,我们用一跟直线来拟合一根有弧度的曲线,效果是不好的
from sklearn.linear_model import LinearRegression lin_reg = LinearRegression() lin_reg.fit(X,y) y_predict = lin_reg.predict(X) plt.scatter(x,y) plt.plot(X,y_predict,color='r')
[<matplotlib.lines.Line2D at 0x1a1c90ecf8>]
[图片上传失败...(image-e9e717-1527345377922)]
3.解决方案,添加一个特征
原来所有的数据都在X中,现在对X中每一个数据都进行平方,
再将得到的数据集与原数据集进行拼接,
在用新的数据集进行线性回归(X**2).shape
(100, 1)
X2 = np.hstack([X,X**2])
lin_reg2 = LinearRegression() lin_reg2.fit(X2,y) y_predict2 = lin_reg2.predict(X2) plt.scatter(x,y) # 由于x是乱的,所以应该进行排序 plt.plot(np.sort(x),y_predict2[np.argsort(x)],color='r')
[<matplotlib.lines.Line2D at 0x1a1c691198>]
[图片上传失败...(image-414310-1527345377922)]
从上图可以看出,当我们添加了一个特征(原来特征的平方)之后,再从x的维度来看,就形成了一条曲线,显然这个曲线对原来数据集的拟合程度是更好的
# 第一个系数是x前面的系数,第二个系数是x平方前面的系数 lin_reg2.coef_
array([1.08043759, 0.52423752])
lin_reg2.intercept_
1.9427736300237914
3.总结
多线性回归在机器学习算法上并没有新的地方,完全是使用线性回归的思路
他的关键在于为原来的样本,添加新的特征。而我们得到新的特征的方式是原有特征的多项式的组合。
采用这样的方式,我们就可以解决一些非线性的问题与此同时需要主要,我们在上一章所讲的PCA是对我们的数据进行降维处理,而我们这一章所讲的多项式回归显然在做一件相反的事情,他让我们的数据升维,在升维之后使得我们的算法可以更好的拟合高纬度的数据
2.scikit-learn中的多项式回归于pipeline
1.使用scikit-learn中的多项式对数据进行预处理
# sklearn中对数据进行预处理的函数都封装在preprocessing模块下,包括之前学的归一化StandardScaler from sklearn.preprocessing import PolynomialFeatures poly = PolynomialFeatures() poly.fit(X) X2 = poly.transform(X) # 第一列是sklearn为我们添加的X的零次方的特征 # 第二列和原来的特征一样是X的一次方的特征 # 第三列是添加的X的二次方的特征 X2[:5]
array([[ 1. , 2.5980174 , 6.74969443],
[ 1. , 2.07484052, 4.30496317],
[ 1. , -1.74999096, 3.06246837],
[ 1. , -2.74141103, 7.51533441],
[ 1. , -1.3420996 , 1.80123135]])2.调用LinearRegression对X2进行预测
lin_reg2 = LinearRegression() lin_reg2.fit(X2,y) y_predict2 = lin_reg2.predict(X2) plt.scatter(x,y) # 由于x是乱的,所以应该进行排序 plt.plot(np.sort(x),y_predict2[np.argsort(x)],color='r')
[<matplotlib.lines.Line2D at 0x1a1c4e8860>]
[图片上传失败...(image-80f622-1527345377922)]
lin_reg2.coef_
array([0. , 1.08043759, 0.52423752])
3.关于PolynomialFeatures
# 测试多维的数据集 X = np.arange(1,11).reshape(5,2) X.shape
(5, 2)
X
array([[ 1, 2],
[ 3, 4],
[ 5, 6],
[ 7, 8],
[ 9, 10]])poly = PolynomialFeatures(degree=2) poly.fit(X) X2 = poly.transform(X) X2
array([[ 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.]])将52的矩阵进行多项式转换后变成了56
- 第一列是1 对应的是0次幂
- 第二列和第三列对应的是原来的x矩阵,此时他有两列一次幂的项
- 第四列是原来数据的第一列平方的结果
- 第六列是原来数据的第二列平方的结果
- 第五列是原来数据的两列相乘的结果
可以想象如果将degree设置为3,那么将产生一下10个元素
- 1,X1,X2
- X21,X22,X1*X2
- X13,X23,X12X2,X22X1
也就是说PolynomialFeatures会穷举出所有的多项式组合
3.Pipline
pipline的英文名字是管道,那么 我们如何使用管道呢,先考虑我们多项式回归的过程
1.使用
PolynomialFeatures
生成多项式特征的数据集
2.如果生成数据幂特别的大,那么特征直接的差距就会很大,导致我们的搜索非常慢,这时候可以进行数据归一化
3.进行线性回归
pipline 的作用就是把上面的三个步骤合并,使得我们不用一直重复这三步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)
from sklearn.preprocessing import PolynomialFeatures from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline # 传入每一步的对象名和类的实例化 poly_reg = Pipeline([ ('poly',PolynomialFeatures(degree=2)), ('std_scaler',StandardScaler()), ('lin_reg',LinearRegression()) ])
poly_reg.fit(X,y) y_predict = poly_reg.predict(X) plt.scatter(x,y) plt.plot(np.sort(x),y_predict[np.argsort(x)],color='r')
[<matplotlib.lines.Line2D at 0x1a1ccc15c0>]
[图片上传失败...(image-4c2d99-1527345377922)]
3.过拟合与前拟合
1.什么是过拟合和欠拟合
import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression 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) lin_reg = LinearRegression() lin_reg.fit(X,y) y_predict = lin_reg.predict(X) plt.scatter(x,y) plt.plot(X,y_predict,color='r')
0.5406237455773699
[图片上传失败...(image-f3daf8-1527345377922)]
# 直接使用线性回归,显然分数太低 lin_reg.score(X,y)
0.5406237455773699
使用均方误差来看拟合的结果,这是因为我们同样都是对一组数据进行拟合,所以使用不同的方法对数据进行拟合
得到的均方误差的指标是具有可比性的,(但是对于多项式回归来说,使用r2score进行衡量是没有问题是)from sklearn.metrics import mean_squared_error y_predict = lin_reg.predict(X) mean_squared_error(y,y_predict)
2.6112077267395803
使用多项式回归
from sklearn.preprocessing import PolynomialFeatures from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline # 使用Pipeline构建多项式回归 def PolynomialRegression(degree): poly_reg = Pipeline([ ('poly',PolynomialFeatures(degree=degree)), ('std_scaler',StandardScaler()), ('lin_reg',LinearRegression()) ]) return poly_reg poly_reg2 = PolynomialRegression(2) poly_reg2.fit(X,y) y2_predict = poly_reg2.predict(X) # 显然使用多项式回归得到的结果是更好的 mean_squared_error(y,y2_predict)
1.000151338154146
plt.scatter(x,y) plt.plot(np.sort(x),y2_predict[np.argsort(x)],color='r')
[<matplotlib.lines.Line2D at 0x1a1d536908>]
[图片上传失败...(image-6e5e74-1527345377922)]
使用更多的维度进行多项式回归
# 使用10个维度 poly_reg10 = PolynomialRegression(10) poly_reg10.fit(X,y) y10_predict = poly_reg10.predict(X) mean_squared_error(y,y10_predict)
0.9394112675409493
plt.scatter(x,y) plt.plot(np.sort(x),y10_predict[np.argsort(x)],color='r')
[<matplotlib.lines.Line2D at 0x1a1d78c198>]
[图片上传失败...(image-97cb38-1527345377922)]
poly_reg100 = PolynomialRegression(100) poly_reg100.fit(X,y) y100_predict = poly_reg100.predict(X) # 显然使用多项式回归得到的结果是更好的 mean_squared_error(y,y100_predict)
0.5431979125088253
plt.scatter(x,y) plt.plot(np.sort(x),y100_predict[np.argsort(x)],color='r')
[<matplotlib.lines.Line2D at 0x1a1da201d0>]
[图片上传失败...(image-463c7c-1527345377922)]
这条曲线只是原来的点对应的y的预测值连接起来的曲线,不过有很多地方可能没有那个数据点,所以连接的结果和原来的曲线不一样
下面尝试真正还原原来的曲线(构造均匀分布的原数据集)X_plot = np.linspace(-3,3,100).reshape(100,1) y_plot = poly_reg100.predict(X_plot) plt.scatter(x,y) plt.plot(X_plot,y_plot,color='r') plt.axis([-3 , 3 , -1,10 ])
[-3, 3, -1, 10]
[图片上传失败...(image-44edb2-1527345377922)]
说明总有一条曲线,他能拟合所有的样本点,使得均方误差的值为0
degree从2到10到100的过程中,虽然均方误差是越来越小的,从均方误差的角度来看是更加小的
但是他真的能更好的预测我们数据的走势吗,例如我们选择2.5到3的一个x,使用上图预测出来的y的大小(0或者-1之间)显然不符合我们的数据换句话说,我们使用了一个非常高维的数据,虽然使得我们的样本点获得了更小的误差,但是这根曲线完全不是我们想要的样子
他为了拟合我们所有的样本点,变的太过复杂了,这种情况就是过拟合【over-fitting】相反,在最开始,我们直接使用一根直线来拟合我们的数据,也没有很好的拟合我们的样本特征,当然他犯的错误不是太过复杂了,而是太过简单了
这种情况,我们成为欠拟合-【under-fitting】对于现在的数据(基于二次方程构造),我们使用低于2项的拟合结果,就是欠拟合;高于2项的拟合结果,就是过拟合
2.为什么要使用训练数据集和测试数据集
模型的泛化能力
使用上小节的过拟合结果,我们可以得知,虽然我们训练出的曲线将原来的样本点拟合的非常好,总体的误差非常的小,
但是一旦来了新的样本点,他就不能很好的预测了,在这种情况下,我们就称我们得到的这条弯弯曲曲的曲线,他的泛化能力(由此及彼的能力)非常弱image.png
训练数据集和测试数据集的意义
我们训练的模型目的是为了使得预测的数据能够尽肯能的准确,在这种情况下,我们观察训练数据集的拟合程度是没有意义的
我们真正需要的是,我们得到的模型的泛化能力更高,解决这个问题的方法也就是使用训练数据集,测试数据集的分离image.png
测试数据对于我们的模型是全新的数据,如果使用训练数据获得的模型面对测试数据也能获得很好的结果,那么我们就说我们的模型泛化能力是很强的。
如果我们的模型面对测试数据结果很差的话,那么他的泛化能力就很弱。事实上,这是训练数据集更大的意义from sklearn.model_selection import train_test_split X_train,X_test,y_train,y_test = train_test_split(X,y) lin_reg = LinearRegression() lin_reg.fit(X_train,y_train) y_predict = lin_reg.predict(X_test) # 训练模型使用的X_train,是预测的模型使用X_test,以计算模型的泛化能力 mean_squared_error(y_test,y_predict)
2.7714817137686794
poly_reg2 = PolynomialRegression(2) poly_reg2.fit(X_train,y_train) y2_predict = poly_reg2.predict(X_test) mean_squared_error(y_test,y2_predict)
0.7922037464116539
poly_reg10 = PolynomialRegression(10) poly_reg10.fit(X_train,y_train), y10_predict = poly_reg10.predict(X_test) mean_squared_error(y_test,y10_predict)
1.336192585265726
使用degree=10的时候得到的均方误差要大于degree=2的时候,说明当degree等于10的时候,他的模型泛化能力变弱了
poly_reg100 = PolynomialRegression(100) poly_reg100.fit(X_train,y_train) y100_predict = poly_reg100.predict(X_test) mean_squared_error(y_test,y100_predict)
4.192433747323001e+21
刚刚我们进行的实验实际上在实验模型的复杂度,对于多项式模型来说,我们回归的阶数越高,我们的模型会越复杂,在这种情况下对于我们的机器学习算法来说,通常是有下面一张图的。横轴是模型复杂度(对于不同的算法来说,代表的是不同的意思,比如对于多项式回归来说,是阶数越高,越复杂;对于KNN来说,是K越小,模型越复杂,k越大,模型最简单,当k=n的时候,模型就简化成了看整个样本里,哪种样本最多,当k=1来说,对于每一个点,都要找到离他最近的那个点),另一个维度是模型准确率(也就是他能够多好的预测我们的曲线)
image.png
通常对于这样一个图,会有两根曲线:
- 一个是对于训练数据集来说的,模型越复杂,模型准确率越高,因为模型越复杂,对训练数据集的拟合就越好,相应的模型准确率就越高
- 对于测试数据集来说,在模型很简单的时候,模型的准确率也比较低,随着模型逐渐变复杂,对测试数据集的准确率在逐渐的提升,提升到一定程度后,如果模型继续变复杂,那么我们的模型准确率将会进行下降(欠拟合->正合适->过拟合)
欠拟合和过拟合的标准定义
欠拟合:算法所训练的模型不能完整表述数据关系
过拟合:算法所训练的模型过多的表达了数据间的噪音关系
4.学习曲线
1. 什么是学习曲线
随着训练样本的主键增多,算法训练出的模型的表现能力
import numpy as np import matplotlib.pyplot as plt np.random.seed(666) 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) plt.scatter(x,y)
<matplotlib.collections.PathCollection at 0x107369240>
[图片上传失败...(image-de5417-1527345377922)]
2.实际编程实现学习曲线
from sklearn.model_selection import train_test_split X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=10) X_train.shape
(75, 1)
2.1观察线性回归的学习曲线:观察线性回归模型,随着训练数据集增加,性能的变化
from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error def plot_learning_curve(algo,X_train,X_test,y_train,y_test): train_score = [] test_score = [] # 计算学习曲线数据 for i in range(1,len(X_train)+1): algo.fit(X_train[:i],y_train[:i]) y_train_predict = algo.predict(X_train[:i]) train_score.append(mean_squared_error(y_train[:i],y_train_predict)) y_test_predict = algo.predict(X_test) test_score.append(mean_squared_error(y_test,y_test_predict)) # 绘制学习曲线 plt.plot([i for i in range(1,len(X_train)+1)],np.sqrt(train_score),label = 'train') plt.plot([i for i in range(1,len(X_train)+1)],np.sqrt(test_score),label = 'test') plt.axis([0,len(X_train)+1,0,4]) plt.legend() plot_learning_curve(LinearRegression(),X_train,X_test,y_train,y_test)
[图片上传失败...(image-7c3927-1527345377922)]
从趋势上看:
- 在训练数据集上,误差是逐渐升高的。这是因为我们的训练数据越来越多,我们的数据点越难得到全部的累积,不过整体而言,在刚开始的时候误差变化的比较快,后来就几乎不变了
- 在测试数据集上,在使用非常少的样本进行训练的时候,刚开始我们的测试误差非常的大,当训练样本大到一定程度以后,我们的测试误差就会逐渐减小,减小到一定程度后,也不会小太多,达到一种相对稳定的情况
- 在最终,测试误差和训练误差趋于相等,不过测试误差还是高于训练误差一些,这是因为,训练数据在数据非常多的情况下,可以将数据拟合的比较好,误差小一些,但是泛化到测试数据集的时候,还是有可能多一些误差
2.2 观察多项式回归的学习曲线
from sklearn.preprocessing import StandardScaler from sklearn.preprocessing import PolynomialFeatures from sklearn.pipeline import Pipeline # 使用Pipline构建多项式回归模型 def PolynomialRegression(degree): return Pipeline([ ("poly",PolynomialFeatures(degree=degree)), ("std_scaler",StandardScaler()), ("lin_reg",LinearRegression()) ]) # 使用二阶多项式回归 poly2_reg = PolynomialRegression(2) plot_learning_curve(poly2_reg,X_train,X_test,y_train,y_test)
[图片上传失败...(image-ec1d61-1527345377922)]
首先整体从趋势上,和线性回归的学习曲线是类似的
仔细观察,和线性回归曲线的不同在于,线性回归的学习曲线1.5,1.8左右;2阶多项式回归稳定在了1.0,0.9左右,2阶多项式稳定的误差比较低,说明
使用二阶线性回归的性能是比较好的# 使用20阶多项式回归 poly20_reg = PolynomialRegression(20) plot_learning_curve(poly20_reg,X_train,X_test,y_train,y_test)
[图片上传失败...(image-db9bf9-1527345377922)]
在使用20阶多项式回归训练模型的时候可以发现,在数据量偏多的时候,我们的训练数据集拟合的是比较好的,但是测试数据集的误差相对来说增大了很多,离训练数据集比较远,通常这就是过拟合的结果,他的泛化能力是不够的
3.总结
image.png
对于欠拟合比最佳的情况趋于稳定的那个位置要高一些,说明无论对于训练数据集还是测试数据集来说,误差都比较大。这是因为我们本身模型选的就不对,所以即使在训练数据集上,他的误差也是大的,所以才会呈现出这样的一种形态
image.png
对于过拟合的情况,在训练数据集上,他的误差不大,和最佳的情况是差不多的,甚至在极端情况,如果degree取更高的话,那么训练数据集的误差会更低,但是问题在于,测试数据集的误差相对是比较大的,并且训练数据集的误差和测试数据集的误差相差比较大(表现在图上相差比较远),这就说明了此时我们的模型的泛化能力不够好,他的泛化能力是不够的
5.验证数据集与交叉验证
使用分割训练数据集和测试数据集来判断我们的机器学习性能的好坏,虽然是一个非常好的方案,但是会产生一个问题:针对特定测试数据集过拟合
我们每次使用测试数据来分析性能的好坏。一旦发现结果不好,我们就换一个参数(可能是degree也可能是其他超参数)重新进行训练。这种情况下,我们的模型在一定程度上围绕着测试数据集打转。也就是说我们在寻找一组参数,使得这组参数训练出来的模型在测试结果集上表现的最好。但是由于这组测试数据集是已知的,我们相当于在针对这组测试数据集进行调参,那么他也有可能产生过拟合的情况,也就是我们得到的模型针对测试数据集过拟合了
image.png
那么怎么解决这个问题呢?
解决的方式其实就是:我们需要将我们的问题分为三部分,这三部分分别是训练数据集,验证数据集,测试数据集。
我们使用训练数据集训练好模型之后,将验证数据集送给这个模型,看看这个训练数据集训练的效果是怎么样的,如果效果不好的话,我们重新换参数,重新训练模型。直到我们的模型针对验证数据来说已经达到最优了。
这样我们的模型达到最优以后,再讲测试数据集送给模型,这样才能作为衡量模型最终的性能。换句话说,我们的测试数据集是不参与模型的创建的,而其他两个数据集都参与了训练。但是我们的测试数据集对于模型是完全不可知的,相当于我们在模型这个模型完全不知道的数据image.png
这种方法还会有一个问题。由于我们的模型可能会针对验证数据集过拟合,而我们只有一份验证数据集,一旦我们的数据集里有比较极端的情况,那么模型的性能就会下降很多,那么为了解决这个问题,就有了交叉验证。
1.交叉验证 Cross Validation
交叉验证相对来说是比较正规的、比较标准的在我们调整我们的模型参数的时候看我们的性能的方式
交叉验证:在训练模型的时候,通常把数据分成k份,例如分成3份(ABC)(分成k分,k属于超参数),这三份分别作为验证数据集和训练数据集。这样组合后可以分别产生三个模型,这三个模型,每个模型在测试数据集上都会产生一个性能的指标,这三个指标的平均值作为当前这个算法训练处的模型衡量的标准是怎样的。
由于我们有一个求平均的过程,所以不会由于一份验证数据集中有比较极端的数据而导致模型有过大的偏差,这比我们只分成训练、验证、测试数据集要更加准确2.编程实现
import numpy as np from sklearn import datasets
digits = datasets.load_digits() X = digits.data y = digits.target
训练train_test_spilt
from sklearn.model_selection import train_test_split X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.4,random_state =666)
from sklearn.neighbors import KNeighborsClassifier best_score,best_k,best_p = 0,0,0 # k为k近邻中的寻找k个最近元素 for k in range(2,10): # p为明科夫斯基距离的p for p in range(1,5): knn_clf = KNeighborsClassifier(weights='distance',n_neighbors=k,p=p) knn_clf.fit(X_train,y_train) score = knn_clf.score(X_test,y_test) if score > best_score: best_score,best_k,best_p = score,k,p print("Best_score = ",best_score) print("Best_k = ",best_k) print("Best_p = ",best_p)
Best_score = 0.9860917941585535
Best_k = 3
Best_p = 4使用交叉验证
# 使用sklearn提供的交叉验证 from sklearn.model_selection import cross_val_score knn_clf = KNeighborsClassifier() # 返回的是一个数组,有三个元素,说明cross_val_score方法默认将我们的数据集分成了三份 # 这三份数据集进行交叉验证后产生了这三个结果 # cv默认为3,可以修改改参数,修改修改不同分数的数据集 cross_val_score(knn_clf,X_train,y_train,cv=3)
array([0.98895028, 0.97777778, 0.96629213])
# 使用交叉验证的方式来进行调参的过程 best_score,best_k,best_p = 0,0,0 # k为k近邻中的寻找k个最近元素 for k in range(2,10): # p为明科夫斯基距离的p for p in range(1,5): knn_clf = KNeighborsClassifier(weights='distance',n_neighbors=k,p=p) scores = cross_val_score(knn_clf,X_train,y_train) score = np.mean(scores) if score > best_score: best_score,best_k,best_p = score,k,p print("Best_score = ",best_score) print("Best_k = ",best_k) print("Best_p = ",best_p)
Best_score = 0.9823599874006478
Best_k = 2
Best_p = 2通过观察两组调参过程的结果可以发现
1.两组调参得出的参数结果是不同的,通常这时候我们更愿意详细使用交叉验证的方式得出的结果。
因为使用train_test_split很有可能只是过拟合了测试数据集得出的结果
2.使用交叉验证得出的最好分数0.982是小于使用分割训练测试数据集得出的0.986,因为在交叉验证的
过程中,通常不会过拟合某一组的测试数据,所以平均来讲这个分数会稍微低一些但是使用交叉验证得到的最好参数Best_score并不是真正的最好的结果,我们使用这种方式只是为了拿到
一组超参数而已,拿到这组超参数后我们就可以训练处我们的最佳模型knn_clf = KNeighborsClassifier(weights='distance',n_neighbors=2,p=2) # 用我们找到的k和p。来对X_train,y_train整体fit一下,来看他对X_test,y_test的测试结果 knn_clf.fit(X_train,y_train) # 注意这个X_test,y_test在交叉验证过程中是完全没有用过的,也就是说我们这样得出的结果是可信的 knn_clf.score(X_test,y_test)
0.980528511821975
回顾网格搜素
我们上面的操作,实际上在网格搜索的过程中已经进行了,只不过这个过程是sklean的网格搜索自带的一个过程
# GridSearchCV里的cv实际上就是交叉验证的方式 from sklearn.model_selection import GridSearchCV param_grid = [ { "weights":['distance'], "n_neighbors":[i for i in range(2,10)], "p":[i for i in range(1,6)] } ] knn_clf = KNeighborsClassifier() # cv默认为3,可以修改改参数,修改修改不同分数的数据集 grid_search = GridSearchCV(knn_clf,param_grid,verbose=1,cv=3) grid_search.fit(X_train,y_train)
Fitting 3 folds for each of 40 candidates, totalling 120 fits
[Parallel(n_jobs=1)]: Done 120 out of 120 | elapsed: 1.0min finished
GridSearchCV(cv=None, error_score='raise',
estimator=KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=1, n_neighbors=5, p=2,
weights='uniform'),
fit_params=None, iid=True, n_jobs=1,
param_grid=[{'weights': ['distance'], 'n_neighbors': [2, 3, 4, 5, 6, 7, 8, 9], 'p': [1, 2, 3, 4, 5]}],
pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
scoring=None, verbose=1)Fitting 3 folds for each of 40 candidates, totalling 120 fits
的意思就是交叉验证中分割了三组数据集,而我们的参数组合为8*5=40中组合
3组数据集,30种组合,一共要进行120次的训练grid_search.best_score_ # 0.9823747680890538 和我们上面得到的best_score 是吻合的
0.9823747680890538
grid_search.best_params_
{'n_neighbors': 2, 'p': 2, 'weights': 'distance'}
best_knn_clf = grid_search.best_estimator_ best_knn_clf.fit(X_train,y_train) best_knn_clf.score(X_test,y_test)
0.980528511821975
3.总结
image.png
虽然整体速度慢了,但是这个结果却是可信赖的
极端情况下,K-folds cross validation可以叫做留一法image.png
6.偏差方差均衡
模型误差=偏差(Bias)均差(Variance)+不可避免的误差
偏差
image.png
方差
模型没有完全的学到数据的中心,而学习到了很多噪音
image.png
-
有一些算法天生是高方差算法。如KNN(过于依赖数据,一点选取的数据点有多数是不正确的,那么预测的结果就是错误的。导致有的很准确,有的非常不准确,方差非常大)
-
非参数学习通常都是高方差的算法。因为不对数据做任何假设
-
有一些算法天生是高偏差算法。如线性回归(用一条直线去拟合一条曲线,导致整体预测结果都距离真实数据查很大,偏差非常大)
-
参数学习通常都是高偏差的算法。因为对数据具有极强的假设
-
大多数算法具有相应的参数,可以调整偏差和方差
-
如KNN中的k,线性回归中使用多项式回归
-
偏差和方差是互相矛盾的。降低方差会提高偏差,降低偏差会提高方差
机器学习的主要调整来源于方差(这是站在算法的角度上,而不是问题的角度上,比如对金融市场的理解,很多人尝试用历史的数据预测未来的金融走势,这样的尝试通常都不太理想。很有可能因为历史的金融趋势不能很好的反应未来的走向,这种预测方法本身带来的非常大的偏差)换句话说,我们很容易让模型变的很复杂,从而降低模型的偏差,但是由于这样的模型的方差非常的大,最终也没有很好的性能。
解决高方差的通常手段:
1.降低模型复杂度
2.减少数据维度;降噪
3.增加样本数(模型太过复杂,模型中的参数非常多,而样本数不足以支撑计算出这么复杂的参数)
4.使用验证集
5.模型正则化image.png
7.模型正则化-Regularization
1.什么是模型正则化
下图是我们之前使用多项式回归过拟合一个样本的例子,可以看到这条模型曲线非常的弯曲,而且非常的陡峭,可以想象这条曲线的一些θ系数会非常的大。
模型正则化需要做的事情就是限制这些系数的大小image.png
模型正则化基本原理
image.png
一些需要注意的细节:
- 对于θ的求和i是从1到n,没有将θ0加进去,因为他不是任意一项的系数,他只是一个截距,决定了整个曲线的高低,但是不决定曲线每一部分的陡峭和缓和
- θ求和的系数二分之一是一个惯例,加不加都可以,加上的原因是因为,将来对θ2>求导的时候可以抵消系数2,方便计算。不要也是可以的
- α实际上是一个超参数,代表在我们模型正则化下新的损失函数中,我们要让每一个θ尽可能的小,小的程度占我们整个损失函数的多少,如果α等于0,相当于没有正则化;如果α是正无穷的话,那么我们主要的优化任务就是让每一个θ尽可能的小
岭回归 Ridge Regression
image.png
2.编程实现岭回归
import numpy as np import matplotlib.pyplot as plt # 模型样本 np.random.seed(42) x = np.random.uniform(-3.0,3.0,size=100) X = x.reshape(-1,1) y = 0.5 * x + 3 + np.random.normal(0,1,size=100) # 绘制样本曲线 plt.scatter(x,y)
<matplotlib.collections.PathCollection at 0x1a159a6c88>
image.png
y.shape
(100,)
from sklearn.preprocessing import PolynomialFeatures from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegression from sklearn.pipeline import Pipeline # 定义多项式回归函数 def PolynomialRegression(degree): return Pipeline([ ("poly",PolynomialFeatures(degree=degree)), ("std_scaler",StandardScaler()), ("lin_reg",LinearRegression()) ])
from sklearn.model_selection import train_test_split # 分割数据集 np.random.seed(666) X_train,X_test,y_train,y_test = train_test_split(X,y)
from sklearn.metrics import mean_squared_error # 多项式回归对样本进行训练,使用20个维度 poly20_reg = PolynomialRegression(20) poly20_reg.fit(X_train,y_train) y20_predict = poly20_reg.predict(X_test) mean_squared_error(y_test,y20_predict)
167.94010860151894
# 定义绘图模型 def plot_module(module): X_plot = np.linspace(-3,3,100).reshape(100,1) y_plot = module.predict(X_plot) plt.scatter(x,y) plt.plot(X_plot[:,0],y_plot,color='r') plt.axis([-3,3,0,6])
# 绘制模型曲线--过拟合(非常的完全,两段有极端的情况) plot_module(poly20_reg)
image.png
使用岭回归
from sklearn.linear_model import Ridge def RidgeRegression(degree,alpha): return Pipeline([ ("poly",PolynomialFeatures(degree=degree)), ("std_scaler",StandardScaler()), ("ridge_reg",Ridge(alpha=alpha)) ])
# 注意alpha后面的参数是所有theta的平方和,而对于多项式回归来说,岭回归之前得到的θ都非常大 # 所以为了限制让他们比较小,我们前面系数可以取的小一些 ridge1_reg = RidgeRegression(degree=20,alpha=0.00001) ridge1_reg.fit(X_train,y_train) ridge1_predict = ridge1_reg.predict(X_test) mean_squared_error(y_test,ridge1_predict)
1.387437803144217
# 通过使用岭回归,使得我们的均方误差小了非常多,曲线也缓和了非常多 plot_module(ridge1_reg)
image.png
ridge2_reg = RidgeRegression(degree=20,alpha=1) ridge2_reg.fit(X_train,y_train) ridge2_predict = ridge2_reg.predict(X_test) mean_squared_error(y_test,ridge2_predict)
1.1888759304218461
# 让ridge2_reg 的alpha值等于1,均差误差更加的缩小,并且曲线越来越趋近于一根倾斜的直线 plot_module(ridge2_reg)
image.png
ridge3_reg = RidgeRegression(degree=20,alpha=100) ridge3_reg.fit(X_train,y_train) ridge3_predict = ridge3_reg.predict(X_test) mean_squared_error(y_test,ridge3_predict)
1.31964561130862
# 得到的误差依然是比较小,但是比之前的1.18大了些,说明正则化做的有些过头了 plot_module(ridge3_reg)
image.png
ridge4_reg = RidgeRegression(degree=20,alpha=100000) ridge4_reg.fit(X_train,y_train) ridge4_predict = ridge4_reg.predict(X_test) mean_squared_error(y_test,ridge4_predict) # 当alpha非常大,我们的模型实际上相当于就是在优化θ的平方和这一项,使得其最小(因为MSE的部分相对非常小) # 而使得θ的平方和最小,就是使得每一个θ都趋近于0,这个时候曲线就趋近于一根直线了 plot_module(ridge4_reg)
image.png
8.LASSO
使用|θ|代替θ2来标示θ的大小
image.png
Selection Operator -- 选择运算符
LASSO回归有一些选择的功能
image.png
1. 实际编程(准备代码参考上一节岭回归)
from sklearn.linear_model import Lasso def LassoRegression(degree,alpha): return Pipeline([ ("poly",PolynomialFeatures(degree=degree)), ("std_scatter",StandardScaler()), ("lasso_reg",Lasso(alpha=alpha)) ])
# 这里穿的alpha起始值比岭回归的时候大了很多,是由于现在是绝对值 lasso1_reg = LassoRegression(degree=20,alpha=0.01) lasso1_reg.fit(X_train,y_train) lasso1_predict = lasso1_reg.predict(X_test) mean_squared_error(lasso1_predict,y_test)
1.1496080843259968
plot_module(lasso1_reg)
image.png
# 增大alpha继续试验 lasso2_reg = LassoRegression(degree=20,alpha=0.1) lasso2_reg.fit(X_train,y_train) lasso2_predict = lasso2_reg.predict(X_test) mean_squared_error(lasso2_predict,y_test)
1.1213911351818648
# 非常接近一根直线 plot_module(lasso2_reg)
image.png
# 增大alpha继续试验 lasso3_reg = LassoRegression(degree=20,alpha=1) lasso3_reg.fit(X_train,y_train) lasso3_predict = lasso3_reg.predict(X_test) mean_squared_error(lasso3_predict,y_test)
1.8408939659515595
# alpha=1的时候正则化已经过头了 plot_module(lasso3_reg)
image.png
2. 总结Ridge和Lasso
image.png
α=100的时候,使用Ridge的得到的模型曲线依旧是一根曲线,事实上,使用Ridge很难得到一根倾斜的直线,他一直是弯曲的形状
但是使用LASSO的时候,当α=0.1,虽然得到的依然是一根曲线,但是他显然比Radge的程度更低,更像一根直线
这是因为LASSO趋向于使得一部分theta值为0(而不是很小的值),所以可以作为特征选择用,LASSO的最后两个字母SO就是Selection Operator的首字母缩写
使用LASSO的过程如果某一项θ等于0了,就说明LASSO Regression认为这个θ对应的特征是没有用的,剩下的那些不等于0的θ就说明LASSO Regression认为对应的这些特征有用,所以他可以当做特征选择用
当使用Ridge的时候,当α趋近与无穷大,那么使用梯度下降法的J(θ)的导数如下图,J(θ)向0趋近的过程中,每个θ都是有值的
image.png
但是LASSO不同,在LASSO的损失函数中,如果我们让α趋近于无穷,只看后面一部分的话,那么后面一部分的绝对值实际上是不可导的,我们可以使用一种sign函数刻画一下绝对值导数,如下图。那么这个时候,同样在J(θ)向0趋近的过程中,他会先走到θ等于0的y轴位置,然后再沿着y轴往下向零点的方向走
image.png
这也说明了Ridge为什么叫岭回归,因为他更像是翻山越岭一样,在梯度下降法中一点一点找坡度缓的方向前进。而LASSO的路径就比较规则,会在训练的过程中碰到一些轴使得某些θ为0。
所以从计算准确度上来说,我们应该更加倾向于Ridge,但是如果我们的维度比较多,样本非常大(比如多项式回归时degree=100)
9.L1,L2和弹性网络
Ridge和LASSO都是在损失函数中添加一项,来调节θ的值使其尽可能的小,使得我们的模型泛化能力更好一些
在机器学习领域中,我们会发明不同的名词来描述不同的标准,比如用Ridge和LASSO来衡量正则化的这一项;MSE和MAE用来衡量回归结果的好坏,欧拉距离和曼哈顿距离用来衡量两点之间的距离。但是他们背后的数学思想是非常的类似的,表达出的数学含义也是一致的。只不过应用到不同的场景中产生了不同的效果
image.png
1.L1,L2正则
对明克夫斯基距离进一步泛化
image.png
对任意一个维度X,我们都可以求这样一个值,他的每一个值x都对他的p次方进行求和,再开p次方根。通常将这个式子成为LP范数。
当P=1的时候就是L1范数(曼哈顿距离|Ridge Regression),当P=2的时候就是 L2范数(欧拉距离|LASSO Regression)image.png
注:有了L1,L2正则项,我们就可以进一步得到LN正则项,虽然实际应用中我们的n不会超过2,但是在数学推导中是有意义的
2.L0正则项
我们希望让θ的个数尽量小,描述的是非零θ元素的个数。我们用这样的方式来限制θ的数量尽可能的小,进而来限制我们的曲线不要太抖
不过实际上我们是很少使用L0正则的,因为L0正则的优化是一个NP难的问题,我们不能使用诸如梯度下降法甚至数学公式来找到一个最优解。
他是一个离散的值,我们需要穷举所有θ的值来找出哪些θ需要,哪些不需要。实际上可以用L1正则(LASS0 Regression)来替代,从而达到选择去掉一些θ的过程image.png
3.弹性网 Elastic NET
在损失函数下,添加上一个L1正则项和一个L2正则项,并引入一个参数r来表示他们之间的比例。同时结合了岭回归和LASSO回归的优势
image.png
实际应用中,通常应该先尝试一下岭回归(如果计算能力足够的话)。但是如果θ数量太大的话,消耗计算资源可能非常大,而LASSO由于有的时候急于把一些θ化为0,可能会导致得到的偏差比价大。这个时候需要使用弹性网
回忆小批量梯度下降法也是将随机梯度下降法和批量梯度下降法结合到了一起。在机器学习领域中,经常使用这种方式来创造出一些新的方法,这些方法虽然名词非常的酷,但是他们背后的意义是非常简单的
模型泛化的一个举例。我们在考试前会做很多练习题。我们做练习题不是为了把全部的练习题(训练数据集)都得到满分,而是为了在最后的那一场考试(真实数据)中得到满分
-
多项式回归:正规方程参数θ的推导过程
2018-09-04 19:07:20多变量线性回归代价函数为: 其中: 正规方程是通过求解下面的方程来找出使得代价函数最小的参数: 设有m个训练实例,每个实例有n个特征,则训练实例集为: 其中表示第i个实例第j个特征。 特征参数为... -
【机器学习】多项式回归原理介绍
2019-09-26 19:39:35【机器学习】多项式回归原理介绍 【机器学习】多项式回归python实现 【机器学习】多项式回归sklearn实现 在上一节中我们介绍了线性回归的原理,然后分别用python和sklearn实现了不同变量个数的线性回归的几个例子。... -
多项式回归(python实现)
2019-12-07 12:51:13我们会遇到不同的数据分布情况,当数据点呈现带状分布的时候我们会选择线性回归的方法去拟合,但是如何数据点是一条曲线的时候使用线性回归的方法去拟合效果就不是那么好了,这个时候我们可以使用多项式回归的方法,... -
1.李航机器学习-多项式回归公式推导及python实现
2019-06-07 10:56:34多项式回归 项目链接:https://github.com/Wchenguang/gglearn/blob/master/PolynomialClassifier/李航机器学习-讲解/PolynomialClassifier.ipynb 公式推导 损失函数定义为平方损失函数 cost=12(f(X)−Y)2L=1N×... -
[Python从零到壹] 十二.机器学习之回归分析万字总结全网首发(线性回归、多项式回归、逻辑回归)
2021-07-03 09:28:40本文介绍回归模型的原理知识,包括线性回归、多项式回归和逻辑回归,并详细介绍Python Sklearn机器学习库的LinearRegression和LogisticRegression算法及回归分析实例。进入基础文章,希望对您有所帮助。 -
多项式回归(polynomial regression)转换为线性回归(linear regression)
2017-11-16 17:26:09一元m次多项式回归方程: 二元二次多项式回归方程: 多元多次的多项式回归方程较复杂,加之实际生产生活中一元m次多项式归回就已经能够解决了,所以略! 对于一元m次多项式回归方程,令: 则该一元m次... -
学习笔记——机器学习--多项式分布及Softmax回归模型推导
2018-08-09 18:37:06多项式分布表达式转为指数分布族表达式推导过程如下: P ( y ; ϕ ) = ϕ 1 { y = 1 } 1 ϕ 1 { y = 2 } 2 ⋯ ϕ 1 { y = k } k = ϕ T ( y ... -
scikit-learn : 线性回归,多元回归,多项式回归
2016-06-17 23:36:53使用scikit-learn学习线性回归,多元回归,多项式回归 -
一文带你完全了解线性回归模型的梯度下降算法、多项式回归、正则化
2018-06-24 11:55:231.线性回归参数求解一般的线性模型,等式如下所示:ŷ是预测值。n是特征的数量。xi是第i个特征值。θj是第j个模型参数(包括偏置项θ0和特征权重θ1,θ2,...,θn)。这可以使用向量的形式写得更简洁:θ是模型的... -
Standford 机器学习学习笔记线性回归和多项式回归
2013-05-08 09:15:02Coursera 上的 Standford 机器学习学习笔记线性回归和多项式回归 -
matlab 多元多项式回归_python数据分析——在python中实现线性回归
2020-11-22 10:24:22线性回归是基本的统计和机器学习技术之一。...而至于线性回归的数学推导、线性回归具体怎样工作,参数选择如何改进回归模型将在以后说明。回归回归分析是统计和机器学习中最重要的领域之一。有许多可用的回归方法。... -
逻辑回归原理详细推导
2018-10-27 21:02:33逻辑回归的基础是线性回归,但如果我们用线性回归可能会出现两个问题,第一个问题受Rachel_zhang的博客启发,描述如下: 如果我们通过线性回归来预测癌症,特征是肿瘤的size。当数据点只有左边八个时,可以用粉红... -
NNs(Neural Networks,神经网络)和Polynomial Regression(多项式回归)等价性之思考,以及深度模型可...
2019-10-08 11:03:33第二章:我们会分别介绍NNs神经网络和PR多项式回归各自的定义和应用场景。 第三章:讨论NNs和PR在数学公式上的等价性,NNs和PR是两个等价的理论方法,只是用了不同的方法解决了同一个问题,这样我们就形成了一个... -
机器学习系列笔记八:多项式回归[下]
2020-07-06 19:50:13机器学习系列笔记八:多项式回归[下] 在上一节,我们引入了多项式回归,并通过一些简单的编程来实现了它,然后,我们通过实验分析出了在回归问题中常见的两种问题:过拟合与欠拟合。 我们提到,测试集的意义就在于在... -
十分钟掌握多项式回归:拟合非线性关系
2019-01-04 16:04:32之前我们曾经学习了简单线性回归模型的推导、sklearn实战,并尝试从零搭建了一个简单线性回归的模型工具。...一、快速理解多项式回归原理 我们先来回顾一下简单线性回归的假设: y^=α+βx\hat{y}=... -
Python3入门机器学习(八)- 多项式回归
2019-01-13 13:18:431.多项式回归简介 2.scikit-learn中的多项式回归于pipeline 3.过拟合与前拟合 4.学习曲线 5.验证数据集与交叉验证 6.偏差方差均衡 7.模型正则化 8.LASSO 9.L1,L2和弹性网络 1.多项式回归简介 考虑下面的数据,... -
机器学习之线性回归、多项式回归
2019-10-07 00:29:42回归属于有监督学习中的一种方法。该方法的核心思想是从连续型统计数据中得到数学模型,然后将该数学模型用于 预测或者分类。该方法处理的数据可以是多维的。 一、线性回归 原理: 在连续型统计数据情况... -
机器学习-2 多元线性回归、多项式回归
2013-11-03 19:03:46多元线性回归: 通过尺寸预测房价,只有尺寸一个特征,如果有多个特征比如楼层、房间数等,则 1. Hypothesis:假设(输入到输出的映射) 2. Parameters:参数/特征权重 3. cost function:代价函数 4.... -
十分钟掌握多项式回归:非线性预测
2019-01-04 08:21:35之前我们曾经学习了简单线性回归模型的推导、sklearn实战,并尝试从零搭建了一个简单线性回归的模型工具。 有任何问题都可以在下方留言,我都会耐心解答。 但是我们遇到的数据并不总是线性的,这时如果我们还拿... -
机器学习2.多项式回归
2022-05-27 11:57:33完整代码 # 多项式回归 import numpy as np import matplotlib.pyplot as plt import sklearn.pipeline as pl import sklearn.preprocessing as sp import sklearn.linear_model as lm from sklearn.metrics import ... -
多项式曲线拟合之最小二乘法推导
2020-12-16 10:27:441、多项式曲线拟合之最小二乘法 1.1 问题来源 1801年,意大利天文学家朱赛普·皮亚齐发现了第一颗小行星谷神星。经过40天的跟踪观测后,由于谷神星运行至太阳背后,使得皮亚齐失去了谷神星的位置。随后全世界的... -
scikit-learn线性回归,多元回归,多项式回归的实现
2020-11-30 01:58:19下面我们用多项式回归,一种特殊的多元线性回归方法,增加了指数项。现实世界中的曲线关系都是通过增加多项式实现的,其实现方式和多元线性回归类似。本例还用一个解释变量,匹萨直径。让我们用下面的数据对两种模型... -
3.0-逻辑回归原理和学习(LR)LogisticRegression(决策边界,多项式逻辑回归,逻辑回归解决多分类问题)
2019-07-18 13:50:20逻辑回归: 即可以看做回归算法,也可以看做分类算法。一般当做分类算法来用,解决二分类问题,如果要解决多分类问题,需要自己改进。 看下逻辑回归的问题点,对于逻辑回归来说,解决二分类问题。...下面附上推导... -
【机器学习】多项逻辑斯蒂回归/softmax回归推导与求解
2018-10-14 22:17:151. 多项逻辑斯蒂回归模型 多项逻辑斯蒂回归模型(multi-...前面在逻辑斯蒂回归原理推导与求解中讲到的逻辑斯蒂回归模型是二项分类模型,用于二分类。将其推广为多项分类,假设离散型随机变量的取值集合是,那么多项...