机器学习代码心得之​有监督学习的模块

作者:陈天奇,本文首发在作者的个人微博 

新年决定写一些关于如何写机器学习代码的科普文,总结一下多年写代码的经验。这一篇讲有监督机器学习的模块。

这是一篇总结讲的东西可以用来回答这样一个问题概括: “如何用最短的代码来实现L1/L2 regularized logistic/linear regression”。这里面包含了机器学习基础课上面常见的几种模型,线性回归(ridge regression),lasso, logisticregression和sparse logisticregression。问题是如何用最简短的代码来实现这种模型。如果,在开始阅读前不妨想一想,如果你知道答案,或许不需要读这篇文章。

一般说我实现了某个机器学习的算法,大家会说我实现了一个线性回归,我有一个关于lasso的代码。我实现了一个GBDT用来做分类,或者是搞了一个LambdaMART。每一个机器学习的算法在这些名词里面是一个整体,但是实际上,每一个机器学习的算法都可以被拆分成几个相互独立的元素,而在实现上,它们就对应了不同的模块。

一般而言,一个有监督机器学习算法包含了下面几个元素: 模型(model) , 目标(objective), 正则化项(regularization)。常见的神经网络,GBDT,LR都是如此。

模型和预测:机器学习的模型一般指给定输入特征,如何给出输出的预测,在这里并没有涉及到学习算法的区别。比如线性回归,和linear SVM在计算输出的时候都是用y = w^T x +b 这样一个线性函数来进行预测,在这个意义上,linear SVM, logistic regression和linear regression都是属于线性模型。另外一个常见相同模型的例子是random forest和boostedtree,在模型上面,这两者没有差别,都可以看成是把各个tree的预测结果加起来的tree ensemble。对于模型而言,最常见的一个共同函数就是预测,给定x,如何预测得到y。而这样一个Predict函数就基本上成了所有模型必备的一个函数了。

目标函数和梯度:Linear SVM, logistic regression在目标上面没有差别,其实两者唯一的差别是在于目标函数不同。Linear SVM优化的是hingeloss,而logistic regression优化的是logistic loss。这两种差别在早期的机器学习论文里面就是两篇不同的文章,但是在实现上面的差别可能就在几行代码上了。不同的学习算法可能需要计算不同的统计量,但是其中最常见的统计量要属梯度。

一般来说一个目标函数可以写成是\sum_i l(y_i),其中y_i是第i个样本的模型预测,l是目标函数,可以是hinge loss或者是logistic loss。如果要对于这样一个做梯度下降或者随机梯度下降,我们唯一需要知道的东西就是l’(y_i),对于具体weight的梯度都可以通过这个量来用chain rule推导出来。对于更加复杂的目标如排序,或者神经网络,都是如此。因此目标函数和梯度计算往往也应该是机器学习程序中一个相对独立的模块。对于其它一些算法,如gradient boosting或者proximal gradient,我们还需要知道二阶梯度。

正则化项和统计量:正则化项也是另外一个常见可以被拆分的模块。这一般也取决于算法,比如如果是coordinate descent, 常见的接口是一个thre-sholding函数,获得梯度的和以及二阶梯度的和,返回更新的结果。而这样一个给定输入统计量来计算更新权值的函数也自然变成了一个常见的模块。

代码参考

XGBoost是去年我们实现的一个快速gradient boosting的库。支持了排序,分类,回归,线性模型以及树模型,L1/L2 regularization其中各个部分都有对应:

目标函数:https://github.com/tqchen/xgboost/blob/master/src/learner/objective.h

模型和预测以及更新:https://github.com/tqchen/xgboost/blob/master/src/gbm/gbm.h

正则化项:https://github.com/tqchen/xgboost/blob/master/src/gbm/gblinear-inl.hpp#L197

这篇文章总结了机器学习程序里面常见的几个模块,当然这不代表这是机器学习程序的全部可能拆分额。根据不同的学习算法还有可能抽象出其它一些常用的模块出来。不过总结起来,这些模块之所以可以被拆分,是因为它们提供或者依赖了一些重要的统计量,比如梯度,梯度和以及二阶梯度。当一部分机器学习逻辑仅仅依赖于少数常见的统计量的时候,一般意味着这可以是一个独立的模块,这些统计量也常常对应于统计里面充分统计量的概念。

合理地理解机器学习算法的各个部分有利于我们写出可以复用的代码,使得一份代码做多样事情成为可能。

习题  最后留下一个问题:如何利用实现一个可以让每个样本带权值的线性回归算法?也就是说每一个样本有不同的重要程度,我们需要在优化的时候考虑样本权值。带权值这个要素应该放在哪一个模块中呢?如果你可以回答这个问题,应该就表明你基本明白这篇文章说的东西了。

作者:陈天奇,本文首发在作者的个人微博 http://www.weibo.com/p/1001603795687165852957?mod=zwenzhang

留下你的评论