developer tip

Dataset.map, Dataset.prefetch 및 Dataset.shuffle에서 buffer_size의 의미

copycodes 2020. 11. 6. 18:54
반응형

Dataset.map, Dataset.prefetch 및 Dataset.shuffle에서 buffer_size의 의미


TensorFlow 문서 에 따라 클래스 prefetchmap메서드에는 tf.contrib.data.Dataset모두라는 매개 변수가 buffer_size있습니다.

의 경우 prefetch에있어서, 상기 파라미터로 알려져 buffer_size및 문서에있어서

buffer_size : 프리 페치시 버퍼링 될 최대 요소 수를 나타내는 tf.int64 스칼라 tf.Tensor.

내용 map에있어서, 상기 파라미터로 알려져 output_buffer_size및 문서에있어서

output_buffer_size : (선택 사항) 버퍼링 될 처리 된 요소의 최대 수를 나타내는 tf.int64 스칼라 tf.Tensor.

shuffle방법과 마찬가지로 문서에 따라 동일한 수량이 나타납니다.

buffer_size : 새 데이터 세트가 샘플링 할이 데이터 세트의 요소 수를 나타내는 tf.int64 스칼라 tf.Tensor입니다.

이 매개 변수 사이의 관계는 무엇입니까?

Dataset다음과 같이 객체를 생성한다고 가정 합니다.

 tr_data = TFRecordDataset(trainfilenames)
    tr_data = tr_data.map(providefortraining, output_buffer_size=10 * trainbatchsize, num_parallel_calls\
=5)
    tr_data = tr_data.shuffle(buffer_size= 100 * trainbatchsize)
    tr_data = tr_data.prefetch(buffer_size = 10 * trainbatchsize)
    tr_data = tr_data.batch(trainbatchsize)

buffer위의 스 니펫에서 매개 변수 가 수행하는 역할은 무엇입니까 ?


TL; DR 비슷한 이름에도 불구하고 이러한 인수는 의미가 상당히 다릅니다. buffer_size에서는 Dataset.shuffle()따라서 데이터 집합의 임의성 및 요소 생산되는 순서에 영향을 줄 수 있습니다. buffer_size의는 Dataset.prefetch()단지 그것을 다음의 요소를 생산하는 데 소요되는 시간에 영향을 미칩니다.


buffer_size의 인수 tf.data.Dataset.prefetch()output_buffer_size의 인수는 tf.contrib.data.Dataset.map()튜닝 할 수있는 방법을 제공하는 성능을 모두 인수가 가장에서의 버퍼를 만들 TensorFlow 알려주기 : 귀하의 의견 파이프 라인의 buffer_size요소, 그리고 백그라운드 스레드는 백그라운드에서 그 버퍼를 채울 수 있습니다. ( output_buffer_size에서에서 Dataset.map()로 이동할 때 인수를 제거 tf.contrib.data했습니다 tf.data. 새 코드는 Dataset.prefetch()after map()사용해야 동일한 동작을 얻을 수 있습니다.)

프리 페치 버퍼를 추가하면 데이터 전처리를 다운 스트림 계산과 겹치게하여 성능을 향상시킬 수 있습니다. 일반적으로 파이프 라인의 맨 끝에 작은 프리 페치 버퍼 (단일 요소 만 포함)를 추가하는 것이 가장 유용하지만, 특히 단일 요소를 생성하는 시간이 다를 수있는 경우 더 복잡한 파이프 라인은 추가 프리 페치의 이점을 누릴 수 있습니다.

반대로, buffer_size인수 는 변형 tf.data.Dataset.shuffle()임의성영향 줍니다 . 메모리에 맞지 않는 너무 큰 데이터 세트를 처리하기 위해 Dataset.shuffle()변환 ( tf.train.shuffle_batch()대체 하는 함수 와 같은)을 설계했습니다 . 전체 데이터 세트를 섞는 대신 buffer_size요소 의 버퍼를 유지하고 해당 버퍼에서 다음 요소를 무작위로 선택합니다 (사용 가능한 경우 다음 입력 요소로 대체). 의 값을 변경하면 buffer_size셔플 링의 균일성에 영향 줍니다 buffer_size. 데이터 세트의 요소 수보다 더 크면 균일 한 셔플을 얻습니다. 만약 그렇다면1그러면 셔플 링이 전혀 없습니다. 매우 큰 데이터 세트의 경우 일반적인 "충분히 좋은"접근 방식은 학습 전에 데이터를 여러 파일로 무작위로 분할 한 다음 파일 이름을 균일하게 섞은 다음 더 작은 셔플 버퍼를 사용하는 것입니다. 그러나 적절한 선택은 훈련 작업의 정확한 특성에 따라 달라집니다.



의 중요성 buffer_sizeshuffle()

내가 강조하는 @mrry에서 이전의 대답에 후속 싶어 중요성buffer_size의를 tf.data.Dataset.shuffle().

낮은 값 buffer_size경우에 따라 열등한 셔플 링제공 할뿐만 아니라 전체 훈련을 망칠 수 있습니다.


실용적인 예 : 고양이 분류기

예를 들어 이미지에 대해 고양이 분류기를 훈련하고 데이터가 다음과 같은 방식으로 구성되었다고 가정합니다 ( 10000각 카테고리의 이미지 포함).

train/
    cat/
        filename_00001.jpg
        filename_00002.jpg
        ...
    not_cat/
        filename_10001.jpg
        filename_10002.jpg
        ...

데이터를 입력하는 표준 방법 tf.data은 파일 이름 목록과 해당 레이블 목록을 가지고 tf.data.Dataset.from_tensor_slices()데이터 세트를 만드는 데 사용할 수 있습니다.

