Descubra o Kedro: Seu passaporte para projetos de ciência de dados modulares, robustos e otimizados!

Guilherme de Mello Nunes
#LocalizaLabs
Published in
10 min readJul 19, 2023

O objetivo desse artigo é fornecer à comunidade uma visão completa sobre o framework Kedro, passando por sua definição, principais vantagens, estrutura e passo a passo de implementação. Espera-se que ao final da leitura, desenvolvedores tenham clareza sobre as funcionalidades da ferramenta e as etapas necessárias para se construir um projeto a partir de seu uso.

O texto está dividido em 3 seções:

  1. O que é o Kedro? Overview da ferramenta, definição, principais vantagens e referências.
  2. Estrutura de um projeto de ciência de dados: Sugestão de estrutura de modularização para projetos de ciência de dados em produção.
  3. Projeto Kedro passo a passo: Passo a passo de configuração, implementação e execução de um projeto Kedro construído a partir da estrutura e exemplo apresentados na seção 2.

Recomenda-se a leitura da documentação da biblioteca em complemento a este artigo:

https://kedro.readthedocs.io/en/stable/

1. O que é Kedro?

Segundo a QuantumBlack, braço de AI da consultoria McKinsey e criadora da ferramenta, “Kedro é um framework Python open-source para criação de projetos de data science reprodutíveis, sustentáveis e modulares”.

O Kedro utiliza de boas práticas de engenharia de software para fornecer uma estrutura de organização de código que permite a criação de pipelines de dados modulares e reprodutíveis. Projetos que são convencionalmente formados por grandes extensões de código, nessa estrutura são divididos em pipelines independentes com funções bem definidas.

A ferramenta permite a execução de diferentes combinações entre esses pipelines modulares na ordenação que o usuário desejar. Isso nos possibilita executar apenas as etapas que quisermos, sem necessidade de executar o projeto por inteiro. Podemos criar módulos responsáveis por cada etapa de um framework de ciência de dados (ex. data engineering, feature engineering, treinamento de modelo, predição, etc) e acioná-los como necessário (ex. apenas data engineering + treinamento de modelo ou feature engineering + predição, etc).

Cada pipeline modular é um fluxo de nodes, unidades de código (funções python) responsáveis por executar uma tarefa específica (recebendo inputs e gerando outputs). Um pipeline de data engineering poderia, por exemplo, conter os nodes: I. tratamento_datasets, II. agrupamento_datasets, III. concatenacao_datasets.

Além disso, a ferramenta possibilita a visualização gráfica da estrutura desses pipelines, permitindo ao usuário ter uma visão ampla de todo o fluxo de execução:

Figura 1 — Pipeline Kedro para treinamento de modelo usando o dataset Iris

Na figura 1, observamos um pipeline que recebe o dataset Iris, o divide entre conjuntos de treino e teste no node “Split Data”, e treina um modelo no node “Train Model”. É possível observar os inputs (“Example Iris Data” e parâmetros) e outputs gerados ao longo do pipeline (“Example Train X”, “Example Train Y”, “Example Test X” e “Example Model”).

Expressivos ganhos de produtividade e organização de projeto são experimentados com a adoção da ferramenta. Dentre os vários benefícios observados com o Kedro, destacam-se:

  • Modularização: ao dividirmos grandes extensões de código em unidades lógicas independentes com funções bem definidas, conseguimos gerar um projeto bem-organizado e ativá-lo de forma inteligente;
  • Fácil desenvolvimento de melhorias e testes: com um código modular bem-organizado, torna-se muito mais fácil desenvolver e testar melhorias;
  • Simplicidade de execução: podemos acionar qualquer combinação de pipelines com apenas um comando bash. Isso nos economiza tempo em trabalhos offline e simplifica muito a produtização de projetos em ferramentas de orquestração (ex. Airflow);
  • Padronização: o Kedro fornece uma estrutura pronta de arquivos e pastas para organizar os componentes do projeto (funções, parâmetros, dataframes, artefatos, etc). Isso gera um código pronto para a produção desde o momento em que é escrito. A padronização acelera a produtização e curva de aprendizado de novos desenvolvedores;
  • Visualização de pipeline: podemos entender facilmente a estrutura do nosso projeto a partir de uma visualização gráfica do fluxo de dados implementado (como observado na figura 1).

