Referência de camadas com BrainScript
CNTK predefini uma série de "camadas" comuns, o que facilita muito a gravação de redes simples que consistem em camadas padrão sobre as outras.
Camadas são objetos de função que podem ser usados como funções BrainScript regulares, mas mantêm parâmetros de aprendizado e têm um par adicional para passar parâmetros ou atributos de {}
construção.
Por exemplo, esta é a descrição da rede para um modelo de camada 1 oculto simples usando a DenseLayer{}
camada:
h = DenseLayer {1024, activation=ReLU} (features)
p = DenseLayer {9000, activation=Softmax} (h)
que pode, por exemplo, ser usado para treinamento em um critério de entropia cruzada:
ce = CrossEntropy (labels, p)
Se sua rede for uma concatenação direta de operações (muitas são), você poderá usar a alternativa
Sequential()
Notação:
myModel = Sequential (
DenseLayer {1024, activation=ReLU} :
DenseLayer {9000, activation=Softmax}
)
e invoque-o assim:
p = myModel (features)
Modelos de exemplo
O seguinte mostra um marcador de slot que insira uma sequência de palavras, processa-a 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 rede convolucional simples 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}
)
Compartilhamento de parâmetros
Se você atribuir uma camada a uma variável e usá-la em vários locais, os parâmetros serão compartilhados. Se você disser
lay = DenseLayer {1024, activation=Sigmoid}
h1 = lay (x)
h2 = lay (h1) # same weights as `h1`
h1
e h2
compartilhará os mesmos parâmetros, como lay()
é a mesma função em ambos os casos.
No caso acima, isso provavelmente não é o que era desejado, então esteja ciente.
Se ambas as invocações acima lay()
forem destinadas a ter parâmetros diferentes, lembre-se de definir duas instâncias separadas, por exemplo lay1 = DenseLayer{...}
, e lay2 = DenseLayer{...}
.
Então, por que esse comportamento?
As camadas permitem compartilhar parâmetros entre seções de um modelo.
Considere um modelo DSSM que processa duas imagens de entrada, por exemplo doc
, e query
de forma idêntica 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
é a parte do modelo que converte imagens em vetor simples.
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
é instanciada uma vez e essa instância contém os parâmetros aprendizes de todos os objetos de função incluídos. Ambas as invocações model()
compartilharão esses parâmetros no aplicativo e seus gradientes serão a soma de ambas as invocações.
Por fim, observe que, se no exemplo query
acima e doc
precisar ter as mesmas dimensões, uma vez que elas são processadas por meio do mesmo objeto de função, e a primeira camada desse objeto de função tem sua dimensão de entrada inferida para corresponder à de ambos query
e doc
.
Se suas dimensões forem diferentes, essa rede será malformada e a inferência/validação da dimensão falhará com uma mensagem de erro.
Nota de implementação
Muitas camadas são wrappers em torno de primitivos CNTK subjacentes, juntamente com os respectivos parâmetros de aprendizado necessários. Por exemplo, ConvolutionalLayer{}
encapsula o Convolution()
primitivo.
Os benefícios do uso de camadas são:
- as camadas contêm parâmetros aprendizes da dimensão correta
- as camadas são redigiveis (cf.
Sequential()
)
DenseLayer{}, LinearLayer{}
Função factory para criar uma camada totalmente conectada.
DenseLayer{}
leva com uma não linearidade opcional.
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 dessa camada -
activation
(DenseLayer{}
somente): passe uma função aqui para ser usada como a função de ativação, comoactivation=ReLU
-
init
('heNormal'
|'glorotUniform'
|...): tipo de inicialização para os pesos. Veja aqui uma lista completa de opções de inicialização. -
initValueScale
: a inicialização aleatória de variação é multiplicada com isso -
bias
: se for falso, não inclua um parâmetro de viés
Valor Retornado
Uma função que implementa a camada totalmente conectada desejada. Confira a descrição.
Descrição
Use essas funções de fábrica para criar uma camada totalmente conectada.
Use DenseLayer{}
se quiser que uma função de ativação seja incluída, caso contrário LinearLayer{}
.
Cada uma dessas funções de fábrica cria um objeto de função que contém uma matriz de peso aprendevel e, a menos que bias=false
, um viés de aprendizado. 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 viés da dimensão [outdim]
e o valor resultante tem dimensão (ou dimensões tensores) conforme fornecido por outDim
.
Suporte ao Tensor
Se a função retornada for aplicada a uma entrada de uma classificação > tensor 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 tensores, 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 ...]
tensor.
O produto de matriz do CNTK interpretará essas dimensões extras de saída ou entrada como se tivessem sido achatadas em um vetor longo.
Para obter mais detalhes sobre isso, consulte a documentação de Times()
Exemplo:
h = DenseLayer {1024, activation=Sigmoid) (v)
ou, como alternativa:
Layer = DenseLayer {1024, activation=Sigmoid)
h = Layer (v)
ConvolutionalLayer{}
Cria uma camada de convolução com não linearidade opcional.
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 de 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. Veja aqui uma lista completa de opções de inicialização aleatória. -
initValueScale
: a inicialização aleatória de variação é multiplicada com isso -
stride
: incremente ao deslizar o filtro sobre a entrada. Por exemplo,(2:2)
para reduzir as dimensões em 2 -
pad
: se não for definido (padrão), o filtro será deslocado sobre a área de entrada "válida", ou seja, nenhum valor fora da área será usado. Sepad
estiver 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
: especifique explicitamente margens diferentes para preenchimento. Os filtros serão deslocados sobre uma região válida que é (virtualmente) aumentada com zeros. Por exemplo,lowerPad=(1:2)
acrescentará uma coluna de zeros e duas linhas de zeros. A dimensão da saída é estendida adequadamente. -
bias
: se for falso, não inclua um parâmetro de viés
Valor Retornado
Uma função que implementa a camada totalmente conectada desejada. Confira a descrição.
Descrição
Use essas funções de fábrica para criar uma camada de convolução.
A camada resultante aplica uma operação de convolução em um tensor unidimensional.
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 cada local da entrada (por exemplo, uma [640 x 480]
imagem de tamanho).
Supondo que o preenchimento esteja habilitado (pad
) e os passos sejam 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, portanto, para cada local de numOutputChannels
entrada, um vetor inteiro é produzido.
Para nosso exemplo acima, definir numOutputChannels
como 64 seria em um [640 x 480 x 64]
tensor de tamanho.
Esse ú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 consistirá em vetores da dimensão do canal da entrada.
Por exemplo, ao aplicar a convolução com uma extensão de filtro espacial especificada de (5:5)
uma [640 x 480 x 3]
imagem de cor do tamanho, cada filtro será um [5 x 5 x 3]
tensor.
Todos os numOutputChannels
filtros empilhados juntos são chamados de kernel.
Em nosso exemplo, a forma do kernel será [5 x 5 x 3 x 64]
.
O seguinte resume a 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 em 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 preenchimento não estiver habilitado, a região de saída será reduzida pelos locais de limite aos quais a extensão completa do filtro não poderá ser aplicada. Por exemplo, a aplicação de um (5:5)
filtro de extensão a uma imagem sem preenchimento, as 2 linhas e colunas mais externas de pixels fariam com que o filtro fosse aplicado fora dos limites.
Portanto, ConvolutionalLayer{}
reduzirá as dimensões adequadamente.
Uma [640 x 480]
imagem evoluída com um (5:5)
filtro sem preenchimento deixará uma [636 x 476]
região de saída do tamanho.
Passos
Os stride
parâmetros especificam o incremento de filtros.
Valores de passo maiores que um levarão a uma sub-amostragem da região de saída.
Por exemplo, filtrar uma [640 x 480]
imagem com um passo a (2:2)
passo resultará em uma [320 x 240]
região do tamanho com preenchimento e [318 x 238]
sem preenchimento.
Observações
Essa camada é um wrapper ao redor do Convolution()
primitivo.
O nome dos parâmetros do kernel de filtro, conforme mostrado na seção de validação do log, terminará em .W
.
No momento, a dimensão não será mostrada como [ (filterShape) x (#input channels) x numOutputChannels ]
descrito acima, mas sim [ numOutputChannels x ((product over filter shape) * (#input channels)) ]'.
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 de 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 for falso, não inclua um parâmetro de viés -
activation
: não linearidade opcional, por exemplo,activation=ReLU
-
init
('heNormal'
|'glorotUniform'
|...): tipo de inicialização aleatória para os pesos. Confira aqui uma lista completa de opções de inicialização aleatória. -
initValueScale
: a inicialização aleatória de variação é multiplicada com isso -
initBias
: o valor inicial do viés -
stride
: incremente ao deslizar o filtro sobre a entrada. Por exemplo,(2:2)
para reduzir as dimensões em 2 -
autoPadding
: se não for definido (padrão), o filtro será deslocado sobre a área de entrada "válida", ou seja, nenhum valor fora da área será usado. 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
: especifique explicitamente margens diferentes para preenchimento para o volume de saída, ou seja, as que foram usadas para a entrada na camada convolucional correspondente. É importante defini-los em correspondência com a camada convolucional para alcançar as mesmas dimensões tensores.
Valor Retornado
Uma função que implementa a camada totalmente conectada desejada. Confira a descrição.
Descrição
Use essas funções de fábrica para criar uma camada de desconvolução.
A camada resultante aplica uma operação de desconvolução em um tensor N dimensional.
Essa camada é um wrapper ao redor do Convolution()
primitivo com deconv=true
.
Um exemplo de uso popular para desconvolução é a reconstrução de uma imagem (veja aqui, por exemplo). Quando a convolução pega uma região de campo receptivo 2D de entrada e calcula a correlação com um filtro 2D, a desconvolução usa um pixel e a espalha por uma região 2D.
Considere uma imagem p(.,.), um local de pixel (x,y) e um filtro centralizado [3 x 3] com o conteúdo a seguir (nenhuma dimensão de profundidade do mapa de recursos 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 usa pixels q(x,y) e os espalha por uma região ao redor (x,y). Se usássemos o mesmo filtro, ele 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 os x e y no plano, 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)
Isso tem a mesma forma que a Convolução acima, exceto que o filtro é espelhado ao longo de ambos os eixos.
Agora, apresentamos mapas de recursos na combinação. Isso é fácil: em vez de ir da profundidade de entrada para a profundidade da saída, vamos para a outra direção.
Em resumo, Convolution (W, x) == Deconvolution (W', x), onde
W : [W x H x C x K]
e
W’ = W
com seus valores reorganizados como: [(W mirrored) x (H mirrored) x K x C]
Ou seja, o que Deconvolution() 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 automático de imagem usando Deconvolution e Unpooling para obter um exemplo detalhado e percorrer.
MaxPoolingLayer{}, AveragePoolingLayer{}
Funções de fábrica para criar uma camada de pool 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 pool, por exemplo,(2:2)
-
stride
: incremento ao deslizar o pool sobre a entrada. Por exemplo,(2:2)
para reduzir as dimensões em 2 -
pad
: se não for definido (padrão), o pool será deslocado sobre a área de entrada "válida", ou seja, nenhum valor fora da área será usado. 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 pool médio, a contagem para a média não inclui valores acolchoados. -
lowerPad
,upperPad
: especifique explicitamente margens diferentes para preenchimento. Os filtros serão deslocados sobre uma região válida que é (virtualmente) aumentada com zeros. Por exemplo,lowerPad=(1:2)
acrescentará uma coluna de zeros e duas linhas de zeros. A dimensão da saída é estendida adequadamente.
Valor Retornado
Uma função que implementa a camada de pooling desejada. Confira a descrição.
Descrição
Use essa função de fábrica para criar uma operação de pooling. Use MaxPoolingLayer{}
para calcular o máximo sobre os valores na área do pool e AveragePoolingLayer{}
para obter sua média.
A operação de pooling desliza uma "janela de pool" sobre os locais de uma região de entrada e calcula o máximo ou a média dos valores na respectiva região do pool.
Essa operação é estruturalmente muito semelhante à convolução, exceto que a operação aplicada à janela deslizante é de natureza diferente.
Todas as considerações sobre dimensões de entrada, preenchimento e passos se aplicam de forma idêntica, portanto, confira 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 a ser desacompilhada (o tamanho da região de saída ), por exemplo,(2:2)
-
stride
: incremento ao deslizar o pool sobre a saída. Por exemplo,(2:2)
para aumentar as dimensões em 2 -
pad
: se não for definido (padrão), o pool será deslocado sobre a área de saída "válida", ou seja, nenhum valor fora da área será usado. -
lowerPad
,upperPad
: especifique explicitamente margens diferentes para preenchimento. Os filtros assumirão uma região de saída válida que é (virtualmente) aumentada.
Valor Retornado
Uma função que implementa a camada de unpooling desejada. Confira a descrição.
Descrição
Use essa função de fábrica para criar uma operação de unpooling.
A operação de unpooling é o inverso de uma operação de pooling. Ele requer duas entradas: a saída de sua camada de pool correspondente, digamos p1
, e a entrada de sua camada de pooling correspondente, digamos r1
, também. Ele desliza uma "janela de pool inverso" sobre os locais de 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 pooling correspondente, ou seja, em r1
. A segunda entrada r1
é necessária em CNTK para determinar o destino da operação Desamarreamento, já que CNTK não armazena as chamadas variáveis de comutador (consulte aqui para obter detalhes).
Exemplo:
unpool_A = MaxUnpoolingLayer {(2:2), stride=(2:2)}(deconv_B, relu_A)
Consulte o codificador automático de imagem usando Deconvolution e Unpooling para obter um exemplo detalhado e passo a passo.
EmbeddingLayer{}
EmbeddingLayer {outDim,
init='glorotUniform', initValueScale=1,
embeddingPath = '', transpose = false}
Parâmetros
-
outDim
: a dimensão do vetor de inserção desejado -
init
('heNormal'
|'glorotUniform'
|...): tipo de inicialização para os pesos. Veja aqui uma lista completa de opções de inicialização. -
initValueScale
: a inicialização aleatória de variação é multiplicada com isso -
embeddingPath
: se for dado, as inserções não serão aprendidas, mas carregadas de um arquivo e não serão atualizadas ainda mais durante o treinamento -
transpose
: permite carregar inserções armazenadas em formulário transposto
Valor Retornado
Uma função que implementa a camada de inserção. Confira a descrição.
Descrição
"Inserção" refere-se a representar palavras ou outros itens discretos por vetores contínuos densos. Essa camada pressupõe que a entrada esteja em forma única. Por exemplo, para um tamanho de vocabulário de 10.000, espera-se que cada vetor de entrada tenha a dimensão 10.000 e consista em zeros, exceto por uma posição que contenha uma 1. O índice desse local é o índice da palavra ou item que ele representa.
Em CNTK, os vetores de inserção correspondentes são armazenados como colunas de uma matriz. Portanto, o mapeamento de uma palavra de entrada para sua inserção é implementado como um produto de matriz. Para que isso seja muito eficiente, é importante que os vetores de entrada sejam armazenados em formato esparso.
Fato divertido: o gradiente de uma matriz de inserção tem a forma de vetores gradientes que só não são zero para palavras vistas em uma minibatch. Como 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 "esparsa de colunas".
Problema conhecido: atualmente, não há suporte para o formulário de gradiente esparso de coluna mencionado acima pela nossa técnica de paralelização de SGD de 1 bit . Em vez disso, use a técnica de dinâmica de bloco .
Exemplo
Uma inserçã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 disso sparse=true
, também é necessário declarar uma entrada como esparsa no reader
bloco de configuração.
Aqui está um exemplo de leitura de entrada de texto esparso com :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 de inserção já existirem e forem carregados de um arquivo, ele terá esta aparência:
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
em que se espera que o arquivo "embedding-en.txt"
consista em 87.636 linhas de texto, cada uma consistindo em 300 números separados por espaço.
Como esse arquivo salva as inserções como linhas em vez de colunas, transpose=true
transporá a matriz em tempo real.
RecurrentLSTMLayer{}, RecurrentLSTMLayerStack{}
Funções de fábrica para criar um LSTM recorrente de camada única ou de várias 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 indicar um tensor da classificação>1, isso pode ser um vetor, por exemplo,(40:2)
-
layerDims
(RecurrentLSTMLayerStack{}
): matriz de dimensões das camadas internas e da saída da rede -
cellShape
( (RecurrentLSTMLayer{}
opcional): a dimensão da célula LSTM. Normalmente, isso é idêntico aoutDim
. Se um valor diferente for fornecido, uma projeção linear adicional será inserida para converter da dimensão da célula para a saída. -
cellShapes
( (RecurrentLSTMLayerStack{}
, opcional): matriz de valores comocellShape
paraRecurrentLSTMLayer()
indicar projeção -
goBackwards
(opcional): se for true, a recorrência será executada para trás -
usePeepholes
(opcional): se verdadeiro, use conexões de olho mágico no LSTM -
init
('heNormal'
|'glorotUniform'
|...): tipo de inicialização para os pesos. Veja aqui uma lista completa de opções de inicialização. -
initValueScale
: a inicialização aleatória de variação é multiplicada com isso -
enableSelfStabilization
(opcional): se for verdadeiro, insira uma operação "estabilizadora" semelhante àStabilizerLayer{}
-
allowOptimizedEngine
(opcional, falso padrão): se verdadeiro, use o mecanismo RNN otimizado do cuDNN sempre que possível
Valor Retornado
Uma função que implementa as camadas desejadas que aplica/aplica um LSTM recorrente à sequência de entrada. Essa camada (-stack) mapeia uma sequência de entrada para uma sequência de estados ocultos do mesmo comprimento.
Descrição
Isso 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. Essa operação manipula automaticamente a entrada de comprimento variável. O valor inicial do estado oculto e da célula é 0.
Aplicar essa camada a uma sequência de entrada retornará a sequência dos estados ocultos do LSTM recorrente (parte superior da pilha) (o valor da célula de memória do LSTM não é retornado).
A sequência retornada tem o mesmo comprimento que a entrada.
Se apenas o último estado for desejado, como em cenários de classificação de sequência ou de sequência para sequência, use BS.Sequences.Last()
apenas para extrair o estado oculto do último item.
(Em uma recorrência anterior, você 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 dá suporte a modelos bidirecionais, você deve construí-lo manualmente usando várias RecurrentLSTMLayer()/Splice()
combinações.
Usando o mecanismo CuDNN5 RNN
Essa função usará automaticamente o mecanismo RNN otimizado do CuDNN5, se possível, ou seja, se
- o modelo especificado é aquele que pode ser implementado pela função cuDNN5
- sem projeção (sem
cellShape
parâmetro) - sem conexões peep-hole
- sem autoestabilização
- não ir para trás
- para
RecurrentLSTMLayerStack{}
, todas as dimensões de camada têm o mesmo valor
- sem projeção (sem
allowOptimizedEngine=true
Especificamente, CNTK requer habilitar allowOptimizedEngine=true
.
Isso ocorre porque o CuDNN5 RNN é implementado como uma operação primitiva CNTK que requer uma GPU.
No entanto, muitos sistemas reais usam GPUs para treinamento, mas servidores somente CPU na implantação.
O CuDNN5 RNN não é adequado aqui.
(Teoricamente é possível usar o RNN CuDNN5 para treinamento e substituí-lo para implantação por uma operação de edição por uma implementação LSTM explícita equivalente no BrainScript.)
Observações
Se allowOptimizedEngine=true
essas duas variantes de camada forem wrappers ao redor do OptimizedRNNStack()
primitivo.
Exemplo
Um classificador de texto simples, que executa uma sequência de palavras por meio de uma recorrência e, em seguida, passa o último estado oculto do LSTM para uma classífera softmax, pode ter esse 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 usa o mecanismo CuDNN5 RNN, altere esta linha:
h = RecurrentLSTMLayerStack {(300:300:300), allowOptimizedEngine=true} (e)
Para criar um LSTM bidirecional de uma camada (por exemplo, usando metade da dimensão oculta em comparação com acima), use este:
hFwd = RecurrentLSTMLayer {150} (e)
hBwd = RecurrentLSTMLayer {150, goBackwards=true} (e)
h = Splice (hFwd:hBwd)
DelayLayer{}
Função factory para criar uma camada que atrase sua entrada.
DelayLayer {T=1, defaultHiddenActivation=0}
Parâmetros
-
T
: o número de etapas de tempo a serem demoradas. Para acessar valores futuros, use um valor negativo -
defaultHiddenActivation
: valor a ser usado para os quadros atrasados nos limites
Valor Retornado
Uma função que implementa a operação de atraso desejada.
Descrição
Essa operação atrasa uma sequência de entrada por T
etapas (padrão 1).
Isso é útil, por exemplo, para transformar uma sequência de palavras em uma sequência de triplos de palavras sobrepostas.
Considere uma sequência de entrada "a b c b", que deve ser codificada como uma sequência de vetores únicos da seguinte maneira:
1 0 0 0
0 1 0 1
0 0 1 0
Aqui, cada coluna é um vetor único e corresponde a uma palavra.
DelayLayer{T=1}
A aplicação a essa entrada gerará esta sequência:
0 1 0 0
0 0 1 0
0 0 0 1
Todos os tokens são adiados por um e a primeira posição é preenchida como um vetor 0.
Da mesma forma, o uso DelayLayer{T=-1}
(atraso negativo) dará acesso aos valores futuros e o pad da direita com um zero:
0 0 0 0
1 0 1 0
0 1 0 0
Observações
Essa camada é um wrapper ao redor dos PastValue()
primitivos.FutureValue()
Exemplo
O seguinte mostra como empilhar três palavras vizinhas em um vetor de trigrama:
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 em lote, normalização de camada e autoestabilizaçã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 agrupados nas primeirasspatialRank
dimensões. Atualmente, os valores permitidos são 0 (sem pools) e 2 (agrupamento em todas as posições de pixel de uma imagem) -
normalizationTimeConstant
(padrão 5000): constante de tempo em exemplos do filtro de passagem baixa de primeira ordem que é usado para calcular estatísticas médias/de variação para uso em inferência -
initialScale
: valor inicial do parâmetro de escala -
epsilon
: valor pequeno que é adicionado à estimativa de variação ao calcular o inverso -
useCntkEngine
: se for verdadeiro, use a implementação nativa do CNTK. Se for falso, use a implementação de cuDNN (somente GPU).
LayerNormalizationLayer
:
-
initialScale
: valor inicial do parâmetro de escala -
initialBias
: valor inicial do parâmetro bias
Valor Retornado
Uma função que implementa uma camada que executa a operação de normalização.
Descrição
BatchNormalizationLayer{}
implementa a técnica descrita no papel Normalização do Lote: acelerando o treinamento de rede profunda reduzindo o deslocamento de covariado interno (Sergey Ioffe, Christian Szegedy).
Ele normaliza suas entradas para cada minibatch pela média/variação da minibatch e a des normaliza com um fator de dimensionamento aprendido e um viés.
Em inferência, em vez de usar média/variação de minibatch, a normalização em lote usa uma estimativa média/var de execução de longo prazo.
Essa estimativa é calculada durante o treinamento por estatísticas de minibatch de filtragem baixa.
A constante de tempo do filtro de passagem baixa pode ser modificada pelo normalizationTimeConstant
parâmetro.
É recomendável começar com o padrão de (5000), mas experimentar outros valores, normalmente 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).
Ele normaliza cada exemplo de entrada subtraindo a média em todos os elementos do exemplo e, em seguida, dividindo pelo desvio padrão sobre todos os elementos do exemplo.
StabilizerLayer{}
implementa um auto-estabilizador por rede neural profunda auto-estabilizada (P. Ghahremani, J. Droppo).
Essa técnica simples, mas eficaz, multiplica sua entrada com um escalar aprendiz (mas ao contrário da normalização da camada, ela não normaliza primeiro a entrada, nem subtrai uma média).
Observe que, em comparação com o artigo original, que propõe um escalar beta
linear ou exponencial Exp (beta)
, achamos benéfico usar uma operação de softplus afiada de acordo com a sugestão do segundo autor, o que evita valores negativos e instabilidade da exponencial.
Observações
BatchNormalizationLayer{}
é um wrapper em torno do BatchNormalization()
primitivo.
LayerNormalizationLayer{}
e StabilizerLayer{}
são expressos diretamente no BrainScript.
Exemplo
Uma camada típica em uma rede convolucional com normalização em 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 recursos por seu desvio médio e padrão.
FeatureMVNLayer{}
Parâmetros
Lista de argumentos {}
vazia.
Valor Retornado
Uma função que implementa uma camada que executa a operação de normalização.
Descrição
Essa camada normaliza a entrada para uma rede neural por seu viés e variação. Esses valores são estimados antecipadamente executando uma passagem completa pelos dados de treinamento e, em seguida, salvos e congelados. Isso acontecerá automaticamente.
Como os parâmetros dessa camada são pré-compilados em um passe separado antes do treinamento principal, ele só pode ser aplicado a variáveis declaradas como Input{}
.
Exemplo
Este é um início típico de uma rede neural para modelagem acústica de fala:
features = Input{40} # e.g. speech features
featNorm = FeatureMVNLayer{} (features)
h = DenseLayer{2048} (featNorm)
...