Главной проблемой при обучении нейросетей остаётся нехватка качественной информации. Всем моделям глубокого обучения может потребоваться большой объём данных длГлавной проблемой при обучении нейросетей остаётся нехватка качественной информации. Всем моделям глубокого обучения может потребоваться большой объём данных дл

Растягиваем кошек, чтобы избежать переобучения. Аугментация данных в машинном обучении

bdf66fc1c80cf9f86c7808dae55fe822.jpg

Главной проблемой при обучении нейросетей остаётся нехватка качественной информации. Всем моделям глубокого обучения может потребоваться большой объём данных для достижения удовлетворительных результатов. Для успешного обучения модели данные должны быть разнообразными и соответствовать поставленной задаче. В противном случае пользы от такой сети будет мало. Хорошо известно, что нехватка данных легко приводит к переобучению.

Но вот беда, трудно предусмотреть и собрать данные, которые покрывали бы все ситуации. Допустим, вы хотите научить систему находить на фото конкретную кошку. Вам потребуются снимки этого животного в самых разных позах — будь то сидя, стоя или обдирающей диван.

А если требуется распознавать кошек в принципе, то вариантов становится в разы больше. Видов кошек в природе тысячи, они все разных цветов и размеров. Почему это важно? Представьте, что наш набор данных может содержать изображения кошек и собак. Кошки в наборе смотрят исключительно влево с точки зрения наблюдателя. Неудивительно, что обученная модель может неправильно классифицировать кошек, смотрящих вправо.

Поэтому всегда нужно проверять свою выборку на разнообразие. Если данные не подходят под реальные условия, то и задачу решить не получится.

Что делать, если у нас дефицит данных

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

Тогда остаётся второй путь. Мы можем создать новые примеры искусственно. Этот метод называется аугментацией. Если у вас есть алгоритм генерации новых образцов, их можно смело использовать для обучения.

Возвращаясь к нашим кошками. Представьте, что ваша сеть видела животных только в нормальном сидячем положении. Можно программно изменить перспективу картинки и перевернуть её, как будто кошка запечатлена вверх ногами. Пусть физически это отличается от реально перевёрнутого котика, но для обучения такой вариант все равно намного полезнее, чем обычное фото.

Принцип разумности

В процессе аугментации важно, чтобы придуманные вами изменения действительно напоминали то, что может встретиться в реальности. Нет смысла натягивать текстуру кота на сложную геометрическую фигуру б̶у̶б̶л̶и̶к̶а̶ тора, такое вряд ли вам встретится, но есть смысл повернуть изображение на некоторый угол, так как фотограф мог просто неровно держать камеру.

Реализация

Создание данных, неотличимых от настоящих, требует немалых усилий. Однако существует набор «стандартных» аугментаций, которые применяются повсеместно. Для них в современных фреймворках глубокого обучения уже реализованы готовые высокоуровневые функции. Разумеется, возможность писать собственные функции преобразования также поддерживается.

В Keras (TensorFlow) аугментации изображений происходят через класс ImageDataGenerator в модуле tensorflow.keras.preprocessing.image.

Специальный объект-генератор, который берёт исходные картинки и выдаёт их изменённые версии.

Возьмём изображение кота из интернета:

import random import requests import numpy as np import cv2 import matplotlib.pyplot as plt import tensorflow as tf from tensorflow.keras.preprocessing.image import load_img, img_to_array, ImageDataGenerator from tensorflow.keras.applications.resnet50 import preprocess_input # Ссылка на картинку target_link = 'https://i.pinimg.com/1200x/b3/bd/2a/b3bd2a055c99e034b131f3545163892b.jpg' response = requests.get(target_link, allow_redirects=True) filename = 'local_sample.jpg' with open(filename, 'wb') as file: file.write(response.content) raw_img = load_img(filename) pixel_array = img_to_array(raw_img).astype('uint8') img_tensor = np.expand_dims(pixel_array, 0) # batch из одного элемента plt.axis('off') plt.imshow(img_tensor[0]) plt.show()Наш подопытный

Наш подопытный

Вот с этим изображением мы будем проводить аугментации.

Чтобы сделать это, надо создать генератор ImageDataGenerator, в котором перечислить все аугментации, и вызвать метод .fit() с исходными данными, чтобы посчитались все необходимые величины. Используем метод .flow() для получения аугментированных изображений из исходных, используем next(), чтобы получить следующий пример из генератора.

