Python深度学习与项目实战

978-7-115-55083-5
作者: 周北
译者:
编辑: 谢晓芳

图书目录:

详情

本书基于Python以及两个深度学习框架Keras与TensorFlow,讲述深度学习在实际项目中的应用。本书共10章,首先介绍线性回归模型、逻辑回归模型、Softmax多分类器,然后讲述全连接神经网络、神经网络模型的优化、卷积神经网络、循环神经网络,最后讨论自编码模型、对抗生成网络、深度强化学习。本书结合计算机视觉、自然语言处理、金融领域等方面的项目,系统讲述深度学习技术,可操作性强。

图书摘要

版权信息

书名:Python深度学习与项目实战

ISBN:978-7-115-55083-5

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

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

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

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

著    周 北

责任编辑 谢晓芳

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

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

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

读者服务热线:(010)81055410

反盗版热线:(010)81055315


本书基于Python以及两个深度学习框架Keras与TensorFlow,讲述深度学习在实际项目中的应用。本书共10章,首先介绍线性回归模型、逻辑回归模型、Softmax多分类器,然后讲述全连接神经网络、神经网络模型的优化、卷积神经网络、循环神经网络,最后讨论自编码模型、生成对抗网络、深度强化学习。本书结合计算机视觉、自然语言处理、金融领域等方面的项目,系统讲述深度学习技术,可操作性强。

本书适合人工智能方面的专业人士阅读。


最近几年,随着深度学习技术的飞速发展,各种各样的应用层出不穷。从计算机视觉中的人脸识别与物体识别、自然语言处理中的机器翻译与聊天机器人,到强化学习中的无人驾驶汽车与阿尔法围棋,随处可见深度学习技术的应用。

很多人希望自己能够走进人工智能领域,但是目前的一些图书要么注重原理部分的讲解,其中各种各样的公式让人望而却步;要么专注技术本身的讲解,使读者只能从应用层面来掌握一些实际案例,没有一定数学或者编程基础的人很难在短时间内同时掌握深度学习技术的原理与项目应用。

基于这样的情况,本书的写作目的如下。

本书分为三部分。第一部分(第1~3章)介绍线性回归模型、逻辑回归模型与Softmax多分类器。第二部分(第4~7章)讲述全连接神经网络、神经网络模型的优化、卷积神经网络与循环神经网络。第三部分(第8~10章)讨论自编码模型、生成对抗网络与深度强化学习。本书中的深度学习框架主要使用Keras和TensorFlow,这两个框架是目前较流行的深度学习框架。所有代码使用Jupyter Notebook作为编辑器,因为Notebook具有交互式的功能,适合用于深度学习模型的构建与训练。

有些读者可能有一定的机器学习基础,但是建议读者从第一部分开始学习。因为第1~3章的内容可以为后续构建深度学习模型做准备,内容的讲解按照循序渐进的方式,并着重展现基础知识与深度学习模型之间的联系。

线性回归模型是一个回归模型,逻辑回归模型是一个二分类器,Softmax多分类器模型是一个多分类器,掌握这3个模型有助于理解有监督学习中的回归模型与分类模型的工作原理。当使用深度学习模型来完成多分类任务时,本质上就在其最后一层中使用Softmax多分类器模型。

第4章主要介绍激活函数、模型参数的初始化、模型的训练与损失函数、梯度下降优化算法等,并通过两个实际项目来讲述理论知识的应用。

第5章介绍深度学习中防止过拟合的方法、批量标准化,以及模型的使用、保存与加载,并讨论使用Keras框架构建一种新的模型的方式等。

第6章首先讲解图片的表示形式,然后讲述卷积神经网络中的卷积层和池化层,并分析如何将所学知识应用到猫与狗图片数据集的分类项目中。接下来,该章讲解经典CNN模型的设计思路,并介绍如何实现这些模型。

第7章讨论情感分析项目、文本生成项目、股票价格预测项目等。除此之外,得益于2018年自然语言处理领域取得的突破性进展,该章还会对其中最重要的3个模型(分别为ELMo、BERT和GPT-2)进行讲解,并将这3个模型应用到实际项目中,使读者能够走在自然语言处理领域的前沿。

第8章讲述深度学习中一种独热模型——自编码模型,讨论如何完成数据降维项目、信用卡异常交易检测项目、图片去噪项目。

第9章介绍深度学习中一种非常有趣的模型——生成对抗网络,通过生成对抗网络能够生成看起来“完全真实”的假图片。这句话读起来是不是感觉比较“诡异”?这就是生成对抗网络能够让人兴奋的原因。

第10章介绍深度强化学习。在阿尔法围棋中应用的主要技术就是深度强化学习。该章会详细讲解Deep Q-Learning算法、策略梯度算法、演员-评判家算法,并将每一个算法应用在《月球登陆》游戏中(结果表明每一个模型都能够“精通”这个游戏)。在掌握了这3个深度学习算法以后,你可以将其应用在曾经让你“无比抓狂”的游戏中。对于当时没能玩好的游戏,现在你能够使用人工智能技术来“精通”。

