金融中的机器学习

978-7-115-56309-5
作者: 简尼斯·克拉斯(Jannes Klaas)
译者: 曾荣飞
编辑: 胡俊英

图书目录:

详情

机器学习是设计与应用算法的科学,可从数据中进行学习和预测,其应用已经非常普遍。金融领域集中了大量的交易数据,为人工智能技术的运用奠定了良好的数据基础。本书面向金融领域的读者,介绍了机器学习技术的原理与实践。 本书包括10章,介绍了神经网络算法、结构化数据的处理、计算机视觉处理技术、时间序列分析、自然语言处理、生成模型的应用、强化学习技术、数据建模与调试、贝叶斯推理和概率编程等内容。 本书由资深金融从业者编写,融合了其在金融项目中关于机器学习的实践经验,适合金融领域的数据科学家、数据分析师、金融科技公司的技术研发人员以及对金融领域的机器学习技术感兴趣的读者阅读。

图书摘要

版权信息

书名:金融中的机器学习

ISBN:978-7-115-56309-5

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

著    [英] 简尼斯•克拉斯(Jannes Klaas)

译    曾荣飞

责任编辑 胡俊英

人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

读者服务热线:(010)81055410

反盗版热线:(010)81055315


Copyright ©2019 Packt Publishing. First published in the English language under the title Machine Learning for Finance.

All rights reserved.

本书由英国Packt Publishing公司授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式或任何手段复制和传播。

版权所有,侵权必究。


机器学习是设计与应用算法的科学,可从数据中进行学习和预测,其应用已经非常普遍。金融领域集中了大量的交易数据,为人工智能技术的运用奠定了良好的数据基础。本书面向金融领域的读者,介绍了机器学习技术的原理与实践。

本书包括10章,介绍了神经网络算法、结构化数据的处理、计算机视觉处理技术、时间序列分析、自然语言处理、生成模型的应用、强化学习技术、数据建模与调试、贝叶斯推理和概率编程等内容。

本书由资深金融从业者编写,融合了其在金融项目中关于机器学习的实践经验,适合金融领域的数据科学家、数据分析师、金融科技公司的技术研发人员以及对金融领域的机器学习技术感兴趣的读者阅读。


简尼斯•克拉斯(Jannes Klaas)是一名拥有金融学和经济学背景的量化分析师。他曾主导过两个机器学习训练营项目,也同研发数据驱动类应用的公司和交易策略类公司有过合作。

目前,他的研究领域包括系统风险和大规模自动化的知识挖掘。


詹姆斯•勒(James Le)是罗切斯特理工学院计算机专业的在读研究生。他的研究关注基于深度学习的计算机视觉领域,他还是数据科学领域的一名活跃的自由职业者,对机器学习、深度学习、推荐系统、数据分析和可视化非常感兴趣。


在大量可用数据计算资源的助力下,机器学习取得了长足的发展。金融行业的核心企业是信息处理类企业,而以这些公司为代表的金融业有大量机会来部署机器学习这项新兴技术。

本书是一本聚焦金融业的现代机器学习的实用指南。本书使用代码优先方法(code-first approach),阐述一些有用的机器学习算法的工作原理,并使用这些算法来解决现实世界中的问题。

三类读者将从本书受益:

本书假设读者具有线性代数、统计学、概率论、微积分等背景知识。但是,不需要成为上述各领域的专家,仅仅掌握基础知识即可。本书并不强求读者具备充分的金融知识。

为了学习示例代码,你应该熟悉Python语言和常用的数据科学相关库,如Pandas、NumPy和Matplotlib等。本书的示例代码使用Jupyter Notebook作为编辑器。

第1章“神经网络和基于梯度的优化”讨论机器学习包括哪些类别,以及在金融的不同细分领域中使用它们的背后动机。我们将学习神经网络的工作原理,并从头构建一个神经网络模型。

第2章“机器学习在结构化数据中的应用”处理诸如关系型数据库中具有固定字段的数据。我们将体验模型的创建过程:从一个启发式模型,到简单的基于特征工程的模型,再到完全基于学习的模型。另外,我们将学习如何使用scikit-learn来评估模型,如何训练基于树状的模型(如随机森林),以及如何使用Keras来构建神经网络模型。

第3章“计算机视觉的应用”讲述了计算机视觉让我们按照比例解释和观察现实世界的过程。在这一章中,我们将学习让计算机视觉识别图像内容的技术,还将学习卷积神经网络和Keras模块,并使用这些模块来设计和训练前沿的计算机视觉模型。

第4章“理解时间序列”讨论分析时间相关的数据所需的大部分工具。在这一章中,我们首先讨论产业界专业人士用于建模时间序列的精选工具,并讲述如何用Python高效地使用这些工具。我们将学习现代机器学习算法是如何在时间序列中挖掘各类模式的,并讨论如何通过传统技术来辅助补充现代机器学习技术。

第5章“用自然语言处理解析文本数据”讨论如何使用spaCy库和大量的新闻数据来让诸如命名实体识别和情感分析等常见任务可以快速高效地执行。我们将学习如何使用Keras库来创建定制化的语言模型。本章还介绍Keras函数式应用程序编程接口(Application Pragramming Interface,API),函数式API可以让我们构建更加复杂的模型(如不同语言之间的翻译模型)。

第6章“生成模型的应用”讲述了使用生成模型来生成数据。当我们没有充足的数据或希望通过模型观察数据并分析数据时,可以使用生成模型来生成数据。在这一章中,我们将学习(变分)自编码器和生成式对抗模型。我们将学习使用t-SNE算法来合理使用上述工具,使用它们来解决传统问题(如信用卡诈骗检测)。我们将通过机器学习来辅助人类的打标签操作,进而提高数据收集和打标签的效率。最后,使用主动学习来收集有用的数据并大大减少需要的数据量。

第7章“金融市场中的强化学习”讨论了强化学习。强化学习不需要人类打标签,可以“正确”地训练数据。本章讨论和实现几个强化学习算法,从Q-learning到A2C(Advantage Actor-Critic)算法。我们将讨论背后的理论、与经济领域的关联,并在实际的案例中了解强化学习如何直接用于挖掘组合的形成。

第8章“调试和发布产品”解决了在构建和出品复杂模型过程中可能遇到的各种问题。我们将讨论如何调试和测试数据,在训练模型过程中保持对数据隐私的敏感性,如何为训练准备数据,并且分析为什么模型会如此预测。我们将学习自动化调节模型超参,学习使用学习速率来减少过拟合,学习诊断和避免梯度消失和梯度爆炸问题。本章还解释了如何去监控和理解生产中的指标,并讨论如何提升建模速度。

第9章“挑战偏见”讨论了机器学习模型可能学习不公平的策略。本章强调了几种强化模型公平性的方法,包括中枢学习(pivot learning)和因果学习(causal learning)。本章展示如何检查模型和挖掘偏见。最后讨论模型所在的复杂系统中的不公平如何导致系统失败,并给出了检查清单来帮助模型减少偏见。

第10章“贝叶斯推理和概率编程”使用PyMC3来讨论使用概率编程的理论优势和实际优势。我们实现了一个采样器,来从数值角度理解贝叶斯理论,最后学习如何推断股票价格波动的分布。

CodeInText:这种字体表示文本中的代码、数据库表名、文件夹名、文件名、文件扩展、伪地址、用户输入、Twitter 用户名等。例如“Mount the downloaded WebStorm-10*.dmg disk image file as another disk in your system”。

本书中的代码块如下所示:

import numpy as np
x_train = np.expand_dims(x_train,-1)
x_test = np.expand_dims(x_test,-1)
x_train.shape

