Spark Summit NA 2019

参考

メモ

気になるセッションを 個人的に気になったセッション に示す。 ただし、個別のDeep Diveネタは除く。

共有

RAPIDS

参考

メモ

概要

一言で言うと…?

PandasライクなAPI、scikit learnライクなAPIなどを提供するライブラリ群。 CUDAをレバレッジし、GPUを活用しやくしている。

コンポーネント群

公式の コンポーネントの関係図 がわかりやすい。 前処理から、データサイエンス(と、公式で言われている)まで一貫して手助けするものである、というのが思想のようだ。

処理性能に関する雰囲気

公式の 性能比較のグラフ がわかりやすい。 2桁台数の「CPUノード」との比較。 DGX-2 1台、もしくはDGX-1 5台。 計算(ロード、特徴エンジニアリング、変換、学習)時間の比較。

関係者

公式ウェブサイトでは、「Contributors」、「Adopters」、「Open Source」というカテゴリで整理されていた。 Contributorsでは、AnacondaやNVIDIAなどのイメージ通りの企業から、Walmartまであった。 Adoptersでは、Databricksが入っている。

分散の仕組みはDaskベース?

Sparkと組み合わせての動作はまだ対応していないようだが、Daskで分散(なのか、現時点でのシングルマシンでのマルチGPU対応だけなのか)処理できるようだ。 RAPIDS0.6に関する公式ブログ記事 によると、Dask-CUDA、Dask-cuDFに対応の様子。

また、Dask-cuMLでは、k-NNと線形回帰に関し、マルチGPU動作を可能にしたようだ。

Dask_with_cuDF_and_XGBoost.ipynb を見ると、DaskでcuDFを使う方法が例示されていた。

Daskのクラスタを定義し、

1
2
3
4
from dask_cuda import LocalCUDACluster

cluster = LocalCUDACluster()
client = Client(cluster)

RMM(RAPIDS Memory Manager)を初期化する。

1
2
3
4
5
6
from librmm_cffi import librmm_config as rmm_cfg

rmm_cfg.use_pool_allocator = True
#rmm_cfg.initial_pool_size = 2<<30 # set to 2GiB. Default is 1/2 total GPU memory
import cudf
return cudf._gdf.rmm_initialize()

また、 RMMの定義 にRAPIDS Memory ManagerのAPI定義が記載されている。

cuDFのAPI定義 にもDaskでの動作について書かれている。 「Multi-GPU with Dask-cuDF」の項目。

cuDF

cuDFのAPI定義 に仕様が記載されている。 現状できることなどの記載がある。

線形なオペレーション、集約処理、グルーピング、結合あたりの基本的な操作対応している。

バージョン0.7での予定

RAPIDS0.6に関する公式ブログ記事 には一部0.7に関する記述もあった。 個人的に気になったのは、Parquetリーダが改善されてGPUを使うようになること、 デバッグメッセージが改善されること、など。

RAPIDS on Databricks Cloud

RAPIDS on Databricks Cloudのブログ を見ると、Databricks CloudでRAPIDSが動くことが書かれている。 ただ、 RAPIDS_PCA_demo_avro_read.ipynb を見たら、SparkでロードしたデータをそのままPandasのDataFrameに変換しているようだった。

Condaでの動作確認

前提

今回AWS環境を使った。 p3.2xlargeインスタンスを利用。

予めCUDA9.2をインストールしておいた。 またDockerで試す場合には、nvidia-dockerをインストールしておく。

動作確認

以下の通り仮想環境を構築し、パッケージをインストールする。

1
2
3
$ conda create -n rapids python=3.6 python
$ conda install -c nvidia -c rapidsai -c pytorch -c numba -c conda-forge \
cudf=0.6 cuml=0.6 python=3.6

その後、GitHubからノートブックをクローン。

1
$ git clone https://github.com/rapidsai/notebooks.git

ノートブックのディレクトリでJupyterを起動。

1
$ jupyter notebook --ip=0.0.0.0 --browser=""

つづいて、notebooks/cuml/linear_regression_demo.ipynbを試した。 なお、%%timeマジックを使っている箇所の変数をバインドできなかったので、 適当にセルをコピーして実行した。

確かに学習が早くなったことは実感できた。

scikit-learn

1
2
3
4
5
6
%%time
skols = skLinearRegression(fit_intercept=True,
normalize=True)
skols.fit(X_train, y_train)
CPU times: user 21.5 s, sys: 5.33 s, total: 26.9 s
Wall time: 7.51 s

cudf + cuml

1
2
3
4
5
6
7
%%time
cuols = cuLinearRegression(fit_intercept=True,
normalize=True,
algorithm='eig')
cuols.fit(X_cudf, y_cudf)
CPU times: user 1.18 s, sys: 350 ms, total: 1.53 s
Wall time: 2.72 s

Dockerで動作確認

つづいてDockerで試す。

1
2
3
$ sudo docker pull rapidsai/rapidsai:cuda9.2-runtime-centos7
$ sudo docker run --runtime=nvidia --rm -it -p 8888:8888 -p 8787:8787 -p 8786:8786 \
rapidsai/rapidsai:cuda9.2-runtime-centos7

Dockerコンテナが起動したら、以下の通り、Jupyter Labを起動する。

1
# bash utils/start-jupyter.sh

線形回帰のサンプルノートブックを試したが、大丈夫だった。

参考)Dockerで動作確認する際の試行錯誤

RAPIDSの公式Getting Started を参考に動かしてみる。

前提

今回は、AWS環境を使った。 予め、CUDAとnvidia-dockerをインストールしておいた。 今回は、CUDA10系を使ったので、以下のようにDockerイメージを取得。

1
$ sudo docker pull rapidsai/rapidsai:cuda10.0-runtime-ubuntu18.04

動作確認。このとき、runtimeにnvidiaを指定する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ sudo docker run --runtime=nvidia --rm nvidia/cuda:10.1-base nvidia-smi
Wed Apr 17 13:28:02 2019
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 418.39 Driver Version: 418.39 CUDA Version: 10.1 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 Tesla M60 Off | 00000000:00:1E.0 Off | 0 |
| N/A 31C P0 42W / 150W | 0MiB / 7618MiB | 81% Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes: GPU Memory |
| GPU PID Type Process name Usage |
|=============================================================================|
| No running processes found |
+-----------------------------------------------------------------------------+

以下のDockerfileを作り、動作確認する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# FROM defines the base image
# FROM nvidia/cuda:10.0
FROM rapidsai/rapidsai:cuda10.0-runtime-ubuntu18.04

