A importância do entendimento de features no modelo de negócio para a construção de modelos preditivos

Como avaliar o seu modelo através da interpretação das features

Ricardo Pinto
Data Hackers
11 min readMay 7, 2020

--

Foto por Carlos Muza no Unsplash

Para o projeto de Machine Learning do curso NEXT da CESAR School o objetivo era procurar bases de dados públicas locais e tentar construir um modelo preditivo.

Assim sendo, foi na prefeitura local de Recife, que ainda é das poucas (ou única) na região que disponibiliza algum tipo de dados públicos online que foram encontradas bases de dados suficientes para levantar uma hipótese de modelo preditivo.

Hipótese

Entre as informações presentes nas bases de dados estavam a localização (geolocalização e/ou bairro), ocorrência de deslizamento, grau de risco do local, existência de vítimas, a data de ocorrência, e presença de lona. Cruzando os dados de data e localização acrescentando a informação da presença ou não de chuva, seria possível construir um modelo preditivo.

Preparação

Existem 5 bases de dados públicas sobre este tema:

1. Chamados (callings no repositório) –É a única base com dados de latitude e longitude, e informações sobre a existência e o tipo de vítimas. Exclui-se a data por existirem bases com este dado mais completo, inclui-se os dados de geolocalização e as features relativas às vítimas.

2. Lonas (canvas no repositório) — informações sobre as lonas, quando e se foi colocado, assim como suas respetivas características.

3. Ocorrências (events no repositório) — apenas contém o tipo de ocorrência, atribuindo-se o valor 1 caso tenha existido um deslizamento e 0 para todos os outros tipos.

4. Inspeções (inspections no repositório) — contém dados burocráticos como a data de avaliação e o nome do avaliador, e a informação do grau de risco. Apenas esse último foi selecionado e definido inicialmente como sendo de 0 a 4, onde 0 representa a ausência de avaliação e 4 um risco muito alto (outras formas de variação desta escala foram escolhidas ao longo dos testes do modelo).

5. Solicitações (solicitations no repositório) — é a única base sem linhas vazias na data, logo a selecionada para avançar com esta feature. O bairro foi mantido caso se fizesse necessário para complementar as linhas vazias nas features de geolocalização. Foi preciso ainda filtrar a descrição dos chamados para isolar as linhas que continham informação sobre a ocorrência de um deslizamento. Ainda nesta base existe uma feature relativa à situação do processo, que poderia ser interpretado como mero dado burocrático (arquivo concluído) ou como se todas as etapas estivessem concluídas neste local (inspeção, colocação ou não de lona, etc.). Dessa forma, foi optado por manter esta feature caso a segunda opção fosse verdadeira.

Até à data de hoje não conseguimos qualquer resposta dos órgãos responsáveis sobre esta indefinição.

Como todas as bases possuem um valor único (processo_ocorrencia) renomeou-se para ID e fez-se a união destas.

Finalmente, para a base de dados da chuva foi selecionada a Agência Pernambucana de Águas e Clima(APAC) , filtrado a partir do ano disponível nas outras bases (2012), nos postos de medição do Recife e usado um conversor de html para csv devido ao erro de exportação dos dados.

Para finalizar a preparação fez-se corresponder para cada linha na respetiva data o posto de medição mais próximo e consequentemente o valor respetivo da chuva. Na ausência da localização foi utilizada a média da chuva em todos os postos de medição naquele dia.

O restante da preparação foi um trabalho mais operacional. Para consultar as escolhas e decisões basta acessar o repositório onde existe uma descrição mais detalhada do passo a passo.

Modelagem

Cada elemento do grupo deste projeto escolheu uma série de modelos para testar. Como se trata de um problema de classificação (ocorreu ou não deslizamento) foi dada prioridade para modelos classificatórios.

Embora, como para o problema em questão ter a probabilidade do evento (deslizamento ocorrer) também poderia ser uma solução, foram testados modelos de regressão como o Random Forest Regressor (RFR) com o objetivo de estabelecer uma comparação com os modelos classificatórios.

