目标
在本实验室你可以:
探索特征工程和多项式回归,它们允许您使用线性回归的机制来拟合非常复杂,甚至是非线性的函数。
工具
您将利用在以前的实验中开发的函数以及matplotlib和NumPy。
import numpy as np
import matplotlib.pyplot as plt
from lab_utils_multi import zscore_normalize_features, run_gradient_descent_feng
np.set_printoptions(precision=2) # reduced display precision on numpy arrays
特征工程和多项式回归概述
线性回归提供了一种建立形式模型的方法:
f
w
,
b
=
w
0
x
0
+
w
1
x
1
+
.
.
.
+
w
n
−
1
x
n
−
1
+
b
(1)
f_{\mathbf{w},b} = w_0x_0 + w_1x_1+ ... + w_{n-1}x_{n-1} + b \tag{1}
fw,b=w0x0+w1x1+...+wn−1xn−1+b(1)
如果你的特征/数据是非线性的,或者是特征的组合呢?
比如:
房价不倾向于与居住面积成线性关系,而是对非常小或非常大的房子不利,导致上图所示的曲线。我们如何使用线性回归的机制来拟合这条曲线呢?回顾一下,我们有能力去修改式子1中的参数w,b达到拟合训练数据中。然而,w和b再多的调整也实现不了(1)中对非线性曲线的拟合。
生成多项式特征
上面我们考虑的是一个数据是非线性的场景。我们试着用已知的知识来拟合非线性曲线。我们从一个简单的二次函数开始:
y
=
1
+
x
2
y = 1+x^2
y=1+x2
这些都可以在 lab_utils.py file 中被使用,We’ll use np.c_[…]。这一个NumPy例程,用于沿着列边界进行连接。
【注解】X@model_w计算的是X和model_w的矩阵乘法。@操作符在Python中表示矩阵乘法
# create target data
x = np.arange(0, 20, 1)
y = 1 + x**2
X = x.reshape(-1, 1)
model_w,model_b = run_gradient_descent_feng(X,y,iterations=1000, alpha = 1e-2)
plt.scatter(x, y, marker='x', c='r', label="Actual Value"); plt.title("no feature engineering")
plt.plot(x,X@model_w + model_b, label="Predicted Value"); plt.xlabel("X"); plt.ylabel("y"); plt.legend(); plt.show()
不出所料,不太合适。类似于 y = w 0 x 0 2 + b y= w_0x_0^2 + b y=w0x02+b或者多项式特征。要实现这一点,您可以修改输入数据来设计所需的特性。如果将原始数据与平方的版本交换X值。那么就可以实现 y = w 0 x 0 2 + b y= w_0x_0^2 + b y=w0x02+b。接下来试试把X换成X**2:
# create target data
x = np.arange(0, 20, 1)
y = 1 + x**2
# Engineer features
X = x**2 #<-- added engineered feature
X = X.reshape(-1, 1) #X should be a 2-D Matrix
model_w,model_b = run_gradient_descent_feng(X, y, iterations=10000, alpha = 1e-5)
plt.scatter(x, y, marker='x', c='r', label="Actual Value"); plt.title("Added x**2 feature")
plt.plot(x, np.dot(X,model_w) + model_b, label="Predicted Value"); plt.xlabel("x"); plt.ylabel("y"); plt.legend(); plt.show()
太棒了!近乎完美的契合。注意w和b的值在图的正上方打印:w,b 由梯度下降可以看到: w: [1.], b: 0.0490。梯度下降修正了的初始值W和b成为(1.0,0.049) y = 1 ∗ x 0 2 + 0.049 y=1*x_0^2+0.049 y=1∗x02+0.049的模型。非常接近我们的目标 y = 1 ∗ x 0 2 + 1 y=1*x_0^2+1 y=1∗x02+1,如果你把它跑久一点,它可能是一个更好的匹配。
特征选取
上面,我们知道需要 x 2 x^2 x2项非常需要,需要哪些特性可能并不总是很明显。我们可以添加各种潜在的特性来尝试找到最有用的。 例如,如果我们尝试: y = w 0 x 0 + w 1 x 1 2 + w 2 x 2 3 + b y=w_0x_0 + w_1x_1^2 + w_2x_2^3+b y=w0x0+w1x12+w2x23+b ?
# create target data
x = np.arange(0, 20, 1)
y = x**2
# engineer features .
X = np.c_[x, x**2, x**3] #<-- added engineered feature
model_w,model_b = run_gradient_descent_feng(X, y, iterations=10000, alpha=1e-7)
plt.scatter(x, y, marker='x', c='r', label="Actual Value"); plt.title("x, x**2, x**3 features")
plt.plot(x, X@model_w + model_b, label="Predicted Value"); plt.xlabel("x"); plt.ylabel("y"); plt.legend(); plt.show()
得到w=[0.08 0.54 0.03],b=0.0106.这意味着拟合/训练后的模型为:
0.08
x
+
0.54
x
2
+
0.03
x
3
+
0.0106
0.08x + 0.54x^2 + 0.03x^3 + 0.0106
0.08x+0.54x2+0.03x3+0.0106
梯度下降强调了最适合
x
2
x^2
x2数据的数据,通过增加相对于其他数据的
w
1
w_1
w1项。如果你要跑很长时间,它会继续减少其他条款的影响。
梯度下降是通过强调其相关参数来为我们选择“正确”的特征
让我们回顾一下这个想法:
最初,这些特征被重新缩放,使它们彼此具有可比性
更小的权重值意味着更不重要/正确的特征,在极端情况下,当权重变为零或非常接近于零时,相关的特征有助于将模型拟合到数据中。
上面,在拟合之后,与
x
2
x^2
x2特征相关的权值比
x
x
x或
x
3
x^3
x3的权值大得多,因为它在拟合数据时最有用。
备用视图
上面,多项式特征是根据它们与目标数据的匹配程度来选择的。考虑这个问题的另一种方式是注意到,一旦我们创建了新的特征,我们仍然使用线性回归。鉴于此,最佳特征将是相对于目标的线性特征。通过一个例子可以更好地理解这一点。
# create target data
x = np.arange(0, 20, 1)
y = x**2
# engineer features .
X = np.c_[x, x**2, x**3] #<-- added engineered feature
X_features = ['x','x^2','x^3']
fig,ax=plt.subplots(1, 3, figsize=(12, 3), sharey=True)
for i in range(len(ax)):
ax[i].scatter(X[:,i],y)
ax[i].set_xlabel(X_features[i])
ax[0].set_ylabel("y")
plt.show()
上面,很明显 x 2 x^2 x2特征映射到目标值 y y y是线性的。然后,线性回归可以很容易地使用该特征生成模型。
标度特性
如上一个实验所述,如果数据集具有明显不同尺度的特征,则应将特征缩放应用于速度梯度下降。在上面的例子中,有 x x x, x 2 x^2 x2和 x 3 x^3 x3,它们自然会有非常不同的尺度。让我们把z分数归一化应用到我们的例子中。
# create target data
x = np.arange(0,20,1)
X = np.c_[x, x**2, x**3]
print(f"Peak to Peak range by column in Raw X:{np.ptp(X,axis=0)}")
# add mean_normalization
X = zscore_normalize_features(X)
print(f"Peak to Peak range by column in Normalized X:{np.ptp(X,axis=0)}")
现在我们可以用一个更激进的alpha值再试一次:
x = np.arange(0,20,1)
y = x**2
X = np.c_[x, x**2, x**3]
X = zscore_normalize_features(X)
model_w, model_b = run_gradient_descent_feng(X, y, iterations=100000, alpha=1e-1)
plt.scatter(x, y, marker='x', c='r', label="Actual Value"); plt.title("Normalized x x**2, x**3 feature")
plt.plot(x,X@model_w + model_b, label="Predicted Value"); plt.xlabel("x"); plt.ylabel("y"); plt.legend(); plt.show()
特征缩放允许更快地收敛。
再次注意
w
\mathbf{w}
w的值。
w
1
w_1
w1项,也就是
x
2
x^2
x2 项是最重要的。梯度下降法几乎消除了
x
3
x^3
x3项。
复数函数
通过特征工程,即使是非常复杂的函数也可以建模:
x = np.arange(0,20,1)
y = np.cos(x/2)
X = np.c_[x, x**2, x**3,x**4, x**5, x**6, x**7, x**8, x**9, x**10, x**11, x**12, x**13]
X = zscore_normalize_features(X)
model_w,model_b = run_gradient_descent_feng(X, y, iterations=1000000, alpha = 1e-1)
plt.scatter(x, y, marker='x', c='r', label="Actual Value"); plt.title("Normalized x x**2, x**3 feature")
plt.plot(x,X@model_w + model_b, label="Predicted Value"); plt.xlabel("x"); plt.ylabel("y"); plt.legend(); plt.show()