2. Estrutura de um projeto de ciência de dados

O poder de modularização do Kedro casa perfeitamente com a estrutura usual de projetos de ciência de dados­­­, que tipicamente é dividida entre as etapas de data_engineering, feature_engineering, model_training e prediction. Nessa seção é apresentado um template para organização de um projeto de ciência de dados utilizando o Kedro:

Figura 2 — Estrutura modular de projeto de ciência de dados utilizando Kedro

Um exemplo de projeto de classificação binária com a base íris implementado em Kedro está disponível no link:

https://github.com/melloguin/exemplo_kedro_iris

O projeto está organizado seguindo a estrutura apresentada na figura 2, contando com os seguintes pipelines modulares:

  • data_engineering

Módulo responsável pela obtenção e preparação dos dados. Os outputs dessa etapa são bases de dados tratadas na granularidade adequada para a criação das features. No projeto exemplo, realizam-se as etapas de tratamento da base íris.

  • feature_engineering

Módulo responsável pela criação do dataset com todas as features necessárias para treino e deploy do(s) modelo(s). No exemplo, criam-se 2 novas features.

  • model_training

Módulo responsável pelo treinamento e armazenamento de modelos. No exemplo, 2 classificadores são treinados e salvos como arquivos no formato .pkl.

  • prediction

Módulo responsável pelo carregamento de modelos pré-treinados e datasets de features para geração de previsões. Também é nessa etapa que as previsões geradas são armazenadas. No exemplo, as classificações são geradas e armazenadas localmente.

A criação de pipelines para execução de outras tarefas (por exemplo, monitoramento, backtesting, train_and_predict, etc) é feita quando necessária.

3. Projeto Kedro passo a passo

Com os conceitos introduzidos, é hora de falarmos de código! Essa seção contém um passo a passo dos procedimentos e comandos bash necessários para criar e executar um projeto Kedro via terminal de comando.

3.0. Habituando-se com o projeto

Recomenda-se o espelhamento do repositório fornecido na seção 2 para execução do passo a passo de implementação descrito a seguir. Iremos realizar os procedimentos de data engineering, feature engineering, treinamento de modelo e predição para um projeto de classificação utilizando a base íris.

O notebook armazenado na raiz do repositório e no link abaixo contém as funções (nodes) utilizadas em todos os processos, além da visualização de seus resultados:

https://github.com/melloguin/exemplo_kedro_iris/blob/main/Passo%20a%20passo%20-%20projeto%20iris%20kedro.ipynb

Ao final de cada etapa, tarefas serão solicitadas para que você possa 
recriar o projeto fornecido no repositório.

3.1. Preparação do ambiente e criação do pipeline

Nesta etapa vamos ensinar como instalar o Kedro e criar projetos e pipelines.

  • Instalação Kedro e ambiente virtual

Recomenda-se a criação de um ambiente virtual exclusivo para o projeto Kedro em questão que receberá a bliblioteca Kedro e todas as outras necessárias para o projeto. A ferramenta anaconda pode ser utilizada no terminal para a criação do ambiente virtual e instalação do Kedro:

conda create --name ambiente_virtual_exemplo
conda activate ambiente_virtual_exemplo
conda install -c conda-forge kedro
kedro info

· Criando um projeto

Também é conveniente criar um repositório no git para hospedagem do projeto. Uma vez com o repositório clonado na máquina, devemos apontar o terminal para a sua pasta raiz e criar/nomear o projeto kedro.

O repositório fornecido já contém um projeto criado. Para não alterá-lo, iremos utilizar um nome diferente para o novo projeto:

cd exemplo_kedro_iris
kedro new

[New Kedro Project]: exemplo_kedro_iris_v2

Após a execução do comando, a pasta do novo projeto (exemplo-kedro-iris-v2) será criada na raiz do repositório com toda a estrutura de pastas e arquivos do template kedro. Arquivos responsáveis pelos componentes do projeto (parâmetros, funções python, dados, endereços de inputs/outputs, logs, etc) são criados automaticamente.

· Criando pipelines

Agora iremos criar todos os pipelines modulares do projeto. Para criar um pipeline devemos apontar para a pasta do projeto (1) e executar o comando “kedro pipeline create” (2) como demonstrado abaixo. Da mesma forma, podemos excluir um pipeline utilizando (3):

cd exemplo-kedro-iris-v2                                    (1)
kedro pipeline create data_engineering (2)
kedro pipeline delete data_engineering (3)
Tarefa:
Crie em seu novo projeto, pipelines para os 4 módulos descritos na seção 2

3.2. Desenvolvendo os componentes

Uma vez com o projeto kedro criado, precisamos entender seus principais componentes e como utilizá-los para a construção de nossos pipelines.

Nesta seção vamos explicar a função de cada componente e dar exemplos de como configurá-los.

Nodes e pipelines

  • scr/nome_do_projeto/pipelines/nome_do_pipeline/nodes.py
  • scr/nome_do_projeto/pipelines/nome_do_pipeline/pipeline.py

Os arquivos nodes.py e pipeline.py são os principais componentes do projeto. Cada pipeline modular terá um arquivo nodes.py (onde iremos guardar as funções e classes presentes no fluxo de trabalho) e um arquivo pipeline (onde conectamos as funções/nodes através de seus inputs e outputs, de forma a gerar o fluxo desejado). Os fluxos de trabalho são construídos a partir de uma cronologia de geração de dados, onde um node que possui inputs gerados por nodes antecessores só é executado após finalizada a execução desses antecessores.

O code block abaixo traz o conteúdo do arquivo de nodes.py do módulo “data_engineering” presente no exemplo fornecido no repositório:

"""
This is a boilerplate pipeline 'data_engineering'
generated using Kedro 0.18.7
"""

import pandas as pd

##################################################
def concatenacao_inputs(sepal_data: pd.DataFrame,
petal_data: pd.DataFrame,
species_data: pd.DataFrame,
) -> pd.DataFrame:

"""
Unifica dataframes sepal_data, petal_data e species_data em um dataframe único (iris_data)

Args:
sepal_data(pd.DataFrame): dataframe contendo as informações de sépala
petal_data(pd.DataFrame): dataframe contendo as informações de pétala
species_data(pd.DataFrame): dataframe contendo os rótulos de espécie de flor

Returns:
iris_data(pd.DataFrame): dataframe contendo todas as informações
"""

iris_data = sepal_data[['example','sepal_length','sepal_width']].merge(petal_data[['example','petal_length','petal_width']],
on = 'example',
how = 'left'
).merge(species_data[['example','species']],
on = 'example',
how = 'left'
)

return iris_data


##################################################
def limpeza_conversao_colunas(iris_data: pd.DataFrame
) -> pd.DataFrame:

"""
Trata valores de iris_data, removendo caracteres indesejados e convertendo colunas para float
(estavam como string) devido aos caracteres indesejados

Args:
iris_data(pd.DataFrame): contém informações sem tratamento

Returns:
iris_data(pd.DataFrame): contém informações tratadas comtipo ajustado
"""

# Trata valores
iris_data['sepal_length'] = iris_data['sepal_length'].replace({'a- ':'', ',':'.'},regex=True)
iris_data['sepal_width'] = iris_data['sepal_width'].replace({',,':'.',',':'.'},regex=True)
iris_data['petal_length'] = iris_data['petal_length'].replace({',':'.'},regex=True)
iris_data['petal_width'] = iris_data['petal_width'].replace({',':'.'},regex=True)

# Converte colunas
iris_data['sepal_length'] = iris_data['sepal_length'].astype(float)
iris_data['sepal_width'] = iris_data['sepal_width' ].astype(float)
iris_data['petal_length'] = iris_data['petal_length'].astype(float)
iris_data['petal_width'] = iris_data['petal_width' ].astype(float)

