自己动手做聊天机器人 二十八-脑洞大开:基于美剧字幕的聊天语料库建设方案

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

美剧字幕

是的,你没有看错,我就是这样获取海量高质聊天语料的。外文电影或电视剧的字幕文件是一个天然的聊天语料,尤其是对话比较多的美剧最佳。为了能下载大量美剧字幕,我打算抓取字幕库网站www.zimuku.net,当然你也可以选择其他网站抓取。

 

自动抓取字幕

有关爬虫相关内容请见我的另一篇文章《教你成为全栈工程师(Full Stack Developer) 三十-十分钟掌握最强大的python爬虫》。在这里我直接贴上我的抓取器重要代码(代码共享在了https://github.com/lcdevelop/ChatBotCourse):

# coding:utf-8
import sys
reload(sys)
sys.setdefaultencoding( "utf-8" )
import scrapy
from w3lib.html import remove_tags
from subtitle_crawler.items import SubtitleCrawlerItem
class SubTitleSpider(scrapy.Spider):
    name = "subtitle"
    allowed_domains = ["zimuku.net"]
    start_urls = [
            "http://www.zimuku.net/search?q=&t=onlyst&ad=1&p=20",
            "http://www.zimuku.net/search?q=&t=onlyst&ad=1&p=21",
            "http://www.zimuku.net/search?q=&t=onlyst&ad=1&p=22",
    ]
    def parse(self, response):
        hrefs = response.selector.xpath('//div[contains(@class, "persub")]/h1/a/@href').extract()
        for href in hrefs:
            url = response.urljoin(href)
            request = scrapy.Request(url, callback=self.parse_detail)
            yield request
    def parse_detail(self, response):
        url = response.selector.xpath('//li[contains(@class, "dlsub")]/div/a/@href').extract()[0]
        print "processing: ", url
        request = scrapy.Request(url, callback=self.parse_file)
        yield request
    def parse_file(self, response):
        body = response.body
        item = SubtitleCrawlerItem()
        item['url'] = response.url
        item['body'] = body
        return item

下面是pipeline.py代码:

class SubtitleCrawlerPipeline(object):
    def process_item(self, item, spider):
        url = item['url']
        file_name = url.replace('/','_').replace(':','_')
        fp = open('result/'+file_name, 'w')
        fp.write(item['body'])
        fp.close()
        return item

看下我抓取的最终效果

[root@centos:~/Developer/ChatBotCourse/subtitle $] ls result/|head -1
http___shooter.zimuku.net_download_265300_Hick.2011.720p.BluRay.x264.YIFY.rar
[root@centos:~/Developer/ChatBotCourse/subtitle $] ls result/|wc -l
82575
[root@centos:~/Developer/ChatBotCourse/subtitle $] du -hs result/
16G   	result/

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

 

字幕文件的解压方法

linux下怎么解压zip文件

直接执行unzip file.zip即可

 

linux下怎么解压rar文件

http://www.rarlab.com/download.htm

wget http://www.rarlab.com/rar/rarlinux-x64-5.4.0.tar.gz

tar zxvf rarlinux-x64-5.4.0.tar.gz

./rar/unrar试试

解压命令:

unrar x file.rar

 

linux下怎么解压7z文件

http://downloads.sourceforge.net/project/p7zip下载源文件,解压后执行make编译后bin/7za可用,用法

bin/7za x file.7z

 

最终字幕的处理方式

有关解压出来的文本字幕文件的处理,我后面的文章会详细讲解如何分词、如何组合,敬请期待。

自己动手做聊天机器人 二十七-用深度学习来做自动问答的一般方法

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

语料库的获取方法

对于一个范问答系统,一般我们从互联网上收集语料信息,比如百度、谷歌等,用这些结果构建问答对组成的语料库。然后把这些语料库分成多个部分:训练集、开发集、测试集

问答系统训练其实是训练一个怎么在一堆答案里找到一个正确答案的模型,那么为了让样本更有效,在训练过程中我们不把所有答案都放到一个向量空间中,而是对他们做个分组,首先,我们在语料库里采集样本,收集每一个问题对应的500个答案集合,其中这500个里面有正向的样本,也会随机选一些负向样本放里面,这样就能突出这个正向样本的作用了

 

基于CNN的系统设计

CNN的三个优点:sparse interaction(稀疏的交互),parameter sharing(参数共享),equivalent respresentation(等价表示)。正是由于这三方面的优点,才更适合于自动问答系统中的答案选择模型的训练。

我们设计卷积公式表示如下(不了解卷积的含义请见《机器学习教程 十五-细解卷积神经网络》):

假设每个词用三维向量表示,左边是4个词,右边是卷积矩阵,那么得到输出为:

如果基于这个结果做1-MaxPool池化,那么就取o中的最大值

 

通用的训练方法

训练时获取问题的词向量Vq(这里面词向量可以使用google的word2vec来训练,有关word2vec的内容可以看《自己动手做聊天机器人 二十五-google的文本挖掘深度学习工具word2vec的实现原理》),和一个正向答案的词向量Va+,和一个负向答案的词向量Va-, 然后比较问题和这两个答案的相似度,两个相似度的差值如果大于一个阈值m就用来更新模型参数,然后继续在候选池里选答案,小于m就不更新模型,即优化函数为:

参数更新方式和其他卷积神经网络方式相同,都是梯度下降、链式求导

对于测试数据,计算问题和候选答案的cos距离,相似度最大的那个就是正确答案的预测

 

神经网络结构设计

以下是六种结构设计,解释一下,其中HL表示hide layer隐藏层,它的激活函数设计成z = tanh(Wx+B),CNN是卷积层,P是池化层,池化步长为1,T是tanh层,P+T的输出是向量表示,最终的输出是两个向量的cos相似度

图中HL或CNN连起来的表示他们共享相同的权重。CNN的输出是几维的取决于做多少个卷积特征,如果有4个卷积,那么结果就是4*3的矩阵(这里面的3在下一步被池化后就变成1维了)

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

以上结构的效果在论文《Applying Deep Learning To Answer Selection- A Study And An Open Task》中有详细说明,这里不赘述

 

总结

要把深度学习运用到聊天机器人中,关键在于以下几点:

1. 对几种神经网络结构的选择、组合、优化

2. 因为是有关自然语言处理,所以少不了能让机器识别的词向量

3. 当涉及到相似或匹配关系时要考虑相似度计算,典型的方法是cos距离

4. 如果需求涉及到文本序列的全局信息就用CNN或LSTM

5. 当精度不高时可以加层

6. 当计算量过大时别忘了参数共享和池化

 

自己动手做聊天机器人 二十六-图解递归神经网络(RNN)

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

递归神经网络

递归神经网络RNN)是两种人工神经网络的总称。一种是时间递归神经网络(recurrent neural network),另一种是结构递归神经网络(recursive neural network)。时间递归神经网络的神经元间连接构成有向图,而结构递归神经网络利用相似的神经网络结构递归构造更为复杂的深度网络。两者训练的算法不同,但属于同一算法变体(百度百科)。本节我们重点介绍时间递归神经网络,下面提到RNN特指时间递归神经网络。

 

时间递归神经网络

传统的神经网络叫做FNN(Feed-Forward Neural Networks),也就是前向反馈神经网络,有关传统神经网络的介绍请见《机器学习教程 十二-神经网络模型的原理》,RNN是在此基础上引入了定向循环,也就是已神经元为节点组成的图中存在有向的环,这种神经网络可以表达某些前后关联关系,事实上,真正的生物神经元之间也是存在这种环形信息传播的,RNN也是神经网络向真实生物神经网络靠近的一个进步。一个典型的RNN是这样的:

