过拟合与欠拟合,就像你早餐三明治一样,太热了下不了嘴,太凉了吃了肚子疼,只有正正好好才刚刚好;下图 Fig.1 为周志华老师《机器学习》一书中我认为是对过拟合和欠拟合最简单的理解方式,请查阅:
过拟合指的是模型在训练数据上表现良好,但在新的数据上表现较差。过拟合通常是因为模型太复杂,导致它过度适应了训练数据的噪声和细节,而忽略了数据中的一般趋势和规律。
欠拟合指的是模型无法在训练数据上得到很好的拟合,更不用说在新的数据上进行预测。欠拟合通常是因为模型过于简单,无法捕捉到数据的复杂性和变化。
欠拟合: 在线性回归中,欠拟合通常指模型无法很好地拟合训练数据的情况,通常是由于模型过于简单( w 1 x + b w_1x+b w1x+b),无法捕捉数据的本质规律所导致。
良好的泛化能力: 我们创建模型的目标是能够使用该模型对新的例子正确预测结果。一个能做到这一点的模型被称为具有良好的泛化能力。
过拟合: 在线性回归中,过拟合通常指模型在训练集上表现良好,但是在测试集或新数据上表现较差的情况。这通常是由于模型的复杂度过高( w 1 x + w 2 x 2 + w 3 x 3 + w 4 x 4 + b w_1x+w_2x^2+w_3x^3+w_4x^4+b w1x+w2x2+w3x3+w4x4+b),或者训练数据量不足导致的。
欠拟合: 逻辑回归模型的欠拟合问题通常是由于模型太简单,无法捕捉数据中的非线性关系和复杂模式,从而导致模型在训练集和测试集上都表现不佳。
过拟合: 逻辑回归模型的过拟合问题和线性回归模型类似,通常是由于模型过于复杂,拟合了过多的训练数据的噪声和异常值,而导致模型在测试集上表现不佳。
正则化线性回归是一种在线性回归模型中添加正则化项来控制模型复杂度和避免过拟合的方法。常见的正则化方法包括L1正则化(Lasso)和L2正则化(Ridge),L1正则化和L2正则化的主要区别在于它们所惩罚的模型参数不同:
而因为我们在线性回归中,使用正则化时经常不会得知具体哪些参数需要调整,所以更多用的是L2正则化,即将所有的参数都调整,从而保留了所有特征的信息。
下面内容将具体阐述如何正则化:
未使用正则化的损失函数:
J ( w ⃗ , b ) = 1 2 m ∑ i = 0 m − 1 ( f w ⃗ , b ( x ⃗ ( i ) ) − y ( i ) ) 2 J(\vec{w},b)=\frac 1 {2m} \sum ^{m-1} _{i=0}(f_{\vec{w},b}(\vec{x}^{(i)})-y^{(i)})^2 J(w,b)=2m1i=0∑m−1(fw,b(x(i))−y(i))2
使用正则化后的损失函数:
J ( w ⃗ , b ) = 1 2 m ∑ i = 0 m − 1 ( f w ⃗ , b ( x ⃗ ( i ) ) − y ( i ) ) 2 + λ 2 m ∑ j = 0 n − 1 w j 2 J(\vec{w},b)=\frac 1 {2m} \sum ^{m-1} _{i=0}(f_{\vec{w},b}(\vec{x}^{(i)})-y^{(i)})^2+\frac {\lambda} {2m} \sum ^{n-1} _{j=0} w_j^2 J(w,b)=2m1i=0∑m−1(fw,b(x(i))−y(i))2+2mλj=0∑n−1wj2
对比正则化前与正则化后的损失函数,发现正则化后损失函数多出一个部分: λ 2 m ∑ j = 0 n − 1 w j 2 \frac {\lambda} {2m} \sum ^{n-1} _{j=0} w_j^2 2mλ∑j=0n−1wj2,该部分称为正则化项。通过该项,增大损失函数值,从而在梯度下降(最小化损失函数)时,对模型参数的取值施加限制,模型的参数会更倾向于取值较小的范围,从而降低过拟合风险。
代码实现:
def compute_cost_linear_reg(X, y, w, b, lambda_ = 1):
m = X.shape[0]
n = len(w)
cost = 0.
for i in range(m):
f_wb_i = np.dot(X[i], w) + b
cost = cost + (f_wb_i - y[i])**2
cost = cost / (2 * m) # 不包含正则化项的损失函数
reg_cost = 0
for j in range(n):
reg_cost += (w[j]**2) # 正则化项
reg_cost = (lambda_/(2*m)) * reg_cost
total_cost = cost + reg_cost
return total_cost # 返回的改损失函数包含正则化项
正则化逻辑回归同样是通过添加正则化项来控制模型复杂度和避免过拟合的方法。同样常见的也是L1和L2,甚至对于损失函数的更改都是一样的:
未使用正则化的损失函数:
J ( w ⃗ , b ) = 1 m ∑ i = 0 m − 1 [ ( − y ( i ) l o g ( f w ⃗ , b ( x ⃗ ( i ) ) ) ) − ( 1 − y ( i ) ) l o g ( 1 − f w ⃗ , b ( x ⃗ ( i ) ) ) ] J(\vec{w},b)=\frac 1 m \sum ^{m-1} _{i=0} [(-y^{(i)}log(f_{\vec{w},b}(\vec{x}^{(i)})))-(1-y^{(i)})log(1-f_{\vec{w},b}(\vec{x}^{(i)}))] J(w,b)=m1i=0∑m−1[(−y(i)log(fw,b(x(i))))−(1−y(i))log(1−fw,b(x(i)))]
使用正则化后的损失函数:
J ( w ⃗ , b ) = 1 m ∑ i = 0 m − 1 [ ( − y ( i ) l o g ( f w ⃗ , b ( x ⃗ ( i ) ) ) ) − ( 1 − y ( i ) ) l o g ( 1 − f w ⃗ , b ( x ⃗ ( i ) ) ) ] + λ 2 m ∑ j = 0 n − 1 w j 2 J(\vec{w},b)=\frac 1 m \sum ^{m-1} _{i=0} [(-y^{(i)}log(f_{\vec{w},b}(\vec{x}^{(i)})))-(1-y^{(i)})log(1-f_{\vec{w},b}(\vec{x}^{(i)}))]+\frac {\lambda} {2m}\sum ^{n-1} _{j=0} w_j^2 J(w,b)=m1i=0∑m−1[(−y(i)log(fw,b(x(i))))−(1−y(i))log(1−fw,b(x(i)))]+2mλj=0∑n−1wj2
同样,增加了正则化项: λ 2 m ∑ j = 0 n − 1 w j 2 \frac {\lambda} {2m} \sum ^{n-1} _{j=0} w_j^2 2mλ∑j=0n−1wj2
代码实现:
def compute_cost_logistic_reg(X, y, w, b, lambda_ = 1):
m,n = X.shape
cost = 0.
for i in range(m):
z_i = np.dot(X[i], w) + b
f_wb_i = sigmoid(z_i)
cost += -y[i]*np.log(f_wb_i) - (1-y[i])*np.log(1-f_wb_i)
cost = cost/m # 标准逻辑回归损失函数部分
reg_cost = 0 # 添加正则化损失函数部分
for j in range(n):
reg_cost += (w[j]**2)
reg_cost = (lambda_/(2*m)) * reg_cost
total_cost = cost + reg_cost
return total_cost # 返回损失函数包含常规逻辑回归损失函数部分以及正则化损失函数部分
由于正则化对于线性回归与逻辑回归的损失函数操作相同,所以我们可以将其一起要讨论:
repeat until convergence: { w j = w j − α ∂ J ( w , b ) ∂ w j for j := 0..n-1 b = b − α ∂ J ( w , b ) ∂ b } \begin{align*} &\text{repeat until convergence:} \; \lbrace \\ & \; \; \;w_j = w_j - \alpha \frac{\partial J(\mathbf{w},b)}{\partial w_j} \; & \text{for j := 0..n-1} \\ & \; \; \; \; \;b = b - \alpha \frac{\partial J(\mathbf{w},b)}{\partial b} \\ &\rbrace \end{align*} repeat until convergence:{wj=wj−α∂wj∂J(w,b)b=b−α∂b∂J(w,b)}for j := 0..n-1
有:
∂ J ( w , b ) ∂ w j = 1 m ∑ i = 0 m − 1 ( f w , b ( x ( i ) ) − y ( i ) ) x j ( i ) + λ m w j ∂ J ( w , b ) ∂ b = 1 m ∑ i = 0 m − 1 ( f w , b ( x ( i ) ) − y ( i ) ) \begin{align*} \frac{\partial J(\mathbf{w},b)}{\partial w_j} &= \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{\mathbf{w},b}(\mathbf{x}^{(i)}) - y^{(i)})x_{j}^{(i)} + \frac{\lambda}{m} w_j\\ \frac{\partial J(\mathbf{w},b)}{\partial b} &= \frac{1}{m} \sum\limits_{i = 0}^{m-1} (f_{\mathbf{w},b}(\mathbf{x}^{(i)}) - y^{(i)}) \end{align*} ∂wj∂J(w,b)∂b∂J(w,b)=m1i=0∑m−1(fw,b(x(i))−y(i))xj(i)+mλwj=m1i=0∑m−1(fw,b(x(i))−y(i))
说明:为什么只对 w 而不对 b 做正则化操作:
在线性回归正则化中,通常只对权重 w 进行正则化而不对偏置 b 进行正则化,这是因为偏置 b 的影响通常不如权重 w 显著。偏置b是一个常数,它可以看作是对预测值的一个基础偏移。因为它对预测值的影响相对较小,所以即使它的值很大,对模型的影响也会相对较小。
def compute_gradient_linear_reg(X, y, w, b, lambda_):
m,n = X.shape
dj_dw = np.zeros((n,))
dj_db = 0.
for i in range(m):
err = (np.dot(X[i], w) + b) - y[i]
dj_dw = dj_dw + err * X[i]
# for j in range(n):
# dj_dw[j] = dj_dw[j] + err * X[i, j]
dj_db = dj_db + err
dj_dw = dj_dw / m
dj_db = dj_db / m
for j in range(n):
dj_dw[j] = dj_dw[j] + (lambda_/m) * w[j] # 加入了正则化损失函数带来的影响
return dj_db, dj_dw
def compute_gradient_logistic_reg(X, y, w, b, lambda_):
m,n = X.shape
dj_dw = np.zeros((n,))
dj_db = 0.0
for i in range(m):
f_wb_i = sigmoid(np.dot(X[i],w) + b)
err_i = f_wb_i - y[i]
dj_dw = dj_dw + err_i * X[i]
# for j in range(n):
# dj_dw[j] = dj_dw[j] + err_i * X[i,j]
dj_db = dj_db + err_i
dj_dw = dj_dw/m
dj_db = dj_db/m
for j in range(n):
dj_dw[j] = dj_dw[j] + (lambda_/m) * w[j] # 加入正则化损失函数带来影响
return dj_db, dj_dw
[1]. 周志华. (2016). 《机器学习》(第1版). 清华大学出版社. https://book.douban.com/subject/26708119/
[2]. Ng, A. (n.d.). The problem of overfitting. Coursera. Retrieved from https://www.coursera.org/learn/machine-learning/lecture/erGPe/the-problem-of-overfitting
[3]. Ng, A. (n.d.). Optional Lab: Regularization. Coursera. Retrieved from https://www.coursera.org/learn/machine-learning/ungradedLab/36A9A/optional-lab-regularization
[4]. Ng, A. (n.d.). Optional Lab: Overfitting. Coursera. Retrieved from https://www.coursera.org/learn/machine-learning/ungradedLab/3nraU/optional-lab-overfitting