Руководство для начинающих по настройке крупных языковых моделей (LLM)

Beginner's Guide to Configuring Large Language Models (LLM)

Введение

Отправьтесь в путешествие сквозь эволюцию искусственного интеллекта и поразительные достижения в области обработки естественного языка (Natural Language Processing, NLP). Всего за миг ИИ выстрелил вверх и изменил наш мир. Сейсмическое воздействие настройки больших языковых моделей полностью преобразило NLP, революционизируя наши технологические взаимодействия. Вернемся к 2017 году, решающему моменту, отмеченному фразой «Внимание – это все, что вам нужно», порождающему новаторскую архитектуру «Трансформер». Эта архитектура теперь является угловым камнем NLP, незаменимым ингредиентом в каждом рецепте больших языковых моделей, включая известный ChatGPT.

Представьте себе возможность легко генерировать связный текст с богатым контекстом – в этом заключается волшебство моделей, таких как GPT-3. Они являются мощными инструментами для создания чат-ботов, переводов и генерации контента, их блестящесть проистекает из архитектуры и сложной взаимосвязи предварительного обучения и настройки. Наша предстоящая статья погружается в эту симфонию, раскрывая искусство использования больших языковых моделей для задач, владея динамичным дуэтом предварительного обучения и настройки с мастерским мастерством. Присоединяйтесь к нам, чтобы разобраться в этих преобразующих техниках!

Цели обучения

  • Понять различные способы создания приложений на основе больших языковых моделей.
  • Изучить техники, такие как извлечение признаков, настройка слоев и методы адаптеров.
  • Настроить большую языковую модель на задачу с использованием библиотеки Huggingface transformers.

Начало работы с LLMs

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

Мы используем приложения на основе этих LLMs ежедневно, даже не осознавая этого. Google использует BERT (Bidirectional Encoder Representations for Transformers) для различных приложений, таких как завершение запроса, понимание контекста запросов, вывод более релевантных и точных результатов поиска, языковой перевод и другие.

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

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

Обзор различных способов создания приложений на основе больших языковых моделей

Часто мы видим интересные приложения LLM в повседневной жизни. Вам интересно узнать, как создавать приложения на основе LLM? Вот 3 способа создания приложений на основе LLM:

  1. Обучение LLM с нуля
  2. Настройка больших языковых моделей
  3. Подсказка

Обучение LLM с нуля

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

Обучение LLM с нуля также известно как предварительное обучение. Предварительное обучение – это техника, при которой большая языковая модель обучается на огромном количестве неразмеченного текста. Но вопрос в том, “Как мы можем обучить модель на неразмеченных данных и ожидать, что модель будет точно предсказывать данные?”. Здесь на помощь приходит концепция «Самообучение». В самообучении модель маскирует слово и пытается предсказать следующее слово с помощью предшествующих слов. Например, предположим, у нас есть предложение: “Я – ученый по данным”.

Модель может создать свои собственные размеченные данные на основе этого предложения, например:

Это называется предсказанием следующего слова, выполняемым моделью с маскировкой слова (Masked Language Model, MLM). BERT, модель с маскировкой слов, использует эту технику для предсказания маскированного слова. Мы можем представить MLM как концепцию “заполнения пропуска”, в которой модель предсказывает, какое слово может подойти в пропуск. Есть разные способы предсказать следующее слово, но в этой статье мы говорим только о BERT, модели с маскировкой слов. BERT может смотреть как на предшествующие, так и на последующие слова, чтобы понять контекст предложения и предсказать маскированное слово.

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

Настройка больших языковых моделей

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

Подсказка

Подсказка – самая простая из всех 3-х техник, но немного хитрая. Она заключается в том, чтобы дать модели контекст (подсказку), на основе которой модель выполняет задачи. Представьте себе, что вы учите ребенка разделу из его учебника подробно, очень детально объясняя материал, а затем просите его решить задачу, связанную с этим разделом.

В контексте LLM, возьмем, например, ChatGPT; мы устанавливаем контекст и просим модель следовать указаниям для решения поставленной задачи.

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

Пример: Я – Data Scientist с двухлетним опытом работы и в настоящее время готовлюсь к собеседованию на работу в такой-то компании. Я люблю решать проблемы и в настоящее время работаю с передовыми моделями NLP. Я в курсе последних тенденций и технологий. Задайте мне очень сложные вопросы о модели Transformer, которые интервьюер этой компании может задать на основе предыдущего опыта компании. Задайте мне десять вопросов и также дайте ответы на них.

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

Понимание различных техник настройки

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

Существуют 3 способа традиционной настройки LLM.

Извлечение признаков

