《Python大规模机器学习》—3.2.4 使用SGD实现大规模SVM
650
2022-05-29
3.5 超参数调整
与成批学习一样,在测试超参数的最佳组合时,在非核心算法中没有捷径,需要尝试一定数量的组合,才能找出可能的最佳解决方案,并使用样本外错误度量手段来评估其性能。
由于你实际上不知道所预测问题是否具有简单平滑的凸损失或更复杂的损失,而且不确切知道超参数如何相互交互,所以没有足够组合很容易陷入次优的局部最小值。不幸的是,目前Scikitlearn还没有针对非核心算法提供专门的优化程序。考虑到在长数据流上训练SGD需要很长时间,调整超参数可能确实会成为使用这些技术在数据上构建模型的瓶颈。
这里,我们提出有助于节省时间和尽量达到最佳结果的经验法则。
首先,调整适合放入内存的数据样本或窗口上的参数。正如在核SVM中看到的那样,即使数据流很大,使用库样本也会非常快。然后你可以在内存中进行优化,并使用流中找到的最佳参数。
如微软研究院的Léon Bottou在其技术论文“Stochastic Gradient Descent Tricks”中所说的那样:
“数学上随机梯度下降与训练集大小完全无关。”
所有关键参数都是如此,但仅学习率除外;对于样本效果更好的学习率对整个数据来说效果最好。另外,通过在小采样数据集上尝试收敛,能猜测理想的数据传递次数。根据经验,我们汇总了算法检查的10**6个示例的指示性数量,正如Scikit-learn文档所指出的那样,我们经常发现该数字是准确的,尽管理想的迭代次数可根据正则化参数而改变。
虽然在使用SGD时大多数工作可以用相对较小的规模完成,但我们必须定义如何解决确定多个参数的问题。传统上,手动搜索和网格搜索是最常用方法,网格搜索将通过系统测试可能参数的所有组合来解决问题(例如,使用10或2的不同乘方以对数尺度进行检查)。
最近,James Bergstra和Yoshua Bengio在其论文“Random Search for Hyper-Parameter Optimization”中提出了一种基于超参数值随机采样的不同方法。尽管基于随机选择,但如果超参数的数量很少,并且可以超过当参数很多而且并非所有参数都与算法性能相关时系统搜索的性能,那么这种方法通常在结果上与网格搜索等价(但运行次数更少)。
关于为什么这种简单而有吸引力的方法在理论上如此有效,我们把它留给读者以发现更多的理由,
请参考前面提到的Bergstrom和Bengio的论文。在实践中,我们已经体验到了相比其他方法的优越性之后,我们提出了一种基于Scikit-learn中ParameterSampler函数的方法,它在上面示例代码中对数据流运行良好,以下示例代码中给出具体实现。ParameterSampler能够随机采样不同组的超参数(来自分布函数或离散值列表),以便随后通过set_params方法来学习SGD:
该代码利用了共享单车数据集非常小并且不需要任何采样这样的事实。在其他情况下,限制被处理的行数很有意义,或者在通过蓄水池采样或其他采样技术之前创建一个较小的样本是合理的。如果你想更深入地探索优化过程,可以更改random_tests变量,从而修改要测试的采样超参数组合的数量。然后使用更接近1.0的数字(如果不是1.0本身)
修改if tmp_rmsle/print_rmsle <= 1.01条件,
从而让算法完全收敛,直到有适当的预测能力。
虽然建议使用分布函数而不是从值列表中选取,但你仍然可通过简单地扩大可能从列表中选取值的数量来适当使用之前讨论的超参数范围。例如,对于L1和L2正则化中的alpha,可以使用NumPy的函数arrange,用一个小步进,例如10.0**- np.arange(1,7,step=0.1),或者对num参数使用NumPy logspace:1.0/np.logspace(1,7,num=50)。
其他SVM快速学习方法
尽管Scikit-learn包提供了足够的工具和算法来进行非核心学习,但在免费软件中还有很多其他选择方法。有些基于与Scikit本身相同的库,例如Liblinear/SBM,其他是全新的,如sofia-ml、LASVM和Vowpal Wabbit。例如,Liblinear/SBMis基于选择性块最小化,并实现为原始库liblinear-cdblock(https://www.csie.ntu.edu.tw/~cjlin/libSVMtools/#large_linear_classification_when_data_cannot_ fit_in_memory)的分叉。Liblinear/SBM使用新样本数据训练并通过使用学习器的技巧,来拟合非线性SVMS,以适应大量无法存储在内存中的数据,并将其与先前用于最小化的样本进行混合(因此算法名中采用blocked项)。
SofiaML(https://code.google.com/archive/p/sofiaml/)是另一种选择方法,SofiaML基于一种名为Pegasos SVM的在线SVM优化算法,该算法是一种在线SVM近似,就像由Leon Bottou创建的另一种名为LaSVM的软件一样(http://leon.bottou.org/projects/laSVM)。所有这些方法都可以处理稀疏数据,特别是文本,并解决回归、分类和排序问题。迄今为止,我们测试过的方案中,除了Vowpal Wabbit以外,
没有别的方案更快速、功能更强大,下面将介绍该软件,并用它演示如何将外部程序与Python集成。
Vowpal Wabbit快速实现非线性
Vowpal Wabbit(VW)是一个快速在线学习开源项目,最初于2007年由雅虎的John Langford、Lihong Li和Alex Strehl (http://hunch.net/?p=309)研究发布,然后由Microsoft Research赞助并且John Langford成为微软首席研究员。该项目已经发展多年,而且有近百名贡献者参与其中,目前为8.1.0版本(本章写作时)。(若要查看多年累积的可视化效果,
请访问使用软件Gource制作的有趣视频https://www.youtube.com/watch?v=-aXelGLMMgk)。迄今为止,VW仍在不断发展,并在每次开发迭代中提高其学习能力。
相比于其他方法(LIBLINEAR、Sofia-ml、SVMgd和Scikit-learn),VM的突出特点是速度非常快。其秘密很简单但非常有效:能同时加载数据并从中学习。异步线程对输入的实例进行解析,学习线程在不相交的特征集上工作,从而确保即使解析过程涉及创建高维特征也具有高计算效率(如二次或三次多项式展开)。在大多数情况下,学习过程的真正瓶颈是磁盘或网络传输数据给VM的传输带宽。
VM可计算分类(甚至是多类和多标签)、回归(OLS和分位数)和主动学习问题,能够提供大量附带学习工具(称为缩减),例如矩阵分解、潜在狄利克雷分布(LDA)、神经网络、n-gram语言模型和拔靴法。
安装VW
不幸的是,获得Windows操作系统上运行的VW版本有点难度。为了创建它,请参考VW本身的文档,其中详细解释了编译过程(https://github.com/JohnLangford/vowpal_wabbit/blob/master/README.windows.txt)。
在本书附带的网站上将提供书中用到的VW 8.1.0版本的32位和64位Windows二进制文件。
理解VW数据格式
VW使用特定数据格式,并从shell调用。John Langford在其在线教程(https://github.com/JohnLangford/vowpal_wabbit/wiki/Tutorial)中使用的样本数据集代表
屋顶可更换的三栋房屋,非常有趣,推荐读者一起学习:
很明显,文件格式没有标题,这是因为VW使用哈希技巧将特征分配到稀疏向量中,因此事先知道那些根本不需要的特征。数据块通过管道(字符|)划分成命名空间,作为不同的特征集群,每个集群都包含一个或多个特征。
第一个名称空间始终是包含响应变量的名称空间,而响应可以是指向要回归的数值的实数(或整数)、二分类或多个类中的某类。响应始终是在一条线上找到的第一个数字。二分类中1表示正数,-1表示负数(0作为响应仅用于回归)。多类的话,则从1开始编号,不建议使用间隔编号,因为VW访问最后一个类时会考虑1和最后一个之间的所有整数。
紧邻响应值后面的数字是权重(用来告诉你将实例看作单个实例还是整体的一部分),然后是起初始预测作用的基数(某种偏差)。最后,前有撇号字符(')的标签可以是数字或者文本,它能在后面VW输出(预测中,每个估计对应一个标签)中找到。权重、基数和标签不是强制性的:如果省略,权重赋值为1,基数和标签无关紧要。
在第一个名称空间后,可根据需要添加尽可能多的名称空间,并用数字或字符串作为其标签。为了说明是名称空间的标签,应该将它放在管道后,例如|label。
在名称空间标签的后面,可以按名称添加任何特征,特征名可以为任何内容,但应包含管道或冒号。可以将整个文本放在名称空间中,这样每个单词都将被视为一个特征,每个特征都将被赋值为1。如果想赋其他不同数字,只需在特征名末尾添加冒号,并将其值放在它后面即可。
例如,Vowpal Wabbit可读取的有效行是:
第一个名称空间中,响应为0,实例权重为1,基数为0.5,其标签为third_house。名称空间无名称并且由四个特征构成,即price(值为.53)、sqft(值为.32)、age(值为.87)和1924(值为1)。
如果某个特征在一个实例中有,但在另一个实例中没有,则算法会在第二个实例中假设此特征值为零。因此,上例中的1924这样的特征可当作二进制变量,因为其存在时自动被赋值为1,缺少时则为0,这也告诉你VW如何处理缺失值——自动将其赋为0值。
缺少值时,可通过添加新特征轻松处理缺失值。例如,如果该特征为age,可以添加一个新特征age_missing,新特征将是一个值为1的二进制变量。在估计系数时,该变量将作为缺失值估计器。
http://hunch.net/~vw/validate.html
Python集成
vowpal_porpoise、Wabbit Wappa和pyvw等软件包能与Python集成,并且在Linux系统中很容易安装,但在Windows上安装比较困难。无论使用Jupyter还是IDE,将VW与Python脚本集成的最简单方式是利用来自subprocess包的Popen函数。它可以使VW与Python并行运行,Python只是等待VM完成其操作并捕获其输出,然后将输出显示在屏幕上:
这些函数返回学习过程的输出列表,使其易于处理,并提取相关的可重用信息(如错误度量)。作为其正确运行的前提条件,请将VW可执行文件(vw.exe文件)放在Python工作目录或系统路径中以便找到它。
通过调用函数来处理先前记录的房屋数据集,可以看到它的工作过程及其输出结果:
输出的初始行只是显示所使用的参数,并确认正在使用哪个数据文件。最令人感兴趣的是按流化实例数显示的逐步骤过程(按2的幂报告,例如1、2、4、8、16等等)。损失函数显示平均损失度量,在第一次迭代后渐进向前,通过将字母h放在后面来表示其损失(如果排除拒绝,则有可能只报告样本内度量)。在example weight列上显示实例权重,然后将实例进一步描述为current label、current predict,并显示在该行上找到的特征数量(current features)。所有这些信息有助于监视流和学习过程。
学习完成后会显示某些报告措施。平均损失最重要,特别是在使用保留时。出于比较原因,使用这种损失最有效,因为它能立即与best constant’s loss(简单常数的基线预测能力)以及使用不同参数配置的不同运行结果进行对比。
集成VW和Python的另一个非常有用的函数是自动将CSV文件转换为VW数据文件的函数。可以在下面代码中找到它,它将帮助处理以前的共享单车和森林覆盖类型问题,它很容易用在你自己的项目中:
几个使用简化SVM和神经网络的示例
VM会最大限度地最小化一般成本函数,如下式所示:
与之前看到的其他公式一样,w为系数向量,根据所选择的损失函数(OLS,logistic或hinge)获取每个xi和yi的最优化。lambda1和lambda2是正则化参数,默认情况下为零,但可以使用VW命令行中的“--l1”和“--l2”选项进行设置。
基于这种基本结构,随着时间推移,通过使用简化范式,VM已经变得更加复杂和完整。简化只是一种重用现有算法以解决新问题的方法,这样无须从头开始编码新的求解算法。换句话说,如果你有复杂的机器学习问题A,只需将其简化到B。解决B的线索在A的解决方法中,这也很有道理。人们对机器学习的兴趣越来越浓厚,问题数量爆炸式增长,却无法创造新解决算法。一种可行的方法就是利用基本算法的已有功能,以及VM随着时间推移适用性越来越强而程序依然很紧凑的原理。如果你对这种方法感兴趣,请参考John Langford的两篇教程:http://hunch.net/~reductions_tutorial/和http://hunch.net/~jl/projects/reductions/reductions.html。
为了进行说明,我们简要介绍两个简化示例,即使用RBFkernel的SVM和以纯非核心方式使用VW的浅层神经网络,为此使用了一些简单数据集。
Iris数据集更改为二分类问题,以预测鸢尾花卉属于Setosa还是Virginica:
然后对回归问题使用波士顿房屋定价数据集:
首先,我们尝试使用SVM。kvsm是基于LaSVM算法(具有在线和主动学习功能的快速核分类器—http://www.jmlr.org/papers/volume6/bordes05a/bordes05a.pdf)的简化,无偏差项。VW版本通常只需一次传递,并对随机选取的支持向量进行1到2次重新处理(尽管有些问题需要多次传递和再处理)。在本例中,我们按顺序使用单次传递和几次重新处理来在二分类问题上拟合RBF核(KSVM仅用于分类问题)。实现的核是线性的径向基函数和多项式。为使它工作,请使用“--kSVM”选项,可以通过“--reprocess”设置重新处理次数(默认值为1),通过“--kernel”选择内核类型(选项为linear、poly和rbf)。如果内核为多项式,则将“--degree”设置为整数。如果使用RBF,则将“—floatband”设置为浮点数(默认值为1.0)。另外必须强制让l2正则化;否则,简化过程无法正常运行。示例中,我们将RBFkernel的带宽设置为0.1:
神经网络是VM的另一个很好的补充,由于Paul Mineiro的工作(http://www.machined-learnings.com/2012/11/unpimp-your-sigmoid.html), 让VM能实现具有双曲正切(tanh)激励函数以及可选丢弃率(使用“--dropout”选项)的单层神经网络。虽然只能决定神经元数量,但简化神经网络在回归和分类问题上都效果很好,并且能顺利进行以VM为输入的其他转换(如二次变量和n-gram),这样使其成为一个多功能(神经网络能解决很多问题)和快速的完整解决方法。示例中使用五个神经元和丢弃率处理波士顿数据集:
更快的共享单车
下面对以前创建的共享单车示例文件使用VW,以便解释输出组成。第一步,必须使用前面介绍的vw_convert函数将CSV文件转换为VW文件。与以前一样,通过使用由vw_convert函数的transform_target参数所传递的apply_log函数,来实现对数字响应的对数转换:
几秒钟准备好新文件后,立即运行我们的算法,这是一种简单的线性回归(VW的默认选项)。预计学习100次,由VW自动实现的样本外验证进行控制(以可重复方式系统地进行抽取,每10次观察一次进行验证)。在此例中,我们决定在16 000个示例(使用“--holdout_after”选项)之后设置拒绝样本。当验证错误增加(而不是减少)时,VW在几次迭代后停止(默认为3次,但可使用“--early_terminate”选项更改次数),以避免数据过拟合:
最终报告显示完成了六次传递(从100种可能中),样本外平均损失为0.428。由于我们关注RMSE和RMSLE,所以必须自己计算它们。
然后在文件pred.test中预测结果,以便能够读取它们并使用与训练集中相同的拒绝策略计算错误度量。与之前用Scikitlearn的SGD所获得的结果相比,这些结果确实要好很多(只用了一点时间):
使用VW处理森林覆盖类型数据集
相比于以前的方法,VW能更好、更容易解决森林覆盖类型问题。这一次,我们将不得不设置一些参数并控制纠错竞争(ECT,VW使用“--ect”参数调用它),其中每个类在淘汰赛中竞争作为示例标签。在很多实例中,ECT胜过一对多(OAA),但这不是通用规则,ECT是多类问题测试方法之一。(另一种选择是“--log_multi”,它使用在线决策树将样本分成更小集合,并应用单预测模型。)我们将学习率设置为1.0,并使用“--cubic”参数创建三次多项式展开式,以指出哪些名称空间必须互乘(在这种情况下,三次命名空间f用nnn字符串表示,后面紧接着是“--cubic”):
为让示例运行更快速,我们将传递数限制为两个,如果时间允许,你可将数字提高到100,并见证如何进一步提高所获得的准确度。
在这里不需要进一步检查错误度量,因为所报告的平均损失是对精确度1.0的补充,我们只是为了完整性
计算它,从而确认最后的拒绝精确度正好是0.769:
Python 机器学习
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。