Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(八)(3)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析DNS,个人版 1个月
云解析 DNS,旗舰版 1个月
简介: Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(八)

Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(八)(2)https://developer.aliyun.com/article/1482462


安装和启动 TensorFlow Serving

有许多安装 TF Serving 的方法:使用系统的软件包管理器,使用 Docker 镜像,从源代码安装等。由于 Colab 运行在 Ubuntu 上,我们可以像这样使用 Ubuntu 的apt软件包管理器:

url = "https://storage.googleapis.com/tensorflow-serving-apt"
src = "stable tensorflow-model-server tensorflow-model-server-universal"
!echo 'deb {url} {src}' > /etc/apt/sources.list.d/tensorflow-serving.list
!curl '{url}/tensorflow-serving.release.pub.gpg' | apt-key add -
!apt update -q && apt-get install -y tensorflow-model-server
%pip install -q -U tensorflow-serving-api

这段代码首先将 TensorFlow 的软件包存储库添加到 Ubuntu 的软件包源列表中。然后它下载 TensorFlow 的公共 GPG 密钥,并将其添加到软件包管理器的密钥列表中,以便验证 TensorFlow 的软件包签名。接下来,它使用apt来安装tensorflow-model-server软件包。最后,它安装tensorflow-serving-api库,这是我们与服务器通信所需的库。

现在我们想要启动服务器。该命令将需要基本模型目录的绝对路径(即my_mnist_model的路径,而不是0001),所以让我们将其保存到MODEL_DIR环境变量中:

import os
os.environ["MODEL_DIR"] = str(model_path.parent.absolute())

然后我们可以启动服务器:

%%bash --bg
tensorflow_model_server \
     --port=8500 \
     --rest_api_port=8501 \
     --model_name=my_mnist_model \
     --model_base_path="${MODEL_DIR}" >my_server.log 2>&1

在 Jupyter 或 Colab 中,%%bash --bg魔术命令将单元格作为 bash 脚本执行,在后台运行。>my_server.log 2>&1部分将标准输出和标准错误重定向到my_server.log文件。就是这样!TF Serving 现在在后台运行,其日志保存在my_server.log中。它加载了我们的 MNIST 模型(版本 1),现在正在分别等待 gRPC 和 REST 请求,端口分别为 8500 和 8501。

现在服务器已经启动运行,让我们首先使用 REST API,然后使用 gRPC API 进行查询。

通过 REST API 查询 TF Serving

让我们从创建查询开始。它必须包含您想要调用的函数签名的名称,当然还有输入数据。由于请求必须使用 JSON 格式,我们必须将输入图像从 NumPy 数组转换为 Python 列表:

import json
X_new = X_test[:3]  # pretend we have 3 new digit images to classify
request_json = json.dumps({
    "signature_name": "serving_default",
    "instances": X_new.tolist(),
})

请注意,JSON 格式是 100%基于文本的。请求字符串如下所示:

>>> request_json
'{"signature_name": "serving_default", "instances": [[[0, 0, 0, 0, ... ]]]}'

现在让我们通过 HTTP POST 请求将这个请求发送到 TF Serving。这可以使用requests库来完成(它不是 Python 标准库的一部分,但在 Colab 上是预安装的):

import requests
server_url = "http://localhost:8501/v1/models/my_mnist_model:predict"
response = requests.post(server_url, data=request_json)
response.raise_for_status()  # raise an exception in case of error
response = response.json()

如果一切顺利,响应应该是一个包含单个"predictions"键的字典。相应的值是预测列表。这个列表是一个 Python 列表,所以让我们将其转换为 NumPy 数组,并将其中包含的浮点数四舍五入到第二位小数:

>>> import numpy as np
>>> y_proba = np.array(response["predictions"])
>>> y_proba.round(2)
array([[0\.  , 0\.  , 0\.  , 0\.  , 0\.  , 0\.  , 0\.  , 1\.  , 0\.  , 0\.  ],
 [0\.  , 0\.  , 0.99, 0.01, 0\.  , 0\.  , 0\.  , 0\.  , 0\.  , 0\.  ],
 [0\.  , 0.97, 0.01, 0\.  , 0\.  , 0\.  , 0\.  , 0.01, 0\.  , 0\.  ]])

万岁,我们有了预测!模型几乎 100%确信第一张图片是 7,99%确信第二张图片是 2,97%确信第三张图片是 1。这是正确的。

REST API 简单易用,当输入和输出数据不太大时效果很好。此外,几乎任何客户端应用程序都可以在没有额外依赖的情况下进行 REST 查询,而其他协议并不总是那么容易获得。然而,它基于 JSON,这是基于文本且相当冗长的。例如,我们不得不将 NumPy 数组转换为 Python 列表,每个浮点数最终都表示为一个字符串。这非常低效,无论是在序列化/反序列化时间方面——我们必须将所有浮点数转换为字符串然后再转回来——还是在有效载荷大小方面:许多浮点数最终使用超过 15 个字符来表示,这相当于 32 位浮点数超过 120 位!这将导致在传输大型 NumPy 数组时出现高延迟和带宽使用。因此,让我们看看如何改用 gRPC。

提示

在传输大量数据或延迟重要时,最好使用 gRPC API,如果客户端支持的话,因为它使用紧凑的二进制格式和基于 HTTP/2 framing 的高效通信协议。

通过 gRPC API 查询 TF Serving

gRPC API 期望一个序列化的PredictRequest协议缓冲区作为输入,并输出一个序列化的PredictResponse协议缓冲区。这些 protobufs 是tensorflow-serving-api库的一部分,我们之前安装过。首先,让我们创建请求:

from tensorflow_serving.apis.predict_pb2 import PredictRequest
request = PredictRequest()
request.model_spec.name = model_name
request.model_spec.signature_name = "serving_default"
input_name = model.input_names[0]  # == "flatten_input"
request.inputs[input_name].CopyFrom(tf.make_tensor_proto(X_new))

这段代码创建了一个PredictRequest协议缓冲区,并填充了必需的字段,包括模型名称(之前定义的),我们想要调用的函数的签名名称,最后是输入数据,以Tensor协议缓冲区的形式。tf.make_tensor_proto()函数根据给定的张量或 NumPy 数组创建一个Tensor协议缓冲区,这里是X_new

接下来,我们将向服务器发送请求并获取其响应。为此,我们将需要grpcio库,该库已预先安装在 Colab 中:

import grpc
from tensorflow_serving.apis import prediction_service_pb2_grpc
channel = grpc.insecure_channel('localhost:8500')
predict_service = prediction_service_pb2_grpc.PredictionServiceStub(channel)
response = predict_service.Predict(request, timeout=10.0)

代码非常简单:在导入之后,我们在 TCP 端口 8500 上创建一个到localhost的 gRPC 通信通道,然后我们在该通道上创建一个 gRPC 服务,并使用它发送一个带有 10 秒超时的请求。请注意,调用是同步的:它将阻塞,直到收到响应或超时期限到期。在此示例中,通道是不安全的(没有加密,没有身份验证),但 gRPC 和 TF Serving 也支持通过 SSL/TLS 的安全通道。

