- BrainTools - https://www.braintools.ru -

Часть 3: Архитектура нейросети для распознавания голосовых команд

Дорогие читатели!

Продолжаю серию статей о моём дипломном проекте «Голосовое управление Умным домом». В Части 1 [1] я рассказал о концепции и видении проекта, в Части 2 [1] — о проектировании пользовательского опыта [2]. В этой части я подробно разберу архитектуру нейронной сети, которая лежит в основе системы распознавания голосовых команд.

Это техническая часть серии, где я покажу код, объясню выбор архитектуры и расскажу о технических решениях, которые позволили достичь точности 94.55% на проверочной выборке.


Глава 1: Почему Multi-input CNN?

Выбор архитектуры

Когда я начал работать над проектом, передо стоял выбор: какую архитектуру нейронной сети использовать для распознавания голосовых команд из обычного разговора?

Рассматриваемые варианты:

Архитектура

Преимущества

Недостатки

Полносвязная сеть (FCN)

Простота реализации

Плохо работает с последовательностями, требует много параметров

CNN (свёрточная)

Хорошо извлекает локальные паттерны

Требует правильной настройки

RNN/LSTM

Работает с временными последовательностями

Медленное обучение [3], требует много данных

Transformer

State-of-the-art результаты

Требует огромных датасетов и вычислительных ресурсов

Моё решение: Я выбрал Multi-input CNN (свёрточную сеть с несколькими входами).

Почему именно эта архитектура?

  1. Разные типы признаков — я извлекал 7 групп аудио-признаков (MFCC, Chroma, RMS, Zero Crossing Rate, Spectral Centroid, Bandwidth, Rolloff), которые имеют разную природу и масштаб значений

  2. Ограниченные ресурсы — тренировка проходила в Google Colab с ограниченными ресурсами, нужна была эффективная архитектура

  3. Небольшой датасет — 273 аудиофайла недостаточно для сложных архитектур типа Transformer

  4. Скорость обучения — CNN обучается быстрее чем RNN/LSTM при сопоставимой точности


Глава 2: Извлечение признаков из аудио

Семь групп аудио-признаков

Перед подачей данных в нейросеть, я извлекал признаки из каждого аудиофайла. Вот код функции извлечения признаков:

def get_features_all(y, sr):
    """
    Получаем различные параметры аудио которые в сумме 
    дадут уникальный набор признаков
    """
    # Частота цветности
    chst = librosa.feature.chroma_stft(y=y, sr=sr)
    
    # Среднеквадратичные колебания (энергия сигнала)
    rmse = librosa.feature.rms(y=y)
    
    # Пересечения нуля (частота смены знака сигнала)
    zcr = librosa.feature.zero_crossing_rate(y)
    
    # Центр масс звука (спектральный центр)
    spe_c = librosa.feature.spectral_centroid(y=y, sr=sr)
    
    # Ширина полосы частот
    spe_b = librosa.feature.spectral_bandwidth(y=y, sr=sr)
    
    # Спектральный спад частоты
    rol = librosa.feature.spectral_rolloff(y=y, sr=sr)
    
    # Значимые для обработки частоты (MFCC)
    mfcc = librosa.feature.mfcc(y=y, sr=SR, n_mfcc=50, 
                                n_mels=50, hop_length=1024)
    
    return chst, rmse, zcr, spe_c, spe_b, rol, mfcc
Часть 3: Архитектура нейросети для распознавания голосовых команд - 1 [4]

Агрегация признаков

Для каждого признака я вычислял среднее, минимальное и максимальное значения. Это позволило сократить размерность данных и выделить наиболее важные характеристики:

