Modelo para Classificação de Saúde de Fetos
- Vinicius Goia
- 12 de set. de 2023
- 8 min de leitura
Se você está com pressa!!!
O principal objetivo deste estudo é testar a ferramenta Pycaret para um fluxo de trabalho. Nele, você encontrará uma análise de exploração de dados de um dataset e o workflow da ferramenta, com conclusões sobre a praticidade do uso de Auto Machine Learning para um problema de classificação.
Contextualizando...
Resumidamente, Auto Machine Learning é uma técnica auxiliada por ferramentas que tem o intuito de otimizar a preparação dos dados para aplicação de Machine Learning, além de conseguir realizar de forma automática validações cruzadas de diversos algoritmos. Dessa maneira, o cientista de dados centraliza seus esforços nas análises exploratórias e na escolha de parâmetros para melhor desempenho.
A ferramenta que utilizaremos aqui é o Pycaret, que, segundo o próprio site, é uma biblioteca simples, fácil de aprender e low code. Basicamente é dividida em 4 módulos: Machine Learning Supervisionado, Machine Learning Não Supervisionado, Séries Temporais (Beta) e Datasets.
Para esse estudo, utilizaremos o Pycaret para a Classificação de Saúde de Fetos, ou seja, ciência de dados e Machine Learning na área da medicina.
Prevendo esses valores, tem-se uma maior agilidade e acurácia no diagnóstico de qualquer problema com o feto, auxiliando médicos a tomarem decisões referentes as tratativas do problema.
Iniciaremos nossos estudos importando as bibliotecas necessárias e salvando o dataset em uma variável. Também veremos as primeiras entradas da tabela.
Os dados podem ser encontrados nesse link.
Obs: As saídas dos comandos em Python referentes à textos e tabelas não serão exibidos. Apenas gráficos, vídeos e outras imagens serão mostradas.
# Importação de bibliotecas
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
# Salvando o dataset em uma variável
df = pd.read_csv("https://raw.githubusercontent.com/vinigoia/MODELO-PARA-CLASSIFICACAO-DE-SAUDE-FETAL/main/fetal_health.csv")
# Visualização das primeiras entradas
df.head()
Sobre os Dados
A redução da mortalidade infantil está presente em diversos objetivos do Programa de Desenvolvimento Sustentável das Nações Unidas e é um índice de progresso humano. Espera-se que até 2030, países consigam reduzir as mortes de crianças de até 5 anos para, pelo menos, 25 a cada 1000 nascidos vivos.
Paralelo a isso, as mortes das mães atingem um número alto, chegando a 295000 óbitos durante a gravidez e no momento do nascimento (dados do ano de 2017). 94% dessas mortes ocorreram em locais com poucos recursos e a maioria poderia ser evitada.
Os dados utilizados neste projeto foram originalmente disponibilizados no Kaggle.
Consistem, basicamente, em dados extraídos de exames de cardiotocografia e classificados por 3 obstetras especialistas. Os equipamentos utilizados para exame são simples e de baixo custo, e funcionam através do envio de pulsos de ultrassom, possibilitando a leitura dos batimentos cardíacos dos fetos, movimentos, contrações, etc.
Dicionário de Variáveis
Sendo assim, as variáveis disponíveis no dataset são:
baseline value: frequência cardíaca fetal basal;
accelerations: aceleração por segundo;
fetal_movement: número de movimentos do feto por segundo;
uterine_contractions: número de contrações uterinas por segundo;
light_decelerations: número de desaceleração leve por segundo;
severe_decelerations: número de desaceleração severa por segundo;
prolongued_decelerations: número de desaceleração prolongada por segundo;
abnormal_short_term_variability: porcentagem de tempo com variabilidade anormal de curto prazo;
mean_value_of_short_term_variability: valor médio da variabilidade de curto prazo;
percentage_of_time_with_abnormal_long_term_variability: porcentagem de tempo com variabilidade anormal de longo prazo;
mean_value_of_long_term_variability: valor médio da variabilidade de longo prazo;
histogram_width: largura do histograma feito usando todos os valores de um registro;
histogram_min: valores mínimos do histograma;
histogram_max: valores máximos do histograma;
histogram_number_of_peaks: número de picos no histograma do exame;
histogram_number_of_zeroes: número de zeros no histograma do exame;
histogram_mode: modo do histograma;
histogram_mean: média do histograma;
histogram_median: mediana do histograma;
histogram_variance: variância do histograma;
histogram_tendency: tendência do histograma;
fetal_health: saúde fetal, sendo 1 para Normal, 2 para Suspeito e 3 para Patológico.
Análise Exploratória
O primeiro contato
Antes de começar qualquer análise, iremos utilizar alguns métodos para verificação de informações básicas do dataset, como valores médios, valores ausentes, e os tipos de variáveis presentes.
# Descrição geral do dataset
df.describe()
A princípio, percebe-se que os dados estão equilibrados. Vamos verificar o tipo de variável presente em cada atributo, bem como a presença de valores ausentes.
# Informações gerais do dataset
df.info()
Nesse comando, verificamos que o tamanho do dataset é de 2126 linhas e 22 colunas. Também notamos que não há valores ausentes e que as variáveis são todas numéricas.
Vamos verificar quais são os valores únicos de cada coluna, para definirmos quais são as variáveis categóricas.
# Verificação de valores únicos por coluna
df.nunique()
Notamos que os atributos 'severe_desaceleration', 'histogram_tendency' e 'fetal_health' possuem uma variação baixa de valores. Vamos analisá-los.
# Contagem de valores
df.severe_decelerations.value_counts()
# Contagem de valores
df.histogram_tendency.value_counts()
# Contagem de valores
df.fetal_health.value_counts()
Poderíamos definir os atributos 'severe_desaceleration' e 'histogram_tendency' como variáveis categóricas por sua baixa variação, porém, ao analisarmos as primeiras entradas, notamos que os valores são muito próximos a zero, o que faz com que o nosso método de contagem considere apenas um valor. O atributo 'fetal_health', obviamente, é nosso atributo Target.
Agora, plotaremos alguns gráficos e veremos se os dados geram algum insght.
Plotaremos, primeiramente, histogramas das colunas e depois verificaremos a correlação entre os atributos.
# Importação de biblioteca necessária
import seaborn as sns
# Plotagem de histograma apenas para casos suspeitos e patológicos
df.loc[df.fetal_health != 1].hist(figsize=(20,20))
plt.show()