如果我们希望你注意代码中的某个部分时,相关代码行或代码内容设置为粗体。

from keras.models import Sequential
img_shape = (28,28,1)
model = Sequential()
model.add(Conv2D(6,3,input_shape=img_shape))

命令行的输入和输出如下所示:

Train on 60000 samples, validate on 10000 samples
Epoch 1/10
60000/60000 [==============================] - 22s 374us/step - loss:
7707.2773 - acc: 0.6556 - val_loss: 55.7280 - val_acc: 0.7322

 提示。

 注意。


本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。

本书提供配套资源(配套代码和彩色图片),要获得该配套资源,请在异步社区本书页面中点击,跳转到下载界面,按提示进行操作即可。注意:为保证购书读者的权益,该操作会给出相关提示,要求输入提取码进行验证。

作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。

当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,点击“提交勘误”,输入勘误信息,单击“提交”按钮即可。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。

我们的联系邮箱是contact@epubit.com.cn。

如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。

如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线投稿(直接访问www.epubit.com/selfpublish/submission即可)。

如果您所在的学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。

如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。

“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT技术图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT技术图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。

“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社近40年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、人工智能、测试、前端、网络技术等。

异步社区

微信服务号


从本质上来讲,金融服务业是一个基于信息处理的行业。投资基金通过信息处理来评估投资决策,保险公司通过信息处理来给保险产品定价,零售银行[1]通过信息处理来决定给客户提供何种产品。因此,金融行业成为最早采用计算机技术的行业并不意外。

第一台股票自动收报机发明于1867年,其实就是一台可打印的电报机。第一台机械加法机就是专门为金融业研发的,并于1885年获得专利。1971年,银行自动柜员机获得专利,这台机器允许用户使用塑料材质银行卡来提取现金。同年,第一个电子股票交易系统纳斯达克(NASDAQ)运行,市场开张。11年之后(即1982年),彭博终端开始安装使用。金融业和计算机业之所以能够“幸福”联姻的主要原因是,一个人在业内(尤其是投资业)的成功往往与其所拥有的信息优势密切相关。

在华尔街早期阶段,镀金时代的传奇人物们厚颜无耻地使用小道消息。例如,当时最富有的人之一Jay Gould就在美国政府安插内线。内线向Jay Gould提前发布美国政府黄金销售信息,并试图通过这种方式来影响时任美国总统尤利西斯•格兰特和他的秘书。在19世纪30年代末,针对上述问题,在投资者和信息优势方之间成立了美国证券交易委员会和美国商品期货交易委员会。

随着信息优势在上述市场中对业绩影响逐渐消失,巧妙的模型开始取而代之,并发挥作用。术语“对冲基金”于1973年被创造并使用;哈里马科维茨模型发表于1953年;布莱克-肖尔方程发表于1973年。随后,这个领域取得了长足的发展,人们也开发了各种包罗万象的金融产品。然而,随着这些模型知识的普及,使用模型的回报率也开始降低。

当我们同时看金融和现代计算技术,信息优势再度回归。这次信息优势不是以圈内人的小道信息和龌龊的交易形式存在,而是源于对大量公开信息的自动分析。

今天的基金经理相较以往会获得更多的信息,这是前辈们所梦寐以求的信息量。但是,单就可以获得更多信息这件事本身而言是没有用的。例如,我们先从新闻报告说起。你可以通过互联网轻而易举地获取新闻,但是,为了充分利用这些新闻,计算机需要阅读和理解,并将这些信息置于上下文背景中。计算机需要知道:这个新闻是讨论哪个公司的?新闻内容是好消息还是坏消息?这个公司和文章中提及的其它公司有什么关系?这只是将新闻置于上下文背景中的几个例子。那些能获取这些替代数据(alternative data)的公司通常具有竞争优势。

故事到这并没有结束。专业金融人士通常是能挣6~7位数字的薪水、在各个昂贵地段拥有办公空间的高端人士。这也验证了专业金融人士大多聪明、接受过良好的教育并且勤奋努力:这类人才也往往比较稀缺,市场对这类人有大量的需求。也正是因为这样,公司都希望最大化这些人的生产效率。管理者通过从这些最优秀的员工身上获得更多价值,使公司可以提供更低价或更丰富的产品。

交易所交易基金(Exchange Traded Fund,ETF)等被动投资不太需要对大量资金的主动管理。诸如跟踪标准普尔500指数(S&P500)的基金等被动投资工具的费率往往低于1%。但随着现代计算技术的兴起,公司可以提升资金管理者的工作生产效率,进而减少产品费率来保持竞争力。

本书不仅仅是关于金融业的投资与交易,更是计算机与金融相结合的最直接成果。投资公司的客户包括保险公司、养老金等,这些客户本身也是金融类服务公司,而他们的客户包括拥有养老金的普通民众和受保险人。大部分银行客户就是日常百姓,人们与银行、保险公司、养老金管理者的交互越来越多地通过客户手机上的App(应用程序)进行。

在过去的20年里,零售银行商都是基于这样一个事实来运营:人们愿意到分支网点去面对面地提取现金或进行交易。当顾客们在分支网点的时候,他们的投资顾问还向他们销售抵押贷款和保险等产品。今天,顾客仍然愿意购买抵押贷款、保险等产品,但是他们并不需要亲自到网点购买这些产品。今天,银行倾向于推荐顾客通过手机App或者网站来在线购买产品。

在线销售的前提是银行能够通过用户数据很好地理解用户的实际需求,并为他们提供定制化的线上服务体验。同样地,从顾客角度来看,他们希望能够通过电话来提交保险订单并立即得到响应。今天,保险公司需要通过自动评估保险订单并形成决策来满足用户的需求。

本书不会教你如何编写能够快速挣钱的交易算法,而会谈到在金融行业中构建机器学习驱动的系统所需的技术与技巧。

构建任何有价值的东西都需要时间和努力。当下,市场在构建这种有价值的东西(比如经济学)方面是非常低效的。机器学习的应用将会在未来的数十年里变革金融行业。这本书为你提供参与这场变革所需的工具。

本书中很多例子使用了非金融领域数据。这本书没有使用股票市场数据,主要有3个原因。

(1)本书呈现的例子证明了这些技术可以很容易地应用到其它数据集。因此,数据集的选择既要容易计算,也要向像你一样的专业人士展现一些共性的问题。

(2)金融数据本质上来讲是具有时效性的。为了让本书在更长时间范围内具有价值,也为了在机器学习仍非常重要的情况下,本书能持续作为你的工具书,我们使用了相关但非金融的数据。

(3)使用可替代和非经典的数据也是为了激励读者思考:在程序中可以使用哪些其它的数据?你能否通过植物的航拍画面来扩充你的谷物定价模型?你能否使用Web浏览器的行为数据来提供金融产品?如果你想充分利用身边的数据,跳出定势来思考是你必须具备的能力。

“作为计算机科学的子领域,机器学习可以使计算机在没有被显式编程的情况下具有学习能力。”——亚瑟•塞缪尔(Arthur Samuel)

我们说的机器学习具体指的是什么?今天,大部分计算机程序都是人编写的。软件工程师们仔细构建着那些控制软件行为的规则,并将这些规则编写成计算机代码。

如果你读的是本书的电子书版本,那么请马上看一下屏幕。你所看到的任何东西都是由软件工程师编写的规则所控制。这种方式已经让我们走得很远,但也不是说这种方式没有局限性。有的时候,可能存在太多的规则需要人们来构建。我们甚至无法思考这些规则,这些规则对聪明的开发人员来说都难以应对。

