摘要: GPU在機器學習中相當重要,但不多有人能解釋清楚,本文對此進行了一番研究。
若是你從事機器學習,或者碰巧是NVidia的投資者,你必定據說過GPU對這個領域相當重要。但對我來講,GPU一直是個謎。我對「GPU=快速」有很好的理解,但僅此而已。服務器
老實說,這能讓你走得更遠。我開始着手使用Keras進行ML工做,Keras是谷歌的TensorFlow庫之上的API。Keras是一個設計精美的API,它作出了一個很是實際的決定:大多數事情應該是簡單的,困難的事情應該是可能的。併發
正由於如此,我真的不須要學習任何關於GPU的知識。我只是運行了一些神奇的操做系統命令和一些神奇的代碼行-嘣!-大規模模型訓練加速。我對這種狀況很是滿意。機器學習
然而,在feedly最新的項目中,咱們決定試用PyTorch,由於它愈來愈流行。但也有一些有利的數據。新的(強烈推薦的)fast.ai課程順序從Keras切換到PyTorch。 PyTorch模型也贏得了不少最近的Kaggle比賽。性能
因此咱們深刻研究並發現PyTorch經過清晰一致的API使全部事情成爲可能。它比Keras更靈活,因此權衡的是簡單的事情變得更難,但困難的事情變得更容易。若是你正在嘗試新的或不尋常的模型(或者你碰巧是一個控制狂),PyTorch絕對是一個更好的選擇。學習
當GPU加速個人PyTorch模型的時候,我谷歌了一下神奇的「GPU -> on」代碼行,我發現它不存在!一如既往,Pytorch使這比Keras更難實現,可是提供了一些關於如何着手作事的API。全部這一切的結果是,我不得不咬緊牙關,創建了一個關於GPU如何被用來加速模型訓練的心智模型。優化
爲何GPU很快編碼
模型一般具備許多不少參數。例如,流行的VGG圖像分類模型有大約1.4億個參數,分爲16層!在運行推理(預測)時,你須要將輸入數據(圖像)傳遞到每一個圖層,一般將該數據乘以圖層參數。在訓練期間,你還必須稍微調整每一個參數以更好地擬合數據。那是很大的計算量!spa
CPU很擅長快速完成一些事情。 這一般很好,有足夠的分支(若是用戶這樣作,那樣作),以及大規模並行性實際上不可能的其餘順序約束。GPU很好,能夠作不少「慢」的事情。因爲它們最初用於執行圖形要求,所以它們但願一次性完成大量工做(考慮將圖像的全部像素轉換爲灰度)。因此這裏有一個權衡,對於ML來講GPU因爲能夠並行完成這些巨大的算術運算而贏得大量時間。操作系統
具體來講,個人macbook有一個運行速度爲3.1Ghz且有4個內核的CPU。NVidia K80 GPU擁有近5000個內核,儘管運行速度要慢得多——562Mhz。雖然這樣作並不公平,但你能夠看到K80的時鐘速度大約慢了6倍,可是並行速度提升了1250倍。設計
如何考慮GPU
PyTorch不是代碼行「GPU -> on」,而是「CUDA」張量。CUDA是一個用於在GPU上執行操做的庫。基本上,PyTorch要求你聲明要在GPU上放置的內容,而後你能夠像往常同樣執行操做。因此我想,讓咱們試着在GPU上添加一個圖層:
我運行代碼,當即獲得了一個錯誤在隱藏層計算中:
爲何?我當即知道它與我添加的.cuda()代碼有關,但我不知道爲何。在思考了GPU應該如何加速以後,我意識到,「固然它不工做,一個張量在GPU上,另外一個仍然在主內存中!」一切都搞定了。Pytorch容許在GPU內存中分配張量,而後使用GPU對這些張量進行操做。可是這些操做的結果會怎樣呢?讓咱們試試另外一個例子:
這是GPU內存中的另外一個張量!通過更多的反覆試驗,我發現我不得不改變本身的思惟方式。原來我覺得內存、CPU和GPU都混在一塊兒了:
我意識到我須要這樣想:
從本質上說,CPU/主內存和GPU/GPU內存都位於各自的小宇宙中。來自軟件工程背景的我開始將GPU操做看做是一個REST API。當你使用REST API時,真正的成本是來回發送數據。在本地執行任務的速度與在遠程服務器上執行任務的速度同樣快。可是你要避免的是大量的數據來回傳輸,由於這是純粹的開銷。
把這個類比往前推,咱們能夠看到,固然,PyTorch matmul結果是一個GPU張量是有道理的。這樣就很容易在GPU上進行進一步的操做,而不須要將數據傳送到主內存,而後再返回到GPU。因此若是咱們想要使用GPU,咱們真的想要GPU上的全部參數,由於這些參數將會被反覆使用在前向傳遞中產生預測而後在後向傳遞中更新。每一批特性都必須被傳送到GPU內存中。可是中間的和最終的結果(好比隱藏層的輸出)只能存在於GPU內存中。咱們須要作的就是不斷向GPU發送命令,告訴它如何操做參數和權重。
所以,在API類比中,咱們只作兩個「重」請求(在上圖中加星標的),一個用於初始化權重,另外一個用於在訓練後得到最終權重。但咱們可能會在這兩個請求之間發出數百萬個輕量級請求來訓練模型。
GPU性能的提升是真實的,並且是驚人的
那麼什麼樣的加速是可能的呢?PyTorch有一個不錯的MNIST小例子咱們可使用。使用CPU運行10個epoch須要153秒,使用GPU須要83秒。咱們能夠從理論上說,更大的模型能夠得到更大的收益。 不錯,真不錯。
一些實驗
這一切都很棒。通過一番思考和一些糟糕的繪圖以後,我對GPU的瞭解要好得多,而且所需的額外編碼也不錯。但個人想法是否正確?回答這個問題的最好方法是創建一個實驗。我想向本身證實運輸數據是工做流程的「緩慢部分」。因此我嘗試瞭如下三件事:
1.作一個200x200矩陣乘以numpy,一個高度優化的CPU線性代數庫。
2.使用PyTorch cuda張量在GPU上進行200x200矩陣乘法運算。
3.使用PyTorch cuda張量在GPU上進行200x200矩陣乘法運算,每次都來回複製數據。
正如預期的那樣,GPU只運行得更快,這一次大約是6倍。有趣的是,1和3幾乎花費了相同的時間。GPU操做的效率幾乎徹底被數據傳輸的低效所平衡!
本文做者:【方向】
本文爲雲棲社區原創內容,未經容許不得轉載。