利用 TensorFlow Profiler:在 AMD GPU 上优化 TensorFlow 模型

TensorFlow Profiler in practice: Optimizing TensorFlow models on AMD GPUs — ROCm Blogs

简介

TensorFlow Profiler 是一组旨在衡量 TensorFlow 模型执行期间资源利用率和性能的工具。它提供了关于模型如何与硬件资源交互的深入见解,包括执行时间和内存使用情况。TensorFlow Profiler 有助于定位性能瓶颈,使我们能够微调模型的执行,以提高效率并加快结果,这在需要接近实时预测的场景中尤为重要。

机器学习算法,特别是深度神经网络,具有很高的计算需求。评估机器学习应用程序的性能,以确保执行的是经过最优化的模型版本,这一点至关重要。这篇博客演示了如何在 AMD 硬件上使用 TensorFlow Profiler 工具,通过训练自编码器模型、收集设备跟踪数据、并根据分析结果修改自编码器的结构来优化模型性能。有关 TensorFlow Profiler 的更多信息,请参见 使用 Profiler 优化 TensorFlow。

在 GitHub 文件夹 中可以找到与本篇博客相关的资源。

前提条件

以下是跟随本博客所需的要求和说明:

操作系统和硬件

以下描述了支持的操作系统和硬件环境,推荐用于运行本博客文章中的示例。

  • AMD 加速器或 GPU:请参阅 ROCm 文档中有关支持的操作系统和硬件的列表,网址为 系统要求(Linux)。

  • ROCm 6.0: 请参阅 ROCm 安装说明。

  • Docker: 使用 适用于 Ubuntu 的 Docker 引擎。

  • TensorFlow 和 TensorBoard:使用官方的 ROCm Docker 镜像 rocm/tensorflow:rocm6.0-tf2.12-runtime,支持 TensorFlow 2.12。

运行此博客

使用 Docker 是搭建所需环境的最简单和最可靠的方法。

  • 确保已安装 Docker。如果没有,请参阅 安装说明。

  • 确保在主机上已安装 amdgpu-dkms(随 ROCm 一起提供),以便从 Docker 内部访问 GPU。有关详细信息,请参阅 ROCm Docker 说明。

  • 克隆库,并进入博客目录:

    git clone https://github.com/ROCm/rocm-blogs.git
    cd rocm-blogs/blogs/software-tools-optimization/tf_profiler
    
  • 构建并启动容器。有关构建过程的详细信息,请参阅 dockerfile。这将启动一个 Jupyter Lab 服务器。

    cd docker
    docker compose build
    docker compose up
    
  • 在浏览器中导航到 http://localhost:8888/lab/tree/src/tf_profiler_ae_example.ipynb 并打开 tf_profiler_ae_example.ipynb 笔记本。

现在,请使用 Jupyter 笔记本来跟随此博客进行学习。

使用 TensorFlow Profiler 记录分析数据

在接下来的部分中,我们将设计并训练一个在 MNIST 数据集上的简单自编码器模型,同时使用 TensorFlow Profiler 收集分析数据。通过在 TensorBoard 中分析这些分析数据,我们将识别性能瓶颈,并做出明智的决策,以优化模型在 AMD 加速器上的性能。

设计一个简单的自动编码器

自动编码器是一种用于无监督学习的神经网络,旨在学习输入数据的高效表示。自动编码器通常用于降维或特征学习等任务。它们包括两个主要部分:*编码器*,将输入压缩为潜在空间表示;*解码器*,从潜在表示中重构输入数据。目标是最小化原始输入与其重建之间的差异,从而使模型能够在没有监督的情况下学习数据的显着特征。

我们将使用 MNIST 数据集来训练这个自动编码器。`MNIST` 数据集是一个大量的手写数字数据集,常用于训练各种图像处理系统。`MNIST` 是 tensorflow_datasets 模块的一部分,并随 TensorFlow 一起安装。对于自动编码器,其目标是让它学习 MNIST 数据集中手写数字的紧凑表示。

为了展示 TensorFlow Profiler 的优势,让我们设计如下的自动编码器神经网络:

首先导入以下模块:

import os
import tensorflow as tf
import tensorflow_datasets as tfds
from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, UpSampling2D
from tensorflow.keras.models import Model
from tensorflow.keras.callbacks import TensorBoard

from datetime import datetime