图中隐藏层中的节点之间构成了全连接,也就是一个隐藏层节点的输出可以作为另一个隐藏层节点甚至它自己的输入

这种结构可以抽象成:

其中U、V、W都是变换概率矩阵,x是输入,o是输出

比较容易看出RNN的关键是隐藏层,因为隐藏层能够捕捉到序列的信息,也就是一种记忆的能力

在RNN中U、V、W的参数都是共享的,也就是只需要关注每一步都在做相同的事情,只是输入不同,这样来降低参数个数和计算量

RNN在NLP中的应用比较多,因为语言模型就是在已知已经出现的词的情况下预测下一个词的概率的,这正是一个有时序的模型,下一个词的出现取决于前几个词,刚好对应着RNN中隐藏层之间的内部连接

 

RNN的训练方法

RNN的训练方法和传统神经网络一样,都是使用BP误差反向传播算法来更新和训练参数。

因为从输入到最终的输出中间经过了几步是不确定的,因此为了计算方便,我们利用时序的方式来做前向计算,我们假设x表示输入值,s表示输入x经过U矩阵变换后的值,h表示隐藏层的激活值,o表示输出层的值, f表示隐藏层的激活函数,g表示输出层的激活函数:

当t=0时,输入为x0, 隐藏层为h0

当t=1时,输入为x1, s1 = Ux1+Wh0, h1 = f(s1), o1 = g(Vh1)

当t=2时,s2 = Ux2+Wh1, h2 = f(s2), o2 = g(Vh2)

以此类推,st = Uxt + Wh(t-1), ht = f(st), ot = g(Vht)

这里面h=f(现有的输入+过去记忆总结)是对RNN的记忆能力的全然体现

通过这样的前向推导,我们是不是可以对RNN的结构做一个展开,成如下的样子:

这样从时序上来看更直观明了

下面就是反向修正参数的过程了,每一步输出o和实际的o值总会有误差,和传统神经网络反向更新的方法一样,用误差来反向推导,利用链式求导求出每层的梯度,从而更新参数,反向推导过程中我们还是把神经网络结构看成展开后的样子:

根据链式求导法则,得出隐藏层的残差计算公式为:

因此W和U的梯度就是:

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

 

LSTM(Long Short Tem Momery networks)

特别讲解一下LSTM是因为LSTM是一种特别的RNN,它是RNN能得到成功应用的关键,当下非常流行。RNN存在一个长序列依赖(Long-Term Dependencies)的问题:下一个词的出现概率和非常久远的之前的词有关,但考虑到计算量的问题,我们会对依赖的长度做限制,LSTM很好的解决了这个问题,因为它专门为此而设计。

借用http://colah.github.io/posts/2015-08-Understanding-LSTMs/中经典的几张图来说明下,第一张图是传统RNN的另一种形式的示意图,它只包含一个隐藏层,以tanh为激发函数,这里面的“记忆”体现在t的滑动窗口上,也就是有多少个t就有多少记忆,如下图

 

那么我们看LSTM的设计,如下,这里面有一些符号,其中黄色方框是神经网络层(意味着有权重系数和激活函数,σ表示sigmoid激活函数,tanh表示tanh激活函数),粉红圆圈表示矩阵运算(矩阵乘或矩阵加)

这里需要分部分来说,下面这部分是一个历史信息的传递和记忆,其中粉红×是就像一个能调大小的阀门(乘以一个0到1之间的系数),下面的第一个sigmoid层计算输出0到1之间的系数,作用到粉红×门上,这个操作表达上一阶段传递过来的记忆保留多少,忘掉多少

其中的sigmoid公式如下:

可以看出忘掉记忆多少取决于上一隐藏层的输出h{t-1}和本层的输入x{t}

下面这部分是由上一层的输出h{t-1}和本层的输入x{t}得出的新信息,存到记忆中:

其中包括计算输出值Ct部分的tanh神经元和计算比例系数的sigmoid神经元(这里面既存在sigmoid又存在tanh原因在于sigmoid取值范围是[0,1]天然作为比例系数,而tanh取值范围是[-1,1]可以作为一个输出值)。其中i{t}和Ct计算公式如下:

那么Ct输出就是:

下面部分是隐藏层输出h的计算部分,它考虑了当前拥有的全部信息(上一时序隐藏层的输出、本层的输入x和当前整体的记忆信息),其中本单元状态部分C通过tanh激活并做一个过滤(上一时序输出值和当前输入值通过sigmoid激活后的系数)

计算公式如下:

 

LSTM非常适合在NLP领域应用,比如一句话出现的词可以认为是不同时序的输入x,而在某一时间t出现词A的概率可以通过LSTM计算,因为词A出现的概率是取决于前面出现过的词的,但取决于前面多少个词是不确定的,这正是LSTM所做的存储着记忆信息C,使得能够得出较接近的概率。

 

总结

RNN就是这样一种神经网络,它让隐藏层自身之间存在有向环,从而更接近生物神经网络,也具有了存储记忆的能力,而LSTM作为RNN中更有实用价值的一种,通过它特殊的结构设计实现了永久记忆留存,更适合于NLP,这也为将深度学习应用到自然语言处理开了先河,有记忆是给聊天机器人赋予智能的前提,这也为我们的聊天机器人奠定了实践基础。

机器学习教程 十七-逻辑回归公式的数学推导

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

逻辑回归中的数学推导

逻辑回归模型是基于这样的逻辑分布得出的模型

F(x) = 1/(1+e^x)

由此也得出了二项逻辑回归分布是:

P(Y=1|x) = e^(wx+b)/(1+e^(wx+b))

P(Y=0|x) = 1/(1+e^(wx+b))

也得出了多项逻辑回归分布是:

P(Y=k|x) =  e^(wx)/(1+∑e^(wx))

那么这个 1/(1+e^x)到底是怎么来的呢?我们来证明这一公式

首先假设0、1分布当Y=1的概率为

P(Y=1) = φ

那么

P(Y=0) = 1-φ

把他们变成统一的形式可以是:

P(y; φ) = φ^y (1-φ)^(1-y)

解释一下,这里如果y=0,那么前一项是1,就是p(y;φ) = 1-φ,而如果y=1,那么后一项就为1,就是p(y;φ) = φ

下面继续推导,我们知道有一个等式:a = e^(ln a)

那么把右面改成指数形式如下:

P(y; φ) = φ^y (1-φ)^(1-y) = e^(log(φ^y (1-φ)^(1-y))) = e ^ (y logφ + (1-y) log(1-φ)) = e^(log(φ/(1-φ))y+log(1-φ))

因为任何分布都能写成一种指数形式的分布:

p(y; η) = b(y) e^(ηT(η) - a(η))

这里面我们按照对应关系得出η=log(φ/(1-φ)),那么φ/(1-φ) = e^η,那么解出φ = 1/(1+e^(-η))

所以得出P(Y=1) = φ =  1/(1+e^(-η))

大功告成,终于知道逻辑回归公式是怎么来的了

教你成为全栈工程师(Full Stack Developer) 四十六-利用yarn多队列实现hadoop资源隔离

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

CapacityScheduler

使用过第一代hadoop的同学应该比较熟悉mapred.job.map.capacity/mapred.job.reduce.capacity这个参数,无论是map还是reduce都可以配置capacity(也就是并发数),表示同时可以有多少个map(或reduce)运行,通过这个参数可以限制一个任务同时占用的资源(节点)数,这样不至于影响其他任务的执行。

在这里有人会问:我把任务的priority设置成VERY LOW不就行了吗?其实这样在某些场景下不能解决全部问题,因为假如你一个VERY LOW的任务刚启动时没有其他人的任务,那么会先占用所有节点,如果你的每一个task运行时间都是1天,那么其他任务就算优先级再高也只能傻等一天,所以才有必要做资源隔离

