机器学习 - 第二周:多变量线性回归

2014-09-09 • 原理拾穗 • #machine learning #机器学习 #coursera #ml-006

好吧……我成功的把 coursera 的 Machine Learning 课完完全全的坑掉了…… Courera 都发信告诉我你快去 check 你的 Review questions 啦否则要不及格了!我才发现我已经完完全全的跟不上进度了哭瞎…… 估计我要跟着 ml-007 班才可能上完这门课了 QAQ 留级什么的……

于是这是多变量线性回归的总结。

多变量线性回归,其实亦是线性回归:其区别主要在于,参与回归的变量变多了,也就是要用多个因素来共同预测同一个结果。

模型表达

$${{\rm{h}}_\theta }(x) = {\theta _0} + {\theta _1}{x_1} + {\theta _2}{x_2} + ... + {\theta _n}{x_n}$$

——其实有 $$x_0$$ 的,只是它等于1,于是就没有 $$x_0$$ 了。

当然它可以用简洁大气的向量形式表示,如下:

$${{\rm{h}}_\theta }(x) = {\theta ^T}x$$

多变量的梯度下降算法

成本函数

$$J(\theta ) = {1 \over {2m}}\sum\limits_{i = 1}^m {{{({h_\theta }({x^{(i)}}) - {y^{(i)}})}^2}} $$

成本函数和单变量的并无本质区别,唯需注意:其中的 $$\theta$$ 和 $$x$$ 已经是向量。

梯度下降

梯度下降也并无区别;唯将同步更新的 $$\theta_0$$ 和 $$\theta_1$$ 变成了 $$\theta_0$$ 到 $$\theta_j$$ 而已。

特征缩放(Feature Scaling)

Google 上随便搜了下,没找着合适的中文翻译,暂且叫他特征缩放吧。其主要目的,则是让各个特征——即参与预测的参数,保持在同一个数量级上,以减少迭代的次数。

参与预测的各特征由于种种原因,其大小并不在同一个数量级上;也就是说,特征的单位往往不统一。而这会造成迭代上的问题——有的次要特征无法快速减小,以至于消耗大量的时间;而主要特征有可能无法快速上升从而达到最佳解。

而特征缩放,将所有的特征都限定在 $$ - 1 \le x \le 1$$ 的范围内,使特征都保持在同一个数量级。这样一来,梯度下降算法将会提升其效率,运行得更加快速干净。

一般我们采用如下算法:记特征的平均值为 $$\mu $$ ,而特征的范围(最大值减去最小值)为 $$s$$ ,则将以下的变量代替特征 $$x$$ 参与迭代:

$${{x - \mu } \over s}$$

学习速率(Learning rate, $$\alpha$$)

提到学习速率之前,需要先了解我们是如何处理梯度下降算法中的问题的——即,我们如何知道梯度下降算法是否正在朝我们想要的结果发展?

最简单明了的方式,就是画出迭代次数-成本函数的直角坐标系图。在每次迭代时,该图线持续下降——亦即,Jmin(t)是一个单调递减的函数。

那学习速率 $$\alpha$$ 又会以怎样的方式影响梯度下降算法呢?回到之前我们的梯度下降算法公式:

$$\theta_{j}:=\theta_{j}-\alpha\frac{\partial}{\partial\theta_{j}}J(\theta_{0},\theta_{1})$$

观察到,每次迭代都将下降$${\partial \over {\partial {\theta _j}}}J({\theta _0},{\theta _1})$$的$$\alpha$$倍。

显然,若是$$\alpha$$取值太小,则每次下降的步幅太小,则效率太低;若$$\alpha$$的取值太大,则将错过最低值而走向离最低值越来越远的道路——穿过最低值后,其导数值可能比上一次迭代更高,于是一次比一次跳跃得更远,也就无法取到最小值了。

选择学习速率$$\alpha$$的方法是凭经验:依次取0.001、0.003、0.01……1、3、10、……如此尝试。在尝试的过程中,可以首先将迭代次数降低;然后试着画出 Jmin-t 的图线;观察图线,然后根据图线调整 $$\alpha$$ 的值。

线性回归中的多次式

之前我们提到的线性回归预测,多是一次项比较多;但某些特征,对结果的影响可能不是一次的——可能是二次、三次甚至更甚。于是,可以用 $$x^2_i$$ 、 $$x^3_i$$ 来代替 $$x_i$$ 参与线性回归。

一般等式

可用下列等式直接求出线性回归时我们所求的 $$\theta$$ 值:

$$\theta = {({X^T}X)^{ - 1}}{X^T}{\rm{y}}$$