作为一个简单的练习,请用一分钟来想一想能描述所有狗的一系列规则,这些规则可以区分狗与其它动物。用毛来区分么?当然,猫也有毛!当狗穿上外套怎么样呢?当然还是狗,只是穿上了外套。研究人员花费许多年时间来构建这些规则,但是他们并没有取得成功。

人们好像并不能很完美地说出为什么这就是狗,但是当人们看到狗的时候就知道这就是狗。作为一个物种,我们似乎能检测那些特定的、难以描述的模式,总体上,这些模式能够让我们识别出狗。机器学习也尝试做同样的工作。我们让计算机通过模式检测来开发自己的规则集,而不是传统地通过手工构建规则集。

有不同的方法可以实现上述目标。这里,我们关注3类不同的学习方法:监督学习、非监督学习和强化学习。

现在让我们再回顾一下狗分类器的例子。事实上,当下有许多类似的分类器在使用。如果你使用Google Images这个应用,并搜索“狗”这个关键字,Google Images就会使用分类器来给你呈现狗的图片,这些分类器就是在监督学习的框架下训练的。

在监督学习中,我们有诸如动物图片这样的大量训练实例,以及希望通过训练这些实例获得的预期成果的标签。在图1.1中,前面图片就与标签“狗”关联一起,猫的图片就与标签“不是狗”的关联在一起。

图1.1 监督学习

如果我们有大量带标签的训练实例,就可以训练一个分类器,这个分类器可以检测到那些可以区分狗和其它动物的细微统计模式。

 

 

注意:

分类器并不知道狗到底是什么,它只知道那些能够将标签“狗”和图片链接起来的统计模式。

 

如果监督学习分类器碰到了与训练数据完全不同的东西,那么分类器将会失效并输出无效结果。

监督学习在过去的几年里取得了长足的发展,本书大部分内容也聚焦那些拥有带标签实例的任务。但是,对于某些案例,我们可能并没有标签。在这种情况下,我们仍然可以使用机器学习来挖掘数据中的隐藏模式。

想象一个公司的产品有大量顾客。在图1.2中,这些顾客被划分到不同的市场区隔(market segment),但是我们并不知道具体的市场区隔有哪些。我们也不能向客户询问他们到底应该属于哪个市场区隔,因为他们也不知道。你到底属于洗发液市场的哪个市场区隔?你知道那些洗发液公司是如何划分顾客的市场区隔的吗?

图1.2 聚类是非监督学习的常见形式

在这个例子中,你需要一个算法,它能分析大量的用户数据,并将用户划分到不同的市场区隔。这就是非监督学习的应用案例。

非监督学习的发展速度远远慢于监督学习,但是它仍然非常具有潜力。

在强化学习中,我们训练那些实际场景中的行为。尽管没有标签,我们也不能指出任何情况的正确行动,但是我们可以分配奖励和惩罚,具体如图1.3所示。例如,我们会对与前车保持足够距离的行为进行奖励。

图1.3 强化学习

一个驾驶教练不会这样告诉学员:“当你向右打方向盘,转到2°的时候,踩刹车踩到一半。”但是,当学员自己摸索出准确的刹车量时,教练会告诉他们做得好坏与否。

在过去几十年里,强化学习也取得了显着的进步,并且被许多人认为是通往通用人工智能的康庄大道,通用人工智能就是让计算机像人类一样聪明。

2009年,谷歌工程师发表了一篇题为“The unreasonable effectiveness of data”的里程碑式论文。在这篇文章中,作者描述了一个已经存在已久的、相对简单的机器学习系统在使用了Google服务器上非常庞大的数据进行训练之后,展现出非常好的性能提升。事实上,作者发现当使用更多的数据来训练时,这些简单系统可以掌握和完成那些之前被认为不可能的任务。

从那时开始,研究人员开始快速重新回顾古老的机器学习技术,发现人工神经网络在使用大量数据集进行训练时表现非常好。大概也是在同一时期,计算资源变得足够廉价和充裕,以至于可以训练比以前更大的网络模型。

这些更大规模的人工神经网络如此有效,人们给它们取名为“深度神经网络”或“深度学习”。深度神经网络在模式检测方面具有独特优势,它们能够发现诸如在图片中识别人脸的光线与灰度信息这样的统计模式。在有足够多数据的情况下,它们甚至可以自动发现这些模式。

因此,机器学习也被认为是人们改变计算机编程的新范例。与以往手动编写规则不同,我们给计算机提供大量的数据信息来训练它给自己编写规则。

当有大量的规则需要编写,或者这些规则比较难描述的时候,这种方式是非常好的。因此,现代机器学习也被看作梳理海量数据的理想工具,金融行业恰好盛产这类数据。

统计学里有一种说法:“所有模型都是错的,但有些模型却是有用的。”机器学习构建了极其复杂的统计模型,像深度学习这样的统计模型往往对人们是不可解释的。这些模型当然有用且有很大价值,但是它们仍然是错误的。原因如下:这些模型是复杂的黑盒;尽管人们应该去质疑黑盒模型的准确性,但倾向不去质疑机器学习的模式。

即使最复杂的深度神经网络也会给出错误的预测,就像2008年金融危机中先进的担保债务凭证(Collateralized Debt Obligation,CDO)模型所做的那样。更糟糕的是,那些黑盒机器学习模型每天做着数百万次贷款审批和保险决策,影响着人们的日常生活,但最终它们仍可能作出错误的决策。

有的时候,模型也是有偏差的。机器学习模型的表现往往与我们给模型提供的数据一样;数据一般来说是有偏见的,正如它所呈现的内容。这些内容我们会在本章后面充分考虑。这些内容是必须花费大量时间去关注和解决的,如果我们在没有任何意识的情况下就去部署了这些算法,那么最终也会带有偏见,这甚至有可能导致另一场金融危机。

金融行业尤其是这样。在金融行业,算法对人们的日常生活有着非常重要的影响,而这些日常生活往往具有一定的隐私性。这些不被质疑的秘密黑盒通过大量使用数学工具而被广泛接受,它们远比电影中具有自我意识的、接管整个世界的人工智能具有更大的威胁。

尽管这不是一本伦理学书籍,但是让行业内的从业人员知道自己工作的伦理道德与意义本身就是有意义的事情。此外,建议你去阅读凯西•奥尼尔的Weapons of math destruction这本书,同时也请深入研读“希波克拉底誓言”。希波克拉底誓言是由伊曼纽尔•德曼和保罗•威尔莫特两位量化研究人员于2008年金融危机劫后提出的:

“我始终牢记世界并非我所造,且这个条件也无法满足我的方程。尽管我将大胆使用模型来估算价值,但不会过分倚重于数据分析。我永远不会为了追求理论的精辟而不惜忽视现实,除非有充分的理由。我也不会对那些错误使用我的模型的人们给出关于模型精度的慰藉。相反,我将明确指出模型中的假设条件和忽略因素;我也明白自己的工作可能将会对社会和经济产生巨大影响,但其中的许多影响超出了我的认知范畴。”

近年来,机器学习取得了一系列大的飞跃,也让研究人员完成了之前被认为不可能完成的任务。从在图片中识别物体,到语音识别与翻译,再到像AlphaGO这样的棋类高手,人工智能在解决在一系列难题上已经跟人类或者未来将会跟人类旗鼓相当,甚至有可能比人类有更好的表现。

有趣的是,深度学习是这些巨大进步背后的关键方法。事实上,大部分进步源自深度学习的子领域——深度神经网络。尽管许多从业人员都熟悉诸如“回归”这样标准的经济学模型,但是很少有人熟悉深度学习模型。

