При записи набора данных TFRecord я также сохранил sklearn StandardScaler с параметрами масштабирования для набора данных. Данные, однако, не были масштабированы. Я хочу масштабировать его в момент чтения, используя функцию tf.data.Dataset.map
, и, хотя я действительно нашел способ сделать это, в настоящее время он выглядит немного громоздким, требуя двух вызовов функции переноса для его работы. Я опубликую пример моего подхода на некоторых фиктивных данных, имитирующих случай записи / чтения:
from sklearn.preprocessing import StandardScaler
import tensorflow as tf
# dummy data
data = np.array([[7, 5, 9], [1, 0, 8], [2, 6, 3,], [2, 3, 6]], dtype=np.float32)
label = np.array([[1], [0], [0], [1]], dtype=np.int32)
# fitting the scale. I use partial fit to simulate the way
# scl will be fit when writing the TFRecordDataset
for row in data:
scl.partial_fit(row.reshape(1, -1))
# *** WRITE EACH ROW TO TFRECORD HERE *** #
# Scale the data
data_s = scl.transform(data)
# Create the tf.data.Dataset. This line simulates reading the TFRecords
# in a TFRecordDataset, and parsing it in a Dataset
dataset = tf.data.Dataset.from_tensor_slices((data, label))
# Scaler function. This is the function called by eager execution
def myscale(segment, label):
return scl.transform(segment.numpy().reshape(1, -1)).reshape(-1), label
# Mapping the scaler function. Here is my point of contention!
mdataset = dataset.map(lambda x, y: tf.py_function(scale, [x, y], (x.dtype, y.dtype)))
# Let's check the outputs:
print('Dataset (only data)')
for element in dataset:
print(element[0], '--', element[1])
print('\nafter scale (only data)')
for element in mdataset:
print(element[0], '--', element[1])
print('\nsklearn scaler.transform output')
print(data_s)
Вывод на печать:
Dataset
tf.Tensor([7. 5. 9.], shape=(3,), dtype=float32) -- tf.Tensor([1], shape=(1,), dtype=int32)
tf.Tensor([1. 0. 8.], shape=(3,), dtype=float32) -- tf.Tensor([0], shape=(1,), dtype=int32)
tf.Tensor([2. 6. 3.], shape=(3,), dtype=float32) -- tf.Tensor([0], shape=(1,), dtype=int32)
tf.Tensor([2. 3. 6.], shape=(3,), dtype=float32) -- tf.Tensor([1], shape=(1,), dtype=int32)
After scale
tf.Tensor([1.7056057 0.65465367 1.0910895 ], shape=(3,), dtype=float32) -- tf.Tensor([1], shape=(1,), dtype=int32)
tf.Tensor([-0.8528029 -1.5275252 0.65465367], shape=(3,), dtype=float32) -- tf.Tensor([0], shape=(1,), dtype=int32)
tf.Tensor([-0.42640144 1.0910895 -1.5275252 ], shape=(3,), dtype=float32) -- tf.Tensor([0], shape=(1,), dtype=int32)
tf.Tensor([-0.42640144 -0.2182179 -0.2182179 ], shape=(3,), dtype=float32) -- tf.Tensor([1], shape=(1,), dtype=int32)
sklearn scaler.transform output
[[ 1.7056057 0.65465367 1.0910895 ]
[-0.8528029 -1.5275252 0.65465367]
[-0.42640144 1.0910895 -1.5275252 ]
[-0.42640144 -0.2182179 -0.2182179 ]]
Как видите, он работает просто хорошо. Но моя маленькая любимая мозоль - это то, как мне нужно дважды обернуть функцию scaler.transform
: одну с привязанной лямбда-функцией, а другую с функцией myscale
внутри py_function
, чтобы я мог использовать активное выполнение Tensorflow и доступ numpy массив моих тензоров для применения scaler.transform.
Итак, мой вопрос: есть ли более простой способ сделать это?
РЕДАКТИРОВАТЬ: я только что заметил еще одну потенциальную проблему с этим решением. Если по какой-либо причине scl
выходит за рамки функции myscaler
, это решение будет нарушено. Поскольку я могу отправлять только тензоры в качестве входных данных для py_function
, я не уверен, что для этого есть простое решение.