Harbinger

Um framework para integração e análise de métodos de detecção de eventos em séries temporais

Rebecca Salles, Luciana Escobar, Lais Baroni, Roccio Zorrilla, Artur Ziviani, Vinicius Kreischer, Flavia C. Delicato, Paulo F. Pires, Luciano Maia, Rafaelli Coutinho, Laura Assis, Eduardo Ogasawara

CEFET/RJ – Centro Federal de Educação Tecnológica Celso Suckow da Fonseca
LNCC – Laboratório Nacional de Computação Científica
UFF – Universidade Federal Fluminense
Petrobras

Emails:
{rebecca.salles,luciana.vignoli,lais.baroni}@eic.cefet-rj.br
{ziviani,roccio.zorrilla,vinicius.kreischer}@lncc.br
{fdelicato,paulo.pires}@ic.uff.br, luciano.maia@petrobras.com.br
{rafaelli.coutinho,laura.assis}@cefet-rj.br, eogasawara@ieee.org

Resumo:
Ao analisar séries temporais é possível observar mudanças significativas no comportamento das observações que frequentemente caracterizam a ocorrência de eventos. Eventos se apresentam como anomalias, pontos de mudança, ou padrões frequentes. Na literatura existem diversos métodos para detecção de eventos. Entretanto, a busca por um método adequado para uma série temporal não é uma tarefa simples, principalmente considerando-se que a natureza dos eventos muitas vezes não é conhecida. Neste contexto, este trabalho apresenta Harbinger, um framework para integração e análise de métodos de detecção de eventos. O Harbinger foi avaliado em dados sintéticos e reais, onde foi possível constatar que suas funcionalidades auxiliam na escolha dos métodos e na compreensão dos eventos detectados.

Apresentação para o SBBD 2020:

Estrutura geral de módulos

A estrutura geral do Harbinger é ilustrada na Fig. 1, sendo formada por quatro principais módulos de funcionalidade: detecção de eventos (em azul), avaliação de qualidade de detecções (em verde), combinação de resultados de detecção (em laranja), e comparação de desempenhos de detecção (em roxo). A descrição dos parâmetros de execução do Harbinger, assim como de seus artefatos produzidos é apresentada na Fig. 2. Mais informações podem ser encontradas no artigo dedicado.

Fig. 1: Diagrama geral dos módulos

diagrama_harbinger

Fig. 2: Parâmetros de execução e artefatos produzidos

Disponibilidade

O Harbinger está disponível no Github e pode ser baixado e carregado ao ambiente R com o código a seguir:

Exemplos de utilização dos métodos de detecção de eventos implementados no framework estão disponíveis em:

Utilização

Para exemplificar a utilização dos módulos do framework Harbinger vamos adotar os seguintes conjuntos de dados:

Conjuntos de dados

Série Temporal Sintética Não Estacionária (NE)

Para evidenciar as mudanças de comportamento de uma série temporal, foi desenvolvida uma série temporal sintética y com propriedades não estacionárias (NE) produzida a partir do trabalho de Salles et al. [2019]. Esta série é composta por 1000 observações (yi, i=1,…,1000) onde a subsequência <y1,…,y200> representa uma série estacionária. As demais subsequências ilustram diversas formas de não estacionariedade. Em <y201,…,y400> observa-se a estacionariedade de tendência, em <y401,…,y600> a estacionariedade de nível, em <y601,…,y800> a heteroscedasticidade, e em <y801,…,y1000> a estacionariedade por diferenciação.

Esta série temporal é construída utilizando-se o seguinte código:

> source("https://raw.githubusercontent.com/cefet-rj-dal/harbinger/master/nonstationarity_sym.r")
> nonstat_ts <- nonstationarity_sym(ts.len=200,ts.mean=0,ts.var=1)
> plot(ts(nonstat_ts),type="l",xlab="time")
Conjunto de Dados GECCO Challenge (GECCO)

Foi também utilizado para análise o conjunto de dados criado para o GECCO Challenge 2018 [Rehbach et al., 2018]. Para compor este conjunto, foram se-lecionadas 1500 observações coletadas a cada minuto contendo 72 eventos identificados. Foram coletadas 9 variáveis relacionadas à qualidade da água, das quais 5 foram selecionadas por apresentar maior variabilidade em suas observações. São elas Tp, pH, Redox, Leit e Trueb, representando a temperatura, PH, potencial Redox, condutividade elétrica e turvação, respectivamente.

Este conjunto de dados é disponibilizado pelo pacote-R EventDetectR e pode ser utilizado com o seguinte código:

> library(EventDetectR)
> gecco <- geccoIC2018Train[16500:18000,]
> plot(ts(gecco$Redox),type="l",xlab="time")

1. Módulos de detecção e combinação

A funcionalidade de detecção de eventos é responsabilidade da função chamada evtdet.

Esta função recebe como entrada os parâmetros:

  • Y: um data.frame contendo uma ou mais variáveis (séries temporais) onde a primeira variável se refere ao tempo (data ou índice sequencial).
  • ƒ: função para detecção de eventos tendo Y como entrada e um data.frame com três variáveis (time (instante/ índice de eventos), serie (nome da série correspondente) e type (tipo do evento)) como saída.
  • par: lista de parâmetros para ƒ.

A saída da função é:

  • um data.frame com três variáveis: time (instante/ índice de eventos), serie (nome da série correspondente) e type (tipo do evento).

Deste modo, a implementação da função evtdet segue o seguinte padrão:

evtdet <- function(data,func,...){
events <- do.call(func,c(list(data),list(...)))
return(events) }

Principais métodos de detecção pré-implementados:

O framework permite ao usuário a definição de métodos de detecção de eventos personalizados. Mesmo assim, Harbinger também conta com a implementação de diversos métodos de detecção de eventos. Os principais métodos implementados são descritos em seu artigo dedicado [].

Fixando as implementações dos métodos mencionados como o parâmetro ƒ da função evtdet definimos funções auxiliares para detecção de eventos. Alguns exemplos são mostrados a seguir:

  • Método AN: evtdet.an_outliers(Y,…)
  • Método CF: evtdet.changepoints_v3(Y,…)
  • Método GARCH: evtdet.garch_volatility_outlier(Y,…)
  • Método SCP: evtdet.changepoints_v1(Y,…)
Exemplos de uso - Detecção de Eventos

Adaptando dados do conjunto NE para estrutura padrão do Harbinger:

> test <- data.frame(time=1:length(nonstat_ts), x=nonstat_ts)

Detecção com o método AN:

#====== Adaptive Normalization Outliers ======
#Detect
> events_an <- evtdet.an_outliers(test,w=20,alpha=1.5)
#Plot
> print(evtplot(test,events_an,reference))

Detecção com o método CF:

#====== ChangeFinder (2005) ======
#Auxiliary model definition
ARIMA <- function(data) forecast::auto.arima(data)
#Detect
> events_cf <- evtdet.changeFinder(test,mdl=ARIMA,m=5)
#Plot
> print(evtplot(test,events_cf, reference, mark.cp=TRUE))

Detecção com o método GARCH:

#====== Garch Volatility Outliers ======
#Garch specs
> garch11 <-
rugarch::ugarchspec(
    variance.model = list(model = "sGARCH", garchOrder = c(1, 1)),        mean.model = list(armaOrder = c(1, 1), include.mean = TRUE),          distribution.model = "norm")
#Detect
> events_garch <-evtdet.garch_volatility_outlier(test,spec=garch11,alpha=1.5)
#Plot
> print(evtplot(test,events_garch, reference))
Exemplo de uso - Combinação de Métodos de Detecção de Eventos

Detecção de eventos a partir da combinação dos métodos AN, CF e GARCH:

#====== Combining detected events ======
#Combining detections
> an_cf_garch <- rbind(events_an, events_cf, events_garch)
#Plot
> print( evtplot( test, an_cf_garch, mark.cp=TRUE ) )

2. Módulos de avaliação e comparação

Uma vez tendo detectado eventos em uma série temporal, é necessário avaliar a qualidade desta detecção e o desempenho do método escolhido. Esta avaliação é de responsabilidade da função evaluate.

Esta função recebe como entrada os parâmetros:

  • E: retorno da função evtdet relativo a uma série temporal particular.
  • g: função para cálculo de métrica de qualidade da detecção de eventos.
  • E_ref: data.frame com o mesmo comprimento de X contendo duas variáveis: time (tempo (data ou índice sequencial)) e event (valores booleanos indicativos da real ocorrência de eventos em X)

A saída da função é:

  • Valor da métrica de qualidade calculada.

Principais métricas de qualidade pré-implementadas:

Com o objetivo de facilitar o processo de avaliação, Harbinger conta com a implementação das principais métricas utilizadas para se avaliar a qualidade da detecção de eventos. Deste modo, a chamada à função evaluate segue o seguinte padrão:

> evaluate(E, E_ref, metric="confusion_matrix")

Neste caso, para se defnir o parâmetro g basta especificar o nome da métrica dentre as seguintes opções:

  • Matriz de confusão: “confusion_matrix”
  • Sensitividade: “sensitivity”
  • Especificidade: “specificity”
  • Valor preditivo positivo: “pos_pred_value”
  • Valor preditivo negativo: “neg_pred_value”
  • Precisão: “precision”
  • Revocação: “recall”
  • F1: “F1”
  • Prevalência: “prevalence”
  • Taxa de detecção: “detection_rate”
  • Prevalência de detecção: “detection_prevalence”
  • Acurácia equilibrada: “balanced_accuracy”
Exemplos de uso - Avaliação da Detecção de Eventos

Adaptando dados do conjunto GECCO para estrutura padrão do Harbinger:

> test <- subset(gecco, select=c(Time, Trueb))
> reference <- subset(train, select=c(Time, EVENT))

Detecção com o método SCP:

#====== Seminal Change Point (1999) ====== 
#Detect 
> events_scp <- evtdet.seminalChangePoint(test,w=100)

Avaliação por matriz de confusão:

> evaluate(events_scp, reference, metric="confusion_matrix")

Reference
Prediction FALSE TRUE
FALSE 1238 0
TRUE 191 72

Avaliação por F1:

> evaluate(events_scp, reference, metric="F1")

F1
0.928384

Avaliação por precisão:

> evaluate(events_scp, reference, metric="precision")

Precision
1

Avaliação por revocação:

> evaluate(events_scp, reference, metric="recall")

  Recall
0.8663401
Exemplo de uso - Avaliação Visual de Eventos

A avaliação visual dos eventos detectados é de responsabilidade da função evtplot.

Esta função recebe como entrada os parâmetros:

  • Y: mesmo utlizado em evtdet.
  • E: retorno da função evtdet.
  • E_ref: mesmo utilizado em evaluate.

A saída da função é:

  • Gráfico com as observações da série temporal, assim como marcações referentes aos resultados da detecção de eventos. Marcações vermelhas indicam eventos detectados. Além disso, são marcados em azul os eventos reais em E_ref, e em verde a coincidência entre o evento detectado e o evento real.
Um exemplo de sua utilização e saída é apresentado a seguir:
#Plot 
> print(evtplot(test,events_scp, reference))
Exemplo de uso - Comparação de Métodos de Detecção de Eventos

Podemos comparar o desempenho de detecção de diversos métodos de maneira a avaliar a sua adequabilidade aos dados assim como observar oportunidades de combinação de métodos:

#Detect
> events_an <- evtdet.an_outliers(test,w=100,alpha=1.5)
> events_cf <- evtdet.changeFinder(test,mdl=ARIMA,m=5)
> events_garch <- evtdet.garch_volatility_outlier(test,spec=garch11,alpha=1.5)
> events_scp <- evtdet.seminalChangePoint(test, w=50)
#Evaluate
> sapply(list(events_an,events_cf,events_garch,events_scp),
evaluate, reference, metric="F1")

F1 F1 F1 F1
0.9488783 0.9434365 0.9077491 0.9283840

Métodos de detecção personalizados

O framework permite ao usuário a definição de métodos de detecção de eventos personalizados.

Para isso, basta implementar o método em uma função (ƒ) para detecção de eventos tendo Y como entrada e um data.frame com três variáveis (time (instante/ índice de eventos), serie (nome da série correspondente) e type (tipo do evento)) como saída.

Um exemplo da implementação de tal função é dado a seguir. O código define uma função que identifica outliers nas observações de uma série temporal com base no primeiro e terceiro quartis e a distância interquartil:

> outliers <- function(data, alpha=1.5){

serie_name <- names(data)[-1]
names(data) <- c("time","serie")
serie <- data$serie

#===== Boxplot analysis of data ======
outliers.index <- function(data, alpha = 1.5){
org = length(data)
if (org >= 30) {
q = quantile(data)
IQR = q[4] - q[2]
lq1 = q[2] - alpha*IQR
hq3 = q[4] + alpha*IQR
cond = data < lq1 | data > hq3
index.out = which(cond)#data[cond,]
}
return (index.out)
}

#Returns index of outlier observations
index.out <- outliers.index(serie,alpha)

anomalies <- cbind.data.frame(time=data[index.out,"time"],
serie=serie_name,
type="anomaly")
names(anomalies) <- c("time","serie","type")

return(anomalies)
}

Uma vez implementado, o método pode ser integrado ao Harbinger da seguinte maneira:

> events <- evtdet(data, outliers, alpha=1.5)

Outra alternativa é criar uma função wrapper do método implementado como feito a seguir:

> evtdet.outliers <- function(data,...){

events <- evtdet(data, outlier , ...)

return(events)
}

Integração com sistemas de gerenciamento do ciclo de vida de modelos

No contexto de aprendizado de máquina, frequentemente usuários precisam lidar com o fardo de gerenciar um grande conjunto de artefatos produzidos durante o ciclo de vida de um modelo, como configurações de algoritmos, dados de entrada/saída e arquivos dos experimentos. Com isso, recentemente foram desenvolvidos sistemas para o gerenciamento do ciclo de vida dos modelos, como o ModelDB, Mistique e MLflow [Silva et al., 2019]. Harbinger se beneficia de tais sistemas para o gerenciamento de suas instâncias e registro de parâmetros de execução, métricas e artefatos produzidos. Este gerenciamento garante a proveniência e a reprodutibilidade das detecções de eventos. Além disso, o registro das aplicações de detecção e suas métricas computadas auxiliam a consulta e análise comparativa de desempenho dos métodos de detecção aplicados. Com esse objetivo, Harbinger se integra ao sistema MLflow [Zaharia et al., 2019] através da API de seu componente Tracking. As estruturas de dados dos parâmetros de execução e artefatos produzidos pelo Harbinger são uniformizadas com o objetivo de sistematizar o processo de execução de diferentes métodos, assim como facilitar o seu registro e busca no sistema.

Exemplo de uso - Integração com o MLFlow

Iremos utilizar a MLflow Tracking API para gerenciar as execuções de instâncias do Harbinger e registrar parâmetros, métricas, e artefatos (arquivos).

Para isso é preciso importar a API e executar a script de instalação com o seguinte código:

library(mlflow)
install_mlflow()

Com o código a seguir podemos detectar eventos utilizando o método AN e avaliar a qualidade da sua detecção. A execução do método e o registro de seus parâmetros, métricas de qualidade e artefatos podem ser feitos da seguinte forma:

#====== MLFlow run - Event detection ======

#Start run
> mlflow_start_run()

#Detect with Adaptive Normalization Outliers
> events_an <- evtdet.an_outliers(test, w=20, alpha=1.5)

#Log parameters
> mlflow_log_param("w", 20)
> mlflow_log_param("alpha", 1.5)

#Evaluate
> confusion_matrix <- evaluate(events_an, reference, metric="confusion_matrix")
> sensitivity <- evaluate(events_an, reference, metric="sensitivity")
> specificity <- evaluate(events_an, reference, metric="specificity")

#Log metrics
> mlflow_log_metric("sensitivity", sensitivity)
> mlflow_log_metric("specificity", specificity)

#Log artefacts
> write(confusion_matrix, file = "confusion_matrix.txt")
> mlflow_log_artifact("confusion_matrix.txt")
> write.csv(events_a, file = "events_a.csv")
> mlflow_log_artifact("events_a.csv")

#End run
> mlflow_end_run()