Referência de camadas com BrainScript
CNTK predefine uma série de "camadas" comuns, o que torna muito fácil escrever redes simples que consistem em camadas padrão em camadas em camadas em cima umas das outras.
As camadas são objetos de função que podem ser usados como funções regulares do BrainScript, mas mantêm parâmetros aprecíveis e têm um par adicional de parâmetros ou atributos de {}
construção.
Por exemplo, esta é a descrição da rede para uma camada simples de 1 oculto modelo usando a DenseLayer{}
camada:
h = DenseLayer {1024, activation=ReLU} (features)
p = DenseLayer {9000, activation=Softmax} (h)
que podem, por exemplo, ser utilizados para a formação contra um critério de entropia cruzada:
ce = CrossEntropy (labels, p)
Se a sua rede é uma concatenação reta de operações (muitas são), pode usar a alternativa
Sequential()
Notação:
myModel = Sequential (
DenseLayer {1024, activation=ReLU} :
DenseLayer {9000, activation=Softmax}
)
e invocá-lo assim:
p = myModel (features)
Exemplos de modelos
O seguinte mostra um tagger de ranhura que incorpora uma sequência de palavras, processa-o com um LSTM recorrente e, em seguida, classifica cada palavra:
taggingModel = Sequential (
EmbeddingLayer {150} : # embed into a 150-dimensional vector
RecurrentLSTMLayer {300} : # forward LSTM
DenseLayer {labelDim} # word-wise classification
)
E o seguinte é uma simples rede convolucional para reconhecimento de imagem:
convNet = Sequential (
# 3 layers of convolution and dimension reduction by pooling
ConvolutionalLayer {32, (5:5), pad=true, activation=ReLU} :
MaxPoolingLayer {(3:3), stride=(2:2)} :
ConvolutionalLayer {32, (5:5), pad=true, activation=ReLU} :
MaxPoolingLayer {(3:3), stride=(2:2)} :
ConvolutionalLayer {64, (5:5), pad=true, activation=ReLU} :
MaxPoolingLayer {(3:3), stride=(2:2)} :
# 2 dense layers for classification
DenseLayer {64, activation=ReLU} :
LinearLayer {10}
)
Partilha de parâmetros
Se atribuir uma camada a uma variável e a usar em vários locais, os parâmetros serão partilhados. Se disser
lay = DenseLayer {1024, activation=Sigmoid}
h1 = lay (x)
h2 = lay (h1) # same weights as `h1`
h1
e h2
partilhará os mesmos parâmetros, como lay()
é a mesma função em ambos os casos.
No caso acima, provavelmente não é o que foi desejado, por isso esteja ciente.
Se ambas as invocações acima lay()
destinam-se a ter parâmetros diferentes, lembre-se de definir dois casos distintos, por exemplo lay1 = DenseLayer{...}
e lay2 = DenseLayer{...}
.
Então, por que este comportamento?
As camadas permitem partilhar parâmetros em secções de um modelo.
Considere um DSSM modelo que processa duas imagens de entrada, digamos doc
e query
idênticas com a mesma cadeia de processamento, e compara os vetores ocultos resultantes:
imageToVec = Sequential (
ConvolutionalLayer {32, (5:5), pad = true, activation = ReLU} :
MaxPoolingLayer {(3:3), stride = (2:2)} :
ConvolutionalLayer {64, (5:5), pad = true, activation = ReLU} :
MaxPoolingLayer {(3:3), stride = (2:2)} :
DenseLayer {64, activation = ReLU} :
LinearLayer {10}
)
zDoc = imageToVec (doc)
zQuery = imageToVec (query) # same model as for zDoc
sim = CosDistance (zdoc, zQuery)
onde imageToVec
está a parte do modelo que converte imagens em vetor plano.
imageToVec
é um objeto de função que, por sua vez, contém vários objetos de função (por exemplo, três instâncias de ConvolutionalLayer{}
).
imageToVec
é instantaneamente uma vez, e este caso contém os parâmetros aprecáveis de todos os objetos de função incluídos. Ambas as invocações model()
partilharão estes parâmetros na aplicação, e os seus gradientes serão a soma de ambas as invocações.
Por último, note que se no exemplo query
acima e doc
deve ter as mesmas dimensões, uma vez que são processados através do mesmo objeto de função, e a primeira camada do objeto de função tem a sua dimensão de entrada inferida para corresponder à de ambos query
e doc
. .
Se as suas dimensões diferirem, esta rede está mal formada e a inferência/validação da dimensão falhará com uma mensagem de erro.
Nota de implementação
Muitas camadas são invólucros em torno CNTK primitivos subjacentes, juntamente com os respetivos parâmetros aprecíveis necessários. Por exemplo, ConvolutionalLayer{}
envolve o Convolution()
primitivo.
Os benefícios da utilização de camadas são:
- as camadas contêm parâmetros aprecíveis da dimensão correta
- as camadas são composíveis (cf.
Sequential()
)
DenseLayer{}, LinearLayer{}
Função de fábrica para criar uma camada totalmente conectada.
DenseLayer{}
leva com uma opcional não-linearidade.
DenseLayer {outDim, activation=Identity, init='glorotUniform', initValueScale=1, bias=true}
LinearLayer {outDim, init='glorotUniform', initValueScale=1, bias=true}
Parâmetros
-
outDim
: dimensão de saída desta camada -
activation
(DenseLayer{}
apenas): passar uma função aqui para ser usado como função de ativação, comoactivation=ReLU
-
init
('heNormal'
|'glorotUniform'
|...): tipo de inicialização para os pesos. Consulte aqui uma lista completa de opções de inicialização. -
initValueScale
: a variação da inicialização aleatória é multiplicada com esta -
bias
: se falso, não inclua um parâmetro de enviesamento
Devolver Valor
Uma função que implementa a camada completamente conectada desejada. Veja a descrição.
Description
Utilize estas funções de fábrica para criar uma camada totalmente ligada.
Utilize DenseLayer{}
se quiser que seja incluída uma função de ativação, caso contrário LinearLayer{}
.
Cada uma destas funções de fábrica criam um objeto de função que contém uma matriz de peso aprecável e, a menos que bias=false
, um enviesamento aprecável. O objeto de função pode ser usado como uma função, que implementa uma destas fórmulas:
DenseLayer{...} (v) = activation (W * v + b)
LinearLayer{...} (v) = W * v + b
onde W
é uma matriz de peso de dimensão [outDim x (dimension of v)]
, b
é o enviesamento da dimensão [outdim]
, e o valor resultante tem dimensão (ou dimensões de tensor) como dado por outDim
.
Suporte tensor
Se a função devolvida for aplicada a uma entrada de um tensor de grau > 1, por exemplo, uma imagem 2D, W
terá a dimensão [outDim x (first dimension of input) x (second dimension of input) x ...]
.
Por outro lado, outDim
pode ser um vetor que especifica dimensões do tensor, por exemplo (10:10)
.
Nesse caso, W
terá a dimensão [outDim[0] x outDim[1] x ... x (dimension of input)]
, e b
terá as dimensões [outDim[0] x outDim[1] x ...]
do tensor.
O produto da matriz da CNTK interpretará estas dimensões extra de saída ou de entrada como se fossem achatadas num vetor longo.
Para mais detalhes sobre este, consulte a documentação de Times()
Exemplo:
h = DenseLayer {1024, activation=Sigmoid) (v)
ou alternativamente:
Layer = DenseLayer {1024, activation=Sigmoid)
h = Layer (v)
ConvolutionalLayer{}
Cria uma camada de convolução com opcional não linearidade.
ConvolutionalLayer {numOutputChannels, filterShape,
activation = Identity,
init = 'glorotUniform', initValueScale = 1,
stride = 1, pad = false, lowerPad = 0, upperPad = 0,
bias = true}
Parâmetros
-
numOutputChannels
: número de canais de saída (número de filtros) -
filterShape
: extensão espacial do filtro, por exemplo(5:5)
, para um filtro 2D. A dimensão do canal de entrada não deve ser incluída aqui. -
activation
: opcional não linearidade, por exemplo.activation=ReLU
-
init
('heNormal'
|'glorotUniform'
|...): tipo de inicialização aleatória para os pesos. Consulte aqui uma lista completa de opções de inicialização aleatória. -
initValueScale
: a variação da inicialização aleatória é multiplicada com esta -
stride
: incremento ao deslizar o filtro sobre a entrada. Por exemplo,(2:2)
reduzir as dimensões em 2 -
pad
: se não estiver definido (predefinido), o filtro será deslocado sobre a área "válida" de entrada, ou seja, não é utilizado qualquer valor fora da área. Sepad
for definido por outro lado, o filtro será aplicado a todas as posições de entrada, e os valores fora da região válida serão considerados zero. -
lowerPad
,upperPad
especificar explicitamente diferentes margens para o enchimento. Os filtros serão deslocados sobre uma região válida que é (virtualmente) aumentada com zeros. Por exemplo,lowerPad=(1:2)
anexará uma coluna de zeros e duas filas de zeros. A dimensão da saída é estendida em conformidade. -
bias
: se falso, não inclua um parâmetro de enviesamento
Devolver Valor
Uma função que implementa a camada completamente conectada desejada. Veja a descrição.
Description
Utilize estas funções de fábrica para criar uma camada de convolução.
A camada resultante aplica uma operação de convolução num tensor n-dimensional.
O chamador especifica a extensão espacial do filtro.
Um conjunto de filtros de uma determinada extensão espacial (por exemplo (5:5)
) está correlacionado com todos os locais da entrada (por exemplo, uma [640 x 480]
imagem de tamanho parcial).
Assumindo que o estofamento está ativado (pad
) e os passos são 1, isso gerará uma região de saída da mesma dimensão ([640 x 480]
).
Normalmente, muitos filtros são aplicados ao mesmo tempo.
numOutputChannels
especifica o número, de modo que para cada local de entrada, um vetor inteiro de numOutputChannels
é produzido.
Para o nosso exemplo acima, definir numOutputChannels
para 64 seria em um [640 x 480 x 64]
tensor de tamanho.
Este último eixo é chamado de dimensão do canal.
Quando a convolução é aplicada a uma entrada com uma dimensão de canal, cada filtro também será composto por vetores da dimensão do canal da entrada.
Por exemplo, ao aplicar convolução com uma extensão de filtro espacial especificada de uma [640 x 480 x 3]
imagem de (5:5)
cor de tamanho grande, cada filtro será um [5 x 5 x 3]
tensor.
Todos os numOutputChannels
filtros empilhados são chamados de núcleo.
No nosso exemplo, a forma do núcleo será [5 x 5 x 3 x 64]
.
Resume a seguinte relação entre as várias dimensões e formas:
input shape : [ (spatial dims) x (#input channels) ]
spatial extent : [ (filterShape) ]
output shape : [ (spatial dims) x x numOutputChannels ]
kernel shape : [ (filterShape) x (#input channels) x numOutputChannels ]
que, no nosso exemplo, são:
input shape : [ 640 x 480 x 3 ]
spatial extent : [ 5 x 5 ]
output shape : [ 640 x 480 x x numOutputChannels ]
kernel shape : [ 5 x 5 x 3 x numOutputChannels ]
Preenchimento
Se o enchimento não estiver ativado, então a região de saída será reduzida pelos locais de fronteira aos quais não é possível aplicar a extensão total do filtro. Por exemplo, aplicando um (5:5)
filtro de extensão a uma imagem sem estofo, as 2 linhas e colunas de pixels ultraperiféricas fariam com que o filtro fosse aplicado fora dos limites.
Assim, ConvolutionalLayer{}
reduzirá as dimensões em conformidade.
Uma [640 x 480]
imagem convolvida com um (5:5)
filtro sem estofamento deixará uma [636 x 476]
região de saída de tamanho grande.
Passos
Os stride
parâmetros especificam o incremento dos filtros.
Valores de avanço superiores a um conduzirá a uma sub-amostragem da região de produção.
Por exemplo, filtrar uma [640 x 480]
imagem com um passo de (2:2)
resultará numa [320 x 240]
região de tamanho grande com estofos e sem estofos [318 x 238]
.
Notas
Esta camada é um invólucro em torno do Convolution()
primitivo.
O nome dos parâmetros do núcleo do filtro, tal como indicado na secção de validação do registo, terminará em .W
.
A dimensão não será atualmente mostrada como [ (filterShape) x (#input channels) x numOutputChannels ]
descrito acima, mas sim [ numOutputChannels x ((produto sobre forma de filtro) * (#input canais))].»
Exemplo:
c = ConvolutionalLayer {64, (3:3), pad = true, stride = (1:1), bias=false} (x)
DeconvLayer{}
Cria uma camada de desconvolução.
DeconvLayer {numOutputChannels,
filterShape, numInputChannels,
bias = true,
activation = (x=>x),
init = 'glorotUniform',
initValueScale = 0.001,
initBias = 0,
stride = 1, autoPadding = false,
lowerPad = 0, upperPad = 0,
maxTempMemSizeInSamples = 0}
Parâmetros
-
numOutputChannels
: número de canais de saída (número de filtros) -
filterShape
: extensão espacial do filtro, por exemplo(5:5)
, para um filtro 2D. A dimensão do canal de entrada não deve ser incluída aqui. -
numInputChannels
: número de canais de entrada (número de filtros do volume de entrada) -
bias
: se falso, não inclua um parâmetro de enviesamento -
activation
: opcional não linearidade, por exemplo.activation=ReLU
-
init
('heNormal'
|'glorotUniform'
|...): tipo de inicialização aleatória para os pesos. Consulte aqui uma lista completa de opções de inicialização aleatória. -
initValueScale
: a variação da inicialização aleatória é multiplicada com esta -
initBias
: o valor inicial para o enviesamento -
stride
: incremento ao deslizar o filtro sobre a entrada. Por exemplo,(2:2)
reduzir as dimensões em 2 -
autoPadding
: se não estiver definido (predefinido), o filtro será deslocado sobre a área "válida" de entrada, ou seja, não é utilizado qualquer valor fora da área. SeautoPadding
for definido por outro lado, o filtro será aplicado a todas as posições de entrada, e os valores fora da região válida serão considerados zero. -
lowerPad
,upperPad
: especificar explicitamente as diferentes margens para o enchimento para o volume de saída, ou seja, as que foram utilizadas para a entrada na camada convolucional correspondente. É importante defini-los em correspondência com a camada convolucional para alcançar as mesmas dimensões de tensor.
Devolver Valor
Uma função que implementa a camada completamente conectada desejada. Veja a descrição.
Description
Utilize estas funções de fábrica para criar uma camada de desconvolução.
A camada resultante aplica uma operação de desconvolução num tensor n-dimensional.
Esta camada é um invólucro em torno do Convolution()
primitivo com deconv=true
.
Um caso popular para desconvolução é reconstruir uma imagem (ver aqui, por exemplo). Onde a convolução leva uma região de campo recetivo 2D de entrada e calcula a correlação com um filtro 2D, a desconvolução pega num pixel e espalha-o por uma região 2D.
Considere uma imagem p(.,.), uma localização de pixel (x,y) e um filtro centrado [3 x 3] com o seguinte conteúdo (sem dimensão de profundidade de mapa de funcionalidades por enquanto, ou seja, um único canal):
[ . . c
a b .
. . . ]
Aqui, a b e c são pesos do filtro, '. Corresponde a um peso zero. Convolution() calcula o pixel de saída q(x, y) no local (x, y) como:
q(x,y) = b * p(x,y) + a * p(x-1,y) + c * p(x+1,y-1)
A desconvolução toma pixels q(x,y) e espalha-os por uma região ao redor (x,y). Se usássemos o mesmo filtro, faria as seguintes contribuições para a saída r(x,y):
r(x,y) += b * q(x,y)
r(x-1,y) += a * q(x,y)
r(x+1,y-1) += c * q(x,y)
Sabendo que o mesmo se aplica a todos x e y através do avião, podemos expressar isso para r(x,y):
r(x,y) += b * q(x,y)
r(x,y) += a * q(x+1,y)
r(x,y) += c * q(x-1,y+1)
ou no total,
r(x,y) = b * q(x,y) + a * q(x+1,y) + c * q(x-1,y+1)
Isto tem a mesma forma que a Convolution acima, exceto que o filtro está espelhado ao longo de ambos os eixos.
Agora introduzimos mapas de recursos na mistura. Isto é fácil: Em vez de ir da profundidade de entrada para a profundidade de saída, vamos na outra direção.
Em resumo, Convolution (W, x) == Deconvolução (W', x), onde
W : [W x H x C x K]
e
W’ = W
com os seus valores reorganizados como: [(W mirrored) x (H mirrored) x K x C]
Ou seja, o que a Deconvolução faz implicitamente é:
- trocar as duas dimensões de profundidade (transpor)
- as dimensões espaciais (inverter a ordem dos dados)
- Convolution() com estes
Exemplo:
deconv_A = DeconvLayer {inputDim, (5:5), cMap1, lowerPad=(2:2:0), upperPad=(2:2:0)}(unpool_A)
Consulte o codificador de imagem automática utilizando Deconvolution e Unpooling para um exemplo detalhado e caminhe.
MaxPoolingLayer{}, AveragePoolingLayer{}
Funciona a fábrica para criar uma camada de agrupamento máximo ou médio.
MaxPoolingLayer {poolShape, stride = 1, pad = false, lowerPad = 0, upperPad = 0}
AveragePoolingLayer {poolShape, stride = 1, pad = false, lowerPad = 0, upperPad = 0} =
Parâmetros
-
poolShape
: a forma da região para piscina, por exemplo.(2:2)
-
stride
: incremento ao deslizar a piscina sobre a entrada. Por exemplo,(2:2)
reduzir as dimensões em 2 -
pad
: se não estiver definido (predefinido), então a piscina será deslocada sobre a área "válida" de entrada, ou seja, não é utilizado qualquer valor fora da área. Sepad
for definido, por outro lado, o pool será aplicado a todas as posições de entrada, e os valores fora da região válida serão considerados zero. Para o agrupamento médio, a contagem para a média não inclui valores acolchoados. -
lowerPad
,upperPad
especificar explicitamente diferentes margens para o enchimento. Os filtros serão deslocados sobre uma região válida que é (virtualmente) aumentada com zeros. Por exemplo,lowerPad=(1:2)
anexará uma coluna de zeros e duas filas de zeros. A dimensão da saída é estendida em conformidade.
Devolver Valor
Uma função que implementa a camada de agrupamento desejada. Veja a descrição.
Description
Utilize esta função de fábrica para criar uma operação de agrupamento. Use MaxPoolingLayer{}
para calcular o máximo sobre os valores na área da piscina, e AveragePoolingLayer{}
para tomar a sua média.
A operação de agrupamento desliza uma "janela de piscina" sobre os locais de uma região de entrada, e calcula o máximo ou a média dos valores na respetiva região da piscina.
Esta operação é estruturalmente muito semelhante à convolução, exceto que a operação aplicada à janela deslizante é de natureza diferente.
Todas as considerações relativas às dimensões de entrada, estofos e passos aplicam-se de forma idêntica, por isso, por favor, consulte ConvolutionalLayer{}
mais detalhes.
Exemplo:
p = MaxPoolingLayer {(3:3), stride=(2:2)} (c)
MaxUnpoolingLayer{}
Cria uma camada max-unooling.
MaxUnpoolingLayer {poolShape,
stride = 1, pad = false,
lowerPad = 0, upperPad = 0}
Parâmetros
-
poolShape
: a forma da região para desacorar (o tamanho da região de saída ), por exemplo.(2:2)
-
stride
: incrementar ao deslizar a piscina sobre a saída. Por exemplo,(2:2)
aumentar as dimensões em 2 -
pad
: se não estiver definido (predefinido), então a piscina será deslocada sobre a área de saída "válida", ou seja, não é utilizado qualquer valor fora da área. -
lowerPad
,upperPad
especificar explicitamente diferentes margens para o enchimento. Os filtros assumirão uma região de saída válida que é (virtualmente) aumentada.
Devolver Valor
Uma função que implementa a camada de desacompanhamento desejada. Veja a descrição.
Description
Utilize esta função de fábrica para criar uma operação de desatar.
A operação de desatar é o inverso de uma operação de agrupamento. Requer duas entradas: a saída da sua camada de agrupamento correspondente, por exemplo p1
, e a entrada da sua camada de agrupamento correspondente, por exemplo r1
, também. Desliza uma "janela inversa de piscina" sobre as localizações da sua entrada p1
, e projeta o valor para essa posição da região de saída que tinha o valor máximo na operação de agrupamento correspondente, ou seja, em r1
. A segunda entrada r1
é necessária em CNTK para determinar o alvo da operação Unpooling, uma vez CNTK não armazena as chamadas variáveis switch (consulte aqui para mais detalhes).
Exemplo:
unpool_A = MaxUnpoolingLayer {(2:2), stride=(2:2)}(deconv_B, relu_A)
Consulte o codificador de imagem automática utilizando Deconvolution e Unpooling para um exemplo detalhado e caminhe.
IncorporandoLayer{}
EmbeddingLayer {outDim,
init='glorotUniform', initValueScale=1,
embeddingPath = '', transpose = false}
Parâmetros
-
outDim
: a dimensão do vetor de incorporação desejado -
init
('heNormal'
|'glorotUniform'
|...): tipo de inicialização para os pesos. Consulte aqui uma lista completa de opções de inicialização. -
initValueScale
: a variação da inicialização aleatória é multiplicada com esta -
embeddingPath
: se dado, as incorporações não são aprendidas, mas carregadas a partir de um ficheiro e não atualizadas mais durante o treino -
transpose
: permite carregar incorporações que são armazenadas sob forma transposta
Devolver Valor
Uma função que implementa a camada de incorporação. Veja a descrição.
Description
"Incorporação" refere-se a representação de palavras ou outros itens discretos por vetores contínuos densos. Esta camada assume que a entrada está em forma de um quente. Por exemplo, para um vocabulário de 10.000, cada vetor de entrada deve ter dimensão 10.000 e consiste em zeros, exceto uma posição que contém um 1. O índice dessa localização é o índice da palavra ou item que representa.
Em CNTK, os vetores correspondentes são armazenados como colunas de uma matriz. Assim, mapear uma palavra de entrada para a sua incorporação é implementado como um produto matricial. Para que isto seja muito eficiente, é importante que os vetores de entrada sejam armazenados em formato escasso.
Facto divertido: O gradiente de uma matriz incorporada tem a forma de vetores gradientes que não são apenas zero para palavras vistas em uma minibatch. Uma vez que para vocabulários realistas de dezenas ou centenas de milhares, a grande maioria das colunas seria zero, CNTK implementa tem uma otimização específica para representar o gradiente na forma "coluna-sparse".
Edição conhecida: O formulário de gradiente de coluna-sparse acima mencionado não é suportado pela nossa técnica de paralelização SGD de 1 bit . Por favor, use a técnica de impulso de bloqueio .
Exemplo
Uma incorporação aprendida que representa palavras de um vocabulário de 87636 como um vetor 300-dimensional:
input = Input{87636, sparse=true} # word sequence, as one-hot vector, sparse format
embEn = EmbeddingLayer{300} (input) # embed word as a 300-dimensional continuous vector
Além de , deve-se sparse=true
também declarar uma entrada como escassa no reader
bloco config.
Aqui está um exemplo de leitura de texto escasso com o CNTKTextFormatReader
:
reader = {
readerType = "CNTKTextFormatReader"
file = "en2fr.txt"
randomize = true
input = {
input = { alias = "E" ; dim = 87636 ; format = "sparse" }
labels = { alias = "F" ; dim = 98624 ; format = "sparse" }
}
}
Se, em vez disso, os vetores incorporados já existirem e deverem ser carregados a partir de um ficheiro, seria assim:
input = Input{87636, sparse=true} # word sequence, as one-hot vector, sparse format
embEn = EmbeddingLayer{300, embeddingPath="embedding-en.txt", transpose=true} (w) # embedding from disk
onde se espera que o ficheiro "embedding-en.txt"
seja composto por 87.636 linhas de texto, cada uma das quais composta por 300 números separados pelo espaço.
Uma vez que este ficheiro salva as incorporações como linhas em vez de colunas, transpose=true
transporá a matriz em movimento.
RecorrenteLSTMLayer{}, RecorrenteRSTMLayerStack{}
Funciona a fábrica para criar um LSTM recorrente de camada única ou multi-camadas.
RecurrentLSTMLayer {outDim, cellShape = None,
goBackwards = false,
usePeepholes = false,
init = 'glorotUniform', initValueScale = 1,
enableSelfStabilization = false,
allowOptimizedEngine = false}
RecurrentLSTMLayerStack {layerDims,
cellShapes = None,
usePeepholes = false,
init = 'glorotUniform', initValueScale = 1,
enableSelfStabilization = false,
allowOptimizedEngine = false}
Parâmetros
-
outDim
(RecurrentLSTMLayer{}
): dimensão da saída da rede. Para denotar um tensor de categoria>1, este pode ser um vetor, por exemplo.(40:2)
-
layerDims
(RecurrentLSTMLayerStack{}
): conjunto de dimensões das camadas e saídas internas da rede -
cellShape
(,RecurrentLSTMLayer{}
opcional): a dimensão da célula do LSTM. Normalmente isto é idêntico aoutDim
. Se for dado um valor diferente, será inserida uma projeção linear adicional para converter da dimensão celular para a saída. -
cellShapes
((RecurrentLSTMLayerStack{}
, opcional): conjunto de valores comocellShape
paraRecurrentLSTMLayer()
denotar projeção -
goBackwards
(opcional): se for verdade, a recorrência é executada para trás -
usePeepholes
(opcional): se for verdade, em seguida, use ligações peephole no LSTM -
init
('heNormal'
|'glorotUniform'
|...): tipo de inicialização para os pesos. Consulte aqui uma lista completa de opções de inicialização. -
initValueScale
: a variação da inicialização aleatória é multiplicada com esta -
enableSelfStabilization
(opcional): se for verdade, insira uma operação de "estabilizador" semelhante àStabilizerLayer{}
-
allowOptimizedEngine
(opcional, falso padrão): se for verdade, em seguida, use o motor RNN otimizado da CUDNN sempre que possível
Devolver Valor
Uma função que implementa a camada(s) desejada que aplica/aplica um LSTM recorrente à sua sequência de entrada. Esta camada (pilha) mapeia uma sequência de entrada para uma sequência de estados ocultos do mesmo comprimento.
Description
Isto implementa o LSTM recorrente a ser aplicado a uma sequência de entradas, em duas variantes: uma única camada e uma pilha de várias camadas. Esta operação lida automaticamente com a entrada de comprimento variável. O valor inicial do estado oculto e da célula são 0.
A aplicação desta camada a uma sequência de entrada retornará a sequência dos estados ocultos do LSTM recorrente (topo de pilha) (o valor da célula de memória do LSTM não é devolvido).
A sequência devolvida tem o mesmo comprimento que a entrada.
Se apenas o último estado for desejado, como na classificação de sequência ou em alguns cenários de sequência-sequência, use BS.Sequences.Last()
para extrair apenas o estado oculto do último item.
(Numa recorrência retrógrada, usaria BS.Sequences.First()
.)
Para criar um modelo bidirecional com RecurrentLSTMLayer()
, use duas camadas, uma com goBackwards=true
, e Splice()
as duas saídas juntas.
RecurrentLSTMLayerStack()
atualmente não suporta modelos bidirecionais, deve construí-lo manualmente usando várias RecurrentLSTMLayer()/Splice()
combinações.
Utilizando o motor CuDNN5 RNN
Esta função utilizará automaticamente o motor RNN otimizado da CuDNN5, se possível, isto é, se
- o modelo especificado é aquele que pode ser implementado pela função de CuDNN5
- nenhuma projeção (sem
cellShape
parâmetro) - sem conexões peep-hole
- sem auto-estabilização
- não andar para trás
- para
RecurrentLSTMLayerStack{}
, todas as dimensões da camada têm o mesmo valor
- nenhuma projeção (sem
allowOptimizedEngine=true
Especificamente, CNTK requer para permitir allowOptimizedEngine=true
.
Isto porque o CuDNN5 RNN é implementado como uma operação CNTK primitiva que requer uma GPU.
No entanto, muitos sistemas reais usam GPUs para treino, mas apenas servidores de CPU em implementação.
O CuDNN5 RNN não é adequado aqui.
(É teoricamente possível utilizar o CuDNN5 RNN para treino, e substituí-lo para implantação por uma operação de edição por uma implementação equivalente explícita LSTM no BrainScript.)
Notas
Se allowOptimizedEngine=true
então estas duas variantes de camada são invólucros em torno do OptimizedRNNStack()
primitivo.
Exemplo
Um simples classificador de texto, que executa uma sequência de palavras através de uma recorrência e, em seguida, passa o último estado oculto do LSTM para um classiferador softmax, poderia ter este formulário:
w = Input{...} # word sequence (one-hot vectors)
e = EmbeddingLayer {150} (w) # embed as a 150-dimensional dense vector
h = RecurrentLSTMLayer {300} (e) # left-to-right LSTM with hidden and cell dim 300
t = BS.Sequences.Last (h) # extract last hidden state
z = DenseLayer {10000, activation=Softmax} (t) # softmax classifier
Para alterar o exemplo acima para uma pilha de 3 camadas que utiliza o motor RNN CuDNN5, altere esta linha:
h = RecurrentLSTMLayerStack {(300:300:300), allowOptimizedEngine=true} (e)
Para criar um LSTM bidirecional de uma camada (por exemplo, utilizando metade da dimensão oculta em comparação com a anterior), utilize isto:
hFwd = RecurrentLSTMLayer {150} (e)
hBwd = RecurrentLSTMLayer {150, goBackwards=true} (e)
h = Splice (hFwd:hBwd)
Retardadorlayer{}
Função de fábrica para criar uma camada que atrasa a sua entrada.
DelayLayer {T=1, defaultHiddenActivation=0}
Parâmetros
-
T
: o número de passos de tempo para atrasar. Para aceder a valores futuros, use um valor negativo -
defaultHiddenActivation
: valor a utilizar para os quadros atrasados nos limites
Devolver Valor
Uma função que implementa a operação de atraso desejada.
Description
Esta operação atrasa uma sequência de entrada por T
etapas (predefinição 1).
Isto é útil, por exemplo, para transformar uma sequência de palavras numa sequência de seqüência de palavras sobrepostas triplica.
Considere uma sequência de entrada "a b c b", que deve ser codificada como uma sequência de vetores de um só calor:
1 0 0 0
0 1 0 1
0 0 1 0
Aqui, cada coluna é um vetor de um só calor e corresponde a uma palavra.
A aplicação desta DelayLayer{T=1}
entrada gerará esta sequência:
0 1 0 0
0 0 1 0
0 0 0 1
Todos os tokens são atrasados por um, e a primeira posição é preenchida como um vetor 0.
Da mesma forma, a utilização DelayLayer{T=-1}
(atraso negativo) dará acesso aos valores futuros, e a almofada da direita com um zero:
0 0 0 0
1 0 1 0
0 1 0 0
Notas
Esta camada é um invólucro em torno dos PastValue()
primitivos FutureValue()
.
Exemplo
O seguinte mostra como empilhar três palavras vizinhas num vetor de trigramas:
x = ... # input value, e.g. a N-dimensional one-hot vector
xp = DelayLayer{} (x) # previous value
xn = DelayLayer{T-1} (x) # next value (negative delay)
tg = Splice (xp : x : xn) # concatenate all into a 3N-dimensional three-hot vector
BatchNormalizationLayer{}, LayerNormalizationLayer{}, StabilizerLayer{}
Funções de fábrica para criar camadas para normalização do lote, normalização da camada e auto-estabilização.
BatchNormalizationLayer {spatialRank = 0,
normalizationTimeConstant = 5000,
initialScale = 1, epsilon = 0.00001, useCntkEngine = true}
LayerNormalizationLayer {initialScale = 1, initialBias = 0}
StabilizerLayer{}
Parâmetros
BatchNormalizationLayer
:
-
spatialRank
: os parâmetros de normalização são agridos sobre as primeirasspatialRank
dimensões. Atualmente os valores permitidos são 0 (sem pooling) e 2 (agrupamento em todas as posições de pixel de uma imagem) -
normalizationTimeConstant
(padrão 5000): constante de tempo em amostras do filtro de baixa passagem de primeira ordem que é usado para calcular estatísticas de média/variação para utilização em inferência -
initialScale
: valor inicial do parâmetro de escala -
epsilon
: pequeno valor que é adicionado à estimativa de variação ao calcular o inverso -
useCntkEngine
: se for verdade, utilize a implementação nativa da CNTK. Se for falso, utilize a implementação da cuDNN (apenas GPU).
LayerNormalizationLayer
:
-
initialScale
: valor inicial do parâmetro de escala -
initialBias
: valor inicial do parâmetro bias
Devolver Valor
Uma função que implementa uma camada que executa a operação de normalização.
Description
BatchNormalizationLayer{}
implementa a técnica descrita na normalização do lote de papel: Acelerando a Formação em Rede Profunda reduzindo a mudança de covaria interna (Sergey Ioffe, Christian Szegedy).
Normaliza as suas entradas para cada minibatch pela média/variação da minibatch, e des normaliza-a com um fator de escala e viés aprendidos.
Em inferência, em vez de utilizar a média/variação do minibatch, a normalização do lote utiliza uma estimativa média/var de funcionamento a longo prazo.
Esta estimativa é calculada durante o treino através de estatísticas de minibatch filtragem de baixo-passe.
A constante de tempo do filtro de baixa passagem pode ser modificada pelo normalizationTimeConstant
parâmetro.
Recomendamos começar com o padrão de (5000), mas experimentamos com outros valores, tipicamente na ordem de vários milhares a dezenas de milhares.
LayerNormalizationLayer{}
implementa a Normalização da Camada (Jimmy Lei Ba, Jamie Ryan Kiros, Geoffrey E. Hinton).
Normaliza cada amostra de entrada subtraindo a média em todos os elementos da amostra e, em seguida, dividindo-se pelo desvio padrão sobre todos os elementos da amostra.
StabilizerLayer{}
implementa um auto-estabilizador por rede neuronal profunda autoestabilizada (P. Ghahremani, J. Droppo).
Esta técnica simples mas eficaz multiplica a sua entrada com um escalar aprecável (mas, ao contrário da normalização da camada, não normaliza primeiro a entrada, nem subtrai uma média).
Note-se que, em comparação com o papel original, que propõe um scalar beta
linear ou um exponencial Exp (beta)
, achámos benéfico utilizar uma operação softplus afiada por sugestão do segundo autor, o que evita tanto os valores negativos como a instabilidade do exponencial.
Notas
BatchNormalizationLayer{}
é um invólucro em torno do BatchNormalization()
primitivo.
LayerNormalizationLayer{}
e StabilizerLayer{}
são expressas diretamente no BrainScript.
Exemplo
Uma camada típica numa rede convolucional com normalização do lote:
MyLayer (x, depth, initValueScale) =
{
c = ConvolutionalLayer {depth, (5:5), pad=true, initValueScale=initValueScale} (x)
b = BatchNormalizationLayer {spatialRank = 2} (c) #####
r = ReLU (b)
p = MaxPoolingLayer {(3:3), stride = (2:2)} (r)
}.p
FeatureMVNLayer{}
Função de fábrica para criar uma camada que normaliza a entrada de características pelo seu desvio médio e padrão.
FeatureMVNLayer{}
Parâmetros
Lista de argumentos {}
vazias.
Devolver Valor
Uma função que implementa uma camada que executa a operação de normalização.
Description
Esta camada normaliza a entrada para uma rede neural pelo seu enviesamento e variação. Estes valores são estimados antecipadamente através da realização de um passe completo através dos dados de treino, e depois guardados e congelados. Isto vai acontecer automaticamente.
Uma vez que os parâmetros desta camada são pré-determinados num passe separado antes do treino principal, só pode ser aplicado a variáveis declaradas como Input{}
.
Exemplo
Este é um início típico de uma rede neural para modelação acústica da fala:
features = Input{40} # e.g. speech features
featNorm = FeatureMVNLayer{} (features)
h = DenseLayer{2048} (featNorm)
...