Havia Robert Bond, um programador de 65 anos da Califórnia. E ele tinha uma esposa de jardinagem que amava muito o gramado limpo. Mas aqui é a Califórnia, não há cercas de dois metros com um sistema de proteção para gatos. Gatos vizinhos andam no gramado e porcaria!

O problema tinha que ser resolvido. Como Robert decidiu isso? Ele comprou ferro no computador, conectou uma câmera de vigilância externa olhando para o gramado e, em seguida, fez algo incomum: baixou o software Open Source gratuito disponível - uma rede neural e começou a treiná-la para reconhecer gatos na imagem da câmera. E a tarefa no começo parece trivial, porque se você aprende algo e é fácil, é para gatos, porque os gatos estão cheios de lixo na Internet, existem dezenas de milhões deles. Se tudo fosse tão simples, mas as coisas estão piores, na vida real, os gatos costumam cagar principalmente à noite. Praticamente não há fotos de gatos noturnos fazendo xixi no gramado na Internet. E alguns gatos até conseguem beber do sistema de irrigação durante o trabalho, mas ainda o despejam.

Abaixo, fornecemos uma descrição do projeto do autor, a versão em inglês pode ser
encontrada aqui .
Esse projeto foi motivado por duas coisas: o desejo de aprender mais sobre o software de rede neural e o desejo de incentivar os gatos vizinhos a sair em outro lugar além do meu gramado.
O projeto inclui apenas três componentes de hardware: a placa
Nvidia Jetson TX1 , a câmera IP
Foscam FI9800P e o
Particle Photon conectado ao
relé . A câmera está montada na lateral da casa, na lateral do gramado. Ela entra em contato com o ponto de acesso WI-FI, seguido por Jetson. O fóton de partículas e os relés são instalados na unidade de controle do meu sistema de irrigação e conectados a um ponto de acesso WI-FI na cozinha.
No processo, a câmera está configurada para monitorar as alterações no quintal. Quando algo muda, a câmera transmite um conjunto de 7 imagens para a Jetson, uma por segundo. O serviço da Jetson rastreia as imagens recebidas, transferindo-as para a rede neural de treinamento profundo da Caffe. Se a rede detectar um gato, Jetson sinaliza para o servidor Particle Photon na nuvem, que envia uma mensagem para o Photon. O Photon responde ligando os aspersores por dois minutos.
Aqui o gato entrou no quadro, ligando a câmera:

Depois de alguns segundos, o gato entrou no meio do quintal, ligando a câmera novamente e ativou os aspersores do sistema de irrigação:

Instalação da câmera
Não havia nada incomum em instalar uma câmera. A única conexão permanente é uma conexão com fio de 12 volts que passa por um pequeno orifício sob a borda. Montei a câmera em uma caixa de madeira para capturar o jardim da frente com um gramado. Um monte de fios estão conectados à câmera, que eu escondi em uma caixa.
Siga as instruções da Foscam para associá-lo ao AP da Jetson (veja abaixo). Na minha configuração, o Jetson está em 10.42.0.1. Atribuai um endereço IP fixo de 10.42.0.11 à câmera para que fosse fácil encontrá-la. Feito isso, conecte o laptop Windows à câmera e configure o parâmetro "Warning" para ativar a alteração. Configure o upload de 7 imagens via FTP por aviso (alerta). Em seguida, forneça o ID do usuário e a senha no Jetson. Minha câmera envia imagens de 640x360 via FTP para o diretório inicial.
Abaixo você pode ver os parâmetros que foram selecionados para a configuração da câmera.

Configurando o Particle Photon
Photon foi fácil de configurar. Coloquei na unidade de controle de irrigação.

