ChatGPT на вашем ноутбуке: бесплатно, анонимно, навсегда
ChatGPT на вашем ноутбуке: бесплатно, анонимно, навсегда

Полная версия

Настройки чтения
Размер шрифта
Высота строк
Поля
На страницу:
4 из 9

В следующей главе мы установим llama-cpp-python, в том числе с поддержкой GPU, и напишем первый код, который «оживит» нашу модель. Мы создадим класс-обёртку LocalModel, который станет фундаментом всего дальнейшего проекта.

Глава 3.2. Установка инастройка llama-cpp-python

Мы выбрали инструмент. Теперь нужно установить его так, чтобы он работал быстро и без сюрпризов. Установка llama-cpp-python — это не просто pip install. Чтобы получить максимальную производительность (особенно если у вас есть видеокарта), нужно указать правильные флаги. В этой главе мы пройдём установку для всех основных платформ и проверим, что всё работает.

Что мы устанавливаем на самом деле

llama-cpp-python — это Python-обёртка. Под капотом она использует скомпилированную библиотеку llama.cpp. При установке через pip происходит компиляция этой библиотеки прямо на вашем компьютере. Поэтому процесс зависит от того, какие инструменты сборки у вас есть и какое железо вы хотите использовать.

Есть два основных варианта установки:

· Только CPU. Самый простой, работает везде. Модель будет использовать процессор и оперативную память.

· С поддержкой GPU. Требует драйверов и SDK от производителя видеокарты. Модель будет использовать видеопамять, что в разы быстрее.

Универсальный способ: установка только для CPU

Этот вариант подходит, если у вас нет мощной видеокарты или вы не хотите возиться с драйверами. Он работает на Windows, macOS и Linux.

bash

pip install llama-cpp-python

Всё. Библиотека скомпилируется с параметрами по умолчанию и будет использовать CPU. Никаких дополнительных действий не требуется. Если у вас macOS с Apple Silicon (M1/M2/M3/M4), этот способ автоматически задействует ускорение через Accelerate (Apple Neural Engine), что уже даст неплохую скорость.

Установка с поддержкой GPU: когда скорость важна

Если у вас есть дискретная видеокарта, вы можете задействовать её для инференса. Это даст прирост скорости в 3–10 раз.

Для видеокарт NVIDIA (CUDA)

Вам потребуются:

· Драйверы NVIDIA (любые современные, ставятся с сайта nvidia.com).

· CUDA Toolkit 12.x (скачивается с developer.nvidia.com/cuda-downloads).

Установите CUDA Toolkit, затем выполните:

bash

CMAKE_ARGS="-DGGML_CUDA=on" pip install llama-cpp-python

Переменная CMAKE_ARGS передаёт флаг -DGGML_CUDA=on компилятору. Он включает поддержку CUDA в llama.cpp. Компиляция займёт несколько минут.

Проверка: после установки запустите Python и выполните:

python

from llama_cpp import Llama

# Если модель загрузилась без ошибки "CUDA not available", всё работает.

Если вы видите предупреждение GGML_CUDA=off, значит, флаг не применился. Удалите библиотеку (pip uninstall llama-cpp-python) и повторите установку.

Для macOS с Apple Silicon (Metal)

На компьютерах Mac с чипами M1/M2/M3/M4 есть встроенное GPU-ускорение через Metal. В большинстве случаев CPU-установка уже использует Accelerate, но можно явно включить Metal для максимальной производительности:

bash

CMAKE_ARGS="-DGGML_METAL=on" pip install llama-cpp-python

После установки модель будет использовать встроенную графику Apple, что заметно быстрее чистого CPU.

Для видеокарт AMD (Vulkan)

Карты AMD поддерживаются через бэкенд Vulkan:

bash

CMAKE_ARGS="-DGGML_VULKAN=on" pip install llama-cpp-python

Перед этим убедитесь, что у вас установлен Vulkan SDK (vulkan.lunarg.com).

Выбор способа установки под вашежелезо.

Только процессор (Intel/AMD). Самый простой и универсальный вариант. Установка одной командой pip install llama-cpp-python без дополнительных флагов. Модель будет использовать все ядра процессора, скорость генерации составит от низкой до средней — примерно 5–15 токенов в секунду для 8B-модели. Подходит, если у вас нет дискретной видеокарты или вы не хотите возиться с драйверами.

