Co to jest aktywacja GELU?

18

Przeglądałem artykuł BERT, który używa GELU (Gaussian Error Linear Unit), który podaje równanie jako co z kolei jest przybliżone do

GELU(x)=xP(Xx)=xΦ(x).
0.5x(1+tanh[2/π(x+0.044715x3)])

Czy możesz uprościć równanie i wyjaśnić, w jaki sposób zostało przybliżone.

tanatoz
źródło

Odpowiedzi:

19

Funkcja GELU

Możemy rozszerzyć skumulowany rozkładN(0,1) , tj. , w następujący sposób: Φ(x)

GELU(x):=xP(Xx)=xΦ(x)=0.5x(1+erf(x2))

Zauważ, że jest to definicja , a nie równanie (lub relacja). Autorzy podali kilka uzasadnień tej propozycji, np. Analogię stochastyczną , jednak matematycznie jest to tylko definicja.

Oto fabuła GELU:

Przybliżenie Tanha

W przypadku tego rodzaju przybliżeń numerycznych kluczową ideą jest znalezienie podobnej funkcji (przede wszystkim opartej na doświadczeniu), sparametryzowanie jej, a następnie dopasowanie do zestawu punktów z pierwotnej funkcji.

Wiedza, że jest bardzo zbliżona doerf(x)tanh(x)

i pierwsza pochodna pokrywa się z na , czyli , przystępujemy do dopasowania (lub więcej terminów) do zestawu punktów .erf(x2)tanh(2)πx)x=02)π

tanh(2)π(x+zax2)+bx3)+dox4+rex5))
(xja,erf(xja2)))

Dopasowałem tę funkcję do 20 próbek pomiędzy ( używając tej strony ), a oto współczynniki:(-1.5,1.5)

Przez ustawienie , oszacowano na . Przy większej liczbie próbek z szerszego zakresu (ta strona dozwolona jest tylko 20), współczynnik będzie bliższy wartości papieru . Wreszcie dostaniemya=c=d=0b0.04495641b0.044715

GELU(x)=xΦ(x)=0.5x(1+erf(x2))0.5x(1+tanh(2π(x+0.044715x3)))

ze średnim kwadratowym błędem dla .108x[10,10]

Zauważ, że jeśli nie wykorzystalibyśmy relacji między pierwszymi pochodnymi, w parametrach uwzględniono by termin w następujący sposób: co jest mniej piękne (mniej analityczne, bardziej numeryczne)!2π

0.5x(1+tanh(0.797885x+0.035677x3))

Wykorzystanie parzystości

Jak sugeruje @BookYourLuck , możemy wykorzystać parzystość funkcji, aby ograniczyć przestrzeń wielomianów, w których szukamy. To znaczy, ponieważ jest funkcją nieparzystą, tj. , a jest również funkcją nieparzystą, funkcja wielomianowa wewnątrz powinien być również nieparzysty (powinien mieć nieparzyste moce ), aby mieć erff(x)=f(x)tanhpol(x)tanhx

erf(x)tanh(pol(x))=tanh(pol(x))=tanh(pol(x))erf(x)

Wcześniej mieliśmy szczęście, że otrzymaliśmy (prawie) zerowe współczynniki dla parzystych mocy i , jednak ogólnie może to prowadzić do przybliżeń niskiej jakości, które na przykład mają termin taki jak który jest anulowane przez dodatkowe warunki (parzyste lub nieparzyste) zamiast po prostu wybrać .x2x40.23x20x2

Przybliżenie sigmoidalne

Podobny związek występuje między i (sigmoid), co zaproponowano w niniejszym dokumencie jako kolejne przybliżenie, z średni błąd kwadratu dla .erf(x)2(σ(x)12)104x[10,10]

Oto kod Pythona do generowania punktów danych, dopasowywania funkcji i obliczania średnich błędów kwadratu:

import math
import numpy as np
import scipy.optimize as optimize


def tahn(xs, a):
    return [math.tanh(math.sqrt(2 / math.pi) * (x + a * x**3)) for x in xs]


def sigmoid(xs, a):
    return [2 * (1 / (1 + math.exp(-a * x)) - 0.5) for x in xs]


