Seals vs neural network 2. Ou execute o SqueezeNet v.1.1 no Raspberry Zero em tempo real (quase)

Olá pessoal!

Depois de escrever a primeira parte, que não era muito séria e nem particularmente útil em termos práticos, minha consciência me engoliu um pouco. E eu decidi terminar o que comecei. Ou seja, escolher a mesma implementação de uma rede neural para rodar no Rasperry Pi Zero W em tempo real (é claro, o máximo possível nesse hardware). Para expulsá-la dos dados da vida real e iluminar os resultados em Habré.

Cuidado Existe um código viável e mais alguns gatos sob o corte do que na primeira parte. Na foto, berço e bacalhau, respectivamente.

imagem

Qual rede escolher?


Lembro-me de que, devido à fraqueza do ferro framboesa, a escolha das realizações da rede neural é pequena. Ou seja:

1. SqueezeNet.
2. YOLOv3 minúsculo.
3. MobileNet.
4. ShuffleNet.

Quão correta foi a escolha a favor do SqueezeNet na primeira parte ? .. Executar cada uma das redes neurais acima mencionadas no seu hardware é um evento bastante longo. Portanto, atormentado por vagas dúvidas, decidi pesquisar no Google se alguém tivesse feito uma pergunta dessas antes de mim. Aconteceu que ele se perguntou e investigou em detalhes. Aqueles que desejam podem consultar a fonte . Vou me limitar a uma única imagem:

imagem

A partir da figura, o tempo de processamento de uma imagem para diferentes modelos treinados no conjunto de dados ImageNet é o mínimo com o SqueezeNet v.1.1. Tomaremos isso como um guia de ação. O YOLOv3 não foi incluído na comparação, mas, tanto quanto me lembro, o YOLO é mais caro que o MobileNet. I.e. também deve ter velocidade inferior ao SqueezeNet.

Implementação da rede selecionada


Os pesos e topologia do SqueezeNet treinados no conjunto de dados ImageNet (estrutura Caffe) podem ser encontrados no GitHub . Apenas para o caso, baixei as duas versões para que depois pudessem ser comparadas. Por que ImageNet? Esse conjunto de todos os disponíveis tem o número máximo de classes (1000 unid.). Portanto, os resultados da rede neural prometem ser bastante interessantes.

Desta vez, veremos como o Raspberry Zero lida com o reconhecimento de quadros da câmera. Aqui está ele, nosso humilde trabalhador do post de hoje:

imagem

Peguei o código-fonte do blog Adrian Rosebrock mencionado na primeira parte como base do código, a partir daqui . Mas eu tive que arar significativamente:

1. Substitua seu modelo pelo MobileNetSSD no SqueezeNet.
2. A implementação da Cláusula 1 levou à expansão do número de classes para 1000. Mas, ao mesmo tempo, a função de destacar objetos com quadros multicoloridos (SSD funcional) foi, infelizmente, removida.
3. Para remover a recepção de argumentos pela linha de comando (por algum motivo, essa entrada de parâmetros me incomoda).
4. Remova o método VideoStream e, com ele, a biblioteca imutils amada por Adrian. Inicialmente, o método foi usado para obter o fluxo de vídeo da câmera. Mas com minha câmera conectada ao Raspberry Zero, estupidamente não funcionou, fornecendo algo como "Instruções ilegais".
5. Adicione a taxa de quadros (FPS) à imagem reconhecida, reescreva o cálculo do FPS.
6. Crie quadros salvos para escrever este post.

No raspberry com o Rapbian Stretch OS, Python 3.5.3 e instalado através do pip3, instale o OpenCV 3.4.1, o seguinte foi iniciado e iniciado:

Código aqui
import picamera from picamera.array import PiRGBArray import numpy as np import time from time import sleep import datetime as dt import cv2 #    prototxt = 'models/squeezenet_v1.1.prototxt' model = 'models/squeezenet_v1.1.caffemodel' labels = 'models/synset_words.txt' #    rows = open(labels).read().strip().split("\n") classes = [r[r.find(" ") + 1:].split(",")[0] for r in rows] #    print("[INFO] loading model...") net = cv2.dnn.readNetFromCaffe(prototxt, model) print("[INFO] starting video stream...") #   camera = picamera.PiCamera() camera.resolution = (640, 480) camera.framerate = 25 #   camera.start_preview() sleep(1) camera.stop_preview() #     raw rawCapture = PiRGBArray(camera) #   FPS t0 = time.time() #     for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True): #    blob frame = rawCapture.array blob = cv2.dnn.blobFromImage(frame, 1, (224, 224), (104, 117, 124)) #    blob,     net.setInput(blob) preds = net.forward() preds = preds.reshape((1, len(classes))) idxs = int(np.argsort(preds[0])[::-1][:1]) #  FPS FPS = 1/(time.time() - t0) t0 = time.time() #    ,   FPS,    text = "Label: {}, p = {:.2f}%, fps = {:.2f}".format(classes[idxs], preds[0][idxs] * 100, FPS) cv2.putText(frame, text, (5, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) print(text) cv2.imshow("Frame", frame) #     Raspberry fname = 'pic_' + dt.datetime.now().strftime('%Y-%m-%d_%H-%M-%S') + '.jpg' cv2.imwrite(fname, frame) #    SD  key = cv2.waitKey(1) & 0xFF #    `q`    if key == ord("q"): break #   raw       rawCapture.truncate(0) print("[INFO] video stream is terminated") #    cv2.destroyAllWindows() camera.close() 


Resultados


O código é exibido na tela do monitor conectado ao Raspberry, o próximo quadro reconhecido neste formulário. Na parte superior do quadro, apenas a classe mais provável é exibida.