本书的大部分内容都是关于深度学习的。深度学习是机器学习中最有希望和潜力的技术,深度学习也会让人们拥有完成之前认为不可能完成任务的能力。

在本章中,为了让读者更好地了解深度学习,我们将探索深度学习的运行机制,以及深度学习行之有效的原因。

在我们真正开始之前,你需要创建自己的工作区。本书中的所有例子都可以在Jupyter Notebook中运行。Jupyter Notebook是一款开发数据科学类应用所经常使用的交互式开发环境,也被认为是嵌入数据驱动应用所必备的环境。

你可以在本地计算机上运行Jupyter Notebook,也可以在云端服务器上运行,或在诸如Kaggle等网站上运行Jupyter Notebook应用。

深度学习是一种计算密集型的运算,并且书中所有例子使用的数据量大小通常都是以吉字节(GB)为单位。深度学习可以使用图形处理器(Graphics Processing Unit,GPU)来加速计算,GPU主要是为了渲染视频和游戏而发明设计的硬件芯片。如果你拥有一台可用GPU加速的计算机,那么你就可以在本地运行这些示例程序。如果你没有这样的计算机,建议使用诸如Kaggle内核这类的服务。

掌握深度学习是一项花费非常昂贵的任务,因为GPU本身就是非常贵重的硬件设备。尽管说有更廉价的选项可用,但是一般来说,如果你想买一个功能强大的GPU,费用高达1万美元;如果你想在云端租用一个GPU,费用大约每小时0.8美元。

如果你有许多需要长期运行的训练任务,那么考虑构建一个深度学习的盒子——具有GPU加速功能的台式计算机,就显得非常有意义了。网上有许多相关说明书,你可以花费几百美元到几千美元的费用来组建一个像模像样的盒子[2]

书中所有例子都可以在Kaggle平台上免费运行。事实上,这些例子就是使用这个网站开发的。

Kaggle是谷歌旗下一个热门的数据科学网站。Kaggle源于一项赛事,在比赛中,所有选手都需要构建机器学习模型对某项任务进行预测。随着时间流逝,Kaggle平台已经拥有热门论坛、在线学习系统以及对我们非常重要的Jupyter服务。

你可以访问Kaggle网站并创建一个账号来使用Kaggle。当完成账号创建后,点击主菜单中的Kernels按钮来打开Kernels页面。具体如图1.4所示。

图1.4 已发布的Kaggle内核

在上面的截图中,你可以看到许多他人写完并发布的内核(kernel)。内核可以是私有的,但是发布内核是一个展示和分享知识的好方法。

如图1.5所示,点击New Kernel来创建新内核。在随后的对话框中,你需要选择Notebook选项。此时,你将打开内核编辑器(the kernel editor),这个页面看起来有点像上面这个屏幕快照。

注意,Kaggle平台会在内核设计上进行主动迭代。因此,有一小部分元素的位置会发生变化,但基本功能都是一样的。Notebook文件中最重要的部分就是编码单元格(code cell)。在这里,你可以输入代码,然后点击左下的运行按钮来运行程序,或者使用Shift+Enter快捷键来运行程序。

单元格中定义的变量都是环境变量,因此你可以在其它单元格中访问它们。文本单元格(Markdown cell)允许用户以Markdown格式来输入文字,这些文字可以描述代码的功能。你可以通过点击右上角的云按钮来上传和下载Notebook文件。

图1.5 Kernel编辑器

如果你想在内核编辑器页面中发布Notebook文件,需要点击Commit & Run按钮,并在Notebook文件配置选项中设置为Public状态。如果你想在Notebook文件中开启GPU功能,请点击右下角的GPU开关。需要特别记住的是开启GPU功能将会重启Notebook文件,也导致环境变量的数据丢失。

一旦你开始运行代码,运行按钮就变成停止按钮。如果代码有bug,你可以单击停止按钮来终止程序。如果你想抹掉所有环境变量数据并重新开始,只需要简单地点击右下角的重启按钮。

在Kaggle系统平台上,你可以将一个内核链接到平台上的任何数据集,或者你也可以随时上传新的数据集。书中所有例子的Notebook文件都已经与相关数据关联在 一起。

Kaggle内核已经预安装了那些会经常用的包资源,因此大部分时间你都不用担心安装包资源的问题。

本书有些例子也会用到Kaggle平台没有默认安装的自定义的包资源。在这种情况下,你可以通过设置(Settings)菜单来增加自定义包资源。当本书需要使用自定义包时,我们会提供相关包安装说明。

Kaggle内核可以免费使用,且可以给你节省很多资金和时间,所以建议你在Kaggle平台上运行例子程序。注意,Kaggle内核可以运行的程序最长6小时。

如果你有一台功能强大到可以运行深度学习计算的计算机,就可以本地运行示例程序。在这种情况下,强烈建议你通过Anaconda安装Jupyter。

请访问Anaconda官网,下载Anaconda的分发版本并安装。图形化安装工具会引导你一步一步在本地安装Anaconda。在安装Anaconda过程中,你也需要安装许多有用的Python库,例如NumPy库、Matplotlib库等,这些库在本书中会使用到。

当Anaconda安装完毕后,你可以打开本地计算机的终端控制台,并输入如下命令来启动Jupyter服务器:

$ jupyter notebook

然后,你可以访问终端控制台显示的URL链接,它将会引导你打开本地Notebook服务器。点击右上角的New按钮来创建一个新的Notebook文件。

书中所有例子的代码都使用Python 3,请确保本地Notebook文件中使用了该版本的Python。如果你在本地运行Notebook文件,还需要安装TensorFlow和Keras,这两个深度学习库在本书中将会经常用到。

(1)安装TensorFlow

在安装Keras之前,你需要先安装TensorFlow。你可以打开一个控制台窗口,并输入如下命令来安装TensorFlow:

$ sudo pip install TensorFlow

获取安装具有GPU加速功能的TensorFlow库的安装说明,请访问TensorFlow网站。

需要注意的是,你需要开启CUDA功能的GPU,才能运行支持CUDA的TensorFlow。获取安装CUDA的相关说明,请访问其官网。

(2)安装Keras

当安装TensorFlow完毕后,你可以按照同样的方式安装Keras。在控制台窗口中输入以下命令:

$ sudo pip install Keras

Keras将会自动使用TensorFlow后端。需要注意的是TensorFlow 1.7内嵌了Keras库。关于Keras与TensorFlow,我们将在本章后面讨论。

(3)使用本地数据

为了在本地使用书中示例程序对应的数据,请访问Kaggle平台的Notebook文件,并从平台下载对应的数据。需要注意的是,数据文件的路径依赖于你保存文件的具体地址。当你在本地运行Notebook文件的时候,需要替换具体数据文件的路径。Kaggle也提供命令行接口,让你更便捷地下载数据。请访问GitHub网站Kaggle页面获取相关说明。

亚马逊Web服务(Amagon Web Services,AWS)提供了一种便捷使用、可以预先配置的方式,能让你在云平台上运行深度学习程序。

访问亚马逊机器学习网站,获取创建亚马逊机器镜像(Amagon Machine Image,AMI)的相关说明。虽然AMI是收费业务,但是AMI的运行时间比Kaggle内核更长。对于较大的机器学习任务,使用AMI可能比使用Kaggle内核更好。

在AMI上运行本书中Notebook文件,你首先需要创建AMI,然后从GitHub平台下载Notebook文件,并将下载文件上传到AMI平台。当然,你还需要从Kaggle平台下载相关数据。

关于如何评价神经网络,有不同的观点,但是也许最有用的方法就是把神经网络看成函数的近似。从数学角度来看,函数就是将输入关联到输出。我们可以将函数写成如下形式:

