我们在学习机器学习的时候会经常听到正则化(Regularization),其一般是用于改善或者减少过度拟合问题。
下图是一个回归问题的例子:
第一个模型是一个线性模型,欠拟合,不能很好地适应我们的训练集;第三个模型是一
个四次方的模型,过于强调拟合原始数据,而丢失了算法的本质:预测新数据。我们可以看
出,若给出一个新的值使之预测,它将表现的很差,是过拟合,虽然能非常好地适应我们的
训练集但在新输入变量进行预测时可能会效果不好;而中间的模型似乎最合适。
分类问题中也存在这样的问题:
就以多项式理解,x 的次数越高,拟合的越好,但相应的预测的能力就可能变差。
问题是,如果我们发现了过拟合问题,应该如何处理?
- 丢弃一些不能帮助我们正确预测的特征。可以是手工选择保留哪些特征,或者使用一些模型选择的算法来帮忙(例如 PCA)
- 正则化。 保留所有的特征,但是减少参数的大小(magnitude)。
在sklearn里实现正则化
X = [[1,-1,2], [2,0,0], [0,1,-1]] # 使用L2正则化 from sklearn.preprocessing import normalize X1 = normalize(X, norm = 'l2') # 使用L1正则化 from sklearn.preprocessing import Normalizer normalizer = Normalizer(norm= 'l1') X2 = normalizer.fit_transform(X)
normalize()参数:
- X : 需要正则化的特征
- norm : 设置范数,‘l1’, ‘l2’, 或者’max’, 默认是’l2’
- return_norm : boolean, 默认False,如果为True将会返回计算后的norm参数
正则化的结果如图:
L1正则化/Lasso
L1正则化将系数w的l1范数作为惩罚项加到损失函数上,由于正则项非零,这就迫使那些弱的特征所对应的系数变成0。因此L1正则化往往会使学到的模型很稀疏(系数w经常为0),这个特性使得L1正则化成为一种很好的特征选择方法。
Scikit-learn为线性回归提供了Lasso,为分类提供了L1逻辑回归。
下面的例子在波士顿房价数据上运行了Lasso,其中参数alpha是通过grid search进行优化的。
from sklearn.linear_model import Lasso from sklearn.preprocessing import StandardScaler from sklearn.datasets import load_boston boston = load_boston() scaler = StandardScaler() X = scaler.fit_transform(boston["data"]) Y = boston["target"] names = boston["feature_names"] lasso = Lasso(alpha=.3) lasso.fit(X, Y) print "Lasso model: ", pretty_print_linear(lasso.coef_, names, sort = True)
可以看到,很多特征的系数都是0。如果继续增加alpha的值,得到的模型就会越来越稀疏,即越来越多的特征系数会变成0。
然而,L1正则化像非正则化线性模型一样也是不稳定的,如果特征集合中具有相关联的特征,当数据发生细微变化时也有可能导致很大的模型差异。
L2正则化/Ridge regression
L2正则化将系数向量的L2范数添加到了损失函数中。由于L2惩罚项中系数是二次方的,这使得L2和L1有着诸多差异,最明显的一点就是,L2正则化会让系数的取值变得平均。对于关联特征,这意味着他们能够获得更相近的对应系数。还是以Y=X1+X2为例,假设X1和X2具有很强的关联,如果用L1正则化,不论学到的模型是Y=X1+X2还是Y=2X1,惩罚都是一样的,都是2alpha。但是对于L2来说,第一个模型的惩罚项是2 alpha,但第二个模型的是4*alpha。可以看出,系数之和为常数时,各系数相等时惩罚是最小的,所以才有了L2会让各个系数趋于相同的特点。
可以看出,L2正则化对于特征选择来说一种稳定的模型,不像L1正则化那样,系数会因为细微的数据变化而波动。所以L2正则化和L1正则化提供的价值是不同的,L2正则化对于特征理解来说更加有用:表示能力强的特征对应的系数是非零。
回过头来看看3个互相关联的特征的例子,分别以10个不同的种子随机初始化运行10次,来观察L1和L2正则化的稳定性。
from sklearn.linear_model import Ridge from sklearn.metrics import r2_score size = 100 #We run the method 10 times with different random seeds for i in range(10): print "Random seed %s" % i np.random.seed(seed=i) X_seed = np.random.normal(0, 1, size) X1 = X_seed + np.random.normal(0, .1, size) X2 = X_seed + np.random.normal(0, .1, size) X3 = X_seed + np.random.normal(0, .1, size) Y = X1 + X2 + X3 + np.random.normal(0, 1, size) X = np.array([X1, X2, X3]).T lr = LinearRegression() lr.fit(X,Y) print "Linear model:", pretty_print_linear(lr.coef_) ridge = Ridge(alpha=10) ridge.fit(X,Y) print "Ridge model:", pretty_print_linear(ridge.coef_) print
Random seed 0 Linear model: 0.728 * X0 + 2.309 * X1 + -0.082 * X2 Ridge model: 0.938 * X0 + 1.059 * X1 + 0.877 * X2
Random seed 1 Linear model: 1.152 * X0 + 2.366 * X1 + -0.599 * X2 Ridge model: 0.984 * X0 + 1.068 * X1 + 0.759 * X2
Random seed 2 Linear model: 0.697 * X0 + 0.322 * X1 + 2.086 * X2 Ridge model: 0.972 * X0 + 0.943 * X1 + 1.085 * X2
Random seed 3 Linear model: 0.287 * X0 + 1.254 * X1 + 1.491 * X2 Ridge model: 0.919 * X0 + 1.005 * X1 + 1.033 * X2
Random seed 4 Linear model: 0.187 * X0 + 0.772 * X1 + 2.189 * X2 Ridge model: 0.964 * X0 + 0.982 * X1 + 1.098 * X2
Random seed 5 Linear model: -1.291 * X0 + 1.591 * X1 + 2.747 * X2 Ridge model: 0.758 * X0 + 1.011 * X1 + 1.139 * X2
Random seed 6 Linear model: 1.199 * X0 + -0.031 * X1 + 1.915 * X2 Ridge model: 1.016 * X0 + 0.89 * X1 + 1.091 * X2
Random seed 7 Linear model: 1.474 * X0 + 1.762 * X1 + -0.151 * X2 Ridge model: 1.018 * X0 + 1.039 * X1 + 0.901 * X2
Random seed 8 Linear model: 0.084 * X0 + 1.88 * X1 + 1.107 * X2 Ridge model: 0.907 * X0 + 1.071 * X1 + 1.008 * X2
Random seed 9 Linear model: 0.714 * X0 + 0.776 * X1 + 1.364 * X2 Ridge model: 0.896 * X0 + 0.903 * X1 + 0.98 * X2
可以看出,不同的数据上线性回归得到的模型(系数)相差甚远,但对于L2正则化模型来说,结果中的系数非常的稳定,差别较小,都比较接近于1,能够反映出数据的内在结构。
#coding:utf-8 from __future__ import division import sys reload(sys) sys.setdefaultencoding('utf-8') import time start_time = time.time() import pandas as pd # 输入训练样本的特征以及目标值,分别存储在变量X_train与y_train之中。 X_train = [[6], [8], [10], [14], [18]] y_train = [[7], [9], [13], [17.5], [18]] # 从sklearn.linear_model中导入LinearRegression。 from sklearn.linear_model import LinearRegression # 使用默认配置初始化线性回归模型。 regressor = LinearRegression() # 直接以披萨的直径作为特征训练模型。 regressor.fit(X_train, y_train) # 导入numpy并且重命名为np。 import numpy as np # 在x轴上从0至25均匀采样100个数据点。 xx = np.linspace(0, 26, 100) xx = xx.reshape(xx.shape[0], 1) # 以上述100个数据点作为基准,预测回归直线。 yy = regressor.predict(xx) # 对回归预测到的直线进行作图。 import matplotlib.pyplot as plt plt.scatter(X_train, y_train) plt1, = plt.plot(xx, yy, label="Degree=1") plt.axis([0, 25, 0, 25]) plt.xlabel('Diameter of Pizza') plt.ylabel('Price of Pizza') plt.legend(handles = [plt1]) plt.show() # 输出线性回归模型在训练样本上的R-squared值。 print 'The R-squared value of Linear Regressor performing on the training data is', regressor.score(X_train, y_train) # 从sklearn.preproessing中导入多项式特征产生器 from sklearn.preprocessing import PolynomialFeatures # 使用PolynominalFeatures(degree=2)映射出2次多项式特征,存储在变量X_train_poly2中。 poly2 = PolynomialFeatures(degree=2) X_train_poly2 = poly2.fit_transform(X_train) # 以线性回归器为基础,初始化回归模型。尽管特征的维度有提升,但是模型基础仍然是线性模型。 regressor_poly2 = LinearRegression() # 对2次多项式回归模型进行训练。 regressor_poly2.fit(X_train_poly2, y_train) # 从新映射绘图用x轴采样数据。 xx_poly2 = poly2.transform(xx) # 使用2次多项式回归模型对应x轴采样数据进行回归预测。 yy_poly2 = regressor_poly2.predict(xx_poly2) # 分别对训练数据点、线性回归直线、2次多项式回归曲线进行作图。 plt.scatter(X_train, y_train) plt1, = plt.plot(xx, yy, label='Degree=1') plt2, = plt.plot(xx, yy_poly2, label='Degree=2') plt.axis([0, 25, 0, 25]) plt.xlabel('Diameter of Pizza') plt.ylabel('Price of Pizza') plt.legend(handles = [plt1, plt2]) plt.show() # 输出2次多项式回归模型在训练样本上的R-squared值。 print 'The R-squared value of Polynominal Regressor (Degree=2) performing on the training data is', regressor_poly2.score(X_train_poly2, y_train) # 从sklearn.preprocessing导入多项式特征生成器。 from sklearn.preprocessing import PolynomialFeatures # 初始化4次多项式特征生成器。 poly4 = PolynomialFeatures(degree=4) X_train_poly4 = poly4.fit_transform(X_train) # 使用默认配置初始化4次多项式回归器。 regressor_poly4 = LinearRegression() # 对4次多项式回归模型进行训练。 regressor_poly4.fit(X_train_poly4, y_train) # 从新映射绘图用x轴采样数据。 xx_poly4 = poly4.transform(xx) # 使用4次多项式回归模型对应x轴采样数据进行回归预测。 yy_poly4 = regressor_poly4.predict(xx_poly4) # 分别对训练数据点、线性回归直线、2次多项式以及4次多项式回归曲线进行作图。 plt.scatter(X_train, y_train) plt1, = plt.plot(xx, yy, label='Degree=1') plt2, = plt.plot(xx, yy_poly2, label='Degree=2') plt4, = plt.plot(xx, yy_poly4, label='Degree=4') plt.axis([0, 25, 0, 25]) plt.xlabel('Diameter of Pizza') plt.ylabel('Price of Pizza') plt.legend(handles = [plt1, plt2, plt4]) plt.show() print 'The R-squared value of Polynominal Regressor (Degree=4) performing on the training data is',regressor_poly4.score(X_train_poly4, y_train) # 准备测试数据。 X_test = [[6], [8], [11], [16]] y_test = [[8], [12], [15], [18]] # 使用测试数据对线性回归模型的性能进行评估。 regressor.score(X_test, y_test) # 使用测试数据对2次多项式回归模型的性能进行评估。 X_test_poly2 = poly2.transform(X_test) regressor_poly2.score(X_test_poly2, y_test) # 使用测试数据对4次多项式回归模型的性能进行评估。 X_test_poly4 = poly4.transform(X_test) regressor_poly4.score(X_test_poly4, y_test) # 从sklearn.linear_model中导入Lasso。 from sklearn.linear_model import Lasso # 从使用默认配置初始化Lasso。 lasso_poly4 = Lasso() # 从使用Lasso对4次多项式特征进行拟合。 lasso_poly4.fit(X_train_poly4, y_train) # 对Lasso模型在测试样本上的回归性能进行评估。 print lasso_poly4.score(X_test_poly4, y_test) # 输出Lasso模型的参数列表。 print lasso_poly4.coef_ # 回顾普通4次多项式回归模型过拟合之后的性能。 print regressor_poly4.score(X_test_poly4, y_test) # 回顾普通4次多项式回归模型的参数列表。 print regressor_poly4.coef_ # 输出上述这些参数的平方和,验证参数之间的巨大差异。 print np.sum(regressor_poly4.coef_ ** 2) # 从sklearn.linear_model导入Ridge。 from sklearn.linear_model import Ridge # 使用默认配置初始化Riedge。 ridge_poly4 = Ridge() # 使用Ridge模型对4次多项式特征进行拟合。 ridge_poly4.fit(X_train_poly4, y_train) # 输出Ridge模型在测试样本上的回归性能。 print ridge_poly4.score(X_test_poly4, y_test) # 输出Ridge模型的参数列表,观察参数差异。 print ridge_poly4.coef_ # 计算Ridge模型拟合后参数的平方和。 print np.sum(ridge_poly4.coef_ ** 2)
"D:\Program Files\Python27\python.exe" D:/PycharmProjects/sklearn/模型正则化.py The R-squared value of Linear Regressor performing on the training data is 0.910001596424 The R-squared value of Polynominal Regressor (Degree=2) performing on the training data is 0.98164216396 The R-squared value of Polynominal Regressor (Degree=4) performing on the training data is 1.0 0.83889268736 [ 0.00000000e+00 0.00000000e+00 1.17900534e-01 5.42646770e-05 -2.23027128e-04] 0.809588079575 [[ 0.00000000e+00 -2.51739583e+01 3.68906250e+00 -2.12760417e-01 4.29687500e-03]] 647.382645692 0.837420175937 [[ 0. -0.00492536 0.12439632 -0.00046471 -0.00021205]] 0.0154989652035