2.2. データ前処理¶
これまで、すぐに利用できるテンソルとして与えられた合成データを扱ってきた。
2.2.1. データセットの読み込み¶
カンマ区切り値(CSV)ファイルは、表形式データ(スプレッドシートのようなデータ)を保存するために広く用いられている。
CSV
では、各行が1つのレコードに対応し、いくつかの(カンマで区切られた)フィールドから構成される。たとえば、“Albert
Einstein,March 14 1879,Ulm,Federal polytechnic school,field of
gravitational physics” のような形式である。
pandas を用いた CSV ファイルの読み込み方を示すため、ここでは
以下のような CSV ファイル ../data/house_tiny.csv を作成する。このファイルは住宅データセットを表し、各行が1軒の住宅に対応する。列は部屋数(
NumRooms)、屋根の種類(RoofType)、価格(Price)を表す。import os
os.makedirs(os.path.join('..', 'data'), exist_ok=True)
data_file = os.path.join('..', 'data', 'house_tiny.csv')
with open(data_file, 'w') as f:
f.write('''NumRooms,RoofType,Price
NA,NA,127500
2,NA,106000
4,Slate,178100
NA,NA,140000''')
次に、pandas をインポートし、read_csv
でデータセットを読み込む。
import pandas as pd
data = pd.read_csv(data_file)
print(data)
NumRooms RoofType Price
0 NaN NaN 127500
1 2.0 NaN 106000
2 4.0 Slate 178100
3 NaN NaN 140000
2.2.2. データの準備¶
教師あり学習では、ある一連の 入力 値が与えられたときに、指定された
目標 値を予測するようモデルを訓練する。
データセットを処理する最初の段階は、入力値に対応する列と目標値に対応する列を分けることである。
列は名前で選択してもよいし、整数位置に基づくインデックス指定(
iloc)で選択してもよい。すでに気づいたかもしれないが、
pandas は CSV 中の NA を特別な
NaN(not a number)値に置き換える。これは、たとえば “3,,,270000”
のように、項目が空欄になっている場合にも起こる。
このような値は 欠損値
と呼ばれ、データサイエンスにおける主要な難題の1つである。実務では継続的に向き合うことになる問題である。
文脈に応じて、欠損値は 補完(imputation)または
削除(deletion)によって処理する。
補完では欠損値を推定値で置き換え、削除では欠損値を含む行または列をデータセットから取り除く。
以下では、よく用いられる補完のヒューリスティックを示す。
カテゴリ型の入力欄では、
NaN を1つのカテゴリとして扱える。RoofType 列は Slate と NaN の値を取るため、pandas
はこの列を RoofType_Slate と RoofType_nan の2列に変換できる。屋根の種類が
Slate である行では、RoofType_Slate と
RoofType_nan の値はそれぞれ 1 と 0 になる。RoofType の値が欠損している行では、その逆になる。inputs, targets = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)
NumRooms RoofType_Slate RoofType_nan
0 NaN False True
1 2.0 False True
2 4.0 True False
3 NaN False True
数値の欠損値については、よく用いられるヒューリスティックとして、NaN
の項目を対応する列の平均値で置き換える 方法がある。
inputs = inputs.fillna(inputs.mean())
print(inputs)
NumRooms RoofType_Slate RoofType_nan
0 3.0 False True
1 2.0 False True
2 4.0 True False
3 3.0 False True
2.2.3. テンソル形式への変換¶
これで inputs と targets
のすべての項目が数値になったので、テンソルに変換できる(2.1 章
を参照)。
import torch
X = torch.tensor(inputs.to_numpy(dtype=float))
y = torch.tensor(targets.to_numpy(dtype=float))
X, y
(tensor([[3., 0., 1.],
[2., 0., 1.],
[4., 1., 0.],
[3., 0., 1.]], dtype=torch.float64),
tensor([127500., 106000., 178100., 140000.], dtype=torch.float64))
from mxnet import np
X, y = np.array(inputs.to_numpy(dtype=float)), np.array(targets.to_numpy(dtype=float))
X, y
[07:03:52] ../src/storage/storage.cc:196: Using Pooled (Naive) StorageManager for CPU
(array([[3., 0., 1.],
[2., 0., 1.],
[4., 1., 0.],
[3., 0., 1.]], dtype=float64),
array([127500., 106000., 178100., 140000.], dtype=float64))
from jax import numpy as jnp
X = jnp.array(inputs.to_numpy(dtype=float))
y = jnp.array(targets.to_numpy(dtype=float))
X, y
No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)
(Array([[3., 0., 1.],
[2., 0., 1.],
[4., 1., 0.],
[3., 0., 1.]], dtype=float32),
Array([127500., 106000., 178100., 140000.], dtype=float32))
import tensorflow as tf
X = tf.constant(inputs.to_numpy(dtype=float))
y = tf.constant(targets.to_numpy(dtype=float))
X, y
(<tf.Tensor: shape=(4, 3), dtype=float64, numpy=
array([[3., 0., 1.],
[2., 0., 1.],
[4., 1., 0.],
[3., 0., 1.]])>,
<tf.Tensor: shape=(4,), dtype=float64, numpy=array([127500., 106000., 178100., 140000.])>)
2.2.4. 議論¶
以上で、データ列を分割し、欠損値を補完し、
pandas
のデータをテンソルへ変換する方法を学んだ。5.7 章
では、さらにいくつかのデータ処理技法を扱う。
この速習では話を単純化したが、実際のデータ処理ははるかに複雑になりうる。
たとえば、データセットが1つの CSV
ファイルにまとまっているとは限らず、リレーショナルデータベースから抽出された複数のファイルに分散していることもある。
電子商取引アプリケーションであれば、顧客の住所があるテーブルにあり、購買データが別のテーブルにあるかもしれない。
さらに、実務ではカテゴリ型や数値型だけでなく、テキスト文字列、画像、音声データ、点群など、多様なデータ型を扱う。
しばしば、データ処理が機械学習パイプライン全体の最大のボトルネックにならないよう、高度なツールや効率的なアルゴリズムが必要になる。
こうした問題は、コンピュータビジョンや自然言語処理へ進むにつれて現れてくる。
最後に、データ品質にも注意を払わなければならない。
現実世界のデータセットには、外れ値、センサーの誤測定、記録ミスなどがしばしば含まれており、どのモデルに入力する前にも対処が必要である。
2.2.5. 演習¶
たとえば UCI Machine Learning Repository の Abalone などのデータセットを読み込み、その性質を調べよ。欠損値を含む割合はどれくらいだろうか。変数のうち、数値型、カテゴリ型、テキスト型の割合はどれくらいだろうか。
列番号ではなく列名によってデータ列をインデックス指定し、選択してみよ。pandas の indexing のドキュメントには、その方法の詳細が載っている。
この方法でどれほど大きなデータセットまで読み込めると思うか。どのような制約があるだろうか。ヒント:データの読み込み時間、表現、処理、メモリ使用量を考えよ。自分のノートパソコンで試してみよ。サーバー上で試すとどうなるか。
カテゴリ数が非常に多いデータをどのように扱うべきか。カテゴリラベルがすべて一意だったらどうだろうか。後者も含めるべきだろうか。
pandas の代替として何が考えられるか。ファイルから NumPy テンソルを読み込む方法はどうだろうか。Pillow、すなわち Python Imaging Library についても調べよ。