В этой статье я покажу вам, как вы можете точно настроить и обучить своего Трансформера с помощью HuggingFace.

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

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

В этой статье мы выполним следующие шаги:

  • Сотрите тексты песен из интернета.
  • Преобразование текста в формат .txt.
  • Скачиваем токенизатор и модель с HuggingFace.
  • Тонкая настройка модели с использованием очищенных данных.
  • Создайте новую песню с результатами модели.

Веб-скрапинг

Мы должны найти веб-сайт, на котором размещены тексты исполнителя, которого мы хотим использовать, в нашем случае Diomedes. Приходите и обнаружите, что на этом веб-сайте есть все тексты песен в доступной (и удаляемой) форме.

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

import requests
from bs4 import BeautifulSoup

Веб-скрапинг — очень повторяющийся процесс, и рекомендуется, чтобы вы понимали HTML при его выполнении, чтобы вы могли понять, что мы делаем.

page = requests.get('https://www.letras.com/diaz-diomedes/')
soup = BeautifulSoup(page.text,'html.parser')

Изучая код веб-сайта, мы видим, что список песен находится внутри тега списка (‹li›), поэтому давайте рассмотрим эти теги конкретно.

listOfSongs = []

for i in soup.find_all('li'):
  listOfSongs.append(i)

listOfSongs = [str(x) for x in listOfSongs]

После того, как мы увидели его содержимое, давайте добавим его в список, если он содержит «https», чтобы убедиться, что мы получаем ссылки на конкретные тексты песен.

newSongs = []

for link in listOfSongs:
  if 'https' in link:
    newSongs.append(link)

Вот как выглядит наш новый список:

Теперь давайте извлечем ссылки из песен:

urls = []
for song in newSongs:
  try:
    urls.append(song.split('"')[13])
  except:
    print(song)

Вот как выглядит наш новый список текстов песен:

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

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

На этом часть сочинения текстов окончена. Теперь мы должны перейти к части статьи о токенизаторе и модели.

Токенизаторы и преобразователи

Прежде чем приступить к выбору нашего токенизатора, нам нужно будет выбрать модель, которую мы будем использовать для генерации текстов. Обычный трансформатор средней мощности - ГПТ-2, поэтому будем использовать именно его. Еще один хорошо сработавший — BERT и RoBERTa, если вы хотите попробовать и их. Я протестировал все вышеперечисленные трансформаторы и обнаружил, что GPT-2 дает наиболее стабильные результаты.

Имея в виду, что все песни Диомедеса Диаса на испанском языке, нам придется использовать испанский токенизатор. В нашем случае мы не будем обучать токенизатор, потому что на данный момент достаточно испанских токенизаторов. Но что именно делает токенизатор? Токенизаторы разбивают предложения на слова, которые затем преобразуются в идентификаторы и сохраняются в таблице поиска.

После тестирования нескольких рекомендуемых токенизаторов с веб-сайта HuggingFace я обнаружил, что flax-community/gpt-2-spanish — отличный токенизатор для нашего случая использования, поскольку он был обучен на наборе данных OSCAR.

До сих пор мы очищали нашу лирику и говорили о токенизации, но мы не взглянули на то, что такое трансформатор.

Преобразователи — это архитектура модели, состоящая из пары кодер-декодер, которые используют механизм внимания для преобразования одной последовательности в другую. Впервые это было представлено в статье Внимание — это все, что вам нужно. Одним из основных преимуществ этих преобразователей является то, что их можно распараллеливать, а не последовательно, как RNN (LSTM, GRU и т. д.). Это помогает сократить время обучения.

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

Если вы хотите узнать больше о трансформерах, я бы посоветовал вам взглянуть на книгу Максима Что такое трансформер? а также статья Джея Аламмара об этом.

Преобразователи отлично подходят для перевода, генерации последовательностей и анализа настроений.

Давайте загрузим токенизатор для нашей обработки текста

from transformers import AutoTokenizer, AutoModelForCausalLM

tokenizer = AutoTokenizer.from_pretrained("flax-community/gpt-2-spanish")

model = AutoModelForCausalLM.from_pretrained("flax-community/gpt-2-spanish")

Теперь давайте загрузим наш тест в нашу записную книжку и разделим его на каждую новую строку.

После этого давайте разделим его на наш обучающий/тестовый набор с помощью SciKit Learns train_test_split.

train, test = train_test_split(lines,test_size=0.15)

print("Train dataset length: "+str(len(train)))
print("Test dataset length: "+ str(len(test)))

>>> Train dataset length: 4676
>>> Test dataset length: 826

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

with open('traincancionesDiomedes.txt', 'w') as f:
  for t in train:
    f.write(t)
    f.write(' ')


