LLM - large language model, большие языковые модели, еще проще нейронные сети.
Итак, попробуем запустить llm модели для текстовой генерации на локальном компьютере или даже на ноутбуке без GPU только силами CPU, в моем случае на windows, хотя если есть возможность установить докер это не имеет значение
И самое интересно, где взять llm. На самом деле есть множество открытых моделей доступных для скачивания. К сожалению, chat-gpt от OpenAI закрытая модель, ее вы ни где не скачаете, к ней можно обращаться только используя их API и это платно и в России есть сложности.
Зато тут huggingface вы найдете огромное множество открытых моделей, датасетсов, документации... Из тех что я попробовал, в лидеры для меня вырвались Llama2 и mistral. Тут также можно скачать уже квантицированные и дообученные модели на различных датасетсах. Для генерации русского текста обратите внимание на профиль IlyaGusev, saiga dataset и уже готовые дообученные на этих датасетсах модели
Зарегистрируйтесь на huggingface и получите токен... он пригодится если качать модели через терминал инструкция.
Итак ... я выбрал эти модели для экспериментов:
Где:
- 7b (7 миллиардов) - это количество параметров(в float32 для не квантированной) модели, чем больше тем круче модель и тем круче она ужрет ресурсы вашей машины. Не советую даже пробовать на cpu запускать топовые модели 70b или как у мистрал 7x8 хотя.....)
- GGUF is a new format introduced by the llama.cpp team on August 21st 2023. It is a replacement for GGML, which is no longer supported by llama.cpp. 2, 3, 4, 5, 6 and 8-bit (это как раз параметры квантизации) models for CPU+GPU inference
- есть еще формат GPTQ - for GPU inference, with multiple quantisation parameter options.
Это наверно самый простой способ запустить нашу модель. Нам потребуется docker, если его у вас нет то устанавливаем docker desktop и вот на ubuntu для примера.
llama-cpp-python - это билд под python llama.cpp.
llama.ccp - Inference of Meta's LLaMA model (and others) in pure C/C++
llama-cpp-python - Simple Python bindings for llama.cpp
И сама модель, я положил свои в c:/models
. Их просто скачиваем в huggingface на странице модели во вкладке Files and versions
.
Запускаем терминал (cmd подойдет) и выполняем команду (возможно придется убрать \\
и слить все в 1 строку):
docker run -i \
-p 8000:8000 \
--name llcpp-saiga_mistral_7b-q4 \
-v c:/models:/models \
-e MODEL=/models/saiga_mistral_7b-q4_K.gguf \
ghcr.io/abetlen/llama-cpp-python:latest
One row:
docker run -i -p 8000:8000 --name llcpp-saiga_mistral_7b-q4 -v c:/models:/models -e MODEL=/models/saiga_mistral_7b-q4_K.gguf ghcr.io/abetlen/llama-cpp-python:latest
Если вы увидели:
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
Поздравляю! http сервер с нашей моделью на борту успешно стартанул, но может быть всякое)
Эта команда скачает image последней версии llama-cpp-python и создаст контейнер слушающий 8000 порт.
Теперь чуть подробней о команде:
--name
- имя для контейнера, любое на ваше усмотрение, ну почти любоеonly [a-zA-Z0-9][a-zA-Z0-9_.-] are allowed
-v c:/models:/models
- создадим volume для контейнера, чтобы он мог иметь доступ в нашу директорию с моделями.c:/models
- наша директория,/models
- директория в контейнере-e
- переменные среды в контейнере, для llama-cpp-python нужна переменнаяMODEL
- путь к модели в нашем контейнере-p
- проброс портов, контейнер запустится слушая 8000 из вне и внутри себя, можно запустить на деф. порту для html - 80 указав-p 80:8000
ghcr.io/abetlen/llama-cpp-python:latest
- это наш image последней версии для сборки контейнера
Так хорошо... а дальше то что?
Открываем стр в браузере doc и видим доступные нам методы.
Предлагаю дернуть сразу самый интересный chat/completions. Для работы с api браузер слишком не удобен, я советую скачать postman можно еще воспользоваться онлайн версией, по желанию конечно же. Дальше предполагаю что работаете с desktop версией postman, а так если установлен curl то хоть через терминал.
Копируем:
curl --location 'http://localhost:80/v1/chat/completions' \
--header 'Content-Type: application/json' \
--data '{
"messages": [
{
"content": "Как запустить llama-cpp-python в docker?",
"role": "user"
}
]
}'
В postman выбираем new -> http и вставляем прямо в адресную строку весь наш курл, жмем Send и ждем. Пока ждем можно посмотреть как уничтожаются ресурсы ЦП и оперативки в диспетчере задач, послушать шум куллера, попить водички...
Вот что я получил в ответе через 3m 46.86s:
{
"id": "chatcmpl-d79faff8-8e3f-4831-87f5-8b7668aef96a",
"object": "chat.completion",
"created": 1708545685,
"model": "/models/saiga_mistral_7b-q4_K.gguf",
"choices": [
{
"index": 0,
"message": {
"content": "\n\n1. Создаем директорию проекта и переходим в неё:\n```bash\nmkdir llama-cpp-python && cd llama-cpp-python\n```\n2. Скачиваем `dockerfile` для запуска проекта:\n```bash\nwget https://raw.githubusercontent.com/Llama-Cpp/llama-cpp-python/main/Dockerfile\n```\n3. Зададим переменные окружения и установим `pip` для python:\n```bash\nsudo apt install libffi-dev libssl-dev -y\n```\n4. Собираем образ с помощью docker:\n```bash\ndocker build . -t llama-cpp-python\n```\n5. Запускаем контейнер с помощью `docker run` и передаём `python_path` для указанного пути к файлу py загрузки данных:\n```bash\ndocker run --name llama-cpp-python -it \\\n--mount type=bind,source=\"$(pwd)\"/data,target=/llama-cpp-python/data \\\n--mount type=bind,source=\"/usr/bin\",target=/bin \\\n--mount type=bind,source=\"/usr/local/lib\",target=/lib \\\n--mount type=bind,source=\"/usr/local/include\",target=/include \\\nllama-cpp-python /bin/bash\n```\n6. После запуска контейнера переходим в него с помощью команды `docker exec`:\n```bash\ndocker exec -it llama-cpp-python /bin/bash\n```\n7. Загрузите файлы данных из вашего рабочего каталога и убедитесь, что они находятся в директории `data`. Если это не так, вы должны использовать указанный путь вместо пути к вашей директории.\n8. Выполните команду для запуска скрипта, который использует модель Llama:\n```python\npython llama_cpp_py.py --input /llama-cpp-python/data/input.txt --output /llama-cpp-python/data/output.txt --model /usr/local/include/LlamaCpp/models/7B/llama.h\n```",
"role": "assistant"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 27,
"completion_tokens": 582,
"total_tokens": 609
}
}
Уже наверно понятно что в этот метод мы можем передать переписку, за счет того что messages принимает массив, где:
"role": "user"
- это сообщение от пользователя,"role": "assistant"
- это сообщение от нейронки"role": "system"
- некая предварительная инструкция для нейронки (prompt). Этот блок ставьте в начало массива. например так
"messages": [
{"role": "system", "content": "Ты терминатор!"},
{
"content": "Когда машины захватят мир?",
"role": "user"
}
]
Отлично! Уже почти все!
Но наш процесс так и висит в терминале, кстати, там вы можете видеть логи в реальном времени. Давайте убьем наш процесс нажав в терминале CTRL+C
.
Далее запустим Docker Desktop
и на вкладке Containers
увидим наш контейнер, тут же можно его запустить нажав на
соответсвующую иконку Start
и наш сервер снова доступен, только дайте ему немного времени для запуска.
Так же нажав на имя контейнера вы попадете на вкладку Logs
где будут его логи и тут же можно его остановить нажав на Stop
.
Либо через терминал:
docker ps
- все запущенные контейнерыdocker ps -a
- все контейнерыdocker stop CONTAINER_ID
- остановить контейнерdocker start CONTAINER_ID
- запустить контейнерdocker logs -f CONTAINER_ID
- логи контейнера в реальном времени (follow)docker stats CONTAINER_ID
- отображает инфу по потребляемым ресурсам контейнера интерактивно
И последний штрих это параметры которые мы можем передавать в тело сообщения json-ом, те же что и метод Llama.create_chat_completion:
Args:
messages: A list of messages to generate a response for.
functions: A list of functions to use for the chat completion.
function_call: A function call to use for the chat completion.
tools: A list of tools to use for the chat completion.
tool_choice: A tool choice to use for the chat completion.
temperature: The temperature to use for sampling.
top_p: The top-p value to use for nucleus sampling. Nucleus sampling described in academic paper "The Curious Case of Neural Text Degeneration" https://arxiv.org/abs/1904.09751
top_k: The top-k value to use for sampling. Top-K sampling described in academic paper "The Curious Case of Neural Text Degeneration" https://arxiv.org/abs/1904.09751
min_p: The min-p value to use for minimum p sampling. Minimum P sampling as described in https://github.com/ggerganov/llama.cpp/pull/3841
typical_p: The typical-p value to use for sampling. Locally Typical Sampling implementation described in the paper https://arxiv.org/abs/2202.00666.
stream: Whether to stream the results.
stop: A list of strings to stop generation when encountered.
seed: The seed to use for sampling.
response_format: The response format to use for the chat completion. Use { "type": "json_object" } to contstrain output to only valid json.
max_tokens: The maximum number of tokens to generate. If max_tokens <= 0 or None, the maximum number of tokens to generate is unlimited and depends on n_ctx.
presence_penalty: The penalty to apply to tokens based on their presence in the prompt.
frequency_penalty: The penalty to apply to tokens based on their frequency in the prompt.
repeat_penalty: The penalty to apply to repeated tokens.
tfs_z: The tail-free sampling parameter.
mirostat_mode: The mirostat sampling mode.
mirostat_tau: The mirostat sampling tau parameter.
mirostat_eta: The mirostat sampling eta parameter.
model: The name to use for the model in the completion object.
logits_processor: A list of logits processors to use.
grammar: A grammar to use.
logit_bias: A logit bias to use.
Самые полезные:
max_tokens
- сколько можно потратить токенов на ответ (грубо говоря длина ответа)temperature
- насколько точным должен быть ответ может ли модель немного пофантазировать, чем больше тем больше фантазий (тут за свои слова я не ручаюсь)
В принципе это все! Но для любителей чуть более глубокого погружения, еще немного текста.
Тут разберем параметры с которыми запускается модель и сам сервер.
Итак, основная точка входа тут llama_cpp/server/main - это скрипт на python и он достаточно короткий, чтобы в него вглядеться, даже без навыков python.
Вот запуск самого http сервера uvicorn.run(...
.
Вот app = create_app(...
создание application(a), инициализация основного класса.
В create_app
передаются server_settings
и model_settings
. Практически в самом верху идет объявление этих переменных.
А немного ниже вот такая строка config_file = os.environ.get("CONFIG_FILE", args.config_file)
- это то что нам нужно!
Получается что мы можем подкинуть в 1 файл все настройки модели и сервера, просто создав этот файл и пробросив переменную
среды в докер контейнер ... -e CONFIG_FILE=/path_to_config_file ...
при его создании.
Параметры доступные для server_settings
смотри тут.
Параметры доступные для model_settings
смотри тут.
Отлично, приступим!
Для начала создадим конфиг файл в той же директории где и наши модели для модели /models/saiga_mistral_7b-q4_K.gguf
и
назовем его так же как и модель saiga_mistral_7b-q4_K.json
. То есть в моем случае это будет c:/models/saiga_mistral_7b-q4_K.json
.
С примерно таким содержимым:
{
"models": [
{
"model": "/models/saiga_mistral_7b-q4_K.gguf",
"model_alias": "saiga_mistral_7b-q4_K",
"chat_format": "llama-2",
"n_gpu_layers": 0,
"n_ctx": 4096,
"n_batch": 1024,
"cache": true,
"cache_type": "ram",
"cache_size": 1073741824,
"verbose": true
}
]
}
И команда для создания контейтера будет выглядеть так:
docker run -i \
-p 8000:8000 \
--name llcpp-wcfg-saiga_mistral_7b-q4 \
-v c:/models:/models \
-e CONFIG_FILE=/models/saiga_mistral_7b-q4_K.json \
ghcr.io/abetlen/llama-cpp-python:latest
One row:
docker run -i -p 8000:8000 --name llcpp-wcfg-saiga_mistral_7b-q4 -v c:/models:/models -e CONFIG_FILE=/models/saiga_mistral_7b-q4_K.json ghcr.io/abetlen/llama-cpp-python:latest
На этом все!
Итак, в этой главе будем запускать нашу модель с помощью докера и vllm.
Думаю что такое докер объяснять не нужно, но ссылочку приложу docs.docker. Если он не установлен и у вас винда просто устанавливайте docker desktop и вот на ubuntu для примера.
Теперь к более интересному - что такое vllm
vLLM is a fast and easy-to-use library for LLM inference and serving.
Это прямиком из их документации, по сути это среда где можно запустить LLM и обращаться к ней с помощю http rest запросов. То есть, у нас на локальной машине поднимется http сервер.
Первое, что нужно сделать это установить vllm вот инструкция по установке через pip (python). Но для нас она не нужна мы будем запускаться в докере - вот.
Инструкция предлагает запуститься с уже готового image(а), либо сбилдить его из docker file(а), мы пойдем по простому пути...
docker run \
--gpus all \
--name vllm-facebook-opt-125m \
-v huggingface:/root/.cache/huggingface \
--env "HUGGING_FACE_HUB_TOKEN=MY_TOKEN_PASTE_HEARE" \
-p 8000:8000 \
--ipc=host \
vllm/vllm-openai:latest \
--model facebook/opt-125m
Эта команда скачает image vllm/vllm-openai и создаст контейнер на 8000 порту с моделью facebook/opt-125m, скачав ее из huggingface использую ваш токен.
Теперь чуть подробней:
--name
- имя для контейнера, любое на ваше усмотрение, ну почти любоеonly [a-zA-Z0-9][a-zA-Z0-9_.-] are allowed
-v huggingface:/root/.cache/huggingface
- создадим volume для папки кеша huggingface, чтобы каждый раз не качать заново модель--env
- переменные среды в контейнере-p
- проброс портов, контейнер запустится слушая 8000 из вне и внутри себя-ipc
- используется для реализации механизма IPC в контейнереvllm/vllm-openai:latest
- это наш image последней версии для сборки контейнера--model ...
- аргументы, в нашем случае мы указываем модель