Aparentemente, os fetos que possuem uma frequência cardíaca maior que 120, uma variabilidade anormal de curto prazo próximo ao 60 e uma média de variabilidade de longo prazo entre 5 e 10 têm mais chances de desenvolverem algum problema.
# Correlação de atributos em ordem crescente apenas de suspeitos e patológicos
df.loc[df.fetal_health != 1].corr()['fetal_health'].sort_values()
Observamos aqui que as maiores correlações positivas para a saúde do feto estão relacionadas ao valor médio da variação de curto prazo, variação do histograma, desaceleração leve e desaceração prolongada. Já as correlações negativas englobam a média, mediana e modo do histograma, bem como a frequência Cardíaca Fetal Basal e a média dos valores de variação de longo prazo. Agora iremos analisar o balanceamento de nossa coluna Target através da plotagem de um gráfico de barras.
# Plotagem de gráfico de barras relacionado à saúde fetal
sns.countplot(df.fetal_health)

Vemos aqui um desbalanceamento de valores, ou seja, muitos casos classificados como normal e poucos como suspeitos ou patológicos. Isso é bom na vida real, mas para nosso modelo de previsão, é como se não tivesse exemplos de casos diferentes de normal, dificultando seu aprendizado no que diz respeito a anormalidade.
Verificaremos a seguir a presença de outliers em nosso dataset através da plotagem de boxplots.
# Configuração para plotagem
fig, ax = plt.subplots(nrows=6, ncols=4, figsize=(20,20))
linha = 0
col = 0
# Plotagem de boxplots
for i in df.columns:
sns.boxplot(df[i],ax=ax[linha][col],showmeans=True)
col +=1
if col > 3:
linha +=1
col = 0
if linha > 5:
break
plt.tight_layout()
plt.show()


