正如在 批量標(biāo)準(zhǔn)化紙 中所解釋的,如果神經(jīng)網(wǎng)絡(luò)的輸入是高斯的,那么訓(xùn)練它就變得容易多了。這很清楚。如果你的模型輸入不是高斯的, RAPIDS 會(huì)在眨眼間把它轉(zhuǎn)換成高斯的。
高斯秩變換 是一種新的標(biāo)準(zhǔn)化技術(shù),用于轉(zhuǎn)換輸入數(shù)據(jù)以訓(xùn)練深層神經(jīng)網(wǎng)絡(luò)。最近,我們?cè)?預(yù)測(cè)分子競(jìng)爭(zhēng)性質(zhì) 中使用了這種技術(shù),它很容易將 m 消息傳遞神經(jīng)網(wǎng)絡(luò)模型 的精度提高了一個(gè)顯著的幅度。這篇博文將展示如何使用 RAPIDS cuDF 和 Chainer CuPy 實(shí)現(xiàn) GPU 加速的Gauss 秩變換,并使用 pandas 和 NumPy 替換來實(shí)現(xiàn) 100 倍加速 。
介紹
輸入歸一化是訓(xùn)練神經(jīng)網(wǎng)絡(luò)的關(guān)鍵。高斯秩變換的思想最早是由 邁克爾·賈勒。 在他的 塞古羅港的安全駕駛預(yù)測(cè) 挑戰(zhàn)的勝利解中提出的。他訓(xùn)練去噪自動(dòng)編碼器,并嘗試了幾種輸入標(biāo)準(zhǔn)化方法。最后,他得出這樣的結(jié)論:
我在過去發(fā)現(xiàn)的最棒的東西是 GaussRank ,它能直接發(fā)揮作用。這通常比標(biāo)準(zhǔn)的 mean / std 定標(biāo)器或 min / max (標(biāo)準(zhǔn)化)好得多。
有三個(gè)步驟可以將任意分布下的連續(xù)值向量轉(zhuǎn)換為基于秩的高斯分布,如圖 1 所示。
圖 1 :高斯秩變換。
CuPy 實(shí)現(xiàn)非常簡(jiǎn)單,非常類似于 NumPy 操作。實(shí)際上,只需更改導(dǎo)入的函數(shù),就可以將整個(gè)進(jìn)程從 CPU 移動(dòng)到 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])
r_gpu = x_gpu.argsort().argsort() # compute the rank
print(r_gpu)
r_cpu = x_cpu.argsort().argsort()
print(r_cpu)
[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
反變換用于從高斯變換中恢復(fù)原始值。這是展示 cuDF 與 CuPy 的互操作性 的另一個(gè)很好的例子。就像您可以使用 NumPy 和 pandas 一樣,您可以在同一個(gè)工作流中將 cuDF 和 CuPy 編織在一起,同時(shí)將數(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í)分布。
因此,我們將高斯秩變換應(yīng)用于訓(xùn)練數(shù)據(jù)的基本事實(shí),為所有鍵類型創(chuàng)建一個(gè)統(tǒng)一的干凈高斯分布。
圖 3 :使用 GaussRank 轉(zhuǎn)換基本事實(shí)的工作流程。
在這個(gè)回歸任務(wù)中,使用 GaussRank 變換訓(xùn)練數(shù)據(jù)的基本事實(shí)。
為了進(jìn)行推斷,我們將反高斯秩變換應(yīng)用于測(cè)試數(shù)據(jù)的預(yù)測(cè),以便它們匹配每種鍵類型的原始不同分布。由于測(cè)試數(shù)據(jù)中目標(biāo)的真實(shí)分布是未知的,因此根據(jù)訓(xùn)練數(shù)據(jù)中目標(biāo)變量的分布計(jì)算測(cè)試數(shù)據(jù)預(yù)測(cè)的逆變換。應(yīng)該注意的是,這種逆變換只需要用于目標(biāo)變量。
圖 4 :預(yù)測(cè)被反變換以匹配原始分布。
通過運(yùn)用這一技巧 平均絕對(duì)誤差( LMAE )的對(duì)數(shù) of our message passing neural network is improved by 18%!
請(qǐng)記住 GaussRank 確實(shí)有一些限制:
它只適用于連續(xù)變量,并且
如果輸入已經(jīng)接近高斯分布,或者非常不對(duì)稱,則性能 MIG ht 不會(huì)得到改善,甚至變得更差。
高斯秩變換與各種神經(jīng)網(wǎng)絡(luò)的相互作用是一個(gè)非?;钴S的研究課題。
加速
我們測(cè)量變換和反變換的總時(shí)間。對(duì)于正在進(jìn)行的 CHAMPS 數(shù)據(jù)集, cuDF + CuPy 在單個(gè) NVIDIA V100 GPU 上的實(shí)現(xiàn)在 Intel Xeon CPU 上實(shí)現(xiàn)了 比 pandas + NumPy 快 25 倍 。我們生成 合成隨機(jī)數(shù)據(jù) 以進(jìn)行更全面的比較。對(duì)于 10M 及以上的數(shù)據(jù)點(diǎn),我們的 RAPIDS 實(shí)現(xiàn)比 快 100 倍。 多
圖 5 : GaussRank 變換+反變換對(duì)合成隨機(jī)數(shù)據(jù)的加速比較。
結(jié)論
RAPIDS 在提供驚人的性能方面取得了長足的進(jìn)步,代碼幾乎沒有變化。這篇博文展示了使用 RAPIDS cuDF 和 CuPy 作為 pandas 和 NumPy 的替代品來實(shí)現(xiàn) gpu 性能改進(jìn)是多么容易。如中所示 完整的筆記本 ,通過 只添加兩行代碼 ,高斯秩變換檢測(cè)到輸入張量在 GPU 上,并自動(dòng)從 pandas + NumPy 切換到 cuDF + CuPy 。再簡(jiǎn)單不過了。
關(guān)于作者
Jiwei Liu 是 NVIDIA 的數(shù)據(jù)科學(xué)家,致力于 NVIDIA 人工智能基礎(chǔ)設(shè)施,包括 RAPIDS 數(shù)據(jù)科學(xué)框架。
審核編輯:郭婷
-
神經(jīng)網(wǎng)絡(luò)
+關(guān)注
關(guān)注
42文章
4814瀏覽量
103707 -
gpu
+關(guān)注
關(guān)注
28文章
4949瀏覽量
131309
發(fā)布評(píng)論請(qǐng)先 登錄
克拉克變換&帕克變換:電機(jī)界的“變形金剛”雙人組
無橋PFC變換器綜述
NVIDIA助力FinCatch開發(fā)智能投資輔助系統(tǒng)
傅立葉變換與拉普拉斯變換的區(qū)別
傅立葉變換的基本概念 傅立葉變換在信號(hào)處理中的應(yīng)用
RAPIDS cuDF將pandas提速近150倍

經(jīng)典傅里葉變換與快速傅里葉變換的區(qū)別
dcdc變換器有幾種變換形式
利用NVIDIA RAPIDS加速DolphinDB Shark平臺(tái)提升計(jì)算性能

數(shù)據(jù)中心應(yīng)用中適用于Intel Xeon Sapphire Rapids可擴(kuò)展處理器的負(fù)載點(diǎn)解決方案

負(fù)阻抗變換器如何實(shí)現(xiàn)負(fù)阻變換
數(shù)據(jù)中心應(yīng)用中適用于Intel? Xeon? Sapphire Rapids可擴(kuò)展處理器的負(fù)載點(diǎn)解決方案

評(píng)論