Assim sendo a partir deste ponto irei apenas descrever os modelos testados por mim.

Inicialmente, foram escolhidos quatro modelos, um mais simples, o Nayve Bayes(NB); o Random Forest Classifier (RFC) que pela sua natureza poderia ser um modelo que pudesse apresentar bons resultados com baixos recursos; o Random Forest Regressor (RFR) para termo de comparação entre modelo classificatório e de regressão; e finalmente, um modelo mais complexo, o Support Vector Machine(SVM) para comparar com os anteriores e avaliar se o ganho justificava o avanço para algo mais complexo.

Antes de avançar com a descrição de cada um, para comparar os modelos entre si, foram escolhidos como parâmetros o score do modelo, a precisão e a acurácia com cross validation. Lembrando que no problema um falso positivo não é necessariamente prejudicial, já que a probabilidade de ocorrência é muita mais significativa para a implementação de um modelo desta natureza.

Além disso, para validar o modelo foi usada a curva ROC e uma análise das features.

  1. Nayve Bayes
model.score(X,y)0.8737671724978773

Score: 0,874

from sklearn.model_selection import cross_val_scorescores = cross_val_score(model, X, y, cv=10)array([0.85894645, 0.94883518, 0.82603963, 0.89244503, 0.80992815,0.99368604, 0.85586762, 0.74461137, 0.95210102, 0.85521446])Accuracy: 0.87 (+/- 0.14)

Acurácia: 0,87

scores_precision = cross_val_score(model, X, y, cv=10, scoring='precision')scores_precisionarray([0.98984199, 0.997669  , 0.99860918, 0.99805068, 0.9953775 ,0.99932841, 0.99651163, 0.99421965, 1.        , 0.99649533])Precision: 1.00 (+/- 0.01)

Precisão: 1,0

Área curva ROC = 0,81

2. Random Forest Classifier (RFC)

Grid search entre profundidade e número de estimadores:

Matriz confusão

model.score(X,y)0.8745945004463217

Score: 0,875

from sklearn.model_selection import cross_val_scorescores = cross_val_score(model, X, y, cv=10)scoresarray([0.8602525 , 0.94927063, 0.82516874, 0.89244503, 0.80949271,0.99368604, 0.85586762, 0.7439582 , 0.95318964, 0.85586762])Accuracy: 0.87 (+/- 0.14)

Acurácia: 0,87

scores_precision = cross_val_score(model, X, y, cv=10, scoring='precision')scores_precisionarray([1.        , 0.99922179, 1.        , 1.        , 1.        ,0.99932841, 1.        , 1.        , 0.97445255, 1.        ])Precision: 1.00 (+/- 0.02)

Precisão: 1,0

Área curva ROC = 0,81

3. Random Forest Regressor (RFR)

Grid search entre profundidade e número de estimadores:

Matriz confusão

model.score(X,y)0.5490548320390607

Score: 0,54

from sklearn.model_selection import cross_val_scorescores = cross_val_score(model, X, y, cv=10)scoresarray([0.48430083, 0.76511753, 0.42599659, 0.58557216, 0.27411691,0.91711221, 0.46720816, 0.07514316, 0.78324268, 0.48640883])Accuracy: 0.53 (+/- 0.47)

Acurácia: 0,53

Área curva ROC = 0,82

4. Support Vector Machine (SVM)

Matriz Confusão

model.score(X,y)0.8737236289216433

Score: 0,873

from sklearn.model_selection import cross_val_scoreX1 = StandardScaler().fit_transform(X)scores = cross_val_score(model, X1, y, cv=4)scoresarray([0.9002003 , 0.83384133, 0.88173822, 0.87841839])Accuracy: 0.87 (+/- 0.05)

Acurácia: 0,87

scores_precision = cross_val_score(model, X1, y, cv=4, scoring='precision')scores_precisionarray([0.99587397, 1.        , 1.        , 0.99958246])Precision: 1.00 (+/- 0.00)

