https://aihub.or.kr/aidata/13594
한국 이미지(음식)
한국 음식 150종(종별 약 1천 장)의 데이터를 구축한 이미지 데이터 제공
aihub.or.kr
https://github.com/kimhwijin/korean_food_classifier/blob/master/dataset.ipynb
GitHub - kimhwijin/korean_food_classifier
Contribute to kimhwijin/korean_food_classifier development by creating an account on GitHub.
github.com
목표 - 위의 16GB넘는 큰 크기의 메모리상에서 훈련되지 않는 대규모 이미지 데이터셋을 Colab 상에서 훈련이 가능하도록 데이터셋 파이프라인을 구축한다.
과정 :
- kfood.zip 파일을 드라이브에 올린다
- Colab 상에서 구글드라이브에 압축을 푼다.
문제 발생 : 압축푸는 시간은 그렇게 오래걸리지 않았지만, 15만개 가량의 이미지가 Colab과 Drive간에 동기화가 너무 느렸다. Drive 업로드 속도는 파일 개수가 많아질수록 급속도로 느려진다. 따라서 Colab 런타임이 종료되면 고생해서 압축푼 데이터들이 전부 날라간다.
해결 방법 : 어차피 tf.data.Dataset 을 이용해서 전처리 및 훈련을 진행할 예정이여서 이미지들을 레이블로 묶어서 tfrecords로 저장하기로 한다.
새로운 목표 - 데이터셋을 tfrecords로 저장하여 파일수를 줄여 업로드속도를 높히고, 데이터셋 생성시에 바로사용할수 있도록 한다.
과정 :
- 큰범주, 작은 범주 로 나뉘는 데이터셋에 분류 모델 특성상 큰범주는 무시하기로 했다.
- 재귀적으로 큰범주 - 작은범주의 Image Filepath List 를 glob 함수로 구한다.
- 검색한 포맷은 *.j* , *.J*, *.png, *.PNG, *.B* 을 사용했고, .properties 라는 crop 위치정보를 담은 파일이 존재하므로 png는 따로 지정해준다.
- 하나의 범주당 1000개 가량의 이미지가 존재하고, 이를 10개의 tfrecord 파일로 나눠서 보관한다.
- tf.io.TFRecordWriter( tfrecord_path ) 를 통해서 tfrecord 파일을 생성하고
- PIL.Image.open( image_path ) 로 이미지를 로드하고, 이를 numpy 배열로 변환한다.
- numpy 배열을 ByteList, Int64List로 image, label을 담은 example 프로토콜을 생성하고 tfrecord 파일에 작성한다.
문제발생 : tfrecord 파일로 저장할때 기존 데이터보다 데이터 크기가 더 커진다. example 프로토콜 로 저장하는 것 자체가 example, features , feature, [ bytelist, intlist ] 의 형식으로 저장되서 크기가 유의미하게 증가한다.
해결 방법 : TFRecord으로 이미지들을 저장하고 불러오는 방법보다, 더 좋은 방법을 확보했다. 데이터셋의 구조가 어떻든 이미지 데이터들의 파일경로를 묶은 배열을 통해서 from_tensor_slices 으로 이미지 파일경로 데이터셋을 만들고, 그 데이터셋에 map 함수를 통해서 이미지를 추출 및 전처리 한다.
또한 드라이브 동기화 관련한 문제는 드라이브에 zip 파일을 저장하고, colab 로컬상에 압축을 푸는 형태로 결정했다. 압축푸는 시간이 걸리기도하고, 런터임이 종료되면 로컬의 데이터가 전부사라지는 단점이 있긴하지만, 드라이브에 압축을 풀어두고 colab상에서 그 데이터를 사용할 경우 드라이브의 데이터에 접근하는 시간이 상당히 오래걸렸다. 매 훈련반복마다 데이터 15만개에 접근해야하는 상황에서 적합하지 않다고 생각했다.
새로운 목표 - 데이터들의 파일경로를 통한 데이터셋을 만들고, dataset.map 함수를 통해서 이미지 로드 및 전처리를 수행한다.
과정 :
- 데이터의filepath 를 glob 함수로 전부 가져온다. 이미지 형식은 png, jpg, jpeg 로 제한하고 파일 포멧을 lower case 로 변경한다.
- 미리 만들어둔 label-class 쌍을 통해서 filepath에 있는 폴더명과 비교해서 각 데이터마다 one-hot label 을 만들어준다.
문제 발생 : map 함수내에서 한글인코딩정보가 다른문제가 발생했다. 예를들어서 class_to_label 에 저장된 한글 클래스 이름과, 레이블 숫자가 있다고하면, filepath 에서 split 해서 얻은 클래스이름 ( 폴더명 ) 과 인코딩한 바이트 문자열이 일치하지 않는 문제가 발생했다. tensorflow 에 저장될때 문자열은 bytes로 인코딩되는데 인코딩 방식을 동일하게 맞춰줘도 변환된 인코딩형태를 보면 다른 형태를 띄고 있다.
해결 방안 : dataset 을 만들때 make_dataset 파이썬 함수를 이용하는데, 여기에 리스트형식의 filepaths를 인자로 받는다. 이 함수 안에서 labels dataset, filepaths dataset 을 각각 만들어 one hot 을 취하고 tf.data.Dataset.zip 함수를 통해 두 데이터셋을 묶어준다.
- class, label 을 일정하게 유지하기위해서 class, label 쌍들을 한번 csv 파일로 저장하고 매번 csv파일을 로드해서 정보를 가져오도록한다.
- dataset 의 map 함수 내에 파일경로를 통해서 이미지를 파싱할때, 레이블을 추가하기로 한다. 왜냐 레이블에 대한 정보가 파일경로에있고, 이미지 파싱함수가 종료되면 파일경로 정보는 사라지기 때문에.
- filepath dataset 과 label dataset 을 zip 함수로 묶어 하나의 데이터셋으로 만들어 준다.
- dataset repeat 으로 반복하고, 이미지 개수만큼의 버퍼사이즈로 shuffle 한다.
- dataset map 함수로 이미지 파싱, 크롭, 리사이징, 정규화를 진행한다. map 함수에는 tensorflow 연산만 들어가야하는 제약사항이 있어 애를 먹었다.
- dataset map preprocess : parse, crop, resize
- paring 함수에서는 tf.io.read_file(path), tf.image.decode_image(image, channels=3, expand_animations=False) 으로 파싱한다. 여기서 파일이 손상되었을경우에 decode에서 예외가 발생하는 경우가 있다. 이를 대비해서 모든 데이터들을 tf.image.decode_image 파일을 수행해보고 예외가 발생한 손상된 파일을 제거시킨 데이터셋을 사용한다.
- crop 정보를 담은 파일에 이미지 이름으로 정보를 찾고, 있을 경우 이미지를 크롭한다.
- 크롭한 이미지는 파라미터에 따라서, central crop, random crop 으로 나뉘어 정사각형 모양으로 자른다.
- 정사각형 모양으로 잘린 이미지를 모델입력 사이즈로 tf.image.resize(image, IMAGE_SIZE, method="nearest") 으로 리사이즈 한다.
만들어진 데이터셋은 데이터를 가져올때마다 배치 크기만큼 가지고있는 filepath 에서 이미지를 메모리에 로드, 전처리후 반환하는 크기가 큰 데이터셋임에도 작은 메모리에서 훈련이 가능한 데이터셋이다.
'머신러닝 > Korean Food Classifier' 카테고리의 다른 글
[Predict] 최대 정확도 모델을 통해 사진을 입력받아 예측하기 (0) | 2022.02.24 |
---|---|
[Train] 데이터셋과 모델을 이용해서 훈련 방식을 지정해가면서 훈련하기. (0) | 2022.02.24 |
[Model] inception_resnset_v2 직접 구현하기 (0) | 2022.01.31 |