# RUN executes a shell command
# You can chain multiple commands together with &&
# A \ is used to split long lines to help with readability
# This particular instruction installs the source files
# for deviceQuery by installing the CUDA samples via apt
RUN apt-get update && apt-get install -y --no-install-recommends \
cuda-samples-$CUDA_PKG_VERSION && \ rm -rf /var/lib/apt/lists/*
# set the working directory
WORKDIR /usr/local/cuda/samples/1_Utilities/deviceQuery

RUN make
# CMD defines the default command to be run in the container
# CMD is overridden by supplying a command + arguments to
# `docker run`, e.g. `nvcc --version` or `bash`CMD ./deviceQuery

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
$ sudo nvidia-docker build -t device-query .
$ sudo nvidia-docker run --rm -ti device-query
$ sudo nvidia-docker run --rm -ti device-query
./deviceQuery Starting...

CUDA Device Query (Runtime API) version (CUDART static linking)

Detected 1 CUDA Capable device(s)

Device 0: "Tesla M60"
CUDA Driver Version / Runtime Version 10.1 / 10.0
CUDA Capability Major/Minor version number: 5.2
Total amount of global memory: 7619 MBytes (7988903936 bytes)
(16) Multiprocessors, (128) CUDA Cores/MP: 2048 CUDA Cores
GPU Max Clock rate: 1178 MHz (1.18 GHz)
Memory Clock rate: 2505 Mhz
Memory Bus Width: 256-bit
L2 Cache Size: 2097152 bytes
Maximum Texture Dimension Size (x,y,z) 1D=(65536), 2D=(65536, 65536), 3D=(4096, 4096, 4096)
Maximum Layered 1D Texture Size, (num) layers 1D=(16384), 2048 layers
Maximum Layered 2D Texture Size, (num) layers 2D=(16384, 16384), 2048 layers
Total amount of constant memory: 65536 bytes
Total amount of shared memory per block: 49152 bytes
Total number of registers available per block: 65536
Warp size: 32
Maximum number of threads per multiprocessor: 2048
Maximum number of threads per block: 1024
Max dimension size of a thread block (x,y,z): (1024, 1024, 64)
Max dimension size of a grid size (x,y,z): (2147483647, 65535, 65535)
Maximum memory pitch: 2147483647 bytes
Texture alignment: 512 bytes
Concurrent copy and kernel execution: Yes with 2 copy engine(s)
Run time limit on kernels: No Integrated GPU sharing Host Memory: No Support host page-locked memory mapping: Yes
Alignment requirement for Surfaces: Yes
Device has ECC support: Enabled
Device supports Unified Addressing (UVA): Yes
Device supports Compute Preemption: No Supports Cooperative Kernel Launch: No
Supports MultiDevice Co-op Kernel Launch: No
Device PCI Domain ID / Bus ID / location ID: 0 / 0 / 30
Compute Mode: < Default (multiple host threads can use ::cudaSetDevice() with device simultaneously) >
deviceQuery, CUDA Driver = CUDART, CUDA Driver Version = 10.1, CUDA Runtime Version = 10.0, NumDevs = 1
Result = PASS

動作確認

コンテナを起動。

1
2
$ sudo docker run --runtime=nvidia --rm -it -p 8888:8888 -p 8787:8787 -p 8786:8786 \
rapidsai/rapidsai:cuda10.0-runtime-ubuntu18.04

1
2
3
4
5
(rapids) root@5d41281699e3:/rapids/notebooks# nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2018 NVIDIA Corporation
Built on Sat_Aug_25_21:08:01_CDT_2018
Cuda compilation tools, release 10.0, V10.0.130

ノートブックも起動してみる。(JupyterLabだった)

1
# bash utils/start-jupyter.sh

サンプルノートブックの確認

回帰分析の内容を確認してみる。

cuml/linear_regression_demo.ipynb

冒頭部分でライブラリをロードしている。

1
2
3
4
5
6
7
8
9
10
import numpy as np
import pandas as pd
import cudf
import os
from cuml import LinearRegression as cuLinearRegression
from sklearn.linear_model import LinearRegression as skLinearRegression
from sklearn.datasets import make_regression

# Select a particular GPU to run the notebook
os.environ["CUDA_VISIBLE_DEVICES"]="2"

cudfcumlあたりがRAPIDSのライブラリか。

途中、cudfを使うあたりで以下のエラーが発生。

1
terminate called after throwing an instance of 'cudf::cuda_error'  what():  CUDA error encountered at: /rapids/cudf/cpp/src/bitmask/valid_ops.cu:170: 48 cudaErrorNoKernelImageForDevice no kernel image is available for execution on the device

参考)Condaで動作確認の試行錯誤メモ

切り分けも兼ねて、Condaで環境構築し、試してみる。

1
2
$ conda create -n rapids python=3.6 python
$ conda install -c nvidia/label/cuda10.0 -c rapidsai/label/cuda10.0 -c numba -c conda-forge -c defaults cudf

エラー。

1
PackageNotFoundError: Dependencies missing in current linux-64 channels:

Condaのバージョンが古かったようなので、conda update condaしてから再度実行。→成功

cumlを同様にインストールしておく。

1
$ conda install -c nvidia -c rapidsai -c conda-forge -c pytorch -c defaults cuml

Jupyterノートブックを起動し、cuml/linear_regression_demoを試す。

cudaをインポートするところで以下のようなエラー。

1
OSError: cannot load library '/home/centos/.conda/envs/rapids/lib/librmm.so': libcudart.so.10.0: cannot open shared object file: No such file or directory

そこで、 https://github.com/rapidsai/cudf/issues/496 を参照し、cudatoolkitのインストールを試す。 状況が変化し、今度は、libcublas.so.9.2が必要と言われた。

1
ImportError: libcublas.so.9.2: cannot open shared object file: No such file or directory

CUDA9系を指定されているように見える。 しかし実際にインストールしたのはCUDA10系。

1
/home/centos/.conda/pkgs/cudatoolkit-10.0.130-0/lib/libcublas.so.10.0

ここでCUDA9系で試すことにする。 改めてCUDA10系をアンインストールし、CUDA9系をインストール後に以下を実行。

1
2
$ conda install -c nvidia -c rapidsai -c pytorch -c numba -c conda-forge \
cudf=0.6 cuml=0.6 python=3.6

その後、少なくともライブラリインポートはうまくいった。

余談だが、手元のJupyterノートブック環境では、%%timeマジックを使ったときに、 そのとき定義した変数がバインドされなかった。 (Dockerで試したときはうまく行ったような気がするが…)

cudfをつかうところで以下のエラー発生。改めてcudatoolkitをインストールする。

1
2
NvvmSupportError: libNVVM cannot be found. Do `conda install cudatoolkit`:
library nvvm not found

その後再度実行。 改めて以下のエラーを発生。

1
what():  CUDA error encountered at: /conda/envs/gdf/conda-bld/libcudf_1553535868363/work/cpp/src/bitmask/valid_ops.cu:170: 48 cudaErrorNoKernelImageForDevice no kernel image is available for execution on the device

インスタンス種類を変えてみる

基本的なことに気がついた。 g3.4xlargeではなく、p3.2xlargeに変更してみた。

うまくいった。

共有

alexisbcook/hello-seaborn

参考

メモ

seabornの公式 から、 seabornのギャラリー の公式ウェブサイトを見ると、 さまざまな例が載っている。 seabornのAPI を見ると、各種APIが載っている。

例えば、seaborn.barplotを見ると、 棒グラフの使い方が記載されている。

基本的な使い方

基本的には、

1
import seaborn as sns

して、

1
sns.lineplot(data=fifa_data)

のように、プロットの種類ごとに定義されたAPIを呼び出すだけ。 もし、グラフの見た目などを変えたいのであれば、matplotlibをインポートし、 設定すれば良い。

1
plt.figure(figsize=(16,6))

や、

1
plt.xticks(rotation=90)

など。

その他、Data Visualization: from Non-Coder to Coderを見ると、 基本的な使い方を理解できるようになっている。

共有

dansbecker/data-leakage

参考

メモ

Leaky Predictor

例では、肺炎発症と抗生物質摂取のケースが挙げられていた。

肺炎が発症したあとで、抗生物質を摂取する。 抗生物質を摂取したかどうかは、肺炎発症の前後で値が変わる。 値が変わることを考慮しないと、抗生物質を摂取しない人は肺炎にならない、というモデルが出来上がる可能性がある。

どう対処するのか?

汎用的な対処方法はなく、データや要件に強く依存する。 Leaky Predictorを発見する コツ は、強い相関のある特徴同士に着目する、 とても高いaccuracyを得られたときに気をつける、など。

Leaky Validation Strategy

例では、train-testスプリットの前に前処理を行おうケースが挙げられていた。

バリデーション対象には、前処理も含めないといけない。 そうでないと、バリデーションで高いモデル性能が得られたとしても、 実際の判定処理で期待したモデル性能が得られない可能性がある。

どう対処するのか?

パイプラインを組むときに、例えばクロスバリデーションの処理内に、 前処理を入れるようにする、など。

クレジットカードのデータの例

クレジットカードの使用がアプリケーションで認められたかどうか、を判定する例。 ここでは、クレジットカードの使用量の特徴が、Leaky Predictorとして挙げられていた。

共有

dansbecker/cross-validation

参考

メモ

いつ使うか?

端的には、データ量が少ないときの効果が大きい。 逆に、データ量が大きいときは、用いなくてもよいかもしれない。

結果論で言えば、クロスバリデーションして結果が書く回で変わらなかったとき、 それはtrain-testスプリットで十分とも言える。

共有

dansbecker/partial-dependence-plots

参考

メモ

PDPはモデルが学習されたあとに計算可能

ただし、様々なモデルに適用可能。

PDPの計算方法

以下のような感じ。

1
2
3
4
5
my_plots = plot_partial_dependence(my_model,       
features=[0, 1, 2], # column numbers of plots we want to show
X=X, # raw predictors data.
feature_names=['Distance', 'Landsize', 'BuildingArea'], # labels on graphs
grid_resolution=10) # number of values to plot on x axis

注意点として挙げられていたのは、grid_resolutionを細かくしたときに、 乱れたグラフが見られたとしても、その細かな挙動に対して文脈を考えすぎること。 どうしてもランダム性があるので、細かな挙動にいちいち気にしているとミスリードになる。

なお、partial_dependenceという関数を用いると、グラフを出力するのではなく、数値データそのものを得られる。

1
2
3
4
my_plots2 = partial_dependence(my_model,       
target_variables=[0, 1, 2], # column numbers of plots we want to show
X=X, # raw predictors data.
grid_resolution=10) # number of values to plot on x axis

なお、微妙にオプションが異なることに注意…。

タイタニックの例

PDPを見ることで、年齢や支払い料金と生存結果の関係を解釈する例が記載されていた。

「考察」に関する議論

PDPで得られた結果を考察すること自体について、議論があるようだ。 意味のある・なし、という点において。

共有

dansbecker/xgboost

参考

メモ

XGBoost自体については、XGBoostの概要がわかりやすい。

チューニングパラメータ

  • n_estimators
    • 大きさは100〜1000の間の値を用いることが多い
  • early_stopping_rounds
    • n_estimators を大きめにしておいて、本パラメータで学習を止めるのが良さそう
  • learning_rate
    • 加減しながら・・・。例では0.05あたりを使っていた。
  • n_jobs
    • 並列処理(コア数の指定)
共有

dansbecker/using-categorical-data-with-one-hot-encoding

参考

メモ

one hot encodingについて。 Kaggleのラーニングコースを読んでみた。

カテゴリ値の判定にカージナリティを利用

コメントにもやや恣意的な…と書かれてはいたが、 カージナリティが低く、dtypeが objectであるカラムをカテゴリ値としたようだ。

1
2
3
4
5
low_cardinality_cols = [cname for cname in candidate_train_predictors.columns if 
candidate_train_predictors[cname].nunique() < 10 and
candidate_train_predictors[cname].dtype == "object"]
numeric_cols = [cname for cname in candidate_train_predictors.columns if
candidate_train_predictors[cname].dtype in ['int64', 'float64']]

カラムのdtype確認

こんな感じで確かめられる。

1
train_predictors.dtypes.sample(10)

結果の例

1
2
3
4
5
6
LandContour     object
ScreenPorch int64
BsmtFinSF2 int64
SaleType object
BedroomAbvGr int64
(snip)

Pandasのget_dummies

1
one_hot_encoded_training_predictors = pd.get_dummies(train_predictors)

学習データとテストデータの列の並びを揃える

学習データとテストデータをそれぞれone hot encodingすると、 それぞれの列の並びが揃わないことがある。 scikit-learnは、列の並びに敏感である。 そこで、DataFrame#alignを用いて並びを揃える。

1
2
3
4
5
one_hot_encoded_training_predictors = pd.get_dummies(train_predictors)
one_hot_encoded_test_predictors = pd.get_dummies(test_predictors)
final_train, final_test = one_hot_encoded_training_predictors.align(one_hot_encoded_test_predictors,
join='left',
axis=1)

なお、joinオプションはSQLにおけるJOINのルールと同等だと考えれば良い。

共有

dansbecker/handling-missing-values

参考

メモ

Kaggleのhandling-missing-values の内容から気になった箇所をメモ。

欠落のあるカラムの排除

最もシンプルな方法として、欠落のあるカラムを排除する方法が挙げられていた。

1
2
3
4
cols_with_missing = [col for col in X_train.columns 
if X_train[col].isnull().any()]
reduced_X_train = X_train.drop(cols_with_missing, axis=1)
reduced_X_test = X_test.drop(cols_with_missing, axis=1)

X_train[col].isnull().any()のところで、カラム内の値のいずれかがNULL値かどうかを確認している。

欠落を埋める

欠落を埋める。

1
2
3
4
5
from sklearn.impute import SimpleImputer

my_imputer = SimpleImputer()
imputed_X_train = my_imputer.fit_transform(X_train)
imputed_X_test = my_imputer.transform(X_test)

なお、SimpleImputer#fit_transformメソッドの戻り値は、numpy.ndarrayである。

また、scikit-learnのimputeの説明を見る限り、カテゴリ値にも対応しているようだ。

欠落を埋めつつ、欠落していたことを真理値として保持

1
2
3
4
5
6
7
8
imputed_X_train_plus = X_train.copy()
imputed_X_test_plus = X_test.copy()

cols_with_missing = (col for col in X_train.columns
if X_train[col].isnull().any())
for col in cols_with_missing:
imputed_X_train_plus[col + '_was_missing'] = imputed_X_train_plus[col].isnull()
imputed_X_test_plus[col + '_was_missing'] = imputed_X_test_plus[col].isnull()
共有

Kaggle入門

参考

メモ

エントリポイント

Kaggleの環境やお作法?になれる目的で、Faster Data Science Educationを見始めた。

共有