Precisão: 1,0

Área curva ROC = 0,81

O RFC, NB e o SVM apresentaram scores e acurácias acima de 0,85, e áreas da curva ROC acima de 0,80, o que indica a possibilidade de construção de um modelo preditivo com estes dados.

Validação

Todos os modelos, exceto o RFR, apresentaram resultados similares então a validação do(s) nosso(s) modelo(s) foi realizada avaliando o peso de cada feature. O SVM e o RFC já possuem funções da própria biblioteca que permitem esta avaliação por isso eles foram escolhidos para essa análise.

RFC

importances = model.feature_importances_for i in range(X.shape[1]):print( X.columns[i],"%.4f" % importances[i])situation 0.8157location 0.0143risk 0.0400victims 0.0006deadly_victims 0.0002length 0.0197num_points 0.0146in_place 0.0594Rain 0.0355

SVM

importances = model.coef_for i in range(0,9):print( X.columns[i],"%.4f" % importances[0,i])situation 0.8043location 0.0000risk -0.0001victims -0.0000deadly_victims 0.0256length 0.0000num_points -0.0000in_place 0.0001Rain 0.0000

Como se pode verificar a feature “situation”, que avalia a conclusão do processo, assume um peso de aproximadamente 80% em ambos os modelos, enquanto que features teoricamente mais importantes, como a chuva ou o grau de risco, não chegam nem a 10%. Assim sendo, conhecendo o nosso problema este não é um modelo válido.

Pode uma feature teoricamente burocrática ser a que maioritariamente faz a predição de um deslizamento?

O próximo passo foi avaliar o modelo sem esta feature:

A partir deste ponto os testes se restringiram ao RFC, já que é mais leve que o SVM e permite uma análise de features mais direta que o NB, além de apresentar resultados idênticos aos demais.

Para os testes todo o algoritmo foi mantido.

Sem a feature “situation” o resultado foi o seguinte:

Área curva ROC = 0,51

Features:

location 0.0602risk 0.1969victims 0.0008deadly_victims 0.0015length 0.2457num_points 0.1381in_place 0.2591Rain 0.0977

Apesar da distribuição das features ter uma representação mais realista do problema, o modelo, como se pode ver pela curva ROC, é puramente randómico. De modo a confirmar esta informação foi executado o NB sem a feature “situation” e a conclusão foi a mesma (área da curva ROC de 0,56).

Então se o modelo com essa feature não se encaixa no problema e sem ela não existe modelo, o que fazer?

A partir deste ponto, foram testadas várias hipóteses. Para não entrar em detalhes sobre cada uma, segue uma lista com todas e o desenvolvimento de algumas:

1- A avaliação do risco contém valores nulos (0), isto é, não foi feita avaliação. Então o primeiro passo foi multiplicar os valores de 1 a 4 para receberem um peso maior e assim diminuir a representatividade das linhas com valor 0 para o risco. Testando-se com e sem a feature “situation”.

2- Elimina-se as linhas com risco 0, testando-se com e sem a feature “situation”.

Esta hipótese com a feature foi aquela que minimizou o peso da feature “situation”:

situation 0.6677location 0.0876risk 0.0324victims 0.0007deadly_victims 0.0001length 0.0501num_points 0.0011in_place 0.0029Rain 0.1574

Mesmo assim, um peso de 67% é demasiado elevado para o problema em questão.

Até então nenhuma tentativa mudou os resultados anteriores, embora o modelo sem a feature “situation” e sem as linhas com risco 0 obteve os seguintes resultados:

location 0.2973risk 0.0948victims 0.0082deadly_victims 0.0010length 0.1178num_points 0.0205in_place 0.0151Rain 0.4452

Demostrando assim a distribuição de peso entre features mais realista, e com um ligeiro ganho sobre o modelo randómico. Assim sendo, os próximos testes foram realizados em cima destas condições.