适用于样本数量 n 比较小的情况——因为 n 大了会比较慢。

当 $${X^T}X$$ 没有逆矩阵时,可以用 octave 的 pinv 方法求得伪逆——主要是特征太多的情况下会导致这种情况的发生。

根据天气和时间动态更换的博客背景图

2014-07-25 • 博客记录 • #博客 #背景 #天气api

之前博客的背景图是一个永恒的晴天……
因为一开始就是当成一个可以根据时间和天气轮换的设计,但是懒得去写 js ,于是就是如大家所见的那样。

刚刚闲得无聊,把整个逻辑补完了。用到的是新浪的天气 js 接口,然后用一组图片搞定了他们。

核心代码如下:

var t = Math.floor(((new Date()).getHours() + 6) / 5),
    f = 'bright' + t;
if (w.indexOf('雨')>=0 || w.indexOf('雪') >= 0) {
    f = 'rainy';
} else if (w.indexOf('云')>=0 || w.indexOf('阴') >= 0) {
    f = 'cloudy' + t;
}
if (document.body.clientWidth > 600) {
    document.body.style.backgroundImage = 'url("' + f + '.jpg")';
}

具体的效果,可以参考本博客背景,也可以移步演示页面

相应的几张背景图为原创,谢绝扒图。如需自用,请联系我。

机器学习 - 第一周:单变量线性回归

2014-07-13 • 原理拾穗 • #machine learning #机器学习 #coursera #ml-006

模型表达

单变量线性回归是一种最简单、最典型的监督式学习算法。我们将用于训练的数据称为“数据集”(dataset),则有以下几个定义:

  • $$m$$ 代表训练数据集的大小(即有多少个训练样本);
  • $$x$$ 代表模型的输入(即模型的输入特征数量);
  • $$y$$ 代表模型的输出(即最终预测的结果);
  • 我们用上标 $${^{(i)}}$$代表数据集中的第 i 组数据,如 $${x^{(i)}}$$ 代表数据集中第二个输出;
  • $$h$$ 则是我们用于预测的函数,具体表述如下:
    将学习算法通过训练数据集训练之后得出函数 $$h$$ 中的不确定参数项,此时 $$h$$ 为一确定的函数;
    将任意的输入 $$x$$ 代入 $$h$$ 中,即可得到对应的输出 $$y$$。
    在单变量线性回归中,我们使用的 $${h_\theta}(x)$$$${h_\theta }(x) = {\theta _0} + {\theta _1}x$$

成本函数

线性回归要达到的目标,即将上述 $${h_\theta}(x)$$ 函数中的 $$\theta$$ 求出。如何选择 $$\theta$$ 们就成了要解决的问题。

给出成本函数 $$J({\theta _0},{\theta _1}) = {1 \over {2m}}\sum\limits_{i = 1}^m {({h_\theta }({x^{(i)}}) - {y^{(i)}})} $$
此函数是线性预测函数 $$h$$ 与实际数据 $$y$$ 的误差的方差值。

则我们的问题就转化为了,如何求成本函数 $$J({\theta _0},{\theta _1})$$ 的最小值。

梯度下降法

选用一种算法,使 $$\theta$$ 从任意位置开始,都能下降到一个局部最小值。

梯度下降法的具体做法是,每次迭代都将 $$\theta$$ 减少一个值,这个值是学习速率 $$\alpha$$ 乘以成本函数 $$J$$ 对 $$\theta_i$$ 的偏导数。具体公式如下:

repeat until convergence {
$$\theta_{j}:=\theta_{j}-\alpha\frac{\partial}{\partial\theta_{j}}J(\theta_{0},\theta_{1})$$ (for j=0 and j=1)
}

** 值得注意的是,此处的 $$\theta$$ 的更新是同步的,亦即计算$$\theta_2$$时使用的$$\theta_1$$是本次更新前的值。**

由于每次更新变量 $$\theta$$ 都是减去一个和梯度线性相关的值,故此算法将会在迭代的过程中自行调整每次下降的幅度,
从而减小迭代次数。如当初始值很高,函数图像很陡峭时,此时梯度足够大,故下降的速率也足够大,
从而在单次的迭代中下降足够多的幅度。
而当 $$\theta$$ 已经位于最小值附近时,由于函数图像的缓和,梯度下降,故下降速率会自行变小。
这就是梯度下降算法的优越之处。

在单变量线性回归算法中,梯度下降算法中的偏导数部分可以求解如下:

$$${\theta _0}: = {\theta _0} - \alpha {1 \over m}\sum\limits_{i = 1}^m {({h_\theta }({x^{(i)}}) - {y^{(i)}})} $$$
$$${\theta _1}: = {\theta _1} - \alpha {1 \over m}\sum\limits_{i = 1}^m {({h_\theta }({x^{(i)}}) - {y^{(i)}})} \cdot {x^{(i)}}$$$

机器学习 - 简介

2014-07-12 • 原理拾穗 • #machine learning #机器学习 #coursera #ml-006

最近在 Coursera 上参加 Andrew Ng 的 Machine Learning 课程,感觉颇为良好, AN 的课讲得很是生动,也比较深入浅出,适合我这种低智商人群学习。

于是,这里是对其中的“Introduction”亦即“介绍”部分,做一个中文版的提纲,以便自己查阅。接下来的学习时间里,我也会整理出相应的提纲资料,一方面是为了自己查询方便(读中文还是比英文舒坦),另一方面也算是给后来者留下一个参考。想要阅读相关的其它文章的话,请点击 ml-006 这个标签。

注意,以下内容均为本人理解(除引用外),本人不对以下信息的准确性负任何责任。转载请参考右边 CC 协议。

定义

A computer program is said to learn from experience E with respect to some task T and some performance measure P, if its performance on T, as measured by P, improves with experience E.
---- Tom Mitchell (1998)

Google 找了一番,没有找到确切的翻译,我就随便翻译一下吧。

所谓机器学习,即能够完成任务 T 的程序,其自行从 E 中学习经验,用 P 来衡量其表现;且在 T 任务上的表现 P 能够由经验 E 来提升。

举个例子:

比如我们平常所用的 GMail 的 SPAM 判定,就是一个典型的机器学习程序:
任务T:判断一封邮件是否为 SPAM
经验E:用户对 SPAM 邮件的标记(“这是垃圾邮件”)
表现P:对垃圾邮件的识别率

模型形态

广为人知的,机器学习分为两种形态,即监督式学习和非监督式学习。两种模型形态的区别主要在于训练数据集。

监督式学习(Supervised learning)

Supervised learning is the machine learning task of inferring a function from labeled training data (via wikipeia).

监督式学习的数据集中的每个点都是“right answers”,即所有的测试数据都是已知的且有着正确的标记。例如预测房价的学习算法,其每个点都是明确已知的:500 ㎡ 的房子卖 $ 100,000 美元。

而监督式学习主要用于回归分析(Regression,即输出是连续的)和数据分类(Classification,输出是离散的)。

非监督式学习(Unsupervised learning)

In machine learning, the problem of unsupervised learning is that of trying to find hidden structure in unlabeled data (via wikipedia).

相对的,非监督式学习即一组没有经过标记的数据——我们只知道这些数据在那里,并不知道这些数据之间的联系、这些数据是什么。例如 Google News 的“相关新闻”聚类:我们只有海量的新闻数据,而并不知道其意味着什么。

非监督式学习主要应用于聚类分析(Cluster analysis)。

gulp.js - stream 驱动的自动构建工具

2014-06-30 • Web 万象 • #gulp #coffee #jade #stylus

最近心情不太好,开始有点自暴自弃。所幸能够写点代码分分心……

开始接触一些前端的项目,就尝试着用一些看起来很 cool 的技术/工具。初步选定的工具包括 jade 、 stylus 和 coffee 。

可是做预编译实在是一个很麻烦的事情,直到我遇见了 gulp.js

gulp.js 是一个简洁明快的自动构建工具,官方文档也异常简洁清晰。主打“stream”式构建,配置文件写起来也是不太复杂。这里针对自己用到的一些插件做简单的介绍。

gulp-concat 是一个将多个文件合并到一起的插件。

gulp-clean 用来清理目标目录中的文件。

gulp-stylusgulp-coffeegulp-jade用来预编译我的代码。

想要达到的效果是:

  1. 将一些静态文件复制到发布目录;
  2. 将框架的 css 与自己的 stylus 编译成的 css 合并;
  3. 将框架的 js 与自己的 coffee 合并;
  4. 将 jade 编译为 html 文件。

目录结构如下:

public/ 存放发布文件
src/ 存放源文件

实现思路:

清理发布目录

var clean = require('gulp-clean');
gulp.task('clean', function () {
    return gulp.src('public').pipe(clean());
});

几乎是最简单的任务。将发布目录整个的删掉。

gulp.src用于读取源文件,这里读取的是public目录;然后将其 pipe 到 gulp-clean 插件,将其清理掉。

处理静态文件

gulp.task('static', function () {
    gulp.src('src/img/**')
        .pipe(gulp.dest('public/img/'));
    gulp.src('src/fonts/**')
        .pipe(gulp.dest('public/fonts/'));
});

