ПрограммированиеФорумОбщее

Нейросети на базе Keras, Tensorflow, Torch и другие. Теория и практика применения. Pyhton/C++/Pascal (22 стр)

Страницы: 121 22 23 2430 Следующая »
#315
(Правка: 16:12) 16:05, 11 авг 2025

Начал неспеша затаскивать код на GCC ARM.

Первый блин комом: отсутствуют std::call_once и std::once_flag, которые должны быть в хедере <mutex>.

Учитывая, что среда однозадачна, сделал такую заглушку(stub):

#include <tuple>
#include <bits/invoke.h>

namespace std
{

 struct once_flag
  {
    constexpr once_flag() noexcept = default;
    once_flag(const once_flag&) = delete;
    once_flag& operator=(const once_flag&) = delete;
  };

 template<typename _Callable, typename... _Args>
    void
    call_once(once_flag& __once, _Callable&& __f, _Args&&... __args)
    {
      auto __callable = [&] {
    std::__invoke(std::forward<_Callable>(__f),
      std::forward<_Args>(__args)...);
      };
    }
}

Проверил её на PC/MingW, отредактировав штатный <mutex> в mingw и пересобрал onnxruntime : вариант рабочий.

Данную хрень Микрософт любит пихать в Лямбда-функторы:

// Returns rprog_, computing it if needed.
re2::Prog* RE2::ReverseProg() const {
  std::call_once(rprog_once_, [](const RE2* re) {
    re->rprog_ =
        re->suffix_regexp_->CompileToReverseProg(re->options_.max_mem() / 3);
    if (re->rprog_ == NULL) {
      if (re->options_.log_errors())
        LOG(ERROR) << "Error reverse compiling '" << trunc(*re->pattern_)
                   << "'";
    }
  }, this);
  return rprog_;
}


По началу делал так - тоже работало:

// Returns rprog_, computing it if needed.
re2::Prog* RE2::ReverseProg() const {
    const RE2* re;
    re->rprog_ =
        re->suffix_regexp_->CompileToReverseProg(re->options_.max_mem() / 3);
    if (re->rprog_ == NULL) {
      if (re->options_.log_errors())
        LOG(ERROR) << "Error reverse compiling '" << trunc(*re->pattern_)
                   << "'";
    }
  return rprog_;
}

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

Едем дальше...

#316
3:20, 12 авг 2025

Ещё один:  std::condition_variable в минимальном исполнении:

namespace std
{
  class condition_variable
  {
  public:
    condition_variable() noexcept;
    ~condition_variable() noexcept;

    condition_variable(const condition_variable&) = delete;
    condition_variable& operator=(const condition_variable&) = delete;

    void    notify_one() noexcept;

    void    notify_all() noexcept;

    void    wait(unique_lock<mutex>& __lock) noexcept;
  };

} // namespace
#317
(Правка: 11:49) 11:36, 13 авг 2025

Запустил ONNXruntime на T113-s3 (ARM Cortex-A7).  Пока прогнал тестовый пример - простая нейросеть:

+ Показать

Результаты совпадают: на ПК и T113-s3 - одинаковые значения.

Для приличия нужно дописать нижний уровень файлового ввода-вывода, чтобы std::fstream работал как минимум на чтение файлов с SD-карты.


Несколько слов о том, как портировал с Винды/ПК на голое железо:

2 | Нейросети на базе Keras, Tensorflow, Torch и другие. Теория и практика применения. Pyhton/C++/Pascal

На каждом этапе делается что-то одно. Крайне тяжело сделать всё сразу - сразу этап 5, поскольку требуется изменить множество параметров и постоянно чистить код.

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

Выполненные преобразования:

1) Динамическая библиотека (DLL) => Статическая библиотека
2) Visual Studio => BAT-скрипты
3) MSVC => GCC
4) ПК => ARM

На каждом этапе делается периодическая проверка обратной связи: при каждой проверке все  исходные коды ДОЛЖНЫ предоставлять работоспособную среду для программного проекта (например, кодек). Если что-то сломалось, возврат к предыдущему этапу и исправляем.