Для начала сделаем пустой генератор, который не применяет никаких аугментаций. Для рисования результата генерации сделаем вспомогательную функцию.

Скрытый текст

def create_base_gen(): """Инициализация базового генератора Keras.""" gen = ImageDataGenerator(fill_mode='constant', dtype='uint8') gen.fit(img_tensor) return gen def display_batch(generator, input_data, rows=1, cols=5, fix_resnet_colors=False): """Вывод сетки аугментированных изображений.""" total_imgs = rows * cols # Создаем поток iterator = generator.flow(input_data, batch_size=1) plt.figure(figsize=(cols * 4, rows * 3)) for i in range(total_imgs): batch_item = next(iterator) single_img = batch_item[0] # Корректировка цветовой схемы для ResNet if fix_resnet_colors: single_img = single_img.copy() mean_values = [103.939, 116.779, 123.68] for c in range(3): single_img[..., c] += mean_values[c] # Разворот BGR в RGB single_img = single_img[..., ::-1] display_img = np.clip(single_img, 0, 255).astype('uint8') plt.subplot(rows, cols, i + 1) plt.axis('off') plt.imshow(display_img) plt.show()

Аугментации

Берём наш начальный генератор, добавляем ему поля для нужных аугментаций.

Сдвиг

gen_obj = create_base_gen() gen_obj.width_shift_range = 0.2 gen_obj.height_shift_range = 0.2 display_batch(gen_obj, img_tensor)

Отражения

datagen = default_datagen() datagen.horizontal_flip = True # добавляем отражения по горизонтали datagen.vertical_flip = True # добавляем отражения по вертикали plot_augmentation(datagen, data) f3343a56c91000b8bb258548ea16b7c0.png

Вращение

datagen = default_datagen() datagen.rotation_range = 25 # добавляем повороты (в градусах) plot_augmentation(datagen, data) bc76684467c6a52ea338b96cdacac8ca.png

Масштабирование

gen_obj = create_base_gen() gen_obj.zoom_range = [0.2, 1.8] display_batch(gen_obj, img_tensor)ff04c5d53d8e676adf4b35febe7dcac2.png

Наклоны

gen_obj = create_base_gen() gen_obj.shear_range = 30 # display_batch(gen_obj, img_tensor)d1c96b8803dfef7f26b7079902ba0afa.png

Яркость

gen_obj = create_base_gen() gen_obj.brightness_range = [0.5, 2.0] display_batch(gen_obj, img_tensor)fb132649e046d1b5ba0136bccbd2955a.png

Сдвиг цветовых каналов

gen_obj = create_base_gen() gen_obj.channel_shift_range = 70.0 display_batch(gen_obj, img_tensor)8762567349673cba191537c757c3b6c1.png

Комбинация всех вариантов

Аугментации можно и нужно применять одновременно. Протестируем комбинации:

mixed_gen = create_base_gen() mixed_gen.fill_mode = 'nearest' mixed_gen.horizontal_flip = True mixed_gen.vertical_flip = True mixed_gen.width_shift_range = 0.2 mixed_gen.height_shift_range = 0.2 mixed_gen.zoom_range = [0.8, 1.2] mixed_gen.rotation_range = 25 mixed_gen.shear_range = 30 mixed_gen.brightness_range = [0.75, 1.5] mixed_gen.channel_shift_range = 70.0 display_batch(mixed_gen, img_tensor, rows=3, cols=5)bc3c7d9b577f1a4ab1d6fad79dd30ab0.png

Из одной картинки у нас получилось множество модифицированных, которые можно использовать для обучения модели.

Все эти методы аугментации пытаются компенсировать «бедность» маленького датасета. Проблема с кошками, которые смотрят только влево, решается аугментацией отражения. Отражение — одна из самых интуитивно понятных стратегий для увеличения размера или разнообразия данных. Однако это может быть неуместно, когда данные имеют уникальные свойства. Например, асимметричные или чувствительные к направлению данные, такие как буквы или цифры, не могут использовать стратегию отражения, поскольку это приводит к неточным меткам у модели или даже к противоположным меткам.

Ещё один подводный камень существует у аугментации вращения. Изображения поворачиваются на заданный угол, и вновь созданные изображения используются вместе с оригиналами в качестве обучающих образцов. Недостатком вращения является то, что оно может привести к потере информации на границах изображения (потому что поворот прямоугольной картинки по траектории круга приводит к появлению пробелов). Существует несколько возможных решений, например, вращение со случайным заполнением ближайшим соседом (RNR), вращение со случайным отражением (RRR) и вращение со случайным циклическим переносом (RWR) для исправления проблемы границ повернутых изображений. В частности, метод RNR повторяет значения ближайших пикселей для заполнения чёрных областей, метод RRR использует подход на основе зеркального отображения, а метод RWR использует стратегию периодических границ для заполнения пробелов