# Check consistência tratamentos
print('iris_data tratada. valores nulos por coluna:')
print(iris_data.isna().sum())

return iris_data

O code block abaixo traz o conteúdo do arquivo pipeline.py do módulo “data_engineering” presente no exemplo fornecido no repositório:

"""
This is a boilerplate pipeline 'data_engineering'
generated using Kedro 0.18.7
"""

from kedro.pipeline import Pipeline, node, pipeline
from .nodes import (concatenacao_inputs,
limpeza_conversao_colunas
)


def create_pipeline(**kwargs) -> Pipeline:
return pipeline([

node(
func=concatenacao_inputs,
inputs=['sepal_data',
'petal_data',
'species_data',
'params:concatenacao'
],
outputs='iris_data_raw',
name='Concatenacao_inputs'
),

node(
func=limpeza_conversao_colunas,
inputs = ['iris_data_raw'
],
outputs= 'iris_data',
name='Limpeza_conversao_colunas'
)

])

Nodes declarados no arquivo pipeline.py podem receber inputs de 3 tipos de origem:

  • outputs de nodes anteriores no fluxo de trabalho;
  • parâmetros estabelecidos previamente à execução, armazenados no arquivo parameters.yml (declarados na forma: ‘param:XYZ’ no arquivo pipeline.py — note o input ‘concatenacao’ da função concatenacao_inputs no code block acima);
  • datasets gerados anteriormente na execução ou obtidos a partir de endereços listados no arquivo cathalog.yml.
Tarefa:
Copie os nodes contidos no notebook para os arquivos node.py dos módulos
do seu projeto. Crie os arquivos pipeline.py conforme o exemplo fornecido
acima.

Parâmetros

  • conf/base/parameters.yml

Aqui vamos listar e atribuir valores a todos os parâmetros (variáveis, listas, dicionários, etc) que iremos fornecer ao nosso pipeline. Temos o arquivo de parâmetros globais (endereço acima) que são compartilhados por todos os pipelines modulares do projeto, e um arquivo de parâmetros locais para cada pipeline modular (localizado na pasta conf/base/nome_do_pipeline).

### Parâmetros data_engineering pipeline
concatenacao: 'usado_apenas_para_ilustrar_o_exemplo'

### Parâmetros model_training pipeline
feature_list: ['sepal_length','sepal_width','petal_length','petal_width','sepal_area','petal_area']
target: 'species'
k_neighbors: 5

Acima temos os parâmetros utilizados no projeto exemplo, com parâmetros assumindo valores string, inteiro e lista de strings.

Catálogo

  • conf/base/catalog.yml

Aqui vamos listar todos os artefatos (datasets, pickles, etc) que iremos carregar e/ou salvar ao longo da execução do projeto, declarando seus respectivos endereços de armazenamento (em pasta local, Google Big Query, Google Cloud Storage, etc).

No início da execução, artefatos do catálogo que não são output de nenhum node (tabelas de de-para, modelos pré treinados, etc) serão carregados a partir dos endereços declarados. Os artefatos gerados na execução de um pipeline que estiverem listados no catálogo serão salvos nos endereços declarados ao fim da execução de seus respectivos nodes geradores.

# Here you can define all your data sets by using simple YAML syntax.
#
# Documentation for this file format can be found in "The Data Catalog"
# Link: https://kedro.readthedocs.io/en/stable/data/data_catalog.html

##################### Inputs
sepal_data:
type: pandas.CSVDataSet
load_args:
sep: ","
filepath: 'data/01_raw/sepal_iris_data.csv'

petal_data:
type: pandas.CSVDataSet
load_args:
sep: ","
filepath: 'data/01_raw/petal_iris_data.csv'

species_data:
type: pandas.CSVDataSet
load_args:
sep: ","
filepath: 'data/01_raw/species_iris_data.csv'

Acima temos o trecho inicial do arquivo catalog.yml do projeto exemplo, onde são listados 3 datasets, no formato .csv, que são utilizados como inputs do pipeline de Data Engineering.

