本文來自雲栖社群官方釘群“ Python技術進階 ”,了解相關資訊可以關注“
”。
在第 11 章,我們讨論了幾種可以明顯加速訓練的技術:更好的權重初始化,批量标準化,複雜的優化器等等。 但是,即使采用了所有這些技術,在具有單個 CPU 的單台機器上訓練大型神經網絡可能需要幾天甚至幾周的時間。
在本章中,我們将看到如何使用 TensorFlow 在多個裝置(CPU 和 GPU)上配置設定計算并将它們并行運作(參見圖 12-1)。 首先,我們會先在一台機器上的多個裝置上配置設定計算,然後在多台機器上的多個裝置上配置設定計算。
與其他神經網絡架構相比,TensorFlow 對分布式計算的支援是其主要亮點之一。 它使您可以完全控制如何跨裝置和伺服器分布(或複制)您的計算圖,并且可以讓您以靈活的方式并行和同步操作,以便您可以在各種并行方法之間進行選擇。
我們來看一些最流行的方法來并行執行和訓練一個神經網絡,這讓我們不再需要等待數周才能完成訓練算法,而最終可能隻會等待幾個小時。 這不僅可以節省大量時間,還意味着您可以更輕松地嘗試各種模型,并經常重新訓練模型上的新資料。
還有其他很好的并行化例子,包括當我們在微調模型時可以探索更大的超參數空間,并有效地運作大規模神經網絡。
但我們必須先學會走路才能跑步。 我們先從一台機器上的幾個 GPU 上并行化簡單圖形開始。
一台機器上多裝置
隻需添加 GPU 卡到單個機器,您就可以獲得主要的性能提升。 事實上,在很多情況下,這就足夠了。 你根本不需要使用多台機器。 例如,通常在單台機器上使用 8 個 GPU,而不是在多台機器上使用 16 個 GPU(由于多機器設定中的網絡通信帶來的額外延遲),可以同樣快地訓練神經網絡。
在本節中,我們将介紹如何設定您的環境,以便 TensorFlow 可以在一台機器上使用多個 GPU 卡。 然後,我們将看看如何在可用裝置上進行分布操作,并且并行執行它們。
安裝
為了在多個 GPU 卡上運作 TensorFlow,首先需要確定 GPU 卡具有 NVidia 計算能力(大于或等于3.0)。 這包括 Nvidia 的 Titan,Titan X,K20 和 K40(如果你擁有另一張卡,你可以在https://developer.nvidia.com/cuda-gpus 檢視它的相容性)。
如果您不擁有任何 GPU 卡,則可以使用具有 GPU 功能的主機伺服器,如 Amazon AWS。 在 ŽigaAvsec 的部落格文章中,提供了在 Amazon AWS GPU 執行個體上使用 Python 3.5 設定 TensorFlow 0.9 的詳細說明。将它更新到最新版本的 TensorFlow 應該不會太難。 Google 還釋出了一項名為 Cloud Machine Learning 的雲服務來運作 TensorFlow 圖表。 2016 年 5 月,他們宣布他們的平台現在包括配備張量處理器(TPU)的伺服器,專門用于機器學習的處理器,比許多 GPU 處理 ML 任務要快得多。 當然,另一種選擇隻是購買你自己的 GPU 卡。 Tim Dettmers 寫了一篇很棒的部落格文章來幫助你選擇,他會定期更新它。
您必須下載下傳并安裝相應版本的 CUDA 和 cuDNN 庫(如果您使用的是 TensorFlow 1.0.0,則為 CUDA 8.0 和 cuDNN 5.1),并設定一些環境變量,以便 TensorFlow 知道在哪裡可以找到 CUDA 和 cuDNN。 詳細的安裝說明可能會相當迅速地更改,是以最好按照 TensorFlow 網站上的說明進行操作。
Nvidia 的 CUDA 允許開發者使用支援 CUDA 的 GPU 進行各種計算(不僅僅是圖形加速)。 Nvidia 的 CUDA 深度神經網絡庫(cuDNN)是針對 DNN 的 GPU 加速原語庫。 它提供了常用 DNN 計算的優化實作,例如激活層,歸一化,前向和後向卷積以及池化(參見第 13 章)。 它是 Nvidia Deep Learning SDK 的一部分(請注意,它需要建立一個 Nvidia 開發者帳戶才能下載下傳它)。 TensorFlow 使用 CUDA 和 cuDNN 來控制 GPU 卡并加速計算(見圖 12-2)。
您可以使用
nvidia-smi
指令來檢查 CUDA 是否已正确安裝。 它列出了可用的 GPU 卡以及每張卡上運作的程序:
最後,您必須安裝支援 GPU 的 TensorFlow。 如果你使用
virtualenv
建立了一個獨立的環境,你首先需要激活它:
$ cd $ML_PATH
# Your ML working directory (e.g., HOME/ml)
$ source env/bin/activate
然後安裝合适的支援 GPU 的 TensorFlow 版本:
$ pip3 install --upgrade tensorflow-gpu
現在您可以打開一個 Python shell 并通過導入 TensorFlow 并建立一個會話來檢查 TensorFlow 是否正确檢測并使用 CUDA 和 cuDNN:
>>> import tensorflow as tf
I [...]/dso_loader.cc:108] successfully opened CUDA library libcublas.so locally
I [...]/dso_loader.cc:108] successfully opened CUDA library libcudnn.so locally
I [...]/dso_loader.cc:108] successfully opened CUDA library libcuda.so.1 locally
I [...]/dso_loader.cc:108] successfully opened CUDA library libcufft.so locally
I [...]/gpu_init.cc:102] Found device 0 with properties:
I [...]/dso_loader.cc:108] successfully opened CUDA library libcurand.so locally
>>> sess = tf.Session()
[...]
name: GRID K520
I [...]/gpu_init.cc:136] 0: Y
major: 3 minor: 0 memoryClockRate (GHz) 0.797
pciBusID 0000:00:03.0
Total memory: 4.00GiB
Free memory: 3.95GiB
I [...]/gpu_init.cc:126] DMA: 0
(/gpu:0) -> (device: 0, name: GRID K520, pci bus id: 0000:00:03.0)
I [...]/gpu_device.cc:839] Creating TensorFlow device
看起來不錯!TensorFlow 檢測到 CUDA 和 cuDNN 庫,并使用 CUDA 庫來檢測 GPU 卡(在這種情況下是 Nvidia Grid K520 卡)。
管理 GPU 記憶體
預設情況下,TensorFlow 會在您第一次運作圖形時自動擷取所有可用 GPU 中的所有 RAM,是以當第一個程式仍在運作時,您将無法啟動第二個 TensorFlow 程式。 如果你嘗試,你會得到以下錯誤:
E [...]/cuda_driver.cc:965] failed to allocate 3.66G (3928915968 bytes) from device: CUDA_ERROR_OUT_OF_MEMORY
一種解決方案是在不同的 GPU 卡上運作每個程序。 為此,最簡單的選擇是設定
CUDA_VISIBLE_DEVICES
環境變量,以便每個程序隻能看到對應的 GPU 卡。 例如,你可以像這樣啟動兩個程式:
$ CUDA_VISIBLE_DEVICES=0,1 python3 program_1.py
# and in another terminal:
$ CUDA_VISIBLE_DEVICES=3,2 python3 program_2.py
程式 #1 隻會看到 GPU 卡 0 和 1(分别編号為 0 和 1),程式 #2 隻會看到 GPU 卡 2 和 3(分别編号為 1 和 0)。 一切都會正常工作(見圖 12-3)。
另一種選擇是告訴 TensorFlow 隻抓取一小部分記憶體。 例如,要使 TensorFlow 隻占用每個 GPU 記憶體的 40%,您必須建立一個
ConfigProto
對象,将其
gpu_options.per_process_gpu_memory_fraction
選項設定為 0.4,并使用以下配置建立
session
:
config = tf.ConfigProto()
config.gpu_options.per_process_gpu_memory_fraction = 0.4
session = tf.Session(config=config)
現在像這樣的兩個程式可以使用相同的 GPU 卡并行運作(但不是三個,因為
3×0.4> 1
)。 見圖 12-4。
如果在兩個程式都運作時運作
nvidia-smi
指令,則應該看到每個程序占用每個卡的總 RAM 大約 40%:
另一種選擇是告訴 TensorFlow 隻在需要時才抓取記憶體。 為此,您必須将
config.gpu_options.allow_growth
設定為
True
。但是,TensorFlow 一旦抓取記憶體就不會釋放記憶體(以避免記憶體碎片),是以您可能會在一段時間後記憶體不足。 是否使用此選項可能難以确定,是以一般而言,您可能想要堅持之前的某個選項。
好的,現在你已經有了一個支援 GPU 的 TensorFlow 安裝。 讓我們看看如何使用它!
裝置布置操作
TensorFlow 白皮書介紹了一種友好的動态布置器算法,該算法能夠自動将操作分布到所有可用裝置上,并考慮到以前運作圖中所測量的計算時間,估算每次操作的輸入和輸出張量的大小, 每個裝置可用的 RAM,傳輸資料進出裝置時的通信延遲,來自使用者的提示和限制等等。 不幸的是,這種複雜的算法是谷歌内部的,它并沒有在 TensorFlow 的開源版本中釋出。它被排除在外的原因似乎是,由使用者指定的一小部分放置規則實際上比動态放置器放置的更有效。 然而,TensorFlow 團隊正在努力改進它,并且最終可能會被開放。
在此之前,TensorFlow都是簡單的放置,它(如其名稱所示)非常基本。
簡單放置
無論何時運作圖形,如果 TensorFlow 需要求值尚未放置在裝置上的節點,則它會使用簡單放置器将其放置在未放置的所有其他節點上。 簡單放置尊重以下規則:
如果某個節點已經放置在圖形的上一次運作中的某個裝置上,則該節點将保留在該裝置上。
否則,如果使用者将一個節點固定到裝置上(下面介紹),則放置器将其放置在該裝置上。
否則,它預設為 GPU#0,如果沒有 GPU,則預設為 CPU。
正如您所看到的,将操作放在适當的裝置上主要取決于您。 如果您不做任何事情,整個圖表将被放置在預設裝置上。 要将節點固定到裝置上,您必須使用
device()
函數建立一個裝置塊。 例如,以下代碼将變量
a
和常量
b
固定在 CPU 上,但乘法節點
c
不固定在任何裝置上,是以将放置在預設裝置上:
with tf.device("/cpu:0"):
a = tf.Variable(3.0)
c = a * b
b = tf.constant(4.0)
其中,
"/cpu:0"
裝置合計多 CPU 系統上的所有 CPU。 目前沒有辦法在特定 CPU 上固定節點或僅使用所有 CPU 的子集。
記錄放置位置
讓我們檢查一下簡單的放置器是否遵守我們剛剛定義的布局限制條件。 為此,您可以将
log_device_placement
選項設定為
True
;這告訴放置器在放置節點時記錄消息。例如:
>>> config = tf.ConfigProto()
>>> config.log_device_placement = True
>>> sess = tf.Session(config=config)
I [...] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GRID K520, pci bus id: 0000:00:03.0)
[...]
>>> x.initializer.run(session=sess)
I [...] a: /job:localhost/replica:0/task:0/cpu:0
I [...] a/Assign: /job:localhost/replica:0/task:0/cpu:0
I [...] a/read: /job:localhost/replica:0/task:0/cpu:0
I [...] mul: /job:localhost/replica:0/task:0/gpu:0
12
I [...] b: /job:localhost/replica:0/task:0/cpu:0
I [...] a/initial_value: /job:localhost/replica:0/task:0/cpu:0
>>> sess.run(c)
Info
中以大寫字母
I
開頭的行是日志消息。 當我們建立一個會話時,TensorFlow 會記錄一條消息,告訴我們它已經找到了一個 GPU 卡(在這個例子中是 Grid K520 卡)。 然後,我們第一次運作圖形(在這種情況下,當初始化變量
a
時),簡單布局器運作,并将每個節點放置在配置設定給它的裝置上。正如預期的那樣,日志消息顯示所有節點都放在
"/cpu:0"
上,除了乘法節點,它以預設裝置
"/gpu:0"
結束(您可以先忽略字首:
/job:localhost/replica:0/task:0;
我們将在一會兒讨論它)。 注意,我們第二次運作圖(計算
c
)時,由于 TensorFlow 需要計算的所有節點
c
都已經放置,是以不使用布局器。
動态放置功能
建立裝置塊時,可以指定一個函數,而不是裝置名稱。TensorFlow 會調用這個函數來進行每個需要放置在裝置塊中的操作,并且該函數必須傳回裝置的名稱來固定操作。 例如,以下代碼将固定所有變量節點到
"/cpu:0"
(在本例中隻是變量
a
)和所有其他節點到
"/gpu:0"
def variables_on_cpu(op):
if op.type == "Variable":
return "/cpu:0"
else:
with tf.device(variables_on_cpu):
return "/gpu:0"
a = tf.Variable(3.0)
c = a * b
b = tf.constant(4.0)
您可以輕松實作更複雜的算法,例如以循環方式用GPU鎖定變量。
操作和核心
對于在裝置上運作的 TensorFlow 操作,它需要具有該裝置的實作;這被稱為核心。 許多操作對于 CPU 和 GPU 都有核心,但并非全部都是。 例如,TensorFlow 沒有用于整數變量的 GPU 核心,是以當 TensorFlow 嘗試将變量i放置到 GPU#0 時,以下代碼将失敗:
>>> with tf.device("/gpu:0"):
... i = tf.Variable(3)
>>> sess.run(i.initializer)
[...]
Traceback (most recent call last):
[...]
tensorflow.python.framework.errors.InvalidArgumentError: Cannot assign a device to node 'Variable': Could not satisfy explicit device specification
請注意,TensorFlow 推斷變量必須是
int32
類型,因為初始化值是一個整數。 如果将初始化值更改為 3.0 而不是 3,或者如果在建立變量時顯式設定
dtype = tf.float32
,則一切正常。
軟放置
預設情況下,如果您嘗試在操作沒有核心的裝置上固定操作,則當 TensorFlow 嘗試将操作放置在裝置上時,您會看到前面顯示的異常。 如果您更喜歡 TensorFlow 回退到 CPU,則可以将
allow_soft_placement
配置選項設定為
True
with tf.device("/gpu:0"):
i = tf.Variable(3)
config.allow_soft_placement = True
config = tf.ConfigProto()
sess.run(i.initializer) # the placer runs and falls back to /cpu:0
sess = tf.Session(config=config)
到目前為止,我們已經讨論了如何在不同裝置上放置節點。 現在讓我們看看 TensorFlow 如何并行運作這些節點。
原文釋出時間為:2018-06-26
”。
Python技術進階交流群