近年来,Machine Learning 在许多领域上已然取得了可喜的成就,很是火热。就我我的来说,有意将业余 Sport Programming 的范围扩展一下,譬如 Topcoder Marathon。在解决实际问题中,方法太 Naive 每每效果不怎么样,依旧须要学习一下相关的基础知识。
本系列文章主要基于 Coursera 的 Machine Learning,我社内部 Machine Learning 课里能说的一部分,wikipedia,以及一些其余的读物。算法
对于某类任务T和性能度量P,若是一个计算机程序在T上以P衡量的性能随着经验E而自我完善,那么咱们称这个计算机程序从经验E中学习。
这是一个比较严谨的界定机器学习问题的 Guideline。若是有什么问题搞不清楚是否是这个范畴,能够尝试套用定义来检查:任务是什么,性能度量是什么,经验是什么,性能是否因为经验而提高。机器学习
我的理解,模型表明着你如何看待这个问题。譬如识别一个东西是否是汽车,若是你认为识别的依据是:金属壳 + 车灯 + 反光镜 + 车轮子 … = 汽车,这个思路就比较接近基于规则,决策树,贝叶斯;若是你考虑这个东西和见过的什么东西比较类似,就是 KNN 的思路。以后咱们要不断学习的实际上都是模型。ide
常见的两个策略是经验风险最小化和结构风险最小化。经验风险最小化意味着咱们倾向于对训练数据取得精准的预测。这个想法很直接,且有必定道理:模型在训练数据上表现不佳,更没法期望在测试数据上取得好结果。可是,在训练集上表现好的模型,未必在测试集上表现好。一般来说,简单的模型会更有通用性,而复杂的模型,每每会有一些 hardcode 了训练数据的感受,效果反而不必定好。结构风险最小化在经验风险最小化的状况下,加入一些因子来限制模型的复杂度。函数
根据策略,能够列出一个须要最优化的式子。算法就是求这个式子最优或者较优解的方法。最多见的方法是梯度降低,其余技能尚未 get,就暂不讨论了。性能
暂时忘记机器学习,如今须要优化一个形如 \( y = f(\theta) \) 的式子,求 \( x = argmax f(\theta) \) 或 \( x = argmin f(\theta) \),有什么好的办法么?
梯度降低法,基于这样的观察:若是实值函数\( F(x) \)在点 a 处可微且有定义,那么函数 \( F(x) \)在 a 点沿着梯度相反的方向 \( -F\nabla(a) \) 降低最快。所以,若是 \( b = a - \gamma\nabla F(a) \) 对于 \( \gamma > 0 \) 且为一个够小数时成立,那么 \( F(a) \geq F(b) \)。换句话说,咱们给出一个对极值的估计 a,不断迭代求 \( a = a - \gamma\nabla F(a) \) ,就能取得一个极值。学习
用一个实际例子来演示一下:对二次函数 \( f(y) = x^{2} + 2x + 10 \) ,使用梯度降低法求 \( min f(x) \) 和 \( argmin f(x) \),函数图像以下:测试
结论不管是从图像仍是初中数学的角度来看都很简单。咱们看看梯度降低算法是如何进行的:优化
f <- function(x) { x^2 + 2 * x + 10 } df <- function(x) { 2 * x + 2 } x <- 5 y <- f(x) learning.rate <- 0.3 plot(f, -5, 5) while (TRUE) { nx = x - df(x) * learning.rate ny = f(nx) if (abs(x - nx) < 0.01) break arrows(x, y, nx, ny, col = "red") x = nx y = ny print(c(x, ny)) }
## [1] 1.40 14.76 ## [1] -0.040 9.922 ## [1] -0.616 9.147 ## [1] -0.8464 9.0236 ## [1] -0.9386 9.0038 ## [1] -0.9754 9.0006 ## [1] -0.9902 9.0001
不管是简单问题仍是复杂问题,参数 learning.rate,也就是前文中提到的\( \gamma \)的选择很是重要。Learning rate 太小则须要更多的迭代。Learning rate 过大则会出现之字降低,甚至之字上升。网站
看一个非凸,多元函数的例子:Rosenbrock函数:\( f(x, y) = (1-x)^2 + 100(y-x^2)^2 \) 很显然 x = y = 1 的时候能够取得最优解,可是求解过程倒是很坑的。我们把 x = y = 1 附近的图像画出来:ui
再研究一下 x = 1 时的切面:
大概能看出来,这个函数在解附近有个很大的很平的底。。。贴一段代码,你们能够 play 一下:
f <- function(x, y) { (1 - x) ** 2 + 100 * (y - x ** 2) ** 2} df.dx <- function(x, y) { x * 2 - 2 - 400 * y + 400 * x ** 3} df.dy <- function(x, y) { 200 * y - 200 } x <- runif(1, 0, 2) y <- runif(1, 0, 2) z = f(x, y) learning.rate = 1E-6 eps <- 1E-10 while (TRUE) { new.x = x - df.dx(x, y) * learning.rate new.y = y - df.dy(x, y) * learning.rate new.z = f(new.x, new.y) if (abs(new.z - z) < eps) break x = new.x y = new.y z = new.z print(c(x, y, z)) }
能够调整一些参数,譬如 learning.rate,eps 去看看某些现象。咱们能够看到他最后几步的收敛极为缓慢,若是 learning.rate 过大,还会之字上升等等。总的来说,选择一个合适的 learning rate 是很是重要的,除去经验性的技巧,每每也只好枚举了,看看 cost function 的变化状况,若是降低过慢,则须要增大 learning rate,若是反而增加了,则须要减小 learning rate。这也就是为何某些时候咱们须要一个比较小的 validate set,咱们能够按期的在训练中的模型上跑一下 validate set,看一下 cost function 的变化,从而决定 learning rate 的调整。
以 Stanford Machine Learning 为例:根据房子的面积预测房价。我们来把一些概念对上号:
策略:\[ minimize J(\theta_{0}, \theta_{1}) = \sum_{i=1}^{m}(\theta_{0}x_{i} + \theta_{1} - y_{i})^2 \]
算法:注意此时咱们要求解的是 \( \theta_{0},\theta_{1} \),而 \( x_{i},y_{i} \) 都是已知量,能够考虑求偏导,而后用梯度降低求解,这就是技能范围之内的东西了,由于每次迭代用了全部的 Training Data,因此这个作法叫 Batch Gradient Descent。
实际应用中,比较好用的算法是 Stochastic Gradient Descent,Batch Gradient Descent 每次迭代,对 \( \sum_{i=1}^{m}(\theta_{0}x_{i} + \theta_{1} - y_{i})^2 \) 求导,至关于 \[ \theta_{0} = \theta_{0} - 2\alpha\sum_{i=1}^{m}(\theta_{0}x_{i} + \theta_{1} - y_{i})x_{i} \] \[ \theta_{1} = \theta_{1} - 2\alpha\sum_{i=1}^{m}(\theta_{0}x_{i} + \theta_{1} - y_{i}) \] 而 Stochastic Gradient Descent 至关与把 Batch Gradient Descent 的 1 次迭代拆成了 m 次,每次对 \( (\theta_{0}x_{i} + \theta_{1} - y_{i})^2 \) 求导,而后 \[ \theta_{0} = \theta_{0} - 2\alpha(\theta_{0}x_{i} + \theta_{1} - y_{i})x_{i} \] \[ \theta_{1} = \theta_{1} - 2\alpha(\theta_{0}x_{i} + \theta_{1} - y_{i}) \]
Batch Gradient Descent 能够求得更精确的解,可是若是模型复杂,或者数据量大,就很难直接 Batch Gradient Descent 了。