由于不同方向的陡峭度是不一样的,即不同维度的数值大小是不同。也就是说梯度下降的快慢是不同的,归一化的一个目的是,使得梯度下降在不同维度 θ \theta θ 参数(不同数量级)上,可以步调一致协同的进行梯度下降。归一化的本质就要把各个特征维度 x 1 、 x 2 、 … … 、 x n x_1、x_2、……、x_n x1、x2、……、xn 的数量级统一,来做到无量纲化。
也称为离差标准化,是对原始数据的线性变换,使结果值映射到[0 - 1]之间。转换函数如下:
.
X = X − X _ m i n X _ m a x − X _ m i n X = \frac{X - X\_min}{X\_max -X\_min} X=X_max−X_minX−X_min
通过公式可以发现,该方式受离群值的影响比较大.
使用scikit-learn函数演示:
import numpy as np
from sklearn.preprocessing import MinMaxScaler
x_1 = np.random.randint(1,10,size = 10)
x_2 = np.random.randint(100,300,size = 10)
x = np.c_[x_1,x_2] # 将数组放到一起
print('归一化之前的数据:')
min_max_scaler = MinMaxScaler()
x_ = min_max_scaler.fit_transform(x)
print('归一化之后的数据:')
display(x_)
这种方法给予原始数据的均值(mean)和标准差(standard deviation)进行数据的标准化,也叫做Z-score标准化。经过处理的数据符合标准正态分布,即均值为0,标准差为1,转化函数为:
.
X ∗ = X − μ σ X^* = \frac{X - \mu}{\sigma} X∗=σX−μ
其中μ为所有样本数据的均值,σ为所有样本数据的标准差。
相对于最大值最小值归一化来说,因为标准归一化除以了标准差,而标准差的计算会考虑到所有样本数据,所以受到离群值的影响会小一些,这就是除以方差的好处!但是,0-均值标准化不一定会把数据缩放到 0 ~ 1 之间了。既然是0均值,也就意味着,有正有负!
使用scikit-learn函数:
import numpy as np
from sklearn.preprocessing import StandardScaler
x_1 = np.random.randint(1,10,size = 10)
x_2 = np.random.randint(100,300,size = 10)
x = np.c_[x_1,x_2]
print('归一化之前的数据:')
standard_scaler = StandardScaler()
x_ = standard_scaler.fit_transform(x)
print('归一化之后的数据:')
display(x_)
注意:
我们在做特征工程的时候,很多时候如果对训练集的数据进行了预处理,比如这里讲的归一化,那么未来对测试集的时候,和模型上线来新的数据的时候,都要进行相同的数据预处理流程,而且所使用的均值和方差是来自当时训练集的均值和方差!
通过把 scaler 对象持久化, 回头模型上线的时候再加载进来去对新来的数据进行处理。
import joblib
joblib.dump(standard_scaler,'scale') # 持久化
standard_scaler = joblib.load('scale') # 加载
standard_scaler.transform(x) # 使用
先从线性回归开始,其损失函数如下:
J ( θ ) = 1 2 ∑ i = 1 n ( h θ ( x ( i ) ) − y ( i ) ) 2 J(\theta) = \frac{1}{2}\sum\limits_{i = 1}^n(h_{\theta}(x^{(i)}) - y^{(i)})^2 J(θ)=21i=1∑n(hθ(x(i))−y(i))2
L1正则化的损失函数,令 J 0 = J ( θ ) J_0 = J(\theta) J0=J(θ):
J = J 0 + α ∗ ∑ i = 1 n ∣ w i ∣ J = J_0 + \alpha * \sum\limits_{i = 1}^n|w_i| J=J0+α∗i=1∑n∣wi∣
令 L 1 = α ∗ ∑ i = 1 n ∣ w i ∣ L_1 = \alpha * \sum\limits_{i = 1}^n|w_i| L1=α∗i=1∑n∣wi∣ :
J = J 0 + L 1 J = J_0 + L_1 J=J0+L1
其中 J 0 J_0 J0 是原始的损失函数,加号后面的一项是L1正则化项, α \alpha α 是正则化系数。注意到 L1正则化是权值的绝对值之和。 J J J 是带有绝对值符号的函数,因此 J J J 是不完全可微的。机器学习的任务就是要通过一些方法(比如梯度下降)求出损失函数的最小值。当我们在原始损失函数 J 0 J_0 J0 后面添加L1正则项时,相当于对 J 0 J_0 J0 做了一个约束。令 L 1 = α ∗ ∑ i = 1 n ∣ w i ∣ L_1 = \alpha * \sum\limits_{i = 1}^n|w_i| L1=α∗i=1∑n∣wi∣ ,则 J = J 0 + L 1 J = J_0 + L_1 J=J0+L1 ,此时我们的任务变成在 L 1 L_1 L1 约束下求出 J 0 J_0 J0 取最小值的解。考虑二维的情况,即只有两个权值 w 1 、 w 2 w_1、w_2 w1、w2 ,此时 L 1 = ∣ w 1 ∣ + ∣ w 2 ∣ L_1 = |w_1| + |w_2| L1=∣w1∣+∣w2∣。
用 λ \lambda λ 表示L1正则化系数:
θ j n + 1 = θ j n − η ∑ i = 1 n ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) − η ∗ λ ∗ s g n ( w i ) \theta_j^{n + 1} = \theta_j^{n} -\eta\sum\limits_{i = 1}^{n} (h_{\theta}(x^{(i)}) - y^{(i)} )x_j^{(i)} - \eta*\lambda * sgn(w_i) θjn+1=θjn−ηi=1∑n(hθ(x(i))−y(i))xj(i)−η∗λ∗sgn(wi)
L1正则化和普通线性回归系数对比:
import numpy as np
from sklearn.linear_model import Lasso
from sklearn.linear_model import SGDRegressor
# 1、创建数据集X,y
X = 2*np.random.rand(100, 20)
w = np.random.randn(20,1)
b = np.random.randint(1,10,size = 1)
y = X.dot(w) + b + np.random.randn(100, 1)
print('原始方程的斜率:',w.ravel())
print('原始方程的截距:',b)
lasso = Lasso(alpha= 0.5)
lasso.fit(X, y)
print('套索回归求解的斜率:',lasso.coef_)
print('套索回归求解的截距:',lasso.intercept_)
# 线性回归梯度下降方法
sgd = SGDRegressor(penalty='l2',alpha=0, l1_ratio=0)
sgd.fit(X, y.reshape(-1,))
print('随机梯度下降求解的斜率是:',sgd.coef_)
print('随机梯度下降求解的截距是:',sgd.intercept_)
也是先从线性回归开始,其损失函数如下:
J ( θ ) = 1 2 ∑ i = 1 n ( h θ ( x ( i ) ) − y ( i ) ) 2 J(\theta) = \frac{1}{2}\sum\limits_{i = 1}^n(h_{\theta}(x^{(i)}) - y^{(i)})^2 J(θ)=21i=1∑n(hθ(x(i))−y(i))2
L2正则化的损失函数(对L2范数,进行了平方运算),令 J 0 = J ( θ ) J_0 = J(\theta) J0=J(θ):
J = J 0 + α ∗ ∑ i = 1 n ( w i ) 2 J = J_0 + \alpha * \sum\limits_{i = 1}^n(w_i)^2 J=J0+α∗i=1∑n(wi)2
令 L 2 = α ∗ ∑ i = 1 n ( w i ) 2 L_2 = \alpha * \sum\limits_{i = 1}^n(w_i)^2 L2=α∗i=1∑n(wi)2 :
J = J 0 + L 2 J = J_0 + L_2 J=J0+L2
二维平面下 L2 正则化的函数图形是个圆(绝对值的平方和,是个圆),与方形相比,被磨去了棱角。因此 J 0 J_0 J0 与 L 2 L_2 L2 相交时使得 w 1 、 w 2 w_1、w_2 w1、w2 等于零的机率小了许多(这个也是一个很直观的想象),这就是为什么L2正则化不具有稀疏性的原因,因为不太可能出现多数 w 都为0的情况(这种情况就叫稀疏性)!
用 λ \lambda λ 表示L2正则化系数:
θ j n + 1 = θ j n ( 1 − η ∗ λ ) − η ∗ ∑ i = 1 n ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) \theta_j^{n + 1} = \theta_j^{n}(1-\eta * \lambda) -\eta *\sum\limits_{i = 1}^{n} (h_{\theta}(x^{(i)}) - y^{(i)} )x_j^{(i)} θjn+1=θjn(1−η∗λ)−η∗i=1∑n(hθ(x(i))−y(i))xj(i)
L2正则化和普通线性回归系数对比:
import numpy as np
from sklearn.linear_model import Ridge
from sklearn.linear_model import SGDRegressor
# 1、创建数据集X,y
X = 2*np.random.rand(100, 5)
w = np.random.randint(1,10,size = (5,1))
b = np.random.randint(1,10,size = 1)
y = X.dot(w) + b + np.random.randn(100, 1)
print('原始方程的斜率:',w.ravel())
print('原始方程的截距:',b)
ridge = Ridge(alpha= 1, solver='sag')
ridge.fit(X, y)
print('岭回归求解的斜率:',ridge.coef_)
print('岭回归求解的截距:',ridge.intercept_)
# 线性回归梯度下降方法
sgd = SGDRegressor(penalty='l2',alpha=0,l1_ratio=0)
sgd.fit(X, y.reshape(-1,))
print('随机梯度下降求解的斜率是:',sgd.coef_)
print('随机梯度下降求解的截距是:',sgd.intercept_)
Elastic-Net 回归,即岭回归和Lasso技术的混合。弹性网络是一种使用 L1, L2 范数作为先验正则项训练的线性回归模型。 这种组合允许学习到一个只有少量参数是非零稀疏的模型,就像 Lasso 一样,但是它仍然保持一些像 Ridge 的正则性质。我们可利用 l1_ratio 参数控制 L1 和 L2 的凸组合。
弹性网络在很多特征互相联系(相关性,比如身高和体重就很有关系)的情况下是非常有用的。Lasso 很可能只随机考虑这些特征中的一个,而弹性网络更倾向于选择两个。
在实践中,Lasso 和 Ridge 之间权衡的一个优势是它允许在迭代过程中继承 Ridge 的稳定性。
弹性网络回归和普通线性回归系数对比:
import numpy as np
from sklearn.linear_model import ElasticNet
from sklearn.linear_model import SGDRegressor
# 1、创建数据集X,y
X = 2*np.random.rand(100, 20)
w = np.random.randn(20,1)
b = np.random.randint(1,10,size = 1)
y = X.dot(w) + b + np.random.randn(100, 1)
print('原始方程的斜率:',w.ravel())
print('原始方程的截距:',b)
model = ElasticNet(alpha= 1, l1_ratio = 0.7)
model.fit(X, y)
print('弹性网络回归求解的斜率:',model.coef_)
print('弹性网络回归求解的截距:',model.intercept_)
# 线性回归梯度下降方法
sgd = SGDRegressor(penalty='l2',alpha=0, l1_ratio=0)
sgd.fit(X, y.reshape(-1,))
print('随机梯度下降求解的斜率是:',sgd.coef_)
print('随机梯度下降求解的截距是:',sgd.intercept_)
升维的目的是为了去解决欠拟合的问题的,也就是为了提高模型的准确率为目的的,因为当维度不够时,说白了就是对于预测结果考虑的因素少的话,肯定不能准确的计算出模型。
在做升维的时候,最常见的手段就是将已知维度进行相乘(或者自乘)来构建新的维度,如下图所示。普通线性方程,无法拟合规律,必须是多项式,才可以完美拟合曲线规律,图中是二次多项式。
对于多项式回归来说主要是为了扩展线性回归算法来适应更广泛的数据集,比如我们数据集有两个维度 x 1 、 x 2 x_1、x_2 x1、x2,那么用多元线性回归公式就是: y ^ = w 0 + w 1 x 1 + w 2 x 2 \hat{y} = w_0 + w_1x_1 + w_2x_2 y^=w0+w1x1+w2x2,当我们使用二阶多项式升维的时候,数据集就从原来的 x 1 、 x 2 x_1、x_2 x1、x2扩展成了 x 1 、 x 2 、 x 1 2 、 x 2 2 、 x 1 x 2 x_1、x_2、x_1^2、x_2^2、x_1x_2 x1、x2、x12、x22、x1x2 。因此多元线性回归就得去多计算三个维度所对应的w值: y ^ = w 0 + w 1 x 1 + w 2 x 2 + w 3 x 1 2 + w 4 x 2 2 + w 5 x 1 x 2 \hat{y} = w_0 + w_1x_1 + w_2x_2 + w_3x_1^2 + w_4x_2^2 + w_5x_1x_2 y^=w0+w1x1+w2x2+w3x12+w4x22+w5x1x2 。
此时拟合出来的方程就是曲线,可以解决一些线性回归的欠拟合问题!
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# 1、创建数据,并进行可视化
X = np.linspace(-1,11,num = 100)
y = (X - 5)**2 + 3*X -12 + np.random.randn(100)
X = X.reshape(-1,1)
plt.scatter(X,y)
# 2、创建预测数据
X_test = np.linspace(-2,12,num = 200).reshape(-1,1)
# 3、不进行升维 + 普通线性回归
model_1 = LinearRegression()
model_1.fit(X,y)
y_test_1 = model_1.predict(X_test)
plt.plot(X_test,y_test,color = 'red')
# 4、多项式升维 + 普通线性回归
X = np.concatenate([X,X**2],axis = 1)
model_2 = LinearRegression()
model_2.fit(X,y)
# 5、测试数据处理,并预测
X_test = np.concatenate([X_test,X_test**2],axis = 1)
y_test_2 = model_2.predict(X_test)
# 6、数据可视化,切片操作
plt.plot(X_test[:,0],y_test_2,color = 'green')