St_Hakky’s blog

Data Science / Human Resources / Web Applicationについて書きます

自然言語処理向けのデータ作成ツールの「doccano」を使ってみたので、まとめる

こんにちは。

最近、仕事で自然言語処理関係のプロジェクトをやっているのですが、その関係でdoccanoというツールを触ってみることになったので、使い方とかをまとめておきます。

doccanoとは

doccanoとは、オープンソースのテキストアノテーションツールです。

github.com

以下の三つのアノテーションタスクをすることができます。

  • Text Classification
  • Sequence Labeling
  • Sequence to Sequence

demoサイトは以下から。

RESTful APIなども搭載されているので、結果の取得をAPI経由で行うなどもすることができます。

doccanoをとりあえずローカルで立ち上げてみる

doccanoをローカルでとりあえず試してみます。

pipでインストールして立ち上げる

以下のコマンドだけで立ち上げることができます。

$ pip install doccano

$ doccano

これで、 http://0.0.0.0:8000/にアクセスするとツールを見れます。

簡単ですね。

dockerで立ち上げる

dockerで立ち上げるのもそう難しくなく、以下のコマンドで対応できます。

$ docker pull doccano/doccano
$ docker container create --name doccano \
  -e "ADMIN_USERNAME=admin" \
  -e "ADMIN_EMAIL=admin@example.com" \
  -e "ADMIN_PASSWORD=password" \
  -p 8000:8000 doccano/doccano

docker container start doccano

これで、 http://localhost:8000/にアクセスするとツールを見れます。

Sequence Labelingを試してみる

今回は、テキスト中の「偉い人」と「そうでない人」を識別するという意味不明なタスクを想定して、その表現部分をアノテーションしていくことをやってみようと思います。

ローカルで立ち上げてログインする

先ほどの方法でローカルで立ち上げます。ここから先は、AWSやGCPなどの環境で立ち上げても同じです。

f:id:St_Hakky:20210108103854p:plain

ユーザー名とパスワードを入れます。

f:id:St_Hakky:20210108104448p:plain

プロジェクトを作成する

入ると、プロジェクトの一覧を見ることができます。最初は、プロジェクトがないので、一覧には何もないですが、今回の私の例ですと、一つプロジェクトが作られています。

f:id:St_Hakky:20210108104456p:plain

今回のアノテーションタスクに関するプロジェクトを立ち上げます。左上のCreateを押すと、以下のようなモーダルが出るので、それに入力します。

f:id:St_Hakky:20210108104502p:plain

プロジェクトを作成して、プロジェクトのページに行くと、以下のような画面が見れます。

f:id:St_Hakky:20210108104801p:plain

左側に、メニューがありますが、それぞれ以下の役割があります。

  • Home:各ステップのやることがYoutubeの動画で学べる
  • Dataset:AnnotationするデータをImport、Exportすることができ、またAnnotationをデータに対して行うことができる
  • Labels:Annotationをするラベルを登録することができる
  • Members:Annotationをするメンバーの一覧を確認できる
  • Guideline:Annotationのガイドラインをここに記載することができる
  • Statistics:Annotationの実施状況を確認することができる
データセットのインポート

早速、データセットのインポートをしてみます。サイドメニューからインポートをします。

f:id:St_Hakky:20210108104512p:plain

以下のようなモーダルが出てくるので、お好きな方法でデータをインポートします。

f:id:St_Hakky:20210108105430p:plain

Createと押すと、データの取り込みが行われます。

f:id:St_Hakky:20210108105530p:plain

ラベルを作成する

今回のアノテーションでつけるラベルを作成します。

f:id:St_Hakky:20210108105559p:plain

Createを押すと、以下のようなモーダルが表示され、必要な情報を記入します。

f:id:St_Hakky:20210108105701p:plain

今回は、偉い人とそうじゃない人を当てるタスクを解きたいので、以下のようなラベルを作成しました。

f:id:St_Hakky:20210108105705p:plain

これで準備完了です。あとはひたすらアノテーションをするだけです。

Annotatonを開始する

左上の「Start Annotation」からでもいいですし、以下の画像にあるようにDatasetの一覧からAnnotationしたいものからスタートしてもいいですし、なんでも大丈夫です。

f:id:St_Hakky:20210108105900p:plain

Annotationをスタートさせると、以下のようにテキストが出てきます。マウスでテキストを洗濯すると、先ほど作成したラベルが表示され、どのラベルにするかを選択することができます。

f:id:St_Hakky:20210108110031p:plain

選択すると、以下のようにラベルがテキストに対して付与されます。

f:id:St_Hakky:20210108110124p:plain

そして、以下のようにラベルを付与した後に間違えたと思ったらバツボタンを押すことで、消すこともできます。

f:id:St_Hakky:20210108110134p:plain

Annotationの実施状況を確認する

Annotationをこのように行っていくのですが、実施した状況については、以下のように統計情報として得ることができます。便利ですね。

f:id:St_Hakky:20210108110143p:plain

Annotationした結果を受け取る

Annotationが終わったら、データをExportすることができます。以下の画面から、Exportを選択して、

f:id:St_Hakky:20210108111012p:plain

モーダルに必要な情報を入力するとExportできます。

f:id:St_Hakky:20210108111016p:plain


以上で一通りの機能については紹介しました。

感想・まとめ

今回のブログで紹介した以外にも、ショートカットキーで操作ができたり、他にも機能としてはいくつかありそう。

ただ、

  • 1プロジェクトにつき1MBまでしかデータがImportできないっぽい…?
  • Sequence Labelingでラベル付与しなかったものを完了ってするのどうするんだろうという気持ち

というような感じで、ちょいちょいどうしたらいいんだろうっていうところはあったので、使いながら確認して行こうかなーと思っています。


それでは。

【 Kedro】Kedroに入門したのでまとめる

こんにちは。

最近、Kedroと言う機械学習向けのパイプライン構築用のツールを使ってみたので、それについてまとめます。

Kedroとは?

f:id:St_Hakky:20201127110345p:plain

概要

Kedro は QuantumBlack というデータ分析企業 が公開している、プロダクションレディなデータ分析用ワークフロー構築ツールです。結構いろんなパイプラインツールがありますし、全部を触ったことがあるわけではないですが、今のところ*1Kedroはすごくすごく良い感じです。

ツールの紹介動画は以下*2

youtu.be

公式周りのサイトのURLは以下の通り。

特徴

めちゃめちゃたくさんの特徴がありますが、以下あたりはすごく便利です。

  • Airflowなどと同じく、Pythonで全てのワークフローを書くことができる
  • DAG形式でパイプラインを定義でき、 Sequentialな実行とParallelな実行の切り替えや、パイプラインの途中から実行するなどできる
  • yamlで定義することができるデータカタログの機能があり、csv, pickle, feather, parquet, DB上のテーブル など様々なデータ形式に対応することができる
  • データセットや学習モデルをバージョン管理し、指定のバージョンでいつでも実行できるよう再現性を担保する
  • Cookiecutter によるテンプレートを利用することで、複数人での作業を管理できる
  • モデルのパラメーターをyamlで管理することができる
  • Jupyter Notebook, Jupyter Lab とのインテグレーション
  • 本番環境への移行がしやすく、複数の環境(AWS, GCPのようなマネージドサービスなど)にデプロイすることができる。具体的にはPythonのパッケージとしてであったり、kubeflowやArgo Workflows、AWS Batchにデプロイなどもできる
  • リッチなパイプラインの可視化がKedro vizでできる

とりあえずやりたいことは一通りできそうな雰囲気を感じ取っていただけると思います笑

Kedroの大まかな構成要素

Kedroは、大まかには次の4つから構成されています。

  • Node
  • Pipeline
  • DataCatalog
  • Runner
Node
  • 実行される処理の単位で、前処理や学習といった処理本体になるもの
  • 入力、出力のデータセットと、それを処理するロジックを定義して、パイプラインに組み込む
Pipeline
  • Nodeの依存関係や実行順序を管理するもの。
  • decorator機能がkedroにはあり、これによって、パイプライン全体の処理に対して機能を付加することもできる
DataCatalog
  • パイプラインで使用するデータを定義するカタログ
  • データセット名、形式、ファイルパス、ロードやセーブ時のオプションなどを指定することが可能
Runner
  • パイプラインを実行するもの。パラメーターを指定して実行することができ、例えば特定のパイプラインだけ実行するとかもできる。
  • SequentialRunner、ParallelRunnerの二つのRunnerがある。

Install

普通にpipとかcondaでインストールできます。

# pipとかcondaでインストールできます
$ pip install kedro
$ conda install -c conda-forge kedro

 # installされたかの確認。kedroっていう文字が出てきたら成功
$ kedro info

 _            _