第二代hadoop因为使用yarn做资源管理,没有了槽位的概念,所以就没有了capacity。但是在yarn中专门有了CapacityScheduler这个组件。这是一个可插装的调度器,它的用途就是对多用户实现共享大集群并对每个用户资源占用做控制

对于很豪的公司来说,每个用户(团队)自己有一个hadoop集群,这样可以提高自身的稳定性和资源供应,但是确降低了资源利用率,因为很多集群大多数时间都是空闲的。CapacityScheduler能实现这样的功能:每个组固定享有集群里的一部分资源,保证低保,同时如果这个固定的资源空闲,那么可以提供给其他组来抢占,但是一旦这些资源的固定使用者要用,那么立即释放给它使用。这种机制在实现上是通过queue(队列)来实现的。当然CapacityScheduler还支持子队列(sub-queue),

 

hadoop资源分配的默认配置

我在《教你成为全栈工程师(Full Stack Developer) 四十五-一文读懂hadoop、hbase、hive、spark分布式系统架构》中详细描述了整体一套hadoop搭建的方法。那么在搭建完成后我们发现对于资源分配方面,yarn的默认配置是这样的

也就是有一个默认的队列

事实上,是否使用CapacityScheduler组件是可以配置的,但是默认配置就是这个CapacityScheduler,如果想显式配置需要修改conf/yarn-site.xml内容如下:

<property>
<name>yarn.resourcemanager.scheduler.class</name>
<value>
org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler
</value>
</property>

上面图中标明了默认队列是default,是使用了CapacityScheduler的默认配置

我们看一下有关这里的default是怎么配置的,见capacity-scheduler.xml配置:

  <property>
    <name>yarn.scheduler.capacity.root.queues</name>
    <value>default</value>
    <description>
      The queues at the this level (root is the root queue).
    </description>
  </property>

这里的配置项格式应该是yarn.scheduler.capacity.<queue-path>.queues,也就是这里的root是一个queue-path,因为这里配置了value是default,所以root这个queue-path只有一个队列叫做default,那么有关default的具体配置都是形如下的配置项:

yarn.scheduler.capacity.root.default.capacity:一个百分比的值,表示占用整个集群的百分之多少比例的资源,这个queue-path下所有的capacity之和是100

yarn.scheduler.capacity.root.default.user-limit-factor:每个用户的低保百分比,比如设置为1,则表示无论有多少用户在跑任务,每个用户占用资源最低不会少于1%的资源

yarn.scheduler.capacity.root.default.maximum-capacity:弹性设置,最大时占用多少比例资源

yarn.scheduler.capacity.root.default.state:队列状态,可以是RUNNING或STOPPED

yarn.scheduler.capacity.root.default.acl_submit_applications:哪些用户或用户组可以提交人物

yarn.scheduler.capacity.root.default.acl_administer_queue:哪些用户或用户组可以管理队列

当然我们可以继续以root.default为queue-path创建他的子队列,比如:

  <property>
    <name>yarn.scheduler.capacity.root.default.queues</name>
    <value>a,b,c</value>
    <description>
      The queues at the this level (root is the root queue).
    </description>
  </property>

这是一个树结构,一般和公司的组织架构有关

配置好上述配置后执行

yarn rmadmin -refreshQueues

生效后发现yarn队列情况类似下面的样子(配置了两个队列:research和default):

如果希望自己的任务调度到research队列,只需在启动任务时指定:mapreduce.job.queuename参数为research即可

自己动手做聊天机器人 二十五-google的文本挖掘深度学习工具word2vec的实现原理

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

你是如何记住一款车的

问你这样一个问题:如果你大脑有很多记忆单元,让你记住一款白色奥迪Q7运动型轿车,你会用几个记忆单元?你也许会用一个记忆单元,因为这样最节省你的大脑。那么我们再让你记住一款小型灰色雷克萨斯,你会怎么办?显然你会用另外一个记忆单元来记住它。那么如果让你记住所有的车,你要耗费的记忆单元就不再是那么少了,这种表示方法叫做localist representation。这时你可能会换另外一种思路:我们用几个记忆单元来分别识别大小、颜色、品牌等基础信息,这样通过这几个记忆单元的输出,我们就可以表示出所有的车型了。这种表示方法叫做distributed representation,词向量就是一种用distributed representation表示的向量

 

localist representation与distributed representation

localist representation中文释义是稀疏表达,典型的案例就是one hot vector,也就是这样的一种向量表示:

[1, 0, 0, 0, 0, 0……]表示成年男子

[0, 1, 0, 0, 0, 0……]表示成年女子

[0, 0, 1, 0, 0, 0……]表示老爷爷

[0, 0, 0, 1, 0, 0……]表示老奶奶

[0, 0, 0, 0, 1, 0……]表示男婴

[0, 0, 0, 0, 0, 1……]表示女婴

……

每一类型用向量中的一维来表示

 

而distributed representation中文释义是分布式表达,上面的表达方式可以改成:

性别 老年 成年 婴儿

[1,       0,      1,      0]表示成年男子

[0,       0,      1,      0]表示成年女子

[1,       1,      0,      0]表示老爷爷

[0,       1,      0,      0]表示老奶奶

[1,       0,      0,      1]表示男婴

[0,       0,      0,      1]表示女婴

如果我们想表达男童和女童,只需要增加一个特征维度即可

 

word embedding

翻译成中文叫做词嵌入,这里的embedding来源于范畴论,在范畴论中称为morphism(态射),态射表示两个数学结构中保持结构的一种过程抽象,比如“函数”、“映射”,他们都是表示一个域和另一个域之间的某种关系。

范畴论中的嵌入(态射)是要保持结构的,而word embedding表示的是一种“降维”的嵌入,通过降维避免维度灾难,降低计算复杂度,从而更易于在深度学习中应用。

理解了distributed representation和word embedding的概念,我们就初步了解了word2vec的本质,它其实是通过distributed representation的表达方式来表示词,而且通过降维的word embedding来减少计算量的一种方法

 

word2vec中的神经网络

word2vec中做训练主要使用的是神经概率语言模型,这需要掌握一些基础知识,否则下面的内容比较难理解,关于神经网络训练词向量的基础知识我在《自己动手做聊天机器人 二十四-将深度学习应用到NLP》中有讲解,可以参考,这里不再赘述。

在word2vec中使用的最重要的两个模型是CBOW和Skip-gram模型,下面我们分别来介绍这两种模型

 

CBOW模型

CBOW全称是Continuous Bag-of-Words Model,是在已知当前词的上下文的前提下预测当前词

CBOW模型的神经网络结构设计如下:

输入层:词w的上下文一共2c个词的词向量

投影层:将输入层的2c个向量做求和累加

输出层:一个霍夫曼树,其中叶子节点是语料中出现过的词,权重是出现的次数

我们发现这一设计相比《自己动手做聊天机器人 二十四-将深度学习应用到NLP》中讲到的神经网络模型把首尾相接改成了求和累加,这样减少了维度;去掉了隐藏层,这样减少了计算量;输出层由softmax归一化运算改成了霍夫曼树;这一系列修改对训练的性能有很大提升,而效果不减,这是独到之处。

 

基于霍夫曼树的Hierarchical Softmax技术

上面的CBOW输出层为什么要建成一个霍夫曼树呢?因为我们是要基于训练语料得到每一个可能的w的概率。那么具体怎么得到呢?我们先来看一下这个霍夫曼树的例子:

在这个霍夫曼树中,我们以词足球为例,走过的路径图上容易看到,其中非根节点上的θ表示待训练的参数向量,也就是要达到这种效果:当在投射层产出了一个新的向量x,那么我通过逻辑回归公式:

