开始

在一个普通的周末打开哔哩哔哩刷新动态的时候突然看到qsc投稿了视频的我是表示震惊的!当看到标题是机器学习算法讲堂的时候,我知道:是时候了,是时候开始学一点机器学习了,那么这也算是最近周末的常驻项目之一了.既然看了视频并且手推了一遍,肯定是要搞个大新闻的,那么就开个博客记录一下自己的笔记吧.

先来贴一下qsc视频的地址:
机器学习算法讲堂(2):线性回归推导
机器学习算法讲堂(2.1):最大似然与线性回归

线性回归

首先允许我再盗一张图:

这是一个非常经典的二维的线性回归的图片.我们的任务就是找到这样一条直线,使得这条直线到平面上所有的点的距离之和最小,写成函数就是这样:
$ f(x)=w_0+w_1x_1+w_2x_2+\cdots=w_nx_n = w^Tx, x\in\mathbb{R}^d,y\in\mathbb{R} $

判断这个函数的好坏我们就定义一个损失函数(loss function)如下(最小二乘法):
$ E=\frac {1} {N}\sum_{i=1}^N(f(x_i)-y_i)^2 $
解释:对每个$x_i$应用预测函数的值f($x_i$)减去数据集中的正确值的差再平方,求1…N所有误差的和再除以N取平均

定义W为使得E最小时的参数:W=argminE,然后我们可以把这个问题转化为求E的导数,找到E的导数为0的点就是E最小的点
先将E化为矩阵的表示形式(这里的x和y都是矩阵的形式):
$$
\begin{split}
E &= \begin{Vmatrix} Xw-y \end{Vmatrix}^2 \\
&=(Xw-y)^T(Xw-y)
\end{split}
$$

接下来对E求导(左导右不导加上右导左不导):
$$
\begin{split}
dE&= (Xdw)^T(Xw-y)+(Xw-y)^TXdw \\
&=2(Xw-y)^TXdw \\
\frac {dE^T} {dw}&=(2(Xw-y)^TX)^T \\
&=2X^T(Xw-y) \Rightarrow \nabla
\end{split}
$$

我们可以把$\nabla$当做是训练的梯度,当$2X^T(Xw-y)=0$的时候w可以取到最小,转换一下:
$w=(X^TX)^{-1}X^Ty$
当$X^TX$可逆的时候就可以直接求解,不可逆的时候就需要使用梯度下降的方法来训练使得E收敛到最小获得w的值.
梯度下降:$w(t+1)=w(t)-\lambda\nabla$,$\lambda$为梯度下降的步长.

最大似然

还是回到上面的图片中,在我们用函数来拟合这组数据的时候,因为无法完美的拟合数据,所以我们假设有我们没有观察到的因素存在导致我们的拟合函数存在误差,而这个拟合函数所缺失的不可解释的那部分误差就是直线到数据点之间的距离.
同时我们换个角度来看这个数据集,既然这些点被我们观测到了,就说明这些点被我们观测到的概率是最大的,由此可以推出,我们可以寻找一个函数,使得我们能够观测到这些点的概率最大,那我们也就找到了对些数据最好的拟合.

似然函数推导

假定存在一个数据集D=$\lbrace(x_1,y_1),(x_2,y_2),\cdots,(x_n,y_n)\rbrace$
我们定义函数$y_i=f(x_i)+\varepsilon_i,\varepsilon_i为不能完全解释的误差$
问题就转化成了:现在我们有N个f(x)函数,我们要评估哪个模型最有可能产生这一组数据,用概率来表示就是:
$P(D|x,\theta)=P(y_1,y_2,y_3,\cdots,y_N|x_1,x_2,x_3,\cdots,x_N,\theta)$
假设数据之间是相互独立的(概率相乘):
$P(y_1,y_2,y_3,\cdots,y_N|x_1,x_2,x_3,\cdots,x_N,\theta)=\prod_{j=1}^N P(y_j|x_j,\theta)$
我们的似然函数也就是:$likelihood(\theta)=\prod_{j=1}^N P(y_j|x_j,\theta)$
因为对于数据集D来说x,y已经确定所以可以将概率转换如下:
$P(y_i|x_i,\theta)=P(y_i|f(x_i)+E)=P(y_i-f(x_i)|E)$
可以看到:对于任意的x,y来说概率就和模型的预测值和实际值之间的误差E的分布相关了.

