Inicie seu detector de rede neural no Raspberry Pi usando o Neural Compute Stick e o OpenVINO

Com a disseminação e desenvolvimento de redes neurais, há uma necessidade crescente de usá-las em dispositivos embarcados e de baixa potência, robôs e drones. O dispositivo Neural Compute Stick, em conjunto com a estrutura Intel OpenVINO, nos permite resolver esse problema, realizando cálculos pesados ​​de redes neurais. Graças a isso, você pode iniciar facilmente um classificador ou detector de rede neural em um dispositivo de baixa potência como o Raspberry Pi quase em tempo real, sem aumentar significativamente o consumo de energia. Nesta postagem, mostrarei como usar a estrutura OpenVINO (em C ++) e o Neural Compute Stick para lançar um sistema simples de detecção de rosto no Raspberry Pi.

Como sempre, todo o código está disponível no GitHub .



Um pouco sobre o Neural Compute Stick e o OpenVINO


No verão de 2017, a Intel lançou o dispositivo Neural Compute Stick (NCS), projetado para executar redes neurais em dispositivos de baixo consumo de energia, e após alguns meses ele pôde ser comprado e testado, o que eu fiz. O NCS é um pequeno módulo de computação com uma caixa de cor azul (também atuando como um radiador), conectado ao dispositivo principal via USB. Dentro, entre outras coisas, está o Intel Myriad VPU , que é essencialmente um processador paralelo de 12 núcleos, aprimorado para operações que ocorrem frequentemente em redes neurais. O NCS não é adequado para o treinamento de redes neurais, mas a inferência em redes neurais já treinadas é comparável em velocidade à da GPU. Todos os cálculos no NCS são realizados em números flutuantes de 16 bits, o que permite aumentar a velocidade. O NCS requer apenas 1 Watt de energia para operar, ou seja, a 5 V, uma corrente de até 200 mA é consumida no conector USB - isso é ainda menos do que a câmera do Raspberry Pi (250 mA).



Para trabalhar com o primeiro NCS, o Neural Compute SDK (NCSDK) foi usado: inclui ferramentas para compilar redes neurais nos formatos Caffe e TensorFlow para o formato NCS, ferramentas para medir seu desempenho, bem como as APIs Python e C ++ para inferência.

Em seguida, uma nova versão da estrutura NCS foi lançada: NCSDK2 . A API mudou bastante e, embora algumas mudanças parecessem estranhas para mim, houve algumas inovações úteis. Em particular, foi adicionada a conversão automática de float 32 bits para float 16 bits em C ++ (anteriormente, as muletas tinham que ser inseridas na forma de código do Numpy). Também apareceram filas de imagens e os resultados de seu processamento.

Em maio de 2018, a Intel lançou o OpenVINO (anteriormente chamado de Intel Computer Vision SDK). Essa estrutura foi projetada para iniciar com eficiência redes neurais em vários dispositivos: processadores e placas gráficas Intel, FPGA e também o Neural Compute Stick.

Em novembro de 2018, uma nova versão do acelerador foi lançada: Neural Compute Stick 2 . O poder de computação do dispositivo aumentou: na descrição no site eles prometem aceleração de até 8x, no entanto, não pude testar a nova versão do dispositivo. A aceleração é alcançada aumentando o número de núcleos de 12 para 16, além de adicionar novos dispositivos de computação otimizados para redes neurais. É verdade que não encontrei informações sobre o consumo de energia das informações.