#318
(Правка: 12:38) 12:33, 13 авг 2025

Gradius

Несколько слов о том, как портировал с Винды/ПК на голое железо:

...
Впечатляет!
Я имею в виду не только этот пост.
Если уж это не учебник, то к прочтению точно должно быть рекомендовано.

#319
(Правка: 15:21) 15:16, 13 авг 2025

Сложная модель тоже даёт одинаковые результаты:

2 | Нейросети на базе Keras, Tensorflow, Torch и другие. Теория и практика применения. Pyhton/C++/Pascal

1)

+ Показать

2)

+ Показать

3)

+ Показать
#320
2:59, 14 авг 2025

Сделал замеры по потреблению памяти нейросетками во время загрузки и выполнения:

Модель WavLM: размер файла - 338 МБ, расходуемая память загрузки в onnxruntime - 647,5 МБ

Модель Focal Compressor: размер файла - 75,8 МБ, расходуемая память загрузки в onnxruntime - 138,8 МБ

Модель Focal Decompressor: размер файла - 75,8 МБ, расходуемая память загрузки в onnxruntime - 151,1 МБ

Модель Vocos: размер файла - 65 МБ, расходуемая память загрузки в onnxruntime - 119,9 МБ

Начальный инит onnxruntime: 3,1 МБ (без сеток)

Дополнительная расходы на память во время инференса: 8,2 МБ.


Что я вижу: загрузка каждой модели требует почти в 2 раза больше памяти. Делаю предположение - возможно, в onxruntime не освобождается память после загрузки файла модели: модель парсится в другой участок памяти, а исходный образ файла модели остаётся в памяти.

Попробую исследовать.

#321
10:02, 14 авг 2025

Gradius

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

Да, похоже.

#322
10:53, 14 авг 2025

Gradius
> Делаю предположение - возможно, в onxruntime не освобождается память после загрузки файла модели: модель парсится в другой участок памяти, а исходный образ файла модели остаётся в памяти.

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

 Ort::SessionOptions session_options;

 session_options.AddConfigEntry("session.use_ort_model_bytes_directly","1");
 session_options.AddConfigEntry("session.disable_prepacking","1");

Теперь все 4 модели + среда выполнения требуют 628 МБ.  А было 1069 МБ.

Но чёрт возьми, даже вокодер Vocos не удаётся загрузить в память T113-s3 :)))

Его файл модели 65 МБ, значит пиковый расход - примерно в 2 раза больше, тоесть 130 МБ.  У T113-s3 всего 128 МБ,  из них 10 МБ сама программа и 1 МБ настройка MMU.  Итого свободно  117 МБ, что не хватает прогрузить и пропарсить Vocos.

Следующий шаг:  квантизация моделей и/или переход на доску с бОльшим объёмом памяти.  Плюс можно добавить сюда дистилляцию модели WavLM (338 МБ) на более легковесную.  Ну и плюс может можно выполнить оптимизацию модели (на стороне ПК средствами ONNXRuntime).

P.S. Ещё можно попробовать парсить с файла, не загружая в буфер.  Но это будет медленно скорее всего. Да и не сильно просто это сделать.

#323
(Правка: 15:42) 14:59, 14 авг 2025

Gradius
> Следующий шаг: квантизация моделей

Попробовал этот шаг.  Все модели сконвертились, кроме кодера WavLM - почему-то не захотел.

Про конвертацию моделей в fp16 здесь:
https://onnxruntime.ai/docs/performance/model-optimizations/float16.html

Размер модели уменьшается почти в 2 раза. Но не всё так просто:

1) Размерность входного и выходного тензоров - специальный тип. Операторы и типкасты расписаны здесь:
https://onnxruntime.ai/docs/api/c/struct_ort_1_1_float16__t.html

Но можно не заморачиваться и заставить модель сохранить входной и выходной тензоры в традиционном формате fp32 (keep_io_types=True):

