Scikit-learn: 到处都是Liblinear收敛失败?

创建于 2018-07-15  ·  70评论  ·  资料来源: scikit-learn/scikit-learn

最近我们做了 liblinear 报告收敛失败。
现在很多地方都有这样的报道。 我希望我们的用户将开始在任何地方看到它。 我们应该改变什么吗? 如果现在大多数使用 LinearSVC 导致“失败”,那就太奇怪了。

High Priority

最有用的评论

因为 SAG/SAGA 保留旧的梯度,所以它提供了对梯度的准确估计。 我会用它作为停止标准。 对于 L1 正则化问题,您不能使用它,因为在最优时梯度不再为零,但可以使用梯度映射代替,即 (1/step_size)(x - prox(x - step_size * grad_estimate))

所有70条评论

此外,SVC 有max_iter=-1而 LinearSVC 有max_iter=1000也很奇怪。 这有什么原因吗?

好问题。 查看 liblinear 代码,我们似乎没有公开它们具有的不同停止标准,并且我们添加了它们似乎没有的 max_iter 参数。

我不知道为什么将其设置为 1000。是否进行了任何基准测试?

不是我记得...

没有强烈的意见。 警告太多意味着用户不看警告。

我唯一可以建议的是添加一个选项来控制这种行为。 我真的不喜欢添加选项(危险是有太多),但似乎这里没有一刀切的选择。

我们也可以增加收费吗? @ogrisel询问我们是否也有这个逻辑回归警告,或者我们是否在那里忽略它。

LogisticRegression 类是否也会出现此问题?

我对增加收费是-1:这意味着许多用户会等待更长时间。

我认为应该有一个选项来控制收敛警告。

增加 tol 意味着更大的 tol。 因此,如果有任何事情,人们会等待更短的时间。

增加 tol 意味着更大的 tol。 因此,如果有任何事情,人们会等待更短的时间。

好吧,我理解错了。

为该选项+1。

致力于此。

@ogrisel确实LogisticRegression也可能受到影响。

正如与@agramfort讨论的那样,我对撞到tol有点怀疑,因为周围有很多不同的默认值:

  • 逻辑回归:max_iter=100,tol=0.0001
  • 线性SV{C,R}:max_iter=1000,tol=0.0001
  • SV{C,R}: max_iter=-1, tol=0.001

这只是关于 liblinear,所以现在 tol 是 0.0001。 因此,它将使其更加一致。 不过,我们可能应该运行一些基准测试?

啊确实,所以也许这并不像我最初想象的那么复杂。
对于所有这些 liblinear 调用,tol 默认应该是 0.001 吗?
我同意基准!

@samronsin是的,我认为那会很好。 这似乎是最后的发布阻止程序之一?

顺便说一句,促成这一切的变化是#10881,它基本上只是冗长的变化:-/

顺便说一句,使用默认求解器,默认情况下,liblinear 中的 tol 为 0.1(!)。 https://github.com/cjlin1/liblinear/blob/master/README

哇。 Liblinear 中有这么多惊喜......

@jnothman这主要是我们有惊喜的包装器,我想?

tbh 我没仔细研究过...

liblinear 命令行实际上具有各种容差默认值,具体取决于所使用的子求解器。

我们要使用这些吗? 这可能需要将默认值切换为'auto'

我希望我们在发布后考虑一下:)

我认为基于求解器的不同公差默认值(和迭代)通常是有意义的! 这里似乎对增加 liblinear 容忍度达成了共识。

那么我们应该让它和 liblinear 中的一样吗? 或者你会如何选择新的默认值? 有弃用周期?

这里的弃用周期令人讨厌......我们可以称之为错误修复吗?
使用 liblinear 默认值的好处是不需要证明我们自己的合理性!

我同意这两个;)

冲刺会议的决议:

  • 将 max_iter 和 tol 更改为特定于求解器,原因是“迭代”对不同的求解器和 tol 意味着某些东西。 这个想法是默认情况下将 tol 和 max_iter 设置为“auto”并根据参数进行切换。

选择需要基准(添加 sag 或 saga 的 PR 可能有一个,它可能在 benchmarks 文件夹中)。

最有可能的是,我们将使用等于 100 和 1000 的 max_iter(例如,100 用于准牛顿方法,1000 用于一阶/坐标下降求解器)。