A segunda versão do NCS já é incompatível com NCSDK ou NCSDK2: o OpenVINO, que é capaz de trabalhar com muitos outros dispositivos além das duas versões do NCS, passou sua autoridade. O próprio OpenVINO possui ótima funcionalidade e inclui os seguintes componentes:

  1. Otimizador de modelo: script Python que permite converter redes neurais de estruturas populares de aprendizado profundo no formato universal OpenVINO. A lista de estruturas suportadas: Caffe , TensorFlow , MXNET , Kaldi (estrutura de reconhecimento de fala), ONNX (formato aberto para representar redes neurais).
  2. Mecanismo de inferência: C ++ e API Python para inferência de rede neural, abstraídas de um dispositivo de inferência específico. O código da API será quase idêntico para CPU, GPU, FPGA e NCS.
  3. Um conjunto de plugins para diferentes dispositivos. Plugins são bibliotecas dinâmicas carregadas explicitamente no código do programa principal. Estamos mais interessados ​​no plugin para o NCS.
  4. Um conjunto de modelos pré-treinados no formato universal OpenVINO (a lista completa está aqui ). Uma impressionante coleção de redes neurais de alta qualidade: detectores de rostos, pedestres, objetos; reconhecimento da orientação de rostos, pontos especiais de rostos, posturas humanas; super resolução; e outros Vale ressaltar que nem todos eles são suportados pelo NCS / FPGA / GPU.
  5. Model Downloader: outro script que simplifica o download de modelos no formato OpenVINO pela rede (embora você possa fazer isso facilmente).
  6. Biblioteca de visão computacional OpenCV otimizada para hardware Intel.
  7. Biblioteca de visão computacional OpenVX .
  8. Biblioteca de computação Intel para redes neurais profundas .
  9. Biblioteca Intel Kernel de matemática para redes neurais profundas .
  10. Uma ferramenta para otimizar redes neurais para FPGA (opcional).
  11. Documentação e exemplos de programas.

Nos meus artigos anteriores, falei sobre como executar o detector de rosto YOLO no NCS (primeiro artigo) , além de como treinar seu detector de rosto SSD e executá-lo no Raspberry Pi e NCS (segundo artigo) . Nestes artigos, usei NCSDK e NCSDK2. Neste artigo, mostrarei como fazer algo semelhante, mas usando o OpenVINO, farei uma pequena comparação dos dois detectores de rosto diferentes e duas estruturas para iniciá-los, e apontarei algumas armadilhas. Eu escrevo em C ++, porque acredito que dessa maneira você poderá obter um melhor desempenho, o que será importante no caso do Raspberry Pi.

Instale o OpenVINO


Não é a tarefa mais difícil, embora haja sutilezas. No momento em que escrevi, o OpenVINO suporta apenas o Ubuntu 16.04 LTS, CentOS 7.4 e Windows 10. Eu tenho o Ubuntu 18 instalado e preciso de pequenas muletas para instalá-lo. Eu também queria comparar o OpenVINO com o NCSDK2, cuja instalação também apresenta problemas: em particular, aperta suas versões do Caffe e TensorFlow e pode alterar um pouco as configurações do ambiente. No final, decidi seguir um caminho simples e instalar as duas estruturas em uma máquina virtual com o Ubuntu 16 (eu uso o VirtualBox ).

Vale ressaltar que, para conectar com êxito o NCS a uma máquina virtual, é necessário instalar os complementos de convidados do VirtualBox e ativar o suporte ao USB 3.0. Também adicionei um filtro universal para dispositivos USB, pelo qual o NCS se conectou sem problemas (embora a webcam ainda precise ser conectada nas configurações da máquina virtual). Para instalar e compilar o OpenVINO, você precisa ter uma conta Intel, escolha uma opção de estrutura (com ou sem suporte a FPGA) e siga as instruções . O NCSDK é ainda mais simples: ele inicializa no GitHub (não esqueça de selecionar o ramo ncsdk2 para a nova versão do framework), após o qual você precisa make install .

O único problema que encontrei ao executar o NCSDK2 em uma máquina virtual é um erro do seguinte formato:

 E: [ 0] dispatcherEventReceive:236 dispatcherEventReceive() Read failed -1 E: [ 0] eventReader:254 Failed to receive event, the device may have reset 

Ocorre no final da execução correta do programa e (ao que parece) não afeta nada. Aparentemente, esse é um pequeno bug relacionado à VM (não deve estar no Raspberry).

A instalação no Raspberry Pi é significativamente diferente. Primeiro, verifique se o Raspbian Stretch está instalado: ambas as estruturas funcionam oficialmente apenas neste sistema operacional. O NCSDK2 precisa ser compilado no modo somente API , caso contrário, ele tentará instalar o Caffe e o TensorFlow, o que dificilmente agradará seu Raspberry. No caso do OpenVINO, existe uma versãomontada para o Raspberry , que você só precisa descompactar e configurar as variáveis ​​de ambiente. Nesta versão, há apenas API C ++ e Python, além da biblioteca OpenCV, todas as outras ferramentas não estão disponíveis. Isso significa que, para ambas as estruturas, os modelos devem ser convertidos antecipadamente em uma máquina com o Ubuntu. Minha demonstração de detecção de rosto funciona no Raspberry e na área de trabalho, então acabei de adicionar os arquivos de rede neural convertidos ao meu repositório GitHub para facilitar a sincronização com o Raspberry. Eu tenho um Raspberry Pi 2 modelo B, mas deve decolar com outros modelos.