总的来说,目前市面上以公式为主的人工智能图书很难理解。当然,深度学习离不开数学,所以本书会将原理部分的公式与项目实战进行融合,使读者既能掌握技术应用又能明白为什么这样应用,这样可以为未来的进一步学习打下良好的基础。如果你已经被深度学习中各种模型的构建公式弄得一头雾水,或者掌握了一些原理内容,但是不知道如何应用,那么希望本书能够帮助你走出目前的困境,使你精通深度学习技术,并在深度学习技术的发展中贡献自己的力量。

虽然我对书中的原理部分以及实战代码进行了反复推敲与更改,但是由于能力有限,书中纰漏在所难免,真诚地希望读者不吝批评、指正。

最后,感谢我的父母对我无私的爱、对我的每一个决定的支持、对我默默的付出,帮助与鼓励我克服每一个困难。感谢恩师韦玮对我多年来的支持、帮助与照顾。感谢人民邮电出版社的编辑的悉心审稿。

周北


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

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

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

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

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

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

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

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

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

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

异步社区

微信服务号


第一部分主要讲解了机器学习中3个重要的算法模型,分别为线性回归模型、逻辑回归模型与Softmax多分类器模型。在人工智能领域中,深度学习是机器学习中的一个重要分支,第一部分详解的这3个模型与深度学习有着千丝万缕的联系,并且对深度学习模型工作原理的理解很有帮助。

第1章从线性回归模型能够解决的问题入手,逐步介绍线性回归模型的构建与训练方式。读者可以从中会学习到一个在机器学习中至关重要的算法—梯度下降算法,梯度下降算法是机器学习中寻找模型最优参数值的方法,在本书后续所有章节中都会使用这个算法来完成对模型的训练。读者通过对这一章的学习能够理解机器学习模型中各种术语(如数据的特征与标签、损失函数、正则项等)的含义,模型的构建与训练的过程,以及在模型训练过程中会经常遇到的过拟合现象出现的原因与解决办法。该章最后应用线性回归模型来对实际房价进行预测,展示线性回归模型在实际项目中的应用。

逻辑回归模型与Softmax多分类器模型分别为二分类器与多分类器。在深度学习模型中,通常将逻辑回归模型放到模型的最后一层中完成二分类的分类任务,或者在最后一层使用Softmax多分类器模型来完成多分类任务。第2章中的二元交叉熵与第3章中的多元交叉熵在深度学习中有着广泛的应用,希望读者能够深入掌握其原理并通过实战代码来彻底掌握其应用。这两章会分别介绍如何使用泰坦尼克数据集与MNIST数据集来应用逻辑回归模型和Softmax多分类器模型完成二分类与多分类项目。


在机器学习中,所有的算法分为3类,分别为有监督学习(supervised learning)算法、无监督学习(unsupervised learning)算法、强化学习(reinforcement learning)算法。有监督学习算法分为回归(regression)算法与分类(classification)算法。本章主要讲解回归算法中的线性回归(linear regression)算法的工作原理和线性回归算法在实际项目中的应用。

考虑这样一个情况,现在有一套房子需要出售。房子的主人并不清楚如何对这套房子进行定价,因此他就在房屋出售的相关网站上查找这套房子附近已经售出的房子的交易价格,可是没有找到一套和他的房子地理位置、房子大小、房间格局等都很类似的房子的交易信息。虽然可以随便给这套房子定一个较高的价格,但是如果把这套房子的价格定得过高,使得其性价比远远低于附近正在售卖的房子,就会直接导致这套房子在短时间内很难卖出去。如果将这套房子的价格定得过低,低于这套房子的市值,房主就会觉得很不甘心。这个时候就可以根据网站上已有的附近正在售卖的房子信息与其对应的价格数据来构建并训练一个线性回归模型,然后使用这个经过训练的线性回归模型来实现对当前房子价格的预测。将预测出的房价作为实际的售卖价格,就能够保证这套房子的性价比不会与周围其他正在售卖的房子差距过大。以下几节将详细讲解如何构建并训练一个线性回归模型来实现对房价的预测。

首先需要搜集用于训练线性回归模型的数据集,可以在网上搜集并记录这套房子附近正在出售的房子相关的详细信息(包括房子大小、房间数、距市中心的距离等)以及每套房子对应的价格数据。这些房子相关的描述信息称为特征(feature),对应的房价称为标签(label)。一套房子的数据,包括描述信息(特征)与房价(标签),称为一个样本。搜集到的所有样本的集合称为数据集。数据集中样本的个数使用来表示。为了简化描述,可以假设房价只取决于房子大小、房间数、距市中心的距离这3个特征值。因此数据集中每个样本表示一套房子的数据,每个样本都有3个特征值与一个对应的标签值。

在机器学习中,通常使用来表示数据集中所有样本的特征的集合,使用来表示数据集中所有样本的标签的集合。数据集中第个样本的特征使用表示。使用特征加下标的方式来分别表示样本中每一个单独的特征值,对于这个房价数据集,分别使用来表示第个样本中的3个特征值。使用表示第个样本的标签值,也就是其对应的房价。

构建数据集以后,就可以构建一个线性回归模型,然后使用数据集中的数据对模型进行训练。在线性回归模型中,使用样本特征值与模型的参数的线性组合作为模型的预测值。如数据集中第个样本使用线性回归模型得到的预测值为