NVIDIA GeForce/RTX. Если у вас видеокарта NVIDIA с поддержкой CUDA, вы можете ускорить модель в разы. Команда установки: CMAKE_ARGS="-DGGML_CUDA=on" pip install llama-cpp-python. Перед этим убедитесь, что установлены драйверы NVIDIA и CUDA Toolkit. Скорость будет высокой — 30–60 токенов в секунду, модель загрузится в видеопамять и освободит процессор для других задач.

Apple M1/M2/M3/M4. На компьютерах Mac с процессорами Apple Silicon можно использовать встроенное GPU-ускорение через Metal. В большинстве случаев достаточно обычной команды pip install llama-cpp-python — система автоматически задействует Accelerate. Для максимальной производительности можно указать флаг -DGGML_METAL=on. Скорость будет от средней до высокой, в зависимости от поколения чипа и объёма unified memory.

AMD Radeon. Видеокарты AMD поддерживаются через бэкенд Vulkan. Команда установки: CMAKE_ARGS="-DGGML_VULKAN=on" pip install llama-cpp-python. Предварительно потребуется установить Vulkan SDK с сайта vulkan.lunarg.com. Скорость средняя или высокая, в зависимости от модели карты и объёма видеопамяти.

Важно: Если вы не уверены, какое у вас железо — просто выполните pip install llama-cpp-python без флагов. Модель заработает в любом случае, просто на процессоре. Позже всегда можно переустановить с флагами под GPU.

Проверяем установку: пишем «Hello, world» для модели

Создайте в проекте файл test_model.py и напишите минимальный код для проверки:

python

# test_model.py

from model_loader import download_model

from llama_cpp import Llama

# 1. Скачиваем модель ( если ещё нет )

model_path = download_model()

# 2. Загружаем модель

print("Загрузка модели в память...")

model = Llama(

model_path=model_path,

n_ctx=2048, # размер контекстного окна (сколько токенов модель «помнит»)

n_threads=4, # количество потоков CPU (поставьте своё число ядер)

verbose=False # убираем технический вывод

)

# 3. Отправляем запрос

print("Генерация ответа...")

response = model.create_chat_completion(

messages=[

{"role": "system", "content": "Ты — полезный ассистент. Отвечай кратко."},

{"role": "user", "content": "Что такое локальный ИИ одним предложением?"}

],

temperature=0.7,

max_tokens=100

)

# 4. Печатаем ответ

answer = response["choices"][0]["message"]["content"]

print(f"\nОтвет модели:\n{answer}")

Запустите:

bash

python test_model.py

Если всё настроено правильно, вы увидите что-то вроде:

text

Загрузка модели Llama-3.1-8B-Instruct-Q4_K_M.gguf из unsloth/Llama-3.1-8B-Instruct...

Модель уже загружена: models/Llama-3.1-8B-Instruct-Q4_K_M.gguf

Загрузка модели в память...

Генерация ответа...

Ответ модели:

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

Что означают параметры в Llama()

· model_path — путь к файлу модели. Мы получаем его из нашей функции download_model().

· n_ctx — размер контекстного окна в токенах. Это максимальная длина диалога, которую модель «помнит». Для начала 2048 токенов достаточно (это около 1500 слов). Позже, при подключении RAG, увеличим до 4096 или 8192.

· n_threads — число потоков CPU, которые будет использовать модель. Поставьте количество физических ядер вашего процессора. Для 4-ядерного процессора — 4, для 8-ядерного — 8.

· verbose=False — отключает подробный лог загрузки. Если что-то пойдёт не так, можно включить обратно (True) для диагностики.

Возможные ошибки и их решение

ModuleNotFoundError: No module named 'llama_cpp'. Библиотека не установлена в текущем виртуальном окружении. Убедитесь, что вы активировали venv и выполнили pip install llama-cpp-python. Если библиотека установлена, но ошибка остаётся — проверьте, что вы находитесь в правильном окружении: в начале строки терминала должен быть префикс (venv).

ImportError: DLL load failed. Проблема возникает на Windows при отсутствии рантаймов Visual C++. Скачайте и установите Microsoft Visual C++ Redistributable с официального сайта Microsoft. После установки перезагрузите компьютер — ошибка должна исчезнуть.

