深度学习的前世今生

导读

博主高考那年,自动化专业火热,然而在下偏偏选择了学计算机。庆幸的是近几年互联网发展迅猛,各行各业纷纷开始转CS(computer science)。CS专业方向上,前几年最为火热的莫过于前端、移动端,而如今深度学习炙手可热,学校里研究生随机抓一把,总有一个是搞深度学习的。当前业界深度学习风头正盛,本篇文章主要希望带大家了解下深度学习的前世今生,对其诞生及发展的背景有一个初步了解。

人工智能、机器学习和深度学习的关系

人工智能出现最早、范围最广;随后出现的是机器学习;最后是深度学习,也是当今人工智能大爆炸的核心驱动。

人工智能

起源

1956年,在美国的达特茅斯学院,John McCarthy(图灵奖得主)、Marvin Minsky(人工智能与认知专家、图灵奖得主)、Claude Shannon(信息论之父)、Allen Newell(计算机科学家)、Herbert Simon(诺贝尔经济学奖得主)等科学家聚在一起,正式提出了人工智能(Artificial Intelligence)的概念。

定义

人工智能,是计算机科学的一个分支,是一门研究机器只能的学科。即用人工的方法和技术来研制智能机器或智能系统,以此来模仿、延伸和扩展人的智能。

目的

人工智能的主要任务是建立智能信息处理理论,使计算机系统拥有近似于人类的智能行为。

低谷

人们为之振奋,而过于乐观的态度和当时人工智能技术不可避免的局限性使得大众对这一领域逐渐失去兴趣。1973年《莱特希尔报告》推出后,人工智能被普遍认为是没有出路的。

机器学习

起源

经历了10年沉寂,到了80年代,以专家系统为代表的机器学习开始兴起,人工智能进入第二个阶段。

定义

卡内基梅隆大学的Tom Michael Mitchell教授在1997年出版的书籍《Machine Learning》中对机器学习做了非常专业的定义:

如果一个程序可以在任务T上,随着经验E的增加,效果P也可以随之增加,则称这个程序可以从经验中学习。

目的

机器学习是一种”训练”算法的方式,目的是使机器能够向算法传送大量的数据,并允许算法进行自我调整和改进,而不是利用具有特定指令的编码软件例程来完成指定的任务。

低谷

而人们意识到不仅软件及算法层面的挑战没有突破,硬件层面也面临危机。随着1987年基于通用计算的Lisp机器在商业上的失败,机器学习也逐渐进入了低迷期。到了20世纪90年代后期,由于计算机计算能力的不断提高,人工智能再次卷土重来。

深度学习

起源

2006年研究人员发现了成功训练深层神经网络的方法,并将这一方法定义为深度学习。

定义

深度学习是基于多层神经网络的,以海量数据为输入的,规则自学习的方法。

优势

相较于传统机器学习,深度学习不再需要人工的方式进行特征提取,而是自动从简单特征中提取、组合更复杂的特征,从数据里学习到复杂的特征表达形式并使用这些组合特征解决问题。

劣势

深度学习不是万能的,像很多其他方法一样,它需要结合特型领域的先验知识,需要和其他方法结合才能得到最好的结果。
深度学习的另一个局限性是可解释性不强,像个”黑盒子”一样难以解释为什么能取的好效果,以及不知如何有针对性的去具体改进。

现状

深度学习凭借其出色表现,在各大领域掀起浪潮,引起了整个科研界和工业界的狂热。

小结

简单来说,机器学习是实现人工智能的方法;深度学习是实现机器学习的技术之一。

深度学习的发展历程

谈到深度学习的历史就不得不追溯到神经网络技术。在深度学习崛起之前,神经网络经历了两个低谷,这两个低谷也讲神经网络的发展分为了三个不同的阶段。

神经网络的第一次高潮

神经网络的第一次高潮是感知机带来的。
1957年,Frank Rosenblatt提出了感知机的概念,成为日后发展神经网络和支持向量积的基础。
感知机是一种用算法构造的”分类器”,是一种线性分类模型,原理是通过不断试错以期寻找一个合适的超平面把数据分开。
单层感知机

神经网络的第一次寒冬

虽然单层感知机简单且优雅,但它显然能力有限。1969年,Marvin Minsky在《Perceptrons》书中,证明感知机仅能分类线性问题,对于异或问题束手无策。
人们试图通过增加隐含层创造多层感知机,随着隐藏层的层数增多,区域可以形成任意形状,因此可以解决任何复杂的分类问题。
虽然多层感知机确实是非常理想的分类器,但是问题也随之而来:隐藏层的权值怎么训练?对于各隐藏层的节点来说,它们并不存在期望输出,所以也无法通过感知机的学习规则来训练多层感知机,人们一直没能找到可靠的学习算法来解决这一问题。
多层感知机

