上篇提到了Lasso,表现虽然不如Ridge好,但是具有特征选择的特性那么能不能把这两个模型进行融合呢,Elastic NEt 就是起到了这样的作用,损失函数表达式如下:
代码如下:
import numpy as np
from sklearn.linear_model import ElasticNetCV
from sklearn.preprocessing import PolynomialFeatures
import matplotlib.pyplot as plt
from sklearn.pipeline import Pipeline
from sklearn.exceptions import ConvergenceWarning
import matplotlib as mpl
mpl.rcParams['font.sans-serif'] = [u'simHei']
mpl.rcParams['axes.unicode_minus'] = False
import warnings
warnings.filterwarnings(action='ignore', category=ConvergenceWarning)
np.random.seed(0)
np.set_printoptions(linewidth=1000)
N = 7
x = np.linspace(0, 6, N) + 0.5 * np.random.randn(N)
x = np.sort(x)
y = x ** 2 - 4 * x - 3 + 2 * np.random.randn(N)
x.shape = -1, 1 # 将一维转换成二维
y.shape = -1, 1
model = Pipeline([('poly', PolynomialFeatures()),
('linear', ElasticNetCV(alphas=np.logspace(-2, 5, 10),
l1_ratio=np.linspace(-1, 2, 10),
fit_intercept=False))])
np.set_printoptions(suppress=True)
plt.figure(figsize=(15, 12), facecolor='w')
d_pool = np.arange(1, N, 1) # 阶
m = d_pool.size
title = 'ElasticNetCV回归'
plt.figure(figsize=(12, 10), facecolor='w')
plt.plot(x, y, 'ro', ms=20, zorder=N)
for i, d in enumerate(d_pool):
print()
model.set_params(poly__degree=d)
model.fit(x, y.ravel())
lin = model.get_params('linear')['linear']
output = '%s:%d阶,系数为:' % (title, d)
if hasattr(lin, 'alpha_'):
idx = output.find('系数')
output = output[:idx] + ('alpha=%.6f,' % lin.alpha_) + output[idx:]
if hasattr(lin, 'l1_ratio_'): # 根据交叉验证结果,从输入l1_ratio(list)中选择的最优l1_ratio_(float)
idx = output.find('系数')
output = output[:idx] + ('l1_ratio=%.6f,' % lin.l1_ratio_) + output[idx:]
print(output, lin.coef_.ravel()) # 权重向量
x_hat = np.linspace(x.min() - 1, x.max() + 1, num=100)
x_hat.shape = -1, 1
y_hat = model.predict(x_hat)
s = model.score(x, y)
print('R2:', s)
z = N - 1 if (d == 2) else 0
# z是下面画图中的zorder参数的值,是指该线在图中的级别,数值越大,级别越高,
# 在多线交叉时会显示在最上面,也就是会压住其他的线显示在最前面,这里是设置二阶拟合的级别最高
label = '%d阶,$R^2$=%.3f' % (d, s)
if hasattr(lin, 'l1_ratio_'):
label += ',L1 ratio=%.2f' % lin.l1_ratio_
plt.plot(x_hat, y_hat, lw=5, alpha=1, label=label, zorder=z)
plt.legend(loc='upper left', facecolor='w', edgecolor='blue', fontsize='xx-large')
plt.grid(True)
plt.title(title, fontsize=18)
plt.xlabel('X', fontsize=16)
plt.ylabel('Y', fontsize=16)
plt.tight_layout(1, rect=(0, 0, 1, 0.95))
# tight_layout会自动调整子图参数,调整子图之间的间隔来减少堆叠,使之填充整个图像区域。
plt.suptitle('多项式曲线拟合比较', fontsize=22)
plt.show()
系数及结果如下:
ElasticNetCV回归:1阶,alpha=0.059948,l1_ratio=0.333333,系数为: [-9.68189207 2.78173425]
R2: 0.8284768687061163
ElasticNetCV回归:2阶,alpha=0.059948,l1_ratio=-1.000000,系数为: [-3.16337538 -3.03225223 0.86461271]
R2: 0.9809928051335852
ElasticNetCV回归:3阶,alpha=12.915497,l1_ratio=-0.333333,系数为: [-0.36471415 -0.39971265 -0.44166661 0.12873411]
R2: 0.7593896789281127
ElasticNetCV回归:4阶,alpha=12.915497,l1_ratio=0.666667,系数为: [-0. -0. -0. -0.07475168 0.01907907]
R2: 0.6233794806606565
ElasticNetCV回归:5阶,alpha=2.154435,l1_ratio=-1.000000,系数为: [-0.81282517 -0.87231938 -0.91768272 0.31423328 -0.00221955 -0.00202697]
R2: 0.8771007327128545
ElasticNetCV回归:6阶,alpha=100000.000000,l1_ratio=0.000000,系数为: [-0.00002788 -0.00004427 -0.00008442 -0.00017548 -0.00034272 -0.00046306 0.00026168]
R2: 0.5968261476702981
画图如下:
表现一般,并没有将两个的长处进行累加,还有就是不懂为啥所有的拟合曲线都会通过最后一个样本点,有知道的大佬请解释下啊。