其中,表示线性回归模型对第个样本的预测值;为模型的参数;参数称为线性回归模型的权重(weight);参数称为线性回归模型的偏差(bias)。模型中所有的参数需要在初始化之后,使用数据集中的样本数据对模型进行训练才能够得到合适的值,1.4节会详细讲解如何训练线性回归模型。线性回归模型中的权重值的大小决定了对应特征对预测结果的重要程度。如该模型经过训练得到的3个权重的值分别为100、10、1,这就说明房子的大小对房价影响最大,其次是房间数,距市中心的距离相对来说对房价影响最小。

构建好了线性回归模型以后,需要制定一个标准来衡量模型的好坏。例如,对于第个样本,它的标签值(实际的房价)为,模型的预测值(模型预测的房价)为,这个标准需要能够衡量模型的预测值与标签值之间的差值,差值越小说明模型的预测值与标签值越接近,也就是模型的预测效果越好。在机器学习中,把这个衡量模型预测值与标签值之间差值的函数称为损失函数(loss function)。

我们首先来看一下可以用来衡量线性回归模型的预测值与标签值的损失函数表达式,如下所示。

其中,为线性回归模型的参数,同时也是损失函数的参数;为数据集中样本个数;分别为第个样本的标签值与线性回归模型对第个样本的预测值。在损失函数中,首先把模型的预测值与标签值相减,得到模型的预测值与标签值的误差,然后对误差求平方。接下来把所有的平方值加起来,并将最后的结果除以样本的个数,得到所有样本误差平方的平均值。公式中的平方运算是为了防止相加后的结果正、负抵消:对于一些样本,标签值与模型的预测值相减得到的是正数;对于另一些样本,相减的值会是负数。无论正数还是负数,都代表了模型预测值与实际值之间的差值,但是如果直接把这些值相加,正、负值就会抵消,从而不能准确地衡量损失值。把所有相减得到的值都求平方以后,所有的差距值都变为正数,这样相加后的结果能够更加准确地衡量模型的预测值与标签值之间的差距。

损失函数中的参数决定了损失函数输出值的大小。因为损失函数的输出值代表了模型对数据集中全部样本的预测值与标签值之间的差值,差值越小说明模型的预测值越准确,所以需要得到参数合适的值,使损失函数的输出值最小。在机器学习中,通常使用梯度下降算法来求使损失函数取最小值的参数的值。

梯度下降算法是一种用来求使函数取最小值的参数的值的算法。在梯度下降算法中,首先,随机选取一个自变量的值,作为自变量的初始值。然后,在函数中自变量初始值的位置计算函数对于自变量的梯度。接下来,根据计算的梯度值,对自变量的值进行一次调整。接着,从改变过的自变量值的位置处,对函数求梯度,并再次根据计算出的梯度值对自变量的值进行调整。像这样对自变量的值进行一次调整的过程称为一次迭代(iteration),经过多次这样的迭代以后,就能够找到让函数取最小值的自变量的值。这里为了统一术语,把一元函数的导数统称为梯度,但是实际上多元函数的导数才是梯度。

可以通过一个实例来应用梯度下降算法找到使得函数取最小值的自变量的值。例如,当应用梯度下降算法求函数的最小值时,先求这个函数的导数。

按照梯度下降算法的工作原理,首先随机初始化自变量的值,将自变量初始化为−18,即,接下来算一下在点处的梯度值(导数值),梯度值为−36(即2)。利用这个梯度值就可以对自变量的值进行调整,调整的方式为= −lr,这里的lr= 0.1是自定义的一个常数,称为学习率(learning rate)。学习率决定了每次更新自变量值的幅度。当学习率的值设置为较大的值时,每次使用梯度值对自变量的调整较大;当学习率的值设置为较小的值时,每次对自变量的调整较小。自变量经过第一次调整以后,得到的自变量的值为−14.4。这样就完成了一次对自变量的更新,更新以后的自变量的值()能够让函数的值比在点处的值更小。接下来对自变量的值进行第二次更新。同样地,首先计算在点处的梯度值,结果为−28.8,然后通过公式自变量进行更新,更新以后的自变量的值记为能够让函数的值比在点处的值小。这样经过多次对梯度值的计算,使用梯度值与学习率对自变量的值进行更新,最终就能够找到让函数取得最小值的自变量的值,或者让函数取得接近最小值的自变量的值,这个变量的值记为

掌握了梯度下降算法的原理以后,通过代码来实际学习如何应用梯度下降算法求得函数的最小值。很显然,让这个函数取得最小值的自变量的值为0。接下来,学习一下如何应用梯度下降算法来找到让该函数取得最小值的自变量的值。首先在程序中加载用于数值计算的NumPy库和用于绘制函数图像的Matplotlib库,然后定义这个函数,如以下代码所示。

# 加载依赖库
import numpy as np
import matplotlib.pyplot as plt 
# 定义 y=x^2+1 函数
def function(x):
     y = x ** 2 + 1
     return y

接下来开始应用梯度下降算法求函数的最小值。首先把自变量的值随机初始化为−18,记为。然后通过get_gradient函数求对应的梯度值,get_gradient函数用于求这个函数在指定位置处的梯度值。得到了函数在点处的梯度值以后,把梯度值乘以学习率,并和的值相减,最后把相减以后得到的自变量的值记为,这样就完成了一次对自变量的更新。依次类推,在这里一共进行了50次更新。更新的次数使用epochs变量来表示。定义一个名为 trajectory的列表来存储每次更新后的值,以便在找到值以后,可视化使用梯度下降算法对自变量更新的过程。具体代码如下。