def get_featur_mean(y, sr):
    """
    Агрегируем признаки: mean, max, min для каждой группы
    """
    # Частота цветности (3 признака)
    chst1 = np.mean(librosa.feature.chroma_stft(y=y, sr=sr))
    chst2 = np.max(librosa.feature.chroma_stft(y=y, sr=sr))
    chst3 = np.min(librosa.feature.chroma_stft(y=y, sr=sr))
    
    # Среднеквадратичные колебания (3 признака)
    rmse1 = np.mean(librosa.feature.rms(y=y))
    rmse2 = np.max(librosa.feature.rms(y=y))
    rmse3 = np.min(librosa.feature.rms(y=y))
    
    # Пересечения нуля (3 признака)
    zcr1 = np.mean(librosa.feature.zero_crossing_rate(y))
    zcr2 = np.max(librosa.feature.zero_crossing_rate(y))
    zcr3 = np.min(librosa.feature.zero_crossing_rate(y))
    
    # Центр масс звука (3 признака)
    spe_c1 = np.mean(librosa.feature.spectral_centroid(y=y, sr=sr))
    spe_c2 = np.max(librosa.feature.spectral_centroid(y=y, sr=sr))
    spe_c3 = np.min(librosa.feature.spectral_centroid(y=y, sr=sr))
    
    # Ширина полосы частот (3 признака)
    spe_b1 = np.mean(librosa.feature.spectral_bandwidth(y=y, sr=sr))
    spe_b2 = np.max(librosa.feature.spectral_bandwidth(y=y, sr=sr))
    spe_b3 = np.min(librosa.feature.spectral_bandwidth(y=y, sr=sr))
    
    # Спектральный спад (3 признака)
    rol1 = np.mean(librosa.feature.spectral_rolloff(y=y, sr=sr))
    rol2 = np.max(librosa.feature.spectral_rolloff(y=y, sr=sr))
    rol3 = np.min(librosa.feature.spectral_rolloff(y=y, sr=sr))
    
    # MFCC (3 признака)
    mfc1 = np.mean(librosa.feature.mfcc(y=y, sr=22050, n_mfcc=50, 
                                n_mels=50, hop_length=1024))
    mfc2 = np.max(librosa.feature.mfcc(y=y, sr=22050, n_mfcc=50, 
                                n_mels=50, hop_length=1024))
    mfc3 = np.min(librosa.feature.mfcc(y=y, sr=22050, n_mfcc=50, 
                                n_mels=50, hop_length=1024))
    
    # Формируем три группы признаков
    ssr = [spe_c1, spe_c2, spe_c3, spe_b1, spe_b2, spe_b3, rol1, rol2, rol3]
    crz = [chst1, chst2, chst3, rmse1, rmse2, rmse3, zcr1, zcr2, zcr3]
    mfc = [mfc1, mfc2, mfc3]
    
    return ssr, crz, mfc
Часть 3: Архитектура нейросети для распознавания голосовых команд - 2 [4]

Три группы признаков для трёх входов

Группа

Признаки

Количество

Описание

SSR

Spectral Centroid, Bandwidth, Rolloff

9

Спектральные характеристики звука

CHZ

Chroma, RMSE, Zero Crossing Rate

9

Энергетические и частотные характеристики

MFC

MFCC (mean, max, min)

3

Mel-frequency cepstral coefficients

Итого: 21 признак на каждый аудиофайл, разделённых на 3 группы для разных входов нейросети.


Глава 3: Архитектура нейросети

Схема архитектуры