A caixa preta à esquerda com o LED azul é um conversor de 24 V CA (5 V) convertido em 5 V CC (CC), adquirido no eBay. Você pode ver o relé branco na placa de relés e o conector azul na frente. O próprio fóton está à direita. Ambos são colados a um pedaço de papelão para mantê-los unidos.
A saída de 5 V do conversor está conectada ao conector Particle Photon VIN. A placa do relé é basicamente analógica: possui um transistor NPN de coletor aberto com uma entrada nominal de 3,3 V para a base do transistor e um relé de 3 V. O controlador de fótons não podia fornecer corrente suficiente para controlar o relé, então conectei o coletor da entrada do transistor a 5 V através de um resistor com uma resistência de 15 Ohms e uma potência de 1/2 W, limitando a corrente. Os contatos do relé são conectados ao ventilador de água em paralelo com o circuito de controle normal.
Aqui está o diagrama de conexão:
Conversor 24VAC 24VAC <---> Caixa de controle 24VAC OUT
Conversor 24VAC + 5V <---> Photon VIN, resistor para placa de relé + 3.3V
Conversor 24VAC GND <---> Photon GND, relé GND
Fóton D0 <---> Entrada do sinal da placa de relé
Relé COM <---> Caixa de controle 24VAC OUT
Relé NÃO <---> Válvula de água do quintal
Instale o Jetson
Os únicos componentes de hardware adicionados ao Jetson são um SSD SATA e um pequeno hub USB da Belkin. O hub possui duas teclas sem fio que conectam um teclado e um mouse.
O SSD surgiu sem problemas. Reformatei-o para EXT4 e instalei-o como / caffe. Eu recomendo excluir todos os códigos do seu projeto, repositórios git e dados do aplicativo do cartão SD interno da Jetson, porque geralmente é mais fácil limpar o sistema durante uma atualização do Jetpack.

A configuração de um ponto de acesso sem fio foi bastante simples (verdadeiro!) Se você seguiu
este guia . Basta usar o menu Ubuntu como indicado e não se esqueça de adicionar
este parâmetro de configuração .
Eu instalei o vsftpd como um
servidor FTP . A configuração é basicamente de estoque. Não ativei o FTP anônimo. Dei à câmera um nome de usuário e senha que não são mais usados para nada.
Eu instalei o Caffe usando a receita
JetsonHacks . Acredito que não há mais um problema LMDB_MAP_SIZE nas versões atuais, portanto, tente construí-lo antes de fazer alterações. Você deve poder executar os testes e a demonstração de tempo mencionados no script de shell do JetsonHacks. Atualmente, estou usando o Cuda 7.0, mas não tenho certeza se isso é significativo neste estágio. Use CDNN, ele economiza uma quantidade significativa de memória nesses pequenos sistemas. Depois de criado, inclua o diretório build na variável PATH para que os scripts possam encontrar o Caffe. Adicione também o diretório lib do Caffe Python ao seu PYTHONPATH.
~ $ echo $PATH /home/rgb/bin:/caffe/drive_rc/src:/caffe/drive_rc/std_caffe/caffe/build/tools:/usr/local/cuda-7.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ~ $ echo $PYTHONPATH /caffe/drive_rc/std_caffe/caffe/python: ~ $ echo $LD_LIBRARY_PATH /usr/local/cuda-7.0/lib:/usr/local/lib
Estou usando a opção Rede totalmente convolucional para segmentação semântica (FCN). Veja
Berkeley Model Zoo ,
github .
Tentei várias outras redes e finalmente decidi pela FCN. Leia mais sobre o processo de seleção no próximo artigo. O Fcn32s funciona bem no TX1 - ocupa pouco mais de 1 GB de memória, é executado em cerca de 10 segundos e segmenta uma imagem de 640x360 em cerca de um terço de segundo. Há um bom conjunto de scripts no repositório atual do github, e a configuração é independente do tamanho da imagem - ela redimensiona a rede para se ajustar ao que você coloca nela.
Para experimentá-lo, você precisará implantar os modelos Caffe já treinados. Demora alguns minutos: o tamanho do arquivo fcn32s-heavy-pascal.caffemodel excede 500 MB.
$ cd voc-fcn32s $ wget `cat caffemodel-url`
Edite infer.py alterando o caminho no comando Image.open () para o correspondente .jpg. Altere a linha "net" para que aponte para o modelo recém-carregado:
-net = caffe.Net('fcn8s/deploy.prototxt', 'fcn8s/fcn8s-heavy-40k.caffemodel', caffe.TEST) +net = caffe.Net('voc-fcn32s/deploy.prototxt', 'voc-fcn32s/fcn32s-heavy-pascal.caffemodel', caffe.TEST)
Você precisará do arquivo voc-fcn32s / deploy.prototxt. É facilmente gerado a partir do voc-fcn32s / train.prototxt. Veja as alterações entre o voc-fcn8s / train.prototxt e o voc-fcn8s / deploy.prototxt para ver como fazer isso, ou você pode obtê-lo no meu repositório
chasing-cats no github. Agora você deve poder executar.
$ python infer.py
Meu repositório inclui várias versões do infer.py, vários utilitários Python que sabem sobre arquivos segmentados, códigos de photon e scripts de gerenciamento e scripts operacionais que eu uso para iniciar e monitorar o sistema. Leia mais sobre o software abaixo.
Seleção de rede
As redes neurais para reconhecimento de imagens geralmente são treinadas para reconhecer um conjunto de objetos. Suponha que atribuamos a cada objeto um índice de um a n. A rede de classificação responde à pergunta "Quais objetos nesta imagem?" retornando uma matriz de zero a n-1, em que cada entrada da matriz possui um valor de zero a um. Zero significa que o objeto não está na imagem. Um valor diferente de zero significa que ele pode estar lá com probabilidade crescente quando o valor se aproxima da unidade. Aqui está um gato e um homem em uma matriz de 5 elementos:

Uma rede segmentada segmenta pixels de imagens de áreas ocupadas por objetos da nossa lista. Ela responde à pergunta retornando uma matriz com um registro correspondente a cada pixel na imagem. Cada registro tem um valor zero se for um pixel de plano de fundo ou um valor de um a n para n objetos diferentes que ele pode reconhecer. Este exemplo fictício pode ser o pé de uma pessoa:

Este projeto faz parte de um projeto maior, destinado a controlar um carro controlado por rádio usando um computador. A idéia é usar uma rede neural para determinar a posição (posição e orientação tridimensional global) de um carro para transmitir comandos de navegação a ele. A câmera é fixa e o gramado é praticamente plano. Posso usar um pouco o gatilho para alterar a posição 3D, para que a rede neural encontre os pixels e a orientação da tela. O papel do gato em tudo isso é o "objetivo pretendido".
Comecei pensando principalmente no carro, porque não sabia como seria, assumindo que reconhecer um gato com uma rede pré-treinada seria trivial. Depois de muito trabalho, que não descreverei em detalhes neste artigo, decidi que você pode determinar a orientação do carro com um alto grau de confiabilidade. Aqui está uma foto de treinamento em um ângulo de 292,5 graus:

A maior parte deste trabalho foi realizada com a rede de classificação, o modelo Caffe bvlc_reference_caffenet. Portanto, decidi deixar que a tarefa de rede de segmentação determine a posição da máquina na tela.
A primeira rede que usei é o Faster R-CNN [1]. Retorna caixas delimitadoras para objetos na imagem, não pixels. Mas a rede em Jetson estava muito lenta para este aplicativo. A idéia de uma caixa delimitadora era muito atraente, então eu também olhei para a rede orientada à direção [2]. Ela também era muito lenta. FCN [3] foi a rede de segmentação mais rápida que tentei. “FCN” significa “rede totalmente convolucional”, rede totalmente convolucional, já que não exige mais a entrada de nenhum tamanho de imagem específico e consiste apenas em convoluções / agrupamentos. Mudar apenas para camadas convolucionais leva a uma aceleração significativa, classificando minhas imagens em cerca de 1/3 de segundo no Jetson. A FCN inclui um bom conjunto de scripts Python para treinamento e fácil implantação. Os scripts Python redimensionam a rede para caber em qualquer tamanho da imagem recebida, facilitando o processamento da imagem principal. Eu tive um vencedor!
A versão FCN GitHub tem várias opções. Primeiro eu tentei o voc-fcn32s. Funcionou perfeitamente. O Voc-fcn32s foi pré-treinado em 20 aulas padrão. Como isso é muito simples, tentei o pascalcontext-fcn32s. Ele foi treinado em 59 aulas, incluindo grama e árvores, então achei que deveria ser melhor. Mas aconteceu que nem sempre - as imagens produzidas tinham muito mais conjuntos de pixels, e a segmentação de gatos e pessoas sobrepostas em grama e arbustos não era tão precisa. A segmentação do siftflow era ainda mais complexa, então voltei rapidamente às opções de voc.
A escolha de redes vocais ainda significa três coisas a serem consideradas: voc-fcn32s, voc-fcn16s e voc-fcn8s. Eles diferem na "etapa" da segmentação de saída. A etapa 32 é a etapa principal da rede: a imagem de 640x360 é reduzida para uma rede de 20x11 quando as camadas convolucionais são concluídas. Essa segmentação bruta "deconvolve" de volta para 640x360, conforme descrito em [3]. As etapas 16 e 8 são alcançadas adicionando mais lógica à rede para melhor segmentação. Eu nem tentei - a segmentação de 32 segmentos é a primeira que tentei e surgiu, e continuei porque a segmentação parece boa o suficiente para este projeto e o treinamento, conforme descrito, parece mais complicado para as outras duas redes.
Treinamento
A primeira coisa que notei quando liguei e iniciei o sistema foi que apenas cerca de 30% dos gatos foram reconhecidos pela rede. Eu encontrei duas razões para isso. Em primeiro lugar, os gatos costumam vir à noite, então a câmera os vê sob luz infravermelha. Isso pode ser facilmente corrigido - basta adicionar algumas imagens infravermelhas segmentadas de gatos para treinamento. O segundo problema que descobri depois de revisar várias centenas de fotografias de gatos no kit de treinamento é que muitas das fotografias pertencem à variedade "olhe minha gatinha fofa". Estas são imagens frontais de um gato ao nível dos olhos de um gato. Ou o gato está deitado de costas ou no colo do dono. Eles não parecem gatos andando pelo meu quintal. Novamente, ele pode ser facilmente corrigido com algumas imagens diurnas segmentadas.