接下来,让我们将PredictResponse协议缓冲区转换为张量:

output_name = model.output_names[0]  # == "dense_1"
outputs_proto = response.outputs[output_name]
y_proba = tf.make_ndarray(outputs_proto)

如果您运行此代码并打印y_proba.round(2),您将获得与之前完全相同的估计类概率。这就是全部内容:只需几行代码,您现在就可以远程访问您的 TensorFlow 模型,使用 REST 或 gRPC。

部署新的模型版本

现在让我们创建一个新的模型版本并导出一个 SavedModel,这次导出到my_mnist_model/0002目录:

model = [...]  # build and train a new MNIST model version
model_version = "0002"
model_path = Path(model_name) / model_version
model.save(model_path, save_format="tf")

在固定的时间间隔(延迟可配置),TF Serving 会检查模型目录是否有新的模型版本。如果找到一个新版本,它会自动优雅地处理过渡:默认情况下,它会用前一个模型版本回答待处理的请求(如果有的话),同时用新版本处理新请求。一旦每个待处理的请求都得到回答,之前的模型版本就会被卸载。您可以在 TF Serving 日志(my_server.log)中看到这个过程:

[...]
Reading SavedModel from: /models/my_mnist_model/0002
Reading meta graph with tags { serve }
[...]
Successfully loaded servable version {name: my_mnist_model version: 2}
Quiescing servable version {name: my_mnist_model version: 1}
Done quiescing servable version {name: my_mnist_model version: 1}
Unloading servable version {name: my_mnist_model version: 1}
提示

如果 SavedModel 包含assets/extra目录中的一些示例实例,您可以配置 TF Serving 在开始使用它来处理请求之前在这些实例上运行新模型。这称为模型预热:它将确保一切都被正确加载,避免第一次请求的长响应时间。

这种方法提供了平稳的过渡,但可能会使用过多的 RAM,特别是 GPU RAM,通常是最有限的。在这种情况下,您可以配置 TF Serving,使其处理所有挂起的请求与先前的模型版本,并在加载和使用新的模型版本之前卸载它。这种配置将避免同时加载两个模型版本,但服务将在短时间内不可用。

正如您所看到的,TF Serving 使部署新模型变得简单。此外,如果您发现第二个版本的效果不如预期,那么回滚到第一个版本就像删除my_mnist_model/0002目录一样简单。

提示

TF Serving 的另一个重要特性是其自动批处理能力,您可以在启动时使用--enable_batching选项来激活它。当 TF Serving 在短时间内接收到多个请求时(延迟可配置),它会在使用模型之前自动将它们批处理在一起。通过利用 GPU 的性能,这将显著提高性能。一旦模型返回预测结果,TF Serving 会将每个预测结果分发给正确的客户端。通过增加批处理延迟(参见--batching_parameters_file选项),您可以在一定程度上牺牲一点延迟以获得更大的吞吐量。

如果您希望每秒获得许多查询,您将希望在多台服务器上部署 TF Serving 并负载平衡查询(请参见图 19-2)。这将需要在这些服务器上部署和管理许多 TF Serving 容器。处理这一问题的一种方法是使用诸如Kubernetes之类的工具,它是一个简化跨多台服务器容器编排的开源系统。如果您不想购买、维护和升级所有硬件基础设施,您将希望在云平台上使用虚拟机,如 Amazon AWS、Microsoft Azure、Google Cloud Platform、IBM Cloud、Alibaba Cloud、Oracle Cloud 或其他平台即服务(PaaS)提供商。管理所有虚拟机,处理容器编排(即使借助 Kubernetes 的帮助),照顾 TF Serving 配置、调整和监控——所有这些都可能成为一项全职工作。幸运的是,一些服务提供商可以为您处理所有这些事务。在本章中,我们将使用 Vertex AI:它是今天唯一支持 TPUs 的平台;它支持 TensorFlow 2、Scikit-Learn 和 XGBoost;并提供一套不错的人工智能服务。在这个领域还有其他几家提供商也能够提供 TensorFlow 模型的服务,比如 Amazon AWS SageMaker 和 Microsoft AI Platform,所以请确保也查看它们。

图 19-2。使用负载平衡扩展 TF Serving

现在让我们看看如何在云上提供我们出色的 MNIST 模型!

在 Vertex AI 上创建一个预测服务

Vertex AI 是 Google Cloud Platform(GCP)内的一个平台,提供各种与人工智能相关的工具和服务。您可以上传数据集,让人类对其进行标记,将常用特征存储在特征存储中,并将其用于训练或生产中,使用多个 GPU 或 TPU 服务器进行模型训练,并具有自动超参数调整或模型架构搜索(AutoML)功能。您还可以管理已训练的模型,使用它们对大量数据进行批量预测,为数据工作流程安排多个作业,通过 REST 或 gRPC 以规模化方式提供模型服务,并在名为Workbench的托管 Jupyter 环境中对数据和模型进行实验。甚至还有一个Matching Engine服务,可以非常高效地比较向量(即,近似最近邻)。GCP 还包括其他 AI 服务,例如计算机视觉、翻译、语音转文本等 API。

在我们开始之前,有一些设置需要处理:

  1. 登录您的 Google 账户,然后转到Google Cloud Platform 控制台(参见图 19-3)。如果您没有 Google 账户,您将需要创建一个。
  2. 如果这是您第一次使用 GCP,您将需要阅读并接受条款和条件。新用户可以获得免费试用,包括价值 300 美元的 GCP 信用,您可以在 90 天内使用(截至 2022 年 5 月)。您只需要其中的一小部分来支付本章中将使用的服务。注册免费试用后,您仍然需要创建一个付款配置文件并输入您的信用卡号码:这是用于验证目的——可能是为了避免人们多次使用免费试用,但您不会被收取前 300 美元的费用,之后只有在您选择升级到付费账户时才会收费。

    图 19-3. Google Cloud Platform 控制台
  3. 如果您以前使用过 GCP 并且您的免费试用已经过期,那么您在本章中将使用的服务将会花费一些钱。这不应该太多,特别是如果您记得在不再需要这些服务时关闭它们。在运行任何服务之前,请确保您理解并同意定价条件。如果服务最终花费超出您的预期,我在此不承担任何责任!还请确保您的计费账户是活动的。要检查,请打开左上角的☰导航菜单,点击计费,然后确保您已设置付款方式并且计费账户是活动的。
  4. GCP 中的每个资源都属于一个 项目。这包括您可能使用的所有虚拟机、存储的文件和运行的训练作业。当您创建一个帐户时,GCP 会自动为您创建一个名为“我的第一个项目”的项目。如果您愿意,可以通过转到项目设置来更改其显示名称:在 ☰ 导航菜单中,选择“IAM 和管理员 → 设置”,更改项目的显示名称,然后单击“保存”。请注意,项目还有一个唯一的 ID 和编号。您可以在创建项目时选择项目 ID,但以后无法更改。项目编号是自动生成的,无法更更改。如果您想创建一个新项目,请单击页面顶部的项目名称,然后单击“新项目”并输入项目名称。您还可以单击“编辑”来设置项目 ID。确保此新项目的计费处于活动状态,以便可以对服务费用进行计费(如果有免费信用)。
    警告
    请始终设置提醒,以便在您知道只需要几个小时时关闭服务,否则您可能会让其运行数天或数月,从而产生潜在的显著成本。
  5. 现在您已经拥有 GCP 帐户和项目,并且计费已激活,您必须激活所需的 API。在☰导航菜单中,选择“API 和服务”,确保启用了 Cloud Storage API。如果需要,点击+启用 API 和服务,找到 Cloud Storage,并启用它。还要启用 Vertex AI API。