| | _____  __| |_ __ ___
| |/ / _ \/ _` | '__/ _ \
|   <  __/ (_| | | | (_) |
|_|\_\___|\__,_|_|  \___/
v0.16.6

kedro allows teams to create analytics
projects. It is developed as part of
the Kedro initiative at QuantumBlack.

No plugins installed

新規のプロジェクトを作成する

kedroのデフォルトのテンプレートを使う場合は、以下のコマンドでやります。いくつか質問が出てきますが、それぞれ読んで答えればオッケー。

$ kedro new

テンプレートをオンにすると、以下のような感じでフォルダが構成されます。

$ tree .

.
├── README.md
├── conf
│   ├── README.md
│   ├── base
│   │   ├── catalog.yml
│   │   ├── credentials.yml
│   │   ├── logging.yml
│   │   └── parameters.yml
│   └── local
├── data
│   ├── 01_raw
│   │   └── iris.csv
│   ├── 02_intermediate
│   ├── 03_primary
│   ├── 04_feature
│   ├── 05_model_input
│   ├── 06_models
│   ├── 07_model_output
│   └── 08_reporting
├── docs
│   └── source
│       ├── conf.py
│       └── index.rst
├── kedro_cli.py
├── logs
│   └── journals
├── notebooks
├── setup.cfg
└── src
    ├── requirements.txt
    ├── sample
    │   ├── __init__.py
    │   ├── hooks.py
    │   ├── pipelines
    │   │   ├── __init__.py
    │   │   ├── data_engineering
    │   │   │   ├── README.md
    │   │   │   ├── __init__.py
    │   │   │   ├── nodes.py
    │   │   │   └── pipeline.py
    │   │   └── data_science
    │   │       ├── README.md
    │   │       ├── __init__.py
    │   │       ├── nodes.py
    │   │       └── pipeline.py
    │   └── run.py
    ├── setup.py
    └── tests
        ├── __init__.py
        ├── pipelines
        │   └── __init__.py
        └── test_run.py

プロジェクトをgitの管理下にするには、以下のような感じでやります。

$ git init
$ git add ./
$ git commit -m "init"
$ git branch -M main
$ git remote add origin <hogehoge>
$ git push origin main

フォルダ構成からわかると思いますが、どこに何を書くかが明確で、サンプルのコードを追いかけるだけで、大体何すればいいかわかります笑*3

とりあえず動かしてみる

kedro newをしたときに、irisのデータセットでサンプルのモデルを動かすためのパイプラインやノード、データカタログが用意されているので、とりあえずこいつをローカルで動かしてみたいと思います。

$ cd <プロジェクトルート>

# まずはプロジェクトの依存関係をインストール
$ kedro install

# 実行
$ kedro run

これだけです。簡単ですね。実行すると、logのフォルダにログが吐き出されるのがわかります。

次からは、処理を実際に追加していくのをどうするか見ていきます。

データソースを追加する(Data Catalogを追加する)

Nodeなどでデータを実際に使うために、データカタログにデータソースを追加します。conf/base/catalog.ymlというファイルに記述していきます。

csvとかであれば、以下のような感じで追加できます。他にも、xlsxやparquet, sqlTableなど様々なデータソースに対応できます。

companies:
  type: pandas.CSVDataSet
  filepath: data/01_raw/companies.csv

reviews:
  type: pandas.CSVDataSet
  filepath: data/01_raw/reviews.csv


dataのディレクトリに、それぞれのデータの状態に合わせてフォルダにデータを入れます。ここら辺も人によってフォルダの分け方が別れることがほとんどですが、事前に定義がされているのでやりやすいです。


処理を追加する(Nodeを編集する)

nodeの処理は単純で、普通に関数を追加するだけです(完)

試しに、テンプレートで出てくるnodes.pyの処理を見て見ます。

from typing import Any, Dict

import pandas as pd


def split_data(data: pd.DataFrame, example_test_data_ratio: float) -> Dict[str, Any]:
    """Node for splitting the classical Iris data set into training and test
    sets, each split into features and labels.
    The split ratio parameter is taken from conf/project/parameters.yml.
    The data and the parameters will be loaded and provided to your function
    automatically when the pipeline is executed and it is time to run this node.
    """
    data.columns = [
        "sepal_length",
        "sepal_width",
        "petal_length",
        "petal_width",
        "target",
    ]
    classes = sorted(data["target"].unique())
    # One-hot encoding for the target variable
    data = pd.get_dummies(data, columns=["target"], prefix="", prefix_sep="")

    # Shuffle all the data
    data = data.sample(frac=1).reset_index(drop=True)

    # Split to training and testing data
    n = data.shape[0]
    n_test = int(n * example_test_data_ratio)
    training_data = data.iloc[n_test:, :].reset_index(drop=True)
    test_data = data.iloc[:n_test, :].reset_index(drop=True)

    # Split the data to features and labels
    train_data_x = training_data.loc[:, "sepal_length":"petal_width"]
    train_data_y = training_data[classes]
    test_data_x = test_data.loc[:, "sepal_length":"petal_width"]
    test_data_y = test_data[classes]

    # When returning many variables, it is a good practice to give them names:
    return dict(
        train_x=train_data_x,
        train_y=train_data_y,
        test_x=test_data_x,
        test_y=test_data_y,
    )

この後出てくるパイプラインとデータの入出力を合わせる必要がありますが、それ以外は普通の関数の処理であることがわかると思います。書く場所をある程度縛るだけで、ここら辺に特にルールがないのは助かりますね。

パイプラインを編集する

パイプラインの処理で編集するのは二箇所で、pipeline.pyhooks.pyの二つです。

pipeline.py

まずpipeline.pyですが、自分で実装したnodeの関数を、kedro.pipeline.nodeを使って、パイプラインに組み込みます。組み込むときには、第一引数に関数、第二引数に入力で渡すデータ、第三引数に出力のデータを渡します。

from kedro.pipeline import Pipeline, node

from .nodes import split_data


def create_pipeline(**kwargs):
    return Pipeline(
        [
            node(
                split_data,
                ["example_iris_data", "params:example_test_data_ratio"],
                dict(
                    train_x="example_train_x",
                    train_y="example_train_y",
                    test_x="example_test_x",
                    test_y="example_test_y",
                ),
            )
        ]
    )

これだけです。

hooks.py

pipeline.pyで作ったパイプラインを実行できるようにします。また、パイプライン間に依存関係がある場合がほとんどだと思いますので、その処理も書きます。

from typing import Any, Dict, Iterable, Optional

from kedro.config import ConfigLoader
from kedro.framework.hooks import hook_impl
from kedro.io import DataCatalog
from kedro.pipeline import Pipeline
from kedro.versioning import Journal

from ab_recommender.pipelines import data_engineering as de
from ab_recommender.pipelines import data_science as ds


class ProjectHooks:
    @hook_impl
    def register_pipelines(self) -> Dict[str, Pipeline]:
        """Register the project's pipeline.

        Returns:
            A mapping from a pipeline name to a ``Pipeline`` object.

        """
        data_engineering_pipeline = de.create_pipeline()
        data_science_pipeline = ds.create_pipeline()

        return {
            "de": data_engineering_pipeline,
            "ds": data_science_pipeline,
            "__default__": data_engineering_pipeline + data_science_pipeline,
        }

    @hook_impl
    def register_config_loader(self, conf_paths: Iterable[str]) -> ConfigLoader:
        return ConfigLoader(conf_paths)

    @hook_impl
    def register_catalog(
        self,
        catalog: Optional[Dict[str, Dict[str, Any]]],
        credentials: Dict[str, Dict[str, Any]],
        load_versions: Dict[str, str],
        save_version: str,
        journal: Journal,
    ) -> DataCatalog:
        return DataCatalog.from_config(
            catalog, credentials, load_versions, save_version, journal
        )


project_hooks = ProjectHooks()

これだけで、実行順序の依存関係を記述することができます。

まとめ

以上がざっくりとしたkedroの使い方です。まだ試せていないのですが、kedroのフレームに則って書いておけば、AWS Batchやkubeflowなどにデプロイすることができるようになるなど、他にも良さげな感じの物が多いので、使ってみたいと思います。

それでは!

*1:経験日数一週間くらいですがw

*2:あんまりわからなかった

*3:かっ、、書かない言い訳ではない笑

【Python】Pandasのメモリ使用量の削減方法のまとめ

こんにちは。

今、とある事情でPandasのメモリ使用量の削減を仕事でしているのですが、その時に改めてPandasのメモリ使用量の削減方法を調べたので、まとめてみます。

メモリ使用量の確認

今回、タスクを実施するにあたってメモリ使用量がどのくらいかかっているのかを同時に調べたんですが、Pandasに限らず、メモリ使用量を確認する方法としては、memory_profilerが良きです。以下の記事で紹介していますので、参考にしていただければと思います。

www.st-hakky-blog.com

ただ、このmemory_profilerは関数の中まで見に行ってメモリ使用量を見にいくわけではないので、関数内でピークを向かえるメモリ使用量などがわからない点に注意が必要です。

メモリ使用量の削減概要

Pandasでよくやる操作でメモリ使用量を削減する方法は、だいたい以下あたりじゃないかなと。

  • 使わないデータをできる限り読み込まないようにする
  • del文からのgc.collect
  • データ型の指定
  • その他処理面での工夫

それぞれについて紹介していきます。

使わないデータをできる限り読み込まないようにする

ぶっちゃけ今回の仕事上では色々やったんですが、これが当たり前ですが一番聞きました笑。例えばcsvでデータを読み込む時に、処理に使用するデータのみ取り込むことで、使用メモリを削減することができます。

data = pd.read_csv('./data.csv', usecols=['columnA', 'columnB', 'columnC'])

csvデータを読み込む時のメモリ使用量の削減ついては、以下の記事でも書きましたので、詳細を知りたい方は、以下の記事を。

www.st-hakky-blog.com

Pandasでは、処理の際に内部的にデータをコピーして処理するなどがあるので、カラムが多ければ多いほど、データをより食う構造に陥りやすいのではないかなと思っています。これを回避する意味でも、必要なデータだけ扱うと言うのはとても大事ですね。。。*1

del文からのgc.collect

これはPandasに限らずの定番ですが、使い終わったデータのメモリ領域を解放することが大事です。

import gc

# 何かしらの処理をしたdfがあるとする

# 削除とガーベジコレクションをする
del df
gc.collect()  

Pythonは、C言語などと違って自動でガーベジコレクションをしてくれるので、特に明示的にしなくてもまぁいいっちゃいいのですが、大きめの物をやろうとするとシビアになってくるので、都度やってます。

ただ、都度やると処理速度は落ちてしまうのですが、大きなデータでメモリがクラッシュするよりは時間食わないので、やったほうがいいと思います。

データ型の指定

Pandasにおいては、これが結構効きます。Pandasだと、データをread_csvなどで読み込んだ時に、自動で型が推定されるのですが、その際に必ず大きめの型が指定されます。float型の場合、float32で大丈夫だったとしても、float64にしてしまいます。float32とfloat64だと、単純に考えても2倍、メモリ使用量が違います。

Pandasのデータの型をしっかり指定してあげることで、使用メモリを削減できるので、いくつかのメモリ型の指定方法を書きます。

.astype

処理したり、データを読み込んだりした後、型を十分なサイズまで小さくするために、型を適切に設定するために、 astypeを使用することができます。

df['value1'] = df['value1'].astype('int8')
読み込み時(read_csv, read_json)で、型を指定する

詳しくは以下の記事でも紹介しているのですが、ファイルを読み込んだ時に、型を指定してしまうと、最初から最低限のメモリ使用量にできるので、事前にデータについてある程度わかっている場合は、これは良きです。

www.st-hakky-blog.com

例えば、以下のように設定することができます。

data = pd.read_csv('./data.csv',
            dtype={'id': np.int64,
                   'url': np.object,
                   'region': np.object,
                   'region_url': np.object,
                   'price': np.int64,
                   'year': np.float16,
                   'manufacturer': np.object,
                   'model': np.object,
                   'condition': np.object,
                   'cylinders': np.object,
                   'fuel': np.object,
                   'odometer': np.float32,
                   'title_status': np.object,
                   'transmission': np.object,
                   'vin': np.object,
                   'long': np.float16})

このように設定することで、メモリの使用量を削減することができます。

Category型にする

PandasにはCategory型と言うものがあり、特定の離散値を取る性質のデータに用いることができます。カラムの中の値のユニーク数が少ないなら、かなり大きな効果が期待できます。

data['gender'] = data['gender'].astype('category')

データの中身をみたときに、category型が使える場所については積極的に使っていくことで、メモリ使用量を削減することができます。

型の自動指定

ここまで、型を自分で指定するアプローチを書いてきましたが、Kaggleの方で以下のような便利な関数を見かけたので、こちらに載せておきます。

これは、各列の値をみて、メモリ使用量が最小になるように型を自動で設定してくれる関数です。

def reduce_mem_usage(df, verbose=True):
    numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
    start_mem = df.memory_usage().sum() / 1024**2
    for col in df.columns:
        col_type = df[col].dtypes
        if col_type in numerics:
            c_min = df[col].min()
            c_max = df[col].max()
            if str(col_type)[:3] == 'int':
                if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
                    df[col] = df[col].astype(np.int8)
                elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
                    df[col] = df[col].astype(np.int16)
                elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
                    df[col] = df[col].astype(np.int32)
                elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
                    df[col] = df[col].astype(np.int64)
            else:
                if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
                    df[col] = df[col].astype(np.float16)
                elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
                    df[col] = df[col].astype(np.float32)
                else:
                    df[col] = df[col].astype(np.float64)
    end_mem = df.memory_usage().sum() / 1024**2
    if verbose:
        print('Mem. usage decreased to {:5.2f} Mb ({:.1f}% reduction)'.format(end_mem, 100 * (start_mem - end_mem) / start_mem))
    return df

この関数にDataFrameを入れてあげることで、メモリ使用量を削減できます。関数が処理を実行する時間のオーバーヘッドはあるものの、かなり効果がでかいです。

その他処理面での工夫

ここからは処理面の工夫でよくやるやつをメモ書き程度に書いておきます。もっと他にもある気がするのですが、思いついたら書いていこうと思います。

mergeの時にcopyオプションをoffにする

Pandasを扱っている方なら、mergeの処理をすると思うのですが、このmergeのオプションに、copyオプションがあり、これをoffにすることで、メモリ使用量を削減することができます。

Pandasのmergeの処理では、内部実装までは追いかけていないのですが、内部の挙動を外からメモリ使用量などでみている感じと、同じ職場の人から聞いた感じだと、mergeするもの・mergeされるもの・merge後のオブジェクトを、Chunkに区切って徐々にマージするっぽいです。

その時に不要なコピーを置いておくみたいなことしているらしく、これをoffにすることで、メモリ使用量を削減することができます。ただ、これによる副作用として、自分はまだであったことがないのですが、たまに変な挙動をすることもあるらしいので注意が必要です。

Numpyにして処理してから再代入

Pandasのデータだと処理がどうしても遅くなってしまう処理などをするときなどは、Numpyにして処理してから再代入するみたいな感じのほうがメモリ使用量もそうですが、処理速度の面でも恩恵を受けることがあるようなイメージがあります。ケースバイケースな感じはありますが。


以上です。

*1:だいたい横着してテーブルでかくなっていくんですが、、、

【Python】時系列予測ライブラリProphetで学習したモデルを保存・呼び出しする

こんにちは。

随分前ですが、Facebookの時系列予測ライブラリのProphetについて記事を書きました。

www.st-hakky-blog.com

ちょっぴり本格的に使う機運が高まってきて、「そういえばどうやってモデルを保存・呼び出しするんだろう」って思って、調べてみました。

今回はその調べた内容についてまとめます。

やること

以下の二つをやってみようと思います。

  • Prophetを使って学習したモデルを、pickle形式で保存
  • pickle形式で保存された学習済みモデルを読み込み

モデルの保存

思ったより普通にできます笑。以下のように、pickleとして保存するだけですね。

# modelの保存
pkl_model_path = 'path/to/prophet-model.pkl'
with open(pkl_model_path, 'wb') as f:
    pickle.dump(m, f)

モデルの読み込み

読み込みも、同じ要領でできます。

with open('path/to/model.pkl', 'rb') as f:
    m = pickle.load(f)

こんな感じで。

【Python】Pandasでapply処理時に複数の値を返せるようにする

こんにちは。

いつも忘れてしまうので、書きます。

やりたいこと

Pandasで、applyの処理を書くことはよくあると思うのですが、このときに複数の値を返して、一度で複数カラムを追加したいのです。

通常、apply関数を使用して素直にやろうとすると、以下のように2行に分ければできます。

df['new1'] = df['old'].apply(lambda x: x*2)
df['new2'] = df['old'].apply(lambda x: x*4)

ただ、2行くらいならいいのですが、3行とか4行に渡ったり、そういう処理が何回もあったりすると流石にシンドイってなります。実際にやっていることは、同じカラムに対しての処理なので一回で終わらせたいんです。

なので、そういうのを無くすためにできる限り無くすために、複数の値をapplyで返したいって感じの気持ちです。

実現方法

あるカラムの値を2倍および4倍した値をカラムに追加する場合の処理については、以下のようにやります。

df[['new1', 'new2']] = df['old'].apply(lambda x: pd.Series([x*2, x*4]))

ミソは、pd.Seriesで囲ってあげることです。これだけ。


これ以外にいい方法あれば教えてください。

それでは。

【folium】Pythonで位置情報の可視化

こんにちは。

今回は、Pythonのライブラリである「folium」を使って、位置情報を可視化してみたので、その方法についてまとめてみます。

概要

Pythonで簡単に使える、地理情報の可視化ライブラリです。以下が関連サイト

このライブラリ、ドキュメントがしっかりしていてすごいなと思います。また、実際のJupyter Notebookで実行するためのサンプルもついているので、こちらもよかったです。

インストール

インストールは、普通に以下の通り。

# pipでインストールする場合はこちら
$ pip install folium

# condaでインストールする場合はこちら
$ conda install -c conda-forge folium

とりあえず可視化させてみる

とりあえずの可視化は、QuickStartにもあるやつでやってみます。

import folium

m = folium.Map(location=[45.5236, -122.6750])

m

Jupyterとかで、mの値をアウトプットすると、以下のような感じで可視化できます。

f:id:St_Hakky:20190422180030p:plain

簡単。複数の位置をMarkerと合わせて表示したいときは、以下のコード。

import folium

m = folium.Map(
    location=[45.372, -121.6972],
    zoom_start=12,
    tiles='Stamen Terrain'
)

tooltip = 'Click me!'

folium.Marker([45.3288, -121.6625], popup='<i>Mt. Hood Meadows</i>', tooltip=tooltip).add_to(m)
folium.Marker([45.3311, -121.7113], popup='<b>Timberline Lodge</b>', tooltip=tooltip).add_to(m)

m

アウトプットは以下のような感じ。

f:id:St_Hakky:20190422180240p:plain

基本的には、mにどんどん追加していく感じです。こだわりたい人は、いろんな可視化の方法が用意されているので、そちらも試していく感じでいいと思います。

終わりに

色々書こうと思ったんですけど、こちらのギャラリーを一通り眺めると、だいたい使い方分かるので、これにて笑(手抜き)。

【データ分析の民主化】データドリブンな組織になるには何をしたらいいのか考えてみた

こんにちは。

今日は、データドリブンな組織になるために、何をしたらいいかを考えてみたので、それについて書きます。

データドリブンな組織の必要性

先日、以下の記事で「データドリブンな組織ってなんで必要なのか」と言う観点で記事を書きました。

st-hakky.hatenablog.com

上の記事を要約すると、以下のようになります。

  • 意思決定を「早く・確実に・納得感を持って」するために、データ分析をする(アナリスト視点)
  • データを活用して新機能の開発やコスト削減を行う(MLエンジニア視点)
  • データ分析をベースにした組織、つまりデータドリブンな組織になるためには「データ分析の民主化」が必要(組織全体の視点)

データドリブンな組織になるためには、「データ分析の民主化」って言う最近のホットワード(?)なのか知りませんが、そう言うのが必要です。

ぼんやりした言葉なので、具体的に何をすればいいのかと言うのを、この記事では考えてまとめてみます。

また、様々な企業様の事例を元に書いている部分がありますが、いつもリスペクトしかなく、大変勉強させていただいております。ここからは私の考えという名の妄想も入りますが、様々な点で参考にさせていただきました。

会社では、一部実践していることもありますが、まだできていないことが多く、こうしたほうがいいんだろうなぁみたいなところも含めて、全部書いてみたいと思います。

データ分析の民主化ができている状態とは

データ分析の民主化ができている状態を考えてみます。字が汚いのとかは一旦置いておいて、イメージとしてはこんな感じです*1

f:id:St_Hakky:20181229175759p:plain

上の図の要素を洗い出すと、以下のようになります。

  • 適切なデータ取得
  • 分析できる形にデータが整形されていること
  • データへのアクセス方法が複数種類用意されている
  • データ分析に関する情報にアクセスできる環境がある
  • データ分析をする人が多い

上から順番にやればなんか良さそうな感じがしますが、順番にやっていった先に「そもそも文化が作れなかった」とかになると泣きたくなると思うのと、なんだかんだ一番「人」の部分が時間がかかると思うので、「全部並行して初めは小さく進めていきつつ、場面によって注力ポイントを変えていく」というのが良いと思います。

以下では、それぞれの項目についてみていきます。

適切なデータ取得

インフラやアプリケーション、GoogleAnalyticsなどのサービスなどから適切なデータを取得します。

ここでは、「基本的に取得したほうがいいよね」というデータとか「この活用方法やデータ分析を見据えて取得する」データなどが思いますが、その意味で「適切なデータ」という表現をしています。

ここがないと全部が破綻しますし、サービス開発初期からこういうのを意識してやっているかどうかとかでもだいぶ変わります*2

また、「適切なデータ」というのは、ポジティブな表現ですが、データ分析の民主化を強く推進するのであれば、別で管理されているデータを能動的にDBにぶち込むとかはやるべきかなと思います。全部をDBに突っ込んでおけば、みんなやるようになると思うので、「環境」からある種の縛りを作るのはありかもしれません。

分析できる形にデータが整形されていること

システム的には十分なデータ、例えば正規化がしっかりとされていたり、とりあえずデータ分析に必要だから貯めているけどいざクエリ叩くとデカ過ぎて処理に時間がかかるなどのデータだったりを、「良い感じで分析できるような形に整形された状態」であることが必要です。

良い感じというのは、

  • テーブルを必要以上にJOINしなくても良く
  • 必要な粒度で集計処理がされた中間テーブルであり、
  • データの一次クレンジングがされている

ようなデータです。

こちらの「データドリブンな組織を作るときにまず行うこと 〜我が社よデータ分析色に染まれ〜 - ハウテレビジョン開発者ブログ」という記事で紹介されている以下の図がありますが、

http://cdn-ak.f.st-hatena.com/images/fotolife/n/n_mao/20150406/20150406142131.png

この図のように分析のために必要なデータは別途集計され、まとめられている必要があります。

でないと、様々な人が高度で難解なSQLクエリをぶっ叩いたり、簡単なことでも無駄に高い技術レベルを身につけたりなどの必要が出てきて、そもそもデータにアクセスするハードルが高くなります。これでは、誰もアクセスしません。

これを防ぐために、「分析しやすいデータにして保持しておく」というのはクソ重要ですし、これをやってくれるデータエンジニアの存在は貴重ですし、感謝の気持ちを持たなければなりません。

データへのアクセス方法が複数種類用意されている

データへのアクセス方法として、使うユーザーに合わせて様々なアクセス方法が用意されているのが好ましいです。どこまでやるかは会社によって違うかなと思いますが、主には以下の方法があるかなと思います。

  • Dashboard:BIツールなどを使って作ったDashboardからアクセス
  • BI / SQL queryBIツールからクエリを叩いてアクセス
  • ChatTool:Slackなどで定期的にbotやグラフのURLとしてデータを発信
  • Cloud:AWS/GCPなどのサービスからアクセス
  • Database:DBに直接アクセス
  • API:API経由でアクセス

手段を用意すればするほど、コストもかかりますが、コストに見合うものがあればやるべきかなと思います。手段が多いほど、データ分析のレベルが高い人も低い人もみんな使うことができるようになるので、攻めてレベルを3段階くらい分けたとして、3つくらいのアクセス方法は用意しておきたいです。

データ分析に関する情報にアクセスできる環境がある

後で紹介する「データ分析をする人が多い」状態にするためにも、勉強会などでデータ分析の全体的なナレッジを底上げしたり、Wikiなどを使ってデータ分析に関する知識を広げたりすることができる必要があります。

また、データ分析のレベルも人によって様々なので、たくさんのナレッジにアクセスできるようにしておくことは重要です。例えば、以下のようなものが挙げられます。

  • Wikiなどでのドキュメント
  • Slack botなどでの情報の取得
  • GithubなどでのQuery Snippets
  • 全体への社内勉強会や個別指導会
  • Slackなどでのデータ分析に関する記事の共有


このように、Wikiなどに書いておくことによって、データ分析に強い人たちへの質問なども減り、個々人が自走できるようになるので、ナレッジをしっかりと蓄積し、伝搬できる状態にするのは重要です。

データ分析をする人が多い

データ分析をある程度のレベル感の人であればできるようになったら、できる人を増やす布教活動を行い、できるメンバーを増やします。

データ分析ができるメンバーが増えれば増えるほど、意思決定の「早さ・確度・納得感」などがどんどん増していくはずなので、これを元にビジネスを加速させていく感じで行ければ良いのかなと思います。

              • -

データ分析の民主化は、基本的にこれを達成するために地道にアクションを取っていくことが重要なのかなと思っています。以下では、アクションについて触れる前に、データ分析を行うにあたってのレベル感についても再考してみたいと思います。

データ分析のレベル

どのような観点で考えるか

前提として、

  • データアナリストとして、SQLをいじって分析ができるのが本業で、Pythonでコードも書けてモデルもいじれる人
  • MLエンジニアとして、Pythonとかインフラまでできてサービスインできるのが本業で、SQLとかで分析もできる人

といった二つの役割は別々だと思っているという前提はあると思いますが*3、ここではあまり意識せずに書いてみたいと思います。

その上で、ここでは個人と組織の2軸で考えてみたいと思います。

個人からみたデータ分析のレベル

これに関しては、以下の記事にもあるような感じなのかなと。

また、これは一般化することは非常に難しいと思うのですが、無理やりやるとだいたい以下のようになるのかなと思います。

  • Lv1 : 用意されたデータを日常的に見て、理解できる
  • Lv2 : エクセルやSQLを使ってデータを可視化して、仮説検証などの分析ができる
  • Lv3 : 統計的な手法や機械学習のモデルを使って分析・自動化ができる

とはいっても、Lv3まで必要がなかったり、そもそもタスク依存であったりします。なので、ここではあまり触れませんが、Lv2の感覚でデータ分析できる人を増やす方が、ビジネス的には全体的にインパクトがあると感じています。

組織からみたデータ分析のレベル

組織からみたデータ分析のレベルについては、以下の記事に書かれていて、

medium.com

前に死ぬほど頷いた記憶があるので、ここで引用いたします。

データサイエンティストは組織におけるデータ活用状況について、レベル分けして考えます。
 
(中略)
 
Lv0:
データ収集、ログ設計
 
Lv1:
システムから切り離された環境でのデータ蓄積=データの民主化
SQL等による基礎統計
統計等からインサイトを得られる状況
 
Lv2:
基礎統計の充実、BIツールによるダッシュボード化
ピボットテーブル等による探索的な手動データマイニング
手動データマイニングから施策を立案できる、手動で実行できる
 
Lv3:
機械学習アルゴリズムを利用した探索的データマイニング
ABテスト等を利用した、データに基づく意思決定
ちくわ大明神
 
Lv4:
機械学習等を利用して、自動的に施策実行される環境を構築する
機械学習により安定的に稼ぐ仕組みを作る 
 
Lv5:
Lv4で作ったシステムで使われている機械学習アルゴリズムをより高度なモノ(例えばディープラーニングとか)に置き換えていき、収益性を改善する

まずは、手元からスモールにでもどんどんデータ分析をガンガンやれるようにしていき、最終的にはアナリストとしてもMLエンジニアとしてもしっかりと事業に貢献できるような組織になっていくのかなと思っています*4

こちらの「貴社の「データドリブン成熟度」は5レベルのどれぐらい? | データドリブン・マーケティング&ADフォーラム レポート | Web担当者Forum」記事にある、以下の図でまとめられている「データドリブン成熟度」の5段階のレベルも面白いです。

f:id:St_Hakky:20181231002017p:plain

この各レベルの割り振りに対して、記事中では、「それぞれのレベルにおける課題と解決アプローチ」も書かれています。

f:id:St_Hakky:20181231002546p:plain

これらは、組織全体におけるデータ分析やデータ基盤の技術レベルよりもどちらかというと、意識や活用度に寄せて議論されていて面白いですね。

また、これらには人数規模の話は出てきていませんが、段階としては、以下のようになると思います。

  • Lv1 : データ分析の専門職の人たちだけができる
  • Lv2 : データ分析の専門職と一部のデータ分析よりの人たちや仲間内だけができる(数人レベル)
  • Lv3 : データ分析の専門職の人じゃない人たちがデータ分析を他の人にも広め、人数が増える(数十人〜数百人レベル)
  • Lv4 : 全員ができるようになる

データ分析の専門職の人たちができるのは当然として、それ以外の人たちも徐々に広めていって、最終的には全員が何かしらの形でデータ分析やそれを行なって得た結果に関わって、意思決定なりをしている状態が理想となります*5

アクションに向けてのまとめ

個人・組織・組織規模のレベルをそれぞれあげていくために、アクションをやっていく必要があります。

どれにも共通しているのが、「最初は小さく初めて、それぞれのステップで効果を出していきながら大きくする」というのなのかなと思っています。

次項からは、「データ分析の民主化」を実現するための状態を中心に、ぞれぞれどうアプローチすればいいか、タスクリストも含めて整理してみようと思います*6

とはいっても、状態のところで結構書いたので、参考になりそうな資料とアクションリストを中心にまとめます。

データ分析の民主化実現に向けてのアクション

前提となる組織の意識

前提として、組織をデータドリブンをしようとした時に、

  • 組織全体がデータドリブンに意思決定をしようと思っている
  • 組織全体では思っていないが、必要だと思うので、小規模に進めたい

というのでは、だいぶ違うと思います。

大抵のシチュエーションでは、組織全体ではまだ浸透していないので、小規模に進めていくというケースがほとんどだと思います。

なので、今回はそのケースを元に考えていきますが、「データドリブンな組織を作ることでメリットが得られそうかどうか」というのに、ぼんやりでもイメージがないと、スモールにでも始める意味はありません。

スモールに初めていって、結果として意味があんまりなかったってなるのは良いことかなと思いますが、そもそも意味があるかはちょっと考えてみる必要はあると思います。

意味があるという前提で、各状態になるプロセスを考えていきます。

適切なデータ取得

まず適切なデータを取得するフェーズにおいては、以下のようなアクションが必要だと思います。

  • 目的に対して必要なデータの整理
  • そのデータの取得

それぞれ見ていきます。

目的に対して必要なデータの整理

これをやるために、以下の項目が必要かなと思います。

  • KGI・KPIや分析したい項目の整理
  • 現在取得・存在しているデータの整理(データ保存先、テーブル定義書の作成など)
  • 顕在化しているやりたいことに必要なデータの整理

参考記事は以下の通りです。

組織によっては、Githubなどの利用状況などを可視化して開発状況を把握したり、

Slackのメッセージを可視化したり*7

など、「解決したい課題」や「設定している目標」によって、必要なデータは様々考えられます。

まとめると、「目的に合わせて必要なデータのリストを作成し、現在取得できているデータとそうじゃないデータをそれぞれ整理する」というのが一番最初にやることかなと思います。

そのデータの取得

データの取得については、

  • 取得したいデータに対して、現状のシステムからのログ取得をどうするか
  • 使っているサービスからAPIなどで取得する

などがあげられると思います。

以下は参考記事です。この辺りは開発しているサービスや目的などに依存するので、適切な手段を選ぶのが良いのかなと思いますので、ここでは深く入り込みません。

分析できる形にデータが整形されていること

以下の記事にあるように、しっかりとデータが利用者や目的にそって整理されている必要があります。

yuzutas0.hatenablog.com

データ基盤の三種類にあるような区分で、データが整備されているとかなり幸せになります。

f:id:St_Hakky:20181231010718p:plain

利用者によってやりたいこと・できることも変わりますし、みたいデータの粒度も変わってきます。なので、しっかりとこんな感じで整理されている方が、みんなハッピーになります。

なので、どのような技術要素を使うかも含めて、データ基盤をしっかりと作ることがここでのステップになると思います。また、作るときも、がっつり一気に作るのではなく、

1. 利用のユースケースの整理する
2. 小さくまずは使ってもらう
3. 運用が回ってきたらシステムに反映する

といったようにステップを細かく踏むことで余計な時間がかからずに済みます。

データへのアクセス方法が複数種類用意されている

データへのアクセス方法が複数用意されている状態を作ります。例えば、以下のような記事が参考になります。

具体的にあげてみると、以下のような方法で様々な人が自由にアクセスできるような環境を作ってあげるのが良いのかなと思っています。

  • Jupyter Notebook:PythonやRなどを使ってのアドホック分析
  • BIツール:Redash、Tableau、Lookerなどを使ってのデータの抽出・可視化・Dashboard作成
  • ChatTool:Slackなどと連携して、受動的または他者へのデータ共有を受けれるようにする
  • CSV:Spreadsheetやエクセルなどを使って分析できるように、CSVとしてデータがダウンロードできる
  • Database:ターミナルからでも、ORmapperからでもなんでも良いが、とにかく直で触って分析できる
  • Cloud:AWS/GCP上もしくはそこからデータを引っ張って分析できるようにする

少なくとも上の環境はサクッと作れるようにしたいですね。ただ、正直技術的にも工数的にも大したことなくても、

  • Githubをビジネスメンバーにも付与するための努力
  • AWSとかのアカウント発行

みたいな、「なんかうん」みたいな作業の方が大変だったりします。

が、そういうのを乗り越えれば、GithubでSQLのクエリを共有できたり、AWS上でこねこねできたりします。

最近では、クラウドのサービス上でJupyter Notebookで分析するのがやりやすく、Amazon SageMakerなどを使って機械学習のモデル開発などを行なっている事例もいくつか出ています。

個人的にも、リクルートさんのこちらの記事「Wantedly の機械学習プロダクト開発を支える機械学習基盤 / #rejectcon2018 - Speaker Deck」のようにやりたいと思いつつ、このためにインフラを作るのはなぁって思っていたので、SageMakerは最初の一歩として選択肢になりそうだなと思って会社でトライアル的に使ってみています。

データ分析に関する情報にアクセスできる環境がある

上でほどんど書いちゃいましたが、以下のようにとにかく情報にアクセスできる環境があるようにするのは大事だと思います。

  • Wikiなどでのドキュメント
  • Slack botなどで、可視化されたデータ分析結果の情報の取得
  • GithubなどでのQuery Snippets
  • 全体への社内勉強会や個別指導会
  • Slackなどでのデータ分析に関する記事の共有

加えて、分析を普段の業務に少しずつ取り入れて、ミーティング中であったり、実際の業務であったりで徐々に「こんな風にやっています」とか「こんな風にやったらうまくいきました」とかを共有するのも大事だと思っています。

こんな感じで、日常的にデータにアクセスする機会や方法を掴んでいくことが大事だと思うので、1日に1回以上は全員が触れるくらいのレベルでやるべきだと思います。

データ分析をする人が多い

データ分析をするメンバーを増やすことも同時に大事です。上でやっていることをいろんな関係者を含めてやれるようにすれば、自ずと分析をする人が多くなるのかなと思いますが、

  • 使っているツールなどをできる限り多くの人にオープンにしてアカウントを発行する
  • 使い方を伝える勉強会を開催する

など、使うパイを多くするような施策も大事だと思っています。

まとめ

DataOpsという考え方

以下のスライドは、少し前に読んだのですが、本当に凄まじいなと思いました。

speakerdeck.com

このレベルでDataOpsがしっかりとしていて、しかもこれほどまでに事例を紹介しているものをまだ私は見ていません*8

f:id:St_Hakky:20181231005420p:plain

今回書いた記事は、データ分析の民主化という話だったのですが、これを実現するためのDataOpsをしっかりやっていくということに尽きるなぁと、改めてスライドを見ながら思いました。

地道に一歩ずつ

なんかこうやってみてみると、一個一個はかなり地味というか、地道に頑張っていくみたいな感じなんだなぁと改めて思いました笑。前のスライドにあった以下のスライドをみて、これに比べたら頑張れるなぁと思いました笑。

f:id:St_Hakky:20181231010307p:plain

以下の記事とかにある、「データ基盤は使われてこそ意味がある」とかも身にしみます。

yuzutas0.hatenablog.com

また、こういうのって張り切ってやっちゃいますが、しっかりと業務に着目して、小さく小さくアウトプットしながら進めていきたいなと思いました。

f:id:St_Hakky:20181231015642p:plain

来年から頑張ってこの辺りをやれそうなチャンスがあるので(やるから思考整理もかねて書いているというのもある)、一通りやってみての振り返り記事とか書ければと思っています。

それでは。

*1:お正月にグラフィックビジュアライゼーションについての本を読んで、ちょっと書いてみました。まだ練習が必要。。。

*2:そして、様々な理由で、多くはあんまり意識されて無いところが多いと思いますが…泣

*3:ここの区分は

*4:そういえば、事業会社とかで機械学習だけやっていて自社のデータ活用が進んでいないところってあんまりねぇなって思いましたがどうなんですかね笑

*5:だいぶ理想ですが…

*6:抜け漏れはあると思いますが、、、汗

*7:これは少しネタ記事に近いとは思いますが

*8:もしあれば知りたい

データドリブンな組織に何故なるのかについて改めて考えてみる

こんにちは。

データドリブンな組織ってそもそもなんの意味があるのかをしっかりとまとめておこうと思い経った機会があったので、まとめてみます*1

f:id:St_Hakky:20181229142940j:plain

思い立ったきっかけ

このところこのテーマについてよく考えるようになり、また必要だなと感じた背景として、最近データ分析系のProductに関わるようになったからです。

自分が分析しないけど、分析をしてビジネス価値を出して欲しい」という立ち位置で諸々を考える必要があって、しかも彼らには昔から「データを元に意思決定をする以外の方法」で既に色々やっているんです。

この中で、

  • データドリブンになることを押し付けることなく、自然とデータドリブンになることのメリットを知ってもらうにはどうしたらいいか
  • 彼らの既存の方法を後押しする、もしくはリプレイスするようなデータ分析ってなんだろうか

というのを真剣に考えるようになりました。今、「データ分析の民主化」を社内で取り組まれているところは多いと思いますが、これをサービスとしてやっているようなものです。

会社では、僕たちはサービスを前提に考えますし、息を吸うようにデータ分析をします。でも、これを「今までやっていなかった人たち」がやるためには、「明確な動機」を感じる必要があります。

その意味で、手法やプロセスに注目が集まりがちなデータ分析について、「そもそもデータドリブンな組織になることのメリット・デメリットってなんだ」っていうのを改めて考えるのには意味があるなと思って、これをしっかりと整理しておきたいなと思います。

そもそもデータ分析で何をするのか

「データを分析」することとして、少し意味を広く捉えて、何をするのかを整理してみたいと思います。

アナリスト的な視点から

データ分析をして何がしたいのかというと基本的には以下のことかなと思います。

  • 戦略・施策を作る
  • 現状・問題点を把握する
  • 仮説を作る
  • 施策を作る
  • 検証する
  • 意思決定を最速化する

これは、アナリスト的な立ち位置でのものです。

メルカリの「「データが好き」だけでは終わらせないメルカリ文化とは? 経営とプロダクトを“数字”で支えるBI×MLマネージャー対談」という記事の中では、以下の三つがデータ分析を使ってできることとしてあげられていて、これについては表現は違えど、さほど間違っていないのかなと思います。

1. 知る・・・必要なデータが取得でき、可視化できている
2. 理解する・・・そのデータがなぜそのような数字になっているのかを説明できる
3. 予測する・・・そのデータからどういったことが起こるのかを予期できる

機械学習エンジニア的な観点から

エンジニア要素が入ってくると以下のこともできるのかなと思います。

  • データを元に、新サービスを作り、売上を上げる
  • データを元に、既存のサービスの効果を高め、コストを削減したり、売上をあげたりする

上記のようなことが「できる」のがデータ分析とします。

そもそも何故データ分析が必要なのか

「できる」ことはわかったとして、そもそも何故データ分析が必要なのかというのも改めて考えてみます。

いわゆる「時代がそういう波になっているか」というのも一つあるのかなと思います。データがより多く取れるようになって、それをさばけるようになってきたというのももちろんあると思いますが、上記のやりたいことをベースで行けば、それはエンジニア要素が入ってくる部分に強くメリットが出ます。もちろんアナリスト系のところでも、分析ができるようになるための環境構築などがやりやすくなったとかもありますが、それでも「動機」というところでは少し弱いです。

「やりやすくなったからやる」というよりも、データ分析の一番のところは「意思決定をより早くすること」にやっぱりあると思います。

上の記事にあるように、改善や戦略策定などを、「現状・問題点の把握から仮説立案、そこからの意思決定までの一連」をデータによってサポートし、「より意味のある意思決定を早くやる」というのがデータ分析が必要となる理由だと思います。以下は、引用文です。

プロダクト改善だけでなく、経営戦略や採用戦略など、さまざまな場面でデータ分析の力が必要とされています。すべての戦略には意思決定の場面があり、そこでデータサイエンティストは力を発揮することできる。

別に、アナリスト的なデータ分析をベースにしなくても、意思決定はいわゆる「勘・経験・度胸」で出来ます。

ただ、意思決定のプロセスを「早く・確実に・納得感を持ってする」ためには、つまり「より早く売上・コスト削減などにインパクトすることをできるだけ確度高く全体の納得感を持って意思決定する」には、

  • 社内に意思決定のプロセスを共有する
  • 個々人、チームでの意思決定を後押しする

などが必要です。これをやるにあたって、データとそれを使った分析があればスピードを早くすることができます。それぞれちょっと細かくみていきます。

社内に意思決定のプロセスを共有する

社内における意思決定のプロセスを共有する時に、「なんとなくこれかなって感じで」みたいな共有の仕方だとアレです。

また、「何故これになったのか」というのを「データ」という基準を元に判断できるようになるので、やりやすくなります。

最近社内でもRedashを使ってデータの共有とかをしているのですが、めちゃめちゃいいです。ちょっとした可視化や結果であれば、Slackでぺっと貼って終わりにできますし、Smallな分析であれば、可視化した図*2を貼ってコミュニケーションして意思決定というのも多いです。

f:id:St_Hakky:20181229115804p:plain

結果をいち早く共有したり、レポートに使いやすい形にしたりなど、最近はそういうことができるようになってきています。

以下の記事でもあるように、「良かったのか・悪かったのか」をしっかりと答えられる分析をして、結果をいち早く共有することが大事なのですが、これがデータ以外だと、多分説明のためのロジックをパワポでゴネゴネ作って、社内の政治を色々やってととても大変なのかなと思います(すみません、やったことないのでイメージですが)。

意思決定のプロセスをデータだけで簡潔にできるのは、とても便利だなと思います。

個々人、チームにおける意思決定を後押しする

こちらのメルカリの記事でも紹介されていますが、個々人・チームにおける意思決定を後押しするというのは、データだからこそできることだと思います。

f:id:St_Hakky:20181229100206p:plain

意思決定をする時に、「AかなBかな」って思って悩んで議論している時間とかって結構長いと思うのですが、これをいち早くやるには、意思決定を早くやる必要があります。これをサポートする立ち位置として、データ分析を使おうという文脈です。

メルカリ社内では、データ分析の部署を「BI(Business Intelligence)チーム」と呼称している。そのミッションは「意思決定力のMAX化」。あくまで意思決定の支援に役立つことを最優先業務とし、決して「データを使って何かやる」ことが目的ではない。たとえば、機械学習などは別部署がおもに手がけている。

「データを使って何かやる」ことが目的ではないと書かれていますが、定量と定性の両方を大事にしながら、目標に対して意思決定をしていくという感じだと思います。以下の記事では、そのあたりの具体例とかが書かれています。

意思決定支援をデータを元にやって、「えいやっ」に対して「それだったらいけるよね」というのを裏付けることで、意思決定を良いものにしつつ、より大胆な決定をやっていくという感じが素敵です。取り扱う分野が増えていくのも頷けます。

f:id:St_Hakky:20181229105511p:plain

その意味で、データだけを頼りに意思決定をするのではない、「データインフォームド」という考え方もあるみたいで、以下の記事で解説されています。

hackernoon.com

こちらに日本語訳の記事があるのですが、その中では以下のように紹介されています。

データ・ドリブンな意思決定はデータが意思決定の中心で、主要な、もしくは唯一のインプットです。どのような施策を打てばいいのかデータのみに頼ってしまいます。

データ・インフォームドな意思決定は、様々なインプットがある中で、データは重要なインプットであるという位置づけです。ユーザーに対してあなたはどういった価値を提供しているのかを深く理解するためにデータを使うのです。

データを重要なインプットの一つとし、意思決定をする。まさにサポートという点と、それもあるからガツンといけるって感じでしょうか。いずれにせよデータ分析で意思決定をサポートすることができるのは事実だと思いますし、それだけで十分でないのは理解できます。

以下の記事とかは「At Netflix, Who Wins When It’s Hollywood vs. the Algorithm?」という記事を紹介した記事ですが、ここであげた内容とは別視点で、データだけで意思決定をすることができない状況を象徴する感じだと思います。

qiita.com

意思決定をしっかりと後押しする、それはデータならではのことと思います。

データドリブンな企業になるために必要なことは何か

ここまでで、以下の二つを見てきました。

  • そもそもデータ分析で何をするのか
  • そもそも何故データ分析が必要なのか

では、データを元に意思決定するデータドリブンな企業になるために必要なことは何かを改めて考えてみたいと思います。

多分、一言でまとめると「データ分析の民主化」を行うことだと思いますが、じゃあ「データ分析の民主化ってなんだよ」って思います。まだしっくりきていない部分もありますが、データ分析の民主化を行うに当たって、以下のことが必要だと思っています。

  • データを見て意思決定をするというトップの意識
  • データにみんながアクセスして活用できるような環境づくり

これも見ていきたいと思います。これらについては、以下の記事に事例も合わせて乗っているので、めちゃめちゃ参考になります。

データを見て意思決定をするというトップの意識

以下の記事をみて、「うむうむ」と思っていたのですが、ぶっちゃけトップがそうじゃなかったら、そうじゃないですよね。

もちろんトップだけがそうじゃだめだと思いますが、まず「データを元に意思決定をしよう」という気持ちがなければ終わりです。

トップの意識があった上で、社内におけるデータを元に意思決定をすることに対する納得感の醸成はとても大事だと思います。

データがないシチュエーションとかでは、普通にデータなしで意思決定をするしかないですが、そんな時に「くっそ、これどっちやねん」とか「だいたいこの辺りじゃないかなぁと思っているんだよねぇ」ってなって、しかもその中に「これはデータをしっかり見ればわかりそう」があると、本当にデータが欲しくなります。

それでも、めっちゃ色々考えて、色々見ていくと「ここが問題点かなぁ」っていうのがだいたいわかってくると思うのですが、それをデータなどなしに他のメンバーに共有するのは並大抵のことではありません笑。

その意味で、「問題点ってここかな」という感覚を組織で作る、とかも大事だと思います。つまり後でも言う、「みんなが常に数値を意識できるような環境を作り」もとても大事な取り組みの一つだと思っています。

データにみんながアクセスして活用できるような環境づくり

様々な企業の取り組みをみていると以下のようなところをやっているようでした。目的に根ざしたデータの取得をしっかり行なっていると言うのがある前提ですが、

  • よく見る数値のDashboard化
  • データのアクセス周りの整備(中間テーブルなどへのアクセス、テーブル定義の整理)
  • SQLのクエリなどをGithubなどで管理し、みんなが見れるようにする
  • データ分析の社内勉強会の開催
  • ChatToolなどとの連携
  • 常時数値が見れるような、PUSH型の情報提供

あたりはどこもやっている様子でした。データ分析をみんなが様々な粒度でできるようにしていくと言うのが大事なんだなと思います。

これらをやった最終形態はどんな風になるのかと言うのを最近見つけました。ちょっと長いですが、「成長企業には「最高の分析チーム」が不可欠 メルカリ、DeNA、グリー、楽天の組織作りとキャリアパス - ログミー[o_O]」の記事にある以下の文章はとても心に刺さりました。

B氏というすごい優秀なプロダクトマネージャーがいるんですけど。B氏はもともとエンジニアで、そこからGoogleのプロダクトマネージャーになって、自分でもガリガリ分析ができて、1人でPDCAサイクルを回しながら、必要な場合はコードを書いたりコードレビューをするという、めちゃくちゃすごい人材なんですよね。
 
 
彼に聞くと、Googleのプロダクトマネージャーってけっこうそういう感じで、いわゆる分析の専任が隣にいなくても、ある程度分析やPDCAサイクルを自分で回せるから、ぜんぜんやっていけるという感じなんですよね。
 
 
この話を聞いたときに、僕はすごい後悔したというか、「えー、じゃあ俺らいらないじゃん」みたいな感じになったんです。
 
 
本当にGoogleはすごいなと思っていて、最終的に1人でPDCAサイクルを回せるというのは一番最強だと思っているので、本当に最強な分析チームが行き着く先というのは、分析チームがいなくて、B氏のようなプロダクトマネージャーだらけの会社なのかもしれないと思っています。
 
それは1個の解かなとは思っているのですが、ただ自分はこの分野の専門家もしくは専門部隊のマネージャーとして、それをある程度くつがえせるような、本当に分析の専門部隊だから出せるような価値をつくりたいと思っています。

こんな風に、ありとあらゆる人が、1人でPDCAを回していけるようにするのがやっぱり最終形態なのかなと思います。またアクセスだけできてもそれを元に意思決定を進めていくと言うのが自然とできるようになっていくのが必要なので、徐々に進めていくしかないのかなと思います。

まとめ

なんか最近アクセスしている情報源に偏りがあって、だいぶ影響された感じの記事になってしまいましたが、どの企業も同じように考えていて、それを会社ごとにカスタマイズしているのかなと思います。

こちらの本とかも、これをしっかり考えるきっかけでした。この本は、複数人の著者が1章ずつ担当してバラバラに書いたそうなのですが、複数人の著者がいくつかのテーマで、共通のことを話しているのをみて、自分でも考えてみたいなと思い、今回まとめてみました。

来年から頑張っていきたいなと思います。

*1:最近この手のことについて調べるとメルカリさんの記事がヒットしまくり、且つ参考になりすぎて、めちゃ影響を受けた感じの記事になってしまいました。すみません、メルカリさん。。

*2:特に意味もなくモザイクかけてる笑

【初めてのABEJA Platform】ABEJA Platformを使ってコード0行でresnet50のモデルをデプロイしてみる

こんにちは。

この記事は、ABEJA Platform Adevent Calendarの2日目の記事です。今回は、現在会社で利用しているサービスであるABEJA Platformを使って、学習したモデルをデプロイしてみましたので、そのやり方をまとめます*1

qiita.com

ABEJA Platformとは

f:id:St_Hakky:20181201112618p:plain

サービス概要

ABEJA Platformとは、ちょっと前に資金調達もした、ベンチャー企業のABEJAが提供する、AI・MLOpsのためのプラットフォームです。サービスの概要については、以下のページにあります。

abejainc.com


以下はサービス概要の引用。

ABEJA Platformは、AIの継続的インテグレーションに必要となる、データの取得・蓄積・アノテーション(教師データ作成)・学習・デプロイ・推論、再学習の パイプラインを一貫して実装可能なプラットフォームです。

要約されまくっているので、もう少し深くみてみます。

機械学習の運用のツラミ

サービスの売りとしては、機械学習における運用のツラミを解消すること。

機械学習とかのサービスを実際に提供して運用したことがある方ならわかると思うのですが、様々な辛いことがあると思います。例えば以下のようなことです。

  • 教師データの作成が辛い。。数千枚の画像をアノテーションするってマジかよみたいな。。。
  • 機械学習エンジニアやデータサイエンティストは必ずしも運用に適したコードがかけるわけでも、処理量に応じたインフラをいい感じに構成することができるわけではない。。。
  • 様々に学習した機械学習のモデルをデプロイするのが面倒。。。

私は、正直AWSとかは個人で使って、まぁ普通になんかするくらいは大丈夫なのですが、実際のサービスインを含めて考えてやってくれとか言われると途端にきついなぁってなりますし、自分の書いたロジックを完璧に背景など含めてエンジニアと連携するのは辛いです。

ワイは、ロジックに集中したいし、それ以外のめんどくさいことは本当にやりたくない」というのが、世のデータサイエンティストの本音だと思っています(勝手に)。

具体的にどのくらいデータを用意したら精度がどのくらい上がりそうみたいなのがわかっているケースはないと思いますが、「1万枚アノテーションしたら成果出そうだなぁ」と思った時に、やりたいと思うでしょうか。

そんなことはありません(だって、めんどくさいもの)。

f:id:St_Hakky:20181201113502p:plain

世のデータサイエンティストがKaggleを楽しむ理由はこういうところにありますよね。

やりたいことに集中しよう

ABEJA Platformでは、この手のめんどくさいことをまるっとできるっぽいです。運用にフォーカスを当てて開発されているので、データの用意*2やモデルの学習やそのデプロイ、運用までいい感じでできるらしいですね。今後もいろいろ使ってみて、試してみようと思います。

ということで、今回ABEJA Platformのサービスを社内で使えるので、実際にやってみました。

学習済みモデルをコード0行でABEJA Platformでデプロイしてみる

ABEJA Platform初心者なのと、もっとコアな使い方はこの後の皆さんがやってくれるだろうという勝手な気持ちを込めて(真顔)、一旦学習済みのモデルをデプロイしてみます。

ImageNetのデータセットで学習済みのresnet50をコード0行で登録&デプロイ

まずは最も簡単な方法から試してみます。ABEJA Platformに、ImageNetで学習済みのresnet50があったので、こちらでとりあえずデプロイしてみます。resnet50の説明とかはしませんが、とりあえずDeep Learningのモデルということだと抑えてもらえればここでは大丈夫です。

ログイン

ABEJA Platformのコンソールにログインします。登録したメールアドレスとパスワードを入れるだけの簡単なお仕事です。

モデルを登録

ログインした後に、左側のメニューから、Modelを選びます。すると、以下のような画面が出てくると思います。ここで、左上にある「Create Model」というボタンを押します。

f:id:St_Hakky:20181201120600p:plain

するとフォームが現れるので、言われるがまま入力します。今回は、ありもののモデルを使うので、resnet50を選びます。「Deploy after creating」のチェックボックスにもチェックを入れます。入力したら、右下にある「Create Model」をクリックします。

f:id:St_Hakky:20181201120147p:plain

以下のような画面が出てくると思うので、しばらく待ちます。

f:id:St_Hakky:20181201120153p:plain

なんということでしょう。もう準備完了です。

f:id:St_Hakky:20181201121005p:plain

ここまでコード0行です*3

まさかの数クリックでのモデル登録&デプロイ。。。簡単すぎる。。。

登録したモデルをコード0行でAPI化

さて、モデルの登録ができたので、これをデプロイして、API化してみます。ABEJA Platformでは、登録したモデルはAPIとして利用できるみたいですね。

Deploymentから登録したモデルを確認

左側のメニューから、「Deployment」を選択すると、登録してデプロイしたモデルの一覧がみれます。

f:id:St_Hakky:20181201122027p:plain

登録したモデルをクリックすると、以下のような画面になります。

f:id:St_Hakky:20181201122445p:plain

HTTPサービスの作成

先ほどの画面から、「Create HTTP Service」をクリックすると、以下のような画面が出てきます。

f:id:St_Hakky:20181201122726p:plain

必要事項を入力します。

f:id:St_Hakky:20181201122831p:plain

入力すると、APIが作成されるので、あとは普通にAPIとして使うだけ、って感じです。

まとめ

今回は、2日目ということもあり、最も簡単な部分を試してみました。コード0行でやってみるみたいなところをやりたかったのであれですが、普通にコードを書いて学習やデプロイなどもできます。

これならAPIを無駄に作る必要もないですし、色々と便利ですね。。

それでは〜

*1:正直もっといろいろ書くつもりだったけど、時間がなかったからとりあえずこれで。。笑

*2:厳密には、ABEJA Platformの中にあるAnnotationToolというサービスで提供しています

*3:正確には、resnet50のコードは事前にABEJAで用意してくださっているので、書いていることになるのですがそこらへんの細かいことは気にしない

【Redash】ユーザーの削除方法について(GUI/Redash CLI/postgres)

こんにちは。

最近、社内のプロジェクトの一環として、Redashを導入したのですが、その際に「そういえば、ユーザーの削除ってどうやってやるんやろ」って思って調べて見ました。

結論

今のところGUI上ではバージョン5でないとできないので(びっくらこいた)、postgresかCLIで対応する必要があります。

対処方法

バージョン5

Admin権限を持っているユーザーであれば、Disableボタンをクリックすることで、アカウントをDisableにできるようになりました。

f:id:St_Hakky:20181014143515j:plain

バージョン5以前における削除の方向性

操作履歴があると削除ができないです。すなわち、ユーザーがログインしているなどの条件があると、消すことができません(めんどくさい)。

なので、削除の流れとしては、

  1. 削除したいユーザー情報を取得する(メールアドレスやuserIDなど)
  2. そのユーザーのイベントを全て削除する
  3. ユーザーのアカウントを削除する

という感じになります。

削除するためには、Redash CLIかRedashのpostgresから直接削除する、の2つのパターンがあるのでそれぞれ紹介します。

Redash CLIからの削除

これに関しては、以下にめちゃめちゃよくまとまっているので、割愛します。

kakakakakku.hatenablog.com

Redashのpostgresから削除

postgresから削除する方法も載せておきます。以下の流れで削除できます。

# redashのpostgresへログインする

# redash上の削除したいユーザーIDの確認
$ select * from users;

# redash上におけるそのユーザーのイベントの削除
$ delete from events where user_id = <user_id>;

# ユーザーの削除
$ delete from users where user_id = <user_id>;
その他

以下の記事は削除というよりも、アクセスできないようにするという観点で対処している例。これもありかもしれない。

ariarijp.hatenablog.com

感想

割とユーザーの削除って管理者権限を持つ人用に最初に作られてもいいような気もするけど、バージョン5まで引っ張ったのには理由があるのかな笑。

リリースされたらバージョン上げまする。

決定木の可視化ライブラリ「dtreeviz」が凄かったのでまとめる

こんにちは。

決定木の可視化といえば、正直scikit-learnとgraphvizを使うやつしかやったことがなかったのですが、先日以下の記事をみて衝撃を受けました。そこで今回は、以下の解説記事中で紹介されていたライブラリ「dtreeviz」についてまとめます。

explained.ai

dtreevizの概要

dtreevizとは

より良い決定木の可視化を目指して作られたライブラリです。

多分百聞は一見に如かずだと思うので、Githubに掲載されているサンプル画像を載せます。

f:id:St_Hakky:20180930141746p:plain
f:id:St_Hakky:20180930141817p:plain
f:id:St_Hakky:20180930141831p:plain

すごい。とにかくすごい。

そもそも良い決定木の可視化とはどういうものなのか?

詳しくは、解説記事に記載されていますが、ここではそこで紹介されていた決定木を可視化することによって、見たいポイントをリストアップします。

  • 決定木の各ノードにおける特徴と目標の値の分布
  • 決定木の各ノードにおける特徴量の名前とその特徴量の分割値
  • 葉ノードの純度
  • 葉ノードの予測値
  • 決定木の各ノードのサンプル数
  • 葉ノードのサンプル数
  • ある特徴ベクトルが樹木を葉に向かってどのように走らせるか(これは、特定の特徴ベクトルによってなぜその予測値を得るのかを説明するのに役立ちます)

このような決定木をベースに見られる重要なポイントを見るために、「dtreeviz」がとても便利です。

結構仕事で決定木とかを使うこともあるのですが、可視化があまりイケてなくて、どうしよっかなーと思っていたところに、このライブラリがきたので感動しました。

従来の決定木の可視化と比較して見ると、、、

通常決定木を可視化させようとすると、scikit-learnとgraphvizを使っての可視化になりますが、それがあんまりいけてないんですよね、、、。

もはや比較するまでもない感がありますが、解説記事中で紹介されているirisのデータで決定木を構築し、可視化させて見た結果を見比べると一目瞭然です。。。

f:id:St_Hakky:20180930143950p:plain


なんというかテンションも上がります笑。色合いとかもモダンな感じですね。。。

解説記事では、R・SAS・SPSSなどの可視化とも比較されているので、興味がある方はぜひ解説記事を見ていただければと思います。以下はSPSSの可視化の内容。scikit-learnよりはいい感じですね。

http://explained.ai/decision-tree-viz/images/spss-tree.png


解説記事中でも紹介されていますが、「dtreeviz」のライブラリ自体は、以下の「A visual introduction to machine learning」にかなりの影響を受けて作成したみたいです。だいぶまえにこれを見たときに、可視化ってすげーなーって思った記憶があります。可視化でこんなに変わるんだなぁって思いますし、そうだからこそ、もっとこだわっていきたい。。。

www.r2d3.us

「dtreeviz」の可視化結果の見方について

分類と回帰の両方においての可視化結果をサンプルデータを使いながら見ていきます。

特徴量と目標ラベル空間の可視化

以下は、Bostonのデータで、priceというターゲットに対してAGEという特徴量のみを使って回帰したものを可視化させた図です。

http://explained.ai/decision-tree-viz/images/boston-TD-AGE.svg

この図から、いくつかのことが読み取れます。

  • 各ノードにおけるAGEの分岐点
  • 各ノードで表示されているグラフの、x軸に平行な点線を見ることで、平均値がわかる
  • 葉ノード以外のノードについては、x軸の範囲が常に一定

決定木の可視化から、特徴量がどこで分岐されていて、それが目標ラベルに対してどのような予測結果の影響を与えているかがわかります。

また、以下は、USER KNOWLEDGEというデータをPEGという一つの特徴量を使って分類したものを可視化させた図です。

http://explained.ai/decision-tree-viz/images/knowledge-TD-PEG.svg

この図からも、いくつかのことが読み取れます。

  • 棒グラフが、各目標となるカテゴリの量を積み上げ式で表現している
  • 葉ノードにおいて、円グラフのサイズがサンプル数のサイズを表していて、直感的にわかりやすい

解説記事によると、円グラフは、その評判の悪さを知りながら、解釈性の高さを意識して使っていると書かれていました。確かに、円グラフの大小を見ることでサンプル数の大きさを直感的に理解できるので、とてもわかりやすいです。

ある一つの観測結果の解釈の可視化

ある特徴量が特定の値を取ったときに、それがどのような結果に繋がるかの可視化もできるみたいです。以下の図みたいにできるそうですね。

http://explained.ai/decision-tree-viz/images/samples/knowledge-TD-3-X.svg

分岐点を見ればまぁ同じことができるのですが、説明時にはとてもわかりやすいですね。。。

左から右方向での可視化

個人的には、これも確かにそうだなって思ったのですが、いつも上から下の図しか見てなかったので、左から右というのはありだなと思いました。

http://explained.ai/decision-tree-viz/images/samples/wine-LR-3.svg

そのほか

解説記事中では、シンプルなバージョンでの可視化や、試したが採用に至らなかった可視化なども紹介されています。

将来的な部分

以下のランダムフォレストの特徴量をみるライブラリも追加されるみたいですね。

github.com

dtreevizの使い方

インストール

pipでライブラリはインストールできます。

$ pip install dtreeviz

加えて、以下のライブラリもbrewでインストールする必要があるみたいです。

$ brew install poppler
$ brew install pdf2svg
$ brew install graphviz --with-librsvg --with-app --with-pango

ちょっと一番最後のコマンドで以下のエラーでつまづいたのですが、以下のサイトを見たら解決できました。

xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance · Issue #569 · nodejs/node-gyp · GitHub

コード

サンプルコードからやって見ました。

from sklearn.datasets import *
from sklearn import tree
from dtreeviz.trees import *
import graphviz

regr = tree.DecisionTreeRegressor(max_depth=2)
boston = load_boston()
regr.fit(boston.data, boston.target)

viz = dtreeviz(regr,
               boston.data,
               boston.target,
               target_name='price',
               feature_names=boston.feature_names)
              
viz    

簡単ですね。。。実際に作成した画像は以下の通り。

f:id:St_Hakky:20180930172824p:plain

わーい!

他にも使いながらいい感じの可視化を楽しんでみようと思います。

今更感あるけど決定木について調べたのでまとめる

こんにちは。

本当にクソいまさらなんですけど、アンサンブル手法とか勉強していたら復習したくなってきたので、ここで復習もかねてまとめておきます。

決定木とは

決定木の概観

決定木はおそらく機械学習とかをやったことがある人なら確実に一回は見たり使ったり聞いたりしたものではないかと思います。決定木は、ツリー構造の形で分類または回帰のモデルを構築するものです。決定木では、そのアルゴリズムによっては、カテゴリと数値の両方のデータを扱うことができます。

https://upload.wikimedia.org/wikipedia/commons/f/f3/CART_tree_titanic_survivors.png
参考:Decision tree learning - Wikipedia

決定木の目的は、変数とその値の組によって表現されたデータをいくつかのサブセットに分割していくことにあります。そして分割していく過程で、より木の深さを深くせず、且つ汎化性能の高い出力を返せるような木を構築していくことがゴールになります。

決定木はその名の通り"木"なので、木によって以下のように分割を表現します。

  • 非終端ノード:属性のラベルが付けられる
  • 枝:非終端ノードから出ている枝には、その属性の取りうる値が示されている
  • 葉:最終的な分類結果の値

決定木のメリット・デメリット

結果の可読性という観点でこの手法の右に出るものはないんじゃないかというくらい、わかりやすいです。

そもそも、木で表現してくれて、分割の基準がどうなっているかや、分割の基準となるものが上から順番に優先度が付いているのでこれもまたわかりやすいです。

また、あたいのスケールにそれほど依存をしないという点も決定木を作るときのメリットと言えます。

ただ、学習データへの依存が激しく、並列化などもできないというデメリットもあります。

基本となるアルゴリズム

あとで紹介するように、決定木には色々種類があるわけなんですが、最も基本となるアルゴリズムであるID3 (Iterative Dichotomiser 3)についてここでは紹介します。

ここで紹介するアルゴリズムはこちらでも簡潔にわかりやすく書かれています

ID3は、1986 年に、Ross Quinlan によって開発されました。逆戻りをしないという制約の中で実現可能なブランチ空間を、 あるステップにおいて局所的に評価の高いパターンを選択する、トップダウンの貪欲な探索をします。

探索を行っていく過程で、どの変数のどんな値を使って分割していくかが重要になってくるわけで、すなわち識別能力が大事になってきます。

それではどうやって識別能力を定義するかが大事なのだが、ID3はEntropyとInformation Gainを使用して決定木を構築していきます。

エントロピー

情報の乱雑さを表す指標としてエントロピーというものがあります。エントロピーは以下の式で定義されます。

$$
I(p_1, ..., p_n) = \sum_{i=1}^{n} P_i \log_2 \frac{1}{P_i}
$$

ちなみに底数は2でなくても良くて、分類するクラスの数と等しくすると値の最大値が1になって便利。どのような値の時にこのエントロピーが高くなるのかをみるために、この式をコイン投げの例に従って可視化させます。

# coding: utf-8
import numpy as np
import matplotlib.pyplot as plt

# 表の出る確率
P1 = np.linspace(0, 1, 1000)

# 裏の出る確率
P2 = 1 - P1

# エントロピー
I = lambda p1, p2: -p1 * np.log2(p1) - p2 * np.log2(p2)

# エントロピーの値の計算
result = np.array([I(p1, p2) for p1, p2 in zip(list(P1), list(P2))])
result[np.isnan(result)] = 0

# 可視化
plt.scatter(P1, result)
plt.show()

上を実行させたのが以下の図。

f:id:St_Hakky:20170810171002p:plain

この図から、エントロピーの値が大きくなるのは、表か裏、どちらがでるかわからない時ということがわかります。すなわち、情報がよりランダムに近いほど、値が大きくなり、逆に情報がよりランダムで無く偏っている時、値が小さくなります。極端な話、ノードのサンプルが全て同じクラスに属している場合は、エントロピーは0となります。

これを使って決定木の各ノードにおいて識別力を求め、どのような識別を行うかを決めるわけなのですが、それは情報量ゲインで話します。

情報量ゲイン

決定木の構造を決定していく過程では、葉ノードにて分類結果を返すことが目標なので、出来る限り葉ノードでは結果が偏っていてほしいとなります。

つまり、エントロピーが根ノードから葉ノードに行くに連れて徐々に下がっていってほしいわけです。この時、あるノードからあるノードに分割した時にそれらのエントロピーの差を情報量ゲインといい、この情報量ゲインの値が大きければ大きいほどより良い分割ができているということになります。

情報量ゲインの式は以下のアルゴリズムで参照します。ID3のアルゴリズムでは、この情報量ゲインが最大になる分割のための変数とその値を貪欲に探していきます。

ID3のアルゴリズム

アルゴリズムについては、こちらの方の記事をベースにさせていただきました(ありがとうございます)。

1. ルートノード $N$ を作成して全ての訓練データ集合を $N$ に所属させる。

2. もし $N$ に所属するクラスが全て同じ決定 $X$ を与えるなら $N$ に $X$ とラベル付けして処理を終了する。

3. 訓練データ集合 $C$ に対するエントロピーを求める、即ち以下の式を計算する。

$$
M(C)=- \sum_{x \subset D} p_x(C) \log p_x(C)
$$

4. $C$ を変数 $a_i$ の値に応じて分割する。$a_i$ が $v_1 … v_n$ の $n$ 通りの値を持つ場合は以下の通りに分割する。

$$
C_{ij} \subset C (a_i = v_j)
$$

5. 分割した $C_{ij}$ に応じて平均情報量を求める。

$$
M(C)=-\sum_{x \subset D} p_x(C_{ij}) log p_x(C_{ij})
$$

6. 計算した平均情報量から独立変数 $a_i$ の平均情報量の期待値 $M_i$ を以下の式で求める。

$$
M_i= M(C) - \sum_{j = 1}^n M(C_{ij}) \times \frac{\mid C_{ij} \mid }{ \mid C\mid}
$$

※上の式が情報量ゲインの式になります。この式を見ると、親ノードから子ノードに写った時のエントロピーの差の期待値という、情報量ゲインのが式から読み取れると思います。

7. $M_i$ が最大となる独立変数を $a_k$ とする。

8. $N$ のラベルを $a_k$ として、$N$ の子ノード $N_j$ を作成し、それぞれに$C_{kj}$ を所属させる。

9. それぞれの子ノードに対して $N = N_j$, $C = C_{kj}$ として、2 以下の操作を再帰的に行う。

これが、決定木の基本となるアルゴリズムです。このように何かしらの基準に従ってノードの変数とその分割基準を選んでいき、決定木を構築していきます。

決定木のアルゴリズムの種類

決定木のアルゴリズムの概観

決定木のアルゴリズムにはいくつか種類があるのですが、そのアルゴリズムの種類と違いを簡単に表でまとめておきたいと思います。

ID3 C4.5(C5.0) CART CHAID
目的変数の型 離散値 離散値 離散値,連続値 離散値,連続値
説明変数の型 離散値 離散値,連続値 離散値,連続値 離散値,連続値
分岐の数 多分岐可能 多分岐可能 2分岐のみ 多分岐可能
評価基準 情報量ゲイン 情報量ゲイン Gini係数 カイ2乗値
欠損値への対応 不可
枝刈りするか しない する する する

大きく異なるのは、目的変数の型として使用可能なものと、ノードの分割条件を決定する際の評価基準というとこでしょうか。それぞれの手法のメリットやデメリットについては以下の各アルゴリズムで触れたいと思います。

決定木アルゴリズムの選択方法

どの決定木のアルゴリズムを選ぶかは、以下の観点で選ぶと良さそうです。

  1. Will tree be binary or not?
  2. Which attribute will be node?
  3. How missing data will be handled?
  4. If tree becomes too large,how to prune it?

参考:What are the differences between ID3, C4.5 and CART? - Quora

上記の問いに、与えられたデータセットや課題を基にどのように答えたかで、アルゴリズムを選べばいいと思います。それぞれのアルゴリズムがどのような特徴をそなえているかどうかは以下で詳細に触れます。

ID3

アルゴリズム

上で説明したため、割愛。

メリット

上位互換がありすぎてここではあまりとりあげることがないかも…汗

デメリット
  • データ数が少ないとデータが過学習しやすい
  • 分岐を決定する時に一つの変数しか考慮することができない
  • 連続値を取り扱うことができず、また欠損値に弱い

C4.5(C5.0)

C4.5 は、ID3 を基に改良されたもので、数値データの特徴量を動的に離散化するロジックを導入し、全ての特徴量がカテゴリカル変数でなればならないという制約を取り除きました。C4.5 は学習済みの木を if-then で表されるセットに変換し、評価や枝刈り (決定木における不要な部分(枝)を取り除くこと) に用いられます。

アルゴリズム:C4.5とC5.0の違い

まず、このアルゴリズムがあると知った時に、最初に気になったのはこの2つがよく並列でとりあげられていることです。なので、この2つに果たして違いはあるのだろうかというのがふつうに疑問として上がります。

もともと、C4.5はID3の改良版で、C4.5は1997年に商用のC5.0に改善され、以下のような変更点がありました。

  • ブースティングを組み込むことで精度が格段に上がった
  • スケーラビリティが向上しているのでマルチコアCPUで有用

詳細はこちらの記事でみることができますが、実行時間が格段に早くなり、しかもメモリ消費量も抑えられ、精度も向上していることがわかります。

メリット
  • ID3と違い、連続値・離散値の両方を扱うことができ、また欠損値にも対応することができる
  • 過学習の問題を枝刈りで防ぐことができる
  • CARTは木の剪定をクロスバリデーションによって行うため時間がかかるがC4.5は二項信頼限界を使うため一方行でできる
デメリット
  • ゼロの値を持つ空のブランチを構築してしまう
  • 異常値を持つデータでモデルを構築すると、過学習が発生してしまう。これは特にデータにノイズが含まれていた際に、起こる。
参考文献

CART

CART (Classification and Regression Trees) は、C4.5 によく類似した手法ですが、数値データで表現される目的変数 (=回帰) にも対応している特徴があります。CART は、ルールセットを計算するのではなく、バイナリ・ツリーを構築します。なお、scikit-learn は最適化したバージョンの CART を実装しています。

アルゴリズム

CART は ID3 とほぼ同じアルゴリズムですが、指標として、Gini係数という不純度を表す指標を用いて分岐を行います。

$$
Gini Index(t) = 1 - \sum_{j=1}^{k} p^{2}(j | t)
$$

$p(j | t)$ は、ノード $t$ 内におけるクラス $j$ の割合です。この値が大きいほど、クラス内の不純度が小さくなります。

つまり、不純度が最も低ければジニ係数の値は0,不純度が高くなればなるほどジニ係数の値が1に漸近することがわかります.

このGini係数を計算して、親ノードと子ノードの差が大きくなるようなものを分岐の条件として選ぶことで、決定木を構築します。

メリット

[工事中]

デメリット
  • 一つの変数でしか分岐条件を作成できない
  • 構築された木が不安定

CHAID

[工事中]

決定木の可視化

先日、こんな記事を見つけて、取ってもびっくりしました。

sckit-learnとかを使っての可視化って結構微妙だなぁって思っていたんですが、ここまでわかりやすくいい感じで表現してくれるのは、とっても驚きでした。以下の記事でまとめてみました。

st-hakky.hatenablog.com

pandas-datareader でデータをダウンロードしてみた

こんにちは。

今日は、pandas-datareader を使ってデータを株価とか人口データをダウンロードしてみたので、そのことについてまとめます。

pandas-datareader とは

Web上の様々なソースにアクセスできるライブラリ。か元々はPandasのライブラリの一つであったが、Pandasの0.19.0からサポートされなくなったらしく、pandas-datareaderが独立して使われるようになった。

Install

conda
$ conda install -c anaconda pandas-datareader 
pip
$ pip install pandas-datareader

取得可能なデータ一覧

バージョンの0.6.0では、以下のデータが取得できる様子。

  • Google Finance
  • Morningstar
  • IEX
  • Robinhood
  • Enigma
  • Quandl
  • St.Louis FED (FRED)
  • Kenneth French’s data library
  • World Bank
  • OECD
  • Eurostat
  • Thrift Savings Plan
  • Nasdaq Trader symbol definitions
  • Stooq
  • MOEX

Yahoo! Financeのデータが仕様が変わったとかで取得できなくなるようなので、注意が必要。

IEXを使ってみる

Investors Exchange (IEX) という株価のデータを取得してみます。

import pandas_datareader.data as web
from datetime import datetime

f = web.DataReader('F', 'iex', datetime(2015, 2, 9), datetime(2017, 5, 24))

データの取得結果は以下の通り。

f:id:St_Hakky:20180726235613p:plain

こんな感じで、いろんなデータが簡単に取得できるので、いい感じですね。

サンプルのデータが何かしら欲しい時には便利かもしれません。

それでは。

【Python】 Pandas-Profilingを使ってみた

こんにちは。

最近Kaggleとかやっていて、EDAやるのめんどくさいとか思ったりしちゃったりした時に、ざくっと簡単に分析することができないかなぁと思って調べていたら「Pandas-Profiling」というものがあったので、実際に使ってみました。

本家のサイトなど

Githubで公開されています。

Demoを見る感じでは、データを準備したらあとは1行コードを書くだけ、って感じみたいですね。。。すごすぎる。。。

Dependencies

  • インターネットのコネクションが必要:Pandas-Profilingは、BootstrapとかjQueryを使うので(これは注意)
  • python (>= 2.7)
  • pandas (>=0.19)
  • matplotlib (>=1.4)
  • six (>=1.9)

Install

インストールは他のやつと同じです。

pip
$ pip install pandas-profiling
conda
$ conda install pandas-profiling

使い方

基本的にPandas-Profilingが吐き出すレポートはHTMLとCSSをベースにしているので、基本Jupyter Notebookを使うといいのかなと思います(もちろんhtmlとして吐き出すことも可能です)。可視化とかしてぼーっと眺めて分析してとかやるので、基本Jupyterでいいのかなとは思いますが。

以下のNotebookがやってみた例です。簡単すぎて禿げますね。。。

なんか表示が崩れているので、画像でもあげます。

f:id:St_Hakky:20180524090934p:plain

画像をみてもらえればわかりますが、以下のコード一発でいけちゃうという衝撃。

# df に pandasのDataFrameを入れる
pandas_profiling.ProfileReport(df)

こんな感じで可視化してくれます。

f:id:St_Hakky:20180524090821p:plain

また、詳細を表示することもできて、「Toggle details」っていう部分をクリックすると、以下のように表示されます。

f:id:St_Hakky:20180524091029p:plain

基本的にJupyter Notebook以外でも、KaggleのカーネルやGoogleColabでも使えるみたいですので、コンペとか出た時はEDAするときとかは積極的に使っていきたいです。GoogleColabでは、若干手間は必要みたいですが、そんなに難しくないのでいけますね。

それでは。