最简单的函数诸如:

在这个函数中,我们给函数一个输入,它就输出乘以4之后的数值,例如:

你可能见过类似的函数,但是函数可以有更多功能。例如,函数可以将一个集合中的元素映像到另一个集合中的元素,其中第一个集合的值是可以被函数所接受的。集合可以是比数字更复杂的任何东西。

例如,函数可以将图片映像成图片中内容的标识:

这个函数会将一个猫的图片映射成“猫”这个标签,具体如图1.6所示。

图1.6 从图像到标签的映射

注意,图像在计算机中本质上是充满数字的矩阵,而图像内容的描述也是以数字矩阵的形式存储。

如果一个神经网络足够大,它就可以近似任何函数。已经从数学角度证明了一个无限大的神经网络可以近似每一个函数。尽管我们不需要使用一个无限大的神经网络,但是我们一定会优先使用大的神经网络。

现代深度学习架构一般都会有几十甚至几百层,参数多达数百万个,因此仅仅存储模型就需要数GB空间。这就意味着一个神经网络,只要足够大就可以近似我们那个从图像映射到内容的函数

神经网络需要足够大的这个条件也解释了为什么大的深度神经网络已经取得长足的发展。事实上,足够大的神经网络可以近似任何函数,这也意味着它们可以在很多任务中都是非常有用的。

在本书中,我们将会构建一个功能强大的神经网络,它能近似极其复杂的函数。我们将会实现从文本到命名实体的映射,从图片到内容的映射,甚至从新闻文章到文章摘要的映射。但是当下,我们将会使用逻辑回归来解决一个简单的问题。逻辑回归是一项在金融和经济领域中常用的技术。

我们来解决一个简单的问题。已知输入矩阵为,我们想输出矩阵的第一列,结果记为。为了更直观地理解这个问题的本质,我们从数学角度来讨论这个问题。

在本章的后面部分中,我们将会用Python工具来实现具体论述。我们已经知道,训练神经网络需要数据,表1.1的数据将是我们这个例子所使用的数据集。

表1.1 数据集

0

1

0

0

1

0

0

1

1

1

1

1

0

1

1

0

在这个数据集中,每行都是一个输入向量和输出

已有数据满足如下公式

我们想近似的函数可以表示成

在这个例子中,我们可以很直接地写出函数的具体形式。但是请记住,在大部分情况下,我们并不能直接给出函数的表达式,因为深度神经网络所表达的近似函数实在是太复杂了。

对于这个简单函数,一个仅有一层的浅层神经网络就足够了。这个浅层神经网络就称为逻辑回归器。

如上所述,最简单的神经网络就是逻辑回归器。逻辑回归接受任何范围内的输入数据,并输出一个从0到1之间的结果。逻辑回归器适合一系列应用,一个简单的例子就是预测房主抵押贷款后违约的可能性。

我们在预测某人债务违约可能性的时候,会考虑各种输入数据,例如债务人的薪水、是否有车、是否在高危行业工作,等等。但是违约可能性的输出值就是0和1之间的值。即使最差的债务人违约可能性也不能超过100%,最好的债务人违约可能性也不能低于0%。

图1.7描述了一个逻辑回归器。是输入向量,包括3个部分

图1.7 逻辑回归器

向量是3个输入部分的权重。你可以把它想象成3个线条的宽度。决定了每个值以多大比重输入到下一层,是偏移量,它可以增大和减少本层的输出。

为了计算逻辑回归器的输出,我们需要先进行线性运算。我们计算输入向量和权重向量的点乘运算。具体来说,就是将的对应元素相乘并求和,然后加上偏移量。接下来,我们再进行非线性运算。

在非线性运算阶段,我们将对线性部分的中间结果使用激活函数。在这个例子中,激活函数是Sigmoid函数。Sigmoid函数将输入映射成一个从0到1的输出,如图1.8所示。

图1.8 Sigmoid函数

如果前面的数学对你来说有点太生涩难懂的话,接下来,我们将使用Python工具来做同样的事情。在这个例子中,我们将使用NumPy库,这个库可以在Python内进行简单、快速的矩阵运算。

NumPy已经与Anaconda一起预装了,并且在Kaggle内核平台上也预装了NumPy。为了确保所有实验得到同样的结果,我们需要设置一个随机种子,具体执行如下代码:

import numpy as np
np.random.seed(1)

因为这里数据集非常小,我们就将它手动定义成一个NumPy矩阵,如下所示:

X = np.array([[0,1,0],
              [1,0,0],
              [1,1,1],
              [0,1,1]])

y = np.array([[0,1,1,0]]).T

我们定义Sigmoid激活函数,该函数把所有输入值转换成0和1之间的值。把激活函数看成普通的Python函数来定义Sigmoid函数,如下:

def sigmoid(x):
    return 1/(1+np.exp(-x))

到目前为止还算不错。现在,我们需要初始化参数。在这个例子中,我们已经知道了的具体值。但是在其它问题中,我们并不知道函数具体形式和权重。所以,我们需要随机指定权重。

权重一般都是随机初始化,权重的均值为0,偏移量也默认设置为0。NumPy库random函数的输入是以数组的形式来定义随机矩阵的维度。用random((3,1))创建一个3×1的矩阵。默认情况下,生成的随机值都是在0和1之间,平均值为0.5,标准差为0.5。

如果我们希望生成随机数的均值为0,标准差为1,我们需要对之前生成的随机数乘2减1。可以通过执行如下代码来实现:

W = 2*np.random.random((3,1)) - 1 
b = 0

至此,所有变量都已初始化完毕。下面开始进行线性运算,具体代码如下:

z = X.dot(W) + b

接下来,非线性运算的代码具体如下:

A = sigmoid(z)

如果我们输出,将会得到如下输出结果:

print(A) 

[[ 0.60841366]
 [ 0.45860596]
 [ 0.3262757 ]
 [ 0.36375058]]

这个结果看起来根本不像我们期望的输出结果!很明显,逻辑回归器描述了某个函数,但是这个函数与我们期望的函数相差甚远。

为了更好地近似我们期望的函数,我们需要调整权重和偏移量。我们将会在1.12节“优化模型参数”来实现上述目标。

我们已经看到了,为了更好地近似我们期望的函数,需要联调权重和偏移量,它们都是模型的参数。

换句话说,我们需要遍历这个模型所描述的所有函数空间,并找到那个能够匹配预期函数的最近似函数

但是,我们怎么知道两个函数有多近似?事实上,我们不知道函数,也就不能知道函数对预期函数的近似度。但是,我们可以测试函数的输出与函数的输出之间的近似度。函数对于输入的输出是标签,所以,我们找到输入变量输出为标签的函数,通过这种方法就可以近似函数

我们知道下面公式成立:

我们也有:

可以通过优化下列公式来找到预期函数

在这个公式中,是模型所能描述的函数空间,也称假设空间;函数是距离函数,用来评估函数的近似度。

 

 

提示:

这个方法有一个关键的假设是输入数据和标签描述了我们的预期函数。实际情况并不是永远这样。当输入数据有误差时,我们可能获得一个能很好的匹配输入数据的函数,但是这个函数与我们期望函数相差甚远。

 

优化模型参数的典型例子就是人力资源管理。假设你在构建一个能预测债务人贷款违约可能性的模型,并使用这个模型来决定谁将获得贷款。

你将会使用过去几年银行经理所做的贷款决策作为训练数据。然而,这样数据也存在问题,因为这些经理们是有主观偏见的。例如,从历史数据来看,一些消费收入较低的人更难获得贷款。

