Oi Habr.
Na
primeira parte , o NVIDIA Jetson Nano foi considerado - uma placa no formato Raspberry Pi, focada na computação de desempenho usando a GPU. É hora de testar o quadro para o qual foi criado - para cálculos orientados à IA.

Considere como diferentes tarefas ocorrem no quadro, como classificar imagens ou reconhecer pedestres ou selos (onde sem eles). Para todos os testes, o código fonte pode ser executado na área de trabalho, Jetson Nano ou Raspberry Pi. Para aqueles que estão interessados, continuaram sob o corte.
Existem duas maneiras de usar este quadro. O primeiro é executar estruturas padrão como Keras e Tensorflow. Funcionará em princípio, funcionará, mas, como já foi visto na primeira parte, o Jetson Nano, é claro, é inferior a uma placa de vídeo de desktop ou laptop de pleno direito. O usuário terá que assumir a tarefa de otimizar o modelo. A segunda maneira é fazer aulas prontas que acompanham o quadro. É mais simples e funciona "pronto para uso", o menos é que todos os detalhes da implementação ficam ocultos em uma extensão muito maior; além disso, você terá que estudar e usar o custom-sdk, que, além dessas placas, não será útil em nenhum outro lugar. No entanto, vamos olhar para os dois lados, começar pelo primeiro.
Classificação da imagem
Considere o problema do reconhecimento de imagem. Para fazer isso, usaremos o modelo ResNet50 fornecido com a Keras (este modelo foi o vencedor do ImageNet Challenge em 2015). Para usá-lo, algumas linhas de código são suficientes.
import tensorflow as tf import numpy as np import time IMAGE_SIZE = 224 IMG_SHAPE = (IMAGE_SIZE, IMAGE_SIZE, 3) resnet = tf.keras.applications.ResNet50(input_shape=IMG_SHAPE) img = tf.contrib.keras.preprocessing.image.load_img('cat.png', target_size=(IMAGE_SIZE, IMAGE_SIZE)) t_start = time.time() img_data = tf.contrib.keras.preprocessing.image.img_to_array(img) x = tf.contrib.keras.applications.resnet50.preprocess_input(np.expand_dims(img_data, axis=0)) probabilities = resnet.predict(x) print(tf.contrib.keras.applications.resnet50.decode_predictions(probabilities, top=5)) print("dT", time.time() - t_start)
Eu nem comecei a remover o código sob o spoiler, porque ele é muito pequeno Como você pode ver, a imagem é redimensionada primeiro para 224x224 (esse é o formato da rede de entrada); no final, a função de previsão faz todo o trabalho.
Tiramos uma foto do gato e executamos o programa.

Resultados:
[[('n02123045', 'tabby', 0.765179), ('n02123159', 'tiger_cat', 0.19059166), ('n02124075', 'Egyptian_cat', 0.013605555), ('n04493381', 'tub', 0.0025916891), ('n04553703', 'washbasin', 0.0021566998)]]
Mais uma vez, chateado com o seu conhecimento de inglês (imagino quantas pessoas não-nativas sabem o que é “malhado”?), Verifiquei a saída com o dicionário, sim, tudo funciona.
O tempo de execução do código do PC foi de
0,5 s para cálculos na CPU e 2 s (!) Para cálculos na GPU. A julgar pelo log, o problema está no modelo ou no Tensorflow, mas quando é iniciado, o código tenta alocar muita memória, recebendo vários avisos no formato “Alocador (GPU_0_bfc) ficou sem memória tentando alocar 2.13GiB com freed_by_count = 0.” . Este é um aviso e não um erro, o código funciona, mas muito mais lento do que deveria.
No Jetson Nano, ainda é mais lento:
2,8 c na CPU e
18,8 c na GPU, enquanto a saída fica assim:

Em geral, mesmo 3s por imagem, isso ainda não é em tempo real. Definir a opção gpu_options.allow_growth recomendada no estouro de pilha não ajuda, se alguém souber de outra maneira, escreva nos comentários.
Editar : conforme solicitado nos comentários, o primeiro início do fluxo tensor sempre leva muito tempo e é incorreto medir o tempo usando-o. De fato, ao processar o segundo e os arquivos subsequentes, os resultados são muito melhores - 0,6s sem uma GPU e 0,2s com uma GPU. Na área de trabalho, a velocidade é, no entanto, de 2,0s e 0,05s, respectivamente.
Um recurso conveniente do ResNet50 é que, na primeira inicialização, ele bombeia todo o modelo para o disco (cerca de 100 MB), então o código funciona de forma totalmente autônoma, sem registro e SMS. O que é especialmente interessante, já que os serviços de IA mais modernos funcionam apenas no servidor e, sem a Internet, o dispositivo se transforma em uma "abóbora".
Gatos vs cães
Considere o seguinte problema. Usando Keras, criaremos uma rede neural que pode distinguir entre gatos e cães. Será uma rede neural convolucional (CNN - Rede Neural Convolucional), que usaremos o design de rede a partir
desta publicação. Um conjunto de imagens de treinamento de gatos e cães já está incluído no pacote tensorflow_datasets, para que você não precise fotografá-las.
Carregamos um conjunto de imagens e o dividimos em três blocos - treinamento, verificação e teste. "Normalizamos" cada imagem, trazendo as cores para o intervalo 0..1.
import tensorflow as tf from tensorflow.keras import layers import tensorflow_datasets as tfds from keras.preprocessing import image import numpy as np import time IMAGE_SIZE = 64 IMG_SHAPE = (IMAGE_SIZE, IMAGE_SIZE, 3) splits = tfds.Split.TRAIN.subsplit(weighted=(80, 10, 10)) (cat_train, cat_valid, cat_test), info = tfds.load('cats_vs_dogs', split=list(splits), with_info=True, as_supervised=True) label_names = info.features['label'].int2str def pre_process_image(image, label): image = tf.cast(image, tf.float32) image = image / 255.0
Escrevemos a função de gerar uma rede neural convolucional.
def custom_model():
Agora podemos executar o treinamento em rede em nosso kit "gato-cachorro". O treinamento leva muito tempo (20 minutos na GPU e 1-2 horas na CPU); portanto, no final, salvamos o modelo em um arquivo.
tl_model = custom_model() t_start = time.time() tl_model.fit(train_batch, steps_per_epoch=8000, epochs=2, validation_data=validation_batch, validation_steps=10, callbacks=None) print("Training done, dT:", time.time() - t_start) print(tl_model.summary()) validation_steps = 20 loss0, accuracy0 = tl_model.evaluate(validation_batch, steps=validation_steps) print("Loss: {:.2f}".format(loss0)) print("Accuracy: {:.2f}".format(accuracy0)) tl_model.save("dog_cat_model.h5")
A propósito, a tentativa de iniciar o treinamento diretamente no Jetson Nano falhou - após 5 minutos, o tabuleiro superaqueceu e travou. Para cálculos com muitos recursos, é necessário um cooler para a placa, embora em geral não faça sentido executar essas tarefas diretamente no Jetson Nano - você pode treinar o modelo em um PC e usar o arquivo salvo final no Nano.
Em seguida, surgiu outra armadilha - a biblioteca tensowflow versão 14 foi instalada no PC e a versão mais recente do Jetson Nano até agora é 13. E o modelo salvo na 14ª versão não foi lido no dia 13, tive que instalar as mesmas versões usando o pip.
Finalmente, podemos carregar o modelo de um arquivo e usá-lo para reconhecer imagens.
def predict_model(model, image_file): img = image.load_img(image_file, target_size=(IMAGE_SIZE, IMAGE_SIZE)) t_start = time.time() img_arr = np.expand_dims(img, axis=0) result = model.predict_classes(img_arr) print("Result: {}, dT: {}".format(label_names(result[0][0]), time.time() - t_start)) model = tf.keras.models.load_model('dog_cat_model.h5') predict_model(model, "cat.png") predict_model(model, "dog1.png") predict_model(model, "dog2.png")
A foto do gato foi usada da mesma forma, mas para o teste do “cão” foram usadas 2 fotos:

O primeiro adivinhou corretamente, e o segundo a princípio teve erros e a rede neural pensou que era um gato, tive que aumentar o número de iterações de treinamento. No entanto, eu provavelmente teria cometido um erro na primeira vez;)
O tempo de execução no Jetson Nano acabou sendo muito pequeno - a primeira foto foi processada em 0,3 segundos, mas todas as subsequentes foram muito mais rápidas, aparentemente os dados são armazenados em cache na memória.

Em geral, podemos assumir que nessas redes neurais simples, a velocidade da placa é suficiente, mesmo sem otimizações, 100fps é um valor suficiente mesmo para vídeo em tempo real.
Conclusão
Como você pode ver, até os modelos padrão da Keras e Tensorflow podem ser usados no Nano, embora com sucesso variável - algo funciona, algo não. No entanto, os resultados podem ser aprimorados, instruções sobre como otimizar o modelo e reduzir o tamanho da memória podem ser lidas
aqui .
Felizmente, para nós, os fabricantes já fizeram isso por nós. Se os leitores ainda tiverem interesse, a parte final será dedicada às
bibliotecas prontas, otimizadas para trabalhar com o Jetson Nano.