Há outra sutileza em relação à interação do Raspberry Pi e do Neural Compute Stick: se, no caso de um laptop, basta enfiar o NCS na porta USB 3.0 mais próxima, então para o Raspberry você precisará encontrar um cabo USB, caso contrário, o NSC bloqueará os três conectores USB restantes com seu corpo. Também vale lembrar que o Raspberry possui todas as versões USB 2.0, portanto a taxa de inferência será menor devido a atrasos na comunicação (uma comparação detalhada será posterior). Mas se você deseja conectar dois ou mais NCS ao Raspberry, provavelmente precisará encontrar um hub USB com energia adicional.

Como é o código do OpenVINO


Muito volumoso. Há muitas ações diferentes a serem iniciadas, começando com o carregamento do plug-in e terminando com a inferência - foi por isso que escrevi uma classe de invólucro para o detector. O código completo pode ser visualizado no GitHub, mas aqui apenas listo os pontos principais. Vamos começar em ordem:

As definições de todas as funções que precisamos estão no arquivo inference_engine.hpp no espaço para nome InferenceEngine .

 #include <inference_engine.hpp> using namespace InferenceEngine; 

As seguintes variáveis ​​serão necessárias o tempo todo. precisamos de inputName e outputName para endereçar a entrada e a saída da rede neural. De um modo geral, uma rede neural pode ter muitas entradas e saídas, mas em nossos detectores haverá uma de cada vez. A variável net é a própria rede, request é um ponteiro para a última solicitação de inferência, inputBlob é um ponteiro para a matriz de dados de entrada da rede neural. As demais variáveis ​​falam por si.

 string inputName; string outputName; ExecutableNetwork net; InferRequest::Ptr request; Blob::Ptr inputBlob; //input shape int netInputWidth; int netInputHeight; int netInputChannels; //output shape int maxNumDetectedFaces; //return code StatusCode ncsCode; 

Agora faça o download do plug-in necessário - precisamos do responsável pelo NCS e NCS2, que pode ser obtido com o nome "MYRIAD". Deixe-me lembrá-lo que, no contexto do OpenVINO, um plug-in é apenas uma biblioteca dinâmica que se conecta por solicitação explícita. O parâmetro da função PluginDispatcher é uma lista de diretórios nos quais procurar plug-ins. Se você configurar as variáveis ​​de ambiente de acordo com as instruções, uma linha vazia será suficiente. Para referência, os plugins estão em [OpenVINO_install_dir]/deployment_tools/inference_engine/lib/ubuntu_16.04/intel64/

 InferencePlugin plugin = PluginDispatcher({""}).getPluginByDevice("MYRIAD"); 

Agora crie um objeto para carregar a rede neural, considere sua descrição e defina o tamanho do lote (o número de imagens processadas simultaneamente). Uma rede neural no formato OpenVINO é definida por dois arquivos: um .xml com uma descrição da estrutura e um .bin com pesos. Embora possamos usar detectores prontos do OpenVINO, mais tarde criaremos os nossos. Aqui std::string filename é o nome do arquivo sem a extensão. Você também precisa ter em mente que o NCS suporta apenas um tamanho de lote 1.

 CNNNetReader netReader; netReader.ReadNetwork(filename+".xml"); netReader.ReadWeights(filename+".bin"); netReader.getNetwork().setBatchSize(1); 

