精通 AWS 机器学习(二)
原文:
annas-archive.org/md5/837d819f8bef8ba992e2d85e9b0ed5bc译者:飞龙
第六章:通过分析游客模式来制定推荐
本章将专注于基于游客参加的主题公园景点寻找相似游客的问题,以制定改进的营销推荐。我们将介绍协同过滤方法,并通过示例展示如何在 Apache Spark(EMR)和通过 AWS SageMaker 内置算法中训练和获取定制推荐。许多公司利用我们在本章中描述的算法类型来通过推荐与类似客户相关联的产品来提高客户的参与度。
在本章中,我们将涵盖以下主题:
-
通过 Flickr 数据推荐主题公园景点
-
通过 Apache Spark 的交替最小二乘法寻找推荐
-
通过 SageMaker 因子分解机推荐景点
通过 Flickr 数据推荐主题公园景点
在本章中,我们将使用来自sites.google.com/site/limkwanhui/datacode的数据集,该数据集包含在不同地点拍照的用户上传的 Flickr 数据,这些照片随后被映射到已知的主题公园景点。Flickr 是一个图像托管服务。假设 Flickr 希望在他们的移动应用上创建一个插件,当用户在各个景点拍照时,能够识别用户偏好并提供可能对他们感兴趣的其它景点的推荐。
让我们再假设用户在特定景点拍摄的照片数量是他们对该景点兴趣的一个指标。我们的目标是分析包含用户 ID、景点、拍摄照片数量的三元组数据集,以便给定用户访问的任意景点集合,模型能够推荐类似用户发现有趣的景点。
协同过滤
协同过滤是一种基于分析大量用户行为来向用户提供推荐的过程。我们在日常生活中的大量应用中观察到这种算法的影响。例如,当你使用流媒体服务,如 Netflix 或 YouTube 时,它会根据你的观看历史推荐你可能感兴趣的视频。社交媒体,如 Twitter 和 LinkedIn,会根据你的当前联系人建议你关注或连接的人。Instagram 和 Facebook 等服务会根据你阅读或赞过的帖子来编辑你朋友的帖子,并定制你的时间线。作为一名数据科学家,当你在基于大量用户数据构建推荐系统时,协同过滤算法非常有用。
协同过滤可以在数据集上有多种实现方式。在本章中,我们将讨论基于记忆的方法和基于模型的方法。
基于记忆的方法
在基于记忆的方法中,我们分两个阶段生成推荐。考虑这样一种情况,即我们试图根据用户的兴趣为特定用户生成推荐。在第一阶段,我们根据兴趣发现与给定用户相似的用户。我们根据用户与给定用户的相似程度对所有用户进行排名。在第二阶段,我们在与给定用户最相似的用户组中找到最感兴趣的兴趣点。这些顶级兴趣根据它们与顶级用户集的相似性进行排名。然后,这个按排名排列的兴趣列表被呈现给原始用户作为推荐。
例如,在电影推荐的过程中,我们查看用户感兴趣或最近观看的电影,并发现观看过类似电影的其他用户。基于相似用户的高排名列表,我们查看他们最近观看的电影,并根据与排名用户列表的相似性进行排名。然后,将排名最高的电影作为推荐呈现给用户。
为了找到用户之间的相似性,我们使用称为相似度度量的函数。相似度度量在搜索引擎中广泛用于对查询词和文档之间的相似性进行排序。在本节中,我们讨论了余弦相似度度量,它在协同过滤中常用。我们将每个用户的兴趣视为一个向量。为了发现具有相似兴趣的用户,我们计算两个用户兴趣向量之间的余弦角。它可以表示如下:
根据给定用户与数据集中所有用户的相似性,我们选择前 k 个用户。然后,我们聚合所有用户的兴趣向量,以发现顶级兴趣并推荐给用户。
注意,基于记忆的模型不使用前几章中讨论的任何建模算法。它们仅依赖于简单的算术来根据用户的兴趣为用户生成推荐。
基于模型的方法
对于基于模型的方法,我们使用机器学习技术训练一个模型,该模型可以预测每个兴趣与给定用户的相关概率。可以应用各种算法,如贝叶斯模型或聚类模型,用于基于模型的协同过滤。然而,在本章中,我们专注于基于矩阵分解的方法。
矩阵分解
矩阵分解方法通过分解用户和用户兴趣的矩阵来工作。在本方法中,我们将用户数据和关于兴趣的信息映射到一组因子。用户对兴趣的评分是通过计算用户和兴趣的向量评分的点积来计算的。这些因子可以从用户评分或从算法中关于兴趣的外部信息中推断出来。
例如,考虑一个矩阵,其中一个维度代表用户,另一个维度代表用户已评分的电影:
| 复仇者联盟 | 勇敢者游戏 | 蜘蛛侠 | |
|---|---|---|---|
| 用户 1 | 4 | 4 | |
| 用户 2 | 1 | 5 | 2 |
| 用户 n | 5 | 2 | 4 |
矩阵中的值是用户提供的评分。矩阵分解方法将电影映射到更短的向量,这些向量代表概念,例如电影的类型。这些向量被称为潜在因素。我们将电影映射到类型,并根据用户对每个类型的电影评分来映射用户感兴趣的类型。使用这些信息,我们可以根据两个向量之间的点积(将用户对类型的兴趣乘以电影属于类型的可能性)来计算用户和电影之间的相似性。因此,矩阵中的未知评分可以通过通过将用户和兴趣合并到更粗粒度的项目(即类型)来使用已知评分的知识进行预测。在我们的前一个例子中,我们假设我们已经有了一个已知的电影到类型的映射。然而,我们不能假设我们总是有显式数据来生成这样的映射到潜在因素。
因此,我们探索可以基于数据自动生成此类映射的方法。因此,矩阵分解模型需要能够通过潜在因素向量在用户和兴趣之间生成映射。为了确保我们可以生成用户潜在因素和项目潜在因素之间的点积,潜在因素长度被设置为固定值。每个兴趣项目,,由一个向量,
,表示,每个用户,
,由一个向量,
,表示。向量
和
都是从数据中导出的潜在因素。用户对项目的评分,
,对用户的评分,
,表示如下:
通常,如果我们已经从用户到兴趣的部分评分集合,我们可以使用它来建模其他用户和兴趣之间的评分。我们使用优化技术来计算这个。我们的目标是预测和
的值。因此,我们通过最小化预测这些向量时使用的已知评分的正则化误差来实现这一点。这可以用以下公式表示:
在这里,k 是一个包含 的集合,其中已知的评分
。现在,让我们看看两种最小化前面方程的方法。
随机梯度下降
我们在第三章,“使用回归算法预测房屋价值”中研究了随机梯度下降算法,关于线性回归。类似的方法用于最小化预测每个用户和兴趣的正确潜在因子的函数。我们使用迭代方法,在每次迭代中,我们根据所有已知的评分计算预测 和
的误差:
根据误差的大小,我们更新 和
的值,方向与误差相反:
当 和
的值收敛后,我们停止迭代。随机梯度下降也用于如因子分解机(FMs)等算法中,它使用它来计算向量的值。FMs 是 支持向量机(SVM)模型的变体,可以在协同过滤框架中应用。我们在这本书中不会详细解释支持向量机或 FMs,但鼓励你了解它们是如何工作的。
交替最小二乘法
最小化预测 和
值的优化函数的一个挑战是,方程不是凸的。这是因为我们试图同时优化两个值。然而,如果我们为其中一个值使用常数,或者
或
,我们可以为另一个变量最优地解决方程。因此,在交替最小二乘技术中,我们交替地将
和
的值设为常数,同时优化另一个向量。
因此,在第一步中,我们为两个向量设定了基础值。假设其中一个值是常数,我们使用线性规划来优化另一个向量。在下一步中,我们将优化向量的值设为常数,并优化另一个变量。我们不会解释如何使用线性规划来优化二次问题,因为这是一个完整的研究领域,并且超出了本书的范围。这种方法将优化每个向量,直到收敛。
随机梯度下降的优点是它比 ALS 方法更快,因为它在修改向量时基于错误比例预测每个步骤中每个向量的值。然而,在 ALS 方法中,系统独立地计算每个向量的值,因此导致更好的优化。此外,当矩阵密集时,梯度下降方法必须从每一组数据中学习,这使得它比 ALS 方法效率更低。
通过 Apache Spark 的 ALS 找到推荐
在本节中,我们将通过 Apache Spark 使用交替最小二乘法(ALS)创建推荐的过程。
数据收集和探索
第一步是从sites.google.com/site/limkwanhui/datacode下载数据。我们将使用poiList-sigir17数据集,其中包含用户在不同主题公园景点(由 Flickr 识别为兴趣点)拍摄的图片。我们感兴趣的以下两个数据集:
- 该兴趣点列表,它捕捉了每个景点的名称和其他属性:
poi_df = spark.read.csv(SRC_PATH + 'data-sigir17/poiList-sigir17',
header=True, inferSchema=True, sep=';')
以下截图显示了poi_df数据框的前几行:
- Flickr 用户在不同兴趣点拍摄的图片:
visits_path = SRC_PATH+'data-sigir17/userVisits-sigir17'
visits_df = spark.read.csv(visits_path,
header=True,
inferSchema=True, sep=';')
以下截图显示了visits_df数据框的一个样本:
在这个数据集中,我们将使用nsid字段(表示拍照的用户)和poiID,它表示拍照时实际访问的兴趣点或景点。为了我们的目的,我们将忽略其余的字段。
让我们对我们的数据集做一些基本的检查。数据集大约有 30 万行数据。通过抽取 1000 个条目的样本,我们可以看到有 36 个独特的 Flickr 用户:
sample_df = visits_df.limit(1000).toPandas()
sample_df.describe()
前一个describe()命令的输出如下:
count 1000
unique 36
top 10182842@N08
freq 365
这很重要,因为我们需要每个用户有足够多的条目来确保我们有足够的信息关于用户来做出预测。此外,了解用户是否访问了不同的景点实际上更为相关。Apache Spark 的一个优点是,可以使用 SQL 在数据集上工作。使用 SQL 轻松地找到用户平均看到的独特景点的数量。
为了使用 SQL,我们首先需要给数据集一个表名。这是通过注册一个临时表来完成的:
poi_df.createOrReplaceTempView('points')
visits_df.createOrReplaceTempView('visits')
一旦我们注册了表,我们就可以进行查询,例如找到独特景点的数量:
spark.sql('select distinct poiID from visits').count()
31
或者我们可以将 SQL 与其他数据集操作相结合,例如.describe():
spark.sql('select nsid,count(distinct poiID) as cnt from visits group by nsid').describe().show()
以下截图包含了show()命令输出的结果:
前面的 SQL 命令找出每个用户访问的独特景点的数量。describe数据集操作找出这些用户的统计数据,这告诉我们,平均而言,用户访问大约五个不同的地点。这很重要,因为我们需要每个用户有足够的景点,以便能够正确识别用户模式。
同样,我们应该查看用户在每个地点拍摄的照片数量,以验证实际上我们可以使用拍摄照片的数量作为用户兴趣的指标。我们通过以下命令来完成:
spark.sql('select nsid,poiID,count(*) from visits group by nsid,poiID').describe().show()
前面命令的输出如下所示:
SQL 命令计算每个用户和景点的条目数量,然后我们使用describe找到统计摘要。因此,我们可以得出结论,平均而言,每个用户在每个访问的地点大约拍摄八张照片。
训练模型
为了训练我们的模型,我们将构建一个数据集,该数据集计算每个用户在每个地点拍摄的照片数量:
train_df = spark.sql('select hash(nsid) as user_hash_id, poiID, count(*) as pictures_taken from visits group by 1,2')
以下截图显示了train_df数据框的前几行:
我们对用户进行哈希处理,因为 ALS 训练器只支持数值作为特征。
要训练模型,我们只需构建一个 ALS 实例,并提供用户列、项目列(在这种情况下是景点 ID)和评分列(在这种情况下,pictures_takes用作评分的代理)。coldStartStrategy设置为丢弃,因为我们不感兴趣为数据集中不存在(即,预测此类条目将被丢弃而不是返回 NaN)的用户或景点进行预测:
from pyspark.ml.recommendation import ALS
recommender = ALS(userCol="user_hash_id",
itemCol="poi_hash_id",
ratingCol="pictures_taken",
coldStartStrategy="drop")
model = recommender.fit(train_df)
获取推荐
一旦我们构建了一个模型,我们就可以为我们数据集中的所有用户生成预测:
recommendations = model.recommendForAllUsers(10)
前面的命令将为每个用户选择前 10 个推荐。请注意,由于 ALS 的工作方式,它实际上可能会推荐用户已经访问过的景点,因此我们需要在我们的目的中丢弃这些推荐,正如我们稍后将要看到的。
推荐看起来如下:
每个用户都会得到一个包含推荐景点以及推荐得分的元组列表。在这种情况下,得分代表我们预计每个用户在推荐地点会拍摄的照片数量。尽管模型只提供景点的 ID,但我们想检查其中的一些推荐,以确保它们是好的。为了做到这一点,我们将通过收集查询结果来构建一个 ID 到景点名称(兴趣点名称)的字典,该查询找出每个景点在景点表中的名称:
row_list = spark.sql('select distinct p.poiName, p.poiID from visits v join points p on (p.poiID=v.poiID) ').collect()
id_to_poi_name = dict(map(lambda x: (x.poiID, x.poiName), row_list))
地图包含以下条目:
{1: 'Test Track',
10: 'Golden Zephyr',
19: "Tarzan's Treehouse",
22: 'Country Bear Jamboree'
....
}
对于每个用户,我们希望移除已访问地点的推荐并输出推荐。为此,我们需要处理每行的元组列表。Apache Spark 通过允许用户创建自定义 SQL 函数或用户定义函数(UDFs)提供了一种方便的方式来做到这一点。我们将定义并注册一个 UDF,它能够通过使用前面的映射提取每个推荐景点的名称:
def poi_names(recommendations, visited_pois):
visited_set = set([id_to_poi_name[poi] for poi in visited_pois])
recommended = str([(id_to_poi_name[poi], weight) \
for (poi,weight) in recommendations
if id_to_poi_name[poi] not in visited_set])
return "recommended: %s ; visited: %s "%(recommended, visited_set)
spark.udf.register("poi_names", poi_names)
poi_names函数接收用户的推荐元组以及访问的景点,然后返回一个包含所有未在访问集合中的推荐景点名称以及已访问景点枚举的字符串。
然后,我们将推荐注册为一个表,以便在下一个查询中使用:
recommendations.createOrReplaceTempView('recommendations')
recommendation_sample = spark.sql('select user_hash_id, collect_list(poiID), poi_names(max(recommendations), collect_list(poiID)) as recommendation from recommendations r join visits v on (r.user_hash_id = hash(v.nsid)) group by 1')\
.sample(fraction=0.1, withReplacement=False) \
.collect()
前面的查询将用户推荐表与访问表连接,通过用户进行连接,收集每个用户访问的所有兴趣点,并通过 UDF 输出推荐的景点以及已访问景点的名称。我们采样并收集一些表实例以进行检查。在配套的笔记本中,我们可以观察到以下条目:
print(recommendation_sample[0].recommendation)
recommended: [("It's A Small World", 31.352962493896484), ('Walt Disney World Railroad', 23.464025497436523), ('Pirates of the Caribbean', 21.36219596862793), ('Buzz Lightyear Astro Blasters', 17.21680450439453), ('Haunted Mansion', 15.873616218566895), ('Country Bear Jamboree', 9.63521957397461), ('Astro Orbiter', 9.164801597595215), ('The Great Movie Ride', 8.167647361755371)] ; visited: {"California Screamin'", 'Sleeping Beauty Castle Walkthrough', 'Voyage of The Little Mermaid', "Tarzan's Treehouse", 'Main Street Cinema', 'The Many Adventures of Winnie the Pooh', 'Jungle Cruise', 'Tom Sawyer Island', 'Test Track', 'The Twilight Zone Tower of Terror'}
我们可以观察到这位用户访问了许多类似冒险的景点,模型推荐了更多。在这里,读者可以检查更多推荐:
print(recommendation_sample[200].recommendation)
recommended: [('Splash Mountain', 0.9785523414611816), ('Sleeping Beauty Castle Walkthrough', 0.8383632302284241), ("Pinocchio's Daring Journey", 0.7456990480422974), ('Journey Into Imagination With Figment', 0.4501221477985382), ("California Screamin'", 0.44446268677711487), ('Tom Sawyer Island', 0.41949236392974854), ("It's A Small World", 0.40130260586738586), ('Astro Orbiter', 0.37899214029312134), ('The Twilight Zone Tower of Terror', 0.3728359639644623)] ; visited: {"Snow White's Scary Adventures"}
print(recommendation_sample[600].recommendation)
recommended: [('Fantasmic!', 20.900590896606445), ('Pirates of the Caribbean', 9.25596809387207), ("It's A Small World", 8.825133323669434), ('Buzz Lightyear Astro Blasters', 5.474684715270996), ('Main Street Cinema', 5.1001691818237305), ('Country Bear Jamboree', 4.3145904541015625), ("California Screamin'", 3.717888832092285), ("It's A Small World", 3.6027705669403076), ('The Many Adventures of Winnie the Pooh', 3.429044246673584)] ; visited: {'Haunted Mansion', 'The Twilight Zone Tower of Terror', 'Journey Into Imagination With Figment'}
通过 SageMaker 因子分解机推荐景点
当涉及到非常稀疏的输入时,FM(因子分解机)是制作推荐中最广泛使用的算法之一。它类似于我们在基于模型的矩阵分解方法下讨论的随机梯度下降(SGD)算法。在本节中,我们将展示如何使用 AWS 内置的 FM 算法实现来为我们主题公园的游客获取推荐。
准备学习数据集
为了使用这样的算法,我们需要以不同的方式准备我们的数据集。我们将推荐问题设定为一个回归问题,其中输入是一个用户和吸引力的配对,输出是这个用户对吸引力的预期兴趣水平。训练数据集必须包含每个用户和吸引力配对的实际经验兴趣(通过拍照数量衡量)。有了这些数据,FM 模型就能预测任何用户对任意吸引力的兴趣。因此,为了获得用户的推荐,我们只需找到那些能产生最高预测兴趣水平的吸引力列表。
那么我们如何在数据集中编码用户和景点呢?
由于 FM 在处理高维特征方面非常出色,我们可以对输入进行 one-hot 编码。由于有 8,903 个用户和 31 个景点,我们的输入向量长度将为 8,934,其中前 31 个向量分量将对应于 31 个不同的景点,其余位置对应于每个用户。向量除了对应于用户和景点的位置外,其余位置都将为零。我们模型中使用的目标特征(标签)将是兴趣水平,我们将通过根据相应的分位数对拍照数量进行归一化将其离散化到 1 到 5 的值。
下图显示了这样一个训练数据集可能的样子:
如您所想象,这个矩阵非常稀疏,因此我们需要使用稀疏表示来编码我们的行。像大多数 SageMaker 算法一样,我们必须将数据放入 S3,以便 SageMaker 进行训练。在过去的章节中,我们使用了 CSV 作为输入。然而,CSV 并不是我们数据集的好表示;鉴于其稀疏性,它将占用太多空间(有很多重复的零!)。实际上,在撰写本文时,SageMaker 甚至不支持 CSV 作为输入格式。在稀疏表示中,每个向量必须指示以下三个值:
-
向量的尺寸
-
我们有非零值的位位置
-
这些非零位置上的值
例如,前图中第一行的稀疏表示如下:
-
向量大小 = 8934
-
非零位置 = [1, 33]
-
非零位置上的值 = [1, 1]
FM(特征矩阵)目前支持的唯一输入格式称为 protobuf recordIO。protobuf,即协议缓冲区,是一种语言无关、平台无关的可扩展机制,用于序列化结构化数据,最初由谷歌开发。在我们的情况下,结构将是矩阵的稀疏表示。我们存储在 S3 中的 protobuf 文件中的每个记录都将包含稀疏表示所需的所有三个项目,以及目标特征(标签)。
接下来,我们将介绍准备数据集并将其上传到 S3 的过程。
我们将从上一节中用于训练的 Spark dataframe(train_df)开始,并应用一个Pipeline,该Pipeline执行 one-hot 编码以及归一化拍照目标特征:
from pyspark.ml.feature import OneHotEncoder
from pyspark.ml.feature import StringIndexer
from pyspark.ml import Pipeline
from pyspark.ml.feature import QuantileDiscretizer
from pyspark.ml.feature import VectorAssembler
pipeline = Pipeline(stages = [
StringIndexer(inputCol='user_hash_id',
outputCol="user_hash_id_index",
handleInvalid='keep'),
OneHotEncoder(inputCol='user_hash_id_index',
outputCol='user_hash_id_encoded'),
StringIndexer(inputCol='poiID',
outputCol='poi_id_indexed',
handleInvalid='keep'),
OneHotEncoder(inputCol='poi_id_indexed',
outputCol='poi_id_encoded'),
QuantileDiscretizer(numBuckets=5,
inputCol='pictures_taken',
outputCol='interest_level'),
VectorAssembler(inputCols=['poi_id_encoded', 'user_hash_id_encoded'],
outputCol='features'),
])
model = pipeline.fit(train_df)
管道与我们在前几章中构建的管道类似,不同之处在于我们没有将机器学习算法作为最终步骤(因为一旦数据集存入 S3,这一阶段将通过 SageMaker 的 FMs 运行)。我们首先对用户和吸引力(兴趣点)特征进行字符串索引,然后将它们链接到一个独热编码器。分位数离散化器将拍摄照片特征根据其百分位数减少到五个桶中。我们将此特征命名为interest_level。此外,我们将这些编码的吸引力和用户向量组装成一个向量。
接下来,我们通过应用模型来转换训练集:
sparse_df = model.transform(train_df)
这将生成一个数据集:
注意编码字段(user_hash_id_encoded、poi_id_encoded和features)显示了向量的稀疏表示。
一旦我们有了这个编码后的数据集,我们可以将其分为测试集和训练集。SageMaker 将使用训练集进行拟合,并在训练过程中每个 epoch 使用测试集来查找验证错误。我们需要将这些数据集转换为 recordio 格式并上传到 s3。
如果我们使用 Scala(Spark 使用的本地编程语言),我们可以做类似这样的事情:
sagemaker_train_df.write.format("sagemaker") \
.option("labelColumnName", "interest_level") \
.option("featuresColumnName", "features") \
.save("s3://mastering-ml-aws/chapter6/train-data")
不幸的是,在撰写本文时,pyspark不支持直接将数据框写入 recordio 格式。相反,我们将收集所有 spark 数据框到内存中,并将每一行转换为稀疏向量,然后上传到 S3。
下面的spark_vector_to_sparse_matrix函数正是这样做的。它接受一个 Spark 数据框行并将其转换为scipy稀疏矩阵(来自scipy,一个具有科学工具的 Python 库)。upload_matrices_to_s3函数接收一个 Spark 数据集(无论是训练集还是测试集),收集每一行,使用特征构建一个稀疏向量,并将它们堆叠成一个矩阵。此外,它构建一个包含所有兴趣级别的目标特征向量。给定这个矩阵和标签向量,我们使用sagemaker库的实用函数write_spmatrix_to_sparse_tensor将数据写入 recordio 格式。最后,我们将该对象上传到 S3。为此,让我们首先导入所有必要的依赖项:
from scipy.sparse import csr_matrix
import numpy as np
import boto3
import io
import numpy as np
import scipy.sparse as sp
import sagemaker.amazon.common as smac
接下来,让我们定义两个辅助函数:spark_vector_to_sparse_matrix,它将接受一行并生成一个scipy稀疏矩阵,以及upload_matrices_to_s3,它负责将测试集或训练集上传到 s3:
def spark_vector_to_sparse_matrix(row):
vect = row['features']
return csr_matrix((vect.values, vect.indices, np.array([0, vect.values.size])),
(1, vect.size),
dtype=np.float32)
def upload_matrices_to_s3(dataframe, dataset_name):
features_matrices =
dataframe.select("features") \
.rdd.map(spark_vector_to_sparse_matrix).collect()
interest_levels =
dataframe.select("interest_level") \
.rdd.map(lambda r: r['interest_level']).collect()
interest_level_vector = np.array(interest_levels, dtype=np.float32)
buffer = io.BytesIO()
smac.write_spmatrix_to_sparse_tensor(buffer, \
sp.vstack(features_matrices), \
interest_level_vector)
buffer.seek(0)
bucket = boto3.resource('s3').Bucket('mastering-ml-aws')
bucket.Object('chapter6/%s-data.protobuf'%dataset_name).upload_fileobj(buffer)
最后,我们需要通过在两个变量上调用upload_matrices_to_s3方法来上传训练集和测试集:
upload_matrices_to_s3(sagemaker_train_df, 'train')
upload_matrices_to_s3(sagemaker_test_df, 'test')
训练模型
现在我们已经将数据以适合学习的正确格式存入 S3,我们可以开始训练我们的模型以获取推荐。
我们将实例化 SageMaker 会话并定义读取和写入数据的路径:
import sagemaker
from sagemaker import get_execution_role
import json
import boto3
sess = sagemaker.Session()
role = get_execution_role()
container = sagemaker.amazon.amazon_estimator.get_image_uri('us-east-1',
"factorization-machines",
"latest")
s3_train_data = 's3://mastering-ml-aws/chapter6/train-data.protobuf'
s3_test_data = 's3://mastering-ml-aws/chapter6/train-data.protobuf'
s3_output_location = 's3://mastering-ml-aws/chapter6/sagemaker/output/'
通过会话,我们可以通过设置要使用的计算机的数量和类型来实例化 SageMaker 估计器。我们还可以指定超参数。两个需要考虑的重要参数是特征维度(这是我们训练向量的长度)和预测器类型。由于我们的问题是设定为回归,我们将使用回归器。如果我们不是将兴趣水平建模为存在/不存在兴趣,而是将其建模为binary_classifier值:
from sagemaker.session import s3_input
recommender = sagemaker.estimator.Estimator(container,
role,
train_instance_count=1,
train_instance_type='ml.c4.xlarge',
output_path=s3_output_location,
sagemaker_session=sess)
recommender.set_hyperparameters(predictor_type='regressor',
feature_dim=8934,
epochs=200,
mini_batch_size=100,
num_factors=128)
recommender.fit({'train': s3_input(s3_train_data), \
'test': s3_input(s3_test_data)})
日志将显示一些验证统计信息和模型完成时的确认信息:
[02/23/2019 22:01:02 INFO 140697667364672] #test_score (algo-1) : ('rmse', 0.19088356774389661)
2019-02-23 22:01:11 Uploading - Uploading generated training model
2019-02-23 22:01:11 Completed - Training job completed
获取推荐
一旦模型拟合完成,我们可以启动预测器网络服务:
predictor = recommender.deploy(instance_type='ml.c5.xlarge', initial_instance_count=1)
这将启动托管训练模型的网络服务端点,现在可以接收带有预测请求的请求。让我们从 Spark 的 ALS 推荐中选取一个用户,并将其与 SageMaker 的预测进行比较:
print(recommendation_sample[1].user_hash_id)
-525385694
我们可以收集那个用户的特征:
sagemaker_test_df.select('features').where('user_hash_id=-525385694') \
.rdd.map(build_request).collect()
[{'data': {'features': {'shape': [8934],
'keys': [4, 3297],
'values': [1.0, 1.0]}}}]
在这里,build_request是一个方便的函数,用于创建与 SageMaker 期望的稀疏编码请求兼容的 JSON 请求:
def build_request(row):
vect = row['features']
return {'data':{ 'features': {'shape':[int(vect.size)],
'keys':list(map(int,vect.indices)),
'values':list(vect.values)}}}
如我们所知,用户 ID 在向量中的位置是3297,景点位置是4。我们可以调用该服务以获取服务的预测:
import json
predictor.content_type = 'application/json'
predictor.predict(json.dumps({'instances': [
{'data': {'features': {'shape': [8934], 'keys': [4, 3297],
'values': [1, 1]}}}]}))
这里是输出:
{'predictions': [{'score': 0.8006305694580078}]}
关于 JSON 请求和响应格式的更多详细信息,请在此处查看:docs.aws.amazon.com/sagemaker/latest/dg/cdf-inference.html。
由于我们可以向预测器询问任意一对(用户,景点)的分数,因此我们将找到问题用户的所有 31 个景点的分数,然后按分数排序:
def predict_poi(poi_position):
prediction = predictor.predict(
json.dumps({'instances': [{'data':
{'features': {'shape': [8934],
'keys': [poi_position, 3297],
'values': [1, 1]}}}]}))
return prediction['predictions'][0]['score']
predictions = [(poi_position, predict_poi(poi_position)) for poi_position in range(0,31)]
predictions.sort(key=lambda x:x[1], reverse=True)
给定这些分数,我们可以找到排名最高的景点名称,排除那些已经访问过的:
user_visited_pois =
[id_to_poi_name[x] for x in set(recommendation_sample[1]['collect_list(poiID)'])]
for (poi_position, score) in predictions[:10]:
recommended_poi = id_to_poi_name[int(model.stages[2].labels[poi_position])]
if recommended_poi not in user_visited_pois:
print(recommended_poi)
输出如下:
Test Track
Walt Disney World Railroad
Main Street Cinema
Tom Sawyer Island
Tarzan's Treehouse
Mark Twain Riverboat
Sleeping Beauty Castle Walkthrough
Snow White's Scary Adventures
让我们将其与 Spark 提供的推荐进行比较:
print(recommendation_sample[1].recommendation)
recommended: [("Pinocchio's Daring Journey", 3.278768539428711), ('Tom Sawyer Island', 2.78713321685791), ('Splash Mountain', 2.114530324935913), ("Tarzan's Treehouse", 2.06896710395813), ('Fantasmic!', 1.9648514986038208), ("Snow White's Scary Adventures", 1.8940000534057617), ('Main Street Cinema', 1.6671074628829956), ('Mark Twain Riverboat', 1.314055323600769), ('Astro Orbiter', 1.3135600090026855)] ; visited: {'The Many Adventures of Winnie the Pooh', 'Rose & Crown Pub Musician', 'Golden Zephyr', "It's A Small World"}
如读者可能注意到的,有许多重叠的建议。为了更深入地分析模型的质量及其预测能力,我们可以使用第三章中讨论的评估方法,即使用回归算法预测房价,因为这个问题被设定为回归问题。
摘要
在本章中,我们研究了称为协同过滤的新类型机器学习算法。该算法用于推荐系统。我们研究了基于相似度度量来寻找与给定用户相似的用户并基于排名最高的相似用户的集体兴趣发现推荐的基于内存的方法。我们还研究了称为矩阵分解的基于模型的方法,该方法将用户和兴趣映射到潜在因子,并基于这些因子生成推荐。我们还研究了 Apache Spark 和 SageMaker 中各种协同过滤方法的实现。
在下一章中,我们将关注一个非常热门的主题:深度学习。我们将涵盖这个高级领域背后的理论以及一些现代应用。
练习
-
找到一个在本章中没有描述的推荐系统示例。评估哪种协同过滤方法适合那种方法。
-
对于电影推荐引擎,探讨数据稀疏性问题如何影响本章中列出的每个算法。
第三部分:深度学习
人工智能和机器学习领域的主要创新之一是深度学习算法的出现。我们将本书的这一部分奉献给深度学习算法,并解释读者如何使用 AWS 中的各种技术来实现它们。我们采用一种实用的方法来解释深度学习算法,而不是理论方法,并借助几个现实世界的例子来解释深度学习是如何工作的。读者将了解深度学习是什么,深度学习的应用,以及如何在 AWS 上实现深度学习系统。
本节包含以下章节:
-
第七章,实现深度学习算法
-
第八章,在 AWS 上使用 TensorFlow 实现深度学习
-
第九章,使用 SageMaker 进行图像分类和检测
第七章:实现深度学习算法
深度学习是近年来在受欢迎程度上显著增长的机器学习领域。深度学习,也被称为深度结构学习或分层学习,指的是使用多层人工神经网络从数据中训练。在过去的几年里,已经能够执行某些任务,如图像识别,比人类做得更好。
我们将在本章中涵盖以下主题:
-
理解深度学习
-
深度学习的应用
-
理解深度神经网络
-
理解卷积神经网络
理解深度学习
深度学习算法在过去十年中获得了流行。自动驾驶汽车、语音识别和机器人技术等技术由于深度学习算法的改进而取得了显著进步。深度学习帮助研究人员在训练模型执行此类任务时显著减少了错误数量,并在某些任务上超越了人类。然而,最有趣的是,深度学习算法的灵感来源于人类大脑的工作方式。
让我们以图像识别为例。我们看到物体并能够根据我们过去看到这些物体的经验来识别它们。然而,让我们分解这个过程,看看具体发生了什么。首先,光线击中物体,进入我们的眼睛,并击中视网膜。视网膜是一种感觉膜,将光线转换为神经信号。然后,这个信号通过视网膜后面的各个层级传递到大脑。我们的大脑识别在我们眼前场景中存在的物体数量。根据过去的参考,我们的大脑可以识别这些物体。
我们观察物体并识别它的过程中没有单一的过程。从光线进入我们的眼睛到我们的大脑识别物体之间有各种抽象层级。当我们的大脑停止并决定它试图解释的信号中的哪些特征时,没有特定的过程。这样的特征提取过程是自动发生的。
深度学习算法也遵循一个类似的过程。深度学习将获取数据到各个抽象层级的任务分解,使得每一层解释输入数据,并为下一层抽象提供有意义的输出。例如,在图像识别任务中,输入可能是一组来自图像的像素。在第一层,像素可以被处理以找到图像中的边缘。在第二层,关于边缘的信息可以被处理以检测这些边缘之间的角。在下一层,这些角和边缘可以被用来检测图像中的对象。下一层可能预测每个对象是什么。这些抽象层不需要我们定义,但可以自动训练。
在诸如朴素贝叶斯和线性回归等算法中,我们总是使用手工制作的特征。我们已经有分析师查看传入的数据集,并根据数据定义特征集。我们将每个类别标记为分类或连续。然而,在深度学习方法中,我们只需要具有简单特征的数据集,并使用抽象层来创建额外的抽象特征。因此,在如图像识别这样的任务中,数据集是像素集,传统算法在学会如何分类之前需要帮助识别图像中的对象。我们还需要从对象中提取特征,如颜色和大小,然后才能将这些特征输入到分类模型中。然而,对于深度学习算法,我们使用图像的像素作为算法的输入,并带有标记的对象,这样深度学习模型就可以识别错误并执行自我纠正。
深度学习算法可以执行监督学习和无监督学习算法。
深度学习的应用
在本节中,我们将展示一些流行的深度学习算法应用实例。
自动驾驶汽车
自动驾驶汽车已成为汽车行业的主流,每个主要公司都在投资建设下一代自动驾驶汽车。大多数公司在其最新车型中提供一定程度的自动驾驶功能。这些算法主要是由深度学习算法驱动的。让我们看看如何使用深度学习开发自动驾驶算法。
自动驾驶算法的任务是分析道路状况并正确地对其做出反应,以便将汽车从起点开到目的地地址。这个算法的输入是他们从汽车四面安装的摄像头接收到的视频流。算法的输出是转向、油门和刹车的信号。
这个任务极其复杂,因为驾驶员在处理道路状况时需要做出瞬间的决策。驾驶员不仅需要记住到达目的地需要转弯的位置或道路上的速度限制,还必须监控道路上其他车辆和可能横穿道路的行人。
为此类任务创建基于规则的算法非常困难,因为道路上可能发生的大量排列组合。此外,生成具有良好定义特征的任何标记数据集也很困难,因为可能出现的情景数量很难在一个全面的数据集中进行标记。
深度学习算法在这种情况下非常完美,因为从视频流中自动提取特征并根据奖励函数训练模型可以帮助我们抽象自动驾驶汽车中的问题。我们可以将深度学习算法的输入设置为视频流中的像素,将奖励函数设置为在遵守所有交通规则的同时向目的地前进的进度。模拟器用于训练这些模型。这种模拟模仿了道路上的实际条件。
深度学习算法可以自动确定如何生成抽象层的层次,将视频流中的像素转换为检测边缘和对象,类似于图像识别模型。一旦检测到对象,基于汽车所做的错误和纠正,我们训练模型学习如何输出油门、刹车和方向盘指令。最初,在运行模型时,自动驾驶汽车会犯错误并撞到物体。然而,经过足够的迭代,深度学习模型可以学会如何避免这样的错误,并在预定的路线上行驶。因此,无需从视频流中手动提取特征,也不需要生成任何结构化数据集,深度学习算法可以自动学习实现某些目标。
使用深度学习算法学习玩视频游戏
深度学习的另一个流行例子是训练机器玩电脑游戏。世界各地的研究人员通过训练模型来玩 2D 平台游戏,如超级马里奥,来测试他们的深度学习算法。模型的输入是屏幕上的像素,而模型生成的输出是一系列控制器指令,这些指令控制角色并在游戏中完成目标。
我们不需要教深度学习模型这是款视频游戏,以及有一个名叫马里奥的角色需要跳上平台来完成关卡。我们只需要定义一个奖励函数,使得如果角色在没有死亡的情况下移动到下一个平台,我们就奖励深度学习模型,如果角色死亡,我们就惩罚模型。如前所述,深度学习模型会自动将问题划分为多个抽象层次。
模型学习如何自动检测屏幕上的边缘和平台。它首先通过角色进行随机移动,并迅速学习当按下不同的控制器按钮时屏幕上的像素是如何被操作的。基于角色的移动,模型学习如何使主要角色前进。类似于自动驾驶汽车,它也会自动学习触摸屏幕上的某些物体会导致惩罚,以及跳上屏幕上的某些边缘会导致玩家掉入坑中。因此,基于这些给模型提供反馈的奖励函数,模型学习如何导航关卡中的障碍物,将玩家移动到正确的方向。经过进一步训练,它还可以学习如何解决游戏中的谜题。
因此,只需向深度学习模型提供屏幕像素,我们就可以训练一台机器来玩电子游戏。你可以在周围看到这些实现的例子。不久,机器将学会如何通过基于这些机器学习模型的理性思考来解决复杂的谜题。
理解深度学习算法
在下一节中,我们将研究最流行的深度学习算法之一,称为深度神经网络。
在我们探讨深度神经网络之前,我们将研究神经网络是什么。然后,我们将学习深度神经网络算法是什么,以及为什么它们比神经网络有改进。最后,我们将研究卷积神经网络——这是在图像识别领域使用的一种神经网络变体——并展示我们如何能够从图像的像素中自动学习抽象层。
神经网络算法
神经网络算法是受生物神经网络算法启发的机器学习算法。神经网络模仿我们大脑中的神经元是如何工作的。它们有输入节点,信息被输入到网络中,以及一个输出层,它传输特定的动作或预测。神经网络定义了一个结构,其中存储了机器学习模型的信息。
以下截图显示了神经网络结构:
来自数据集的输入特征被输入到神经网络输入节点中。例如,如果我们有一个包含诸如温度、云条件和风速等特征的数据库,并且我们的任务是预测某一天是否会下雨,那么这些特征就被作为输入输入到神经网络中。这些特征可以是二进制或连续值。请注意,每个输入特征对应一个输入节点。
模型的信息存储在隐藏层中的边和节点上。有各种算法可以用来训练神经网络。大多数算法迭代地在神经网络中传递输入参数,并根据隐藏节点中的权重预测输出节点的值。基于预测中的误差,这些权重被调整以改进模型。
输出节点对应于神经网络算法需要做出的预期动作或预测。我们的目标是训练隐藏节点中的权重,使得输出节点的值是准确的。
因此,神经网络松散地基于可以处理输入信号并基于该神经元的函数产生输出的生物神经元。
激活函数
现在,让我们看看神经网络算法是如何训练来计算每个隐藏节点的权重的。在我们开始训练神经网络模型之前,我们需要定义每个隐藏节点将如何处理输入信号并产生输出。用于根据输入函数计算隐藏节点输出的函数称为激活函数。激活函数定义了隐藏节点可以生成的输出范围。在其最简单形式中,激活函数可以是一个步骤函数,其中节点输出基于输入是 0 或 1。在我们天气数据集中的简单例子是这样的:如果天空多云,隐藏节点的输出可能是 1,作为预测降雨的预测,如果天空晴朗,隐藏节点的输出是 0。
这样的步骤激活函数定义为以下:
类似地,如果我们计划使用逻辑或 Sigmoid 步骤函数,输出范围从 到
。
逻辑步骤函数定义为以下:
基于我们使用的学习算法,我们可以选择激活函数。大多数支持神经网络学习的机器学习库也支持使用各种激活函数。
每个节点之间的边都分配一个权重, ,使得该链接位于神经元
和神经元
之间。
反向传播
一旦我们确定了神经网络中连接的权重和激活函数,神经网络就能够根据给定的输入有效地产生输出。然而,这是一个未训练的神经网络,需要一个算法根据它在预测输出时犯的错误来修改和调整神经网络。
使用随机梯度下降进行反向传播的权重更新可以使用以下方程执行。
反向传播算法是能够实现这一结果的流行机制之一。反向传播算法定义了一种方法,通过修改连接的值,如何将输出中的错误传播到连接中。算法背后的直觉非常简单。考虑一个孩子触摸到一个非常热的平底锅并学会不要触摸放在炉子上的锅。孩子犯了一个错误,但从中学习并避免再次犯同样的错误。反向传播算法也允许神经网络犯错误。预测输出和期望输出之间的差异可以使用公式计算,例如均方误差。一旦我们量化了错误,我们就可以使用梯度下降等算法来确定如何修改连接的权重。我们还使用了梯度下降算法来处理第三章中的线性回归算法,使用回归算法预测房价。反向传播过程类似于我们学习线性回归算法系数的方式。然而,我们不是学习回归器的值,而是在神经网络中估计连接权重的值。
使用随机梯度下降进行反向传播的权重更新可以使用以下方程进行:
在这个方程中,是神经网络的 学习率。这是一个可调参数,定义了神经网络能够多快适应训练数据集。权重
是基于连接的先前权重计算的。权重变化值由学习率决定,即错误、先前权重和一个随机项之间的差异。
我们通过将训练数据传递到神经网络中并在每次迭代中修改连接的权重来遍历训练数据。权重被修改,以便每次迭代时错误率都会降低。尽管随机梯度下降不能达到全局最大值,但它对于训练神经网络以减少错误是有效的。当错误低于某个可接受的值或收敛到准确性改进最小的时候,我们终止迭代。
神经网络可以用于训练监督学习以及无监督学习。
深度神经网络简介
深度神经网络(DNN)是神经网络的一种变体,其中我们使用一个以上的隐藏层。数据必须通过一个以上的隐藏层,网络才能被认定为深度神经网络。这增加了神经网络模型的复杂性,因为它极大地增加了网络中的连接,从而增加了学习时间。
这里展示了深度神经网络的一个表示:
然而,增加额外的隐藏层也允许网络通过多个层次的模式识别传递输入数据。每个隐藏层都从前一个隐藏层接收输入。因此,它们可以识别比前一层更复杂的模式。这是因为在之前的层中,特征被聚合和重新组合。这被称为特征层次。DNN 中更深层的特征可以识别更复杂的模式。因此,DNN 更擅长处理具有复杂模式的数据库。此外,由于隐藏层自动生成这些抽象层,因此不需要领域专业知识进行特征提取。例如,在图像识别中,我们不需要对图像中物体的边缘进行标记,因为初始层可以学会识别边缘,而深层层则学会识别可能由这些边缘生成的物体。
深度学习和 DNN 是数据科学家在业界经常听到的热门词汇。对于大多数应用,如自动驾驶汽车或机器人技术,DNN 与人工智能同义。由于 GPU 架构的进步,这些 DNN 结构的生成非常适合,因此这些算法无法处理大量数据集以训练高度准确的机器学习算法。
理解卷积神经网络
在本节中,我们将探讨一种 DNNs 的变体,其中网络结构被修改以适应图像识别任务。
在本章迄今为止讨论的神经网络中,我们看到了所有输入层都是一维的。然而,图像是二维的。为了捕捉图像如何被输入神经网络进行训练,我们必须修改输入层的结构。传统算法需要人类对图像中物体的边缘进行标记。卷积神经网络(CNNs)可以通过足够的训练自动检测图像中的物体,并且根据图像的标签,它们可以学习如何在不显式标记图像边缘的情况下识别图像中的物体。
CNNs 需要一个预处理阶段,其中图像必须被准备成特定的数据结构,该结构用作前馈 DNNs 的输入。预处理阶段的第一项任务是分解图片成更小的图像,这样我们就不丢失图像中的任何信息。CNN 的灵感来源于人类视觉皮层的组织。我们的神经元对特定视野中看到的视觉做出反应。这被称为局部感受野。这些局部感受野相互重叠。同样,在 CNN 中,我们以图像作为输入,并将图像的重叠子区域表示为局部感受野。
以下图表展示了如何使用局部感受野的概念,通过滑动窗口从图像中生成特征图:
使用这种方法的优点是它消除了图像中对象的大小和位置问题。例如,想象图像中有一只猫。根据我们的训练示例,我们已对包含猫的图片进行了标记。使用局部感受野,我们检测到这只猫并将特征图标记为图像中有猫的图片。在新的图像中,无论猫在图像中的位置如何,我们都会找到一个包含猫的图像的特征图,因为我们使用这种滑动窗口方法创建了多个子图像。从图像中生成的这一层特征图被称为卷积层。
我们还可以通过应用各种过滤器到过程中,从同一组像素生成多个特征图。例如,我们可以应用颜色过滤器到像素,并从同一组像素生成三个特征图。作为一个数据科学家,你必须根据你想要从图像中提取的信息量以及我们生成这些网络时可以使用的处理能力来设计 CNN。
一旦生成了卷积层,我们通过使用称为池化的过程从图像中创建压缩特征图。这有助于我们以更小的特征图来表示特征图。在压缩特征图时,可以应用两种流行的池化过程。在最大池化方法中,通过仅从每个网格中选择最大值来降低特征图的维度。
以下截图展示了最大池化如何从每个特征图中取最大值,并将特征图的维度从 4x4 矩阵降低到 2x2:
另一种池化类型称为平均池化,这是在池化数据时选择网格中值的平均值。以下图表展示了平均池化是如何工作的:
最大池化通常比平均池化更受欢迎,因为它在降低特征图维度时起到噪声抑制的作用,并移除了非主导特征。与卷积层类似,池化层也可以使用重叠窗口来创建更小的特征图。请注意,这些决策可以根据你想要从图像中捕获的细节程度来做出。
CNN 的另一个组成部分是卷积层。当我们设计 CNN 时,一组图像可能会决定我们从图像中提取哪些特征图。然而,根据应用需求,我们可能希望从图像中提取不同的特征。例如,如果我们的图像识别软件正在检测由地震仪(一种检测地震的设备)生成的图表,我们的特征图将包含黑白图表,其中我们的算法需要对时间序列中的边缘进行敏感检测。在这种情况下,我们可以设计一个卷积核,将特征图中的一定模式转换为另一个可以注释这些模式的特征图。同样,如果你正在处理带有对象的彩色图像,为每种颜色创建三个特征图,检测边缘,然后合并特征图,这很有帮助。因此,卷积层帮助设计此类神经网络的科学家根据特定应用对其进行调整。我们不会解释如何设置卷积层的细节,因为大多数库都允许你使用预设计的 CNN 应用于你的应用。
因此,通过使用局部感受野、卷积层和池化操作,我们构建以下结构将图像展平为 DNN 的输入:
通过使用局部感受野方法,第一层卷积将图像转换为特征图。然后,我们通过池化数据来减少特征图的维度,将特征图的维度从 20x20 减少到 10x10。在下一阶段,我们根据可能选择的核将池化后的特征图转换为更多的特征图。这些核可能将检测直线或交叉的特征图转换为其他特征图。然后,我们将卷积层的输出池化到 4x4 特征图。此时,原始图像被转换为 DNN 任务特定信息。这些特征图也代表了图像的空间成分。然后,DNN 根据这些数据训练,并学习根据特征图可能表示的内容来预测输出。
摘要
在本章中,我们解释了深度学习的含义以及它在现实世界中的应用。我们还研究了应用案例,例如自动驾驶汽车和视频游戏机器人,以及它们如何使用深度学习自动学习执行任务。我们解释了神经网络是什么,以及 DNN 是如何改进它们的。我们还研究了 DNN 的一个变体,称为 CNN,并介绍了 CNN 的各个组成部分。
本章的目标是向您提供有关深度学习算法的信息,以便您了解它们如何在现实世界中应用。尽管我们没有深入探讨深度学习的数学原理,也没有提供关于激活函数等概念的所有细节,但我们希望您在深度学习领域获得了实际的知识。对于那些好奇心旺盛的人,这个领域正在进行着大量的研究,我们敦促您更多地了解您感兴趣的算法。
在下一章中,我们将探讨如何使用流行的技术,如 TensorFlow 和 MXNet,来实现深度学习。这些知识将帮助您实现一系列深度学习算法。
练习
-
如果你拥有一部智能手机,你的手机上就有很多应用使用了深度学习。探索你的手机上哪些应用使用了本章中列出的算法之一,并研究如何设计这样的算法。
-
列出 CNN 的各种组件,并设计一个能够检测人脸特征的 CNN。
第八章:在 AWS 上使用 TensorFlow 实现深度学习
TensorFlow 是一个非常流行的深度学习框架,可以用来训练深度神经网络,例如前一章中描述的那些。
在本章中,我们将涵盖以下主题:
-
关于 TensorFlow
-
TensorFlow 作为通用机器学习库
-
通过 SageMaker 训练和部署 TensorFlow 模型
-
使用 TensorFlow 创建自定义神经网络
关于 TensorFlow
TensorFlow 是一个深度学习库,由 Google 于 2015 年首次发布。最初,它包含一个核心库,允许用户以符号形式处理张量(多维数组),从而实现高性能的低级神经网络设计和训练。如今,它是一个完整的深度学习库,允许数据科学家使用高级原语构建用于复杂问题的模型,例如图像识别。您还可以使用 TensorFlow 解决标准机器学习问题,例如我们在过去章节中考虑过的问题。TensorFlow 具有与我们在 scikit-learn、Apache Spark 和 SageMaker 中使用的类似抽象。例如,它允许用户使用高级抽象,如估计器、预测器和评估器,来创建分类模型或回归模型。
TensorFlow 作为通用机器学习库
在本节中,我们将展示如何使用 TensorFlow 为 第三章 中的房屋估值问题创建回归模型,即 使用回归算法预测房屋价值。为了开始,我们首先启动一个 SageMaker 笔记本,并选择 TensorFlow 内核 (conda_tensorflow_p36),它包含本节所需的全部 TensorFlow 依赖项:
现在,让我们考虑 第三章 中的估值问题,使用回归算法预测房屋价值。回想一下,我们有一组指标(房屋年龄、距离最近中心等)来估计房屋的中值(在 medv 列中表达,这是我们的目标特征),如下截图所示:
在 第三章 的 使用回归算法预测房屋价值 中,我们确定了 11 个学习特征用于预测目标特征 (medv):
training_features = ['crim', 'zn', 'indus', 'chas', 'nox', 'rm', 'age', 'dis', 'tax', 'ptratio', 'lstat']
label = 'medv'
基于这些信息,我们定义了一个 TensorFlow 线性回归器,它能够使用预构建的神经网络解决我们的回归问题:
tf_regressor = tf.estimator.LinearRegressor(
feature_columns=[tf.feature_column.numeric_column('inputs',
shape=(11,))])
对于回归器,我们决定创建一个单特征输入,它将其他特征组装成一个代表输入层的数字向量。也有可能为每个训练特征创建一个命名特征(就像我们在第三章中做的,使用回归算法预测房屋价值),但我们将只有一个向量特征来简化本节末尾讨论的预测服务。
要构建一个回归器,我们需要传递 TensorFlow 特征列,这些列可以是几种不同类型之一。tf.feature_column 包提供根据模型使用的编码构建不同类型列的函数(例如,分类、分桶等)。特征列通知模型提交为输入的数据的预期格式。在我们的情况下,我们只需告诉模型期望长度为 11 的向量行。
为了构建要传递给模型的实际数据,我们需要创建一个矩阵。pandas 库有一个方便的方法,as_matrix(),因此我们将切片训练特征并构建一个矩阵:
training_df[training_features].as_matrix()
同样,我们将创建特征向量:
training_df[label].as_matrix()
一旦我们有了这两样东西,我们就可以开始将数据插入到模型中。TensorFlow 期望通过定义一个知道如何将数据源到张量(TensorFlow 的多维数组的基本构建块)的函数来提供数据。
以下是将数据插入的代码块:
training_input_fn = tf.estimator.inputs.numpy_input_fn(
x={'inputs': training_df[training_features].as_matrix()},
y=training_df[label].as_matrix(),
shuffle=False,
batch_size=1,
num_epochs=100,
queue_capacity=1000,
num_threads=1)
tf.estimator.inputs.numpy_input_fn 工具能够通过提供训练矩阵和目标特征向量来构建这样一个函数。它还将创建数据分区,以便在多个 epoch 中运行网络。它还允许用户选择批次的尺寸(回想一下第三章中提到的迷你批次方法,使用回归算法预测房屋价值,用于随机梯度下降)。本质上,底层回归器的神经网络依赖于 training_input_fn 函数在每个算法阶段创建输入张量。
同样,我们创建一个类似的函数来提供测试数据,为模型评估做准备:
test_input_fn = tf.estimator.inputs.numpy_input_fn(
x={'inputs': test_df[training_features].as_matrix()},
y=test_df[label].as_matrix(),
shuffle=False,
batch_size=1)
要训练模型,我们调用常规的 fit() 方法,提供我们创建的数据来源函数:
tf_regressor.train(input_fn=training_input_fn, steps=50000)
steps 参数是我们可以对总步数施加的限制。在这里,一个步骤是指对一个批次进行一次梯度下降更新。因此,每个 epoch 运行一定数量的步骤。
一旦完成训练,TensorFlow 将在 final epoch 输出损失度量:
INFO:tensorflow:Loss for final step: 1.1741621.
我们可以通过运行测试数据集(通过提供测试数据集来源函数)来评估我们模型的准确性:
tf_regressor.evaluate(input_fn=test_input_fn)
上述代码生成以下输出:
{'average_loss': 37.858795, 'label/mean': 22.91492, 'loss': 37.858795, 'prediction/mean': 21.380392, 'global_step': 26600}
平均损失取决于目标特征的单位,因此让我们看看构建一个类似于我们在第三章中创建的散点图,使用回归算法预测房价,以比较实际与预测的房价。为此,我们首先需要获得predictions。
我们只需调用predict()函数来获取predictions,再次提供测试数据集来源函数:
predictions = tf_regressor.predict(input_fn=test_input_fn)
predictions返回了一个实际上是单值向量的 Python 生成器,因此我们可以通过构造列表来获取predictions:
predicted_values = [prediction['predictions'][0] for prediction in predictions]
因此,我们可以检查predicted_values:
predicted_values[:5]
之前的代码生成了以下输出:
[22.076485, 23.075985, 17.803957, 20.629128, 28.749748]
我们可以将预测值作为列插入到原始的pandas测试数据框中:
test_df['prediction'] = predicted_values
这允许我们使用 pandas 绘图方法来创建图表:
test_df.plot(kind='scatter', x=label, y='prediction')
我们可以在以下屏幕截图中看到结果:
注意到存在明显的相关性。为了提高性能,我们可能需要调整我们的回归模型、批大小、步骤、周期等。
通过 SageMaker 训练和部署 TensorFlow 模型
与在笔记本实例中训练模型不同,我们使用 SageMaker 基础设施来训练模型。在之前的章节中,我们使用了内置的估计器,如 BlazingText、XGBoost 和因子分解机(FMs)。在本节中,我们将展示如何构建自己的 TensorFlow 模型并通过 SageMaker 训练它们,就像我们处理这些预构建模型一样。为此,我们只需教会 SageMaker 如何构建我们的 TensorFlow 模型,并遵守一些关于数据格式、位置和结构的约定。通过一个 Python 脚本,我们指定所有这些。
SageMaker 将依赖于这个 Python 脚本来在 SageMaker 训练实例中执行训练:
import sagemaker
from sagemaker import get_execution_role
import json
import boto3
from sagemaker.tensorflow import TensorFlow
sess = sagemaker.Session()
role = get_execution_role()
tf_estimator = TensorFlow(entry_point='tf_train.py', role=role,
train_instance_count=1, train_instance_type='ml.m5.large',
framework_version='1.12', py_version='py3')
tf_estimator.fit('s3://mastering-ml-aws/chapter8/train-data/')
上述代码块的前几行是启动 SageMaker 所需的常规导入和会话创建。下一个重要的事情是创建一个 TensorFlow 估计器。注意我们如何向构造函数提供 Python 脚本、TensorFlow 版本和 Python 版本,以及实例数量和类型的常规参数。
当调用tf_estimator.fit(training_data_s3_path)函数时,SageMaker 将执行以下任务:
-
启动一个 EC2 实例(服务器)。
-
将 S3 数据下载到本地目录。
-
调用
tf_train.pyPython 脚本来训练模型。Python 脚本预计将模型存储在 EC2 实例的某个本地目录中。 -
将存储的模型打包成
.tar.gz文件并上传到 S3。此外,它还将创建一个 Amazon 容器和 SageMaker 模型标识符。
因此,训练是在 SageMaker 管理的服务器上进行的,但它产生的模型是 SageMaker 兼容的模型,可以用于提供预测或运行批量转换作业,就像我们在前几章中所做的那样。
让我们看看 tf_train.py Python 脚本,它负责模型训练和保存模型。
这个 Python 脚本必须从 SageMaker 容器接收一些信息。特别是,它必须接收以下信息:
-
SageMaker 下载数据的本地目录(从 S3)
-
Python 脚本需要存储训练好的模型的位置
-
模型需要的其他超参数(我们目前不会深入探讨,而只是使用固定值,但我们将展示在 第十四章,在 Spark 和 SageMaker 中优化模型,如何使用这些参数进行超参数调整)
看看下面的代码:
import pandas as pd
import argparse
import os
import tensorflow as tf
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--epochs', type=int, default=100)
parser.add_argument('--batch_size', type=int, default=1)
parser.add_argument('--steps', type=int, default=12000)
parser.add_argument('--model_dir', type=str)
parser.add_argument('--local_model_dir', type=str,
default=os.environ.get('SM_MODEL_DIR'))
parser.add_argument('--train', type=str, default=os.environ.get('SM_CHANNEL_TRAINING'))
args, _ = parser.parse_known_args()
housing_df = pd.read_csv(args.train + '/train.csv')
training_features = ['crim', 'zn', 'indus', 'chas', 'nox',
'rm', 'age', 'dis', 'tax', 'ptratio', 'lstat']
label = 'medv'
tf_regressor = tf.estimator.LinearRegressor(
feature_columns=[tf.feature_column.numeric_column('inputs',
shape=(11,))])
training_input_fn = tf.estimator.inputs.numpy_input_fn(
x={'inputs': housing_df[training_features].as_matrix()},
y=housing_df[label].as_matrix(),
shuffle=False,
batch_size=args.batch_size,
num_epochs=args.epochs,
queue_capacity=1000,
num_threads=1)
tf_regressor.train(input_fn=training_input_fn, steps=args.steps)
def serving_input_fn():
feature_spec = tf.placeholder(tf.float32, shape=[1, 11])
return tf.estimator.export.build_parsing_serving_input_receiver_fn(
{'input': feature_spec})()
tf_regressor.export_savedmodel(export_dir_base=args.local_model_dir + '/export/Servo',
serving_input_receiver_fn=serving_input_fn)
脚本的前一部分只是设置一个参数解析器。由于 SageMaker 将此脚本作为黑盒调用,它需要能够将此类参数注入脚本中。有了这些参数,它可以训练 TensorFlow 模型。你可能注意到,训练与我们在前一部分所做的是一样的。唯一的新部分是保存模型和定义一种新的函数(serving_input_fn)。这个函数与我们在训练和测试中使用的函数有类似的目的,但它在服务时间(即每次向服务发出预测请求时)将被使用。它负责定义从输入张量占位符到模型期望的特征的必要转换。tf.estimator.export.build_parsing_serving_input_receiver_fn 工具可以方便地构建用于此类目的的函数。它构建一个期望 tf.Example(特征的一个 protobuf 序列化字典)被输入到字符串占位符中的函数,以便它可以解析这样的示例到特征张量。在我们的例子中,我们只有一个向量作为输入,所以转换是直接的。我们脚本中的最后一行将模型保存到 SageMaker 通过 local_model_dir 参数请求的位置。为了使反序列化和解包工作,惯例是将模型保存到 /export/Servo 子目录中。
一旦我们运行 fit() 命令,我们就可以像往常一样部署模型:
predictor = tf_estimator.deploy(instance_type='ml.m5.large', initial_instance_count=1)
对于这个例子,我们使用了一个非 GPU 实例类型,但这些在很大程度上被推荐用于严肃的服务和训练。我们将在 第十五章,调整机器学习集群 中深入探讨这一点。
deploy() 命令将启动一个容器,该容器能够为我们构建的模型提供服务。然而,构造要发送给此类服务的有效负载并不像前一章中的示例那样简单,因为我们需要构造 tf.Example。
在预测时,我们希望根据特定的特征向量获得价格。假设我们想要找到以下特征的价格:
features_vector = [0.00632, 18.0, 2.31, 0.0, 0.538, 6.575, 65.2, 4.09, 296.0, 15.3, 4.98]
第一步是构建一个 tf.train.Example 实例,在我们的情况下,它由一个名为 inputs 的单个特征组成,该特征包含 features_vector 的浮点值:
model_input = tf.train.Example(features=tf.train.Features(
feature={"inputs": tf.train.Feature(float_list=tf.train.FloatList(value=features_vector))}))
下一步是使用 SerializeToString 将 model_input protobuf 消息序列化:
model_input = model_input.SerializeToString()
由于这实际上是一个字节字符串,我们需要进一步编码 model_input,以便它可以作为字符串发送到负载中,而不包含特殊字符。我们使用 base64 编码来完成这项工作:
encoded = base64.b64encode(model_input).decode()
最后,我们通过组装一个 JSON 请求来调用我们的 predictor 服务:
predictor.predict('{"inputs":[{"b64":"%s"}]}' % encoded)
注意,在发送通过创建以 b64 为键的字典来编码的 base64 protobuf 示例时,有一个特殊的约定。从 JSON 解码的输出是一个包含以下预测的字典:
{'outputs': [[24.7537]]}
inputs 和 outputs 负载 JSON 键是 SageMaker 的合约的一部分,不应与我们的单个特征 inputs 的名称混淆,inputs 可以是任意字符串。
使用 TensorFlow 创建自定义神经网络
在上一节“通过 SageMaker 训练和部署 TensorFlow 模型”中,我们使用了 TensorFlow 的高级库,使用 LinearRegressor 构建了一个回归模型。在本节中,我们将展示如何使用 TensorFlow 的 Keras 库构建一个实际的神经网络。Keras 通过隐藏核心(低级)TensorFlow 库背后的某些复杂性来简化神经网络的设计。
在本章中,我们将使用无处不在的 MNIST 数据集,它包含一系列手写数字的图像以及真实的标签(0 到 1 之间的值)。MNIST 数据集可以从 www.kaggle.com/c/digit-recognizer/data 下载。
数据集以 CSV 格式提供,包含 784 列,对应于 28 x 28 图像中的每个像素。每列的值代表像素在 0 到 255 的灰度强度。它还有一个额外的列用于标签,其值介于 0 到 9 之间,对应于实际的数字。
让我们下载数据集,并使用 pandas 和 scikit-learn 进行我们通常的测试和训练分割:
import pandas as pd
from sklearn.model_selection import train_test_split
mnist_df = pd.read_csv('mnist/train.csv')
train_df, test_df = train_test_split(mnist_df, shuffle=False)
我们可以通过 train.head() 检查数据集:
如我们所见,列被标记为 pixelX,其中 x 是介于 0 和 783 之间的数字。让我们将这些列的名称定义为不同的变量:
pixel_columns = ['pixel' + str(i) for i in range(0, 784)]
label_column = 'label'
数据集中的每一行都成为了一个训练示例,因此代表了我们的网络输入层。在网络的另一端,我们将有 10 个节点,每个节点代表给定每个输入向量的每个数字的概率。在我们的例子中,我们将只使用一个中间层。
以下图展示了我们的网络结构:
在 Keras 中定义这样的网络非常简单:
import tensorflow as tf
from tensorflow import keras
model = keras.Sequential([
keras.layers.InputLayer(input_shape=(784,), batch_size=5),
keras.layers.Dense(256, activation=tf.nn.relu),
keras.layers.Dense(10, activation=tf.nn.softmax)
])
注意定义这样一个模型是多么容易。它由三个层组成:
-
一个输入层,其中每个向量的大小为 784,并且每个梯度下降更新将提供五个示例的迷你批次
-
一个中间密集层(意味着每个节点将连接到下一层的每个节点)在每个节点上具有Rectified Linear Unit(ReLU)激活函数
-
一个大小为 10 的输出层,使用 softmax 激活函数(因为我们想要数字的概率分布)
除了通过一系列层定义网络之外,TensorFlow 还需要编译模型。这基本上意味着提供要使用的优化方法、loss函数和所需的指标:
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
下一个阶段将是将模型与我们的数据拟合。为了将数据集输入到 TensorFlow 中,我们需要创建numpy矩阵,其中每一行是一个训练实例,每一列代表输入层中的一个节点。方便的是,pandas方法dataframe.as_matrix()正好能完成这个任务,因此我们将数据集切片以包含训练列并构建这样一个矩阵。此外,我们将矩阵归一化,以便每个灰度值介于 0 和 1 之间:
import numpy as np
vectorized_normalization_fn = np.vectorize(lambda x: x / 255.0)
normalized_matrix =
vectorized_normalization_fn(train_df
[pixel_columns].as_matrix())
同样,我们通过将pandas序列转换为数字向量来获得labels向量:
labels = train_df[label_column].as_matrix()
现在我们已经有了训练矩阵和标签,我们准备拟合我们的模型。我们通过简单地调用fit()并提供标记的训练数据来完成此操作:
model.fit(normalized_matrix, labels, epochs=3)
训练将以训练数据集中的损失和准确度指标结束:
Epoch 3/3 31500/31500 [==============================] - 16s 511us/sample - loss: 0.0703 - acc: 0.9775
为了确定我们的模型是否过拟合(即,它只是学会了如何分类我们的训练数据集中的图像,但无法推广到新的图像),我们需要在测试数据集中测试我们的模型。为此,我们将对测试数据集执行我们在训练数据集上所做的相同转换。
我们模型的evaluate()函数将提供准确度评估指标:
normalized_test_matrix = vectorized_normalization_fn(test_df[pixel_columns].as_matrix())
test_labels = test_df[label_column].as_matrix()
_, test_acc = model.evaluate(normalized_test_matrix, test_labels)
print('Accuracy on test dataset:', test_acc)
上述代码生成以下输出:
Accuracy on test dataset: 0.97
注意,我们的简单模型实际上相当准确。让我们检查测试数据集中的几个图像,看看预测是否与实际数字匹配。为此,我们将绘制图像,并通过以下步骤将它们与预测数字进行比较:
- 首先,我们将定义一个函数,该函数可以获得测试数据集矩阵中特定行(
index)的预测标签:
def predict_digit(index):
predictions = model.predict(normalized_test_matrix[index:index + 1])
return np.argmax(predictions, axis=1)[0]
model.predict()将根据特征矩阵获得预测。在这种情况下,我们只需要一行,所以我们切片我们的矩阵为单行以获得特定索引的预测。预测将是一个包含 10 个组件的向量,每个组件代表每个数字的强度。我们使用argmax函数找到强度最大的数字(即,找到最可能的数字)。
- 接下来,我们定义一个函数,
show_image(),它将根据索引绘制图像:
from IPython.display import display
from PIL import Image
def show_image(index):
print("predicted digit: %d" % predict_digit(index))
print("digit image:")
vectorized_denormalization_fn = np.vectorize(lambda x: np.uint8(x * 255.0))
img_matrix = normalized_test_matrix[index].reshape(28, 28)
img_matrix = vectorized_denormalization_fn(img_matrix)
img = Image.fromarray(img_matrix, mode='L')
display(img)
我们依赖PIL库来进行绘图。为了绘制图像,我们需要将我们的值反归一化到 0-255 的范围,并将 784 个像素重塑为 28x28 的图像。
让我们通过以下截图中的几个实例来考察一下:
第二个例子:
以下图像无法被模型正确识别:
第二个例子:
你可能同意,即使是人类也可能犯类似的错误。
那么,我们如何在我们的model之上构建一个服务呢?
做这件事的一个简单方法是从我们的model创建一个estimator实例:
estimator = tf.keras.estimator.model_to_estimator(model)
记得我们在上一节中使用的LinearRegressor也是一个estimator实例,因此从这个estimator实例开始,训练、序列化和部署模型的过程是相同的。
摘要
在本章中,我们介绍了创建两个不同的 TensorFlow 模型的过程:一个使用高级 estimator 库,另一个使用 Keras 构建自定义神经网络。在这个过程中,我们还展示了 SageMaker 如何无缝处理 TensorFlow 模型的训练和部署。
在下一章,使用 SageMaker 进行图像分类和检测中,我们将展示如何在 AWS 上使用深度学习直接检测和识别图像。
练习
本章的问题如下:
-
一个 epoch、batch 和 step 之间的区别是什么?
-
你会如何设计一个网络,能够为第六章中考虑的主题公园数据集提供推荐,该章节名为分析游客模式以提供推荐?
-
你会如何构建一个能够将第五章中提到的广告分类为点击/未点击的网络,该章节名为使用聚类算法进行客户细分?
第九章:使用 SageMaker 进行图像分类和检测
我们研究了一种称为卷积神经网络(CNN)的深度学习算法,它能够对图像进行分类。然而,在实际中实现这样的算法极其复杂,需要大量的专业知识。Amazon SageMaker 提供了功能,允许您使用深度学习能力训练机器学习模型,例如图像分类算法。
本章我们将涵盖以下主题:
-
介绍 Amazon SageMaker 用于图像分类
-
使用 Amazon SageMaker 训练深度学习模型
-
使用 Amazon SageMaker 对图像进行分类
介绍 Amazon SageMaker 用于图像分类
由于 Tensorflow 和 SageMaker 等服务,数据科学领域已经发生了革命。过去,复杂的算法,如深度学习,只有大公司和研究实验室才能访问。然而,得益于 SageMaker 等服务,任何能够编写代码调用这些服务的人都可以训练和使用复杂的机器学习算法。这使得对机器学习有实际了解的青少年能够创建能够执行复杂机器学习任务的应用程序。通过访问 SageMaker 市场中的最先进机器学习模型,您将能够以与世界顶级科学家相同的能力执行机器学习任务。
Amazon SageMaker 提供了大量的算法,数据科学家可以使用这些算法来训练他们的机器学习模型,并且它还提供了工具来对一批测试数据进行预测或创建一个端点以将模型作为服务使用。当我们处理较小的测试数据集时,我们可以使用 Python 机器学习库,如 scikit-learn。然而,当我们处理较大的数据集时,我们必须依赖框架,如 Apache Spark,并使用库,如 MLLib。
亚马逊在 SageMaker 中提供了一系列机器学习库,我们可以使用来自不同供应商的预调优模型来训练我们的机器学习模型。因此,当您处理问题时,您可以在 Amazon SageMaker 市场中搜索已可用的算法。如果有来自不同供应商的多个算法和模型可供选择,您可以根据它们的定价模型和准确性来选择算法。
SageMaker 市场可以用来选择亚马逊以外的供应商提供的模型。因此,如果您需要一个针对遗传工程领域功能进行优化的专用算法,或者像建筑工人检测器这样的图像分类算法的专用版本,您可以选择一个预训练模型并直接获取预测。
亚马逊 SageMaker 还提供调整市场算法参数的作业,以便它们可以适应您的集群大小和应用。这类作业被称为超参数调整作业。您可以为参数提供各种值来检查一个算法。然后,亚马逊 SageMaker 可以自动训练以选择最适合您应用的调整参数。您也可以手动设置这些参数的值。
在本章中,我们将通过一个图像分类器的示例来展示如何使用亚马逊 SageMaker。该算法从标记的图像集中学习,然后通过为测试图像中的每个对象分配存在概率来检测测试数据集中的对象。对于这次测试,我们使用了一个公开的数据集,称为 Caltech265 (www.vision.caltech.edu/Image_Datasets/Caltech256/)。该数据集包含 30,608 张图像。数据集用 256 个对象进行了标记。
请将以下数据集文件下载到您的 AWS S3 桶中:data.mxnet.io/data/caltech-256/caltech-256-60-train.rec 和 data.mxnet.io/data/caltech-256/caltech-256-60-val.rec
为了我们的实验目的,我们将训练数据文件存储在 AWS 桶中的 image-classification-full-training/train 文件夹下。该文件包含 15,420 个图像文件,这些图像已调整为 224 x 224 像素。
使用亚马逊 SageMaker 训练深度学习模型
在本节中,我们将展示如何使用此数据集训练图像分类模型。同样,将 validation 文件下载到 image-classification-full-training/validation 文件夹下的 AWS 桶中。
在 第七章,实现 AWS 上的深度学习算法中,我们研究了一种称为 CNN 的算法,该算法使用深度神经网络构建对象检测模型。该模型在标记图像上训练,并学习如何使用各种深度神经网络层识别图像中的对象。从头开始构建这个深度学习模型是困难的。亚马逊 SageMaker 提供了一种简单的方法,使用您自己的数据集来训练图像分类算法,然后将该模型部署到检测图像中的对象。我们将提供一个使用 caltech256 数据集训练模型的代码示例,然后在下一节,使用亚马逊 SageMaker 分类图像中对其进行测试。
与第八章,在 AWS 上使用 TensorFlow 实现深度学习类似,您将需要启动一个新的 SageMaker 实例并使用 Jupyter Notebooks 来开始测试。亚马逊 SageMaker 已经为您提供了大量的示例代码以供开始。要访问这些示例,请参考 SageMaker 示例标签页:
本章中使用的代码也是对 SageMaker 提供的图像分类示例的修改。您可以使用conda_python3内核创建一个新的笔记本:
在第五章“使用聚类算法进行客户细分”和第六章“分析访客模式以提供建议”等章节中,我们使用了亚马逊提供的 Amazon SageMaker 高级sagemaker Python 库。在这里,我们选择展示如何从boto3库中使用 SageMaker 通用客户端。这个库提供了一个声明式接口,更接近 SageMaker 背后的 API。希望读者您能通过本章的示例理解 API 的低级调用。
我们在此提供了一个代码示例,说明如何使用 boto3 客户端通过 Amazon Sagemaker 创建图像分类模型。
- 初始化我们想在 SageMaker 中使用的角色和image-classification图像,然后指定我们桶的名称:
import boto3
import re
from sagemaker import get_execution_role
from sagemaker.amazon.amazon_estimator import get_image_uri
role = get_execution_role()
bucket='mastering-ml-aws'
training_image = get_image_uri(boto3.Session().region_name, 'image-classification')
被称为image-classification的训练图像是图像分类算法的 Docker 镜像。Amazon SageMaker 提供了大量此类镜像,您可以使用它们来训练您的分类器。每个镜像都有自己的调整参数,您也可以在训练该算法时提供。
- 我们将在以下代码块中声明这些调整参数:
# Define Parameters
num_layers = "18"
image_shape = "3,224,224"
num_training_samples = "15420"
num_classes = "257"
mini_batch_size = "64"
epochs = "2"
learning_rate = "0.01"
图像分类算法使用深度神经网络;这些参数对我们来说很熟悉,因为我们已经在第七章,“实现深度学习算法”中研究过。
我们定义深度学习算法将使用的隐藏层数量。我们还需要指定通道数和每个图像的大小。我们定义训练图像的数量和类(对象类型)的数量。迭代次数定义了我们将在训练数据集上迭代的次数。深度学习分类器的准确率随着我们对数据集迭代的次数增加而提高。学习率定义了深度学习算法允许对权重进行的更改次数。
我们建议您使用不同的参数运行此算法,以观察对评估和训练时间的影响。
- 一旦我们定义了参数,我们就会初始化用于 S3 的 boto3 客户端,我们在那里存储了我们的训练和验证文件。
import time
import boto3
from time import gmtime, strftime
# caltech-256
s3_train_key = "image-classification-full-training/train"
s3_validation_key = "image-classification-full-training/validation"
s3_train = 's3://{}/{}/'.format(bucket, s3_train_key)
s3_validation = 's3://{}/{}/'.format(bucket, s3_validation_key)
s3 = boto3.client('s3')
- 我们构建了一个 JSON,其中包含了训练我们的图像分类器所需的全部参数:
# create unique job name
job_name_prefix = 'example-imageclassification'
timestamp = time.strftime('-%Y-%m-%d-%H-%M-%S', time.gmtime())
job_name = job_name_prefix + timestamp
training_params = \
{
# specify the training docker image
"AlgorithmSpecification": {
"TrainingImage": training_image,
"TrainingInputMode": "File"
},
"RoleArn": role,
"OutputDataConfig": {
"S3OutputPath": 's3://{}/{}/output'.format(bucket, job_name_prefix)
},
"ResourceConfig": {
"InstanceCount": 1,
"InstanceType": "ml.p2.xlarge",
"VolumeSizeInGB": 50
},
"TrainingJobName": job_name,
"HyperParameters": {
"image_shape": image_shape,
"num_layers": str(num_layers),
"num_training_samples": str(num_training_samples),
"num_classes": str(num_classes),
"mini_batch_size": str(mini_batch_size),
"epochs": str(epochs),
"learning_rate": str(learning_rate)
},
"StoppingCondition": {
"MaxRuntimeInSeconds": 360000
},
"InputDataConfig": [
{
"ChannelName": "train",
"DataSource": {
"S3DataSource": {
"S3DataType": "S3Prefix",
"S3Uri": s3_train,
"S3DataDistributionType": "FullyReplicated"
}
},
"ContentType": "application/x-recordio",
"CompressionType": "None"
},
{
"ChannelName": "validation",
"DataSource": {
"S3DataSource": {
"S3DataType": "S3Prefix",
"S3Uri": s3_validation,
"S3DataDistributionType": "FullyReplicated"
}
},
"ContentType": "application/x-recordio",
"CompressionType": "None"
}
]
}
在这个 JSON 中有很多东西要学习。我们在AlgorithmSpecification部分定义了我们想要用于训练的算法。OutputDataConfig定义了模型将存储的位置。ResourceConfig定义了用于训练作业的实例类型。请注意,在 AWS 上,基于 GPU 的实例可以更快地运行图像分类等任务。所有算法参数都在HyperParameters部分定义。我们在 JSON 的InputDataConfig部分下设置了训练数据集和验证数据集。此 JSON 配置将在下一个代码块中用于设置训练作业的参数。
以下代码块启动了一个sagemaker训练作业:
# create the Amazon SageMaker training job
sagemaker = boto3.client(service_name='sagemaker')
sagemaker.create_training_job(**training_params)
在您开始训练作业后,您可以在 Amazon SageMaker 仪表板上观察训练作业的进度:
此仪表板还显示了您模型的统计数据,包括 CPU 和 GPU 的使用情况,以及内存利用率。您还可以在此仪表板上观察我们正在训练的模型的训练和验证准确率。
由于我们只使用了两个 epoch,因此此模型的训练准确率较低:
您已成功使用 SageMaker 训练了一个图像分类模型。SageMaker 非常易于使用,您只需选择算法镜像,选择训练数据集,并设置算法的参数。SageMaker 将根据这些信息自动训练模型,并将模型存储在您的 S3 桶中。
使用 Amazon SageMaker 对图像进行分类
您已训练的 SageMaker 模型现在可用于预测图像中的对象。正如我们在本章开头所讨论的,SageMaker 提供了一个市场,您可以直接使用许多模型来执行您的任务。
- 由于我们训练了自己的机器学习模型,我们必须创建一个 SageMaker 模型,该模型可用于预测。以下代码展示了如何在 Amazon Sagemaker 中生成一个可用的模型。
import boto3
from time import gmtime, strftime
sage = boto3.Session().client(service_name='sagemaker')
model_name="example-full-image-classification-model"
info = sage.describe_training_job(TrainingJobName=job_name)
model_data = info['ModelArtifacts']['S3ModelArtifacts']
hosting_image = get_image_uri(boto3.Session().region_name, 'image-classification')
primary_container = {
'Image': hosting_image,
'ModelDataUrl': model_data,
}
create_model_response = sage.create_model(
ModelName = model_name,
ExecutionRoleArn = role,
PrimaryContainer = primary_container)
要在 SageMaker 中创建一个模型,我们必须指定在之前步骤中生成的模型名称。在我们的例子中,模型名称被设置为example-full-image-classification-model。我们还需要指定模型将存储的容器。由于我们使用了图像分类 Docker 镜像来生成此模型,我们必须将其指定为一个参数。此镜像将帮助 SageMaker 读取训练好的模型并定义如何用于预测。
create_model函数将创建模型并返回模型的Amazon 资源名称(ARN)。这可以用于调用模型以生成预测。
对于测试,我们将从Caltech256数据集下载原始图像并将它们存储在一个S3桶中。我们将使用这些图像来生成预测:
!wget -r -np -nH --cut-dirs=2 -P /tmp/ -R "index.html*" http://www.vision.caltech.edu/Image_Datasets/Caltech256/images/008.bathtub/
batch_input = 's3://{}/image-classification-full-training/test/'.format(bucket)
test_images = '/tmp/images/008.bathtub'
!aws s3 cp $test_images $batch_input --recursive --quiet
一旦我们下载了所有图像并将它们存储在 S3 桶中,我们就会指定运行批量预测任务的参数。这个任务将预测图像中 256 个对象中每个对象存在的概率:
timestamp = time.strftime('-%Y-%m-%d-%H-%M-%S', time.gmtime())
batch_job_name = "image-classification-model" + timestamp
request = \
{
"TransformJobName": batch_job_name,
"ModelName": model_name,
"MaxConcurrentTransforms": 16,
"MaxPayloadInMB": 6,
"BatchStrategy": "SingleRecord",
"TransformOutput": {
"S3OutputPath": 's3://{}/{}/output'.format(bucket, batch_job_name)
},
"TransformInput": {
"DataSource": {
"S3DataSource": {
"S3DataType": "S3Prefix",
"S3Uri": batch_input
}
},
"ContentType": "application/x-image",
"SplitType": "None",
"CompressionType": "None"
},
"TransformResources": {
"InstanceType": "ml.p2.xlarge",
"InstanceCount": 1
}
}
print('Transform job name: {}'.format(batch_job_name))
print('\nInput Data Location: {}'.format(s3_validation))
正如您所猜测的,我们必须在ModelName参数中指定模型名称,在TransformInput参数中指定输入文件夹。我们还需要指定存储预测的output文件夹。我们必须在TransformResources参数中指定我们使用的实例类型,并在MaxConcurrentTransforms参数中指定要处理的文件的最大数量。
以下代码使用参数并启动create_transform_job:
sagemaker = boto3.client('sagemaker')
sagemaker.create_transform_job(**request)
您可以在 SageMaker 仪表板上的推理 | 批量转换作业部分监控您的转换作业。一旦任务完成,您就可以访问您指定的作为output文件夹的 S3 桶中的预测结果。
预测结果可以按照以下格式查看:
{
"prediction": [
0.0002778972266241908,
0.05520012229681015,
...
]
}
由于我们的模型有 256 个对象类别,输出指定了每个对象在图像中存在的概率。您可以在各种数据集上运行模型,以检查您的模型是否可以正确预测数据集中的对象。
SageMaker 提供了一个非常易于使用的服务,不仅可以训练深度学习模型,还可以在应用程序中使用它们来生成预测。尽管该服务非常直观,但当您在大数据集上使用预构建的模型生成预测时,SageMaker 的成本很高。根据正在开发的应用程序,数据科学家应该始终考虑使用此类服务时相比在 Apache Spark 自己的集群上构建相同模型所承担的整体成本。
摘要
在本章中,我们研究了 Amazon SageMaker 如何提供各种现成的机器学习模型来生成预测,以及可以用于训练您的模型的算法镜像。Amazon SageMaker 在您和设置自己的集群以训练和创建自己的机器学习模型这些杂乱的细节之间生成了一层抽象。Amazon SageMaker 仪表板还提供了一个存储您的训练模型和监控您的预测批量处理作业的地方。
您还可以使用 SageMaker 和自己的数据集训练自己的机器学习模型。我们展示了如何训练一个能够在图像中执行对象检测的机器学习模型。我们演示了如何将此模型部署在 SageMaker 上,并用于运行批量预测任务。您将能够使用这个模板在 Amazon SageMaker 上工作其他算法。
在这本书中,我们的目标是向您解释机器学习算法是如何工作的,以及您如何利用 Apache Spark、Tensorflow 和 SageMaker 等强大工具来部署大规模的训练和预测任务。
练习
-
对于前几章中提供的每个示例,在 Amazon SageMaker 市场中找到一个适用于解决该问题的算法。
-
Amazon SageMaker 还提供了一种创建端点以生成预测的服务。对于前面的示例,为训练好的模型创建一个端点,并为一张图片生成预测。
第四部分:集成现成的 AWS 机器学习服务
本节的目标是向读者介绍 AWS 提供的各种机器学习服务,以执行特定的机器学习任务。由于读者到本书的这一部分已经对机器学习有了一定的了解,他们将学习如何使用 AWS 提供的工具来执行图像识别和自然语言处理等机器学习任务。
本节包含以下章节:
-
第十章,使用 AWS Comprehend
-
第十一章,使用 AWS Rekognition
-
第十二章,使用 AWS Lex 构建对话界面
第十章:使用 AWS Comprehend
作为数据科学家,了解机器学习算法的工作原理非常重要。然而,构建自己的机器学习模型来执行某些任务可能并不高效,因为这需要大量的努力和时间来设计一个最优算法。在第十章 使用 AWS Comprehend、第十一章 使用 AWS Rekognition 和第十二章 使用 AWS Lex 构建对话界面 中,我们将探讨您可以在 AWS 中访问的 机器学习即服务(MLaaS)产品。这些产品允许您使用在 AWS 中预先训练的模型,无论是通过 AWS 仪表板还是 API 调用。
在本章中,我们将涵盖以下主题:
-
介绍 Amazon Comprehend
-
访问 Amazon Comprehend
-
使用 Comprehend 测试实体识别
-
使用 Comprehend 测试情感分析
-
使用 Comprehend API 实现文本分类
介绍 Amazon Comprehend
Amazon Comprehend 是 AWS 上提供的一项服务,它提供了自然语言处理(NLP)算法。NLP 是机器学习中的一个领域,它分析人类(自然)语言,并可以识别这些语言的多种属性。在我们之前的多数章节中,我们查看了一些结构化数据的示例。这些数据具有预定义的特征,并按观察值的行组织。然而,自然语言数据集更难以处理。这样的数据集被称为 非结构化数据集,因为特征的结构没有很好地定义。
因此,需要算法从文本文档中提取结构和信息。例如,自然语言中的单词是按照语法结构排列的。自然语言句子也有关键词,它们包含有关地点、人物和其他细节的更多信息。它们还有一个上下文,这非常难以学习,并且相同的单词根据它们的排列方式可能传达不同的含义。
NLP 领域研究如何处理这些文本文档并从中提取信息。NLP 不仅涉及对文档进行聚类和分类,还包括对数据进行预处理,以从文本中提取重要的关键词和实体信息。根据文本文档的领域,需要不同的预处理,因为书面文档的风格会变化。例如,医学和法律文本包含大量术语,并且结构良好。然而,如果您使用 NLP 算法处理 Twitter 数据,文本可能由语法较差的句子和标签组成。因此,根据数据的领域,您需要单独的过程来预处理数据以及如何训练模型。在训练 NLP 模型时通常需要领域专业知识。
AWS Comprehend 提供了训练机器学习模型和使用预训练模型执行自然语言处理任务的工具。它提供了实时仪表板来分析文本数据,同时也提供了使用其用户界面训练机器学习算法的工具。
在本章中,我们将探讨可以使用 AWS Comprehend 完成的四个自然语言处理任务。我们还将建议数据科学家何时应使用现成的工具,何时应投入时间构建自己的机器学习算法。
访问 AmazonComprehend
Amazon Comprehend 可在 AWS 控制台中使用。当您登录 AWS 管理控制台时,在 AWS 服务框中搜索 Amazon Comprehend。选择 Amazon Comprehend 将带您进入 AWS Comprehend 启动屏幕,如下面的截图所示:
当您到达此屏幕时,请单击“启动 Comprehend”,这将带您进入 AWS Comprehend 仪表板。您应该能够从该页面访问以下部分使用的算法。
使用 Comprehend 进行命名实体识别
命名实体识别(NER)是自然语言处理中的一个领域,它对非结构化文本中提到的命名实体进行标记。命名实体包括人名、地名、组织等名称。例如,考虑以下句子:
Tim Cook 前往纽约参加苹果商店的开业。
在这个句子中,有三个命名实体。Tim Cook 是一个人的名字,New York 是一个城市的名称(位置),Apple 是一个组织的名称。因此,我们需要一个能够检测这些实体的 NER 模型。请注意,Apple 是一个歧义名词,因为它可以是公司或水果的名称。NER 算法应理解术语使用的上下文,并据此识别。
AWS Comprehend 提供了一个很好的 NER 工具,可以用来识别实体。此工具可以通过他们的仪表板或使用他们的 API 在实时中使用。AWS Comprehend 检测以下实体:
-
商品:品牌名称
-
日期:不同格式的日期
-
事件:音乐会、节日、选举等的名称
-
位置:城市、国家等的名称
-
组织:公司和国有组织的名称
-
人物:人名
-
数量:用于量化数字的常用单位
-
标题:电影、书籍等的名称
要访问 AWS 仪表板中的 NER,请转到菜单中的实时分析选项卡。然后您可以在页面提供的文本框中添加输入文本。以下截图展示了 Amazon Comprehend 如何执行 NER 任务:
您可以看到,Amazon Comprehend 中的 NER 工具会自动标记句子中的实体。除了标记实体的类别外,它还给出了一个置信度分数。这个分数可以用来确定我们是否信任工具的结果。
亚马逊 Comprehend 中的 NER 工具也可以通过 AWS 提供的 API 进行访问。
以下代码展示了如何调用 Comprehend 工具来获取实体分数:
import boto3
import json
comprehend = boto3.client(service_name='comprehend')
text = "Tim Cook traveled to New York for an Apple store opening"
print(json.dumps(comprehend.detect_entities(Text=text, LanguageCode='en'), sort_keys=True, indent=4))
你使用的是boto3包,这是一个 Python 的 AWS 工具包。我们首先初始化 Comprehend 客户端,然后将我们的文本传递给客户端以获取包含有关命名实体信息的 JSON 响应。在以下代码块中,我们可以看到我们从客户端收到的响应:
{
"Entities": [
{
"Score": 0.9999027252197266,
"Type": "PERSON",
"Text": "Tim Cook",
"BeginOffset": 0,
"EndOffset": 8
},
{
"Score": 0.992688775062561,
"Type": "LOCATION",
"Text": "New York",
"BeginOffset": 21,
"EndOffset": 29
},
{
"Score": 0.9699087738990784,
"Type": "ORGANIZATION",
"Text": "Apple",
"BeginOffset": 37,
"EndOffset": 42
}
]
}
因此,解析 JSON 可以让我们了解文本中的实体信息。
你还可以在 AWS Comprehend 中使用自定义 NER 算法,通过左侧菜单中的自定义实体识别选项进行训练。你可以添加训练样本文档和实体标注列表。算法会自动学习如何在正确的上下文中标记这些实体,并更新现有模型。
NER 算法在各种应用中被应用。它们的一个重要应用领域是新闻聚合。你可以自动为文档生成标签,以便用户可以根据文档中的实体进行搜索。NER 在推荐算法领域也非常有用,其中 NER 用于检测关键词,我们可以创建一个新闻推荐算法。我们可以构建一个协同过滤模型,推荐关于当前文章读者可能感兴趣的实体的文章。
使用 Comprehend 进行情感分析
情感分析算法分析文本并根据文本中的情感或观点对其进行分类。情感分析检测文本中表达的主观观点。例如,亚马逊市场中的评论给出了对产品的良好或不良评价。使用情感分析,我们可以检测评论是正面还是负面。我们还可以识别评论中的情感细微差别,例如评论者对特定产品是愤怒、兴奋还是中立。在这个社交媒体时代,我们有大量途径来表达我们对产品、电影、政治等的观点。数据科学家使用情感分析算法分析大量数据,并根据非结构化文本数据提取关于某个实体的观点。
亚马逊 Comprehend 通过提供实时仪表板来分析文本中的情感,使得情感分析任务变得简单。你可以像访问 NER 算法一样访问情感分析仪表板。我们将提供两个示例,说明 Comprehend 如何对我们的数据进行情感分析。我查看了两篇亚马逊的评论,一篇是正面的,另一篇是负面的,并使用 Comprehend 对它们进行了情感分析。考虑以下截图中的第一个示例:
在这个例子中,评论者使用了诸如失望之类的词语。这些术语具有负面含义。然而,情感分析算法可以检测到用户在这个词之前也使用了负面词汇,并正确预测这段文本具有积极的情感。同样,考虑以下例子:
您可以看到,评论者最初对产品感到满意,但后来出现了问题。因此,评论者对产品不满意。因此,情感分析算法正确预测评论为负面的置信度为 70%。然而,它还预测在这篇评论中存在一些混合情感,并提供了 22% 的置信度。我们使用 softmax 方法对具有最高置信度的情感进行像素化。
情感分析也可以通过 Amazon API 获取。在这里,我们提供了示例代码,展示了如何使用 boto3 Python 包调用情感分析 API:
import boto3
import json
comprehend = boto3.client(service_name='comprehend')
text = " It worked fine for the first 6 weeks, then I lost battery power I turned the phone off at night while charging, did not help. Then every else started to fail."
print(json.dumps(comprehend.detect_sentiment(Text=text, LanguageCode='en'), sort_keys=True, indent=4))
此 API 调用返回以下 JSON,其中包含有关文本情感的资料:
{
"Sentiment": {
"Sentiment": "NEGATIVE",
"SentimentScore": {
"Positive": 0.03148878738284111,
"Negative": 0.6730570793151855,
"Neutral": 0.047707948833703995,
"Mixed": 0.24774616956710815
}
}
}
您可以使用 API 对大量评论进行分类,以检测给定产品的整体情感。
情感分析是一个非常强大的工具,公司用它来分析社交媒体数据,以检测对其产品的整体情感,并确定用户为何对其产品不满意。电影评论聚合器,如烂番茄,也使用它来检测评论是正面还是负面,以便它们可以对它们进行分类并生成汇总评分。
使用 Comprehend 进行文本分类
文本分类是将文本文档分类到类别中的过程。与我们在第二章使用朴素贝叶斯分类 Twitter 流和第六章分析访问模式以提供建议中研究的分类算法类似,文本分类算法也基于标记的训练观察结果生成模型。然后,分类模型可以应用于任何观察结果以预测其类别。此外,我们在前几章中研究的相同算法,如第二章使用朴素贝叶斯分类 Twitter 流、第三章使用回归算法预测房屋价值和第四章使用基于树的预测用户行为,也可以用于文本分类。
文本数据是无结构数据。因此,我们需要从文本文档中生成特征,以便这些特征可以作为我们分类模型的输入。对于文本数据集,特征通常是文档中的术语。例如,考虑以下句子:
蒂姆·库克前往纽约参加苹果商店的开业。
让我们考虑该文档的类别为Technology。此句子将被翻译成以下结构化数据:
Tim Cook | traveled | to | New York | Apple | Store | Opening | Microsoft | Google | Class |
|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | Technology |
每个术语都将被视为数据集中的特征。因此,对于包含许多文档的大型数据集,特征集可以与该语言的词汇表一样大。特征值根据该术语是否存在于该文档中设置为0或1。由于我们的示例包含诸如Tim Cook和New York之类的单词,这些特征的观察值设置为1。由于 Microsoft 和 Google 这两个术语在句子中不存在,这些特征的值设置为0。Class变量设置为Technology。
在本节中,我们将展示如何在 Comprehend 上逐步训练自定义分类器的方法。我们将使用一个流行的文本分类数据集20 Newsgroups来生成一个机器学习模型,该模型可以标记评论为正面或负面。数据集可以从archive.ics.uci.edu/ml/datasets/Twenty+Newsgroups下载。
数据集可以下载为单独的文本文件,这些文件组织在 20 个文件夹中。每个文件夹的名称代表文件夹中文档的类别。该数据集是一个公开可用的数据集,它包含被分类到以下类别的新闻文章:
-
alt.atheism -
comp.graphics -
comp.os.ms-windows.misc -
comp.sys.ibm.pc.hardware -
comp.sys.mac.hardware -
comp.windows.x -
misc.forsale -
rec.autos -
rec.motorcycles -
rec.sport.baseball -
rec.sport.hockey -
sci.crypt -
sci.electronics -
sci.med -
sci.space -
soc.religion.christian -
talk.politics.guns -
talk.politics.mideast -
talk.politics.misc -
talk.religion.misc
您可以使用以下步骤来训练分类器:
- 第一步是将数据下载并预处理成 Comprehend 工具可读的格式。Comprehend 要求训练数据以以下 CSV(逗号分隔值)格式:
| 类别 | 文档 |
|---|
因此,一旦您下载了数据集,将其转换为上述格式并上传到您的 S3 存储桶。
- 您可以在 Comprehend 仪表板的左侧自定义标签页下访问自定义分类工具。要训练模型,您必须点击“训练分类器”选项。请注意,Comprehend 允许您在此仪表板上训练您的机器学习模型并将它们存储起来,以便您将来使用。
当您点击“训练分类器”选项时,您将看到以下截图:
- 为分类器命名并选择文档的语言。添加存储训练 CSV 文档的 S3 位置。选择正确的角色后,您可以给分类器添加相关标签,这有助于您在将来搜索它们。一旦您添加了所有信息,请点击训练分类器:
- 您将被带回到仪表板屏幕,您将看到分类器训练正在进行。一旦训练完成,分类器的状态将被标记为已训练:
- 然后,您可以点击分类器查看模型的评估指标。如您所见,我们的分类模型准确率为 90%:
- 由于我们现在有一个经过训练的分类器,您可以使用此模型对任何文档进行预测。我们创建一个包含 100 个文档的
test.csv文件,以从该模型获取预测。为了开始预测过程,请点击前一个屏幕上显示的“创建作业”选项。
这将带您进入另一个屏幕,您可以在其中添加有关您想要用于测试的文件以及输出应存储位置的详细信息:
在创建分析作业的屏幕上,添加有关要使用的分类器的详细信息:输入数据存储的位置(在 S3 上)以及输出存储的 S3 位置。您可以指定每行一个文档或每个文件一个文档的输入数据,并将输入数据指向包含所有文件的目录。在我们的示例中,由于test.csv文件每行包含一个文档,我们使用该格式。
- 一旦您点击创建作业,它将自动对文档进行分类并将输出存储在输出位置。输出以 JSON 格式存储,其中
output文件的每一行都包含对该行的分析。
以下是一个生成的输出示例:
{
"File": "test_2.csv",
"Line": "0",
"Classes": [
{
"Name": "alt.atheism",
"Score": 0.8642
},
{
"Name": "comp.graphics",
"Score": 0.0381
},
{
"Name": "comp.os.ms-windows.misc",
"Score": 0.0372
},
...
{
"Name": "talk.religion.misc",
"Score": 0.0243
}
]
}
因此,您可以看到我们的模型将输入文件的第一行标记为"alt.atheism",置信度为 86.42%。
您还可以使用 Amazon Comprehend API 创建文档分类器和预测作业:
import boto3
client = boto3.client('comprehend')
response = client.create_document_classifier(
DocumentClassifierName='20NG-test',
DataAccessRoleArn='Data Access ARN value',
InputDataConfig={
'S3Uri': 's3://masteringmlsagemaker/comprehend/train.csv'
},
OutputDataConfig={
'S3Uri': 's3://masteringmlsagemaker/comprehend/'
},
LanguageCode='en')
运行此函数将自动生成我们在前一步骤中创建的相同分类器。您可以从“我的安全凭证”页面上的角色选项卡访问您的 ARN 值。这是我们在第 3 步中创建的相同 IAM 角色的 ARN 值。输出数据配置位置将自动获取分类器评估的混淆度量,响应字符串将返回如下:
{
'DocumentClassifierArn': 'string'
}
字符串将是标识分类器的 Amazon 资源名称。您还可以使用 API 运行预测作业。以下代码可以用于生成输入文件的预测:
import boto3
client = boto3.client('comprehend')
response = client.start_document_classification_job( JobName='Testing Model', DocumentClassifierArn='<ARN of classifier returned in the previous step>', InputDataConfig={ 'S3Uri': 's3://masteringmlsagemaker/comprehend/test.csv', 'InputFormat': 'ONE_DOC_PER_LINE' }, OutputDataConfig={ 'S3Uri': 's3://masteringmlsagemaker/comprehend/', }, DataAccessRoleArn='<Data Access ARN value>')
上述代码将启动与我们在仪表板上创建的完全相同的分类作业。因此,您可以控制何时使用某个分类器,并根据需要在不同数据集上生成预测。函数的响应将是作业的状态。作业还将生成一个作业 ID,您可以使用describe_document_classification_job()函数 ping 该 ID 来检查作业的状态。
因此,我们已经在 AWS 上使用 Comprehend 工具生成了一个自定义文档分类器。这些工具将帮助您快速创建这些分类器,无需担心选择哪些分类算法、如何调整参数等问题。亚马逊会根据其研究团队的专长自动更新 Comprehend 使用的算法。然而,主要缺点是如果您在大数据集上运行操作,Comprehend 工具可能会很昂贵,因为它们按预测收费。您可以在aws.amazon.com/comprehend/pricing/访问 AWS Comprehend 的定价信息。
摘要
在本章中,我们研究了如何在 AWS 中使用内置的机器学习工具 Comprehend。我们简要讨论了自然语言处理(NLP)领域,并介绍了其子领域,如命名实体识别(NER)和情感分析。我们还研究了如何使用 Comprehend 提供的仪表板创建自定义文档分类器。此外,我们还研究了如何使用 Python 中的boto3包访问 Comprehend 的 API。
这些工具非常吸引人,因为它们将帮助您快速创建复杂的机器学习模型,并开始将它们应用于您的应用程序中。现在,对 NLP 领域只有初步了解的数据科学家现在可以训练复杂的机器学习模型,并使用它们做出最优决策。然而,大多数数据科学家面临的问题是,这些工具提供的定价是否比使用 Python 包自行构建算法更经济。请注意,Comprehend 通过让数据科学家关注底层集群配置,在数据科学家和机器学习模型之间添加了一层抽象。根据我们的经验,我们在项目的快速原型阶段使用这些工具来评估产品。如果我们决定投入生产,很容易计算出使用 AWS 工具与自行构建算法并在我们的集群上维护它们之间的成本差异。
我们将在下一章介绍亚马逊 Rekognition。这项服务用于图像识别,是对象检测和类似应用的即用型解决方案。
练习
-
您的任务是使用亚马逊 Comprehend 提供的 API 在大型数据集上执行命名实体识别(NER)。使用 Kaggle 竞赛中提供的标注 NER 数据集在 Comprehend 中创建自定义实体识别(
www.kaggle.com/abhinavwalia95/chemdner-iob-annotated-chemical-named-etities)。 -
在 Kaggle 的 Yelp 数据集上应用情感分析,然后评估您的预测是否与评论评分相匹配(
www.kaggle.com/yelp-dataset/yelp-dataset)。
第十一章:使用 AWS Rekognition
我们在第七章,实现深度学习算法,和第九章,使用 SageMaker 进行图像分类和检测中研究了深度学习算法及其使用 SageMaker 的实现方法。你一定已经意识到训练一个好的卷积神经网络(CNN)需要大量的专业知识和资源。此外,它还需要大量的带有对象的标记图像。亚马逊提供了一个现成的图像识别解决方案,称为Amazon Rekognition,它提供了各种使用预训练图像识别模型的图像识别工具。
在本章中,我们将涵盖以下主题:
-
介绍 Amazon Rekognition
-
实现对象和场景检测
-
实现面部分析
介绍 Amazon Rekognition
使用深度学习构建图像识别模型非常具有挑战性。首先,你需要一个大型、标记的数据集来训练深度学习模型以执行特定任务。其次,你需要了解如何设计网络和调整参数以获得最佳精度。最后,在规模上训练这样的深度学习模型需要昂贵的基于 GPU 的集群来训练这些模型。
Amazon Rekognition (aws.amazon.com/rekognition/) 是 AWS 提供的一个工具,它提供了已经预训练并可用于你应用程序中的图像识别模型。Amazon Rekognition 模型基于对数十亿个视频和图像的分析。类似于 Amazon Comprehend 提供作为服务的 NLP 模型,Rekognition 提供了各种可以执行特定任务的图像识别模型。使用 Amazon Rekognition 的优势在于,你可以简单地使用仪表板和 API 以高精度执行图像识别任务,而无需训练此类机器学习模型所需的高级专业知识。
Amazon Rekognition 只提供有限数量的模型,用于执行特定任务。在本节中,我们将查看 Amazon Rekognition 仪表板中可用的各种工具。我们还将探讨如何使用 Python 中的 AWS API 访问这些功能。
实现对象和场景检测
对象和场景检测算法可以识别图像中的各种对象,并为每个预测分配置信度。此算法使用标签层次结构来标记对象,并在检测到对象时返回所有叶节点。对象检测是图像识别的经典应用。它使我们能够识别图像中的内容并将其标记。例如,考虑一个新闻编辑室,摄影师每天提交数百张图片和视频。你需要有人标记这些图片,以便如果你希望访问在车祸中拍摄到的名人图像,这些图像库可以搜索。
目标检测允许您自动标记这些图像,以便它们可以高效地存储、组织和检索。目标检测算法的一个关键特性是它们必须全面,并且应该能够检测大量对象。此外,此类算法还检测对象的边缘,并且应该能够返回对象的边界框。Amazon Rekognition 有效地执行这两项任务。
您可以通过 AWS 控制台访问 Amazon Rekognition 仪表板。只需在搜索栏中搜索 Rekognition,您就可以访问 Amazon Rekognition 的演示。演示向您展示了工具的工作方式,但如果您想分析多个图像,则需要使用 API。
一旦您进入演示屏幕,选择对象和场景检测以访问一个演示,您可以在其中选择单个图像并检测对象中的图像。
为了演示的目的,我使用了芝加哥河上的渡轮截图:
如前一个截图所示,目标检测工具返回了图像中对象的排名列表以及检测的置信度。正如我们之前提到的,由于该工具使用类别层次结构,它可能会在顶部检测到相似类别。例如,它能够检测图像中的船。然而,它还以相同的置信度分数返回了车辆和交通类别。我们还可以看到,演示显示了它检测到的图像中每个对象的边界框。
然而,使用此工具进行目标检测可能很繁琐,因为它一次只能处理一张图像。因此,我们还可以使用 API 来访问 Amazon Rekognition 工具。您需要将图像上传到 S3 存储桶中的一个文件夹,以便在它们上执行目标检测。
以下 Python 代码可以用于对图像执行相同的操作:
import boto3
import json
client = boto3.client('rekognition')
response = client.detect_labels(
Image={
'S3Object':
{
'Bucket': 'masteringmlsagemaker',
'Name': 'ImageRecognition/chicago_boats.JPG'
}
},
MaxLabels=5,
MinConfidence=90
)
print(json.dumps(response, sort_keys=True, indent=4))
图像必须在 S3 存储桶中,并且您必须指定存储桶名称和图像名称作为请求函数的参数。响应很长,所以我们只显示了响应 JSON 中第一个预测的格式:
{
"LabelModelVersion": "2.0",
"Labels": [
{
"Confidence": 99.86528778076172,
"Instances": [
{
"BoundingBox": {
"Height": 0.29408860206604004,
"Left": 0.5391838550567627,
"Top": 0.6836633682250977,
"Width": 0.25161588191986084
},
"Confidence": 99.86528778076172
},
{
"BoundingBox": {
"Height": 0.11046414822340012,
"Left": 0.23703880608081818,
"Top": 0.6440696120262146,
"Width": 0.07676628232002258
},
"Confidence": 99.5784912109375
},
{
"BoundingBox": {
"Height": 0.040305182337760925,
"Left": 0.5480409860610962,
"Top": 0.5758911967277527,
"Width": 0.04315359890460968
},
"Confidence": 77.51519012451172
}
],
"Name": "Boat",
"Parents": [
{
"Name": "Vehicle"
},
{
"Name": "Transportation"
}
]
},
...
]
}
如您从响应中观察到的,我们在图像中发现了三个**Boat**对象的实例。响应提供了图像中找到的每个对象的边界框。此外,您还可以观察到最右侧的船很小,因此检测它的置信度远低于图像中的其他两艘船。响应还返回了对象在层次结构中的父级。因此,如果您有数百张图像需要分类,您可以将它们全部添加到 S3 存储桶中,并使用此代码遍历它们并检测这些对象的标签。由于有像 Amazon Rekognition 这样的工具,数据科学家现在可以访问世界级的深度学习模型,并将它们应用于他们正在构建的工具中。然而,这种对象检测算法仅适用于有限数量的对象。例如,我们尝试在这个工具中使用癌症 X 光图像的算法,但它无法返回任何结果。如果您正在开发一个非常专业的产品,试图检测肿瘤的医学图像或太空望远镜的图像,您将需要根据大量标记的图像训练自己的模型。
实现面部分析
Amazon Rekognition 还提供了一种强大的工具,可以对图像执行面部分析。它可以根据观察图像预测有趣的属性,如年龄和性别。它还可以从该模型检测到微笑或该人是否戴眼镜等特征。这样的模型将通过分析大量标记的面部图像,并训练一个图像识别模型来识别这些特征来训练。我们在第七章实现深度学习算法中研究的 CNN 模型非常适合此类应用,因为它可以使用局部感受野方法从图像中自动生成特征图,并检测包含这些面部特征的框。
面部分析演示可以像对象检测演示一样访问。为了测试模型,我们选择了莱昂纳多·达·芬奇的蒙娜丽莎画像。关于这幅画的一个长期谜团是画中的女士是否在微笑。
在下面的屏幕截图中,我们可以看到面部分析演示如何从图像中提供面部特征:
面部分析模型确实预测图像中存在面部,并围绕它创建了一个正确的框。它正确地预测了图像是女性,并为该人预测了年龄范围。它预测图像中的人没有在微笑。它还正确地预测了该人没有戴眼镜。
您也可以通过 API 调用访问相同的信息。
使用以下 Python 代码,您可以执行与前面演示中相同的面部分析任务:
import boto3
import json
client = boto3.client('rekognition')
response = client.detect_faces(
Image={
'S3Object': {
'Bucket': 'masteringmlsagemaker',
'Name': 'ImageRecognition/monalisa.jpg'
}
},
Attributes=['ALL']
)
print(json.dumps(response, sort_keys=True, indent=4))
你必须将你的图片存储在 S3 存储桶中,并向 API 调用提供存储桶和图片名称。你还可以指定需要返回的属性,或者指定All以获取所有属性。
此调用的响应以 JSON 格式呈现,如下所示:
{
"FaceDetails": [
{
"BoundingBox": {
"Width": 0.22473210096359253,
"Height": 0.21790461242198944,
"Left": 0.35767847299575806,
"Top": 0.13709242641925812
},
"AgeRange": {
"Low": 26,
"High": 43
},
"Smile": {
"Value": false,
"Confidence": 96.82086944580078
},
"Gender": {
"Value": "Female",
"Confidence": 96.50946044921875
},
"Emotions": [
{
"Type": "CALM",
"Confidence": 34.63209533691406
},
{
"Type": "SAD",
"Confidence": 40.639801025390625
}
],
"Landmarks": [
{
"Type": "eyeLeft",
"X": 0.39933907985687256,
"Y": 0.23376932740211487
},
{
"Type": "eyeRight",
"X": 0.49918869137763977,
"Y": 0.23316724598407745
},
"Confidence": 99.99974060058594
}
]
}
我们已编辑此响应以保持简洁。然而,你可以观察到,你可以看到关于年龄、性别和微笑的信息,正如我们在演示中所见。然而,它还识别出脸上的情绪,如悲伤和平静。它还定位了脸上的地标,如眼睛、鼻子和嘴唇。
这些工具在当前智能手机中使用,微笑可以触发拍照。它们在餐厅的消费调查中使用,以衡量餐厅中人们的统计数据以及他们是否对服务满意。
其他 Rekognition 服务
Amazon Rekognition 还提供其他图像识别服务。你可以使用本章中的示例来访问这些服务。我们将在此列出一些服务及其应用。
图片审查
我们可以使用 Rekognition 来监控图片并检查内容是否具有暗示性或不安全。此类技术用于调节实时视频服务,如 Twitch 或 Facebook Live,其中人工智能(AI)可以自动检测不安全内容。由于 YouTube 或 Instagram 等服务每天上传的数据量巨大,使用此类 AI 技术可以帮助降低平台监管的成本。
以下截图显示了图片审查工具如何检测图片中的暗示性主题并自动标记它们:
明星识别
识别还可以用于自动检测图片或视频中的名人。这可以通过从标记的图片和视频中学习的图像识别模型来完成。深度学习算法可以自动提取面部特征,然后进行比较以预测可能是哪位名人。例如,Amazon Prime 等服务上的许多电影和电视节目可以使用此技术显示屏幕上的演员名字。手动标记这些场景中的演员名字可能是一项非常繁琐的任务;然而,深度学习算法可以自动完成这项工作。
在以下示例中,Amazon Rekognition 检测到一张杰夫·贝索斯的图片并将其正确标记:
面部比较
明星识别技术可以进一步扩展,用于面部比较和检测相似的面部。例如,您的 Facebook 账户会自动将您上传的图片中的面部与您的朋友匹配,并自动标记图片。他们使用这样的图像识别算法来训练每个面部的模型,并在您上传的图片上运行这些模型以检测您的朋友是否在图片中。Amazon Rekognition 还提供了一种名为 面部比较 的功能,该功能比较两张图片中的面部,并检测是否有人在两张图片中都出现。
在以下截图中,我们可以观察到面部比较算法可以自动匹配两张图片中的面部并检测哪些面部彼此相似:
Amazon Rekognition 还提供另一种可以检测图片中文本的工具。此模型与我们第八章实现 AWS 上的 TensorFlow 深度学习中构建的类似,我们的模型能够检测数字。此工具在读取现实世界中的文本也非常有用。例如,Google Translate 这样的应用可以分析相机图像并将它们翻译成您的母语。自动驾驶汽车也可以使用这项技术来读取路标并做出相应反应。
以下截图显示了 Amazon Rekognition 如何检测图像中的文本:
该识别在此图片上工作不准确,但能够框选并重新创建此图片中的文本。
在本节中,我们没有提供这些服务的代码示例。API 调用与我们在本节前两个工具中讨论的内容类似。我们鼓励您尝试这些服务的 API 调用并测试它们的工作方式。
摘要
Amazon Rekognition 允许数据科学家通过 API 调用访问高质量的图像识别算法。使用深度学习最大的障碍之一是生成大量数据集和运行昂贵的基于 GPU 的集群来训练模型。AWS Rekognition 使用户能够更容易地访问这些功能,而无需具备训练此类模型所需的专业知识。应用开发者可以专注于构建功能,而无需花费大量时间在深度学习任务上。在本章中,我们研究了 Amazon Rekognition 中可用的各种工具,还学习了如何进行 API 调用和读取响应 JSON。此外,我们还研究了这些工具可能有用的一些应用场景。
在下一章中,我们将演示如何使用名为 Amazon Lex 的服务构建自动聊天机器人。
练习
-
使用 Python 创建一个应用程序,你可以传递一张团体照片,并检测当时房间的情绪。提供基于面部分析工具的代码检测细节,以及你是如何总结结果以在照片中找到情绪的。
-
创建一个工具,用于识别电影剪辑中的演员,并给出演员出现在屏幕上的时间。
第十二章:使用 AWS Lex 构建对话界面
机器学习最受欢迎的应用之一是聊天机器人;它们可以像人类一样与你交谈,并理解你的指令。这些聊天机器人使用自然语言处理(NLP)来解析指令,并根据你的问题返回查询或答案。亚马逊提供了一种名为Lex(它是Alexa的简称)的服务,你可以用它构建复杂的聊天机器人,这些机器人可以执行各种任务。
在本章中,我们将介绍以下主题:
-
介绍 Amazon Lex
-
使用 Amazon Lex 构建自定义聊天机器人
介绍 Amazon Lex
Amazon Lex (aws.amazon.com/lex/) 提供的服务可用于创建对话机器人。对话机器人使用各种机器学习技术,如语音识别、NLP和深度学习。由于近年来这些领域的进步,对话机器人已成为我们日常生活中的常客。数百万人使用亚马逊 Alexa、谷歌助手、Siri 或 Cortana 作为对话设备来完成各种任务。这些设备可以执行简单的任务,例如告诉你天气、为你叫一辆 Uber、订购披萨,以及控制你的照明。许多企业也提供聊天机器人以提供客户支持。例如,互联网服务提供商 Verizon FIOS 提供了一种聊天机器人,可以根据与你的对话执行诸如指向正确的故障排除文档或根据你的聊天重置路由器等任务。许多公司也使用此类对话机器人进行自动电话呼叫(机器人电话),在这种情况下,很难判断另一端的通话者是否不是真人。
从零开始构建这样的对话机器人并不容易。正如我们在第十章 Working with AWS Comprehend 中学习的那样,自然语言并不遵循严格的语法结构,我们有多种方式来表达相同的意思。因此,对话机器人需要能够从自然语言查询中解析相关数据,并给出最可能的答案。例如,亚马逊 Echo 可以理解不同格式的查询,并发现哪些是最相关的信息可以展示给用户。首先,这样的设备需要理解语音并将其转换为机器可以理解的文本。其次,它们需要触发能够回答该问题的正确技能,并将用户输入呈现给该技能。一旦技能生成答案,它必须使用文本到语音转换器将其转换回语音。所有这些步骤都需要专门的、高质量的深度学习模型来执行这些任务。例如,亚马逊使用深度学习模型来确定其文本到语音转换器中单词之间的停顿。
虽然构建这样的对话机器人可能听起来是一项艰巨的任务,但 Amazon 还提供了您可以使用他们的模型来生成此类任务的服务。这项服务称为 Amazon Lex,您可以通过 AWS 控制台访问它。
使用 Amazon Lex 构建 custom chatbot
在本节中,我们将使用 Amazon Lex 构建一个简单的自定义对话机器人。要访问 Amazon Lex 控制台,只需进入 AWS 控制台并搜索此服务。一旦到达控制台,您将有一个选项来创建一个新的机器人。您可以构建可以处理特定任务的独立机器人。在本例中,我们提供了以下步骤来创建一个用户可以要求在指定时间从特定餐厅订购食物的机器人:
- 要开始,请点击控制台上的“创建机器人”选项。您将能够访问以下截图:
您必须在屏幕上指定机器人名称以及测试机器人时想要选择的语音。您还可以指定会话超时时间,这样离开订单未完成并离开机器的人就不会有其他人继续他们的聊天会话的风险。在本例中,我们创建了一个自定义机器人。然而,您也可以访问示例机器人来测试服务并了解这些机器人是如何创建的。
- 当您点击创建按钮时,您将被带到下一个屏幕,您需要输入有关您的机器人如何工作的信息。首先,您必须指定您的特定机器人在聊天屏幕中是如何被触发的。在我们的案例中,用户有多种方式可以让聊天窗口知道他们饿了,因此您应该添加应该触发您的机器人的查询示例。这些查询在 Amazon Lex 中被称为utterances。我们添加了以下将触发我们的机器人的 utterances:
-
Amazon Lex 将使用机器学习来扩展 utterances 列表,这样如果用户提出“你能订购一些食物”这样的问题,我们的机器人仍然会被触发,因为 utterance 与我们指定的相似。
-
一旦我们指定了将触发我们的机器人,我们必须指定机器人启动时会发生什么。您可以使用 AWS 上的 Lambda 函数执行特定任务,或者使用控制台来设计聊天。由于设计 Lambda 函数不在本书的范围内,我们将使用控制台来询问用户他们想订购什么。以下屏幕选项显示了我们可以添加的预期用户信息:
-
我们定义了三个变量,我们希望我们的聊天机器人能够获取这些变量的输入。例如,我们想知道他们想要从哪家餐厅订购,他们想要订购什么,以及他们希望食物准备好的时间。Amazon Lex 提供了预构建的槽位(变量类型),您在获取输入时可以选择。例如,AMAZON.Food 槽位类型将尝试确保变量的值是食物类型,而 AMZON.Time 变量类型将确保添加的时间是有效的时间。
-
当我们的机器人有了所有必需变量的信息后,您必须指定机器人将如何响应。在我们的例子中,为了保持简单,我们只需告诉用户我们已经订购了食物(请注意,此代码实际上并没有订购食物)。如果您正在构建一个真正的订购食物的应用程序,您还可以调用一个 lambda 函数,该函数可以运行与变量名称相关的自定义代码。以下截图显示了如何添加有关机器人响应的信息,以及确认屏幕:
- 如果用户确认,您可以使用以下 Fulfillment 选项向用户发送感谢信息:
- 当您填写完表格后,您可以使用屏幕上的构建选项来构建您的机器人。如果您在屏幕上犯了任何错误,构建会提示您修复它们。最后,一旦您成功构建了机器人,您可以通过选择右侧的测试聊天机器人选项来测试它。以下屏幕显示了我们的聊天机器人是如何工作的。如您所见,我们能够与我们的聊天机器人进行聊天,并(假装)从它那里订购食物,如下面的截图所示:
通过在真实机器学习模型和用户之间添加一层抽象层,Amazon Lex 使得创建聊天机器人对每个人都非常容易。您可以专注于构建最适合您需求的机器人,而无需担心背后的算法。由于 Amazon Lex 是一项服务,AWS 会根据您对其机器学习模型的调用次数向您收费。
此外,您可以通过每个机器人的操作下拉菜单中的“导出”选项轻松地将 Amazon Lex 模型导出到 Alexa 技能套件。因此,通过使用 Amazon Lex,您可以在几分钟内设计聊天机器人并将其发布供 Alexa 使用。Amazon Lex 还提供了 API,您可以使用这些 API 来构建机器人,以便您可以使用代码更新或编辑您的语句或槽位。请参阅 boto3 API 文档(boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lex-models.html)以了解如何使用 API。对 API 的调用使用与我们第十章“与 AWS Comprehend 一起工作”和第十一章“使用 AWS Rekognition”中提供的示例类似的代码。
摘要
Amazon Lex 使得构建对话机器人对每个人来说都更容易、更易于访问。对话机器人使用许多机器学习模型来为用户提供快速回答。Amazon Lex 提供了一个图形界面,您可以在其中指定您的机器人应该响应哪些语句,应该收集哪些信息槽位,以及机器人应该向用户提出哪些确认问题。此类工具可以直接在仪表板上进行测试,正如我们在上一节中所展示的那样。
作为数据科学家,需要构建能够让客户惊叹的应用程序,使用 Amazon Comprehend、Rekognition 和 Lex 等工具快速构建这些原型是一个好方法。然而,当大规模使用时,这些服务可能会变得昂贵。在这种情况下,我们总是致力于使用 Apache Spark 或 SageMaker 等框架构建我们自己的模型。
在下一章中,我们将学习如何设置新的 AWS 集群,并探讨如何根据您的任务选择正确的集群的细微差别。
练习
-
创建一个聊天机器人,根据用户提供的信息提供航班状态。
-
创建一个能够回答各种与天气相关的问题的聊天机器人。
第五部分:通过 AWS 优化和部署模型
在掌握 AWS 上机器学习各种工具的使用后,读者必须掌握的一个重要步骤是优化这些模型并将它们部署到生产环境中。在本部分书中,我们讨论了使用 AWS 工具训练的机器学习模型如何进行优化并准备好在生产环境中部署。
本节包含以下章节:
-
第十三章,在 AWS 上创建集群
-
第十四章,在 Spark 和 SageMaker 中优化模型
-
第十五章,调整集群以适应机器学习
-
第十六章,在 AWS 上构建模型的部署
第十三章:在 AWS 上创建集群
机器学习中的一个关键问题是如何在多台机器上扩展和并行化学习。无论你是训练深度学习模型,这些模型对硬件的使用非常重,还是仅仅为了创建预测而启动机器,选择合适的硬件配置对于成本考虑和运行时性能都是至关重要的。
在本章中,我们将涵盖以下主题:
-
选择您的实例类型
-
分布式深度学习
选择您的实例类型
在第四章“使用基于树的算法预测用户行为”和其他章节中,我们不得不启动 EMR 集群和 SageMaker 实例(服务器)来进行学习和模型服务。在本节中,我们讨论了不同实例类型的特性。在本章中,你可以找到 AWS 提供的所有支持的实例类型,请参阅aws.amazon.com/ec2/instance-types/。
根据手头的任务,我们应该使用不同的实例类型。例如,我们可能需要一个带有 GPU 而不是 CPU 的实例类型来进行深度学习。当在 Apache Spark 上启动大型迭代提取、转换和加载(ETL)(即数据转换作业)时,我们可能需要大量的内存。为了使用户更容易操作,AWS 已经将实例分类为针对不同用例的系列。此外,AWS 为每个系列不断提供新的硬件配置。这些被称为代。通常,新一代提供了比上一代更好的性能。然而,旧一代通常仍然可用。反过来,每个系列在计算和内存能力方面都有不同大小的机器。
最常用的系列如下:
-
计算优化型(C 系列)
-
内存优化型(M 系列)
-
加速计算型(P 系列)
-
存储优化型(I 系列)
-
通用型(R 系列)
每个优化目标都有其他系列,但在之前的列表中,我们列出了每个系列中最常用的一个。每个系列可能具有不同的配置。下表显示了 C 系列和 M 系列的一些配置。每个配置都有不同的价格。例如,在撰写本文时,AWS us-east-1 区域第五代 xlarge 和 C 系列机器的价格为每小时 0.085 美元。正如您所看到的,在给定的价格水平上,用户可以选择支付具有更多内存和较少计算能力的配置,或者相反。下表中的**内存(GB)**列显示的是千兆字节的值,而 vCPUs 是虚拟机中的处理能力单位,由 AWS 测量。表中的价格只是参考价格,对应于 2019 年 3 月 AWS 弗吉尼亚数据中心区域的价格。目前,AWS 按机器开启的每秒钟收费(即,尽管价格显示为每小时金额,但机器可以运行 120 秒,用户只需支付相应的小时价格的一部分):
| 模型 | vCPU | 内存(GB) | On-demand 价格(us-east-1 区域) |
|---|---|---|---|
| c5.large | 2 | 4 | $0.085 per hour |
| c5.xlarge | 4 | 8 | $0.17 per hour |
| c5.2xlarge | 8 | 16 | $0.34 per hour |
| c5.4xlarge | 16 | 32 | $0.68 per hour |
| m5.large | 2 | 8 | $0.096 per hour |
| m5.xlarge | 4 | 16 | $0.192 per hour |
| m5.2xlarge | 8 | 32 | $0.384 per hour |
给定配置的价格可能会因多种因素而变化,具体如下:
-
机器的区域(数据中心)
-
实例是否请求为 spot 或 on-demand
-
使用预留定价
On-demand 与 spot 实例定价
On-demand 是请求云中机器最灵活的方式。on-demand 实例的价格是固定的,一旦启动机器,就保证其持续运行(除非发生错误或 AWS 正在实验容量问题,这极为罕见)。另一方面,spot 定价基于拍卖。AWS 有一系列过剩容量机器,通常以低于 on-demand 的价格拍卖。为了获得这样的机器,在启动时,用户需要指定他或她愿意为这样的实例支付多少。如果当前市场价格低于出价价值,机器将成功配置。一旦市场价格超过出价,机器就可以从用户那里收回。因此,如果您使用 spot 定价,您需要知道机器可以在任何时候关闭。话虽如此,根据我们的经验,spot 定价对于大规模(数千台机器)生产工作负载可以可靠地成功。重要的是要适当地选择出价价格和机器配置,并准备好根据 spot 市场价格的变化不时地更改这些配置。
在以下链接中,您可以检查不同地区和可用区域(这些是区域内的独立隔离数据中心)中每种实例类型的市值:console.aws.amazon.com/ec2sp/v1/spot/home?region=us-east-1#
上述图表显示了 2019 年 3 月至 2 月期间 c5.4xlarge 机器的市场价格。读者可能会注意到,区域 us-east-1d 的市场价格似乎比其他地区低。这意味着,只要可能,您可以在该区域以较低出价请求 spot 实例。
目前,SageMaker 不支持 spot 定价,只允许按需实例。此外,还有针对 SageMaker 支持的实例的不同价格表,您可以通过以下链接找到:aws.amazon.com/sagemaker/pricing/。SageMaker 可以用于不同的事情(笔记、训练作业、批量转换作业、端点等),因此有不同的价格。
对于 弹性映射减少(EMR),它确实支持 spot 实例。然而,当通过 EMR 启动时,会额外增加原始实例类型成本的一小部分。
预留定价
如果您提前准确估计了您的计算需求,可以降低成本。在这种情况下,您可以预先支付 AWS 并获得按需实例的显著折扣。例如,如果您计划在一年内为 m5.xlarge 机器花费 1,000 美元,您可以选择预先支付 1,000 美元并获得 40% 的节省。您预付的越多,节省率就越高。
详细信息可以在以下链接中找到:aws.amazon.com/ec2/pricing/reserved-instances/pricing/
亚马逊机器镜像(AMIs)
机器可以通过 弹性计算 服务(aws.amazon.com/ec2)直接在 EMR 或 SageMaker 外启动。当你想在 AWS 云上部署自己的应用程序或想自定义配置实例上可用的软件包时,这非常有用。通过 EC2 启动实例时,你可以选择一个 AMI,机器将带有为你的应用程序所需的所有库和软件包。你可以从一个正在运行的实例创建自己的 AMI,以便稍后重用或通过 Docker 规范。然而,AWS 提供了几个预配置的 AMI,这些 AMI 对于深度学习非常有用。我们强烈建议您通过此链接查看可用的 AMI:aws.amazon.com/machine-learning/amis/。这些 AMI 包括最常见的机器学习软件包(如 TensorFlow、Anaconda 和 scikit-learn),以确保不同库版本之间的兼容性(通常是一个棘手的问题)。这些 深度学习 AMI 通常被称为 DLAMIs。
深度学习硬件
AWS 中的大多数实例类型都是基于 CPU 的。CPU 实例通常适用于执行各种顺序任务。然而,加速计算实例类型(例如,P 或 G 系列)是基于 图形处理单元(GPU)。这些最初在游戏机上流行的实例类型,最终被证明非常适合深度学习。GPU 的特点是拥有比 CPU 更多的核心,但处理能力较低。因此,GPU 能够快速并行处理简单的指令。
尤其是 GPU 允许进行非常快速和并行的矩阵乘法。回想一下 第七章,实现深度学习算法,深度学习涉及将权重与不同层输入的信号相乘,就像向量点积一样。实际上,矩阵乘法涉及同时进行多个列和行的点积。矩阵乘法通常是深度学习中的主要瓶颈,而 GPU 极擅长执行此类操作,因为有机会并行进行大量计算。
在以下表格中,我们可以看到用于深度学习的典型机器配置及其相关特性。当涉及到分配深度学习工作负载时,GPU 数量和网络性能尤其重要,我们将在以下章节中讨论:
| 模型 | GPU | vCPU | Mem (GiB) | GPU Mem (GiB) | 网络性能 |
|---|---|---|---|---|---|
| p3.2xlarge | 1 | 8 | 61 | 16 | 最高 10 吉比特 |
| p3.8xlarge | 4 | 32 | 244 | 64 | 10 吉比特 |
| p3.16xlarge | 8 | 64 | 488 | 128 | 25 吉比特 |
弹性推理加速
2018 年,AWS 宣布了一个新功能,允许我们通过网络以 GPU 实例的一小部分成本将通过基于 GPU 的加速设备附加的常规实例组合起来。详细信息可以在 docs.aws.amazon.com/sagemaker/latest/dg/ei.htm 找到。
分布式深度学习
让我们接下来探索 分布式深度学习 概念。
模型与数据并行化
当训练大量数据或网络结构巨大时,我们通常需要在不同的机器/线程之间分发训练,以便可以并行执行学习。这种并行化可能发生在具有多个 GPU 的单台机器内,或者通过网络在不同机器之间同步。分发深度学习工作负载的两种主要策略是数据并行化和模型并行化。
在数据并行化中,我们使用相同的权重(即相同的模型)并行运行多个小批量。这意味着在一系列运行中同步不同小批量的权重。合并不同并行运行权重的策略之一是将每个并行小批量产生的权重进行平均。使用允许以分布式方式合并梯度而不需要中央合并器的算法(如 AllReduce)来平均每个机器或线程的梯度是一种有效的方法。其他替代方案包括托管一个参数服务器,该服务器充当同步权重的中央位置。
模型并行化,另一方面,涉及不同的线程或机器在并行处理相同的 mini-batch 的同时,分发实际的处理。正在运行的算法需要能够在不同的线程中分发工作。这种方法通常在具有多个 GPU 且共享高速总线的机器上运行良好,因为模型并行化通常只需要在每个前向传递后同步每个层的输出。然而,这种同步可能涉及的数据量可能比数据并行化中的权重同步多或少,这取决于网络的结构。
分布式 TensorFlow
TensorFlow 本地支持在具有多个 GPU 的单台机器上使用 AllReduce 进行数据并行化。TensorFlow 中通过 TensorFlow 分发学习的算法是一个活跃的开发领域。
例如,我们可以启动一个具有多个 GPU 的笔记本实例:
在这个例子中,我们有一个四 GPU 机器。让我们看看我们如何修改代码来适应我们在 第八章 中考虑的回归估计器,在 AWS 上使用 TensorFlow 实现深度学习。回想一下,我们使用了 LinearRegressor 来解决我们的房屋价值估计问题。为了在 GPU 之间启用分布式学习,我们需要定义一个分布策略。
最简单的是MirroredStrategy,它使用 AllReduce 技术。这种策略被实例化并作为输入提交给回归器,正如我们在下面的代码块中所示:
distribution = tf.contrib.distribute.MirroredStrategy(num_gpus=4)
config = tf.estimator.RunConfig(train_distribute=distribution)
tf_regressor = tf.estimator.LinearRegressor(
config=config,
optimizer=tf.train.GradientDescentOptimizer(learning_rate=0.0000001),
feature_columns=[tf.feature_column.numeric_column('inputs',
shape=(11,))],
)
目前,分布策略支持接受学习率作为输入的GradientDescentOptimizer。此外,与我们在第八章,在 AWS 上使用 TensorFlow 实现深度学习中所做的方法相比,提供输入函数的方式需要稍作改变。在分布式处理中,输入函数需要返回我们通过pandas的as_matrix()函数获得的张量创建的tf.Dataset:
def training_input_fn():
return tf.data.Dataset.from_tensor_slices(
({'inputs': training_df[training_features].as_matrix()},
training_df[label].as_matrix())).repeat(50).batch(1)
训练方式与我们在第八章,在 AWS 上使用 TensorFlow 实现深度学习中做的一样:
tf_regressor.train(input_fn=training_input_fn)
在train_distributed_tensorflow.ipynb笔记本中,你可以看到完整的示例。在这个特定的玩具示例中,分布式学习并不合理。然而,它应该为读者提供一个参考,因为目前关于如何在多 CPU 环境中成功进行训练的文档和示例并不多。
通过 Apache Spark 进行分布式学习
在前面的章节中,我们展示了如何通过 Spark ML 库使用 Apache Spark 进行分布式机器学习。然而,如果你想要将 Apache Spark 与 TensorFlow 等深度学习库结合使用,则可以获得显著的好处。
数据并行化
在这个方案中,相同的迷你批次在 Spark 执行器中并行运行(类似于映射转换),权重被平均(类似于归约操作)。例如,SparkFlow (github.com/lifeomic/sparkflow)这样的工具允许我们定义一个简单的 TensorFlow 模型(例如我们在第八章,在 AWS 上使用 TensorFlow 实现深度学习)中开发的模型)并通过让 Spark 驱动器充当参数服务器来执行并行训练。通过这个库,我们可以使用作为 TensorFlow 图智能包装器的管道抽象(估计器和转换器)。同样,BigDL (bigdl-project.github.io)允许我们使用allreduce 随机梯度下降(SGD)实现来分布深度学习训练。
模型并行化
在撰写本章时,没有本机库允许我们通过 Apache Spark 使用 TensorFlow 进行模型并行化。然而,Apache Spark 确实提供了一个多层感知器分类器(MLPC)(spark.apache.org/docs/latest/ml-classification-regression.html#multilayer-perceptron-classifier)的实现,该实现通过 Apache Spark 实现模型并行化。与 TensorFlow 等库的强大功能相比,这种实现相对简单。例如,网络结构和激活函数是固定的。你只能定义层数和少数其他参数。尽管如此,由于你的数据管道已经在 Spark 中,这是一个开始分布式深度学习的良好方式。
分布式超参数调整
通过拥有一个 Spark 集群,可以在不同的机器上训练同一神经网络的变体。这些变体可能具有不同的超参数,甚至稍微不同的网络结构。例如,你可能想要切换特定层的激活函数。如果我们事先预定义所有这些神经网络的组合,Spark 可以通过简单的map()转换来执行。每个并行训练作业都可以返回生成的模型以及损失指标。例如,sparkdl库(github.com/databricks/spark-deep-learning)提供了执行此类任务的良好工具(特别是如果你正在处理图像)。我们将在第十五章调整集群以进行机器学习中更详细地介绍超参数调整。
分布式大规模预测
一旦我们有了序列化的模型,就可以通过将模型发送到不同的执行器并将它应用于 Spark 分布式数据来并行地进行预测。例如,sparkdl库实现了 Keras 转换器,它可以在给定 Keras 模型(如我们在第八章在 AWS 上使用 TensorFlow 实现深度学习中开发的模型)的情况下进行分布式预测。
SageMaker 中的并行化
在上一节中确定的大多数用例也可以仅通过使用SageMaker轻松解决。使用 SageMaker,我们可以启动多个实例,执行不同模型的并行训练变体。SageMaker 的许多内置算法都是设计用来执行模型并行化的,这就是为什么我们通常指定用于训练的机器数量(和类型)。此外,它还提供了高级参数调整功能,我们将在第十五章“调整集群以适应机器学习”中探讨。最后,分布式预测是通过批量转换作业完成的,例如我们在第四章“使用基于树的预测用户行为”中展示的。
摘要
在本章中,我们介绍了关于如何选择训练集群机器类型的基本考虑因素。这些考虑因素包括在成本、内存大小、计算能力和供应限制之间进行权衡。至于深度学习,我们提供了一个如何在 SageMaker 笔记本上运行分布式 TensorFlow 的具体示例,以及一些如何在 EMR 上通过 Apache Spark 进一步分布式你的深度学习管道的指南。在下一章“在 Spark 和 SageMaker 中优化模型”中,我们将深入探讨从模型准确性的角度调整我们的模型以实现最佳性能的问题。
第十四章:在 Spark 和 SageMaker 中优化模型
在 AWS 上训练的模型可以通过修改训练指令或超参数进行进一步优化。在本章中,我们将讨论读者可以使用来提高其算法性能的各种技术。
在本章中,我们将涵盖以下主题:
-
模型优化的重要性
-
自动超参数调整
-
Apache Spark 和 SageMaker 中的超参数调整
模型优化的重要性
很少有算法在第一次尝试就能产生优化的模型。这是因为算法可能需要数据科学家进行一些参数调整以提高其准确性或性能。例如,我们在第七章中提到的学习率,实现深度学习算法,对于深度神经网络需要手动调整。低学习率可能导致算法运行时间更长(如果我们是在云上运行,那么成本更高),而高学习率可能会错过最优的权重集。同样,具有更多层的树可能需要更多时间来训练,但可能创建一个具有更好预测能力的模型(尽管它也可能导致树过度拟合)。这些指导算法学习的参数被称为超参数,与模型参数(例如,网络的权重)不同,这些参数在整个训练过程中并没有被学习。一些超参数不仅用于优化或调整模型,还用于定义或约束问题。例如,簇的数量也被视为超参数,尽管这并不是真的关于优化,而是用于定义要解决的问题。
调整这些超参数以获得最佳性能并非易事,在许多情况下,它需要理解手头的数据以及底层算法的工作方式。那么,为什么不学习这些超参数呢?许多数据科学家使用调整这些超参数值的算法来查看它们是否会产生更准确的结果。这种方法的缺点是,我们可能会找到在测试数据集上最优的超参数,而我们可能会认为我们的模型具有更好的准确性,当我们只是过度拟合测试数据集时。因此,我们通常将数据集分为三个部分:训练数据集,用于训练模型;验证数据集,用于参数调整;测试数据集,仅在参数调整完成后用于评估模型的最终准确性。
自动超参数调整
调整超参数的最简单方法被称为网格搜索。我们为每个超参数定义我们想要尝试的不同值。例如,如果我们正在训练树,我们可能想要尝试深度为 5、10 和 15。同时,我们还想看看最佳杂质度量是否是信息增益或基尼系数。这总共创建了六种组合,需要测试以确定准确性。正如你可能预料到的,组合的数量将随着要考虑的超参数数量的指数增长。因此,其他技术被用来避免测试所有可能的组合。一种简单的方法是随机化要尝试的组合。一些组合可能会被遗漏,但一些变化将在没有归纳偏差的情况下被测试。
AWS SageMaker 提供了一个智能选择要测试的超参数的调优服务。在网格搜索和随机化中,每次训练运行都不会使用先前运行中获得的准确性信息。SageMaker 使用一种称为贝叶斯优化的技术,能够根据先前测试组合的准确性值选择下一组要测试的超参数组合。该算法背后的主要思想是在超参数空间上构建一个概率分布。每次我们获得给定组合的准确性时,概率分布都会调整以反映新的信息。成功的优化将利用已知组合的信息,这些组合产生了良好的准确性,以及对新组合的充分探索,这些新组合可能导致潜在改进。你会欣赏这是一个极其困难的问题,因为每次训练运行都很慢,可能也很昂贵。我们通常负担不起测试太多的组合。
Apache Spark 中的超参数调整
回想一下我们来自第三章的回归问题,预测房价的回归算法,其中我们构建了一个线性回归来估计房屋的价值。在那个阶段,我们为超参数使用了一些任意值。
在下面的代码块中,我们将展示 Apache Spark 如何测试elasticNetParam、regParam和solver的 18 种不同的超参数组合:
from pyspark.ml.tuning import CrossValidator, ParamGridBuilder
from pyspark.ml import Pipeline
linear = LinearRegression(featuresCol="features", labelCol="medv")
param_grid = ParamGridBuilder() \
.addGrid(linear.elasticNetParam, [0.01, 0.02, 0.05]) \
.addGrid(linear.solver, ['normal', 'l-bfgs']) \
.addGrid(linear.regParam, [0.4, 0.5, 0.6]).build()
pipeline = Pipeline(stages=[vector_assembler, linear])
crossval = CrossValidator(estimator=pipeline,
estimatorParamMaps=param_grid,
evaluator=evaluator,
numFolds=10)
optimized_model = crossval.fit(housing_df)
我们将像往常一样开始构建我们的分类器,不提供任何超参数。我们将回归器存储在linear变量中。接下来,我们通过定义一个参数网格来定义要测试的每个超参数的不同值。设置值的方法的函数引用被传递给ParamGridBuilder,它负责保持要测试的组合。
与往常一样,我们可以使用任何预处理阶段(在这种情况下,我们使用向量组装器)来定义我们的管道。CrossValidator接受管道、参数网格和评估器。回想一下,评估器被用来使用测试数据集获得一个特定的分数:
evaluator = RegressionEvaluator(labelCol="medv", predictionCol="prediction", metricName="r2")
在这种情况下,我们将使用与第三章,使用回归算法预测房价中相同的 R2 指标。CrossValidator在调用fit()时将运行所有组合,并找到实现最高 R2 值的超参数。
一旦完成,我们可以通过访问optimized_model.bestModel引用来检查底层最佳模型。通过它,我们可以展示在最佳模型中实际使用的超参数集:
[(k.name, v) for (k, v) in optimized_model.bestModel.stages[1].extractParamMap().items()]
上述语句的输出如下:
[('epsilon', 1.35),
('featuresCol', 'features'),
('predictionCol', 'prediction'),
('loss', 'squaredError'),
('elasticNetParam', 0.02),
('regParam', 0.6),
('maxIter', 100),
('labelCol', 'medv'),
('tol', 1e-06),
('standardization', True),
('aggregationDepth', 2),
('fitIntercept', True),
('solver', 'l-bfgs')]
然而,比实际使用的参数更有趣的是看到不同组合测试中的准确度变化。optimized_model.avgMetrics值将显示所有 18 个超参数组合的准确度值:
[0.60228046689935, 0.6022857524897973, ... 0.6034106428627964, 0.6034118340373834]
我们可以使用CrossValidator返回的optimized_model来使用最佳模型进行预测,因为它也是一个转换器:
_, test_df = housing_df.randomSplit([0.8, 0.2], seed=17)
evaluator.evaluate(optimized_model.transform(test_df))
在这种情况下,我们得到了一个 R2 值为 0.72,这比我们在第三章中,使用回归算法预测房价所得到的任意超参数集要好一些。
SageMaker 中的超参数调整
正如我们在上一节中提到的,自动超参数调整,SageMaker 有一个使用贝叶斯优化的智能参数调整库。在本节中,我们将展示如何进一步调整我们在第四章,使用基于树的预测用户行为中创建的模型。回想一下,在那个章节中,我们提出了一个二元分类问题,试图预测用户是否会点击广告。我们使用了xgboost模型,但在那个阶段我们还没有进行任何参数调整。
我们将首先创建 SageMaker 会话并选择xgboost:
import boto3
import sagemaker
from sagemaker import get_execution_role
sess = sagemaker.Session()
role = get_execution_role()
container = sagemaker.amazon.amazon_estimator.get_image_uri('us-east-1', "xgboost", "latest")
s3_validation_data = 's3://mastering-ml-aws/chapter4/test-vector-csv/'
s3_train_data = 's3://mastering-ml-aws/chapter4/training-vector-csv/'
s3_output_location = 's3://mastering-ml-aws/chapter14/output/'
接下来,我们定义估计器,就像我们在第四章,使用基于树的预测用户行为中所做的那样:
sagemaker_model = sagemaker.estimator.Estimator(container,
role,
train_instance_count=1,
train_instance_type='ml.c4.4xlarge',
train_volume_size=30,
train_max_run=360000,
input_mode='File',
output_path=s3_output_location,
sagemaker_session=sess)
sagemaker_model.set_hyperparameters(objective='binary:logistic',
max_depth=5,
eta=0.2,
gamma=4,
min_child_weight=6,
subsample=0.7,
silent=0,
num_round=50)
正如我们总是对 SageMaker 服务调用所做的那样,我们定义了训练和验证输入数据的位置和格式:
train_data = sagemaker.session.s3_input(s3_train_data, distribution='FullyReplicated',
content_type='text/csv', s3_data_type='S3Prefix')
validation_data = sagemaker.session.s3_input(s3_validation_data, distribution='FullyReplicated',
content_type='text/csv', s3_data_type='S3Prefix')
data_channels = {'train': train_data, 'validation': validation_data}
在定义了基础估计器和确定了输入数据后,我们现在可以构建一个训练作业,该作业将使用这个估计器,并运行一系列的训练作业,以改变超参数:
from sagemaker.tuner import HyperparameterTuner, ContinuousParameter,IntegerParameter
tree_tuner = HyperparameterTuner
(estimator=sagemaker_model,
objective_metric_name='validation:auc',
max_jobs=10,
max_parallel_jobs=3,
hyperparameter_ranges={'lambda':
ContinuousParameter(0, 1000),
'max_depth': IntegerParameter(3,7),
'eta':ContinuousParameter(0.1, 0.5)})
tree_tuner.fit(inputs=data_channels, logs=True)
SageMaker:创建名为xgboost-190407-1532的超参数调整作业
第一步是创建一个HyperparameterTuner实例,在其中我们设置以下内容:
-
基础估计器,超参数将在其上变化。
-
目标指标,它将被用来找到最佳的超参数组合。由于我们处理的是一个二元分类问题,在验证数据上使用曲线下面积指标是一个不错的选择。
-
我们希望为每个超参数测试的不同范围。这些范围可以使用
ContinuousParameter为连续变化的参数指定,或者使用IntegerParameter或CategoricalParameter为离散参数指定。 -
要运行的作业数量,以及并行运行的最大作业数量。这里在准确性和速度之间有一个权衡。您运行的并行作业越多,用于告知下一组尝试的超参数的数据就越少。这会导致范围搜索次优。然而,它将更快地完成调整。在这个例子中,我们只运行了 10 个作业。我们通常希望运行更多的作业以获得显著的改进。在这里,我们只展示一个低值,以便读者可以快速获得结果。
可以通过 AWS 控制台(console.aws.amazon.com/sagemaker/home?region=us-east-1#/hyper-tuning-jobs)或通过 Python SDK 中的方法来监控拟合过程,我们可以看到作业的状态。
一旦完成,AWS 控制台应该看起来像下面的截图;在其中,您可以查看已运行的不同的作业和获得的不同性能指标:
让我们使用 SDK 检查哪个训练作业产生了最佳性能。首先,要找到最佳作业的名称:
tree_tuner.best_training_job()
'xgboost-190407-1342-001-5c7e2a26'
使用会话对象中的方法,我们可以显示最优训练作业的超参数值:
sess.sagemaker_client.describe_training_job(TrainingJobName=tree_tuner.best_training_job())
前一个 describe 命令的输出如下:
{'TrainingJobName': 'xgboost-190407-1532-005-0e830ada',
'TrainingJobArn': 'arn:aws:sagemaker:us-east-1:095585830284:training-job/xgboost-190407-1532-005-0e830ada',
'TuningJobArn': 'arn:aws:sagemaker:us-east-1:095585830284:hyper-parameter-tuning-job/xgboost-190407-1532',
'ModelArtifacts': {'S3ModelArtifacts': 's3://mastering-ml-aws/chapter14/output/xgboost-190407-1532-005-0e830ada/output/model.tar.gz'},
'TrainingJobStatus': 'Completed',
'SecondaryStatus': 'Completed',
'HyperParameters': {'_tuning_objective_metric': 'validation:auc',
'eta': '0.4630125855085939',
'gamma': '4',
'lambda': '29.566673825272677',
'max_depth': '7',
'min_child_weight': '6',
'num_round': '50',
'objective': 'binary:logistic',
'silent': '0',
'subsample': '0.7'},....}
使用describe_hyper_parameter_tuning_job()方法,我们还可以获取最优 AUC 指标的最终值:
sess.sagemaker_client.describe_hyper_parameter_tuning_job(HyperParameterTuningJobName='xgboost-190407-1532')
以下输出是前一个命令的结果:
{'HyperParameterTuningJobName': 'xgboost-190407-1532',
'HyperParameterTuningJobArn': 'arn:aws:sagemaker:us-east-1:095585830284:hyper-parameter-tuning-job/xgboost-190407-1532',
'HyperParameterTuningJobConfig': {'Strategy': 'Bayesian',
'HyperParameterTuningJobObjective': {'Type': 'Maximize',
'MetricName': 'validation:auc'},
'ResourceLimits': {'MaxNumberOfTrainingJobs': 10,
'MaxParallelTrainingJobs': 3},
....
'FinalHyperParameterTuningJobObjectiveMetric': {'MetricName': 'validation:auc',
'Value': 0.6545940041542053},
'
...}
您应该探索完整的 API 和 Python SDK,以获取有关自动调整的完整功能集和选项。请查看:github.com/aws/sagemaker-python-sdk 我们希望这个介绍能帮助您开始了解如何微调模型。
摘要
在本章中,我们介绍了通过超参数优化进行模型调整的重要性。我们提供了在 Apache Spark 中进行网格搜索的示例,以及如何使用 SageMaker 的高级参数调整。
在下一章中,我们将专注于优化硬件和集群设置,这是我们训练和应用模型的基础。模型优化和硬件优化对于成功且成本效益高的 AI 流程都至关重要。
练习
-
关于寻找最佳超参数的方法,比较网格搜索、随机搜索和贝叶斯优化在超参数调整中的应用的优缺点。
-
为什么我们在进行超参数调整时通常需要三个数据分割?
-
您认为哪个指标最适合我们的
xgboost示例:validation:auc还是training:auc?