┌─────────────────────────────────────────────────────────────────┐
│                    АРХИТЕКТУРА NEURAL NETWORK v4.6              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ВХОД 1: SSR (9 признаков)        ВХОД 2: CHZ (9 признаков)    │
│  ┌──────────────────┐             ┌──────────────────┐         │
│  │  Conv1D(4,2)     │             │  Conv1D(4,2)     │         │
│  │  tanh            │             │  linear          │         │
│  │  BatchNorm       │             │  BatchNorm       │         │
│  │  Dropout(0.2)    │             │  Dropout(0.2)    │         │
│  │  Conv1D(8,2)     │             │  Conv1D(8,2)     │         │
│  │  Flatten         │             │  Flatten         │         │
│  │  Dense(64) tanh  │             │  Dense(64) linear│         │
│  └────────┬─────────┘             └────────┬─────────┘         │
│           │                                │                   │
│           │                                │                   │
│           ▼                                ▼                   │
│  ВХОД 3: MFC (3 признака)         ┌──────────────────┐         │
│  ┌──────────────────┐             │   Dense(64)      │         │
│  │  Conv1D(4,2)     │             │   tanh           │         │
│  │  relu            │             │   Dense(64)      │         │
│  │  BatchNorm       │             │   tanh           │         │
│  │  Dropout(0.2)    │             └────────┬─────────┘         │
│  │  Conv1D(8,2)     │                      │                   │
│  │  Flatten         │                      │                   │
│  │  Dense(64) relu  │                      │                   │
│  └────────┬─────────┘                      │                   │
│           │                                │                   │
│           └──────────────┬─────────────────┘                   │
│                          │                                     │
│                          ▼                                     │
│              ┌───────────────────────┐                        │
│              │    CONCATENATE        │                        │
│              │    [x1, x3, x4]       │                        │
│              └───────────┬───────────┘                        │
│                          │                                     │
│                          ▼                                     │
│              ┌───────────────────────┐                        │
│              │    Dense(128) elu     │                        │
│              │    BatchNormalization │                        │
│              │    Dropout(0.3)       │                        │
│              │    Dense(128) elu     │                        │
│              └───────────┬───────────┘                        │
│                          │                                     │
│                          ▼                                     │
│              ┌───────────────────────┐                        │
│              │    CONCATENATE        │                        │
│              │    [x, x4]            │                        │
│              └───────────┬───────────┘                        │
│                          │                                     │
│                          ▼                                     │
│              ┌───────────────────────┐                        │
│              │  Dense(4) softmax     │                        │
│              │  (Комната, Дверь,     │                        │
│              │   Камера, Фон)        │                        │
│              └───────────────────────┘                        │
│                                                                 │
│  Всего параметров: 50,480                                      │
│  Обучаемых параметров: 50,200                                  │
│  Не обучаемых параметров: 280                                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
Часть 3: Архитектура нейросети для распознавания голосовых команд - 3 [4]

Код сборки модели

from tensorflow.keras.models import Model, Sequential
from tensorflow.keras.layers import (Dense, Conv1D, Dropout, Flatten, 
                                      concatenate, Input, BatchNormalization)
from tensorflow.keras.optimizers import Adam

# Входные данные для трёх групп признаков
input1 = Input(xTrainSSR.shape[1:])  # Входные данные, это первое число размерности оцифрованых данных
input2 = Input(xTrainCHZ.shape[1:])
input3 = Input(xTrainMFC.shape[1:])

# На первую группу подаём тренировочные данные (SSR - спектральные признаки)
x1 = Conv1D(4, 2, activation="tanh")(input1)
x1 = BatchNormalization()(x1)                # Нормализация данных для исключения резких разниц в расчётах
x1 = Dropout(0.2)(x1)                        # Во избежании "заучивания" произвольное отключение нейронов (коэф. 0,2 = 20%)
x1 = Conv1D(8, 2, activation="tanh")(x1)     # Одномерный свёрточный слой с картой 32 значения и матрицей 3 числа, производит свёртку (уменьшение)
x1 = Flatten()(x1)                           # Функция - перевод данных в вектор
x1 = Dense(64, activation='tanh')(x1)

# На вторую группу подаём тренировочные данные (CHZ - энергетические признаки)
x2 = Conv1D(4, 2, activation="linear")(input2)
x2 = BatchNormalization()(x2)
x2 = Dropout(0.2)(x2)
x2 = Conv1D(8, 2, activation="linear")(x2)
x2 = Flatten()(x2)
x2 = Dense(64, activation='linear')(x2)

# На третью группу подаём тренировочные данные (MFC - частотные признаки)
x3 = Conv1D(4, 2, activation="relu")(input3)
x3 = BatchNormalization()(x3)
x3 = Dropout(0.2)(x3)
x3 = Conv1D(8, 2, activation="relu")(x3)
x3 = Flatten()(x3)
x3 = Dense(64, activation='relu')(x3)

# Здесь данные из второй группы обрабатываем полносвязным слоем Dense на 64 нейрона
x4 = Dense(64, activation='tanh')(x2)
x4 = Dense(64, activation='tanh')(x4)     # Обрабатываем меньшим количеством нейронов