Então acontece o seguinte:

  1. Para entrar na rede neural, defina o tipo de dados como char não assinado de 8 bits. Isso significa que podemos inserir a imagem no formato em que ela vem da câmera, e o InferenceEngine cuidará da conversão (o NCS realiza cálculos no formato float de 16 bits). Isso irá acelerar um pouco no Raspberry Pi - pelo que entendi, a conversão é feita no NCS, portanto, há menos atrasos na transferência de dados via USB.
  2. Nós obtemos os nomes de entrada e saída, para que mais tarde possamos acessá-los.
  3. Nós obtemos a descrição das saídas (este é um mapa do nome da saída para um ponteiro para um bloco de dados). Recebemos um ponteiro para o bloco de dados da primeira saída (única).
  4. Temos seu tamanho: 1 x 1 x número máximo de detecções x comprimento da descrição da detecção (7). Sobre o formato da descrição das detecções - mais tarde.
  5. Defina o formato de saída para flutuar 32 bits. Novamente, a conversão do float 16 bits cuida do InferenceEngine.

 //we can set input type to unsigned char: conversion will be performed on device netReader.getNetwork().getInputsInfo().begin()->second->setPrecision(Precision::U8); //get input and output names and their info structures inputName = netReader.getNetwork().getInputsInfo().begin()->first; outputName = netReader.getNetwork().getOutputsInfo().begin()->first; OutputsDataMap outputInfo(netReader.getNetwork().getOutputsInfo()); InputsDataMap inputInfo(netReader.getNetwork().getInputsInfo()); DataPtr &outputData = (outputInfo.begin()->second); //get output shape: (1 x 1 x maxNumDetectedFaces x faceDescriptionLength(7)) const SizeVector outputDims = outputData->getTensorDesc().getDims(); maxNumDetectedFaces = outputDims[2]; //set input type to float32: calculations are all in float16, conversion is performed on device outputData->setPrecision(Precision::FP32); 

Agora, o ponto mais importante: carregamos a rede neural no plug-in (ou seja, no NCS). Aparentemente, a compilação para o formato desejado está em andamento. Se o programa travar nessa função, a rede neural provavelmente não é adequada para este dispositivo.

 net = plugin.LoadNetwork(netReader.getNetwork(), {}); 

E finalmente - faremos uma inferência experimental e obteremos os tamanhos de entrada (talvez isso possa ser feito de maneira mais elegante). Primeiro, abrimos uma solicitação de inferência, depois obtemos um link para o bloco de dados de entrada e já solicitamos o tamanho dele.

 //perform single inference to get input shape (a hack) request = net.CreateInferRequestPtr(); //open inference request //we need the blob size: (batch(1) x channels(3) x H x W) inputBlob = request->GetBlob(inputName); SizeVector blobSize = inputBlob->getTensorDesc().getDims(); netInputWidth = blobSize[3]; netInputHeight = blobSize[2]; netInputChannels = blobSize[1]; request->Infer(); //close request 

Vamos tentar fazer upload de uma imagem para o NCS. Da mesma forma, criamos uma solicitação de inferência, obtemos um ponteiro para um bloco de dados e, a partir daí, obtemos um ponteiro para o próprio array. Em seguida, basta copiar os dados da nossa imagem (aqui já está reduzido ao tamanho desejado). Vale ressaltar que no cv::Mat e inputBlob medidas são armazenadas em ordem diferente (no OpenCV, o índice do canal muda mais rápido que tudo, no OpenVINO é mais lento que tudo), portanto, o memcpy não é suficiente. Então começamos a inferência assíncrona.

Por que assíncrono? Isso otimizará a alocação de recursos. Enquanto o NCS considera a rede neural, você pode processar o próximo quadro - isso levará a uma aceleração perceptível no Raspberry Pi.

 cv::Mat data; ... //get image somehow //create request, get data blob request = net.CreateInferRequestPtr(); inputBlob = request->GetBlob(inputName); unsigned char* blobData = inputBlob->buffer().as<unsigned char*>(); //copy from resized frame to network input int wh = netInputHeight*netInputWidth; for (int c = 0; c < netInputChannels; c++) for (int h = 0; h < wh; h++) blobData[c * wh + h] = data.data[netInputChannels*h + c]; //start asynchronous inference request->StartAsync(); 

Se você conhece bem as redes neurais, pode ter uma pergunta sobre em que ponto dimensionamos os valores dos pixels de entrada da rede neural (por exemplo, trazemos para o intervalo [ 0 , 1 ] ) O fato é que, nos modelos OpenVINO, essa transformação já está incluída na descrição da rede neural e, ao usar nosso detector, faremos algo semelhante. E como a conversão em float e a escala de entradas são realizadas pelo OpenVINO, precisamos redimensionar a imagem.