RuntimeError: CUDA error: no CUDA-capable device is detected. Вы установили версию с поддержкой CUDA, но видеокарта NVIDIA не обнаружена. Либо у вас нет карты NVIDIA, либо не установлены драйверы. Решение: установите драйверы с сайта nvidia.com, или переустановите llama-cpp-python без флага CUDA для работы на процессоре.

Illegal instruction (core dumped). Возникает на Linux, когда процессор не поддерживает инструкции AVX2, которые llama.cpp использует по умолчанию. Обычно это случается на очень старых процессорах. Добавьте флаг -DGGML_NATIVE=off при установке — это соберёт библиотеку с базовыми инструкциями, совместимыми со всеми процессорами.

Очень медленная генерация — 1–2 токена в секунду. Скорее всего, модель работает на процессоре в однопоточном режиме. Проверьте параметр n_threads при создании объекта Llama — он должен быть равен количеству физических ядер вашего процессора. Если у вас есть видеокарта, переустановите библиотеку с поддержкой GPU — скорость вырастет в разы.

Ошибка памяти — out of memory. Модель не помещается в доступную оперативную или видеопамять. У вас два варианта: выбрать более сильное квантование (например, Q3_K_M вместо Q4_K_M — файл будет меньше), или взять модель меньшего размера (7B вместо 8B). Также закройте другие программы, занимающие память — браузеры, IDE, мессенджеры.

Как переключить бэкенд в коде

По умолчанию llama-cpp-python использует тот бэкенд, который был включён при компиляции. Но можно явно указать, сколько слоёв модели загружать на GPU, а сколько оставить на CPU. Это полезно, если видеопамяти не хватает на всю модель:

python

model = Llama(

model_path=model_path,

n_ctx=2048,

n_threads=4,

n_gpu_layers=20 # Первые 20 слоёв — на GPU, остальные — на CPU

)

Параметр n_gpu_layers принимает число от 0 (всё на CPU) до -1 (всё на GPU, если хватает памяти). Поэкспериментируйте с этим значением, чтобы найти баланс между скоростью и стабильностью для вашего железа.

Что мы получили

Теперь в нашем проекте есть:

· Файл model_loader.py — умеет скачивать модель.

· Файл test_model.py — умеет загружать модель и получать от неё ответ.

Мы готовы к следующему шагу: написать полноценный класс-обёртку LocalModel, который станет фундаментом для всех будущих возможностей (чат, потоковая генерация, RAG, голос). Этим мы займёмся в главе 3.3.

Глава 3.3. Пишемкласс-обёртку LocalModel с методами chat() и stream().

Мы установили llama-cpp-python, проверили, что модель отвечает на запросы в тестовом скрипте. Теперь пора превратить этот разрозненный код в аккуратный, переиспользуемый компонент. Мы создадим класс LocalModel, который станет «сердцем» нашего проекта. Все будущие возможности — чат, работа с документами, голос — будут опираться именно на него.

Зачем нужна обёртка

Прямые вызовы Llama() из llama-cpp-python работают, но у них есть недостатки:

· Каждый раз приходится передавать десятки параметров (путь к модели, количество потоков, температуру).

· Нет удобного управления историей диалога.

· Код, который работает с моделью, перемешан с бизнес-логикой приложения.

· Трудно тестировать и заменять реализацию (вдруг мы захотим переключиться на другую библиотеку).

Наш класс LocalModel решит эти проблемы. Он будет:

· Хранить загруженную модель и её конфигурацию,

· Предоставлять простые методы chat() и stream_chat(),

· Следить за историей диалога (опционально),

· Давать тот же формат сообщений, что и OpenAI API, чтобы к нему привыкли все, кто писал под ChatGPT.

Код класса LocalModel

Создайте в проекте файл local_model.py. Ниже — полный код класса с подробными комментариями.

python

# local_model.py

from typing import List, Dict, Optional, Generator

from llama_cpp import Llama

class LocalModel:

"""

Обёртка над llama-cpp-python, предоставляющая удобный интерфейс

для общения с локальной языковой моделью.

"""

def __init__(

self,

model_path: str,

n_ctx: int = 2048,

n_threads: int = 4,

n_gpu_layers: int = 0,

verbose: bool = False

):

