精通-Sklearn-和-TensorFlow-预测性分析-全-
精通 Sklearn 和 TensorFlow 预测性分析(全)
原文:
annas-archive.org/md5/2835379a6027af4010dd4021fd62ced4译者:飞龙
前言
Python 是一种编程语言,在数据科学领域提供了多种功能。在本书中,我们将接触到两个 Python 库:scikit-learn 和 TensorFlow。我们将学习集成方法的各种实现,了解它们如何与实际数据集结合使用,以及它们如何提高分类和回归问题中的预测准确性。
本书从学习集成方法及其特点开始。我们将看看 scikit-learn 如何提供合适的工具来选择模型的超参数。从这里,我们将深入研究预测分析,探索其各种特性和特点。我们将了解人工神经网络、TensorFlow 以及用于构建神经网络的核心概念。
在最后一部分,我们将考虑计算能力、改进方法和软件增强等因素,以提高预测分析的效率。你将熟练掌握使用深度神经网络(DNN)来解决常见挑战。
本书适用对象
本书适用于数据分析师、软件工程师和机器学习开发者,特别是那些有兴趣使用 Python 实现高级预测分析的人。商业智能专家也会发现本书不可或缺,因为它将教会他们如何从基础预测模型开始,逐步构建更高级的模型,并做出更好的预测。假设读者具备 Python 知识并熟悉预测分析的概念。
本书内容概述
第一章,回归与分类的集成方法,讲述了集成方法或算法在模型预测中的应用。我们将通过回归和分类问题中的集成方法应用进行学习。
第二章,交叉验证与参数调优,探讨了组合和构建更好模型的各种技术。我们将学习不同的交叉验证方法,包括保留法交叉验证和 k 折交叉验证。我们还将讨论什么是超参数调优。
第三章,特征工程,探讨了特征选择方法、降维、主成分分析(PCA)和特征工程。我们还将学习如何通过特征工程来改善模型。
第四章,人工神经网络与 TensorFlow 简介,是对人工神经网络(ANNs)和 TensorFlow 的介绍。我们将探讨网络中的各种元素及其功能。我们还将在其中学习 TensorFlow 的基本概念。
第五章,使用 TensorFlow 和深度神经网络进行预测分析,通过 TensorFlow 和深度学习的帮助探索预测分析。我们将学习 MNIST 数据集及其分类模型的应用。我们将了解 DNN(深度神经网络)、它们的功能以及 DNN 在 MNIST 数据集中的应用。
最大限度地发挥本书的价值
本书介绍了一些最先进的预测分析工具、模型和技术。主要目标是展示如何通过构建更复杂的模型来提高预测模型的性能,其次,通过使用相关技术大幅提高预测模型的质量。
下载示例代码文件
你可以从你的帐户在 www.packt.com 下载本书的示例代码文件。如果你是从其他地方购买的本书,可以访问 www.packt.com/support,并注册以便直接通过电子邮件接收文件。
你可以按照以下步骤下载代码文件:
-
登录或注册 www.packt.com。
-
选择 SUPPORT 标签。
-
点击代码下载与勘误。
-
在搜索框中输入书名,并按照屏幕上的指示操作。
一旦文件下载完成,请确保使用以下最新版本解压或提取文件夹:
-
WinRAR/7-Zip for Windows
-
Zipeg/iZip/UnRarX for Mac
-
7-Zip/PeaZip for Linux
本书的代码包也托管在 GitHub 上,链接为github.com/PacktPublishing/Mastering-Predictive-Analytics-with-scikit-learn-and-TensorFlow。如果代码有更新,它将会在现有的 GitHub 仓库中进行更新。
我们还提供了其他来自我们丰富的图书和视频目录中的代码包,链接为github.com/PacktPublishing/。快来看看吧!
下载彩色图像
我们还提供了一个 PDF 文件,其中包含本书中使用的截图/图表的彩色图像。你可以在这里下载:www.packtpub.com/sites/default/files/downloads/9781789617740_ColorImages.pdf。
使用的规范
本书中使用了多种文本规范。
CodeInText:表示文本中的代码词、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟网址、用户输入和 Twitter 用户名。示例:“以下截图显示了用于导入train_test_split函数和RobustScaler方法的代码行。”
一段代码如下所示:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline
粗体:表示新术语、重要单词或你在屏幕上看到的词语。例如,菜单或对话框中的词语会像这样出现在文本中。以下是一个例子:“用于选择最佳估计器的算法,或选择所有超参数的最佳值,称为超参数调优。”
警告或重要提示以这种方式出现。
小贴士和技巧以这种方式出现。
联系我们
我们非常欢迎读者的反馈。
一般反馈:如果你对本书的任何方面有疑问,请在邮件主题中注明书名,并通过customercare@packtpub.com联系我们。
勘误表:虽然我们已经尽力确保内容的准确性,但错误有时会发生。如果你在本书中发现错误,我们将不胜感激你向我们报告。请访问www.packt.com/submit-errata,选择你的书籍,点击勘误提交表单链接,并输入相关详情。
盗版:如果你在互联网上发现我们的作品有任何非法复制形式,我们将不胜感激你提供相关地址或网站名称。请通过copyright@packt.com与我们联系,并提供该材料的链接。
如果你有兴趣成为作者:如果你在某个领域具有专业知识,并且有兴趣写书或为书籍做贡献,请访问authors.packtpub.com。
评论
请留下评论。阅读并使用本书后,为什么不在购买地点留下评论呢?潜在读者可以看到并参考你的客观意见做出购买决策,我们在 Packt 也能了解你对我们产品的看法,而我们的作者可以看到你对他们书籍的反馈。谢谢!
欲了解更多关于 Packt 的信息,请访问packt.com。
第一章:回归和分类的集成方法
高级分析工具在商业企业中被广泛应用,以利用数据解决问题。分析工具的目标是分析数据并提取相关信息,以解决问题或提高业务某些方面的绩效。它还涉及各种机器学习算法,通过这些算法我们可以创建预测模型,从而获得更好的结果。
在本章中,我们将探讨一种可以显著提高基础预测模型性能的简单思想。
在本章中,我们将涵盖以下主题:
-
集成方法及其工作原理
-
回归的集成方法
-
分类的集成方法
集成方法及其工作原理
集成方法基于一个非常简单的思想:与其使用单一模型进行预测,不如使用多个模型,然后采用某种方法来聚合这些预测。拥有不同的模型就像拥有不同的视角,已经证明,通过聚合提供不同视角的模型,预测可以更加准确。这些方法进一步提高了模型的泛化能力,因为它们减少了选择表现不佳的分类器的风险:

在前面的图示中,我们可以看到每个对象属于三种类别之一:三角形、圆形和正方形。在这个简化的例子中,我们有两个特征来将对象分离或分类到不同的类别中。正如你所见,我们可以使用三种不同的分类器,且这三种分类器代表不同的方法,并具有不同类型的决策边界。
集成学习将所有个体预测结合为一个单一的预测。通过结合三个边界做出的预测通常比单一模型产生的预测具有更好的性能。这就是集成方法背后的简单思想,也称为集成学习。
最常用的集成方法如下:
-
自助抽样
-
自助法(Bagging)
-
随机森林
-
提升法(Boosting)
在对这些方法进行高层次解释之前,我们需要讨论一种非常重要的统计技术,称为自助抽样。
自助抽样
许多集成学习方法使用一种统计技术,称为自助抽样(bootstrap sampling)。数据集的自助抽样是通过从原始数据集中随机抽样观测值有放回地生成的另一个数据集。
这种技术在统计学中广泛应用,例如,它用于估计样本统计量的标准误差,如均值或标准差。
让我们通过查看以下图示来更好地理解这一技术:

假设我们有一个从 1 到 10 的种群数据,可以视为原始种群数据。为了获得自助样本,我们需要从原始数据中以有放回的方式抽取 10 个样本。想象你有 10 张写着数字的卡片放在一顶帽子里;对于样本的第一个元素,你从帽子中随机抽取一张卡片并记录下来,然后将卡片放回帽子里,这个过程一直进行,直到你得到 10 个元素。这就是你的自助样本。如前面的例子所示,9在自助样本中被重复了三次。
这种带放回的数字重抽样提高了对真实种群数据的准确性。它还有助于理解在重抽样过程中涉及的各种差异和特征,从而提高相同过程的准确性。
袋装法
袋装法,也称为自助聚合法,是一种常用的减少机器学习模型方差的程序。它基于自助抽样技术,通常与回归或分类树一起使用,但原则上,这种袋装技术可以用于任何模型。
袋装过程包括以下步骤:
-
我们选择使用的估计器或单独模型的数量。我们将其视为参数 B。
-
我们从 B 中使用自助抽样法从训练集中抽取样本数据集。
-
对于每一个训练数据集,我们在每个自助样本中拟合机器学习模型。这样,我们就得到了参数 B 的单独预测器。
-
我们通过聚合所有单独的预测结果来获得集成预测。
在回归问题中,获取集成预测结果的最常见方法是求所有单独预测的平均值。
在分类问题中,最常见的获取聚合预测结果的方法是通过多数投票。我们可以通过一个例子来解释多数投票。假设我们有 100 个单独的预测器,其中 80 个投票支持某一特定类别。那么,我们就选择该类别作为我们的聚合预测结果。这就是多数投票的含义。
随机森林
这个集成方法是专门为回归或分类树创建的。它与袋装法非常相似,因为在这里,每棵单独的树都是在训练数据集的自助样本上训练的。与袋装法的不同之处在于,它使模型非常强大,并且在对树进行节点分裂时,选择的分裂是特征的随机子集中的最佳分裂。因此,每个单独的预测器都会考虑特征的随机子集。这会导致每个单独的预测器稍微变得较差且更具偏差,但由于单独预测器之间的相关性,整体集成预测通常比单个预测器要好。
提升法
提升(Boosting)是集成学习的另一种方法。提升有许多方法,但最成功和最受欢迎的一种方法是AdaBoost算法,也叫自适应提升。该算法的核心思想是,不是单独训练多个预测器,而是训练一系列弱学习器。下一个算法依赖于前一个算法的结果。在 AdaBoost 算法中,每次迭代都会重新加权所有样本。这里的训练数据会根据前一个学习器或模型的结果重新加权。
例如,在分类中,基本思想是错误分类的示例会增加权重,而正确分类的示例则减少权重。因此,序列中的下一个学习器或模型会更关注错误分类的示例。
回归的集成方法
关于回归,我们将训练这些不同的模型,并随后比较它们的结果。为了测试所有这些模型,我们需要一个样本数据集。我们将使用这个数据集来实现这些方法,看看它如何帮助我们提高模型的性能。
钻石数据集
让我们通过使用不同的集成学习模型对钻石价格进行实际预测。我们将使用一个钻石数据集(可以在此处找到:www.kaggle.com/shivam2503/diamonds)。该数据集包含约 54,000 颗钻石的价格以及其他特征。以下是该数据集中的特征:
-
特征信息:一个包含 53,940 行和 10 个变量的数据框
-
价格:以美元为单位的价格
以下是九个预测特征:
-
carat:该特征表示钻石的重量(0.2-5.01) -
cut:该特征表示切割的质量(Fair,Good,Very Good,Premium,Ideal) -
color:该特征表示钻石的颜色,从J(最差)到D(最好) -
clarity:该特征表示钻石的清晰度测量(I1(最差),SI2,SI1,VS2,VS1,VVS2,VVS1,IF(最好)) -
x:该特征表示钻石的长度,单位为毫米(0-10.74) -
y:该特征表示钻石的宽度,单位为毫米(0-58.9) -
z:该特征表示钻石的深度,单位为毫米(0-31.8) -
depth:该特征表示 z/平均值(x, y) = 2 * z/(x + y)(43-79) -
table:该特征表示钻石顶部宽度与最宽点的比率(43-95)
x、y 和 z 变量表示钻石的尺寸。
我们将使用的库有numpy、matplotlib和pandas。要导入这些库,可以使用以下代码:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline
以下截图展示了我们用来调用原始数据集的代码行:

上述数据集包含一些数值特征和一些类别特征。这里,53,940 是我们数据集中样本的确切数量。现在,为了编码这些类别特征中的信息,我们使用独热编码技术将这些类别特征转换为虚拟特征。这样做的原因是因为scikit-learn只能处理数字。
下图展示了将类别特征转换为数字时使用的代码行:

在这里,我们可以看到如何使用来自pandas的get_dummies函数实现这一点。最终的数据集看起来类似于以下截图所示的样子:

在这里,对于类别变量中的每个类别,我们都有虚拟特征。当类别在特定的钻石中出现时,值为1,否则为0。
现在,为了重新缩放数据,我们将使用RobustScaler方法将所有特征转换到相似的尺度。
下图展示了导入train_test_split函数和RobustScaler方法时使用的代码行:

在这里,我们提取了X矩阵中的特征,指定了目标,然后使用来自scikit-learn的train_test_split函数将数据分为两组。
训练不同的回归模型
下图展示了我们将用来记录这些模型度量值的 dataframe,以及我们将使用的性能指标。由于这是一个回归任务,我们将使用均方误差。在这里,列中展示了我们将使用的四个模型。我们将使用KNN、Bagging、RandomForest和Boosting变量:

KNN 模型
K-Nearest Neighbours(KNN)模型不是集成学习模型,但它在所有简单模型中表现最好:

在上述模型中,我们可以看到在做 KNN 时使用的过程。我们将使用 20 个邻居。我们使用euclidean度量来衡量点之间的距离,然后训练模型。在这里,性能度量已保存,因为其值为1,即均方误差。
Bagging 模型
Bagging 是一种集成学习模型。任何估算器都可以与 Bagging 方法一起使用。所以,假设我们使用 KNN,如下图所示:

使用n_estimators参数,我们可以生成 15 个独立的估计器。因此,这将生成 15 个训练数据集的自助抽样(bootstrap samples),然后在每个样本中,使用 20 个邻居来拟合 KNN 回归模型。最后,我们将使用袋装方法(bagging method)获得个别的预测结果。该算法生成个别预测的方式是通过多数投票。
随机森林模型
随机森林是另一种集成学习模型。在这里,我们从scikit-learn中的ensemble子模块获取所有集成学习对象。例如,在这里,我们使用RandomForestRegressor方法。以下截图展示了这个模型使用的算法:

所以,在生成 50 个独立预测器的森林的情况下,这个算法会生成 50 棵独立的树。每棵树的max_depth为16,然后通过多数投票方法生成单独的预测结果。
Boosting 模型
Boosting 也是一种集成学习模型。在这里,我们使用AdaBoostRegressor模型,并且我们将再次生成50个估计器。以下截图展示了这个模型使用的算法:

以下截图展示了训练所有这些模型后得到的train_mse和test_mse结果:

以下截图展示了算法,并基于测试集均方误差值对这些模型进行比较。结果通过水平条形图显示:

现在,当我们比较所有这些模型的结果时,可以看到随机森林模型是最成功的。袋装(bagging)和 KNN 模型分别排名第二和第三。这就是为什么我们将 KNN 模型与袋装模型结合使用的原因。
以下截图展示了用于生成预测价格与实际价格之间图形表示的算法,同时还展示了随机森林模型的性能:

使用该模型时,通过predict API 或predict方法,我们可以获得个别预测。
例如,假设我们要预测从测试数据集获得的前十个预测值。以下算法展示了这个随机森林模型做出的预测结果,同时展示了测试数据集中钻石的真实价格和预测价格:

从这张截图中可以看到,真实价格和预测价格的值非常接近,无论是对于昂贵的钻石还是便宜的钻石。
使用集成方法进行分类
我们现在已经熟悉了集成学习和集成方法的基本概念。接下来,我们将实际应用这些方法,利用各种机器学习算法构建模型,并比较它们生成的结果。为了实际测试这些方法,我们需要一个样本数据集,以便将这些方法应用于给定数据集,并查看它如何帮助我们提升模型的性能。
预测信用卡数据集
让我们以一个信用卡数据集为例。这个数据集来自台湾的一个金融机构,可以在这里找到:www.kaggle.com/uciml/default-of-credit-card-clients-dataset。看看下面的截图,它展示了数据集的信息和特征:

这里,我们有每个客户的以下详细信息:
-
它包含了信用额度,即提供给使用信用卡的客户的信用限额。
-
然后,我们有一些关于每个客户的个人信息特征,比如性别、教育、婚姻状况和年龄。
-
我们还拥有过去付款的历史记录。
-
我们还有账单金额。
-
我们有从上个月到六个月前客户的账单金额和以前支付金额的历史记录。
有了这些信息,我们将预测下个月客户的付款状态。我们将首先对这些特征做一点转换,以便更容易理解。
在这种情况下,正类将是违约,所以数字 1 代表属于违约状态的客户,数字 0 代表已支付信用卡账单的客户。
现在,在开始之前,我们需要通过运行一些命令来导入所需的库,如下代码片段所示:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline
以下截图展示了用于准备信用卡数据集的代码行:

让我们为教育特征生成虚拟变量grad_school、university和high_school。用male虚拟变量代替性别一词,并且用married特征代替婚姻状况。这个特征在当一个人已婚时赋值为 1,否则为 0。对于pay_1特征,我们将进行一点简化处理。如果我们看到这里有一个正数,意味着客户在i个月内延迟了付款。这意味着 ID 为 1 的客户延迟了前两个月的付款。我们可以看到,3 个月前他/她并没有延迟付款。数据集大致如下:

在拟合我们的模型之前,我们做的最后一件事是重新缩放所有特征,因为正如我们在这里看到的,我们有些特征的尺度差异非常大。例如,limit_bal的尺度与age有很大不同。
这就是我们将使用scikit-learn中的RobustScaler方法的原因——为了尝试将所有特征转换到类似的尺度:

正如我们在前面的截图中看到的,在最后一行代码中,我们将数据集划分为训练集和测试集,在此之后,CMatrix函数用于打印每个模型的混淆矩阵。这个函数在下面的代码片段中进行了说明:
def CMatrix(CM, labels=['pay', 'default']):
df = pd.DataFrame(data=CM, index=labels, columns=labels)
df.index.name='TRUE'
df.columns.name='PREDICTION'
df.loc['Total'] = df.sum()
df['Total'] = df.sum(axis=1)
return df
训练不同的回归模型
以下截图显示了一个数据框,我们将把性能保存到这个数据框中。我们将运行四个模型,分别是逻辑回归、装袋、随机森林和提升:

在这种情况下,我们将使用以下评估指标:
-
accuracy:该指标衡量模型预测违约者和非违约者的准确性 -
precision:该指标衡量模型预测违约的准确性以及模型的正确预测频率 -
recall:该指标表示模型正确预测的实际违约者的比例
这些指标中最重要的是recall。原因在于我们希望最大化模型识别实际违约者的比例,因此选择具有最佳召回率的模型。
逻辑回归模型
正如在scikit-learn中,我们只需导入对象,然后实例化估算器,再将训练集X和训练集Y传递给fit()方法。首先,我们将预测测试数据集,然后生成准确率、精度和召回率的分数。以下截图显示了代码和混淆矩阵的输出:

稍后,我们将把这些数据保存到我们刚刚创建的pandas数据框中。
装袋模型
使用集成学习方法中的装袋模型进行训练时,需要导入带有逻辑回归方法的装袋分类器。为此,我们将拟合 10 个逻辑回归模型,然后通过装袋方法将这 10 个单独的预测结果合并为一个预测结果。之后,我们将把它保存在我们的指标数据框中。
以下截图显示了代码和混淆矩阵的输出:

随机森林模型
要使用随机森林模型进行分类,我们必须导入RandomForestClassifier方法。例如,我们选择 35 棵单独的树,每棵树的max_depth为20。max_features参数告诉scikit-learn,在决定最佳拆分特征时,我们应该使用特征总数的平方根。这些都是我们可以调整的超参数。
以下截图显示了代码和混淆矩阵作为输出:

提升模型
在使用提升模型进行分类时,我们将使用AdaBoostClassifier对象。在这里,我们还将使用50个估计器来组合各个预测。我们将使用的学习率是0.1,这是该模型的另一个超参数。
以下截图显示了代码和混淆矩阵:

现在,我们将比较以下截图中显示的四个模型:

上述截图显示了四个模型的准确率相似,但对于这个特定应用,最重要的指标是recall(召回率)。
以下截图显示了最佳召回率和准确率的模型是随机森林模型:

上述截图证明了随机森林模型总体上优于其他模型。
要查看precision(精准率)、recall(召回率)和threshold(阈值)之间的关系,我们可以使用scikit-learn中的precision_recall_curve函数。在这里,传入预测结果和实际观察值,得到的结果将是允许我们生成precision_recall_curve函数代码的对象。
以下截图显示了scikit-learn中precision_recall_curve函数的代码:

以下截图将可视化使用随机森林模型和逻辑回归模型时,精准率和召回率之间的关系:

上述截图显示了随机森林模型更好,因为它位于逻辑回归曲线之上。因此,在0.30的精准率下,随机森林模型的召回率比逻辑回归模型更高。
为了查看RandomForestClassifier方法的性能,我们改变分类阈值。例如,我们设置分类阈值为0.12,这样我们将获得30的精准率和84的召回率。该模型将正确预测 84%的可能违约者,对于金融机构来说非常有用。这表明,提升模型比逻辑回归模型更适合这种情况。
以下截图显示了代码和混淆矩阵:

特征重要性是我们在使用随机森林模型时获得的非常重要的信息。scikit-learn库会计算我们在模型中使用的每个特征的特征重要性指标。内部计算使我们能够得到每个特征在预测中的重要性指标。
以下截图展示了这些特征的可视化,从而突出了使用RandomForestClassifier方法的重要性:

对于预测客户是否会在下个月违约或客户是否在上个月已违约,最重要的特征是pay_1。在这里,我们只需要验证客户是否在上个月支付过。该模型的其他重要特征是两个月的账单金额,而另一个重要的特征是年龄。
对于预测目标不重要的特征包括客户的性别、婚姻状况和教育水平。
总体来说,随机森林模型已经证明比逻辑回归模型更好。
根据“没有免费午餐”定理,没有一个模型能在所有问题和数据集上都表现最好。这意味着集成学习并不总是优于简单方法,因为有时简单方法的表现比复杂方法更好。因此,对于每一个机器学习问题,我们必须先使用简单方法,再评估两种方法的性能,以获得最佳结果。
总结
在本章中,我们介绍了不同的集成方法,如自助抽样、装袋法、随机森林和提升法,并通过一些例子解释了它们的工作原理。然后,我们将它们用于回归和分类问题。对于回归,我们以一个钻石数据集为例,同时也训练了一些 KNN 和其他回归模型。随后,我们比较了它们的性能。对于分类,我们以信用卡数据集为例,再次训练了所有回归模型。我们比较了它们的性能,发现随机森林模型表现最好。
在下一章中,我们将学习 k 折交叉验证和参数调优。我们将使用 k 折交叉验证比较不同的集成学习模型,随后,我们将利用 k 折交叉验证进行超参数调优。
第二章:交叉验证与参数调优
预测分析是关于对未知事件进行预测的。我们使用它来生成能够泛化数据的模型。为此,我们使用一种叫做交叉验证的技术。
交叉验证是一种验证技术,用于评估统计分析结果的泛化能力,能够给出样本外准确度的度量。它通过对数据进行多次随机划分成训练样本和测试样本,然后对这些划分进行平均来完成这项任务。它通常用于超参数调优,即通过对多个可能的参数值进行交叉验证,选择给出最低交叉验证平均误差的参数值。
交叉验证有两种类型:穷举交叉验证和非穷举交叉验证。K 折交叉验证就是非穷举交叉验证的一种例子。它是一种获得更准确的模型性能评估的技术。通过使用 K 折交叉验证,我们可以进行超参数调优。超参数调优是选择适合我们模型的最佳超参数。像 K 折交叉验证和超参数调优这样的技术对于构建优秀的预测分析模型至关重要。交叉验证有许多变种或方法,例如保留法交叉验证和 K 折交叉验证。
在本章中,我们将涵盖以下主题:
-
保留法交叉验证
-
K 折交叉验证
-
使用 K 折交叉验证比较模型
-
超参数调优简介
保留法交叉验证
在保留法交叉验证中,我们将一部分观察数据保留出来,因此会得到两个数据集。一个被称为训练数据集,另一个被称为测试数据集。在这里,我们使用测试数据集来计算评估指标,剩下的数据则用于训练模型。这就是保留法交叉验证的过程。
保留法交叉验证的主要优点是它非常容易实现,是一种非常直观的交叉验证方法。
这种交叉验证方法的问题在于它只提供了模型评估指标的一个估计值。这个问题在于某些模型依赖于随机性。因此,原则上,由于随机因素的影响,计算出的测试评估指标有时会有很大的波动。因此,保留法交叉验证的主要问题是我们只得到评估指标的一个估计值。
K 折交叉验证
在 K 折交叉验证中,我们基本上多次执行保留法交叉验证。因此,在 K 折交叉验证中,我们将数据集划分为k个相等大小的子样本。在这些k个子样本中,单个子样本被保留作为验证数据,用于测试模型,其余的k−1个子样本用作训练数据。这个交叉验证过程将重复k次,每个子样本都恰好作为验证数据使用一次。然后,这k个结果可以进行平均,从而得出一个单一的估计值。
以下截图展示了 5 折交叉验证的可视化示例(k=5):

