티스토리 친구하기

본문 바로가기

딥러닝/Your own dataset

mnist your own dataset - tensorflow.data.Dataset 1

728x90

Update

2019.7.26: Posting

2019.8.17: 폰트 및 오타 수정

 

  이제부터 포스팅하려고 하는 내용이 My own data 시리즈에서 가장 중요한 부분이 될 것이다. 데이터를 학습시키기 위해 우리는 정답(label)을 알고 있는 우리의 이미지를 몇 장 골라서(batch) 우리가 디자인한 deep learning graph에 넣어준다. 학습이 잘 되도록 graph에 넣기 전에 학습할 이미지를 섞어(shuffle) 주는 것도 잊지 말자!! 두서없이 얘기했지만, 위의 내용을 구현하기 위해서는 다음과 같은 순서로 코드를 작성해야 한다.

 

  1. 이미지와 라벨(정답) 매칭시키기 [tf.data.Dataset.from_tensor_slices]
  2. 데이터 섞기(순서대로 학습하면 제대로 학습이 되지 않음) [shuffle]
  3. 마지막 데이터에 도달했을 때 다시 처음 데이터를 조회하도록 하기 [repeat]
  4. 학습할 이미지의 양 정하기 [batch]

이제부터 코드를 보면서 하나씩 구현해 보도록 하자!!

 

import tensorflow as tf
import numpy as np

from glob import glob
from PIL import Image

### 사용할 함수 정의 ###
def get_label_from_path(DATA_LIST):
	label_list = []
	for path in DATA_LIST:
		label = path.split('/')[-2]
		label_list.append(label)
	return label_list

def _read_py_function(DATA_PATH_LIST, LABEL_LIST):
	image = np.array(Image.open(path))
	image.reshape(image.shape[0], image.shape[1], 1)
	LABEL_LIST = np.array(LABEL_LIST, dtype=np.uint8)
	return image.astype(np.int32), LABEL_LIST