# 指定自变量更新的次数(迭代的次数)
epochs = 50
# 指定学习率的值
lr = 0.1
# 对自变量的值进行初始化
xi = -18
# 求函数的梯度值
def get_gradient(x):
     gradient = 2 * x
     return gradient
# 用于存储每次自变量更新后的值
trajectory = []
# 利用梯度下降算法找到使得函数取最小值的自变量的值x_star
def get_x_star(xi):
     for i in range(epochs):
          trajectory.append(xi)
          xi = xi - lr * get_gradient(xi)
     x_star = xi
     return x_star
# 运行get_x_star函数
get_x_star(xi)

get_x_star函数中传入初始化的自变量的值后,运行以上代码,就可以利用梯度下降算法找到让函数取最小值的自变量的值get_x_star函数的输出值为−0.000 25,实际上,的值为0。可以看出,使用梯度下降算法计算出的函数最小值与实际的函数最小值几乎没有任何差别。

接下来可以将自变量在更新过程中对应的函数值减小的过程进行可视化。以下这段代码可以把自变量每次更新的值以及对应的函数值画在函数图像上。

x = np.arange(-20, 20, 0.1)
y = function(x)
# 画出函数图像
plt.plot(x, y)
x_trajectory = np.array(trajectory)
y_trajectory = function(trajectory)
# 画出更新过程中的自变量及其对应的函数的值
plt.scatter(x_trajectory, y_trajectory)
plt.show()

可视化的结果如图1.1所示,从图中可以看出,将自变量的值初始化为−18以后,每一次使用梯度下降算法对自变量的值进行调整都会让函数的值变得更小,最终得到让函数取得最小值的自变量的值。

在梯度下降算法中,如果把学习率的值设置为很小的值(如0.001),就需要很多次更新才能得到让函数取最小值的自变量的值;如果把学习率的值设置为比较大的值(如0.5),自变量每次更新的幅度就会比较大,可能使自变量很难更新到让函数取最小值对应的值。因此,选取合适的学习率的值,对得到让函数取最小值对应的自变量的值至关重要。

图1.1 可视化的结果

掌握了梯度下降算法以后,接下来就可以应用梯度下降算法找到使得损失函数取最小值的线性回归模型中的参数值,模型的参数就是损失函数的自变量。与1.1.3节中的损失函数一样,线性回归模型的损失函数如下所示。

根据梯度下降算法的工作原理,首先对参数值进行初始化,的初始值为 0。然后对损失函数求梯度。计算出梯度值以后,就可以应用梯度下降算法逐步找到使得损失函数取最小值的参数值。损失函数的梯度使用来表示,如下所示。

其中各项的值分别如下。

模型中对权重逐个求偏导数的过程可以使用向量的形式进行表示,如下所示。

通过以上的方式就能够分别计算出损失函数对于参数值的梯度值。计算出梯度值以后,就可以应用梯度下降算法对模型中的所有参数按照指定的学习率lr进行逐次迭代更新。将模型参数初始化为后,使用梯度下降算法进行一次更新以后的参数使用来表示,如下所示。

同理,按照同样的方式可以继续对参数进行多次迭代更新,最后得到参数值,使损失函数取最小值。的值就是这个线性回归模型的最优参数值。

掌握了线性回归模型的构建方式与使用梯度下降算法来求解模型的最优参数的方法后,接下来使用代码实现线性回归模型,并对其进行训练。为了实现可视化,数据集中的每一个样本只有一个特征,与其对应的标签使用来表示。

接下来分别使用两个不同的线性回归模型来拟合数据,这样能对线性回归模型有全面的理解和掌握。在对机器学习中的模型进行训练时,会经常遇到模型在训练过程中出现过拟合(overfitting)的现象。本节的第2个模型在训练时会出现过拟合的现象,在实际遇到过拟合的现象后再来学习过拟合出现的原因与解决办法会加深对过拟合的理解。

首先在程序中加载数据集,数据集中的全部样本存储在dataset.csv文件中,文件中有两列数据,列名分别为XyX这一列的数据为所有样本的特征值,y这一列的数据为所有样本的标签值。可以使用Pandas模块将数据集加载到程序中,然后将数据集中所有样本的特征值与标签值取出,分别使用变量X与变量y来保存,如以下代码所示。

import pandas as pd
import numpy as np
# 加载数据集
dataset = pd.read_csv('dataset.csv')
# 取出每个样本的特征值
X = np.array(dataset['X'])
# 取出每个样本的标签值
y = np.array(dataset['y'])

在实际项目中,通常不是使用数据集中全部的样本数据对线性回归模型进行训练,而是将整个数据集按照一定比例分为训练集(train set)与测试集(test set),然后只使用训练集的数据对模型进行训练。使用训练集中的数据训练好模型以后,再使用测试集数据对模型进行评估。因为模型在训练时没有接触过测试集的数据,所以能够保证使用测试集对模型评估时结果更准确。例如,对于一个学生来说,在平时学习过程中做的练习题需要和考试中出现的题尽可能地不同,这样才能够检测出这个学生在平时是否学得好。如果练习题与考试的题完全一样,学生可以背下每一道题目的答案,而不是掌握题目的解题思路,那么会直接导致当遇到练习题以外的题目时,完全不知道怎么解决。这就是为什么要把数据集分成训练集与测试集。

