CV-классификация объектов по кратковременным акустическим сигналам с помощью нейросетей на примере часовых механизмов

Автор статьи, к.т.н. Соловьев Аркадий Павлович
Я, Соловьев Аркадий Павлович, студент Университета искусственного интеллекта (УИИ). Прошел две стажировки, это моя первая статья в рамках лаборатории УИИ. Область моих профессиональных интересов не очень близка к искусственному интеллекту и нейросетям. Для меня это еще один инструмент, как мне представляется очень эффективный, для проведения исследований в предметной области.

Статья может быть интересна студентам УИИ, начинающим нейронщикам и тем, кто присматривается к этой области знаний. Коллеги, возможно, смогут избежать, по крайней мере, части тех граблей, по которым пришлось прогуляться мне. С этой целью я подробно расскажу о тех подводных камнях, которые в достаточном количестве разбросаны на этом пути.

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

Постановка задачи

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

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

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

Конкретная цель – создание нейронной сети для идентификации наручных часов по единичному акустическому сигналу (тику).

Проведение исследований

Традиционно при анализе звуковых сигналов используются такие параметры, как форма сигнала, ширина спектра, АЧХ, при периодических сигналах изучаются период следования, джиттер, амплитуда, фаза. При анализе речи используются специальные методы, например, метод мел-кепстральных коэффициентов и другие. Существенно реже исследуется тонкая структура самого короткого сигнала.

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

Кстати, дискуссия, проведенная с ChatGPT, показала, что эта частная задача может быть интересна коллекционерам раритетных часов и экспертам в этой области. Возможно акустический сигнал (тик) может являться своеобразным «отпечатком пальца» часов, который очень сложно подделать, так как в формировании звука участвует очень много элементов часов (шестеренок) со своими индивидуальными размерами. Сделать их идентичными, чтобы они издавали одинаковые звуки практически невозможно.

Для сбора датасета у коллег были позаимствованы 5 экземпляров наручных кварцевых часов разных фирм. Предполагалось, что тикать они должны по-разному. С целью усложнения задачи дополнительно, на одном из маркетплейсов, были куплены 10 экземпляров кварцевых часов (не очень дорогих) одной фирмы и одной модели (рисунок 1). Предполагалось, что тики этих часов должны быть максимально похожими.

Рисунок 1 – 15 экземпляров часов

Запись тиков этих часов проводилась на аппаратуре «PULSE» фирмы «Brüel & Kjær». В качестве датчика использовался акселерометр 4508 этой же фирмы. Граничная частота измерительной аппаратуры составила 32 кГц, хотя в паспорте измерительного блока типа 3560-C указано 25 кГц.

Каждому экземпляру часов был присвоен номер от 0 до 14. Среднее время записи тиков для каждого экземпляра часов составило ~ 12 минут. В среднем частота тиков составляет ~ 1 Гц. Таким образом, для каждого экземпляра часов собрано по ~ 720 тиков.

Общий датасет для 15 классов часов составил ~ 12 000 примеров. Такой объем датасета можно признать достаточно большим, поэтому аугментация не проводилась.

Изучение вида записанных тиков проводилось в программе Adobe Audition. Очевидно, что импульсы даже для одного класса во временном представлении значительно различаются по форме (рисунок 2). На рисунке 2 импульсы приближены друг к другу для удобства сравнения.

Рисунок 2 – 6 импульсов тиков одного экземпляра часов во временном представлении

Первой идеей была идея провести анализ с помощью мел-кепстральных частотных коэффициентов (MFCC).

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

В этом преобразовании учитываются физиологические особенности восприятия звука человеком.

Шкала мел соотносит воспринимаемую частоту или высоту чистого тона (мел) с фактической измеренной частотой (Гц). Эмпирическая зависимость M(f) (мел) от частоты f:
M(f) = 1127, 01048 ln(1 + f /700)

Рисунок 3 – зависимость мел от частоты

Значения мел и частоты совпадают только при f = 1000 Гц.

Исходный акустический сигнал во временном представлении делится на фреймы длительностью 20 – 40 мс. Представляется, что за это время речевой сигнал не претерпевает заметных изменений.

В нашем случае наиболее информативная часть импульса имеет длительность ~ 50 мс. В пределах этого времени сигнал претерпевает значимые изменения.

Зависимость мел от частоты с ростом частоты входит в насыщение (график на рисунке 3). Граничная частота импульса тиков составляет 32 кГц, что существенно превышает звуковую частоту речевого сигнала и, следовательно, используя MFCC, мы теряем часть значимой информации сигнала.

