as# OReilly.Hands-On Machine Learning with Scikit-Learn and TensorFlow读书笔记
以线性回归(Linear Regression)为例,介绍两种训练模型的方式:
然后,以多项式回归为例,介绍如何利用学习曲线检测过拟合,以及如何利用正则化减少过拟合。
最后,Logistic回归和Softmax回归。
Equation 4-1. Linear Regression model prediction
y ^ = θ 0 + θ 1 x 1 + θ 2 x 2 + ⋯ + θ n x n \hat y=\theta_0+\theta_1x_1+\theta_2x_2+\cdots+\theta_nx_n y^=θ0+θ1x1+θ2x2+⋯+θnxn
Equation 4-2. Linear Regression model prediction (vectorized form)
y ^ = h θ ( x ) = θ T ⋅ x \hat y=h_\theta(\textbf{x})=\theta^T\cdot\textbf{x} y^=hθ(x)=θT⋅x
Equation 4-3. MSE cost function for a Linear Regression model
MSE ( X , h θ ) = 1 m ∑ i = 1 m ( θ T ⋅ x ( i ) − y ( i ) ) 2 \textrm{MSE}(\textbf{X},h_\theta)=\frac{1}{m}\sum_{i=1}^m\left(\theta^T\cdot \textbf{x}^{(i)}-y^{(i)}\right)^2 MSE(X,hθ)=m1i=1∑m(θT⋅x(i)−y(i))2
均方根误差RMSE(Root Mean Square Error)是最常见的回归模型的性能度量方法。在实际操作中,选用更简单的MSE(Mean Square Error)。通常,我们会让学习算法优化一个与性能度量不同的函数以评价模型。原因在于,该函数更容易计算,该函数具有性能度量所缺乏的微分属性,或者我们希望在训练过程中使用正则化约束模型。
可以从中得到封闭解的方程,称为正规方程。
Equation 4-4. Normal Equation
θ ^ = ( X T ⋅ X ) − 1 ⋅ X T ⋅ y \hat\theta=\left(\textbf{X}^T\cdot \textbf{X}\right)^{-1}\cdot \textbf{X}^T\cdot \textbf{y} θ^=(XT⋅X)−1⋅XT⋅y
import numpy as np
X=2*np.random.rand(100,1) #随机生成一个训练集
y=4+3*X+np.random.randn(100,1)
X_b = np.c_[np.ones((100, 1)), X] # add x0 = 1 to each instance
theta_best = np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y)
#使用正规方程解出最优解
theta_best
#array([[4.25376544],
# [2.41907629]])
#预测
X_new=np.array([[0],[2]])
X_new_b=np.c_[np.ones((2,1)),X_new]
y_predict=X_new_b.dot(theta_best)
y_predict
#array([[4.25376544],
# [9.09191802]])
#绘制数据散点图
import matplotlib.pyplot as plt
plt.plot(X, y, "b.")#数据散点图
plt.plot(X_new,y_predict,"r-")#根据测试数据和预测值绘制的直线
plt.xlabel("$X$", fontsize=18)
plt.ylabel("$y$", rotation=0, fontsize=18)
plt.axis([0, 2, 0, 15])
plt.show();
使用Scikit-Learn代码达到同样的效果:
from sklearn.linear_model import LinearRegression
lin_reg=LinearRegression()
lin_reg.fit(X,y)
lin_reg.intercept_,lin_reg.coef_
#(array([4.25376544]), array([[2.41907629]]))
lin_reg.predict(X_new)
#array([[4.25376544],
# [9.09191802]])
如果 X \textbf{X} X为 n × n n\times n n×n矩阵,则求 ( X T ⋅ X ) − 1 \left(\textbf{X}^T\cdot \textbf{X}\right)^{-1} (XT⋅X)−1的计算复杂度是 O ( n 2.4 ) O(n^{2.4}) O(n2.4)到 O ( n 3 ) O(n^3) O(n3)。当特征数量变大,正规方程变得很慢。
好的方面是,正规方程关于训练集实例数量 m m m是线性的,即 O ( m ) O(m) O(m),只要训练集可以放入内存。
预测相对于实例数量和特征数量都是线性的。
如果特征数量过大,或者训练集无法放入内存,需要寻找其他方法来训练线性回归模型。
比喻:山脚可以看做全局最低点,最快的下山路线是一直沿着最陡峭的方向。
梯度下降的前提是损失函数是可微的凸函数。
使用所有样本(m个)计算损失函数关于每个模型参数 θ j \theta_j θj的偏导数(partial derivative)。
Equation 4-5. Partial derivatives of the cost function
∂ ∂ θ j MSE ( θ ) = 2 m ∑ i = 1 m ( θ T ⋅ x ( i ) − y ( i ) ) x j ( i ) \frac{\partial}{\partial \theta_j}\textrm{MSE}(\theta)=\frac{2}{m}\sum_{i=1}^m\left(\theta^T\cdot \textbf{x}^{(i)}-y^{(i)}\right)x_j^{(i)} ∂θj∂MSE(θ)=m2i=1∑m(θT⋅x(i)−y(i))xj(i)
Equation 4-6. Gradient vector of the cost function
∇ θ MSE ( θ ) = ( ∂ ∂ θ 0 MSE ( θ ) ∂ ∂ θ 1 MSE ( θ ) ⋮ ∂ ∂ θ n MSE ( θ ) ) = 2 m X T ⋅ ( X ⋅ θ − y ) \nabla_\theta \textrm{MSE}(\theta)=\left(\begin{array}{c}\frac{\partial}{\partial \theta_0}\textrm{MSE}(\theta)\\\frac{\partial}{\partial \theta_1}\textrm{MSE}(\theta)\\\vdots \textrm{ }\textrm{ }\textrm{ }\textrm{ }\textrm{ }\textrm{ }\textrm{ }\textrm{ }\textrm{ }\\\frac{\partial}{\partial \theta_n}\textrm{MSE}(\theta)\\\end{array}\right)=\frac{2}{m}\textbf{X}^T\cdot(\textbf{X}\cdot \theta-\textbf{y}) ∇θMSE(θ)=⎝⎜⎜⎜⎛∂θ0∂MSE(θ)∂θ1∂MSE(θ)⋮ ∂θn∂MSE(θ)⎠⎟⎟⎟⎞=m2XT⋅(X⋅θ−y)
Equation 4-7. Gradient Descent step
θ ( next step ) = θ − η ∇ θ MSE ( θ ) \theta^{(\textrm{next step})}=\theta-\eta\nabla_\theta\textrm{MSE}(\theta) θ(next step)=θ−η∇θMSE(θ)
eta=0.1
n_iteration=1000
m=100
theta=np.random.randn(2,1)#随机初始化
for iteration in range(n_iteration):
gradients=2/m*X_b.T.dot(X_b.dot(theta)-y)
theta=theta-eta*gradients
theta
#array([[4.25376544],
# [2.41907629]])
如果训练集很大,批量梯度下降因为每一步(包括计算梯度和更新模型参数)要使用所有样本计算梯度而变慢。
随机梯度下降(Stochastic Gradient Descent)每一步在训练集中随机选取一个实例计算梯度,速度更快。另一方面,因为每次随机选择实例,会造成损失函数忽高忽低。因此,算法到达最小值后,仍然会继续在这个值附近摆动。好处是更有机会通过这种摆动找到全局最小值而不是停在局部最小值上。
随机性有助于离开局部最优点,但难以停在全局最优点。解决办法之一是逐渐减小学习率。这一过程称为模拟退火(simulated annealing)。决定每次迭代过程中学习率的函数称为学习计划(learning schedule)。学习率减少太快,容易陷入局部最小值,或到达局部最小值之前就半路停止了。学习率减少太慢,会在最小值附近摆动很长时间,如果过早终止训练,会停在次优解上。
n_epochs=50
t0,t1=5,50
def learning_schedule(t):
return t0/(t+t1)
theta=np.random.rand(2,1)
for epoch in range(n_epochs):
for i in range(m):
random_index = np.random.randint(m)
xi=X_b[random_index:random_index+1]
#注意切片与取元素值的区别,目的是令xi的shape是(2,1),转置后是(1,2)
yi=y[random_index:random_index+1]
gradients=2*xi.T.dot(xi.dot(theta)-yi)
eta=learning_schedule(epoch*m +i)
theta=theta-eta*gradients
从上面代码的双层循环可以看出,算法执行多轮迭代,每轮迭代称为一个epoch,每个epoch包含在整个数据集上的 m m m个迭代。批量梯度下降在整个训练集上迭代1000 次,而随机梯度下降一般在整个训练集上迭代50次就可以达到一个比较好的结果。
Sciki-Learn提供了SGDRegressor类实现线性回归,缺省使用平方误差(squared error)作为损失函数。
from sklearn.linear_model import SGDRegressor
sgd_reg=SGDRegressor(n_iter=50,penalty=None,eta0=0.1)
sgd_reg.fit(X,y.ravel())#将fit()的第二个参数从(100,1)转换为(100,)
既不像批处理梯度下降在整个数据集上计算梯度,也不像随机梯度下降在单个实例上计算梯度,小批量梯度下降在一个实例的小批量随机集合上计算梯度。
与SGD相比,主要优势在于可以通过对矩阵操作的硬件优化,如GPU,获得大规模的性能提升。
比SGD更易收敛到最小值,但更难离开局部最小值。
Algorithm | Large m | Out-of-core support | Large n | Hyperparams | Scaling required | Scikit-Learn |
---|---|---|---|---|---|---|
Normal Equation | Fast | No | Slow | 0 | No | LinearRegression |
Batch GD | Slow | No | Fast | 2 | Yes | n/a |
Stochastic GD | Fast | Yes | Fast | ≥ 2 \ge 2 ≥2 | Yes | SGDRegression |
Mini-batch GD | Fast | Yes | Fast | ≥ 2 \ge 2 ≥2 | Yes | n/a |
将特征的幂作为新的特征,然后在扩展的特征集合上训练一个线性模型。这个技术称为多项式回归(Polynomial Regression)。
#随机生成非线性关联数据
m=100
X=6*np.random.rand(m,1)-3
y=0.5*X**2+2+np.random.randn(m,1)
#绘制散点图
plt.plot(X,y,'b.')
plt.xlabel("X")
plt.ylabel("y")
#预处理
from sklearn.preprocessing import PolynomialFeatures
poly_features=PolynomialFeatures(degree=2,include_bias=False)#最高2次
X_poly=poly_features.fit_transform(X)
#查看预处理前后,数据变化
X[0]#array([1.63210985])
X_poly[0]#array([1.63210985, 2.66378256])
#利用线性回归模型训练
lin_reg=LinearRegression()
lin_reg.fit(X_poly,y)
lin_reg.intercept_,lin_reg.coef_
#(array([2.02146742]), array([[-0.10915689, 0.51436093]]))
利用交叉验证可以粗略估计出模型的泛化能力。如果模型在训练集上表现良好,但通过交叉验证发现其泛化能力很差,则模型过拟合了。如果模型在训练集和交叉验证中都很差,则模型欠拟合。由此可知模型是太简单还是太复杂了。
另一种方式是绘制学习曲线(learning curves):模型分别在训练集和验证集上的性能指标,是以训练集大小为自变量的函数。
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
def plot_learning_curves(model,X,y):
X_train,X_val,y_train,y_val=train_test_split(X,y,test_size=0.2)
train_errors,val_errors=[],[]
for m in range(1,len(X_train)):
model.fit(X_train[:m],y_train[:m])
y_train_predict=model.predict(X_train[:m])
y_val_predict=model.predict(X_val)
train_errors.append(mean_squared_error(y_train_predict,y_train[:m]))
val_errors.append(mean_squared_error(y_val_predict,y_val))
plt.plot(np.sqrt(train_errors),"r-+",linewidth=2,label="Train set")
plt.plot(np.sqrt(val_errors),"b-",linewidth=3,label="Validation set")
plt.xlabel('Training set size',fontsize=18)
plt.ylabel('RMSE')
plt.xlim(0,80.0)
plt.ylim(0,3.0)
plt.legend()
plt.show()
#绘制线性回归模型的学习曲线
lin_reg=LinearRegression()
plot_learning_curves(lin_reg,X,y)
#绘制相同数据上10次多项式回归模型拟合的学习曲线
from sklearn.pipeline import Pipeline
polynomial_regression=Pipeline((
("poly_features",PolynomialFeatures(degree=10,include_bias=False)),
("sgd_reg",LinearRegression())
))
plot_learning_curves(polynomial_regression,X,y)
通过观察和比较线性回归模型和多项式回归模型的学习曲线,
统计学和机器学习领域的一个重要结论是,模型的泛化误差可以表示为三种误差的和:
偏差(bias):由于错误假设导致的误差,例如假设数据是线性的,而实际是二次的。通常导致欠拟合。
方差(variance):这部分误差是由于模型对训练数据的微小变化过于敏感。一个多自由度的模型(例如一个高阶多项式模型)更容易有高的方差,因此会导致模型过拟合。模型过拟合,会使得泛化误差(测试时)增大,即对于新的数据拟合不好,方差高。
不可约误差:这部分误差源于数据本身的噪声。降低这部分误差的唯一方法就是进行数据清洗(例如:修复数据源,如坏的传感器,或者识别和剔除异常值) 。
正则化多项式模型的一个简单方式是减少多项式的次数。
正则化线性模型一般采取约束模型权重。岭回归(Ridge Regression)、Lasso回归和弹性网络(Elastic Net)是约束权重的三种典型方式。
岭回归(又称为Tikhonov regularization)是线性回归的正则化版本:向损失函数中添加 α ∑ i = 1 n θ i 2 \alpha \sum_{i=1}^n \theta_i^2 α∑i=1nθi2的正则化项。要求学习算法不仅拟合数据,还要令权重尽可能小。注意,正则化项只在训练过程中添加到损失函数中。训练结束,使用无正则化的性能度量方法来评价模型性能。
训练时的损失函数和测试时的性能度量方法通常不同。除正则化外,另一个原因是损失函数应该在优化过程中易于求导,而性能度量应该尽可能接近于最终目标。例如,训练分类器时使用对数损失函数,而测试时使用准确率/召回率。
超参数 α \alpha α控制正则化的程度。 α = 0 \alpha=0 α=0,则岭回归就是线性回归。如果 α \alpha α很大,则所有权重都接近于0,并将得到一个通过训练数据均值的水平直线。
Equation 4-8. Ridge Regression cost function
J ( θ ) = MSE ( θ ) + α 1 2 ∑ i = 1 n θ i 2 J(\theta)=\textrm{MSE}(\theta)+\alpha \frac{1}{2}\sum_{i=1}^n \theta_i^2 J(θ)=MSE(θ)+α21i=1∑nθi2
注意,偏差项 θ 0 \theta_0 θ0没有正则化。如果定义 w \textbf{w} w为权重向量(从 θ 1 \theta_1 θ1到 θ n \theta_n θn),则正则化项等于 α / 2 ( ∣ ∣ w ∣ ∣ 2 ) 2 \alpha/2(||\textbf{w}||_2)^2 α/2(∣∣w∣∣2)2,其中 ∣ ∣ ⋅ ∣ ∣ 2 ||\cdot||_2 ∣∣⋅∣∣2表示 l 2 l_2 l2范式。对于梯度下降,只需向MSE梯度向量中(Equation 4-6等式最右侧)添加 α w \alpha\textbf{w} αw(即正则化项关于参数向量 w \textbf{w} w的导数)。
在执行岭回归之前,需要缩放数据(例如,使用StandardScaler),因为岭回归对输入特征值的大小敏感。对于大多数正则化模型来说都是如此。
Equation 4-9. Ridge Regression closed-form solution
θ ^ = ( X T ⋅ X + α A ) − 1 ⋅ X T ⋅ y \hat\theta=\left(\textbf{X}^T\cdot \textbf{X}+\alpha\textbf{A}\right)^{-1}\cdot \textbf{X}^T\cdot \textbf{y} θ^=(XT⋅X+αA)−1⋅XT⋅y
下面是另一种Cholesky提出的使用矩阵分解求封闭解的方法,是Equation 4-9的一个变种。
from sklearn.linear_model import Ridge
ridge_reg=Ridge(alpha=1,solver="cholesky")
ridge_reg.fit(X,y)
ridge_reg.predict([[1.5]])#array([[3.44729363]])
下面是使用随机梯度下降方法,其中penalty超参数指定正则化项为权重向量的 l 2 l_2 l2范式,这正是岭回归。
sgd_reg=SGDRegressor(penalty="l2")
sgd_reg.fit(X,y.ravel())
sgd_reg.predict([[1.5]])#array([2.60265113])
Lasso Regression(Least Absolute Shrinkage and Selection Operator Regression)是线性回归的另一个正则化版本。与岭回归类似,但正则化项是 l 1 l_1 l1范式。
Equation 4-10. Lasso Regression cost function
J ( θ ) = MSE ( θ ) + α ∑ i = 1 n ∣ θ i ∣ J(\theta)=\textrm{MSE}(\theta)+\alpha\sum_{i=1}^n|\theta_i| J(θ)=MSE(θ)+αi=1∑n∣θi∣
Lasso回归倾向于完全消除最不重要特征的权重(例如,设为0)。换句话说,Lasso回归自动进行特征选择并输出一个稀疏模型(只有很少非零权重)。
尽管Lasso损失函数在 θ i = 0 , i = 1 , 2 , ⋯   , n \theta_i=0, i=1,2,\cdots, n θi=0,i=1,2,⋯,n处不可微,但在任何 θ i = 0 \theta_i=0 θi=0时,使用一个子梯度向量(subgradient vector) g \textbf{g} g替换梯度向量,梯度下降仍然好用。可以将不可微点的子梯度向量想象为该点周围梯度向量之间的中间向量。
Equation 4-11. Lasso Regression subgradient vector
g ( θ , J ) = ∇ θ MSE ( θ ) + α ( sign ( θ 1 ) sign ( θ 2 ) ⋮ sign ( θ n ) ) , where sign ( θ i ) = { − 1 , if θ i < 0 0 , if θ i = 0 1 , if θ i > 0 g(\theta,J)=\nabla_\theta \textrm{MSE}(\theta)+\alpha\left( \begin{array}{c} \textrm{sign}(\theta_1)\\ \textrm{sign}(\theta_2)\\ \vdots\\ \textrm{sign}(\theta_n)\\ \end{array} \right), \textrm{where sign}(\theta_i)= \left\{\begin{array}{rl} -1,& \textrm{ if }\theta_i<0\\ 0,& \textrm{ if }\theta_i=0\\ 1,& \textrm{ if }\theta_i>0 \end{array}\right. g(θ,J)=∇θMSE(θ)+α⎝⎜⎜⎜⎛sign(θ1)sign(θ2)⋮sign(θn)⎠⎟⎟⎟⎞,where sign(θi)=⎩⎨⎧−1,0,1, if θi<0 if θi=0 if θi>0
from sklearn.linear_model import Lasso
lasso_reg=Lasso(alpha=0.1)
lasso_reg.fit(X,y)
lasso_reg.predict([[1.5]])#array([3.49614412])
介于岭回归和Lasso回归两者之间,其正则化项是两者的混合,可以控制混合比率 r r r。
Equation 4-12. Elastic Net cost function
J ( θ ) = MSE ( θ ) + r α ∑ i = 1 n ∣ θ i ∣ + 1 − r 2 α ∑ i = 1 n θ i 2 J(\theta)=\textrm{MSE}(\theta)+r\alpha\sum_{i=1}^n|\theta_i|+\frac{1-r}{2}\alpha\sum_{i=1}^n\theta_i^2 J(θ)=MSE(θ)+rαi=1∑n∣θi∣+21−rαi=1∑nθi2
如何确定何时使用线性回归、岭回归、Lasso回归和弹性网络:缺省使用岭回归,如果怀疑只有少数特征有用,选择Lasso回归或弹性网络,可以将无用特征的权重降为0。一般来说,选用弹性网络优于Lasso回归,因为在特征数量远大于训练实例数量或多个特征强相关时,Lasso回归常常犯错。
from sklearn.linear_model import ElasticNet
elastic_net=ElasticNet(alpha=1,l1_ratio=0.5)
elastic_net.fit(X,y)
elastic_net.predict([[1.5]])#array([3.52605147])
迭代学习算法(例如梯度下降)正则化的一种特殊方式是在验证误差到达最小值时停止训练。
使用随机梯度和小批量梯度下降的迭代学习算法的验证误差曲线不是平滑曲线,很难知道是否达到最小值。一种解决方案是,在验证误差高于最小值一段时间后(确信该模型不会变得更好了)才停止,之后将模型参数回滚到验证误差取得最小值的位置。
from sklearn.preprocessing import StandardScaler
from sklearn.base import clone
np.random.seed(42)
m = 100
X = 6 * np.random.rand(m, 1) - 3
y = 2 + X + 0.5 * X**2 + np.random.randn(m, 1)
X_train, X_val, y_train, y_val = train_test_split(X[:50], y[:50].ravel(),
test_size=0.5, random_state=10)
poly_scaler = Pipeline([
("poly_features", PolynomialFeatures(degree=90, include_bias=False)),
("std_scaler", StandardScaler()),
])
X_train_poly_scaled = poly_scaler.fit_transform(X_train)
X_val_poly_scaled = poly_scaler.transform(X_val)
#注意:当 warm_start=True,调用 fit()方法后,训练会从停下来的地方继续,而不是从头开始。
sgd_reg=SGDRegressor(n_iter=1,warm_start=True,penalty=None,
learning_rate="constant",eta0=0.0005)
minimum_val_error=float("inf")
best_epoch=None
best_model=None
for epoch in range(1000):
sgd_reg.fit(X_train_poly_scaled,y_train)
y_val_predict=sgd_reg.predict(X_val_poly_scaled)
val_error=mean_squared_error(y_val_predict,y_val)
if val_error<minimum_val_error:
minimum_val_error=val_error
best_epoch=epoch
best_model=clone(sgd_reg)
best_epoch, best_model
#(243,
SGDRegressor(alpha=0.0001, average=False, early_stopping=False, epsilon=0.1,
eta0=0.0005, fit_intercept=True, l1_ratio=0.15,
learning_rate='constant', loss='squared_loss', max_iter=None,
n_iter=1, n_iter_no_change=5, penalty=None, power_t=0.25,
random_state=None, shuffle=True, tol=None, validation_fraction=0.1,
verbose=0, warm_start=True))
某些回归算法可以用于分类。Logistic回归(又称Logit回归)常用于估计实例属于某个类的概率。如果估计的概率大于某个阈值(如50%),则预测实例属于该类(称为正类positive class,标记为“1”),否则不属于该类(属于负类negative class,标记为“0”)。这时Logistic回归是一个二分类器。
与线性回归模型一样,Logistic回归模型也计算输入特征的加权和(加上偏差项),但不像线性回归模型那样直接输出计算结果,而是输出结果的logistic。
Equation 4-13. Logistic Regression model estimated probability (vectorized form)
p ^ = h θ ( x ) = σ ( θ T ⋅ x ) \hat p=h_\theta(\textbf{x})=\sigma(\theta^T\cdot\textbf{x}) p^=hθ(x)=σ(θT⋅x)
Logistic,又称logit,表示为 σ ( ⋅ ) \sigma(\cdot) σ(⋅),是一个sigmoid函数(即图像呈S形的函数),该函数输出介于0和1之间的数值。
Equation 4-14. Logistic function
σ ( t ) = 1 1 + exp ( − t ) \sigma(t)=\frac{1}{1+\exp(-t)} σ(t)=1+exp(−t)1
根据Equation 4-13计算出来的概率 p ^ \hat p p^,可以预测实例的标签。
Equation 4-15. Logistic Regression model prediction
y ^ = { 0 , if p ^ < 0.5 1 , if p ^ ≥ 0.5 \hat y=\begin{cases} 0, \textrm{ if } \hat p<0.5\\ 1, \textrm{ if } \hat p\ge0.5\\ \end{cases} y^={0, if p^<0.51, if p^≥0.5
注意,当 t < 0 t<0 t<0时 σ ( t ) < 0.5 \sigma(t)<0.5 σ(t)<0.5,当 t ≥ 0 t\ge 0 t≥0时 σ ( t ) ≥ 0.5 \sigma(t)\ge 0.5 σ(t)≥0.5,因此当 θ T ⋅ x \theta^T\cdot \textbf{x} θT⋅x是正数时,Logistic回归模型输出 1,反之则输出 0。
训练的目标是找到这样的参数向量 θ \theta θ,使得对于正例( y = 1 y=1 y=1)得到高概率,对于负例( y = 0 y=0 y=0)得到低概率。Equation 4-16中单个训练实例 x \textbf{x} x的损失函数表现了这一思想。
Equation 4-16. Cost function of a single training instance
c ( θ ) = { − log ( p ^ ) , if y = 1 − log ( 1 − p ^ ) , if y = 0 c(\theta)=\begin{cases} -\log(\hat p), \textrm{ if } y=1\\ -\log(1-\hat p), \textrm{ if } y=0\\ \end{cases} c(θ)={−log(p^), if y=1−log(1−p^), if y=0
这个损失函数是合理的,因为 − log ( t ) -\log(t) −log(t)在 t t t接近0时变得非常大,所以如果模型将一个正例的概率估计为接近0,则损失函数变得很大。如果模型将一个负例的概率估计为接近1,则损失函数同样变得很大。另一方面,在 t t t接近1时, − log ( t ) -\log(t) −log(t)接近0,所以如果模型将一个负例的概率估计为接近0或正例的概率接近1,则损失函数接近0,正是我们所需要的。
在整个训练集上的损失函数不过是在所有训练实例上的平均损失。可以将其写作Equation 4-17的形式,称为对数损失(log loss):
Equation 4-17. Logistic Regression cost function (log loss)
J ( θ ) = − 1 m ∑ i = 1 m [ y ( i ) log ( p ^ ( i ) ) + ( 1 − y ( i ) ) log ( 1 − p ^ ( i ) ) ] J(\theta)=-\frac{1}{m}\sum_{i=1}^m\left[y^{(i)}\log\left(\hat p^{(i)}\right)+\left(1-y^{(i)}\right)\log\left(1-\hat p^{(i)}\right)\right] J(θ)=−m1i=1∑m[y(i)log(p^(i))+(1−y(i))log(1−p^(i))]
尽管没有等价的正规方程来计算 θ \theta θ的封闭解,但损失函数是凸函数,可以使用梯度下降等优化算法找到全局最小值。
Equation 4-18. Logistic cost function partial derivatives
∂ σ ( t ) ∂ t = ∂ ∂ t ( 1 1 + exp ( − t ) ) = exp ( − t ) ( 1 + exp ( − t ) ) 2 = σ ( t ) − σ 2 ( t ) = σ ( t ) ( 1 − σ ( t ) ) ∂ ∂ θ j p ^ ( i ) = ∂ ∂ θ j σ ( θ T ⋅ x ( i ) ) = ∂ σ ( t ) ∂ t ∂ t ∂ θ j = σ ( t ) ( 1 − σ ( t ) ) x j ( i ) = p ^ ( i ) ( 1 − p ^ ( i ) ) x j ( i ) , where σ ( t ) = p ^ ( i ) , t = θ T ⋅ x ( i ) ∂ ∂ θ j J ( θ ) = − 1 m ∑ i = 1 m [ y ( i ) ∂ ∂ θ j log ( p ^ ( i ) ) + ( 1 − y ( i ) ) ∂ ∂ θ j log ( 1 − p ^ ( i ) ) ] = − 1 m ∑ i = 1 m [ y ( i ) p ^ ( i ) ∂ ∂ θ j p ^ ( i ) + 1 − y ( i ) 1 − p ^ ( i ) ∂ ∂ θ j ( 1 − p ^ ( i ) ) ] = − 1 m ∑ i = 1 m [ y ( i ) ( 1 − p ^ ( i ) ) − ( 1 − y ( i ) ) p ^ ( i ) ] x j ( i ) = 1 m ∑ i = 1 m ( σ ( θ T ⋅ x ( i ) ) − y ( i ) ) x j ( i ) \frac{\partial \sigma(t)}{\partial t}=\frac{\partial}{\partial t}\left(\frac{1}{1+\exp(-t)}\right)=\frac{\exp(-t)}{(1+\exp(-t))^2}=\sigma(t)-\sigma^2(t)=\sigma(t)(1-\sigma(t))\\ \frac{\partial}{\partial \theta_j}\hat p^{(i)}=\frac{\partial}{\partial \theta_j}\sigma(\theta^T\cdot \textbf{x}^{(i)})=\frac{\partial \sigma(t)}{\partial t}\frac{\partial t}{\partial \theta_j}=\sigma(t)(1-\sigma(t))\textbf{x}_j^{(i)}=\hat p^{(i)}(1-\hat p^{(i)})\textbf{x}_j^{(i)},\\ \textrm{where } \sigma(t)=\hat p^{(i)}, t=\theta^T\cdot \textbf{x}^{(i)}\\ \frac{\partial}{\partial \theta_j}J(\theta)=-\frac{1}{m}\sum_{i=1}^m\left[y^{(i)}\frac{\partial}{\partial \theta_j}\log\left(\hat p^{(i)}\right)+\left(1-y^{(i)}\right)\frac{\partial}{\partial \theta_j}\log\left(1-\hat p^{(i)}\right)\right]\\ =-\frac{1}{m}\sum_{i=1}^m\left[\frac{y^{(i)}}{\hat p^{(i)}}\frac{\partial}{\partial \theta_j}\hat p^{(i)}+\frac{1-y^{(i)}}{1-\hat p^{(i)}}\frac{\partial}{\partial \theta_j}\left(1-\hat p^{(i)}\right)\right]\\ =-\frac{1}{m}\sum_{i=1}^m\left[y^{(i)}(1-\hat p^{(i)})-(1-y^{(i)})\hat p^{(i)} \right]\textbf{x}_j^{(i)}\\ =\frac{1}{m}\sum_{i=1}^m\left(\sigma\left(\theta^T\cdot\textbf{x}^{(i)}\right)-y^{(i)}\right)\textbf{x}_j^{(i)} ∂t∂σ(t)=∂t∂(1+exp(−t)1)=(1+exp(−t))2exp(−t)=σ(t)−σ2(t)=σ(t)(1−σ(t))∂θj∂p^(i)=∂θj∂σ(θT⋅x(i))=∂t∂σ(t)∂θj∂t=σ(t)(1−σ(t))xj(i)=p^(i)(1−p^(i))xj(i),where σ(t)=p^(i),t=θT⋅x(i)∂θj∂J(θ)=−m1i=1∑m[y(i)∂θj∂log(p^(i))+(1−y(i))∂θj∂log(1−p^(i))]=−m1i=1∑m[p^(i)y(i)∂θj∂p^(i)+1−p^(i)1−y(i)∂θj∂(1−p^(i))]=−m1i=1∑m[y(i)(1−p^(i))−(1−y(i))p^(i)]xj(i)=m1i=1∑m(σ(θT⋅x(i))−y(i))xj(i)
其中, x j ( i ) \textbf{x}_j^{(i)} xj(i)表示训练集中第 i i i个实例 x ( i ) \textbf{x}^{(i)} x(i)的第 j j j个特征值。
Equation 4-18与Equation 4-5很相似:对于每个实例,计算预测误差,并将其与第 j j j个特征值相乘,再在所有实例上计算均值。一旦得到包含所有偏导数的梯度向量,就可以在梯度向量上使用批量梯度下降算法。 也就是知道如何训练 Logistic 回归模型。对于随机梯度下降,只需要每一次使用一个实例,对于小批量梯度下降,每一次使用一个小批量实例集。
Sciki-Learn中LogisticRegression模型中控制正则化强度的超参数不是alpha,而是其倒数C。C越大,正则化程度越低。
#载入鸢尾花数据集
from sklearn.datasets import load_iris
iris=load_iris()
list(iris.keys())
#['data', 'target', 'target_names', 'DESCR', 'feature_names', 'filename']
X=iris['data'][:,3:]#取所有行,第3列以后的所有列,即petal width列
y=(iris['target']==2).astype(np.int)#1 if Iris-Virginica, else 0
from sklearn.linear_model import LogisticRegression
log_reg=LogisticRegression()
log_reg.fit(X,y)
X_new=np.linspace(0,3,1000).reshape(-1,1)#reshape to a column vector
#reshape(1,-1) reshape to a row vector
y_proba=log_reg.predict_proba(X_new)
plt.plot(X_new,y_proba[:,1],"g-",label="Iris-Virginica")
plt.plot(X_new,y_proba[:,0],"b--",label="Not Iris-Virginica")
log_reg.predict([[1.7], [1.5]])#array([1, 0])
Logistic模型可以泛化为支持多分类,无需(像Chapter 3介绍的)训练和结合多个二分类器。这称为Softmax回归,或多项式Logistic回归。
思想很简单:给定一个实例 x \textbf{x} x,Softmax模型首先为每个类计算一个得分 s k ( x ) s_k(\textbf{x}) sk(x)。然后,对所有分数应用softmax函数(也称为归一化指数normalized exponential)以估计实例属于每个类的概率。
Equation 4-19. Softmax score for class k
s k ( x ) = θ k T ⋅ x s_k(\textbf{x})=\theta_k^T\cdot \textbf{x} sk(x)=θkT⋅x
注意,每个类都有单独的参数向量 θ k \theta_k θk。每个参数向量一行,构成参数矩阵 Θ \Theta Θ。
Equation 4-20. Softmax function
p ^ k = σ ( s ( x ) ) k = exp ( s k ( x ) ) ∑ j = 1 K exp ( s j ( x ) ) \hat p_k =\sigma(\textbf{s}(\textbf{x}))_k=\frac{\exp(s_k(\textbf{x}))}{\sum_{j=1}^K\exp(s_j(\textbf{x}))} p^k=σ(s(x))k=∑j=1Kexp(sj(x))exp(sk(x))
Equation 4-21. Softmax Regression classifer prediction
y ^ = arg max k σ ( s ( x ) ) k = arg max k s k ( x ) = arg max k ( θ k T ⋅ x ) \hat y=\arg\max_k\sigma(\textbf{s}(\textbf{x}))_k=\arg\max_k s_k(\textbf{x})=\arg\max_k\left(\theta_k^T\cdot \textbf{x}\right) y^=argkmaxσ(s(x))k=argkmaxsk(x)=argkmax(θkT⋅x)
模型训练的目标是令目标类拥有高概率,其它类低概率。损失函数使用交叉熵(cross entropy)。
Equation 4-22. Cross entropy cost function
J ( Θ ) = − 1 m ∑ i = 1 m ∑ k = 1 K y k ( i ) log ( p ^ k ( i ) ) J(\Theta)=-\frac{1}{m}\sum_{i=1}^m\sum_{k=1}^Ky_k^{(i)}\log\left(\hat p_k^{(i)}\right) J(Θ)=−m1i=1∑mk=1∑Kyk(i)log(p^k(i))
交叉熵是概率密度 p ( x ) p(x) p(x)的熵 H ( p ) H(p) H(p)与KL散度 D K L ( p ∣ ∣ q ) D_{KL}(p||q) DKL(p∣∣q)的和,即
H ( p , q ) = H ( p ) + D K L ( p ∣ ∣ q ) = − E X ∼ p log p ( x ) + E X ∼ p log p ( x ) q ( x ) = − ∑ x p ( x ) log q ( x ) D K L ( p ∣ ∣ q ) = H ( p , q ) − H ( p ) H(p,q)=H(p)+D_{KL}(p||q)=-\mathbb{E}_{X\sim p}\log p(x)+\mathbb{E}_{X\sim p}\log \frac{p(x)}{q(x)}\\=-\sum_xp(x)\log q(x)\\ D_{KL}(p||q)=H(p,q)-H(p) H(p,q)=H(p)+DKL(p∣∣q)=−EX∼plogp(x)+EX∼plogq(x)p(x)=−x∑p(x)logq(x)DKL(p∣∣q)=H(p,q)−H(p)
KL散度度量了当用预测的分布 q ( x ) q(x) q(x)代替真实分布 p ( x ) p(x) p(x)所造成的信息量的差异。交叉熵 H ( p , q ) H(p,q) H(p,q)度量了当用预测的分布 q ( x ) q(x) q(x)代替(未知的)真实分布 p ( x ) p(x) p(x)的平均信息量。因此,KL散度更适合作为损失函数,优化目标是找到模型参数,使得KL散度最小。KL散度中包含的 H ( p ) H(p) H(p)不包含模型参数,因此可以省略掉,于是优化KL散度等价于优化交叉熵,因此选用交叉熵作为损失函数。
Equation 4-22中 y k ( i ) y_k^{(i)} yk(i)即为真实分布 p ( x ) p(x) p(x),因为训练集中标签只有0和1。
Equation 4-23. Cross entropy gradient vector for class k
∇ θ k J ( Θ ) = 1 m ∑ i = 1 m ( p ^ k ( i ) − y k ( i ) ) x ( i ) \nabla_{\theta_k}J(\Theta)=\frac{1}{m}\sum_{i=1}^m \left(\hat p_k^{(i)}-y_k^{(i)}\right)\textbf{x}^{(i)} ∇θkJ(Θ)=m1i=1∑m(p^k(i)−yk(i))x(i)
X=iris['data'][:,(2,3)]
y=iris['target']
softmax_reg=LogisticRegression(multi_class="multinomial",
solver="lbfgs",C=10)
softmax_reg.fit(X,y)
softmax_reg.predict([[5,2]])#array([2])
softmax_reg.predict_proba([[5,2]])
#array([[6.38014896e-07, 5.74929995e-02, 9.42506362e-01]])