1. 程式人生 > >tensorflow視訊記憶體管理、tensorflow使用多個gpu訓練

tensorflow視訊記憶體管理、tensorflow使用多個gpu訓練

 通常在程式開始之前並不知道需要多大的視訊記憶體,程式會去申請GPU的視訊記憶體的50%

比如一個8G的記憶體,被佔用了2G,那麼程式會申請4G的視訊記憶體(因為有足夠的剩餘視訊記憶體)

如果此時視訊記憶體被佔用7G,那麼程式會申請剩下的所有的1G的視訊記憶體。也許你的程式根本用不著這麼多視訊記憶體,200M也許就夠了,這時候如果程式能按照需求去申請就好了,幸運的是,這樣的方法是存在的:

import tensorflow as tf  
import os  
os.environ["CUDA_VISIBLE_DEVICES"] = '0'   #指定第一塊GPU可用  
config = tf.ConfigProto()  
config.gpu_options.per_process_gpu_memory_fraction = 0.5  # 程式最多隻能佔用指定gpu50%的視訊記憶體  
config.gpu_options.allow_growth = True      #程式按需申請記憶體  
sess = tf.Session(config = config)  

能看到,只使用單個GPU跑程式,但三塊顯示卡的視訊記憶體都被佔用。

這是因為TensorFlow訓練時預設佔用所有GPU的視訊記憶體。

這樣如果有人想使用其他兩個GPU跑程式,就會因為視訊記憶體不足而無法執行。 
所以需要人為指定視訊記憶體佔用率或指定使用單張顯示卡。

  1. 第一種是使用 allow_growth,實現視訊記憶體執行時分配。當allow_growth設定為True時,TF程式一開始被分配很少的視訊記憶體,在執行時根據需求增長而擴大視訊記憶體的佔用。

    config = tf.ConfigProto()  
    config.gpu_options.allow_growth = True  
    sess = tf.Session(config=config, ...)  
  2. 第二種是使用 per_process_gpu_memory_fraction,指定每個可用GPU的視訊記憶體分配率。在構造tf.Session()時候通過tf.GPUOptions配置引數,顯式地指定需要分配的視訊記憶體比例。

    
    #告訴TF它可以使用每塊GPU視訊記憶體的40%  
    
    config = tf.ConfigProto()
    config.gpu_options.per_process_gpu_memory_fraction = 0.4
    session = tf.Session(config=config, ...)

    這種方法指定了每個GPU程序的視訊記憶體佔用上限,但它會同時作用於所有GPU,不能對不同GPU設定不同的上限。

  3. 在執行訓練程式前,在使用者根目錄下配置環境(~/.bashrc):

    export CUDA_VISIBLE_DEVICES = NUM  

    NUM是使用者指定顯示卡的序號(0,1,2…),可以先用 nvidia-smi 檢視當前哪塊顯示卡可用。但這種方法限制了使用者可見的GPU數量,比如你的其他程式在你的目錄裡無法選擇別的GPU; 你的程式也沒法使用multiple GPUs。

關於多gpu訓練,tf並沒有給太多的學習資料,比較官方的只有

:tensorflow-models/tutorials/image/cifar10/cifar10_multi_gpu_train.py

但程式碼比較簡單,只是針對cifar做了資料並行的多gpu訓練,利用到的layer、activation型別不多,針對更復雜網路的情況,並沒有給出指導。

一、思路

單GPU時,思路很簡單,前向、後向都在一個GPU上進行,模型引數更新時只涉及一個GPU。多GPU時,有模型並行和資料並行兩種情況。模型並行指模型的不同部分在不同GPU上執行。資料並行指不同GPU上訓練資料不同,但模型是同一個(相當於是同一個模型的副本)。在此只考慮資料並行,這個在tf的實現思路如下:

模型引數儲存在一個指定gpu/cpu上,模型引數的副本在不同gpu上,每次訓練,提供batch_size*gpu_num資料,並等量拆分成多個batch,分別送入不同GPU。前向在不同gpu上進行,模型引數更新時,將多個GPU後向計算得到的梯度資料進行平均,並在指定GPU/CPU上利用梯度資料更新模型引數。

