Mac 上機器學習 Machine Learning 使用GPU加速

Mac上使用GPU進行機器學習加速

2017年時,初次接觸機器學習,當時不記得tensorflow是什麼版本了,是不支持AMD GPU進行加速的,對於一個是Macbook Pro 15寸帶AMD獨顯的人來說,入門訓練很不友好。

我相信只要試過GPU加速訓練的感覺,是絕對不會再想使用CPU進行訓練了,即使是驗證想法,CPU訓練也太慢了。想起了多年前爲了讓本機的Tensorflow支持Intel的向量指令集,還要本地編譯源碼好久,然後比起原來也沒多大提升,甚至浮點運算精度還下降了,都是淚。

使用Mac的GPU進行ML訓練的意義

相信很多人會說,有誰會用Mac進行訓練?Mac寫代碼,服務器多個卡一起跑不好嗎?

的確,對於有這個多卡服務器資源的人來說,Mac訓練毫無意義,裝了科學計算卡的服務器顯存大得多,浮點計算能力也強。但是對於剛剛入門的新手,很少的實驗室老闆不會爽快地給你服務器資源進行訓練,很多學生都是在自己的電腦上折騰入門的,這時只有Mac的同學就欲哭無淚的。

我之前入門的一些ML課,服務資源有這樣幾個來源

  1. Google Cloud Platform 提供300刀的免費額度,可以開帶科學計算卡GPU的雲服務器,這個很爽。
  2. 一些課提供GPU平臺,這個是良心的。
  3. 厚臉皮地問實驗室老師蹭服務器,但是很少會爽快地給資源來提升你個人

對於很多人來說,有自己的電腦的一個GPU可以加速訓練,無論是入門學習,還是驗證想法都是有益的。

支持Mac的GPU的機器學習後端 PlaidML 前端Keras

PlaidML 是Intel開發的機器學習後端,2018年發佈。目前支持Keras、nGraph等前端平臺。github網址爲https://github.com/plaidml/plaidml
這意味着,使用Keras的項目幾乎可以無成本地將後端從tensorflow切換到PlaidML。 使用Tesorflow的Keras前端的項目,遷移也是較容易的。2019年末看到了PlaidML社區中關於對tensorflow支持的討論,但是目前還沒有發佈成果。
PlaidML 支持Metal、OpenCL等並行計算框架,這意味着Mac上的顯卡(包括核顯、A卡、N卡)都能得到很好的支持。

PlaidML安裝流程

macOS官方安裝教程https://plaidml.github.io/plaidml/docs/install.html#macos
十分簡單,就四五行shell命令,我的Macbook Pro安裝時沒有碰到bug。
首先創建一個python虛擬環境,如果還不知道什麼是python虛擬環境的建議先去搜索virtualenv是什麼,虛擬環境十分有用,可以避免把你電腦上的python環境搞亂,我見過很多入門的人各種包版本出錯(包括使用conda的),因此瞭解python虛擬環境的使用是必要的,也很簡單。
下面的代碼做了兩件事,第一行創建虛擬環境,環境文件在名爲plaidml-venv的文件目錄中,第二行是激活該環境,激活之後的python調用都是使用的該目錄下的python環境,pip安裝也都是安裝到該目錄下。

python3 -m venv plaidml-venv
source plaidml-venv/bin/activate

然後安裝帶PlaidML的Keras

pip install -U plaidml-keras

然後設置PlaidML

plaidml-setup

然後會有三個選擇,
第一個是問你要不要Dev模式,如下

Default Config Devices:
   llvm_cpu.0 : CPU (via LLVM)
   metal_amd_radeon_pro_560.0 : AMD Radeon Pro 560 (Metal)
   metal_intel(r)_hd_graphics_630.0 : Intel(R) HD Graphics 630 (Metal)

Experimental Config Devices:
   llvm_cpu.0 : CPU (via LLVM)
   opencl_amd_radeon_pro_560_compute_engine.0 : AMD AMD Radeon Pro 560 Compute Engine (OpenCL)
   opencl_intel_hd_graphics_630.0 : Intel Inc. Intel(R) HD Graphics 630 (OpenCL)
   metal_amd_radeon_pro_560.0 : AMD Radeon Pro 560 (Metal)
   metal_intel(r)_hd_graphics_630.0 : Intel(R) HD Graphics 630 (Metal)

Using experimental devices can cause poor performance, crashes, and other nastiness.

Enable experimental device support? (y,n)[n]:

爲了速度與穩定性,當然是選擇n

第二個問題問你用什麼設備進行默認計算,如下

Multiple devices detected (You can override by setting PLAIDML_DEVICE_IDS).
Please choose a default device:

   1 : llvm_cpu.0
   2 : metal_amd_radeon_pro_560.0
   3 : metal_intel(r)_hd_graphics_630.0

Default device? (1,2,3)[1]:

爲了速度,選擇amd顯卡
最後一個問題問你是不是保存設置到用戶文件夾,保存就是了

Save settings to /Users/***/.plaidml? (y,n)[y]:y

然後就安裝好了,可以用下面的命令測試一下MobileNet的運行效果

pip install plaidml-keras plaidbench
plaidbench keras mobilenet

測試訓練神經網絡

當然要用一個簡單的神經網絡訓練一下測試
就使用mnist數據機跑個簡單的cnn測試一下,測試代碼如下