Создание своего генератора данных

Но базовые аугментации не предел! Библиотека позволяет реализовывать собственные генераторы данных, если стандартных инструментов недостаточно. Для внедрения уникальных методов аугментации необходимо создать новый класс и унаследовать его от базового ImageDataGenerator. Внутри этого класса описываются требуемые параметры и логика обработки.

Скрытый текст

import requests import numpy as np target_link = 'https://i.pinimg.com/736x/63/5b/89/635b891ba4049eed0914f5a036bd6ce5.jpg' response = requests.get(target_link, allow_redirects=True) filename = 'local_sample.jpg' with open(filename, 'wb') as file: file.write(response.content) # Подготовка тензора raw_img = load_img(filename) pixel_array = img_to_array(raw_img).astype('uint8') img_tensor = np.expand_dims(pixel_array, 0)

Второй подопытный
Второй подопытный

Теперь создадим новый класс генератора. Реализуем в нём функции: изменения цветных каналов, добавления шума, случайного вырезания сектора, наложения части картинки на саму на себя, искажения перспективы и размытия.

Скрытый текст

class ComplexAugmentor(ImageDataGenerator): def __init__(self, r_factor=None, # Диапазон красного g_factor=None, # Диапазон зеленого b_factor=None, # Диапазон синего noise_lvl=None, # Уровень шума mask_dim=None, # Размер вырезаемого сектора p_cutout=0.0, # Вероятность вырезания сектора p_mix=0.0, # Вероятность наложения p_warp=0.0, # Вероятность искажения перспективы p_blur=0.0, # Вероятность размытия **kwargs): self._external_preprocessor = kwargs.pop('preprocessing_function', None) super().__init__( preprocessing_function=self._pipeline_handler, **kwargs ) self.rgb_factors = (r_factor, g_factor, b_factor) self.noise_lvl = noise_lvl self.mask_dim = mask_dim self.probs = { 'cutout': p_cutout, 'mix': p_mix, 'warp': p_warp, 'blur': p_blur } def _pipeline_handler(self, input_img): """Пайплан обработки.""" proc_img = input_img.copy().astype(np.float32) # 1. Перспектива if self.probs['warp'] > 0 and random.random() < self.probs['warp']: proc_img = self._transform_perspective(proc_img) # 2. Наложение фрагментов if self.probs['mix'] > 0 and random.random() < self.probs['mix']: proc_img = self._patch_overlay(proc_img) # 3. Цветокоррекция каналов proc_img = self._adjust_channels(proc_img) # 4. Эффекты камеры (размытие и шум) if self.probs['blur'] > 0 and random.random() < self.probs['blur']: proc_img = self._add_motion_blur(proc_img) if self.noise_lvl: proc_img = self._add_gaussian_noise(proc_img) # 5. Удаление секторов if self.mask_dim and self.probs['cutout'] > 0: if random.random() < self.probs['cutout']: proc_img = self._apply_cutout_mask(proc_img) proc_img = np.clip(proc_img, 0, 255) if self._external_preprocessor: proc_img = self._external_preprocessor(proc_img) return proc_img def _adjust_channels(self, img): for idx, bounds in enumerate(self.rgb_factors): if bounds: coeff = random.uniform(bounds[0], bounds[1]) img[:, :, idx] *= coeff return img def _add_gaussian_noise(self, img): noise_map = np.random.normal(0, self.noise_lvl, img.shape) return img + noise_map def _apply_cutout_mask(self, img): h_img, w_img, _ = img.shape sz = self.mask_dim c_y = np.random.randint(0, h_img) c_x = np.random.randint(0, w_img) y_min = max(0, c_y - sz // 2) y_max = min(h_img, c_y + sz // 2) x_min = max(0, c_x - sz // 2) x_max = min(w_img, c_x + sz // 2) img[y_min:y_max, x_min:x_max, :] = 127.0 return img def _patch_overlay(self, img): """Берет часть изображения и вставляет в другое место.""" h, w, _ = img.shape p_h, p_w = h // 3, w // 3 start_y = np.random.randint(0, h - p_h) start_x = np.random.randint(0, w - p_w) crop = img[start_y:start_y + p_h, start_x:start_x + p_w].copy() if random.random() > 0.5: crop = np.flip(crop, axis=1) else: crop *= random.uniform(0.8, 1.2) dest_y = np.random.randint(0, h - p_h) dest_x = np.random.randint(0, w - p_w) img[dest_y:dest_y + p_h, dest_x:dest_x + p_w] = crop return img def _transform_perspective(self, img): """Имитация изменения угла обзора.""" rows, cols = img.shape[:2] src_points = np.float32([[0, 0], [cols, 0], [0, rows], [cols, rows]]) shift_x = cols * 0.2 shift_y = rows * 0.2 dst_points = np.float32([ [random.uniform(0, shift_x), random.uniform(0, shift_y)], [cols - random.uniform(0, shift_x), random.uniform(0, shift_y)], [random.uniform(0, shift_x), rows - random.uniform(0, shift_y)], [cols - random.uniform(0, shift_x), rows - random.uniform(0, shift_y)] ]) matrix = cv2.getPerspectiveTransform(src_points, dst_points) return cv2.warpPerspective(img, matrix, (cols, rows), borderMode=cv2.BORDER_REFLECT) def _add_motion_blur(self, img): """Фильтр размытия в движении.""" k_size = random.randint(5, 15) kernel = np.zeros((k_size, k_size)) mid = int((k_size - 1) / 2) kernel[mid, :] = np.ones(k_size) kernel /= k_size return cv2.filter2D(img, -1, kernel)

Теперь наша аугментация будет куда разнообразнее.

custom_aug = ComplexAugmentor( # Стандартные параметры rotation_range=30, width_shift_range=0.2, horizontal_flip=True, vertical_flip=True, # Цветовые параметры r_factor=(0.5, 1.2), b_factor=(0.7, 1.1), # Шум и дефекты noise_lvl=15.0, mask_dim=130, p_cutout=0.8, # Наложени, Искажения и блюр p_mix=0.5, p_warp=0.7, p_blur=0.3, preprocessing_function=preprocess_input ) custom_aug.fit(img_tensor) display_batch(custom_aug, img_tensor, rows=4, cols=5, fix_resnet_colors=True)5c2e664f7de1e57bf70ed0cf1a37c94f.png

К базовым аугментациям мы добавили несколько новых. Например случайное вырезание (cutout). Это метод аугментации данных, который, как правило, не пытается изменить значения отдельных пикселей изображения. Вместо этого он заменяет значения пикселей внутри прямоугольника произвольного размера на изображении случайным значением. Можно рассматривать случайное вырезание как своего рода шумовую технику, фокусирующуюся на локальных областях, а не на отдельных пикселях. Она предназначена для того, чтобы сделать модель устойчивой к перекрытию объектов на изображениях и, таким образом, снизить вероятность переобучения. Cutout повышает разнообразие данных без увеличения их размера, потому что нам не надо сохранять изображения с вырезанием: оно применяется по время обучения.

Но поскольку метод случайного стирания выбирает прямоугольную область (т.е. область перекрытия) случайным образом, он может полностью стереть информацию об объекте, подлежащем классификации на изображении. Поэтому его не рекомендуется использовать при категоризации чувствительных данных, которые не допускают удаления случайно сгенерированной локальной области на изображениях, как в случаях категоризации номеров и букв номерных знаков.

Интересный факт! Помните такую штуку, когда на изображение добавляли определенную «маску шума», и модель начинала галлюцинировать и путать панду с гиббоном.

Вот этот пример
Вот этот пример

Этот эффект назвали adversarial attacks, также известныё как машинная иллюзия. Adversarial attacks также можно рассматривать как часть семейства аугментации данных путем внедрения шума. При внедрении систематического шума в данное изображение свёрточная нейронная сеть выдаёт совершенно другой прогноз, даже если человеческий глаз не может обнаружить разницу.

Например, в одной работе были созданы adversarial attacks путем изменения одного пикселя на изображение. Adversarial training заключается в добавлении этих примеров в обучающий набор, чтобы сделать модель устойчивой к атакам. Поскольку такие маски могут выявлять слабые места в обученной модели, этот способ можно рассматривать как эффективный подход к аугментации.


Эксперимент, очевидно... удачный.

Аугментация выжала максимум из имеющейся данных. Для сети это означает повышение обобщающей способности без затрат на сбор новых данных. Возможность создавать кастомные генераторы открывает безграничный простор для экспериментов.

Процесс можно адаптировать под любую специфику. Те же медицинские снимки или фото с камер наблюдения. Искажения должны быть достаточно сильными для обучения, но при этом сохранять суть изображения. Экспериментируйте с параметрами и создавайте свои методы обработки. Чем разнообразнее будет опыт модели, тем увереннее она покажет себя в проде.

© 2026 ООО «МТ ФИНАНС»

Источник

Отказ от ответственности: Статьи, размещенные на этом веб-сайте, взяты из общедоступных источников и предоставляются исключительно в информационных целях. Они не обязательно отражают точку зрения MEXC. Все права принадлежат первоисточникам. Если вы считаете, что какой-либо контент нарушает права третьих лиц, пожалуйста, обратитесь по адресу [email protected] для его удаления. MEXC не дает никаких гарантий в отношении точности, полноты или своевременности контента и не несет ответственности за любые действия, предпринятые на основе предоставленной информации. Контент не является финансовой, юридической или иной профессиональной консультацией и не должен рассматриваться как рекомендация или одобрение со стороны MEXC.

Вам также может быть интересно

Какие криптовалюты лучше всего подходят для азартных игр в Web3? Топ-3 варианта

Какие криптовалюты лучше всего подходят для азартных игр в Web3? Топ-3 варианта

Узнайте, какие криптовалюты лучше всего подходят для азартных игр Web3 в 2026 году. Научитесь делать ставки с помощью криптовалюты, сравните Bitcoin, стейблкоины и быстрые сети для крипто
Поделиться
Cryptodaily2026/01/20 23:53
В Игре Hrum Представляется Новая Цитата Дня Для Выполнения Комбо Дейлика на 20 Января

В Игре Hrum Представляется Новая Цитата Дня Для Выполнения Комбо Дейлика на 20 Января

Откройте печеньку с новым предсказанием в Hrum для увеличения депозита! Сегодня, 20 января, ежедневный комбо дейлик с цитатой дня в Hrum обновлен и это значит,
Поделиться
Coinspot2026/01/21 00:09
Кэти Вуд называет Bitcoin системой денег, основанной на правилах, предпочитает его Ethereum

Кэти Вуд называет Bitcoin системой денег, основанной на правилах, предпочитает его Ethereum

Пост «Кэти Вуд называет Биткоин денежной системой, основанной на правилах, и предпочитает его Ethereum» появился на BitcoinEthereumNews.com. Кэти Вуд рассказала в подкасте Master Investor, который ведет Уилфред Фрост, что она не верит в существование множества криптовалют в долгосрочной перспективе. «Биктоин владеет криптовалютным пространством, когда речь идет о чистой крипте. Биктоин — это криптовалюта. Мы думаем, что он станет самым крупным, причем с большим отрывом. С огромным отрывом», — сказала она. Согласно беседе, Кэти разделила то, что она называет «криптовалютами», от «крипто-активов», и поставила Биткоин в самый центр своего прогноза. Она описала Биткоин как денежную систему, построенную на правилах, где предложение ограничено 21 миллионом единиц, из которых около 20 миллионов уже находятся в обращении сегодня. Затем она сравнила Биткоин со стейблкоинами, называя их криптовалютами, но объясняя, что они привязаны к доллару США через обеспечение, в основном состоящее из казначейских ценных бумаг. Кэти сказала, что стейблкоины нашли свое место в DeFi, потому что их можно использовать для получения дохода. Кэти описывает внедрение стейблкоинов и одноранговые финансы Когда ее спросили, зачем людям в таких городах, как Лондон или Нью-Йорк, нужны стейблкоины, когда они уже могут легко перемещать доллары или фунты, Кэти ответила, что на рынке есть два доминирующих игрока. «Tether в основном находится за пределами Соединенных Штатов и за пределами Европы сейчас после Мика — или вы называете его Мика или Мика, я не знаю. Эти два имеют 90% рынка. Circle, так сказать, более соответствует нормативным требованиям, особенно в Соединенных Штатах. И есть евро-версия USDC в Европе, которая не взлетела», — сказала она. Кэти признала, что стейблкоины забрали часть спроса у Биткоина, чего ее ранний анализ не предполагал. Она пошла дальше, заявив, что реальное изменение, принесенное криптовалютой, — это устранение посредников в финансах. Она описала традиционный банкинг как полный «сборщиков пошлин», которые взимают высокие...
Поделиться
BitcoinEthereumNews2025/09/28 18:16