在刚刚加载的数据集中,共有40个样本数据。可以把其中的前30个样本数据作为训练集,训练集中所有样本的特征值与标签值分别使用X_trainy_train变量来存储,使用变量n_train来表示训练集中的样本个数。将数据集中最后10个样本作为测试集,测试集中所有样本中的特征值与标签值分别使用X_testy_test变量来存储,使用n_test变量来表示测试集中的样本个数。实现方式如以下代码所示。

# 训练集
X_train = X[0: 30]
y_train = y[0: 30]
n_train = len(X_train)
# 测试集
X_test = X[30:]
y_test = y[30:]
n_test = len(X_test)

将数据集划分为训练集与测试集以后,构建一个线性回归模型来拟合训练集数据。这个线性回归模型的表达式为。其中,分别表示样本的特征值与线性回归模型对样本的预测值,为模型的参数。

为了使用梯度下降算法对这个线性回归模型进行训练,进而找到模型的最优参数值,需要使用损失函数来衡量模型的预测值与样本实际标签值之间的差值。这个模型的损失函数如下。

其中,为训练集中的样本个数,分别为模型对第个样本的预测值与样本的标签值。为了应用梯度下降算法求让损失函数取值最小的参数值,需要对模型参数求梯度,参数的梯度分别为

求出线性回归模型中参数的梯度值后,再对参数进行初始化,然后就可以使用合适的学习率来利用梯度下降算法对模型参数进行多次迭代更新,直到找到模型的最优参数值

现在,可以利用梯度下降算法,使用训练集中的数据对模型进行多次迭代训练,最终得到最优参数值。首先构建线性回归模型,并对模型的参数进行随机初始化。将模型的参数分别初始化为−0.3与0.6。将在梯度下降算法中使用的学习率的值设置为0.001。指定模型使用梯度下降算法迭代更新参数的次数为5 000。最后构建线性回归模型,如以下代码所示。

# 把模型的参数w与b分别随机初始化为-0.3和0.6
w = -0.3
b = 0.6
# 指定学习率的值
lr = 0.001
# 指定模型使用梯度下降算法迭代更新参数的次数
epochs = 5000
# 构建线性回归模型
def model(x):
     y_hat = w * x + b
     return y_hat

将线性回归模型构建好,并对其参数进行初始化以后,使用梯度下降算法对其训练。按照上面推导出的对损失函数求梯度的计算公式,更新参数。首先,从训练集中依次取出每一个样本的特征值,使用线性回归模型对其进行预测得到预测值,将样本的预测值与标签值相减,并乘以对应样本的特征值。然后,将每一个样本的计算结果相加,再除以训练集中样本的个数。最后,乘以2,结果即为参数的梯度值。求参数的梯度值与求参数的梯度值的方式类似。得到了参数的梯度值以后,就可以对参数使用梯度下降算法进行更新。具体实现方式如以下代码所示。

for epoch in range(epochs):
     # sum_w与sum_b用于存储计算梯度时相加的值
     sum_w = 0.0
     sum_b = 0.0
     # 求参数w与b的梯度值
     for i in range(n_train):
          xi = X_train[i]
          yi = y_train[i]
          yi_hat = model(xi)
          sum_w += (yi_hat - yi) * xi
          sum_b += (yi_hat - yi)
     # grad_w与grad_b分别为参数w、b对应的梯度值
     grad_w = (2.0 / n_train) * sum_w
     grad_b = (2.0 / n_train) * sum_b
     # 使用梯度下降算法更新模型参数
     w = w - lr * grad_w
     b = b - lr * grad_b

训练好模型以后,为了直观地看出这个线性回归模型对数据的拟合程度,可以将数据集中的样本与线性回归模型的图像画在一张图上。首先,在程序中加载用于数据可视化的Matplotlib库,为了能够在可视化的图像中显示中文字体,需要对其中的字体参数进行配置。然后,在图像中依次以散点图的方式画出数据集中的样本、以线条的形式画出函数的图像。具体实现方式如以下代码所示。

import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
%matplotlib inline
def plots(w, b, X, y):
     fig, ax = plt.subplots()
     # 画出数据集中的样本
     ax.scatter(X, y)
     # 画出线性回归模型的图像
     ax.plot([i for i in range(0, 20)], 
               [model(i) for i in range(0, 20)])
     plt.legend(('模型', '数据'), 
                  loc='upper left', 
                  prop={'size': 15})
     plt.title("线性回归模型", fontsize=15)
     plt.show()
plots(w, b, X, y)

运行以上代码,将数据集中的样本与线性回归模型的图像可视化的结果如图1.2所示。从图1.2中可以看出,数据集中几乎所有的样本点分布在线性回归模型的图像两侧,这说明线性回归模型能够较好地拟合数据集中的数据。

图1.2 数据集中的样本与线性回归模型的图像的可视化结果

