この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
どうも、DA事業本部の大澤です
SageMakerの組み込みアルゴリズムではデータ形式として、RecordIOに対応していることがよくあります。 今回はSageMakerで使われるRecordIO形式のデータの作成と読み込みについて紹介します。
バージョン情報
今回紹介する内容はSageMaker Python SDKに強く依存するため、バージョン情報を書いておきます。
- SageMaker Python SDK 1.36.4
ライブラリの読み込み
まずは必要なライブラリを読み込みます。
import numpy as np
from scipy import sparse
import sagemaker.amazon.common as smac
import tempfile
from sagemaker.amazon.record_pb2 import Record
ndarrayとRecordIO
NumPyの配列であるndarrayからRecordIO形式のファイルに変換し、RecordIO形式のファイルを読み込んで値を確認します。
array_data = np.array([[1.0, 2.0, 3.0], [10.0, 20.0, 30.0]])
labels = np.array([1.0, 0.0])
with tempfile.TemporaryFile() as f:
# ndarrayからRecordIOに変換
smac.write_numpy_to_dense_tensor(f, array_data, labels)
f.seek(0)
# RecordIOを読み込む
records = smac.read_recordio(f)
# 行ごとにデータを取り出す
for record_string in records:
print('record_string', record_string)
record = Record()
record.ParseFromString(record_string)
print('record', record)
print('values', record.features['values'].float64_tensor.values)
print('label', record.label['values'].float64_tensor.values)
出力
record_string b'\n\x1c\n\x06values\x12\x12\x1a\x10\n\x08\x00\x00\x00\x00\x00\x00\xf0?\x12\x01\x01\x1a\x01\x03\x12\x16\n\x06values\x12\x0c\x1a\n\n\x08\x00\x00\x00\x00\x00\x00\x00\x00'
record features {
key: "values"
value {
float64_tensor {
values: 1.0
keys: 1
shape: 3
}
}
}
label {
key: "values"
value {
float64_tensor {
values: 0.0
}
}
}
keys [1]
values [1.0]
shape [3]
label [0.0]
record_string b'\n%\n\x06values\x12\x1b\x1a\x19\n\x10\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?\x12\x02\x00\x02\x1a\x01\x03\x12\x16\n\x06values\x12\x0c\x1a\n\n\x08\x00\x00\x00\x00\x00\x00\xf0?'
record features {
key: "values"
value {
float64_tensor {
values: 1.0
values: 1.0
keys: 0
keys: 2
shape: 3
}
}
}
label {
key: "values"
value {
float64_tensor {
values: 1.0
}
}
}
今回はfloat64形式のデータなので、record.features['values'].float64_tensor.values
で値を参照しています。float32であればrecord.features['values'].float32_tensor.values
といった形で参照方法が変化します。
疎行列(scipy.sparse)とRecordIO
疎行列であるcoo_matrixからRecordIO形式のファイルに変換し、RecordIO形式のファイルを読み込んで値を確認します。 今回はcoo形式の疎行列で試していますが、cscやcsr、lil形式の疎行列でも同様に変換可能です。
array_data = [[0.0, 1.0, 0.0], [1.0, 0.0, 1.0]]
labels = np.array([0.0, 1.0])
sparse_array = sparse.coo_matrix(np.array(array_data))
with tempfile.TemporaryFile() as f:
# 疎行列からRecordIOに変換
smac.write_spmatrix_to_sparse_tensor(f, sparse_array, labels)
f.seek(0)
# RecordIOを読み込む
records = smac.read_recordio(f)
# 行ごとにデータを取り出す
for record_string in records:
print('record_string', record_string)
record = Record()
record.ParseFromString(record_string)
print('record', record)
print('keys', record.features['values'].float64_tensor.keys)
print('values', record.features['values'].float64_tensor.values)
print('shape', record.features['values'].float64_tensor.shape)
print('label', record.label['values'].float64_tensor.values)
出力
record_string b'\n\x1c\n\x06values\x12\x12\x1a\x10\n\x08\x00\x00\x00\x00\x00\x00\xf0?\x12\x01\x01\x1a\x01\x03\x12\x16\n\x06values\x12\x0c\x1a\n\n\x08\x00\x00\x00\x00\x00\x00\x00\x00'
record features {
key: "values"
value {
float64_tensor {
values: 1.0
keys: 1
shape: 3
}
}
}
label {
key: "values"
value {
float64_tensor {
values: 0.0
}
}
}
keys [1]
values [1.0]
shape [3]
label [0.0]
record_string b'\n%\n\x06values\x12\x1b\x1a\x19\n\x10\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?\x12\x02\x00\x02\x1a\x01\x03\x12\x16\n\x06values\x12\x0c\x1a\n\n\x08\x00\x00\x00\x00\x00\x00\xf0?'
record features {
key: "values"
value {
float64_tensor {
values: 1.0
values: 1.0
keys: 0
keys: 2
shape: 3
}
}
}
label {
key: "values"
value {
float64_tensor {
values: 1.0
}
}
}
keys [0, 2]
values [1.0, 1.0]
shape [3]
label [1.0]
RecordIOに変換する際に、write_spmatrix_to_sparse_tensor
を使ってる点が、ndarrayの場合と異なります。
読み込み方はndarrayの場合と基本的に同じですが、疎行列から変換した場合はkeysとshapeというデータが追加されています。
疎行列なので、次のような特定の列が値を持ち、それ以外が0みたいな行列を扱うケースが多いかと思います。 そういった場合でもRecordIOでは必要なデータのみを保持するようになっています。 また、今回はラベル無しで試してみます。
array_data = [[0.0, 1.0, 0.0], [1.0, 0.0, 1.0]]
sparse_array = sparse.coo_matrix(np.array(array_data))
with tempfile.TemporaryFile() as f:
smac.write_spmatrix_to_sparse_tensor(f, sparse_array)
f.seek(0)
records = smac.read_recordio(f)
for record_string in records:
print('record_string', record_string)
record = Record()
record.ParseFromString(record_string)
print('record', record)
print('keys', record.features['values'].float64_tensor.keys)
print('values', record.features['values'].float64_tensor.values)
print('shape', record.features['values'].float64_tensor.shape)
出力
record_string b'\n\x1c\n\x06values\x12\x12\x1a\x10\n\x08\x00\x00\x00\x00\x00\x00\xf0?\x12\x01\x01\x1a\x01\x03'
record features {
key: "values"
value {
float64_tensor {
values: 1.0
keys: 1
shape: 3
}
}
}
keys [1]
values [1.0]
shape [3]
record_string b'\n%\n\x06values\x12\x1b\x1a\x19\n\x10\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?\x12\x02\x00\x02\x1a\x01\x03'
record features {
key: "values"
value {
float64_tensor {
values: 1.0
values: 1.0
keys: 0
keys: 2
shape: 3
}
}
}
keys [0, 2]
values [1.0, 1.0]
shape [3]
必要な値のみが格納されていることがわかります。今回は6要素しかありませんが、これが数百万数千万もしくはそれ以上のデータを扱う場合には省メモリ効果がかなり大きくなります。
さいごに
SageMakerの組み込みアルゴリズムでよく使われるRecordIO形式のデータの作成方法と読み込み方法について紹介しました。 SageMakerのサンプルノートブックに従ってRecordIO形式のデータを作ったけども、これどうやって読み込むんやろか...みたいな時等に試していただければと思います。