Как разделить набор данных tensorflow на N наборов данных с помощью перетасовки

#tensorflow #tensorflow2.0

Вопрос:

У меня есть набор данных tensorflow ds , и я хотел бы разделить его на N наборов данных, объединение которых является исходным набором данных и которые не разделяют образцы между собой. Я пытался:

 ds_list = [ds.shard(N,index=i) for i in range(N)]
 

Но, к сожалению, это не случайно: каждый новый набор данных всегда будет получать одни и те же образцы из исходного набора данных. Например, ds_list[0] будет иметь образцы с номерами 0,N,2N,3N…, в то время как ds_list[1] будет иметь 1,N 1,2 N 1,3 N 1…
Существует ли какой-либо способ случайного разбиения исходного набора данных на наборы данных одинакового размера?

К сожалению, простое перетасование ранее не решит проблему:

 import tensorflow as tf
import math

ds = tf.data.Dataset.from_tensor_slices([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ,15, 16, 17, 18, 19, 20])

N=2
ds = ds.shuffle(20)
ds_list = [ds.shard(N,index=i) for i in range(N)]


for ds in ds_list:
    shard_set = sorted(set(list(ds.as_numpy_iterator())))
    print(shard_set)
 

Выход:

     [3, 5, 6, 8, 11, 12, 14, 15, 19, 20]
    [1, 2, 4, 5, 6, 7, 8, 14, 15, 20]
 

Так же, как:

 ds = tf.data.Dataset.from_tensor_slices([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ,15, 16, 17, 18, 19, 20])
N=2
ds_list = []
ds = ds.shuffle(20)
size = ds.__len__()
sub = math.floor(size/N)
for n in range(N):
    ds_sub = ds.take(sub)
    remainder = ds.skip(sub)
    ds_list.append(ds_sub)
    ds = remainder  

for ds in ds_list:
    shard_set = sorted(set(list(ds.as_numpy_iterator())))
    print(shard_set)
 

Ответ №1:

Возможно (для N осколков):

 ds_list = []
ds = ds.shuffle()
size = ds.__len__()
sub = floor(size/N)
for n in range(N):
    ds_sub = ds.take(sub)
    remainder = ds.skip(sub)
    ds_list.append(ds_sub)
    ds = remainder  
 

Комментарии:

1. Спасибо за ваш ответ. Предложенное вами решение не работает, потому что оно не гарантирует, что все образцы находятся в одном и только одном наборе данных «разбиения»

2. Я считаю, что это так, так как в конце цикла ds устанавливается на остаток. Таким образом, точки, добавленные в ds_sub, больше никогда не рассматриваются.

3. Я предполагаю, что это из-за перетасовки, так как операция взятия и пропуска может включать образцы в другом порядке. Не стесняйтесь использовать код, который я предоставил в основном вопросе.

4. Нет, перетасовка происходит раньше. Затем вы продолжаете принимать и пропускать, пока все данные не будут назначены.

5. Вы протестировали свой код? Просто попробуйте код, который я ввел в основной вопрос. Вы увидите, что получите осколки, которые разделяют некоторые образцы

Ответ №2:

Вы можете сначала перетасовать набор данных, а затем разделить его на фрагменты:

 ds = ds.shuffle(buffer_size)
ds_list = [ds.shard(N,index=i) for i in range(N)]
 

Вот buffer_size размер буфера, используемого TF для сортировки. Если размер набора данных невелик, вы можете передать общее количество примеров как buffer_size . В противном случае будет работать меньшее число (что-то вроде 100), которое может поместиться в память.

Комментарии:

1. Спасибо за ваш ответ. Предложенное вами решение не работает, потому что оно не гарантирует, что все образцы находятся в одном и только одном наборе данных «разбиения»

2. Я думаю, что это так, потому что shuffle не рассматривает один и тот же пример несколько раз (поскольку вы не повторяете набор данных). Можете ли вы привести пример, какая обувь в этом же примере поставляется в нескольких вариантах? Если вы используете повтор перед перемешиванием, то то, что вы говорите, имеет смысл, но поскольку здесь этого не происходит, я думаю, что это должно сработать.

3. Привет, вот возможный код для визуальной проверки. Есть некоторые образцы, которые есть в обоих наборах данных, а некоторые-нет. Я отредактировал основной вопрос