Como segmentar um objeto em uma imagem de treinamento? Minha abordagem é subtrair a imagem de plano de fundo e depois processar os pixels do primeiro plano para indicar o rastreamento do objeto. Na prática, isso funciona muito bem, porque no meu arquivo da câmera geralmente há uma imagem que foi tirada alguns segundos antes da imagem segmentada. Mas há artefatos que precisam ser limpos e a segmentação geralmente precisa ser esclarecida. Por isso, escrevi um utilitário de preparação para editar segmentos de imagem, src / extract_fg.cpp. Veja a nota na parte superior do arquivo de origem para uso. É um pouco desajeitado e possui pequenos erros de verificação e precisa de algum aprimoramento, mas funciona bem o suficiente para a tarefa.
Agora que temos algumas imagens para treinamento, vamos ver como fazê-lo. Clonei o voc-fcn32s no diretório rgb_voc_fcn32s. Todos os nomes de arquivos se referirão a esse diretório até o final desta lição.
$ cp -r voc-fcn32s rgb_voc_fcn32s
Código no meu github, incluindo amostra de arquivo de treinamento em data / rgb_voc. As principais alterações estão indicadas abaixo.
Formato do arquivo de treinamento
A camada de dados distribuídos espera imagens codificadas e diretórios de segmentação. O arquivo de treinamento possui uma linha por arquivo; a camada de dados obtém os nomes dos arquivos e segmentos de imagem, adicionando nomes de diretório codificados. Isso não funcionou para mim, porque tenho várias classes de dados de treinamento. Meus dados de treinamento têm um conjunto de linhas, cada uma contendo uma imagem e segmentação para essa imagem.
$ head data/rgb_voc/train.txt /caffe/drive_rc/images/negs/MDAlarm_20160620-083644.jpg /caffe/drive_rc/images/empty_seg.png /caffe/drive_rc/images/yardp.fg/0128.jpg /caffe/drive_rc/images/yardp.seg/0128.png /caffe/drive_rc/images/negs/MDAlarm_20160619-174354.jpg /caffe/drive_rc/images/empty_seg.png /caffe/drive_rc/images/yardp.fg/0025.jpg /caffe/drive_rc/images/yardp.seg/0025.png /caffe/drive_rc/images/yardp.fg/0074.jpg /caffe/drive_rc/images/yardp.seg/0074.png /caffe/drive_rc/images/yard.fg/0048.jpg /caffe/drive_rc/images/yard.seg/0048.png /caffe/drive_rc/images/yard.fg/0226.jpg /caffe/drive_rc/images/yard.seg/0226.png
Substituí voc_layers.py por rgb_voc_layers.py, que entende o novo esquema:
--- voc_layers.py 2016-05-20 10:04:35.426326765 -0700 +++ rgb_voc_layers.py 2016-05-31 08:59:29.680669202 -0700 ... - # load indices for images and labels - split_f = '{}/ImageSets/Segmentation/{}.txt'.format(self.voc_dir, - self.split) - self.indices = open(split_f, 'r').read().splitlines() + # load lines for images and labels + self.lines = open(self.input_file, 'r').read().splitlines()
E modifiquei o train.prototxt para usar meu código rgb_voc_layers. Observe que os argumentos também são diferentes.
--- voc-fcn32s/train.prototxt 2016-05-03 09:32:05.276438269 -0700 +++ rgb_voc_fcn32s/train.prototxt 2016-05-27 15:47:36.496258195 -0700 @@ -4,9 +4,9 @@ top: "data" top: "label" python_param { - module: "layers" - layer: "SBDDSegDataLayer" - param_str: "{\'sbdd_dir\': \'../../data/sbdd/dataset\', \'seed\': 1337, \'split\': \'train\', \'mean\': (104.00699, 116.66877, 122.67892)}" + module: "rgb_voc_layers" + layer: "rgbDataLayer" + param_str: "{\'input_file\': \'data/rgb_voc/train.txt\', \'seed\': 1337, \'split\': \'train\', \'mean\': (104.00699, 1
Quase a mesma alteração no val.prototxt:
--- voc-fcn32s/val.prototxt 2016-05-03 09:32:05.276438269 -0700 +++ rgb_voc_fcn32s/val.prototxt 2016-05-27 15:47:44.092258203 -0700 @@ -4,9 +4,9 @@ top: "data" top: "label" python_param { - module: "layers" - layer: "VOCSegDataLayer" - param_str: "{\'voc_dir\': \'../../data/pascal/VOC2011\', \'seed\': 1337, \'split\': \'seg11valid\', \'mean\': (104.00699, 116.66877, 122.67892)}" + module: "rgb_voc_layers" + layer: "rgbDataLayer" + param_str: "{\'input_file\': \'data/rgb_voc/test.txt\', \'seed\': 1337, \'split\': \'seg11valid\', \'mean\': (104.00699, 116.66877, 122.67892)}"
Solver.py
Execute o resolve.py para iniciar seu treino:
$ python rgb_voc_fcn32s / solve.py
Modifica alguns dos mecanismos normais do Caffe. Em particular, o número de iterações é definido na parte inferior do arquivo. Nessa configuração específica, a iteração é uma imagem, pois o tamanho da rede muda para cada imagem e as imagens são puladas uma de cada vez.
Uma das grandes vantagens de trabalhar com a Nvidia é que equipamentos realmente ótimos estão disponíveis. Eu tenho um Titan incorporado em uma estação de trabalho e minha gerência não se importava em me deixar usá-lo para algo tão dúbio quanto este projeto. Minha última corrida de treinamento foi de 4.000 iterações, o que levou um pouco mais de duas horas no Titan.
Eu aprendi algumas coisas
- Um punhado de imagens (menos de 50) foi suficiente para treinar a rede para reconhecer invasores noturnos.
- As tomadas noturnas ensinaram a rede a pensar que as sombras na trilha são gatos.
- Fotos negativas, ou seja, imagens sem pixels segmentados, ajudam a lidar com o problema das sombras.
- É fácil treinar novamente a rede usando uma câmera estacionária, para que tudo o que difere seja classificado como algo aleatório.
- Gatos e humanos, sobrepostos em contextos aleatórios, ajudam nos problemas decorrentes do excesso de treinamento.
Como você pode ver, o processo é iterativo.
Recomendações
[1] R-CNN mais rápido: rumo à detecção de objetos em tempo real com redes de propostas de regiões Shaoqing Ren, Kaiming He, Ross Girshick, Jian Sun
abs / 1506.01497v3 .
[2] Uma avaliação empírica da aprendizagem profunda na condução de rodovias Brody Huval, Tao Wang, Sameep Tandon, Jeff Kiske, Will Song, Joel Pazhayampallil, Mykhaylo Andriluka, Pranav Rajpurkar, Toki Migimatsu, Royce Cheng-Yue, Fernando Cojuju, Fernando Mujica, Andrew Y.
NX arXiv: 1504.01716v3 ,
github.com/brodyh/caffe.git .
[3] Redes totalmente convolucionais para segmentação semântica Jonathan Long, Evan Shelhamer, Trevor Darrell
arXiv: 1411.4038v2 ,
github.com/shelhamer/fcn.berkeleyvision.org.git .
Conclusões
Para ensinar a rede neural a reconhecer gatos noturnos, foi necessário adicionar os dados necessários, acumulando-os. Depois disso, o último passo foi dado - o sistema é conectado à válvula, que inicia o pulverizador. A idéia é que, assim que o gato entra no gramado e quer se adaptar, ele começa a ser regado. O gato despeja. Assim, a tarefa é resolvida, a esposa é feliz e todo esse estranho milagre é uma rede neural que ensina a reconhecer gatos, descobre que a Internet não possui imagens fonte suficientes para treinamento e que aprendeu isso, tornou-se a única rede neural no mundo que pode reconhecer gatos noturnos.
Vale notar que tudo isso foi feito por uma pessoa que não é um hiperprogramador que trabalhou no Yandex ou no Google a vida toda e com a ajuda de hardware, bastante barato, compacto e simples.
Um pouco de publicidade :)
Obrigado por ficar conosco. Você gosta dos nossos artigos? Deseja ver materiais mais interessantes? Ajude-nos fazendo um pedido ou recomendando a seus amigos, um
desconto de 30% para os usuários da Habr em um servidor analógico exclusivo que inventamos para você: Toda a verdade sobre o VPS (KVM) E5-2650 v4 (6 núcleos) 10GB DDR4 240GB SSD 1Gbps de US $ 20 ou como dividir o servidor? (as opções estão disponíveis com RAID1 e RAID10, até 24 núcleos e até 40GB DDR4).
Dell R730xd 2 vezes mais barato? Somente temos
2 TVs Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV a partir de US $ 199 na Holanda! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - a partir de US $ 99! Leia sobre
Como criar um prédio de infraestrutura. classe usando servidores Dell R730xd E5-2650 v4 custando 9.000 euros por um centavo?