Loading...
 

Machine Learning for IoT Data Quality

Autores

  • Eduardo Chequetto Machado (eduardochequettomachado@gmail.com)
  • Matheus Faustino Demetrio (matheus.fdemetrio@gmail.com)
  • Nathan Frois Pereira Paiva (nathanfrois@hotmail.com)

Motivação

Embora haja muitos avanços referentes a qualidade dos dados transmitidas por dispositivos IoT, isso não depende unicamente das capacidades computacionais dos sensores e de como ocorre o roteamento dos dados. A qualidade de uma base de dados coletada por esses sensores distribuídos pode também ser comprometida por falha física, fenômenos naturais atípicos e outras formas de interferência. De forma a não comprometer os sistemas que fazem uso desses dispositivos, é necessário averiguar a confiança nos dados coletados com rigor e frequência. Assim, técnicas de aprendizado de máquina (machine learning) podem ser usadas de forma a medir confiabilidade de bases de dados.

Objetivo

O objetivo deste trabalho é embarcar uma rede neural já treinada no EPOS, de forma a aferir a confiança dos dados detectados pelos sensores do EPOSMote em relação a um conjunto de treinamento previamente estabelecido.

Metodologia

É adotada uma abordagem majoritariamente prática para a execução do projeto, destinando a parte teórica para estudo das documentações do EPOS, MATLAB e da biblioteca de redes neurais escolhida. Adicionalmente oderá ser realizada alguma consulta na literatura de redes neurais por Lin, C.T e C.S George Lee, (1996). Ainda, o projeto deverá ter a sua documentação feita durante todo o seu andamento e relatada aqui na wiki. Em relação a prática do projeto utilizaremos o MATLAB para estudar os dados coletados pelos sensores EPOSMote e fornecidos pelo LISHA. A partir deste estudo, os dados poderão ser eventualmente re-estruturados por intermédio do MATLAB, para que possam ser acomodados na realização do treinamento por uma rede neural feedforward com algoritmo backpropagation, a qual ocorrerá no próprio MATLAB ou no Cranium. Posteriormente com a rede treinada, então embarcaremos ela no EPOS, através de uma biblioteca de redes neurais que sejam leves para sistemas embarcados, o Cranium, que é header-only, facilitando assim o port dela para o EPOS. Por fim, realizaremos testes práticos demonstrando o seu funcionamento.

Tarefas

  1. Entregar o plano trabalho no moodle em formato PDF, contendo a motivação, objetivos, metodologia, tarefas, entregas, cronograma e revisão da literatura referentes a este projeto.
  2. Teste das tecnologias necessárias.
    • Descrição e justificativa do uso das bibliotecas.
  3. Criação da rede neural feedforward com algoritmo backpropagation.
    • Analisar e estudar o conjunto de dados obtido por meio do LISHA, definindo sobre este os atributos correspondentes a entradas e saídas.
    • Definir os parâmetros de treinamento da rede neural.
    • Treinar o conjunto de dados por meio de uma rede neural feedforward com algoritmo backpropagation.
  4. Embarcar a rede neural gerada no EPOS por meio da biblioteca escolhida.
    • Realizar simulações e testes sobre a rede neural, a partir de amostra de dados coletados pelos EPOSMotes.
  5. Relatar os resultados do projeto.
    • Inclui a entrega de um relatório no formato de PDF, cujo conteúdo é o mesmo desta página.
    • Documentação inclusa no relatório e na página.
  6. Apresentação do Projeto.

Entregáveis (Deliverables)

D1 - Plano de projeto (25/09).
D2 - Teste das tecnologias necessárias (02/10).
D3 - Criação da rede neural feedforward’ ‘com algoritmo backpropagation’’ (23/10).
D4 - Embarcar rede neural feedforward’ ‘com algoritmo backpropagation’’ no EPOS (22/11).
D5 - Relatar na wiki os resultados do projeto (22/11).
D6 - Apresentação do Projeto (22/11).

Cronograma

Tarefa 25/09 02/10 09/10 16/10 23/10 30/10 06/11 13/11 20/11 22/11
Tarefa 1 D1
Tarefa 2 x D2 
Tarefa 3 x x x D3 
Tarefa 4 x x x x x D4
Tarefa 5 x x x x x D5
Tarefa 6 x D6

Revisão da Literatura

Séries Temporais (Time Series)

Os dados a serem utilizados neste projeto estão sob a forma de Séries Temporais (Time Series), uma coleção dados medidos por algum processo físico e ordenados temporalmente [4]. Estes dados foram obtidos por meio de sensores de temperatura, umidade, potência elétrica, etc.

Redução de Dimensionalidade (Dimensionality Reduction)

É o processo pelo qual se busca reduzir o número de features (dimensões) de uma matriz de dados. No contexto de séries temporais existem duas categorias de técnicas: feature extraction e feature selection. A primeira, feature extraction, realiza transformações nos dados originais dificultando sua compreensão, mas eliminando o número de features existentes. Já feature selection relaciona o conjunto de dados com suas saídas, fornecendo um ranking de influência das dimensões sem modificar os dados originais [10]. Por possuir uma melhor visualização, tanto dos dados quanto dos resultados, optamos por utilizar ‘feature selection’ para determinar as dimensões a serem utilizadas pela rede neural.

Redes Neurais Artificiais
É um modelo de inteligência artificial, composto por estruturas conexionistas formadas por nodos ou neurônios, capazes de aprender, recordar e generalizar semelhante como um cérebro humano [12]. Neste projeto trabalharemos com redes neurais do tipo multi-layer feedforward com algoritmo backpropagation, por ser a abordagem mais utilizada [10]. Este mesmo tipo de rede foi utilizada para trabalhar com a previsão do nível de água do rio Dungun na Malásia [2].
A rede neural a ser utilizada consiste em três entidades, conforme especificado por Lin, C.T e C.S George Lee (1996) [10]:

  1. Processamento dos dados: Cada neurônio na camada de entrada recebe como todos os atributos do vetor de entrada e nas demais camadas, cada neurônio tem como entrada a saída de todos os outros neurônios. A cada conexão entre dois neurônios é associado um peso aleatório, que caso positivo favorece a ativação, e desfavrece a ativação caso seja negativo. Para cada neurônio, combina-se os pesos com as entradas num valor denominado net input e sobre ele é aplicada a função de ativação, cuja saída é a entrada para o neurônio seguinte [10].
  2. Estrutura da rede: Define como os neurônios estão conectados. Neste modelo assumimos que os neurônios estão organizados em camadas, uma de entrada, 0 ou mais camadas intermediárias e uma camada de saída. Os neurônios se conectam apenas com os neurônios da camada seguinte, propagando o resultado de suas funções de ativação adiante.
  3. Aprendizagem: Neste projeto trabalharemos com aprendizagem supervisionada, na qual a rede é treinada a partir de exemplos prontos, com entradas e saídas já conhecidas. O objetivo é adaptar o valor dos pesos, para que as saídas da rede se aproximem dos valores das saída originais.


__

Teste das tecnologias necessárias

Componentes desta entrega:

  • Uma pasta comprimida da versão arm do EPOS contendo:
    • A biblioteca Cranium embarcado no EPOS:
      • arm-projeto-so2/app/CraniumEPOS
    • Uma aplicação para testar uma rede neural que treina e reconhece a operação and:
      • arm-project-so2/app/neura_and_test.cc
    • Uma aplicação para testar o header extra_math_test.h:
      • arm-project-so2/app/extra_math_test.cc
  • Um relatório pdf contendo esta seção da wiki.

MATLAB

É um software matemático proprietário e externo ao projeto, seu uso deverá auxiliar na análise e estudos dos dados de treinamento.

Cranium

É uma biblioteca de código aberto escrita em vanilla C99, desenvolvida para treinar e simular redes neural do tipo feedforward multi-layer com algoritmo backpropagation. Sua principal característica é possuir poucas dependências, o que facilita a portagem para uma plataforma como o EPOS. A documentação original do Cranium pode ser encontrada aqui.

CraniumEPOS

Apesar das facilidades da biblioteca, existem alguns fatores a considerar ao portar o Cranium para o EPOS. A biblioteca utiliza includes nativos do gcc como assert.h, time.h, string.h, math.h, float.h, stdio.h e stdlib.h, os quais foram substituidos por equivalentes na API do EPOS. Por exemplo: printf do stdio.h por uma instância de OStream em ostream.h. Observamos também que um dos headers do Cranium (network.h) conflita com um header do EPOS de mesmo nome.

O processo de adaptação necessário para embarcar o Cranium no EPOS é descrito abaixo.

Passo 1: Copiar a pasta src/ da biblioteca do Cranium para arm-projeto-so2/app/CraniumEPOS

  • Alternativamente você deixar a pasta CraniumEPOS em arm-projeto-so2/includes/CraniumEPOS


Passo 2: Alterar network.h para neural_network.h e std_includes.h para epos_includes.h.

  • Inclui alteração do nome do struct de Network’ ‘para NeuralNetwork no arquivo neural_network.h’‘.
  • epos_includes.h agora inclui os seguintes headers:
    • utility/ostream.h
    • utility/malloc.h
    • utility/math.h
    • utility/string.h
    • clock.h
    • chronometer.h


Passo 3: Adaptar os headers do Cranium para torná-lo compatível com o EPOS.

  • Incluir using namespace EPOS nos headers com implementação.
    • Substituir printf por uma instância de OStream da biblioteca do EPOS.
    • Chamadas para calloc, malloc e free, agora são redirecionadas a biblioteca do EPOS.
  • Acrescentar um header (extra_math.h) para suplementar funções e constantes matemáticas ausentes na API do EPOS.
    • Funções adicionadas: exponencial para float, tangente hiperbólica, seno e cosseno.
    • Constantes adicionadas: FLT_MIN e FLT_MAX (de float.h), RAND_MAX (de time.h).
    • Devido ao comportamento inesperado da API do EPOS, definimos uma função para raiz quadrada e uma para gerar valores aleatórios.
    • Referências para a origem das funções e constantes estão indicadas no código.
  • Originalmente o Cranium tem suporte opcional ao uso do CBLAS para acelerar a multiplicação de matrizes, uma vez que o uso é opcional, optamos por não utilizar isso nesse projeto.
  • Também não portamos a leitura e escrita de arquivos: (readNeuralNetowrk) e (saveNeuralNetwork), iremos fazer isso a partir de um servidor ou hard-coded na fase final do projeto.
    • Sua retirada não interfere na execução de um programa que crie, treine e simule uma rede neural.


Passo 4: Definição de testes:

  • Em system/config.h do EPOS:
    • Comentar: #define assert(expr) (static_cast<void>(0))
    • Tirar comentários:
#define assert(expr)    ((expr) ? static_cast<void>(0) : Assert::fail (#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__))
  • A biblioteca Cranium por default já contém alguns asserts espalhados pelos seus headers.
    • A biblioteca Cranium também possui testes de unidade os quais portamos para o CraniumEPOS, indicamos eles na seção de Documentação.
  • A aplicação extra_math_test.cc contém testes para o header extra_math.h.
  • Em caso de erro, uma mensagem assert fail deverá aparecer no ambiente de execução.


Passo 5: Configuração do ambiente de execução:

  • Em neural_and_test_traits.h e extra_math_test._traits.h -> Traits<Build>:
    • ARCHITECTURE = IA32
    • MACHINE = PC
    • MODEL = Legacy_PC


Passo 6: Execução das aplicações:

  • neural_and_test.cc:
    • make APPLICATION=neural_and_test run
  • extra_math_test.cc:
    • make APPLICATION=extra_math_test run


Ocasionalmente ao realizar make veryclean as configurações do passo voltam a ficar respectivamente ARMv7, Cortex e LM1855. Neste caso, demanda-se alterar manualmente para as configurações do Passo 4, que por sua vez pode ocasionar erros no qemu.

__

Criação da Rede Neural feedforward com algoritmo backpropagation


Componentes desta entrega:

  • Um tarball contendo o código fonte do projeto final.
    • Uma pasta arm-projeto-so2 com o estado atual do projeto do EPOS arm + CraniumEPOS (cujo conteúdo está fora do escopo desta entrega).
    • Uma pasta MATLAB contendo dados:
      • Um programa main.m e uma função analise.m que faz a análise dos dados extraídos e gera 12 arquivos nomeVariavel_ANN.csv, contendo os dados a serem inseridos nas redes neurais e que não estão nessa pasta, mas está no subdiretório iot_net da pasta external_Cranium.
      • 12 arquivos .csv referentes aos dados de cada variável: _nomeVariavel.csv
    • Uma pasta external_Cranium
      • Contendo o código fonte do Cranium (subdiretório: src).
        • Não é a versão Cranium+EPOS.
      • Contendo 12 planilhas nomeVariavel_ANN.csv, uma por variável e resultantes de todo o processo da análise de dados. (subdiretório: iot_net).
      • Contendo o código fonte da geração das redes neurais (subdiretório: iot_net).
        • Arquivo timeseries.cpp para cada variável, faz chamada para criação de rede em net.hpp.
      • 12 arquivos: nomeVariavel_network.pkl referente a rede neural de cada uma das 12 variáveis, as quais serão utilizas na última fase deste projeto (subdiretório: iot_net).
  • Um relatório no formato pdf gerado a partir da wiki do projeto.


Requisitos de Software:

  • Execução das aplicações (independentes do projeto EPOS arm + CraniumEPOS):
    • Navegador com suporte a programação e execução de JavaScript como Google Chrome, necessário para a extração dos dados.
    • Versão R2017a do MATLAB para o programa main.m.
    • Compilador g++ e linguagem c++11 para a execução do make da pasta external_Cranium.


Execução dos programas:

  • MATLAB:
    • No command window do MATLAB: digite main
  • external_Cranium:
    • Na pasta external_Cranium e subdiretório iot_net: digite no terminal make all (Se julgar necessário, faça backup do arquivo .pkl gerado pois durante a execução eles serão sobrescritos).

Extração dos dados

Utilizamos o script escrito em JavaScript disponível na documentação do EPOS. Os dados extraídos são de uma empresa que utiliza os sensores dos EPOSMotes, e não podem ser divulgados por questões contratuais. Obtivemos séries temporais referentes as seguintes variáveis: pressão (pressure), temperatura (temperature), velocidade do vento (wind velocity”), índice pluviométrico (pluviometric), potêncial elétrico (eletric potential), e irradiação (irradiance_global, irradicance_horizontal, irradiance_inclined_1, irradiance_inclined_2, irradiance_inclined_3, irradiance_diffuse e irradiance_direct’‘). Os dados foram obtidos entre Quinta-feira, 1 de Janeiro de 2015 às 00:01:00 GMT-02:00 DST e Terça-feira, 6 de Janeiro de 2015 às 00:00:00 GMT-02:00 DST. O processo de extração é explicado abaixo.

O script fornecido pela documentação:

<script>

    var epoch = Date.now()*1000;

    var content = { 
        "series": {
            "version" : "1.1",
            "unit"    : 2224179493,
            "x"       : 741868770,
            "y"       : 679816011,
            "z"       : 25285,
            "r"       : 100*30,
            "t0"      : epoch - (5*60*1000000),
            "t1"      : epoch
        },
        "credentials": {
            "domain" : "smartlisha"
        }
    };

    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        document.write(this.responseText);
    };

    xhttp.open("POST", "https://iot.lisha.ufsc.br/api/get.php", true);
    xhttp.send(JSON.stringify(content));

</script>


Passo 1: Execução de scripts no console do navegador web Google Chrome:

  • Em unit:
    • Pressure: 2223941924
    • Temperature: 2224179556
    • Eletric Potential: 2224723748
    • Pluviometric: 2224441636
    • Velocity (Wind): 2224437540
    • Irradiance: 2224199972

  • A variável epoch foi adaptada para comportar os dados na faixa temporal acima:
    • t0 = 1420077660000000 e t1 = 1420509600000000
  • A requisição foi feita de forma síncrona, ou seja, com argumento em false no fim do script acima, pois isso se a requisição for assíncrona, no nosso navegador, os dados retornam de forma duplicada e com algumas sobreposições entre os atributos.
  • Demais parâmetros como x,y,z, r,credentials, por razões contratuais, não podemos revelar.


Passo 2: Dados obtidos:

  • Salvos da página HTML para arquivos .txt.
  • 7200 séries temporais para cada unidade.
    • O intervalo entre duas séries consecutivas é de 1 minuto,
  • Unidades das medidas:
    • pressure
      • Pressão barométrica em pascal.
    • temperature
      • Temperatura ambiente em Kelvin.
    • pluviometric
      • Índice pluviométrico em m.
    • electric_potential
      • Potêncial elétrico em V.
    • wind_velocity
      • Velocidade do vento em m/s
    • irradiance (todas as 7)
      • Em W/m²
  • Exemplo genérico de obtenção para um dado da base do Smart LISHA (todos os dados de qualquer unidade e qualquer base de dados se organizam da mesma forma) :

{"timestamp":1507315265397687,"value":197,"error":0,"confidence":0,"x":741868320,"y":679816251,"z":25300,"mac":"0"},

  • Atributos: (para maiores detalhes consultar a documentação aqui):
    • timestamp: Tempo em microssegundos do UNIX de quando o dado foi medido.
    • value: Valor do dado medido, no caso a corrente elétrica ou a intensidade luminosa.
    • error: Erro de medição do dado.
    • confidence: Índice de confiança sobre o dado medido.
    • x, y, z: Coordenadas absolutas de onde os dados foram medidos.
    • mac: O Message Authenticator Code.
  • Formatação dos dados para a inserção em planilhas de dados no MATLAB:
    • Através do software notepad++, removeu-se nome dos atributos, chaves e aspas de forma a restar apenas valores numéricos.
    • Após a formatação, os dados do exemplo acima ficaram: 1507315265397687,197,0,0,741868320,679816251,25300,0
    • Salvos em um arquivo .csv por variável:
      • _pressure.csv
      • _temperature.csv
      • _pluviometric.csv
      • _electric_potential.csv
      • _wind_velocity.csv
      • _irradiance_global.csv
      • _irradiance_horizontal.csv
      • _irradiance_inclined_1.csv
      • _irradiance_inclined_2.csv
      • _irradiance_inclined_3.csv
      • _irradiance_difuse.csv
      • _irradiance_direct.csv


Observações

  • Cada requisição de dados expõe 86400 dados por vez, o que seria um número demasiado grande para um rede neural como o Cranium. Assim, selecionamos os primeiros 7200 dados, equivalente a uma cobertura de 5 dias.
  • As séries temporais das sete irradiações possuem intervalo de 1 segundo, enquanto as demais possuem intervalos de 1 minutos. Assim fizemos 5 requisições cheias para cada variável de irradiação.
    • É importante ressaltar que não há possibilidade de filtragem avançada na consulta que substituísse esse processo manual inconveniente.
    • Matriz com 432 mil elementos.
      • Com um algoritmo simples coletamos apenas a primeira linha a cada 60, resultando numa matriz de 7200 elementos.
  • Os dados do Smart LISHA são muito recentes (outubro de 2017) além de possuírem intervalos muito pequenos, portanto foram desconsiderados.

Análise dos dados

Antes de embarcar os dados na rede neural é necessário analisá-los. Arbain et al. (2012) recomenda em seu artigo que os dados sobre a forma de time series sejam pré-processados [2]. Ou seja, é necessário tratar os dados que estão com atributos parcialmente completos, como a "confidence" que se encontra nula em todos os dados. Também é necessário normalizar os dados de todos atributos entre 0 e 1 de forma a dar maior acurácia as computações numéricas realizadas sobre eles e por fim aplicar um algoritmo que selecione os features mais importantes de cada variável, filtrando estes dos demais que não influenciam nessa variável .

Para fim de testes, estamos considerando que os dados foram capturados de uma aplicação síncrona que recebe dados dos sensores de potencial elétrico, temperatura, índice pluviométrico, velocidade do vento, pressão e irradiação sobre 7 perspectivas diferentes e já descritas acima, em que todas as variáreis tem seus dados variando no decorrer de um dia. Como os dados obtidos possuem valor 0 em todos os atributos "confidence", "error" e "mac", nós optamos assumir que os dados são confiáveis e geramos assim artificialmente um valor para "confidence". Os detalhes do tratamento são explicados abaixo.

A nossa análise de dados foi realizada sobre o MATLAB em um script main.m que chama uma função analise.m que trata os dados..


Passo 1: Concatenação de planilhas

  • Planilhas de potencial elétrico, temperatura, índice pluviométrico, velocidade do vento, pressão e de irradiações foram concatenadas em uma única matriz timeseries.
    • O número de elementos de timeseries com 7200 amostras X (72 colunas ou 12 variáveis X 8 atributos).
    • Atributos para cada variável: timestamps, values, errors, confidence, x, y, z e mac.



Passo 2: Eliminação de atributos nulos e irrelevantes por variável

  • Remoção da Coluna 8 ( "mac" ).
    • Removido por tratar-se de um código identificador que possui valor 0 em todas as linhas.
  • Remoção das Colunas 5,6 e 7: ( "x", "y", "z"): O escopo do projeto consiste em comparar os valores das variáveis considerando o tempo em que foi medido. Incluir a localização tornaria a rede específica demais e incapaz de racionar sobre diferentes padrões apresentados.
    • Imagine a situação em que esta rede neural fosse aplicada nos Motes de uma empresa do sul da ilha de Florianópolis, se o treinamento efetuado na rede conter a localização de, por exemplo, dos Motes de Jurerê Internacional, isso não contribuiria nada para a resolução do problema, que é extrair a confiança dos dados mensurados.
  • Remoção da Colunas 3 ( "error" ) e 4 ( "confidence" ):
    • Removidos por possuírem 0 em todas as linhas.
  • Resulta em uma matriz com 7200 amostras X (24 colunas ou 2 atributos X 12 variáveis ou features)
  • OBS: Como estão em uma única matriz, para remover um atributo de cada variável, removemos a cada três colunas para remover a terceira coluna de cada variável e assim por diante.



Passo 3: Normalização
Explicaremos abaixo o tratamento individualizado para cada coluna da matriz timeseries:

  • Colunas 1,3,5,7,9,11,13,15,17,21,23 ( "timestamp" ): Normalização entre 0 e 1.
  • Nessas coluna o tempo está representado em microssegundos a partir 1º de Janeiro de 1970. Como em nosso escopo decidimos considerar variações no decorrer de um dia, convertemos os timestamp em microssegundos de um dia e então normalizamos o valor.
    • Um dia em possui 86.400.000.000‬ microssegundos:
      • 1 dia = 24 horas * 60 minutos * 60 segundos * 1000 milissegundos * 1000 microssegundos = ‪86.400.000.000‬ microssegundos.
    • Aplicamos módulo no timestamp obtendo assim os microssegundos decorridos de um dia individual:
      • periodo_dia = "timestamp" mod ‪86.400.000.000‬ microssegundos
    • Normalização entre 0 e 1:
      • periodo_dia_normalizado = periodo_dia / ‪86.400.000.000
  • Colunas 2,4,6,8,10,12,14,16,18,20,22,24: ( "value" ): Normalização entre 0 e 1 sem tratamento adicional.



Passo 4: Determinando a matriz timeseries por variável
Uma a vez que a matriz timeseries foi tratada nos últimos 4 passos acima, agora cada uma das 12 variáveis tem a sua própria cópia idêntica da matriz timeseries, que inclui os dados da própria variável em questão. A justificativa para esse passo adicional, é que o próximo irá selecionar para cada variável, quais das outras 11 variáveis são mais importantes para aquela.


Passo 5: Feature Selection
Aplicamos feature selection nos dados por meio da função fsrnca (Feature selection using neighborhood component analysis for regression) do MATLAB. A retorna um valor de significância para cada feature, sendo 0 insignificância total. Como não é indicado um critério específico, optamos por eliminar features cujo peso seja menor ou igual a 0.1.

Abaixo o pseudo-algoritmo explica a matriz resultante de cada variável após o feature selection, a qual contém o "timestamp" e "value" desta variável e também das demais selecionadas para ela.

for int i = 1 : num_variaveis

   //seleciono os n-1 preditores
   n-1_variaveis = variaveis(setDiff(num_variaveis,i));

   // fsrnca("predictors", "response")
   k_variaveis_selecionadas = fsrnca(n-1_variaveis, variaveis(i));
  
  // se i = num variaveis,  k_variaveis_selecionadas estarão antes do timestamp e value da i-ésima variável.
   variavel(i) = [ ... timestamp(i) value(i) k_variaveis_selecionadas ]  

end


As imagens abaixo mostra os resultados do feature selection para cada variável:


1. Potencial Elétrico:

  • Features selecionados:
    • Pressão, temperatura, irradiação inclinada 1, irradiação difusa, irradiação direta.

Image


2. Índice Pluviométrico:

  • Features selecionados:
    • Pressão e irradiação difusa.

Image


3. Pressão:

  • Features selecionados:
    • Potencial elétrico, temperatura, irradiação inclinada 1, irradiação inclinada 2, irradiação difusa e irradiação direta.

Image


4. Temperatura:

  • Features selecionados:
    • Potencial elétrico, pressão, irradiação global, irradiação inclinada 1,irradiação difusa e irradiação direta.

Image


5. Velocidade do Vento:

  • Features selecionados:
    • Nenhum.

Image


6. Irradiação Global:

  • Features selecionados:
    • Potencial elétrico, pressão, temperatura, irradiação inclinada 1, irradiação inclinada 3, irradiação difusa e irradiação direta.

Image


7. Irradiação Horizontal:

  • Features selecionados:
    • Potencial elétrico, pressão, temperatura, irradiação global, irradiação inclinada 1, irradiação inclinada 2, irradiação inclinada 3, irradiação difusa e irradiação direta.

Image


8. Irradiação Inclinada 1:

  • Features selecionados:
    • Potencial elétrico, pressão, temperatura, irradiação global, irradiação horizontal, irradiação inclinada 2, irradiação inclinada 3, irradiação difusa e irradiação direta.

Image


9. Irradiação Inclinada 2:

  • Features selecionados:
    • Irradiação inclinada 3.

Image


10. Irradiação Inclinada 3:

  • Features selecionados:
    • Irradiação horizontal e irradiação inclinada 2 .

Image


11. Irradiação Difusa:

  • Features selecionados:
    • Potencial elétrico, pressão, temperatura, irradiação global, irradiação inclinada_1 , irradiação inclinada 2, irradiação inclinada 3, irradiação difusa.

Image


12.Irradiação Direta:

  • Features selecionados:
    • Potencial elétrico, pressão, temperatura, irradiação global, irradiação horizontal, irradiação inclinada_1 , irradiação inclinada 2, irradiação inclinada 3, irradiação difusa.

Image


Passo 6: Definição da confiança( "confidence" )

Visando efetuar o treinamento das redes neurais, pressupomos que todas as séries temporais obtidas nos passos anteriores são confiáveis. O que nos valores normalizados utilizados para todas as entradas significa uma confiança 1.

A "confidence" será a última coluna em cada planilha. Por exemplo: uma planilha contendo a variável Temperatura, a qual têm relação com Pressão e outras K variáveis, em sua última coluna, há um único valor "confidence", o qual é a confiança de todas as colunas prévias (série temporal). Ou seja, supondo uma variável tenha ao fim do passo 5, uma matriz com 7200 linhas X 12 colunas ( "value" e "timestamp" de 6 variáveis), ao fim deste passo resultará em 7200 linhas X 13 colunas ( "value" e "timestamp" de 6 variáveis, somando a mais uma coluna "confidence").


Passo 7: Geração de 12 arquivos nomeVariavel_ANN.csv:

  • Foi criado um arquivo (no formato nomeVariavel_ANN.csv) para cada uma das variáveis contendo os dados dela própria e das features significantes para cada uma delas. Cada um destes arquivos será utilizado para treinar uma rede neural diferente, a qual corresponde a sua própria variável.


Observações:

  • Todos os "values" da variável velocidade do vento estão em zero. Mesmo assim o deixamos com os demais dados, e como esperado ele não foi selecionado pela feature selection, assim como nenhuma variável foi selecionada pelo feature selection da velocidade do vento.


Concepção das redes neurais feedforward com algoritmo backpropagation.


Com uma planilha para cada uma das 12 variáveis, é possível criar e treinar as 12 redes neurais necessárias para deduzir a confiança de cada variável. Descrevemos as redes neurais por meio da biblioteca do Cranium, de forma a obter compatibilidade com a versão embarcada no EPOS. A seguir são apresentados a estrutura da rede e o resultado dos treinamentos.

Entradas e Saídas

Em ambas redes, temos os mesmos atributos selecionados para entrada e os mesmos para a saída, onde da mesma forma como está sendo tratado até agora, cada linha representa um padrão único e as colunas são os atributos. Apresentamos abaixo as entradas e a saída padronizadas para a rede neural de cada variável:

  • Atributos de entrada:
    • "timestamp" e "value".
      • Para a própria variável e demais variáveis selecionadas no feature selection (passo 5 da análise de dados).

  • Atributo de saída:
    • Confiança da série temporal (última coluna):
      • "Confidence"


Parâmetros

Como a saída da rede neural é um valor aproximado em relação a saída de treinamento, essas redes neurais resolvem um problema de regressão. Os parâmetros da rede são especificamos abaixo::

  • Camadas:
    • Uma para entrada, duas camadas intermediárias e uma camada de saída.
  • Número de neurônios:
    • Primeira camada Intermediária: 20.
    • Segunda camada Intermediária: 20.
  • Funções de ativação:
    • Rectified Linear Unit (ReLU) (primeira camada intermediária).
    • Rectified Linear Unit (ReLU) (segunda camada intermediária).
    • Linear (camada de saída):
      • Recomendação da documentação do Cranium.
  • Função de performance: mean squared error.
  • Taxa de aprendizagem:
    • Corrente elétrica: 0.06
  • Épocas: 500.
  • Momentum Factor: 0.9
    • Seguimos a recomendação da documentação do Cranium.



Treinamentos e resultados prévios

Com o objetivo de avaliar as redes neurais, as mesmas foram simuladas tomando os dados de treinamento como entrada. A seguir comparados as saídas obtidas com as saídas originais, subtraindo-as e aplicando módulo. Os resultados encontrados são apresentados abaixo, indicando também o arquivo correspondente:

1.Potencial Elétrico:

  • Arquivo: eletric_potential_network.pkl

Image


2.Índice Pluviométrico:

  • Arquivo: pluviometric_network.pkl

Image


3.Pressão:

  • Arquivo: pressure_network.pkl

Image


4.Temperatura:

  • Arquivo: temperature_network.pkl

Image

5. Velocidade do Vento:

  • Arquivo: wind_velocity_network.pkl

Image


6.Irradiação Global:

  • Arquivo: irradiance_global_network.pkl

Image


7.Irradiação Horizontal:

  • Arquivo: irradiance_horizontal_network.pkl

Image


8.Irradiação Inclinada 1:

  • Arquivo: irradiance_inclined_1_network.pkl


Image


9.Irradiação Inclinada 2:

  • Arquivo: irradiance_inclined_2_network.pkl

Image


10.Irradiação Inclinada 3:

  • Arquivo: irradiance_inclined_3_network.pkl

Image


11.Irradiação Difusa:

  • Arquivo: irradiance_difuse_network.pkl

Image


12.Irradiação Direta:

  • Arquivo: irradiance_direct_network.pkl

Image


Conclusões:

Os resultados parecem promissores, com diferenças relativamente pequenas em relação a reentrância dos dados (média geral em 0.00). É necessário ressaltar que essa diferença é importante pois permite que a rede possa ser generalizada para a entrada de outros padrões, variando no valor dos "timestamps" e "values".

Observações:

  • No arquivo timeseries.cpp, foi necessário criar uma rede neural por execução. Notamos que na versão original do Cranium, alguns ponteiros não são deletados, acarretando em resultados incorretos quando as 12 redes são criadas num mesmo programa.
  • Algumas saídas apresentam valores maiores que 1, como 1,04, nesse caso, consideramos que este valor tenha o mesmo significado que 0,96, uma vez que ambos se distanciam do valor de 1 da mesma forma, ou seja, 0,04.


Embarque das redes neurais feedforward com algoritmo backpropagation no EPOS


Componentes desta entrega:

  • Um tarball contendo o código fonte do projeto final.
    • Uma pasta arm-projeto-so2 com o estado atual do projeto do EPOS arm + CraniumEPOS.
      • subdiretório includes: pastas CraniumEPOS e Timeseries, contendo os headers.
      • subdiretório app: contendo o programa principal neural_sim.cc e demais testes para os headers acima, nomeados nomeHeader_test.cc.
    • Uma pasta MATLAB:
      • extra contendo os scripts e imagens do projeto. Os dados serão removidos por questões de privacidade.
    • Uma pasta Cranium_IoT_Training:
      • extra contendo os código utilizado para geração das redes neurais, bem como os arquivos com os pesos das redes.
  • Uma pasta diff: Para um futuro projetista consultar especificamente a diferença de um arquivo do CraniumEPOS para o Cranium Original.
    • O script para execuçao dos diff contém diretórios da minha máquina, portanto não deverá funcionar em outra máquina e para consulta será necessário baixar o Cranium original, disponível em [5];

Requisitos de Software:

  • Versão arm do EPOS.

Execução dos programas:

  • Em D4-ProjetoSO2: make APPLICATION=neural_sim


Ambiente da simulação


De forma a demonstrar o funcionamento do embarque das redes neurais no EPOS, foi preparada uma simulação. É necessário considerar que:

  • A simulação é realizada offline:
    • A interação entre os nodos e o servidor se dão apenas por chamadas de métodos no simulador. Não há troca de mensagens ou uso de protocolos de rede. Futuramente, o simulador pode ser expandido ou substituído conforme necessário.
    • Todos os dados das redes neurais foram hard-coded, não havendo leitura de arquivo para extração dos dados. Mais detalhes podem ser encontrados na documentação final.
  • Cada nodo representa uma variável da qual se quer estimar a confiança.
    • Por dificuldades operacionais não estamos utilizando o smart_data.h.
    • Consideramos que os nodos estejam locais entre si, ignorando suas coordenadas.
    • Quando o nodo é criado ele já normaliza os seus dados.
    • Enviam “mensagens” para cada nodo que represente uma variável (feature) que componha o cálculo da estimativa de confiança.
    • Após receber os dados de cada nodo, realizam o cálculo da estimativa de confiança, fazendo uma chamada ao servidor offline.
      • Este servidor então carrega os dados armazenados em (include/Timeseries/simulation_data.h) da rede neural, referente a variável representada por esse nodo e constrói assim a rede neural, retornando-a ao nodo. A construção da rede neural é uma adaptação do método readNeuralNetwork() da versão original do Cranium.

  • Extração dos dados da simulação:
    • Optamos por utilizar dados de Janeiro de 2016 para a simulação, enquanto os dados utilizados para treinamento são de Janeiro de 2015.
      • Para cada variável colhemos 481 dados entre os timestamps: 1453717560000000 (Segunda-feira, 25 de Janeiro de 2016 às 08:26:00 GMT-02:00 DST) e 1453746360000000 (Segunda-feira, 25 de Janeiro de 2016 às 16:26:00 GMT-02:00 DST), intercalados a cada 60 segundos.


Execução da simulação


O diagrama de interação abaixo resume a execução de uma simulação:

Image

Na pasta arm-project-so2 e subdiretório app, abrir o arquivo neural_sim.cc:

Introdução: Considere as seguintes instâncias da classe Node:

  • *ep: Potencial elétrico.
  • *pv: Pressão.
  • *pr: Índice pluviométrico.
  • *t: Temperatura.
  • *wv: Velocidade do vento.
  • *i_g: Irradiação global.
  • *i_h: Irradiação horizontal.
  • *i_i1: Irradiação inclinada 1.
  • *i_i2: Irradiação inclinada 2.
  • *i_i3: Irradiação inclinada 3.
  • *i_dif:Irradiação difusa.
  • *i_dir: Irradiação direta.


Passo 1: Configurar as opções de simulação:

No mesmo arquivo:

  • Defina o número de séries temporais que serão simuladas:
    • #define NUM_DATA [0 à 481]
  • Modo de simulação: Para o nodo que você deseja simular, considerando as instâncias do Passo 0, altere se desejar o seguinte parâmetro na criação deste nodo.
    • Node *nodo = new Node(..., ... , Neighbors_Options::SERIAL_TIME)
      • A opção acima considera que o “timestamp” e “values” dos vizinhos recebidos estejam no mesmo “timestamp atual” de node.
    • Node *nodo = new Node(..., ... , Neighbors_Options::RANDOM_FIVE_MINUTES)
      • A opção acima considera que o “timestamp” e “values” dos vizinhos recebidos estejam em uma faixa aleatória de 0 a 5 minutos, antes ou depois do “timestamp atual” de node.
      • A opção acima só é valida para NUM_DATA > 5, caso contrário a opção Neighbors_Options::SERIAL_TIME será assumida.
  • Assuma um valor Node *sim = node;, onde node é a instância do nodo que você quer simular .


Passo 2: Verificar as configurações de execução:

  • Em app/neural_sim_traits.h, altere as contantes para as especificações desejadas:

    • static const unsigned int ARCHITECTURE = IA32;
    • static const unsigned int MACHINE = PC;
    • static const unsigned int MODEL = Legacy_PC;


Passo 3: Execute e observe os resultados na tela

  • Em arm-project-so2
    • make APPLICATION=neural_sim


Passo 4: Obtenção da confiança:

  • A confiança para cada dado é obtida da seguinte forma (resultado na tela): 1 - abs(1 - saída_rede_neural).


Simulação 1:

Parâmetros usados: NUM_DATA = 10 e Node *sim = i_i3 com Neighbors_Options::RANDOM_FIVE_MINUTES

Image


Simulação 2:

Parâmetros usados: NUM_DATA = 481 e Node *sim = i_i3 com Neighbors_Options::RANDOM_FIVE_MINUTES

Image

Conclusões e Trabalhos Futuros


Demonstramos alcançar o objetivo do projeto, embarcando uma rede neural treinada no EPOS e aferindo a confiança de dados coletados por sensores num simulador. Embora não utilizemos a API SmartData, é importante ressaltar que tanto os dados de treinamento quanto os utilizados na simulação foram capturados por EPOSMotes. Descrevemos o processo utilizado para estabelecer a relação quantitativa entre as 12 variáveis coletadas, as quais se encontram num mesmo intervalo de tempo, permitindo a trabalhos futuros repetir o processo em outros contextos de dados.

Além do embarque da rede no EPOS, adaptamos a biblioteca Cranium para tornar-se compatível com a plataforma e demonstramos seu pleno funcionamento por meio de testes de unidade. O CraniumEPOS é capaz de resolver variados problemas de regressão, bem como ser utilizado para criar classificadores de dados.

Assim, sugerimos duas opções como:

  • Implementar a simulação deste trabalho utilizando a API de comunicação do EPOS. Realizando uma comunicação prática entre o servidor e os nodos, e calculando a confiança em tempo real.

  • Utilizar a biblioteca CraniumEPOS para desenvolver um novo projeto qualquer que utilize redes neurais feedforward com algoritmo backpropagation no EPOS.

Documentação


Acrescentamos este capítulo para apresentar a versão adaptada do Cranium para o EPOS, bem como o simulador para testar sua aplicação.

CraniumEPOS


Com esta biblioteca é possível criar e simular redes neurais, independente da aplicação. Entretanto não é possível atualmente salvar e carregar arquivos de redes neurais (.pkl), sendo necessário treinar e simular numa mesma execução ou criar as redes externamente e criá-la manualmente no código, como no caso deste projeto.
Para os testes de unidade do CraniumEPOS, utilizamos o quanto possível, os mesmos testes de unidade definidos por [5].
A documentação original do Cranium pode ser encontrada em [13].


Image

cranium.h

  • É o header principal, sendo este o único do CraniumEPOS a ser incluído na sua aplicação.
    • #include "CraniumEPOS/cranium.h"

#include "epos_includes.h"
#include "matrix.h"
#include "function.h"
#include "layer.h"
#include "neural_network.h"
#include "optimizer.h"
//
#include "extra_math.h"
#include <cstddef>
#include <cstdio>


epos_includes.h

  • É o header que substitui o std_includes.h da versão original do Cranium, substituindo o mesmo funções da biblioteca nativa do EPOS.
    • OBS: Ao referenciar ou chamar alguma variável da biblioteca do EPOS, além de incluir este header, não se esqueça de colocar a instrução: using namespace EPOS;

#include <utility/math.h>
#include <utility/string.h>
#include <utility/ostream.h>
#include <utility/malloc.h>
#include <clock.h>


optimizer.h

  • Métodos adicionados: nenhum.
  • Testes de unidade: app/optimizer_test.cc
    • make APPLICATION=optimizer_test run

// types of loss functions available to optimize under
typedef enum LOSS_FUNCTION_ {
    CROSS_ENTROPY_LOSS, 
    MEAN_SQUARED_ERROR
} LOSS_FUNCTION;

// convenience struct for easier parameter filling
typedef struct ParameterSet_ {
    NeuralNetwork* network;
    DataSet* data;
    DataSet* classes;
    LOSS_FUNCTION lossFunction;
    size_t batchSize;
    float learningRate;
    float searchTime;
    float regularizationStrength;
    float momentumFactor;
    int maxIters;
    int shuffle;
    int verbose;
} ParameterSet;

// batch gradient descent main function
// $network is the network to be trained
// $data is the training data
// $classes are the true values for each data point in $data, in order
// $batchSize is the size of each batch to use (1 for SGD, #rows for batch)
// $learningRate is the initial learning rate
// $searchTime is the other parameter in the search-and-converge method
// $regularizationStrength is the multiplier for L2 regularization
// $momentumFactor is the multiplier for the moment factor
// $maxIters is the number of epochs to run the algorithm for
// $shuffle, if non-zero, will shuffle the data between iterations
// $verbose, if non-zero, will print loss every 100 epochs
static void batchGradientDescent(NeuralNetwork* network, DataSet* data, DataSet* classes, LOSS_FUNCTION lossFunction, size_t batchSize, float learningRate, float searchTime, float regularizationStrength, float momentumFactor, int maxIters, int shuffle, int verbose);

// optimizes given parameters
static void optimize(ParameterSet params);


matrix.h

  • Métodos adicionados: meanByRow, maxByRow, minByRow, meanByCol, maxByCol, minByCol e matrixABS.
  • Testes de unidade: app/matrix_test.cc
    • make APPLICATION=matrix_test run

// represents user-supplied training data
typedef struct DataSet_ {
    size_t rows;
    size_t cols;
    float** data;
} DataSet;

// represents a matrix of data in row-major order
typedef struct Matrix_ {
    size_t rows;
    size_t cols;
    float* data;
} Matrix;

#define MAX(a,b) (((a)>(b))?(a):(b))
#define MIN(a,b) (((a)<(b))?(a):(b))

// create dataset given user data
static DataSet* createDataSet(size_t rows, size_t cols, float** data);

// uses memory of the original data to split dataset into batches
static DataSet** createBatches(DataSet* allData, int numBatches);

// split a dataset into row matrices
static Matrix** splitRows(DataSet* dataset);

// shuffle two datasets, maintaining alignment between their rows
static void shuffleTogether(DataSet* A, DataSet* B);

// destroy dataset
static void destroyDataSet(DataSet* dataset);

// convert dataset to matrix
static Matrix* dataSetToMatrix(DataSet* dataset);

// creates a matrix given data
static Matrix* createMatrix(size_t rows, size_t cols, float* data);

// creates a matrix zeroed out
static Matrix* createMatrixZeroes(size_t rows, size_t cols);

// get an element of a matrix
static float getMatrix(Matrix* mat, size_t row, size_t col);

// set an element of a matrix
static void setMatrix(Matrix* mat, size_t row, size_t col, float val);

// sets the values in $to equal to values in $from
static void copyValuesInto(Matrix* from, Matrix* to);

// prints the entries of a matrix
static void printMatrix(Matrix* input);

// sets each entry in matrix to 0
static void zeroMatrix(Matrix* orig);

// returns transpose of matrix
static Matrix* transpose(Matrix* orig);

// transposes matrix and places data into $origT
static void transposeInto(Matrix* orig, Matrix* origT);

// adds two matrices and returns result
static Matrix* add(Matrix* A, Matrix* b);

// adds $from to $to and places result in $to
static void addTo(Matrix* from, Matrix* to);

// adds $B, a row vector, to each row of $A
static Matrix* addToEachRow(Matrix* A, Matrix* B);

// multiplies every element of $orig by $C
static void scalarMultiply(Matrix* orig, float c);

// multiplies $A and $B (ordering: AB) and returns product matrix
static Matrix* multiply(Matrix* A, Matrix* B);

// multiplies $A and $B (ordering: AB) and places values into $into
static void multiplyInto(Matrix* A, Matrix* B, Matrix* into);

// element-wise multiplcation
static Matrix* hadamard(Matrix* A, Matrix* B);

// places values of hadamard product of $A and $B into $into
static void hadamardInto(Matrix* A, Matrix* B, Matrix* into);

// returns a shallow copy of input matrix
static Matrix* copy(Matrix* orig);

// returns a Matrix with the mean by row of A
static Matrix* meanByRow(Matrix* A);

// returns a Matrix with the mean by collumn of A
static Matrix* meanByCol(Matrix* A);

// returns a Matrix with the max element by row of A
static Matrix* maxByRow(Matrix* A);

// returns a Matrix with the max element by collumn of A
static Matrix* maxByCol(Matrix* A);

// returns a Matrix with the min element by row of A
static Matrix* minByRow(Matrix* A);

// returns a Matrix with the min element by collumn of A
static Matrix* minByCol(Matrix* A);

// return a Matrix with all elements >=0
static Matrix* matrixABS(Matrix *A);

// returns 1 if matrices are equal, 0 otherwise
static int equals(Matrix* A, Matrix* B);

// frees a matrix and its data
static void destroyMatrix(Matrix* matrix);


function.h

  • Métodos adicionados: nenhum
  • Testes de unidade: app/function_test.cc
    • make APPLICATION=function_test run

typedef void (*Activation)(Matrix*);

#define MAX(a,b) (((a)>(b))?(a):(b))

// raw sigmoid function
static float sigmoidFunc(float input);

// derivatve of sigmoid given output of sigmoid
static float sigmoidDeriv(float sigmoidInput);

// raw ReLU function
static float reluFunc(float input);

// derivative of ReLU given output of ReLU
static float reluDeriv(float reluInput);

// raw tanh function
static float tanHFunc(float input);

// derivatve of tanh given output of sigmoid
static float tanHDeriv(float sigmoidInput);

// applies sigmoid function to each entry of $input
static void sigmoid(Matrix* input);

// applies ReLU function to each entry of input
static void relu(Matrix* input);

// applues tanh function to each entry of input
static void tanH(Matrix* input);

// applies softmax function to each row of $input
static void softmax(Matrix* input);

// applies linear function to each row of $input
static void linear(Matrix* input);

// derivative of linear function given output of linear
static float linearDeriv(float linearInput);

// sample from the unit guassian distribution (mean = 0, variance = 1)
static float box_muller();

// return the string representation of activation function
static const char* getFunctionName(Activation func);

// return the activation function corresponding to a name
static Activation getFunctionByName(const char* name);

// return derivative of activation function
static float (*activationDerivative(Activation func))(float);


layer.h

  • Métodos adicionados: nenhum
  • Testes de unidade: app/layer_test.cc
    • make APPLICATION=layer_test run

// possible types of layers in a network
typedef enum LAYER_TYPE_ {
    INPUT,
    HIDDEN,
    OUTPUT
} LAYER_TYPE;

// input matrix will continue to store values
// even after activation occurs, so it is best
// to think of it simply as a store rather than
// strictly as input
typedef struct Layer_ {
    LAYER_TYPE type;
    size_t size;
    Activation activation;
    Matrix* input; // (num_examples x size)
} Layer;

// represents a link between two layers
typedef struct Connection_ {
    Layer* from;
    Layer* to;
    Matrix* weights; // (from_size x to_size)
    Matrix* bias; // (1 x to_size)
} Connection;

// returns layer given metadata and configuration
static Layer* createLayer(LAYER_TYPE type, size_t size, Activation activation);

// creates a connection and creates weights and bias matrices
static Connection* createConnection(Layer* from, Layer* to);

// initializes weights and biases within connection
static void initializeConnection(Connection* connection);

// applies activation function to each input in layer
static void activateLayer(Layer* layer);

// frees layer and its input
static void destroyLayer(Layer* layer);

// frees connection, its weights, and biases, but not its layers
static void destroyConnection(Connection* connection);


neural_network.h

  • Métodos adicionados: nenhum
  • Testes de unidade: em observância da ausência de leitura e escrita de arquivos de redes neurais, os testes relativos a estes foram retirados.
    • app/neural_network_test.cc
      • make APPLICATION=neural_network_test run

// represents a network as a composition of layers and connections
typedef struct NeuralNetwork_ {
    size_t numLayers;
    Layer** layers;
    size_t numConnections;
    Connection** connections;
} NeuralNetwork;

// constructor to create a network given sizes and functions
// hiddenSizes is an array of sizes, where hiddenSizes[i] is the size
// of the ith hidden layer
// hiddenActivations is an array of activation functions,
// where hiddenActivations[i] is the function of the ith hidden layer
static NeuralNetwork* createNetwork(size_t numFeatures, size_t numHiddenLayers, size_t* hiddenSizes, Activation* hiddenActivations, size_t numOutputs, Activation outputActivation);

// will propagate input through entire network
// result will be stored in input field of last layer
// input should be a matrix where each row is an input
static void forwardPass(NeuralNetwork* network, Matrix* input);

// will propagate input through entire network
// result will be stored in input field of last layer
// input should be a dataset where each row is an input
static void forwardPassDataSet(NeuralNetwork* network, DataSet* input);

// calculate the cross entropy loss between two datasets with 
// optional regularization (must provide network if using regularization)
// [normal cross entropy] + 1/2(regStrength)[normal l2 reg]
static float crossEntropyLoss(NeuralNetwork* network, Matrix* prediction, DataSet* actual, float regularizationStrength);

// calculate the mean squared error between two datasets with 
// optional regularization (must provide network if using regularization)
// 1/2[normal mse] + 1/2(regStrength)[normal l2 reg]
static float meanSquaredError(NeuralNetwork* network, Matrix* prediction, DataSet* actual, float regularizationStrength);

// return matrix of network output
static Matrix* getOuput(NeuralNetwork* network);

// returns indices corresponding to highest-probability classes for each
// example previously inputted
// assumes final output is in the output layer of network
static int* predict(NeuralNetwork* network);

// return accuracy (num_correct / num_total) of network on predictions
static float accuracy(NeuralNetwork* network, DataSet* data, DataSet* classes);

// frees network, its layers, and its connections
static void destroyNetwork(NeuralNetwork* network);

extra_math.h

  • Header utilizado para substituir as funções originais do math.h original do C++
  • Testes de unidade: app/extra_math_test.cc
    • make APPLICATION=extra_math_test run

#define FLT_MAX 1E+37 // https://www.tutorialspoint.com/c_standard_library/float_h.htm
#define FLT_MIN 1E-37
#define RAND_MAX 32767 // library-dependent, at least 32767 // http://www.cplusplus.com/reference/cstdlib/RAND_MAX/
#define pi 3.14159265359
#define halfpi 1.57079633
#define precision 100
#define fatorial_size 27
#define fatorial_precision 12

//a fatorial list of 26 elements to be used on "expf" by taylor series
float fatorial_list [] =  {...};

// Taylor series version of expf: 
static float expf(float x);

// Taylor series version of: x^n
static float pow_f_i(float x, int n);

// Adapted from: https://stackoverflow.com/questions/3581528/how-is-the-square-root-function-implemented
static float square_root(float x);

// Source: http://codingconnect.net/cpp-program-sine-series/
// Source: http://codingconnect.net/cpp-program-cosine-series/
static float sin(float x);
static float cos(float x);