Agora (depois de fazer algum trabalho útil), concluiremos o pedido de inferência. O programa é bloqueado até que os resultados da execução cheguem. Temos um ponteiro para o resultado.

 float * output; ncsCode = request->Wait(IInferRequest::WaitMode::RESULT_READY); output = request->GetBlob(outputName)->buffer().as<float*>(); 

Agora é hora de pensar em que formato o NCS retorna o resultado do detector. Vale ressaltar que o formato é um pouco diferente do que era ao usar o NCSDK. De um modo geral, a saída do detector é quadridimensional e possui uma dimensão (1 x 1 x número máximo de detecções x 7), podemos assumir que essa é uma matriz de tamanho ( maxNumDetectedFaces x 7).

O parâmetro maxNumDetectedFaces é definido na descrição da rede neural e é fácil alterá-lo, por exemplo, na descrição .prototxt da rede no formato Caffe. Anteriormente, obtivemos o objeto que representa o detector. Este parâmetro está relacionado às especificidades da classe de detectores SSD (Single Shot Detector) , que inclui todos os detectores NCS suportados. Um SSD sempre considera o mesmo número (e muito grande) de caixas delimitadoras para cada imagem e, depois de filtrar as detecções com uma classificação de confiança baixa e remover os quadros sobrepostos usando a supressão não máxima, eles geralmente deixam os 100-200 melhores. É exatamente por isso que o parâmetro é responsável.

Os sete valores na descrição de uma detecção são os seguintes:

  1. o número da imagem no lote em que o objeto é detectado (no nosso caso, deve ser zero);
  2. classe de objeto (0 - segundo plano, a partir de 1 - outras classes, somente as detecções com uma classe positiva são retornadas);
  3. confiança na presença de detecção (no intervalo [ 0 , 1 ] );
  4. coordenada x normalizada do canto superior esquerdo da caixa delimitadora (no intervalo [ 0 , 1 ] );
  5. da mesma forma - coordenada y;
  6. largura normalizada da caixa delimitadora (no intervalo [ 0 , 1 ] );
  7. da mesma forma - altura;