with open('testcancionesDiomedes.txt', 'w') as f:
  for t in test:
    f.write(t)
    f.write(' ')

train_path = 'traincancionesDiomedes.txt'
test_path = 'testcancionesDiomedes.txt'

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

from transformers import TextDataset,DataCollatorForLanguageModeling

def load_dataset(train_path,test_path,tokenizer):
    train_dataset = TextDataset(
          tokenizer=tokenizer,
          file_path=train_path,
          block_size=128)

    test_dataset = TextDataset(
          tokenizer=tokenizer,
          file_path=test_path,
          block_size=128)

    data_collator = DataCollatorForLanguageModeling(
        tokenizer=tokenizer, mlm=False,
    )
    return train_dataset,test_dataset,data_collator

train_dataset,test_dataset,data_collator = load_dataset(train_path,test_path,tokenizer)

При вызове DataCollatorForLanguageModeling мы будем использовать mlm = False (mlm означает моделирование с использованием маскированного языка).

Теперь давайте начнем настройку аргумента обучения модели, указав выходной каталог (output_dir), количество эпох обучения (num_train_epochs), размер пакета для обучения (per_device_train_batch_size), размер пакета для оценки модели (per_device_eval_batch_size), количество шагов обновления между двумя оценками (eval_steps), контрольная точка для сохранения модели после n шагов (save_steps) и количество шагов прогрева для планировщика скорости обучения (warmup_steps).

model = AutoModelWithLMHead.from_pretrained("flax-community/gpt-2-spanish")

training_args = TrainingArguments(
    output_dir="./gpt2-diomedes-2", 
    overwrite_output_dir=True,
    num_train_epochs=300, 
    per_device_train_batch_size=32,
    per_device_eval_batch_size=64,
    eval_steps = 100, 
    save_steps=800, 
    warmup_steps=500
    )

trainer = Trainer(
    model=model,
    args=training_args,
    data_collator=data_collator,
    train_dataset=train_dataset,
    eval_dataset=test_dataset
)

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

Теперь, когда мы настроили наш тренер и наши данные, давайте начнем обучение модели.

trainer.train()

Используя стандартный графический процессор Google Colab Pro, потребовалось около часа, чтобы пройти 300 эпох. Вот результаты обучения модели

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

from transformers import pipeline

diomedes = pipeline('text-generation',model='./gpt2-diomedes-2', tokenizer='flax-community/gpt-2-spanish')

results = diomedes('Ay! ')[0]['generated_text']

>>> 'Ay! Soy partidario decidir maldecir Que me perdones todo lo que yo hago Que no te conocí en la playa porque no me gustaba su actitud Que ya te la voy a pasar lista andarán mis canciones que acaban pero'

Сгенерированный текст на самом деле звучит как песня! Давайте теперь посмотрим, как мы можем создать полную песню!

Создание песни

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

Из нашего набора данных давайте выберем несколько примеров, возьмем их первые одно или два слова и используем их в качестве подсказки.

import random

prompts = []
for line in lines:
  prompts(' '.join(line.split(' ')[:2]))

random_prompts =  random.sample([x for x in prompts], 20)

При этом у нас есть список из 20 случайно выбранных подсказок из двух слов, которые мы будем использовать для создания песни из 20 строк.

Давайте теперь вызовем модель и повторим ее, чтобы сгенерировать нашу новую песню. Мы также можем установить параметр max_length, чтобы ограничить количество символов, генерируемых моделью. Давайте попробуем задать max_length от 20 до 30 символов, которые будут выбраны случайным образом с помощью random.randint().

song = []

for line in random_inits:
  song.append(diomedes(f'{line}',max_length=random.randint(20, 30))[0]['generated_text'])

Посмотрим, что сгенерировала наша модель.

>>> ['con el paso del tiempo Que no crea que todo terminó Y ni se imaginan el mundo se va, yo era el',
 'Y que los cumplas a todos gusto y alegremente deseando que pase un año más más lleno de proyectos que he',
 'Qué triste sería no quererte a ti cerca Para ser un gran victoria También es un compositor que vive d',
 'Al fin : Aleluas No sé por qué será, pero aléjense de la',
 'Dime nada yo tengo el present. Y otra ves maana morirme Pero no tienes que temer, no tienes porque aclarar',
 'Y al que encuentra es al difunto  Con lo más sublime del sentimiento  Dime que hizo mal presentándote con su',
 ...
 'Yo soy un hombre solo Será en este lugar especial Quedar para ninguno Sí, y yo no',
 'Y andabas pensando en mí dijo la malvade, a la luna que respiros (se escuchaban dulces quejas ']

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

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

Заключительные замечания

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

Еще раз спасибо, что нашли время прочитать это.

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