线性模型之三:正则化详解
一、引起过拟合的原因
- 训练数据过少,训练数据的分布不能表示整体样本的分布。
- 特征过多。特征太多其实也属于模型复杂。
- 模型过于复杂。高阶多项式。
二、缓解过拟合的方法——正则化
正则化的本质是使模型简单,解决了特征过多,模型过于复杂的问题。
本质是对权重W的约束。y=wx,当w变小时,相应的x就没有意义了,相当于x变小了。
通常的解释是,越小的权重,模型复杂度越低(例如特征X剧烈变化时,由于w很小,y的变化就会比较小),因此能够更简单有效的描绘数据,所以我们倾向于选择较小的权重。
三、岭回归(L2)与LASSO回归(L1)
- 岭回归是基于L2惩罚项的模型,是在最小二乘代价函数中加入了权重的平方和。
其中
- LASSO回归:对于基于稀疏数据训练的模型,还有另外一种解决方案,即LASSO。基于正则化项的强度,某些权重可以为零(使得对应的权重 xi 失去作用),这也使得LASSO成为一种监督特征选择技术。
其中
一般回归分析中,w表示特征 x 的系数,是特征重要性的度量,表示输入特征 x 对输出 y 的影响度。正则化正是对系数 w 做了处理(限制)。
- L1正则化是指权重值向量 w 中各个元素的绝对值之和, 通常表示为 ||w||。
- L2正则化是指权值向量 w 中各个元素的平方和然后求平方根。(可以看到Ridge回归的L2正则化项有平方符号),通常表示为 ||w||2 。
一般会在正则化项之前添加一个系数,这个系数需要由用户指定。
L1和L2正则化的作用:
- L1正则化可以产生稀疏权值矩阵的作用,即产生一个稀疏模型,用于特征选择。
- L2正则化可以防止模型过拟合(overfitting);一定程度上,L1也可以防止过拟合。
毕竟一个简单的解释的出现似乎不可能仅仅是因为巧合,我们猜测这个模型一定表达了关于这个现象的一些潜在真理。
复杂模型往往容易学习到了一些局部噪声的影响(现实的数据总是有噪声的)。因此当 一个复杂模型(比如一个n次多项式模型)完美拟合了特定数据集的时候,这样的模型一般都不能很好泛化到其它数据集上,所以包含一定噪声的线性模型在预测中会有更好的表现。
更倾向简单模型的想法,人们有时把它称作「奥卡姆剃刀」,并且就好像它是科学原理一样,热情地应用它。然而,它并不是一个普遍成立的科学原理。并不存在一个先验的符合逻辑的理由倾向于简单的模型,而不是复杂的模型。实际上,有时候更复杂的模型反而是正确的。
需要根据现实的数据和应用来正确的判断,只是大多数情况下,在满足一定性能的前提下,选择更简单的模型。
如下图所示,绿色表示真实的分布。M表示幂函数的幂次。虽然高次幂函数可以很好的拟合现有训练数据,但是泛化性能不好。相反如果倾向于用更简单的模型来拟合数据,可能直线也是一个可选的模型。但实际情况确实一个三次幂函数,既不是最简单的模型,也不是那个拟合最好的复杂模型。所以要根据现实场景来判断。
四、正则项作用原理的解读
下面两个图,左侧图形使用了最高10次项的多项式拟合(没有加正则项)。虽然能够很好的拟合样本中的所有的点,但并不是理想的拟合,似乎泛化能力很弱。右侧图形在左侧图形基础上加上了L2正则项(Ridge Regression),得到了一个相对理想的模型。
原因分析:
对于一个十次多项式的假设和一个二次多项式的假设。
H_10的假设是包含H_2的假设的。如果把H_10中的 w_3 … w_10等项都设为0,就得到H_2假设。可以理解为二次多项式就是十次多项式对w加上一些限制得到的。 也就是说,我们的目标是找出w_3 … w_10都为0的假设,就可以得出目标二次多项式。如果条件放宽松点,把w的长度规定在C以内,即,则会有更大的可能找出w_3 … w_10都为0的假设。这就是正则化的由来,把w限制在一定范围之内。
我们求解的问题就是
把上面两个带约束的假设化成图就是如下图所示(假设w是二维的)。L2是在W空间中加了一个球形区域的约束,在这个球形区域内找最优解,而L1就是在这个菱形区域找最优解。
为什么L1正则化更容易将特征稀疏化?
见下面这个图。
不同颜色曲线为等高图,颜色越冷表示代价函数J(w1,w2)的值越小,由于被限制在L1的菱形范围内,所以等高线一旦接触到顶点,就满足了限制范围内的最小值,因为圈越往外,J值越大。
因为四个顶点上更容易在极值点上被接触,而四个顶点上,会使得部分w为0。
正则化的一般形式如下:
越大,则最优解w可以落入的区域越小,越不容易发生过拟合。但是也越容易发生欠拟合。需要在实际使用中尝试不同参数,找出最好的 值。
另外,L1正则项用于得到稀疏的w,L2正则项用于得到长度比较小的w,在实际使用过程中两者可以结合使用。
*******************************************************************************华丽的分割线****************************************************************************
源代码
boston_house 数据文件
# In[0] import pandas as pd # 绘制散点图,计算MSE, R2 def model_eval(y_train_pred, y_train, y_test_pred, y_test): # 绘制散点图 plt.scatter(y_train_pred, y_train_pred - y_train, c=\'steelblue\', marker=\'o\', edgecolor=\'white\', label=\'Training_data\') plt.scatter(y_test_pred, y_test_pred-y_test, c=\'limegreen\', marker=\'s\', edgecolor=\'white\', label=\'Test_data\') plt.xlabel(\'Predicted values\') plt.ylabel(\'Residuals\') plt.legend(loc=\'upper left\') plt.hlines(y=0, xmin=-10, xmax=50, color=\'black\', lw=2) plt.xlim([-10, 50]) # 设置坐标轴的取值范围 plt.tight_layout() plt.show() # 计算均方误差MSE、决定系数R2 from sklearn.metrics import r2_score from sklearn.metrics import mean_squared_error print("MSE of train: %.2f, test, %.2f" % ( mean_squared_error(y_train, y_train_pred), mean_squared_error(y_test, y_test_pred))) print("R^2 of train: %.2f, test, %.2f" % ( r2_score(y_train, y_train_pred), r2_score(y_test, y_test_pred))) # 从csv载入房价数据 def read_house_data(file_path): df = pd.read_csv(file_path) df.columns = [\'row\', \'CRIM\', \'ZN\', \'INDUS\', \'CHAS\', \'NOX\', \'RM\', \'AGE\', \'DIS\', \'RAD\', \'TAX\', \'PTRATIO\', \'B\', \'LSTAT\', \'MEDV\'] df.drop("row", axis=1, inplace=True) #删除第一列的行号 X = df.iloc[:, :-1].values y = df[\'MEDV\'].values return X, y # In[1] X, y = read_house_data(\'boston_house.csv\') # In[2] from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0, test_size=0.3) # In[4] # 开始训练 from sklearn.linear_model import LinearRegression import numpy as np import pandas as pd import matplotlib.pyplot as plt lr = LinearRegression() lr.fit(X_train, y_train) y_train_pred = lr.predict(X_train) #训练数据的预测值 y_test_pred = lr.predict(X_test) #测试数据的预测值 model_eval(y_train_pred, y_train, y_test_pred, y_test) # In[7] # Ridge regression 岭回归 from sklearn.linear_model import Ridge ridge = Ridge(alpha=10) ridge.fit(X_train, y_train) y_train_pred = ridge.predict(X_train) y_test_pred = ridge.predict(X_test) print(ridge.coef_) print(lr.coef_) model_eval(y_train_pred, y_train, y_test_pred, y_test) # In[8] # Elastic Net regression from sklearn.linear_model import ElasticNet elanet = ElasticNet(alpha=0.1, l1_ratio=0.5) elanet.fit(X_train, y_train) y_train_pred = elanet.predict(X_train) y_test_pred = elanet.predict(X_test) print(elanet.coef_) print(lr.coef_) model_eval(y_train_pred, y_train, y_test_pred, y_test)