티스토리 친구하기

본문 바로가기

딥러닝/Your own dataset

mnist your own dataset - tensorflow.data.Dataset 2

728x90

Update

2019.8.9: Posting

 

def data_slice_and_batch(DATA_PATH_LIST, labels, bufferSize, batch_size):
	dataset = tf.data.Dataset.from_tensor_slices((path_list, labels))
	dataset = dataset.map(lambda path_list, labels:
		tuple(tf.py_func(_read_py_function, [path_list, labels],
			[tf.int32, tf.uint8])))

	dataset = dataset.shuffle(buffer_size=(bufferSize))
	dataset = dataset.repeat()
	dataset = dataset.batch(batch_size)

	return dataset

 

  def data_slice_and_batch(DATA_PATH_LIST, label_list, bufferSize, batch_size) 함수는 다음의 4가지를 input으로 받는다.

 

  • DATA_PATH_LIST: image data가 저장된 경로들의 list
  • label_list: get_label_from_path 함수를 통해 얻은 label의 list
  • bufferSize: shuffle(데이터 섞기)을 하기 위해 필요한 공간의 크기
  • batch_size: 한번에 학습할 데이터의 수 (이미지의 수)

1-1. 학습할 이미지와 라벨(정답) 나란히 놓기

 

dataset = tf.data.Dataset.from_tensor_slices((DATA_PATH_LIST, label_list))

 

DATA_PATH_LIST에는 학습할 이미지의 경로(path), 그리고 label_list에는 이미지에 해당하는 label(정답)이 담겨있다. 

 

위의 왼쪽의 그림과 같이 이미지의 경로와 label이 제대로 매칭이 되어 놓이지 않으면 엉뚱한 것을 정답으로 학습하므로, 오른쪽과 같이 이미지와 label의 값이 일치하다록 데이터를 정리해야 한다. 앞의 포스팅에서 glob 모듈을 이용해서 이미지를 불러오고, get_label_from_path 함수를 이용하여 저장한 label_list는 위의 오른쪽 그림처럼 순서대로 저장되어 있으므로 tf.data.Dataset.from_tensor_slices에 입력으로 그대로 넣어주면 된다. 5개만 print 해보면 다음과 같은 결과를 얻을 수 있다.

 

 

위는 데이터를 섞은(shuffle) 후에 이미지의 경로와 label을 출력한 것이다. 왼쪽의 array에서 label directory의 이름이 각각 5,5,2,1,3 인 것을 알 수 있는데, 이는 오른쪽 array의 5,5,2,1,3과 같음을 알 수 있다. 

 

1-2. [이미지의 경로 - label]을 [이미지 - label]로 바꾸기

 

  딥러닝은 이미지의 경로와 label이 일치하도록 학습하는 것이 아니라, 이미지와 label이 일치하도록 하습시키는 것이다. 따라서 이미지 경로를 이용해 이미지를 불러오는 함수를 작성해야 한다.

 

dataset = dataset.map(lambda DATA_PATH_LISTlabel_list:

tuple(tf.py_func(_read_py_function, [DATA_PATH_LIST, label_list],

[tf.int32, tf.uint8])))

 

위의 함수에서 우리가 알아야 할 것은 dataset.map 함수, lambda, 그리고 tf.py_func 연산자이다. 

lambda[2]는 다음과 같이 함수를 간단히 한 줄로 나타낼 수 있다.

 

def square(x):
    return x * x
square = lambda x : x * x

 

함수의 이름도 필요가 없고 반환할 값만 콜론(:)뒤에 적으면 되기 때문에 코드가 훨씬 간결해진다.

 

tf.py_func(_read_py_function, [path_list, labels], [tf.int32, tf.uint8])

 

python 코드를 실행히려면 sess.run()을 해주어야 하는데, tf.py_func 연산자[3]는 sess.run() 대신에 tensorflow 그래프 중간에 그래프를 실행하도록 해준다. 우리는 학습을 위해 sess.run()을 하기전에, 이미지 경로를 이용해서 이미지를 불러오기 위해 그래프 중간에 그래프를 한번 실행해야 한다. tf.py_func 연산자는 우리가 정의했던 _read_py_function 함수를 실행하여 이미지와 label를 리턴해준다. 

 

a = Dataset.range(1, 3) --> [1,2,3]
a.map(lambda x: x + 1)  --> [2,3,4]

 

a.map() 함수는 괄호() 안에서 계산된 결과값을 다시 a에 저장하는 역할을 한다. 즉, 코드에서 dataset에는 이미지 경로와 label의 리스트가 저장되어 있었는데, dataset.map()를 실행하면 dataset에는 이미지와 label의 리스트가 tuple의 타입으로 저장되는 것이다.

 

2. 데이터 섞기 (shuffle)

 

  dataset에는 이미지와 label이 0~9까지 순서대로 저장되어 있다. 

 

< without shuffle>

 

< with shuffle >

 

위의 그림에서 첫 번째 그림은 셔플을 하지 않은 경우, 아래의 그림은 셔플을 한 경우이다. 셔플을 하지 않으면 label이 '0'인 경우를 모두 학습한 후 label이 '1'인 경우를 학습하고, 순서대로 label이 '9'인 경우까지 학습한다. 셔플을 하면 label의 순서가 뒤섞이게 되어 여러가지 종류의 label이 골고루 학습된다.

 

아마도 처음으로 학습하는 label이 '0'인 경우로 weight가 치우치기 때문에 나머지 이미지들에 대한 학습이 잘 이루어지지 않는 것으로 생각된다[4]. 위의 경우에서 결과를 비교해보길 바란다.

 

dataset = dataset.shuffle(buffer_size=(bufferSize))

 

셔플은 한 줄의 코드로 구현이 가능하다. 여기서 buffer_size는 셔플할 데이터를 담을 그릇의 크기를 의미한다. 만약 데이터가 60,000개인데 buffer_size=10,000으로 설정하면 10,000개의 데이터만 셔플되고, 나머지의 데이터는 순서대로인 채로 남게 된다. 따라서 buffer_size는 학습하려는 데이터보다 크게 설정해야 한다. Github에 올려놓은 코드에는 60,000개의 이미지 데이터를 학습할 것이기 때문에 bufferSize=100,000으로 설정했다.

 

3. 마지막 데이터에 도달했을 때 다시 처음부터 데이터를 조회하도록 하기

 

dataset = dataset.repeat()

 

  .repeat()는 60,000개의 데이터를 몇 번 반복해서 사용할지 결정하는 함수이다[5]. 괄호()를 비워놓으면 계속 반복해서 사용하라는 뜻이다. 보통 데이터를 몇 번 반복할지는 epoch 수로 제어하기 때문에, .repeat()는 데이터를 학습한 후에 처음부터 데이터를 다시 조회할 때 사용한다.

 

4. 학습할 이미지의 양 정하기

 

dataset = dataset.batch(batch_size)

 

  한 번에 몇 개의 이미지를 학습할지 정하는 것이다. 딥러닝을 학습할 때 시간이 오래걸린다는 얘기를 많이 들었을 것이다. 여기서 학습을 하는데 시간이 걸리는 것은 크게 2가지로 생각된다.

 

  • 학습하는 시간
  • 데이터를 불러오는 시간
  • batch size 증가: batch 당 학습시간 증가, 데이터 불러오는 시간 감소
  • batch size 감소: batch 당 학습시간 감소, 데이터 불러오는 시간 증가

데이터를 불러오기 위해서는 데이터 불러오는 함수를 호출하는 등의 시간이 걸리기 때문에 같은 양의 이미지를 학습시킨다고 가정했을 경우 한 번에 불러오는게 더 효율적이다. 그러면 batch size의 크기를 무조건 크게하면 좋은것인가(?). 정확하지는 않지만 컴퓨터의 성능이 허락한다면 좋다고 생각한다(학습 성능을 비교해보면 batch size가 작으면 적은 데이터에 대한 gradient approximation이, 크게 했을 경우 좀 더 많은 데이터에 대한 gadient approximation이 되기 때문에, batch size를 크게 설정하면 성능이 더 좋다고 한다[6]). 

  딥러닝을 하는 사람들은 학습 속도를 높이기 위해 GPU를 사용한다. GPU의 사용률은 단위 시간 당 GPU의 사용시간을 의미하는데 데이터를 중간 중간에 데이터를 불러오는 것은 CPU를 사용하기 때문에 GPU의 성능을 100% 사용하지 못하는 것이다(GPU 사용률에 영향을 미치는 요인은 이 외에도 여러 가지가 있을 수 있다). 따라서 효율적으로 학습을 하기 위해서는 컴퓨터의 성능이 허락하는 한 batch size의 크게하여 학습하도록 하자(혹시 잘못된 부분이 있으면 지적해주세요!!).

 

 

 

[1] https://cyc1am3n.github.io/2018/09/13/how-to-use-dataset-in-tensorflow.html

[2] https://bluese05.tistory.com/63

[3] https://riptutorial.com/ko/tensorflow/example/13342/tf-py-func%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0

[4] https://bcho.tistory.com/tag/Deep%20learning

[5] https://cyc1am3n.github.io/2018/09/13/how-to-use-dataset-in-tensorflow.html

[6] https://forums.fast.ai/t/batch-size-effect-on-validation-accuracy/413/5

 

 

 

반응형