σ(xTθ) = 1/(1+e^(-xTθ))

就可以得出在每一层被分到左节点(1)还是右节点(0)的概率分别是

p(d|x,θ) = 1-σ(xTθ)

p(d|x,θ) = σ(xTθ)

那么就有:

p(足球|Context(足球)) = ∏ p(d|x,θ)

现在模型已经有了,下面就是通过语料来训练v(Context(w))、x和θ的过程了

我们以对数似然函数为优化目标,盗取一个网上的推导公式:

假设两个求和符号里面的部分记作L(w, j),那么有

于是θ的更新公式:

同理得出x的梯度公式:

因为x是多个v的累加,word2vec中v的更新方法是:

想想机器学习真是伟大,整个模型从上到下全是未知数,竟然能算出来我真是服了

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

 

Skip-gram模型

Skip-gram全称是Continuous Skip-gram Model,是在已知当前词的情况下预测上下文

Skip-gram模型的神经网络结构设计如下:

输入层:w的词向量v(w)

投影层:依然是v(w),就是一个形式

输出层:和CBOW一样的霍夫曼树

后面的推导公式和CBOW大同小异,其中θ和v(w)的更新公式除了把符号名从x改成了v(w)之外完全一样,如下:

 

体验真实的word2vec

首先我们从网上下载一个源码,因为google官方的svn库已经不在了,所以只能从csdn下了,但是因为还要花积分才能下载,所以我干脆分享到了我的git上(https://github.com/lcdevelop/ChatBotCourse/tree/master/word2vec),大家可以直接下载

下载下来后直接执行make编译(如果是mac系统要把代码里所有的#include <malloc.h>替换成#include <sys/malloc.h>)

编译后生成word2vec、word2phrase、word-analogy、distance、compute-accuracy几个二进制文件

我们先用word2vec来训练

首先我们要有训练语料,其实就是已经切好词(空格分隔)的文本,比如我们已经有了这个文本文件叫做train.txt,内容是"人工 智能 一直 以来 是 人类 的 梦想 造 一台 可以 为 你 做 一切 事情 并且 有 情感 的 机器 人"并且重复100遍

执行

./word2vec -train train.txt -output vectors.bin -cbow 0 -size 200 -window 5 -negative 0 -hs 1 -sample 1e-3 -thread 12 -binary 1

会生成一个vectors.bin文件,这个就是训练好的词向量的二进制文件,利用这个文件我们可以求近义词了,执行:

./distance vectors.bin
Enter word or sentence (EXIT to break): 人类
Word: 人类  Position in vocabulary: 6
                                              Word       Cosine distance
------------------------------------------------------------------------
                                            可以       		0.094685
                                               为      		0.091899
                                            人工       		0.088387
                                            机器       		0.076216
                                            智能       		0.073093
                                            情感       		0.071088
                                               做      		0.059367
                                            一直       		0.056979
                                            以来       		0.049426
                                            一切       		0.042201
                                              </s>     		0.025968
                                            事情       		0.014169
                                               的      		0.003633
                                               是      		-0.012021
                                               有      		-0.014790
                                            一台       		-0.021398
                                               造      		-0.031242
                                               人      		-0.043759
                                               你      		-0.072834
                                            梦想       		-0.086062
                                            并且       		-0.122795
……

如果你有很丰富的语料,那么结果会很漂亮

自己动手做聊天机器人 二十四-将深度学习应用到NLP

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

词向量

自然语言需要数学化才能够被计算机认识和计算。数学化的方法有很多,最简单的方法是为每个词分配一个编号,这种方法已经有多种应用,但是依然存在一个缺点:不能表示词与词的关系。

词向量是这样的一种向量[0.1, -3.31, 83.37, 93.0, -18.37, ……],每一个词对应一个向量,词义相近的词,他们的词向量距离也会越近(欧氏距离、夹角余弦)

词向量有一个优点,就是维度一般较低,一般是50维或100维,这样可以避免维度灾难,也更容易使用深度学习

 

词向量如何训练得出呢?

首先要了解一下语言模型,语言模型相关的内容请见我另外一篇文章《自己动手做聊天机器人 十三-把语言模型探究到底》。语言模型表达的实际就是已知前n-1个词的前提下,预测第n个词的概率。

词向量的训练是一种无监督学习,也就是没有标注数据,给我n篇文章,我就可以训练出词向量。

基于三层神经网络构建n-gram语言模型(词向量顺带着就算出来了)的基本思路:

最下面的w是词,其上面的C(w)是词向量,词向量一层也就是神经网络的输入层(第一层),这个输入层是一个(n-1)×m的矩阵,其中n-1是词向量数目,m是词向量维度

第二层(隐藏层)是就是普通的神经网络,以H为权重,以tanh为激活函数

第三层(输出层)有|V|个节点,|V|就是词表的大小,输出以U为权重,以softmax作为激活函数以实现归一化,最终就是输出可能是某个词的概率。

另外,神经网络中有一个技巧就是增加一个从输入层到输出层的直连边(线性变换),这样可以提升模型效果,这个变换矩阵设为W

假设C(w)就是输入的x,那么y的计算公式就是y = b + Wx + Utanh(d+Hx)

这个模型里面需要训练的有这么几个变量:C、H、U、W。利用梯度下降法训练之后得出的C就是生成词向量所用的矩阵,C(w)表示的就是我们需要的词向量

上面是讲解词向量如何“顺带”训练出来的,然而真正有用的地方在于这个词向量如何进一步应用。

 

词向量的应用

第一种应用是找同义词。具体应用案例就是google的word2vec工具,通过训练好的词向量,指定一个词,可以返回和它cos距离最相近的词并排序。

第二种应用是词性标注和语义角色标注任务。具体使用方法是:把词向量作为神经网络的输入层,通过前馈网络和卷积网络完成。

第三种应用是句法分析和情感分析任务。具体使用方法是:把词向量作为递归神经网络的输入。

第四种应用是命名实体识别和短语识别。具体使用方法是:把词向量作为扩展特征使用。

另外词向量有一个非常特别的现象:C(king)-C(queue)≈C(man)-C(woman),这里的减法就是向量逐维相减,换个表达方式就是:C(king)-C(man)+C(woman)和它最相近的向量就是C(queue),这里面的原理其实就是:语义空间中的线性关系。基于这个结论相信会有更多奇妙的功能出现。

机器学习教程 十六-深究熵的概念和公式以及最大熵原理

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

有关熵的介绍,我在《自己动手做聊天机器人 十五-一篇文章读懂拿了图灵奖和诺贝尔奖的概率图模型》中做过简单的介绍,熵的英文是entropy,本来是一个热力学术语,表示物质系统的混乱状态。

我们都知道信息熵计算公式是H(U)=-∑(p logp),但是却不知道为什么,下面我们深入熵的本源来证明这个公式

假设下图是一个孤立的由3个分子构成一罐气体

那么这三个分子所处的位置有如下几种可能性:

图中不同颜色表示的是宏观状态(不区分每个分子的不同),那么宏观状态一共有4种,而微观状态(每一种组合都是一种微观状态)一共有2^3=8种

再来看4个分子的情况

这时,宏观状态一共有5种,而微观状态一共有2^4=16种

事实上分子数目越多,微观数目会成指数型增长

这里面提到的宏观状态实际上就是熵的某一种表现,如果气体中各种宏观状态都有,那么熵就大,如果只存在一种宏观状态,那么熵就很小,如果把每个分子看做状态的形成元素,熵的计算就可以通过分子数目以某种参数求对数得到,这时我们已经了解了为什么熵公式中是对数关系

上面我们描述的一个系统(一罐气体),假如我们有两罐气体,那么它们放在一起熵应该是可以相加的(就像上面由四种状态加了一个状态的一个分子变成5个状态),即可加性,而微观状态是可以相乘的(每多一个分子,微观状态就会多出n-1种),即相乘法则

综上,我们可以得出熵的计算公式是S=k ln Ω,其中k是一个常数,叫做玻尔兹曼常数,Ω是微观状态数,这个公式也满足了上面的可加性和相乘法则,即S1+S2=k ln (Ω1Ω2)

 

最大熵

在机器学习中我们总是运用最大熵原理来优化模型参数,那么什么样的熵是最大熵,为什么它就是最优的

这还是要从物理学的原理来说明,我们知道当没有外力的情况下气体是不断膨胀的而不会自动收缩,两个温度不同的物体接触时总是从高温物体向低温物体传导热量而不可逆。我们知道宏观状态越多熵越大,那么气体膨胀实际上是由较少的宏观状态向较多的宏观状态在变化,热传导也是一样,如此说来,一个孤立系统总是朝着熵增加的方向变化,熵越大越稳定,到最稳定时熵达到最大,这就是熵增原理

换句话说:熵是孤立系统的无序度的量度,平衡态时熵最大

将熵增原理也可以扩大到一切自发过程的普遍规律,比如如果你不收拾屋子,那么屋子一定会变得越来越乱而不会越来越干净整洁,扩大到统计学上来讲,屋子乱的概率更大,也就是说孤立系统中一切实际过程总是从概率小的状态向概率大的状态的转变过程,并且不可逆

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

 

信息熵

1948年,信息论之父香农发表的《通信的数学理论》中提出了“信息熵”的概念,从此信息熵对通信和计算机行业产生了巨大的影响。那么他到底说了些什么呢?

一个随机变量ξ有A1、A2、A3……共n个不同的结果,每个结果出现的概率是p1、p2、p3……,那么我们把ξ的不确定度定义为信息熵,参考上面物理学熵的定义,A1、A2、A3……可以理解为不同的微观状态,那么看起来信息熵应该是log n喽?不然,因为这个随机变量ξ一次只能取一个值而不是多个值,所以应该按概率把ξ劈开,劈成n份,每份的微观状态数分别是1/p1、1/p2、1/p3……,这样这n份的熵分别是log 1/p1、log 1/p2、log 1/p3……,再根据熵的可加性原理,得到整体随机变量ξ的信息熵是∑(p log 1/p),即H(ξ) = -∑(p log p)

 

最大熵原理

继续看上面的信息熵公式,从公式可以看出,出现各种随机结果可能性越大,不确定性就越大,熵就越大。相反,如果只可能出现一种结果,那么熵就为0,因为这时p=1,-∑(p log p)=0

举个例子,投1000次硬币,最有可能的概率是正面1/2,负面1/2,因此熵是H(X) = -(0.5log0.5+0.5log0.5) = -0.5*math.log(2,1/2)*2 = -0.5*-1*2 = 1

那么假设只会出现正面,熵是H(X) = -1log1 = 0

实际上哪种是最符合实际情况的呢?显然是第一种,这就是最大熵模型的原理:在机器学习中之所以优化最大熵公式能训练出最接近正确值的参数值,是因为这是“最符合实际”的可能。换句有哲理的话说:熵越大越趋向于自然,越没有偏见

 

最大熵模型

机器学习中用到的最大熵模型是一个定义在条件概率分布P(Y|X)上的条件熵。其中X、Y分别对应着数据的输入和输出,根据最大熵原理,当熵最大时,模型最符合实际情况。那么这个条件熵应该是什么样的呢?

条件概率分布P(Y|X)上的条件熵可以理解为在X发生的前提下,Y发生所“新”带来的熵,表示为H(Y|X),那么有

H(Y|X) = H(X,Y) - H(X)

其中H(X,Y)表示X、Y的联合熵,表示X、Y都发生时的熵,H(Y|X)的计算公式推导如下:

因此我们在机器学习中想方设法优化的就是这个东东,由于这里的p(x,y)无法统计,因此我们转成p(x)p(y|x),这样得到公式如下:

H(Y|X) = -∑p(x)p(y|x)log p(y|x)

那么机器学习训练的过程实际就是求解p(y|x)的过程,其中p(x)可以通过x的最大似然估计直接求得

 

总结

至此,我们介绍完了熵的概念和公式以及最大熵原理和最大熵模型公式的由来,总之,熵来源于热力学,扩展于信息论,应用在机器学习领域,它表达的是一种无序状态,也是最趋向于自然、最符合实际的情况。为了更深入感受最大熵模型的魅力,后续我会把最大熵模型的应用不断渗透到机器学习教程的具体算法中

自己动手做聊天机器人 二十三-用CNN做深度学习

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

卷积神经网络(CNN)

卷积神经网络(Convolutional Neural Network,CNN)是将二维离散卷积运算和人工神经网络相结合的一种深度神经网络。它的特点是可以自动提取特征。有关卷积神经网络的数学原理和训练过程请见我的另一篇文章《机器学习教程 十五-细解卷积神经网络》。

 

手写数字识别

为了试验,我们直接采用http://yann.lecun.com/exdb/mnist/中的手写数据集,下载到的手写数据集数据文件是用二进制以像素为单位保存的几万张图片文件,通过我的github项目https://github.com/lcdevelop/ChatBotCourse中的read_images.c把图片打印出来是像下面这样的输出:

具体文件格式和打印方式请见我的另一篇基于简单的softmax模型的机器学习算法文章《机器学习教程 十四-利用tensorflow做手写数字识别》中的讲解

 

多层卷积网络设计

为了对mnist手写数据集做训练,我们设计这样的多层卷积网络:

 

第一层由一个卷积和一个max pooling完成,其中卷积运算的“视野”是5×5的像素范围,卷积使用1步长、0边距的模板(保证输入输出是同一个大小),1个输入通道(因为图片是灰度的,单色),32个输出通道(也就是设计32个特征)。由于我们通过上面read_images.c的打印可以看到每张图片都是28×28像素,那么第一次卷积输出也是28×28大小。max pooling采用2×2大小的模板,那么池化后输出的尺寸就是14×14,因为一共有32个通道,所以一张图片的输出一共是14×14×32=6272像素

第二层同样由一个卷积和一个max pooling完成,和第一层不同的是输入通道有32个(对应第一层的32个特征),输出通道我们设计64个(即输出64个特征),因为这一层的输入是每张大小14×14,所以这一个卷积层输出也是14×14,再经过这一层max pooling,输出大小就是7×7,那么一共输出像素就是7×7×64=3136

第三层是一个密集连接层,我们设计一个有1024个神经元的全连接层,这样就相当于第二层输出的7×7×64个值都作为这1024个神经元的输入

为了让算法更“智能”,我们把这些神经元的激活函数设计为ReLu函数,即如下图像中的蓝色(其中绿色是它的平滑版g(x)=log(1+e^x)):

 

最终的输出层,我们以第三层的1024个输出为输入,设计一个softmax层,输出10个概率值

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

 

tensorflow代码实现

# coding:utf-8
import sys
reload(sys)
sys.setdefaultencoding( "utf-8" )
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
flags = tf.app.flags
FLAGS = flags.FLAGS
flags.DEFINE_string('data_dir', './', 'Directory for storing data')
mnist = input_data.read_data_sets(FLAGS.data_dir, one_hot=True)
# 初始化生成随机的权重(变量),避免神经元输出恒为0
def weight_variable(shape):
    # 以正态分布生成随机值
    initial = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(initial)
# 初始化生成随机的偏置项(常量),避免神经元输出恒为0
def bias_variable(shape):
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial)
# 卷积采用1步长,0边距,保证输入输出大小相同
def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
# 池化采用2×2模板
def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
        strides=[1, 2, 2, 1], padding='SAME')