接下来,加载数据集并进行预处理操作:将每个图像规范化到 [0,1] 范围。

# 加载数据集

def preprocess_image(features):

    image = tf.cast(features['image'], tf.float32) / 255.0
    image = tf.image.resize(image, [28,28])
    image = tf.expand_dims(image[:, :, 0], -1)

    return image, image # Returns input and output the same for the autoencoder

# 创建 mnist 训练和测试拆分
ds_train = tfds.load('mnist', split='train', as_supervised=False)
ds_train = ds_train.map(preprocess_image, num_parallel_calls = tf.data.AUTOTUNE).batch(64)

ds_test = tfds.load('mnist', split='test', as_supervised=False)
ds_test = ds_test.map(preprocess_image, num_parallel_calls = tf.data.AUTOTUNE).batch(64)

让我们定义以下自动编码器架构:

# 定义架构
input_img = Input(shape = (28,28,1))

# 编码器部分
x = Conv2D(512,(3,3), activation = 'relu', padding = 'same')(input_img)
x = MaxPooling2D((2,2), padding = 'same')(x)
x = Conv2D(128, (3,3), activation = 'relu', padding = 'same')(x)
encoded = MaxPooling2D((2,2), padding='same')(x)

# 解码器部分
x = Conv2D(128,(3,3), activation = 'relu', padding = 'same')(encoded)
x = UpSampling2D((2,2))(x)
x = Conv2D(512,(3,3), activation = 'relu', padding = 'same')(x)
x = UpSampling2D((2,2))(x)
decoded = Conv2D(1,(3,3), activation='sigmoid', padding='same')(x)

autoencoder = Model(input_img, decoded)

最后,设置 profiling 回调函数以在模型训练时收集 profiling 数据。

# 定义优化器和损失函数
autoencoder.compile(optimizer = 'adam', loss = 'binary_crossentropy')
 
# 设置 profiling。将 profiling 日志存储在相应的文件夹中
tensorboard_callback = TensorBoard(log_dir = './logs/' + datetime.now().strftime("%Y%m%d-%H%M%S"),
                                   histogram_freq=1, profile_batch='500,520')

# 训练模型10轮
autoencoder.fit(ds_train,
                epochs = 10,
                validation_data = ds_test,
                callbacks = [tensorboard_callback], # callback needed for use of tensorboard tools
                )


# 复制文件 "events.out.tfevents.1583461681.localhost.profile-empty" 到每个记录的日志中以显示数据
source_path = "./events.out.tfevents.1583461681.localhost.profile-empty"
destination_path = log_dir
shutil.copy(source_path, destination_path)

注意:
在上面的代码中,shutil.copy被用来将文件 events.out.tfevents.1583461681.localhost.profile-empty 复制到对应训练运行的 logs 文件夹中。这是 TensorBoard 的一个限制;它需要手动放置此文件才能正确显示 profiling 数据,并使 PROFILE 菜单可用。

为了探索 TensorFlow Profiler 的使用和功能,我们特意将自动编码器的训练限制为 10 轮。让我们运行以下代码来可视化自动编码器的输出。

# 显示图像

import matplotlib.pyplot as plt
import numpy as np
import tensorflow_datasets as tfds

# 提取一个测试图像
for test_images, _ in ds_test.take(30): # get image with index 30
    test_image = test_images[0:1]

reconstructed_image = autoencoder.predict(test_image)

# 绘制原始图像
fig, axes = plt.subplots(1,2)
axes[0].imshow(test_image[0,:,:,0], cmap='gray')
axes[0].set_title('Original Image')
axes[0].axis('off')

# 绘制重建图像
axes[1].imshow(reconstructed_image[0,:,:,0], cmap='gray')
axes[1].set_title('Reconstructed Image')
axes[1].axis('off')

plt.show()

我们可以比较自动编码器的输入(原始图像)和它的输出(重建图像):

Figure1

探索使用 TensorBoard 的 TensorFlow Profiler 数据

我们将使用 TensorBoard 来探索收集到的性能分析数据。TensorBoard 是一个用于机器学习实验的可视化工具。它可以帮助可视化训练指标和模型权重,使我们可以更深入地了解模型的训练过程。

