Coursera ML - 优化算法的选择会影响多类逻辑回归的准确性吗?

数据挖掘 机器学习 Python 逻辑回归 准确性
2021-09-19 15:21:38

我最近在 Coursera 上使用 Python 完成了 Andrew Ng 的机器学习练习 3

在最初完成练习的第 1.4 到 1.4.1 部分时,我遇到了困难,以确保我训练的模型具有与预期 94.9% 匹配的准确度。即使在调试并确保我的成本和梯度函数没有错误并且我的预测器代码正常工作之后,我仍然只能获得 90.3% 的准确率。我在scipy.optimize.minimize.

出于好奇,我决定尝试另一种算法,并使用 Broyden-Fletcher-Goldfarb-Shannon (BFGS)。令我惊讶的是,准确率大幅提升至 96.5%,超出预期。CG 和 BFGS 这两种不同结果的比较可以在我的笔记本中查看标题Difference in accuracy due to different optimization algorithm

造成这种准确性差异的原因是由于优化算法的选择不同吗?如果是,那么有人可以解释为什么吗?

此外,我将非常感谢对我的代码进行任何审查,以确保我的任何函数中都没有导致此问题的错误。

谢谢你。

编辑:在下面我添加了问题中涉及的代码,根据我在此页面中的评论中的请求,而不是让读者参考我的 Jupyter 笔记本的链接。

模型成本函数:

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

def compute_cost_regularized(theta, X, y, lda):
    reg =lda/(2*len(y)) * np.sum(theta[1:]**2) 
    return 1/len(y) * np.sum(-y @ np.log(sigmoid(X@theta)) 
                             - (1-y) @ np.log(1-sigmoid(X@theta))) + reg

def compute_gradient_regularized(theta, X, y, lda):
    gradient = np.zeros(len(theta))
    XT = X.T
    beta = sigmoid(X@theta) - y
    regterm = lda/len(y) * theta
    # theta_0 does not get regularized, so a 0 is substituted in its place
    regterm[0] = 0 
    gradient = (1/len(y) * XT@beta).T + regterm
    return gradient

实现一对多分类训练的功能:

from scipy.optimize import minimize

def train_one_vs_all(X, y, opt_method):
    theta_all = np.zeros((y.max()-y.min()+1, X.shape[1]))
    for k in range(y.min(),y.max()+1):
        grdtruth = np.where(y==k, 1,0)
        results = minimize(compute_cost_regularized, theta_all[k-1,:], 
                           args = (X,grdtruth,0.1),
                           method = opt_method, 
                           jac = compute_gradient_regularized)
        # optimized parameters are accessible through the x attribute
        theta_optimized = results.x
        # Assign thetheta_optimized vector to the appropriate row in the 
        # theta_all matrix
        theta_all[k-1,:] = theta_optimized
    return theta_all

调用该函数以使用不同的优化方法训练模型:

theta_all_optimized_cg = train_one_vs_all(X_bias, y, 'CG')  # Optimization performed using Conjugate Gradient
theta_all_optimized_bfgs = train_one_vs_all(X_bias, y, 'BFGS') # optimization performed using Broyden–Fletcher–Goldfarb–Shanno

我们看到预测结果因所使用的算法而异:

def predict_one_vs_all(X, theta):
    return np.mean(np.argmax(sigmoid(X@theta.T), axis=1)+1 == y)*100

In[16]: predict_one_vs_all(X_bias, theta_all_optimized_cg)
Out[16]: 90.319999999999993

In[17]: predict_one_vs_all(X_bias, theta_all_optimized_bfgs)
Out[17]: 96.480000000000004

对于任何想要获取任何数据来尝试代码的人,他们可以在我的 Github 中找到它,正如本文中链接的那样。

2个回答

数值精度和稳定性的限制导致优化程序陷入困境。

您可以通过将正则化项更改为 0.0 来最轻松地看到这一点——原则上没有理由不这样做,而且您没有使用任何特别需要它的特征工程。将正则化设置为 0.0,您将看到已达到精度限制,并在计算成本函数时尝试取 0 的对数。由于在路线上采用不同的采样点,这两个不同的优化例程受到不同程度的影响。

我认为,将正则化项设置得高,可以消除数值不稳定性,但代价是看不到计算的实际情况——实际上,正则化项在困难的训练示例中占主导地位。