如果使用这样的训练数据,那么我们的函数也会具有这些偏见。我们将会获得一个真实反映或者放大人类偏见的函数,而不是一个可以很好预测哪些是优质债权人的函数。

我们经常会错误地认为神经网络会找到我们希望找到的那个易于理解的函数。实际上,神经网络找到的函数是最匹配训练数据的函数,而与我们是否希望找到没有任何关系。

如前所述,我们通过最小化距离函数来优化模型参数,这个距离函数也被称为损失函数,用于评价潜在函数的性能。在机器学习中,损失函数测量了模型最坏的性能情况。一个高损失函数与低准确率密切相关;反之如果如果损失函数越小,模型准确率越高。

在这个例子中,我们的问题是二分类问题。因此,我们使用二元交叉熵损失函数,具体形式如下所示:

让我们一步一步来看这个公式。

在Python中,损失函数通过如下代码实现:

def bce_loss(y,y_hat):
  N = y.shape[0]
  loss = -1/N * (y*np.log(y_hat) + (1 - y)*np.log(1-y_hat))
  return loss

逻辑回归器的输出等于,所以我们可以按照下面方法计算二值交叉熵:

loss = bce_loss(y,A)
print(loss)

0.82232258208779863

正如我们所看到的,这个损失非常大,所以我们需要思考如何优化模型。我们的目标是让模型损失为0,或至少接近0。

你可以把不同函数假设的损失看成一个面,这个面有时也被称为损失面(loss surface)。损失面有点像山脉,在山峰上具有高点,在峡谷中具有低点。

我们的目标就是在山脉中寻找到绝对的最低点,也就是最低的峡谷,或者全局最小值。全局最小值就是在函数假设空间内损失值最小的点。

相反,局部最小值就是那些比周边空间点都小的损失值。局部最小值对应的函数是有问题的,因为它们从表面上看像是可用的好函数,但是实际上有更多的好函数可供使用。记住,我们通过梯度下降来遍历空间,梯度下降方法将会在我们的函数空间内找到最小值。

现在我们已经知道了如何评价候选模型,但是如何去调整模型参数来获得更好的模型呢?神经网络中最主流的优化算法就是梯度下降。使用这个方法,我们可以沿着损失函数的微分(也就是斜率)慢慢移动寻找。

设想一下你在山林中远足,现在你所在的位置点没有任何道路信息,你现在在丛林中想要找到谷底。问题是有许多树遮挡了视野,导致你看不清谷底在哪,你只能看到脚下的地面。

现在问问自己,怎么能够找到通向谷底的路?一个直观的方法就是沿着坡往下走。哪个地方有向下的坡,你就往哪个方向走。梯度下降也采用同样的方法。

再回到我们的假设问题中,在森林场景下的损失函数就是山。你为了取得更小的损失值,你需要沿着下坡路走。所谓坡也就是损失函数的微分。当我们在山上往下走的过程中,我们在持续更新所在位置的坐标点。

这个算法更新了神经网络的参数,如图1.9所示。

图1.9 梯度下降

梯度下降需要损失函数对我们所需要优化的参数是可微的。大部分监督学习问题都可以比较好地应用梯度下降,但当我们解决不明显可微的函数时,就存在比较严重的问题。

梯度下降可以优化模型参数、权重、偏移量等,但不能优化的是模型有多少层,或者应该使用什么样激活函数。这是因为没有办法计算不同模型拓扑对应的梯度。梯度下降方法所不能优化的参数配置称之为超参,超参一般需要人为设置。

你已经看到了如何一步步缩小损失函数的值,但如何更新模型参数呢?为了实现这个目标,我们需要另一个方法——反向传播(backpropagation)。

反向传播可以让我们实现将梯度下降所带来的更新应用到模型参数中。为了更新参数,我们需要计算损失函数对权重和偏移量的微分。

设想下模型参数就像我们在山林中的地理坐标,计算损失函数相对于参数的微分就相当于思考山脉在北向的坡度,坡度的思考帮助我们决定是往北走还是往南走。

图1.10说明了逻辑回归器的前向和反向传递。

图1.10 逻辑回归的前向和反向传递

为了简便,我们将损失函数对任何变量的微分记为d。例如,将损失函数相对于权重的微分记录为

我们使用链式法则(chain rule)来计算模型相对于不同参数的梯度。链式法则[3]如下:

上面公式有时也写成以下形式:

链式法则本质上是说,你对嵌套复合函数求导,可以通过内部函数的导数乘以外部函数的导数来计算。

因为神经网络和逻辑回归器都是嵌套的复合函数,因此链式法则非常有用。输入首先经过线性运算,线性运算是输入,权重和偏移量的函数;线性运算的输出需要再输入到激活函数中。

当我们计算损失函数相对于权重和偏移量的微分时,需要先计算损失函数相对于线性阶段输出的微分,然后使用这个结果来计算相对于权重的微分。具体代码如下:

dz = (A - y)
dW = 1/N * np.dot(X.T,dz)
db = 1/N * np.sum(dz,axis=0,keepdims=True)

现在我们已经有了梯度,如何更新模型参数?让我们再回到山林找路的场景,现在我们知道了山脉在北和东的方向是上升的,我们往哪个方向走?答案当然是南和西。

从数学角度来看,我们朝向梯度的反方向走。如果某个参数的梯度是正,那么就是说坡度是升高的,我们需要减少参数。如果某个参数的梯度是负,也就是下坡,我们需要增加参数。当坡度越陡,我们在梯度方向上的移动得越快。

对参数的更新规则如下所示:

其中,是模型的参数(既可以是权重,也可以是偏移量),是损失函数相对于的微分,是学习速率。

学习速率有点像汽车的汽油踏板,它设置了我们使用梯度更新来修订参数的比例。学习速率也是一个超参,需要手动设置。我们将会在第2章中继续讨论学习速率。

参数更新的具体代码如下:

alpha = 1
W -= alpha * dW
b -= alpha * db

我们已经分析了训练神经网络所需的所有细节部分。本章前面的几个步骤中,我们训练一个单层的神经网络,也就是逻辑回归器。

在定义数据之前,我们首先导入了NumPy库,我们通过执行如下代码实现上述 任务:

import numpy as np
np.random.seed(1)

X = np.array([[0,1,0],
              [1,0,0],
              [1,1,1],
              [0,1,1]])

y = np.array([[0,1,1,0]]).T

下一步就是定义Sigmoid激活函数和损失函数,具体代码如下:

def sigmoid(x):
    return 1/(1+np.exp(-x)) 

def bce_loss(y,y_hat):
    N = y.shape[0]
    loss = -1/N * np.sum((y*np.log(y_hat) + (1 - y)*np.log(1-y_hat)))
    return loss

接下来,我们初始化模型,代码如下:

W = 2*np.random.random((3,1)) - 1
b = 0

作为一个必要步骤,我们需要设置一些超参。第一个超参就是步长,这里我们设置为1。越大意味着模型训练得越快,目标很快就能够实现。相反,更小的让梯度下降训练得更加仔细,找到那些容易被忽视的小峡谷。

第二个超参是运行训练的次数,也是我们希望训练的轮次。我们通过如下代码来设置:

alpha = 1
epochs = 20

既然是训练中所用,也有必要定义数据样本的数量。我们还定义了一个空数组来记录模型随时时间的损失变化情况。具体代码如下:

N = y.shape[0]
losses = []

现在,训练模型的主体如下:

for i in range(epochs):
    # Forward pass
    z = X.dot(W) + b
    A = sigmoid(z)

    # Calculate loss
    loss = bce_loss(y,A)
    print('Epoch:',i,'Loss:',loss)
    losses.append(loss)

    # Calculate derivatives
    dz = (A - y)
    dW = 1/N * np.dot(X.T,dz)
    db = 1/N * np.sum(dz,axis=0,keepdims=True)

    # Parameter updates
    W -= alpha * dW
    b -= alpha * db

上面代码将会获得如下执行结果。

Epoch: 0 Loss: 0.822322582088
Epoch: 1 Loss: 0.722897448125
Epoch: 2 Loss: 0.646837651208
Epoch: 3 Loss: 0.584116122241
Epoch: 4 Loss: 0.530908161024
Epoch: 5 Loss: 0.48523717872
Epoch: 6 Loss: 0.445747750118
Epoch: 7 Loss: 0.411391164148
Epoch: 8 Loss: 0.381326093762
Epoch: 9 Loss: 0.354869998127
Epoch: 10 Loss: 0.331466036109
Epoch: 11 Loss: 0.310657702141
Epoch: 12 Loss: 0.292068863232
Epoch: 13 Loss: 0.275387990352
Epoch: 14 Loss: 0.260355695915
Epoch: 15 Loss: 0.246754868981
Epoch: 16 Loss: 0.234402844624
Epoch: 17 Loss: 0.22314516463
Epoch: 18 Loss: 0.21285058467
Epoch: 19 Loss: 0.203407060401

在上面的结果中,你可以看到训练过程的输出结果。损失值从0.822322582088开始逐步减少,最终损失值为0.203407060401。

为了更直观地看到结果,我们将损失值绘制成图表格式。绘图代码如下:

import matplotlib.pyplot as plt
plt.plot(losses)
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

输出如图1.11所示。

图1.11 前面代码的执行结果,损失率随着时间在减少

在本章前面已介绍过,我们需要构建了一个更大、更复杂的深度网络来近似一个更复杂的函数。创建深度网络需要叠加更多层。

在本章中,我们将会创建一个两层的神经网络,如图1.12所示。

图1.12 两层神经网络的架构

输入数据乘以第一层的权重产生中间结果;中间结果通过激活函数,生成第一层激活后的结果

这些激活后的结果再乘以第二层权重,得到中间结果;中间结果经过第二个激活函数,得到整个神经网络的最终输出结果

z1 = X.dot(W1) + b1
a1 = np.tanh(z1)
z2 = a1.dot(W2) + b2
a2 = sigmoid(z2)

 

 

提示:

这个例子的代码可以在GitHub的本书项目中寻找。

 

正如你所见,第一个激活函数并不是Sigmoid函数,而是tanh函数,如图1.13所示。tanh函数是隐层中经常使用的激活函数,效果有点像Sigmoid函数,差别在于tanh函数输出结果是−1和1之间的值,而不是0和1之间的值。

图1.13 tanh函数

链式法则在我们的深度神经网络中反向传递依然有效。我们在神经网络中反向传递并乘以微分结果,如图1.14所示。

图1.14 两层神经网络的前向和后项传递

前面方程可以表示成如下Python代码:

# Calculate loss derivative with respect to the output
dz2 = bce_derivative(y=y,y_hat=a2)

# Calculate loss derivative with respect to second layer weights
dW2 = (a1.T).dot(dz2)

# Calculate loss derivative with respect to second layer bias
db2 = np.sum(dz2, axis=0, keepdims=True)

# Calculate loss derivative with respect to first layer
dz1 = dz2.dot(W2.T) * tanh_derivative(a1)

# Calculate loss derivative with respect to first layer weights
dW1 = np.dot(X.T, dz1)

# Calculate loss derivative with respect to first layer bias
db1 = np.sum(dz1, axis=0)

需要注意的是,输入和输出数据的大小是问题本身决定的,隐层的大小却是可以自由选择的。隐层的大小是你可以设置的另一个超参。隐层越大,模型可近似的函数就越复杂。但副作用就是模型容易过拟合。所谓过拟合就是近似了一个函数,这个函数除了将我们期望的数据关系进行了训练,也将噪声进行了训练。

图1.15所示为双月数据集(the two moons dataset)。但是,现在我们增加一些噪声,让两类数据集的区分变得对人类而言比较困难。你可以在GitHub中找到这个两层神经网络代码和生成这些例子中数据的代码。

图1.15 两个月形数据集

图1.16展示了决策边界的可视化结果,图中决策边界是使用隐层大小为1的模型得到的、可以区分两类数据集的一条直线。

图1.16 隐层大小为1的决策边界

如你所见,神经网络并没有真正捕获数据之间的关系。这是因为神经网络模型太简单了。在图1.17中,你将会看到隐层大小为500的决策边界。

图1.17 隐层大小为500的决策边界

这个模型拟合了噪声而不是月型数据。这个例子中,隐层大小的最优值为3。

找到合适的隐层大小和层数是设计有效学习模型的最重要部分之一。使用NumPy来构建模型是非常笨拙的,也容易出错。幸运的是,我们有一个更快速、简便的工具来构建神经网络,该工具就是Keras。

Keras是运行在TensorFlow平台上的高层神经网络API,也是一种数据流编程(dataflow programming)的库。这意味着Keras以一种高度优化的方式来运行神经网络所需的各类运算。因此,可以比TensorFlow更快、更简单地使用。由于Keras是Tensorflow的接口,使用Keras可以更简便地构建那些复杂的神经网络。本书将会使用Keras库来构建神经网络。

在导入Keras库时,我们一般仅导入需要使用的模块。在这个例子中,我们需要使用两类层结构。

通过下面代码导入两类模块:

from keras.layers import Dense, Activation

Keras库提供序列API和函数式API两种方式来构建神经网络模型。因为序列API更容易使用,也可以让我们更快速地构建模型,我们在本书中主要使用这种方法。但在后面的章节中,我们也将讨论函数式API。

通过下面代码访问序列API:

from keras.models import Sequential

使用系列化API构建神经网络的方法如下:

(1)堆叠层

首先创建一个空序列化模型,没有任何层。

model = Sequential()

然后,我们在这个模型上添加层,就像堆层状蛋糕一样。

model.add()

对于第一层,我们需要描述输入的维度。在这个例子中,数据有两个特征,即点的坐标。我们还增加了一个大小为3的隐层。代码如下:

model.add(Dense(3,input_dim=2))

注意我们在model.add()中嵌套了函数,描述了Dense层,位置参数(positional argument)是层的大小。现在这个Dense层只做了线性运算。

调用如下代码来增加tanh激活函数:

model.add(Activation('tanh'))

接下来,我们在输出层中以同样的方式增加线性运算和激活函数。具体代码如下:

model.add(Dense(1))
model.add(Activation('sigmoid'))

如果想看到模型中所有层的情况,可以使用下面的命令:

mode.summary()

生成模型的总体情况如下:

Layer (type) Output Shape Param #
=================================================================
dense_3 (Dense) (None, 3) 9
_____________________________________________________________________________
activation_3 (Activation) (None, 3) 0
_____________________________________________________________________________
dense_4 (Dense) (None, 1) 4
_____________________________________________________________________________
activation_4 (Activation) (None, 1) 0
=================================================================
Total params: 13
Trainable params: 13
Non-trainable params: 0

你可以看到层信息的全面展示,包括输出的形状、层所包含的参数数量等。输出形状中的None表示该层没有固定输入大小,该层将会接受任何维度输入数据。在我们的例子中,None是指该层可以接受任何数量的采样数据。

在许多神经网络中,你将会看到在第一层的输入维度是可变的,为了允许不同数量的采样数据。

(2)编译模型

