Partilhar via


Construa o seu próprio classificador de imagem utilizando transfer Aprendizagem

Tabela de Conteúdos

Resumo

imageimageimageimage

As imagens acima são imagens de teste usadas na segunda parte deste tutorial. A tarefa é formar um classificador que possa distinguir diferentes categorias de imagens (no nosso exemplo ovelha e lobo) modificando um classificador existente modelo, a base modelo. Aqui usamos um ResNet_18 modelo que foi treinado no corpus ImageNet. Treinamos apenas 15 imagens por classe em poucos segundos e prevemos corretamente todas as 10 imagens de teste (note os poucos grãos de sal).

Seguem-se os principais recursos para o tutorial de aprendizagem de transferências:

Receita TransferLearning.py e TransferLearning_Extended.py (ver Exemplos/Imagem/TransferLearning).
modelos pré-treinados Como base modelo para a aprendizagem de transferência, usamos um ResNet_18 modelo pré-treinado.
Dados Os dados das Flores são definidos com 102 categorias e imagens de exemplo de ovelhas e lobos (ver Configuração).
Como correr Siga a descrição abaixo.

Configuração

Para executar o código neste exemplo, precisa de um ambiente CNTK Python (consulte aqui para obter ajuda de configuração).

Para descarregar os dados necessários e os modelo pré-treinados executar o seguinte formulário de comando a pasta Exemplos/Imagem/TransferLearning:

python install_data_and_model.py

Executar o exemplo

imageimageimageimage

Nesta secção vamos construir um classificador para o conjunto de dados Flores. O conjunto de dados foi criado pelo Grupo de Geometria Visual da Universidade de Oxford para tarefas de classificação de imagem. É composta por 102 categorias diferentes de flores comuns ao Reino Unido e contém cerca de 8000 imagens que são divididas em três conjuntos de uma vez 6000 e duas vezes 1000 imagens. Para mais detalhes consulte a página inicial do VGG.

Para treinar e avaliar um modelo de aprendizagem de transferência na corrida de dados das Flores

python TransferLearning.py

O modelo obtém 93% de precisão no conjunto de dados flowers após o treino para 20 épocas.

O conceito básico de aprendizagem de transferência

Quando usamos uma base modelo para a aprendizagem de transferências, essencialmente baseamos-nos nas características e conceitos que foram aprendidos durante o treino da base modelo. Para um DNN convolucional, ResNet_18 no nosso caso, isto significa, por exemplo, que cortamos a camada densa final que é responsável por prever os rótulos de classe da base original modelo e substituí-la por uma nova camada densa que irá prever os rótulos de classe da nossa nova tarefa em mãos. A entrada para a antiga e a nova camada de previsão é a mesma, simplesmente reutilizamos as características treinadas. Depois treinamos esta rede modificada, ou apenas os novos pesos da nova camada de previsão ou todos os pesos de toda a rede.

O seguinte código é a parte TransferLearning.py que cria o novo modelo a partir da base modelo:

    # 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)

Construa o seu próprio classificador de imagem personalizado

Na secção anterior treinámos um classificador que distingue 102 categorias diferentes de flores utilizando cerca de 6000 imagens para treino. Nesta secção só usaremos 15 imagens por categoria para construir um classificador que possa distinguir um lobo de uma ovelha. Usamos a mesma ResNet_18 base modelo para a aprendizagem de transferências. Para treinar e avaliar a modelo correr

python TransferLearning_Extended.py

O modelo é testado em cinco imagens de ovelhas e lobos cada e prevê todos os rótulos corretamente. O ficheiro 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": "..."}]

Note que as últimas três imagens não têm uma classe de verdade básica atribuída, o que é, naturalmente, um cenário válido, por exemplo, para marcar imagens invisíveis num webservice. As imagens reais são as três imagens mostradas abaixo. A classe de verdade básica para estes na saída JSON está definida para unknown. Note que as previsões para os conceitos em que o classificado foi treinado são muito boas apesar das poucas imagens de treino. Isto é em grandes partes devido à base pré-treinada modelo. As previsões para conceitos invisíveis, por exemplo, imagens de aves, não são, naturalmente, muito significativas, uma vez que o classificador conhece apenas ovelhas e lobos. Mais sobre isto mais tarde.

