動手學深度學習12.3.自動并行-筆記練習(PyTorch)

以下內容為結合李沐老師的課程和教材補充的學習筆記,以及對課后練習的一些思考,自留回顧,也供同學之人交流參考。

本節課程地址:無

本節教材地址:12.3. 自動并行 — 動手學深度學習 2.0.0 documentation

本節開源代碼:...>d2l-zh>pytorch>chapter_optimization>auto-parallelism.ipynb


自動并行

深度學習框架(例如,MxNet、飛槳和PyTorch)會在后端自動構建計算圖。利用計算圖,系統可以了解所有依賴關系,并且可以選擇性地并行執行多個不相互依賴的任務以提高速度。例如,12.2節?中的 圖12.2.2 獨立初始化兩個變量。因此,系統可以選擇并行執行它們。

通常情況下單個操作符將使用所有CPU或單個GPU上的所有計算資源。例如,即使在一臺機器上有多個CPU處理器,dot操作符也將使用所有CPU上的所有核心(和線程)。這樣的行為同樣適用于單個GPU。因此,并行化對單設備計算機來說并不是很有用,而并行化對于多個設備就很重要了。雖然并行化通常應用在多個GPU之間,但增加本地CPU以后還將提高少許性能。例如, (Hadjis?et al., 2016) 則把結合GPU和CPU的訓練應用到計算機視覺模型中。借助自動并行化框架的便利性,我們可以依靠幾行Python代碼實現相同的目標。對自動并行計算的討論主要集中在使用CPU和GPU的并行計算上,以及計算和通信的并行化內容。

請注意,本節中的實驗至少需要兩個GPU來運行。

import torch
from d2l import torch as d2l

基于GPU的并行計算

從定義一個具有參考性的用于測試的工作負載開始:下面的run函數將執行?10?次矩陣-矩陣乘法時需要使用的數據分配到兩個變量(x_gpu1x_gpu2)中,這兩個變量分別位于選擇的不同設備上。

devices = d2l.try_all_gpus()
def run(x):return [x.mm(x) for _ in range(50)]x_gpu1 = torch.rand(size=(4000, 4000), device=devices[0])
x_gpu2 = torch.rand(size=(4000, 4000), device=devices[1])

現在使用函數來處理數據。通過在測量之前需要預熱設備(對設備執行一次傳遞)來確保緩存的作用不影響最終的結果。torch.cuda.synchronize()函數將會等待一個CUDA設備上的所有流中的所有核心的計算完成。函數接受一個device參數,代表是哪個設備需要同步。如果device參數是None(默認值),它將使用current_device()找出的當前設備。

run(x_gpu1)
run(x_gpu2)  # 預熱設備
torch.cuda.synchronize(devices[0])
torch.cuda.synchronize(devices[1])with d2l.Benchmark('GPU1 time'):run(x_gpu1)torch.cuda.synchronize(devices[0])with d2l.Benchmark('GPU2 time'):run(x_gpu2)torch.cuda.synchronize(devices[1])

輸出結果:
GPU1 time: 1.4571 sec
GPU2 time: 1.4560 sec

如果刪除兩個任務之間的synchronize語句,系統就可以在兩個設備上自動實現并行計算。

with d2l.Benchmark('GPU1 & GPU2'):run(x_gpu1)run(x_gpu2)torch.cuda.synchronize()

輸出結果:
GPU1 & GPU2: 1.5222 sec

在上述情況下,總執行時間小于兩個部分執行時間的總和,因為深度學習框架自動調度兩個GPU設備上的計算,而不需要用戶編寫復雜的代碼。

并行計算與通信

在許多情況下,我們需要在不同的設備之間移動數據,比如在CPU和GPU之間,或者在不同的GPU之間。例如,當執行分布式優化時,就需要移動數據來聚合多個加速卡上的梯度。讓我們通過在GPU上計算,然后將結果復制回CPU來模擬這個過程。

def copy_to_cpu(x, non_blocking=False):return [y.to('cpu', non_blocking=non_blocking) for y in x]with d2l.Benchmark('在GPU1上運行'):y = run(x_gpu1)torch.cuda.synchronize()with d2l.Benchmark('復制到CPU'):y_cpu = copy_to_cpu(y)torch.cuda.synchronize()

輸出結果:
在GPU1上運行: 1.8508 sec
復制到CPU: 3.1686 sec