在开始训练模型之前,我们需要描述自己期望训练的模型的准确度。更重要的是,我们需要指定使用什么损失函数和优化器。

我们已经使用过的最简单优化器就是随机梯度下降(Stochastic Gradient Descent,SGD)。在第2章中,将会介绍更多的优化器。

我们在二值分类问题使用的损失函数叫做二元交叉熵。我们已经描述了希望在训练中跟踪的参数。在这个例子中,准确率(accuracy,acc)是需要跟踪关注的:

model.compile(optimizer='sgd',
              loss='binary_crossentropy',
              metrics=['acc'])

(3)训练模型

现在我们开始运行训练操作,具体代码如下:

history = model.fit(X,y,epochs=900)

这里会在900次迭代中对模型进行训练,每次迭代被称为一个周期(epoch)。输出结果类似如下:

Epoch 1/900
200/200 [==============================] - 0s 543us/step -
loss: 0.6840 - acc: 0.5900
Epoch 2/900
200/200 [==============================] - 0s 60us/step -
loss: 0.6757 - acc: 0.5950
...

Epoch 899/900
200/200 [==============================] - 0s 90us/step -
loss: 0.2900 - acc: 0.8800
Epoch 900/900
200/200 [==============================] - 0s 87us/step -
loss: 0.2901 - acc: 0.8800

为了节省篇幅,训练过程的全部输出未能全部在此呈现。但是,你仍然可以看到损失在逐步减少,而准确率却在稳步提升。这意味着我们成功了!

在本书中,我们将会对这些方法增加更多华丽点缀和详细介绍。但是现在,我们对深度学习的理论有了深入的理解。我们仅仅忽略了一个模块:Keras背后到底如何工作?TensorFlow是什么?为什么深度学习在GPU上运行更快?

我们将会在下面和本章最后来回答上述这些问题。

Keras是一个高级库,也可被看成是TensorFlow的简化接口。这意味着Keras本身并不执行运算,而是一种与TenserFlow交互的简单方式,而TensorFlow则是后端运行的平台。

TensorFlow是谷歌开发的软件库,在深度学习领域非常流行。在本书中,我们一般通过Keras来使用TensorFlow,因为通过Keras使用TensorFlow平台要比直接使用TensorFlow平台更便捷。但有的时候,我们为了构建更加高级的模型,也自己试着写一些TensorFlow代码。

TensorFlow的目标就是尽可能快地运行深度运算所需的各种运算。正如TensorFlow的名字,它通过数据流图中的张量(tensor)来实现快速运算。从TensorFlow 1.7版本开始,Keras就是TensorFlow的核心组成部分。

所以,我们可以使用如下代码导入Keras库:

from tensorflow.keras.layers import Dense, Activation

本书将会把Keras当成一个独立库看待。然而,未来某一天你可能会使用Keras来访问不同的后端,因此如果使用更短的import语句,我们将会保持代码的简洁。

张量是一个按照特定规则运算的数组,最简单的张量就是一个数字,也称之为标量。标量有的时候也被称为阶为0的张量。

下一类张量是向量,也就阶为1的张量。按照阶的顺序,再下一个张量就是矩阵,也就阶为2的张量。三维矩阵是阶为3的张量。具体不同阶的张量如表1.2所示。

表1.2 不同阶的张量

名称

含义

0

标量

大小

1

向量

大小和方向

2

矩阵

数的二维矩阵

3

三维矩阵

数的三维矩阵

维度矩阵

你懂的

本书中的张量一般都是指阶为3或者以上的张量。

TensorFlow和其他深度学习库都是沿着计算图来进行运算。在计算图中,诸如矩阵乘法或者激活函数等运算都是网络中的节点。张量沿着两个不同运算节点所对应的边来演进。

在简单的神经网络中的前向传递如图1.18所示。

图1.18 一个简单的计算图

将计算结构化成图的优势是方便节点进行并行计算。通过并行计算,我们并不需要很快的计算机器,在稍慢的计算机上通过拆分任务仍可以实现快速计算的目的。

这也是为什么GPU对深度学习如此重要。不同于CPU只有几个计算速度非常快的核,GPU有许多慢速核。一个现代主流的CPU一般都有四核,而主流的GPU有数百甚至上千个核。

一个简单模型的整个计算图看起来都非常复杂。你可以看到Dense层的各个元素。图1.19中有矩阵乘法,还增加了偏移量和ReLU激活函数。

图1.19 在TensorFlow中一个简单神经网络模型的计算图(注:该图截屏于TensorBoard)

使用计算图的另一个优势是TensorFlow和其他库都可以沿着图来自动快速的计算微分。正如我们在本章中所讨论的,计算微分是训练神经网络中一个非常重要的步骤。

现在,我们结束了第1章。你将会看到一些挑战性问题。下面有一些练习供大家选择,题目都是围绕本章所讨论的问题。

1.使用Python语言将两层神经网络扩展到三层神经网络。

2.在GitHub对应项目中,找到一个名称为“1 Excel Exercise”的文件。本题目的目标是通过品质数据来识别出3类不同的葡萄酒。在Excel中构建一个逻辑回归器实现上述目标。

3.在Excel中,构建一个两层的神经网络。

4.试着调节上面两层神经网络中隐层大小和学习速率等参数。看看哪组参数将会具有最低的损失值。这个最低的损失是否捕获了这些数据的真实关系?

在本章中,我们已经了解了神经网络是如何工作的机理。在本书的剩余部分,我们将会构建一个更加复杂的神经网络来近似更加复杂的函数。

实际上,为了更好地完成特定的任务(比如图像识别),我们需要在基本结构之上进行调整。但是,本章所讲述的基本思想是不变的。

本章的一个重要结论就是尽管我们在寻找函数,但是我们可以通过优化一个函数让它在数据集上的运行结果与我们的期望函数非常相似。一个重要但细微的差别在于我们并不知道函数是否与作用相似。最经常引用的例子就是军队的某个项目,该项目使用深度学习来在图像上识别坦克。该模型在数据集上训练得很好,但是一到实际应用中,就莫名失败了。

在坦克这个例子中,人们花费了很长时间发现训练模型所用的数据集中所有包含坦克的图片都是阴天拍摄的,反而没有坦克的图片都是晴天拍摄的。模型没有识别出坦克,而是识别了阴天。

这个例子说明了模型运行的结果可能跟你想象,或者你希望它运行的效果完全不一样。有缺陷的数据可能让你的模型完全失效,有的时候甚至在你没有留意的情况下发生。然后,对于每个失败故事,又都有成功的故事与之对应存在。机器学习是一个具有高度影响力的技术,未来将会重塑金融领域。

在第2章中,我们将会实实在在地处理金融领域常见的一类数据,即结构化表格数据。更具体地说,我们将会去解决金融诈骗问题,这是许多金融机构都需要去解决的问题,而现代的机器学习是一个非常有用的工具。我们将会学习使用Keras、scikit-learn和XGBoost来准备数据并进行预测。

[1] 零售银行(Retail Banking)的服务对象是普通民众、中小企及个体商户。

[2] 译者注:带有GPU加速的台式计算机。

[3] 译者注:所谓链式法则是复合函数求导方法。


相关图书

深度学习的数学——使用Python语言
深度学习的数学——使用Python语言
动手学自然语言处理
动手学自然语言处理
Web应用安全
Web应用安全
Python高性能编程(第2版)
Python高性能编程(第2版)
图像处理与计算机视觉实践——基于OpenCV和Python
图像处理与计算机视觉实践——基于OpenCV和Python
Python数据科学实战
Python数据科学实战

相关文章

相关课程