Научитесь создавать конвейер ввода для изображений, чтобы эффективно использовать ресурсы ЦП и ГП для обработки набора данных изображений и сократить время обучения для модели глубокого обучения.
Из этого поста вы узнаете
- Как ресурсы ЦП и ГП используются при простом подходе во время обучения модели?
- Насколько эффективно использовать ресурсы ЦП и ГП для предварительной обработки данных и обучения?
- Зачем использовать tf.data для создания эффективного конвейера ввода?
- Как создать эффективный конвейер входных данных для изображений с помощью tf.data?
Как работает наивный подход к конвейеру входных данных и обучению модели?
При создании конвейера входных данных обычно выполняется процесс ETL (извлечение, преобразование и загрузка).
- Извлечение, извлечение данных из различных источников данных, таких как локальные источники данных, которые могут быть с жесткого диска, или извлекать данные из удаленных источников данных, например из облачного хранилища.
- Преобразование: вы перемешиваете данные, создаете пакеты, применяете векторизацию или увеличение изображения.
- Загрузка данных включает в себя очистку данных и преобразование их в формат, который мы можем передать модели глубокого обучения для обучения.
Предварительная обработка данных происходит на CPU, и модель обычно обучается на GPU / TPU.
В наивном подходе к обучению модели ЦП предварительно обрабатывает данные, чтобы подготовить их к обучению модели, в то время как графический процессор / TPU бездействует. Когда GPU / TPU начинает обучение модели, CPU простаивает. Это неэффективный способ управления ресурсами, как показано ниже.
Какие есть варианты ускорения тренировочного процесса?
Чтобы ускорить обучение, нам необходимо оптимизировать извлечение данных, преобразование данных и процесс загрузки данных, причем все это происходит на ЦП.
Извлечение данных: оптимизация данных, считываемых из источников
Преобразование данных: распараллеливание увеличения данных
Загрузка данных: предварительная загрузка данных на шаг впереди обучения
Эти методы будут эффективно использовать ресурсы CPU и GPU / TPU для предварительной обработки данных и обучения.
Как добиться оптимизации входного конвейера?
Оптимизация извлечения данных
Извлечение данных оптимизируется за счет одновременной обработки нескольких файлов. tf.data.interleave () оптимизирует процесс извлечения данных, чередуя операцию ввода-вывода для чтения файла и map () для применения предварительной обработки данных.
Количество перекрывающихся наборов данных указывается аргументом cycle_length, а уровень параллелизма задается параметром num_parallel_calls аргумент. Вы можете использовать AUTOTUNE, чтобы делегировать решение об уровне параллелизма, которого нужно достичь.
num_parallel_calls порождают несколько потоков для использования нескольких ядер на машине для распараллеливания процесса извлечения данных с использованием нескольких процессоров.
Как узнать, сколько процессоров или ядер использовать?
Вы можете найти количество ядер на машине и указать это, но лучший вариант - делегировать уровень параллелизма tf.data с помощью tf.data.experimental.AUTOTUNE.
- AUTOTUNE попросит tf.data динамически настроить значение во время выполнения.
- tf.data найдет правильный бюджет ЦП для всех настраиваемых операций.
- AUTOTUNE определяет уровень параллелизма для размера буфера, бюджета ЦП, а также для операций ввода-вывода.
Распараллеливание преобразования данных
Увеличение изображения, часть предварительной обработки, происходит на центральном процессоре. Каждое увеличение, нормализация, изменение масштаба изображения - дорогостоящая операция, которая замедлит процесс обучения.
Что, если вы можете запускать все эти операции с изображениями, используя все ядра, обрабатывая их параллельно.
tf.data.map () может принимать пользовательскую функцию, содержащую все улучшения изображений, которые вы хотите применить к набору данных.
tf.data.map () имеет параметр num_parallel_calls для создания нескольких потоков для использования нескольких ядер на машине для распараллеливания предварительной обработки с использованием нескольких процессоров.
Кеширование данных
cache () позволяет кэшировать данные в указанном файле или в памяти.
- При кэшировании в памяти при первой итерации данные будут кэшироваться, а при всех последующих итерациях данные будут считываться из кеша.
- При кэшировании файла даже данные первой итерации будут считаны из кешированного файла.
- Кэширование создает одни и те же элементы для каждой итерации, используйте shuffle () для рандомизации элементов в итерациях после кэширования данных.
Предварительная выборка данных путем наложения обработки данных и обучения
Функция предварительной выборки в tf.data перекрывает предварительную обработку данных и обучение модели. Предварительная обработка данных выполняется на шаг впереди обучения, как показано ниже, что сокращает общее время обучения модели.
Количество элементов для предварительной выборки должно быть равно или больше размера пакета, используемого для одного шага обучения. Мы можем использовать AUTOTUNE, чтобы запросить tf.data для динамического выделения значения размера буфера во время выполнения.
Все операции: map, prefetch, interleave, batch, repeat , shuffle, и cache являются частью tf.data, позволяющей создавать
- Более быстрые и эффективные конвейеры данных за счет использования вычислительных ресурсов, GPU, CPU и TPU для эффективного извлечения данных из источника данных.
- Гибкость для обработки различных форматов данных, таких как текстовые данные, изображения и структурированные данные, содержащие числовые и категориальные данные.
- Легко создавать сложные конвейеры входных данных, применяя увеличение данных, перетасовывая набор данных и создавая пакеты данных для обучения
Как создать конвейер данных для пользовательского набора данных изображений с помощью tf.data?
В этом разделе вы построите конвейер ввода данных для популярного набора данных C ats и Fogs от Kaggle.
Здесь мы будем использовать трансферное обучение с использованием MobileNetV2 и TensorFlow 2.3.
Импорт необходимых библиотек
import tensorflow as tf config = tf.compat.v1.ConfigProto() config.gpu_options.allow_growth = True sess = tf.compat.v1.Session(config=config) import numpy as np import pandas as pd import pathlib import os from os import getcwd import pandas as pd from glob import glob import multiprocessing from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
Установите каталоги train и val для набора данных
train_dir=r'\dogs-vs-cats\train_data' val_dir=r'\dogs-vs-cats\validation_data'
Преобразуйте файлы в объект набора данных
Используйте tf.data.Dataset.list_files () для возврата имен файлов на основе соответствующего шаблона глобуса. Здесь мы хотим, чтобы весь файл из подпапки находился в папке train_dir и val_dir, поэтому мы указали «\\ * \\ *»
train_files = tf.data.Dataset.list_files(str(train_dir + '\\*\\*'), shuffle=False) val_files = tf.data.Dataset.list_files(str(val_dir + '\\*\\*'), shuffle=False) #getting the number of files in train and val dataset train_num_files=len([file for file in glob(str(train_dir + '\\*\\*'))]) val_num_files=len([file for file in glob(str(val_dir + '\\*\\*'))]) print("No. of files in Train folder: ",train_num_files) print("No. of files in Val folder: ",val_num_files)
Предварительная обработка набора данных для обучения и проверки
Установите параметры
epoch=10 batch_size = 32 img_height = 224 img_width = 224
Применение технологии предварительной обработки MobileNet V2
#Get class names from the folders class_names = np.array(sorted([dir1 for dir1 in os.listdir(train_dir)])) class_names #To process the label def get_label(file_path): # convert the path to a list of path components separated by sep parts = tf.strings.split(file_path, os.path.sep) # The second to last is the class-directory one_hot = parts[-2] == class_names # Integer encode the label return tf.argmax(tf.cast(one_hot, tf.int32)) # To process the image def decode_img(img): # convert the compressed string to a 3D uint8 tensor img = tf.image.decode_jpeg(img, channels=3) # resize the image to the desired size return tf.image.resize(img, [img_height, img_width]) def process_TL(file_path): label = get_label(file_path) # load the raw data from the file as a string img = tf.io.read_file(file_path) img = decode_img(img) img = preprocess_input(img) return img, label
Оптимизация процесса извлечения и преобразования данных с помощью чередования
Interleave () распараллеливает этап загрузки данных, чередуя операцию ввода-вывода для чтения файла и map () , чтобы применить предварительную обработку данных к содержимому наборов данных.
#Interleaving the train dataset to read the file and apply preprocessing train_dataset = train_files.interleave(lambda x: tf.data.Dataset.list_files(str(train_dir + '\\*\\*'), shuffle=True), cycle_length=4).map(process_TL, num_parallel_calls=tf.data.experimental.AUTOTUNE) #Interleaving the val dataset to read the file and apply preprocessing val_dataset = val_files.interleave(lambda x: tf.data.Dataset.list_files(str(val_dir + '\\*\\*'), shuffle=True), cycle_length=4).map(process_TL, num_parallel_calls=tf.data.experimental.AUTOTUNE)
Количество наборов данных для перекрытия установлено равным 4, что задается аргументом длина_цикла. Уровень параллелизма определяется параметром num_parallel_calls,, для которого установлено значение AUTOTUNE.
Загрузите набор данных для обучения
Кэшировать набор данных в памяти
##Cache the dataset in-memory train_dataset = train_dataset.cache() val_dataset = val_dataset.cache() train_dataset = train_dataset.repeat().shuffle(buffer_size=512 ).batch(batch_size) val_dataset = val_dataset.batch(batch_size)
Метод repeat () класса tf.data.Dataset используется для повторения тензоров в наборе данных.
shuffle () перемешивает набор train_dataset с буфером размером 512 для выбора случайных записей.
batch () возьмет первые 32 записи в зависимости от установленного размера пакета и сделает из них пакет.
train_dataset = train_dataset.repeat().shuffle(buffer_size=512 ).batch(batch_size) val_dataset = val_dataset.batch(batch_size)
Функция предварительной выборки в tf.data перекрывает предварительную обработку данных и обучение модели
train_dataset =train_dataset.prefetch(tf.data.experimental.AUTOTUNE ) val_dataset =val_dataset.prefetch(tf.data.experimental.AUTOTUNE )
Создание увеличения данных для отражения изображения по вертикали и горизонтали, поворота изображения, масштабирования и применения контраста.
data_augmentation = tf.keras.Sequential([ tf.keras.layers.experimental.preprocessing.RandomFlip('horizontal'), tf.keras.layers.experimental.preprocessing.RandomFlip('vertical'), tf.keras.layers.experimental.preprocessing.RandomRotation(0.45), tf.keras.layers.experimental.preprocessing.RandomContrast(0.2), tf.keras.layers.experimental.preprocessing.RandomZoom(0.1),])
Создание модели Transfer Learned путем первого применения дополнения данных
def create_model(): input_layer = tf.keras.layers.Input(shape=(224, 224, 3)) x= data_augmentation(input_layer) base_model = tf.keras.applications.MobileNetV2(input_tensor=x, weights='imagenet',include_top=False) base_model.trainable = False x = tf.keras.layers.GlobalAveragePooling2D()(base_model.output) x = tf.keras.layers.Dense(2, activation='softmax')(x) model = tf.keras.models.Model(inputs=input_layer, outputs=x) model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) return model model= create_model()
При создании порога контрольной точки обучение будет продолжаться до тех пор, пока мы не получим точность проверки 99,96% или пока не будет завершено указанное количество эпох.
class MyThresholdCallback(tf.keras.callbacks.Callback): def __init__(self, threshold): super(MyThresholdCallback, self).__init__() self.threshold = threshold def on_epoch_end(self, epoch, logs=None): val_acc = logs["val_accuracy"] if val_acc >= self.threshold: self.model.stop_training = True my_callback = MyThresholdCallback(threshold=0.9996)
Подгоните набор тренировочных данных к модели
import time start_time= time.perf_counter() history_tfdata =model.fit(train_dataset, steps_per_epoch=int((train_num_files)/batch_size), validation_data= val_dataset, validation_steps=int(val_num_files/batch_size), callbacks=[my_callback], epochs=epoch) print(time.perf_counter()-start_time)
Если мы обучим набор данных с помощью ImageDataGenerator, как показано ниже, мы сможем сравнить разницу во времени обучения.
from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPooling2D from tensorflow.keras.preprocessing.image import ImageDataGenerator, img_to_array, load_img, array_to_img from tensorflow.keras.models import load_model from tensorflow.keras import optimizers, callbacks #Creating Image Train DataGenerator image_gen_train = ImageDataGenerator(rescale=1./255, zoom_range=0.1, rotation_range=45, shear_range=0.1, horizontal_flip=True, vertical_flip=True) train_data_gen = image_gen_train.flow_from_directory(batch_size=batch_size, directory=train_dir, shuffle=True, target_size=(224,224), class_mode='sparse') # Val data generator image_gen_val = ImageDataGenerator(rescale=1./255) val_data_gen = image_gen_val.flow_from_directory(batch_size=batch_size,directory=val_dir, target_size=(224,224),class_mode='sparse') def create_model(): input_layer = tf.keras.layers.Input(shape=(224, 224, 3)) input_layer=preprocess_input(input_layer) base_model = tf.keras.applications.MobileNetV2(input_tensor=input_layer, weights='imagenet', include_top=False) base_model.trainable = False x = tf.keras.layers.GlobalAveragePooling2D()(base_model.output) x = tf.keras.layers.Dense(2, activation='softmax')(x) model = tf.keras.models.Model(inputs=input_layer, outputs=x) model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) return model model_idg=create_model() start_time2= time.perf_counter() history = model_idg.fit( train_data_gen, steps_per_epoch=len(train_data_gen), epochs=10, callbacks=[tboard_callback], validation_data=val_data_gen, validation_steps=len(val_data_gen) ) print(time.perf_counter()-start_time2)
Сравнение времени завершения обучения с использованием входного конвейера tf.data со временем обучения с использованием ImageDataGenerator
Вы можете видеть, что время завершения обучения с использованием tf.data составило 290,53 секунды, в то время как обучение для тех же данных с использованием ImageDataGenerator составило 2594,89 секунды, что является существенным выигрышем с точки зрения времени обучения.
Код доступен здесь
Заключение:
tf.data позволяет создавать эффективные конвейеры входных данных для различных форматов данных за счет эффективного использования вычислительных ресурсов, таких как GPU, CPU и TPU, что сокращает время обучения.
Использованная литература:
Https://github.com/tensorflow/docs/blob/master/site/en/guide/data.ipynb