作品分解:

  • [ ] 调查将 lbfgs 的 tol 类型更改为 ftol 而不是 pgtol
  • [ ] 调查什么值 tol 导致对不同数据和不同求解器的良好预测(对于 liblinear,我们应该调查 liblinear 的默认值)
  • [ ] 调查 max_iter 的什么值导致最常收敛到上述 tol

这里要注意的另一件事是,在某些时候,我们在 liblinear 中暴露了 max_iter(在添加其他求解器之后),因此在没有告诉用户的情况下将默认迭代次数从 1000 更改为 100。

如果它提供更合理的收敛速度,我们愿意在这里更改默认值作为错误修复,而不保持向后兼容性。

@GaelVaroquaux是分配给它的人,还是我们应该将其标记为 Help Wanted?)

@FollowKenny :我认为您可能有时间(一旦您完成了两行代码的优化)。

我开始着手解决这个问题,因为我无法弄清楚这两条线有什么问题......

顺便说一句,我刚刚意识到,在当前设置下,如果不使用任何求解器的 tol 和 max_iter ,就很难在有限时间内使逻辑回归收敛于 mnist 。 那不是很好。

(通过收敛我的意思是停止拟合)

请注意,在 Windows 平台上,众所周知,liblinear 具有很强的收敛性问题,因为随机数的生成方式:Windows 中的最大随机数是 15 位(即使在 64 位 Windows 上也是如此),即 32767,而 linux+ 中的最大随机数GCC 是 31 位(我猜是 64 位系统中的 63 位),所以是 2147483647(分别是 9223372036854775807)。

这是 liblinear FAQ 中记录的一个已知错误,但建议的解决方法是错误的 - 我在几年前做了一个补丁,得到了几个用户的批准,但从未合并: https :

很抱歉迟到了,但是我所有现有的工作流程都是使用 LinearSVC() 我开始在 0.20.x 的所有地方遇到 Liblinear 收敛失败,而完全相同的代码在 0.19.x 中完美运行。 我从来没有收到 0.19.x 的收敛警告。 发生了什么变化?

(除了我上面的帖子)我目前在另一个项目上使用 libsvm 和 liblinear,上面报告的 windows 平台的收敛错误似乎也存在于 libsvm 中。 另一个用户报告了它

我看到原来的来源实际上被复制到 scikit-learn 中。 所以我会提出一个拉取请求。
同时,我将尝试通过链接到这篇文章和 PR 来激励作者。
欢迎任何反馈/想法/想法:)

我可能弄错了,但我认为我们没有报告
liblinear 中的 ConvergenceWarnings 直到 0.20。 你有同样的身材吗
在 0.19 和 0.20 中,@hermidalc?

我可能弄错了,但我认为我们直到 0.20 才在 liblinear 中报告 ConvergenceWarnings。 @hermidalc,您在 0.19 和 0.20 中得到相同的拟合吗?

哦,我不知道,我会检查几个数据集来查看并报告

我们是否应该将 liblinear max_iter 设置回在 LogisticRegression 中创建 max_iter 之前的状态?

顺便说一句,我刚刚意识到,在当前设置下,如果不使用任何求解器的 tol 和 max_iter ,就很难在有限时间内使逻辑回归收敛于 mnist 。 那不是很好。

事实上,显然它也确实取决于求解器:

  • 对于 SAG / SAGA,停止标准基于系数变化的最大绝对值,并且实际上使用默认正则化它并没有在 MNIST 上的数千次迭代后停止: max_change值似乎不是单调的跨迭代减少(如使用verbose = 1监控)。 我必须杀死我的 python 程序才能让它停止。 ctrl-c 甚至不起作用,因为它在纯 Cython 循环中(但这是一个不同的问题)。

  • 对于 l-BFGS,有几个停止标准,基于梯度的范数(由pgtol )和一个基于目标值的变化(由factr )。 当在 MNIST 上以 verbose=1 运行 LogisticRegression 时,我观察到循环在数十或数百次迭代(取决于正则化)后停止,总是因为基于目标的停止标准。 在 scikit-learn 中更改 tol 参数时,会更改 l-BFGS 的pgtol参数,这对factr参数和基于目标的停止标准没有影响。 必须将 tol 增加很多(例如 1e-1 或 1e-2)才能使基于梯度的停止标准首先停止。

这里参考的是两个 l-BFGS 停止参数的确切含义:

factr : float, optional

    The iteration stops when (f^k - f^{k+1})/max{|f^k|,|f^{k+1}|,1} <= factr * eps, where eps is
     the machine precision, which is automatically generated by the code. Typical values for
     factr are: 1e12 for low accuracy; 1e7 for moderate accuracy; 10.0 for extremely high
     accuracy. See Notes for relationship to ftol, which is exposed (instead of factr) by the
     scipy.optimize.minimize interface to L-BFGS-B.