É clara a presença de outliers em diversos atributos, porém devemos tomar um pouco de cuidado aqui. Como nosso dataset possui muitos valores zerados ou próximo de zero, qualquer valor que seja diferente pode ser classificado como outlier. Ou seja, esse valores diferentes podem ser cruciais na classificação.
Preparando o Terreno
Nesta etapa, realizaremos uma cópia de nosso dataset original e separaremos 20% das entradas para teste. Os outros 80% serão utilizados para treino diretamente no Pycaret.
# Cópia do dataset original
df_wk = df.copy()
print("O dataset possui {} linhas e {} colunas".format(df_wk.shape[0],df_wk.shape[1]))
# Definição do dataset de teste
df_teste = df_wk.sample(frac=0.2, random_state=0)
print("O dataset de teste possui {} linhas e {} colunas".format(df_teste.shape[0],df_teste.shape[1]))
# Definição do dataset de treino
df_treino = df_wk.drop(df_teste.index)
print("O dataset de treino possui {} linhas e {} colunas".format(df_treino.shape[0],df_treino.shape[1]))
Em testes realizados previamente, verificamos a necessidade da transformação da coluna 'fetal_health' em categórica do tipo string para o correto funcionamento do Pycaret.
# Transformação da coluna categórica
df_treino['fetal_health'] = df_treino['fetal_health'].map({1.0: 'low', 2.0: 'med', 3.0: 'high'})
# Contagem de valores
df_treino.fetal_health.value_counts()
# Reset dos índices de ambos os datasets
df_teste.reset_index(inplace=True)
df_treino.reset_index(inplace=True)
Utilizando o Pycaret
O Pycaret é uma biblioteca que necessita de instalação. Portanto, com os comandos abaixo, iremos instalar a biblioteca e importar os componentes necessários para a execução no Collab.
# Instalação da biblioteca
!pip install pycaret -q
# Importação de utilitário para funcionamento no Collab
from pycaret.utils import enable_colab
enable_colab
# Importação de todos os módulos
from pycaret.classification import *
Pycaret necessita de uma triagem inicial, onde informamos nosso dataset de treino e nosso atributo alvo. A partir dessa primeira análise, ele nos retorna uma tabela de parâmetros que podem ser modificados de acordo com as necessidades de tratamento.
# Setup inicial do Pycaret
clf = setup (data = df_treino, target = 'fetal_health')
Após analisarmos a tabela, modificaremos dois parâmetros necessários à nossa análise: Transformation, referente a utilização da Padronização dos valores, e Fix Imbalance, referente ao balanceamento do atributo alvo.
# Setup com parâmetros adicionais
clf = setup (data = df_treino,
target = 'fetal_health',
transformation = True,
fix_imbalance = True,
)
Com os dados carregados e os parâmetros definidos, podemos utilizar o método que executa todos os algoritmos de Machine Learning disponíveis na ferramenta e nos retorna uma tabela com valores de diferentes métricas, ordenados por ordem decrescente a partir da métrica Accuracy e utilizando 10 folds de validação cruzada como padrão.
# Comparação da performance dos algoritmos
best = compare_models(sort='Accuracy')
Como mostrado na tabela, os três melhores modelos que performaram com um número maior na métrica Accuracy foram: Light Gradient Boosting Machine, Random Forest Classifier e Gradient Boosting Classifier. Podemos printar o melhor modelo e os parâmetros utilizados.
# Parâmetros do melhor modelo
print(best)
Apesar de verificarmos os melhores modelos com o método acima, ele não nos retorna modelos treinados. É apenas um ponto de partida para a escolha do melhor modelo. Desta maneira, utilizaremos os comandos abaixo para instanciar os nossos três melhores modelos.
# Instanciamento do Light Gradient Boosting Machine
lightgbm = create_model('lightgbm')
# Instanciamento do Gradient Boosting Classifier
gbc = create_model('gbc')
# Instanciamento do Random Forest Classifier
rf = create_model('rf')
O Pycaret nos dá a liberdade de testar quantos algoritmos quisermos sem um total aprofundamento, desde que ele esteja presente em sua biblioteca.
Instanciado os modelos, iremos realizar o tuning de parâmetros para podermos obter os melhores valores para Accuracy.
# Tuning de parâmetros do Gradient Boosting Regressor
lightgbm_tuned = tune_model(lightgbm, optimize = 'Accuracy')
# Tuning de parâmetros do Random Forest Regressor
gbc_tuned = tune_model(gbc, optimize = 'Accuracy')
# Tuning de parâmetros do Light Gradient Boosting Machine
rf_tuned = tune_model(rf, optimize = 'Accuracy')
Observamos que, apenas para o Gradient Boosting Classifier, o tuning de parâmetros aumentou a performance se comparado ao primeiro instanciamento.
Sendo assim, iremos utilizá-lo. Abaixo, os melhores parâmetros encontrados.
# Parâmetros definidos após o tuning
print(gbc_tuned)
Com o modelo escolhido, podemos plotar vários gráficos e analisá-los de forma mais profunda.
# Gráfico de erro
plot_model(gbc_tuned, plot='confusion_matrix')