您可以通过修改成本函数来抵消一些准确性问题:

def compute_cost_regularized(theta, X, y, lda):
    reg =lda/(2*len(y)) * np.sum(theta[1:]**2) 
    return reg - 1/len(y) * np.sum(
      y @ np.log( np.maximum(sigmoid(X@theta), 1e-10) ) 
      + (1-y) @ np.log( np.maximum(1-sigmoid(X@theta), 1e-10) ) )

还可以在培训期间获得一些反馈,您可以添加

                       options = {
                           'disp': True
                       }

来电minimize

通过此更改,您可以尝试将正则化项设置为零。当我这样做时,我得到:

predict_one_vs_all(X_bias, theta_all_optimized_cg)
Out[156]:
94.760000000000005
In [157]:

predict_one_vs_all(X_bias, theta_all_optimized_bfgs)
/usr/local/lib/python3.6/site-packages/ipykernel/__main__.py:2: RuntimeWarning: overflow encountered in exp
  from ipykernel import kernelapp as app
Out[157]:
98.839999999999989

94.76 的 CG 值似乎与预期结果很好地匹配 - 所以我想知道这是否是在没​​有正则化的情况下完成的。BFGS 值仍然“更好”,尽管在训练和评估期间考虑到警告消息,我不确定我对它的信任程度。要判断这个明显更好的训练结果是否真的转化为更好的数字检测,您需要在保留测试集上测量结果。

CG 不像 BFGS 那样收敛到最小值

如果我也可以在这里为我自己的问题添加一个答案,感谢一位自愿查看我的代码的好朋友。他不在 Data Science stackexchange 上,并且觉得不需要创建一个帐户来发布答案,所以他错过了这个机会向我发帖。

我还会参考@Neil Slater,因为他对数值稳定性问题的分析可能会解释这一点。

所以我的解决方案背后的主要前提是:

我们知道成本函数是凸的,这意味着它没有局部变量,只有全局最小值。由于使用 BFGS 训练的参数的预测优于使用 CG 训练的参数,这意味着 BFGS 比 CG 更接近最小值。BFGS 是否收敛到全局最小值,我们不能肯定,但可以肯定地说它比 CG 更接近。

因此,如果我们采用使用 CG 训练的参数,并使用 BFGS 将它们传递给优化例程,我们应该看到这些参数得到进一步优化,因为 BFGS 使一切更接近最小值。这应该会提高预测准确度,并使其更接近使用普通 BFGS 训练获得的预测准确度。

下面是验证这一点的代码,变量名称与问题中的相同:

# Copy the old array over, else only a reference is copied, and the 
# original vector gets modified
theta_all_optimized_bfgs_from_cg = np.copy(theta_all_optimized_cg)

for k in range(y.min(),y.max()+1):
    grdtruth = np.where(y==k, 1,0)
    results = minimize(compute_cost_regularized,theta_all_optimized_bfgs_from_cg[k-1,:], 
                       args = (X_bias,grdtruth,0.1),
                       method = "BFGS", 
                       jac = compute_gradient_regularized, options={"disp":True})
    # optimized parameters are accessible through the x attribute
    theta_optimized = results.x
    # Assign thetheta_optimized vector to the appropriate row in the 
    # theta_all matrix
    theta_all_optimized_bfgs_from_cg[k-1,:] = theta_optimized

在循环执行期间,只有一次迭代产生了一条消息,显示优化例程迭代的次数非零,这意味着执行了进一步的优化:

Optimization terminated successfully.
         Current function value: 0.078457
         Iterations: 453
         Function evaluations: 455
         Gradient evaluations: 455

结果得到了改善:

In[19]:  predict_one_vs_all(X_bias, theta_all_optimized_bfgs_from_cg)
Out[19]:  96.439999999999998

通过进一步训练最初从 CG 获得的参数,通过额外的 BFGS 运行,我们进一步优化了它们以提供96.44%非常接近96.48%直接使用仅 BFGS 获得的预测精度!

我用这个解释更新了我的笔记本。

当然,这引发了更多的问题,比如为什么 CG 在这个成本函数上的效果不如 BFGS,但我想这些问题是为了另一篇文章而准备的。