3- As features relativas às vítimas contêm muito poucas entradas, 80 e 7 nas mortais e não mortais respetivamente, em aproximadamente 46mil entradas totais, sendo assim removeram-se estas.

4- Removaram-se as features relativas às características das lonas, mantendo-se apenas a relativa à existência ou não de lona.

5- Cada localização (bairro) foi transformada numa feature.

Este último, foi o teste que melhor resultado obteve, embora apenas com os seguintes resultados:

risk 0.1206victims 0.0038deadly_victims 0.0006length 0.0651num_points 0.0158in_place 0.0119Rain 0.5166

As demais features de localização oscilaram entre 0 e 5%.

6- Eliminaram-se as localizações (bairros) com menos de 10 entradas.

Como nenhuma das hipóteses surtiu efeito, decidiu-se correr uma análise T-distributed stochastic neighbor embedding(t-SNE) para tentar identificar se existia algum cluster de dados que estivesse interferindo no nosso modelo, executando-se essa análise com e sem a feature “situation”.

t-SNE com a feature “situation”

t-SNE sem a feature “situation”

Pode-se observar alguns clusters pequenos mas nada que mude a realidade destes dados.

Finalmente foi analisada a matriz correlação para tentar identificar alguma hipótese não explorada até então:

Como se pode verificar, apenas as features relativas às lonas e suas características, e os deslizamentos e a conclusão do processo, apresentam correlação entre si.

Conclusão

Pouco se conhece sobre a procedência dos dados (eles apenas estão disponibilizados online, onde pouco ou nada é dito sobre eles), então foi necessário fazer algumas suposições para tentar modelar o problema:

1- A data associada ao número do processo foi a data em que ocorreu o deslizamento:

Isso permitiria associar um deslizamento à quantidade de chuva naquele dia. Logicamente que associar um deslizamento a uma quantidade diferente da que levou ao acontecimento afeta e muito qualquer modelo preditivo. Em particular numa região como a do Recife que ocorrem chuvas de elevada intensidade num curto período de tempo.

Para avaliar a forma como a data era preenchida nas bases realizou-se um teste que mostrou que a data da base de dados da solicitação era posterior à data de colocação da lona, o que automaticamente levantou suspeitas sobre a veracidade deste dado.

2- Os dados foram bem coletados:

Não existe nenhuma informação de como estes dados foram coletados muito menos preenchidos, e até hoje não obtivemos qualquer resposta sobre os questionamentos feitos aos respectivos órgãos.

Assim sendo, ficou claro que se a feature “situation” representa todas as etapas concluídas neste local (inspeção, colocação ou não de lona, etc.) talvez um modelo pudesse ser cogitado.

Para tentar de alguma forma esclarecer esta dúvida, um questionamento foi feito: Quantos vezes ocorreu um deslizamento quando a feature “situation” sinalizava o processo concluído (ou seja o seu valor era igual a 1)?

sliding = sliding[sliding.situation!=0]sliding.info()<class 'pandas.core.frame.DataFrame'>Int64Index: 9357 entries, 0 to 45930Data columns (total 10 columns):situation         9357 non-null int64location          9357 non-null int64risk              9357 non-null int64victims           9357 non-null int64deadly_victims    9357 non-null int64length            9357 non-null int64num_points        9357 non-null int64in_place          9357 non-null int64sliding           9357 non-null int64Rain              9357 non-null float64dtypes: float64(1), int64(9)memory usage: 804.1 KBsliding.sliding.value_counts()1    9357Name: sliding, dtype: int64

A resposta é todas! Então a suspeita que se trata de um dado burocrático aumenta.

Finalmente, o único teste pensado mas não executado, foi transformar a localização em regiões norte, sul, este, oeste (por exemplo). Porém os dados apontem por esta não ser uma solução.

Portanto a conclusão é que depois da interpretação das features não temos um modelo preditivo compatível com estes dados.

--

--

Ricardo Pinto
Data Hackers

Data Scientist with a civil engineering background. Water polo player. Loves ML/AI, data, decision science, gaming, manga.