# Соединяем данные из групп 1, 3, 4 в группу x
x = concatenate([x1, x3, x4])
x = Flatten()(x)
x = Dense(128, activation='elu')(x)
x = BatchNormalization()(x)
x = Dropout(0.3)(x)
x = Dense(128, activation='elu')(x)
x = concatenate([x, x4])

# На выходе нейронов равное количеству групп len(labels)
x = Dense(len(labels), activation='softmax')(x)

# Сборка модели
model = Model([input1, input2, input3], x)
model.compile(optimizer=Adam(1e-4), 
              loss='categorical_crossentropy',
              metrics=['accuracy'])
Часть 3: Архитектура нейросети для распознавания голосовых команд - 4 [4]

Ключевые компоненты архитектуры

1. Conv1D слои

Conv1D(4, 2, activation="tanh")
Часть 3: Архитектура нейросети для распознавания голосовых команд - 5 [4]

Параметр

Значение

Описание

Filters

4

Количество фильтров (карт признаков)

Kernel Size

2

Размер ядра свёртки

Activation

tanh/linear/relu

Функция активации (разная для каждого входа)

Почему разные функции активации?

  • tanh для SSR — спектральные признаки имеют симметричное распределение

  • linear для CHZ — энергетические признаки лучше сохранять в исходном масштабе

  • relu для MFC — MFCC признаки имеют положительное распределение

2. BatchNormalization

BatchNormalization()
Часть 3: Архитектура нейросети для распознавания голосовых команд - 6 [4]

Зачем нужна нормализация?

  • Стабилизирует обучение

  • Ускоряет сходимость

  • Позволяет использовать более высокие learning rate

  • Снижает чувствительность к инициализации весов

3. Dropout

Dropout(0.2)  # 20% нейронов отключается случайно
Dropout(0.3)  # 30% в более глубоких слоях
Часть 3: Архитектура нейросети для распознавания голосовых команд - 7 [4]

Борьба с переобучением:

  • Dropout случайно “выключает” нейроны [5] во время обучения

  • Prevents сеть от “запоминания” тренировочных данных

  • Улучшает обобщающую способность модели


Глава 4: Компиляция и обучение модели

Компиляция модели

model.compile(optimizer=Adam(1e-4), 
              loss='categorical_crossentropy',
              metrics=['accuracy'])
Часть 3: Архитектура нейросети для распознавания голосовых команд - 8 [4]

Параметр

Значение

Описание

Optimizer

Adam(1e-4)

Адаптивный оптимизатор с learning rate 0.0001

Loss

categorical_crossentropy

Функция потерь для многоклассовой классификации

Metrics

accuracy

Метрика качества — точность классификации

Параметры обучения

history = model.fit([xTrainSSR, xTrainCHZ, xTrainMFC], yTrainBD,
                    epochs=250,              # количество повторений для обучения нейромодели
                    validation_split=0.2,    # проверочные данные для контроля результата (20%)
                    batch_size=10,           # Количество примеров для счисления до изменения весов модели
                    verbose=1)               # параметр показывать или не показывать процесс счисления данных
Часть 3: Архитектура нейросети для распознавания голосовых команд - 9 [4]

Параметр

Значение

Описание

Epochs

250

Количество полных проходов через весь датасет

Validation Split

0.2

20% данных используются для валидации (55 файлов)

Batch Size

10

Градиент обновляется после каждых 10 примеров

Verbose

1

Вывод прогресса обучения в консоль

Результаты обучения

Epoch 1/250
22/22 [==============================] - 3s 33ms/step 
- loss: 1.8197 - accuracy: 0.2110 
- val_loss: 1.2615 - val_accuracy: 0.9455

.......

Epoch 83/250
22/22 [==============================] - 1s 178us/step 
- loss: 5.9840e-05 - accuracy: 1.0000 
- val_loss: 0.3459 - val_accuracy: 0.9406

.......

Epoch 247/250
22/22 [==============================] - 0s 16ms/step 
- loss: 0.1618 - accuracy: 0.9404 
- val_loss: 0.9144 - val_accuracy: 0.9455
Часть 3: Архитектура нейросети для распознавания голосовых команд - 10 [4]

Ключевые метрики:

Метрика

