HuggingArtists | Генерируем текст песен с трансформером за 5 минут

МЕНЮ


Главная страница
Поиск
Регистрация на сайте
Помощь проекту
Архив новостей

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


В этой статье я расскажу о том, как создавался проект HuggingArtists и что у него под капотом. Мне будет очень приятно, если вы поставите сразу звезду в репозитории: GitHub stars

В HuggingArtists, мы можем создавать тексты песен на основе конкретного исполнителя. Это было сделано путем fine-tune (точной настройки) предварительно обученного трансформера HuggingFace на собранных данных Genius. Кроме того, мы используем интеграцию Weights & Biases для автоматического учета производительности и прогнозов модели.

Запустить Демо ? Открыть в Colab

Полный отчет с красивыми и интерактивными графиками можно прочитать тут.

Все запуски сохраняются и визуализируются с помощью Weights & Biases и сохраняются по ссылке: Открыть в Colab

Disclaimer: Данный проект не предназначен для публикации какой-либо ложной информации или неприятных слов, а предназначен для проведения исследований по Natural Language Generation.

Eminem:

I am a bit of a nutter Like a fucking crazy nut But I just wanted to break and get a little more serious, so be patient I dont think I need no medical attention But I do know that these niggas gonna be in my prayers And they say Im a nutter Like a fucking crazy nut But I love you I am a nutter Like a fuckin nutter But I feel like the greatest that I can be With your body and your spirits But I dont know how I will act When I finally get out And I get pissed off Ill be right back at ya with the first one on the floor But I aint ever gonna get the respect youve been getting And Im a nutter...

I am bipolar while I’m rhyming, bipolar while I am hate When I’m rhyming, I may seem a little bit crazy But the thing’s with these rhymes that I pick I stick to the syllable ? unless it’s on a syllable Bread is full of charmin I am thinkin all of the garbage Hear the bass line and ya hear the bass line But hip hop is what makes’em songs I try to describe how I’m gettin’ em all, but I’m lazy When I’m rapin on a song, I’m just making it If I don’t got the right, In my mind...

Drake:

I am never livin the life that I never lived I walk around with my Glock 9 And lay down with my feet up This is my house, bitch Welcome to Detroit, this is my city Ive made it all my people proud I even sold my house down To get a possible placement as a burnt pit My little girl, she should ntroller Wanna flip sometimes on the upside But its hard to hold a grudge, I dont bite I say my wholehearted, prayer is my heart I pray for you when Im gone, but I gotta leave you in peace That we did not plan to be seeing each other, well We did plan to be seeing each other, well And if you ever need anything, I...


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

Парсим данные

Все данные были собраны с Genius с помощью этого скрипта.

Здесь мы используем asyncio и aiohttp для парализации сбора данных.

Все наборы данных доступны здесь: ссылка.

Пример использования в Colab:

!python parse.py        -?-artist_id=$ARTIST_ID       -?-token=$GENIUS_API_TOKEN       -?-save_path=$SAVE_PATH

Как использовать датасеты

Как загрузить набор данных непосредственно из библиотеки datasets (пример — Eminem):

from datasets import load_dataset dataset = load_dataset(?"huggingartists/eminem"?)

Структура датасета

Пример «train» выглядит следующим образом:

Этот пример был слишком длинным и был обрезан: {     "text"?: "Look, I was gonna go easy on you Not to hurt your feelings But I'm only going to get this one chance Something's wrong, I can feel it..." }

Поля датасета

Поля данных одинаковы для всех разбиений.

  • text: строка.

Разделение данных

Все данные сгруппированы в «train», но можно легко разделить на «train», «validation» и «test» с помощью нескольких строк кода:

from datasets import load_dataset, Dataset, DatasetDict import numpy as np datasets = load_dataset(?"huggingartists/eminem"?) train_percentage = 0.9 validation_percentage = 0.07 test_percentage = 0.03 train, validation, test = np.split(datasets[?'train'?]?[?'text'?]?, [?int?(?len?(datasets[?'train'?]?[?'text'?]?)?*train_percentage)?, int?(?len?(datasets[?'train'?]?[?'text'?]?)?*?(train_percentage + validation_percentage)?)?]?) datasets = DatasetDict(         {              'train'?: Dataset.from_dict(?{?'text'?: list?(train)?}?)?,              'validation'?: Dataset.from_dict(?{?'text'?: list?(validation)?}?)?,              'test'?: Dataset.from_dict(?{?'text'?: list?(test)?}?)         } )


Проще всего использовать Colab за пару минут: Открыть в Colab

