Обучение gtp2 с 0
15.01.2025, 13:28. Показов 837. Ответов 1
Будет большой пост. Я что-то застрял в своих начинаниях.
Цель: создать чат бота на основе gpt2, обученного материалом с нуля. Чисто ради академического опыта
Что было сделано.
1. написан скрипт создание пустой модели gpt2-medium
| Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| from transformers import GPT2Config, GPT2LMHeadModel, GPT2Tokenizer
def create_empty_gpt2_xl_with_vocab(save_path, new_tokens=None):
# Настройка конфигурации модели GPT-2 Medium
config = GPT2Config.from_pretrained("gpt2-medium")
# Создание пустой модели GPT-2 Medium
model = GPT2LMHeadModel(config)
# Загружаем базовый токенизатор GPT-2
tokenizer = GPT2Tokenizer.from_pretrained("gpt2-medium")
# Если есть новые токены, добавляем их в токенизатор
if new_tokens:
num_added_tokens = tokenizer.add_tokens(new_tokens)
print(f"Добавлено {num_added_tokens} новых токенов.")
# Обновляем размер эмбеддингов модели, чтобы соответствовать новым токенам
model.resize_token_embeddings(len(tokenizer))
# Сохраняем модель и токенизатор
model.save_pretrained(save_path)
tokenizer.save_pretrained(save_path)
print(f"Пустая модель с добавленным словарем сохранена в '{save_path}'")
return model, tokenizer
if __name__ == "__main__":
# Пример списка новых токенов (здесь это слова на кириллице)
new_tokens = []
# Создаем модель с добавленным словарем
create_empty_gpt2_xl_with_vocab('current', new_tokens) |
|
Создалась модель
current
2. Далее создал папку для обучающего материала в txt, закинул пару книг с открытого доступа.
3. Создал скрипт дообучения. У меня были проблемы с тем чтобы записать модель в туже папку, где исходная, поэтому я делал ротацию папок
current берется за основу, перемещается в prev, а current кладется дообученная версия. Тогда можно многократно запускать дообучение, папки ротируются, в prev лежит бекап прошлой версии
| Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
| import os
import shutil
from transformers import AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments
from datasets import Dataset
# Указываем путь к папкам с моделью и файлам с текстом
pretrained_model_dir = "./current" # Папка с вашей дообученной моделью
text_file = "texts/data.txt" # Путь к вашему новому текстовому файлу
# Функция для проверки, что путь существует
def check_path(path, is_file=True):
if not os.path.exists(path):
raise FileNotFoundError(f"Путь {path} не существует.")
if is_file and not os.path.isfile(path):
raise ValueError(f"Указанный путь {path} не является файлом.")
if not is_file and not os.path.isdir(path):
raise ValueError(f"Указанный путь {path} не является директорией.")
print(f"Путь {path} проверен и валиден.")
# Проверяем путь к файлу с текстом
check_path(text_file, is_file=True)
# Переименовываем или удаляем старую модель, если она существует
if os.path.exists(pretrained_model_dir):
prev_model_dir = "prev"
if os.path.exists(prev_model_dir):
# Если папка с предыдущей моделью уже существует, удаляем её
shutil.rmtree(prev_model_dir)
print(f"Папка {prev_model_dir} удалена.")
# Переименовываем папку с текущей моделью
os.rename(pretrained_model_dir, prev_model_dir)
print(f"Папка {pretrained_model_dir} переименована в {prev_model_dir}.")
else:
print(f"Папка {pretrained_model_dir} не существует, создадим новую модель.")
# Создаем папку для новой модели
if not os.path.exists(pretrained_model_dir):
os.makedirs(pretrained_model_dir)
print(f"Создана папка для модели: {pretrained_model_dir}")
# Загружаем токенизатор и модель из папки с дообученной моделью
tokenizer = AutoTokenizer.from_pretrained(prev_model_dir if os.path.exists(prev_model_dir) else pretrained_model_dir)
model = AutoModelForCausalLM.from_pretrained(prev_model_dir if os.path.exists(prev_model_dir) else pretrained_model_dir)
# Убедимся, что у токенизатора есть токен паддинга
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token # Используем eos_token как токен паддинга
# Чтение текста из файла
with open(text_file, "r", encoding="utf-8") as file:
text = file.read()
# Создание dataset из текста
data = {"text": [text]}
dataset = Dataset.from_dict(data)
# Токенизация текста
def tokenize_function(examples):
return tokenizer(examples['text'], truncation=True, padding="max_length", max_length=512)
tokenized_datasets = dataset.map(tokenize_function, batched=True)
# Добавляем метки в данные
def add_labels(examples):
examples['labels'] = examples['input_ids'].copy() # Ставим input_ids как labels
return examples
tokenized_datasets = tokenized_datasets.map(add_labels, batched=True)
# Параметры обучения
training_args = TrainingArguments(
output_dir=pretrained_model_dir,
learning_rate=5e-5,
per_device_train_batch_size=2,
num_train_epochs=3,
weight_decay=0.01,
save_strategy="epoch",
eval_strategy="no", # Убедитесь, что валидация не выполняется
)
# Используем Trainer для дообучения
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_datasets,
)
# Начинаем обучение
trainer.train()
# Сохраняем модель
model.save_pretrained(pretrained_model_dir)
tokenizer.save_pretrained(pretrained_model_dir)
print("Обучение завершено! Модель сохранена в папке:", pretrained_model_dir) |
|
py train.py
Путь texts/data.txt проверен и валиден.
Папка ./current переименована в prev.
Создана папка для модели: ./current
Map: 100%|███████████████████████████████████ ████████████████████████████████████████ ████████████████████████████████████████ ████████████████████████████████████████ ████████████████████████████████████████ ██████████████████| 1/1 [00:01<00:00, 1.18s/ examples]
Map: 100%|███████████████████████████████████ ████████████████████████████████████████ ████████████████████████████████████████ ████████████████████████████████████████ ████████████████████████████████████████ █████████████████| 1/1 [00:00<00:00, 124.91 examples/s]
{'train_runtime': 78.2653, 'train_samples_per_second': 0.038, 'train_steps_per_second': 0.038, 'train_loss': 9.03712526957194, 'epoch': 3.0}
100%|███████████████████████████████████ ████████████████████████████████████████ ████████████████████████████████████████ ████████████████████████████████████████ ████████████████████████████████████████ ██████████████████████████████| 3/3 [01:18<00:00, 26.10s/it]
Обучение завершено! Модель сохранена в папке: ./current
|
И скрипт сравнение оветов. У нас всегда есть две версии модели prev и current, мы даем вопрос обоим и выводим результат на экран
| Python | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
| from transformers import GPT2LMHeadModel, GPT2Tokenizer
# Загрузка оригинальной модели (например, GPT-2)
original_model_name = "prev" # Путь к пустой модели
original_model = GPT2LMHeadModel.from_pretrained(original_model_name)
original_tokenizer = GPT2Tokenizer.from_pretrained(original_model_name)
# Загрузка дообученной модели (например, сохраненной в "final_model")
fine_tuned_model_name = "./current" # Путь к вашей дообученной модели
fine_tuned_model = GPT2LMHeadModel.from_pretrained(fine_tuned_model_name)
fine_tuned_tokenizer = GPT2Tokenizer.from_pretrained(fine_tuned_model_name)
# Проверяем и добавляем токен паддинга, если его нет
if original_tokenizer.pad_token is None:
original_tokenizer.pad_token = original_tokenizer.eos_token # Используем eos_token как паддинг
if fine_tuned_tokenizer.pad_token is None:
fine_tuned_tokenizer.pad_token = fine_tuned_tokenizer.eos_token # Используем eos_token как паддинг
# Функция для генерации ответа от модели
def generate_response(model, tokenizer, input_text):
# Токенизация текста
inputs = tokenizer(input_text, return_tensors="pt", padding=True, truncation=True)
# Добавляем attention_mask, если нужно
attention_mask = inputs.get('attention_mask', None)
# Генерация ответа
outputs = model.generate(
inputs['input_ids'],
attention_mask=attention_mask,
max_length=100,
pad_token_id=tokenizer.pad_token_id # Указываем явно pad_token_id
)
# Декодирование и возвращение ответа
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
return response
# Массив контрольных вопросов
questions = [
"Что такое худежественное фото?",
"Как стать успешным предпринимателем?",
"Какие преимущества у зеленой энергетики?",
"Что такое квантовые вычисления?",
"Почему важно заботиться о психическом здоровье?"
]
# Сравнение ответов для всех вопросов
for question in questions:
print(f"Вопрос: {question}")
# Получаем ответ от оригинальной модели
original_response = generate_response(original_model, original_tokenizer, question)
# Получаем ответ от дообученной модели
fine_tuned_response = generate_response(fine_tuned_model, fine_tuned_tokenizer, question)
# Выводим оба ответа
print("Ответ от оригинальной модели:")
print(original_response)
print("\nОтвет от дообученной модели:")
print(fine_tuned_response)
print("-" * 50) # Разделитель между вопросами |
|
OUT:
Вопрос: Что такое худежественное фото?
Ответ от оригинальной модели:
Что такое худежественное фото? в в в вотото вогото вого ва ва вала вото вотото в ватото воготото
Ответ от дообученной модели:
Что такое худежественное фото? дабабабабабапабабабебабабачабабапабапаба чаба
|
Идея моя была такой
Закинуть материал для обучение, обучить с ротацией, запустить проверку ответов, повторить.
По мере скармливания материал был виден прогрес в ответах:
1. сперва ответы были в одну букву
2. затем в слога
3. затем в более сложные слога
В итоге все доходит до набора каких-то бессмысленных сочетаний буков
Итог: Я скормил >100mb текста и не добился даже чего-то похожего на слова
У меня сомнения: я что-то не так делаю концептуально? Или материала мало, вроде 100мб уже не плохо чтобы хотябы слово написать? Или метод обучения неверный, я пробовал раскатывать с 0 и обучать в разных конфигурациях.
0
|