Código para extrair caixas delimitadoras da saída do detector
 void get_detection_boxes(const float* predictions, int numPred, int w, int h, float thresh, std::vector<float>& probs, std::vector<cv::Rect>& boxes) { float score = 0; float cls = 0; float id = 0; //predictions holds numPred*7 values //data format: image_id, detection_class, detection_confidence, //box_normed_x, box_normed_y, box_normed_w, box_normed_h for (int i=0; i<numPred; i++) { score = predictions[i*7+2]; cls = predictions[i*7+1]; id = predictions[i*7 ]; if (id>=0 && score>thresh && cls<=1) { probs.push_back(score); boxes.push_back(Rect(predictions[i*7+3]*w, predictions[i*7+4]*h, (predictions[i*7+5]-predictions[i*7+3])*w, (predictions[i*7+6]-predictions[i*7+4])*h)); } } } 

aprendemos numPred partir do próprio detector w,h - tamanhos de imagem para visualização.

Agora, sobre como é o esquema geral de inferência em tempo real. Primeiro, inicializamos a rede neural e a câmera, iniciamos cv::Mat para quadros brutos e mais um para quadros reduzidos ao tamanho desejado. Enchemos nossos quadros com zeros - isso aumentará a confiança de que, em um único começo, a rede neural não encontrará nada. Então começamos o ciclo de inferência:

  • Carregamos o quadro atual na rede neural usando uma solicitação assíncrona - o NCS já começou a funcionar e, neste momento, temos a oportunidade de tornar o trabalho principal útil no processador principal.
  • Exibimos todas as detecções anteriores no quadro anterior, desenhamos um quadro (se necessário).
  • Obtemos um novo quadro da câmera, compactamos no tamanho desejado. Para o Raspberry, recomendo usar o algoritmo de redimensionamento mais simples - no OpenCV, essa é a interpolação de vizinhos mais próximos. Isso não afetará a qualidade do desempenho do detector, mas pode adicionar um pouco de velocidade. Também espelho o quadro para facilitar a visualização (opcional).
  • Agora é a hora de obter o resultado com o NCS, preenchendo a solicitação de inferência. O programa será bloqueado até que o resultado seja recebido.
  • Processamos novas detecções, selecionamos quadros.
  • O resto: exercitar as teclas digitadas, contar quadros, etc.

Como compilá-lo


Nos exemplos do InferenceEngine, eu não gostei dos arquivos CMake volumosos e decidi reescrever tudo no meu Makefile:

 g++ $(RPI_ARCH) \ -I/usr/include -I. \ -I$(OPENVINO_PATH)/deployment_tools/inference_engine/include \ -I$(OPENVINO_PATH_RPI)/deployment_tools/inference_engine/include \ -L/usr/lib/x86_64-linux-gnu \ -L/usr/local/lib \ -L$(OPENVINO_PATH)/deployment_tools/inference_engine/lib/ubuntu_16.04/intel64 \ -L$(OPENVINO_PATH_RPI)/deployment_tools/inference_engine/lib/raspbian_9/armv7l \ vino.cpp wrapper/vino_wrapper.cpp \ -o demo -std=c++11 \ `pkg-config opencv --cflags --libs` \ -ldl -linference_engine $(RPI_LIBS) 

Essa equipe trabalhará no Ubuntu e Raspbian, graças a alguns truques. Os caminhos para procurar cabeçalhos e bibliotecas dinâmicas que eu indiquei para o Raspberry e a máquina Ubuntu. Das bibliotecas, além do OpenCV, você também deve conectar o libinference_engine e o libdl - uma biblioteca para vincular dinamicamente outras bibliotecas, é necessário para carregar o plug-in. Ao mesmo tempo, o próprio libmyriadPlugin não precisa ser especificado. Entre outras coisas, para o Raspberry, também conecto a biblioteca Raspicam para trabalhar com a câmera (este é $(RPI_LIBS) ). Eu também tive que usar o padrão C ++ 11.

Separadamente, é importante notar que, ao compilar no Raspberry, o -march=armv7-a é necessário (este é $(RPI_ARCH) ). Se você não o especificar, o programa será compilado, mas falhará com um segfault silencioso. Você também pode adicionar otimizações usando -O3 , isso aumentará a velocidade.

Quais são os detectores


O NCS suporta apenas detectores Caffe SSD da caixa, embora com alguns truques sujos eu consegui executar o YOLO do formato Darknet nele. O Single Shot Detector (SSD) é uma arquitetura popular entre redes neurais leves e, com a ajuda de diferentes codificadores (ou redes de backbone), você pode variar de forma flexível a proporção de velocidade e qualidade.

Vou experimentar com diferentes detectores de rosto:

  • YOLO, retirado daqui , convertido primeiro para o formato Caffe, depois para o formato NCS (somente com NCSDK). Imagem 448 x 448.
  • Meu detector Mobilenet + SSD, sobre o treinamento sobre o qual falei em uma publicação anterior . Ainda tenho uma versão recortada desse detector, que vê apenas rostos pequenos e, ao mesmo tempo, um pouco mais rápido. Vou verificar a versão completa do meu detector no NCSDK e no OpenVINO. Imagem 300 x 300.
  • Detector de detecção de rosto adas-0001 do OpenVINO: MobileNet + SSD. Imagem 384 x 672.
  • Detector OpenVINO de detecção de rosto-varejo-0004: leve SqueezeNet + SSD. Imagem 300 x 300.

Para detectores do OpenVINO, não há escalas no formato Caffe ou no formato NCSDK, portanto, só posso iniciá-las no OpenVINO.

Transforme seu detector em formato OpenVINO


Eu tenho dois arquivos no formato Caffe: .prototxt com uma descrição da rede e .caffemodel com pesos. Preciso obter dois arquivos deles no formato OpenVINO: .xml e .bin com uma descrição e pesos, respectivamente. Para fazer isso, use o script mo.py do OpenVINO (também conhecido como Model Optimizer):

 mo.py \ --framework caffe \ --input_proto models/face/ssd-face.prototxt \ --input_model models/face/ssd-face.caffemodel \ --output_dir models/face \ --model_name ssd-vino-custom \ --mean_values [127.5,127.5,127.5] \ --scale_values [127.5,127.5,127.5] \ --data_type FP16 

output_dir especifica o diretório em que novos arquivos serão criados, model_name é o nome para novos arquivos sem extensão, data_type (FP16/FP32) é o tipo de saldo na rede neural (o NCS suporta apenas FP16). Os mean_values, scale_values definem a média e a escala para pré-processar as imagens antes de serem lançadas na rede neural. A conversão específica é assim:

(pixel valuesmédia values)/scale values



Nesse caso, os valores são convertidos do intervalo [0,255] no intervalo [0,1] . Em geral, esse script possui muitos parâmetros, alguns dos quais são específicos para estruturas individuais. Recomendamos que você consulte o manual do script.

A distribuição OpenVINO para Raspberry não possui modelos prontos, mas eles são bastante simples de baixar.

Por exemplo, assim.
  wget --no-check-certificate \ https://download.01.org/openvinotoolkit/2018_R4/open_model_zoo/face-detection-retail-0004/FP16/face-detection-retail-0004.xml \ -O ./models/face/vino.xml; \ wget --no-check-certificate \ https://download.01.org/openvinotoolkit/2018_R4/open_model_zoo/face-detection-retail-0004/FP16/face-detection-retail-0004.bin \ -O ./models/face/vino.bin 


Comparação de detectores e estruturas


Usei três opções de comparação: 1) NCS + Máquina Virtual com Ubuntu 16.04, processador Core i7, conector USB 3.0; 2) NCS + A mesma máquina, conector USB 3.0 + cabo USB 2.0 (haverá mais atraso na troca com o dispositivo); 3) NCS + Raspberry Pi 2 modelo B, Raspbian Stretch, conector USB 2.0 + cabo USB 2.0.