По этим двум причинам использование MFCC для анализа коротких (длительностью 50 мс) импульсов не очень хорошая идея.

Более плодотворной идеей является использование частотного представления сигнала тиков.

В частотном представлении тики в пределах одного класса более похожи друг на друга, чем во временном представлении. На рисунке 4 представлены 15 импульсов одного класса. Для удобства анализа импульсы приближены друг к другу на временной оси.

Рисунок 4 – 15 импульсов тиков одного экземпляра часов в частотном представлении

Рисунок 5 – 15 импульсов тиков по одному для каждого из 15 экземпляров часов в частотном представлении

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

Графическое представление спектрограмм как изображение (картинку) можно подавать на вход сверточной нейронной сети для обучения распознаванию импульсов тиков.

Стандартным размером изображений де факто для подачи на вход нейросети является размер 224×224 пикселей. Каждый пиксель представлен 3 байтами цвета (RGB).

Преобразование сигналов из временного представления (записанных в WAV-файлах) в форму, пригодную для подачи на вход нейросети можно проводить двумя разными способами.

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

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

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

Альтернативный способ – вначале нарезать WAV-файлы во временном представлении на отдельные файлы импульсов, а затем перевести их в спектральное представление.

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

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

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

Способ основан на гипотезе, что в пределах относительно короткого файла длительностью 12 – 14 минут акустические импульсы (тики) следуют с постоянным периодом длительностью 1 с. Начало первого импульса в спектральном представлении совмещалось с началом файла и, с интервалом в одну секунду, вырезался каждый отрезок сигнала длительностью 50 мс во временном представлении. После этого отрезки сохранялись в виде отдельных WAV-файлов.
Проверка этой гипотезы проведена в программе Adobe Audition, в которой перевод из временного в спектральное представление осуществляется очень быстро. В спектральном представлении акустического файла можно точно определить начало импульса по переднему фронту.

В целом гипотеза оказалась верной, однако за время 12 – 14 минут некоторые экземпляры часов спешили или отставали на величину ± 20 мс. Для длительности импульса 50 мс это значимая величина.

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

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

Эта проблема решилась автоматически, так как с периодом в одну секунду вырезался только первый импульс.

Для реализации этого алгоритма написан скрипт на Phyton и проведена нарезка файлов. Общее время нарезки 15 акустических файлов составило ~ 3,5 минут.

После этого нарезанные акустические файлы с помощью алгоритма БПФ преобразованы в спектрограммы. При подготовке к преобразованию было выбрано рациональное соотношение частоты дискретизации и периода квантования во времени для получения файла со сжатием без потерь (с расширением .png). После этого проведен ресайз до размеров 224×224 пикселя. Потери информации при ресайзе были минимальными.

Общее время преобразования всех импульсов в 15 файлах из временного представления в спектрограммы составило ~ 1 часа 8 минут.

Если бы был применен первый способ, то это время было бы в 20 раз больше и составило бы величину ~ 22,7 часов. Скорее всего, был бы превышен суточный лимит CoLab и эта процедура растянулась бы на несколько дней.

В качестве примера на рисунке 6 приведены по три спектрограммы из двух разных классов часов одной и той же марки.

Верхний ряд – спектрограммы 8 класса, нижний ряд – спектрограммы 9 класса.

Рисунок 6 – спектрограммы тиков двух классов

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

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

Анализ картинок выявил также наличие черных точек на изображении спектрограмм.

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

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

По мнению ChatGPT это может быть нормальным явлением, особенно, если анализируется аудио сигнал или любое другое время-зависимое сигнальное представление.

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

Еще одной задачей перед обучением нейросети была оценка необходимости балансировки датасета. С этой целью вычислено среднее значение числа примеров в каждом классе и отклонение их от этой величины. Отклонение от среднего не превысило 20 %, что позволяет считать датасет сбалансированным.

Последнее, о чем имеет смысл сказать, это то, что в небольшой части файлов 12 класса, того, где наблюдались двойные тики, спектрограммы оказались сдвинутыми относительно временного окна (рисунок 7).
a
б
в

Рисунок 7 – спектрограммы 12 класса

На рисунке 7а – нормальное положение спектрограммы, на рисунке 7б – спектрограмма сдвинута вправо, на рисунке 7в – спектрограмма сдвинута влево.

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

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