pgtol : float, optional

    The iteration will stop when max{|proj g_i | i = 1, ..., n} <= pgtol where pg_i is the i-th
     component of the projected gradient.
  • 对于 newton-cg:似乎只有梯度的范数被用作停止标准。 我还没有使用该求解器对 mnist 进行广泛的实验,以检查缺乏基于目标的停止标准是否会导致问题。

@TomDLT @fabianp @arthurmensch您知道依靠参数更改作为 SAG / SAGA 的停止标准是否是一种好习惯? 对于弱正则化逻辑回归模型,这似乎是有问题的。 我们可能想要添加第二个基于目标函数的停止标准,如 l-BFGS 中。 问题是我们不想在一个 epoch 结束时重新扫描整个数据集来计算目标值。 可以累积小批量损失值的滞后平均值,并在每个时期结束时将此滞后平均值与其前一个时期值进行比较,但这不再与确切的时期结束目标值完全相关。 我对 SAG 和 SAGA 的收敛理论不够熟悉,不知道我的建议是否有意义。

第二个问题:更一般地说,我们是否应该对tol参数进行更改以一致的方式影响所有求解器的基于目标值的停止标准? 我相信是这样,但是如何权衡基于参数变化的停止标准? 我们要引入第二个容差参数吗?

因为 SAG/SAGA 保留旧的梯度,所以它提供了对梯度的准确估计。 我会用它作为停止标准。 对于 L1 正则化问题,您不能使用它,因为在最优时梯度不再为零,但可以使用梯度映射代替,即 (1/step_size)(x - prox(x - step_size * grad_estimate))

谢谢,这听起来是个好建议。 这是您在自己的 SAGA 实现中使用的策略吗? 您是否将其与基于两次迭代之间的一些滞后平均损失变化的第二个标准结合起来? 或者,在迭代中变化的样本顺序的随机性可能会导致求解器过早停止,使用基于目标值的这种近似的天真停止标准。

我不这样做(但应该)。

我可能弄错了,但我认为我们直到 0.20 才在 liblinear 中报告 ConvergenceWarnings。 @hermidalc,您在 0.19 和 0.20 中得到相同的拟合吗?

@jnothman等人,很抱歉耽搁了。 我现在在 0.20 和 0.21 上检查过,是的,我得到了与 0.19 相同的拟合度。 我还可以确认,当使用带有 kernel=linear 的 SVC 时,我得到了类似的拟合。

同样奇怪的是,即使我设置了 LinearSVC max_iter=5000,即使在非常适合的问题上(例如 ROC AUC >= 0.85),它仍然无法收敛所有警告

我们/我们是否应该为正确设置公差而存在逻辑回归问题?

13317是相关的pr

我现在尝试抑制 Liblinear 收敛警告,但看到运行GridSearchCV ,默认的 joblib loky 后端会忽略filterwarnings设置:

warnings.filterwarnings("ignore", category=ConvergenceWarning, message="^Liblinear failed to converge")

使用 joblib 多处理后端,这可以工作并抑制来自工作进程的此类警告。

有谁知道如何让filterwarnings设置传播到 loky 工作进程?

此外,对于大多数 scikit-learn 工作流程,是否每个人都推荐 loky 而不是多进程? 他们每个人都有自己的优点/缺点。

我现在看到这个问题与 loky 和 ​​filterwarnings 在 issue #12939 中讨论。

虽然现在仍然想为大多数 sklearn 工作流程推荐 loky 或多处理后端?

我认为不应该过滤这些警告,我们应该找到更好的默认容差。

好的,这很糟糕:

from sklearn.datasets import load_digits
from sklearn.svm import LinearSVC
digits = load_digits()
svm = LinearSVC(tol=1, max_iter=10000)
svm.fit(digits.data, digits.target)

如果数据未缩放,则对偶求解器(默认设置)将永远不会收敛于数字数据集。

