Codificador de imagem auto usando desconvolução e desconserção
Tabela de Conteúdos
Resumo
O exemplo Image\GettingStarted\07_Deconvolution_PY.py mostra como usar Deconvolution e Unpooling para gerar um simples codificador de imagem automática (07_Deconvolution_BS.cntk é a versão BrainScript correspondente). Utiliza o conjunto de dados MNIST, que tem uma resolução de 28x28x1, codifica-o numa representação 7x7x1 utilizando convolução e agrupamento e descodifica a resolução original. O critério de formação é um erro de raiz-médio-quadrado (RMSE). A figura acima mostra visualizações da imagem original, a imagem codificada e a imagem descodificada para as primeiras cinco imagens do conjunto de testes do MNIST.
Configuração
Para executar o exemplo, precisa do conjunto de dados do MNIST. Pode obter os dados executando o seguinte comando a Examples\Image\DataSets\MNIST
partir da pasta:
python install_mnist.py
Executar o exemplo
O exemplo encontra-se na Examples\Image\GettingStarted
pasta. Para executar este exemplo, utilize o seguinte comando para executar a versão Python (a partir de um ambiente Python CNTK):
python 07_Deconvolution_PY.py
ou este comando para a versão BrainScript:
cntk configFile=07_Deconvolution_BS.cntk
Os valores de RMSE para treino e teste são 0.225 e 0.223, respectivamente. Para visualizar as imagens codificadas e descodificada executam o seguinte comando:
python 07_Deconvolution_Visualizer.py
Definido use_brain_script_model=True
para o BrainScript modelo e False
para o Python modelo.
As visualizações serão armazenadas na Output
pasta em conjunto Examples\Image\GettingStarted
com uma representação de texto do codificador e da saída do descodificador.
Detalhes técnicos
Abaixo está a definição modelo para o codificador de imagem simples em BrainScript (para o ficheiro completo config ver Image\GettingStarted\07_Deconvolution_BS.cntk)
cMap = 1
model = inputFeatures => {
conv1 = ConvolutionalLayer {cMap, (5:5), pad = true, activation=ReLU}(inputFeatures)
pool1 = MaxPoolingLayer {(4:4), stride=(4:4)}(conv1)
unpool1 = MaxUnpoolingLayer {(4:4), stride=(4:4)}(pool1, conv1)
deconv1 = DeconvLayer {1, (5:5), cMap, lowerPad=(2:2:0), upperPad=(2:2:0), bias=false}(unpool1)
}.deconv1
A definição modelo correspondente em 07_Deconvolution_PY.py é
cMap = 1
conv1 = cntk.layers.Convolution ((5,5), cMap, pad=True, activation=cntk.ops.relu)(scaled_input)
pool1 = cntk.layers.MaxPooling ((4,4), (4,4))(conv1)
unpool1 = cntk.layers.MaxUnpooling ((4,4), (4,4))(pool1, conv1)
z = cntk.layers.Deconvolution((5,5), num_channels, cMap, lower_pad=(0,2,2), upper_pad=(0,2,2), bias=False, init=cntk.glorot_uniform(0.001))(unpool1)
Descrevemos a versão BrainScript aqui, a versão Python é análoga. O modelo aplica primeiro um ConvolutionalLayer com uma profundidade das características de entrada seguida de cMap=1
uma ativação ReLU e utiliza um MaxPoolingLayer com uma forma de filtro e passo de (4:4)
. Isto resulta num tensor codificado de tamanho 7x7x1
. Em seguida, utiliza um MaxUnpoolingLayer e um DeconvLayer com as formas de filtro correspondentes para descodificá-lo de volta à resolução original.
A parte descodificadora comprime os números originais 784
(28x28) para 49
(7x7), um fator de 16
. A utilização de apenas uma profundidade para o ConvolutionalLayer tem a vantagem de 1
que os resultados do codificador podem ser visualizados de forma significativa (ver figura no topo desta página). Pode-se aumentar o número de filtros convolucionais, por exemplo, para cMap=3
ter menos compressão e, esperemos, melhores resultados de descodição. Neste exemplo, o RMSE para treinos e testes é reduzido a 0.196
. Outra forma de ter menos compressão é usar uma forma de filtro menor e passo para a camada de piscina. A utilização (2:2)
tanto para a piscina como para desajustar produz um tensor codificado de tamanho 14x14x1
e reduz o RMSE neste exemplo para 0.136
treino e 0.131
teste. A figura abaixo mostra a visualização da imagem original e a imagem descodificada para as cinco primeiras imagens do conjunto de testes MNIST para as três definições discutidas.
Desconvolução e Unpooling
Vamos olhar um pouco mais de perto para o MaxUnpoolingLayer e o DeconvLayer.
MaxUnpoolingLayer {(4:4), stride=(4:4)}(pool1, conv1)
O MaxPoolingLayer requer duas entradas, que são a saída da camada de agrupamento correspondente (pool1
neste caso) e a entrada da camada de agrupamento correspondente (conv1
neste caso). conv1
é necessário em CNTK determinar o alvo da operação Unpooling, uma vez CNTK não armazena as chamadas variáveis switch (consulte aqui para mais detalhes).
DeconvLayer {1, (5:5), cMap, lowerPad=(2:2:0), upperPad=(2:2:0)}
O primeiro parâmetro do DeconvLayer é a profundidade do volume de saída, o segundo é a forma do núcleo (largura:altura) e o terceiro é a profundidade do volume de entrada. Os parâmetros de enchimento devem ser definidos de acordo com a forma do núcleo para alcançar a largura e altura desejadas do tensor de saída (28x28 neste caso). Para mais detalhes no DeconvLayer consulte a página de Referência da Camada.
Codificação de vários camadas de automóveis
Você pode empilhar mais camadas de Conv/Deconv e Pool/Unpool para um codificador de automóveis mais complexo. Segue-se um exemplo com duas camadas de cada tipo em 07_Deconvolution_BS.cntk
que pode utilizar (basta substituir a modelo no ficheiro):
inputDim = 1
cMap1 = 5
cMap2 = 1
model = inputFeatures => {
conv_A = ConvolutionalLayer {cMap1, (5:5), pad = true, activation=ReLU}(inputFeatures)
pool_A = MaxPoolingLayer {(2:2), stride=(2:2)}(conv_A)
conv_B = ConvolutionalLayer {cMap2, (5:5), pad = true, activation=ReLU}(pool_A)
pool_B = MaxPoolingLayer {(2:2), stride=(2:2)}(conv_B)
unpool_B = MaxUnpoolingLayer {(2:2), stride=(2:2)}(pool_B, conv_B)
deconv_B = DeconvLayer {cMap1, (5:5), cMap2, lowerPad=(2:2:0), upperPad=(2:2:0)}(unpool_B)
unpool_A = MaxUnpoolingLayer {(2:2), stride=(2:2)}(deconv_B, conv_A)
deconv_A = DeconvLayer {inputDim, (5:5), cMap1, lowerPad=(2:2:0), upperPad=(2:2:0)}(unpool_A)
}.deconv_A
Para visualizar os resultados, tem de ser substituído z.pool1
z.pool_B
07_Deconvolution_Visualizer.py
antes de o executar para endereçar o nome correto do nó para a saída do codificante. Para investigar todos os nomes de nó no modelo simplesmente descomprometê-lo print_all_node_names(model_file)
no Python script.