您可以继续通过 GCP 控制台完成所有操作,但我建议改用 Python:这样您可以编写脚本来自动化几乎任何您想要在 GCP 上完成的任务,而且通常比通过菜单和表单点击更方便,特别是对于常见任务。

在您使用任何 GCP 服务之前,您需要做的第一件事是进行身份验证。在使用 Colab 时最简单的解决方案是执行以下代码:

from google.colab import auth
auth.authenticate_user()

认证过程基于 OAuth 2.0:一个弹出窗口会要求您确认您希望 Colab 笔记本访问您的 Google 凭据。如果您接受,您必须选择与 GCP 相同的 Google 帐户。然后,您将被要求确认您同意授予 Colab 对 Google Drive 和 GCP 中所有数据的完全访问权限。如果您允许访问,只有当前笔记本将具有访问权限,并且仅在 Colab 运行时到期之前。显然,只有在您信任笔记本中的代码时才应接受此操作。

警告

如果您使用来自 https://github.com/ageron/handson-ml3 的官方笔记本,则应格外小心:如果笔记本的作者心怀不轨,他们可能包含代码来对您的数据进行任何操作。

现在让我们创建一个 Google Cloud Storage 存储桶来存储我们的 SavedModels(GCS 的存储桶是您数据的容器)。为此,我们将使用预先安装在 Colab 中的google-cloud-storage库。我们首先创建一个Client对象,它将作为与 GCS 的接口,然后我们使用它来创建存储桶:

from google.cloud import storage
project_id = "my_project"  # change this to your project ID
bucket_name = "my_bucket"  # change this to a unique bucket name
location = "us-central1"
storage_client = storage.Client(project=project_id)
bucket = storage_client.create_bucket(bucket_name, location=location)
提示

如果您想重用现有的存储桶,请将最后一行替换为bucket = storage_client.bucket(bucket_name)。确保location设置为存储桶的地区。

GCS 使用单个全球命名空间用于存储桶,因此像“machine-learning”这样的简单名称很可能不可用。确保存储桶名称符合 DNS 命名约定,因为它可能在 DNS 记录中使用。此外,存储桶名称是公开的,因此不要在名称中放入任何私人信息。通常使用您的域名、公司名称或项目 ID 作为前缀以确保唯一性,或者只需在名称中使用一个随机数字。

如果您想要,可以更改区域,但请确保选择支持 GPU 的区域。此外,您可能需要考虑到不同区域之间价格差异很大,一些区域产生的 CO₂比其他区域多得多,一些区域不支持所有服务,并且使用单一区域存储桶可以提高性能。有关更多详细信息,请参阅Google Cloud 的区域列表Vertex AI 的位置文档。如果您不确定,最好选择"us-central1"

接下来,让我们将my_mnist_model目录上传到新的存储桶。在 GCS 中,文件被称为blobs(或objects),在幕后它们都只是放在存储桶中,没有任何目录结构。Blob 名称可以是任意的 Unicode 字符串,甚至可以包含斜杠(/)。GCP 控制台和其他工具使用这些斜杠来产生目录的幻觉。因此,当我们上传my_mnist_model目录时,我们只关心文件,而不是目录。

def upload_directory(bucket, dirpath):
    dirpath = Path(dirpath)
    for filepath in dirpath.glob("**/*"):
        if filepath.is_file():
            blob = bucket.blob(filepath.relative_to(dirpath.parent).as_posix())
            blob.upload_from_filename(filepath)
upload_directory(bucket, "my_mnist_model")

这个函数现在运行良好,但如果有很多文件要上传,它会非常慢。通过多线程可以很容易地大大加快速度(请参阅笔记本中的实现)。或者,如果您有 Google Cloud CLI,则可以使用以下命令:

!gsutil -m cp -r my_mnist_model gs://{bucket_name}/

接下来,让我们告诉 Vertex AI 关于我们的 MNIST 模型。要与 Vertex AI 通信,我们可以使用google-cloud-aiplatform库(它仍然使用旧的 AI Platform 名称而不是 Vertex AI)。它在 Colab 中没有预安装,所以我们需要安装它。之后,我们可以导入该库并进行初始化——只需指定一些项目 ID 和位置的默认值——然后我们可以创建一个新的 Vertex AI 模型:我们指定一个显示名称,我们模型的 GCS 路径(在这种情况下是版本 0001),以及我们希望 Vertex AI 使用的 Docker 容器的 URL 来运行此模型。如果您访问该 URL 并向上导航一个级别,您将找到其他可以使用的容器。这个支持带有 GPU 的 TensorFlow 2.8:

from google.cloud import aiplatform
server_image = "gcr.io/cloud-aiplatform/prediction/tf2-gpu.2-8:latest"
aiplatform.init(project=project_id, location=location)
mnist_model = aiplatform.Model.upload(
    display_name="mnist",
    artifact_uri=f"gs://{bucket_name}/my_mnist_model/0001",
    serving_container_image_uri=server_image,
)

现在让我们部署这个模型,这样我们就可以通过 gRPC 或 REST API 查询它以进行预测。为此,我们首先需要创建一个端点。这是客户端应用程序在想要访问服务时连接的地方。然后我们需要将我们的模型部署到这个端点:

endpoint = aiplatform.Endpoint.create(display_name="mnist-endpoint")
endpoint.deploy(
    mnist_model,
    min_replica_count=1,
    max_replica_count=5,
    machine_type="n1-standard-4",
    accelerator_type="NVIDIA_TESLA_K80",
    accelerator_count=1
)