Также вы можете использовать любую модель непосредственно с помощью pipeline для генерации текста:

from transformers import pipeline generator = pipeline(?'text-generation'?,                      model=?'huggingartists/eminem'?) generator(?"I am"?, num_return_sequences=?5?)

Или с библиотекой трансформеров:

from transformers import AutoTokenizer, AutoModelWithLMHead tokenizer = AutoTokenizer.from_pretrained(?"huggingartists/eminem"?) model = AutoModelWithLMHead.from_pretrained(?"huggingartists/eminem"?)


Мы будем использовать tokenizer, чтобы наша нейросеть могла понимать текст. Tokenizer переводит текст в цифры.
Например:

encoded_input = tokenizer("Hello, I'm a single sentence!") print(encoded_input)

Результат:

{       'input_ids': [101, 138, 18696, 155, 1942, 3190, 1144, 1572, 13745, 1104, 159, 9664, 2107, 102],        'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],        'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }

Программа возвращает словарь. input_ids — это индексы, соответствующие каждому токену в нашем предложении. Про attention_mask и token_type_ids можно почитать в документации.

Токенизатор может декодировать список идентификаторов токенов в соответствующем предложении:

tokenizer.decode(encoded_input["input_ids"], add_special_tokens=False)

Результат:

"Hello, I'm a single sentence!"

Теперь нам нужно тожесамое сделать с нашими данными. Мы можем вызвать токенизатор для всех наших текстов. Это очень просто, используя метод map из библиотеки datasets. Сначала мы определяем функцию, которая вызывает токенизатор в наших текстах:

def tokenize_function(examples):     return tokenizer(examples["text"])

Затем мы применяем его ко всем разделениям в нашем объекте datasets, используя batched=True и 4 процесса для ускорения предварительной обработки. После этого нам не понадобится текстовая колонка, поэтому мы ее отбрасываем.

tokenized_datasets = datasets.map(tokenize_function, batched=True, num_proc=4, remove_columns=["text"])

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

tokenized_datasets["train"][1]

Результат:

{       'attention_mask': [1, 1, 1, 1, 1, 1],       'input_ids': [238, 8576, 9441, 2987, 238, 252] }

Теперь самое сложное: нам нужно объединить все наши тексты вместе, а затем разделить результат на небольшие фрагменты определенного размера блока. Для этого мы снова будем использовать метод map с параметром batched=True. Эта опция фактически позволяет нам изменять количество примеров в наборах данных, возвращая другое количество примеров, чем мы получили. Таким образом, мы можем создавать наши новые образцы из серии примеров.

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

# block_size = tokenizer.model_max_length block_size = int(tokenizer.model_max_length / 4)

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

def group_texts(examples):     # Concatenate all texts.     concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()}     total_length = len(concatenated_examples[list(examples.keys())[0]])     # We drop the small remainder, we could add padding if the model supported it instead of this drop, you can         # customize this part to your needs.     total_length = (total_length // block_size) * block_size     # Split by chunks of max_len.     result = {         k: [t[i : i + block_size] for i in range(0, total_length, block_size)]         for k, t in concatenated_examples.items()     }     result["labels"] = result["input_ids"].copy()     return result

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

Также обратите внимание, что по умолчанию метод map отправит пакет из 1000 примеров для обработки функцией предварительной обработки. Поэтому здесь мы отбросим оставшуюся часть, чтобы сделать объединенные маркированные тексты кратными block_size каждые 1000 примеров. Вы можете настроить это, передав больший размер пакета (который также будет обрабатываться медленнее). Вы также можете ускорить предварительную обработку с помощью многопроцессорной обработки:

lm_datasets = tokenized_datasets.map(     group_texts,     batched=True,     batch_size=1000,     num_proc=1, )

Ура! Наши данные готовы!

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

from transformers import Trainer, TrainingArguments training_args = TrainingArguments(     f"output/{model_name}",     overwrite_output_dir=True,     learning_rate=1.372e-4,     weight_decay=0.01,     num_train_epochs=num_train_epochs,     save_total_limit=1,     save_strategy='epoch',     save_steps=1,     seed=seed_data,     logging_steps=5, )  trainer = Trainer(     model=model,     args=training_args,     train_dataset=lm_datasets["train"], )


Запускаем тренировку и ждем:

trainer.train()

Ожидаем завершения и сохраняем модель куда удобно.

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

Все запуски сохраняются и визуализируются с помощью Weights & Biases и сохраняются по ссылке: Открыть в Colab

Полный отчет с красивыми и интерактивными графиками можно прочитать тут.

Зап


Источник: habr.com

Комментарии: