Научитесь создавать конвейер ввода для изображений, чтобы эффективно использовать ресурсы ЦП и ГП для обработки набора данных изображений и сократить время обучения для модели глубокого обучения.

Из этого поста вы узнаете

  • Как ресурсы ЦП и ГП используются при простом подходе во время обучения модели?
  • Насколько эффективно использовать ресурсы ЦП и ГП для предварительной обработки данных и обучения?
  • Зачем использовать 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