和上面的范例类似,只不过用的是gulp.dest,用于将 pipe 过来的文件发布到 public 目录。

注意这里的路径是 src/img/** ,表示对 img 目录下的文件进行递归处理。

构建 jade

var jade = require('gulp-jade');
gulp.task('html', function () {
    return gulp.src(['src/jade/**/*.jade', '!src/jade/partial/**'])
        .pipe(jade())
        .pipe(gulp.dest('public/'));
});

这里只是换用了gulp-jade插件而已。

需要注意的是,这次的路径传入进来的是个数组;数组会如同你想象的那样,将数组中的元素挨个匹配;!开头的表示去除src/jade/partial目录下的所有 *.jade 文件;**/*.jade则表示该目录下递归中所有的 jade 文件。

这里将 gulp 链 return 回去,是为了在处理依赖的时候,能够正确确定函数处理进度,以保证运行顺序。

合并 js 和 css

var concat = require('gulp-concat');
//合并 css
gulp.task('css', function () {
    return gulp.src('src/css/*.css')
        .pipe(concat('0.css'))
        .pipe(gulp.dest('.tmp/css/'));
});
//处理 js
gulp.task('js', function () {
    //加载jquery、modernizr、foundation、vue
    return gulp.src([
        'src/js/vendor/jquery.js',
        'src/js/vendor/modernizr.js',
        'src/js/foundation.min.js',
        'src/js/vendor/vue.min.js'
    ])
        .pipe(concat('0.js'))
        .pipe(gulp.dest('.tmp/js/'));
});

这里用到的是gulp-concat插件。它能把多个文件合并成为同一个。

这里将输出送到了 .tmp 目录下,是为了后续能够处理它。

编译 stylus 和 coffee

var stylus = require('gulp-stylus');
var coffee = require('gulp-coffee');
//stylus 编译
gulp.task('stylus', function () {
    return gulp.src('src/styl/**/*.styl')
        .pipe(stylus())
        .pipe(concat('1.css'))
        .pipe(gulp.dest('.tmp/css/'));
})
//处理 coffee
gulp.task('coffee', function () {
    return gulp.src('src/coffee/**/*.coffee')
        .pipe(coffee({
            bare: true
        }).on('error', gutil.log))
        .pipe(concat('1.js'))
        .pipe(gulp.dest('.tmp/js'));
});

这里依然将输出送到了 .tmp 下。接下来要将 .tmp 下的文件合并为真正能够使用的 css 和 js 文件。

合并临时 css 与 js 文件

gulp.task('concat-css', ['css', 'stylus'], function () {
    return gulp.src('.tmp/css/*')
        .pipe(concat('common.css'))
        .pipe(gulp.dest('public/css'));
});
gulp.task('concat-js', ['js', 'coffee'], function () {
    return gulp.src('.tmp/js/*')
        .pipe(concat('common.js'))
        .pipe(gulp.dest('public/js'));
});

这里用到的是 deps 也就是“依赖”语法。task 的第二个参数是一个数组。在执行这个任务时,会自动执行数组中的任务;并且,当且仅当数组中所有任务执行完毕后,才会执行后面的 function 中的任务。

这里的应用就是,在先合并好了框架的 css 和自己写的 stylus 编译完成之后,再将临时文件中的两组文件合并起来。

清理临时文件

gulp.task('concat-and-clean', ['concat-css', 'concat-js'], function () {
    return gulp.src('.tmp').pipe(clean());
});

这里则是用到了刚刚提到的 gulp=clean 插件和 deps 语法。

默认动作

gulp.task('default', ['concat-and-clean', 'html', 'static']);

在命令行下,执行 gulp 时,则会自动执行 default 任务。这里定义的 default 任务没有事实上的动作;它只是同时执行上面所述的几个任务,然后自己退出。

监视改动

gulp.task('watch', ['default'], function () {
    //js
    gulp.watch('src/coffee/**', ['concat-js']);
    //css
    gulp.watch('src/styl/**', ['concat-css']);
    //html
    gulp.watch('src/jade/**', ['html']);
    //静态
    gulp.watch('src/img/**', ['static']);
})

gulp.watch 能够监视指定文件的改动,并启动任务;也就是说,当src/coffee/中的文件改动时,gulp 会自动执行 concat-js 任务。

然后我们只需要在命令行中输入 gulp watch ,gulp 将会自动跑一轮 default 任务,然后监视我们的源文件;当发现文件变动时,将自动执行编译。

至此,我的 gulpfile.js 就编写完毕了。Enjoy it!

友情链接