imagem

Portanto, um mouse de computador foi identificado como um mouse com uma probabilidade muito alta. Ao mesmo tempo, as imagens são atualizadas com uma frequência de 0,34 FPS (ou seja, aproximadamente a cada três segundos). É um pouco chato segurar a câmera e esperar que o próximo quadro seja processado, mas você pode viver. A propósito, se você remover o quadro de salvamento no cartão SD, a velocidade de processamento aumentará para 0,37 ... 0,38 FPS. Certamente, existem outras maneiras de dispersar. Vamos esperar e ver, em qualquer caso, deixaremos esta pergunta para os próximos posts.

Separadamente, peço desculpas pelo balanço de branco. O fato é que a câmera de infravermelho com a luz de fundo ligada estava conectada ao Rapberry; portanto, a maioria dos quadros parece estranha. Mas quanto mais valioso cada acerto da rede neural. Obviamente, o balanço de branco no conjunto de treinamento estava mais correto. Além disso, decidi inserir apenas os quadros brutos, para que o leitor os veja da mesma maneira que vêem a rede neural.

Primeiro, vamos comparar o trabalho das versões 1.0 do SqueezeNet (no quadro esquerdo) e 1.1 (no direito):

imagem

Pode-se ver que a versão 1.1 funciona duas vezes e quinze vezes mais rápido que 1.0 (0,34 FPS versus 0,15). O ganho de velocidade é palpável. Não vale a pena tirar conclusões sobre a precisão do reconhecimento neste exemplo, pois a precisão depende muito da posição da câmera em relação ao objeto, iluminação, brilho, sombras etc.

Em vista de uma vantagem de velocidade tão significativa v1.1 sobre v.1.0 no futuro, apenas o SqueezeNet v.1.1 foi usado. Para avaliar o desempenho do modelo, apontei a câmera para vários objetos que estavam à mão e recebi os seguintes quadros na saída:

imagem

Um teclado é pior que um mouse. Talvez no conjunto de treinamento, a maioria dos teclados fosse branca.

imagem

Um telefone celular é muito bem definido se você ligar a tela. Uma célula com uma tela desligada não conta uma rede neural como célula.

imagem

Uma xícara vazia é razoavelmente definida como uma xícara de café. Até agora, tudo está indo muito bem.

imagem

As tesouras estão em piores condições: são teimosamente definidas pela rede como um grampo de cabelo. No entanto, entrar na macieira, se não no alvo)

Vamos complicar a tarefa


Vamos tentar colocar algo complicado na rede neural do porco . Acabei de encontrar um brinquedo infantil caseiro. Acredito que a maioria dos leitores o reconheça como um gato de brinquedo. Eu me pergunto o que será considerado por nossa inteligência artificial rudimentar.

imagem

No quadro à esquerda, a luz infravermelha apagou todas as tiras do tecido. Como resultado, o brinquedo foi definido como uma máscara de oxigênio com uma probabilidade bastante decente. Porque não A forma do brinquedo realmente se assemelha a uma máscara de oxigênio.

Na moldura à direita, cobri meus dedos com um destaque de infravermelho, para que as listras aparecessem no brinquedo e o balanço de branco se tornasse mais crível. Na verdade, esse é o único quadro que parece mais ou menos normal neste post. Mas a rede neural tem uma abundância de detalhes na imagem confusa. Ela identificou o brinquedo como um moletom. Devo dizer que isso também não parece um "dedo no céu". Bata se não estiver na "macieira", pelo menos no pomar).

Bem, abordamos sem problemas o clímax de nossa ação. O vencedor de destaque da batalha, consagrado em detalhes no primeiro post, entra no ringue. E tira facilmente o cérebro da nossa rede neural desde os primeiros quadros.

imagem

É curioso que um gato praticamente não mude de posição, mas cada vez que é determinado de forma diferente. E desta perspectiva, é mais parecido com um gambá. Em segundo lugar, é a semelhança com um hamster. Vamos tentar mudar o ângulo.

imagem

Sim, se você tirar uma foto de um gato de cima, é determinado corretamente, mas se você mudar um pouco a posição do corpo do gato no quadro, para a rede neural, ele se torna um cachorro - do husky e malamute siberiano (cão de trenó esquimó), respectivamente.

imagem

E essa seleção é bonita, pois um cão de raças diferentes é definido em cada quadro separado de um gato. E as raças não se repetem)

imagem

A propósito, há poses nas quais as redes neurais se tornam óbvias de que ainda é um gato, não um cachorro. Ou seja, o SqueezeNet v.1.1 ainda conseguiu provar a si mesmo em um objeto tão difícil de analisar. Dado o sucesso da rede neural no reconhecimento de objetos no início do teste e no reconhecimento de um gato como um gato no final, desta vez declaramos um forte empate de combate)

Bem, isso é tudo. Convido todos a experimentar o código proposto em sua framboesa e em qualquer objeto que tenha sido exibido como objetos animados e inanimados. Serei especialmente grato àqueles que medem o FPS no Rapberry Pi B +. Prometo incluir os resultados neste post com referência à pessoa que enviou os dados. Eu acredito que deve sair significativamente mais do que 1 FPS!

Espero que algumas das informações contidas neste post sejam úteis para fins educacionais ou de entretenimento, e alguém possa até ter novas idéias.

Tenham uma boa semana de trabalho! E até breve)

imagem

UPD1: No Raspberry Pi 3B +, o script acima funciona com uma frequência de 2 com um pequeno FPS.

UPD2: No RPi 3B + com Movidius NCS, o script é executado em 6 FPS.

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


All Articles