神经网络的第二次高潮

直到反向传播算法(BP算法)被提出,才真正解决了感知机的局限性,再一次将神经网络带向高潮。
1986年,Geoffrey Hinton和David Rumelhart合作在《自然》杂志上发表了论文《Learning Representations by Back-Propagating Errors》,第一次系统简洁地阐述了反向传播算法(BP算法)在神经网络模型上的应用。这一算法通过在神经网络里增加一个所谓的隐层(Hidden Layer),解决了感知机无法实现异或分类的难题。加之计算机运行速度的提高,使一层以上的神经网络进入了实用阶段。

神经网络的第二次寒冬

虽然BP算法将神经网络带入了实用阶段,但当时的神经网络仍存在很多缺陷。
首先是浅层的限制问题,人们发现神经网络中越远离输出层的参数越难以被训练,且层数越多问题越明显,称为”梯度爆炸”问题。
另外,当计算机资源不足的情况下,数据集都很小,无法满足训练深层网络的要求。

深度学习的来临

2006年,Hinton发表了一篇突破性的文章《A Fast Learning Algorithm for Deep Belif Nets》,论文中Hinton介绍了一种成功训练多层神经网络的办法,他将这种网络称为深度信念网络。
Hinton提出了两个观点:

多层人工神经网络模型有很强的特征学习能力,深度学习模型得到的特征数据对原始数据有更本质的代表性,这将大大便于分类和可视化问题。
对于深度神经网络很难训练达到最优的问题,可以采用逐层训练方法解决,将上层训练好的结果作为下层训练过程中的初始化参数。

总结

纵观技术兴衰史,潮起潮落。不知道未来是下一个寒冬先到来,还是强人工智能先出现,拭目以待。

参考资料

PaddlePaddle深度学习实战

用python写一个简单的区块链

区块内容

通常,一个区块包含数据、时间戳、指向上一个区块的索引。今天的简易区块类Block主要包含以下几个属性:

  • index:链上用来追踪区块位置的编号
  • timestamp:区块创建的时间或日期
  • data:真实存储在区块上的数据
  • previous hash:链上上一个区块的hash码

使用SHA-256算法计算区块的hash码

class Block:
    """
    区块类
    """

    def __init__(self, index, timestamp, data, previous_hash=' '):
        """
        初始化函数
        """
        self.index         = index
        self.timestamp     = timestamp
        self.data          = data
        self.previous_hash = previous_hash
        self.hash          = self.calculate_hash()

    def calculate_hash(self):
        """
        计算hash
        """
        return hashlib.sha256(str(self.index) + self.previous_hash + self.timestamp + json.dumps(self.data)).hexdigest()

    def print_block(self):
        """
        输出区块信息
        """
        print "Block #" + str(self.index)
        print "Data " + str(self.data)
        print "Block Hash: " + str(self.hash)
        print "Block Previous: " + str(self.previous_hash)

区块链

初始化一条链

首先链上的第一个区块被称作”创世块”,这个区块仅作为整条链的始端。在本次实现中,当BlockChain实例化后,便自动创建创世块。

添加一个区块

为了向链上添加一个区块,需要先获取链上最后一个区块的hash码,然后计算新区块的hash码。get_last_block方法用来获取链的最后一个区块,当做添加新区块操作时,原来链上的最后一个区块就变成了上一个区块(相对于新区块来说)。

链的安全性

链在设计上防止被修改。确保链安全性的一部分是保证两个相邻区块不会被修改,例如区块#3的previous_hash码需要严格等于#2的hash码,本次实现中通过is_chain_valid方法进行校验。另一部分是确保块创建后数据不会被修改,如果数据更改,块本身对应hash码也会被修改(因为data是计算hash码的元素之一),hash码值的变更将被is_chain_valid方法校验出来。

class BlockChain:
    """
    区块链类
    """

    def __init__(self):
        """
        初始化函数
        """
        self.chain = [self.create_genesis_block()]

    def create_genesis_block(self):
        """
        创建创世块
        """
        return Block(0, "29/10/2018", "Genesis Block", "0")

    def get_last_block(self):
        """
        获取最近的一个区块
        """
        return self.chain[len(self.chain) - 1]

    def add_block(self, new_block):
        """
        添加区块
        """
        new_block.previous_hash = self.get_last_block().hash
        new_block.hash          = new_block.calculate_hash()
        self.chain.append(new_block)

    def is_chain_valid(self):
        """
        校验区块链是否合法
        """
        chain_len = len(self.chain)
        for i in range(1, chain_len):
            current_block  = self.chain[i]
            previous_block = self.chain[i-1]
            # 校验数据是否被篡改
            if current_block.hash != current_block.calculate_hash():
                return False
            if current_block.previous_hash != previous_block.hash:
                return False
        return True

    def print_block_chain(self):
        """
        输出区块链
        """
        chain_len = len(self.chain)
        for i in range(1, chain_len):
            self.chain[i].print_block()