Создание и обучение нейросети я решил проводить с помощью библиотеки AutoKeras. Учитывая рекомендации руководителя УИИ Дмитрия Романова, которые он давал на вебинарах, и ограниченное время, отпущенное на лабораторию, активно использовались промты для написания скриптов. Программирование и обучение нейросети проводилось в среде CoLab с использованием видеокарт Google T4 GPU.

Первый запуск скрипта по созданию и обучению нейросети закончился аварийной остановкой по причине исчерпания оперативной памяти.

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

Более приемлемой рекомендацией является применение батчей для подачи датасета на вход нейросети.

Скрипт изменен в соответствии с этой рекомендацией. По умолчанию выбрано значение батча равное 32. Переполнение памяти все равно происходило. Уменьшение размера батча последовательно до величин 16, 8, 4 эффекта не дало. Датасет все равно при загрузке в ОЗУ переполняет память.

Я решил определить объем датасета. Размер изображения 224 х 224 х 3 х 8 х 12000 ≈ 14 Гб. Такой объем действительно может перегрузить ОЗУ.

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

В AutoKeras были выбраны 2 гиперпараметра: 10 эпох и 10 разных нейросетей.
Первая нейросеть на валидационной выборке на 3 эпохе показала вероятность распознавания тиков 0,9973, на остальных эпохах вероятность распознавания не росла, а только уменьшалось значение лосов до величины 0,01.

Вторая нейросеть дала лучшую вероятность 0,97, но были значительные флюктуации этой величины на разных эпохах.

В процессе обучения третьей нейросети AutoKeras начал подгружать предобученную сеть со стороннего сайта и произошло аварийное завершение программы из-за перегрузки памяти ОЗУ.

Повторно запустил скрипт, уменьшив число нейросетей до 1.

AutoKeras просчитал первую нейросеть дважды получив то же значение вероятности 0,9973 и начал считать вероятность на тестовой выборке, на которой на 4 эпохе достигнута вероятность 0,9989, после этого вновь произошло аварийное завершение программы из-за перегрузки памяти ОЗУ.

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

В скрипт добавлена функция с использованием генератора и проблема перегрузки памяти исчезла:
# Функция для загрузки данных с использованием генератора
def load_dataset(data_dir, subset):
    # Возвращает генератор данных, который будет использоваться для загрузки изображений
    return tf.keras.preprocessing.image_dataset_from_directory(
        data_dir, # Путь к директории с набором данных
        validation_split=0.2, # Определяет процент данных, который будет использован для 
                              # валидации
        subset=subset, # Указание какой подмножество данных генерировать (обучение или 
                       # валидация/тест)
        seed=123, # Инициализация генератора случайных чисел для последовательности
        image_size=(img_height, img_width), # Установка размера изображений
        batch_size=batch_size # Установка размера батча
    )

Результаты

В результате создана нейронная сеть:
Таблица 1
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
┃ Layer (type)                         ┃ Output Shape                ┃         Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
│ input_layer (InputLayer)             │ (None, 224, 224, 3)         │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ cast_to_float32 (CastToFloat32)      │ (None, 224, 224, 3)         │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ normalization (Normalization)        │ (None, 224, 224, 3)         │               7 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d (Conv2D)                      │ (None, 222, 222, 32)        │             896 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_1 (Conv2D)                    │ (None, 220, 220, 64)        │          18,496 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ max_pooling2d (MaxPooling2D)         │ (None, 110, 110, 64)        │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout (Dropout)                    │ (None, 110, 110, 64)        │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ flatten (Flatten)                    │ (None, 774400)              │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout_1 (Dropout)                  │ (None, 774400)              │               0 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense (Dense)                        │ (None, 15)                  │      11,616,015 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ classification_head_1 (Softmax)      │ (None, 15)                  │               0 │
└──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
 Total params: 11,635,414 (44.39 MB)
 Trainable params: 11,635,407 (44.39 MB)
 Non-trainable params: 7 (32.00 B)
Параметры нейронной сети вполне ожидаемые, из 11 слоев два сверточных слоя, слой макспуллинг, дважды применен слой отключения нейронов дропаут, выравнивающий слой, полносвязанный и на выходе слой классификации на 15 нейронов.

Всего в нейронной сети ~ 11,6 миллионов параметров. Это средняя по размерам сеть.