最大似然和线性回归

根据我们之前的定义:$y_i=f(x_i)+\varepsilon_i$ 我们可以推出:$\varepsilon_i=y_i-w^Tx_i$ (也就是前面线性回归推导中使用的误差)
我们假设Y服从均值为$w^Tx$ ,方差为1的分布:
$$
P(Y|x)=P(\varepsilon|x)=\frac {1} {\sqrt{2\pi}\times e^{\frac {-\varepsilon^2} {2}}}
$$
将上面的$\varepsilon_i=y_i-w^Tx_i$ 代入公式:
$$
P(Y|x)=P(\varepsilon|x)=\frac {1} {\sqrt{2\pi}\times e^{\frac {-(y_i-w^Tx_i)^2} {2}}}
$$
由于误差符合正态分布,我们将概率代入似然函数中并且对似然函数取log:
$$
likelihood(\theta)=\log \prod_{j=1}^N \frac {1} {\sqrt{2\pi}\times e^{\frac {-(y_i-w^Tx_i)^2} {2}}}
$$
化简得到:
$$
N\log(\frac {1} {\sqrt{2\pi}})+\frac {\sum_{i=1}^N-(y_i-w^Tx_i)^2} {2}
$$
去掉前面的常数项和后面的常系数之后可以化简为:
$$\sum_{i=1}^N-(y_i-w^Tx_i)^2$$
我们要使得概率最大也就是使得似然函数最大:
$$max\sum_{i=1}^N-(y_i-w^Tx_i)^2$$
也就是(去掉负号):
$$min\sum_{i=1}^N(y_i-w^Tx_i)^2$$
完成!是不是发现这和最小二乘法给出的公式(loss function)是一样的?因为最小二乘法是最大似然解法的一种特殊情况.
从最大似然解法的推导中我们可以看到:均方误差最小化求解线性回归和假设预测符合高斯分布然后利用最大似然估计求解线性回归得到的结果是相同的

总结

虽然是最简单的线性回归的推导,但是从概率的角度利用最大似然来推出loss function的那一刻还是感觉到:数学真炫酷啊~
之前看机器学习的时候感觉数学公式看到就很头疼,但是现在自己硬着头皮推一遍之后发现并没有那么复杂而且通过这种硬盒的方式来学机器学习的算法是获益匪浅.最后再说一句:qsc的视频我是期期都看啊~

需知

本文主要的内容是Hadoop-3.1.0安装Tez-0.9.1的坑和细节,详细的安装过程请参照官网的安装教程.

Hadoop生态的安装还是坑很多,很多时候安装配置之后能启动起来现在想想都还是运气好,个人感觉Hadoop生态最坑的还是各个框架之间的版本配合问题了,因为看文档说Hadoop升级到3之后性能提升了很多,也就在正式环境上部署了Hadoop-3.1.0的版本,结果在尝试更换MR为Tez的时候还是遇到了
坑.**最主要的就是:**安装各个软件的时候一定要看好版本.

安装

因为Hadoop的版本为3.1.0,所以一定一定要选择0.9.1版本的Tez,因为在0.9.1版本的org.apache.tez.runtime.api的InputInitializerContext接口中才有_addCounters(TezCounters tezCounters)_这个方法,不然在启动Tez的时候会报NoSuchMethodError这个错误(个人感觉这个巨坑,0.9.0版本的API中就没有这个方法).Tez-0.9.1版本-org.apache.tez.runtime.api

下载

所以Hadoop3.1.0个人现在推荐下载0.9.1版本的包,更老版本的Tez在其他地方也出过一些问题,最后选择了0.9.1版本成功安装了.

Tez-0.9.1官网下载