"""

Инициализация модели.

:param model_path: путь к файлу .gguf

:param n_ctx: размер контекстного окна (максимальное количество токенов,

которое модель «помнит»)

:param n_threads: количество потоков процессора

:param n_gpu_layers: сколько слоёв загрузить на GPU (-1 = все, 0 = ничего)

:param verbose: выводить ли подробный лог загрузки

"""

self.model_path = model_path

self.n_ctx = n_ctx

# Загружаем модель в память

self._llm = Llama(

model_path=model_path,

n_ctx=n_ctx,

n_threads=n_threads,

n_gpu_layers=n_gpu_layers,

verbose=verbose

)

# Хранилище истории диалогов (ключ — id беседы)

self._histories: Dict[str, List[Dict[str, str]]] = {}

def chat(

self,

messages: List[Dict[str, str]],

temperature: float = 0.7,

max_tokens: int = 512,

system_prompt: Optional[str] = None

) -> str:

"""

Отправляет сообщения модели и возвращает её ответ.

:param messages: список сообщений в формате OpenAI:

[{"role": "user", "content": "..."}, ...]

:param temperature: креативность (0 — строго, до 1.5 — фантазия)

:param max_tokens: максимальная длина ответа

:param system_prompt: системный промпт (если не указан в messages)

:return: строка с ответом модели

"""

# Если передан system_prompt, добавляем его в начало списка

full_messages = []

if system_prompt:

full_messages.append({"role": "system", "content": system_prompt})

full_messages.extend(messages)

response = self._llm.create_chat_completion(

messages=full_messages,

temperature=temperature,

max_tokens=max_tokens

)

return response["choices"][0]["message"]["content"]

def stream_chat(

self,

messages: List[Dict[str, str]],

temperature: float = 0.7,

max_tokens: int = 512,

system_prompt: Optional[str] = None

) -> Generator[str, None, None]:

"""

Потоковая версия chat(). Возвращает генератор, который выдаёт

токены по мере их генерации. Позволяет отображать ответ

«на лету», как в ChatGPT.

:param messages: список сообщений

:param temperature: креативность

:param max_tokens: максимальная длина ответа

:param system_prompt: системный промпт

:yield: кусочки текста (токены)

"""

full_messages = []

if system_prompt:

full_messages.append({"role": "system", "content": system_prompt})

full_messages.extend(messages)

stream = self._llm.create_chat_completion(

messages=full_messages,

temperature=temperature,

max_tokens=max_tokens,

stream=True

)

for chunk in stream:

# В каждом чанке может быть одна или несколько «дельт»

if "choices" in chunk and len(chunk["choices"]) > 0:

delta = chunk["choices"][0].get("delta", {})

content = delta.get("content", "")

if content:

yield content

def save_history(self, conversation_id: str, messages: List[Dict[str, str]]):

"""Сохраняет историю диалога под указанным id."""

self._histories[conversation_id] = messages.copy()

def load_history(self, conversation_id: str) -> List[Dict[str, str]]:

"""Загружает историю диалога по id. Если нет — пустой список."""

return self._histories.get(conversation_id, [])

def clear_history(self, conversation_id: str):

"""Удаляет историю диалога."""

if conversation_id in self._histories:

del self._histories[conversation_id]

Объяснение ключевых моментов

Конструктор __init__

Мы передаём путь к модели и настройки производительности. Важно, что модель загружается один раз при создании объекта LocalModel и остаётся в памяти до его уничтожения. Это экономит время при множественных запросах. Параметр n_ctx определяет «память» модели: чем он больше, тем более длинные диалоги она может вести, но тем больше потребляется памяти. Для начала 2048 токенов достаточно. n_gpu_layers мы подробно обсудили в предыдущей главе.

Метод chat()

Принимает список сообщений в формате OpenAI — таком же, какой используют облачные API. Это значит, что вы можете взять любой код, написанный для ChatGPT, заменить создание клиента на наш LocalModel, и всё продолжит работать. Внутри мы добавляем системный промпт, если он передан, и вызываем create_chat_completion. Ответ возвращается целиком, в виде строки.

Метод stream_chat()

Потоковая генерация — это когда ответ появляется по одному слову, как в ChatGPT. Вместо того чтобы ждать полного ответа, мы получаем токены по мере их создания. В Python это реализуется через генератор (ключевое слово yield). Внешний код может делать так:

python

for token in model.stream_chat(messages):

print(token, end="", flush=True)