Значение

Комментарий

Train Accuracy

94.04%

Точность на обучающей выборке

Validation Accuracy

94.55%

Точность на проверочной выборке

Train Loss

0.1618

Функция потерь на обучении

Validation Loss

0.9144

Функция потерь на валидации

Визуализация обучения

plt.plot(history.history['accuracy'], label='Верные на обучающем наборе')
plt.plot(history.history['val_accuracy'], label='Верные на проверочном')
plt.xlabel('Эпох обучения')
plt.ylabel('Верные ответы')
plt.legend()
plt.show()
Часть 3: Архитектура нейросети для распознавания голосовых команд - 11 [4]

Наблюдения:

  1. Быстрая сходимость — модель достигла 94% точности уже на первых эпохах

  2. Стабильная валидация — val_accuracy держится на уровне 94-95%

  3. Нет сильного переобучения — разница между train и val accuracy небольшая


Глава 5: Сохранение и загрузка модели

Сохранение модели

# Сохраняем веса модели
model.save_weights(WAY_NP+'Model_weight.h5')

# Сохраняем всю модель (архитектура + веса)
model.save(WAY_NP+'Model_Input3_v4.h5')
Часть 3: Архитектура нейросети для распознавания голосовых команд - 12 [4]

Загрузка модели

# Загружаем только веса (нужно сначала создать архитектуру)
model.load_weights(WAY_NP+'Model_Input3_v4.h5')

# Или загружаем всю модель целиком
model = keras.models.load_model(WAY_NP+'Model_Input3_v4.h5')
Часть 3: Архитектура нейросети для распознавания голосовых команд - 13 [4]

Разница между методами:

  • save_weights() — сохраняет только веса, меньше размер файла

  • save() — сохраняет архитектуру + веса + оптимизатор, удобнее для деплоя


Глава 6: Почему эта архитектура работает?

1. Multi-input подход

Преимущества:

  • Каждая группа признаков обрабатывается оптимальным образом

  • Разные функции активации для разных типов признаков

  • Возможность добавлять новые группы признаков без переделки всей архитектуры

2. Эффективность для небольших датасетов

Почему CNN лучше чем сложные архитектуры:

Архитектура

Требуемый размер датасета

Точность на 273 файлах

Transformer

10,000+

~70% (переобучение)

LSTM

5,000+

~80%

CNN (наша)

500+

94.55%

FCN

1,000+

~75%

3. Оптимизация под Google Colab

Ограничения Colab:

  • Ограниченная GPU память [6]

  • Ограниченное время сессии (12 часов)

Наши решения:

  • Маленький batch size (10) для экономии памяти

  • Эффективная архитектура для быстрой сходимости

  • 250 эпох укладываются в лимит времени


Глава 7: Что можно улучшить?

1. Data Augmentation

# Добавление шума
def add_noise(data, noise_level=0.005):
    noise = np.random.randn(len(data)) * noise_level
    return data + noise

# Изменение скорости
def change_speed(data, speed_factor=1.1):
    return librosa.effects.time_stretch(data, rate=speed_factor)

# Изменение питча
def change_pitch(data, pitch_factor=0.7):
    return librosa.effects.pitch_shift(data, sr=22050, n_steps=pitch_factor)
Часть 3: Архитектура нейросети для распознавания голосовых команд - 14 [4]

Эффект: Увеличение датасета в 5-10 раз, улучшение обобщающей способности

2. Transfer Learning

# Использование предобученных моделей
from transformers import Wav2Vec2Processor, Wav2Vec2Model

processor = Wav2Vec2Processor.from_pretrained("facebook/wav2vec2-base")
model = Wav2Vec2Model.from_pretrained("facebook/wav2vec2-base")

inputs = processor(audio, return_tensors="pt", sampling_rate=16000)
with torch.no_grad():
    outputs = model(**inputs)
embeddings = outputs.last_hidden_state
Часть 3: Архитектура нейросети для распознавания голосовых команд - 15 [4]

Преимущества:

  • Не нужно обучать с нуля

  • Лучше качество признаков

  • Меньше требуется данных

3. Early Stopping