对线性回归模型有一个直观的理解后,使用损失函数来分别计算出模型在训练集与测试集上的损失值,并将损失值进行对比,进而分析出模型在训练集数据上的预测效果。损失函数的定义如以下代码所示。首先依次取出传入损失函数中数据集的每一个样本,使用线性回归模型对其进行预测,然后将模型对样本的预测值与标签值相减并求平方,接下来将模型对每个样本的预测误差值累加,最后除以样本的个数得到平均损失值。

def loss_funtion(X, y):
     total_loss = 0
     # 数据集中样本的个数
     n_samples = len(X)
     # 依次取出每一个数据中的每一个样本
     for i in range(n_samples):
          xi = X[i]
          yi = y[i]
          # 使用模型根据样本特征值进行预测
          yi_hat = model(xi)
          # 计算模型预测值与标签值的差值的平方
          total_loss += (yi_hat - yi) ** 2 
          # 对于给定数据集,计算模型预测的平均损失值
     avg_loss = (1 / n_samples) * total_loss
     return avg_loss

定义好损失函数以后,分别使用其计算出训练好的线性回归模型在训练集与测试集上的平均损失值,如以下代码所示。

train_loss = loss_funtion(X_train, y_train)
test_loss = loss_funtion(X_test, y_test)

运行以上代码,可以得出,模型在训练集上的平均损失值为95.2,在测试集上的平均损失值为96.2,说明模型在训练集与测试集上的效果都比较好。

在1.4.2节中,我们将构建另一个较复杂的线性回归模型来拟合训练集数据。介绍这个模型的构建与训练主要为了讲解过拟合现象,在后续模型的学习中我们会经常遇到过拟合现象,所有在本章中通过人为制造过拟合现象的方式可以让我们更加深入理解过拟合现象出现的原因,以及知道如何对过拟合现象进行处理。

这个复杂线性回归模型的表达式为。其中,分别表示样本的特征值与线性回归模型对样本的预测值,为模型的参数。

为了找到模型的最优参数值,需要使用梯度下降算法对这个线性回归模型进行训练,因此需要构建这个模型在训练时使用的损失函数。这个模型的损失函数如下。

其中,为训练集样本个数,分别为模型对第个样本的预测值与样本的标签值。为了应用梯度下降算法求让损失函数取最小值的参数值,需要对模型参数求梯度,参数的梯度分别为如下。

求出损失函数对线性回归模型中参数的梯度值后,再对参数进行初始化,然后就可以使用合适的学习率来利用梯度下降算法对模型参数进行多次迭代更新,直到找到模型的最优参数值

由此就可以利用计算出的梯度值对模型的参数值进行更新。首先将模型的参数值进行初始化,然后利用梯度下降算法对模型的参数进行多次迭代更新,更新的方式与上文中的方式一致,在这里就不赘述了,如以下代码所示。

import numpy as np
# 把模型的参数w与b进行随机初始化
w = np.random.rand(2)
b = 1.1
# 指定学习率的值
lr = 1e-6
# 指定模型使用梯度下降算法迭代更新参数的次数
epochs = 50000
# 构建复杂线性回归模型
def model(x):
     y_hat = w[0]*x + w[1]*(x**2) + b
     return y_hat
# 使用梯度下降算法更新模型参数
for epoch in range(epochs):
     sum_w = np.zeros(2)
     sum_b = 0.0
     for i in range(n_train):
          xi = X_train[i]
          yi = y_train[i]
          yi_hat = model(xi)
          sum_w[0] += (yi_hat - yi) * xi
          sum_w[1] += (yi_hat - yi) * (xi**2)
          sum_b += (yi_hat - yi)
     grad_w = (2.0 / n_train) * sum_w
     grad_b = (2.0 / n_train) * sum_b
     w = w - lr * grad_w
     b = b - lr * grad_b

将这个复杂线性回归模型训练好了以后,调用之前定义的plots函数在一张图中同时画出数据中的样本与复杂线性回归模型的图形,如以下代码所示。

plots(w, b, X, y)

可视化的结果如图1.3所示,可以很明显地看出,这个复杂的线性回归模型对数据中的部分数据没能很好地拟合。

接下来,使用上一节中定义的损失函数来分别查看模型在训练集与测试集上的平均损失值,如以下代码所示。

train_loss = loss_funtion(X_train, y_train)
test_loss = loss_funtion(X_test, y_test)

运行以上代码可以得到,在训练集上模型的平均损失值为230.6,在测试集上模型的平均损失值为1 705.6。可以发现,模型在测试集上得到的平均损失值比训练集上的平均损失值大很多。

模型在测试集上的效果比在训练集上的效果差很多的这种现象称为过拟合。过拟合在模型的训练过程中经常发生,尤其在模型较复杂的情况下,如对于本节使用的数据集来说,线性回归模型比较复杂,因此出现了较严重的过拟合现象。

图1.3 复杂线性回归模型的可视化

这个复杂线性回归模型经过训练以后,得到模型的权重值,。这个模型出现严重过拟合,除了模型较复杂以外,还因为模型的权重值较大。模型固定了以后,一种常用的防止模型出现过拟合的方法为在损失函数中加入正则项。