filenames = ["filename_00001.jpg", "filename_00002.jpg", ..., 
             "filename_10001.jpg", "filename_10002.jpg", ...]
labels = [1, 1, ..., 0, 0...]  # 1 for cat, 0 for not_cat

dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
dataset = dataset.shuffle(buffer_size=1000)  # 1000 should be enough right?
dataset = dataset.map(...)  # transform to images, preprocess, repeat, batch...

위 코드 큰 문제 는 데이터 세트가 실제로 올바른 방식으로 섞이지 않는다는 것입니다. 신기원의 전반기에는 고양이 이미지 만, 후반에는 고양이가 아닌 이미지 만 표시됩니다. 이것은 훈련을 많이 해칠 것입니다.
훈련이 시작될 때 데이터 세트는 첫 번째 1000파일 이름 을 가져 와서 버퍼에 넣은 다음 그중에서 임의로 하나를 선택합니다. 첫 번째 1000이미지는 모두 고양이 이미지 이므로 처음 에는 고양이 이미지 만 선택하겠습니다.

여기서 수정 사항 buffer_size은이보다 큰지 확인 20000하거나 사전에 filenameslabels(분명히 동일한 인덱스를 사용하여) 섞는 것입니다.

모든 파일 이름과 레이블을 메모리에 저장하는 것은 문제가되지 않으므로 실제로을 사용 buffer_size = len(filenames)하여 모든 것이 함께 섞이도 록 할 수 있습니다. tf.data.Dataset.shuffle()무거운 변환 (이미지 읽기, 처리, 일괄 처리 등)을 적용하기 전에 호출해야합니다 .

dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
dataset = dataset.shuffle(buffer_size=len(filenames)) 
dataset = dataset.map(...)  # transform to images, preprocess, repeat, batch...

요점은 셔플 링이 무엇을 할 것인지 항상 다시 확인하는 것입니다. 이러한 오류를 포착하는 좋은 방법은 시간에 따른 배치 분포를 그리는 것입니다 (배치에 학습 세트와 거의 동일한 분포가 포함되어 있는지 확인하십시오.


암호

import tensorflow as tf
def shuffle():
    ds = list(range(0,1000))
    dataset = tf.data.Dataset.from_tensor_slices(ds)
    dataset=dataset.shuffle(buffer_size=500)
    dataset = dataset.batch(batch_size=1)
    iterator = dataset.make_initializable_iterator()
    next_element=iterator.get_next()
    init_op = iterator.initializer
    with tf.Session() as sess:
        sess.run(init_op)
        for i in range(100):
            print(sess.run(next_element), end='')

shuffle()

산출

[298][326][2][351][92][398][72][134][404][378][238][131][369][324][35][182][441][370][372][144][77][11][199][65][346][418][493][343][444][470][222][83][61][81][366][49][295][399][177][507][288][524][401][386][89][371][181][489][172][159][195][232][160][352][495][241][435][127][268][429][382][479][519][116][395][165][233][37][486][553][111][525][170][571][215][530][47][291][558][21][245][514][103][45][545][219][468][338][392][54][139][339][448][471][589][321][223][311][234][314]


I found that @olivier-moindrot is indeed correct, I tried the code provided by @Houtarou Oreki, using the modifications pointed by @max. The code I used was the following:

fake_data = np.concatenate((np.arange(1,500,1),np.zeros(500)))

dataset = tf.data.Dataset.from_tensor_slices(fake_data)
dataset=dataset.shuffle(buffer_size=100)
dataset = dataset.batch(batch_size=10)
iterator = dataset.make_initializable_iterator()
next_element=iterator.get_next()

init_op = iterator.initializer

with tf.Session() as sess:
    sess.run(init_op)
    for i in range(50):
        print(i)
        salida = np.array(sess.run(next_element))
        print(salida)
        print(salida.max())

The code output was indeed a number ranging from 1 to (buffer_size+(i*batch_size)), where i is the number of times you ran next_element. I think the way it is working is the following. First, buffer_size samples are picked in order from the fake_data. Then one by one the batch_size samples are picked from the buffer. Each time a batch sample is picked from the buffer it is replaced by a new one, taken in order from fake_data. I tested this last thing using the following code:

aux = 0
for j in range (10000):
    with tf.Session() as sess:
        sess.run(init_op)
        salida = np.array(sess.run(next_element))
        if salida.max() > aux:
            aux = salida.max()

print(aux)

The maximum value produced by the code was 109. So you need to assure a balanced sample within your batch_size to ensure a uniform sampling during training.

I also tested what @mrry said about performance, I found that the batch_size will prefetch that amount of samples into memory. I tested this using the following code:

dataset = dataset.shuffle(buffer_size=20)
dataset = dataset.prefetch(10)
dataset = dataset.batch(batch_size=5)

Changing the dataset.prefetch(10) amount resulted in no change in memory (RAM) used. This is important when your data does no fit into RAM. I think the best way is to shuffle your data/file_names before feeding them to tf.dataset, and then control the buffer size using buffer_size.


Actually the answer by @olivier-moindrot is not correct.

You can verify it by creating filenames and labels as he/she mention and print the shuffle values.

You will see each shuffle procedure will generate sample randomly with the size equals to buffer size from the dataset.

dataset = dataset.shuffle(buffer_size=1000)
iterator = dataset.make_one_shot_iterator()
next_element = iterator.get_next()
with tf.Session() as sess:
    for i in range(1000):
        print(sess.run(next_element))

참고URL : https://stackoverflow.com/questions/46444018/meaning-of-buffer-size-in-dataset-map-dataset-prefetch-and-dataset-shuffle

반응형