imageimageimage

Estrutura de pasta para conjuntos de imagem personalizados

Podes usar o TransferLearning_Extended.py guião com as tuas próprias imagens. Aqui está o que precisa:

  1. class_mapping - Uma matriz que contém os nomes das suas categorias, por exemplo. ['wolf', 'sheep']
  2. train_map_file - Um ficheiro de texto que contenha por linha primeiro um URL de imagem e um separador separam o índice de categoria correspondente, por exemplo 0 , para o lobo ou 1 para os ovinos:
  3. test_map_file - Um ficheiro de texto os mapas testam as imagens para a categoria correspondente. Para categorias desconhecidas em imagens de teste, utilize -1 como índice de categoria.

O script pode gerar os três itens acima para si se estruturar as suas imagens da seguinte forma:

<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 sub-fase da Train pasta será considerada como uma categoria (apenas num único nível, sem recursão). Para a formação de imagens individuais na pasta raiz Train são ignoradas uma vez que não têm uma categoria atribuída. As sub-dobradeiras adicionais na Test pasta que não ocorrem Train na pasta são ignoradas. Para testar imagens individuais não categorizadas também são usadas para pontuar, ou seja, imagens que são armazenadas diretamente na Test pasta como as três imagens de pássaros no nosso exemplo.

Para utilizar as suas pastas de imagem personalizadas, tem de definir train_image_folder e test_image_folder no topo 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, simplesmente correr python TransferLearning_Extended.py. Como utilizar uma base diferente modelo é descrito abaixo.

Para marcar não tem de usar um test_map_file, por exemplo, se quiser marcar imagens individuais, uma a uma. Basta carregar a aprendizagem de transferência treinada modelo uma vez e depois 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

As suas imagens de treino devem cobrir suficientemente os cenários que pretende marcar mais tarde. Se o classificador vir novos conceitos ou contextos, é provável que tenha um mau desempenho. Apenas alguns exemplos:

  • Treina-se apenas em imagens de um ambiente de constrangimento (por exemplo, interior) e tenta-se marcar imagens de um ambiente diferente (ao ar livre).
  • Treina-se apenas em imagens de uma certa marca e tenta marcar outros.
  • As suas imagens de teste têm características em grande parte diferentes, por exemplo no que diz respeito à iluminação, fundo, cor, tamanho, posição, etc.
  • As suas imagens de teste contêm conceitos inteiramente novos.

Adicionar uma categoria catch-all pode ser uma boa ideia, mas apenas se os dados de treino para essa categoria contiverem imagens que são novamente suficientemente semelhantes às imagens que espera na hora de pontuação. Como no exemplo acima, se treinamos um classificador com imagens de ovelhas e lobos e a usamos para marcar uma imagem de um pássaro, o classificador ainda só pode atribuir um rótulo de ovelha ou lobo, uma vez que não conhece outras categorias. Se adicionarmos uma categoria de catch-all e adicionarmos-lhe imagens de treino de aves, então 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, enfrenta o mesmo problema que antes, pois só conhece ovelhas, lobos e aves (a que acabamos de chamar catch-all). Assim, os seus dados de treino, também para catch-all, precisam de cobrir suficientemente os conceitos e imagens que espera mais tarde na hora de marcar.

Outro aspeto a ter em mente é que uma base específica modelo pode funcionar muito bem para algumas tarefas de aprendizagem 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 esta base modelo em aprendizagem de transferência para construir um classificador para objetos semelhantes todos os dias pode funcionar bem. Usar a mesma modelo como uma base modelo para construir um classificador para imagens de microrganismos ou desenhos de lápis pode produzir apenas resultados medíocres.

Usando uma base diferente modelo

Para utilizar um modelo diferente como base modelo é necessário adaptar os seguintes parâmetros em 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 os nomes dos nós estão na sua modelo e qual escolher como last_hidden_node pode imprimir todos os nomes de nó e formas de nó usando as seguintes linhas (ver __main__ 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))