Было проведено несколько запусков нейронной сети на обучение. Учитывая промежуточные результаты процесса обучения, число эпох сокращено до 5. Лучший результат представлен в таблице 2.
Таблица 2
Epoch 1/5
206/206 --- 23s 96ms/step - accuracy: 0.8139 - loss: 4.5952 - val_accuracy: 1.0000 - val_loss: 1.0648e-05
Epoch 2/5
206/206 --- 18s 88ms/step - accuracy: 1.0000 - loss: 1.4746e-05 - val_accuracy: 1.0000 - val_loss: 6.7586e-06
Epoch 3/5
206/206 --- 20s 84ms/step - accuracy: 1.0000 - loss: 6.5371e-06 - val_accuracy: 1.0000 - val_loss: 2.5670e-06
Epoch 4/5
206/206 --- 18s 88ms/step - accuracy: 1.0000 - loss: 3.3535e-06 - val_accuracy: 1.0000 - val_loss: 2.0957e-06
Epoch 5/5
206/206 --- 17s 84ms/step - accuracy: 1.0000 - loss: 1.7451e-06 - val_accuracy: 1.0000 - val_loss: 1.6064e-06
Проверка на тестовой выборке:
56/56 ------------ 806s 14s/step - accuracy: 1.0000 - loss: 4.5530e-05
Test accuracy: 1.00
Время обучения варьировалось от 40 минут до 1 часа. Вероятность правильного распознавания на тестовой выборке составила величину accuracy = 1,0000.

Проверка на тестовой выборке показала абсолютный результат: безошибочно были идентифицированы все 1778 предъявленных спектрограмм тиков.

Формально последнее, что необходимо было сделать, это рассчитать матрицу ошибок.

Соответствующая программа написана, но при запуске диагональ, на которой располагаются значения числа правильно распознанных классов, отобразилась неверно (рисунок 8).

Рисунок 8 – неверное отображение матрицы ошибок

Если вывести за скобки весь процесс поиска причины такой ошибки, то, как выяснилось, это следствие «особенности» программы CoLab.

В процессе работы с датасетом я выбрал вариант представления классов, располагая примеры тиков в подпапках с названиями классов. Это не единственно возможный вариант, но все они примерно равноценны. Подпапки (классы, метки) были названы целыми числами от 0 до 14.

CoLab названия папок преобразовал в строковые значения и они были отсортированы как строки в порядке: 0, 1, 10, 11, 12, 13, 14, 2, 3, 4, 5, 6, 7, 8, 9. По этой причине диагональ в матрице ошибок (рисунок 8) получилась «рваной».

Консультация с ChatGPT и эксперименты позволили решить эту проблему. Решение оказалось неочевидным: необходимо было в явном виде назвать папки в виде строк: “000”, “001”, “002”, “003”, “004”, “005”, “006”, “007”, “008”, “009”, “010”, “011”, “012”, “013”, “014”. В кавычках, с лидирующим нулем и обязательно в трехразрядном виде. В программе после этого пришлось отловить первый вход названия папок и преобразовать их в целые числа, заодно удалив двойные кавычки. В этом случае папки оказались отсортированы в правильном порядке, и матрица ошибок отобразилась правильно (рисунок 9).

Рисунок 9 – правильное представление матрицы ошибок

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

Обсуждение полученных результатов

В статье рассмотрена нейронная сеть, созданная для распознавания наручных кварцевых часов по единичному акустическому сигналу (тику). Получен абсолютный результат – вероятность правильного распознавания часов accuracy = 1,0000 на тестовой выборке, которую нейросеть никогда не видела.

Кварцевые часы, выбранные в качестве предмета, который распознается нейронной сетью – выбраны не случайно. Этот пример, довольно рафинированный, можно считать равноудаленным от реальных областей применения создаваемого инструмента распознавания. Любой пример из конкретной предметной области несет на себе печать вовлеченности в эту сферу исследований, и для читателей не всегда просто понять, насколько представленный инструмент применим в их области интересов.

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

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

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

Выводы

1. Экспериментально проверена возможность создания инструмента для идентификации устройств по единичным кратковременным (длительностью ~ 50 мс) акустическим сигналам.

2. Созданная и обученная нейронная сеть показала абсолютный результат (вероятность правильной идентификации экземпляра часов по одному акустическому событию accuracy = 1,0000) при распознавании тиков кварцевых часов.

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

4. Создание и обучение нейронной сети происходило с активным применением ChatGPT. Без его участия выполнить такой объем работы за короткое время (2 месяца работы по выходным и вечерам) было бы невозможно.

5. Созданная нейросеть обладает, по-видимому, значительным «запасом прочности» - устойчивости к воздействию помех. Это свойство исследуем во второй статье.
Made on
Tilda