Iniciei meu detector com o OpenVINO e o NCSDK2, detectores do OpenVINO apenas com sua estrutura nativa, YOLO apenas com o NCSDK2 (provavelmente, ele também pode ser executado no OpenVINO).

A tabela FPS para diferentes detectores é semelhante a esta (os números são aproximados):

ModeloUSB 3.0USB 2.0Raspberry pi
SSD personalizado com NCSDK210,89,37.2
SSD de longo alcance personalizado com NCSDK211,810.07.3
YOLO v2 com NCSDK25.34.63.6.
SSD personalizado com OpenVINO10,69,97,9
OpenVINO detecção de rosto-varejo-000415,614,29,3
Detecção de rosto OpenVINO-adas-00015,85.53.9


Nota: o desempenho foi medido para todo o programa de demonstração, incluindo processamento e visualização de quadros.

YOLO foi o mais lento e mais instável de todos. Frequentemente ignora a detecção e não pode funcionar com quadros iluminados.

O detector que eu treinei funciona duas vezes mais rápido, é mais resistente à distorção nos quadros e até detecta rostos pequenos. No entanto, às vezes ainda ignora a detecção e, às vezes, detecta falsos. Se você cortar as últimas camadas, ela se tornará um pouco mais rápida, mas deixará de ver rostos grandes. O mesmo detector lançado pelo OpenVINO se torna um pouco mais rápido ao usar o USB 2.0, a qualidade não muda visualmente.

Os detectores OpenVINO, é claro, são muito superiores ao YOLO e ao meu detector. (Eu nem começaria a treinar meu detector se o OpenVINO existisse na sua forma atual naquele momento). O modelo retail-0004 é muito mais rápido e quase nunca erra o rosto, mas consegui enganá-lo um pouco (embora a confiança nessas detecções seja baixa):


Ataque competitivo da inteligência natural contra artificial

O detector adas-0001 é muito mais lento, mas funciona com imagens grandes e deve ser mais preciso. Não percebi a diferença, mas verifiquei quadros bastante simples.

Conclusão


Em geral, é muito bom que, em um dispositivo de baixa energia como o Raspberry Pi, você possa usar redes neurais e até em tempo quase real. O OpenVINO fornece uma funcionalidade muito extensa para a inferência de redes neurais em muitos dispositivos diferentes - muito mais ampla do que eu descrevi no artigo. Acho que o Neural Compute Stick e o OpenVINO serão muito úteis na minha pesquisa robótica.

Source: https://habr.com/ru/post/pt436744/


All Articles