# 28*28=784
x = tf.placeholder(tf.float32, [None, 784])
# 输出类别共10个:0-9
y_ = tf.placeholder("float", [None,10])
# 第一层卷积权重,视野是5*5,输入通道1个,输出通道32个
W_conv1 = weight_variable([5, 5, 1, 32])
# 第一层卷积偏置项有32个
b_conv1 = bias_variable([32])
# 把x变成4d向量,第二维和第三维是图像尺寸,第四维是颜色通道数1
x_image = tf.reshape(x, [-1,28,28,1])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
# 第二层卷积权重,视野是5*5,输入通道32个,输出通道64个
W_conv2 = weight_variable([5, 5, 32, 64])
# 第二层卷积偏置项有64个
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
# 第二层池化后尺寸编程7*7,第三层是全连接,输入是64个通道,输出是1024个神经元
W_fc1 = weight_variable([7 * 7 * 64, 1024])
# 第三层全连接偏置项有1024个
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
# 按float做dropout,以减少过拟合
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
# 最后的softmax层生成10种分类
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
# Adam优化器来做梯度最速下降
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
sess = tf.InteractiveSession()
sess.run(tf.initialize_all_variables())
for i in range(20000):
    batch = mnist.train.next_batch(50)
    if i%100 == 0:
        train_accuracy = accuracy.eval(feed_dict={
            x:batch[0], y_: batch[1], keep_prob: 1.0})
        print "step %d, training accuracy %g"%(i, train_accuracy)
    train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