我认为这不能用 tol 和 max_iter 真正解决:(

我们是否警告过 SVR 的缩放?

我认为我们不会警告任何地方的缩放?
此外,这是一个线性模型。 它应该真正收敛而无需缩放。

那么你认为在 LinearSVC 中 dual 应该默认为 False 吗?

@jnothman我认为我们需要进行基准测试,但可能吗?

我认为我们不会警告任何地方的缩放?
此外,这是一个线性模型。 它应该真正收敛而无需缩放。

SVC(kernel='linear')即 libsvm 也不会收敛,如果您之前不缩放数据,对于我拥有的许多数据集,实际上会更糟糕地挂起 100% CPU(自max_iter=-1 )。 所以我在这里不同意......如果你的特征与其他人的尺度截然不同,以合理的公差拟合最佳超平面将变得困难。

好的,这很糟糕:

from sklearn.datasets import load_digits
from sklearn.svm import LinearSVC
digits = load_digits()
svm = LinearSVC(tol=1, max_iter=10000)
svm.fit(digits.data, digits.target)

如果数据未缩放,则对偶求解器(默认设置)将永远不会收敛于数字数据集。

我认为这不能用 tol 和 max_iter 真正解决:(

在 sklearn 文档的任何地方,您都特别警告用户他们需要在使用许多分类器之前缩放数据等。如果将tolmax_iter设置为 liblinear L2-penalized dual solver 的正确默认值然后digits收敛:

from sklearn.datasets import load_digits
from sklearn.svm import LinearSVC
digits = load_digits()
p = Pipeline([('s', StandardScaler()),
              ('c', LinearSVC(tol=1e-1, max_iter=1000))])
p.fit(digits.data, digits.target)

@hermidalc可以肯定的是,您运行的是 Windows 还是类似 Unix 的? 实际上,Windows 存在一个已知问题(#13511)-但仅当特征或样本的数量非常大时才会发生,所以我想这不是您面临的问题。

@hermidalc可以肯定的是,您运行的是 Windows 还是类似 Unix 的? 实际上,Windows 存在一个已知问题(#13511)-但仅当特征或样本的数量非常大时才会发生,所以我想这不是您面临的问题。

Linux。 我遇到的唯一问题是LinearSVC收敛警告,因为 sklearn 中的默认tol=1e-4不是 liblinear 状态应该是默认 L2 双解算器的默认1e-1 。 当您设置tol=1e-1事先标准化您的数据(这对于 SVM 和许多其他分类器来说是必须的),那么这些收敛问题就会消失。

不想在锅中添加更多...但是收敛警告是否也是特定于操作系统的,因为它在每个操作系统上的行为应该不同? 我假设不是,但根据我的发现,它似乎是。 我已经在 macOS 10.15.2 (Catalina) 与 Linux Fedora 30 上进行了测试。

> -我跑到离卡代码https://github.com/scikit-learn/scikit-learn/issues/11536#issuecomment通过@amueller -529637160,正如你可以看到下面适用于MacOS的错误不显示,但linux 它确实显示了该错误(相同的代码!!!)。 我不确定为什么? 是不是因为 mac 上的liblinear版本可能与 linux 不同?

在具有旧库和最近库的两个 python 主要版本中进行了测试,结果是相同的。

  • macos -> py2.7与库numpy==1.16.3 scikit-learn==0.20.3 scipy==1.2.1
  • fedora -> py2.7与库numpy==1.16.3 scikit-learn==0.20.3 scipy==1.2.1
  • fedora -> py3.7与库numpy==1.17.4 scikit-learn==0.22 scipy==1.3.3

mac结果

python test/svc.py
LinearSVC(C=1.0, class_weight=None, dual=True, fit_intercept=True,
     intercept_scaling=1, loss='squared_hinge', max_iter=10000,
     multi_class='ovr', penalty='l2', random_state=None, tol=1, verbose=0)

软呢帽结果

python /vagrant/test/svc.py
/home/vagrant/.local/lib/python2.7/site-packages/sklearn/svm/base.py:931: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
LinearSVC(C=1.0, class_weight=None, dual=True, fit_intercept=True,
     intercept_scaling=1, loss='squared_hinge', max_iter=10000,
     multi_class='ovr', penalty='l2', random_state=None, tol=1, verbose=0)

有什么想法吗?

如果您的特征与其他特征的尺度大相径庭,则以合理的公差拟合最佳超平面将变得困难。

这有点取决于您所说的“困难”是什么意思。 您可能可以执行 #15583 之类的操作并很好地解决原始优化问题。 我并不是说不缩放数据是一个好主意,我只是说如果您的优化算法足够健壮,尽管用户给了您严重缩放的数据,但完全有可能很好地解决优化问题。

如果您的特征与其他特征的尺度大相径庭,则以合理的公差拟合最佳超平面将变得困难。

这有点取决于您所说的“困难”是什么意思。

抱歉,我所暗示的困难与该线程的主题相关,这意味着在最大迭代次数或最大迭代次数之前解决低于特定容差的优化问题。 未缩放的功能使 SVM 更难做到这一点,除非如您所说,您使用非常强大的算法来解决优化问题。 我认为 LIBLINEAR 使用坐标下降是不是很健壮?

是的,坐标下降对数据缩放非常稳健。

>

Liblinear 有几个求解器。 我认为他们默认使用自己的 TRON(信任区域牛顿)。

另外:我们只是将默认值从 liblinear 更改为...

我认为,哪种问题是“难”的问题可能取决于解决者,或者您如何制定问题。

另外:我们只是将默认值从 liblinear 更改为...

@amueller能否请您指出相应的问题/公关?我在主代码库中没有看到。谢谢!

啊,好吧,我误以为这是关于 SVC 的。 谢谢!

如果您的特征与其他特征的尺度大相径庭,则以合理的公差拟合最佳超平面将变得困难。

这有点取决于您所说的“困难”是什么意思。 您可能可以执行 #15583 之类的操作并很好地解决原始优化问题。 我并不是说不缩放数据是一个好主意,我只是说如果您的优化算法足够健壮,尽管用户给了您严重缩放的数据,但完全有可能很好地解决优化问题。

回到这一点,这里有一些额外的证据在实际使用中挑战这种信念:

来自 LIBSVM 和 LIBLINEAR 的创建者:
https://www.csie.ntu.edu.tw/~cjlin/papers/guide/guide.pdf

第 2.2 节 缩放
在应用 SVM 之前进行缩放非常重要。 Sarle 的神经网络常见问题解答的第 2 部分 Sarle (1997) 解释了这一点的重要性,并且大多数考虑因素也适用于 SVM。 缩放的主要优点是避免较大数值范围内的属性支配较小数值范围内的属性。 另一个优点是避免了计算过程中的数值困难。 因为核值通常取决于特征向量的内积,例如线性核和多项式核,较大的属性值可能会导致数值问题。 我们建议将每个属性线性缩放到 [−1, +1] 或 [0, 1] 范围内。
当然,我们必须使用相同的方法来扩展训练和测试数据。 例如,假设我们将训练数据的第一个属性从 [−10, +10] 缩放到 [−1, +1]。 如果测试数据的第一个属性在 [−11, +8] 范围内,我们必须将测试数据缩放到 [−1.1, +0.8]。 请参阅附录 B 中的一些真实示例。

@hermidalc我观察到它在我尝试的某些设置中比 lbfgs 更稳定,请参阅预处理问题和公关。

我不完全确定我们如何才能在这里改善用户体验:-/ 即使在缩放之后,我也看到了很多收敛问题,但我没有时间编写它们。

我正在尝试从里程碑中删除超过 2 个版本的问题。 但是这个似乎很紧迫,你真的很关心它@amueller 。 将它留在 0.24 的里程碑中,但我们真的应该更好地跟进这些。

@hermidalc我观察到它在我尝试的某些设置中比 lbfgs 更稳定,请参阅预处理问题和公关。

我不完全确定我们如何才能在这里改善用户体验:-/ 即使在缩放之后,我也看到了很多收敛问题,但我没有时间编写它们。

我不得不说@amueller我现在更同意你的看法。 随着各种高维数据集,我一直在与这些过去的几个月中,我已经看到频繁的衔接问题LinearSVC经过适当改造和预先缩放数据,甚至设置后tol=1e-1这是 LIBLINEAR 所拥有的并设置max_iter=10000或更大。 当更高的值(如 1e2 或更大)在C的范围内执行模型选择时,优化算法似乎特别有收敛问题

SVC(kernel='linear')完全相同的工作流程通常没有任何收敛问题。 虽然这两个模型的分数通常有些相似,但即使LinearSVC无法收敛,它们也不相同,对于某些数据集来说确实不同。 因此,对于我之前使用LinearSVC L2 惩罚线性分类,我现在要回到SVCSGDClassifier

问题是只有LinearSVC可以解决penalty='l1'dual=False问题,例如SelectFromModel特征选择,因此 scikit-learn 修复实施的问题。 可能可以使用SGDClassifierpenalty='l1'代替吗?

也许最新的LIBLINEAR 代码有更新/修复,已经纠正了根本问题是什么? 看起来 sklearn 中的主要 liblinear 代码来自 2014 年。

此页面是否有帮助?
0 / 5 - 0 等级