在机器学习中,通常在损失函数中加入正则项来防止过拟合现象的发生。正则项通过“惩罚”模型中值过大的权重,使得模型的权重值变小,从而有效防止过拟合现象的发生。

因为损失函数用来衡量模型对样本的预测值与实际值之间的差距,所以可使用梯度下降算法来找到合适的权重值,使得这些权重值能够让损失函数取得最小值。为了防止过拟合现象的出现,需要减小模型的权重值。所以可以把权重值放到损失函数中组成新的损失函数,这样在通过梯度下降算法来降低新的损失函数值的时候,就可以同时降低损失值与模型的权重值,一举两得。在损失函数中加入模型权重值后,组成的新的损失函数如下。

其中,称为L2正则(L2-regularization)项。常数为权衡模型损失值与模型权重值的重要程度的一个超参数。模型的超参数为在模型进行训练之前需要人为设定的值,这个值在模型训练过程中保持不变。如果希望新的损失函数在训练的时候将模型参数值降低得多一些,就设置为较大的值,如1 000;如果希望在训练的时候主要降低模型的预测损失值,就设置为较小的值,如0.1。

构建了这个新的损失函数以后,同样利用梯度下降算法来求使得损失函数取最小值时的权重值。首先求模型参数的梯度值,对新的损失函数中权重求梯度的方法如下所示。

同理,对其他权重求偏导数与求 的偏导数类似。但是因为防止过拟合时一般不考虑参数,所以对参数求梯度的公式与之前的一样。

掌握了在损失函数中加入正则项来防止过拟合的原理以后,将其应用到实际的代码中。首先对模型的参数进行随机初始化,将模型的权重使用随机函数进行初始化,将偏移项的值随机初始化为1.1。指定好模型在使用梯度下降算法进行训练时需要使用的学习率的值与模型迭代训练的次数以后,定义与上文一样的复杂线性回归模型。实现方式如以下代码所示。

import numpy as np
w = np.random.rand(2)
b = 1.1
lr = 1e-6
epochs = 10000000
# 定义复杂线性回归模型
def model(x):
     y_hat = w[0]*x + w[1]*(x**2) + b
     return y_hat

接下来在损失函数中加入正则项,利用梯度下降算法对模型的参数进行更新。在加入了正则项以后,在梯度下降算法中唯一需要改动的地方为在求权重对损失函数的梯度时,需要加入的值。其余部分均保持不变,如以下代码所示。

# 指定正则项中lambda的值
reg = 10000
for epoch in range(epochs):
     sum_w = np.zeros(2)
     sum_b = 0.0
     for i in range(n_train):
          xi = X_train[i]
          yi = y_train[i]
          yi_hat = model(xi)
          sum_w[0] += (yi_hat - yi) * xi
          sum_w[1] += (yi_hat - yi) * (xi**2)
          sum_b += (yi_hat - yi)
     # 正则项在梯度下降算法中的应用
     grad_w = (2.0 / n_train) * sum_w + (2.0 * reg * w)
     grad_b = (2.0 / n_train) * sum_b
     w = w - lr * grad_w
     b = b - lr * grad_b

在使用正则项来防止模型在训练过程中出现过拟合的现象以后,这个模型分别在训练集与测试集上的平均预测损失值可以通过之前定义的loss_function函数得到,如以下代码所示。

train_loss = loss_funtion(X_train, y_train)
test_loss = loss_funtion(X_test, y_test)

运行以上代码,得到模型在训练集上的平均预测损失值为348.3,在测试集上的平均预测损失值为490.4。当应用正则项来防止过拟合现象发生时,模型在测试集上的平均预测损失值为1 705.6,过拟合现象得到了很大程度的缓解。最重要的是,使用了正则项以后,模型经过训练以后的权重值,相对,于之前没有使用正则项时训练后得到的权重值,它减小了很多。正是权重值的减小,有效地降低了过拟合现象发生的概率。

波士顿房价数据集是美国波士顿1970年的真实房价数据。在这个数据集中共有506个样本,其中每一个样本代表一所房子相关的特征与房价。每一所房子都有13个特征值与1个对应的标签值,标签值即为房子的价格。因为每一所房子的相关信息都使用13个特征值来描述,所以这些特征值能够很好地捕获房子的属性,有利于模型的训练与预测。这13个特征值分别表示城镇人均犯罪率、住宅平均房间数、到波士顿5个中心区域的加权距离、城镇师生比例、自住房平均价格等。

因为在sklearn(scikit-learn)模块中已经封装了波士顿房价数据集,所以可以直接在程序中使用sklearn模块加载数据集。sklearn模块是使用Python编程语言编写的机器学习工具,在本书中主要应用其对数据集进行预处理。使用sklearn模块加载波士顿房价数据集的代码如下所示。

from sklearn.datasets import load_boston
dataset = load_boston()
X = dataset.data
y = dataset.target

在把训练集数据加载到模型中用于模型训练之前,需要把训练集的全部样本的特征值进行标准化(standardization)。其原因可以通过下面这个示例进行解释。如果训练集数据中的每个样本都由3个特征值组成,分别记为,其中特征值 的范围为0~1,特征值的范围为0~100,特征值的范围为0~1 000。因为样本的每一个特征值的范围有很大的不同,使模型训练变得非常困难,所以应用标准化将每一个特征值的范围都转换成0~1,能够提高模型的训练速度。