TensorBoard 的一个有用特性是 TensorBoard 回调 API。这个功能可以集成到模型的训练循环中,自动记录诸如训练和验证指标之类的数据。TensorBoard 包括一个 Profile 选项,可以用于分析 TensorFlow 操作的时间和内存性能。这对于识别训练或推理过程中的瓶颈情况非常有用。启用性能分析设置 profile_batch 在 TensorBoard 回调中。

在我们的案例中,我们创建了一个 TensorBoard 回调实例 (tensorboard_callback = TensorBoard()) 并配置了以下参数:

  • log_dir: 对应于日志数据存储的位置。在这种情况下,文件夹还附加了当前时间戳: './logs/' + datetime.now().strftime("%Y%m%d-%H%M%S")

  • histogram_freq: 对应于记录日志的频率(以周期数表示)。值 histogram_freq=1 表示每个周期都记录数据。

  • profile_batch: 配置需要分析的批次。在这种情况下,我们定义`profile_batch='500,520'`。这将在批次 500 到批次 520 内分析操作。由于我们使用的批次大小为 64(`batch(64)`),并且训练集的大小为 60,000,
    我们有 60000 / 64 = 938 批次,因此 profile_batch='500,520' 的范围在我们可分析的批次范围内。

让我们通过在 TensorBoard 上可视化分析结果来探索我们的 profiler 结果。

首先加载 Jupyter notebook 上的 TensorBoard 扩展:

%load_ext tensorboard

接下来,在 notebook 内启动 TensorBoard(可能需要几秒钟才能显示出来):

%tensorboard --logdir='./logs' --bind_all --port 6006

显示如下屏幕:

Figure2

要可视化分析数据,请转到`PROFILE` 标签。在这里,我们会看到以下选项:

  • 当前的 Run,对应选中的日志文件夹。

  • Tools 部分,选择 overview_page

  • Hosts 列表,显示当前执行 TensorFlow 代码时的 CPU 或主机。

Figure3

性能摘要包含若干性能指标。它还显示了一个 Step-time 图表,其中 x 轴对应于之前选择的批次(`profile_batch='500,520'`)。另外,TensorFlow Profiler 给了我们一些可以为更好性能实施的潜在建议。

TensorBoard 提供了几种工具来更好地理解我们的 TensorFlow 代码的执行。让我们专注于在同一次训练运行中收集的跟踪数据。在 Tools 菜单下选择 trace_viewer

Figure4

trace_viewer 选项允许我们观察每批次(`profile_batch='500,520'`)收集的跟踪数据。在跟踪视图内,x 轴表示时间,y 轴列出在可用设备上收集的每个跟踪。使用 A D W 和 S 键分别向左移动,向右移动,放大和缩小。

我们还可以查看任何 TensorFlow 操作的运行时间并可视化其跟踪。让我们重点关注批次 500。就我们的情况,我们观察到 GPU:1 上的 Conv2D 操作的持续时间约为 475,680 纳秒(ns)。根据这张图表,这个操作消耗了大部分处理时间。我们还可以观察其他 GPU 操作的运行时间以及在 CPU 上记录的跟踪。

Figure5

比较不同 Profiler 运行的数据

到目前为止,我们已经展示了如何在 TensorFlow 中分析和可视化自动编码器实现的跟踪数据。我们还展示了 TensorFlow Profiler 如何创建训练过程中计算操作的性能摘要,甚至为我们提供潜在的改进性能的建议。让我们看看在按照 TensorFlow Profiler 提供的建议对代码进行一些更改后,性能摘要如何变化。

首先,通过左侧的 Tools 菜单探索 op_profile 选项。这次我们观察到 Conv2DBackpropFilter 和 Conv2D 在训练阶段占用了大部分处理时间。如果我们考虑在 Conv2D 操作中减少滤波器的数量来减少计算时间,同时在自动编码器的输出(重构)的质量之间进行权衡会怎么样呢?

Figure6

让我们在对应的部分修改编码器和解码器中的滤波器数量如下:

# Encoder
x = Conv2D(16,(3,3), activation = 'relu', padding = 'same')(input_img)
x = MaxPooling2D((2,2), padding = 'same')(x)
x = Conv2D(8, (3,3), activation = 'relu', padding = 'same')(x)

同样地,对于解码器:

# Decoder
x = Conv2D(8,(3,3), activation = 'relu', padding = 'same')(encoded)
x = UpSampling2D((2,2))(x)
x = Conv2D(16,(3,3), activation = 'relu', padding = 'same')(x)