下载时需要下载源码自己进行编译,因为官网的编译好的bin包默认的_Hadoop版本_是2.6的,安装时参照官网的安装教程

安装protobuf-2.5.0

在源码编译的时候,需要安装maven和protobuf(Tez依赖),在pom.xml中可以看到Hadoop version和protobuf版本的需求,protobuf version:2.5.0.

1
2
3
4
5
6
7
8
9
10
11
12
# 下载
wget https://github.com/google/protobuf/releases/download/v2.5.0/protobuf-2.5.0.tar.gz
# 解压
tar -xzvf ./protobuf-2.5.0.tar.gz
# 编译及安装
cd protobuf-2.5.0
# --prefix为安装位置
./configure --prefix=/usr/local/protobuf
make
make check
# make install时要使用root账户,否则会因为权限的问题报错
sudo make install

修改pom.xml

在tez目录下修改pom.xml,首先将hadoop version改为当前运行的Hadoop版本.

_(可选):_因为tez-ui依赖node.js以及bower, bower install时间过久而且报错,可以在pom.xml中搜索tez-ui模块并且注释掉.

安装node.js以及bower

1
2
3
4
5
6
7
# 环境为centOS,其他linux版本请修改命令
rpm -ivh http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-remi
yum -y install nodejs npm --enablerepo=epel
npm install -g bower

开始编译

mvn clean package -DskipTests=true -Dmaven.javadoc.skip=true -Phadoop28 -Dhadoop.version=3.1.0

注意:一定要带版本 不然编译出错

package结束之后,文件在tez-dict/target目录中,将_tez-0.9.1-minimal.tar.gz_解压之后就是Tez的本体了,可以cp出来放到软件目录下,然后将_tez-0.9.1.tar.gz_放到Hadoop的HDFS中,接下来的配置_tez.lib.uris_在官网的教程和其他的安装教程中都可以找到,这里就不再赘述了.

总结

版本问题很重要!版本问题很重要!版本问题很重要!,在历次的Hadoop的坑中都是被版本所困扰,在选择Hadoop和其他的软件的时候一定要先考虑清楚版本的兼容性再开始安装,在兼容正确的情况下安装是非常非常的轻松的,但是一旦兼容出了问题,各种问题就会从各个地方出现.(血泪教训)

安装过程中报错调试常用的技巧

hive.log是最常用的查看错误的地方,但是在这次的安装中也查看了yarn的报错信息,主要是因为yarn节点没有能够正常启动起来导致报错信息不会向上抛出,直接使用yarn命名查看log会发现没有对应的错误信息,但是在Hadoop的log文件下面还是找到了yarn启动时的log

tail -f /tmp/{username}/hive.log

yarn报错之后信息在hadoop/logs/userlog下面的app中的container中的syslog文件中
因为启动时失败,直接使用yarn查看log是看不到错误信息的

前言

实习之后感觉节奏和学校还是有些变化的,不知不觉俩月也过去了,SICP却才开始第二章的总结,需要督促自己了.

第二章主要是:

  • 复杂数据的抽象
  • 数据导向编程
  • 数据类型闭包
  • 符号表达式处理(引用)

现在到了数学抽象中最关键的一步:让我们忘记这些符号所表示的对象.
(数学家)不应该在这里停步,有许多操作可以应用于这些符号,而根本不用考虑它们到底代表着什么东西.
                               ——Hermann Weyl,思维的数学方式

总结

数据抽象

  • 数据抽象可以使我们从构造对象的细节中脱离出来而使用一些基本的数据对象来完成对复杂对象的操作.
  • 数据抽象的基本观点就是使用抽象的数据来构造复杂的数据对象.
  • 抽象数据之间的接口通过_selectors_和_constructors_来实现
  • 构造抽象数据的一个有力的方法就是_closure_(闭包)

Pairs(序对)

pairs可以说是Lisp中所有的数据结构的基石了,而Lisp中的pairs相比C++或者Java有着更强大的能力,因为它是closure的,可以创造出无限复杂的数据结构出来.更重要的一点是,从pairs出发,_数据和过程的边界_也变得越来越模糊了,在SICP课程中教授也是从pairs出发,模糊了数据和过程的边界.