Люди используют эту технику для извлечения признаков из заданного текста, но зачем нам нужно извлекать вложения из заданного текста? Ответ прост. Потому что компьютеры не понимают текст, нужно иметь представление о тексте, которое мы можем использовать для выполнения различных задач. После извлечения вложений они способны выполнять задачи, такие как анализ тональности, идентификация схожести документов и т. д. При извлечении признаков мы блокируем базовые слои модели, то есть мы не обновляем параметры этих слоев; обновляются только параметры слоев классификатора. Слои классификатора включают полносвязные слои.

Полная настройка модели

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

Настройка с помощью адаптера

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

Реализация: настройка BERT на конкретную задачу

Теперь, когда мы знаем техники настройки, давайте выполним анализ тональности отзывов на фильмы IMDB с использованием BERT. BERT – это большая языковая модель, объединяющая слои трансформера и являющаяся только кодировщиком. Его разработала Google и он показал отличные результаты в различных задачах. BERT поставляется в разных размерах и вариантах, таких как BERT-base-uncased, BERT Large, RoBERTa, LegalBERT и многие другие.

Модель BERT для выполнения анализа тональности

Давайте использовать модель BERT для выполнения анализа тональности отзывов на фильмы IMDB. Для бесплатного использования GPU рекомендуется использовать Google Colab. Начнем обучение, загрузив некоторые важные библиотеки.

Поскольку BERT (Bidirectional Encoder Representations for Encoders) основан на трансформерах, первым шагом будет установка transformers в нашу среду.

!pip install transformers

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

import pandas as pd
import numpy as np
import os
from sklearn.model_selection import train_test_split
import torch
import torch.nn as nn
from transformers import BertTokenizer, BertModel

Для более быстрого вычисления нам нужно изменить устройство с CPU на GPU

device = torch.device("cuda")

Следующим шагом будет загрузка нашего набора данных и просмотр первых 5 записей в наборе данных.

df = pd.read_csv('/content/drive/MyDrive/movie.csv')
df.head()

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

x_train, x_val, y_train, y_val = train_test_split(df.text, df.label, random_state = 42, test_size = 0.2, stratify = df.label)

Импорт и загрузка модели BERT

Давайте импортируем и загрузим модель BERT и токенизатор.

from transformers.models.bert.modeling_bert import BertForSequenceClassification
# импорт предобученной модели BERT-base
BERT = BertModel.from_pretrained('bert-base-uncased')
# Загрузка токенизатора BERT
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')

Мы будем использовать токенизатор для преобразования текста в токены с максимальной длиной 250 и заполнением и обрезкой при необходимости.

train_tokens = tokenizer.batch_encode_plus(x_train.tolist(), max_length = 250, pad_to_max_length=True, truncation=True)
val_tokens = tokenizer.batch_encode_plus(x_val.tolist(), max_length = 250, pad_to_max_length=True, truncation=True)

Токенизатор возвращает словарь с тремя парами ключ-значение, содержащими input_ids, которые являются токенами, относящимися к определенному слову; token_type_ids, который является списком целых чисел, разделяющих разные сегменты или части ввода, и attention_mask, который указывает, на какой токен следует обратить внимание.

Преобразование этих значений в тензоры

train_ids = torch.tensor(train_tokens['input_ids'])
train_masks = torch.tensor(train_tokens['attention_mask'])
train_label = torch.tensor(y_train.tolist())
val_ids = torch.tensor(val_tokens['input_ids'])
val_masks = torch.tensor(val_tokens['attention_mask'])
val_label = torch.tensor(y_val.tolist())

Загрузка TensorDataset и DataLoaders для дальнейшей предобработки данных и их подготовки для модели.

from torch.utils.data import TensorDataset, DataLoader
train_data = TensorDataset(train_ids, train_masks, train_label)
val_data = TensorDataset(val_ids, val_masks, val_label)
train_loader = DataLoader(train_data, batch_size = 32, shuffle = True)
val_loader = DataLoader(val_data, batch_size = 32, shuffle = True)

Наша задача – заморозить параметры BERT с использованием нашего классификатора, а затем дообучить эти слои на нашем пользовательском наборе данных. Итак, давайте заморозим параметры модели. for param in BERT.parameters():param.requires_grad = FalseТеперь нам нужно определить прямой и обратный проход для добавленных слоев. Модель BERT будет выступать в качестве извлекателя признаков, в то время как мы должны явно определить прямой и обратный проходы для классификации.

class Model(nn.Module):
  def __init__(self, bert):
    super(Model, self).__init__()
    self.bert = bert
    self.dropout = nn.Dropout(0.1)
    self.relu = nn.ReLU()
    self.fc1 = nn.Linear(768, 512)
    self.fc2 = nn.Linear(512, 2)
    self.softmax = nn.LogSoftmax(dim=1)
  def forward(self, sent_id, mask):
    # Передача входных данных в модель
    outputs = self.bert(sent_id, mask)
    cls_hs = outputs.last_hidden_state[:, 0, :]
    x = self.fc1(cls_hs)
    x = self.relu(x)
    x = self.dropout(x)
    x = self.fc2(x)
    x = self.softmax(x)
    return x