Pipeline registry

  • scr/nome_do_projeto/pipeline_registry.py

Nesse arquivo vamos declarar as chamadas de pipelines completos a partir da combinação de pipelines modulares:

"""Project pipelines."""

from typing import Dict
from kedro.pipeline import Pipeline, pipeline
from kedro.framework.project import find_pipelines
from exemplo_kedro_iris2.pipelines import (data_engineering,
feature_engineering,
model_training,
prediction,
)

def register_pipelines() -> Dict[str, Pipeline]:
"""Register the project's pipelines.

Returns:
A mapping from a pipeline name to a ``Pipeline`` object.
"""
DE = data_engineering.create_pipeline()
FE = feature_engineering.create_pipeline()
MT = model_training.create_pipeline()
PD = prediction.create_pipeline()

return {
"__default__": DE + FE,
"pipeline_treinamento": DE + FE + MT,
"pipeline_previsao": DE + FE + PD
}

No code block, observamos a declaração de 2 pipelines de execução:

  • pipeline_treinamento: execução dos pipelines modulares necessários para treinar o modelo: (data_engineering + feature_engineerig + model_training)
  • pipeline_previsao: execução dos pipelines modulares necessários para gerar previsão a partir do modelo: (data_engineering + feature_engineerig + prediction)
Tarefa:
Copie os arquivos parameters.yml, catalog.yml e pipeline_registry.py
do projeto pronto para seu projeto, observando a semântica de cada arquivo
e realizando as modificações necessárias.

Outros componentes importantes do projeto kedro são:

  • Arquivo de requirements (scr/requirements.txt) com todas as bibliotecas necessárias para a execução de todos os pipelines;
  • Diretórios (data/) para armazenamento de arquivos gerados no projeto.

3.3. Executando os pipelines para treinamento e deploy do modelo

Nesta seção iremos explicar como treinar o modelo e fazer uma predição a partir do pipeline criado e configurado anteriormente.

· Instalando requirements

Finalizado o desenvolvimento do código, devemos listar no arquivo requirements.txt todas as bibliotecas necessárias. Para instalar as bibliotecas em nosso ambiente virtual, executamos os comandos:

cd exemplo_kedro_iris/exemplo-kedro-iris
kedro build-reqs
pip install -r src/requirements.txt

· Executando pipelines

Finalmente estamos prontos para executar nosso código. No arquivo pipeline_registry.py configuramos os pipelines de execução (pipeline_treinamento e pipeline_previsao), que são acionados a partir dos comandos:

cd exemplo_kedro_iris/exemplo-kedro-iris       #caso já não esteja na pasta
kedro run --pipeline pipeline_treinamento
kedro run --pipeline pipeline_previsao

Ao executar os comandos acima, podemos observar que nosso objetivo foi concluído. Ao acionarmos o pipeline de treinamento, os dados de inputs foram tratados e convertidos em features, que por sua vez foram utilizadas no treinamento de modelos classificadores (salvos localmente em data/06_models no formato .pkl). Em seguida, esses modelos foram consumidos pelo pipeline de previsão de forma a gerar previsões que por sua vez foram armazenadas na pasta data/07_model_output.

Podemos também executar pipelines atribuindo valores de parâmetros na chamada da execução. No exemplo abaixo valores são atribuídos aos parâmetros k_neighbors e target:

kedro run --pipeline pipeline_treinamento --params k_neighbors:30,target:'species'

Considerações finais

O objetivo desse artigo foi fornecer o conhecimento necessário para que um desenvolvedor entenda as principais vantagens do Kedro, identifique casos de uso e consiga implementar do zero um projeto utilizando esse framework.

Ao adotar esse framework no #LocalizaLabs, experimentamos ganho de produtividade e melhoria na gestão de nossos projetos de ciência de dados.

Referências

https://medium.com/quantumblack/introducing-kedro-the-open-source-library-for-production-ready-machine-learning-code-d1c6d26ce2cf

https://mapmeld.medium.com/transparent-data-flow-with-kedro-eba842de4eb2

--

--