从虚无中创造出数据

这是教授上课时讲的,我们可以从虚无中创造出数据,例子如下(pairs):

  1. 首先定义CONS过程的一种实现:
    1
    2
    3
    4
    5
    6
    (define (cons a b)    
    (lamdba (pick)
    (cond ((= pick 1) a)
    ((= pick 2) b))))
    (define (car x) (x 1))
    (define (cdr x) (x 2))
  2. 对一个例子使用代换模型:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    (car (cons 37 49))
    ==>(car (lamdba (pick)
    (cond ((= pick 1) 37)
    ((= pick 2) 49))))
    ==>((lamdba (pick)
    (cond ((= pick 1) 37)
    ((= pick 2) 49)) 1) 1)
    ==>(cond((= 1 1) 37)
    ((= 1 2) 49))
    ==>37

当时看到这里的时候,真的是深深被Lisp的数据和过程的理念所折服了,我们真的是从虚无中创造了数据,我们从纯过程的代换模型中成功的得到了pair的值,amazing!

抽象屏障

在我们实现每一层的数据抽象的时候,我们应该使其形成一层屏障,也就是要保证对于这一层级的数据的操作都被封锁在这一层中,不能向上也不能向下扩散,对外只能提供select和construct的接口.
在课程中教授也说到这可以视作是控制去中心化的思想,也就是每一层都来管理自己的东西,没有一个总的控制或者调度数据的地方.主要的优势是:

  1. 屏蔽了每个抽象数据类型的计算细节
  2. 对外提供的统一的接口可供调用
  3. 抽象不仅可以做水平的,也可以做每一层中不同实现的抽象,方便不同的实现方法.

消息传递vs.数据导向

这里主要讲到的是消息传递这种方式,但是结合后面的数据导向不免要做一番比较就写在这里了吧.

  • 消息传递举了一种消息传递例子是:基于类型的派分.个人的感觉是消息传递就是给出一张信息表,每次调用的时候都去查表找到对应的操作交由它来完成功能并且返回结果.例子中讲到基于类型的派分也就是这样了(面向对象中常用的方法).缺陷是每次新加入表的一列(操作),之前的每一个类型都需要修改代码来实现这个操作,阻碍了系统拓展的灵活性.
  • 数据导向数据导向更像是做出一个足够强大的类型,能够使其适应各种可能的操作,然后把它们组装在一起,在后面的例子通用运算包中也就是抽象了各种运算符(我觉得主要也是因为FP的特性,使得这种抽象很容易做到所以数据导向才会是一种更好的方式),然后只要将其组装在一起就可以了,在新增类型的时候,只要把类型和对应的操作实现,就可以直接”安装”在原有的系统中,而且原有的系统不需要修改一行代码就可以适应这种变化(课程中说到这是”免费”获得新的能力).

分层数据及其闭包性质

主要是讲了怎么使用闭包的性质来制造一些常用的数据结构(数据的组织和对应的操作方法).主要包括:

  • Sequence(序列)
  • List,Mapping list
  • Tree(包括遍历和叶子节点概念),Mapping tree

  

用序列来连接传统的方法

主要是讲了怎么用模块连接的方式来解决问题.举例是遍历树的为奇数值的节点并且求平方之和和偶数的Fibnacci序列,在传统我们解决这类问题的时候都是把判断,筛选,组织结果的东西都放在一起,因为这样效率更高,但是一旦需求改变,这样的地方就需要全部重写,不仅拓展性差而且也不可复用.所以书中提到了流处理这种方式,就是把数据看做是流动的,从一系列的模块中数据流过去,自然而且就被过滤,选择并且获得了结果.

Ex.1:为奇数的树节点求和平方

  1. 枚举每一个节点
  2. 过滤,选择其中的奇数的节点
  3. 对2的所有数字分别平方
  4. 从0开始累积结果(求和)