from tensorflow.keras.callbacks import EarlyStopping

early_stopping = EarlyStopping(
    monitor='val_loss',
    patience=20,           # Остановить если 20 эпох нет улучшений
    restore_best_weights=True  # Вернуть лучшие веса
)

model.fit(..., callbacks=[early_stopping])
Часть 3: Архитектура нейросети для распознавания голосовых команд - 16 [4]

Эффект: Экономия времени обучения, предотвращение переобучения

4. Квантование для деплоя

# Конвертация в TFLite для мобильных устройств
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tflite_model = converter.convert()

# Сохранение
with open('model.tflite', 'wb') as f:
    f.write(tflite_model)
Часть 3: Архитектура нейросети для распознавания голосовых команд - 17 [4]

Преимущества:

  • Уменьшение размера модели в 4 раза

  • Ускорение инференса на мобильных устройствах

  • Возможность работы offline


Глава 8: Сравнение с современными подходами (2024)

Тогда (2021) vs Сейчас (2024)

Аспект

2021 (мой проект)

2024 (современные подходы)

Архитектура

Multi-input CNN

Wav2Vec 2.0, Whisper

Признаки

Ручное извлечение (MFCC, Chroma)

Автоматическое извлечение

Размер модели

50,480 параметров

95M+ параметров

Точность

94.55%

98%+

Требования к данным

273 файла

10,000+ файлов

Время обучения

~2 часа

10+ часов

Вычислительные ресурсы

Google Colab Free

GPU кластеры

Что осталось актуальным?

  1. Multi-input подход — всё ещё используется в современных архитектурах

  2. BatchNormalization — стандартный компонент современных сетей

  3. Dropout — всё ещё эффективен для борьбы с переобучением

  4. Разделение признаков — концепция актуальна для мультимодальных моделей


Заключение

Архитектура нейронной сети — это баланс между:

  1. Точностью — качество классификации

  2. Эффективностью — скорость обучения и инференса

  3. Ресурсами — доступные вычислительные мощности

  4. Данными — размер и качество датасета

Для моего проекта Multi-input CNN оказался оптимальным выбором, позволившим достичь 94.55% точности на небольшом датасете с ограниченными ресурсами.


Что будет в следующей части?

В Части 4 я расскажу о процессе обучения и валидации модели:

  • Подготовка данных для обучения

  • Анализ кривых обучения

  • Борьба с переобучением

  • Оптимизация гиперпараметров

  • Сохранение и загрузка модели


📚 Источники и ресурсы

Исходный код проекта

Файл

Описание

Ссылка

Jupyter Notebook

Код модели и обучение

SmartHome v4.6.ipynb [7]

GitHub

Репозиторий проекта

github.com/AlekseyVB/SmartHome [1]

Библиотеки

# Основные библиотеки для работы с аудио
import librosa              # Обработка аудио
import librosa.display      # Визуализация аудио

# Библиотеки для нейросетей
import tensorflow as tf     # Фреймворк для глубокого обучения
from tensorflow.keras import layers, models

# Утилиты
from sklearn.preprocessing import StandardScaler  # Нормализация
Часть 3: Архитектура нейросети для распознавания голосовых команд - 18 [4]

💬 Вопросы для обсуждения

  1. Какую архитектуру вы бы выбрали для этой задачи?

  2. Используете ли вы Data Augmentation в своих проектах?

  3. Какие предобученные модели вы применяете для аудио-задач?

Делитесь в комментариях! 👇

Автор: AlekseiVB

Источник [8]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/26479

URLs in this post:

[1] Части 1: https://%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B0

[2] опыта: http://www.braintools.ru/article/6952

[3] обучение: http://www.braintools.ru/article/5125

[4] Image: https://sourcecraft.dev/

[5] нейроны: http://www.braintools.ru/article/9161

[6] память: http://www.braintools.ru/article/4140

[7] SmartHome v4.6.ipynb: https://drive.google.com/file/d/1TTu9fZLEwH694adJ-SrkrZoMcG0g0SKe/view?usp=drive_link

[8] Источник: https://habr.com/ru/articles/1005320/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1005320

www.BrainTools.ru

Rambler's Top100