這種方式效率不高。注意到當列表中的其余部分還在計算時,我們可能就已經開始將y的部分復制到CPU了。例如,當計算一個小批量的(反傳)梯度時。某些參數的梯度將比其他參數的梯度更早可用。因此,在GPU仍在運行時就開始使用PCI-Express總線帶寬來移動數據是有利的。在PyTorch中,to()copy_()等函數都允許顯式的non_blocking參數,這允許在不需要同步時調用方可以繞過同步。設置non_blocking=True以模擬這個場景。

with d2l.Benchmark('在GPU1上運行并復制到CPU'):y = run(x_gpu1)y_cpu = copy_to_cpu(y, True)torch.cuda.synchronize()

輸出結果:
在GPU1上運行并復制到CPU: 2.6157 sec

兩個操作所需的總時間少于它們各部分操作所需時間的總和。請注意,與并行計算的區別是通信操作使用的資源:CPU和GPU之間的總線。事實上,我們可以在兩個設備上同時進行計算和通信。如上所述,計算和通信之間存在的依賴關系是必須先計算y[i],然后才能將其復制到CPU。幸運的是,系統可以在計算y[i]的同時復制y[i-1],以減少總的運行時間。

最后,本節給出了一個簡單的兩層多層感知機在CPU和兩個GPU上訓練時的計算圖及其依賴關系的例子,如 圖12.3.1 所示。手動調度由此產生的并行程序將是相當痛苦的。這就是基于圖的計算后端進行優化的優勢所在。

小結

  • 現代系統擁有多種設備,如多個GPU和多個CPU,還可以并行地、異步地使用它們。
  • 現代系統還擁有各種通信資源,如PCI Express、存儲(通常是固態硬盤或網絡存儲)和網絡帶寬,為了達到最高效率可以并行使用它們。
  • 后端可以通過自動化地并行計算和通信來提高性能。

練習

  1. 在本節定義的run函數中執行了八個操作,并且操作之間沒有依賴關系。設計一個實驗,看看深度學習框架是否會自動地并行地執行它們。

解:
run函數實際是執行了50個矩陣乘法操作,設計實驗比較單個矩陣乘法和用run函數執行50個矩陣乘法的時間,發現用run函數執行50個矩陣乘法的時間小于單個矩陣乘法執行50次的時間,證明深度學習框架會自動地并行地執行它們。
代碼如下:

# 單個矩陣乘法時間基準
with d2l.Benchmark('Single matmul'):x_gpu1.mm(x_gpu1)torch.cuda.synchronize()# 多個獨立矩陣乘法時間
with d2l.Benchmark('Multiple matmuls'):run(x_gpu1)torch.cuda.synchronize()

輸出結果:
Single matmul: 0.0457 sec
Multiple matmuls: 1.4930 sec

2. 當單個操作符的工作量足夠小,即使在單個CPU或GPU上,并行化也會有所幫助。設計一個實驗來驗證這一點。

解:
還是基于矩陣乘法,將x的尺寸設置為10×10的小尺寸,在單個CPU或GPU上,用run函數自動并行的計算時間都更少,說明當單個操作符的工作量足夠小,即使在單個CPU或GPU上,并行化也會有所幫助。
代碼如下:

def benchmark_matmul(size, device):x = torch.randn(size, size, device=device)# 順序執行基準with d2l.Benchmark(f'Size {size}x{size} (Sequential)'):for _ in range(50):_ = x.matmul(x)if device.type == 'cuda': torch.cuda.synchronize()# 自動并行執行(框架隱式優化)with d2l.Benchmark(f'Size {size}x{size} (Auto-Parallel)'):run(x)if device.type == 'cuda':torch.cuda.synchronize()
# 單個CPU
device = torch.device('cpu')
benchmark_matmul(10, device)

輸出結果:
Size 10x10 (Sequential): 0.0518 sec
Size 10x10 (Auto-Parallel): 0.0005 sec

# 單個GPU
devices = d2l.try_all_gpus()
benchmark_matmul(10, devices[0])

輸出結果:
Size 10x10 (Sequential): 0.0025 sec
Size 10x10 (Auto-Parallel): 0.0010 sec

3. 設計一個實驗,在CPU和GPU這兩種設備上使用并行計算和通信。

解:
本節的12.3.2中的實驗可以說明,在CPU和GPU這兩種設備上可以同時進行并行計算和通信,減少總體運行時間。