假設有兩個GPU(gpu0,gpu1),模型引數實際存放在cpu0上,實際一次訓練過程如下圖所示:

二、tf程式碼實現

大部分需要修改的部分集中在構建計算圖上,假設在構建計算圖時,資料部分基於tensorflow1.4版本的dataset類,那麼程式碼要按照如下方式編寫:

next_img, next_label = iterator.get_next()
image_splits = tf.split(next_img, num_gpus)
label_splits = tf.split(next_label, num_gpus)
tower_grads = []
tower_loss = []
counter = 0
for d in self.gpu_id:
    with tf.device('/gpu:%s' % d):
        with tf.name_scope('%s_%s' % ('tower', d)):
            cross_entropy = build_train_model(image_splits[counter], label_splits[counter], for_training=True)
            counter += 1
            with tf.variable_scope("loss"):
                grads = opt.compute_gradients(cross_entropy)
                tower_grads.append(grads)
                tower_loss.append(cross_entropy)
                tf.get_variable_scope().reuse_variables()

mean_loss = tf.stack(axis=0, values=tower_loss)
mean_loss = tf.reduce_mean(mean_loss, 0)
mean_grads = util.average_gradients(tower_grads)
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
    train_op = opt.apply_gradients(mean_grads, global_step=global_step)

第1行得到image和對應label

第2-3行對image和label根據使用的gpu數量做平均拆分(預設兩個gpu運算能力相同,如果gpu運算能力不同,可以自己設定拆分策略)

第 4-5行,儲存來自不同GPU計算出的梯度、loss列表

第7-16行,開始在每個GPU上建立計算圖,最重要的是14-16三行,14,15把當前GPU計算出的梯度、loss值append到列表後,以便後續計算平均值。16行表示同名變數將會複用,這個是什麼意思呢?假設現在gpu0上建立了兩個變數var0,var1,那麼在gpu1上建立計算圖的時候,如果還有var0和var1,則預設複用之前gpu0上的建立的那兩個值。

第18-20行計算不同GPU獲取的grad、loss的平均值,其中第20行使用了cifar10_multi_gpu_train.py中的函式。

第23行利用梯度平均值更新引數。

注意:上述程式碼中,所有變數(vars)都放在了第一個GPU上,執行時會發現第一個GPU佔用的視訊記憶體比其他GPU多一些。如果想把變數放在CPU上,則需要在建立計算圖時,針對每層使用到的變數進行裝置指定,很麻煩,所以建議把變數放在GPU上。

單機多GPU訓練

先簡單介紹下單機的多GPU訓練,然後再介紹分散式的多機多GPU訓練。 
單機的多GPU訓練, tensorflow的官方已經給了一個cifar的例子,已經有比較詳細的程式碼和文件介紹, 這裡大致說下多GPU的過程,以便方便引入到多機多GPU的介紹。 
單機多GPU的訓練過程:

  1. 假設你的機器上有3個GPU;

  2. 在單機單GPU的訓練中,資料是一個batch一個batch的訓練。 在單機多GPU中,資料一次處理3個batch(假設是3個GPU訓練), 每個GPU處理一個batch的資料計算。

  3. 變數,或者說引數,儲存在CPU上

  4. 剛開始的時候資料由CPU分發給3個GPU, 在GPU上完成了計算,得到每個batch要更新的梯度。

  5. 然後在CPU上收集完了3個GPU上的要更新的梯度, 計算一下平均梯度,然後更新引數。

  6. 然後繼續迴圈這個過程。

通過這個過程,處理的速度取決於最慢的那個GPU的速度。如果3個GPU的處理速度差不多的話, 處理速度就相當於單機單GPU的速度的3倍減去資料在CPU和GPU之間傳輸的開銷,實際的效率提升看CPU和GPU之間資料的速度和處理資料的大小。

通俗解釋

寫到這裡覺得自己寫的還是不同通俗易懂, 下面就打一個更加通俗的比方來解釋一下:

