文章目录
Underfitting & Overfitting
在训练模型的过程中,我们通常希望模型能够很好地反映训练集的特征的同时,也能够泛化(generalization)到新的数据上(在新的数据集上也能表现得很好)。
在训练集上表现不好的模型,称为欠拟合(underfit),也称模型与训练集有很高的偏差(bias);在训练集上表现得很好,在新数据上表现不好的模型,称为过拟合(overfit),也称有高方差(high variance)。如图所示,在房价预测模型中设计不同阶的特征,左边的模型欠拟合,右边的模型过拟合。
对于分类任务,同样需要关注欠拟合与过拟合问题,如图所示,过拟合和欠拟合的模型都不能称得上是良好的模型。
Addressing Overfitting
后面会讲如何debug和检测过拟合,这里先讲如何处理过拟合
- 获取更多的训练数据(复杂的特征需要更多数据的支持)。有的时候很难实现。
-
使用更少的特征,尤其是高阶多项式特征,并尝试通过特征设计选择合适的特征(feature selection),但注意,排除掉部分特征可能导致信息丢失。可以通过自动化手段来选择这些特征。
-
正则化(Regularization)。保留所有的特征,减少过大特征的影响(缩小对应特征的数量级);通常只需要正则化 w j w_j wj,不需要正则化 b b b
Regularization
前面提到,使用正则化的目的是使
w
j
w_j
wj尽可能的小,从而减小每个特征的影响,降低过拟合的可能性;
w
j
w_j
wj尽可能的小,也即
w
j
w_j
wj尽可能趋向0,我们可以使用最小二乘来建模这一关系
arg
min
w
⃗
,
b
∑
j
=
1
n
(
w
j
−
0
)
2
\arg \min_{\vec w, b} \sum_{j = 1}^n (w_j-0)^2
argw,bminj=1∑n(wj−0)2
这个式子将被带入cost函数中,作为一个惩罚项存在,以线性回归的最小二乘cost为例
arg
min
w
⃗
,
b
[
1
2
m
∑
i
=
1
m
(
f
w
⃗
,
b
(
x
⃗
i
)
−
y
i
)
2
+
λ
2
m
∑
j
=
1
n
w
j
2
]
\arg \min_{\vec w, b} [\frac 1 {2m} \sum_{i = 1}^m (f_{\vec w, b}(\vec x_i) - y_i)^2 + \frac \lambda {2m} \sum_{j = 1}^n w_j^2]
argw,bmin[2m1i=1∑m(fw,b(xi)−yi)2+2mλj=1∑nwj2]
其中,新引入的式子称为正则项,
λ
>
0
\lambda > 0
λ>0为正则参数;在上式中,均方误差项最小化使模型拟合(fit data),正则项最小化保证
w
j
w_j
wj不会过高,两者之间的平衡通过
λ
\lambda
λ调整。过小的
λ
\lambda
λ不能有效抑制过拟合;而过大的
λ
\lambda
λ会将特征值压得过小,导致模型趋近
y
=
b
y = b
y=b,表现为欠拟合。
正则项除2m,吴恩达老师的解释是在实践上,这样更有利于选择稳定的 λ \lambda λ。
更标准的说法是 L p L_p Lp范数, L 0 , L 1 L_0, L_1 L0,L1范数使分量稀疏,非零分量个数尽可能少,因为 L 0 L_0 L0不连续、非凸、不可微,所以用 L 1 L_1 L1代替; L 2 L_2 L2范数(类似上面取平方的形式)使分量稠密,且更加均衡。
Scikit-Learn的
Ridge
线性模型就使用了 L 2 L_2 L2范数
Regularized Linear Regression
Equations
已知引入正则化之后的cost
J
(
w
⃗
,
b
)
=
1
2
m
∑
i
=
1
m
(
f
w
⃗
,
b
(
x
⃗
i
)
−
y
i
)
2
+
λ
2
m
∑
j
=
1
n
w
j
2
J(\vec w, b) =\frac 1 {2m} \sum_{i = 1}^m (f_{\vec w, b}(\vec x_i) - y_i)^2 + \frac \lambda {2m} \sum_{j = 1}^n w_j^2
J(w,b)=2m1i=1∑m(fw,b(xi)−yi)2+2mλj=1∑nwj2
使用梯度下降最优化参数,求导如下
∂
∂
w
j
J
(
w
⃗
,
b
)
=
1
m
∑
i
=
1
m
(
f
w
⃗
,
b
(
x
⃗
i
)
−
y
i
)
x
i
,
j
+
λ
m
w
j
∂
∂
b
J
(
w
⃗
,
b
)
=
1
m
∑
i
=
1
m
(
f
w
⃗
,
b
(
x
⃗
i
)
−
y
i
)
\frac {\partial} {\partial w_j} J(\vec w, b) = \frac 1 m \sum_{i = 1}^m(f_{\vec w, b}(\vec x_i) - y_i)x_{i, j} + \frac \lambda m w_j \newline \frac {\partial} {\partial b} J(\vec w, b) = \frac 1 m \sum_{i = 1}^m(f_{\vec w, b}(\vec x_i) - y_i)
∂wj∂J(w,b)=m1i=1∑m(fw,b(xi)−yi)xi,j+mλwj∂b∂J(w,b)=m1i=1∑m(fw,b(xi)−yi)
因为没有对b正则化,因此对b的偏导没有变化。
可以通过重新整理表达式,来更直观观察正则化对参数更新的影响
w
j
=
w
j
−
α
∂
∂
w
j
J
(
w
⃗
,
b
)
=
w
j
−
α
[
1
m
∑
i
=
1
m
(
f
w
⃗
,
b
(
x
⃗
i
)
−
y
i
)
x
i
,
j
+
λ
m
w
j
]
=
(
1
−
α
λ
m
)
w
j
−
α
r
a
c
1
m
∑
i
=
1
m
(
f
w
⃗
,
b
(
x
⃗
i
)
−
y
i
)
x
i
,
j
w_j = w_j - \alpha \frac {\partial} {\partial w_j} J(\vec w, b) \newline = w_j - \alpha [\frac 1 m \sum_{i = 1}^m(f_{\vec w, b}(\vec x_i) - y_i)x_{i, j} + \frac \lambda m w_j] \newline = (1 - \alpha \frac \lambda m)w_j - \alpha rac 1 m \sum_{i = 1}^m(f_{\vec w, b}(\vec x_i) - y_i)x_{i, j}
wj=wj−α∂wj∂J(w,b)=wj−α[m1i=1∑m(fw,b(xi)−yi)xi,j+mλwj]=(1−αmλ)wj−αrac1mi=1∑m(fw,b(xi)−yi)xi,j
假定
α
=
0.01
,
λ
=
1
,
m
=
50
\alpha = 0.01, \lambda = 1, m = 50
α=0.01,λ=1,m=50,可知
w
j
w_j
wj的系数为0.9998
,即正则化在每轮迭代将参数缩小一点点。
Implements
首先实现Cost的计算
def compute_cost_linear_reg(X, y, w, b, lambda_ = 1):
'''
Computes total cost of all examples
Args:
X (ndarray (m, n)): Data, m examples, n features
y (ndarray (m, )): target values
w (ndarray (n, )): parameters
b (scalar) : interpret parameter
lambda_ (scalar) : the amout of regularization, add "_" to distinguish from key word "lambda"
Returns:
total_cost (scalar): the total cost of all examples
'''
m, n = X.shape
errors = np.dot(X, w) + b - y
cost = (1/2/m) * np.dot(errors, errors) + (lambda_/2/m) * np.dot(w, w)
return cost
np.dot(a,b)
算法支持多种支持多种运算规则,在这里np.dot(X, w)
相当于对w做一次线性变换。需要注意的是,这种计算方式是会进行广播(broadcast)的。当a, b
都是1-D array的时候,计算向量内积。
np.matmul(x1,x2)
,或者说运算符@
提供矩阵乘法,要求shape满足*(n,k),(k,m)->(n,m)*。x1, x2
不能是scalar或array;虽然用np.dot
也可实现这一运算,但推荐使用@
。
np.multiply(a, b)
或者说运算符*
提供数乘操作,要求任意一个输入为scalar。同样可以用np.dot
实现,但推荐使用*
。
梯度下降函数不会因为引入正则项发生变化,但计算梯度的函数需要做出调整
def compute_gradient_linear_reg(X, y, w, b, lambda_):
"""
Computes the gradient for linear regression
Args:
X (ndarray (m,n): Data, m examples with n features
y (ndarray (m,)): target values
w (ndarray (n,)): model parameters
b (scalar) : model parameter
lambda_ (scalar): Controls amount of regularization
Returns:
dj_dw (ndarray (n,)): The gradient of the cost w.r.t. the parameters w.
dj_db (scalar): The gradient of the cost w.r.t. the parameter b.
"""
m,n = X.shape #(number of examples, number of features)
errors = np.dot(X, w) + b - y
dj_db = errors.mean()
dj_dw = np.dot(errors.reshape(1, -1), X).reshape(-1, )/m + (lambda_/m) * w
# 或者这样写也可以,但是逻辑上会变成(m, ) dot (m, n),
# 虽然numpy会自动修正过来,但最好先reshape
# dj_dw = np.dot(errors, X)/m + (lambda_/m) * w
return dj_db, dj_dw
Regularized Logistic Regression
Equations
同理,在对率回归中引入
L
2
L_2
L2范数项
J
(
w
⃗
,
b
)
=
−
1
m
∑
i
=
1
m
y
i
[
log
(
f
w
⃗
,
b
(
x
⃗
i
)
)
+
(
1
−
y
i
)
log
(
1
−
f
w
⃗
,
b
(
x
⃗
i
)
)
]
+
λ
m
w
j
2
J(\vec w, b) = -\frac 1 m \sum_{i = 1}^m y_i [\log(f_{\vec w, b}(\vec x_i)) + (1 - y_i) \log(1 - f_{\vec w, b}(\vec x_i))] +\frac \lambda m w_j^2
J(w,b)=−m1i=1∑myi[log(fw,b(xi))+(1−yi)log(1−fw,b(xi))]+mλwj2
引入正则项不会对似然项的求导造成影响,因此求得导数
∂
∂
w
⃗
j
J
(
w
⃗
,
b
)
=
1
m
∑
i
=
1
m
(
f
w
⃗
,
b
(
x
⃗
i
)
−
y
i
)
x
⃗
i
,
j
+
λ
m
w
j
∂
∂
b
J
(
w
⃗
,
b
)
=
1
m
∑
i
=
1
m
(
f
w
⃗
,
b
(
x
⃗
i
)
−
y
i
)
\frac \partial {\partial \vec w_j}J(\vec w, b) = \frac 1 m \sum_{i = 1}^m(f_{\vec w, b}(\vec x_i) - y_i) \vec x_{i, j} + \frac \lambda m w_j \newline \frac \partial {\partial b}J(\vec w, b) = \frac 1 m \sum_{i = 1}^m(f_{\vec w, b}(\vec x_i) - y_i)
∂wj∂J(w,b)=m1i=1∑m(fw,b(xi)−yi)xi,j+mλwj∂b∂J(w,b)=m1i=1∑m(fw,b(xi)−yi)
代入梯度下降表达式中,即可求得参数迭代式
在Scikit-Learn中,
LogisticRegression
模型支持通过penalty
参数来选用正则化
penalty r(w) None 0 l 1 l_1 l1 ∣ ∣ w ∣ ∣ 1 ||w||_1 ∣∣w∣∣1 l 2 l_2 l2 1 2 ∣ ∣ w ∣ ∣ 2 2 = 1 2 w T w \frac 1 2 ||w||^2_2 = \frac 1 2 w^Tw 21∣∣w∣∣22=21wTw
Implements
计算cost
def compute_cost_logistic_reg(X, y, w, b, lambda_ = 1):
"""
Computes the cost over all examples
Args:
Args:
X (ndarray (m,n): Data, m examples with n features
y (ndarray (m,)): target values
w (ndarray (n,)): model parameters
b (scalar) : model parameter
lambda_ (scalar): Controls amount of regularization
Returns:
total_cost (scalar): cost
"""
m,n = X.shape
reg_cost = (lambda_/2/m) * np.dot(w, w)
f_wbs = sigmoid(np.dot(X, w) + b)
cost = -(np.dot(y, np.log(f_wbs)) + np.dot(1-y, np.log(1-f_wbs)))/m #scalar
total_cost = cost + reg_cost
return total_cost
sigmoid函数在此前实现过,核心代码为
g = 1.0/(1.0+np.exp(-z))
计算梯度
def compute_gradient_logistic_reg(X, y, w, b, lambda_):
"""
Computes the gradient for linear regression
Args:
X (ndarray (m,n): Data, m examples with n features
y (ndarray (m,)): target values
w (ndarray (n,)): model parameters
b (scalar) : model parameter
lambda_ (scalar): Controls amount of regularization
Returns
dj_dw (ndarray Shape (n,)): The gradient of the cost w.r.t. the parameters w.
dj_db (scalar) : The gradient of the cost w.r.t. the parameter b.
"""
m,n = X.shape
errors = sigmoid(np.dot(X, w) + b) - y
dj_db = errors.mean()
dj_dw = np.dot(errors.reshape(1, -1), X).reshape(-1, )/m + (lambda_/m) * w
return dj_db, dj_dw
参考
- 吴恩达《机器学习》2022
- 《机器学习》周志华
- 南瓜书
- 《统计学习方法》李航