Это создаёт эффект «печатания» текста и делает интерфейс более отзывчивым. Мы будем использовать этот метод при создании GUI.

Управление историей

Класс содержит словарь _histories, который хранит списки сообщений по идентификаторам бесед. Это примитивная, но работающая система для многопользовательского или многозадачного режима. Позже мы заменим её на постоянное хранение (файлы или базу данных), но для старта достаточно.

Практический пример: ведём диалог

Создайте файл chat_demo.py:

python

# chat_demo.py

from model_loader import download_model

from local_model import LocalModel

# 1. Скачиваем и загружаем модель

model_path = download_model()

print("Загрузка модели...")

model = LocalModel(model_path=model_path, n_threads=4)

# 2. Системный промпт (необязательно, но полезно)

system = "Ты — ассистент-повар. Отвечай коротко, предлагай простые рецепты."

# 3. Диалог в цикле

conversation_id = "cooking"

messages = [] # Начнём с пустой истории

while True:

user_input = input("\nВы: ")

if user_input.lower() in ["выход", "quit", "exit"]:

break

# Добавляем сообщение пользователя в историю

messages.append({"role": "user", "content": user_input})

# Получаем ответ (потоково)

print("Повар: ", end="", flush=True)

full_response = ""

for token in model.stream_chat(

messages=messages,

system_prompt=system,

temperature=0.7

):

print(token, end="", flush=True)

full_response += token

print() # Перевод строки после ответа

# Сохраняем ответ в историю

messages.append({"role": "assistant", "content": full_response})

model.save_history(conversation_id, messages)

Запустите:

bash

python chat_demo.py

Пример диалога:

text

Вы: Как сварить яйцо?

Повар: Всмятку — 4 минуты в кипящей воде. Вкрутую — 8-10 минут. Охладите под холодной водой, чтобы легче чистились.

Вы: А яичницу?

Повар: Разогрейте сковороду с маслом, разбейте яйца, жарьте 2-3 минуты. Для глазуньи накройте крышкой на минуту, чтобы белок схватился сверху.

Управление памятью: что делать, если диалог слишком длинный

Контекстное окно n_ctx ограничивает суммарное количество токенов, которые модель может «видеть» одновременно. Если диалог становится слишком длинным, нужно обрезать историю. Простейший способ — оставить только последние N сообщений. Мы добавим метод trim_history в наш класс (можно дополнить local_model.py):

python

def trim_history(self, messages: List[Dict[str, str]], max_messages: int = 20) -> List[Dict[str, str]]:

"""

Оставляет только последние max_messages сообщений.

Системный промпт (первое сообщение с role='system') сохраняется всегда.

"""

if len(messages) <= max_messages:

return messages

# Сохраняем системный промпт, если он есть

system_messages = [m for m in messages if m["role"] == "system"]

other_messages = [m for m in messages if m["role"] != "system"]

# Обрезаем остальные

trimmed = other_messages[-max_messages:]

return system_messages + trimmed

Возможные проблемы и ихрешения.

Модель «забывает», о чём говорили раньше. Это происходит, когда диалог превышает размер контекстного окна n_ctx. Llama.cpp автоматически обрезает историю, и начало разговора теряется. Решение простое: увеличьте n_ctx при создании модели — например, с 2048 до 4096 токенов. Учитывайте, что каждый токен контекста потребляет дополнительную память. Если увеличить n_ctx нельзя из-за нехватки ОЗУ, используйте метод trim_history() — он вручную оставляет только последние N сообщений, сохраняя системный промпт.

Потоковый вывод «дёргается» или зависает. Обычно проблема в слишком маленьком параметре max_tokens — модель упирается в лимит и останавливается на полуслове. Увеличьте max_tokens до 500 или 1000. Другая возможная причина — буферизация вывода. Убедитесь, что при печати токенов используется flush=True: print(token, end="", flush=True). Это заставляет Python выводить текст немедленно, а не копить в буфере.

Ответ содержит бред на незнакомую тему. Модель не обучалась на ваших специфических данных и пытается «угадать» ответ, порождая галлюцинации. Первое, что стоит сделать — добавить системный промпт, ограничивающий тематику: «Ты — ассистент по договорному праву. Если вопрос не по теме, скажи об этом». Если этого недостаточно — подключите RAG из Части 4 книги: модель будет искать ответ в ваших документах, а не выдумывать.

На страницу:
4 из 9