这段代码可能需要几分钟才能运行,因为 Vertex AI 需要设置一个虚拟机。在这个例子中,我们使用一个相当基本的 n1-standard-4 类型的机器(查看https://homl.info/machinetypes 获取其他类型)。我们还使用了一个基本的 NVIDIA_TESLA_K80 类型的 GPU(查看https://homl.info/accelerators 获取其他类型)。如果您选择的区域不是 "us-central1",那么您可能需要将机器类型或加速器类型更改为该区域支持的值(例如,并非所有区域都有 Nvidia Tesla K80 GPU)。

注意

Google Cloud Platform 实施各种 GPU 配额,包括全球范围和每个地区:您不能在未经 Google 授权的情况下创建成千上万个 GPU 节点。要检查您的配额,请在 GCP 控制台中打开“IAM 和管理员 → 配额”。如果某些配额太低(例如,如果您需要在特定地区更多的 GPU),您可以要求增加它们;通常需要大约 48 小时。

Vertex AI 将最初生成最少数量的计算节点(在这种情况下只有一个),每当每秒查询次数变得过高时,它将生成更多节点(最多为您定义的最大数量,这种情况下为五个),并在它们之间负载均衡查询。如果一段时间内 QPS 速率下降,Vertex AI 将自动停止额外的计算节点。因此,成本直接与负载、您选择的机器和加速器类型以及您在 GCS 上存储的数据量相关。这种定价模型非常适合偶尔使用者和有重要使用高峰的服务。对于初创公司来说也是理想的:价格保持低延迟到公司真正开始运营。

恭喜,您已经将第一个模型部署到云端!现在让我们查询这个预测服务:

response = endpoint.predict(instances=X_new.tolist())

我们首先需要将要分类的图像转换为 Python 列表,就像我们之前使用 REST API 向 TF Serving 发送请求时所做的那样。响应对象包含预测结果,表示为 Python 浮点数列表的列表。让我们将它们四舍五入到两位小数并将它们转换为 NumPy 数组:

>>> import numpy as np
>>> np.round(response.predictions, 2)
array([[0\.  , 0\.  , 0\.  , 0\.  , 0\.  , 0\.  , 0\.  , 1\.  , 0\.  , 0\.  ],
 [0\.  , 0\.  , 0.99, 0.01, 0\.  , 0\.  , 0\.  , 0\.  , 0\.  , 0\.  ],
 [0\.  , 0.97, 0.01, 0\.  , 0\.  , 0\.  , 0\.  , 0.01, 0\.  , 0\.  ]])

是的!我们得到了与之前完全相同的预测结果。我们现在在云上有一个很好的预测服务,我们可以从任何地方安全地查询,并且可以根据 QPS 的数量自动扩展或缩小。当您使用完端点后,请不要忘记将其删除,以避免无谓地支付费用:

endpoint.undeploy_all()  # undeploy all models from the endpoint
endpoint.delete()

现在让我们看看如何在 Vertex AI 上运行作业,对可能非常大的数据批次进行预测。

在 Vertex AI 上运行批量预测作业

如果我们需要进行大量预测,那么我们可以请求 Vertex AI 为我们运行预测作业,而不是重复调用我们的预测服务。这不需要端点,只需要一个模型。例如,让我们在测试集的前 100 张图像上运行一个预测作业,使用我们的 MNIST 模型。为此,我们首先需要准备批处理并将其上传到 GCS。一种方法是创建一个文件,每行包含一个实例,每个实例都格式化为 JSON 值——这种格式称为 JSON Lines——然后将此文件传递给 Vertex AI。因此,让我们在一个新目录中创建一个 JSON Lines 文件,然后将此目录上传到 GCS:

batch_path = Path("my_mnist_batch")
batch_path.mkdir(exist_ok=True)
with open(batch_path / "my_mnist_batch.jsonl", "w") as jsonl_file:
    for image in X_test[:100].tolist():
        jsonl_file.write(json.dumps(image))
        jsonl_file.write("\n")
upload_directory(bucket, batch_path)

现在我们准备启动预测作业,指定作业的名称、要使用的机器和加速器的类型和数量,刚刚创建的 JSON Lines 文件的 GCS 路径,以及 Vertex AI 将保存模型预测的 GCS 目录的路径:

batch_prediction_job = mnist_model.batch_predict(
    job_display_name="my_batch_prediction_job",
    machine_type="n1-standard-4",
    starting_replica_count=1,
    max_replica_count=5,
    accelerator_type="NVIDIA_TESLA_K80",
    accelerator_count=1,
    gcs_source=[f"gs://{bucket_name}/{batch_path.name}/my_mnist_batch.jsonl"],
    gcs_destination_prefix=f"gs://{bucket_name}/my_mnist_predictions/",
    sync=True  # set to False if you don't want to wait for completion
)
提示

对于大批量数据,您可以将输入拆分为多个 JSON Lines 文件,并通过gcs_source参数列出它们。

这将需要几分钟的时间,主要是为了在 Vertex AI 上生成计算节点。一旦这个命令完成,预测将会以类似prediction.results-00001-of-00002的文件集合中可用。这些文件默认使用 JSON Lines 格式,每个值都是包含实例及其对应预测(即 10 个概率)的字典。实例按照输入的顺序列出。该作业还会输出prediction-errors文件,如果出现问题,这些文件对于调试可能会有用。我们可以使用batch_prediction_job.iter_outputs()迭代所有这些输出文件,所以让我们遍历所有的预测并将它们存储在y_probas数组中:

y_probas = []
for blob in batch_prediction_job.iter_outputs():
    if "prediction.results" in blob.name:
        for line in blob.download_as_text().splitlines():
            y_proba = json.loads(line)["prediction"]
            y_probas.append(y_proba)

现在让我们看看这些预测有多好:

>>> y_pred = np.argmax(y_probas, axis=1)
>>> accuracy = np.sum(y_pred == y_test[:100]) / 100
0.98

很好,98%的准确率!

JSON Lines 格式是默认格式,但是当处理大型实例(如图像)时,它太冗长了。幸运的是,batch_predict()方法接受一个instances_format参数,让您可以选择另一种格式。它默认为"jsonl",但您可以将其更改为"csv""tf-record""tf-record-gzip""bigquery""file-list"。如果将其设置为"file-list",那么gcs_source参数应指向一个文本文件,其中每行包含一个输入文件路径;例如,指向 PNG 图像文件。Vertex AI 将读取这些文件作为二进制文件,使用 Base64 对其进行编码,并将生成的字节字符串传递给模型。这意味着您必须在模型中添加一个预处理层来解析 Base64 字符串,使用tf.io.decode_base64()。如果文件是图像,则必须使用类似tf.io.decode_image()tf.io.decode_png()的函数来解析结果,如第十三章中所讨论的。

当您完成使用模型后,如果需要,可以通过运行mnist_model.delete()来删除它。您还可以删除在您的 GCS 存储桶中创建的目录,可选地删除存储桶本身(如果为空),以及批量预测作业。

for prefix in ["my_mnist_model/", "my_mnist_batch/", "my_mnist_predictions/"]:
    blobs = bucket.list_blobs(prefix=prefix)
    for blob in blobs:
        blob.delete()
bucket.delete()  # if the bucket is empty
batch_prediction_job.delete()

您现在知道如何将模型部署到 Vertex AI,创建预测服务,并运行批量预测作业。但是如果您想将模型部署到移动应用程序,或者嵌入式设备,比如加热控制系统、健身追踪器或自动驾驶汽车呢?

将模型部署到移动设备或嵌入式设备

机器学习模型不仅限于在拥有多个 GPU 的大型集中式服务器上运行:它们可以更接近数据源运行(这被称为边缘计算),例如在用户的移动设备或嵌入式设备中。去中心化计算并将其移向边缘有许多好处:它使设备即使未连接到互联网时也能智能化,通过不必将数据发送到远程服务器来减少延迟并减轻服务器负载,并且可能提高隐私性,因为用户的数据可以保留在设备上。

然而,将模型部署到边缘也有其缺点。与强大的多 GPU 服务器相比,设备的计算资源通常很少。一个大模型可能无法适应设备,可能使用过多的 RAM 和 CPU,并且可能下载时间过长。结果,应用可能变得无响应,设备可能会发热并迅速耗尽电池。为了避免这一切,您需要制作一个轻量级且高效的模型,而不会牺牲太多准确性。 TFLite库提供了几个工具,帮助您将模型部署到边缘,主要有三个目标:

  • 减小模型大小,缩短下载时间并减少 RAM 使用量。
  • 减少每次预测所需的计算量,以减少延迟、电池使用量和发热。
  • 使模型适应特定设备的限制。

为了减小模型大小,TFLite 的模型转换器可以接受 SavedModel 并将其压缩为基于 FlatBuffers 的更轻量级格式。这是一个高效的跨平台序列化库(有点像协议缓冲区),最初由谷歌为游戏创建。它设计成可以直接将 FlatBuffers 加载到 RAM 中,无需任何预处理:这样可以减少加载时间和内存占用。一旦模型加载到移动设备或嵌入式设备中,TFLite 解释器将执行它以进行预测。以下是如何将 SavedModel 转换为 FlatBuffer 并保存为 .tflite 文件的方法:

converter = tf.lite.TFLiteConverter.from_saved_model(str(model_path))
tflite_model = converter.convert()
with open("my_converted_savedmodel.tflite", "wb") as f:
    f.write(tflite_model)
提示

您还可以使用 tf.lite.TFLiteConverter.from_keras_model(model) 将 Keras 模型直接保存为 FlatBuffer 格式。

转换器还优化模型,既缩小模型大小,又减少延迟。它修剪所有不需要进行预测的操作(例如训练操作),并在可能的情况下优化计算;例如,3 × a + 4 ×_ a_ + 5 × a 将被转换为 12 × a。此外,它尝试在可能的情况下融合操作。例如,如果可能的话,批量归一化层最终会合并到前一层的加法和乘法操作中。要了解 TFLite 可以对模型进行多少优化,可以下载其中一个预训练的 TFLite 模型,例如Inception_V1_quant(点击tflite&pb),解压缩存档,然后打开优秀的Netron 图形可视化工具并上传*.pb文件以查看原始模型。这是一个庞大而复杂的图形,对吧?接下来,打开优化后的.tflite*模型,惊叹于其美丽!

除了简单地使用较小的神经网络架构之外,您可以减小模型大小的另一种方法是使用较小的位宽:例如,如果您使用半精度浮点数(16 位)而不是常规浮点数(32 位),模型大小将缩小 2 倍,代价是(通常很小的)准确度下降。此外,训练速度将更快,您将使用大约一半的 GPU 内存。

TFLite 的转换器可以进一步将模型权重量化为固定点、8 位整数!与使用 32 位浮点数相比,这导致了四倍的大小减小。最简单的方法称为后训练量化:它只是在训练后量化权重,使用一种相当基本但高效的对称量化技术。它找到最大绝对权重值m,然后将浮点范围–m到+m映射到固定点(整数)范围–127 到+127。例如,如果权重范围从–1.5 到+0.8,则字节–127、0 和+127 将分别对应于浮点–1.5、0.0 和+1.5(参见图 19-5)。请注意,当使用对称量化时,0.0 始终映射为 0。还请注意,在此示例中不会使用字节值+68 到+127,因为它们映射到大于+0.8 的浮点数。

图 19-5。从 32 位浮点数到 8 位整数,使用对称量化

要执行这种训练后的量化,只需在调用convert()方法之前将DEFAULT添加到转换器优化列表中:

converter.optimizations = [tf.lite.Optimize.DEFAULT]

这种技术显著减小了模型的大小,使得下载速度更快,占用的存储空间更少。在运行时,量化的权重在使用之前会被转换回浮点数。这些恢复的浮点数与原始浮点数并不完全相同,但也不会相差太远,因此精度损失通常是可以接受的。为了避免一直重新计算浮点值,这样会严重减慢模型的速度,TFLite 会对其进行缓存:不幸的是,这意味着这种技术并不会减少 RAM 的使用量,也不会加快模型的速度。它主要用于减小应用程序的大小。

减少延迟和功耗的最有效方法是对激活进行量化,使得计算可以完全使用整数,而无需任何浮点运算。即使使用相同的位宽(例如,32 位整数而不是 32 位浮点数),整数计算使用的 CPU 周期更少,消耗的能量更少,产生的热量也更少。如果还减少位宽(例如,降至 8 位整数),可以获得巨大的加速。此外,一些神经网络加速器设备(如 Google 的 Edge TPU)只能处理整数,因此权重和激活的完全量化是强制性的。这可以在训练后完成;它需要一个校准步骤来找到激活的最大绝对值,因此您需要向 TFLite 提供代表性的训练数据样本(不需要很大),它将通过模型处理数据并测量量化所需的激活统计信息。这一步通常很快。

量化的主要问题是它会失去一点准确性:这类似于在权重和激活中添加噪声。如果准确性下降太严重,那么您可能需要使用量化感知训练。这意味着向模型添加虚假量化操作,以便它在训练过程中学会忽略量化噪声;最终的权重将更加稳健地适应量化。此外,校准步骤可以在训练过程中自动处理,这简化了整个过程。

我已经解释了 TFLite 的核心概念,但要完全编写移动或嵌入式应用程序需要一本专门的书。幸运的是,一些书籍存在:如果您想了解有关为移动和嵌入式设备构建 TensorFlow 应用程序的更多信息,请查看 O’Reilly 的书籍TinyML: Machine Learning with TensorFlow on Arduino and Ultra-Low Power Micro-Controllers,作者是 Pete Warden(TFLite 团队的前负责人)和 Daniel Situnayake,以及AI and Machine Learning for On-Device Development,作者是 Laurence Moroney。

那么,如果您想在网站中使用您的模型,在用户的浏览器中直接运行呢?

在网页中运行模型

在客户端,即用户的浏览器中运行您的机器学习模型,而不是在服务器端运行,可以在许多场景下非常有用,例如:

  • 当您的网络应用经常在用户的连接不稳定或缓慢的情况下使用(例如,徒步者的网站),因此在客户端直接运行模型是使您的网站可靠的唯一方法。
  • 当您需要模型的响应尽可能快时(例如,用于在线游戏)。消除查询服务器进行预测的需要肯定会减少延迟,并使网站更加响应。
  • 当您的网络服务基于一些私人用户数据进行预测,并且您希望通过在客户端进行预测来保护用户的隐私,以便私人数据永远不必离开用户的设备。

对于所有这些场景,您可以使用TensorFlow.js(TFJS)JavaScript 库。该库可以在用户的浏览器中加载 TFLite 模型并直接进行预测。例如,以下 JavaScript 模块导入了 TFJS 库,下载了一个预训练的 MobileNet 模型,并使用该模型对图像进行分类并记录预测结果。您可以在https://homl.info/tfjscode上尝试这段代码,使用 Glitch.com,这是一个允许您免费在浏览器中构建 Web 应用程序的网站;点击页面右下角的预览按钮查看代码的运行情况:

import "https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest";
import "https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@1.0.0";
const image = document.getElementById("image");
mobilenet.load().then(model => {
    model.classify(image).then(predictions => {
        for (var i = 0; i < predictions.length; i++) {
            let className = predictions[i].className
            let proba = (predictions[i].probability * 100).toFixed(1)
            console.log(className + " : " + proba + "%");
        }
    });
});

甚至可以将这个网站转变成一个渐进式 Web 应用程序(PWA):这是一个遵守一系列标准的网站,使其可以在任何浏览器中查看,甚至可以在移动设备上作为独立应用程序安装。例如,在移动设备上尝试访问https://homl.info/tfjswpa:大多数现代浏览器会询问您是否想要将 TFJS 演示添加到主屏幕。如果您接受,您将在应用程序列表中看到一个新图标。点击此图标将在其自己的窗口中加载 TFJS 演示网站,就像常规移动应用程序一样。PWA 甚至可以配置为离线工作,通过使用服务工作者:这是一个在浏览器中以自己独立线程运行的 JavaScript 模块,拦截网络请求,使其可以缓存资源,从而使 PWA 可以更快地运行,甚至完全离线运行。它还可以传递推送消息,在后台运行任务等。PWA 允许您管理 Web 和移动设备的单个代码库。它们还使得更容易确保所有用户运行您应用程序的相同版本。您可以在 Glitch.com 上玩这个 TFJS 演示的 PWA 代码,网址是https://homl.info/wpacode

提示

https://tensorflow.org/js/demos上查看更多在您的浏览器中运行的机器学习模型的演示。

TFJS 还支持在您的网络浏览器中直接训练模型!而且速度相当快。如果您的计算机有 GPU 卡,那么 TFJS 通常可以使用它,即使它不是 Nvidia 卡。实际上,TFJS 将在可用时使用 WebGL,由于现代网络浏览器通常支持各种 GPU 卡,TFJS 实际上支持的 GPU 卡比常规的 TensorFlow 更多(后者仅支持 Nvidia 卡)。

在用户的网络浏览器中训练模型可以特别有用,可以确保用户的数据保持私密。模型可以在中央进行训练,然后在浏览器中根据用户的数据进行本地微调。如果您对这个话题感兴趣,请查看联邦学习

再次强调,要全面涵盖这个主题需要一本完整的书。如果您想了解更多关于 TensorFlow.js 的内容,请查看 O’reilly 图书《云端、移动和边缘的实用深度学习》(Anirudh Koul 等著)或《学习 TensorFlow.js》(Gant Laborde 著)。

现在您已经看到如何将 TensorFlow 模型部署到 TF Serving,或者通过 Vertex AI 部署到云端,或者使用 TFLite 部署到移动和嵌入式设备,或者使用 TFJS 部署到 Web 浏览器,让我们讨论如何使用 GPU 加速计算。

使用 GPU 加速计算

在第十一章中,我们看了几种可以显著加快训练速度的技术:更好的权重初始化、复杂的优化器等等。但即使使用了所有这些技术,使用单个 CPU 的单台机器训练大型神经网络可能需要几个小时、几天,甚至几周,具体取决于任务。由于 GPU 的出现,这种训练时间可以缩短到几分钟或几小时。这不仅节省了大量时间,还意味着您可以更轻松地尝试各种模型,并经常使用新数据重新训练您的模型。

在之前的章节中,我们在 Google Colab 上使用了启用 GPU 的运行时。您只需从运行时菜单中选择“更改运行时类型”,然后选择 GPU 加速器类型;TensorFlow 会自动检测 GPU 并使用它加速计算,代码与没有 GPU 时完全相同。然后,在本章中,您看到了如何将模型部署到 Vertex AI 上的多个启用 GPU 的计算节点:只需在创建 Vertex AI 模型时选择正确的启用 GPU 的 Docker 镜像,并在调用endpoint.deploy()时选择所需的 GPU 类型。但是,如果您想购买自己的 GPU 怎么办?如果您想在单台机器上的 CPU 和多个 GPU 设备之间分发计算(参见图 19-6)?这是我们现在将讨论的内容,然后在本章的后面部分我们将讨论如何在多个服务器上分发计算。

图 19-6。在多个设备上并行执行 TensorFlow 图

获取自己的 GPU

如果你知道你将会长时间大量使用 GPU,那么购买自己的 GPU 可能是经济上合理的。你可能也想在本地训练模型,因为你不想将数据上传到云端。或者你只是想购买一张用于游戏的 GPU 卡,并且想将其用于深度学习。

如果您决定购买 GPU 卡,那么请花些时间做出正确的选择。您需要考虑您的任务所需的 RAM 数量(例如,图像处理或 NLP 通常至少需要 10GB),带宽(即您可以将数据发送到 GPU 和从 GPU 中发送数据的速度),核心数量,冷却系统等。Tim Dettmers 撰写了一篇优秀的博客文章来帮助您选择:我鼓励您仔细阅读。在撰写本文时,TensorFlow 仅支持具有 CUDA Compute Capability 3.5+的 Nvidia 卡(当然还有 Google 的 TPU),但它可能会将其支持扩展到其他制造商,因此请务必查看TensorFlow 的文档以了解今天支持哪些设备。

如果您选择 Nvidia GPU 卡,您将需要安装适当的 Nvidia 驱动程序和几个 Nvidia 库。这些包括计算统一设备架构库(CUDA)工具包,它允许开发人员使用支持 CUDA 的 GPU 进行各种计算(不仅仅是图形加速),以及CUDA 深度神经网络库(cuDNN),一个 GPU 加速的常见 DNN 计算库,例如激活层、归一化、前向和反向卷积以及池化(参见第十四章)。cuDNN 是 Nvidia 的深度学习 SDK 的一部分。请注意,您需要创建一个 Nvidia 开发者帐户才能下载它。TensorFlow 使用 CUDA 和 cuDNN 来控制 GPU 卡并加速计算(参见图 19-7)。

图 19-7. TensorFlow 使用 CUDA 和 cuDNN 来控制 GPU 并加速 DNNs

安装了 GPU 卡和所有必需的驱动程序和库之后,您可以使用nvidia-smi命令来检查一切是否正确安装。该命令列出了可用的 GPU 卡,以及每张卡上运行的所有进程。在这个例子中,这是一张 Nvidia Tesla T4 GPU 卡,大约有 15GB 的可用内存,并且当前没有任何进程在运行:

$ nvidia-smi
Sun Apr 10 04:52:10 2022
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.32.03    Driver Version: 460.32.03    CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla T4            Off  | 00000000:00:04.0 Off |                    0 |
| N/A   34C    P8     9W /  70W |      3MiB / 15109MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

要检查 TensorFlow 是否真正看到您的 GPU,请运行以下命令并确保结果不为空:

>>> physical_gpus = tf.config.list_physical_devices("GPU")
>>> physical_gpus
[PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]

管理 GPU 内存

默认情况下,TensorFlow 在第一次运行计算时会自动占用几乎所有可用 GPU 的 RAM,以限制 GPU RAM 的碎片化。这意味着如果您尝试启动第二个 TensorFlow 程序(或任何需要 GPU 的程序),它将很快耗尽 RAM。这种情况并不像您可能认为的那样经常发生,因为通常您会在一台机器上运行一个单独的 TensorFlow 程序:通常是一个训练脚本、一个 TF Serving 节点或一个 Jupyter 笔记本。如果出于某种原因需要运行多个程序(例如,在同一台机器上并行训练两个不同的模型),那么您需要更均匀地在这些进程之间分配 GPU RAM。

如果您的机器上有多个 GPU 卡,一个简单的解决方案是将每个 GPU 卡分配给单个进程。为此,您可以设置CUDA_VISIBLE_DEVICES环境变量,以便每个进程只能看到适当的 GPU 卡。还要设置CUDA_DEVICE_ORDER环境变量为PCI_BUS_ID,以确保每个 ID 始终指向相同的 GPU 卡。例如,如果您有四个 GPU 卡,您可以启动两个程序,将两个 GPU 分配给每个程序,通过在两个单独的终端窗口中执行以下命令来实现:

$ CUDA_DEVICE_ORDER=PCI_BUS_IDCUDA_VISIBLE_DEVICES=0,1python3program_1.py*`#` `and``in``another``terminal:`*$ CUDA_DEVICE_ORDER=PCI_BUS_IDCUDA_VISIBLE_DEVICES=3,2python3program_2.py
• 1

程序 1 将只看到 GPU 卡 0 和 1,分别命名为"/gpu:0""/gpu:1",在 TensorFlow 中,程序 2 将只看到 GPU 卡 2 和 3,分别命名为"/gpu:1""/gpu:0"(注意顺序)。一切都将正常工作(参见图 19-8)。当然,您也可以在 Python 中通过设置os.environ["CUDA_DEVICE_ORDER"]os.environ["CUDA_VISIBLE_DEVICES"]来定义这些环境变量,只要在使用 TensorFlow 之前这样做。

图 19-8。每个程序获得两个 GPU

另一个选项是告诉 TensorFlow 只获取特定数量的 GPU RAM。这必须在导入 TensorFlow 后立即完成。例如,要使 TensorFlow 只在每个 GPU 上获取 2 GiB 的 RAM,您必须为每个物理 GPU 设备创建一个逻辑 GPU 设备(有时称为虚拟 GPU 设备),并将其内存限制设置为 2 GiB(即 2,048 MiB):

for gpu in physical_gpus:
    tf.config.set_logical_device_configuration(
        gpu,
        [tf.config.LogicalDeviceConfiguration(memory_limit=2048)]
    )

假设您有四个 GPU,每个 GPU 至少有 4 GiB 的 RAM:在这种情况下,可以并行运行两个像这样的程序,每个程序使用所有四个 GPU 卡(请参见图 19-9)。如果在两个程序同时运行时运行nvidia-smi命令,则应该看到每个进程在每张卡上占用 2 GiB 的 RAM。

图 19-9。每个程序都可以获得四个 GPU,但每个 GPU 只有 2 GiB 的 RAM

另一个选项是告诉 TensorFlow 只在需要时获取内存。同样,在导入 TensorFlow 后必须立即执行此操作:

for gpu in physical_gpus:
    tf.config.experimental.set_memory_growth(gpu, True)

另一种方法是将TF_FORCE_GPU_ALLOW_GROWTH环境变量设置为true。使用这个选项,TensorFlow 一旦分配了内存就不会释放它(再次,为了避免内存碎片化),除非程序结束。使用这个选项很难保证确定性行为(例如,一个程序可能会崩溃,因为另一个程序的内存使用量激增),因此在生产环境中,您可能会选择之前的选项之一。然而,有一些情况下它非常有用:例如,当您使用一台机器运行多个 Jupyter 笔记本时,其中几个使用了 TensorFlow。在 Colab 运行时,TF_FORCE_GPU_ALLOW_GROWTH环境变量被设置为true

最后,在某些情况下,您可能希望将一个 GPU 分成两个或更多逻辑设备。例如,如果您只有一个物理 GPU,比如在 Colab 运行时,但您想要测试一个多 GPU 算法,这将非常有用。以下代码将 GPU#0 分成两个逻辑设备,每个设备有 2 GiB 的 RAM(同样,在导入 TensorFlow 后立即执行):

tf.config.set_logical_device_configuration(
    physical_gpus[0],
    [tf.config.LogicalDeviceConfiguration(memory_limit=2048),
     tf.config.LogicalDeviceConfiguration(memory_limit=2048)]
)

这两个逻辑设备被称为"/gpu:0""/gpu:1", 你可以像使用两个普通 GPU 一样使用它们。你可以像这样列出所有逻辑设备:

>>> logical_gpus = tf.config.list_logical_devices("GPU")
>>> logical_gpus
[LogicalDevice(name='/device:GPU:0', device_type='GPU'),
 LogicalDevice(name='/device:GPU:1', device_type='GPU')]

现在让我们看看 TensorFlow 如何决定应该使用哪些设备来放置变量和执行操作。

将操作和变量放在设备上

Keras 和 tf.data 通常会很好地将操作和变量放在它们应该在的位置,但如果您想要更多控制,您也可以手动将操作和变量放在每个设备上:

  • 通常,您希望将数据预处理操作放在 CPU 上,并将神经网络操作放在 GPU 上。
  • GPU 通常具有相对有限的通信带宽,因此重要的是要避免不必要的数据传输进出 GPU。
  • 向机器添加更多的 CPU RAM 是简单且相对便宜的,因此通常有很多,而 GPU RAM 是内置在 GPU 中的:它是一种昂贵且有限的资源,因此如果一个变量在接下来的几个训练步骤中不需要,它可能应该放在 CPU 上(例如,数据集通常应该放在 CPU 上)。

默认情况下,所有变量和操作都将放置在第一个 GPU 上(命名为"/gpu:0"),除非变量和操作没有 GPU 内核:这些将放置在 CPU 上(始终命名为"/cpu:0")。张量或变量的device属性告诉您它被放置在哪个设备上。

>>> a = tf.Variable([1., 2., 3.])  # float32 variable goes to the GPU
>>> a.device
'/job:localhost/replica:0/task:0/device:GPU:0'
>>> b = tf.Variable([1, 2, 3])  # int32 variable goes to the CPU
>>> b.device
'/job:localhost/replica:0/task:0/device:CPU:0'

您现在可以安全地忽略前缀/job:localhost/replica:0/task:0;我们将在本章后面讨论作业、副本和任务。正如您所看到的,第一个变量被放置在 GPU#0 上,这是默认设备。但是,第二个变量被放置在 CPU 上:这是因为整数变量没有 GPU 内核,或者涉及整数张量的操作没有 GPU 内核,因此 TensorFlow 回退到 CPU。

如果您想在与默认设备不同的设备上执行操作,请使用tf.device()上下文:

>>> with tf.device("/cpu:0"):
...     c = tf.Variable([1., 2., 3.])
...
>>> c.device
'/job:localhost/replica:0/task:0/device:CPU:0'
注意

CPU 始终被视为单个设备("/cpu:0"),即使您的计算机有多个 CPU 核心。放置在 CPU 上的任何操作,如果具有多线程内核,则可能在多个核心上并行运行。

如果您明确尝试将操作或变量放置在不存在或没有内核的设备上,那么 TensorFlow 将悄悄地回退到默认选择的设备。当您希望能够在不具有相同数量的 GPU 的不同机器上运行相同的代码时,这是很有用的。但是,如果您希望获得异常,可以运行tf.config.set_soft_device_placement(False)

现在,TensorFlow 如何在多个设备上执行操作呢?


Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(八)(4)https://developer.aliyun.com/article/1482470

相关文章
|
9天前
|
机器学习/深度学习 数据采集 算法
机器学习到底是什么?附sklearn代码
机器学习到底是什么?附sklearn代码
|
17天前
|
机器学习/深度学习 数据采集 TensorFlow
使用TensorFlow进行模型训练:一次实战探索
【8月更文挑战第22天】本文通过实战案例详解使用TensorFlow进行模型训练的过程。首先确保已安装TensorFlow,接着预处理数据,包括加载、增强及归一化。然后利用`tf.keras`构建卷积神经网络模型,并配置训练参数。最后通过回调机制训练模型,并对模型性能进行评估。此流程为机器学习项目提供了一个实用指南。
|
7天前
|
API UED 开发者
如何在Uno Platform中轻松实现流畅动画效果——从基础到优化,全方位打造用户友好的动态交互体验!
【8月更文挑战第31天】在开发跨平台应用时,确保用户界面流畅且具吸引力至关重要。Uno Platform 作为多端统一的开发框架,不仅支持跨系统应用开发,还能通过优化实现流畅动画,增强用户体验。本文探讨了Uno Platform中实现流畅动画的多个方面,包括动画基础、性能优化、实践技巧及问题排查,帮助开发者掌握具体优化策略,提升应用质量与用户满意度。通过合理利用故事板、减少布局复杂性、使用硬件加速等技术,结合异步方法与预设缓存技巧,开发者能够创建美观且流畅的动画效果。
26 0
|
7天前
|
开发者 算法 虚拟化
惊爆!Uno Platform 调试与性能分析终极攻略,从工具运用到代码优化,带你攻克开发难题成就完美应用
【8月更文挑战第31天】在 Uno Platform 中,调试可通过 Visual Studio 设置断点和逐步执行代码实现,同时浏览器开发者工具有助于 Web 版本调试。性能分析则利用 Visual Studio 的性能分析器检查 CPU 和内存使用情况,还可通过记录时间戳进行简单分析。优化性能涉及代码逻辑优化、资源管理和用户界面简化,综合利用平台提供的工具和技术,确保应用高效稳定运行。
17 0
|
7天前
|
前端开发 开发者 设计模式
揭秘Uno Platform状态管理之道:INotifyPropertyChanged、依赖注入、MVVM大对决,帮你找到最佳策略!
【8月更文挑战第31天】本文对比分析了 Uno Platform 中的关键状态管理策略,包括内置的 INotifyPropertyChanged、依赖注入及 MVVM 框架。INotifyPropertyChanged 方案简单易用,适合小型项目;依赖注入则更灵活,支持状态共享与持久化,适用于复杂场景;MVVM 框架通过分离视图、视图模型和模型,使状态管理更清晰,适合大型项目。开发者可根据项目需求和技术栈选择合适的状态管理方案,以实现高效管理。
16 0
|
10天前
|
机器学习/深度学习 数据采集 算法
如何使用机器学习神器sklearn做特征工程?
如何使用机器学习神器sklearn做特征工程?
|
21天前
|
机器学习/深度学习 算法 TensorFlow
【人工智能】TensorFlow和机器学习概述
TensorFlow的性能优化将是持续的工作重点。这包括更高效的GPU和TPU支持、更快速的模型训练与推理、以及优化的内存使用。同时,随着硬件的发展,TensorFlow将不断优化其代码库以充分利用新型硬件的能力。
12 0
|
3月前
|
机器学习/深度学习 人工智能 算法
海洋生物识别系统+图像识别+Python+人工智能课设+深度学习+卷积神经网络算法+TensorFlow
海洋生物识别系统。以Python作为主要编程语言,通过TensorFlow搭建ResNet50卷积神经网络算法,通过对22种常见的海洋生物('蛤蜊', '珊瑚', '螃蟹', '海豚', '鳗鱼', '水母', '龙虾', '海蛞蝓', '章鱼', '水獭', '企鹅', '河豚', '魔鬼鱼', '海胆', '海马', '海豹', '鲨鱼', '虾', '鱿鱼', '海星', '海龟', '鲸鱼')数据集进行训练,得到一个识别精度较高的模型文件,然后使用Django开发一个Web网页平台操作界面,实现用户上传一张海洋生物图片识别其名称。
155 7
海洋生物识别系统+图像识别+Python+人工智能课设+深度学习+卷积神经网络算法+TensorFlow
|
3月前
|
机器学习/深度学习 人工智能 算法
【乐器识别系统】图像识别+人工智能+深度学习+Python+TensorFlow+卷积神经网络+模型训练
乐器识别系统。使用Python为主要编程语言,基于人工智能框架库TensorFlow搭建ResNet50卷积神经网络算法,通过对30种乐器('迪吉里杜管', '铃鼓', '木琴', '手风琴', '阿尔卑斯号角', '风笛', '班卓琴', '邦戈鼓', '卡萨巴', '响板', '单簧管', '古钢琴', '手风琴(六角形)', '鼓', '扬琴', '长笛', '刮瓜', '吉他', '口琴', '竖琴', '沙槌', '陶笛', '钢琴', '萨克斯管', '锡塔尔琴', '钢鼓', '长号', '小号', '大号', '小提琴')的图像数据集进行训练,得到一个训练精度较高的模型,并将其
48 0
【乐器识别系统】图像识别+人工智能+深度学习+Python+TensorFlow+卷积神经网络+模型训练
|
3月前
|
机器学习/深度学习 人工智能 TensorFlow
TensorFlow 是一个由 Google 开发的开源深度学习框架
TensorFlow 是一个由 Google 开发的开源深度学习框架
50 3

热门文章

最新文章

下一篇
DDNS