Neste gráfico, podemos verificar a matriz confusão com os acertos, falsos positivos e falsos negativos.
# Gráfico de importância de atributo
plot_model(gbc_tuned, plot='feature')

Segundo o modelo, os atributos mais importantes para as previsões foram o 'percentage_of_time_with_abnormal_long_term_variability', 'abnormal_short_term_variability' e 'histogram_mean'.
# Opções de gráficos
evaluate_model(gbc_tuned)

Na tabela acima podemos ver a evolução de umas das métricas. Com o comando abaixo, podemos realizar as predições pelo conjunto de dados de treino e verificar a performance final. Neste caso, o Accuracy ficou em torno de 0,93. Na tabela, também é criado uma nova coluna chamada 'Label' com os valores das predições e uma coluna chamada 'score' com a pontuação da previsão.
# Predições
predict_model(gbc_tuned)
Podemos observar também pela tabela acima a eficácia dos métodos que foram instanciados no setup para a transformação dos dados. Podemos reparar nos valores padronizados e a utilização do One Hot Encoding para as variáveis categóricas. O procedimento do Pycaret nos orienta a finalizar o processo através de um método para depois aplicar o modelo nos dados de teste.
# Finalização do procedimento
final_gbc = finalize_model(gbc_tuned)
Procedimento finalizado, podemos novamente realizar as previsões nos dados de treino. Verificamos assim um aumento para 1,0 da métrica Accuracy, bem como em todas as outras métricas. Isso pode indicar que nosso modelo está em overfitting e pode não ser adequado para entrar em produção. Mesmo assim, iremos dar continuidade nos procedimentos e verificar o resultado final. Podemos verificar também os parâmetros finais utilizados.
# Predições de treino finais
predict_model(final_gbc)
# Parâmetros finais utilizados
print(final_gbc)
Com o modelo pronto, iremos utilizá-lo nos dados de teste e verificar a performance.
# Predições nos dados de teste
unseen_predictions = predict_model(final_gbc, data=df_teste)
unseen_predictions.head()
Mesmo com indícios de overfitting, nosso modelo atingiu um valor de 0,8518 na métrica Accuracy. Algumas outras métricas não tiveram um bom desempenho, como a AUC e o Recall. Neste momento, o ideal seria verificar os motivos do overfitting dos dados de treino e refazer todo o procedimento para elevar os resultados das métricas. Como nosso intuito aqui é mostrar o uso da ferramenta Pycaret, daremos como finalizado esse estudo.
Conclusão
Percebe-se que a ferramenta Pycaret nos auxilia e muito na preparação dos dados e nos testes de algoritmo. Com poucos comandos, é possível verificar várias métricas de diversos modelos e utilizar as melhores combinações para a criação de um modelo performático.
Apesar de toda essa praticidade, é de extrema importância o conhecimento de todo o processo por parte do cientista de dados, uma vez que os algoritmos podem fazer certas considerações que não fazem sentido dentro da linha de raciocínio.
Com essa otimização de tempo, a análise exploratória dos dados pode ser realizada de maneira mais detalhada, com geração de insghts que extrapolam o âmbito da previsão de resultados por métodos de classificação.
Neste caso, mesmo com a possibilidade de tantas verificações e escolhas de parâmetros, nosso modelo sofreu overfitting. A decisão de uma revisão ou continuidade fica na mãos do cientista de dados em conjunto com o cliente.
Commentaires