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” のように、項目が空欄の場合にも起こりる。
これらは 欠損値
と呼ばれ、データサイエンスにおける難所の一つである。あなたのキャリアを通じて向き合い続けることになる厄介な問題と言えるだろう。
文脈に応じて、欠損値は 補完 (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 も調べてみよう。