model=onnx.load("coder.onnx")
model_fp16=float16.convert_float_to_float16(model,keep_io_types=True)
onnx.save(model_fp16,"coder_fp16.onnx")

2) При многократном выполнении сетки, первые 2 итерации - растёт потребление памяти, а потом останавливается.

Лечится принудительной установкой следующих сетов:

 session_options.DisableMemPattern();
 session_options.DisableCpuMemArena();
 session_options.AddConfigEntry("session.use_memory_arena","0");
 session_options.AddConfigEntry("session.use_arena_allocation","0");

Подробнее тут:
https://github.com/microsoft/onnxruntime/discussions/22763

3) Скорость работы сконверченной модели fp16 на целевом железе T113-s3 немного просела.

+ Показать

И кстати, видна небольшая разница в результатах выдачи на ПК и ARM: в младших разрядах.

#324
16:37, 14 авг 2025

Gradius
> Скорость работы сконверченной модели fp16 на целевом железе T113-s3 немного просела.
Не работает векторный asm или нет нативной поддержки fp16? Нейросети — практически идеальный случай для векторизации, должно быть, наоборот, ускорение в ~2 раза.

#325
(Правка: 17:45) 17:11, 14 авг 2025

}:+()___ [Smile]
> Не работает векторный asm или нет нативной поддержки fp16? Нейросети — практически идеальный случай для векторизации, должно быть, наоборот, ускорение в ~2 раза.

Скорее всего нет нативной поддержки fp16.  Есть только fp32, fp64.
NEON включен. Часть сорцов даже с ним на ассемблере.

Заданы ключи:

-mfloat-abi=hard -mfpu=neon-vfpv4 -ftree-vectorize -fno-math-errno

Замерил время:  у fp32 модели  1 выполнение 330 мс, у fp16 модели 406 мс.


Плюс ещё обнаружил, что если модель fp16 имеет сильно большие модули значений, то они будут nan и inf.  Такое у Focal Compressor. 

Там потом идёт нормализация вектора перед квантователем - но это не слой, а жёсткая функция без параметров обучения.


P.S. Проверил.  Действительно, аппаратная плавучка fp16 не поддерживается у T113-s3. Есть только аппаратная плавучка fp32, fp64 и NEON.

https://developer.arm.com/documentation/dui0774/g/chr1383660321827

printf("0x%02x\n", __ARM_FP);  вернул 0x0e

#326
(Правка: 3:58) 3:13, 18 авг 2025

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

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

Общее время работы кодека определяется формулой (выведена из практических результатов):

Tcodec = Tcoder + Tcompressor + 2 * (Tdecompressor + Tdecoder)

Множитель 2 на декодирующей стороне: декомпрессор и декодер работают дважды, так как требуется перекрытие с оконными функциями для сглаживания сегментов в один непрерывный  поток. Выбрано перекрытие окон 50%.

Львиную долю времени отнимает декодер(Tdecoder), редуцированная версия вокодера Vocos.

При сегменте в 64 фрейма (по 20 мс) каждый: суммарное время Tcodec не должно превышать 1280 мс.  Именно при таких условиях кодек будет успевать потоково кодировать и декодировать.

Кроме того, нужно ещё учесть время на затраты вычислений FFT.

Широкое пространство свободы:

- на практике при полу-дуплексной связи требуется только либо кодирование, либо декодирование

- можно использовать два ядра : одно кодирует, другое декодирует - тем самым увеличив модели, улучшить результат и получить бОльший временной ресурс Tcodec.


Прорабатываю схему дистилляции с пониженным размером выходного тензора у обучаемой сети:

1 | Нейросети на базе Keras, Tensorflow, Torch и другие. Теория и практика применения. Pyhton/C++/Pascal

Суть следующая:

Есть эталонный кодер - мощный обученный на нескольких сотнях тысячах часов трансформер WavLM, обладающий следующими ценными свойствами:

- подавляет шум
- подавляет помеху 50 Гц
- выделяет речевые признаки