当将数据集中的样本进行标准化时,实际为对数据集中所有样本在同一个位置的特征值进行标准化。对于一个有个样本的数据集,每一个样本中的特征使用来表示。每一个样本有3个特征值,分别使用来表示第个样本的3个特征值。可以通过以下几个公式对这个数据集中所有样本的特征值进行标准化。

首先计算出所有样本中特征值的均值,均值的计算公式为将所有值相加再除以值的总数,如下所示。

计算出均值后,根据均值计算出所有样本特征值的方差,计算公式如下所示。

最后,将所有样本特征值减去均值并除以方差,即可得到训练集中每一个样本经过标准化以后的特征值,如下所示。

如果训练集的数据已经被分割成训练集与测试集,则需要对训练集与测试集的数据分别进行标准化。在训练集中计算出的用于标准化样本中每一个特征值的均值与方差会同样应用于测试集数据的标准化。也就是说,标准化测试集的数据时,不需要重新计算测试集的样本中每一个特征值的均值与方差,而应该使用标准化训练集样本时使用的均值与方差。当训练集与测试集用同样的方式进行标准化以后,才可以将训练集用于模型训练,将测试集用于测试模型预测的准确率。

对于波士顿房价数据集的标准化可以使用以下代码实现。首先求数据集中所有样本特征值的均值,然后求数据集中所有样本特征值的方差,最后利用计算出的均值与方差完成对数据集的标准化。

# 求数据集中所有样本特征值的均值
mean = X.mean(axis=0)
# 求数据集中所有样本特征值的方差
std = X.std(axis=0)
# 对数据集进行标准化
X = (X - mean) / std

在将数据集进行标准化以后,需要将整个数据集分为用于线性回归模型训练的训练集与用于模型验证的测试集。在sklearn模块中提供了train_test_split函数用于按照指定比例划分数据集,函数中的test_size参数为划分的测试集占全部数据集中数据的比例,如将其指定为0.2时,会将数据集中80%的数据划分为训练集数据,其余的20%划分为测试集数据。将数据集划分好以后,分别使用n_train变量与n_features变量表示训练集中的样本个数与每个样本的特征值个数。具体代码如下。

# 将数据集分为训练集与测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 训练集中的样本个数
n_train = X_train.shape[0]
# 训练集中每个样本的特征值个数
n_features = X_train.shape[1]

接下来,对模型的参数wb进行初始化。模型的权重w中值的个数必须与每个样本中特征值的个数一致。指定模型在使用梯度下降算法进行训练时的学习率为0.001,对模型进行3 000次迭代训练,并定义线性回归模型。具体代码如下。

import numpy as np
# 模型参数中的权重
w = np.random.rand(n_features)
# 模型参数中的偏差
b = 1.1
# 指定学习率的值
lr = 0.001
# 指定模型迭代训练的次数
epochs = 3000
# 定义线性回归模型
def model(x):
     y_hat = w.dot(x) + b
     return y_hat

最后对模型使用梯度下降算法进行训练。在训练过程中,为了防止严重过拟合现象的出现,在损失函数中加入了正则项。模型训练的代码如下所示。

# 指定正则项中lambda的值
reg = 0.5
# 使用梯度下降算法对模型进行迭代训练
for epoch in range(epochs):
     sum_w = 0.0
     sum_b = 0.0
     for i in range(n_train):
          xi = X_train[i]
          yi = y_train[i]
          yi_hat = model(xi)
          sum_w += (yi_hat - yi) * xi
          sum_b += (yi_hat - yi)
     grad_w = (2.0 / n_train) * sum_w + (2.0 * reg * w)
     grad_b = (2.0 / n_train) * sum_b
     w = w - lr * grad_w
     b = b - lr * grad_b

接下来使用在之前定义的loss_function函数来计算当前训练好的线性回归模型分别在训练集与测试集上的平均预测损失值,如以下代码所示。

train_loss = loss_funtion(X_train, y_train)
test_loss = loss_funtion(X_test, y_test)

这个线性回归模型在训练集与测试集上的平均预测损失值分别为26.7与23.7,因为模型在测试集上得到的平均预测损失值比训练集上的平均预测损失值还要小,说明正则项有效地减小了过拟合现象出现的概率。

线性回归模型在机器学习中是一个重要的回归模型。除了线性回归模型以外,后续的章节会详细讲解如何利用深度学习模型来解决回归问题,因此深入理解线性回归模型对后续算法的学习很有帮助。

本章从实际问题出发,讲解构建线性回归模型的应用场景,通过损失函数衡量模型对样本预测值与样本标签值之间的误差,还利用梯度下降算法找到使损失函数取最小值的参数的值,这样的参数值能够让模型有更好的预测能力。

本章展示构建了两个线性回归模型,分别为简单模型与复杂模型,并应用梯度下降算法对模型进行训练。在训练复杂模型的过程中出现了严重的过拟合现象,我们通过在损失函数中加入正则项对其进行了有效的缓解。在最后的项目实战中应用了L2正则项,除了L2正则项以外,还可以在损失函数中加入L1正则项防止过拟合,其中表示模型中权重的个数。


相关图书

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

相关文章

相关课程