MLOpsフレームワーク Kedro入門

## はじめに

![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407956/123201fc-c92c-dcf9-c49b-749b956eb0d6.png)

最近人気なMLOpsフレームワークのKedroを紹介したいと思います。
日本語の記事が少ないため、この記事が何かの参考になれば幸いです。

本記事では、Kedroの中の中心的なコンセプトと実際の使い方、サンプルの動かし方を説明します。
また、筆者の経験から、導入時のメリットや相性の良いMLOpsツールなども紹介しています。

## Kedroとは
[Kedro](https://kedro.readthedocs.io/en/stable/01_introduction/01_introduction.html)とはオープンソースのMLOps用Pythonフレームワークです。Kedroを使うことでコードの保守のしやすさや再利用性を考慮したデータサイエンスプロジェクトを作成することができます。2022/3/31時点でGithubのスター数が6800程度と類似ツールの中では人気がある方です。

MLOpsの文脈で言うと、データカタログやパイプラインの管理なんかを提供してくれます。
また、Spark、Airflow、Kubeflowなどのツールとの統合も提供されています。

https://github.com/kedro-org/kedro

## インストール方法

```bash
# pip
pip install kedro

# conda
conda install -c conda-forge kedro
```
**2022/3/31日時点でPython3.7以下でしかインストールできないので注意。（[参考Issue](https://github.com/kedro-org/kedro/issues/209)）**

## Hello Worldとコンセプトの解説
公式ドキュメントで紹介されているチュートリアルコードの再掲です。

```hello_kedro.py
from kedro.io import DataCatalog, MemoryDataSet
from kedro.pipeline import node, Pipeline
from kedro.runner import SequentialRunner

# Prepare a data catalog
data_catalog = DataCatalog({"my_salutation": MemoryDataSet()})

def return_greeting():
    return "Hello"


def join_statements(greeting):
    return f"{greeting} Kedro!"
    
# Prepare first node  
return_greeting_node = node(func=return_greeting, inputs=None, outputs="my_salutation")
# Prepare second node
join_statements_node = node(
    join_statements, inputs="my_salutation", outputs="my_message"
)

# Assemble nodes into a pipeline
pipeline = Pipeline([return_greeting_node, join_statements_node])

# Create a runner to run the pipeline
runner = SequentialRunner()

# Run the pipelinet
print(runner.run(pipeline, data_catalog))
```
`hello_kedro.py`に保存した後、Pythonで実行します。

```bash
python hello_kedro.py
# {'my_message': 'Hello Kedro!'}
```
### 解説
#### Node
Nodeでは最終的に実行するパイプラインの各ステップで実行したい処理をPython関数として定義します。
`inputs`, `outputs`でNodeの依存関係を定義することができます。この例では、`join_statements_node` の`inputs`として`my_salutation`をしてすることで、`return_greeting_node`のoutputsを入力にとるように定義しています。

#### Pipeline

`Pipeline`にNodeのリストを渡すことで処理パイプラインが構築されます。ちなみにこの時、`Pipeline`に渡すリストの順番は任意で構いません。具体的にはこの例の　`Pipeline`は以下のように定義しても同様に動作します。

```python
pipeline = Pipeline([join_statements_node, return_greeting_node])
```

#### DataCatalog
DataCatalogはNodeの`inputs`もしくは`outputs` をKeyとして受け取り、`Dataset`に書き出す、もしくは読み込みます。
`Dataset`はファイルへの読み書き機能を提供し、さまざまなファイルフォーマットへ対応しています。今回の例では、`MemoryDataSet`を使ってメモリにデータをストアしています。そのほかの`Dataset`は[こちら](https://kedro.readthedocs.io/en/stable/kedro.extras.datasets.html)から確認できます。

#### Runner
`Pipeline`の実行を担当します。`Pipeline`の最初から最後まで実行し、最終的な結果を出力します。

## Kedro Project
Hello WorldではKedroの機能を使って自分でパイプラインを定義しましたが、Kedroにはプロジェクトをテンプレートから作成する機能があります。
基本的にこの機能を使ってプロジェクトを作成していくことになります。

### Projectの作成
2通りのプロジェクト作成方法があります。

1. シェルインタラクティブ

以下のコマンドを実行すると、インタラクティブにプロジェクトを作成していくことができます。

```bash
kedro new
```
イメージは`poetry init`とかに近いですかね。質問された通りに、`project_name`, `repo_name`, `python_package`を入力するとプロジェクトが作成されます。

2. Configファイルから作成

以下のようなconfig.yamlを作成します。

```config.yaml
output_dir: ~/code
project_name: Get Started
repo_name: get-started
python_package: get_started
```

以下のコマンドを実行することでプロジェクトを作成できます。チームでプロジェクト名を自動生成したいときなんかはこちらを使うことが多いです。

```bash
kedro new --config config.yml
```

### Irisデータセットをつかったプロジェクト例

`starter`に`pandas-iris` を指定することでirisデータセットのテンプレートプロジェクトを作成することができます。

```
kedro new --starter=pandas-iris
```

実行すると以下のようなディレクトリが作成されていると思います。

```
<DIRECTORY_NAME>    # Parent directory of the template
├── conf            # Project configuration files
├── data            # Local project data (not committed to version control)
├── docs            # Project documentation
├── logs            # Project output logs (not committed to version control)
├── notebooks       # Project related Jupyter notebooks (can be used for experimental code before moving the code to src)
├── README.md       # Project README
├── setup.cfg       # Configuration options for `pytest` when doing `kedro test` and for the `isort` utility when doing `kedro lint`
└── src             # Project source code
```

以下のコマンドで依存パッケージのインストールとパイプラインの実行が行えます。

```bash
cd <DIRECTORY_NAME>
# Install dependecy
kedro install
# Run pipeline
kedro run
```

ここで押さえておきたいポイントとしては以下の通りです。

 - `src/<package_name>`以下にPipelineとNodeが定義してある
 - `conf/base`以下にデータカタログやパラメータを設定したファイルが置いてある。
   - `conf/base/catalog.yml`で設定したデータセットはNodeの引数として指定できる。
   - `conf/base/parameters.yml`で設定したパラメータは`params:xxx` のような形でNodeの引数として指定できる。（[参照](https://github.com/quantumblacklabs/kedro-starter-pandas-iris/blob/master/%7B%7B%20cookiecutter.repo_name%20%7D%7D/src/%7B%7B%20cookiecutter.python_package%20%7D%7D/pipelines/data_engineering/pipeline.py#L45)）

実際にパイプラインを実行してみてわかったと思いますが、依存関係の管理から実行まで直感的に利用できます。kedroを導入することで、部署や組織においてコードの属人性の排除などで大きなメリットを得られる可能性があります。

その他のIrisを使ったハンズオンの詳細な説明は[公式ドキュメント](https://kedro.readthedocs.io/en/stable/02_get_started/05_example_project.html)に譲りたいと思います。


## その他便利なコマンドなど

jupyterをインストールした環境で以下を実行することでkedro環境でjupyterを起動できる。

```bash
kedro jupyter notebook
kedro jupyter lab
```

IPythonも同様。

```bash
kedro ipython
```

Jupyterセルに`node`タグをつけて以下を実行することで`src/<package_name>/nodes/`以下に対象のセルのコードを自動でコピーできます。
タグの付け方は[こちら](https://jupyter-notebook.readthedocs.io/en/stable/changelog.html#cell-tags)を参考にしてください。

```bash
kedro jupyter convert <filepath_to_my_notebook>
```

## その他のMLOpsツールとの使い方
　
最初に言及した通り、KedroはデータカタログとPipeline、プロジェクト管理などを提供してくれます。
他のMLOpsツールと用いるとしたら、`wandb`や`mlflow`による実験管理や`kubeflow`や`mlflow`を用いたモデルのデプロイなんかは相性がいいです。
ちなみに、[kedroで作成したパイプラインをKubeflow Pipelineに変換](https://kedro.readthedocs.io/en/latest/10_deployment/06_kubeflow.html)してそのまま実行できたりもします。

大規模処理をする場合はSparkやAirflowをバックエンドにして実行することもできます。

https://kedro.readthedocs.io/en/latest/11_tools_integration/01_pyspark.html

https://kedro.readthedocs.io/en/stable/10_deployment/11_airflow_astronomer.html

## 終わりに
チームの状況によってはKedroを導入することで得られるメリットは大きそうですね。プロジェクト自体活発に開発が進んでいるので、とりあえず導入してみるのもいいかと思います。
今回の記事が何かの参考になれば幸いです。