И это лучше, чем просто использовать спектр на входе Focal Compressor.

Фичность WavLM имеет размерность 1024, сама модель большая.

Теперь необходимо сделать не только дистилляцию, но и понизить размерность фичности новой легковесной сети (назвал её MelLM).

Для этого необходимо добавить проекцию (Projection: линейный слой без активации), чтобы сравнять размерность выходных тензоров по фичам (с 256 до 1024).

Затем произвести обучение(MelLM и Projection) по критерию лосса MSE.

Использовать обученную сеть MelLM, естественно уже без слоя проекции: features mini размерности 256.

#327
(Правка: 9:49) 9:38, 18 авг 2025

Gradius

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

Нужно ли проводить предворительные экспиременты, или сразу на "железе" будешь делать?
Gradius

Прорабатываю схему дистилляции с пониженным размером выходного тензора у обучаемой сети:

Значит придётся обучать сеть снова, или уже обученную использовать будешь?
Gradius

Есть эталонный кодер - мощный обученный на нескольких сотнях тысячах часов трансформер WavLM, обладающий следующими ценными свойствами:

Правильно я понимаю, что это уже готовые сторонние данные?

#328
(Правка: 11:32) 11:24, 18 авг 2025

flint2
> Нужно ли проводить предворительные экспиременты, или сразу на "железе" будешь делать?

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

flint2
> Значит придётся обучать сеть снова, или уже обученную использовать будешь?

Естественно.  Все 4 узла кодека: кодер, компрессор, декомпрессор и декодер обучаются по-новой. В противном случае придётся тащить за собой проекционные костыли для снижения/повышения размерности.

К примеру - переобучил вокодер Vocos.  При этом уменьшил его модель с 65 до 9 МБ. Просто уменьшил число блоков ConvNET с 8 до 4, входной тензор 80 мел-полос, а не 1024.  Плюс число каналов скрытых слоёв с 512 уменьшил до 160.  Результат - работает.  Правда, голос получился с небольшим эхом, но это из-за того что вместо 2,5 млн. итераций обучения сделал всего 500 000.

flint2
> Правильно я понимаю, что это уже готовые сторонние данные?

Верно. Трансформер-кодер  использую уже обученным. 

Как вы в одиночку представляете себе обучение на сотнях тысячах часов разговорной речи на одном GPU? :) При этом данные на выходе у завода-изготовителя (Microsoft) настолько насыщенные и репрезентативные, что трескаются, как переспелый и вкусный арбуз :)

https://github.com/microsoft/unilm/tree/master/wavlm

https://arxiv.org/pdf/2110.13900

А вот  сдистиллировать знания, да и ещё уменьшить тензор - это будет намного быстрее.  68 000 итераций достаточно, дальше лосс уменьшается очень лениво.

Вот что получилось:  слева - выход с учителя (WavLM, 360 MB, dim 1024, волна на входе), справа - выход ученика + проекция (MelLM, 6 МБ, dim 256, спектр на входе) :

+ Показать


Похожи?

P.S.  По сути, я делаю свой кодек, в основе которого лежат такие же принципы, как в FocalCodec.

С некоторыми отличиями:

- поддержка потокового/каузального режима

- спектр на входе, вместо звуковой волны

- уменьшенные модели

#329
(Правка: 13:59) 12:42, 18 авг 2025

Gradius

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

Примерно представляю. ))
Я с горяча похожее делал, но только с текстом - вся библиотека flibusta + по мелочам (СЕРИЯ.Звёзды_научной_фантастики, СЕРИЯ.Настоящая_фантастика, Библиотека приключений и научной фантастики...) в общем вся фантастика без повторений + детективы и триллеры.. Ну очень долго всё, месяцы.
Gradius

Похожи?

Очень.
Gradius

По сути, я делаю свой кодек, в основе которого лежат такие же принципы, как в FocalCodec.

С некоторыми отличиями:

Моя понимать.
Круто!

Страницы: 121 22 23 2430 Следующая »
ПрограммированиеФорумОбщее