// Source: https://stackoverflow.com/questions/29239343/faster-very-accurate-approximation-for-tanh
//		   http://www.maths.gla.ac.uk/wws/cabripages/hyperbolic/appendix.html
static float tanh(float x);

// Source: https://stackoverflow.com/questions/4768180/rand-implementation
static unsigned long int next = 1;
static int rand (void);
static void srand(unsigned long int seed);


Simulador


A seguir são apresentados um diagrama e os headers do simulador e que se encontram na pasta “include/Timeseries.”

Image

timeseries.h

  • É o include principal a ser utilizado no simulador do projeto.
    • include "Timeseries/timeseries.h"

#include "variables.h"
#include "server.h"
#include "normalize_timeseries.h"
#include "node.h"

variables.h

  • Testes de unidade: app/variables_test.cc
    • make APPLICATION=variables_test run

enum Variaveis {eletric_potential, pluviometric, pressure, temperature, wind_velocity, irradiance_global, irradiance_horizontal, irradiance_inclined_1, irradiance_inclined_2, irradiance_inclined_3, irradiance_difuse, irradiance_direct};

// return number of attributs inputs for a $var (variable) neural network
int getInputs(Variaveis var) 

// return the weights for the $var (variable) neural network
float* getWeights(Variaveis var)

// returns simulation timestamps for a $var (variable) 
float* getTimestamps(Variaveis var)

// returns simulation values for a $var (variable) 
float* getValues(Variaveis var)

// returns a string corresponding to the $var (variable) name 
char const* getName(Variaveis var)


node.h

  • Testes de unidade: app/node_tests.cc
    • make APPLICATION=node_tests run

Class Node{

	float *values;
	float *timestamps;
	int num_data;
	Matrix *inputs;
	Variaveis variable;
	Neighbors_Options simulation;
	NeuralNetwork *net;


// $var = variable type, $_num_data = select quantity of timeseries to be simulated 
	Node(Variaveis var, int _num_data, Neighbors_Options sim)  {  //initialize matrix with sense() data }

// sense all data for simulation
	void senseData()

// return all values to a neighbor node
	float* requestValues();

// return all timestamps to a neighbor node
	float* requestTimestamps();

// set in the inputs matrix $val and $time values (normalized according to its $var) in $collumn_start and  $collumn_start + 1
	void setToMatrix(int collumn_start, float *times, Variaveis var, float *vals)

// set $time and $val in inputs matrix according to the $var collumn position.
	void updateMatrix(Variaveis var, float *times, float *vals);

// requests values and timestamps from neighbor node
        void sendTo(Node *neighbor);

// estimates confidence for the node timeseries
	Matrix* estimateConfidence();

// print all simulation stats including confidence
	void printSimulation(Matrix *output);

};

normalize_timeseries.h

  • Testes de unidade: app/normalize_timeseries.cc
    • make APPLICATION=normalize_timeseries run

// normalize a microseconds $timestamp between 0 and 1 during a day with 86400000000 microseconds.
float normalizeTimeStamp(long long timestamp);

// normalize the given $value between 0 and 1, by the corresponding $min and $max variable values
float normalize(float value, float min, float max);

// normalize $value selecting the $var (variable) max and min values
float normalizeValue(Variaveis var, float value);

server.h

  • Testes de unidade: app/server_test,cc
    • make APPLICATION=server_test run

//returns a neural network for the corresponding $var (variable)
NeuralNetwork* requestNetwork(Variaveis var)

weight_data.h

  • Contém os pesos da rede neural de cada variável:

// variable neural network weights

float weights_eletric_potencial [701] = {...};
float weights_pluviometric [581] = {...};
float weights_temperature[741] = {...};
float weights_pressure [741] = {...};
float weights_wind_vel[501] = {...};
float weights_irr_global [781]  = {...};
float weights_irr_hor [861] = {...};
float weights_irr_inc_1 [861] = {...};
float weights_irr_inc_2 [541] = {...};
float weights_irr_inc_3 [581] = {...};
float weights_irr_dif[821] = {...};
float weights_irr_dir [861] = {...};


simulation_data.h

  • Contém as opções de simulação e os dados relativos a simulação, os quais por questões de contrato não podemos divulgar.
    • timestamps e values de cada variável.

// serial: for any quantity of num_data, random_five_minutes: for num_data > 5
enum neighbors_options {SERIA_TIME, RANDOM_FIVE_MINUTES};

// variables timestamps for simulation

float timestamp_eletric_potential[481] = {..}; 
float timestamp_pluviometric[481] = {..}; 
float timestamp_pressure[481] = {..}; 
float timestamp_temperature[481] = {..}; 
float timestamp_wind_vel[481] = {..}; 
float timestamp_irr_global[481] = {..}; 
float timestamp_irr_hor[481] = {..}; 
float timestamp_irr_inc_1[481] = {..}; 
float timestamp_irr_inc_2[481] = {..}; 
float timestamp_irr_inc_3[481] = {..}; 
float timestamp_irr_dif[481] = {..}; 
float timestamp_irr_dir[481] = {..};

// variables values for simulation

float value_eletric_potential[481] = {..}; 
float value_pluviometric[481] = {..}; 
float value_pressure[481] = {..}; 
float value_temperature[481] = {..}; 
float value_wind_vel[481] = {..}; 
float value_irr_global[481] = {..}; 
float value_irr_hor[481] = {..}; 
float value_irr_inc_1[481] = {..}; 
float value_irr_inc_2[481] = {..}; 
float value_irr_inc_3[481] = {..}; 
float value_irr_dif[481] = {..}; 
float value_irr_dir[481] = {..};




__

Bibliografia

  1. Lin, C.T and C.S George Lee (1996) Neural Fuzzy Systems: A Neuro-Fuzzy Synergism to Intelligent Systems Prentice Hall P T R, Upper Saddle River, NJ 07458, p. 1-9, 210, 244.
  2. Arbain, Siti & Wibowo, Antoni. (2012). Neural Networks Based Nonlinear Time Series Regression for Water Level Forecasting of Dungun River. Journal of Computer Science. 8. 1506-1513. 10.3844/jcssp.2012.1506.1513.
  3. Mirylenka, Katsiaryna & Dallachiesa, Michele & Palpanas, Themis. (2017). Data Series Similarity Using Correlation-Aware Measures. 1-12. 10.1145/3085504.3085515.
  4. Zaiyong Tang and Paul A. Fishwick. Feedforward Neural Nets as Models for Time Series Forecasting. ORSA Journal on Computing 19935:4 , 374-385.
  5. Cranium disponível em: https://github.com/100/Cranium
  6. MATLAB Feedforward neural network disponível em: https://www.mathworks.com/help/nnet/ref/feedforwardnet.html?s_tid=doc_ta
  7. Documentação do EPOS disponível em: http://epos.lisha.ufsc.br/EPOS+2+User+Guide
  8. Projeto Embedded Artificial Neural Networks disponível em: https://epos.lisha.ufsc.br/Embedded+Artificial+Neural+Networks
  9. Projeto AI for Embedded ANN Selection and Configuration disponível em: https://epos.lisha.ufsc.br/AI+for+Embedded+ANN+Selection+and+Configuration
  10. Dimension Reduction disponível em: https://en.wikipedia.org/wiki/Dimensionality_reduction
  11. Feature Selection disponível em: https://www.mathworks.com/help/stats/fsrnca.html
  12. Frois P.P, Nathan, “Simulação de evacuação de multidões usando redes neuro-fuzzy”, Trabalho de Conclusão de Curso, UFSC, (2017).
  13. Documentação do Cranium disponível em: https://100.github.io/Cranium/