Crie seu próprio classificador de imagem usando Transfer Learning
Sumário
- Resumo
- Instalação
- Execute o exemplo – treinar um classificador de flores
- Criar seu próprio classificador de imagem personalizado
- Usando um modelo base diferente
Resumo
As imagens acima são imagens de teste usadas na segunda parte deste tutorial. A tarefa é treinar um classificador que possa distinguir diferentes categorias de imagens (em nosso exemplo de ovelha e lobo) modificando um modelo de classificador existente, o modelo base. Aqui, usamos um modelo ResNet_18 que foi treinado no corpus ImageNet. Treinamos em apenas 15 imagens por classe em alguns segundos e prevemos todas as 10 imagens de teste corretamente (observe os poucos grãos de sal).
Veja a seguir os principais recursos para o tutorial de aprendizado de transferência:
Receita | TransferLearning.py e TransferLearning_Extended.py (consulte Exemplos/Imagem/TransferLearning). |
modelos pré-treinados | Como modelo base para o aprendizado de transferência, usamos um modelo de ResNet_18 pré-treinado. |
Dados | O conjunto de dados Flowers com 102 categorias e imagens de exemplo de ovelhas e lobos (consulte Instalação). |
Como executar | Siga a descrição abaixo. |
Instalação
Para executar o código neste exemplo, você precisa de uma CNTK ambiente do Python (consulte aqui para obter ajuda de instalação).
Para baixar os dados necessários e o modelo pré-treinado, execute o seguinte formulário de comando da pasta Examples/Image/TransferLearning :
python install_data_and_model.py
Executar o exemplo
Nesta seção, criaremos um classificador para o conjunto de dados Flowers. O conjunto de dados foi criado pelo Grupo de Geometria Visual da Universidade de Oxford para tarefas de classificação de imagem. Ele consiste em 102 categorias diferentes de flores comuns ao Reino Unido e contém cerca de 8.000 imagens que são divididas em três conjuntos de uma vez 6000 e duas vezes 1000 imagens. Para obter mais detalhes, consulte a página inicial do VGG.
Para treinar e avaliar um modelo de aprendizado de transferência na execução do conjunto de dados Flowers
python TransferLearning.py
O modelo obtém 93% de precisão no conjunto de dados Flowers após o treinamento para 20 épocas.
O conceito básico de aprendizado de transferência
Quando usamos um modelo base para o aprendizado de transferência, essencialmente nos baseamos nos recursos e conceito que foram aprendidos durante o treinamento do modelo base. Para um DNN convolucional, ResNet_18 em nosso caso, isso significa, por exemplo, que cortamos a camada densa final responsável por prever os rótulos de classe do modelo base original e substituí-lo por uma nova camada densa que preverá os rótulos de classe de nossa nova tarefa em questão. A entrada para a camada de previsão antiga e nova é a mesma, simplesmente reutilizamos os recursos treinados. Em seguida, treinamos essa rede modificada, seja apenas os novos pesos da nova camada de previsão ou todos os pesos de toda a rede.
O código a seguir é a parte que TransferLearning.py
cria o novo modelo a partir do modelo base:
# Load the pretrained classification net and find nodes
base_model = load_model(base_model_file)
feature_node = find_by_name(base_model, feature_node_name)
last_node = find_by_name(base_model, last_hidden_node_name)
# Clone the desired layers with fixed weights
cloned_layers = combine([last_node.owner]).clone(
CloneMethod.freeze if freeze else CloneMethod.clone,
{feature_node: Placeholder(name='features')})
# Add new dense layer for class prediction
feat_norm = input_features - Constant(114)
cloned_out = cloned_layers(feat_norm)
z = Dense(num_classes, activation=None, name=new_output_node_name) (cloned_out)
Criar seu próprio classificador de imagem personalizado
Na seção anterior, treinamos um classificador que distingue 102 categorias diferentes de flores usando cerca de 6.000 imagens para treinamento. Nesta seção, usaremos apenas 15 imagens por categoria para criar um classificador que possa dizer a um lobo de uma ovelha. Usamos o mesmo ResNet_18
modelo base para o aprendizado de transferência. Para treinar e avaliar a execução do modelo
python TransferLearning_Extended.py
O modelo é testado em cinco imagens de ovelhas e lobo cada e prevê todos os rótulos corretamente. O arquivo de saída contém por linha uma representação JSON dos resultados da previsão:
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":0.997, "Wolf":0.003}, "image": "..."}]
[{"class": "Sheep", "predictions": {"Sheep":1.000, "Wolf":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "Wolf", "predictions": {"Wolf":1.000, "Sheep":0.000}, "image": "..."}]
[{"class": "unknown", "predictions": {"Sheep":0.994, "Wolf":0.006}, "image": "..."}]
[{"class": "unknown", "predictions": {"Sheep":0.614, "Wolf":0.386}, "image": "..."}]
[{"class": "unknown", "predictions": {"Wolf":0.980, "Sheep":0.020}, "image": "..."}]
Observe que as três últimas imagens não têm uma classe de verdade básica atribuída, o que é, naturalmente, um cenário válido, por exemplo, para pontuar imagens não vistas em um webservice. As imagens reais são as três imagens de pássaros mostradas abaixo. A classe de verdade básica para eles na saída JSON está definida como unknown
. Observe que as previsões para os conceitos nos quais o classificador foi treinado são muito boas, apesar das poucas imagens de treinamento. Isso ocorre em grandes partes devido ao modelo base pré-treinado. As previsões para conceitos invisíveis, por exemplo, imagens de pássaros, não são, naturalmente, muito significativas, uma vez que o classificador conhece apenas ovelhas e lobos. Mais sobre isso mais tarde.
Estrutura de pastas para conjuntos de imagens personalizados
Você pode usar o TransferLearning_Extended.py
script com suas próprias imagens. Aqui está o que você precisa:
class_mapping
- Uma matriz que contém os nomes de suas categorias, por exemplo.['wolf', 'sheep']
train_map_file
- Um arquivo de texto que contém por linha primeiro uma URL de imagem e uma guia separaram o índice de categoria correspondente, por exemplo0
, para lobo ou1
para ovelhas:test_map_file
- Um arquivo de texto que mapeia imagens de teste para sua categoria correspondente. Para categorias desconhecidas em imagens de teste, use-1
como índice de categoria.
O script pode gerar os três itens acima se você estruturar suas imagens da seguinte maneira:
<image root folder>
Train
Sheep
Wolf
Test
Sheep
Wolf
<optional: image files with unknown label directly here>
Veja <cntk root>/Examples/Image/DataSets/Animals/
como um exemplo. Cada subpasta na Train
pasta será considerada como uma categoria (somente nível único, sem recursão). Para treinar imagens individuais na Train
pasta raiz são ignoradas, pois elas não têm uma categoria atribuída. Subpastas adicionais na Test
pasta que não ocorrem na Train
pasta são ignoradas. Para testar imagens individuais não categorizadas também são usadas para pontuação, ou seja, imagens armazenadas diretamente na Test
pasta, como as três imagens de pássaro em nosso exemplo.
Para usar suas pastas de imagem personalizadas, você precisa definir train_image_folder
e test_image_folder
na parte superior do TransferLearning_Extended.py
script:
# define data location and characteristics
train_image_folder = "<your_image_root_folder>/Train"
test_image_folder = "<your_image_root_folder>/Test"
Em seguida, basta executar python TransferLearning_Extended.py
. Como usar um modelo base diferente é descrito abaixo.
Para pontuar, você não precisa usar uma test_map_file
, por exemplo, se você quiser marcar imagens individuais uma a uma. Basta carregar o modelo de aprendizado de transferência treinado uma vez e ligar eval_single_image
sempre que quiser obter previsões para uma nova imagem:
# once:
# load the trained transfer learning model
trained_model = load_model(new_model_file)
# for every new image:
# get predictions for a single image
probs = eval_single_image(trained_model, img_file, image_width, image_height)
Alguns grãos de sal
Suas imagens de treinamento devem abranger suficientemente os cenários que você deseja pontuar mais tarde. Se o classificador vir conceitos ou contextos totalmente novos, provavelmente terá um desempenho ruim. Apenas alguns exemplos:
- Você treina apenas em imagens de um ambiente de restrição (por exemplo, interior) e tenta pontuar imagens de um ambiente diferente (ao ar livre).
- Você treina apenas em imagens de uma determinada marca e tenta marcar outras pessoas.
- Suas imagens de teste têm características em grande parte diferentes, por exemplo, em relação à iluminação, plano de fundo, cor, tamanho, posição etc.
- Suas imagens de teste contêm conceitos totalmente novos.
Adicionar uma categoria catch-all pode ser uma boa ideia, mas somente se os dados de treinamento dessa categoria contiverem imagens que são novamente suficientemente semelhantes às imagens esperadas no momento da pontuação. Como no exemplo acima, se treinarmos um classificador com imagens de ovelhas e lobo e usá-lo para pontuar uma imagem de um pássaro, o classificador ainda poderá atribuir apenas um rótulo de ovelha ou lobo, já que não conhece nenhuma outra categoria. Se adicionarmos uma categoria catch-all e adicionarmos imagens de treinamento de pássaros a ela, o classificador poderia prever a classe corretamente para a imagem do pássaro. No entanto, se o apresentarmos, por exemplo, uma imagem de um carro, ele enfrenta o mesmo problema de antes, pois conhece apenas ovelhas, lobos e pássaros (o que acabamos de chamar de catch-all). Portanto, seus dados de treinamento, também para o catch-all, precisam abranger suficientemente esses conceitos e imagens que você espera mais tarde no tempo de pontuação.
Outro aspecto a ter em mente é que um modelo base específico pode funcionar muito bem para algumas tarefas de aprendizado de transferência e não tão boa para outras. Por exemplo, o modelo acima ResNet_18
foi pré-treinado no corpus ImageNet, que contém muitas imagens de animais, pessoas, carros e muitos outros objetos diários. Usar esse modelo base no aprendizado de transferência para criar um classificador para objetos semelhantes todos os dias pode funcionar bem. Usar o mesmo modelo que um modelo base para criar um classificador para imagens de microrganismos ou desenhos de lápis pode produzir apenas resultados medíocres.
Usando um modelo base diferente
Para usar um modelo diferente como modelo base, você precisa adaptar os seguintes parâmetros ( TransferLearning.py
o mesmo para TransferLearning_Extended.py
):
# define base model location and characteristics
_base_model_file = os.path.join(base_folder, "..", "..", "..", "PretrainedModels", "ResNet_18.model")
_feature_node_name = "features"
_last_hidden_node_name = "z.x"
_image_height = 224
_image_width = 224
_num_channels = 3
Para investigar quais são os nomes de nó em seu modelo e qual deles escolher, pois last_hidden_node
você pode imprimir todos os nomes de nó e formas de nó usando as linhas a seguir (consulte __main__
o método em TransferLearning.py
):
# You can use the following to inspect the base model and determine the desired node names
node_outputs = get_node_outputs(load_model(_base_model_file))
for out in node_outputs: print("{0} {1}".format(out.name, out.shape))