正如在 批量標準化紙 中所解釋的,如果神經(jīng)網(wǎng)絡的輸入是高斯的,那么訓練它就變得容易多了。這很清楚。如果你的模型輸入不是高斯的, RAPIDS 會在眨眼間把它轉換成高斯的。
高斯秩變換 是一種新的標準化技術,用于轉換輸入數(shù)據(jù)以訓練深層神經(jīng)網(wǎng)絡。最近,我們在 預測分子競爭性質 中使用了這種技術,它很容易將 m 消息傳遞神經(jīng)網(wǎng)絡模型 的精度提高了一個顯著的幅度。這篇博文將展示如何使用 RAPIDS cuDF 和 Chainer CuPy 實現(xiàn) GPU 加速的Gauss 秩變換,并使用 pandas 和 NumPy 替換來實現(xiàn) 100 倍加速 。
介紹
輸入歸一化是訓練神經(jīng)網(wǎng)絡的關鍵。高斯秩變換的思想最早是由 邁克爾·賈勒。 在他的 塞古羅港的安全駕駛預測 挑戰(zhàn)的勝利解中提出的。他訓練去噪自動編碼器,并嘗試了幾種輸入標準化方法。最后,他得出這樣的結論:
我在過去發(fā)現(xiàn)的最棒的東西是 GaussRank ,它能直接發(fā)揮作用。這通常比標準的 mean / std 定標器或 min / max (標準化)好得多。
有三個步驟可以將任意分布下的連續(xù)值向量轉換為基于秩的高斯分布,如圖 1 所示。
圖 1 :高斯秩變換。
CuPy 實現(xiàn)非常簡單,非常類似于 NumPy 操作。實際上,只需更改導入的函數(shù),就可以將整個進程從 CPU 移動到 GPU ,而無需任何其他代碼更改。
import cupy as cp
from cupyx.scipy.special import erfinv
import matplotlib.pyplot as plt
import numpy as np
from scipy.special import erfinv as sp_erfinv
x_gpu = cp.random.rand(20) # input array
x_cpu = cp.asnumpy(x_gpu)
x_gpu
array([0.55524998, 0.42394212, 0.01200076, 0.13974612, 0.74289723, 0.19072088, 0.47061846, 0.61921186, 0.96994115, 0.44076614, 0.04326316, 0.33698309, 0.47978816, 0.00819107, 0.63463167, 0.03370001, 0.0369827 , 0.84651929, 0.25335235, 0.75172228])
[13 9 1 5 16 6 11 14 19 10 4 8 12 0 15 2 3 18 7 17] [13 9 1 5 16 6 11 14 19 10 4 8 12 0 15 2 3 18 7 17]
r_gpu = (r_gpu/r_gpu.max()-0.5)*2 # scale to (-1,1)
epsilon = 1e-6
r_gpu = cp.clip(r_gpu,-1+epsilon,1-epsilon)
print(r_gpu)
r_cpu = (r_cpu/r_cpu.max()-0.5)*2 # scale to (-1,1)
r_cpu = cp.clip(r_cpu,-1+epsilon,1-epsilon)
print(r_cpu)
[ 0.36842105 -0.05263158 -0.89473684 -0.47368421 0.68421053 -0.36842105 0.15789474 0.47368421 0.999999 0.05263158 -0.57894737 -0.15789474 0.26315789 -0.999999 0.57894737 -0.78947368 -0.68421053 0.89473684 -0.26315789 0.78947368] [ 0.36842105 -0.05263158 -0.89473684 -0.47368421 0.68421053 -0.36842105 0.15789474 0.47368421 0.999999 0.05263158 -0.57894737 -0.15789474 0.26315789 -0.999999 0.57894737 -0.78947368 -0.68421053 0.89473684 -0.26315789 0.78947368]
r_gpu = erfinv(r_gpu) # map to gaussian
print(r_gpu)
r_cpu = sp_erfinv(r_cpu) # map to gaussian
print(r_cpu)
[ 0.3390617 -0.0466774 -1.14541135 -0.44805114 0.70933273 -0.3390617 0.14085661 0.44805114 3.45891074 0.0466774 -0.56893556 -0.14085661 0.23761485 -3.45891074 0.56893556 -0.8853822 -0.70933273 1.14541135 -0.23761485 0.8853822 ] [ 0.3390617 -0.0466774 -1.14541135 -0.44805114 0.70933273 -0.3390617 0.14085661 0.44805114 3.45891074 0.0466774 -0.56893556 -0.14085661 0.23761485 -3.45891074 0.56893556 -0.8853822 -0.70933273 1.14541135 -0.23761485 0.8853822 ]
n_bins = 5
fig, axs = plt.subplots(1, 2, sharey=True, tight_layout=True)
fig.set_figheight(5)
fig.set_figwidth(15)
axs[0].hist(cp.asnumpy(x_gpu), bins=n_bins)
axs[0].set_title('input',fontsize=15)
axs[1].hist(cp.asnumpy(r_gpu), bins=n_bins)
axs[1].set_title('transform',fontsize=15)
print('GaussRank transformation GPU')
GaussRank transformation
n_bins = 5
fig, axs = plt.subplots(1, 2, sharey=True, tight_layout=True)
fig.set_figheight(5)
fig.set_figwidth(15)
axs[0].hist(x_cpu, bins=n_bins)
axs[0].set_title('input',fontsize=15)
axs[1].hist(r_cpu, bins=n_bins)
axs[1].set_title('transform',fontsize=15)
print('GaussRank transformation CPU')
GaussRank transformation CPU
反變換用于從高斯變換中恢復原始值。這是展示 cuDF 與 CuPy 的互操作性 的另一個很好的例子。就像您可以使用 NumPy 和 pandas 一樣,您可以在同一個工作流中將 cuDF 和 CuPy 編織在一起,同時將數(shù)據(jù)完全保存在 GPU 上。
import warnings
warnings.filterwarnings("ignore")
import cupy as cp
from cupyx.scipy.special import erfinv
import cudf as gd
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.special import erfinv as sp_erfinv
GaussRank transformation
x_gpu = cp.random.rand(20) # input array
x_cpu = cp.asnumpy(x_gpu)
r_gpu = x_gpu.argsort().argsort() # compute the rank
r_cpu = x_cpu.argsort().argsort()
r_gpu = (r_gpu/r_gpu.max()-0.5)*2 # scale to (-1,1)
epsilon = 1e-6
r_gpu = cp.clip(r_gpu,-1+epsilon,1-epsilon)
r_cpu = (r_cpu/r_cpu.max()-0.5)*2 # scale to (-1,1)
r_cpu = cp.clip(r_cpu,-1+epsilon,1-epsilon)
r_gpu = erfinv(r_gpu) # map to gaussian
r_cpu = sp_erfinv(r_cpu) # map to gaussian
Inverse transformation step by step
df_cpu = pd.DataFrame({'src':x_cpu,'tgt':r_cpu})
df_gpu = gd.DataFrame({'src':x_gpu,'tgt':r_gpu}) # pass cupy array to cudf dataframe
df_cpu = df_cpu.sort_values('src') # sort
df_gpu = df_gpu.sort_values('src')
pos_cpu = df_cpu['tgt'].searchsorted(r_cpu, side='left') # search
pos_gpu = df_gpu['tgt'].searchsorted(r_gpu, side='left')
def linear_inter_polate(df,x,pos):
N = df.shape[0]
pos[pos>=N] = N-1
pos[pos-1<=0] = 0
if isinstance(x,cp.ndarray):
pos = pos.values
x1 = df['tgt'].values[pos]
x2 = df['tgt'].values[pos-1]
y1 = df['src'].values[pos]
y2 = df['src'].values[pos-1]
relative = (x-x2) / (x1-x2)
return (1-relative)*y2 + relative*y1
x_inv_cpu = linear_inter_polate(df_cpu,r_cpu,pos_cpu) # linear inter polate
x_inv_gpu = linear_inter_polate(df_gpu,r_gpu,pos_gpu)
n_bins = 5
fig, axs = plt.subplots(1, 3, sharey=True, tight_layout=True)
fig.set_figheight(5)
fig.set_figwidth(15)
axs[0].hist(x_cpu, bins=n_bins)
axs[0].set_title('input',fontsize=15)
axs[1].hist(r_cpu, bins=n_bins)
axs[1].set_title('transform',fontsize=15)
_ = axs[2].hist(x_inv_cpu, bins=n_bins)
axs[2].set_title('inverse transform',fontsize=15)
print('GaussRank CPU')
GaussRank CPU
n_bins = 5
fig, axs = plt.subplots(1, 3, sharey=True, tight_layout=True)
fig.set_figheight(5)
fig.set_figwidth(15)
axs[0].hist(cp.asnumpy(x_gpu), bins=n_bins)
axs[0].set_title('input',fontsize=15)
axs[1].hist(cp.asnumpy(r_gpu), bins=n_bins)
axs[1].set_title('transform',fontsize=15)
_ = axs[2].hist(cp.asnumpy(x_inv_gpu), bins=n_bins)
axs[2].set_title('inverse transform',fontsize=15)
print('GaussRank GPU')
GaussRank GPU
圖 2 :每種鍵合類型的基本事實分布。
因此,我們將高斯秩變換應用于訓練數(shù)據(jù)的基本事實,為所有鍵類型創(chuàng)建一個統(tǒng)一的干凈高斯分布。
圖 3 :使用 GaussRank 轉換基本事實的工作流程。
在這個回歸任務中,使用 GaussRank 變換訓練數(shù)據(jù)的基本事實。
為了進行推斷,我們將反高斯秩變換應用于測試數(shù)據(jù)的預測,以便它們匹配每種鍵類型的原始不同分布。由于測試數(shù)據(jù)中目標的真實分布是未知的,因此根據(jù)訓練數(shù)據(jù)中目標變量的分布計算測試數(shù)據(jù)預測的逆變換。應該注意的是,這種逆變換只需要用于目標變量。
圖 4 :預測被反變換以匹配原始分布。
通過運用這一技巧 平均絕對誤差( LMAE )的對數(shù) of our message passing neural network is improved by 18%!
請記住 GaussRank 確實有一些限制:
它只適用于連續(xù)變量,并且
如果輸入已經(jīng)接近高斯分布,或者非常不對稱,則性能 MIG ht 不會得到改善,甚至變得更差。
高斯秩變換與各種神經(jīng)網(wǎng)絡的相互作用是一個非?;钴S的研究課題。
加速
我們測量變換和反變換的總時間。對于正在進行的 CHAMPS 數(shù)據(jù)集, cuDF + CuPy 在單個 NVIDIA V100 GPU 上的實現(xiàn)在 Intel Xeon CPU 上實現(xiàn)了 比 pandas + NumPy 快 25 倍 。我們生成 合成隨機數(shù)據(jù) 以進行更全面的比較。對于 10M 及以上的數(shù)據(jù)點,我們的 RAPIDS 實現(xiàn)比 快 100 倍。 多
圖 5 : GaussRank 變換+反變換對合成隨機數(shù)據(jù)的加速比較。
結論
RAPIDS 在提供驚人的性能方面取得了長足的進步,代碼幾乎沒有變化。這篇博文展示了使用 RAPIDS cuDF 和 CuPy 作為 pandas 和 NumPy 的替代品來實現(xiàn) gpu 性能改進是多么容易。如中所示 完整的筆記本 ,通過 只添加兩行代碼 ,高斯秩變換檢測到輸入張量在 GPU 上,并自動從 pandas + NumPy 切換到 cuDF + CuPy 。再簡單不過了。
關于作者
Jiwei Liu 是 NVIDIA 的數(shù)據(jù)科學家,致力于 NVIDIA 人工智能基礎設施,包括 RAPIDS 數(shù)據(jù)科學框架。
審核編輯:郭婷
-
神經(jīng)網(wǎng)絡
+關注
關注
42文章
4762瀏覽量
100539 -
gpu
+關注
關注
28文章
4701瀏覽量
128705
發(fā)布評論請先 登錄
相關推薦
評論