Переместим модель на GPU

model = Model(BERT)
# отправляем модель на GPU
model = model.to(device)

Определение оптимизатора

# оптимизатор от библиотеки hugging face transformers
from transformers import AdamW
# определение оптимизатора
optimizer = AdamW(model.parameters(),lr = 1e-5)

На данный момент мы предобработали набор данных и определили нашу модель. Теперь пришло время обучить модель. Мы должны написать код для обучения и оценки модели. Функция обучения:

def train():
  model.train()
  total_loss, total_accuracy = 0, 0
  total_preds = []
  for step, batch in enumerate(train_loader):
    # Перемещаем пакет на GPU, если доступно
    batch = [item.to(device) for item in batch]
    sent_id, mask, labels = batch
    # Очищаем ранее вычисленные градиенты
    optimizer.zero_grad()
    # Получаем предсказания модели для текущего пакета
    preds = model(sent_id, mask)
    # Вычисляем потери между предсказаниями и метками
    loss_function = nn.CrossEntropyLoss()
    loss = loss_function(preds, labels)
    # Добавляем к общим потерям
    total_loss += loss.item()
    # Обратное распространение и обновление градиента
    loss.backward()
    optimizer.step()
    # Перемещаем предсказания на CPU и преобразуем в массив numpy
    preds = preds.detach().cpu().numpy()
    # Добавляем предсказания модели
    total_preds.append(preds)
  # Вычисляем средние потери
  avg_loss = total_loss / len(train_loader)
  # Соединяем предсказания
  total_preds = np.concatenate(total_preds, axis=0)
  # Возвращаем средние потери и предсказания
  return avg_loss, total_preds

Функция оценки

def evaluate():
  model.eval()
  total_loss, total_accuracy = 0, 0
  total_preds = []
  for step, batch in enumerate(val_loader):
    # Перемещаем пакет на GPU, если доступно
    batch = [item.to(device) for item in batch]
    sent_id, mask, labels = batch
    # Очищаем ранее вычисленные градиенты
    optimizer.zero_grad()
    # Получаем предсказания модели для текущего пакета
    preds = model(sent_id, mask)
    # Вычисляем потери между предсказаниями и метками
    loss_function = nn.CrossEntropyLoss()
    loss = loss_function(preds, labels)
    # Добавляем к общим потерям
    total_loss += loss.item()
    # Обратное распространение и обновление градиента
    loss.backward()
    optimizer.step()
    # Перемещаем предсказания на CPU и преобразуем в массив numpy
    preds = preds.detach().cpu().numpy()
    # Добавляем предсказания модели
    total_preds.append(preds)
  # Вычисляем средние потери
  avg_loss = total_loss / len(val_loader)
  # Соединяем предсказания
  total_preds = np.concatenate(total_preds, axis=0)
  # Возвращаем средние потери и предсказания 
  return avg_loss, total_preds

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

# устанавливаем начальные потери как бесконечность
best_valid_loss = float('inf')
# определение эпох
epochs = 5
# пустые списки для сохранения обучающих и проверочных потерь на каждую эпоху
train_losses=[]
valid_losses=[]
# для каждой эпохи
for epoch in range(epochs):
  print('\n Эпоха {:} / {:}'.format(epoch + 1, epochs))
  # обучаем модель
  train_loss, _ = train()
  # оцениваем модель
  valid_loss, _ = evaluate()
  # сохраняем лучшую модель
  if valid_loss < best_valid_loss:
    best_valid_loss = valid_loss
    torch.save(model.state_dict(), 'saved_weights.pt')
    # добавляем обучающие и проверочные потери
  train_losses.append(train_loss)
  valid_losses.append(valid_loss)
  print(f'\n Обучающая потеря: {train_loss:.3f}')
  print(f'Проверочная потеря: {valid_loss:.3f}')

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

Заключение

В этой статье мы исследовали мир донастройки больших языковых моделей (LLMs) и их значительное влияние на обработку естественного языка (NLP). Обсудили процесс предварительного обучения, где LLM обучаются на большом количестве неразмеченного текста с использованием самообучения. Мы также углубились в донастройку, которая включает адаптацию предварительно обученной модели для конкретных задач, и подсказку, где моделям предоставляется контекст для генерации соответствующих выходных данных. Кроме того, мы рассмотрели различные методы донастройки, такие как извлечение признаков, полная донастройка модели и донастройка на основе адаптеров. Большие языковые модели перевернули мир NLP и продолжают стимулировать прогресс в различных приложениях.

Часто задаваемые вопросы