Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Funkcje to sposób tworzenia modułów wielokrotnego użytku w języku BrainScript. Funkcje są wyrażeniami sparametryzowanymi i można je definiować w prosty sposób, np.:
Sqr (x) = x * x
Funkcje są typami danych pierwszej klasy w tym sensie, że funkcje mogą akceptować i zwracać inne funkcje.
Definiowanie funkcji
Istnieją dwa sposoby definiowania funkcji, standardowej składni i anonimowej składni lambda.
Standardowa składnia
Nazwane funkcje są elementami członkowskimi rekordów, które są definiowane jako przypisania składowych rekordów tych dwóch formularzy:
f (arg1, arg2, ..., optionalArg1=optionalArg1Default, ...) = expression of args
f {arg1, arg2, ..., optionalArg1=optionalArg1Default, ...} = expression of args
Dwa formularze są identyczne składniowo, ale zgodnie z konwencją drugi formularz (przy użyciu nawiasów klamrowych { }) jest używany dla funkcji, które zwracają obiekty funkcji z wbudowanymi parametrami uczenia się, takimi jak wstępnie zdefiniowane warstwy CNTK.
Argumenty funkcji tworzą zakres nazw, taki jak rekord; tzn. argumenty funkcji są tak, jakby zostały zdefiniowane jako elementy członkowskie w rekordzie otaczającym.
Parametry opcjonalne
Funkcje mogą mieć parametry opcjonalne. Są one przekazywane przez poprzedzanie ich wartości przez name=. Jeśli opcjonalny parametr nie zostanie przekazany, zostanie użyta jego wartość domyślna. W definicji funkcji opcjonalne parametry są deklarowane przy użyciu formularza name=defaultValue.
Oto przykład funkcji Softplus, która przyjmuje opcjonalny parametr stromości:
Softplus (x, steepness=1) =
if steepness == 1 # use the simpler formula if no steepness specified
then Log (Constant(1) + Exp (x)) # softplus(x)
else { # version with steepness: 1/s softplus (s x)
steepnessAsConstant = Constant (steepness)
y = Reciprocal (steepnessAsConstant) .* Softplus (steepnessAsConstant .* x)
# Note: .* denotes an elementwise product
}.y
Uwaga: wartości domyślne parametrów opcjonalnych nie mogą uzyskać dostępu do innych parametrów. Jeśli na przykład dla powyższej funkcji Softplus masz zmienną definiującą stromość, którą chcesz przekazać do funkcji, wystarczy powiedzieć:
steepness = 4
z = Softplus (x, steepness=steepness)
gdzie pierwszy steepness jest nazwą parametru, a drugi jest wyrażeniem, które ma przekazać wartość.
Nazwy nie będą powodować konfliktu.
Składnia lambda
Oprócz składni definicji funkcji język BrainScript umożliwia anonimowe wyrażeń lambda przy użyciu składni (x => f(x)), która ma na celu naśladowanie języka C#. Następujące dwie definicje są równoważne:
sqr(x) = x * x
sqr = (x => x * x)
W rzeczywistości analizator BrainScript wewnętrznie przekształca ten ostatni w poprzednią formę.
Składnia lambda jest obecnie ograniczona do jednego parametru, a także nie obsługuje parametrów opcjonalnych. Wyrażenia lambda z więcej niż jednym parametrem lub z opcjonalnymi parametrami muszą być zdefiniowane jako nazwane funkcje.
Wywoływanie funkcji
Funkcje są wywoływane zgodnie z oczekiwaniami: przekazując argumenty w nawiasach; i przekazywanie opcjonalnych argumentów jako nazwanych parametrów. Na przykład samo stabilizator (technika skalowania nieco podobna do normalizacji wsadowej) przy użyciu softplus ze stromością 4 zostanie napisana z następującymi dwoma wywołaniami funkcji:
beta = ParameterTensor {1, initValue=1}
xStabilized = x .* Softplus (beta, steepness=4)
Funkcje są wykonywane z opóźnieniem po wywołaniu. W szczególności, jeśli funkcja definiuje parametry modelu wewnątrz (czyli warianty ParameterTensor{} i dowolną funkcję, która wywołuje ją wewnątrz), każde wywołanie funkcji daje niezależne wystąpienie tych parametrów modelu. Często wynik wywołania funkcji jest następnie przypisywany do zmiennej lub przekazywany jako argument funkcji. Wielokrotne użycie takiej wartości nie powoduje wielokrotnego wykonywania.
Czystość/Przezroczystość referentii
Chociaż język BrainScript ma wspólne elementy w językach funkcjonalnych, należy pamiętać, że funkcje BrainScript nie są całkowicie czyste, w tym może mieć jeden bardzo specyficzny efekt uboczny: tworzenie wystąpień nowych parametrów modelu. Na przykład, gdy wyrażenie jest równoważne sqr(x) + sqr(x) (z elementem sqr(x) = x*x), nie jest to przypadek dla 2 * ParameterTensor{N} wartości vs. ParameterTensor{N} + ParameterTensor{N}.2 * sqr(x)
Zgodnie z konwencją należy użyć nawiasów okrągłych ( ) , w których podano przezroczystość odwołań, ale nawiasy klamrowe { } wskazują, gdzie nie jest.
Funkcje jako wartości
W języku BrainScript funkcje są wartościami. Nazwaną funkcję można przypisać do zmiennej i przekazać jako argument. W rzeczywistości nazwane funkcje to tylko elementy członkowskie rekordów, które mają typ "function". Wyszukiwanie nazw funkcji jest takie samo jak wyszukiwanie elementu członkowskiego rekordu. Dzięki temu można na przykład grupować funkcje w "przestrzenie nazw", definiując je wewnątrz wyrażenia rekordu.
Nazwane funkcje jako wartości
Jeśli nazwa funkcji jest używana bez następujących nawiasów, będzie ona odwoływać się do samej funkcji jako obiektu.
Częściowa aplikacja/currying
Język BrainScript obecnie nie obsługuje syntatyki częściowej aplikacji ani curryingu. Aby osiągnąć podobny efekt, można zdefiniować nową funkcję, która wywołuje inną funkcję z powiązanymi/przechwyconymi parametrami:
Layer (x, m, n, f) = f (ParameterTensor {(m:n)} * x + ParameterTensor {n})
Sigmoid512Layer (x) = Layer (x, 512, 512, Sigmoid)
Przykłady
W poniższym przykładzie zdefiniowano prostą hierarchię typowych typów warstw sieciowych:
Layers = {
# base feed-forward without and with parameterized energy function
# AffineLayer() defines model parameters inside.
AffineLayer (x, m, n) = ParameterTensor {(m:n), init='heNormal'} * x
+ ParameterTensor {n, initValue=0}
# NLLayer applies a non-linearity on top of an affine layer
NLLayer (x, m, n, f) = f (AffineLayer (x, m, n))
# a few non-linearities
SigmoidLayer (x, m, n) = NLLayer (x, m, n, Sigmoid) # pass Sigmoid() function as the non-linaerity
SoftplusLayer (x, m, n) = NLLayer (x, m, n, (x => Log (Constant(1) + Exp(x)))/*Softplus as lambda*/)
ReLULayer (x, m, n) = NLLayer (x, m, n, RectifiedLinear)
SoftmaxLayer (x, m, n) = NLLayer (x, m, n, Softmax)
}
którego można użyć w następujący sposób:
# constants defined
# Sample, Hidden, and Label dimensions
SDim = 28*28 # feature dimension
HDim = 256 # hidden dimension
LDim = 10 # number of classes
features = Input {SDim}
labels = Input {LDim}
# layers
h = Layers.ReLULayer (features, HDim, SDim)
z = Layers.AffineLayer (h, LDim, HDim) # input to softmax; same as log softmax without normalization
# output and criteria
P = Softmax (z)
ce = CrossEntropyWithSoftmax (labels, z)
errs = ErrorPrediction (labels, z)
featureNodes = (features)
labelNodes = (labels)
criterionNodes = (ce)
evaluationNodes = (errs)
outputNodes = (P)
Jest to taki sam, jak w pierwszym przykładzie w sekcji Podstawowe pojęcia, ale przy użyciu funkcji.
Dalej: Dowiedz się więcej o edytowaniu modelu lub przejdź bezpośrednio do dokumentacji pełnej funkcji.
NDLNetworkBuilder (przestarzałe)
Wcześniejsze wersje CNTK używały przestarzałego NDLNetworkBuilder elementu zamiast BrainScriptNetworkBuilder. NDLNetworkBuilderDefinicja funkcji różniła się na kilka sposobów. Następująca definicja jest prawidłowa w pliku NDLNetworkBuilder:
FF (X1, W1, B1) # no equal sign
[ # both curly braces and brackets are allowed
T = Times (W1, X1)
FF = Plus (T, B1)
] # return value is FF, and not the entire record
Szczególnie zwracana jest wartość zwracana za pośrednictwem zmiennej lokalnej, która ma taką samą nazwę jak funkcja; lub jeśli taka zmienna nie zostanie znaleziona, zostanie zwrócona zmienna zdefiniowana jako ostatnia. W języku BrainScript musi to być teraz napisane jako:
FF (X1, W1, B1) =
{
T = Times (W1, X1)
Y = Plus (T, B1)
}.Y
W szczególności wartość Y zwracana musi być jawnie wybrana na końcu przy użyciu . składni. W przeciwnym razie wartość funkcji będzie całym rekordem.