Ex.2:偶数Fibnacci

  1. 从0开始枚举n
  2. 计算每一个n的Fibnacci值
  3. 过滤,选择其中的偶数
  4. 从0开始累积结果(求和)

我们可以看到这2个过程之间是有很多的相同的地方的,如果可以提取的话就可以复用而且增加拓展性.书中给出的抽象的方法就是流处理.大致的过程是:

  1. 产生数据的数据源
  2. 中间使用的各种过滤方法
  3. 最后对产生的结果进行累积的方法

可以发现经过这样的抽象之后,我们可以很轻松的拓展和修改上面的两个例子.比如:把过滤奇数的节点换成过滤偶数的,或者任意的规则,在累积结果时,也可以选择各种策略.相比把代码聚拢在一块里面的处理方式来说,将过程拆分开,变成处理数据流的方式确实更好.流处理数据的方式现在也被更多的语言和技术看重,Java甚至也可以直接使用streaming了.

例子:一个图形语言

主要就是使用前面讲到的类型的抽象和屏障对递归式的生成图片的方法进行了抽象
图形语言
感觉还是十分的有趣的,可能是因为(人类的本质是复读机吧),还是挺喜欢看这种重复的图案构成花纹的感觉的.

符号语言

主要是讲了Lisp中的‘引用,举了使用引用来实现一个简单的微积分系统的例子和SET集合的例子.(引用主要是解决了一个哲学问题,就是用来区分引用和直接使用,从这里延伸下去又是一场哲学的战斗).

Huffman编码树

Huffman树感觉是这一节比较重要的例子就单独拿来出来,可以看到第二章还是贯穿了很多的数据结构的东西在里面的.

还是盗一张图吧,懒得自己搞图床了:)

在看Huffman编码树的时候又去看了一遍刘末鹏的知其所以然(三):为什么算法这么难?,也是有了更深的理解(打算做一个总结,不过还没想好怎么写).

抽象数据的多重表示

主要还是举例子,复数的表示及其运算的表示,主要的思想在于结合了使用直角坐标系和极坐标系各自的优势,可以使得在处理复数的时候发挥不同的实现方式各自的优势,也就是之前提到过的抽象层中的竖直抽象层.

数据导向编程和可加性

主要还是对复数包功能的拓展和对数据导向编程和消息传递的比较.下面给出一个总结:

  • 显式分派: 这种策略在增加新操作时需要使用者避免命名冲突,而且每当增加新类型时,所有通用操作都需要做相应的改动,这种策略不具有可加性,因此无论是增加新操作还是增加新类型,这种策略都不适合.
  • 数据导向:数据导向可以很方便地通过包机制增加新类型和新的通用操作,因此无论是增加新类型还是增加新操作,这种策略都很适合.
  • 消息传递:消息传递将数据对象和数据对象所需的操作整合在一起,因此它可以很方便地增加新类型,但是这种策略不适合增加新操作,因为每次为某个数据对象增加新操作之后,这个数据对象已有的实例全部都要重新实例化才能使用新操作.

通用的算术运算包

继续盗图:

核心在于各种的抽象,抽象操作,抽象类型,做成分层的.当时看视频印象最深刻的就是当教授说当我们把所有的计算中的+-*/都换成抽象过程的时候,我们就可以免费的获得强大的拓展能力的时候,真的是_感觉很炫酷_,又_令人印象深刻_:不仅可以直接处理多项式的加法,而且多项式支持前面所有以及包含的有理数,复数,自然数的操作.由于多项式将自己注册入表中,我们甚至可以直接处理多项式的多项式.

总结

第二章的阅读进度不是很如意,感觉还是太拖沓了一点(TI看比赛也耽误了一个双休),总的来说第二章还是以大量的例子和习题为主,虽然感觉贯穿的知识点就那么几个,但是综合在一起之后所表现出来的强大的能力还是很令人震撼的,最起码可以实现一个简易的计算器了吧.感觉实习之后确实时间的分配不像学校那么的轻松了,但是能够每天还有自学的时间还算是很不错的了吧.

吾常终日而思矣,不如须臾之所学也.

0%