print "test accuracy %g"%accuracy.eval(feed_dict={
    x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})

输出结果

[root@centos $] python digital_recognition_cnn.py
Extracting ./train-images-idx3-ubyte.gz
Extracting ./train-labels-idx1-ubyte.gz
Extracting ./t10k-images-idx3-ubyte.gz
Extracting ./t10k-labels-idx1-ubyte.gz
step 0, training accuracy 0.14
step 100, training accuracy 0.86
step 200, training accuracy 0.9
step 300, training accuracy 0.86
step 400, training accuracy 1
step 500, training accuracy 0.92
step 600, training accuracy 1
step 700, training accuracy 0.96
step 800, training accuracy 0.88
step 900, training accuracy 1
step 1000, training accuracy 0.96
step 1100, training accuracy 0.98
step 1200, training accuracy 0.94
step 1300, training accuracy 0.92
step 1400, training accuracy 0.98
……

最终准确率大概能达到99.2%

教你成为全栈工程师(Full Stack Developer) 四十五-一文读懂hadoop、hbase、hive、spark分布式系统架构

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址。本文较长,精华在最后

本文结构

首先,我们来分别部署一套hadoop、hbase、hive、spark,在讲解部署方法过程中会特殊说明一些重要配置,以及一些架构图以帮我们理解,目的是为后面讲解系统架构和关系打基础。

之后,我们会通过运行一些程序来分析一下这些系统的功能

最后,我们会总结这些系统之间的关系

 

分布式hadoop部署

首先,在http://hadoop.apache.org/releases.html找到最新稳定版tar包,我选择的是

http://apache.fayea.com/hadoop/common/hadoop-2.7.2/hadoop-2.7.2.tar.gz

下载到/data/apache并解压

在真正部署之前,我们先了解一下hadoop的架构

hadoop分为几大部分:yarn负责资源和任务管理、hdfs负责分布式存储、map-reduce负责分布式计算

先来了解一下yarn的架构:

yarn的两个部分:资源管理、任务调度。

资源管理需要一个全局的ResourceManager(RM)和分布在每台机器上的NodeManager协同工作,RM负责资源的仲裁,NodeManager负责每个节点的资源监控、状态汇报和Container的管理

任务调度也需要ResourceManager负责任务的接受和调度,在任务调度中,在Container中启动的ApplicationMaster(AM)负责这个任务的管理,当任务需要资源时,会向RM申请,分配到的Container用来起任务,然后AM和这些Container做通信,AM和具体执行的任务都是在Container中执行的

yarn区别于第一代hadoop的部署(namenode、jobtracker、tasktracker)

然后再看一下hdfs的架构:hdfs部分由NameNode、SecondaryNameNode和DataNode组成。DataNode是真正的在每个存储节点上管理数据的模块,NameNode是对全局数据的名字信息做管理的模块,SecondaryNameNode是它的从节点,以防挂掉。

最后再说map-reduce:Map-reduce依赖于yarn和hdfs,另外还有一个JobHistoryServer用来看任务运行历史

hadoop虽然有多个模块分别部署,但是所需要的程序都在同一个tar包中,所以不同模块用到的配置文件都在一起,让我们来看几个最重要的配置文件:

各种默认配置:core-default.xml, hdfs-default.xml, yarn-default.xml, mapred-default.xml

各种web页面配置:core-site.xml, hdfs-site.xml, yarn-site.xml, mapred-site.xml

从这些配置文件也可以看出hadoop的几大部分是分开配置的。

除上面这些之外还有一些重要的配置:hadoop-env.sh、mapred-env.sh、yarn-env.sh,他们用来配置程序运行时的java虚拟机参数以及一些二进制、配置、日志等的目录配置

下面我们真正的来修改必须修改的配置文件。

修改etc/hadoop/core-site.xml,把配置改成:

<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://127.0.0.1:8000</value>
    </property>
    <property>
        <name>io.file.buffer.size</name>
        <value>131072</value>
    </property>
</configuration>

这里面配置的是hdfs的文件系统地址:本机的9001端口

修改etc/hadoop/hdfs-site.xml,把配置改成:

<configuration>
    <property>
        <name>dfs.namenode.name.dir</name>
        <value>file:/data/apache/dfs/name</value>
    </property>
    <property>
        <name>dfs.datanode.data.dir</name>
        <value>file:/data/apache/dfs/data</value>
    </property>
    <property>
        <name>dfs.datanode.fsdataset.volume.choosing.policy</name>
        <value>org.apache.hadoop.hdfs.server.datanode.fsdataset.AvailableSpaceVolumeChoosingPolicy</value>
    </property>
    <property>
        <name>dfs.namenode.http-address</name>
        <value>127.0.0.1:50070</value>
    </property>
    <property>
        <name>dfs.namenode.secondary.http-address</name>
        <value>127.0.0.1:8001</value>
    </property>
</configuration>

这里面配置的是hdfs文件存储在本地的哪里以及secondary namenode的地址

修改etc/hadoop/yarn-site.xml,把配置改成:

<configuration>
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>127.0.0.1</value>
    </property>
    <property>
        <name>yarn.resourcemanager.webapp.address</name>
        <value>127.0.0.1:8088</value>
    </property>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
    <property>
        <name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
        <value>org.apache.hadoop.mapred.ShuffleHandler</value>
    </property>
    <property>
        <name>yarn.log-aggregation-enable</name>
        <value>true</value>
    </property>
    <property>
        <name>yarn.log-aggregation.retain-seconds</name>
        <value>864000</value>
    </property>
    <property>
        <name>yarn.log-aggregation.retain-check-interval-seconds</name>
        <value>86400</value>
    </property>
    <property>
        <name>yarn.nodemanager.remote-app-log-dir</name>
        <value>/YarnApp/Logs</value>
    </property>
    <property>
        <name>yarn.log.server.url</name>
        <value>http://127.0.0.1:19888/jobhistory/logs/</value>
    </property>
    <property>
        <name>yarn.nodemanager.local-dirs</name>
        <value>/data/apache/tmp/</value>
    </property>
    <property>
        <name>yarn.scheduler.maximum-allocation-mb</name>
        <value>5000</value>
    </property>
    <property>
        <name>yarn.scheduler.minimum-allocation-mb</name>
        <value>1024</value>
    </property>
    <property>
        <name>yarn.nodemanager.vmem-pmem-ratio</name>
        <value>4.1</value>
    </property>
    <property>
        <name>yarn.nodemanager.vmem-check-enabled</name>
        <value>false</value>
    </property>
