Введение
В области обработки естественного языка (NLP) модели на основе архитектуры GPT (Generative Pretrained Transformer) стали основой для множества приложений, включая генерацию текста, чат-боты и перевод. В данной статье мы рассмотрим, как создать графическое интерфейсное приложение с использованием PyQt6 для настройки и создания кастомной модели GPT с использованием библиотеки transformers от Hugging Face.
Обзор проекта
Этот скрипт реализует интерфейс на PyQt6, который позволяет пользователю настроить параметры модели GPT и запустить процесс её создания. Приложение поддерживает загрузку уже существующего токенизатора и настройку модели через GUI. Процесс создания модели осуществляется в отдельном потоке, чтобы приложение оставалось отзывчивым.
Описание основных компонентов
- Основные элементы интерфейсаПриложение разделено на две основные вкладки:Создание модели: Вкладка отображает процесс создания модели и обновляет статус на каждом шаге. В завершение пользователю предлагается закрыть приложение.
Настройки: Эта вкладка позволяет пользователю настроить параметры модели, такие как количество слоев, количество голов внимания, размер эмбеддингов и другие критические параметры. - Потоковая обработкаСоздание модели выполняется в отдельном потоке (ModelThread), чтобы основной интерфейс оставался интерактивным. Это предотвращает "замораживание" приложения во время долгих вычислительных операций.
Как работает приложение?
- Создание пользовательского интерфейса
Основной класс MainWindow наследует QMainWindow и содержит метод initUI, который инициализирует элементы интерфейса. Приложение имеет две вкладки: одну для настройки параметров и другую для отображения процесса создания модели. - Настройка параметров модели
Вкладка "Настройки" (createSettingsTab) предоставляет пользователю возможность ввести параметры модели GPT, такие как путь к токенизатору, количество слоев, размер эмбеддингов и другие. Эти параметры затем используются для конфигурации модели. - Процесс создания модели
Когда пользователь нажимает на кнопку "Начать создание модели", параметры модели передаются в поток ModelThread. Этот поток загружает токенизатор, создает конфигурацию модели GPT, используя заданные параметры, и сохраняет модель и токенизатор в указанной директории. - Обновление статуса
Поток отправляет сигналы в основной интерфейс для обновления текста в QTextEdit, что позволяет пользователю следить за прогрессом создания модели. После завершения процесса кнопка "Выход" становится активной, позволяя пользователю закрыть приложение.
Основные шаги в процессе создания модели
- Загрузка токенизатора
Сначала поток загружает токенизатор из указанного пути, используя метод GPT2Tokenizer.from_pretrained. - Создание конфигурации модели
Используя предоставленные параметры, поток создает объект конфигурации модели GPT2 (GPT2Config), который включает все основные настройки, такие как количество слоев, размер эмбеддингов, количество голов внимания и т.д. - Создание и сохранение модели
На основе созданной конфигурации поток создает модель GPT2 (GPT2LMHeadModel) и сохраняет её вместе с токенизатором в указанной пользователем директории.
Заключение
Этот скрипт представляет собой мощный инструмент для пользователей, которым необходимо гибко и быстро настраивать и создавать кастомные модели GPT. Благодаря простому и удобному интерфейсу на PyQt6, пользователи могут легко настроить все необходимые параметры модели и запустить процесс её создания, при этом приложение остаётся отзывчивым и предоставляет информацию о текущем прогрессе. Этот подход особенно полезен для исследователей и разработчиков, работающих в области NLP, которые хотят экспериментировать с различными конфигурациями моделей GPT.
Сам скрипт:
import sys
import os
from PyQt6.QtWidgets import QApplication, QMainWindow, QTextEdit, QPushButton, QLabel, QLineEdit, QVBoxLayout, QWidget, QFormLayout, QTabWidget
from PyQt6.QtCore import QThread, pyqtSignal
from transformers import GPT2Tokenizer, GPT2Config, GPT2LMHeadModel
class ModelThread(QThread):
update_text_signal = pyqtSignal(str)
model_saved_signal = pyqtSignal()
def __init__(self, config):
super().__init__()
self.config = config
def run(self):
if not os.path.exists(self.config['tokenizer_path']):
self.update_text_signal.emit(f"Путь к токенизатору не существует: {self.config['tokenizer_path']}")
return
self.update_text_signal.emit("Загружаем предварительно созданный токенизатор")
tokenizer = GPT2Tokenizer.from_pretrained(self.config['tokenizer_path'])
self.update_text_signal.emit("Создаем конфигурацию модели с нужными параметрами")
model_config = GPT2Config(
vocab_size=tokenizer.vocab_size,
n_layer=self.config['n_layer'],
n_head=self.config['n_head'],
n_embd=self.config['n_embd'],
intermediate_size=self.config['intermediate_size'],
hidden_size=self.config['hidden_size'],
max_position_embeddings=self.config['max_position_embeddings'],
num_attention_heads=self.config['num_attention_heads'],
gradient_checkpointing=self.config['gradient_checkpointing'],
bos_token_id=tokenizer.bos_token_id,
eos_token_id=tokenizer.eos_token_id,
pad_token_id=tokenizer.pad_token_id,
sep_token_id=tokenizer.sep_token_id,
activation_function=self.config['activation_function'],
initializer_range=self.config['initializer_range'],
layer_norm_eps=self.config['layer_norm_eps'],
scale_attn_by_inverse_layer_idx=self.config['scale_attn_by_inverse_layer_idx'],
reorder_and_upcast_attn=self.config['reorder_and_upcast_attn']
)
self.update_text_signal.emit("Создаем модель на основе заданной конфигурации")
model = GPT2LMHeadModel(config=model_config)
model.resize_token_embeddings(len(tokenizer))
self.update_text_signal.emit("Модель создана.")
self.update_text_signal.emit("Сохраняем модель и токенизатор.")
model.save_pretrained(self.config['model_save_path'])
tokenizer.save_pretrained(self.config['model_save_path'])
self.update_text_signal.emit("Модель и токенизатор сохранены.")
self.model_saved_signal.emit()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Создание модели GPT")
self.setGeometry(100, 100, 600, 400)
self.initUI()
def initUI(self):
self.tabs = QTabWidget(self)
self.setCentralWidget(self.tabs)
self.createModelTab()
self.createSettingsTab()
self.model_thread = None
def createModelTab(self):
self.modelTab = QWidget()
self.tabs.addTab(self.modelTab, "Создание модели")
layout = QVBoxLayout()
self.text_edit = QTextEdit(self)
self.text_edit.setReadOnly(True)
self.exit_button = QPushButton("Выход", self)
self.exit_button.setEnabled(False)
self.exit_button.clicked.connect(self.close)
layout.addWidget(self.text_edit)
layout.addWidget(self.exit_button)
self.modelTab.setLayout(layout)
def createSettingsTab(self):
self.settingsTab = QWidget()
self.tabs.addTab(self.settingsTab, "Настройки")
layout = QFormLayout()
self.tokenizerPathEdit = QLineEdit("D:/GPT_pro/token")
self.modelSavePathEdit = QLineEdit("D:/GPT_pro/model")
self.nLayerEdit = QLineEdit("22")
self.nHeadEdit = QLineEdit("32")
self.nEmbdEdit = QLineEdit("2048")
self.intermediateSizeEdit = QLineEdit("3072")
self.hiddenSizeEdit = QLineEdit("1536")
self.maxPositionEmbeddingsEdit = QLineEdit("3072")
self.numAttentionHeadsEdit = QLineEdit("32")
self.gradientCheckpointingEdit = QLineEdit("True")
self.activationFunctionEdit = QLineEdit("gelu_new")
self.initializerRangeEdit = QLineEdit("0.02")
self.layerNormEpsEdit = QLineEdit("1e-5")
self.scaleAttnByInverseLayerIdxEdit = QLineEdit("True")
self.reorderAndUpcastAttnEdit = QLineEdit("True")
layout.addRow("Путь к токенизатору:", self.tokenizerPathEdit)
layout.addRow("Путь сохранения модели:", self.modelSavePathEdit)
layout.addRow("Количество слоев:", self.nLayerEdit)
layout.addRow("Количество голов:", self.nHeadEdit)
layout.addRow("Размер эмбеддингов:", self.nEmbdEdit)
layout.addRow("Промежуточный размер:", self.intermediateSizeEdit)
layout.addRow("Скрытый размер:", self.hiddenSizeEdit)
layout.addRow("Макс. позиционные эмбеддинги:", self.maxPositionEmbeddingsEdit)
layout.addRow("Количество голов внимания:", self.numAttentionHeadsEdit)
layout.addRow("Контроль градиентов:", self.gradientCheckpointingEdit)
layout.addRow("Функция активации:", self.activationFunctionEdit)
layout.addRow("Диапазон инициализации:", self.initializerRangeEdit)
layout.addRow("Эпсилон нормализации слоев:", self.layerNormEpsEdit)
layout.addRow("Масштабировать внимание по индексу слоя:", self.scaleAttnByInverseLayerIdxEdit)
layout.addRow("Перестроить и увеличивать внимание:", self.reorderAndUpcastAttnEdit)
self.start_button = QPushButton("Начать создание модели")
self.start_button.clicked.connect(self.start_model_creation)
layout.addWidget(self.start_button)
self.settingsTab.setLayout(layout)
def start_model_creation(self):
config = {
'tokenizer_path': self.tokenizerPathEdit.text(),
'model_save_path': self.modelSavePathEdit.text(),
'n_layer': int(self.nLayerEdit.text()),
'n_head': int(self.nHeadEdit.text()),
'n_embd': int(self.nEmbdEdit.text()),
'intermediate_size': int(self.intermediateSizeEdit.text()),
'hidden_size': int(self.hiddenSizeEdit.text()),
'max_position_embeddings': int(self.maxPositionEmbeddingsEdit.text()),
'num_attention_heads': int(self.numAttentionHeadsEdit.text()),
'gradient_checkpointing': self.gradientCheckpointingEdit.text().lower() == 'true',
'activation_function': self.activationFunctionEdit.text(),
'initializer_range': float(self.initializerRangeEdit.text()),
'layer_norm_eps': float(self.layerNormEpsEdit.text()),
'scale_attn_by_inverse_layer_idx': self.scaleAttnByInverseLayerIdxEdit.text().lower() == 'true',
'reorder_and_upcast_attn': self.reorderAndUpcastAttnEdit.text().lower() == 'true'
}
self.model_thread = ModelThread(config)
self.model_thread.update_text_signal.connect(self.update_text)
self.model_thread.model_saved_signal.connect(self.enable_exit_button)
self.model_thread.start()
def update_text(self, text):
self.text_edit.append(text)
def enable_exit_button(self):
self.exit_button.setEnabled(True)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())