4. 使用諸如NVIDIA的Nsight之類的調試器來驗證代碼是否有效。

解:
沒有Nsight,改用Pytorch的Profiler驗證,從Profiler打印的結果表格中可以看到,多個任務的Self CUDA %都是100%,說明確實進行了并行計算。
代碼如下:

from torch.profiler import ProfilerActivitywith torch.profiler.profile(activities=[ProfilerActivity.CUDA, ProfilerActivity.CPU],schedule=torch.profiler.schedule(wait=1, warmup=1, active=3),on_trace_ready=torch.profiler.tensorboard_trace_handler('./log')
) as prof:for _ in range(5):run(x_gpu1)run(x_gpu2)torch.cuda.synchronize()prof.step()
print(prof.key_averages().table()
輸出結果:
-------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  Name    Self CPU %      Self CPU   CPU total %     CPU total  CPU time avg     Self CUDA   Self CUDA %    CUDA total  CUDA time avg    # of Calls  
-------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ProfilerStep*         0.04%       1.745ms       100.00%        4.440s        1.480s       0.000us         0.00%        8.837s        2.946s             3  aten::mm         0.13%       5.705ms         0.17%       7.698ms      25.659us        8.837s       100.00%        8.837s      29.458ms           300  cudaOccupancyMaxActiveBlocksPerMultiprocessor         0.00%     202.240us         0.00%     202.240us       0.674us       0.000us         0.00%       0.000us       0.000us           300  cudaLaunchKernel         0.04%       1.790ms         0.04%       1.790ms       5.968us       0.000us         0.00%       0.000us       0.000us           300  ProfilerStep*         0.00%       0.000us         0.00%       0.000us       0.000us        8.838s       100.00%        8.838s        1.473s             6  volta_sgemm_128x64_nn         0.00%       0.000us         0.00%       0.000us       0.000us        8.837s       100.00%        8.837s      29.755ms           297  cudaDeviceSynchronize        99.79%        4.430s        99.79%        4.430s        1.108s       0.000us         0.00%       0.000us       0.000us             4  
-------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  
Self CPU time total: 4.440s
Self CUDA time total: 8.837s

5. 設計并實驗具有更加復雜的數據依賴關系的計算任務,以查看是否可以在提高性能的同時獲得正確的結果。

解:
以下實驗可以證明,在利用并行提高計算性能的同時,獲得與串行一致的結果。
代碼如下:

# 構建復雜依賴關系:
#   A → B → C
#   │    │
#   ↓    ↓
#   D → E → Fimport timedef task(x, name):"""模擬不同計算任務"""if name == 'A': return x @ x.Telif name == 'B': return x * x.sum()elif name == 'C': return x.cos() + x.sin()elif name == 'D': return x.pow(2).mean()elif name == 'E': return x.norm(dim=1)elif name == 'F': return x.softmax(dim=0)def serial_execution(x):"""串行執行(嚴格按依賴順序)"""a = task(x, 'A')b = task(a, 'B')d = task(a, 'D')c = task(b, 'C')e = task(b, 'E')f = task(e, 'F')return c, d, fdef parallel_execution(x):"""并行執行(重疊無依賴任務)"""# 第一層并行stream1 = torch.cuda.Stream()stream2 = torch.cuda.Stream()with torch.cuda.stream(stream1):a = task(x, 'A')torch.cuda.synchronize()  # 確保a完成with torch.cuda.stream(stream1):b = task(a, 'B')with torch.cuda.stream(stream2):d = task(a, 'D')  # 與b無依賴,可并行torch.cuda.synchronize()  # 等待b,d完成with torch.cuda.stream(stream1):c = task(b, 'C')with torch.cuda.stream(stream2):e = task(b, 'E')  # 依賴b,但c/e之間無依賴torch.cuda.synchronize()  # 等待e完成f = task(e, 'F')return c, d, f
def run_test(matrix_size=1000):x = torch.randn(matrix_size, matrix_size, device='cuda')# 串行執行torch.cuda.synchronize()start = time.time()c_serial, d_serial, f_serial = serial_execution(x)torch.cuda.synchronize()serial_time = time.time() - start# 并行執行torch.cuda.synchronize()start = time.time()c_parallel, d_parallel, f_parallel = parallel_execution(x)torch.cuda.synchronize()parallel_time = time.time() - start# 結果對比def check_equal(t1, t2):return torch.allclose(t1, t2, rtol=1e-4, atol=1e-6)is_correct = (check_equal(c_serial, c_parallel) and check_equal(d_serial, d_parallel) and check_equal(f_serial, f_parallel))print(f"矩陣大小: {matrix_size}x{matrix_size}")print(f"串行時間: {serial_time:.4f}s")print(f"并行時間: {parallel_time:.4f}s")print(f"加速比: {serial_time/parallel_time:.2f}x")print(f"結果一致: {is_correct}")
run_test(matrix_size=1000)

輸出結果:
矩陣大小: 1000x1000
串行時間: 0.3834s
并行時間: 0.0062s
加速比: 61.86x
結果一致: True

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/80677.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/80677.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/80677.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++類和對象之初始化列表

初始化列表 C初始化列表詳解:性能優化與正確實踐什么是初始化列表?初始化列表的三大核心作用1. 性能優化:避免不必要的賦值操作2. 強制初始化:處理const和引用成員3. 基類初始化:正確調用父類構造函數4.必須使用初始化…

continue通過我們的開源 IDE 擴展和模型、規則、提示、文檔和其他構建塊中心,創建、共享和使用自定義 AI 代碼助手

?一、軟件介紹 文末提供程序和源碼下載 Continue 使開發人員能夠通過我們的開源 VS Code 和 JetBrains 擴展以及模型、規則、提示、文檔和其他構建塊的中心創建、共享和使用自定義 AI 代碼助手。 二、功能 Chat 聊天 Chat makes it easy to ask for help from an LLM without…

基于Spring Boot + Vue的母嬰商城系統( 前后端分離)

一、項目背景介紹 隨著母嬰行業在互聯網平臺的快速發展,越來越多的家庭傾向于在線選購母嬰產品。為了提高商品管理效率和用戶購物體驗,本項目開發了一個基于 Spring Boot Vue 技術棧的母嬰商城系統,實現了商品分類、商品瀏覽、資訊展示、評…

實戰演練:用 AWS Lambda 和 API Gateway 構建你的第一個 Serverless API

實戰演練:用 AWS Lambda 和 API Gateway 構建你的第一個 Serverless API 理論千遍,不如動手一遍!在前面幾篇文章中,我們了解了 Serverless 的概念、FaaS 的核心原理以及 BaaS 的重要作用。現在,是時候把這些知識運用起來,親手構建一個簡單但完整的 Serverless 應用了。 …

node.js 實戰——express圖片保存到本地或服務器(七牛云、騰訊云、阿里云)

本地 ? 使用formidable 讀取表單內容 npm i formidable ? 使用mime-types 獲取圖片后綴 npm install mime-types? js 中提交form表單 document.getElementById(uploadForm).addEventListener(submit, function(e){e.preventDefault();const blob preview._blob;if(!blob)…

2025最新:3分鐘使用Docker快速部署單節點Redis

🧑?🏫 詳細教程:通過 Docker 安裝單節點 Redis 🛠? 前提條件: 你需要在 Ubuntu 系統上進行操作(如果你在其他系統上操作,可以按相似步驟進行調整)。已安裝 Docker 和 Docker Com…

CentOS 7 系統下安裝 OpenSSL 1.0.2k 依賴問題的處理

前面有提到過這個openssl的版本沖突問題,也是在這次恢復服務器時遇到的問題,我整理如下,供大家參考。小小一個軟件的安裝,挺坑的。 一、問題 項目運行環境需要,指定PHP7.0.9這個版本,但是?系統版本與軟件…

LoRA(Low-Rank Adaptation)原理詳解

LoRA(Low-Rank Adaptation)原理詳解 LoRA(低秩適應)是一種參數高效微調(Parameter-Efficient Fine-Tuning, PEFT)技術,旨在以極低的參數量實現大模型在特定任務上的高效適配。其核心思想基于低秩分解假設,即模型在適應新任務時,參數更新矩陣具有低秩特性,可用少量參…

Solana批量轉賬教程:提高代幣持有地址和生態用戶空投代幣

前言 Solana區塊鏈因其高吞吐量和低交易費用成為批量操作(如空投)的理想選擇。本教程將介紹幾種在Solana上進行批量轉賬的方法,幫助您高效地向多個地址空投代幣。 solana 賬戶模型 在Solana中有三類賬戶: 數據賬戶,…

基于LSTM與SHAP可解釋性分析的神經網絡回歸預測模型【MATLAB】

基于LSTM與SHAP可解釋性分析的神經網絡回歸預測模型【MATLAB】 一、引言 在數據驅動的智能時代,時間序列預測已成為許多領域(如金融、氣象、工業監測等)中的關鍵任務。長短期記憶網絡(LSTM)因其在捕捉時間序列長期依…

手機網頁提示ip被拉黑名單什么意思?怎么辦

?當您使用手機瀏覽網頁時,突然看到“您的IP地址已被列入黑名單”的提示,是否感到困惑和不安?這種情況在現代網絡生活中并不罕見,但確實會給用戶帶來諸多不便。本文將詳細解釋IP被拉黑的含義、常見原因,并提供一系列實…

Java消息隊列性能優化實踐:從理論到實戰

Java消息隊列性能優化實踐:從理論到實戰 1. 引言 在現代分布式系統架構中,消息隊列(Message Queue,MQ)已經成為不可或缺的中間件組件。它不僅能夠實現系統間的解耦,還能提供異步通信、流量削峰等重要功能…

BUUCTF——Cookie is so stable

BUUCTF——Cookie is so stable 進入靶場 頁面有點熟悉 跟之前做過的靶場有點像 先簡單看一看靶場信息 有幾個功能點 flag.php 隨便輸了個admin 根據題目提示 應該與cookie有關 抓包看看 構造payload Cookie: PHPSESSIDef0623af2c1a6d2012d57f3529427d52; user{{7*7}}有…

json格式不合法情況下,如何盡量保證數據可用性

背景 在工作流程中,并非所有數據都如人所愿,即使json版本也會由于csv、tsv、excel、text等不同文件格式轉化、獲取數據源不完整等問題,造成我們要處理的json文件存在不合法。 嘗試方案 除了人為修正外,有效的方法是使用json“修…

Python基礎總結(十)之函數

Python函數 函數是Python中也是非常重要的,函數是帶名字的代碼塊,用于完成具體的工作。要執行函數定義的特定任務,可調用該函數。 一、函數的定義 函數的定義要使用def關鍵字,def后面緊跟函數名,縮進的為函數的代碼塊。 def test():print("Hello,World")上述…

懶人美食幫SpringBoot訂餐系統開發實現

概述 快速構建一個訂餐系統,今天,我們將通過”懶人美食幫”這個基于SpringBoot的訂餐系統項目,為大家詳細解析從用戶登錄到多角色權限管理的完整實現方案。本教程特別適合想要學習企業級應用開發的初學者。 主要內容 1. 用戶系統設計與實現…

AI(學習筆記第三課) 使用langchain進行AI開發(2)

文章目錄 AI(學習筆記第三課) 使用langchain進行AI開發(2)學習內容:1. 返回結構化數據(structured_output pydantic)1.1 使用背景1.2 返回結構化數據示例代碼(pydantic)1.3 執行測試代碼2 返回結構化數據(json)2.1 示例代碼2.2 執行結果3 給提供一些例子(few shot pr…

unity 使用藍牙通訊(PC版,非安卓)

BlueTooth in pc with unity 最近接到的需求是在unity里面開發藍牙功能,其實一開始我并不慌,因為據我所知,unity有豐富的插件可以使用,但是問題隨之而來 1.unity里面無法直接與藍牙通訊(后來找到了開啟runtime一類的東西,但是我找了半天也沒找到在哪里可以打開) 2.引入dll通過d…

MySQL中的意向鎖 + next-key鎖 + 間隙鎖

引言 在數據庫并發控制中,鎖機制是保障數據一致性和隔離性的核心手段。MySQL中意向鎖、間隙鎖以及next-key鎖等復雜鎖類型,旨在協調表級鎖與行級鎖之間的關系,防止數據的臟讀、不可重復讀和幻讀現象,尤其是在可重復讀隔離級別下發…

機器學習 數據集

數據集 1. scikit-learn工具介紹1.1 scikit-learn安裝1.2 Scikit-learn包含的內容 2 數據集2.1 sklearn玩具數據集介紹2.2 sklearn現實世界數據集介紹2.3 sklearn加載玩具數據集示例1:鳶尾花數據示例2:分析糖尿病數據集 2.4 sklearn獲取現實世界數據集示…