Um desafio bem interessante, são os trabalhos com os dados da Sensor Data Management System (SDMS) promovidos e disponibilizados pela Força Aérea Americana, neste caso utilizei os dados MSTAR /IU T-72 variants.
Os dados públicos aqui, em questão, são imagens de SAR (Radar de Abertura Sintética) e ISAR (Radar de Abertura Sintética Inversa), os sensores que produzem essas possuem vantagens quando a meteorologia, pois, suas ondas atravessam as nuvens possibilitando a obtenção das informações sem obstruir a visão sobre o alvo observado, o que se encaixa bem os alvos aqui providos que são as variações dos tanques de guerras T-72, que você pode saber mais nesse artigo da wikipedia.
No entanto, existem diversas variações, mas neste caso nos dados disponibilizados somente existem oito classes, como você pode ver abaixo:
Preparando ambiente
Para fazer o desafio precisei de poucas bibliotecas, basicamente do Arcpy, da API do ArcGIS, especificamente o módulo learn e por último importei a biblioteca pandas.
from arcgis.learn import *
import arcpy
import os
import json
import pandas as pd
Preparando dados
Primeiro passo para realizar um treinamento utilizando o ArcGIS é realizar a preparação dos dados, mas antes de executar a função de preparação foi necessário realizar ajustes na massa de dados fornecida pelo SDMS, para que essa fique no formato correto para o módulo interpretar.
Então como primeiro passo, foi necessário mover todas as pastas das classes de cada uma das rotações para a pasta raiz de imagens, posteriormente foi somente realizar o mapeamento do diretório de cada uma das imagens e criando o arquivo map.txt usando a função que criei abaixo, além disso, retirei da pasta de dados duas imagens as quais utilizarei para inferênciar o modelo sobre elas, testando a execução deste.
dir_images= "D:\T72Variants\images"
res = os.listdir(dir_images)
lista_string = []
for sample in res:
for path in os.scandir(dir_images + "\\"+ sample):
if path.is_file():
if path.name.endswith(".JPG"):
print( "images\\"+ sample + "\\" + path.name)
lista_string.append("images\\"+ sample + "\\" + path.name)
with open('D:\T72Variants\map.txt', 'w') as f:
for line in lista_string:
f.write(f"{line}\n")
Depois desses passos, foi necessário realizar as alterações dos valores e nomes das classes das imagens, nos arquivos esri_accumulated_stats.json e esri_model_definition.emd.
file_esri_st = open('esri_accumulated_stats.json')
file_esri_def = open('esri_model_definition.emd')
esri_st = json.load(file_esri_st)
esri_def = json.load(file_esri_def)
new_classes = {'Classes':[{'ClassValue': 1,'ClassName': 'A04'},
{'ClassValue': 2, 'ClassName': 'A05'},
{'ClassValue': 3, 'ClassName': 'A07'},
{'ClassValue': 4, 'ClassName': 'A10'},
{'ClassValue': 5, 'ClassName': 'A32'},
{'ClassValue': 6, 'ClassName': 'A62'},
{'ClassValue': 7, 'ClassName': 'A63'},
{'ClassValue': 8, 'ClassName': 'A64'}]}
esri_st.update(new_classes)
esri_def.update(new_classes)
with open('esri_accumulated_stats.json', "w") as outfile:
json.dump(esri_st, outfile)
with open('esri_model_definition.emd', "w") as outfile:
json.dump(esri_def, outfile)
E pronto! Com isso o ArcGIS será capaz de executar o treinamento sem problemas.
Para a preparação dos dados do treinamento, defini o mini batch de 68 e realizei o reajuste do tamanho das imagens usando chip_size para 256. Para a amostragem utilizei o atributo de estratificação e utilizei random seed de 500, ou seja, estratificando aleatoriamente em 500 grupos dentro de toda a passa de dados para validação, onde a validação informei que essa seria de 30% da massa de dados, porcentagem escolhida devido a pouca quantidade de amostras.
data = prepare_data(out_amostras, batch_size= 68, dataset_type="ImageNet",
chip_size = 256, seed = 500, val_split_pct = 0.3,
stratify = True)
Então, tivemos no final 3794 imagens para treino e 1627 imagens para validação.
data
Veja que mesmo eu passando chip_size 256 as imagens possuem menor resolução, sendo essa de 138, então a função desprezou meu valor e adotou o valor 138.
Aqui abaixo você pode ver um exemplo de um dos batch's que serão utilizados no treinamento.
data.show_batch()
Agora era realizar o treinamento do modelo.
Treinando o modelo
Utilizando a função FeatureClassifier é preciso somente informar o conjunto de dados, a arquitetura base que usei, neste caso, utilizei o resnet18 e informei o oversample como True, pois, como as classes e as imagens são desbalanceadas a função irá ajustar tal problema.
model = FeatureClassifier(data, backbone="resnet18", oversample=True)
Modelo compilado, foi somente utilizar a função .lr_find (find learning rate) para executar iterativamente, estima qual será nosso melhor valor para o learning rate do modelo de deep learning, que neste caso a função encontrou 0.0014.
lr = model.lr_find()
print("Learning Rate:",lr)
Encontrado o melhor Learning Rate executei o treinamento para 100 épocas, mas informei o early stop como falso, ou seja, que o modelo não iria parar quando o validation loss e o train loss aumentasse a diferença e também informei o checkpoint, salvando os melhores modelos durante o treinamento.
model.fit(100, lr =lr, early_stopping=False, checkpoint=True)
Depois das 100 épocas de treinamento o modelo chegou ao melhor acurácia de 97.48% na época 87 e como eu identifiquei o checkpoint como True esse foi o modelo final que executamos.
Com o modelo finalizado podemos ver então o gráfico de perdas, onde mostra que o modelo treinou bem, pois as duas curvas desceram em conjunto até chegar em um ponto de estabilização, como é de se esperar.
model.plot_losses()
Como a função é de feature classifier, foi possível também ver a matriz de confusão, onde foi possível verificar que a maioria da confusão está entre as classes A04 - A32, A32 - A04 e A62 - A64.
model.plot_confusion_matrix()
Onde esses casos de erros são mostrados, quando utilizamos a função .plot_hard_examples que aponta as piores imagens e o Grad-CAM (Gradient-weighted Class Activation Mapping) de cada um deles.
model.plot_hard_examples(2)
No final foi possível ver o resultado, neste caso, escolhi somente duas imagens, onde consequentemente, as duas foram da classe A32 e obtiveram a classificação correta.
model.show_results(2)
Prevendo novas imagens
Para prever as classes das imagens de teste, criei uma função para buscas dentro dos metadados das imagens de teste, realizando a leitura do arquivo e informando qual a classe da imagem e assim comparar com o resultado previsto pelo modelo.
dir_teste = "D:\T72Variants\teste"
def read_metadata(diretorio):
lista_files = []
for path in os.scandir(dir_teste):
if path.is_file():
if not path.name.endswith(".JPG"):
img_test1_char = pd.read_fwf(diretorio+path.name,
encoding='unicode_escape').iloc[12][0][-3:]
lista_files.append(img_test1_char)
return lista_files
E então foi possível ver que a primeira e a segunda imagem eram respectivamente A04 e A63
read_metadata(dir_teste)
E então realizei a predição do modelo utilizando a função .predict e informei que gostaria de visualizar a execução, mas não gostaria de ver o gradcam.
model.predict(f"{dir_teste}\\HB14937.JPG", visualize=True, gradcam=False)
model.predict(f"{dir_teste}\\HB14961.JPG", visualize=True, gradcam=False)
Com isso foi possível ver que no modelo, para essas duas imagens de teste, ele acertou a classe das duas.
Conclusão
O artigo aqui mostra mais uma vez o potencial do ArcGIS para treinar modelos de deep learning em sua forma low-code, que facilita a gente formar mais, em modificar parâmetros e no experimento que na codificação dos modelos, como foi mostrado que realizei o treinamento de uma modelo com 97.48% acurácia e que acertou as duas imagens de teste, tendo pouco falsos positivos e negativos durante a validação.
Além disso, demostra o potencial crescente entre a união da Inteligência Artificial com Sensoriamento Remoto para realizar aplicações efetivas e resolver problemas como esses na área defesa, no qual informações como essa poderia balizar a tomada de decisão em táticas de guerra.
Se tiver interesse em ver o jupyter notebook onde realizei o treinamento do modelo esse é o link do github.
Comments