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


100

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위의 스 니펫에서 매개 변수 가 수행하는 역할은 무엇입니까 ?


1
"문서"에 대한 404 링크를 찾을 수 없습니다.
Pradeep Singh

답변:


150

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그러면 셔플 링이 전혀 없습니다. 매우 큰 데이터 세트의 경우 일반적인 "충분히 좋은"접근 방식은 학습 전에 데이터를 여러 파일로 무작위로 분할 한 다음 파일 이름을 균일하게 섞은 다음 더 작은 셔플 버퍼를 사용하는 것입니다. 그러나 적절한 선택은 훈련 작업의 정확한 특성에 따라 달라집니다.



이 설명에 대해 여전히 약간의 혼란이 tf.data.Dataset.shuffle()있습니다. 정확한 셔플 링 과정을 알고 싶습니다. 첫 번째 batch_size샘플은 첫 번째 buffer_size요소 에서 무작위로 선택 됩니다.
Bs He

1
@mrry IIUC 셔플 링 파일 이름은 중요합니다. 그렇지 않으면 각 epoch가 0 ... 999 배치에서 동일한 요소를 볼 수 있기 때문입니다. 및 배치 1000.1999; 등, 여기서 1 파일 = 1000 배치라고 가정합니다. 파일 이름 셔플 링을 사용하더라도 여전히 임의성이 아닙니다. 파일 #k의 예제가 모든 시대에서 모두 서로 가깝기 때문입니다. 파일 #k 자체가 무작위이기 때문에 나쁘지 않을 수도 있습니다. 여전히 어떤 경우에는 훈련을 엉망으로 만들 수도 있습니다. 완벽한 셔플을 얻는 유일한 방법 buffer_size은 파일 크기와 동일하게 설정 하는 것 입니다 (물론 파일을 셔플).
최대

Tensorflow RC 15.0.0 함께 dataset.shuffle(buffer_size=1)셔플 계속 발생. 이견있는 사람?
Sergey Bushmanov

@SergeyBushmanov 그것은 당신의 셔플 이전의 변환에 의존 할 수 있습니다.
Xiaolong

129

의 중요성 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...

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


1
다음 샘플은 항상 버퍼 (여기서는 크기 1000)에서 선택됩니다. 따라서 첫 번째 샘플은 처음 1000 개의 파일 이름에서 가져옵니다. 버퍼 크기가 999로 줄어들 기 때문에 다음 입력 ( filename_01001)을 받아 추가합니다. 두 번째 샘플은 이러한 1000 개의 파일 이름에서 무작위로 가져옵니다 (첫 번째 파일 이름 1001 개에서 첫 번째 샘플을 뺀 값).
Olivier Moindrot

1
이 낮은 버퍼 크기의 문제는 첫 번째 배치에만 고양이가 있다는 것입니다. 따라서 모델은 "고양이"만 예측하는 방법을 간단하게 학습합니다. 네트워크를 훈련시키는 가장 좋은 방법은 "고양이"와 "고양이가 아닌"양이 같은 배치를 갖는 것입니다.
Olivier Moindrot

1
tf.summary.histogram시간에 따른 레이블 분포를 그리는 데 사용할 수 있습니다 .
Olivier Moindrot

3
오타가 아닙니다. :) 데이터 세트에는 각 클래스의 이미지가 10k이므로 총 버퍼 크기는 20k 이상이어야합니다. 그러나 위의 예에서는 너무 낮은 1k의 버퍼 크기를 사용했습니다.
Olivier Moindrot

1
예, 버퍼 크기를 데이터 세트 크기로 설정하는 것은 일반적으로 괜찮습니다. 데이터 세트 크기를 초과하는 것은 어쨌든 쓸모가 없습니다 (그리고 셔플하기 전에 데이터 세트를 반복하지 않으면 버퍼가 데이터 세트보다 클 수 없습니다).
Olivier Moindrot

7

암호

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]


2
이는 반복기에 의해 생성 된 모든 요소에 대해 버퍼가 이전에 버퍼에 없었던 데이터 세트의 각 다음 요소로 채워지고 있음을 나타냅니다.
Alex

2

실제로 @ olivier-moindrot의 대답은 올바르지 않습니다.

셔플 값을 언급하고 인쇄 할 때 파일 이름과 레이블을 만들어 확인할 수 있습니다.

각 셔플 프로 시저가 데이터 세트의 버퍼 크기와 동일한 크기로 무작위로 샘플을 생성하는 것을 볼 수 있습니다.

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))

2

@ olivier-moindrot이 실제로 정확하다는 것을 알았고 @max가 가리키는 수정 사항을 사용하여 @Houtarou Oreki가 제공 한 코드를 시도했습니다. 내가 사용한 코드는 다음과 같습니다.

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())

코드 출력은 실제로 1에서 (buffer_size + (i * batch_size))까지 의 숫자였으며 , 여기서 inext_element 를 실행 한 횟수 입니다. 작동 방식은 다음과 같습니다. 먼저, buffer_size 샘플은 fake_data 에서 순서대로 선택됩니다 . 그런 다음 버퍼에서 batch_size 샘플 을 하나씩 선택합니다. 배치 샘플이 버퍼에서 선택 될 때마다 fake_data 에서 순서대로 가져온 새 샘플로 대체됩니다 . 다음 코드를 사용하여 마지막 작업을 테스트했습니다.

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)

코드에 의해 생성 된 최대 값은 109입니다. 따라서 훈련 중에 균일 한 샘플링을 보장하려면 batch_size 내에서 균형 잡힌 샘플 을 확보해야합니다.

또한 @mrry가 성능에 대해 말한 내용을 테스트했는데 batch_size 가 해당 양의 샘플을 메모리로 미리 가져 오는 것으로 나타났습니다 . 다음 코드를 사용하여 이것을 테스트했습니다.

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

변화 dataset.prefetch를 (10) 양은 메모리 (RAM)에 변화가 초래 사용. 이는 데이터가 RAM에 맞지 않을 때 중요합니다. 가장 좋은 방법은 데이터 / 파일 이름을 tf.dataset에 공급하기 전에 섞은 다음 buffer_size를 사용하여 버퍼 크기를 제어하는 ​​것 입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.