老師給小明和小華佈置了10000張紙的乘法題並且把所有的乘法的結果加起來, 每張紙上有128道乘法題。 這裡一張紙就是一個batch, batch_size就是128. 小明算加法比較快, 小華算乘法比較快,於是小華就負責計算乘法, 小明負責把小華的乘法結果加起來 。 這樣小明就是CPU,小華就是GPU.

這樣計算的話, 預計小明和小華兩個人得要花費一個星期的時間才能完成老師佈置的題目。 於是小明就招來2個算乘法也很快的小紅和小亮。 於是每次小明就給小華,小紅,小亮各分發一張紙,讓他們算乘法, 他們三個人算完了之後, 把結果告訴小明, 小明把他們的結果加起來,然後再給他們沒人分發一張算乘法的紙,依次迴圈,知道所有的算完。

這裡小明採用的是同步模式,就是每次要等他們三個都算完了之後, 再統一算加法,算完了加法之後,再給他們三個分發紙張。這樣速度就取決於他們三個中算乘法算的最慢的那個人, 和分發紙張的速度。

相關推薦

tensorflow視訊記憶體管理tensorflow使用gpu訓練

 通常在程式開始之前並不知道需要多大的視訊記憶體,程式會去申請GPU的視訊記憶體的50% 比如一個8G的記憶體,被佔用了2G,那麼程式會申請4G的視訊記憶體(因為有足夠的剩餘視訊記憶體) 如果此時視訊記憶體被佔用7G,那麼程式會申請剩下的所有的1G的視訊記憶體。也許你的程

90Tensorflow實現分散式學習,臺電腦,GPU 非同步試學習

2017-05-28 22:38:45.122523: W c:\tf_jenkins\home\workspace\release-win\device\gpu\os\windows\tensorflow\core\platform\cpu_feature_guard.cc:45] The TensorFl

tensorflow視訊記憶體載入模型優化器(個人筆記)

在使用tensorflow做實驗的這短暫一段時間內,遇到了不少問題,把還沒忘問題寫在這裡,方便以後查閱。 1. 執行sess=tf.Session() 或 sess=tf.InteractiveSession()後發現所有GPU的視訊記憶體全部佔滿 A:

TensorFlow 同時調用訓練好的模型

預測 txt success ucc data 分享 ext font 訓練 在某些任務中,我們需要針對不同的情況訓練多個不同的神經網絡模型,這時候,在測試階段,我們就需要調用多個預訓練好的模型分別來進行預測。 調用單個預訓練好的模型請點擊此處 弄明白了如何調用單個

Cocos2dx記憶體管理優化減少記憶體執行緒方面的問題

Cocos2d-x中的引用計數(Reference Count)和自動釋放池(AutoReleasePool) 引用計數 引用計數是c/c++專案中一種古老的記憶體管理方式。當我8年前在研究一款名叫TCPMP的開源專案的時候,引用計數就已經有了。 iOS SDK把這項計

Tensorflow視訊記憶體溢位

報錯資訊 一直是 InternalError: Dst tensor is not initialized. 然後顯示一大堆資訊,通過谷歌大部分找到的就是 GPU 視訊記憶體溢位。然後 加上 CUDA_VISIBLE_DEVICES=1 Environment Variab

TensorFlow:儲存/恢復和混合模型

這篇教程是翻譯Morgan寫的TensorFlow教程,作者已經授權翻譯,這是原文 目錄 在學習這篇部落格之前,我希望你已經掌握了Tensorflow基本的操作。如果沒有,你可以閱讀這篇入門文章。 為什麼要學習模型的儲存和恢復呢?因為這對於避免資料的混亂無序是至關重要的,特別是在你程式碼中的不同圖。

lvs 一個網卡單個管理ip,跨網段VIP解決辦法

lvs keepalived 說明:lvs的vip和realserver的rip是可以跨網段的ifconfig輸出如下:[[email protected]/* */ scripts]# ifconfig eth0 Link encap:Ethernet HWaddr 00:

【Appnium+C#+Winform自動化測試系列】一獲取本機連接的設備啟動Appnium和獲取本機啟動的Appnium

net 系列 () 定向 目的 res listening toa 路徑     本系列內容,準備根據所完成的項目為基線,一步一步的把整個設計和實現過程梳理。 先從基本的一些環境問題入手,梳理清楚關於手機設備和Appnium。因為我們在後面的建立Appnium連接時,需要

