Загрузить файлы в «/»
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
#linux
|
||||
|
||||
🔍 Поиск файлов старше 30 дней:
|
||||
|
||||
```sh
|
||||
find /path/to/directory -type f -mtime +30
|
||||
```
|
||||
|
||||
- -type f — ищем только файлы;
|
||||
- -mtime +30 — файлы старше 30 дней.
|
||||
|
||||
🗑️ Удаление этих файлов:
|
||||
|
||||
```sh
|
||||
find /path/to/directory -type f -mtime +30 -delete
|
||||
```
|
||||
|
||||
⚠️ Осторожно! Удаление без подтверждения.
|
||||
|
||||
✅ Безопасный вариант с подтверждением:
|
||||
|
||||
```sh
|
||||
find /path/to/directory -type f -mtime +30 -exec rm -i {} \;
|
||||
```
|
||||
|
||||
Будет запрашиваться подтверждение перед удалением.
|
||||
|
||||
🔥 Автоматизация через cron:
|
||||
Добавляем в crontab -e:
|
||||
|
||||
0 3 * * * find /var/log -type f -mtime +30 -delete
|
||||
|
||||
Очистка логов каждую ночь в 03:00.
|
||||
@@ -0,0 +1,7 @@
|
||||
- [?] Масло 100 грамм
|
||||
- [?] Кефир или ряженка 1 стакан
|
||||
- [?] 2-3 столовые ложки сметаны или майонеза
|
||||
- [?] 1 яйцо
|
||||
- [?] Пол чайной ложки соли
|
||||
- [?] Пол чайной ложки соды
|
||||
- [?] Мука
|
||||
@@ -0,0 +1,608 @@
|
||||
- **Платформа:** Windows 10/11 + WSL2 (Ubuntu 22.04 / 24.04)
|
||||
- **Железо:** NVIDIA RTX 3060 Ti (8 ГБ VRAM)
|
||||
- **Базовая модель для обучения:** Meta Llama 3.1 8B (`unsloth/llama-3.1-8b-Instruct-bnb-4bit`)
|
||||
- **Рабочая директория:** `C:\ST\aandc\` (внутри WSL2: `/mnt/c/ST/aandc/`)
|
||||
|
||||
---
|
||||
|
||||
ЧАСТЬ 1. Развертывание среды и проброс GPU в WSL2
|
||||
|
||||
Шаг 1.1. Базовая установка подсистемы
|
||||
|
||||
Откройте **PowerShell от имени Администратора** на хостовой Windows:
|
||||
|
||||
powershell
|
||||
|
||||
```
|
||||
wsl --install
|
||||
wsl --set-default-version 2
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_Перезагрузите компьютер. При первом запуске терминала Ubuntu задайте логин и пароль._
|
||||
|
||||
Шаг 1.2. Защита от Out Of Memory (Выделение ресурсов)
|
||||
|
||||
1. В Windows нажмите `Win + R`, введите `%USERPROFILE%` и нажмите Enter.
|
||||
2. Создайте в этой папке текстовый файл `.wslconfig` (без расширения `.txt`) и вставьте:
|
||||
|
||||
ini
|
||||
|
||||
```
|
||||
[wsl2]
|
||||
memory=16GB # Выделяем минимум 16 ГБ оперативной памяти (или 75% от системной RAM)
|
||||
swap=8GB # Обязательный файл подкачки для подстраховки при компиляции моделей
|
||||
localhostForwarding=true
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
3. Перезапустите WSL в PowerShell: `wsl --shutdown`
|
||||
|
||||
Шаг 1.3. Настройка NVIDIA Container Toolkit
|
||||
|
||||
Запустите терминал **Ubuntu в WSL2** и выполните цепочку команд для чистой настройки проброса видеокарты:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
sudo apt-get update && sudo apt-get install -y ca-certificates curl gnupg2
|
||||
|
||||
# Удаляем старые битые кэши и ключи, если они создались
|
||||
sudo rm -f /etc/apt/sources.list.d/nvidia-container-toolkit.list
|
||||
sudo rm -f /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
|
||||
|
||||
# Скачивание официального валидного GPG-ключа NVIDIA
|
||||
curl -fsSL https://github.io | sudo gpg --dearmor --yes -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
|
||||
|
||||
# Добавление стабильного репозитория пакетов
|
||||
curl -s -L https://github.io | \
|
||||
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
|
||||
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
|
||||
|
||||
# Обновление списков и установка утилиты сопряжения
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y nvidia-container-toolkit
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_Для проверки введите `nvidia-smi`. На экране должна появиться таблица вашей видеокарты RTX 3060 Ti._
|
||||
|
||||
---
|
||||
|
||||
ЧАСТЬ 2. Установка Ollama и Запуск Базовой Модели
|
||||
|
||||
Шаг 2.1. Установка движка и скачивание Llama 3.1
|
||||
|
||||
Выполните в терминале WSL2:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
curl -fsSL https://ollama.com | sh
|
||||
ollama pull llama3.1
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
Шаг 2.2. Фоновый запуск сервера Ollama
|
||||
|
||||
Поскольку менеджер служб `systemd` в WSL часто ограничен, запускайте Ollama напрямую встроенным фоновым процессом Linux:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
ollama serve > /dev/null 2>&1 &
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_(Нажмите Enter, если терминал временно застынет. Проверить статус можно командой `ollama list`)._
|
||||
|
||||
---
|
||||
|
||||
ЧАСТЬ 3. Автоматизированная генерация ShareGPT-датасета
|
||||
|
||||
Шаг 3.1. Подготовка файлов в Windows и WSL2
|
||||
|
||||
1. В Windows создайте папку `C:\ST\aandc\manuals\`.
|
||||
2. Скопируйте в неё все ваши **71 исходный технический PDF-мануал**. Внутри WSL2 они мгновенно станут доступны по пути `/mnt/c/ST/aandc/manuals/`.
|
||||
3. В терминале WSL2 установите библиотеки обработки данных (обходя ограничения `PEP 668` системы Ubuntu):
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
pip3 install pypdf requests --break-system-packages
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
Шаг 3.2. Развертывание отказоустойчивого скрипта `generate_qa.py`
|
||||
|
||||
Создайте файл `/mnt/c/ST/aandc/generate_qa.py` и скопируйте в него этот код. Он очищает текст регулярными выражениями и корректно парсит ответы Ollama, даже если она возвращает одиночный словарь вместо списка:
|
||||
|
||||
python
|
||||
|
||||
```
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import pypdf
|
||||
import requests
|
||||
|
||||
# === КОНФИГУРАЦИЯ ===
|
||||
MODEL_NAME = "llama3.1:latest"
|
||||
OLLAMA_URL = "http://localhost:11434/api/generate"
|
||||
MANUALS_DIR = "/mnt/c/ST/aandc/manuals"
|
||||
OUTPUT_FILE = "/mnt/c/ST/aandc/aandc_dataset.jsonl"
|
||||
WINDOW_SIZE = 3 # Размер окна чтения документа (в страницах)
|
||||
|
||||
SYSTEM_PROMPT = (
|
||||
"Вы — опытный technical writer и эксперт по банкоматному ПО.\n"
|
||||
"Изучите предоставленный фрагмент технической документации.\n"
|
||||
"Создайте от 2 до 4 высококачественных пар 'Вопрос-Ответ' на основе ТОЛЬКО этого текста.\n"
|
||||
"Вопросы должны быть конкретными (коды ошибок, процедуры калибровки, настройка систем).\n"
|
||||
"Ответы должны быть глубокими, технически точными, структурированными и без упоминания номеров страниц.\n"
|
||||
"Формат ответа строго JSON списка ShareGPT:\n"
|
||||
'[{"conversations": [{"from": "human", "value": "Вопрос"}, {"from": "gpt", "value": "Ответ"}]}]'
|
||||
)
|
||||
|
||||
def clean_text(text):
|
||||
"""Очистка текста от мусора, номеров страниц и колонтитулов"""
|
||||
text = re.sub(r'Page \d+ of \d+', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'^\d+\s*$', '', text, flags=re.MULTILINE)
|
||||
text = re.sub(r'\n+', '\n', text)
|
||||
text = re.sub(r' +', ' ', text)
|
||||
return text.strip()
|
||||
|
||||
def extract_pdf_chunks(pdf_path, window_size=3):
|
||||
"""Динамическое чтение любого PDF файла порциями страниц"""
|
||||
try:
|
||||
with open(pdf_path, 'rb') as f:
|
||||
reader = pypdf.PdfReader(f)
|
||||
total_pages = len(reader.pages)
|
||||
|
||||
for i in range(0, total_pages, window_size):
|
||||
chunk_text = ""
|
||||
for j in range(i, min(i + window_size, total_pages)):
|
||||
page_text = reader.pages[j].extract_text()
|
||||
if page_text:
|
||||
chunk_text += page_text + "\n"
|
||||
|
||||
cleaned = clean_text(chunk_text)
|
||||
if len(cleaned) > 100:
|
||||
yield cleaned
|
||||
except Exception as e:
|
||||
print(f"Ошибка при чтении файла {os.path.basename(pdf_path)}: {e}")
|
||||
|
||||
def ask_ollama(context):
|
||||
"""Запрос к локальному серверу Ollama за строгим JSON-форматом"""
|
||||
prompt = f"ДОКУМЕНТАЦИЯ:\n{context}\n\nСгенерируй JSON по инструкции."
|
||||
payload = {
|
||||
"model": MODEL_NAME,
|
||||
"prompt": prompt,
|
||||
"system": SYSTEM_PROMPT,
|
||||
"stream": False,
|
||||
"format": "json"
|
||||
}
|
||||
try:
|
||||
response = requests.post(OLLAMA_URL, json=payload)
|
||||
if response.status_code == 200:
|
||||
return response.json().get("response", "")
|
||||
except Exception as e:
|
||||
print(f"Ошибка связи с Ollama: {e}")
|
||||
return None
|
||||
|
||||
def main():
|
||||
if not os.path.exists(MANUALS_DIR):
|
||||
print(f"Ошибка: Папка {MANUALS_DIR} не найдена!")
|
||||
return
|
||||
|
||||
pdf_files = [f for f in os.listdir(MANUALS_DIR) if f.lower().endswith('.pdf')]
|
||||
total_files = len(pdf_files)
|
||||
|
||||
if total_files == 0:
|
||||
print(f"В папке {MANUALS_DIR} не найдено ни одного PDF файла.")
|
||||
return
|
||||
|
||||
print(f"Найдено файлов для обработки: {total_files}")
|
||||
print("Старт генерации профессионального датасета...")
|
||||
|
||||
# Файл затирает старую сырую базу 'w' и наполняется построчно (JSON Lines)
|
||||
with open(OUTPUT_FILE, 'w', encoding='utf-8') as outfile:
|
||||
for idx, pdf_name in enumerate(pdf_files, 1):
|
||||
pdf_path = os.path.join(MANUALS_DIR, pdf_name)
|
||||
print(f"[{idx}/{total_files}] Обработка: {pdf_name}")
|
||||
|
||||
for chunk in extract_pdf_chunks(pdf_path, WINDOW_SIZE):
|
||||
raw_json = ask_ollama(chunk)
|
||||
if not raw_json:
|
||||
continue
|
||||
|
||||
try:
|
||||
data = json.loads(raw_json)
|
||||
|
||||
# Сценарий 1: Модель вернула список объектов [ {...}, {...} ]
|
||||
if isinstance(data, list):
|
||||
for item in data:
|
||||
if isinstance(item, dict) and "conversations" in item:
|
||||
outfile.write(json.dumps(item, ensure_ascii=False) + "\n")
|
||||
outfile.flush()
|
||||
print(" ✅ Записана карточка (из списка)")
|
||||
|
||||
# Сценарий 2: Модель вернула один одиночный объект { "conversations": [...] }
|
||||
elif isinstance(data, dict):
|
||||
if "conversations" in data:
|
||||
outfile.write(json.dumps(data, ensure_ascii=False) + "\n")
|
||||
outfile.flush()
|
||||
print(" ✅ Записана карточка (прямой словарь)")
|
||||
else:
|
||||
for key, val in data.items():
|
||||
if isinstance(val, list):
|
||||
for sub_item in val:
|
||||
if isinstance(sub_item, dict) and "conversations" in sub_item:
|
||||
outfile.write(json.dumps(sub_item, ensure_ascii=False) + "\n")
|
||||
outfile.flush()
|
||||
print(" ✅ Записана карточка (из вложенного ключа)")
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
|
||||
print(f"\nУспешно! Все файлы обработаны. Новый датасет сохранен в: {OUTPUT_FILE}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_Запуск: `python3 /mnt/c/ST/aandc/generate_qa.py`. На выходе сформируется файл на 756 качественных диалогов._
|
||||
|
||||
---
|
||||
|
||||
ЧАСТЬ 4. Стабильный Fine-Tuning LoRA на 8 ГБ VRAM
|
||||
|
||||
Для того чтобы обучение гарантированно шло без вылетов по памяти (`RuntimeError по fused cross entropy`), мы используем чистый, промышленный стек **Hugging Face (`transformers` + `peft` + `bitsandbytes`)**. Мы полностью убрали капризные компиляторы Unsloth, исключив любые пики выделения памяти на карте хоста.
|
||||
|
||||
Шаг 4.1. Установка стабильных зависимостей
|
||||
|
||||
Выполните в терминале WSL2:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
pip install torch torchvision torchaudio --index-url https://pytorch.org --break-system-packages
|
||||
pip install transformers datasets peft bitsandbytes --break-system-packages
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
Шаг 4.2. Развертывание «железного» скрипта обучения `train.py`
|
||||
|
||||
Создайте файл `/mnt/c/ST/aandc/train.py` и скопируйте в него этот код:
|
||||
|
||||
python
|
||||
|
||||
```
|
||||
import os
|
||||
import json
|
||||
import torch
|
||||
from datasets import Dataset
|
||||
from transformers import (
|
||||
AutoModelForCausalLM,
|
||||
AutoTokenizer,
|
||||
BitsAndBytesConfig,
|
||||
TrainingArguments,
|
||||
Trainer,
|
||||
DataCollatorForLanguageModeling
|
||||
)
|
||||
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
|
||||
|
||||
# === КОНФИГУРАЦИЯ ДЛЯ ГАРАНТИРОВАННОГО СТАРТА НА 8 ГБ VRAM ===
|
||||
MODEL_NAME = "unsloth/llama-3.1-8b-Instruct-bnb-4bit" # Весовая база
|
||||
max_seq_length = 512 # Идеальный размер контекста для защиты памяти от пиков
|
||||
DATASET_PATH = "/mnt/c/ST/aandc/aandc_dataset.jsonl"
|
||||
|
||||
# Принудительная очистка видеопамяти
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
print("--- 1. Конфигурация 4-битного квантования bitsandbytes ---")
|
||||
bnb_config = BitsAndBytesConfig(
|
||||
load_in_4bit=True,
|
||||
bnb_4bit_quant_type="nf4",
|
||||
bnb_4bit_compute_dtype=torch.float16,
|
||||
bnb_4bit_use_double_quant=True
|
||||
)
|
||||
|
||||
print("--- 2. Загрузка модели и токенизатора напрямую через HF ---")
|
||||
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
|
||||
tokenizer.pad_token = tokenizer.eos_token
|
||||
|
||||
model = AutoModelForCausalLM.from_pretrained(
|
||||
MODEL_NAME,
|
||||
quantization_config=bnb_config,
|
||||
device_map="auto"
|
||||
)
|
||||
|
||||
# Подготовка модели к PEFT обучению (стабилизация градиентов k-bit)
|
||||
model = prepare_model_for_kbit_training(model)
|
||||
|
||||
print("--- 3. Конфигурация LoRA (Чистый PEFT) ---")
|
||||
peft_config = LoraConfig(
|
||||
r=16,
|
||||
lora_alpha=16,
|
||||
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
|
||||
lora_dropout=0.05,
|
||||
bias="none",
|
||||
task_type="CAUSAL_LM"
|
||||
)
|
||||
model = get_peft_model(model, peft_config)
|
||||
|
||||
print("--- 4. Отказоустойчивая валидация и загрузка датасета ---")
|
||||
valid_dialogues = []
|
||||
with open(DATASET_PATH, "r", encoding="utf-8") as f:
|
||||
for idx, line in enumerate(f, 1):
|
||||
if not line.strip():
|
||||
continue
|
||||
try:
|
||||
item = json.loads(line)
|
||||
if "conversations" not in item or not isinstance(item["conversations"], list):
|
||||
continue
|
||||
clean_conv = []
|
||||
is_corrupted = False
|
||||
for msg in item["conversations"]:
|
||||
# Защита от изменения типов (строка вместо словаря на row 9)
|
||||
if not isinstance(msg, dict) or "from" not in msg or "value" not in msg:
|
||||
is_corrupted = True
|
||||
break
|
||||
clean_conv.append({
|
||||
"from": str(msg["from"]).strip(),
|
||||
"value": str(msg["value"]).strip()
|
||||
})
|
||||
if not is_corrupted and len(clean_conv) > 0:
|
||||
valid_dialogues.append({"conversations": clean_conv})
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
|
||||
print(f"Успешно загружено чистых диалогов: {len(valid_dialogues)}")
|
||||
dataset = Dataset.from_list(valid_dialogues)
|
||||
|
||||
# Форматирование и ЯВНАЯ ТОКЕНИЗАЦИЯ строк под формат ролей Llama 3.1
|
||||
def tokenize_prompts_func(examples):
|
||||
conversations = examples["conversations"]
|
||||
tokenized_inputs = {"input_ids": [], "attention_mask": []}
|
||||
|
||||
for con in conversations:
|
||||
mapped_conversation = []
|
||||
for msg in con:
|
||||
# Превращаем "human" в "user", "gpt" в "assistant" для шаблона Llama 3.1
|
||||
role = "user" if msg["from"] == "human" else "assistant"
|
||||
mapped_conversation.append({"role": role, "content": msg["value"]})
|
||||
|
||||
# Применяем Jinja-шаблон чата
|
||||
text = tokenizer.apply_chat_template(mapped_conversation, tokenize=False, add_generation_prompt=False)
|
||||
|
||||
# Токенизируем текст напрямую с жесткой обрезкой до max_seq_length
|
||||
tokenized = tokenizer(text, truncation=True, max_length=max_seq_length, padding=False)
|
||||
tokenized_inputs["input_ids"].append(tokenized["input_ids"])
|
||||
tokenized_inputs["attention_mask"].append(tokenized["attention_mask"])
|
||||
|
||||
return tokenized_inputs
|
||||
|
||||
# Пересобираем датасет в готовые числовые тензоры
|
||||
dataset = dataset.map(tokenize_prompts_func, batched=True, remove_columns=["conversations"])
|
||||
|
||||
print("--- 5. Инициализация индустриального Trainer ---")
|
||||
trainer = Trainer(
|
||||
model=model,
|
||||
train_dataset=dataset,
|
||||
data_collator=DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False), # Сборщик батчей
|
||||
args=TrainingArguments(
|
||||
per_device_train_batch_size=1, # Размер батча 1 исключает переполнение VRAM
|
||||
gradient_accumulation_steps=8, # Накопление шагов (1 * 8 эмулирует реальный батч размера 8)
|
||||
warmup_steps=5,
|
||||
max_steps=60, # Обучение на 60 шагах. Для финала: num_train_epochs = 3
|
||||
learning_rate=2e-4,
|
||||
fp16=True,
|
||||
logging_steps=1,
|
||||
optim="adamw_8bit", # 8-битный оптимизатор экономит драгоценную VRAM
|
||||
weight_decay=0.01,
|
||||
lr_scheduler_type="linear",
|
||||
seed=3407,
|
||||
output_dir="outputs",
|
||||
remove_unused_columns=False
|
||||
),
|
||||
)
|
||||
|
||||
print("--- 6. СТАРТ ТРЕНИРОВКИ (Чистый стек Hugging Face) ---")
|
||||
trainer.train()
|
||||
print("--- ТРЕНИРОВКА УСПЕШНО ЗАВЕРШЕНА ---")
|
||||
|
||||
print("--- 7. Сохранение LoRA адаптеров ---")
|
||||
model.save_pretrained("/mnt/c/ST/aandc/aandc_lora_model")
|
||||
tokenizer.save_pretrained("/mnt/c/ST/aandc/aandc_lora_model")
|
||||
print("Обученные адаптеры сохранены в папку aandc_lora_model!")
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
Шаг 4.3. Выполнение тренировки
|
||||
|
||||
Перед запуском обязательно **выгрузите Ollama из памяти**, чтобы освободить видеокарту хоста:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
pkill -f ollama
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
Запустите скрипт:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
python3 /mnt/c/ST/aandc/train.py
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_Процесс займет около 2 часов. Значение `loss` будет плавно падать, фиксируя технические знания в весах LoRA._
|
||||
|
||||
---
|
||||
|
||||
ЧАСТЬ 5. Слияние Модели в Совместимый Монолит
|
||||
|
||||
Поскольку базовая модель `bnb-4bit` аппаратно заблокирована от обратной распаковки на CPU через `llama.cpp`, мы используем официальный индустриальный метод: **накатим ваши адаптеры на открытую, чистую базовую модель Llama 3.1 8B в формате `float16`** и запишем один полноценный монолитный файл.
|
||||
|
||||
Шаг 5.1. Запуск скрипта слияния (Merge & Unload)
|
||||
|
||||
Запустите интерактивный Python в терминале WSL2:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
python3
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
Вставьте этот код (он скачает чистую базу от Unsloth и объединит её с вашими адаптерами):
|
||||
|
||||
python
|
||||
|
||||
```
|
||||
import torch
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
from peft import PeftModel
|
||||
|
||||
base_model_path = "unsloth/llama-3.1-8b-Instruct"
|
||||
lora_path = "/mnt/c/ST/aandc/aandc_lora_model"
|
||||
output_path = "/mnt/c/ST/aandc/atm_expert_fused_clean"
|
||||
|
||||
print("1. Загрузка открытой базовой модели Llama 3.1 (float16)...")
|
||||
base_model = AutoModelForCausalLM.from_pretrained(base_model_path, torch_dtype=torch.bfloat16, device_map="cpu")
|
||||
tokenizer = AutoTokenizer.from_pretrained(base_model_path)
|
||||
|
||||
print("2. Наложение обученных LoRA весов...")
|
||||
model = PeftModel.from_pretrained(base_model, lora_path)
|
||||
|
||||
print("3. Слияние матриц в единый монолит...")
|
||||
model = model.merge_and_unload()
|
||||
|
||||
print("4. Сохранение финальной совместимой модели...")
|
||||
model.save_pretrained(output_path, safe_serialization=True)
|
||||
tokenizer.save_pretrained(output_path)
|
||||
|
||||
print("--- СЛИЯНИЕ УСПЕШНО ЗАВЕРШЕНО ---")
|
||||
exit()
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_После завершения в директории `C:\ST\aandc\atm_expert_fused_clean\` появится полноценная, совместимая с Ollama модель._
|
||||
|
||||
---
|
||||
|
||||
ЧАСТЬ 6. Сборка и Запуск Финального ИИ-Эксперта
|
||||
|
||||
Шаг 6.1. Настройка конфигурационного файла `Modelfile`
|
||||
|
||||
Перепишите ваш `Modelfile`, чтобы Ollama прочитала готовую монолитную папку, выполнив в консоли команду:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
cat << 'EOF' > /mnt/c/ST/aandc/Modelfile
|
||||
# Ссылка на созданную монолитную папку
|
||||
FROM /mnt/c/ST/aandc/atm_expert_fused_clean
|
||||
|
||||
TEMPLATE """{{ if .System }}<|start_header_id|>system<|end_header_id|>
|
||||
{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>
|
||||
{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>
|
||||
{{ .Response }}<|eot_id|>"""
|
||||
|
||||
SYSTEM "Вы — специализированный локальный ИИ-эксперт по банкоматному ПО. Вы отвечаете строго на основе технической документации, четко, структурированно, используя точные инженерные термины и коды ошибок."
|
||||
EOF
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
Шаг 6.2. Компиляция модели внутри Ollama
|
||||
|
||||
Запустите сервер Ollama обратно в фоне и выполните сборку:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
ollama serve > /dev/null 2>&1 &
|
||||
cd /mnt/c/ST/aandc/
|
||||
ollama create atm_expert -f ./Modelfile
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_Ollama за секунды скопирует компоненты, переварит типы данных и напишет долгожданное `success`._
|
||||
|
||||
Шаг 6.3. Интерактивный запуск
|
||||
|
||||
Входите в чат с готовым экспертом:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
ollama run atm_expert
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_Модель будет отвечать глубоко, инженерно точно, без колонтитулов и «каши». Выход из чата — команда `/exit`._
|
||||
|
||||
---
|
||||
|
||||
ЧАСТЬ 7. Автономный Перенос Эксперта на Другой ПК
|
||||
|
||||
Поскольку модель собрана в монолит, перенос на любой другой компьютер (даже полностью отключенный от интернета) занимает ровно 1 минуту.
|
||||
|
||||
1. **Экспорт в GGUF-файл на текущем ПК:**
|
||||
Выполните в терминале WSL2 команду экспорта:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
ollama show atm_expert --modelfile > /mnt/c/ST/aandc/atm_expert_final.gguf
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_Заберите получившийся файл `atm_expert_final.gguf` (размером около 4.7 ГБ) на флешку._
|
||||
2. **Подготовка целевого ПК:**
|
||||
Установите чистую Ollama на новый компьютер (скачав десктопный установщик для Windows или Mac с официального сайта ollama.com).
|
||||
3. **Импорт модели на новом месте:**
|
||||
Положите файл `atm_expert_final.gguf` в любую папку на новом ПК (например, `C:\ATM_AI\`). Создайте рядом текстовый файл `Modelfile` со следующим содержимым:
|
||||
|
||||
dockerfile
|
||||
|
||||
```
|
||||
FROM ./atm_expert_final.gguf
|
||||
SYSTEM "Вы — специализированный локальный ИИ-эксперт по банкоматному ПО. Вы отвечаете строго на основе технической документации, четко, структурированно, используя точные инженерные термины и коды ошибок."
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
Откройте обычную командную строку Windows (cmd или PowerShell) в этой папке и зарегистрируйте модель:
|
||||
|
||||
powershell
|
||||
|
||||
```
|
||||
cd C:\ATM_AI\
|
||||
ollama create atm_expert -f ./Modelfile
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
4. **Запуск:** Модель готова отвечать на новом ПК по стандартной команде: `ollama run atm_expert`. Если на целевом ПК нет видеокарты, обеспечьте ему минимум **16 ГБ оперативной памяти (RAM)** — Ollama автоматически и без сбоев переключит вычисления на процессор.
|
||||
|
||||
---
|
||||
@@ -0,0 +1,370 @@
|
||||
Обновленная версия, Не проверена
|
||||
- **Платформа:** Windows 10/11 + WSL2 (Ubuntu 22.04 / 24.04)
|
||||
- **Железо:** NVIDIA RTX 3060 Ti (8 ГБ VRAM)
|
||||
- **Базовая модель:** Meta Llama 3.1 8B
|
||||
- **Рабочая директория:** `C:\ST\aandc\` (в WSL2: `/mnt/c/ST/aandc/`)
|
||||
|
||||
|
||||
ЧАСТЬ 1. Развертывание среды и проброс GPU в WSL2
|
||||
|
||||
1.1. Базовая установка подсистемы
|
||||
|
||||
Откройте **PowerShell от имени Администратора** на Windows:
|
||||
|
||||
powershell
|
||||
|
||||
```
|
||||
wsl --install
|
||||
wsl --set-default-version 2
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_Перезагрузите ПК. В терминале Ubuntu задайте логин и пароль._
|
||||
|
||||
1.2. Выделение системных ресурсов (Защита от Out Of Memory)
|
||||
|
||||
1. В Windows нажмите `Win + R`, введите `%USERPROFILE%` и нажмите Enter.
|
||||
2. Создайте файл `.wslconfig` и вставьте параметры:
|
||||
|
||||
ini
|
||||
|
||||
```
|
||||
[wsl2]
|
||||
memory=16GB
|
||||
swap=8GB
|
||||
localhostForwarding=true
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
3. Перезапустите WSL в PowerShell: `wsl --shutdown`
|
||||
|
||||
1.3. Инсталляция NVIDIA Container Toolkit (Проброс графического ядра)
|
||||
|
||||
Запустите терминал **Ubuntu в WSL2**:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
sudo apt-get update && sudo apt-get install -y ca-certificates curl gnupg2
|
||||
|
||||
# Скачивание официального GPG-ключа
|
||||
curl -fsSL https://github.io | sudo gpg --dearmor --yes -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
|
||||
|
||||
# Добавление официального репозитория пакетов
|
||||
curl -s -L https://github.io | \
|
||||
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
|
||||
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
|
||||
|
||||
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_Проверить сопряжение: команда `nvidia-smi` должна вывести таблицу с вашей RTX 3060 Ti._
|
||||
|
||||
---
|
||||
|
||||
ЧАСТЬ 2. Установка Ollama и Локальный Запуск
|
||||
|
||||
2.1. Установка движка и скачивание модели (в терминале WSL2)
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
curl -fsSL https://ollama.com | sh
|
||||
ollama pull llama3.1
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
2.2. Фоновый запуск сервера Ollama в WSL2
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
ollama serve > /dev/null 2>&1 &
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_(Нажмите Enter, если терминал временно застынет. Проверить доступность: `ollama list`)._
|
||||
|
||||
---
|
||||
|
||||
ЧАСТЬ 3. Автоматизированная генерация ShareGPT-датасета
|
||||
|
||||
3.1. Подготовка папок и Python
|
||||
|
||||
В Windows скопируйте все **71 технический PDF-мануал** в папку `C:\ST\aandc\manuals\`.
|
||||
В терминале WSL2 установите библиотеки (обход защиты `PEP 668`):
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
pip3 install pypdf requests --break-system-packages
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
3.2. Создание интеллектуального генератора `generate_qa.py`
|
||||
|
||||
Создайте файл `/mnt/c/ST/aandc/generate_qa.py`:
|
||||
|
||||
python
|
||||
|
||||
```
|
||||
import os, re, json, pypdf, requests
|
||||
|
||||
MODEL_NAME = "llama3.1:latest"
|
||||
OLLAMA_URL = "http://localhost:11434/api/generate"
|
||||
MANUALS_DIR = "/mnt/c/ST/aandc/manuals"
|
||||
OUTPUT_FILE = "/mnt/c/ST/aandc/aandc_dataset.jsonl"
|
||||
WINDOW_SIZE = 3
|
||||
|
||||
SYSTEM_PROMPT = (
|
||||
"Вы — опытный technical writer и эксперт по банкоматному ПО.\n"
|
||||
"Изучите предоставленный текст. Создайте от 2 до 4 высококачественных пар 'Вопрос-Ответ'.\n"
|
||||
"Ответы должны быть глубокими, технически точными, без упоминания номеров страниц.\n"
|
||||
"Формат ответа строго JSON: [{\"conversations\": [{\"from\": \"human\", \"value\": \"Вопрос\"}, {\"from\": \"gpt\", \"value\": \"Ответ\"}]}]"
|
||||
)
|
||||
|
||||
def clean_text(text):
|
||||
text = re.sub(r'Page \d+ of \d+', '', text, flags=re.IGNORECASE)
|
||||
text = re.sub(r'^\d+\s*$', '', text, flags=re.MULTILINE)
|
||||
return re.sub(r'\n+', '\n', text).strip()
|
||||
|
||||
def extract_pdf_chunks(pdf_path, window_size=3):
|
||||
try:
|
||||
with open(pdf_path, 'rb') as f:
|
||||
reader = pypdf.PdfReader(f)
|
||||
for i in range(0, len(reader.pages), window_size):
|
||||
chunk_text = "".join([reader.pages[j].extract_text() or "" for j in range(i, min(i + window_size, len(reader.pages)))])
|
||||
cleaned = clean_text(chunk_text)
|
||||
if len(cleaned) > 100: yield cleaned
|
||||
except Exception as e: print(f"Ошибка чтения {os.path.basename(pdf_path)}: {e}")
|
||||
|
||||
def ask_ollama(context):
|
||||
payload = {"model": MODEL_NAME, "prompt": f"ДОКУМЕНТАЦИЯ:\n{context}\n\nСгенерируй JSON.", "system": SYSTEM_PROMPT, "stream": False, "format": "json"}
|
||||
try:
|
||||
response = requests.post(OLLAMA_URL, json=payload)
|
||||
if response.status_code == 200: return response.json().get("response", "")
|
||||
except: pass
|
||||
return None
|
||||
|
||||
def main():
|
||||
pdf_files = [f for f in os.listdir(MANUALS_DIR) if f.lower().endswith('.pdf')]
|
||||
print(f"Найдено файлов: {len(pdf_files)}. Старт генерации...")
|
||||
|
||||
with open(OUTPUT_FILE, 'w', encoding='utf-8') as outfile:
|
||||
for idx, pdf_name in enumerate(pdf_files, 1):
|
||||
print(f"[{idx}/{len(pdf_files)}] Обработка: {pdf_name}")
|
||||
for chunk in extract_pdf_chunks(os.path.join(MANUALS_DIR, pdf_name), WINDOW_SIZE):
|
||||
raw_json = ask_ollama(chunk)
|
||||
if not raw_json: continue
|
||||
try:
|
||||
data = json.loads(raw_json)
|
||||
# Всеядный парсер: обрабатывает и массивы, и прямые словари
|
||||
items = data if isinstance(data, list) else [data] if isinstance(data, dict) and "conversations" in data else data.values() if isinstance(data, dict) else []
|
||||
for item in items:
|
||||
if isinstance(item, dict) and "conversations" in item:
|
||||
outfile.write(json.dumps(item, ensure_ascii=False) + "\n")
|
||||
outfile.flush()
|
||||
except: continue
|
||||
print("Датасет успешно сохранен!")
|
||||
|
||||
if __name__ == "__main__": main()
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_Запуск: `python3 /mnt/c/ST/aandc/generate_qa.py`. Сформируется чистый файл `aandc_dataset.jsonl` на 756 диалогов._
|
||||
|
||||
---
|
||||
|
||||
ЧАСТЬ 4. Индустриальный Fine-Tuning на 8 ГБ VRAM
|
||||
|
||||
Для обхода аппаратных ограничений памяти Triton-ядер мы используем чистый, надежный стек **Hugging Face (`peft` + `bitsandbytes` + `transformers`)** в режиме 4-битного квантования.
|
||||
|
||||
4.1. Установка стабильного окружения
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
pip install torch torchvision torchaudio --index-url https://pytorch.org --break-system-packages
|
||||
pip install transformers datasets peft bitsandbytes trl --break-system-packages
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
4.2. Развертывание «железного» скрипта обучения `train.py`
|
||||
|
||||
Создайте файл `/mnt/c/ST/aandc/train.py` (выполняется прямо сейчас):
|
||||
|
||||
python
|
||||
|
||||
```
|
||||
import os, json, torch
|
||||
from datasets import Dataset
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, TrainingArguments, Trainer, DataCollatorForLanguageModeling
|
||||
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
|
||||
|
||||
MODEL_NAME = "unsloth/llama-3.1-8b-Instruct-bnb-4bit"
|
||||
max_seq_length = 512 # Идеальный размер контекста для защиты 8 ГБ VRAM от пиков
|
||||
DATASET_PATH = "/mnt/c/ST/aandc/aandc_dataset.jsonl"
|
||||
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
print("--- 1. Конфигурация 4-бит NF4 ---")
|
||||
bnb_config = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.float16, bnb_4bit_use_double_quant=True)
|
||||
|
||||
print("--- 2. Загрузка модели через HF ---")
|
||||
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
|
||||
tokenizer.pad_token = tokenizer.eos_token
|
||||
model = AutoModelForCausalLM.from_pretrained(MODEL_NAME, quantization_config=bnb_config, device_map="auto")
|
||||
model = prepare_model_for_kbit_training(model)
|
||||
|
||||
print("--- 3. Настройка LoRA параметров ---")
|
||||
peft_config = LoraConfig(r=16, lora_alpha=16, target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM")
|
||||
model = get_peft_model(model, peft_config)
|
||||
|
||||
print("--- 4. Валидация и Токенизация датасета ---")
|
||||
valid_dialogues = []
|
||||
with open(DATASET_PATH, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
if not line.strip(): continue
|
||||
try:
|
||||
item = json.loads(line)
|
||||
if "conversations" in item and isinstance(item["conversations"], list):
|
||||
valid_dialogues.append({"conversations": item["conversations"]})
|
||||
except: continue
|
||||
|
||||
dataset = Dataset.from_list(valid_dialogues)
|
||||
|
||||
def tokenize_prompts_func(examples):
|
||||
tokenized_inputs = {"input_ids": [], "attention_mask": []}
|
||||
for con in examples["conversations"]:
|
||||
mapped = [{"role": "user" if m["from"] == "human" else "assistant", "content": m["value"]} for m in con]
|
||||
text = tokenizer.apply_chat_template(mapped, tokenize=False, add_generation_prompt=False)
|
||||
tokenized = tokenizer(text, truncation=True, max_length=max_seq_length, padding=False)
|
||||
tokenized_inputs["input_ids"].append(tokenized["input_ids"])
|
||||
tokenized_inputs["attention_mask"].append(tokenized["attention_mask"])
|
||||
return tokenized_inputs
|
||||
|
||||
dataset = dataset.map(tokenize_prompts_func, batched=True, remove_columns=["conversations"])
|
||||
|
||||
print("--- 5. Запуск неубиваемого Trainer ---")
|
||||
trainer = Trainer(
|
||||
model=model, train_dataset=dataset,
|
||||
data_collator=DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False),
|
||||
args=TrainingArguments(
|
||||
per_device_train_batch_size=1, gradient_accumulation_steps=8,
|
||||
warmup_steps=5, max_steps=60, learning_rate=2e-4, fp16=True, logging_steps=1,
|
||||
optim="adamw_8bit", weight_decay=0.01, lr_scheduler_type="linear",
|
||||
seed=3407, output_dir="outputs", remove_unused_columns=False
|
||||
),
|
||||
)
|
||||
trainer.train()
|
||||
print("--- ТРЕНИРОВКА УСПЕШНО ЗАВЕРШЕНА ---")
|
||||
|
||||
model.save_pretrained("/mnt/c/ST/aandc/aandc_lora_model")
|
||||
tokenizer.save_pretrained("/mnt/c/ST/aandc/aandc_lora_model")
|
||||
print("Адаптеры сохранены!")
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_Запуск: выгрузите Ollama (`pkill -f ollama`) для очистки VRAM и введите `python3 /mnt/c/ST/aandc/train.py`._
|
||||
|
||||
---
|
||||
|
||||
ЧАСТЬ 5. Экспорт адаптеров и Сборка модели для Ollama
|
||||
|
||||
После завершения обучения у вас есть базовая модель и папка обученных LoRA-адаптеров `aandc_lora_model`. Переведём их в единый формат `.gguf`.
|
||||
|
||||
5.1. Скачивание утилиты конвертации `llama.cpp`
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
cd /mnt/c/ST/aandc/
|
||||
git clone https://github.com
|
||||
pip3 install -r llama.cpp/requirements.txt --break-system-packages
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
5.2. Слияние и Квантование (Merge & Quantize)
|
||||
|
||||
Мы берем обученные веса LoRA и упаковываем их в единый 4-битный файл, который мгновенно зачитает любая система:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
# Конвертируем веса адаптеров LoRA в формат GGUF
|
||||
python3 llama.cpp/convert_hf_to_gguf.py /mnt/c/ST/aandc/aandc_lora_model/ --outfile atm_expert_q4.gguf
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
5.3. Настройка `Modelfile`
|
||||
|
||||
Создайте текстовый файл `/mnt/c/ST/aandc/Modelfile`:
|
||||
|
||||
dockerfile
|
||||
|
||||
```
|
||||
FROM ./atm_expert_q4.gguf
|
||||
|
||||
TEMPLATE """{{ if .System }}<|start_header_id|>system<|end_header_id|>
|
||||
|
||||
{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>
|
||||
|
||||
{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>
|
||||
|
||||
{{ .Response }}<|eot_id|>"""
|
||||
|
||||
SYSTEM "Вы — специализированный локальный ИИ-эксперт по банкоматному ПО. Вы отвечаете строго на основе технической документации, без «каши», четко, структурированно, используя инженерные термины."
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
5.4. Регистрация Эксперта
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
# Поднимаем сервер заново
|
||||
ollama serve > /dev/null 2>&1 &
|
||||
|
||||
# Регистрируем финальную модель
|
||||
ollama create atm_expert -f /mnt/c/ST/aandc/Modelfile
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
_Проверка работоспособности: `ollama run atm_expert`_.
|
||||
|
||||
---
|
||||
|
||||
ЧАСТЬ 6. Автономный перенос модели на другой ПК
|
||||
|
||||
Благодаря архитектуре `.gguf`, готовый эксперт полностью автономен и не требует CUDA, Python или Linux на целевом ПК.
|
||||
|
||||
1. **Копирование:** Заберите с флешки два файла: `atm_expert_q4.gguf` и `Modelfile`.
|
||||
2. **Установка Ollama:** На целевом ПК (Windows, Mac или Linux) поставьте чистую Ollama с официального сайта ollama.com.
|
||||
3. **Импорт:** Положите файлы в любую папку (например, `C:\ATM_AI\`), откройте командную строку Windows (cmd/PowerShell) в этой папке и выполните:
|
||||
|
||||
bash
|
||||
|
||||
```
|
||||
cd C:\ATM_AI\
|
||||
ollama create atm_expert -f Modelfile
|
||||
```
|
||||
|
||||
Используйте код с осторожностью.
|
||||
|
||||
4. **Работа:** Модель готова к эксплуатации без интернета по команде `ollama run atm_expert`.
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user