在这里,我们看到我们的数据集被划分为五个部分。我们使用第一部分进行测试,其余部分用于训练。
以下是我们在 5 折交叉验证方法中遵循的步骤:
-
我们获得了评估指标的首次估计。
-
我们使用第二部分进行测试,其余部分用于训练,并用其获取评估指标的第二次估计。
-
我们使用第三部分进行测试,其余部分用于训练,依此类推。这样,我们会得到五次评估指标的估计。
在 k 折交叉验证中,观察到* k 次评估矩阵后,我们会取其平均值。这将为我们提供模型性能的更好估计。因此,我们不仅能获得该评估指标的一个估计,而是通过 k 折交叉验证获得n*次估计,然后取其平均值,从而为模型性能提供更好的估计。
如此所见,k 折交叉验证方法的优点在于它不仅可以用于模型评估,还可以用于超参数调优。
在这种验证方法中,k的常见值为 5 和 10。
以下是 k 折交叉验证的变体:
-
重复交叉验证: 在重复交叉验证中,我们会进行多次 k 折交叉验证。所以,如果我们需要 30 次评估指标的估计,我们可以进行 5 折交叉验证六次。这样我们就能得到 30 次评估指标的估计。
-
留一法(LOO)交叉验证: 在这种方法中,我们使用整个数据集进行训练,除了一个数据点。我们使用这个数据点进行评估,然后对数据集中的每个数据点重复此过程。
如果我们有数百万个数据点,这种验证方法在计算上会非常昂贵。在这种情况下,我们使用重复的 k 折交叉验证,因为这种验证方法能够提供相对较好的结果。
实现 k 折交叉验证
让我们以diamond数据集为例来理解 k 折交叉验证的实现。
在scikit-learn中执行 k 折交叉验证时,我们首先需要导入将要使用的库。以下代码片段展示了用于导入库的代码:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline
第二步是准备我们将在此示例中使用的diamond数据集。以下展示了用于准备该数据集数据的代码:
# importing data
data_path= '../data/diamonds.csv'
diamonds = pd.read_csv(data_path)
diamonds = pd.concat([diamonds, pd.get_dummies(diamonds['cut'], prefix='cut', drop_first=True)],axis=1)
diamonds = pd.concat([diamonds, pd.get_dummies(diamonds['color'], prefix='color', drop_first=True)],axis=1)
diamonds = pd.concat([diamonds, pd.get_dummies(diamonds['clarity'], prefix='clarity', drop_first=True)],axis=1)
diamonds.drop(['cut','color','clarity'], axis=1, inplace=True)
准备好数据后,我们将创建用于建模的对象。以下展示了用于创建建模对象的代码:
from sklearn.preprocessing import RobustScaler
target_name = 'price'
robust_scaler = RobustScaler()
X = diamonds.drop('price', axis=1)
X = robust_scaler.fit_transform(X)
y = diamonds[target_name]
# Notice that we are not doing train-test split
#X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=55)
这是我们在第一章《回归与分类的集成方法》中使用的相同单元格。这里的区别是我们没有使用train_test_split函数。在这里,我们正在生成X矩阵,其中包含所有特征,同时也包含我们的目标特征。因此,我们有了X矩阵和y向量。
在训练模型时,我们将实例化我们的RandomForestRegressor函数,经过测试,我们发现它是第一章《回归与分类的集成方法》中最适合该数据集的模型。以下展示了实例化RandomForestRegressor函数的代码:
from sklearn.ensemble import RandomForestRegressor
RF = RandomForestRegressor(n_estimators=50, max_depth=16, random_state=123, n_jobs=-1)
为了执行 k 折交叉验证,我们从scikit-learn的model_selection模块中导入了cross_validate函数。以下展示了导入cross_validate函数的代码:
# this will work from sklearn version 0.19, if you get an error
# make sure you upgrade: $conda upgrade scikit-learn
from sklearn.model_selection import cross_validate
这个cross_validate函数的工作原理如下:
- 我们提供了估算器,它将是
RandomForestRegressor函数。以下展示了应用RandomForestRegressor函数的代码:
scores = cross_validate(estimator=RF,X=X,y=y,
scoring=['mean_squared_error','r2'],
cv=10, n_jobs=-1)
在这里,我们传递了X对象和y对象。
- 我们为该模型和数据集提供了一组我们希望评估的指标。在本例中,评估是使用
mean_squared_error函数和r2指标来完成的,如前面的代码所示。在这里,我们传递了cv中的k值。因此,在本例中,我们将进行十折交叉验证。
从这个cross_validate函数中得到的输出将是一个字典,包含相应的矩阵。为了更好地理解,输出被转换为一个数据框。以下截图展示了用于在数据框中可视化分数的代码以及数据框的输出:

在这里,我们应用了test_mean_squared_error和test_r2,这正是我们希望评估的两个指标。评估后,我们得到了train_mean_squared_error值和test_r2集合。因此,我们对测试指标感兴趣。
为了更好地评估模型的性能,我们将对所有单独的测量结果取平均值(均值)。
以下展示了获取平均测试 MSE和平均测试 R 平方值的代码,以及输出显示它们的值:
print("Mean test MSE:", round(scores['test_mean_squared_error'].mean()))
print("Mean test R-squared:", scores['test_r2'].mean())
因此,在这里,通过取平均,我们看到测试 MSE 的均值就是我们这里的值,另外一个指标是 R 平方评估指标的平均值。
使用 k 折交叉验证比较模型
由于 k 折交叉验证方法被证明是更好的方法,它更适合用来比较模型。其原因在于,k 折交叉验证提供了对评估指标的更多估计,通过对这些估计进行平均,我们可以更好地评估模型性能。
以下展示了用于导入比较模型的库的代码:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline
导入库之后,我们将导入diamond数据集。以下是用于准备这个diamond数据集的代码:
# importing data
data_path= '../data/diamonds.csv'
diamonds = pd.read_csv(data_path)
diamonds = pd.concat([diamonds, pd.get_dummies(diamonds['cut'], prefix='cut', drop_first=True)],axis=1)
diamonds = pd.concat([diamonds, pd.get_dummies(diamonds['color'], prefix='color', drop_first=True)],axis=1)
diamonds = pd.concat([diamonds, pd.get_dummies(diamonds['clarity'], prefix='clarity', drop_first=True)],axis=1)
diamonds.drop(['cut','color','clarity'], axis=1, inplace=True)
现在,我们需要在准备好数据集以进行模型比较后,准备建模对象。以下展示了用于准备建模对象的代码。在这里,我们有X矩阵,表示特征,以及y向量,表示该数据集的目标:
from sklearn.preprocessing import RobustScaler
target_name = 'price'
robust_scaler = RobustScaler()
X = diamonds.drop('price', axis=1)
X = robust_scaler.fit_transform(X)
y = diamonds[target_name]
在这里,我们将比较 KNN 模型、随机森林模型和集成模型。在这些模型中,使用十折交叉验证,我们将使用KNNRegressor、RandomForestRegressor和AdaBoostRegressor函数。
然后,我们将导入cross_validate函数。以下是用于导入这三个模型的cross_validate函数的代码:
from sklearn.neighbors import KNeighborsRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.ensemble import AdaBoostRegressor
from sklearn.model_selection import cross_validate
下一步是使用cross_validate函数比较模型。以下是用于比较这三种模型的代码块:
## KNN
knn = KNeighborsRegressor(n_neighbors=20, weights='distance', metric='euclidean', n_jobs=-1)
knn_test_mse = cross_validate(estimator=knn,X=X,y=y,
scoring='mean_squared_error',
cv=10, n_jobs=-1)['test_score']
## Random Forests
RF = RandomForestRegressor(n_estimators=50, max_depth=16, random_state=55, n_jobs=-1)
RF_test_mse = cross_validate(estimator=RF,X=X,y=y,
scoring='mean_squared_error',
cv=10, n_jobs=-1)['test_score']
## Boosting
boosting = AdaBoostRegressor(n_estimators=50, learning_rate=0.05, random_state=55)
boosting_test_mse = cross_validate(estimator=boosting,X=X,y=y,
scoring='mean_squared_error',
cv=10, n_jobs=-1)['test_score']
在这里,我们看到将用于每个模型的测试评估指标的结果。我们使用mean_squared_error函数。对于每个模型,我们使用十折交叉验证,获得结果后,我们得到test_score变量。这个test_score变量在此情况下是均方误差,并在以下代码中显示:
mse_models = -1*pd.DataFrame({'KNN':knn_test_mse,
'RandomForest': RF_test_mse,
'Boosting':boosting_test_mse})
下图展示了运行每个模型的十折交叉验证后得到的结果代码,并且展示了三种模型的 10 次评估指标估算值的表格:

该表展示了每个模型评估值的较大变化。为了了解任何模型的实际表现,我们取结果的平均值。因此,在前面的图表中,我们取所有值的均值后再绘制。
下图展示了用于计算均值的代码,以及显示每个模型的均值 MSE 值的图表:

经平均后,随机森林模型在这三种模型中表现最好。因此,经过平均后,第二名是 KNN 模型,集成模型则排名最后。
下图展示了用于获取这些评估指标箱线图的代码,以及显示三种模型箱线图的结果:

因此,观察这些评估指标的箱线图,我们看到随机森林表现最佳。
为了检查这些模型的变异度,我们可以分析回归模型测试 MSE 的标准差。以下截图展示了用于检查变异度的代码,以及展示这些模型标准差的图表:

上面的截图显示,在这个案例中,最常用的模型是 KNN 模型,其次是提升模型,而随机森林模型的变化最小。所以,在这三种模型中,随机森林模型是最好的。即使对于这个数据集,随机森林模型表现最佳。
超参数调优介绍
用于选择特定数据集的最佳估算器或选择所有超参数最佳值的方法称为超参数调优。超参数是那些在估算器中不能直接学习的参数。它们的值由建模人员决定。
例如,在RandomForestClassifier对象中,有许多超参数,如n_estimators、max_depth、max_features和min_samples_split。建模人员决定这些超参数的值。
穷举网格搜索
执行超参数调优的一个非常重要且常用的方法叫做穷举网格搜索。这是一种暴力搜索方法,因为它会尝试所有超参数网格中的组合。然后,对于每个超参数组合,使用 k 折交叉验证和任何其他指定的度量标准来评估模型。所以,给出最佳度量标准的组合将由我们将在scikit-learn中使用的对象返回。
让我们来看一个超参数网格的例子。在这里,我们尝试为n_estimators超参数选择三个不同的值,如 10、30 和 50。我们为max_features选择两个选项,如 auto 和平方根,并为max_depth分配四个值——5、10、20 和 30。所以,在这种情况下,我们将有 24 种超参数组合。这 24 个组合将会被评估。在这 24 个组合的每一种情况下,我们使用十折交叉验证,计算机将训练并评估 240 个模型。网格搜索面临的最大缺点是维度灾难,接下来的章节将详细讨论。维度灾难本质上意味着,随着参数数量的增加,你需要评估模型的次数会呈指数级增长。
如果某些超参数组合没有被测试,则可以将不同的网格传递给GridSearchCV对象。在这里,可以通过字典列表的形式传递不同的网格,因为每个网格在scikit-learn中都是一个字典。
永远不要使用整个数据集来调优参数,调优参数时始终进行训练集-测试集划分,否则超参数可能会过拟合于特定数据集,导致模型无法很好地泛化到新数据。
因此,我们进行训练集-测试集划分,并使用数据集的一部分来学习模型的超参数;我们留作测试的部分应当用于最终模型评估,之后再使用整个数据集来拟合模型。
在 scikit-learn 中的超参数调优
让我们以diamond数据集为例来理解scikit-learn中的超参数调整。
为了进行超参数调整,我们首先需要导入我们将使用的库。导入库的代码如下:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.metrics import mean_squared_error
%matplotlib inline
接着,我们对将用于本示例的diamond数据集进行转换。以下展示了为该数据集准备数据的代码:
# importing data
data_path= '../data/diamonds.csv'
diamonds = pd.read_csv(data_path)
diamonds = pd.concat([diamonds, pd.get_dummies(diamonds['cut'], prefix='cut', drop_first=True)],axis=1)
diamonds = pd.concat([diamonds, pd.get_dummies(diamonds['color'], prefix='color', drop_first=True)],axis=1)
diamonds = pd.concat([diamonds, pd.get_dummies(diamonds['clarity'], prefix='clarity', drop_first=True)],axis=1)
diamonds.drop(['cut','color','clarity'], axis=1, inplace=True)
在准备好数据后,我们将创建用于建模的对象。以下展示了创建建模对象的代码:
from sklearn.preprocessing import RobustScaler
from sklearn.model_selection import train_test_split
target_name = 'price'
robust_scaler = RobustScaler()
X = diamonds.drop('price', axis=1)
X = robust_scaler.fit_transform(X)
y = diamonds[target_name]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=123)
在执行并创建用于建模的对象之后,我们执行train_test_split函数。在前面的代码块中,我们将数据的 10%(0.1)用于测试,因此这一部分数据将在调整超参数后用于模型评估。
我们将使用以下参数来调整RandomForestRegressor模型:
-
n_estimators:此参数表示森林中树的数量。 -
max_features:此参数表示在寻找最佳分割时考虑的特征数量。可选择的选项有n_features,对应于自动超参数,或者特征数量的log2平方根。 -
max_depth:此参数表示树的最大深度。
将使用不同的网格搜索值来调整这些参数。对于n_estimators和max_depth,我们将使用四个不同的值。对于n_estimators,我们将使用[25,50,75,100]作为值,对于max_depth,我们将使用[10,15,20,30]作为值。对于max_features,我们将使用自动和平方根。
现在我们将显式实例化RandomForestRegressor模型,不带任何超参数。以下展示了用于实例化它的代码:
from sklearn.ensemble import RandomForestRegressor
RF = RandomForestRegressor(random_state=55, n_jobs=-1)
参数网格基本上是一个字典,其中我们传递超参数的名称和我们想尝试的值。以下代码块展示了具有不同参数的参数网格:
parameter_grid = {'n_estimators': [25,50,75,100],
'max_depth': [10,15,20,30],
'max_features': ['auto','sqrt']}
总的来说,我们有四个n_estimators值,四个max_depth值,以及两个max_features值。因此,计算后,总共有 32 种超参数组合。
为了在scikit-learn中执行超参数调整,我们将使用GridSearchCV对象。以下展示了用于从scikit-learn导入该对象的代码:
from sklearn.model_selection import GridSearchCV
在这里,我们传递我们想要调整的估算器,在此案例中是RandomForestRegressor。以下截图展示了使用的代码和我们得到的输出:

然后我们传递要尝试的参数网格。在这里,refit表示该估计器对象将使用通过网格搜索和交叉验证过程找到的最佳参数进行重新拟合。这是该对象用来评估所有可能超参数组合的评估指标。在这个例子中,我们将使用十折交叉验证。创建完之后,我们可以使用fit方法并传递训练对象。由于我们使用的是十折交叉验证,并且有 32 种组合,模型将评估 320 个模型。
我们可以使用从GridSearchCV方法创建的对象中的cv_results_属性来获取结果。以下截图展示了获取结果的代码和输出结果:

在这里,最重要的是获取best_params_。通过best_params_,我们可以看到所有 32 种组合的参数。以下截图展示了获取参数的输入和显示最佳结果的参数组合的输出:

在这里,我们可以看到能给出best_params_的组合是max_depth值为20,max_features值为 auto,n_estimators值为100。所以这是参数的最佳组合。
我们还可以获取best_estimator_对象,这个对象包含了完整的超参数列表。以下截图展示了获取best_estimator_的代码和输出结果:

所以,在调优了随机森林模型的超参数后,我们得到的值与之前得到的值不同;之前我们设置n_estimators为50,max_depth为16,max_features为 auto 时,模型的参数是未调优的。
比较调优和未调优的模型
我们可以将调优后的最佳模型与未调优的最佳模型进行比较,后者的n_estimators值为50,max_depth值为16,max_features为 auto,在这两种情况下,都是使用随机森林。以下代码展示了调优和未调优模型的参数值:
## Random Forests
RF_model1 = RandomForestRegressor(n_estimators=50, max_depth=16, random_state=123, n_jobs=-1)
RF_model1.fit(X_train, y_train)
RF_model1_test_mse = mean_squared_error(y_pred=RF_model1.predict(X_test), y_true=y_test)
## Random Forest with tunned parameters
RF_tunned_test_mse = mean_squared_error(y_pred=RF_classifier.predict(X_test), y_true=y_test)
要实际看到调优和未调优模型之间的比较,我们可以查看均方误差的值。以下截图展示了获取两个模型的mean_squared_error值的代码,以及比较两个模型 MSE 值的图表:

我们可以清楚地观察到,在比较中,这些调优后的参数在两个随机森林模型中表现得比未调优的参数更好。
为了查看两个模型之间的实际数值差异,我们可以做一些计算。以下截图展示了如何计算改进的百分比,并输出实际的百分比值:

在这里,调优后的模型比未调优的模型提高了 4.6%,这是一个非常好的结果。在这些模型中,即使是 1%-3%的改进也可能产生巨大的实际影响。
总结
在本章中,我们了解了交叉验证及其不同方法,包括保持法交叉验证和 k 折交叉验证。我们了解到,k 折交叉验证其实就是多次进行保持法交叉验证。我们使用diamond数据集实现了 k 折交叉验证。我们还通过 k 折交叉验证比较了不同的模型,并找到了表现最佳的模型——随机森林模型。
然后,我们讨论了超参数调优。我们遇到了穷举网格搜索方法,这是一种用于执行超参数调优的方法。我们再次使用diamond数据集实现了超参数调优。我们还比较了调优后的模型和未调优的模型,发现调优后的参数使得模型表现优于未调优的模型。
在下一章,我们将学习特征选择方法、降维技术和主成分分析(PCA),以及特征工程。我们还将学习一种通过特征工程改进模型的方法。
第三章:特征工程
本章将详细探讨特征在特征工程技术中的重要作用。我们将学习一些技术,这些技术将帮助我们从两个方面改进预测分析模型:一是提升模型的性能指标,二是理解特征与我们试图预测的目标变量之间的关系。
本章将涵盖以下主题:
-
特征选择方法
-
降维和 PCA
-
创建新特征
-
通过特征工程改进模型
特征选择方法
特征选择方法用于选择可能有助于预测的特征。以下是三种特征选择方法:
-
去除低方差的虚拟特征
-
统计学上识别重要特征
-
递归特征消除
在构建预测分析模型时,某些特征可能与目标无关,这将对预测帮助较小。问题在于,将无关特征包含在模型中可能会引入噪声并增加模型的偏差。因此,特征选择技术是一系列技术,用于选择最相关和有用的特征,以帮助预测或理解我们的模型。
去除低方差的虚拟特征
我们将学习的第一种特征选择技术是去除低方差的虚拟特征。到目前为止,我们对特征进行的唯一变换是使用编码技术转换类别特征。如果我们取一个类别特征并使用这种编码技术,我们会得到一组虚拟特征,需要检查这些特征是否具有变异性。因此,方差非常低的特征可能对预测几乎没有影响。那么,为什么会这样呢?假设你有一个数据集,其中有一个性别特征,且 98%的样本都是女性。这个特征对预测没有任何影响,因为几乎所有的样本都属于同一类别,所以变异性不足。这些样本成为待淘汰的候选项,这类特征应该更仔细地进行检查。接下来,看看以下公式:

你可以去除在超过 x%的样本中为 0 或 1 的所有虚拟特征,或者你可以为这些特征建立一个最小方差阈值。现在,这些特征的方差可以通过前面的公式计算,其中p是虚拟特征中1的数量或比例。我们将在 Jupyter Notebook 中看到这种方法的应用。
统计学上识别重要特征
这种方法将帮助你利用一些统计检验来识别和选择相关特征。举个例子,对于分类任务,我们可以使用 ANOVA F 统计量来评估数值特征与目标之间的关系,目标将是一个分类特征,因为这是经典任务的例子。或者,评估分类特征与目标之间的统计关系时,我们将使用卡方检验来评估这种关系。在scikit-learn中,我们可以使用SelectKBest对象,接下来我们将展示如何在 Jupyter Notebook 中使用这些对象。
递归特征消除
识别重要特征并去除我们认为不重要的特征的过程叫做递归特征消除(RFE)。RFE 也可以在scikit-learn中应用,我们可以使用这种技术来计算系数,比如线性回归、逻辑回归,或者通过模型计算所谓的特征重要性。随机森林模型为我们提供了这些特征重要性指标。因此,对于那些不计算系数或特征重要性的模型,无法使用这些方法;例如,对于 KNN 模型,你不能应用 RFE 技术,因为这首先要求预定义模型中需要使用的特征。使用所有特征时,该方法先拟合模型,然后基于系数或特征重要性,最不重要的特征被消除。这个过程会在选定的特征集上递归重复,直到最终选择出所需数量的特征。
选择模型中重要特征的方法如下:
-
L1 特征
-
选择阈值方法
-
基于树的方法
让我们进入 Jupyter Notebook,看看我们如何在scikit-learn中实际应用这些方法。以下截图展示了需要导入的必要库和模块:

在下面的截图中,我们首先使用了信用卡默认数据集,并对原始数据应用了传统的转换方法:

以下截图展示了数据集中我们拥有的虚拟特征和数值特征,取决于特征的类型:

在这里,我们应用了特征建模的缩放操作:

在演示中我们讨论的第一个方法是使用var()方法去除低方差的虚拟特征,从而得到特征的方差:

让我们只查看虚拟特征的方差。例如,方差的阈值将仅考虑方差大于0.1的虚拟特征。在这种情况下,使用 0.1 作为阈值,pay_5和pay_6这两个候选特征将是第一个需要删除的方差较低的虚拟特征。请看下面的截图,展示了候选的删除特征:

我们讨论的第二种方法是通过统计方法选择与目标相关的特征,我们有两种情况,虚拟特征和数值特征。
让我们对虚拟特征执行统计检验。我们将从scikit-learn库中的feature_selection模块导入chi2对象。我们还将使用SelectKBest对象,对所有虚拟特征执行统计检验,如下图所示:

在这里,我们实例化了一个名为dummy _selector的对象,并传入了要应用的统计检验。这里,我们传入了k ="all"参数,因为这个统计检验是应用于所有的虚拟特征。实例化这个对象后,调用fit()方法。请看下面的截图:

在下图中,我们展示了卡方得分。这不是一个统计检验,数字越大,特征与目标之间的关系越强:

现在,如果你还记得你的统计学课,这其实是一个假设检验的设置。因此,我们也可以计算 p 值,并且可以说,pvalues_大于0.05的特征与目标没有关系。现在,在这种情况下,我们得到所有特征的非常小的 p 值,如下图所示:

目标与所有虚拟特征之间都有关系,因此在这种方法下,我们不应该删除任何一个虚拟特征。
现在,我们可以使用另一个统计检验方法,叫做f_ classif,来评估数值特征与目标之间的关系,如下图所示:

重新使用这个f_classif对象,我们将传入所需的统计检验和特征数量。在这种情况下,我们希望将检验应用于所有数值特征,然后再次使用fit()方法,处理数值特征和目标:

我们从这个统计检验中得到的 p 值如下图所示:

我们可以通过f_classif统计检验,然后选择那些 p 值大于0.05的数值特征,这通常是统计检验的阈值;这里得到的特征是bill_amt4、bill_amt5和bill_amt6,这些特征可能不相关或与目标无关:

我们有三个待排除的候选特征,可以排除也可以保留。我们已经使用了前面步骤中的第二种技术,现在我们将在下一部分使用第三种技术。
RFE 是我们将使用RandomForestClassifier模型的第三种技术,记住这里有 25 个特征:

所以,假设我们只想选择 12 个特征,并且我们希望使用仅包含 12 个特征的模型。也就是说,我们只使用了大约一半的特征。我们可以使用scikit-learn中的RFE对象,来自feature_selection模块。我们可以用它来实际选择这 12 个特征,使用 RFE 技术。所以,我们通过传递所需的估计器和要选择的特征数量来实例化这个对象:

现在,记住随机森林为我们提供了特征重要性度量,这可以与 RFE 技术一起使用:

在对整个数据集使用fit()方法后,我们得到recursive_selector.support_,对于包含在模型中的特征(我们想要的 12 个特征)返回True,而对于应该排除的特征返回False:

所以,根据这个对象和方法,我们应该在我们的随机森林模型中包含 12 个最重要的特征,以便预测目标,比如limit_bal、age、pay,所有账单金额,以及pay_amt1、pay_amt2和pay_amt3,如以下截图所示:

这些是应该被排除的特征,因为根据这个方法和模型,它们与预测目标的相关性不大:

现在我们可以评估更简单的模型,这个模型只有 12 个特征,与我们目前为止使用的完整模型进行比较,然后我们可以使用交叉验证来计算这些指标。所以,在这个例子中,我们使用了 10 折交叉验证来估算这两个模型的性能。记住,这个选择器模型是根据 RFE 技术得到的完整模型,以下是结果:

完整模型的召回率是0.361365,而只包含 12 个特征的模型的召回率是0.355791。由于这个模型的召回率较低,完整模型依然是最好的。但是,如果我们使用一半的特征,完整模型也会给出类似的表现:

正如你在下面的截图中看到的那样,这些值非常接近:

现在,你可以决定是使用完整模型,还是使用更简单的模型。这个取决于你,但在准确性方面,我们几乎得到了相同的结果,尽管完整模型的准确性稍微高一些:

现在,你有了一种技术来决定是否使用一个更复杂的模型,它使用更多的特征,或者使用一个更简单的模型。
降维和 PCA
降维方法是通过获取一组主变量来减少考虑的特征数量的过程。主成分分析(PCA)技术是用于降维的最重要技术。在这里,我们将讨论为什么需要降维,并且还将看到如何在scikit-learn中执行 PCA 技术。
这些是在进行预测分析时拥有大量特征的原因:
-
它使得模型简化,从而更容易理解和解释。如果你处理的是成千上万的特征,可能会涉及一些计算上的考虑。在这种情况下,减少特征数量有助于节省计算资源。
-
另一个原因是避免“维度灾难”。这是一个技术术语,指的是当你处理高维数据时会遇到的一组问题。
-
这也有助于我们减少过拟合,因为如果你包含了很多无关的特征来预测目标,那么你的模型可能会对这些噪声过拟合。因此,去除无关特征有助于避免过拟合。
本章之前看到的特征选择可以被认为是一种降维方式。当你拥有一组密切相关甚至冗余的特征时,PCA 将是首选技术,用更少的特征编码相同的信息。那么,什么是 PCA 呢?它是一种统计过程,将一组可能相关的变量转换为一组线性不相关的变量,称为主成分。我们不深入讨论 PCA 背后的数学细节。
假设我们有一个二维的数据集。PCA 识别出一个方向,在这个方向上数据集变化最大,并将这两个特征上的最大信息量编码成一个特征,从而将维度从二维降低到一维。此方法将每个点投影到这些轴或新的维度上。
正如你在下面的截图中看到的那样,这两个特征的第一个主成分将是这些点投影到红线上的结果,这就是 PCA 中发生的主要数学直觉:

现在,让我们进入 Jupyter Notebook,看看如何实现降维方法并在给定的数据集上应用 PCA:

在这种情况下,我们将使用信用卡违约数据集。所以,在这里我们做了到目前为止我们已覆盖的转换:

现在,让我们看看账单金额特征。我们有这六个特征,它们是来自一到六个月前的账单金额历史,正如你从以下截图中生成的可视化中看到的,它们密切相关:

所以,它们表示相同的信息。如果你看到一个客户在两三个月前账单金额非常高,那很有可能他在一个月前也有非常高的账单金额。正如你从以下截图中的可视化中看到的,这些特征真的有很高的相关性:

我们通过计算相关系数来确认这一点。正如你所看到的,它们的相关性非常高:

账单金额一个月前和两个月前之间的相关系数为0.95。我们有非常高的相关性,这是应用降维技术的一个好机会,比如在scikit-learn中的 PCA,我们从sklearn.decomposition导入它,如下截图所示:

之后,我们实例化这个PCA对象。然后,我们传入我们想要应用 PCA 分解的列或特征:

所以,在使用从该对象派生的fit()方法后,我们获得了其中一个属性——解释方差比率,如下图所示:

让我们绘制这个量,以便了解这些特征的情况。正如你在这里看到的,我们得到了所有六个组件的解释方差:

阅读这个图的方式是,我们对这六个特征做的 PCA 的第一个成分编码了所有六个特征总方差的 90%以上。第二个成分展示了非常小的方差,第三、第四、第五和第六个成分也有最小的方差。
现在,我们可以在以下截图中看到累积解释方差的图像:

如你所见,第一个主成分编码了我们使用的六个特征中超过 90%的方差。因此,你只用一个特征就能获得超过 90%的信息。因此,与你使用六个特征相比,你只需使用一个特征就能得到超过 90%的方差。或者,你可以使用前两个主成分,通过这两个主成分就能获得六个特征中超过 95%的信息。所以,这就是实际操作的方式,我们可以将其作为进行特征工程的一种技术。
特征工程
特征工程在使机器学习算法有效工作中起着至关重要的作用,如果执行得当,它可以增强机器学习算法的预测能力。换句话说,特征工程是从原始数据中提取现有特征或利用领域知识、问题背景或专业技术创造新特征的过程,从而得出更准确的预测模型。这是一个领域知识和创造力发挥重要作用的活动。这是一个重要的过程,能够显著提高我们预测模型的性能。你对问题的背景了解得越多,创造新特征的能力就越强。基本上,特征工程过程将特征转换为算法可以理解的输入值。
实现特征工程的方式有很多种。你可能不会发现所有技术都可行,可能最终会排除一些方法。这里的目的不是进行学术讨论,而是展示一些我们在处理特征和尝试创建新特征时常用的技术。第一种是特征缩放,用于将特征的范围转换为更合适的范围。另一种是更好的编码信息,稍后我们将在本章中看到一个例子。特征工程涉及从现有特征中创建新特征,以便通过对它们进行一些数学运算来组合现有特征。
另一种创建新特征的方式是使用降维技术,比如我们之前看到的 PCA。无论你使用什么技术,只要能够富有创造性就行。如前所述,你对问题了解得越多,效果就越好。
创建新特征
我们将在这里使用信用卡违约和钻石数据集。现在,让我们进入 Jupyter Notebook,创建新特征,并看看这些技术如何在实践中应用:

让我们通过执行几个命令来导入信用卡违约数据集,具体操作请参考下面的截图:

我们将做的第一个转换是创建另一种编码education特征信息的方式。到目前为止,我们在education特征中使用了一种编码技术,我们将利用x变量的上下文来提出另一种编码方式。具有研究生教育水平的人,比其他教育水平的人更受教育。因此,我们可以为这些特征想出某种积分系统;例如,我们可以给具有研究生教育的人分配两分,给具有大学教育的人分配一分,并为数据集中其他教育水平的人分配负分。
让我们看一下下面的截图,看看这是如何做到的:

上面的截图反映了我们在这些教育水平中的顺序,所以这可能是另一种编码信息的方式。这可能有助于或可能没有帮助于预测下个月的违约者。然而,我们可以尝试这种新技术来编码这些信息,并查看接下来的截图中的结果:

我们可以在这个数据集中使用的另一个技巧是,利用账单金额和支付金额特征,在这个问题的背景下计算这两个变量/特征之间的差异。因此,如果我们取某个月的账单金额,并减去该月的支付金额,就会得到一个数值或数量。在这个示例中,我们将其称为bill_minus_pay变量,代表客户在该月针对账单支付的金额。因此,这个新衍生的数量可以用来预测下个月的违约者。我们已经将其包含在这个数据集的潜在预测模型中:

现在让我们来看一下下面的输出,它显示了某个月的违约者情况:

现在,我们可以使用另一种方法,既然我们已经有了这些特征的一部分信息,在一个名为bill_minus_pay的新特征中,我们可以使用 PCA 技术将前面截图中显示的六个特征的主要信息总结为仅一个特征:

我们可以对支付特征做同样的操作。从之前的分析中,我们知道pay_1特征对于预测谁将下一个支付非常重要。因此,为了将其他五个pay_i特征减少到仅两个,我们将六个账单金额特征减少到仅一个,并将六个pay_i特征减少到两个。除此之外,我们再次在剩余的五个pay_i特征上应用 PCA 技术,将这五个特征减少到仅一个。看看下面的截图:

这些是一些特征工程技术,带有示例,您可以在数据集中执行这些操作,但您可能希望根据现有特征创建其他转换或变量。
现在,让我们看几个钻石数据集的例子。我们需要通过执行几个命令来导入钻石数据集,如下图所示:

如前面的截图所示,我们已经使用编码技术对一些分类特征进行了转换。现在,让我们来看一下我们导入的数据集,如下图所示:

这是我们四个特征的散点图矩阵:x、y、z 和 price。前三个特征指的是钻石的测量值,而price表示这三个特征与钻石定价之间的关系:

在前面的截图中,正如你所看到的,前三个特征之间有非常强的线性关系,这也是该散点图矩阵中的一个有趣之处。
由于钻石是三维物体,我们将这三个特征合并成一个单一的特征,称为体积。
现在,我们将乘以x、y和z轴的测量值,这将得出一个接近该物体体积的数字:

现在我们知道它们不是盒子,而且没有固定的形状。然而,这将是一个非常好的体积近似值。因此,这可以是我们从数据集中现有特征中创建新特征体积的另一种方法。
在下一个截图中,我们有了物体的体积,以及钻石的重量(以克拉为单位),我们将使用这些来创建一个新的特征,称为密度:

正如你在前面的截图中看到的,我们将克拉除以体积,从而得到钻石物体的密度。
这是我们如何从给定的背景中创建两个特征,这也证明了“对问题的知识或背景越多,效果越好”这一说法。正如你所看到的,凭借仅有的背景知识,我们能够提出新的特征。
现在,让我们试着看看这些特征在预测模型中的帮助。我们将在这里使用的例子是,如何将现有特征组合起来生成新的特征。下图展示了体积与价格之间的紧密关系:

我们可以假设体积在预测价格中会有帮助。
然而,在下面的密度与价格的散点图中,我们发现所有钻石的预期密度都是相同的:

当我们看到 price 和 carat 之间的相关性时,正如我们之前所说,似乎 density 与 price 的关系不大:

所以,这个新特征可能对预测帮助不大。volume 和 carat 特征之间有相似的关系。我们可能无法通过这个特征获得太多的预测能力,但解释这个例子的主要目的是展示如何结合数据集中已有的不同特征来创建新特征。
这就是特征工程的核心。你也许还可以为这个数据集提出其他特征。
通过特征工程改进模型
现在我们已经看到特征工程技术如何帮助构建预测模型,接下来让我们尝试提高这些模型的性能,并评估新构建的模型是否比之前的模型表现得更好。然后,我们将讨论两个在进行预测分析时必须始终牢记的重要概念,这就是预测模型中的可约误差和不可约误差。
首先,我们需要导入必要的模块,如下图所示:

所以,让我们进入 Jupyter Notebook,看看我们在本章前面看到的信用卡违约数据集,但正如你所看到的,这个数据集已经做了一些修改:

对于这个模型,之前我们一直在使用的 sex 和 marriage 特征被转换成了两个虚拟特征——male 和 married;因此,让我们以稍微不同的方式编码这些信息,看看这种方式是否更有效。所以,我们将这些信息编码为 married_male 和 not_married_female,并看看这种方法是否更好。这是我们在这里进行的第一次转换。数据集如下所示:

现在,让我们做一些更多的特征工程。我们首先要做的是计算这些新特征,这些特征是通过从账单金额中减去支付金额得到的,如下图所示:

对于这个问题,我们将执行一个数学操作。我们将使用前面截图中显示的新特性来预测目标。账单金额特性中的大部分信息现在已编码到这些特性中,因此这些特性不再需要,但我们可以做的是使用 PCA 技术将六个账单金额特性减少到一个特性。因此,让我们应用 PCA 技术将六个特性减少到一个组件。现在有一个新的特性叫做bill_amt_new_feat。这就是我们执行的第二个特性工程步骤。最后,对于pay_i特性,我们将保留第一个特性不变,并对最后五个特性pay_2、pay_3、pay_4、pay_5和pay_6应用 PCA 技术,将这五个特性减少为两个组件。你可以使用PCA对象的fit_transform方法来获取这些组件。
现在,让我们看看下面的截图,展示了所有与金钱相关的特性。正如你所看到的,这里的方差非常大,因为货币金额很大:

现在,通过将这些特性除以 1,000 来重新缩放它们,以减少方差,如下图所示:

这有助于我们使这些数字更易理解。因此,这就是我们进行的另一个转换,现在让我们使用这些新特性来训练我们的模型。
训练你的模型
以下模型是一个新模块,因为它与其他模型相比具有不同的特性。由于特性发生了变化,我们需要使用GridSearchCV模块为RandomForestClassifier模块找到最佳超参数。因此,之前找到的最佳参数可能不适合这些新特性;因此,我们将再次运行GridSearchCV算法:

如下图所示,在这种情况下,这些新特性最佳的参数组合是max_depth为30,max_features为auto,n_estimators(估计器数量)应该是100:

现在,让我们评估我们通过特性工程构建的这个新模型,并与我们之前构建的模型的度量进行比较:

正如你在前面的截图中看到的,我们使用了 0.2 的阈值。该模型生成了71.39%的召回率和37.38的精确度。这里,精确度相似,但正如前面提到的,召回率可能是我们更应关注的指标,因为它与之前的模型略有不同。我们通过这个模型略微提高了召回率;这个变化可能只有 2%或 3%,看起来不算多,但请记住,在这些金融应用中,1%或 2%的提升在实际中可能意味着大量资金。所以,通过这个小小的特征工程技巧,我们在模型的预测能力上取得了一些微小的改进;接下来我们来看下一个截图中的特征重要性:

你可以在下面这张随机森林模型的截图中评估这个特征重要性是否合理:

你可以将这个特征重要性与之前的进行比较。在应用特征工程后,你可以做很多事情。我们可能会改善性能,并从模型中获得洞见。观察到通过使用这个技巧,我们的模型有所改进。现在,你可以通过不同的方式组合现有特征,以进一步改善模型。这只是一个小而简单的例子,旨在向你展示,你实际上可以通过合适的方式对特征进行调整。
可约错误和不可约错误
在继续之前,有两个非常重要的概念需要在预测分析中讲解。错误可以分为以下两种类型:
-
可约错误:这些错误可以通过对模型进行一定的改进来减少。
-
不可约错误:这些错误是无法减少的。
假设在机器学习中,特征与目标之间的关系通过一个函数表示,如下图所示:

假设目标(y)是机器学习的基本假设,特征与目标之间的关系由一个函数表示。由于在大多数情况下,我们认为特征和目标之间的关系具有某种随机性,我们在这里加入了一个噪声项,这在现实中总是存在的。这就是机器学习中的基本假设。
在模型中,我们尝试通过使用实际函数来近似理论函数,同时进行特征工程、调优参数等操作:

因此,我们的预测是这些近似结果应用到概念性或理论性f的结果。我们在机器学习中所做的一切,都是通过训练模型来尽量逼近这个f函数。
训练模型意味着逼近这个函数。从数学上可以证明,定义为真实y与预测y之间差异的期望误差,可以分解为两个项。一个项叫做可约误差,另一个项叫做不可约误差,如以下截图所示:

现在,不可约误差 项是该随机项的方差。你无法控制这个项。总会存在一个不可约误差成分。因此,你的模型总是会出错;无论你有多少特征和数据点,你的模型无法始终达到 100%的准确度。我们必须尝试使用更好的、更复杂的方法进行特征工程,并尽量让我们的估计尽可能接近真实函数。仅仅因为你使用了更复杂的模型或拥有更多数据,并不意味着你的模型会完美,你无法精确预测y的值,因为几乎所有你将使用的过程都包含一些随机性。所以,这是一个非常有趣部分的结束。
摘要
在本章中,我们讨论了特征选择方法,如何区分有用特征和那些在预测中不太可能有帮助的特征。我们讨论了降维,并学习了如何在scikit-learn中执行 PCA。我们还讨论了特征工程,并尝试为我们至今使用的数据集提出新的特征。最后,我们通过提出新特征并结合本章所学的所有技术,尝试改进我们的信用卡模型。希望你喜欢本章内容。
在下一章中,我们将学习人工神经网络,并了解在使用神经网络和人工智能时如何使用tensorflow库。
第四章:人工神经网络和 TensorFlow 简介
本章将介绍人工神经网络(ANNs),这些基本上是受到生物大脑启发的计算模型,以及感知器,它们是 ANNs 的构建块。我们还将讨论在构建深度神经网络模型时需要考虑的所有元素。接着,我们将介绍 TensorFlow,这是我们将用于创建这些深度神经网络模型的库。最后,我们将讨论使用该库的核心概念,如变量、占位符、会话、图和其他在使用该库时至关重要的概念。
接下来我们将覆盖以下主题:
-
人工神经网络简介
-
深度神经网络的元素
-
TensorFlow 的安装和简介
-
TensorFlow 的核心概念
人工神经网络简介
人工神经网络(ANNs)是生物学启发的计算模型,可用于通过数据训练计算机执行任务。这些模型属于机器学习模型的广泛范畴。与其他模型的区别在于,这些模型基于一组名为人工神经元的连接单元。
人工神经网络有许多类型,在本书中,我们将使用一种特定类型,称为多层感知器(MLP)。请注意,人工神经网络有更多的变种。这些是机器学习模型,我们可以用它们进行分类和回归任务,但我们实际上可以扩展这些模型,将其应用于其他非常具体的任务,如计算机视觉、语音识别和机器翻译。这些模型是深度学习这一激动人心且不断发展的领域的基础,近年来在许多领域取得了巨大的成功。
感知器
感知器是最简单的人工神经元类型,是作为二分类的简单模型发明的。我们使用本书中使用的数据集,即信用卡数据集。假设我们只有两个特征来分类违约者和非违约者:年龄和账单金额。那么感知器的思路是创建某种得分。为此,你将一个常数w1与age的值相乘,然后再加上另一个常数w2,它与bill金额的值相乘,如下所示:
score = w1age+w2bill
作为规则,如果score > b,我们将此人分类为违约者。
因此,从这个简单的操作中,我们创建了一个得分。接着,我们根据规则将人们分类为违约者或非违约者。所以,如果这个score大于某个数值,那么我们就将此人归类为违约者。
以下截图显示了一种等效的规则表述方式:

所以,这个模型的预测结果将是 1,即违约者,如果该数量大于 0,预测将是 0,即非违约者,如果该数量小于或等于 0。b 值也称为阈值或偏置。
一般来说,如果我们有 n 个特征,那么我们的感知器看起来会类似于下面的截图:

如你所见,我们有相同的形式。如果所有特征的权重与特征值的乘积和 -b 实际上大于 0,那么我们预测 1,否则我们预测 0。假设所有特征都在相同的尺度上,那么权重将表示每个特征在做出决策时的重要性。因此,我们知道,在这个特定问题中,所有特征的尺度是非常不同的。例如,年龄的尺度与账单金额的尺度不同,但假设你将所有特征设置为相似的尺度。你可以将 w 变量视为权重,它们是做出决策时每个特征最重要的部分。
以下截图展示了另一种可视化感知器的方法:

所以,你有阈值或偏置 b 的值,以及 Age(年龄)、x1 和 Bill amount(账单金额)、x2 的值。然后这三个值会进入一个操作,得出输出。现在,我们可以对感知器做一点修改,那就是添加一个被称为 激活函数 的东西。激活函数是任何一个函数,它接受操作结果,并通过 f 函数对输入值进行某些变换。因此,激活函数的输入是操作的结果量,经过激活函数 f 后,我们将得到以下输出:

这是感知器。我们可以向感知器添加一个激活函数,然后我们得到规则或分类 1 或 0。
现在,也许你在想我们如何决定感知器的最佳权重和阈值?我们可以使用什么激活函数?这些问题的答案由感知器学习算法提供。所以,我们有一个学习算法,可以用来实际训练感知器。感知器的好处是它们非常容易理解。然而,与我们在之前章节中使用的更复杂的方法相比,它们的性能非常弱。因此,实际上没有必要学习这个感知器学习算法。然而,这些非常简单的模型是人工神经网络(ANNs)的构建块。
多层感知器
人工神经网络(ANN)是基于感知器或其他类似基本构建块的模型,本书中我们将学习的就是基于感知器的模型。最流行的人工神经网络模型之一是多层感知器(MLP),我们将在本书中使用它。使用感知器作为 ANN 的一部分的动机是,为什么不使用多个感知器,而不是仅仅使用一个感知器进行分类呢?请看以下截图:

在这里,我们有三个感知器,并注意到每个感知器都有不同的偏置项。但我们的特征值在所有情况下都是相同的。如果我们使用三个感知器,我们将得到三个输出值,但我们知道这是一个二分类问题,因此我们只需要一个输出。现在我们有了三个输出值,可以将它们结合起来,或者将这些输出值视为另一个感知器的输入。请看以下截图:

如下图所示,我们可以将前面感知器的输出值作为输入值提供给另一个感知器,后者将输出结果。因此,这就是构建神经网络或多层感知器(MLP)的直观理解,下面是一个人工神经网络(ANN)的示例:

在前面的截图中,我们展示了一个多层感知器(MLP)的三层结构:
-
输入层:在此层中,您将拥有原始数据或用于训练此模型的训练数据
-
隐藏层:该中间层是前一个感知器的输出,作为下一个感知器的输入
-
输出层:在此层中,您将得到从网络获得的输出
以下截图是另一种可视化相同人工神经网络(ANN)的方法:

这是一种更紧凑的可视化方式,但它实际上是相同的网络。所以,除了使用三个偏置项外,我们为每个观察值添加了一个常数特征1。该值1会与不同的偏置相乘,并作为输入提供给隐藏层的神经元。x1的值会与某些权重相乘,并作为下一个神经元的输入,x2的值也是如此。然后,隐藏层神经元的结果将作为最后一个感知器的输入,得出整体输出。
深度神经网络模型的元素
深度神经网络(DNN)的动机是类似的,这里问的是,如果我们使用多个隐藏层,而不是一个隐藏层,会怎样?在这种情况下,我们的模型将看起来类似于以下结构:

在这里,我们有相同的输入层。然而,在这种情况下,我们将有许多隐藏层,而输出层保持不变。这里的关键是网络的隐藏部分,即隐藏层;我们不再只有一个隐藏层,而是有许多隐藏层,这被称为DNN。
深度学习
深度学习是一类基于神经网络的机器学习模型及其相关技术,用于使用数据训练此类模型。深度学习模型有很多种。它们是一类具有以下特点的机器学习算法:
-
这些模型使用许多非线性处理单元的多层结构,可以进行抽象特征提取和转换。
-
这些模型使用某种形式的梯度下降,通过反向传播进行训练。
-
这些模型通常需要大量数据和大量计算能力才能表现得非常好。
-
这些模型现在被认为是许多应用(如计算机视觉、语音识别和游戏)中的最先进技术。
MLP 模型的元素
在构建深度学习模型时,有许多需要考虑的因素,特别是在多层感知机中。你必须考虑架构、激活函数、优化算法、loss函数、权重初始化策略、正则化策略和训练策略。我们将在以下列表中进一步讨论它们:
- 架构:在构建深度学习模型时,第一个需要考虑的元素是你的 MLP 架构。当我们说架构时,我们指的是层数和每层的神经元数量。输入层的神经元数量由数据集中你所拥有的特征数量决定。输出值的数量也是如此。所以,它们基本上由你的问题在分类设置中来决定。输出值的数量通常是你分类问题中的类别数,在回归问题中,输出层只会有一个输出。你需要做出的选择是使用多少隐藏层以及每个隐藏层的神经元数量。没有简单的规则来设置这些数字;在实践中,我们通常先使用几个层。如果几个层不起作用,也许我们会增加更多的层,而每层的神经元数量通常是在输入值数量和输出数量之间的一个数字,
[n_inputs, n_outputs]。
这只是一个经验法则。然而,还有一些更正式的方法可以选择隐藏层的数量和神经元的数量,研究人员也在不断努力提出更好的方法来选择这些值。
-
激活函数:激活函数是每个隐藏层神经元中使用的函数。有很多选择;sigmoid 是这些模型最早使用的函数,但后来研究人员发现使用这个函数存在很多问题,因此他们提出了其他激活函数,如修正线性单元(ReLU)、双曲正切、漏泄 ReLU,以及一些其他选择,我们将在接下来的示例中使用这些函数。
-
优化算法:这是用于学习网络权重的算法。每个你选择的算法都有不同的超参数,需要由你,作为建模者来选择。训练这些网络的最基本算法是梯度下降。然而,梯度下降可能较慢,并且存在一些问题,因此研究人员提出了其他算法,如动量优化器、AdaGrad、RMSProp 和 Adam 动量算法。在 TensorFlow 中,我们有很多算法可以选择,包括 Adam 动量算法,实际上我们将在示例中使用的就是这个算法。
-
损失函数:这是一个函数,它会生成一个量,这个量将被优化器最小化。损失函数的选择取决于问题。如果我们处理的是回归问题,可以选择均方误差或均方成对误差。对于分类问题,有更多的选择,比如交叉熵、平方损失和铰链损失。这就像试错一样;有时候,一个损失函数适用于你的问题,而有时候则不适用。因此,你必须考虑很多不同的损失函数。然而,记住,损失函数会生成一个量,这个量将被优化算法用来调整网络中不同感知机的不同权重。因此,这是生成量的函数,而优化器的目标是使这个量尽可能小。
-
权重初始化策略:网络中每个感知机的权重必须初始化为一些值,这些值将被优化算法逐步调整,以最小化损失。初始化这些值的方法有很多种。你可以选择初始化为零。多年来,研究人员通常使用随机正态分布进行初始化,但近年来,研究人员提出了更好的选择,包括 Xavier 初始化和 He 初始化。
-
正则化策略:这是一种可选但强烈推荐的功能,因为深度学习模型由于计算的参数数量,往往会出现过拟合的情况。你可以使用多种选择,包括 L1 正则化、L2 正则化和 Dropout 正则化策略。在本书中,我们的示例中不会使用正则化,但请记住,如果你想构建真正有效的深度学习模型,正则化策略很可能是必需的。
-
训练策略:训练策略指的是数据呈现给训练算法的方式。这不是模型本身的一部分,但它会影响模型的结果和性能。在谈到训练深度学习模型时,你会听到“epoch”这个词。一个 epoch 就是所有训练样本通过网络的一次传递。在这些深度学习模型中,你需要多次将数据输入网络,以便网络可以学习到模型的最佳参数。这里还有一个概念:批次大小(batch size)。这是指同时呈现给训练算法的元素数量。因此,在深度学习模型中,我们不会将整个训练数据集一次性提供给模型。我们做的是将数据集分成批次,在每个批次中,我们仅发送一些样本,可能是 100 个或 50 个,这就是我们训练深度学习模型的方式。现在,你可以使用 epoch 和批次大小来计算模型中的迭代次数,而迭代次数就是训练步骤的数量,也就是优化算法对模型中的权重进行调整的次数。例如,如果你有 1,000 个训练样本,并且你使用的批次大小是 100,那么完成一个 epoch 需要 10 次迭代。你可以使用以下公式来计算总迭代次数:

因此,作为建模者,你需要做出许多决策。这些模型非常复杂,训练起来可能非常棘手。所以,在你开始使用这些模型之前,以下是一些需要考虑的指导:
-
由于这些模型中有很多选择,它们的构建可能非常棘手。因此,当尝试进行预测时,它们不应是你的首选。总是先从简单且易于理解的模型开始,如果这些模型不起作用,再转向更复杂的模型。
-
对于我们看到的所有选择,都有最佳实践,但如果你想构建有效的深度学习模型,你需要更多关于这些元素的知识。
-
为了让这些模型表现得很好,你需要大量的数据。因此,你不能在非常小的数据集上使用这些模型。
-
了解更多关于这些模型的理论,以便更好地使用它们。所以,如果你真的想用这些模型来解决实际问题,深入学习这些模型背后的理论是必须的。
TensorFlow 简介
TensorFlow 是一个开源软件库,用于通过数据流图进行数值计算。计算图的概念在 TensorFlow 中非常重要,专为创建深度学习模型而设计。这个库允许开发者将计算任务部署到一个或多个 CPU 或 GPU 上,可以是台式机、服务器,甚至是移动设备。这个库最初由谷歌的研究人员和工程师开发,并于 2015 年开源。从那时起,它已经成为机器学习领域的主要库之一。
TensorFlow 提供了多个 API,通常可以分为以下两大类:
-
低级:也称为 TensorFlow Core,这是最低级别的 API。这个 API 为我们提供了完全的编程控制,旨在为研究人员和需要在构建深度学习模型时拥有高度灵活性的用户提供服务。
-
高级:高级 API,例如
tf.contrib.learn、keras和 TF-Slim,通常更容易使用。它们会处理重复的任务和低级细节,作为高级用户,你无需担心这些细节。它们旨在快速实现常用的模型。
TensorFlow 安装
现在,为了准备我们的安装,我们将在 Anaconda 中创建一个新的虚拟环境。我们可以通过以下指令来完成:
-
我们打开 Anaconda 提示符。
-
我们输入以下命令行来创建一个新的虚拟环境,并使用
anaconda传递环境名称,这样会安装 Anaconda 中包含的所有包:
conda create-n apa anaconda
这里 apa 代表高级预测分析。根据你的网络速度,安装可能需要一些时间。
- 安装完成后,输入
activate apa来激活新的虚拟环境。以下是 Anaconda 提示符的屏幕截图,显示了 Anaconda 包的安装过程:

现在,新的虚拟环境已经激活,我们准备在这个新的虚拟环境中安装 TensorFlow。
但是在安装 TensorFlow 之前,你必须知道,基本上有以下两种 TensorFlow 安装方式:
-
仅支持 CPU 的 TensorFlow
-
支持 GPU 的 TensorFlow
第二种选择通常更快,因为它利用了你电脑或设备中的 GPU,但这种安装需要 Nvidia 的支持。你还需要额外的软件才能运行这种安装,而且安装过程稍微复杂一些。
在这里,为了方便起见,我们将安装并使用 CPU 版本,因为在编写程序和运行程序时,CPU 版本与 GPU 版本没有区别,唯一的差别是速度。我们使用以下代码行来安装 TensorFlow 到我们的系统中:
pip install --ignore-installed --upgrade tensorflow
运行代码后,TensorFlow 的安装将会启动,安装完成后,你将在屏幕上看到以下输出:

现在,我们将启动一个 Python shell,通过执行以下步骤来测试安装:
-
我们输入
python启动 Python shell。 -
我们使用
import tensorflow as tf将 TensorFlow 导入到我们的 Python shell 中。 -
我们运行
hello = tf.constant("Hello");这将创建一个名为hello的常量。 -
我们使用
sess = tf.Session()创建一个会话。
如果你看到类似以下截图中的警告消息,可以忽略它们,因为它们只是告诉你可以通过不同的选项进行安装,从而让 TensorFlow 运行得更快。
- 让我们通过在会话中运行常量
print(sess.run(hello))来打印hello的结果:

如果你得到一个类似于Hello的结果,如截图所示,这意味着我们的安装是正确的。所以,现在我们准备使用 TensorFlow 来构建一些模型。
TensorFlow 中的核心概念
在实际使用tensorflow库之前,有一些重要的概念需要理解。以下是本书将涵盖的概念:
-
张量
-
计算图
-
会话
-
变量
-
占位符
-
常量
张量
张量是 TensorFlow 中数据的核心单元。张量由一组原始值组成,这些值被组织成一个任意维度的数组。它基本上是一个多维数组,类似于 NumPy 数组。维度的数量定义了张量的秩。我们来看一些以下的示例:
-
3:如果我们有一个单一的数字,那么张量将被视为秩为0的张量。这可以是一个具有shape[]的标量。 -
[2., 2., 1.]:如果我们有一个向量,它将被视为秩为1的张量,因此这就是我们所谓的形状为3的向量,因为它有三个元素。 -
[[9., 5., 3.], [4., 5., 7]]:一个形状为[2, 3]的矩阵将是一个秩为2的张量。 -
[[[8., 3.]], [[7., 9.,]]]:一个形状为[2, 1, 2]的矩阵将是一个秩为3的张量,正如你在最外层看到的,我们有两个元素,然后在下一层我们只有一个元素,最后一维中有两个元素。这就是为什么我们有2、1和2这些值,它们都是张量。
计算图
计算图是由一系列 TensorFlow 操作(也称为操作)组成的,这些操作被排列成一个节点图。TensorFlow 核心使用以下两个主要步骤:
-
定义计算图
-
运行计算图
让我们通过一个非常简单的例子来理解这个概念。假设你有一个包含两个变量x和y的函数,如下截图所示:

我们将使用前面的公式来计算或构建一个计算图,以获得当你分别传入x和y的值3和2时该函数的实际值:

现在,让我们为实际从这个计算模型中获取结果构建一个计算图:

在前面的截图中,我们看到值流经计算图到达图中的不同节点。因此,在第一个节点中,值3被赋给x,在另一个节点中,值2被赋给y。现在,x的值流向一个操作节点,在那里它被平方,而该节点的结果流向另一个操作,结果与y的值相乘。我们还有另一个节点,其中y的值与4相乘。x和y乘法节点的结果以及y乘法节点的结果流向最后一个节点,即加法节点,最终输出26。所以,这基本上就是 TensorFlow 的工作原理。在节点之间流动的是张量。
以下是我们在 TensorFlow 中使用的其他对象:
-
会话:会话是一个封装执行操作对象环境的对象。因此,会话是将操作放置到设备(如 CPU 或 GPU)上的对象。
-
占位符:占位符是一个承诺,表示稍后会提供一个值。这些对象通常用于在机器学习模型中提供训练和测试值。
-
变量:这些是初始化时有值的对象,并且该值可以在图执行过程中改变。通常,它们用作机器学习模型中的可训练变量。
-
常量:常量是那些值永远不变的对象。
为了更好地理解这些对象概念,让我们看一个例子。首先,我们将通过执行以下代码片段来导入所需的库:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
然后,我们通过执行以下代码片段来定义一些 TensorFlow 对象、占位符和常量:
#Placeholders
x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
c = tf.constant(5)
在这里,我们定义了一个名为x的占位符和另一个名为y的占位符。你必须明确给出在 TensorFlow 中将要使用的对象类型,在我们的示例中是float32。然后我们定义了一个常量c,其值为5。
创建这些对象后,如果你尝试打印它们,你将不会看到对象的值,而是会显示对象的类型,如下截图所示:

现在,让我们用我们的占位符来实现以下函数:

我们将使用刚刚创建的占位符,通过执行以下代码行来定义我们图中的不同节点:
square_node = x*x
mult_node = square_node*y
quadruple_node = 4*y
adder_node = mult_node + quadruple_node
同样,如果你尝试打印这些对象的值,你将得到对象类型而不是值,如下截图所示:

因此,为了对这些对象进行计算,你必须创建一个会话对象,然后在会话内运行所有对象:

如果你正在进行一些计算,你不需要定义计算图,因为 TensorFlow 会在后台自动完成这一过程。所以,假设你想计算f并打印出其值,它仍然会给出对象类型。但要实际看到f的值,我们将在会话对象中再次运行该函数:

你可以通过两种方式在 TensorFlow 中运行对象。还有其他方式,但这些是你可以运行对象的基本且最常见的方式。你可以使用会话中的run()方法,或者你可以使用张量中的eval()方法:

如我们所见,我们使用with语句创建了一个会话,并在这个语句中运行了这两个方法。
现在,我们将构建一个基本的线性模型。我们将让 TensorFlow 猜测下面截图中显示的b和w参数的最佳值:

在前面的方程中,w的值是5,b的值是1。我们将使用这些值进行训练并将它们绘制在散点图上:

如你所见,我们在这两个值之间建立了线性关系。
现在,我们将初始化变量对象w和b,并将其值设为0,它们将成为我们的可训练参数。占位符通常是我们用来传递数据的对象,因此我们将创建两个占位符,x和y,现在线性模型将是我们计算图中的一个节点。接着,我们将定义一个loss函数,优化器将使用它来实际改变我们变量的值。每次运行训练操作时,优化器都会调整w和b的值,以最小化损失。然后,我们将初始化这些变量并创建一个会话来运行init初始化节点,如下图所示:

现在,我们可以开始训练我们的机器学习模型。我们将运行训练操作 20 次,这将对w和b的值进行修正,以最小化损失:

如我们所见,在第一次迭代后,优化器修正了w和b的值,这在每次迭代中都会发生。
我们也可以使用一些线性代数来实现这一点,但请记住,机器学习的目标实际上是从数据中学习参数,在这种情况下,我们已经使用 TensorFlow 运行了我们的第一个机器学习模型。
总结
在这一章中,我们讨论了人工神经网络(ANNs)、深度学习以及深度学习模型的要素。接着,我们安装了 TensorFlow,并学习了 TensorFlow 中的核心概念。
在下一章中,我们将使用 TensorFlow 和深度学习进行预测分析。
第五章:使用 TensorFlow 和深度神经网络进行预测分析
TensorFlow 是由Google Brain 团队开发的开源库。它用于大规模机器学习应用,如神经网络,并用于数值计算。开发者可以使用 TensorFlow 创建数据流图,这些图展示了数据的流动。TensorFlow 可以用于训练和运行深度神经网络,用于图像识别、机器语言翻译和自然语言处理等各种应用。
我们已经知道,预测分析是关于对未知事件做出预测。我们将在这里使用 TensorFlow 来实现它。
在本章中,我们将涵盖以下主题:
-
使用 TensorFlow 进行预测
-
使用深度神经网络(DNNs)进行回归
-
使用 DNNs 进行分类
使用 TensorFlow 进行预测
我们将执行深度学习的hello world示例。这个示例用于检查和确保模型按预期工作。为此,我们将使用 MNIST 数据集。
MNIST 数据集简介
MNIST 代表混合国家标准与技术研究所,它生成了一个手写数字数据集。这是机器学习中最常被研究的数据集之一,用于分类手写数字。这个数据集因其庞大的规模而有助于预测分析,使得深度学习能够高效地发挥作用。该数据集包含 60,000 个训练图像和 10,000 个测试图像,格式为 28 x 28 像素的单色图像。以下截图显示了该数据集中的图像:

在前面的截图中,我们可以看到,对于每个手写数字,都有一个对应的真实标签;因此,我们可以使用这个数据集来构建分类模型。所以我们可以利用图像将每个手写数字分类为 0 到 9 之间的其中一个数字。
使用 MNIST 数据集构建分类模型
让我们来看一下以下步骤,学习如何构建一个分类模型:
- 我们需要导入在这个数据集中将使用的库。使用以下代码行导入
tensorflow、numpy和matplotlib库:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.contrib.layers import fully_connected
%matplotlib inline
- 我们将从
tensorflow.contrib.layers导入fully_connected函数,用于构建我们网络的各层。
DNN 模型的元素
在运行模型之前,我们首先需要确定构建多层感知器模型时将使用的元素。以下是我们将在此模型中使用的元素:
-
架构:模型的输入层包含 728 个神经元。这是因为我们有 28 张图像,每张图像有 28 个像素。在这里,每个像素都是一个特征,因此我们有 728 个像素。输出层将包含 10 个元素,我们还将使用三个隐藏层,尽管我们可以使用任意数量的隐藏层。这里,我们将使用三个隐藏层。每个层中神经元的数量为:第一层 350 个,第二层 200 个,最后一层 100 个。
-
激活函数:我们将使用 ReLU 激活函数,如以下代码块所示:
vector = np.arange(-5,5,0.1)
def relu(x) :
return max(0.,x)
relu = np.vectorize(relu)
如果输入是负数,函数输出0;如果输入是正数,函数输出与输入相同的值。因此,从数学上讲,ReLU 函数看起来类似于这个。下面的截图展示了用于生成 ReLU 激活函数图形表示的代码行:

它计算输入值与0之间的最大值。这个激活函数将在每个隐藏层的神经元中使用。
-
优化算法:这里使用的优化算法是带有学习率 0.01 的梯度下降法。
-
损失函数:对于
loss函数,我们将使用cross_entropy函数,但与本书中使用的其他损失函数一样,该函数测量实际值与模型预测值之间的距离。 -
权重初始化策略:为此,我们将使用 Xavier 初始化器,这是一种实际上作为
fully_connected函数的默认值随 TensorFlow 一起提供的方法。 -
正则化策略:我们不会使用任何正则化策略。
-
训练策略:我们将使用 20 个 epoch。数据集将被展示给网络 20 次,每次迭代中我们将使用批量大小 80。因此,我们将一次向网络提供 80 个数据点,并且整个数据集会展示 20 次。
构建 DNN
现在我们将导入我们要使用的数据集。选择这个数据集的原因是它容易获得。我们将实际使用这个数据集,并围绕它构建一个 DNN 模型。在接下来的部分中,我们将看到构建 DNN 模型的步骤。
读取数据
这里,我们在单元格中读取数据。以下截图展示了用于读取数据的代码行:

定义架构
我们将使用三个隐藏层,第一个层有 256 个神经元,第二个层有 128 个神经元,第三个层有 64 个神经元。以下代码片段展示了分类示例的架构:
n_inputs = 28*28
n_hidden1 = 350
n_hidden2 = 200
n_hidden3 = 100
n_outputs = 10
输入和标签的占位符
不同层的值是对象,也被称为输入和标签的占位符。这些占位符用于将数据输入网络。以下代码行用于显示输入和标签的占位符:
X = tf.placeholder(tf.float32, shape=[None, n_inputs])
y = tf.placeholder(tf.int64)
所以我们有一个占位符X用于特征,这是输入层,另外还有一个占位符y用于目标值。因此,这个对象将包含数字的真实标签。
构建神经网络
在构建 DNN 时,我们使用fully_connected函数作为第一个隐藏层。这个隐藏层的输入是x,即来自占位符的数据。n_hidden1是我们在该隐藏层中拥有的神经元数量,你会记得它是 350 个神经元。现在,隐藏层 1 将作为隐藏层 2 的输入,n_hidden2是这一层的神经元数量。同样,隐藏层 2 将作为第三隐藏层的输入,我们将在这一层使用相应数量的神经元。最后,输出层,我们称之为logits,是我们用作输入的完全连接层,隐藏层 3。以下截图显示了用于构建神经网络的代码行:

我们将输出设置为 10,因为我们的分类问题有 10 个类别,并且我们知道在输出层中我们不使用任何激活函数。
损失函数
对于我们的loss函数,我们使用了交叉熵函数。TensorFlow 提供了许多此类函数。例如,在这种情况下,我们使用的是sparse_softmax_cross_entropy_with_logits函数,因为这里我们得到了来自网络的logits。所以,在这个函数中,我们传入了实际标签。这些是真实标签,即logits——我们网络的结果或输出。以下截图显示了用于显示reduce_mean函数与交叉熵一起计算损失的代码行:

现在,使用这个交叉熵,我们可以将损失计算为我们在这里得到的向量的均值。这就是loss函数和交叉熵的均值。
定义优化器和训练操作
优化器的目标是最小化损失,它通过调整我们网络中所有层的不同权重来实现这一点。这里使用的优化器是带有0.01学习率的梯度下降。以下截图展示了定义优化器的代码行,并显示了训练操作。

每次运行训练操作training_op时,优化器都会稍微改变这些权重的值。通过这样做,它最小化了损失,使得预测值和实际值尽可能接近。
分类的训练策略和准确性评估
在这里,我们设置训练策略。我们将使用 20 个周期,每个批次大小为 80。在这些单元中,我们已经构建了将在该程序中使用的计算图。以下截图展示了用于显示训练策略以及用于评估分类准确度的几个节点的代码:

运行计算图
为了实际运行计算图,首先我们需要初始化程序中的所有变量。以下截图展示了用于运行计算图的代码行:

在第 3 行中,我们初始化了程序中的所有变量。现在,在这里,我们并没有显式定义任何变量。然而,内部的变量是完全连接的。fully_connected函数是我们定义所有隐藏层并包含权重的地方。这些就是变量,因此我们必须使用global_variables_initializer对象来初始化变量并运行此节点。对于每个周期,我们运行此循环 20 次。现在,对于每个迭代,我们遍历批次大小为 80 的所有示例,获取特征值和目标值。这意味着每次迭代都会有 80 个数据点。然后,我们运行训练操作并传递特征值作为x,目标值则传递给y。记住,x和y是我们的占位符。接着,我们评估训练的准确度,并评估测试数据集的准确度,我们会从mnist.test.images获取测试数据集。这些现在是特征,test.labels是目标。最后,在这两个循环完成后,我们打印出两个准确度。
接着我们为测试数据集中的前 15 张图片生成一些单独的预测。运行后,我们得到第一个周期,训练准确率为 86%,测试准确率为 88-89%。以下截图展示了不同周期的训练和测试结果:

程序运行需要一些时间,但经过 20 个周期后,测试准确率几乎达到了 97%。下面的截图展示了实际标签和预测标签。这些是网络做出的预测:

所以我们已经构建了第一个 DNN 模型,并且能够用这个程序以接近 97%的准确率对手写数字进行分类。
深度神经网络(DNN)的回归
对于使用 DNN 的回归,我们首先需要导入将要使用的库。我们将导入 TensorFlow、pandas、NumPy 和 matplotlib,以下截图展示了相关的代码:

我们将使用来自tensorflow.contrib.layers模型的fully_connected函数。
DNN 模型的元素
在运行模型之前,我们首先需要确定在构建多层感知机模型时将使用的元素,如下所示:
-
架构: 模型的输入层包含 23 个元素,因此这个数据集有 25 个特征。输出层只有一个元素,我们将使用三个隐藏层,尽管我们可以使用任意数量的隐藏层。我们将为第一个隐藏层使用 256 个神经元,第二个隐藏层使用 128 个神经元,第三个隐藏层使用 64 个神经元。这些数字是 2 的幂。
-
激活函数: 我们将选择 ReLu 激活函数。
-
优化算法: 这里使用的优化算法是 Adam 优化器。Adam 优化器是最受欢迎的优化器之一,因为它是许多问题的最佳选择。
-
损失函数: 我们将使用均方误差,因为我们正在做回归问题,而这是
loss函数的最佳选择之一。 -
权重初始化策略: 为此,我们将使用 Xavier 初始化器,这是 TensorFlow 中的
fully_connected函数的默认初始化方式。 -
正则化策略: 我们不会使用任何正则化策略。
-
训练策略: 我们将使用 40 个 epoch。我们将在每次训练操作时将数据集展示给网络 40 次,并且每次迭代时,我们将使用每次 50 个数据点的批量。所以,我们将使用 50 个数据集元素。
构建 DNN
首先,我们导入将要使用的数据集。选择这个数据集的原因是它容易获得。以下是构建 DNN 模型所涉及的步骤。
读取数据
我们将在该单元格中读取数据,并根据我们的需求进行过滤。下图展示了用于读取数据的代码行:

建模对象
导入数据集后,我们为建模准备对象。因此,我们在这里为x和y分别准备了训练和测试数据。下图展示了用于准备建模对象的代码行:

训练策略
这是带有 40 个 epoch 和 50 批次大小的训练策略。通过以下代码行创建:
n_epochs = 40
batch_size = 50
DNN 的输入管道
由于这是一个外部数据集,我们必须使用数据输入管道,而 TensorFlow 提供了多种工具来将数据导入到深度学习模型中。在这里,我们通过以下代码行创建数据集对象和迭代器对象:

首先,我们生成数据集对象。然后,我们将整个训练数据集传递给一些我们将要使用的占位符。接下来,我们打乱数据,并将训练数据集划分成 50 个一批的批次。因此,数据集对象已准备好,包含所有的训练样本,并分割成大小为 50 的批次。接着,我们创建一个迭代器对象。然后,通过get_next方法,我们创建一个名为next_element的节点,提供来自训练示例的 50 个批次。
定义架构
我们使用三个隐藏层,第一个层有 256 个神经元,第二层有 128 个,第三层有 64 个。以下代码片段展示了这一过程的架构:
n_inputs = X_train.shape[1] #23
n_hidden1 = 256
n_hidden2 = 128
n_hidden3 = 64
n_outputs = 1
输入值和标签的占位符
不同层的值是对象,也叫做占位符,用于输入和标签。这些占位符用于将数据传递到网络中。以下代码行展示了用于输入和标签的占位符:
X = tf.placeholder(X_train.dtype, shape=[None,n_inputs])
y = tf.placeholder(y_train.dtype)
构建 DNN
为了构建以下示例,我们首先必须定义DNN函数。该函数将接收X_values并输出预测结果。对于第一个隐藏层,我们使用fully_connected函数。该隐藏层的输入是X,即来自占位符的数据,n_hidden1是该隐藏层中的神经元数量。记住,我们在第一个隐藏层中有 350 个神经元。现在,第一个隐藏层成为第二个隐藏层的输入,n_hidden2是我们在第二个隐藏层中使用的神经元数量。同样,第二个隐藏层成为第三个隐藏层的输入,我们在该层中使用的神经元数量也是如此。最后,我们有输出层,我们将其称为y_pred,它是一个全连接层,第三个隐藏层作为输入。这个输出层没有激活函数。以下截图展示了构建神经网络的代码行:

损失函数
我们将使用mean_squared_error函数—TensorFlow 为我们提供了许多类似的函数。我们传递观测值和预测值,该函数计算均方误差。以下截图展示了用于显示mean_squared_error函数的代码行:

定义优化器和训练操作
优化器的目标是最小化损失,它通过调整我们网络中所有层的不同权重来实现这一目标。这里使用的优化器是 Adam 优化器,学习率为 0.001。
以下截图展示了用于定义优化器的代码行,同时也显示了训练操作:

以下截图展示了我们创建的一些 NumPy 数组,这些数组将用于评估目的:

运行计算图
在实际运行计算图时,首先我们需要初始化程序中的所有变量。以下截图展示了用于运行计算图的代码行:

这些变量是 fully_connected 函数中隐式的权重。然后,对于每一个周期,我们初始化迭代器对象并传入训练数据集。在这里,我们有 batch_data,运行这个 next_ element 节点后,我们获得 50 个数据的批次。我们可以获取特征值和标签,获取标签后可以进行训练操作。当对象没有数据时,我们会遇到错误。此时,若遇到这种错误,意味着我们已经使用完了所有训练数据集。然后,我们跳出这个 while 循环,进入下一个周期。稍后,我们会产生一些单独的预测结果,您可以查看该神经网络做出的具体预测。
以下截图展示了在我们将数据输入到这个网络时,训练和测试的 MSE(均方误差)在 40 个周期中的变化情况:

在最后一次测试的 MSE(第 40 个周期)中,我们得到了训练和测试 MSE 的最终值。
我们从网络中获得了实际的预测结果,这些值相对较为接近。这里,我们可以看到预测的价格。对于便宜的钻石,网络生成的值相对较为接近。而对于非常昂贵的钻石,网络生成的值较高。此外,预测值与观察值也非常接近。以下截图展示了我们从网络中得到的实际值和预测值:

以下截图展示了训练 MSE 与测试 MSE 的图表,以及生成该图表的代码行:

使用 DNN 进行分类
为了理解 DNN 的分类,我们首先需要理解指数线性单元函数的概念以及模型的各个元素。
指数线性单元激活函数
指数线性单元(ELU)函数是对 ReLU 函数的一个相对较新的修改。它与 ReLU 函数非常相似,但在数学性质上却有很大的不同。以下截图展示了 ELU 函数:

前面的截图显示,在0时,我们没有出现拐角。对于 ReLU 函数,我们有一个拐角。在这个函数中,取值并不会直接趋向0,而是 ELU 函数会慢慢逼近负的 alpha 参数。
使用 DNN 进行分类
对于 DNN(深度神经网络)的分类,我们首先需要导入将要使用的库。请使用以下截图中的代码行导入 tensorflow、pandas、numpy 和 matplotlib 库:

我们还将从 sklearn.model_selection 导入 train_test_split 函数,从 sklearn.preprocessing 导入 RobustScaler,并从 sklearn.metrics 导入 precision_score、recall_score 和 accuracy_score。我们还将从 tensorflow.contrib.layers 导入 fully_connected 函数,用于构建网络的各层。
DNN 模型的元素
在运行模型之前,我们首先必须确定我们将使用的元素
构建一个多层感知器模型,如下所示:
-
架构:该模型在输入层包含 25 个元素,因为我们有
数据集中的 25 个特征。输出层中有两个元素,我们将
还将使用三个隐藏层,尽管我们可以使用任意数量的隐藏层
层。我们将在每层使用相同数量的神经元,200。这里我们使用
2 的幂,这是一个任意选择。
-
激活函数:我们将选择 ELU 激活函数,已被
如前一章所述。
-
优化算法:这里使用的优化算法是 Adam
学习率为 0.001 的优化器。
-
损失函数:对于
loss函数,我们将使用交叉熵函数。 -
权重初始化策略:为此,我们将使用 Xavier 初始化器,
方法,这是
fully_connected函数的默认方法TensorFlow。
-
正则化策略:我们不打算使用任何正则化策略。
-
训练策略:我们将使用 40 个 epoch。因此,我们将呈现数据集
40 次迭代,每次迭代我们将使用 100 的批量大小。
构建 DNN
现在,我们导入我们将使用的数据集。使用这个数据集的原因是它
数据是轻松获取的。构建 DNN 模型的步骤如下。
读取数据
我们将在该单元中读取数据。以下截图显示了用于读取数据的代码行
用于读取数据:

生成建模对象
现在,我们生成用于建模的对象。我们将使用 10% 用于测试
90% 用于训练。以下截图显示了用于
生成建模对象:

训练策略
这就是我们之前提到的训练策略,40 个 epoch 和批量大小为
100。以下代码块显示了我们在此策略中设置的参数:
n_epochs = 40
batch_size = 100
DNN 的输入流水线
现在,我们执行与回归示例相同的操作。我们创建一个
dataset 对象和一个迭代器对象。最后,我们有 next_element。这将是我们计算图中的一个节点,每次给我们 100 个数据点。因此,我们得到
批次。以下截图显示了用于生成输入的代码行
DNN 的流水线:

定义架构
我们将使用三个隐藏层,每个层有 200 个神经元。以下代码片段
显示了我们将在这个示例中使用的架构:
n_inputs = X_train.shape[1] #25
n_hidden1 = 200
n_hidden2 = 200
n_hidden3 = 200
n_outputs = 2
输入和标签的占位符
不同层的值是对象,也称为占位符,用于输入和
标签。这些占位符用于将数据输入网络。以下
以下代码行用于展示输入和标签的占位符:
X = tf.placeholder(X_train.dtype, shape=[None,n_inputs])
y = tf.placeholder(y_train.dtype)
构建神经网络
对于构建深度神经网络,我们将使用DNN函数。我们有三层,并将使用 ELU 函数作为激活函数。你可以从
TensorFlow,tf.nn.elu,它提供了许多可以帮助你构建深度学习模型的功能。以下截图显示了用于
生成此函数并以logits形式获取输出:

最后一层叫做logits层。我们不会在此层使用任何激活函数
这一层。
损失函数
对于loss函数,我们再次将从 DNN 中获取logits,然后将此logits传递给 TensorFlow 中的softmax_cross_entropy_with_logits函数。我们传入真实标签和logits,然后可以使用reduce_mean函数和cross_entropy来计算损失。以下截图显示了用于展示如何使用reduce_mean函数和cross_entropy来计算损失的代码行:

评估节点
现在,进行评估时,我们将计算违约和非违约变量的概率;你可以通过对logits应用softmax函数来获得概率。以下截图显示了softmax函数:

softmax函数用于为不同类别提供概率。
优化器和训练操作
优化器的目标是最小化损失,通过调整我们网络中各层的不同权重来实现。
以下截图显示了用于定义优化器并展示训练操作的代码行:

在这种情况下,优化器再次使用 Adam 优化器,学习率为0.001。训练操作是优化器最小化损失的操作。
运行计算图
要实际运行计算图,首先我们需要初始化所有变量
程序。变量是fully_connected函数中隐含的权重。我们运行四个周期,在每个周期中初始化我们的迭代器对象。我们传入训练数据x和y,然后运行这个循环。只要next_elementelement中有数据,我们就会运行这个循环。因此,我们获取接下来的 100 个元素,然后在下一次迭代中获取下一个 100 个元素,以此类推。在每次迭代中,我们都会运行训练操作。训练操作的作用是要求优化器调整参数和权重,以便做出更好的预测。
以下截图展示了用于运行计算图的代码行:

最终,我们可以获得概率值,并可用于评估目的。
使用设定阈值评估模型
probabilities对象实际上是用来评估模型性能的。
不同的分类阈值。分类阈值可以根据需要修改,用于二分类问题。
分类问题,并可用于计算召回率、精确度和
精度。使用分类阈值0.16时,在测试数据集中得到的指标如下:

通过计算,我们得到了82.53的召回率,34.02的精确度,以及60.7的准确率。
总结
在本章中,我们学习了如何使用 TensorFlow 进行预测。我们研究了 MNIST 数据集,并使用该数据集进行了模型分类。我们了解了 DNN 模型的各个元素及其构建过程。之后,我们深入研究了使用 DNN 进行回归和分类。我们对手写数字进行了分类,并学习了如何在 TensorFlow 中构建模型。这标志着本书的结束!我们学会了如何使用集成算法生成准确的预测。我们应用了多种技术来组合并构建更好的模型。我们还学习了如何高效地进行交叉验证。我们实施了多种技术来解决预测分析领域当前的问题。最重要的是,我们使用自己构建的 DNN 模型来解决分类和回归问题。本书帮助我们实现了多种机器学习技术,构建了先进的预测模型,并将其应用于现实世界。


浙公网安备 33010602011771号