日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人工智能 > Caffe >内容正文

Caffe

Caffe代码阅读

發(fā)布時間:2025/3/21 Caffe 169 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Caffe代码阅读 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)載自:

Caffe代碼閱讀——層次結構 - 無痛的機器學習 - 知乎專欄 ?https://zhuanlan.zhihu.com/p/21796890

Caffe源碼閱讀——Net組裝 - 無痛的機器學習 - 知乎專欄 ?https://zhuanlan.zhihu.com/p/21875025

Caffe代碼閱讀——Solver - 無痛的機器學習 - 知乎專欄 ?https://zhuanlan.zhihu.com/p/21800004

1.Caffe代碼閱讀——層次結構

作者:馮超
鏈接:https://zhuanlan.zhihu.com/p/21796890
來源:知乎
著作權歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權,非商業(yè)轉(zhuǎn)載請注明出處。

Caffe是一款優(yōu)秀的深度神經(jīng)網(wǎng)絡的開源軟件,下面我們來聊聊它的源代碼以及它的實現(xiàn)。Caffe的代碼整體上可讀性很好,架構比較清晰,閱讀代碼并不算是一件很困難的事情。不過在閱讀代碼之前還是要回答兩個問題:
  • 閱讀代碼是為了什么?
  • 閱讀到什么程度?(這個問題實際上和前面的問題相關)

  • 閱讀代碼大體上來說有下面幾個目的:

  • 搞清楚代碼所實現(xiàn)的算法或者功能。對算法本身不是很了解,希望通過閱讀代碼了解算法。
  • 搞清楚代碼在實現(xiàn)算法過程中的細節(jié)。這種情況下,一般對算法已經(jīng)有大概的了解,讀代碼是為了了解代碼中對算法細節(jié)的考量。當然,如果想使用代碼,了解代碼細節(jié)是很有幫助的。
  • 擴展代碼。在開源代碼的基礎上,利用已有的框架,增加或者修改功能,來實現(xiàn)自己想要的功能。這個就需要對代碼的架構細節(jié)有更加深入的了解。

  • 我們的目標是擴展代碼。Caffe中主要的擴展點就是Layer和Solver,當然其他的部分也可以擴展,只不過要改動的代碼會多一些。

    當確定了上面第一個問題,下面就是第二個問題了。讀代碼要讀到什么程度?一般來說,我覺得閱讀代碼這件事情可以用一個Logistic型的函數(shù)來表示:

    這個圖上,橫軸是閱讀代碼花費的時間,縱軸是閱讀代碼帶來的效果。對于代碼量比較大的項目,一開始閱讀肯定是蒙的,需要花一定的時間梳理清楚各個文件,各個模塊之間的關系。隨著結構關系逐漸清晰,讀者開始領會代碼中所表達的含義,閱讀代碼的效果直線上升。然而當我們把代碼主線和重要支線弄懂后,再讀一些小支線的收益就不會太大。所以根據(jù)閱讀代碼的性價比和Caffe代碼自身的特點,我們只會將主線和一些重要支線閱讀完,估計也就是整體代碼量的一半。

    Caffe代碼的主線結構抽象

    不同于其他的一些框架,Caffe沒有采用符號計算的模式進行編寫,整體上的架構以系統(tǒng)級的抽象為主。所謂的抽象,就是逐層地封裝一些細節(jié)問題,讓上層的代碼變得更加清晰。那么就讓我們來順著Caffe的抽象層級看看Caffe的主線結構:

    SyncedMem:這個類的主要功能是封裝CPU和GPU的數(shù)據(jù)交互操作。一般來說,數(shù)據(jù)的流動形式都是:硬盤->CPU內(nèi)存->GPU內(nèi)存->CPU內(nèi)存->(硬盤),所以在寫代碼的過程中經(jīng)常會寫CPU/GPU之間數(shù)據(jù)傳輸?shù)拇a,同時還要維護CPU和GPU兩個處理端的內(nèi)存指針。這些事情處理起來不會很難,但是會很繁瑣。因此SyncedMem的出現(xiàn)就是把CPU/GPU的數(shù)據(jù)傳輸操作封裝起來,只需要調(diào)用簡單的接口就可以獲得兩個處理端同步后的數(shù)據(jù)。

    Blob:這個類做了兩個封裝:一個是操作數(shù)據(jù)的封裝。在這里使用Blob,我們可以操縱高維的數(shù)據(jù),可以快速訪問其中的數(shù)據(jù),變換數(shù)據(jù)的維度等等;另一個是對原始數(shù)據(jù)和更新量的封裝。每一個Blob中都有data和diff兩個數(shù)據(jù)指針,data用于存儲原始數(shù)據(jù),diff用于存儲反。向傳播的梯度更新值。Blob使用了SyncedMem,這樣也得到了不同處理端訪問的便利。這樣Blob就基本實現(xiàn)了整個Caffe數(shù)據(jù)部分結構的封裝,在Net類中可以看到所有的前后向數(shù)據(jù)和參數(shù)都用Blob來表示就足夠了。

    數(shù)據(jù)的抽象到這個就可以了,接下來是層級的抽象。前面我們也分析過,神經(jīng)網(wǎng)絡的前后向計算可以做到層與層之間完全獨立,那么每個層只要依照一定的接口規(guī)則實現(xiàn),就可以確保整個網(wǎng)絡的正確性。

    Layer:Caffe實現(xiàn)了一個基礎的層級類Layer,對于一些特殊種類還會有自己的抽象類(比如base_conv_layer),這些類主要采用了模板的設計模式(Template),也就是說一些必須的代碼在基類寫好,一些具體的內(nèi)容在子類中實現(xiàn)。比方說在Layer的Setup中,函數(shù)中包括Setup的幾個步驟,其中的一些步驟由基類完成,一些步驟由子類完成。還有十分重要的Forward和Backward,基類實現(xiàn)了其中需要的一些邏輯,但是真正的運算部分則交給了子類。這樣當我們需要實現(xiàn)一個新的層時,我們不需要管理瑣碎的事物,只要關系好層的初始化和前后向即可。

    Net:Net將數(shù)據(jù)和層組合起來做進一步的封裝,對外暴露了初始化和前后向的接口,使得整體看上去和一個層的功能類似,但內(nèi)部的組合可以是多種多樣。同時值得一提的是,每一層的輸入輸出數(shù)據(jù)統(tǒng)一保存在Net中,同時每個層內(nèi)的參數(shù)指針也保存在Net中,不同的層可以通過WeightShare共享相同的參數(shù),所以我們可以通過配置實現(xiàn)多個神經(jīng)網(wǎng)絡層之間共享參數(shù)的功能,這也增強了我們對網(wǎng)絡結構的想象力。

    Solver:有了Net我們實際上就可以進行網(wǎng)絡的前向后向計算了,但是關于網(wǎng)絡的學習訓練的功能還有些缺乏,于是在此之上,Solver類進一步封裝了訓練和預測相關的一些功能。與此同時,它還開放了兩類接口:一個是更新參數(shù)的接口,繼承Solver可以實現(xiàn)不同的參數(shù)更新方法,如大家喜聞樂見的Momentum,Nesterov,Adagrad等。這樣使得不同的優(yōu)化算法能夠應用其中。另外一個是訓練過程中每一輪特定狀態(tài)下的可注入的一些回調(diào)函數(shù),在代碼中這個回調(diào)點的直接使用者就是多卡訓練算法。

    IO:有了上面的東西就夠了?還不夠,我們還需要輸入數(shù)據(jù)和參數(shù),正所謂巧婦難為無米之炊,沒有數(shù)據(jù)都是白搭。DataReaderDataTransformer幫助準備輸入數(shù)據(jù),Filler對參數(shù)進行初始化。一些Snapshot方法幫助模型的持久化,這樣模型和數(shù)據(jù)的IO問題也解決了。

    多卡:對于單GPU訓練來說,基本的層次關系到這里也就結束了,如果要進行多GPU訓練,那么上層還會有InternalThreadP2PSync兩個類,這兩個類屬于最上層的類了,而他們所調(diào)用的也只有Solver和一些參數(shù)類。

    其實到這里,Caffe的主線也就基本走完了。我們可以畫一張圖把Caffe的整體層次關系展示出來:

    如果對這張圖和圖中的一些細節(jié)比較清楚的話,那么你對Caffe的了解應該已經(jīng)不錯了。后面關于Caffe源碼分析的文章就可以不看了。如果沒有,那么我們還是可以繼續(xù)關注一下。當然如果想真正理解這張圖中所表達的含義,還是要真正地讀一下代碼,去理解一些細節(jié)。但是有些細節(jié)這里就不做詳細的分析了,下一回我們會站在Layer的角度去看一個Layer在訓練過程的全部經(jīng)歷。


    2.

    Caffe源碼閱讀——Net組裝

    最近忙著看TI沒有及時寫文章,今天趕緊補一篇……

    Net是Caffe代碼中一個比較核心的類,往下看它封裝了所有的Layer,構建起了整個神經(jīng)網(wǎng)絡;往上看它對外提供了前向后向計算,以及核心數(shù)據(jù)結構的訪問結構,使得再上層的Solver可以利用Net比較輕松地實現(xiàn)Train和Test的策略。當然,正是因為它的重要性,組裝Net是一個比較復雜的部分。這一回我們就來看看Net的內(nèi)容。

    當然,說在前面,看Net組裝的代碼有兩個目的:

  • 了解作為一個成熟的CNN模型框架需要考慮的一些問題;
  • 如果想對網(wǎng)絡結構做擴展,如寫一個新的Layer,其中的一些數(shù)據(jù)是如何在Layer和Net之間流動的

  • 首先,為了使問題不那么復雜,我們先從訓練模型時輸出的log看看Net組裝的幾個關鍵步驟,然后再把這個過程慢慢展開,了解組裝的所有細節(jié)。

    Log眼中的Net組裝

    為了更好地展示Net組裝的一些細節(jié),我們在這里選取了一個實際例子,就是Caffe的examples里面的siamese model。關于這個model的細節(jié)這里就不多說了,感興趣的可以去看官方或者非官方的文檔,這里只提一點:這個網(wǎng)絡除了包含其他正常網(wǎng)絡中的一些特性之外,還具有網(wǎng)絡參數(shù)復用的特點,在后面的分析中我們會用到。

    下面我們要看的就是Net組裝的Log。這段Log一般都是大家在訓練網(wǎng)絡時一閃而過的大段Log,當然如果它沒有一閃而過而是停下來了,有可能是你的網(wǎng)絡定義有問題爆出了錯誤。這段Log內(nèi)容比較多,總體來說就是Train階段和Test階段的兩個網(wǎng)絡組裝起來。我們重點關注其中的幾個片段,來大概了解Net組裝的一些核心內(nèi)容,也是那些比較值得打印出來的內(nèi)容。

    首先是一個正常的卷積層conv1,Log如下所示(以下代碼的行號可能會有不同,但位置是相近的):

    layer_factory.hpp:77] Creating layer conv1 net.cpp:92] Creating Layer conv1 net.cpp:428] conv1 <- data net.cpp:402] conv1 -> conv1 net.cpp:144] Setting up conv1 net.cpp:151] Top shape: 64 20 24 24 (737280) net.cpp:159] Memory required for data: 3752192

    這其中第一行是創(chuàng)建這個Layer實例的代碼,具體的創(chuàng)建過程在layer_factory里面。為了方便創(chuàng)建Layer,Caffe采用了工廠方法的設計模式,只要提供Layer的名字(在配置文件中參數(shù)叫type),就可以根據(jù)名字和對應參數(shù)實例化一個Layer。這部分的細節(jié)只要認真看一下就會明白。

    第3,4行顯示了創(chuàng)建當前層的bottom和top數(shù)據(jù)的過程。這里涉及到net.cpp中的AppendBottom和AppendTop兩個方法,因為每一個bottom blob和top blob都有名字,這里就將他們之間的關系輸出在了這里。

    第5行看上去沒什么干貨,但是它代表了Layer的Setup函數(shù)已經(jīng)調(diào)用完成(或者Layer被share)。Layer的Setup函數(shù)是Layer初始化的關鍵函數(shù),這里面涉及到以下幾個具體的操作:

    CheckBlobCounts(bottom, top); LayerSetUp(bottom, top); Reshape(bottom, top); SetLossWeights(top);

    總結地說,這四句完成了:

  • 對bottom blob, top blob數(shù)量的檢查,父類實現(xiàn)。
  • 對Layer內(nèi)部相關變量的初始化,由具體的子類實現(xiàn)
  • 傳入時bottom blob的維度已經(jīng)確定,Layer需要根據(jù)自己要做的計算確定top blob的緯度。比方說這一層是卷積層,維度是20*5*5,輸入圖像是1*28*28,也就是bottom blob的維度,那么輸入就是20*24*24,這也是上面log里面算出的結果,只不過還加了一個batch size。這個函數(shù)由具體的子類實現(xiàn)。
  • 對Layer是否輸出loss以及輸出loss要做的操作進行初始化。父類實現(xiàn)。必須說一句,Caffe中關于Loss Layer中Loss_weight,loss_,top.cpu_diff的數(shù)據(jù)設定還是有點繞且有點trick的。

  • 好了回到上面的log。接下來的那一句告訴了我們top層應該輸出的維度。這里輸出了維度就是為了讓不放心的朋友算一下,看看和你想的是否一樣。當然,輸出這句log的循環(huán)不是只做了這件事,它的主要工作就是設置top blob的loss_weight。

    最后一句計算了該層top blob所占用的內(nèi)存??梢钥闯鼋刂恋竭@一層,內(nèi)存消耗大約是3M多,還不算大。

    好,這就是一個最典型的Layer的初始化,下面這個ReLU層就稍微有些不同了:

    layer_factory.hpp:77] Creating layer relu1 net.cpp:92] Creating Layer relu1 net.cpp:428] relu1 <- ip1 net.cpp:389] relu1 -> ip1 (in-place) net.cpp:144] Setting up relu1 net.cpp:151] Top shape: 64 500 (32000) net.cpp:159] Memory required for data: 5769472

    這里面最不同的就是第4行結尾的(in-place),這說明relu的bottom blob和top blob是同一個數(shù)據(jù),這和我們在網(wǎng)絡中的定義是一樣的。in-place的好處就是減少內(nèi)存的操作,但是這里在統(tǒng)計內(nèi)存消耗時并沒有考慮in-place帶來的節(jié)省。

    接下來就是共享網(wǎng)絡的conv1_p了:

    layer_factory.hpp:77] Creating layer conv1_p net.cpp:92] Creating Layer conv1_p net.cpp:428] conv1_p <- data_p net.cpp:402] conv1_p -> conv1_p net.cpp:144] Setting up conv1_p net.cpp:151] Top shape: 64 20 24 24 (737280) net.cpp:159] Memory required for data: 8721664 net.cpp:488] Sharing parameters 'conv1_w' owned by layer 'conv1', param index 0 net.cpp:488] Sharing parameters 'conv1_b' owned by layer 'conv1', param index 1

    這一段最有特點的是最后兩句“Sharing”,因為siamese model中擁有參數(shù)完全相同的兩個網(wǎng)絡,所以在構建時候,第二個網(wǎng)絡檢測到參數(shù)名字已經(jīng)存在,說明該層的參數(shù)和其他層共享,于是在這里打印出來告訴用戶這一點。當然,這一句之前沒有打印出來的內(nèi)容告訴了我們,實際上Net類中還負責了參數(shù)相關的初始化。這部分的內(nèi)容實際上還挺多,除了參數(shù)共享,還有對參數(shù)learning rate,weight decay的設定。

    最后是最特別的一層:loss層

    net.cpp:92] Creating Layer loss net.cpp:428] loss <- feat net.cpp:428] loss <- feat_p net.cpp:428] loss <- sim net.cpp:402] loss -> loss net.cpp:144] Setting up loss net.cpp:151] Top shape: (1) net.cpp:154] with loss weight 1 net.cpp:159] Memory required for data: 10742020

    這一層看上去沒有什么特別,該有的和前面一樣,但是唯一不同的就是它的倒數(shù)第二行,這說明這一層是有l(wèi)oss weight的。至于有l(wèi)oss weight有什么用,以后我們會詳細說這個事情。這里簡單說一下,有l(wèi)oss weight表示這個blob會被用于計算loss。

    前面的log主要解決了網(wǎng)絡的組裝和前向的一些計算,從log中,我們可以看出Net完成了以下的事情:

  • 實例化Layer
  • 創(chuàng)建bottom blob,top blob
  • Setup Layer(初始化Layer,確定top blob維度)
  • 確定layer的loss_weight
  • 確定layer的參數(shù)是否共享,不共享則創(chuàng)建新的

  • 從上面的過程也可以看出,整個網(wǎng)絡中所有的流動性變量(bottom blob,top blob)都保存在Net中,同時對于各層的參數(shù),根據(jù)各層的共享關系做了標記。這樣好處是集中管理了網(wǎng)絡中的數(shù)據(jù),方便對數(shù)據(jù)進行操作。

    再往下面,我們可以截取一小段log來:

    net.cpp:220] pool1 needs backward computation. net.cpp:220] conv1 needs backward computation. net.cpp:222] slice_pair does not need backward computation. net.cpp:222] pair_data does not need backward computation. net.cpp:264] This network produces output loss net.cpp:277] Network initialization done.

    接下來是統(tǒng)計一個層次是否需要進行反向傳播的計算。一般來說我們的層是都需要計算的,但是也會有一些層不需要計算,比方說數(shù)據(jù)層,就像上面的log那樣,還有就是一些希望固定的層,這個一般在finetune網(wǎng)絡的時候用的上。因為反向計算一般比前向計算慢,如果有不需要計算的Layer,直接跳過計算是可以節(jié)省時間的。

    最后是整個網(wǎng)絡產(chǎn)生的輸出,這個輸出會在訓練迭代中顯示出來的。

    了解了這些,我們就對Net裝載有了大概的了解,再去看它的代碼就會輕松些。

    最后,關于Net類中所有的成員變量與它們之間的關系,我們可以用下面的一張圖來理解就好:

    把Net的初始化理解后,其實Net以下的架構方面的問題就不多了。下面我再看看Net以上的東西,Solver以及Caffe里“簡單”的多卡訓練。

    3.

    Caffe代碼閱讀——Solver

    前面我們聊了Net組裝的內(nèi)容,接下來我們來看看Solver的內(nèi)容。Solver主體有兩部分:初始化和訓練。初始化內(nèi)容相對比較簡單,這里就不說了;下面我們來說說訓練中的幾個關鍵函數(shù)。

    核心函數(shù):Step

    真正的訓練在Step函數(shù)內(nèi),這里有多卡訓練的關鍵回調(diào)函數(shù):on_start()和on_gradient_ready(),具體的調(diào)用方法我們后面再說,在這兩個回調(diào)函數(shù)中間有兩個重要的過程:ForwardBackward和UpdateSmoothedLoss。在on_gradient_ready之后有一個關鍵函數(shù)ApplyUpdate(),這里面的代碼在Sgd_solver中。下面我們詳細看一下。

    ForwardBackward

    這里主要調(diào)用了Net中的代碼,主要完成了前向后向的計算,前向用于計算模型的最終輸出和Loss,后向用于計算每一層網(wǎng)絡和參數(shù)的梯度。對于前向后向的具體內(nèi)容這里需要詳細敘述了,唯一值得一提的是前向的Loss計算,這部分代碼實際上實在Layer里面,具體涉及到loss_weight這個參數(shù)相關的初始化和loss()的判斷,同時還有Loss_Layer在Setup函數(shù)中的初始化。

    UpdateSmoothedLoss

    這個函數(shù)主要做Loss的平滑。由于Caffe的訓練方式是SGD,我們無法把所有的數(shù)據(jù)同時放入模型進行訓練,那么部分數(shù)據(jù)產(chǎn)生的Loss就可能會和全樣本的平均Loss不同,在必要時候?qū)oss和歷史過程中更新的Loss求平均就可以減少Loss的震蕩問題。代碼中的平滑方法比較簡單,大家一看便知。

    下面就是ApplyUpdate函數(shù),這個函數(shù)真正完成了參數(shù)更新的任務。Caffe的參數(shù)更新只利用了模型的梯度信息,沒有利用二階信息。下面就詳細介紹下更新參數(shù)的幾個過程:

    • GetLearningRate
    • ClipGradients
    • Normalize
    • Regularize
    • ComputeUpdateValue

    GetLearningRate

    learning rate的故事我們前面已經(jīng)聊過了,在CNN訓練中這確實是個大問題。Caffe為了讓learning rate的設計更靈活,提供了一系列的learning rate方案:

    • fixed:lr永遠不變
    • step
    • exp
    • inv
    • multistep:直接寫iter在某個范圍內(nèi)時lr應該是多少
    • poly
    • sigmoid

    這些方案各有優(yōu)劣,選擇自己順手的就好。

    ClipGradients

    這一步主要是對梯度值做一個限制,如果梯度值過大,那么這里就會對梯度做一個修剪,對所有的參數(shù)乘以一個縮放因子,使得所有參數(shù)的平方和不超過參數(shù)中設定的梯度總值。這個功能感覺上像是對全局函數(shù)設置了一個Trust Region,可以防止更新的量過大二導致梯度發(fā)散。我認為這一步的想法是很好的,但是實際操作中可能會有問題。實際中可能只有部分參數(shù)的梯度比較大,而其他參數(shù)的梯度本身比較小,那么對所有的參數(shù)乘以相同的因子會讓一些本來比較小的參數(shù)變得更小,這樣會帶來一些不公平。

    Normalize

    這一步同樣考慮了一些單一Batch不足以完成訓練的問題,通過限制每個Batch的更新量來控制更新總量,代碼比較簡單。

    Regularize

    到這一步終于要計算正則項的梯度了。Caffe提供兩種正則方法——L2和L1,其中L2采用了標準的梯度下降方法,L1采用了sub-gradient的計算方法。L2的優(yōu)化計算比較簡單,沒有什么好說的,但是L1的計算還是有點值得玩味的地方的。這里采用的sub-gradient方法其實本身沒有什么問題,不過Lasso的優(yōu)化還可以有其他的方法,這個問題以后可以再細聊。

    ComputeUpdateValue

    到這里,我們終于來到了梯度計算的最后一站,這時候我們終于完成了對梯度的計算,下面該考慮lr和梯度結合起來如何計算最終的梯度優(yōu)化值了。sgd方法主要采用momentum加梯度的優(yōu)化方法。關于momentum的優(yōu)勢我們前面已經(jīng)聊過了。除此之外,Caffe還提供了一系列的梯度計算方法,這些優(yōu)化方法各有特點,以后我們可以慢慢來看。

    當計算完這一步,我們就可以調(diào)用Blob中的Update把每個參數(shù)的data和diff進行相加,計算出最終的結果。這樣,整個優(yōu)化過程就完成了。至于剩下的一些內(nèi)容都不是核心過程,就略去不看了。

    如果我們采用單卡訓練的策略,那么閱讀代碼到這里也差不多了。不過多卡訓練對于大規(guī)模的訓練任務來說是必不可少的,所以我們接下來趁熱打鐵地看看Caffe的多卡訓練。

    多卡訓練算法

    Caffe的多卡訓練算法總體思路是數(shù)據(jù)并行,我們用不同的GPU處理不同的數(shù)據(jù),然后將所有的梯度更新匯總。由于Solver在訓練中給了兩個回調(diào)函數(shù),多卡訓練也主要利用了這兩個回調(diào)函數(shù)進行:

  • on_start():將參數(shù)拷貝到每一個GPU中。
  • ForwardBackward():每個GPU各自計算自己的前向后向結果。
  • on_gradient_ready():將反向梯度匯總到一起。
  • ApplyUpdate():在匯總的線程上進行參數(shù)更新

  • 其中第2步由每一個CPU線程和自己的GPU并行完成,第4步由匯總的CPU和自己的GPU完成,剩下的1,3兩步主要是完成數(shù)據(jù)傳輸?shù)娜蝿?#xff0c;也是多卡計算中主要完成的部分。

    Caffe采用樹型結構進行參數(shù)傳遞,其中一個CPU線程和GPU作為樹型結構的根,其他的則作為根下面的節(jié)點。為了更快地傳輸GPU數(shù)據(jù),樹型結構的構建要考慮GPU之間是否相近,比方說兩個GPU之間是否可以進行P2P的直傳。在前面的翻譯博客中我們已經(jīng)聊過GPU之間數(shù)據(jù)傳輸?shù)膯栴}了,這里的樹形結構也主要以此做考慮。

    我們假設4塊GPU的拓撲結構如下:


    nvidia-smi topo -mGPU0 GPU1 GPU2 GPU3 GPU0 X PHB SOC SOC GPU1 PHB X SOC SOC GPU2 SOC SOC X PHB GPU3 SOC SOC PHB X

    那么我們構造出的樹型結構如下所示,數(shù)據(jù)傳輸也是按照這樣的結構傳輸:

    這樣1,3的數(shù)據(jù)傳遞就解決了,具體的過程請詳細閱讀代碼,這里就不敘述了。

    對Caffe代碼的基本介紹就到這里了,我們對代碼的整體結構有了比較清晰的認識,下面我們將分析模型中各個部分的特性。



    總結

    以上是生活随笔為你收集整理的Caffe代码阅读的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。