tensorflow:Google】三tensorflow入門

als 管理 神經網絡 等價 問題 sign ria init 節點 【一】計算圖模型 節點是計算,邊是數據流, a = tf.constant( [1., 2.] )定義的是節點,節點有屬性 a.graph 取得默認計算圖  g1 = tf.get_default_gr

用靜態工廠方法代替構造器遇到構造器參數時要考慮用構建器

泛型 不用 推斷 frame public 多參數 eof ram api 一、用靜態工廠方法代替構造器 類通過共有的構造方法可以提供很大的優點:1、構造方法可一有不同的名字,我們可以通過名字區分構造什麽樣子的對象,而構造器名字相同,當參數列表的數目相同 順序不同時 很大的

Java實現FTP伺服器上傳下載下載寫入本地刪除

場景:需要從FTP伺服器一個檔案目錄下down下來所有的檔案,上傳到專案某個目錄下。 上傳下載刪除獲取檔案下所有檔案,順便加上獲取到所有檔案進行下載。 package a1; /** * 1----public boolean uploadFile(String path ,Fi

如何一條Mediainfo --Inform語句同時獲取視訊引數和音訊引數Parameters

mediainfo是一個能獲取多媒體檔案詳細資訊的軟體,包括內容資訊,視訊資訊,音訊資訊,文字資訊等,有了它我們可以檢視視訊的寬高,比例,播放時長,音軌,位元率等等資訊 準備工作 個人部落格地址為:dangbowen.com,歡迎加入收藏夾 參考php-mediainfo教程安裝mediainf

C語言(記憶體管理檔案處理)

記憶體的理解 計算機記憶體是以位元組為單位進行儲存,每個位元組都有自己的編號即地址(指標)。 本圖為原始碼 其中01 00 00 00 中的兩個連在一起的數為一個位元組,0x00FAFB7C是01的地址,之後的三個位元組的地址值分別遞增1 上圖中,num[3]為int

vue-router 2.0 跳轉之傳參傳遞引數

在vue專案中,往往會遇到這樣的情況,就是要實現在一文章列表中,點選其中一條跳轉到下個頁面,然後將這一條的相關資料帶到下個頁面中顯示,無論點哪一條都是跳到相同的頁面結構(下一個頁面的頁面使用的元件是一樣的),只是填的資料不一樣,這個時候就需要實現跳轉的時候一起把引數攜帶過去。

Leetcode演算法——23合併連結串列

合併 k 個有序連結串列,返回一個新的有序連結串列。 示例: Input: [ 1->4->5, 1->3->4, 2->6 ] Output: 1->1->2

JavaScript如何工作:記憶體管理+如何處理4常見的記憶體洩漏

摘要: 作者將自己常用的JavaScript模組分享給大家。 原文:JavaScript如何工作:記憶體管理+如何處理4個常見的記憶體洩漏 Fundebug經授權轉載,版權歸原作者所有。 本系列的第一篇文章簡單介紹了引擎、執行時間和堆疊的呼叫。第二篇文章研究了谷歌V

第十二章(2)——記憶體管理隨機數

本文部分參考https://blog.csdn.net/fireflylane/article/details/83660791 分配記憶體malloc()和free() 定義在stdlib.h中 malloc()函式接收一個引數:所需要的記憶體位元組數 該函式

ToolBox APP使用,管理JetBrains的IDE

支援模糊搜尋! 可以管理多個版本,並可以做到直接升級。 可以設定IDEA記憶體等 注:現在工具存在一個問題,當前系統配置的是64位的JDK,當開啟32位JDK的專案,會提示無法開啟;只能使用IDE

docker工具之埠對映容器互聯封裝映象啟動服務

1.埠對映 為什麼要埠對映? 在啟動容器時,如果不配置宿主機器與虛擬機器的埠對映,外部程式是無法訪問虛擬機器的, 因為沒有埠,所以需要進行埠對映。 埠對映的兩個關鍵詞: 埠對映有兩個關鍵詞-P -p 一個是大寫一個是小寫 通過run --help也可以看