print_points = 0
np.random.seed(123)
# xs = [-2, -1, -.9, -.7, 0.6, -.5, -.4, -.3, -0.2, -.1, 0,
#       .1, 0.2, .3, .4, .5, 0.6, .7, .9, 2]
# xs = np.concatenate((np.arange(-1, 1, 0.2), np.arange(-4, 4, 0.8)))
# xs = np.concatenate((np.arange(-2, 2, 0.5), np.arange(-8, 8, 1.6)))
xs = np.arange(-10, 10, 0.001)
erfs = np.array([math.erf(x/math.sqrt(2)) for x in xs])
ys = np.array([0.5 * x * (1 + math.erf(x/math.sqrt(2))) for x in xs])

# Fit tanh and sigmoid curves to erf points
tanh_popt, _ = optimize.curve_fit(tahn, xs, erfs)
print('Tanh fit: a=%5.5f' % tuple(tanh_popt))

sig_popt, _ = optimize.curve_fit(sigmoid, xs, erfs)
print('Sigmoid fit: a=%5.5f' % tuple(sig_popt))

# curves used in https://mycurvefit.com:
# 1. sinh(sqrt(2/3.141593)*(x+a*x^2+b*x^3+c*x^4+d*x^5))/cosh(sqrt(2/3.141593)*(x+a*x^2+b*x^3+c*x^4+d*x^5))
# 2. sinh(sqrt(2/3.141593)*(x+b*x^3))/cosh(sqrt(2/3.141593)*(x+b*x^3))
y_paper_tanh = np.array([0.5 * x * (1 + math.tanh(math.sqrt(2/math.pi)*(x + 0.044715 * x**3))) for x in xs])
tanh_error_paper = (np.square(ys - y_paper_tanh)).mean()
y_alt_tanh = np.array([0.5 * x * (1 + math.tanh(math.sqrt(2/math.pi)*(x + tanh_popt[0] * x**3))) for x in xs])
tanh_error_alt = (np.square(ys - y_alt_tanh)).mean()

# curve used in https://mycurvefit.com:
# 1. 2*(1/(1+2.718281828459^(-(a*x))) - 0.5)
y_paper_sigmoid = np.array([x * (1 / (1 + math.exp(-1.702 * x))) for x in xs])
sigmoid_error_paper = (np.square(ys - y_paper_sigmoid)).mean()
y_alt_sigmoid = np.array([x * (1 / (1 + math.exp(-sig_popt[0] * x))) for x in xs])
sigmoid_error_alt = (np.square(ys - y_alt_sigmoid)).mean()

print('Paper tanh error:', tanh_error_paper)
print('Alternative tanh error:', tanh_error_alt)
print('Paper sigmoid error:', sigmoid_error_paper)
print('Alternative sigmoid error:', sigmoid_error_alt)

if print_points == 1:
    print(len(xs))
    for x, erf in zip(xs, erfs):
        print(x, erf)

Wynik:

Tanh fit: a=0.04485
Sigmoid fit: a=1.70099
Paper tanh error: 2.4329173471294176e-08
Alternative tanh error: 2.698034519269613e-08
Paper sigmoid error: 5.6479106346814546e-05
Alternative sigmoid error: 5.704246564663601e-05
E-mail
źródło
2
Dlaczego potrzebne jest przybliżenie? Czy nie mogliby po prostu użyć funkcji erf?
SebiSebi
8

Najpierw zauważ, że według parzystości . Musimy pokazać, że za .

Φ(x)=12)mirfado(-x2))=12)(1+mirfa(x2)))
mirfa
mirfa(x2))tanh(2)π(x+zax3)))
za0,044715

W przypadku dużych wartości obie funkcje są ograniczone w . Dla małych odpowiednia seria Taylora brzmi: i Podstawiając, otrzymujemy i Zrównując współczynnik dla , znajdujemy blisko papier nax[-1,1]x

tanh(x)=x-x3)3)+o(x3))
mirfa(x)=2)π(x-x3)3))+o(x3)).
tanh(2)π(x+zax3)))=2)π(x+(za-2)3)π)x3))+o(x3))
mirfa(x2))=2)π(x-x3)6)+o(x3)).
x3)
za0,04553992412
0,044715.

BookYourLuck
źródło