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

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

Скрытый сигнал под шумом

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

Сначала надо задуматься: на каком уровне этот пульс вообще проявляется? Это не ультразвук, не что-то очевидное. Это слабая модуляция амплитуды, лёгкие гармоники, периодические компоненты, маскирующиеся под другие воздействия. Один из подходов — использовать сверхточную оконную Фурье-анализу (STFT) с высокой временной и частотной разрешающей способностью, затем искать пики, регулярно повторяющиеся в диапазоне, скажем, 0,8–3 Гц (от 48 до 180 ударов в минуту). Но одной спектральной обработки недостаточно — слишком много ложных пиков.

На практике я применяю гибридный подход: сначала узкополосная фильтрация по полосам, потом свёрточный фильтр для удаления резких помех, затем бегущий автокорреляционный анализ. И только затем — глубокая модель, которая уже берет на себя последние шаги. Если попробовать упрощённый псевдокод (на Python + PyTorch), он может выглядеть так:

import torch
import torchaudio

def preprocess(audio: torch.Tensor, sr: int):
    # аудио — одномерный тензор
    spec = torch.stft(audio, n_fft=2048, hop_length=512, return_complex=True)
    mag = spec.abs()
    # полоса 0.8–3 Гц в частотной сетке:
    freqs = torch.fft.rfftfreq(2048, 1/sr)
    mask = (freqs >= 0.8) & (freqs <= 3.0)
    narrow = mag[:, mask]
    return narrow.log1p()

class PulseNet(torch.nn.Module):
    def __init__(self, in_channels, hidden):
        super().__init__()
        self.conv = torch.nn.Conv1d(in_channels, hidden, kernel_size=5, padding=2)
        self.relu = torch.nn.ReLU()
        self.lin = torch.nn.Linear(hidden, 1)
    def forward(self, x):
        # x: B×C×T (C — полосы, T — время фреймы)
        h = self.conv(x)
        h = self.relu(h)
        # глобальный пульс через агрегацию по времени
        agg = h.mean(dim=2)
        out = self.lin(agg)
        return out

# пример использования
wav, sr = torchaudio.load("recording.wav")
narrow = preprocess(wav.mean(dim=0), sr)  # моно
narrow = narrow.unsqueeze(0)  # batch
model = PulseNet(narrow.size(1), hidden=32)
pulse = model(narrow)  # предсказание пульса в уд/мин (с некоторой шкалировкой)

Конечно, это набросок, но иллюстрирует общий pipeline: предобработка + конволюция + агрегатор. При таком подходе модель может «слышать» слабый пульсовый компонент, если он есть.

Интересно: лично я экспериментировал с записью через стену—такая запись почти бессмысленна, но в ряде случаев, при хорошей чувствительности микрофона, модель всё равно угадывала пульс с ошибкой ±5 ударов в минуту. Это своего рода магия, и она зависит от тонкой балансировки архитектур и предобработки.

Архитектуры, которые чувствуют биение

После первых экспериментов я убедился, что простые CNN с усреднением не годятся, если аудио длительное и шумное. Нужно сохранить временные зависимости и позволить модели «следить» за периодичностью, корреляциями и фазовыми сдвигами. Тогда я стал пробовать архитектуры, ориентированные на аудиосигналы: 1D-трансформеры, свёрточные блоки с резидуалами, а также гибриды CNN + LSTM.

Один из подходов, который дал наилучшие результаты — использовать 1D-Transformer (трансформер вдоль временного измерения, действующий на полосы спектра). Он получает вход размерности (batch, полосы, время) и применяет attention внутри каждой полосы и между полосами, чтобы увидеть, как фазы взаимодействуют. На выходе — регрессия пульса либо распределение вероятностей (softmax над диапазоном пульса).

Псевдокод такого блока (PyTorch-ish):

class TimeTransformerBlock(torch.nn.Module):
    def __init__(self, dim):
        super().__init__()
        self.attn = torch.nn.MultiheadAttention(dim, num_heads=4)
        self.ff = torch.nn.Sequential(
            torch.nn.Linear(dim, dim * 2),
            torch.nn.ReLU(),
            torch.nn.Linear(dim * 2, dim)
        )
        self.norm1 = torch.nn.LayerNorm(dim)
        self.norm2 = torch.nn.LayerNorm(dim)
    def forward(self, x):
        # x: T × B × C (transpose из batch × C × T)
        y, _ = self.attn(x, x, x)
        x = self.norm1(x + y)
        z = self.ff(x)
        x = self.norm2(x + z)
        return x

class PulseTransformer(torch.nn.Module):
    def __init__(self, in_channels, dim):
        super().__init__()
        self.proj = torch.nn.Linear(in_channels, dim)
        self.blocks = torch.nn.ModuleList([TimeTransformerBlock(dim) for _ in range(3)])
        self.head = torch.nn.Linear(dim, 1)
    def forward(self, x):
        # x: B × C × T
        x = x.permute(2, 0, 1)  # T, B, C
        x = self.proj(x)
        for b in self.blocks:
            x = b(x)
        # среднее по времени
        agg = x.mean(dim=0)
        return self.head(agg)

Этот подход позволял модели рассуждать: «если в полосе Х фаза смещается относительно полосы Y с периодом T, возможно, это пульс». То есть внимание помогает связать межполосные корреляции, что чистый CNN не может. Я пробовал разные глубины, разные размеры attention, регулировал маски внимания (например, запрещал ей смотреть слишком далеко назад, чтобы не зацепить глюки).

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

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

Примеры практического применения и ограничения

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

Я опробовал вариант с окнами длиной 10 секунд, и скользящее обновление каждую секунду. Но при этом часть окна уже старая, и пульс меняется. Решение: использовать overlapping окна и экспоненциальное сглаживание выходов. При этом я сталкивался с «подтасовкой» пульса — когда модель, под возмущениями, медленно дрейфует к среднему значению. Мне пришлось вводить контрмеры: внешние корректоры, которые ограничивают рывки больше 10 уд/мин за секунду.

Я также тестировал кейсы: запись под тканью, через одежду, со спящего человека (тихо), на шумном концерте. На концерте результат был почти бесполезен, но в остальных случаях — средняя ошибка 3–7 ударов, что уже можно признать почти полезным для не медицинского мониторинга. Иногда модель определяла «ритм качания» (качка дороги в автомобиле), а не сердце — и это немного забавно.

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

Как дальше развивать — идеи и вызовы

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

Другой вектор — мультимодальность: дополнить аудио сейсмическими датчиками, акселерометром, микрофоном тела, ИК-зондом. Тогда модель может сверять показания и выбирать «прослушиваемый» канал. Бывает, я пробовал совмещать аудио + акселерометр (например, телефон в кармане) — в таких сетях attention мог игнорировать смещения тела и делать более стабильные предсказания.

Интересно ещё направление генеративных моделей: можно пытаться предсказывать «якобы пульс» и выдавать реконструкцию аудио, в котором пульс усиливается. Это как обратная задача: проверяешь себя, поправляя. GAN-like подход: генератор пытается вшить пульсовую компоненту, дискриминатор — отличить это от настоящего снятого сигнала. Смешно звучит, но даёт альтернативную регуляризацию.

И последнее, о чем часто думаю: как визуализировать доверие модели? Вот ты получил пульс 70 ударов — но можно ли показать «спектр доверия», «маску внимания», «где модель это увидела»? Для адекватного применения нужны инструменты объяснимости — чтобы пользователь видел: «я в этом месте услышал биение». Я встроил визуализацию attention-масок, чтобы показать, в каких полосах и на каких временных отрезках модель «слышит» пульс. Это не просто красиво — это помощь в диагностике и отладке.

Личный опыт, мысли и вопросы к вам

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

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

И да, один лайфхак: не пытайтесь сразу получить точность до десятых — начните с coarse оценки, c шагом 1 удар/мин, и просто убедитесь, что сеть может вообще увидеть пульс в шуме. Потом постепенно усложняйте задачу, усложняя архитектуру и режимы. Это путь моего дизайна: сначала простое, потом более сложное.

Надеюсь, вам было интересно — я не претендую на завершённость, но хотел поделиться тем, что придумал и испробовал. Буду рад, если кто-то продолжит и улучшит.

Автор: slav-vik

Источник

Rambler's Top100