def data_slice_and_batch(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
    
batch_size = 500
shuffle_buffer_size = 100000

DATA_PATH_LIST = glob('./mnist_png/training/*/*.png')
LABELS = get_label_from_path(DATA_PATH_LIST)
dataset = data_slice_and_batch(DATA_PATH_LIST, LABELS, shuffle_buffer_size, batch_size)

init = tf.global_variables_initializer()
iterator = dataset.make_initializable_iterator()

with tf.Session() as sess:
	sess.run(init, iterator)
	image, label = sess.run(iterator.get_next())

 

  코드가 갑자기 복잡해져서 죄송한 마음이 든다(ㅜㅜ). 위의 코드에서 가장 중요한 부분은 "def data_slice_and_batch()" 함수로, "dataset = ..." 으로 시작하는 한 줄 한 줄의 코드가 위의 1~4번까지 이미지를 라벨과 매칭 시키는 것부터 학습할 데이터의 양(batch size)을 정하는 것까지 모두를 나타내고 있다. 나머지의 함수들은 "def data_slice_and_batch()" 의 입력으로 넣거나 데이터를 변환하기 위해 필요한 함수들이다. 

  그럼에도 불구하고, 위의 코드에서 정리한 함수들을 하나하나 설명할 필요는 있는 것 같다. 왜냐하면 본 포스팅의 목적은 실제 나의 데이터를 활용하기 위해서 필요한 기술적인 부분들을 설명하기 위함이기 때문이다(이론적인 부분에 대해서는 잘 정리된 블로그들이 많이 있다). 따라서 본 포스팅은 몇 부분으로 나눠서 함수 및 코드 설명을 진행하도록 하겠다. 

 

 

함수 설명

 

1. 데이터 경로의 리스트 → Label의 리스트

 

def get_label_from_path(DATA_PATH_LIST):
	label_list = []
	for path in DATA_PATH_LIST:
		label = path.split('/')[-2]
		label_list.append(label)
	return label_list

 

  "get_label_from_path(DATA_PATH_LIST)" 함수는 glob 모듈을 통해서 label 경로에 대한 리스트가 저장되어 있는 'DATA_PATH_LIST'를 입력으로 받아서 경로에 대한 정보는 제외하고 label에 대한 정보만을 저장하여 리스트로 만드는 함수이다. 

label_list = []

label_list라는 자료형이 list인데 비어있는 list은 empty list 를 하나 만든다. 나중에 여기에다가 결과 값을 넣어 리턴할 것이다.

 

label = path.split('/')[-2]

경로 정보는 './mnist_png/training/9/36655.png' 라는 형식으로 저장되어 있다. split 함수는 문자열을 나누는 함수로, 위의 경로에서 오른쪽에서 두 번째([-2])에 있는 '/'를 기준으로 바로 오른쪽에 위치한 문자를 label에 저장한다. 만약, [-1] 이면 36655.png를 label에 저장하고, [-2]이면 9를 저장, 그리고 [-3]이면 training이라는 문자를 label에 저장할 것이다. 여기에서는 '9'를 저장한다.

 

label_list.append(label) 

split 함수를 이용해서 label에 저장한 '9' 라는 문자열을 좀 전에 만들었던 비어있는 'label_list'라는 리스트에 넣는다.   

 

for path in DATA_PATH_LIST:

for loop를 실행하는 동안, DATA_PATH_LIST에 저장되어 있는 경로들이 순서대로 path에 저장된다. 즉, path 저장되어 있는 것은 './mnist_png/training/9/36655.png'이다. for loop는 DATA_PATH_LIST에 있는 모든 경로 값을 실행하면(여기서는 60,000번) 끝이 난다.

  정리하면, 데이터의 경로가 저장된 리스트인 DATA_PATH_LIST를 받아서 각 리스트의 끝에서 두 번째에 위치하는 문자인 '9'를 label에 저장한 후, 이것들을 'label_list'라는 리스트에 계속 쌓아 넣고, for loop가 끝나면 60,000개의 label 문자열이 쌓인 'label_list'를 리턴한다.

 

2. Image 경로 → Image

 

def _read_py_function(DATA_PATH_LIST, LABEL_LIST):

	image = np.array(Image.open(DATA_PATH_LIST))
	image.reshape(image.shape[0], image.shape[1], 1)
	
	LABEL_LIST = np.array(LABEL_LIST, dtype=np.uint8)
	return image.astype(np.int32), LABEL_LIST

 

  "_read_py_function(DATA_PATH_LIST, LABEL_LIST)" 함수는 데이터의 경로가 저장되어 있는 DATA_PATH_LIST와 "get_label_from_path(DATA_PATH_LIST)" 함수의 결괏값으로 얻은 'label_list'를 입력으로 받아서, Image와 label_list를 그대로 리턴하는 함수이다.

 

image = np.array(Image.open(DATA_PATH_LIST))

PIL 모듈에 있는 Image.open이라는 함수에 'DATA_PATH_LIST'를 넣으면, 경로에 있는 이미지들을 불러올 수 있고, 이를 array 형식으로 image에 저장한다.

 

image.reshape(image.shape[0], image.shape[1], 1)

image에 채널의 의미를 더해주기 위해 reshape을 하였다. 만약 RGB의 color 이미지였다면 '1'이 아니라 '3'이어야 한다. 그러나 MNIST dataset은 grayscale 이미지이므로 '1'로 설정하였다.

 

LABEL_LIST = np.array(LABEL_LIST, dtype=np.uint8)

Label_list에 저장되어 있는 label 리스트들의 데이터들은 어차피 양의 정수들이므로 데이터 타입을 uint8로 바꿔주었다.

 

return image.astype(np.int32), LABEL_LIST

이미지의 데이터 타입도 32bit의 정수 값으로 변환을 하고, 데이터 타입이 변화된 LABEL_LIST와 함께 리턴한다.

  정리하면, 경로 값을 받아서 이미지 불러와 리턴하는 함수라고 생각하면 되겠다.

 

 

def data_slice_and_batch 함수는 다음 포스팅에서 작성하도록 하겠다.

 

 

참조

[1] https://hiseon.me/data-analytics/tensorflow/tensorflow-dataset/

[2] https://medium.com/trackin-datalabs/input-data-tf-data-%EC%9C%BC%EB%A1%9C-batch-%EB%A7%8C%EB%93%A4%EA%B8%B0-1c96f17c3696

 

 

반응형