测试区块链

测试上面写好的程序,创建一个名叫my_coin的BlockChain实例,并且向链上添加区块。通过is_chain_valid方法校验链的合法性,并试图改变某个区块的值,看是否能通过合法性校验。

if __name__ == "__main__":
    my_coin = BlockChain()
    my_coin.add_block(Block(1, "23/10/2018", {"account": "DoubleQ", "amount": 25, "action": "buy"}))
    my_coin.add_block(Block(2, "24/10/2018", {"account": "Eric", "amount": 10, "action": "buy"}))
    my_coin.add_block(Block(3, "25/10/2018", {"account": "Winky", "amount": 20, "action": "buy"}))
    my_coin.add_block(Block(4, "26/10/2018", {"account": "Xxz", "amount": 4, "action": "buy"}))
    my_coin.print_block_chain()
    print "Chain valid? " + str(my_coin.is_chain_valid())
    my_coin.chain[1].data = {"account": "DoubleQ", "amount": 100, "action": "buy"}
    print "Chain valid? " + str(my_coin.is_chain_valid())

输出如下:

Block #1
Data {'action': 'buy', 'account': 'DoubleQ', 'amount': 25}
Block Hash: 20d59499ef38bda24de15df140fd195b2627a9707453c6afd12458866ab5eba9
Block Previous: c3f4fe865ecc110ea0ed13fb3602beb2468eb2fdedc7a9aeb9eec245dd414763
Block #2
Data {'action': 'buy', 'account': 'Eric', 'amount': 10}
Block Hash: 0797ecc35e15f4758b635ee4053006bbb9a61ca9cba8876fbfa6d9a2c186a882
Block Previous: 20d59499ef38bda24de15df140fd195b2627a9707453c6afd12458866ab5eba9
Block #3
Data {'action': 'buy', 'account': 'Winky', 'amount': 20}
Block Hash: 28bf741c567028c4a3f3975aacb6f7e23561d240f06608cd1241d308fe40557b
Block Previous: 0797ecc35e15f4758b635ee4053006bbb9a61ca9cba8876fbfa6d9a2c186a882
Block #4
Data {'action': 'buy', 'account': 'Xxz', 'amount': 4}
Block Hash: 29eae18d6f72bd2203c7a3fdce3f688178aae8b371fd530ddffa67a042be41c1
Block Previous: 28bf741c567028c4a3f3975aacb6f7e23561d240f06608cd1241d308fe40557b
Chain valid? True
Chain valid? False

Git代码

传送门

参考资料

Learning about Block Chain with Python

微信好友画像

概述

主要基于python的wxpy库统计微信好友性别、省份、个性签名数据,基于pyecharts库作图表呈现。

相关知识

wxpy库

wxpy在itchat的基础上,通过大量接口优化提升了模块的易用性,并进行了丰富的功能扩展,主要用来实现各种微信个人号的自动化操作。例如:

  • 控制路由器、智能家居等具有开放接口的玩意儿
  • 运行脚本时自动把日志发送到你的微信
  • 加群主为好友,自动拉进群中
  • 跨号或跨群转发消息
  • 自动陪人聊天
  • 逗人玩
  • ……

pyecharts库

pyecharts 是一个用于生成 Echarts 图表的类库。Echarts 是百度开源的一个数据可视化 JS 库。用 Echarts 生成的图可视化效果非常棒,pyecharts 是为了与 Python 进行对接,方便在 Python 中直接使用数据生成图。

jieba库

“结巴”是python中文分词组件,支持三种分词模式:

  • 精确模式,试图将句子最精确地切开,适合文本分析;
  • 全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义;
  • 搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。

停用词表

综合了”百度停用词表”,”哈工大停用词表”,”四川大学机器学习实验室停用词表”等若干停用词表,取交集并去除了不需要的标点符号和英文单词。

代码思想

第一阶段

创建bot对象,获取全部微信好友

第二阶段

遍历好友信息,计算性别分布、省份分布、个性签名切词生成词云。其中词云处稍微复杂,需要过滤空格、html标签、emoj表情以及停用词。

第三阶段

分别获取性别饼状图、省份地图、词云三者的图表对象。词云图表在处理前对数据进行一次归一化。

第四阶段

创建Page类,将图表对象add进去,最后渲染页面

Git代码

传送门

参考资料

  1. wxpy: 用 Python 玩微信
  2. pyecharts 文档
  3. jieba
  4. 停用词表