import numpy as np
import os
import time

os.environ["KERAS_BACKEND"] = "plaidml.keras.backend"

import keras

from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K

batch_size = 256
num_classes = 10
epochs = 10

# input image dimensions
img_rows, img_cols = 28, 28

# the data, split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()

if K.image_data_format() == 'channels_first':
    x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
    x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
    input_shape = (1, img_rows, img_cols)
else:
    x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
    x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
    input_shape = (img_rows, img_cols, 1)

x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')

# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

model = Sequential()
model.add(Conv2D(16, kernel_size=(3, 3), activation='relu', input_shape=input_shape))
model.add(Dropout(0.5))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, kernel_size=(3, 3), activation='relu'))
model.add(Dropout(0.5))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(num_classes, activation='softmax'))

model.compile(loss=keras.losses.categorical_crossentropy,
              optimizer=keras.optimizers.Adadelta(),
              metrics=['accuracy'])
train_start_t = time.time()
model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=epochs,
          verbose=1,
          validation_data=(x_test, y_test))
train_end_t = time.time()
train_dur_t = train_end_t - train_start_t
print("train use time %f s" % train_dur_t)
test_start_t = time.time()
score = model.evaluate(x_test, y_test, verbose=0)
test_end_t = time.time()
test_dur_t = test_end_t - test_start_t
print("test use time %f s" % test_dur_t)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

訓練過程輸出如下

Using plaidml.keras.backend backend.
x_train shape: (60000, 28, 28, 1)
60000 train samples
10000 test samples
INFO:plaidml:Opening device "metal_amd_radeon_pro_560.0"
Train on 60000 samples, validate on 10000 samples
Epoch 1/10
60000/60000 [==============================] - 9s 145us/step - loss: 0.4366 - acc: 0.8646 - val_loss: 0.4369 - val_acc: 0.9487
Epoch 2/10
60000/60000 [==============================] - 7s 122us/step - loss: 0.1542 - acc: 0.9539 - val_loss: 0.2412 - val_acc: 0.9733
Epoch 3/10
60000/60000 [==============================] - 7s 124us/step - loss: 0.1192 - acc: 0.9636 - val_loss: 0.2171 - val_acc: 0.9778
Epoch 4/10
60000/60000 [==============================] - 7s 121us/step - loss: 0.1019 - acc: 0.9683 - val_loss: 0.1783 - val_acc: 0.9803
Epoch 5/10
60000/60000 [==============================] - 7s 119us/step - loss: 0.0904 - acc: 0.9722 - val_loss: 0.1661 - val_acc: 0.9831
Epoch 6/10
60000/60000 [==============================] - 7s 119us/step - loss: 0.0822 - acc: 0.9749 - val_loss: 0.1371 - val_acc: 0.9839
Epoch 7/10
60000/60000 [==============================] - 7s 119us/step - loss: 0.0756 - acc: 0.9766 - val_loss: 0.1437 - val_acc: 0.9829
Epoch 8/10
60000/60000 [==============================] - 7s 121us/step - loss: 0.0722 - acc: 0.9776 - val_loss: 0.1240 - val_acc: 0.9859
Epoch 9/10
60000/60000 [==============================] - 7s 119us/step - loss: 0.0667 - acc: 0.9794 - val_loss: 0.1159 - val_acc: 0.9870
Epoch 10/10
60000/60000 [==============================] - 7s 119us/step - loss: 0.0641 - acc: 0.9799 - val_loss: 0.1299 - val_acc: 0.9877
train use time 73.713526 s
test use time 1.666734 s
Test loss: 0.1298656805753708
Test accuracy: 0.9877

跑下來一個epoch用7s作用,雖然比核彈卡要慢一些,但是還是很快的。

我之前用nvidia-tesla-p100,使用tensorflow.keras跑這個網絡,100個epoch花了227s,現在使用AMD Radeon Pro 560,100個epoch 734s。

這樣看小數據的訓練也只是核彈卡3倍時間,尚能接受,當然由於顯存原因,大的網絡和數據就不太合適,新的Macbook Pro有8G顯存的Radeon 5500M Pro,應該會好些,iMac、iMac Pro和Mac Pro就更不在話下了,但是我想應該沒有什麼人買個iMac Pro來做機器學習的。

使用CPU的tensorflow與使用CPU、核顯、獨立GPU的PlaidML進行對比,1個epoch訓練時間如下

tf CPU Intel CPU 2.9GHz 6Thread 4Core Intel hd graphics 630 AMD Radeon Pro 560
21.39 111.47s 28.69s 7.34s

首先可以發現,tensorflow的CPU向量加速做的還是比PlaidML做的好的,可能PlaidML沒有過多地投注精力在CPU加速上。獨立GPU的訓練速度是使用CPU的tesorflow的3倍,加速效果明顯。另外使用核顯訓練時loss有些問題,可能對Intel 核顯的支持還不夠完善。
另外,這裏的網絡比較小,當我擴大網絡規模時,gpu加速的效果更加明顯。

總結

使用Mac上的GPU訓練優點師能夠幫助ML初學者快速入門、煉丹師方便地驗證想法,對Keras、nGraph框架支持較好。
缺點是目前對tensorflow、pytorch的支持還不夠完善。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章