</configuration>

这里面配置的是yarn的日志地址以及一些参数配置

通过cp etc/hadoop/mapred-site.xml.template etc/hadoop/mapred-site.xml创建etc/hadoop/mapred-site.xml,内容改为如下:

<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
        <description>Execution framework set to Hadoop YARN.</description>
    </property>
    <property>
        <name>yarn.app.mapreduce.am.staging-dir</name>
        <value>/tmp/hadoop-yarn/staging</value>
    </property>
    <property>
        <name>mapreduce.jobhistory.address</name>
        <value>127.0.0.1:10020</value>
    </property>
    <property>
        <name>mapreduce.jobhistory.webapp.address</name>
        <value>127.0.0.1:19888</value>
    </property>
    <property>
        <name>mapreduce.jobhistory.done-dir</name>
        <value>${yarn.app.mapreduce.am.staging-dir}/history/done</value>
    </property>
    <property>
        <name>mapreduce.jobhistory.intermediate-done-dir</name>
        <value>${yarn.app.mapreduce.am.staging-dir}/history/done_intermediate</value>
    </property>
    <property>
        <name>mapreduce.jobhistory.joblist.cache.size</name>
        <value>1000</value>
    </property>
    <property>
        <name>mapreduce.tasktracker.map.tasks.maximum</name>
        <value>8</value>
    </property>
    <property>
        <name>mapreduce.tasktracker.reduce.tasks.maximum</name>
        <value>8</value>
    </property>
    <property>
        <name>mapreduce.jobtracker.maxtasks.perjob</name>
        <value>5</value>
        <description>The maximum number of tasks for a single job.
            A value of -1 indicates that there is no maximum.
        </description>
    </property>
</configuration>

这里面配置的是mapred的任务历史相关配置

如果你的hadoop部署在多台机器,那么需要修改etc/hadoop/slaves,把其他slave机器ip加到里面,如果只部署在这一台,那么就留一个localhost即可

下面我们启动hadoop,启动之前我们配置好必要的环境变量:

export JAVA_HOME="你的java安装地址"

先启动hdfs,在此之前要格式化分布式文件系统,执行:

./bin/hdfs namenode -format myclustername

如果格式化正常可以看到/data/apache/dfs下生成了name目录

然后启动namenode,执行:

./sbin/hadoop-daemon.sh --script hdfs start namenode

如果正常启动,可以看到启动了相应的进程,并且logs目录下生成了相应的日志

然后启动datanode,执行:

./sbin/hadoop-daemon.sh --script hdfs start datanode

如果考虑启动secondary namenode,可以用同样的方法启动

下面我们启动yarn,先启动resourcemanager,执行:

./sbin/yarn-daemon.sh start resourcemanager

如果正常启动,可以看到启动了相应的进程,并且logs目录下生成了相应的日志

然后启动nodemanager,执行:

./sbin/yarn-daemon.sh start nodemanager

如果正常启动,可以看到启动了相应的进程,并且logs目录下生成了相应的日志

然后启动MapReduce JobHistory Server,执行:

./sbin/mr-jobhistory-daemon.sh start historyserver

如果正常启动,可以看到启动了相应的进程,并且logs目录下生成了相应的日志

下面我们看下web界面

打开http://127.0.0.1:8088/cluster看下yarn管理的集群资源情况(因为在yarn-site.xml中我们配置了yarn.resourcemanager.webapp.address是127.0.0.1:8088)

打开http://127.0.0.1:19888/jobhistory看下map-reduce任务的执行历史情况(因为在mapred-site.xml中我们配置了mapreduce.jobhistory.webapp.address是127.0.0.1:19888)

打开http://127.0.0.1:50070/dfshealth.html看下namenode的存储系统情况(因为在hdfs-site.xml中我们配置了dfs.namenode.http-address是127.0.0.1:50070)

到此为止我们对hadoop的部署完成。下面试验一下hadoop的功能

先验证一下hdfs分布式文件系统,执行以下命令看是否有输出:

[root@MYAY hadoop]# ./bin/hadoop fs -mkdir /input
[root@MYAY hadoop]# cat data
1
2
3
4
[root@MYAY hadoop]# ./bin/hadoop fs -put input /input
[root@MYAY hadoop]# ./bin/hadoop fs -ls /input
Found 1 items
-rw-r--r--   3 root supergroup          8 2016-08-07 15:04 /input/data

这时通过http://127.0.0.1:50070/dfshealth.html可以看到存储系统的一些变化

下面我们以input为输入启动一个mapreduce任务

[root@MYAY hadoop]# ./bin/hadoop jar ./share/hadoop/tools/lib/hadoop-streaming-2.7.2.jar -input /input -output /output -mapper cat -reducer wc

之后看是否产生了/output的输出:

[root@MYAY hadoop]# ./bin/hadoop fs -ls /output
Found 2 items
-rw-r--r--   3 root supergroup          0 2016-08-07 15:11 /output/_SUCCESS
-rw-r--r--   3 root supergroup         25 2016-08-07 15:11 /output/part-00000
[root@MYAY hadoop]# ./bin/hadoop fs -cat /output/part-00000
      4       4      12

这时通过http://127.0.0.1:19888/jobhistory可以看到mapreduce任务历史:

也可以通过http://127.0.0.1:8088/cluster看到任务历史

为什么两处都有历史呢?他们的区别是什么呢?

我们看到cluster显示的其实是每一个application的历史信息,他是yarn(ResourceManager)的管理页面,也就是不管是mapreduce还是其他类似mapreduce这样的任务,都会在这里显示,mapreduce任务的Application Type是MAPREDUCE,其他任务的类型就是其他了,但是jobhistory是专门显示mapreduce任务的

请尊重原创,转载请注明来源网站www.lcsays.com以及原始链接地址

 

hbase的部署

首先从http://www.apache.org/dyn/closer.cgi/hbase/下载稳定版安装包,我下的是https://mirrors.tuna.tsinghua.edu.cn/apache/hbase/stable/hbase-1.2.2-bin.tar.gz

解压后修改conf/hbase-site.xml,改成:

<configuration>
    <property>
        <name>hbase.cluster.distributed</name>
        <value>true</value>
    </property>
    <property>
        <name>hbase.rootdir</name>
        <value>hdfs://127.0.0.1:8001/hbase</value>
    </property>
    <property>
        <name>hbase.zookeeper.quorum</name>
        <value>127.0.0.1</value>
    </property>
</configuration>

其中hbase.rootdir配置的是hdfs地址,ip:port要和hadoop/core-site.xml中的fs.defaultFS保持一致

其中hbase.zookeeper.quorum是zookeeper的地址,可以配多个,我们试验用就先配一个

启动hbase,执行:

./bin/start-hbase.sh

这时有可能会让你输入本地机器的密码

启动成功后可以看到几个进程起来,包括zookeeper的HQuorumPeer和hbase的HMaster、HRegionServer

下面我们试验一下hbase的使用,执行:

hbase(main):001:0> status
1 active master, 0 backup masters, 1 servers, 0 dead, 3.0000 average load

创建一张表

hbase(main):004:0> create 'table1','field1'
0 row(s) in 1.3430 seconds
=> Hbase::Table - table1

