原文:
annas-archive.org/md5/daa8f16fb595e16a266512112b9ef347
译者:飞龙
第八章:第八章:创建一个端到端的机器学习工作流
在之前的章节中,我们学习了 Pachyderm 基础知识,以及如何在本地和云平台上安装 Pachyderm。我们已经部署了第一个管道,学习了如何更新管道,并执行了一些基本的 Pachyderm 操作,如拆分。我希望到现在为止,你已经相信 Pachyderm 是一个功能极其强大的工具,能够提供很多灵活性和处理机器学习管道的能力。为了让这一点更加明显,我们将部署一个比之前更复杂的示例,远超之前的任何部署。希望这一章能特别有趣,能让你更加深入理解数据基础设施的特性。
本章将部署一个多步骤的 自然语言处理 (NLP) 工作流,演示如何大规模使用 Pachyderm。
本章包括以下主题:
-
NLP 示例概述
-
创建仓库和管道
-
创建一个命名实体识别 (NER) 管道
-
重新训练一个命名实体识别 (NER) 模型
技术要求
本章要求您安装并配置以下组件。
对于本地 macOS 安装,您需要以下组件:
-
macOS Mojave, Catalina, Big Sur 或更高版本
-
Docker Desktop for Mac 10.14
-
minikube
v1.9.0 或更高版本 -
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
对于本地 Windows 安装,您需要以下组件:
-
Windows Pro 64 位 v10 或更高版本
-
Windows Subsystem for Linux (WSL) 2 或更高版本
-
Microsoft PowerShell v6.2.1 或更高版本
-
Hyper-V
-
minikube
v1.9.0 或更高版本 -
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
对于 Amazon 弹性 Kubernetes 服务 (Amazon EKS) 安装,您需要以下组件:
-
kubectl
v.18 或更高版本 -
eksctl
-
aws-iam-authenticator
-
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
对于 Microsoft Azure 云安装,您需要以下组件:
-
kubectl
v.18 或更高版本 -
Azure CLI
-
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
-
jq
1.5 或更高版本
对于 Google Kubernetes Engine (GKE) 云安装,您需要以下组件:
-
Google Cloud SDK v124.0.0 或更高版本
-
kubectl
v.18 或更高版本 -
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
云端或本地虚拟机的最低硬件要求如下:
-
CPU 数量:4
-
内存:8,192 MB
-
磁盘:20 GB
现在我们已经了解了完成本章任务所需的技术要求,我们可以部署一个具有足够资源的 Pachyderm 实例来运行本章描述的示例。
调整虚拟机参数
要运行本节描述的示例,您必须确保运行 Pachyderm 的虚拟机有足够的内存和 CPU,以满足管道的需求。无论是云环境还是本地环境,均适用此要求。
如果您在云平台上运行 Pachyderm,请确保已在符合本章技术要求部分中列出的最低硬件要求的虚拟机规格上部署了 Kubernetes。然后,按照第五章《在云平台上安装 Pachyderm》的描述重新部署您的 Pachyderm 集群。
如果您在本地计算机上通过minikube
运行 Pachyderm,请确保minikube
虚拟机足够大。如果您按照第四章《本地安装 Pachyderm》的描述部署了minikube
虚拟机,您需要删除它,并部署一个具有更大 CPU 和内存的新minikube
虚拟机。
为此,请完成以下步骤:
-
卸载旧的 Pachyderm 集群:
helm uninstall pachd
系统响应如下:
release "pachd" uninstalled
-
删除现有的
minikube
虚拟机:minikube delete
您应该看到以下系统响应:
Deleting "minikube" in docker ...
Deleting container "minikube" ...
…
-
删除旧机器后,启动一个新的虚拟机,并使用以下参数:
minikube start --cpus 4 --memory 8192
此命令返回以下响应:
…
Starting control plane node minikube in cluster minikube
Creating docker container (CPUs=4, Memory=8192MB) ...
…
Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
-
现在,按照第四章《本地安装 Pachyderm》的描述,重新部署您的 Pachyderm 集群。为了简单起见,以下是您需要运行的命令:
helm install --set deployTarget=LOCAL pachd ./pachyderm
您应该看到以下系统响应:
NAME: pachd
LAST DEPLOYED: Thu Aug 19 13:03:36 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
...
-
运行以下命令连接到
pachd
:pachctl config import-kube local --overwrite pachctl config set active-context local
现在我们已经部署了足够资源的 Pachyderm 来运行本章中的示例,让我们回顾一下我们将要创建的 NLP 管道。
NLP 示例概述
在本节中,我们将回顾端到端的机器学习工作流,帮助我们理解如何在 Pachyderm 中调度它。
为了演示此功能,我们将创建一个 NLP 管道,对*《沉睡谷的传说》这本由华盛顿·欧文*所写的短篇小说书进行各种文本优化。
但首先,让我们回顾一下什么是 NLP 以及 NLP 涉及的典型阶段。
自然语言处理简介
NLP 是一种机器学习技术,使您能够分析自然文本,即语音或书面文本。这一人工智能分支已经存在多年,但随着计算机和互联网技术的进步,它找到了新的应用方式。
那么,如何在您的商业或学术研究中使用自然语言处理(NLP)呢?有很多方法,但最常见的包括以下几种:
-
语音识别:使计算机能够理解人类语音的技术。
-
聊天机器人:能够回答问题并从提供的答案中学习的软件。旧版聊天机器人基于标准软件工程技术定义的规则,因此它们无法进化,只能产生平庸的回答。新型聊天机器人则更加先进。
-
机器翻译:自动化将文本和语音从一种语言翻译成另一种语言的技术。最常见的例子当然是谷歌翻译。
-
文本提取、总结和分类:在信息过载的世界中,这是一项非常需要的技术。NLP 使你能够创建提供文本洞察的管道,比如研究论文的摘要或页面上使用的关键词信息。
-
情感分析:一种帮助根据信息的正面或负面情感来分类的著名技术。这项技术最著名的应用是 Gmail 邮箱分类器,它将你的电子邮件分为三类:主邮件、促销邮件和社交邮件。
这些是 NLP 的主要示例。然而,这个列表并不完整。NLP 还被应用于生物信息学中分析基因数据,在金融领域用于理解市场事件和趋势,在医疗保健中用于理解患者信息,以及在许多其他领域。
现在我们知道了 NLP 适用的领域,让我们回顾一下构建 NLP 管道所涉及的主要阶段。
学习 NLP 阶段
正如我们在上一节讨论的那样,NLP 用于解决各种与文本和语音相关的任务。像机器学习的其他领域一样,当你需要解决一个 NLP 问题时,你需要构建一个管道。NLP 管道有几种定义,但通常情况下,NLP 管道的阶段包括以下内容:
-
文本预处理或 清理:这一阶段包括诸如单词和句子分割、标记化、去除停用词和标点符号、将单词转换为小写字母、词形还原或词干提取等操作。
-
结构分析:这一阶段深入分析文本的主题。它包括如词性(POS)标注、依赖解析和词块切分等操作。
-
特征提取:这一阶段主要是回答关于数据的特定问题,并找出文本实体之间的关系。它可能包括如命名实体识别(NER)和命名实体消歧(NED)或链接和情感分析等任务。
-
建模:这一阶段是你在训练数据上训练模型并进行测试,以便将其进一步投入生产环境。
根据你的使用案例,管道可能包含所有或部分这些阶段,而且这些阶段的顺序也可能不同。
以下图示展示了一个示例的自然语言处理管道:
图 8.1 – 自然语言处理管道
现在我们知道了 NLP 管道的阶段,让我们更仔细地看看我们将要实现的示例。
回顾 NLP 示例
在我们的示例中,我们将使用华盛顿·欧文的《睡谷传奇》文本,最终我们将创建并训练一个 NER 管道,帮助我们回答书中主要人物是谁的问题。为了创建这个多步骤的工作流,我们需要创建以下管道:
-
数据清洗管道:该管道将从提供的 URL 下载文本,并清除所有 HTML 标签、标题及其他无关内容。然后,它将对文本进行分词、去除停用词和标点符号,接着进行词干提取和词形还原。
-
POS 标注管道:该管道将基于单词在句子中的位置和上下文,为从前一个管道清洗过的文本添加词性标注。
-
NER 管道:该管道将使用一个预训练模型处理我们的文本,并尝试为结果标注正确的实体。
-
NER 训练管道:该管道将基于提供的训练数据训练一个新的 NER 模型,以纠正第一个 NER 管道的结果。
-
改进版 NER 管道:该管道将使用新的 NER 模型处理我们的文本,并将故事中的人物列表输出到一个文本文件。
这是我们完整 NLP 管道工作流的示意图:
图 8.2 – Pachyderm NLP 管道
现在我们已经回顾了管道步骤,让我们一步步创建所有需要的仓库和管道。
创建仓库和管道
在本节中,我们将创建在前一节中回顾过的所有管道。六步工作流程将清理数据,应用词性标注(POS),执行命名实体识别(NER),根据提供的数据训练一个新的自定义模型,运行改进的管道,并将结果输出到最终的代码库。
第一步是创建数据清洗管道,它将从文本中剥离我们在进一步处理时不需要的元素。
重要提示
你需要从github.com/PacktPublishing/Reproducible-Data-Science-with-Pachyderm/tree/main/Chapter08-End-to-End-Machine-Learning-Workflow
下载所有示例文件。Docker 镜像存储在hub.docker.com/repository/docker/svekars/nlp-example
。
创建数据清洗管道
数据清洗通常是在执行其他任务之前进行的。对于这个管道,我们创建了一个 Python 脚本,使用自然语言工具包(NLTK)平台来执行数据清洗任务。NLTK 是一个开源的库集合,能够完成各种 NLP 相关任务,包括分词、词干提取、去除停用词和词形还原。
这是我们将用于该管道的管道规范:
---
pipeline:
name: data-clean
description: A pipeline that tokenizes the text.
input:
pfs:
glob: "/data.txt"
repo: data
transform:
cmd:
- python3
- "/data-clean.py"
image: svekars/nlp-example:1.0
这个管道执行以下操作:
-
从
data
仓库中的data.txt
文件获取提供的 URL -
使用
svekars/nlp-example:1.0
镜像 -
运行
data-clean.py
脚本,该脚本已添加到svekars/nlp-example:1.0
镜像中
你可能注意到,在管道规格中使用的 glob 模式只处理一个文件——data.txt
。这个文件包含了指向位于Project Gutenberg: Free eBooks网站的*《睡谷传奇》*文本的 URL。要访问该网站,请前往gutenberg.org
。
现在我们已经回顾了管道,让我们更仔细地看看我们的脚本做了什么。以下是我们将在data-clean.py
脚本中导入的组件列表:
from bs4 import BeautifulSoup
from urllib.request import urlopen
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from nltk.stem.porter import PorterStemmer
nltk.download('wordnet')
from nltk.stem import WordNetLemmatizer
我们需要BeautifulSoup
来解析包含我们文本的 HTML 文件。我们使用urlopen
来打开data.txt
文件中的 URL。我们需要 NLTK 以及stopwords
、word_tokenize
、PorterStemmer
、WordNet
和WordNetLemmatizer
来执行各种 NLP 操作。
代码的第一部分打开我们已放置在数据仓库中的data.txt
文件,读取文件并使用BeautifulSoup
HTML 解析器解析文本。在paragraphs
行中,我们去除除了<p>
HTML 标签以外的所有 HTML 元素:
with open('/pfs/data/data.txt', "r", encoding='utf-8') as f:
data=f.read().replace('\n', '')
url = urlopen(data).read()
soup = BeautifulSoup(url, 'html.parser')
content = soup.get_text(strip=True)
paragraphs = soup.find_all('p')
脚本的第二部分将下载的文本保存到输出仓库中的文本文件。我们将需要下游管道:
f = open('pfs/out/text.txt', 'w', encoding='utf-8')
for i in paragraphs:
all_text = i.get_text()
f.writelines(all_text)
f.close()
在代码的下一部分中,我们使用word_tokenize
NLTK 方法将文本拆分为单独的标记,并将其保存到输出仓库中的tokens.txt
文件中:
tokens = []
for i in paragraphs:
tokens += word_tokenize(i.text)
with open('/pfs/out/tokens.txt', 'w', encoding='utf-8') as filehandle:
for item in tokens:
filehandle.write('%s\n' % item)
代码的下一部分将之前标记化的文本去除停用词,并将结果保存到输出仓库中的no_stopwords.txt
文件中。停用词是指包括冠词、代词等常用词,这些词对文本的价值较小,可以忽略,以节省处理时间:
stopwords = stopwords.words("english")
no_stopwords = []
for word in tokens:
if not word in stopwords:
no_stopwords.append(word)
appendFile = open('/pfs/out/no_stopwords.txt', 'a', encoding='utf-8')
appendFile.write(word)
appendFile.write("\n")
appendFile.close()
代码的下一部分移除已经进行过分词并去除停用词的文本中的标点符号。代码将结果保存到一个名为no_punctuation.txt
的单独文件中:
no_punctuation = []
for word in no_stopwords:
if word.isalpha():
no_punctuation.append(word)
appendFile = open('/pfs/out/no_punctuation.txt', 'a', encoding='utf-8')
appendFile.write(word)
appendFile.write("\n")
appendFile.close()
接下来是词干提取。grouping
和grouped
会被简化为group
。有时,这种技术可能被认为过于激进,可以使用词形还原来代替。词干化后的输出会保存到stemmed.txt
:
port_stem = PorterStemmer()
stemmed = []
for word in no_punctuation:
stemmed_word = port_stem.stem(word)
stemmed.append(stemmed_word)
appendFile = open('/pfs/out/stemmed.txt', 'a', encoding='utf-8')
appendFile.write(stemmed_word)
appendFile.write("\n")
appendFile.close()
脚本的最后一部分是词形还原,它使用 NLTK 的 WordNet Lemmatizer 数据库对已经标记化且去除了停用词和标点符号的文本进行词形还原。最后一段代码将结果保存到lematized.txt
文件中。我们将在下一个管道中使用该文件:
lemmatizer = WordNetLemmatizer()
lemmatized = []
for word in no_punctuation:
l_text = lemmatizer.lemmatize(word)
lemmatized.append(l_text)
appendFile = open('/pfs/out/lematized.txt', 'a', encoding='utf-8')
appendFile.write(l_text)
appendFile.write("\n")
appendFile.close()
现在我们知道了管道的功能,让我们来创建它。
要创建data-clean.py
管道,请完成以下步骤:
-
打开终端并验证 Pachyderm 是否正常运行:
pachctl version
系统输出如下:
COMPONENT VERSION
pachctl 2.0.0
pachd 2.0.0
-
创建数据仓库:
pachctl create repo data
-
验证数据仓库是否已创建:
pachctl list repo
系统输出如下:
NAME CREATED SIZE (MASTER) DESCRIPTION
data 10 seconds ago ≤ 0B
-
从你存放
data.txt
文件的目录,将其放入数据仓库:pachctl put file -f data.txt data@master
系统输出如下:
data.txt 49.00b / 49.00 b [==============] 0s 0.00 b/s
该文件只有一行——一个指向古腾堡网站上《沉睡山庄的传说》文本的链接:www.gutenberg.org/files/41/41-h/41-h.htm
。
-
检查文件是否已放入具有
file
类型的仓库:pachctl list file data@master
系统输出如下:
NAME TYPE SIZE
/data.txt file 49B
-
创建
data-clean
流水线:pachctl create pipeline -f data-clean.yaml
不会返回任何输出。
-
检查流水线是否已创建并且开始运行:
pachctl list pipeline
系统输出如下:
NAME VERSION INPUT CREATED STATE / LAST JOB DESCRIPTION
data-clean 1 data:/data.txt 4 seconds ago running / - A pipeline that tokenizes the text.
-
大约一分钟后,流水线应完成运行并将结果上传到输出仓库。请检查仓库:
pachctl list repo
你应该能看到以下输出:
NAME CREATED SIZE (MASTER) DESCRIPTION
data-clean 15 seconds ago ≤ 315.8KiB Output repo for pipeline data-clean.
data 1 minute ago ≤ 49B
如你所见,Pachyderm 自动创建了一个名为data-clean
的输出仓库,并将 315.8 KiB 的数据上传到该仓库的主分支。
-
让我们列出仓库中的文件:
pachctl list file data-clean@master
系统输出如下:
NAME TYPE SIZE
/lemmatized.txt file 42.25KiB
/no_punctuation.txt file 42.99KiB
/no_stopwords.txt file 47.88KiB
/stemmed.txt file 38.01KiB
/text.txt file 75.18KiB
/tokens.txt file 69.54KiB
-
你可以看到,流水线已经将六个文件上传到输出仓库。我们故意将它们保存在不同的文件中,这样你可以看到它们之间的差异。你可以查看每个文件的内容并进行比较。例如,在 macOS 上打开
lemmatized.txt
文件,可以运行以下命令:pachctl get file data-clean@master:/lemmatized.txt | open -f -a TextEdit.app
你应该能看到以下输出:
图 8.3 – 词形还原的单词
在本节中,我们已经创建了一个清理文本的流水线。接下来的章节,我们将创建下一个流水线,应用 POS 标签到我们的词形还原文本。
创建 POS 标注流水线
POS 标注是一种 NLP 技术,它为每个单词标记相关的词性。这个过程在许多 NLP 问题中都非常有用,比如文本歧义消解和文本转语音。
对于这项任务,我们使用了spaCy,这是一款免费的库,能够执行 POS 标注、命名实体识别(NER)等任务。例如,假设你有如下句子:
任何快乐的人都会让别人也感到快乐。
下面是使用 spaCy 进行 POS 标注的示例:
图 8.4 – POS 标注示例
我们将使用 spaCy 从data-clean
流水线中找到我们词形还原文本的 POS 标签。
下面是我们 POS 标注流水线规范的样子:
---
pipeline:
name: pos-tag
description: A pipeline that performs POS tagging.
input:
pfs:
glob: "/lemmatized.txt"
repo: data-clean
transform:
cmd:
- python3
- "/pos-tag.py"
image: svekars/nlp-example:1.0
该流水线执行以下操作:
-
从
data-clean
仓库获取lemmatized.txt
文件 -
使用
svekars/nlp-example:1.0
Docker 镜像 -
运行
pos-tag.py
脚本处理我们的词形还原文本 -
输出一张表格,其中包含在
pos_table.txt
文件中找到的所有 POS 标签,另有一份包含每种 POS 标签总数的文件pos_number.txt
,并创建一个依赖关系图,保存为pos-tag-dependency.svg
文件
现在我们已经回顾了流水线的工作内容,接下来让我们看看pos-tag.py
脚本。
该脚本导入了以下组件和库:
import spacy
import en_core_web_sm
from spacy import displacy
import IPython
from pathlib import Path
import spacy.attrs
from contextlib import redirect_stdout
我们需要spacy
及其模块来执行词性标注、统计它们并可视化结果。我们导入en_core_web_sm
预训练的 spaCy 模型来完成标注任务。我们需要 IPython 作为 spaCy 的依赖。最后,我们使用pathlib
和redirect_stdout
来保存结果。
代码的第一部分导入了一个名为en_core_web_sm
的预训练 spaCy 模型。词性标注要求你使用预训练模型或自定义模型。en_core_web_sm
模型在词性标注方面表现良好。因此,我们将直接使用它。然后,脚本打开我们的lematized.txt
文件,对文件中的所有单词进行词性标注,并将结果输出到pos-table.txt
文件:
sp = spacy.load('en_core_web_sm')
textfile = sp(open('pfs/data-clean/lematized.txt', "r", encoding='utf-8').read())
with open('/pfs/out/pos-table.txt', 'w') as f:
with redirect_stdout(f):
for word in textfile:
print(f'{word.text:{12}} {word.pos_:{10}} {word.tag_:{8}} {spacy.explain(word.tag_)}')
代码的下一部分统计处理过的文本中每个标签的数量,并将结果输出到pos-number.txt
文件:
with open('/pfs/out/pos-number.txt', 'w') as file:
with redirect_stdout(file):
count_tags = textfile.count_by(spacy.attrs.IDS['POS'])
for i, count in count_tags.items():
tags = textfile.vocab[i].text
print(tags, count)
最后,脚本的最后部分生成一个依赖图,并将其保存为 SVG 图像pos-tag-dependency.svg
:
image = displacy.render(textfile, style='dep', options={"compact": True, "distance": 70})
f = open('/pfs/out/pos-tag-dependency.svg', "w")
f.write(image)
f.close()
现在,让我们创建我们的管道。
要创建一个词性标注管道,请执行以下操作:
-
打开你的终端并验证 Pachyderm 是否已启动并运行:
pachctl version
你应该看到以下输出:
COMPONENT VERSION
pachctl 2.0.0
pachd 2.0.0
-
创建词性标注管道:
pachctl create pipeline -f pos-tag.yaml
没有返回系统输出。
-
检查管道是否已创建并正在运行:
pachctl list pipeline
这是此命令返回的输出:
NAME VERSION INPUT CREATED STATE / LAST JOB DESCRIPTION
pos-tag 1 data-clean:/lemmatized.txt 2 seconds ago running / - A pipeline that performs POS tagging.
data-clean 1 data:/data.txt 1 minute ago running / success A pipeline that tokenizes the text.
-
当管道运行完成后,检查输出仓库:
pachctl list repo
这个命令返回以下输出:
NAME CREATED SIZE (MASTER) DESCRIPTION
pos-tag 22 seconds ago ≤ 10.82MiB Output repo for pipeline pos-tag.
data-clean 2 minutes ago ≤ 315.8KiB Output repo for pipeline data-clean.
data 3 minutes ago ≤ 49B
Pachyderm 创建了一个名为pos-tag
的输出仓库,并将 10.82 MiB 文件上传到该仓库的主分支。
-
现在,让我们查看上传到输出仓库的文件:
pachctl list file pos-tag@master
这个命令返回以下系统输出:
NAME TYPE SIZE
/pos-number.txt file 132B
/pos-table.txt file 564.1KiB
/pos-tag-dependency.svg file 10.27MiB
-
让我们看看我们文本中每个标签的数量:
pachctl get file pos-tag@master:/pos-number.txt
你应该看到以下输出:
ADP 154
SPACE 6291
NOUN 3110
NUM 67
ADJ 789
PROPN 346
VERB 1053
ADV 374
DET 98
AUX 69
PRON 130
PART 9
SCONJ 59
CCONJ 15
INTJ 7
X 11
-
最后,让我们看看依赖图。如果你使用的是 macOS,请运行以下命令:
pachctl get file pos-tag@master:/pos-tag-dependency.svg > pos-tag-dependency.svg | open -f pos-tag-dependency.svg -a "Google Chrome"
Google Chrome 打开文件:
图 8.5 – 词性依赖图
重要提示
你可能需要在浏览器中向下滚动才能看到图表。由于我们对整本书进行了词性标注,这个图表非常长。你需要横向滚动才能查看完整内容。
在本节中,我们使用 Pachyderm 和 spaCy 配置了一个词性标注管道,并通过依赖图进行了可视化。接下来,我们将配置一个命名实体识别(NER)管道,帮助我们找到故事中的主要人物。
创建命名实体识别(NER)管道
命名实体识别(NER)是一种信息提取技术,它能够识别文本中的实体,并将它们归类为特定类别,如人物、地点和组织。例如,假设我们有以下短语:
Snap Inc.宣布 2021 年第一季度财务结果
如果你对这个短语使用 spaCy 的en_core_web_lg
模型,你将得到以下结果:
Snap Inc. - 0 - 9 - ORG - Companies, agencies, institutions, etc.
First Quarter 2021 - 20 - 38 - DATE - Absolute or relative dates or periods
名字识别可以在多种任务中派上用场。在本节中,我们将使用它来提取《沉睡谷传说》中的主要人物。
这是我们 NER 管道规范的样子:
---
pipeline:
name: ner
description: A NER pipeline
input:
pfs:
glob: "/text.txt"
repo: data-clean
transform:
cmd:
- python3
- "/ner.py"
image: svekars/nlp-example:1.0
这个管道执行以下操作:
-
从
data-clean
仓库获取The Legend of Sleepy Hollow的原始文本 -
使用
svekars/nlp-example:1.0
Docker 镜像 -
运行
ner.py
脚本 -
将结果输出到
ner
仓库
现在,让我们看看ner.py
脚本的功能。以下是脚本导入的组件列表:
import spacy
from spacy import displacy
from contextlib import redirect_stdout
我们需要spacy
来执行 NER 任务,displacy
模块用于可视化结果。redirect_stdout
是一种将打印输出重定向到文件的便捷方法。
其余的代码导入了 spaCy 的预训练模型en_core_web_lg
。这个模型似乎在 NER 任务上的表现优于我们在 POS 标注管道中使用的模型。接着,脚本从data-clean
仓库中的text.txt
文件中获取原始文本,并执行 NER 任务:
sp = spacy.load("en_core_web_lg")
def display_entities(text):
with open ('/pfs/out/ner-list.txt', 'w') as f:
with redirect_stdout(f):
if text.ents:
for i in text.ents:
print(i.text+' - '+str(i.start_char)+' - '+str(i.end_char)+' - '+i.label_+' - '+str(spacy.explain(i.label_)))
text = sp(open('/pfs/data-clean/text.txt', "r", encoding='utf-8').read())
display_entities(text)
最后,脚本使用displacy
可视化结果并将其保存为 HTML 格式:
with open ('/pfs/out/ner-labels.html', 'w') as f:
with redirect_stdout(f):
for i in text.ents:
html=displacy.render(text, style="ent", page=True)
print(html)
现在我们知道了脚本的功能,接下来我们来创建管道。
要创建 NER 管道,完成以下步骤:
-
打开终端,验证 Pachyderm 是否已启动并正在运行:
pachctl version
此命令返回以下输出:
COMPONENT VERSION
pachctl 2.0.0
pachd 2.0.0
-
创建 POS 标注管道:
pachctl create pipeline -f ner.yaml
没有返回系统输出。
-
检查管道是否创建并已开始运行:
pachctl list pipeline
你应该会看到以下输出:
NAME VERSION INPUT CREATED STATE / LAST JOB DESCRIPTION
ner 1 data-clean:/text.txt 5 seconds ago running / running A NER pipeline
pos-tag 1 data-clean:/lematized.txt 1 minutes ago running / success A pipeline that performs POS tagging.
data-clean 1 data:/data.txt 2 minutes ago running / success A pipeline that tokenizes the text.
-
当管道运行完毕后,检查输出仓库:
pachctl list repo
该命令返回以下系统输出:
NAME CREATED SIZE (MASTER) DESCRIPTION
ner 36 seconds ago ≤ 43.49MiB Output repo for pipeline ner.
pos-tag 2 minutes ago ≤ 10.82MiB Output repo for pipeline pos-tag.
data-clean 3 minutes ago ≤ 315.8KiB Output repo for pipeline data-clean.
data 4 minutes ago ≤ 49B
Pachyderm 创建了一个名为ner
的输出仓库,并上传了 43.49 MiB 的数据到主分支。
-
让我们看看上传到输出仓库的文件:
pachctl list file ner@master
返回以下输出:
NAME TYPE SIZE
/ner-labels.html file 43.47MiB
/ner-list.txt file 19.36KiB
该仓库有两个文件。一个是文件中找到的所有实体的列表,另一个是所有实体的可视化结果。
-
将
ner-list.txt
文件的前 10 行打印到终端:pachctl get file ner@master:/ner-list.txt | awk 'FNR <= 10'
此命令返回以下输出:
one - 36 - 39 - CARDINAL - Numerals that do not fall under another type
Hudson - 108 - 114 - LOC - Non-GPE locations, mountain ranges, bodies of water
Dutch - 186 - 191 - NORP - Nationalities or religious or political groups
the Tappan Zee - 203 - 217 - EVENT - Named hurricanes, battles, wars, sports events, etc.
St. Nicholas - 303 - 315 - ORG - Companies, agencies, institutions, etc.
Greensburgh - 417 - 428 - PERSON - People, including fictional
Tarry Town - 498 - 508 - GPE - Countries, cities, states
former days - 547 - 558 - DATE - Absolute or relative dates or periods
about two miles - 891 - 906 - QUANTITY - Measurements, as of weight or distance
first - 1330 - 1335 - ORDINAL - "first", "second", etc. NUM 67
如你所见,NER 模型已经识别了文本中的许多实体。让我们打开 HTML 文件,查看所有实体。
-
打开 HTML 文件:
pachctl get file ner@master:/ner-labels.html > ner-labels.html | open -f ner-labels.html -a "Google Chrome"
文件将在 Google Chrome 中打开:
图 8.6 – NER 标签
你可以看到,spaCy 模型已经正确地识别了文本中的许多实体。然而,如果你开始浏览,你会注意到它遗漏了一些实体。例如,在某些情况下,它没有将Headless Horseman标记为PERSON。这是预训练模型的一个已知准确性问题。在下一节中,我们将通过重新训练模型来修正这个问题,以便使用这些实体。
重新训练 NER 模型
NER(命名实体识别)管道结果的不准确性是一个常见问题。解决这个问题的唯一方法是重训练现有模型,或者从头开始完全训练一个新的模型。从头训练一个模型是一个既困难又耗时的操作。在我们的案例中,我们不一定需要训练一个完全新的模型,而是可以重训练现有模型,以理解缺失的上下文。为了完成这个任务,我们将把训练数据放入 data-clean
仓库,创建一个训练管道在该数据上进行训练,将模型保存到输出仓库中,然后再次运行重训练后的模型对原始文本进行处理。
在 Pachyderm 的术语中,这意味着我们将创建两个管道:
-
第一个管道,名为
retrain
,将训练我们的模型并将新模型输出到train
输出仓库。 -
第二个管道,名为
my-model
,将使用新模型分析我们的文本,并将结果上传到my-model
仓库。
现在,让我们创建重训练管道。
创建重训练管道
对于这个管道,我们将创建以下管道规范:
---
pipeline:
name: retrain
description: A pipeline that retrains the NER model.
input:
pfs:
glob: "/training-data.json"
repo: data
transform:
cmd:
- python3
- "/ner-improved.py"
image: svekars/nlp-example:1.0
这个管道采用包含我们训练数据的 training-data.json
文件,并运行 ner-improved.py
脚本以改进现有模型。结果将保存到 retrain
仓库中。对于这个示例,我们不需要太多的训练实例,但在实际使用中,你可能需要成百上千的示例来提高模型的准确性。
以下是 ner-improved.py
脚本导入的组件列表:
import spacy
import random
from spacy.util import minibatch
from spacy.training import Example
from contextlib import redirect_stdout
import simplejson as json
我们需要 spacy
库中的 minibatch
和 Example.from_dict
方法来训练模型。我们使用 random
来打乱文件顺序,以便更好地训练。需要 simplejson
Python 解码器来读取 JSON 格式的训练数据文件,并且需要 redirect_stdout
来保存输出结果。
脚本的下一部分加载 spaCy 模型,读取训练数据文件,并打开 spaCy NER 管道:
nlp=spacy.load("en_core_web_lg")
ner=nlp.get_pipe("ner")
data = open("/pfs/data/training-data.json")
data = json.loads(data.read())
这部分代码使用了一个优化器,它执行梯度下降计算。然后,脚本指定只需训练 NER 管道,忽略其他管道。接下来的 for
循环执行实际的训练,更新模型,并打印损失值。我们将训练模型 30 次:
optimizer = nlp.create_optimizer()
other_pipes = [p for p in nlp.pipe_names if p != "ner"]
with nlp.disable_pipes(*other_pipes):
for i in range(30):
random.shuffle(data)
losses = {}
for text, annotations in data:
doc = nlp.make_doc(text)
example = Example.from_dict(doc, annotations)
nlp.update([example], drop=0.1, sgd=optimizer, losses=losses)
print(losses)
脚本的最后一部分测试重训练管道在测试文本上的效果,并将结果输出到 ner-improved.txt
文件。重训练后的模型将使用 pickle.dump
保存在 output
仓库中的 ner-improved-model.p
文件目录中:
test_text = 'Headless Horseman came to see Ichabod Crane.'
doc = nlp(test_text)
with open ('/pfs/out/ner-improved.txt', 'w') as f:
with redirect_stdout(f):
for i in doc.ents:
print(i.label_, " -- ", i.text)
pickle.dump(nlp, open('/pfs/out/ner-improved-model.p', 'wb'))
现在,让我们创建管道:
-
打开终端并验证 Pachyderm 是否正在运行:
pachctl version
系统输出如下:
COMPONENT VERSION
pachctl 2.0.0
pachd 2.0.0
-
将
training-data.json
放入数据仓库:pachctl put file -f training-data.json data@master
-
创建重训练管道:
pachctl create pipeline -f retrain.yaml
没有系统输出返回。
-
检查管道是否已创建,并且正在启动或运行:
pachctl list pipeline
系统输出如下:
NAME VERSION INPUT CREATED STATE / LAST JOB DESCRIPTION
retrain 1 data:/training-data.json About a minute ago running / success A pipeline that retrains the NER model.
ner 1 data-clean:/text.txt 2 minutes ago running / success A NER pipeline
pos-tag 1 data-clean:/lemmatized.txt 3 minutes ago running / success A pipeline that performs POS tagging.
data-clean 1 data:/data.txt 5 minutes ago running / success A pipeline that tokenizes the text.
-
一段时间后,检查输出仓库:
pachctl list repo
系统输出如下:
NAME CREATED SIZE (MASTER) DESCRIPTION
retrain About a minute ago ≤ 821.8MiB Output repo for pipeline retrain.
ner 2 minutes ago ≤ 43.49MiB Output repo for pipeline ner.
pos-tag 3 minutes ago ≤ 10.82MiB Output repo for pipeline pos-tag.
data-clean 5 minutes ago ≤ 315.8KiB Output repo for pipeline data-clean.
data 6 minutes ago ≤ 205B
如你所见,Pachyderm 将 816.7 MiB 上传到 retrain
仓库。这是我们重新训练的模型,占用了这么多空间。
-
列出
retrain
仓库中的文件:pachctl list file retrain@master
系统输出如下:
NAME TYPE SIZE
/ner-improved-model.p file 821.8MiB
/ner-improved.txt file 56B
-
查看
ner-improved.txt
文件,它应该包含重新训练的模型对测试文本运行的结果:pachctl get file retrain@master:/ner-improved.txt
系统输出如下:
PERSON -- Headless Horseman
PERSON -- Ichabod Crane
很棒!Headless Horseman 和 Ichabod Crane 被定义为 PERSON。
现在我们已经重新训练了模型,接下来让我们部署最终的流水线,它将提供改进后的 NER,并将故事中的所有人物输出到一个文件中。
部署重新训练的流水线
我们的重新训练流水线需要是一个跨流水线,以便将我们新的重新训练模型与文本结合起来:
---
pipeline:
name: my-model
description: A retrained NER pipeline
input:
cross:
- pfs:
repo: data-clean
glob: "/text.txt"
- pfs:
repo: retrain
glob: "/ner-improved-model.p"
transform:
cmd:
- python3
- "/ner-my-model.py"
image: svekars/nlp-example:1.0
这个流水线将使用与原始 NER 流水线相同的 text.txt
文件,并将我们重新训练的模型与该文本的交叉产品生成。它将结果输出到 my-model
仓库中。生成的文件将包含一个改进了 NER 标记的 HTML 文件和一个包含 The Legend of Sleepy Hollow 中人物列表的文本文件。
ner-my-model.py
与原始的 ner.py
脚本非常相似,主要有以下区别:
它通过使用 pickle.load
而不是原始的 spaCy 模型来加载我们的改进模型:
nlp = pickle.load(open('/pfs/retrain/ner-improved-model.p', 'rb')))
它统计每个 /pfs/out/person-label-count.txt
文件中实例的总数:
with open ('/pfs/out/person-label-count.txt', 'w') as f:
with redirect_stdout(f):
person_label=[]
for i in text.ents:
if i.label_ =='PERSON':
person_label.append(i.text)
count = Counter(person_label)
for key, counter in count.most_common():
它将 HTML 可视化保存到 ner-improved-labels.html
文件中。它将所有实体保存到 ner-improved-list.txt
文件中,该文件位于 my-model
仓库。
让我们创建最终的流水线:
-
打开终端并确认 Pachyderm 正在运行:
pachctl version
系统输出如下:
COMPONENT VERSION
pachctl 2.0.0
pachd 2.0.0
-
创建
my-model
流水线:pachctl create pipeline -f my-model.yaml
这个命令不会返回任何输出。
-
检查流水线是否已创建并正在启动或运行:
pachctl list pipeline
系统输出如下:
NAME VERSION INPUT CREATED STATE / LAST JOB DESCRIPTION
my-model 1 (data-clean:/text.txt ⨯ retrain:/ner-improved-model.p) 6 seconds ago running / running A retrained NER pipeline
retrain 1 data:/training-data.json 9 minutes ago running / success A pipeline that retrains the NER model.
ner 1 data-clean:/text.txt 10 minutes ago running / success A NER pipeline
pos-tag 1 data-clean:/lemmatized.txt 11 minutes ago running / success A pipeline that performs POS tagging.
data-clean 1 data:/data.txt 13 minutes ago running / success A pipeline that tokenizes the text.
请注意,my-model
流水线正在启动。它不同于我们为此示例创建的所有其他流水线。因为我们将模型保存在 retrain
仓库中,并且需要将其与数据仓库中的文本结合,所以标准流水线无法完成这个任务。为了实现这一目标,我们创建了一个跨流水线,将两个输入结合起来。
-
让我们检查输出仓库:
pachctl list repo
系统输出如下:
NAME CREATED SIZE (MASTER) DESCRIPTION
my-model About a minute ago ≤ 31.44MiB Output repo for pipeline my-model.
retrain 10 minutes ago ≤ 821.8MiB Output repo for pipeline retrain.
ner 11 minutes ago ≤ 43.49MiB Output repo for pipeline ner.
pos-tag 12 minutes ago ≤ 10.82MiB Output repo for pipeline pos-tag.
data-clean 14 minutes ago ≤ 315.8KiB Output repo for pipeline data-clean.
data 15 minutes ago ≤ 205B
Pachyderm 将 26.15 MiB 上传到 my-model
仓库。这是我们计算的结果。
-
列出
my-model
仓库中的文件:pachctl list file my-model@master
系统输出如下:
NAME TYPE SIZE
/ner-improved-labels.html file 26.14MiB
/ner-improved-list.txt file 13.43KiB
/person-label-count.txt file 654B
-
查看
person-label-count.txt
文件,它应该提供每个独特 PERSON 实例的总计数:pachctl get file my-model@master:/person-label-count.txt | awk 'FNR <= 15'
系统输出如下:
Ichabod: 35
Brom Bones: 9
Ichabod Crane: 8
Van Tassel: 5
Hans Van: 5
Galloping Hessian: 4
André: 4
Headless Horseman: 3
Brom: 3
Hans Van Ripper: 3
Brouwer: 3
Tarry Town: 2
Cotton Mather: 2
Mather: 2
Baltus Van: 2
如你所见,输出结果仍然不完全准确,因为我们看到 Ichabod 和 Ichabod Crane 分别出现。如果我们提供更多的训练数据,就能改善这些结果。不过,你已经能看到这个列表中最常出现的人物,并且可以理解 Ichabod Crane 很可能是这个故事的主要人物。
-
打开 HTML 文件以查看高亮版本的结果:
pachctl get file my-model@master:/ner-improved-labels.html > ner-improved-labels.html | open -f ner-labels.html -a "Google Chrome"
这结束了我们关于 spaCy NER 模型的实验。你可以添加更多的训练数据,看看在更多训练样本的情况下,准确度会如何提高。
现在,让我们清理我们的集群。
清理中
完成实验后,你可能想要清理集群,以便以全新安装开始下一个实验。要清理环境,请执行以下操作:
-
删除所有的管道和仓库:
pachctl delete pipeline –all && pachctl delete repo --all
-
验证你的集群中没有仓库和管道:
pachctl list repo && pachctl list pipeline
你应该看到以下输出:
NAME CREATED SIZE (MASTER) DESCRIPTION
NAME VERSION INPUT CREATED STATE / LAST JOB DESCRIPTION
你已成功清理了你的集群。
总结
在本章中,我们学习了如何使用 NER 管道示例构建一个复杂的机器学习工作流。我们学习了如何使用 NLTK 库清理数据,如何进行词性标注(POS tagging),以及如何在 Pachyderm 中重新训练 spaCy 模型并输出结果以供预览。你可以做更多的操作,通过添加更多训练数据和调整模型训练参数,进一步提升 NER 的准确度。
在下一章中,我们将学习如何在 Pachyderm 中进行超参数调优,以预测房价为例。
进一步阅读
-
NLTK 文档:
www.nltk.org/
-
spaCy 文档:
spacy.io/api/doc
-
睡谷的传说 在古腾堡项目网站上的链接:
www.gutenberg.org/files/41/41-h/41-h.htm
第九章:第九章:使用 Pachyderm 进行分布式超参数调优
在第八章中,创建端到端机器学习工作流,我们基于命名实体识别(NER)管道示例实现了一个端到端(E2E)机器学习(ML)工作流。这是一个多步骤的管道,包含许多计算阶段,包括数据清理、词性(POS)标注、模型训练,以及将新模型应用于不同数据。我们的目标是找出故事中的主要角色,这一点我们成功地实现了。
在本章中,我们将探讨可以实施的各种策略,以选择适用于机器学习问题的最佳参数。这个技术叫做超参数调优或优化。在本章的第二部分,我们将基于房价预测示例实现一个超参数调优管道。
本章包括以下主题:
-
审查超参数调优技术和策略
-
创建一个超参数调优管道在 Pachyderm 中
技术要求
本章要求您安装并配置特定组件。
对于本地 macOS 安装,您应具备以下条件:
-
macOS Mojave、Catalina、Big Sur 或更高版本
-
Docker Desktop for Mac 10.14
-
minikube
v1.9.0 或更高版本 -
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
对于本地 Windows 安装,您应具备以下条件:
-
Windows Pro 64 位 v10 或更高版本
-
Windows 子系统 for Linux(WSL)2 或更高版本
-
Microsoft PowerShell v6.2.1 或更高版本
-
Hyper-V
-
minikube
v1.9.0 或更高版本 -
kubectl
v1.18 或更高版本 -
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
对于Amazon 弹性 Kubernetes 服务(Amazon EKS)安装,您应具备以下条件:
-
kubectl
v.18 或更高版本 -
eksctl
-
aws-iam-authenticator
-
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
对于 Microsoft Azure 云安装,您应该具备以下条件:
-
kubectl
v.18 或更高版本 -
Azure 命令行接口(Azure CLI)
-
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
-
jq
1.5 或更高版本
对于Google Kubernetes 引擎(GKE)云安装,您应具备以下条件:
-
Google Cloud 软件开发工具包(SDK)v124.0.0 或更高版本
-
kubectl
v.18 或更高版本 -
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
要运行本章中的管道,您不需要任何特殊硬件。如果您是在本地运行 Pachyderm 集群,任何现代笔记本电脑都应该支持本节中的所有操作。如果您在云平台上运行 Pachyderm,您将需要有一个持久卷(PV)。有关更多细节,请参见第五章,在云平台上安装 Pachyderm。
本章中描述的所有脚本和数据都可以在github.com/PacktPublishing/Reproducible-Data-Science-with-Pachyderm/tree/main/Chapter09-Distributed-Hyperparameter-Tuning-with-Pachyderm
找到。
现在我们已经回顾了本章的技术要求,让我们更详细地了解一下我们的工作流。
回顾超参数调优技术和策略
超参数调优(hyperparameter tuning)或超参数优化(hyperparameter optimization)是一种机器学习专业人员用来确定最佳参数以解决特定机器学习问题的技术。在不同的问题中,你需要调节不同类型的参数,比如神经网络中的权重,随机森林算法中的树的数量,或者模型的学习率。最终,选择最佳参数有助于你确定最适合解决问题的方法。数据科学家需要理解他们所使用算法中的可调参数,以便正确地优化它们。
有许多机器学习算法可以帮助解决超参数优化问题。让我们回顾一下最常见的几种。
网格搜索
网格搜索是最简单的算法,有时被称为暴力搜索方法用于超参数优化。该方法计算超参数的最佳值。
在网格搜索中,你通常会定义诸如学习率、丢弃率或批次大小等超参数。然后,定义一个可能的值范围。接下来,算法运行并搜索所有可能的配置。
网格搜索的一个缺点是它计算开销大,通常只用于较小的超参数集。
尽管如此,网格搜索(Grid search)仍然是一种流行的超参数调优算法,也是最容易理解的。
以下图表展示了网格搜索:
图 9.1 – 网格搜索
现在,让我们来看看另一种超参数优化技术——随机搜索。
随机搜索
随机搜索与网格搜索相似,但不同之处在于,它不是检查所有可能的组合,而是随机选择它们,这通常能带来更好的性能并减少计算时间和资源。在许多情况下,随机搜索比网格搜索方法更快地找到最佳组合。
该图展示了随机搜索:
图 9.2 – 随机搜索
现在我们知道了随机搜索(Random search)和网格搜索(Grid search)方法,接下来我们将了解一种稍微复杂一点的超参数调优方法——贝叶斯优化。
贝叶斯优化
贝叶斯优化是一种超参数调整技术,用于找到一个函数的最小值。贝叶斯优化与网格搜索/随机搜索的主要区别在于它会跟踪之前的迭代和评估结果,因此使用概率(P)来预测最佳组合。
使用贝叶斯优化训练的模型在数据更多的情况下能提供更好的结果。由于它考虑了过去的结果,像这样的模型可以通过较少的迭代找到最佳结果。基于之前的迭代,贝叶斯优化构建了一个更接近现实的后验模型。
该图示范了贝叶斯优化的概念:
图 9.3 – 贝叶斯优化
我们已经了解了三种主要的超参数优化技术。虽然还有更多方法,但这三种似乎是最受欢迎和广泛使用的。现在,让我们来看看可以用于确定模型表现的模型评估指标。由于我们将在本章后面讨论的问题是回归问题,我们将只考虑回归评估指标。
回归评估指标
仅选择正确的算法只是成功的一半。我们需要使用一个评估指标来确定模型的表现。评估指标可以应用于各种参数,以确定最佳参数。它们还可以应用于多个算法,以便进行比较并为进一步分析提供依据。
由于房价预测示例是回归问题,我们将只考虑回归评估指标。最常见的评估指标包括以下几种:
-
R 平方(R2)
-
均方误差(MSE)
-
平均绝对误差(MAE)
这些指标是已知的统计方法,用于评估性能。
R2
R2 是一个用于统计学中的评估指标,用于确定因变量的方差或数据与回归线的接近程度。该参数以百分比形式衡量。如果你得到 100%的 R2 值,意味着数据完美地拟合回归模型。然而,其他值也是可以接受的,包括 75%、50%等等。
有许多方法可以用公式表示 R2,但最简单的表示方式如下:
如果你不太熟悉数学,不必太担心,因为在我们的代码中,我们使用scikit-learn
模块来计算 R2。
现在你对 R2 有了大致了解,让我们学习另一种评估指标——MSE。
MSE
MSE 是一种评估指标,用于估算预测值与观察值之间的差异。MSE 受到离群值的影响很大,离群值指的是超出标准范围的值。因此,在评估模型之前,你必须去除离群值。
MSE 值越低,结果越接近真实值。
例如,如果你有一个根据服务年限预测薪资的模型,模型的 MSE 可能是 200,这意味着预测值比实际值**美元(USD)**高出 200 美元。根据样本大小、整体规模和期望的精度,这个数字可能具有不同的重要性。
以下公式用于计算 MSE:
以下值用于该公式:
-
n:样本大小
-
observed:实际值
-
predicted:模型预测的值
与 R2 一样,不必过于担心公式,因为我们将使用 scikit-learn
的 MSE 模块来计算 MSE。
现在,让我们了解 MAE。
MAE
MAE 是另一种常用于回归模型的评估指标。它计算模型中的总误差平均值,或者实际值与预测值之间的绝对差异。如果我们要用最简单的公式表示 MAE,它的形式如下:
这个指标仅供参考。我们在计算中不会使用它。但是,如果你愿意,也可以尝试使用 scikit-learn
的 MAE 模块来计算。
除了这些指标外,还有许多其他指标可以帮助你评估模型,包括根均方误差(RMSE)和调整后的 R2。然而,我们之前提到的这些是最常用的,也是最容易理解的。
现在我们已经了解了方法论、评估指标以及我们将用来配置 Pachyderm 中超参数调优的算法,让我们回顾实际的示例、模型、代码和管道规范。在下一节结束时,我们将拥有一个在 Pachyderm 中工作的超参数调优示例。
在 Pachyderm 中创建超参数调优管道
在本节中,我们将探讨超参数调优管道,并将在 Pachyderm 中创建所有必要的属性,以运行我们的示例。
示例概述
房价预测挑战是超参数调优优化的经典机器学习示例之一。它听起来可能并不复杂,甚至可以根据你的经验进行简单预测。很可能,你对自己所住的地区相当了解,可以根据房屋的平方英尺数、房间数量、相邻土地面积等参数估算房价。
这些信息可以以二维(2D)数组或表格的形式表示,包含上述参数。以下是此类表格的示例:
图 9.4 – 示例房屋数据
基于这些信息,你可以仅通过观察这些数字来预测一栋具有相似特征的房子的价格,而无需使用任何机器学习模型。
但想象一下,你所拥有的所有数据只是一个逗号分隔值(CSV)文件,里面包含成千上万行和 60 多个列。你对这个区域一无所知,而且你从未住在那里。想象一下,你希望根据不断变化的数据,持续预测房价。此时,创建一个高效的机器学习模型就显得非常有用。
在我们的示例中,我们将使用一个免费的数据集,train.csv
版本的数据集,因为我们将对其进行修改以清理数据。
train.csv
数据集包括81列和1,461行。你可以查看data_description.txt
文件来回顾列的描述。列中包含了影响房价的各种参数。每一行代表一个房屋销售实例,并带有一个具体的销售价格。
这是数据集中的一个摘录:
](https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/reprod-ds-pachyderu/img/B17085_09_005.jpg)
图 9.5 – 房屋数据集摘录
我们将尝试创建一个模型,利用我们的训练数据对其进行训练,预测房价,并通过使用我们在前一节中讨论的 R2 和 MSE 评估指标来评估模型的性能。
以下图表展示了我们的模型:
图 9.6 – 超参数调优管道
现在我们已经对管道有了一个基本的了解,让我们更详细地回顾一下管道工作流的每个步骤。
创建探索性分析管道
我们的第一个管道探索了数据,并为我们提供了有关所用数据集的一些基本信息。以下是探索性分析管道的规范:
---
pipeline:
name: data-explore
description: A pipeline that performs exploratory analysis.
input:
pfs:
glob: "/*"
repo: data
transform:
cmd:
- python3
- "/data-explore.py"
image: svekars/hyperparameter-example:1.0
该管道从位于/*
下的数据仓库中获取所有数据,并运行data-explore.py
脚本。该管道使用hyperparameter-example:1.0
Docker 镜像。
让我们回顾一下data-explore.py
脚本的作用。该脚本导入了以下组件:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
我们将使用pandas
来操作 CSV 表格,并将其表示为DataFrame
结构。pandas
是一个开源的 Python 库,广泛应用于数据科学家进行数据操作,尤其是二维表格数据。
我们将使用之前章节中介绍过的matplotlib
,结合seaborn
库来可视化我们的计算结果。seaborn
基于matplotlib
,但提供了更复杂且视觉上更吸引人的图表。
data-explore.py
脚本的第一部分从data
仓库中读取housing-train.csv
文件,并通过使用pandas.DataFrame.corr()
方法计算数据集中所有列之间的相关性矩阵。代码接着创建了一个相关性矩阵的热图,并将其保存在管道输出仓库中。相关代码如下所示:
plt.subplots(figsize=(20,15))
data = pd.read_csv("/pfs/data/housing-train.csv", delimiter=',')
dataset = data.corr().round(2)
plt.subplots(figsize=(20,15))
my_plot = sns.heatmap(dataset, annot=True,cmap='YlGnBu', linecolor='white')
fig = my_plot.get_figure()
fig.savefig('/pfs/out/heatmap.png', dpi=400)
代码的第二部分保存列中数据对象的类型。通常,分析和处理数值数据与类别数据采用不同的方法,因此获取这些信息可能很重要。代码将这些信息保存到data-types.csv
文件中,如下所示:
data_types = data.dtypes.to_frame('dtypes').reset_index()
data_types.to_csv('/pfs/out/data-types.csv', index=False)
脚本的第三部分检查缺失数据的列,创建一个包含缺失数据列百分比的表格,并将其保存为no-data.csv
,如下代码片段所示:
cols_no_data = data.isnull().sum() / data.shape[0] * 100.00
no_data = pd.DataFrame({'Column': data.columns, 'Percentage Missing': cols_no_data})
no_data.sort_values(by=['Percentage Missing'], inplace=True, ascending=False)
header = ["Column", "Percentage Missing"]
no_data.to_csv('/pfs/out/no-data.csv', columns = header, index=False)
我们来创建这个管道,步骤如下:
-
通过执行以下命令,验证 Pachyderm 是否正常运行:
pachctl version
该命令返回以下输出(你的pachctl
和pachd
版本可能不同):
COMPONENT VERSION
pachctl 2.0.0
pachd 2.0.0
-
通过运行以下命令创建数据仓库:
pachctl create repo data
该命令不会返回任何输出。
-
通过运行以下命令,将
housing-train.csv
文件移至data
仓库的主分支:pachctl put file data@master -f housing-train.csv
系统响应应如下所示:
housing-train.csv 460.68KB / 460.68 KB [=========] 0s 0.00 b/s
-
通过运行以下命令,验证文件是否已以
file
类型添加到仓库:pachctl list file data@master
系统响应应如下所示:
NAME TYPE SIZE
/housing-train.csv file 449.9KiB
-
通过使用
data-explore.yaml
文件创建data-explore
管道,步骤如下:pachctl create pipeline -f data-explore.yaml
该命令不会返回任何响应。
-
通过运行以下命令,验证管道是否已创建:
pachctl list pipeline
这是你应该看到的系统响应:
NAME VERSION INPUT CREATED STATE / LAST JOB DESC
data-explore 1 data:/* 34 seconds ago running / running A pipeline that performs exploratory data analysis.
等待管道运行完毕,并显示最后一个作业的success
状态。
-
列出仓库,命令如下:
pachctl list repo
你应该看到data-explore
管道已上传3.361MiB
的数据到data-explore
仓库,输出如下所示:
NAME CREATED SIZE (MASTER) DESCRIPTION
data-explore 46 seconds ago 3.361MiB Output repo for pipeline data-explore.
data 26 minutes ago 449.9KiB
-
通过运行以下命令,我们来探索仓库中的数据:
pachctl list file data-explore@master
你应该能看到以下三个文件:
NAME TYPE SIZE
/data-types.csv file 1.37KiB
/heatmap.png file 3.359MiB
/no-data.csv file 1.447KiB
-
我们来打开
/data-types.csv
文件,如下所示:pachctl get file data-explore@master:/data-types.csv
文件包含每一列的数据类型,如下代码片段所示:
index,dtypes
Id,int64
MSSubClass,int64
MSZoning,object
LotFrontage,float64
...
-
通过运行以下命令,我们来查看
no-data.csv
文件中的内容:pachctl get file data-explore@master:/no-data.csv
另外,你也可以在你的电脑上用应用程序打开该文件。例如,在 macOS 中,你可以在Numbers
应用中打开它,如下所示:
pachctl get file data-explore@master:/no-data.csv | open -f -a "Numbers"
该文件包含有关列和这些列中缺失数据百分比的信息。这对于数据清理非常有用。一些列的缺失数据超过了 80%。这些列可以被删除,以避免干扰我们的计算。我们将在下一个管道中进行处理。以下是缺失数据大部分的列列表:
图 9.7 – 缺失数据的列
-
现在,通过运行以下命令,我们来查看热图:
pachctl get file data-explore@master:/heatmap.png | open -f -a "Preview.app"
你应该看到以下热图,它展示了数据集中所有列之间的相关性:
图 9.8 – 所有参数的热图
这个热图参数太多,难以阅读。但即便在这个热图上,我们也能看到有些参数对销售价格的影响大于其他参数。例如,看起来OverallQuality
参数对价格的影响最大,还有GrLivArea
参数(即大居住区)。我们将在下一个管道步骤中尝试将数据集缩小到这些参数。
我们已经探索了数据集并对数据有了基本了解。现在,让我们回顾一下下一个管道,它将根据我们的发现清理数据。
创建数据清理管道
我们的下一步是创建一个清理数据的管道。这个管道将根据上一部分的发现清理数据。以下是管道的规范:
---
pipeline:
name: data-clean
description: A pipeline that removes empty cells from the CSV.
input:
pfs:
glob: "/"
repo: data
transform:
cmd:
- python3
- "/data-clean.py"
image: svekars/hyperparameter-example:1.0
这是一个标准的 Pachyderm 管道,它从data
仓库中获取数据,并对这些数据运行data-clean.py
脚本。在这种情况下,数据是我们的housing-train.csv
数据集。
让我们来看一下data-clean.py
脚本。该脚本导入了以下组件:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from pandas import Series
这些组件与data-explore
管道中的组件相似。唯一新增的组件是pandas.Series
,我们需要它来将数据保存到 CSV 文件中。
我们脚本的第一部分读取来自housing-train.csv
数据集的数据,作为DataFrame
。然后,我们删除那些有超过 40%缺失数据的列,并将被删除的列保存到col_drop.csv
文件中,如下所示:
data = pd.read_csv("/pfs/data/housing-train.csv", delimiter=',')
col_drop = set(data.count()[data.count()<0.60*max(data.count())].index.tolist())
pd.Series(list(col_drop)).to_csv('/pfs/out/col_drop.csv', index=False)
接下来,我们创建一个新的相关矩阵,仅包括对SalePrice
列影响系数为 0.5 或更大的参数。我们绘制一个新的热图,并将其保存在heatmap2.png
文件中,如下所示:
data = data.drop((col_drop), axis=1)
corr = data.corr()
r_var = corr.SalePrice[(corr.SalePrice > 0.5)]
r_col = list(r_var.index.values)
new_corr = data[r_col].corr()
plt.subplots(figsize=(20,15))
my_plot2 = sns.heatmap(new_corr, annot=True,cmap='YlGnBu', linecolor='white')
fig = my_plot2.get_figure()
fig.savefig('/pfs/out/heatmap2.png', dpi=400)
最后,我们移除不属于新相关矩阵的列,并将它们保存在一个名为cleaned-data.csv
的新数据集里,放在管道输出仓库中,如下所示:
new_data = data.loc[:, data.columns.intersection(r_col)]
new_data.to_csv('/pfs/out/cleaned-data.csv', index=True)
现在,让我们创建这个数据清理管道,如下所示:
-
通过执行以下命令验证 Pachyderm 是否正在运行:
pachctl version
这里是输出:
COMPONENT VERSION
pachctl 2.0.0
pachd 2.0.0
你使用的 Pachyderm 版本可能不同。
-
创建一个
data-clean
管道,如下所示:pachctl create pipeline -f data-clean.yaml
没有系统响应返回。
-
通过运行以下命令,验证 Pachyderm 是否成功创建了管道:
pachctl list pipeline
这是你应该看到的系统响应:
NAME VERSION INPUT CREATED STATE / LAST JOB DESCRIPTION
data-clean 1 data:/ 6 seconds ago running / running A pipeline that removes empty cells from the CSV.
data-explore 1 data:/* 4 minutes ago running / success A pipeline that performs exploratory analysis.
你需要等待管道的状态变为success
。
-
当管道成功运行完毕后,通过运行以下命令列出仓库:
pachctl list repo
你应该能看到data-clean
管道添加了780.4KiB
的数据,如下所示的输出所示:
NAME CREATED SIZE (MASTER) DESCRIPTION
data-clean 42 seconds ago ≤ 780.4KiB Output repo for pipeline data-clean.
data-explore 3 minutes ago ≤ 3.361MiB Output repo for pipeline data-explore.
data 12 minutes ago ≤ 449.9KiB
-
让我们通过运行以下命令查看仓库中的数据:
pachctl list file data-clean@master
输出应该如下所示:
NAME TYPE SIZE
/cleaned-data.csv file 67.14KiB
/col_drop.csv file 45B
/heatmap2.png file 713.2KiB
-
让我们通过运行以下命令查看被删除的列:
pachctl get file data-clean@master:/col_drop.csv
该文件包括每列的数据类型,正如我们在这里看到的:
0
PoolQC
Alley
FireplaceQu
MiscFeature
Fence
这些是那些有超过 40%空列的列。
-
我们还去除了新相关矩阵中映射的所有列,这些列的相关系数小于 0.5。运行以下命令查看我们的新相关矩阵:
pachctl get file data-clean@master:/heatmap2.png | open -f -a "Preview.app"
这是新的热图:
图 9.9 – 精细化热图
这个热图更有意义了。我们可以清晰地看到在确定房屋销售价格时最显著的参数。我们的数据集已被大幅度减少,仅剩 11 列,并已保存到新的cleaned-data.csv
文件中。现在,这个策略可能并不适用于所有的用例——你可能决定保留更多的参数,以确保并检查是否在更多参数下模型表现更好。但就这个示例而言,这应该足够了。
现在我们已经清理了数据,还需要确保去除任何异常值或超出标准范围的参数。我们将在下一部分进行这个操作。
创建一个去除异常值的管道
我们的下一个管道将评估数据集中的异常值,并将其去除,以确保模型的表现不受影响。我们将再次使用标准的 Pachyderm 管道规范来实现这一目标。以下是管道规范:
---
pipeline:
name: remove-outliers
description: A pipeline that removes outliers from the dataset.
input:
pfs:
glob: "/"
repo: data-clean
transform:
cmd:
- python3
- "/outliers.py"
image: svekars/hyperparameter-example:1.0
该管道规范从data-clean
代码库获取清理后的数据,并将outliers.py
Python 脚本应用于这些数据。管道使用与前两个相同的 Docker 镜像。
outliers.py
脚本导入了与我们前面管道步骤中的脚本相同的组件列表,包括seaborn
、matplotlib
和pandas
。
脚本从cleaned-data.csv
文件读取数据。然后,它创建一个直方图,显示数据集中的异常值,并将该直方图保存为histogram.png
文件。接着,我们只保留中间范围内 50%的数据,并去除其余部分。我们创建另一个直方图,显示这些新数据。我们从数据集中删除数据并将其保存为一个名为removed-outliers-data.csv
的新 CSV 文件。代码如下所示:
data = pd.read_csv("/pfs/data-clean/cleaned-data.csv", delimiter=',', encoding='utf-8')
my_plot=sns.boxplot(x=data['SalePrice'])
fig = my_plot.get_figure()
fig.savefig('/pfs/out/histogram.png', dpi=400)
q1 = data['SalePrice'].quantile(0.25)
q3 = data['SalePrice'].quantile(0.75)
iqr = q3-q1
lw = q1 - 1.5*iqr
uw = q3 + 1.5*iqr
dataset = data[(data['SalePrice']>lw)&(data['SalePrice']<uw)]
plt.figure(figsize=(20,10))
my_plot2 = sns.histplot(data=dataset, x="SalePrice", color="orange", element="poly")
fig = my_plot2.get_figure()
fig.savefig('/pfs/out/histogram-outliers.png', dpi=400)
dataset.to_csv('/pfs/out/removed-outliers-data.csv', index=True)
现在,让我们创建这个管道,步骤如下:
-
通过执行以下命令验证 Pachyderm 是否正常运行:
pachctl version
你应该得到类似以下的输出:
COMPONENT VERSION
pachctl 2.0.0
pachd 2.0.0
你的版本可能会有所不同。
-
创建一个
data-clean
管道,步骤如下:pachctl create pipeline -f remove-outliers.yaml
这个命令没有返回任何输出。
-
通过运行以下命令检查管道是否已创建:
pachctl list pipeline
这是你应该看到的系统响应:
NAME VERSION INPUT CREATED STATE / LAST JOB DESCRIPTION
remove-outliers 1 data-clean:/ 5 seconds ago running / starting A pipeline that removes outliers from the dataset.
data-clean 1 data:/ 4 minutes ago running / success A pipeline that removes empty cells from the CSV.
data-explore 1 data:/* 8 minutes ago running / success A pipeline that performs exploratory analysis.
remove-outliers
管道正在启动。你可以多次运行pachctl list pipeline
命令,直到管道成功。
-
通过运行以下命令列出代码库:
pachctl list repo
remove-outliers
代码库应该上传了413.7KiB
的数据,以下输出显示了这一点:
NAME CREATED SIZE (MASTER) DESCRIPTION
remove-outliers About a minute ago 413.7KiB Output repo for pipeline remove-outliers.
data-clean 49 minutes ago 780.4KiB Output repo for pipeline data-clean.
data-explore 53 minutes ago 3.361MiB Output repo for pipeline data-explore.
data About an hour ago 449.9KiB
-
通过运行以下命令列出代码库中的文件:
pachctl list file remove-outliers@master
输出应如下所示:
NAME TYPE SIZE
/histogram-outliers.png file 290.8KiB
/histogram.png file 52.8KiB
/removed-outliers-data.csv file 70.04KiB
-
让我们先通过运行以下命令打开
histogram.png
文件:pachctl get file remove-outliers@master:/histogram.png | open -f -a "Preview.app"
这是您应该看到的内容:
图 9.10 – 数据集中的异常值
这个箱线图显示,大多数房屋的售价在 50,000 美元到 350,000 美元之间,其中大部分集中在 110,000 美元到 220,000 美元之间。还有少数房屋的售价远远超出了这个范围,可能被视为一个单独的类别。我们的流水线会移除位于主箱体之外的异常值。
-
现在,让我们看看去除异常值后的直方图。我们可以通过运行以下命令来实现:
pachctl get file remove-outliers@master:/histogram-outliers.png | open -f -a "Preview.app"
这是新的直方图:
图 9.11 – 去除异常值后的直方图
我们已经从数据集中移除了一些行,现在我们有了 1,400 行数据,而不是之前的 1,481 行。
现在我们已经完成了数据清洗,接下来可以训练我们的模型了。
创建训练流水线
我们的下一个流水线将对数据集中的训练部分进行模型训练。以下是流水线的规范:
---
pipeline:
name: train
description: A pipeline that trains the model with a selected estimator.
input:
pfs:
glob: "/"
repo: remove-outliers
transform:
cmd:
- python3
- "/train.py"
image: svekars/hyperparameter-example:1.0
如您所见,这是另一个标准的 Pachyderm 流水线。它从 remove-outliers
仓库中获取数据,并应用 train.py
脚本。它使用与本节其他流水线相同的 Docker 镜像。
以下是 train.py
脚本导入的组件列表:
from sklearn.model_selection import train_test_split
from sklearn import metrics
import pandas as pd
from sklearn.metrics import r2_score, mean_squared_error, make_scorer
from sklearn.linear_model import Ridge
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from contextlib import redirect_stdout
我们从 sklearn
库中导入 train_test_split
、metrics
、r2_score
、mean_squared_error
和 make_scorer
模块,以将数据拆分为训练数据和测试数据,并计算模型的 R2 和 MSE 指标。我们从 sklearn.linear_model
导入 Ridge
回归模型,使用 Ridge 回归来训练我们的模型。Ridge 回归是线性回归的一种变体,是您可以用于这种回归问题的算法之一。我们导入 seaborn
和 matplotlib
来可视化结果,导入 pandas
和 numpy
来处理数据。redirect_stdout
用于将输出重定向到文件。
我们脚本的第一部分从 remove-outliers
仓库读取 removed-outliers-data.csv
文件,并将其作为 DataFrame
处理。然后,我们使用 train_test_split
将数据集分为训练集和测试集。训练集用于训练数据,测试集用于在交叉验证阶段测试模型的性能。以下是代码示例:
data = pd.read_csv("/pfs/remove-outliers/removed-outliers-data.csv", delimiter=',')
X=data.drop('SalePrice', axis=1)
y=data['SalePrice']
train_X, test_X, train_y, test_y = train_test_split(X,y,test_size=0.4, random_state=0)
接下来,我们定义了我们的估算器,它是 Ridge 回归。alpha
是我们将调优的参数,以提高性能。我们最初将 alpha
设置为 1
,进行预测,并将我们的 R2 和 MSE 分数保存在 r_squared_mse.txt
文件中,以下是代码示例:
estimator = Ridge(alpha=1).fit(train_X, train_y)
prediction = estimator.predict(test_X)
with open('/pfs/out/r_squared_mse.txt', 'w', encoding='utf-8') as f:
with redirect_stdout(f):
print('R-squared:', metrics.r2_score(test_y, prediction))
print('MSE:', np.sqrt(metrics.mean_squared_error(test_y, prediction)))
最后,我们将绘制数据并将其保存在 prediction.png
文件中,如下所示:
plt.figure(figsize=(20,10))
myplot1 = sns.distplot(test_y, hist=True, kde=False)
myplot2 = sns.distplot(prediction, hist=True, kde=False)
plt.legend(labels=['Real Price', 'Predicted Price'])
plt.xlim(0,)
fig1 = myplot1.get_figure()
fig1.savefig('/pfs/out/prediction.png', dpi=400)
让我们按照以下步骤创建这个流水线:
-
通过执行以下命令检查 Pachyderm 集群是否正在运行:
pachctl version
这是输出结果:
COMPONENT VERSION
pachctl 2.0.0
pachd 2.0.0
-
通过运行以下命令创建一个
train
管道:pachctl create pipeline -f train.yaml
不返回任何输出。
-
通过运行以下命令列出所有管道:
pachctl list pipeline
你应该看到以下输出:
NAME VERSION INPUT CREATED STATE / LAST JOB DESCRIPTION
train 1 remove-outliers:/ 34 seconds ago running / success A pipeline that trains the model with a selected estimator.
remove-outliers 1 data-clean:/ 5 seconds ago running / starting A pipeline that removes outliers from the dataset.
data-clean 1 data:/ 4 minutes ago running / success A pipeline that removes empty cells from the CSV.
data-explore 1 data:/* 8 minutes ago running / success A pipeline that performs exploratory analysis.
输出应列出train
管道。等待管道运行完毕。
-
让我们查看一下仓库。我们可以通过运行以下命令来实现:
pachctl list repo
你应该看到一个名为train
的新仓库,并且数据量为186.3KiB
,如以下输出所示:
NAME CREATED SIZE (MASTER) DESCRIPTION
train 2 minutes ago 186.3KiB Output repo for pipeline train.
remove-outliers About a minute ago 413.7KiB Output repo for pipeline remove-outliers.
data-clean 49 minutes ago 780.4KiB Output repo for pipeline data-clean.
data-explore 53 minutes ago 3.361MiB Output repo for pipeline data-explore.
data About an hour ago 449.9KiB
-
现在,让我们查看上传到仓库中的文件。我们可以通过运行以下命令来完成:
pachctl list file train@master
输出应该像这样:
NAME TYPE SIZE
/prediction.png file 186.2KiB
/r_squared_mse.txt file 54B
应该有两个文件。
-
打开
r_squared_mse.txt
文件以检查 MSE 和 R2 分数。你可以通过运行以下命令来完成:pachctl get file train@master:/r_squared_mse.txt
输出应该像这样:
R-squared: 0.7803812645495943
MSE: 29521.138357806965
我们的 R2 值相当高,这意味着计算应该相当精确。
-
现在,让我们通过运行以下命令打开
prediction.png
文件:pachctl get file train@master:/prediction.png | open -f -a "Preview.app"
你应该看到如下内容:
图 9.12 – 预测价格与实际价格
如你所见,预测价格与实际价格非常接近,只有少数几个小的例外。
在我们最后一个管道步骤中,我们将尝试找到最佳的alpha
值,并使用网格搜索对我们的参数进行交叉验证。
创建一个评估管道
我们的评估管道规范如下:
---
pipeline:
name: evaluate
description: A pipeline that evaluates the performance of the model.
input:
pfs:
glob: "/"
repo: remove-outliers
transform:
cmd:
- python3
- "/grid-search.py"
image: svekars/hyperparameter-example:1.0
既然你已经见过这些内容,你可能猜到它是一个标准的 Pachyderm 管道,它从remove-outliers
仓库获取数据,并将grid-search.py
文件应用于这些数据。这个管道使用与所有其他管道相同的 Docker 镜像。
grid-search.py
文件导入了我们在前面章节中已经熟悉的组件。此外,它还导入了sklearn.model_selection
库中的GridSearchCV
和joblib
,后者用于将模型保存为pickle
文件。
脚本的第一部分执行与train.py
相同的数据处理——它打开数据文件并将其分割成两个数据集。
接下来,我们设置estimator
属性为Ridge
回归,并指定scoring
值和alpha
参数,如下所示:
estimator = Ridge(alpha=10)
scoring={'R_squared':'r2','MSE':'neg_mean_squared_error'}
params = {'alpha':[1,0.1,0.01,0.001,0.0001,0,10,100,1000]}
脚本的下一部分使用GridSearchCV
来训练并确定最佳的alpha
参数,并将最佳分数和最佳alpha
参数保存在best_score.txt
文件中。模型也保存在my_model.pkl
文件中。代码如下所示:
with open('/pfs/out/best_score.txt', 'w', encoding='utf-8') as f:
with redirect_stdout(f):
for i, v in scoring.items():
grid = GridSearchCV(estimator, params, cv=10, scoring= "r2")
grid.fit(train_X, train_y)
print(i)
print('Best params:', grid.best_params_)
if grid.best_score_ > 0:
print('Best score:', grid.best_score_)
else:
print('Best score:', np.sqrt(abs(grid.best_score_)))
print()
joblib.dump(estimator, '/pfs/out/my_model.pkl', compress =1)
最后,我们绘制我们的性能图并将其保存为performance-plot.png
。
按照以下步骤创建此管道:
-
通过执行以下命令验证 Pachyderm 是否正在运行:
pachctl version
你应该看到以下输出。请注意,你的pachctl
和pachd
版本可能有所不同:
COMPONENT VERSION
pachctl 2.0.0
pachd 2.0.0
-
通过运行以下命令创建一个
evaluate
管道:pachctl create pipeline -f evaluate.yaml
此命令不会返回任何输出。
-
通过运行以下命令查看活动管道:
pachctl list pipeline
你应该看到以下输出:
NAME VERSION INPUT CREATED STATE / LAST JOB DESCRIPTION
evaluate 1 remove-outliers:/ 5 seconds ago running / running A pipeline that evaluates the performance of the model.
train 1 remove-outliers:/ 34 seconds ago running / success A pipeline that trains the model with a selected estimator.
remove-outliers 1 data-clean:/* 5 seconds ago running / starting A pipeline that removes outliers from the dataset.
data-clean 1 data:/* 4 minutes ago running / success A pipeline that removes empty cells from the CSV.
data-explore 1 data:/* 8 minutes ago running / success A pipeline that performs exploratory analysis.
您应该看到evaluate
管道正在运行或已完成,并显示成功
状态。
-
查看通过运行以下命令创建的
evaluate
管道所生成的仓库:pachctl list repo
您应该看到一个名为train
的新仓库,里面有121KiB
的数据,具体如下所示:
NAME CREATED SIZE (MASTER) DESCRIPTION
evaluate 2 minutes ago 121KiB Output repo for pipeline evaluate.
train 2 minutes ago 186.3KiB Output repo for pipeline train.
remove-outliers About a minute ago 413.7KiB Output repo for pipeline remove-outliers.
data-clean 49 minutes ago 780.4KiB Output repo for pipeline data-clean.
data-explore 53 minutes ago 3.361MiB Output repo for pipeline data-explore.
data About an hour ago 449.9KiB
-
通过运行以下命令列出
evaluate
仓库中的文件:pachctl list file evaluate@master
输出应如下所示:
NAME TYPE SIZE
/best_score.txt file 132B
/my_model.pkl file 187B
/performance-plot.png file 120.7KiB
这些文件是我们的模型、最佳的 MSE 和 R2 得分、最佳的alpha
参数,以及展示训练数据与测试数据对比的图表。
-
让我们通过运行以下命令来查看我们的最佳得分:
pachctl get file evaluate@master:/best_score.txt
输出应如下所示:
R_squared
Best params: {'alpha': 10}
Best score: 0.7040913319322766
MSE
Best params: {'alpha': 10}
Best score: 0.7040913319322766
Alpha 10 是我们最佳的alpha
参数。它应该用于预测房价。
-
performance-plot.png
文件应该展示我们的训练数据与测试数据的对比。我们可以通过运行以下命令查看:pachctl get file evaluate@master:/performance-plot.png | open -f -a "Preview.app"
这是它输出的图表:
图 9.13 – 性能图
如您所见,alpha=10
很可能是在我们提供的范围内最佳的参数。此管道结束了我们的示例。生成的模型可以用于使用训练过的alpha
超参数预测房价。
这就结束了我们的示例。现在,让我们清理集群。
清理
完成实验后,您可能希望清理集群,以便在下一个实验时从全新安装开始。要清理环境,请按以下步骤操作:
-
通过运行以下命令删除所有管道和仓库:
pachctl delete pipeline –all && pachctl delete repo --all
-
通过运行以下命令验证集群中是否存在仓库和管道:
pachctl list repo && pachctl list pipeline
您应该看到以下输出:
NAME CREATED SIZE (MASTER) DESCRIPTION
NAME VERSION INPUT CREATED STATE / LAST JOB DESCRIPTION
您已经成功清理了集群。
总结
在本章中,我们学习了如何实现一个机器学习管道,用于对房价预测示例进行超参数调优。我们创建了该管道的五个步骤,每个步骤都会将相关的文件和信息输出到 Pachyderm 的输出仓库中。在第一个管道中,我们进行了探索性分析,收集了关于数据集的总体理解,并构建了一个热图,帮助我们勾画出数据集中各种参数之间的相关性。在第二个管道中,我们清理了缺失信息的列,并移除了对房屋销售价格影响较小的参数。在第三个管道中,我们去除了异常值——那些超出标准范围的值。我们的第四个管道将数据集分为两部分——一部分用于测试,另一部分用于训练。最后,第五个管道对alpha
参数进行了超参数调优,并找到了最适合我们用例的 alpha 值。最后一个管道将我们的模型输出为一个.pkl
文件,并创建了一个图表,展示了我们的模型在训练数据和测试数据上的表现。
在下一章,我们将学习 Pachyderm 语言客户端。虽然你可以在 Pachyderm 中使用纯 Python、R 或 Scala,你还可以利用我们提供的语言客户端,甚至自己构建一个,进一步发挥 Pachyderm 的优势。
进一步阅读
-
Kaggle 房价数据集:
www.kaggle.com/lespin/house-prices-dataset
-
seaborn
:seaborn.pydata.org/
第三部分:Pachyderm 客户端与工具
本节重点介绍了 Pachyderm 可以集成的工具和库,并提供了如何使用 Pachyderm 语言客户端的实际示例,还将教你如何使用 Pachyderm Hub 和 Pachyderm Notebooks。
本节包含以下章节:
-
第十章,Pachyderm 语言客户端
-
第十一章,使用 Pachyderm Notebooks
第十章:第十章: Pachyderm 语言客户端
在前面的章节中,我们已经学习了如何通过pachctl
来使用 Pachyderm。我们简要地介绍了 Pachyderm 的用户界面(UI)—或称为仪表板—虽然我们没有广泛使用它。Pachyderm CLI 允许您执行所有 Pachyderm 管理操作,并且通常提供比 Pachyderm UI 更多的功能。
然而,许多用户可能决定通过使用 Pachyderm pachctl
或仪表板进一步扩展 Pachyderm 的功能。许多 Pachyderm 用户开发脚本,并通过这些脚本直接调用 Pachyderm API。到目前为止,Pachyderm 提供了两个官方的 Pachyderm 编程语言客户端,Golang(Go)和 Python,以便高级用户进一步扩展 Pachyderm 的功能。
此外,如果您熟悉pps.proto
文件,可以通过 C、C++和 Java 等语言访问 Pachyderm。
在本章中,您将学习如何使用 Python 和 Go Pachyderm 客户端。您将学习如何通过这两种客户端运行基本操作,包括如何创建仓库和管道。
本章旨在展示如何使用官方 Pachyderm 语言客户端。
我们将涵盖以下主题:
-
使用 Pachyderm Go 客户端
-
克隆 Pachyderm 源代码库
-
使用 Pachyderm Python 客户端
技术要求
您应该已经安装了接下来列出的组件。
对于本地 macOS 安装,您需要以下组件:
-
macOS Mojave、Catalina、Big Sur 或更高版本
-
Docker Desktop for Mac 10.14
-
minikube
v1.9.0 或更高版本 -
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
对于本地 Windows 安装,您需要以下组件:
-
Windows Pro 64 位 v10 或更高版本
-
Windows 子系统 Linux(WSL)2 或更高版本
-
Microsoft PowerShell v6.2.1 或更高版本
-
Hyper-V
-
minikube
v1.9.0 或更高版本 -
kubectl
v1.18 或更高版本 -
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
对于Amazon 弹性 Kubernetes 服务(Amazon EKS)的安装,您需要以下组件:
-
kubectl
v1.18 或更高版本 -
eksctl
-
aws-iam-authenticator
-
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
对于 Microsoft Azure Kubernetes Service(AKS)云端安装,您需要以下组件:
-
kubectl
v1.18 或更高版本 -
Azure CLI
-
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
-
jq
1.5 或更高版本
对于Google Kubernetes Engine(GKE)云端安装,您需要以下组件:
-
Google Cloud 软件开发工具包(SDK)v124.0.0 或更高版本
-
kubectl
v1.18 或更高版本 -
pachctl
2.0.0 或更高版本 -
Pachyderm 2.0.0 或更高版本
下载源文件
本章的所有脚本可以在github.com/PacktPublishing/Reproducible-Data-Science-with-Pachyderm/tree/main/Chapter10-Pachyderm-Language-Clients
获取。
我们将使用我们在第六章中的图像处理示例,创建你的第一个 Pipeline。如果你还没有这些文件,可以从这里下载该示例的文件:github.com/PacktPublishing/Reproducible-Data-Science-with-Pachyderm/tree/main/Chapter06-Creating-Your-First-Pipeline
。
使用 Pachyderm Go 客户端
Pachyderm Go 客户端使你能够通过 Go API 与 Pachyderm 进行通信。Go 是一种由 Google 开发的流行编程语言,近年来在开发者社区中变得广泛流行。在本章中,我们将学习如何启用 Pachyderm Go 客户端,并如何使用 Go 客户端执行基本的 Pachyderm 操作。
你可以参考的主要源文件位于 Pachyderm 源代码库的github.com/pachyderm/pachyderm/tree/master/src/client
目录下。这些文件包含了你可以用来与 Pachyderm 对象和原语进行通信的所有方法——具体来说,包括以下文件:
-
github.com/pachyderm/pachyderm/blob/master/src/client/client.go
-
github.com/pachyderm/pachyderm/blob/master/src/client/pfs.go
-
github.com/pachyderm/pachyderm/blob/master/src/client/pps.go
这些文件包括了大部分重要的 Pachyderm 方法,以及我们在接下来的章节中将使用的方法。
在你的计算机上安装 Go
为了开始使用,我们需要验证环境中是否有有效的 Go 安装。Go 支持所有主要操作系统,包括 Microsoft Windows、Linux 和 macOS。通过运行以下命令,检查你的计算机上是否安装了 Go:
go version
你应该能看到类似下面的输出:
go version go1.16.4 darwin/amd64
如果你的计算机上没有安装 Go,请按照以下步骤进行安装:
-
访问
golang.org/doc/install
并下载适用于你操作系统的 Go 版本。 -
打开下载的包并按照提示在系统中安装 Go。安装完成后,你应该能看到如下屏幕:
![图 10.1 – Go 安装]
](https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/reprod-ds-pachyderu/img/B17085_10_001.jpg)
图 10.1 – Go 安装
-
根据你操作系统的说明,验证 Go 是否已按
golang.org/doc/install
中的描述安装。 -
重启你的终端,并再次运行
go version
来验证你的安装。你应该能看到类似下面的输出:go version go1.16.4 darwin/amd64
现在我们已经安装了 Go,接下来配置$GOPATH
。
配置$GOPATH
如果你以前从未使用过 Go,你需要确保你的$GOPATH
目录已正确设置;否则,本节中描述的任何脚本都无法正常工作。安装 Go 时,它可能已经被配置好了。不过,你可能还是想要配置以下内容:
-
验证你在
~/.bash_profile
、~/.profile
或~/.zsh_profile
文件中有以下内容:export GOPATH="$HOME/go" PATH="$GOPATH/bin:$PATH"
-
如果你的 shell 配置文件中没有这个配置,请添加它,然后像这样执行你的 shell 配置文件:
source ~/.<shell-profile>
-
通过运行以下命令检查你的
$GOPATH
目录:go env
此命令将打印出你的 Go 环境配置。如果你使用的是 macOS,$GOPATH
目录应该是`GOPATH="/Users/<username>/go"`.
-
如果你还没有这个目录,请在你的
$GOPATH
目录下创建一个src
目录,如下所示:mkdir $GOPATH/src
-
在
$GOPATH/src
下创建一个github.com
目录,如下所示:mkdir $GOPATH/src/github.com
你需要将 Pachyderm 仓库克隆到这个目录中,具体步骤将在下一章节中描述。
-
更新到最新版本的
grpc
,如下所示:go get google.golang.org/grpc
配置好$GOPATH
后,你需要克隆 Pachyderm 源代码仓库。
克隆 Pachyderm 源代码仓库
在使用 Pachyderm 语言客户端之前,你需要将 Pachyderm 源代码仓库克隆到你的机器上,以便使用 API。你将通过客户端方法与现有的 Pachyderm 集群进行交互。Pachyderm 仓库存储在 GitHub 上,地址为github.com/pachyderm/pachyderm
。此外,你还需要确保切换到与 pachd
和 pachctl
版本相匹配的分支和标签。在本节中,我们将学习如何克隆 Pachyderm 仓库,以及如何切换到所需的分支和标签。
为了能够运行本节脚本中使用的 Go 模块,你需要在计算机上的$GOPATH
目录下克隆 Pachyderm 仓库。在 Mac 上,Go 安装在/Users/<username>/go
目录下,你可以在/Users/<username>/go/src/github.com/
路径下克隆 Pachyderm 仓库。
克隆 Pachyderm 仓库,请完成以下步骤:
-
点击代码标签,如下图所示:
图 10.2 – Pachyderm 源代码仓库
-
在下拉菜单中,选择使用HTTPS或SSH进行克隆,并点击克隆图标。
重要说明
如果你决定使用安全外壳(SSH)进行克隆,并且这是你第一次从 GitHub 克隆,可能需要配置一个 SSH 密钥对。有关更多信息,请参见
docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh
。 -
打开终端,使用您在步骤 3中复制的超文本传输协议安全 (HTTPS) 或 SSH 地址运行
git clone
命令,如下所示:git clone git@github.com:pachyderm/pachyderm.git
您应该看到类似于以下的输出:
Cloning into 'pachyderm'...
remote: Enumerating objects: 226153, done.
remote: Counting objects: 100% (171/171), done.
...
Pachyderm 源代码将被克隆到 pachyderm
目录。
-
通过运行以下命令进入
pachyderm
目录:cd pachyderm
-
通过运行以下命令检查您所在的分支:
git branch
-
通过运行以下命令获取标签列表:
git fetch –tags
-
通过运行以下命令验证您正在使用的
pachctl
和pachd
版本:pachctl version
-
您的输出可能如下所示:
COMPONENT VERSION pachctl 2.0.1 pachd 2.0.1
-
查看与您使用的
pachctl
和pachd
版本相对应的标签。在这个例子中,我们需要查看2.0.1
标签:git checkout tags/v2.0.1
-
通过运行以下命令检查您是否切换到了正确的版本:
git branch
您应该看到以下输出:
* (HEAD detached at v2.0.1)
master
我们现在有了有效的 Pachyderm 源代码,我们将用它来访问我们的 Pachyderm 集群。接下来,让我们使用 Go 客户端连接到 Pachyderm。
使用 Go 客户端连接到 Pachyderm
您必须有一个正在运行的 Pachyderm 集群才能使用 Go API 客户端。如果您已经按照前面的步骤操作,您可能已经在您选择的云平台或本地运行了一个集群。如果没有,请返回到 第四章,本地安装 Pachyderm,或者 第五章,在云平台上安装 Pachyderm,并部署一个集群。
我们将使用 access.go
脚本来访问 Pachyderm。让我们看看这个脚本,了解它是如何工作的。脚本的第一部分导入了所需的组件,如我们在这里看到的:
package main
import (
"fmt"
"log"
"github.com/gogo/protobuf/types"
"github.com/pachyderm/pachyderm/v2/src/client"
)
脚本的第二部分定义了一个 main
函数。您必须在 Go 中使用 main
函数,否则它将无法工作。main
函数定义了 localhost
或 127.0.0.1
。30650
是 pachd
端口:
func main() {
c, err := client.NewFromURI("grpc://localhost:30650")
if err != nil {
log.Fatal(err)
}
脚本的第三部分,如下所示,获取您的集群版本:
version, err := c.VersionAPIClient.GetVersion(c.Ctx(), &types.Empty{})
if err != nil {
panic(err)
}
fmt.Println(version)
}
要连接到 Pachyderm 集群,您需要知道集群的 IP 地址。如果在本地机器上运行这些示例,grpc://localhost:30650
应该有效。
现在,让我们运行脚本。请按照以下步骤操作:
-
除非您已经部署了一个负载均衡器,以便访问您的集群,否则在通过 API 访问集群时,您还需要确保始终运行 Pachyderm 的端口转发。要启动 Pachyderm 端口转发,请在另一个终端窗口中运行以下命令:
pachctl port-forward
-
运行
access.go
脚本,如下所示:go run access.go
这是一个您应该获得的示例响应:
major:2 micro:1
我们已经通过 Go API 成功访问了我们的集群。我们的集群正在运行版本 2.0.1\。您的版本可能不同。
现在,让我们使用 Go API 创建一个 Pachyderm 仓库。
使用 Go 客户端创建仓库
现在我们知道如何连接到 Pachyderm,让我们使用 create-repo.go
脚本中的代码来创建一个仓库。
下面是脚本导入的内容:
package main
import (
"fmt"
"log"
"github.com/pachyderm/pachyderm/v2/src/client"
"github.com/pachyderm/pachyderm/v2/src/pfs"
)
脚本的下一部分定义了一个main
函数,执行以下操作:
-
连接到 Pachyderm 集群。
-
创建了一个名为
photos
的仓库。 -
列出了该集群上的所有仓库。
它的样子如下:
func main() {
c, err := client.NewOnUserMachine("user")
if err != nil {
log.Fatal(err)
}
if _, err := c.PfsAPIClient.CreateRepo(
c.Ctx(),
&pfs.CreateRepoRequest{
Repo: client.NewRepo("photos"),
Description: "A repository that stores images.",
Update: true,
},
); err != nil {
panic(err)
}
repos, err := c.ListRepo()
if err != nil {
log.Fatal(err)
}
fmt.Println(repos)
}
你必须运行端口转发,并确保将脚本中列出的 IP 地址替换为你的集群 IP 地址。如果你在minikube
中运行集群,你可能不需要更改任何内容。
-
运行
create-repo.go
脚本,具体如下:go run create-repo.go
该命令返回以下输出:
[repo:<name:"photos" type:"user" > created:<seconds:1637264349 nanos:440180000 > description:"A repository that stores images." auth_info:<permissions:REPO_READ permissions:REPO_INSPECT_COMMIT permissions:REPO_LIST_COMMIT permissions:REPO_LIST_BRANCH permissions:REPO_LIST_FILE permissions:REPO_INSPECT_FILE permissions:REPO_ADD_PIPELINE_READER permissions:REPO_REMOVE_PIPELINE_READER permissions:PIPELINE_LIST_JOB permissions:REPO_WRITE permissions:REPO_DELETE_COMMIT permissions:REPO_CREATE_BRANCH permissions:REPO_DELETE_BRANCH permissions:REPO_ADD_PIPELINE_WRITER permissions:REPO_MODIFY_BINDINGS permissions:REPO_DELETE roles:"repoOwner" > ]
现在我们已经创建了一个仓库,接下来让我们往里面添加一些数据。
使用 Go 客户端将数据放入 Pachyderm 仓库
在前一部分中,我们创建了一个名为photos
的 Pachyderm 仓库。接下来,我们将把我们在第六章中使用的文件,创建你的第一个管道,添加到这个仓库。我们将使用put-files.go
脚本添加这些文件。下面是脚本导入的内容:
package main
import (
"fmt"
"log"
"os"
"github.com/pachyderm/pachyderm/v2/src/client"
)
脚本的下一部分连接到 Pachyderm 集群,并将landscape.png
、red_vase.png
和hand.png
文件添加到photos
仓库的master
分支中。
这是连接到仓库的部分。确保你将 IP 地址替换为集群的地址:
func main() {
c, err := client.NewOnUserMachine("user")
if err != nil {
log.Fatal(err)
}
这一部分添加了文件:
myCommit := client.NewCommit("photos","master", "")
f1, err := os.Open("landscape.png")
if err != nil {
panic(err)
}
if err := c.PutFile(myCommit, "landscape.png", f1); err != nil {
panic(err)}
f2, err := os.Open("brown_vase.png")
if err != nil {
panic(err)
}
if err := c.PutFile(myCommit, "brown_vase.png", f2); err != nil {
panic(err)
}
f3, err := os.Open("hand.png")
if err != nil {
panic(err)
}
if err := c.PutFile(myCommit, "hand.png", f3); err != nil {
panic(err)
}
最后一部分,如下所示,列出了photos
仓库master
分支中的文件:
files, err := c.ListFileAll(myCommit, "/")
if err != nil {
panic(err)
}
fmt.Println(files)
}
让我们通过以下命令运行这个脚本:
go run put-files.go
这个脚本返回了以下输出:
[file:<commit:<branch:<repo:<name:"photos" type:"user" > name:"master" > id:"2c15226b838f48cabd2ae13b43c26517" > path:"/brown_vase.png" datum:"default" > file_type:FILE committed:<seconds:1637299733 nanos:503150000 > size_bytes:93481 hash:"\2061\023\026\376O&\323\313\212\215\226Ra\346\245=Er\r_@E\023\360\352\240\275}\204\235\346" file:<commit:<branch:<repo:<name:"photos" type:"user" > name:"master" > id:"2c15226b838f48cabd2ae13b43c26517" > path:"/hand.png" datum:"default" > file_type:FILE committed:<seconds:1637299733 nanos:503150000 > size_bytes:856063 hash:"\014X\224\032\0251\260(\263\267\234\345{\016\353a\0310\3579\354\323\372\013\357yFg\274\256\000}" file:<commit:<branch:<repo:<name:"photos" type:"user" > name:"master" > id:"2c15226b838f48cabd2ae13b43c26517" > path:"/landscape.png" datum:"default" > file_type:FILE committed:<seconds:1637299733 nanos:503150000 > size_bytes:54009 hash:"\320:\265\036\3363z&\264\324]\364unfv\243\300\001[\206\347\344b\257\274\366\220JnR\004" ]
太好了!我们已经有一个包含数据的仓库了。现在,让我们学习如何创建管道。
使用 Go 客户端创建管道
最后,我们可以从第六章,创建你的第一个管道,为我们的示例创建管道。
下面是create-pipeline.go
脚本导入的内容:
package main
import (
"fmt"
"log"
"github.com/pachyderm/pachyderm/v2/src/client"
"github.com/pachyderm/pachyderm/v2/src/pps""
)
脚本的第二部分通过使用pachd
的 IP 地址连接到 Pachyderm 集群,具体如下:
func main() {
c, err := client.NewFromAddress("127.0.0.1:30650")
if err != nil {
log.Fatal(err)
}
脚本的下一部分创建了一个contour
管道。你可以看到脚本使用了svekars/contour-histogram:1.0
镜像,并通过/
全局模式从photos
仓库中获取数据。这里需要注意的一点是,你需要为所有管道指定parallelism_spec
:
if err := c.CreatePipeline(
"contour",
"svekars/contour-histogram:1.0 ",
[]string{"python3", "/contour.py"},
[]string{},
&pps.ParallelismSpec{
Constant: 1,
},
client.NewPFSInput("photos", "/"),
"",
false,
); err != nil {
panic(err)
}
接下来,脚本创建了一个histogram
管道,具体如下:
if err := c.CreatePipeline(
"histogram",
"svekars/contour-histogram:1.0",
[]string{"python3", "/histogram.py"},
[]string{},
&pps.ParallelismSpec{
Constant: 1,
},
client.NewPFSInput("contour", "/"),
"",
false,
); err != nil {
panic(err)
}
最后,脚本列出了所有已创建的管道,具体如下:
pipelines, err := c.ListPipeline(true)
if err != nil {
panic(err)
}
fmt.Println(pipelines)
}
运行以下命令:
go run create-pipeline.go
这是一个示例响应:
[pipeline:<name:"histogram" > version:1 spec_commit:<branch:<repo:<name:"histogram" type:"spec" > name:"master" > id:"44945b0d0e2944e3b1015617e224e3e3" > state:PIPELINE_STARTING job_counts:<key:1 value:1 > last_job_state:JOB_CREATED parallelism:1 type:PIPELINE_TYPE_TRANSFORM details:<transform:<image:"svekars/contour-histogram:1.0" cmd:"python3" cmd:"/histogram.py" > parallelism_spec:<constant:1 > created_at:<seconds:1637300756 nanos:806783300 > output_branch:"master" input:<pfs:<name:"contour" repo:"contour" repo_type:"user" branch:"master" glob:"/" > > salt:"0715a02027ba4489a79bd8a400f349ad" datum_tries:3 reprocess_spec:"until_success" >
pipeline:<name:"contour" > version:1 spec_commit:<branch:<repo:<name:"contour" type:"spec" > name:"master" > id:"f3f8bf226e5a4dda8a9f27da10b7fd87" > state:PIPELINE_STARTING job_counts:<key:1 value:1 > last_job_state:JOB_CREATED parallelism:1 type:PIPELINE_TYPE_TRANSFORM details:<transform:<image:"svekars/contour-histogram:1.0 " cmd:"python3" cmd:"/contour.py" > parallelism_spec:<constant:1 > created_at:<seconds:1637300756 nanos:592992600 > output_branch:"master" input:<pfs:<name:"photos" repo:"photos" repo_type:"user" branch:"master" glob:"/" > > salt:"98c0a867ea56439eb1f2466fbf1aa838" datum_tries:3 reprocess_spec:"until_success" > ]
你可以看到脚本已经创建了我们预期的两个管道。我们已经将整个示例上传到这一章的 GitHub 仓库中的contour-go-example.go
文件里。现在你已经学会了如何做,你可以只运行这个脚本,通过一个命令创建一个完整的轮廓管道示例。接下来,我们将学习如何清理我们的集群。
使用 Go 客户端清理集群
cleanup.go
脚本会清理集群并删除所有管道、数据和仓库。只有在你不再希望保留数据时,才运行它。
这个脚本只需要从 Pachyderm 仓库中导入客户端。为此,需要以下代码:
package main
import (
"fmt"
"log"
"github.com/pachyderm/pachyderm/v2/src/client"
)
脚本的下一部分删除所有的仓库和管道。我们将所有管道和仓库的 force
标志设置为 true
,以便 Pachyderm 不会因为下游管道的依赖关系而中断删除。代码如下所示:
if err := c.DeleteRepo("contour", true); err != nil {
panic(err)
}
if err := c.DeleteRepo("photos", true); err != nil {
panic(err)
}
if err := c.DeleteRepo("histogram", true); err != nil {
panic(err)
}
if err := c.DeletePipeline("contour", true); err != nil {
panic(err)
}
if err := c.DeletePipeline("histogram", true); err != nil {
panic(err)
}
脚本的最后部分返回空列表,因为我们删除了所有的管道和仓库,如以下代码片段所示:
pipelines, err := c.ListPipeline(true)
if err != nil {
panic(err)
}
fmt.Println(pipelines)
repos, err := c.ListRepo()
if err != nil {
log.Fatal(err)
}
fmt.Println(repos)
}
运行以下命令:
go run cleanup.go
这个命令会返回以下输出:
[]
[]
在这一部分中,我们学习了如何使用 Go 客户端创建 Pachyderm 管道和仓库。接下来,让我们学习如何使用 Pachyderm Python 客户端来实现这一点。
使用 Pachyderm Python 客户端
Python 可能是软件工程和数据科学社区中最受欢迎的编程语言之一。Pachyderm 通过 python-pachyderm
包提供了一个官方支持的 Python 客户端。你可以在 GitHub 上找到 Python Pachyderm 的源代码仓库,网址为 github.com/pachyderm/python-pachyderm
,也可以在 Python 包索引 (PyPI) 上找到,网址为 pypi.org/project/python-pachyderm/
。
作为 Python 客户端参考的主要文件位于 Pachyderm 源代码仓库的 github.com/pachyderm/python-pachyderm/tree/master/src/python_pachyderm/mixin
目录下,最重要的文件如下:
-
github.com/pachyderm/python-pachyderm/blob/master/src/python_pachyderm/client.py
-
github.com/pachyderm/python-pachyderm/blob/master/src/python_pachyderm/mixin/pfs.py
-
github.com/pachyderm/python-pachyderm/blob/master/src/python_pachyderm/mixin/pps.py
在继续之前,你的机器上必须配置以下组件:
-
Pachyderm 仓库的一个副本(参见 克隆 Pachyderm 源代码仓库 部分)。使用 Python Pachyderm,你可以将仓库克隆到机器上的任何目录。它不必在
$GOPATH
中。 -
机器上安装了 Python 3.6 或更高版本。
-
访问一个活动的 Pachyderm 集群。如果是本地安装,你需要在通过 API 与仓库交互时保持 Pachyderm 的端口转发始终运行。如果是云安装,你需要启用负载均衡器以允许访问集群,或者你也可能能够使用 Pachyderm 的端口转发。
我们已经回顾了本节的先决条件。现在,让我们安装 python-pachyderm
客户端。
安装 Pachyderm Python 客户端
在开始使用 Pachyderm Python 客户端之前,你需要在你的机器上安装它。
要安装 Python Pachyderm 客户端,请完成以下步骤:
-
打开一个终端窗口。
-
如果你使用的是 macOS 或 Linux,请运行以下命令:
pip install python-pachyderm
你应该看到以下输出:
Collecting python-pachyderm
Downloading python-pachyderm-6.2.0.tar.gz (409 kB)
...
Successfully installed grpcio-1.38.0 protobuf-3.17.0 python-pachyderm-6.2.0
你安装的 python-pachyderm
包的版本可能不同。
现在我们已经安装了 python-pachyderm
,让我们通过使用 python-pachyderm
连接到 Pachyderm。
使用 Python 客户端连接到你的 Pachyderm 集群
为了开始,我们使用 access.py
脚本连接到你的集群。确保端口转发在你的机器上正在运行。这里是脚本:
import python_pachyderm
client = python_pachyderm.Client()
print(client.get_remote_version())
这个脚本通过使用 python_pachyderm.Client()
调用连接到运行在 localhost:30650
上的 pachd
,并打印出你正在运行的 Pachyderm 版本。
让我们运行这个脚本,看看它返回什么输出。
使用以下命令运行 access.py
脚本:
python access.py
你应该看到类似于以下的输出:
major: 2
micro: 1
这个输出意味着我们正在使用版本 2.0.1。你的输出可能不同。
现在我们知道如何访问集群了,接下来让我们创建一个 Pachyderm 仓库。
使用 Python 客户端创建一个 Pachyderm 仓库
我们将使用 create-repo.py
脚本创建一个名为 photos
的 Pachyderm 仓库。
这是脚本的代码:
import python_pachyderm
client = python_pachyderm.Client()
client.create_repo("photos")
print(list(client.list_repo()))
使用以下命令运行 create-repo.py
脚本:
python create-repo.py
这是一个示例输出:
repo {
name: "photos"
type: "user"
}
created {
seconds: 1637207890
nanos: 80987000
}
auth_info {
permissions: REPO_READ
permissions: REPO_INSPECT_COMMIT
...
现在我们已经创建了一个仓库,让我们把一些数据放入其中。
使用 Python 客户端将数据放入 Pachyderm 仓库
我们将把在 [第六章 中使用的相同文件,即 创建你的第一个管道,放入我们刚刚创建的 photos
仓库中。以下是我们将使用的脚本:
import python_pachyderm
client = python_pachyderm.Client()
with client.commit('photos', 'master') as i:
client.put_file_url(i, 'landscape.png', 'https://i.imgur.com/zKo9Mdl.jpg')
client.put_file_url(i, 'hand.png', 'https://i.imgur.com/HtZ8FyG.png')
client.put_file_url(i, 'red_vase.png', 'https://i.imgur.com/d45jop9.jpg') print(list(client.list_file(("photos","master"), "/")))
该脚本使用 client.commit
方法开始提交到 photos
仓库的主分支,client.put_file_bytes
向仓库添加三个文件。请注意,client.list_file
需要是 list
类型,而不是 string
,以便命令能够正确运行。
让我们运行这个脚本。
使用以下命令运行 put-files.py
脚本:
python put-files.py
这是你应该得到的系统响应:
[file {
commit {
branch {
repo {
name: "photos"
type: "user"
}
name: "master"
}
id: "e29c6f5c49244ce193fe5f86c9df0297"
}
path: "/hand.png"
datum: "default"
}
file_type: FILE
committed {
seconds: 1637208291
nanos: 161527000
}
size_bytes: 856063
hash: "\014X\224\032\0251\260(\263\267\234\345{\016\353a\0310\3579\354\323\372\013\357yFg\274\256\000}"
...
]
上面的输出被截断了。你应该会看到我们添加到仓库的每个文件的相同输出。
现在我们已经添加了文件,让我们为这个示例创建管道。
使用 Pachyderm Python 客户端创建管道
现在我们已经上传了仓库和文件,让我们使用create-pipeline.py
脚本根据我们在第六章中介绍的示例,创建两个管道,创建你的第一个管道。
python-pachyderm
提供了两种方法来创建管道,具体如下:
-
create_pipeline
:此方法适用于所有语言,等同于pachctl create pipeline
方法。 -
create_python_pipeline
:此管道旨在与 Python 代码一起运行,并提供稍微不同的用户体验(UX)。你可以在 Pachyderm 文档中了解更多关于此方法的信息,访问docs.pachyderm.com
。
我们将使用标准的create_pipeline
方法来创建这个管道。
脚本的第一部分创建一个contour
管道,代码如下:
import python_pachyderm
from python_pachyderm.service import pps_proto
client = python_pachyderm.Client()
client.create_pipeline(
pipeline_name="contour",
transform=pps_proto.Transform(
cmd=["python3", "contour.py"],
image="svekars/contour-histogram:1.0",
),
input=pps_proto.Input(
pfs=pps_proto.PFSInput(glob="/", repo="photos")
),
)
脚本的第二部分创建一个histogram
管道,代码如下:
client.create_pipeline(
pipeline_name="histogram",
transform=pps_proto.Transform(
cmd=["python3", "histogram.py"],
image="svekars/contour-histogram:1.0",
),
input=pps_proto.Input(
pfs=pps_proto.PFSInput(glob="/", repo="contour")
),
)
脚本的最后一部分返回一个列表,显示已创建的管道,如以下代码片段所示:
Print(list(client.list_pipeline()))
让我们运行这个脚本。
使用以下命令运行create-pipeline.py
脚本:
python create-pipeline.py
这是输出的一部分:
[pipeline {
name: "histogram"
}
version: 1
spec_commit {
branch {
repo {
name: "histogram"
type: "spec"
}
name: "master"
}
id: "94286ef36318425c8177bd4e0f959c57"
}
state: PIPELINE_STARTING
job_counts {
key: 1
value: 1
}...
在本节中,我们已经学习了如何使用python-pachyderm
客户端创建管道。接下来,让我们清理我们的集群。
使用 Python 客户端清理集群
我们已经成功地重建了我们的轮廓(contour)和直方图(histogram)管道示例。整个示例作为一个文件contour-histogram-example.py
,可以在 GitHub 代码库中找到。你可以通过github.com/PacktPublishing/Reproducible-Data-Science-with-Pachyderm/tree/main/Chapter10-Pachyderm-Language-Clients
下载并根据需要多次重建。
在本节中,我们将清理集群,以便为第十一章《使用 Pachyderm 笔记本》提供一个干净的安装环境。我们将使用cleanup.py
脚本来实现这一点,代码如下:
import python_pachyderm
client.delete_repo("photos", force=True)
client.delete_pipeline(pipeline_name="contour", force=True, keep_repo=False)
client.delete_pipeline(pipeline_name="histogram", force=True, keep_repo=False)
print(list(client.list_repo()))
print(list(client.list_pipeline()))
该脚本使用了delete_all_pipelines
方法,它会删除集群中的所有管道。你还可以使用delete_all
来删除集群中的所有对象和原始数据。
让我们运行这个脚本。
使用以下命令运行cleanup.py
脚本:
python cleanup.py
该命令应该返回以下输出:
[]
[]
就这样!我们已经成功地清理了我们的集群。
总结
在本章中,我们学习了如何使用两个官方支持的 Pachyderm 语言客户端——Pachyderm Go 客户端和 Python 客户端。我们学习了如何克隆 Pachyderm 代码库并切换到正确的分支和标签。我们学习了如何连接、创建仓库、将文件放入仓库并创建管道,以及完成后删除所有对象。你可以使用这两个语言客户端做更多事情,但本章中的示例为你提供了如何使用它们的一般思路。
在下一章节,我们将学习如何将 Pachyderm 与流行的数据科学 python-pachyderm
客户端集成到 JupyterHub 中。
进一步阅读
-
使用 Traefik 设置 Ingress 以访问 Pachyderm UI:
docs.pachyderm.com/latest/deploy-manage/deploy/ingress/pach-ui-ingress/#traefik-ingress-controller-on-pachyderm-uis-cluster-in-one-diagram
-
使用 SSH 连接 GitHub:
docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh
-
Go 文档:
golang.org/doc/
-
Python 3 文档:
docs.python.org/3/
-
Python Pachyderm 客户端文档:
python-pachyderm.readthedocs.io/en/stable/