我们已经将编码器中的滤波器数量从原来的 512 修改为 16 ,并且在相应的 Conv2D 卷积层中从 128 修改为 8 。由于解码器的架构与编码器的架构是镜像对称的,因此在解码器部分也做了相应的修改。让我们比较输入、我们之前的自动编码器的输出和新更新后的自动编码器的输出:

Figure7

在未优化和优化的重构中,我们没有看到两个自动编码器的重构之间有很大的视觉差异。

检查此更新运行记录的 TensorFlow Profiler 数据。

Figure8

让我们也检查一下 Conv2D 计算中所占用的时间百分比。

Figure9

太好了!Profiler 工具帮助我们识别了模型中的潜在瓶颈,源于 Conv2D 操作。如果我们考虑在输出质量有所损失与减少计算时间之间进行一些权衡,我们可以在训练流程中实现更好的性能。

总结

TensorFlow Profiler 是一个优化深度神经网络计算的关键工具。它提供了详细的洞察力以优化 TensorFlow 模型,特别是展示了利用 AMD GPU 功能的显著优势。此设置确保了更快的机器学习计算,使得 AMD GPU 成为高级 TensorFlow 应用程序的绝佳选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/920298.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

二叉树——输出叶子到根节点的路径

目录 代码 算法思想 例子 思维拓展 代码 int LeaveBit(Bitree T,int flag,int g) {if (!T) {return 0;}if (T->rchild NULL && T->lchild NULL) {//cout << "empty:" << T->data << endl;s.push(T->data);while (!s.emp…

PIL学习---彩色RGB图像按通道输出

要将 RGB 图像拆分为单独的 R、G、B 通道并分别展示&#xff0c;可以通过 PIL 中的 split() 方法将图像的三个通道分开&#xff0c;并使用 matplotlib 来显示每个通道的图像。效果如下图所示&#xff1a; 代码部分&#xff1a; from PIL import Image import matplotlib.pypl…

CSS实现实现当文本内容过长时,中间显示省略号...,两端正常展示

HTML 结构解析 文档结构: <ul class"con">: 一个无序列表&#xff0c;包含多个列表项。 每个 <li class"wrap"> 表示一个列表项&#xff0c;内部有两个 <span> 元素&#xff1a; <span class"txt">: 显示文本内容。<…

ROS VRRP软路由双线组网方式

虚拟路由冗余协议 Virtual Router Redundancy Protocol (VRRP)&#xff0c;MikroTik RouteROS VRRP 协议遵循 RFC 2338。 VRRP 协议是保证访问一些资源不会中断&#xff0c;即通过多台路由器组成一个网关集合&#xff0c;如果其中一台路由器出现故障&#xff0c;会自动启用另外…

设计编程网站集:简述可扩展性系统设计(笔记)

视频连接&#xff1a;简述可扩展性系统设计 三个关键原则 无状态 松散耦合 异步处理 扩展 负载均衡 缓存 分片

openCV与eigen两种方法---旋转向量转旋转矩阵

#include <Eigen/Dense> #include <opencv2/core/eigen.hpp> #include <opencv2/opencv.hpp> using namespace cv; using namespace std; int main() {// opencv 旋转向量cv::Vec3d rvec(1.0, 2.0, 3.0);cv::Mat rotation_matrix;cv::Rodrigues(rvec, rotati…

卷积运算和卷积定理

卷积运算 卷积运算是信号处理、图像处理和深度学习中的核心概念&#xff0c;用于表示两个函数之间的相互作用。它将一个函数通过滑动窗口的方式与另一个函数结合&#xff0c;产生一个新的函数&#xff0c;反映两者的重叠程度。 1. 定义 连续信号的卷积&#xff1a; 给定两个连…

【板间连接器焊接】

一、背景 近期工作需要,用到了AX7Z020核心板(黑金),官网链接:https://www.alinx.com/detail/271。 板子打好之后,遇到了焊接问题。对自身焊接技术还是比较自信的,直接上去焊接了2个连接器。拖锡搞了3小时后,放弃了。热风枪1分钟不到就把连接器吹下来了,看引脚90%都是…

低代码开发平台搭建思考与实战

什么是低代码开发平台&#xff1f; 低代码开发平台是一种平台软件&#xff0c;人们能通过它提供的图形化配置功能&#xff0c;快速配置出满足各种特定业务需求的功能软件。 具有以下特点&#xff1a; 提供可视化界面进行程序开发0代码或少量代码快速生成应用 什么是低代码产…

React Native 基础

React 的核心概念 定义函数式组件 import组件 要定义一个Cat组件,第一步要使用 import 语句来引入React以及React Native的 Text 组件: import React from react; import { Text } from react-native; 定义函数作为组件 const CatApp = () => {}; 渲染Text组件

ftdi_sio应用学习笔记 3 - GPIO

目录 1. 查找gpiochip 2. 打开GPIO 2.1 libgpiod库方式 2.2 系统方式 3. 关闭GPIO 3.1 libgpiod库方式 3.2 系统方式 4. 设置方向 4.1 libgpiod库方式 4.2 系统方式 5. 设置GPIO电平 5.1 libgpiod库方式 5.2 系统方式 6. 读取GPIO电平 6.1 libgpiod库方式 6.2 …

微信小程序登录注册页面设计(小程序项目)

需求 在微信小程序设计并实现登录页面&#xff0c;并填写相关登录注册函数 实现效果 代码实现 html代码 <view class"top" style"border-bottom-style: none;background-color:#FF8C69;"><!-- <view class"back" bind:tap"…

神经网络(系统性学习三):多层感知机(MLP)

相关文章&#xff1a; 神经网络中常用的激活函数 神经网络&#xff08;系统性学习一&#xff09;&#xff1a;入门篇 神经网络&#xff08;系统性学习二&#xff09;&#xff1a;单层神经网络&#xff08;感知机&#xff09; 多层感知机&#xff08;MLP&#xff09; 多层感…

Android 14 screenrecord录制视频失败的原因分析

文章目录 1. 权限问题2. 存储空间不足3. 命令被中断4. 目标路径问题5. Android 14 的新限制6. 文件系统同步问题7. 录制失败检查步骤总结&#xff1a; 在 Android 14 系统上&#xff0c;使用 screenrecord 命令录制视频后&#xff0c;生成的文件大小为 0&#xff0c;可能的原因…

Uniapp 简单配置鸿蒙

Uniapp 简单配置鸿蒙 前言下载并配置鸿蒙IDEHbuilder X 配置基本的信息生成相关证书登录官网获取证书IDE配置证书添加调试设备可能出现的问题前言 如今鸿蒙的盛起,作为多端开发的代表也是开始兼容鸿蒙应用的开发,接下来我将介绍如何在uniapp中配置鸿蒙。 注意:hbuilder X的…

git使用(一)

git使用&#xff08;一&#xff09; 为什么学习git?两种版本控制系统在github上创建一个仓库&#xff08;repository&#xff09;windows上配置git环境在Linux上配置git环境 为什么学习git? 代码写了好久不小心删了&#xff0c;可以使用git防止&#xff0c;每写一部分代码通…

C# 数据结构之【树】C#树

以二叉树为例进行演示。二叉树每个节点最多有两个子节点。 1. 新建二叉树节点模型 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace DataStructure {class TreeNode{public int Data { get;…

HarmonyOs鸿蒙开发实战(20)=>一文学会基础使用组件导航Navigation

敲黑板&#xff0c;以下是重点技巧。文章末尾有实战项目效果截图及代码截图可参考 1.概要 Navigation是路由导航的根视图容器Navigation组件主要包含​导航页&#xff08;NavBar&#xff09;和子页&#xff08;NavDestination&#xff09;&#xff0c;导航页不存在页面栈中&am…

python从入门到精通:pyspark实战分析

前言 spark&#xff1a;Apache Spark是用于大规模数据&#xff08;large-scala data&#xff09;处理的统一&#xff08;unified&#xff09;分析引擎。简单来说&#xff0c;Spark是一款分布式的计算框架&#xff0c;用于调度成本上千的服务器集群&#xff0c;计算TB、PB乃至E…

Ubuntu从入门到精通(二)远程和镜像源配置齐全

Ubuntu从入门到精通(二) 1 常见操作配置 1.1 英文语言配置 1.1.1 打开设置 1.1.2 设置语言为英文 1.1.3 重启生效 1.1.4 再次进入,选择更新名字 1.1.5 再次进入,发现已经变成了英文 1.2 输入法配置 1.3 rustdesk安装 1.3.1 Windows系统配置 登陆:https://github.com…