获取一张表

hbase(main):005:0> t1 = get_table('table1')
0 row(s) in 0.0010 seconds
=> Hbase::Table - table1

添加一行

hbase(main):008:0> t1.put 'row1', 'field1:qualifier1', 'value1'
0 row(s) in 0.4160 seconds

读取全部

hbase(main):009:0> t1.scan
ROW                                                                 COLUMN+CELL
 row1                                                               column=field1:qualifier1, timestamp=1470621285068, value=value1
1 row(s) in 0.1000 seconds

我们同时也看到hdfs中多出了hbase存储的目录:

[root@MYAY hbase]# ./hadoop/bin/hadoop fs -ls /hbase
Found 7 items
drwxr-xr-x   - root supergroup          0 2016-08-08 09:05 /hbase/.tmp
drwxr-xr-x   - root supergroup          0 2016-08-08 09:58 /hbase/MasterProcWALs
drwxr-xr-x   - root supergroup          0 2016-08-08 09:05 /hbase/WALs
drwxr-xr-x   - root supergroup          0 2016-08-08 09:05 /hbase/data
-rw-r--r--   3 root supergroup         42 2016-08-08 09:05 /hbase/hbase.id
-rw-r--r--   3 root supergroup          7 2016-08-08 09:05 /hbase/hbase.version
drwxr-xr-x   - root supergroup          0 2016-08-08 09:24 /hbase/oldWALs

这说明hbase是以hdfs为存储介质的,因此它具有分布式存储拥有的所有优点

hbase的架构如下:

其中HMaster负责管理HRegionServer以实现负载均衡,负责管理和分配HRegion(数据分片),还负责管理命名空间和table元数据,以及权限控制

HRegionServer负责管理本地的HRegion、管理数据以及和hdfs交互。

Zookeeper负责集群的协调(如HMaster主从的failover)以及集群状态信息的存储

客户端传输数据直接和HRegionServer通信

 

hive的部署

http://mirrors.hust.edu.cn/apache/hive下载安装包,我下的是http://mirrors.hust.edu.cn/apache/hive/stable-2/apache-hive-2.1.0-bin.tar.gz

解压后,我们先准备hdfs,执行:

[root@MYAY hadoop]# ./hadoop/bin/hadoop fs -mkdir /tmp
[root@MYAY hadoop]# ./hadoop/bin/hadoop fs -mkdir /user
[root@MYAY hadoop]# ./hadoop/bin/hadoop fs -mkdir /user/hive
[root@MYAY hadoop]# ./hadoop/bin/hadoop fs -mkdir /user/hive/warehourse
[root@MYAY hadoop]# ./hadoop/bin/hadoop fs -chmod g+w /tmp
[root@MYAY hadoop]# ./hadoop/bin/hadoop fs -chmod g+w /user/hive/warehourse

使用hive必须提前设置好HADOOP_HOME环境变量,这样它可以自动找到我们的hdfs作为存储,不妨我们把各种HOME和各种PATH都配置好,如:

HADOOP_HOME=/data/apache/hadoop
export HADOOP_HOME
HBASE_HOME=/data/apache/hbase
export HBASE_HOME
HIVE_HOME=/data/apache/hive
export HIVE_HOME
PATH=$PATH:$HOME/bin
PATH=$PATH:$HBASE_HOME/bin
PATH=$PATH:$HIVE_HOME/bin
PATH=$PATH:$HADOOP_HOME/bin
export PATH

拷贝创建hive-site.xml、hive-log4j2.properties、hive-exec-log4j2.properties,执行

[root@MYAY hive]# cp conf/hive-default.xml.template conf/hive-site.xml
[root@MYAY hive]# cp conf/hive-log4j2.properties.template conf/hive-log4j2.properties
[root@MYAY hive]# cp conf/hive-exec-log4j2.properties.template conf/hive-exec-log4j2.properties

修改hive-site.xml,把其中的${system:java.io.tmpdir}都修改成/data/apache/tmp,你也可以自己设置成自己的tmp目录,把${system:user.name}都换成用户名

:%s/${system:java.io.tmpdir}/\/data\/apache\/tmp/g
:%s/${system:user.name}/myself/g

初始化元数据数据库(默认保存在本地的derby数据库,也可以配置成mysql),注意,不要先执行hive命令,否则这一步会出错,具体见http://stackoverflow.com/questions/35655306/hive-installation-issues-hive-metastore-database-is-not-initialized,下面执行:

[root@MYAY hive]# schematool -dbType derby -initSchema

成功之后我们可以以客户端形式直接启动hive,如:

[root@MYAY hive]# hive
hive> show databases;
OK
default
Time taken: 1.886 seconds, Fetched: 1 row(s)
hive>

试着创建个数据库是否可以:

hive> create database mydatabase;
OK
Time taken: 0.721 seconds
hive> show databases;
OK
default
mydatabase
Time taken: 0.051 seconds, Fetched: 2 row(s)
hive>

这样我们还是单机的hive,不能在其他机器登陆,所以我们要以server形式启动:

nohup hiveserver2 &> hive.log &

默认会监听10000端口,这时可以通过jdbc客户端连接这个服务访问hive

hive的具体使用在这里不赘述

 

spark部署

首先在http://spark.apache.org/downloads.html下载指定hadoop版本的安装包,我下载的是http://d3kbcqa49mib13.cloudfront.net/spark-2.0.0-bin-hadoop2.7.tgz

spark有多种部署方式,首先支持单机直接跑,如执行样例程序:

./bin/spark-submit examples/src/main/python/pi.py 10

它可以直接运行得出结果

下面我们说下spark集群部署方法:

解压安装包后直接执行:

[root@MYAY spark-2.0.0-bin-hadoop2.7]# sbin/start-master.sh

这时可以打开http://127.0.0.1:8080/看到web界面如下:

根据上面的url:spark://MYAY:7077,我们再启动slave:

[root@MYAY spark-2.0.0-bin-hadoop2.7]# ./sbin/start-slave.sh spark://MYAY:7077

刷新web界面如下:

出现了一个worker,我们可以根据需要启动多个worker

下面我们把上面执行过的任务部署到spark集群上执行:

./bin/spark-submit --master spark://MYAY:7077 examples/src/main/python/pi.py 10

web界面如下:

spark程序也可以部署到yarn集群上执行,也就是我们部署hadoop时启动的yarn

我们需要提前配置好HADOOP_CONF_DIR,如下:

HADOOP_CONF_DIR=${HADOOP_HOME}/etc/hadoop/
export HADOOP_CONF_DIR

下面我们把任务部署到yarn集群上去:

./bin/spark-submit --master yarn --deploy-mode cluster examples/src/main/python/pi.py 10

http://127.0.0.1:8088/cluster效果如下:

 

总结一下

hdfs是所有hadoop生态的底层存储架构,它主要完成了分布式存储系统的逻辑,凡是需要存储的都基于其上构建

yarn是负责集群资源管理的部分,这个资源主要指计算资源,因此它支撑了各种计算模块

map-reduce组件主要完成了map-reduce任务的调度逻辑,它依赖于hdfs作为输入输出及中间过程的存储,因此在hdfs之上,它也依赖yarn为它分配资源,因此也在yarn之上

hbase基于hdfs存储,通过独立的服务管理起来,因此仅在hdfs之上

hive基于hdfs存储,通过独立的服务管理起来,因此仅在hdfs之上

spark基于hdfs存储,即可以依赖yarn做资源分配计算资源也可以通过独立的服务管理,因此在hdfs之上也在yarn之上,从结构上看它和mapreduce一层比较像

总之,每一个系统负责了自己擅长的一部分,同时相互依托,形成了整个hadoop生态。