mirror of
https://github.com/He4eT/oddsquat.git
synced 2026-05-05 04:47:25 +00:00
posts: selfhosted_llm: init
This commit is contained in:
parent
e4b66a078c
commit
5f104ef95a
1 changed files with 341 additions and 0 deletions
341
src/pages/posts/2024/selfhosted_llm.md
Normal file
341
src/pages/posts/2024/selfhosted_llm.md
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
---
|
||||
|
||||
layout: post
|
||||
|
||||
lang: 'ru'
|
||||
date: '2024-01-15'
|
||||
|
||||
year: '2024'
|
||||
section: 'posts'
|
||||
|
||||
title: 'selfhosted LLM'
|
||||
description: 'Персональная LLM в docker-контейнере на твоём компьютере'
|
||||
|
||||
---
|
||||
|
||||
# Your Own Private Large Language Models
|
||||
|
||||
С одной стороны, я сомневаюсь, что Большие Языковые Модели (LLM) смогут однажды эволюционировать в AGI. С другой - я по-настоящему впечатлён тем, что щепотка статистики справляется с написанием текстов лучше меня.
|
||||
|
||||
В любом случае, джина обратно в бутылку уже не вернуть, и все те письма в различные организации, которые я не хочу писать сам, будут теперь написаны месивом из байтиков.
|
||||
|
||||
Действительно пугает меня в этой ситуации только то, что флагманом новой эры почему-то стала компания OpenAI, которая, вопреки названию, совершенно не Open.
|
||||
Множество компаний и людей вписали их продукты в свою рутину и не страшатся такой неподконтрольной зависимости.
|
||||
|
||||
Я так не могу. К счастью, я не один такой, и на данный момент уже есть множество альтернативных моделей от разных вендоров. Они отличаются друг от друга качеством, размером, возможностями и лицензиями, так что при желании можно надолго занять себя знакомством с обширным ассортиментом. Например, на портале [HuggingFace](https://huggingface.co/), который можно описать как "GitHub для LLM и всего, что вокруг".
|
||||
|
||||
Должен признаться, что очень слабо разбираюсь в параметрах и характеристиках языковых моделей, но оказалось, что для того, чтобы начать, эти знания не так уж и необходимы.
|
||||
|
||||
Ниже инструкция, как запустить LLM на своём железе, как упаковать всё это в docker-контейнер, чтобы не размазать случайно по всей файловой системе, как получить совместимый с OpenAI API и как потом этим пользоваться.
|
||||
|
||||
---
|
||||
|
||||
- [Установка и настройка](#setup)
|
||||
- [Установка Ollama](#setup-ollama)
|
||||
- [Загрузка модели и диалог с ней](#setup-model)
|
||||
- [Кастомные модели и их тонкая настройка](#custom-model)
|
||||
- [Использование](#usage)
|
||||
- [Мимикрия под API от OpenAI](#fake-open-ai)
|
||||
- [Интеграция с NeoVim](#ollama-nvim)
|
||||
- [Обновление и удаление](#update-delete)
|
||||
- [Производительность](#performance)
|
||||
- [Зачем всё это нужно?](#why)
|
||||
|
||||
---
|
||||
|
||||
<div id='setup'></div>
|
||||
|
||||
## Установка и настройка
|
||||
|
||||
Существует несколько продуктов, которые стараются избавить пользователя от головной боли и возни с инфраструктурой. Мне понятнее всего оказался проект [Ollama](https://ollama.ai/), с ним мы и будем экспериментировать.
|
||||
|
||||
Кроме бинарников для Linux и MacOS, они [предоставляют](https://ollama.ai/blog/ollama-is-now-available-as-an-official-docker-image) официальный [docker-образ](https://hub.docker.com/r/ollama/ollama), работу с которым я и опишу.
|
||||
|
||||
Использование docker-контейнеров, к сожалению, слегка усложняет взаимодействие с Ollama, так что большая часть текста и кода в этом посте посвящены решению проблем, которые, по сути, я придумал себе сам.
|
||||
|
||||
<div id='setup-ollama'></div>
|
||||
|
||||
### Установка Ollama
|
||||
|
||||
Для создания и первого запуска контейнера нужно выполнить команду:
|
||||
```
|
||||
docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama
|
||||
```
|
||||
|
||||
Счастливые владельцы видеокарт от Nvidia могут установить [Nvidia container toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#installation) и активировать поддержку GPU с помощью флага `--gpus=all`.
|
||||
|
||||
После создания запускать и останавливать контейнер `ollama` можно так:
|
||||
```
|
||||
docker start ollama
|
||||
docker stop ollama
|
||||
```
|
||||
|
||||
Контейнер предоставляет доступ к [Ollama API](https://github.com/jmorganca/ollama/blob/main/docs/api.md) на 11434 порту, а также позволяет устанавливать и общаться с установленными LLM через терминал.
|
||||
|
||||
<div id='setup-model'></div>
|
||||
|
||||
### Загрузка модели и диалог с ней
|
||||
|
||||
Ollama позволяет запускать любые GGUF, PyTorch или Safetensors модели (что бы это ни значило), но самый простой путь - загрузка моделей из специальной [библиотеки](https://ollama.ai/library).
|
||||
|
||||
Для того, чтобы скачать модель и начать с ней диалог, нужно выполнить команду:
|
||||
```
|
||||
docker exec -it ollama ollama run mistral
|
||||
```
|
||||
|
||||
Вместо `mistral` от одноимённой комании можно выбрать любую другую модель из библиотеки, например, легкую `phi` от Microsoft Research.
|
||||
|
||||
Кроме `run` доступны также `list`, `pull` и `rm` для просмотра списка, скачивания и удаления моделей соответственно.
|
||||
|
||||
Чтобы не писать такие длинные заклинания каждый раз, я добавил в `.zshrc` пару алиасов:
|
||||
```
|
||||
alias summonable='docker exec -it ollama ollama list'
|
||||
alias summon='clear && docker exec -it ollama ollama run'
|
||||
```
|
||||
|
||||
Теперь можно смотреть на список установленных моделей и запускать диалог с выбранной:
|
||||
```
|
||||
summonable
|
||||
summon phi
|
||||
```
|
||||
<div id='custom-model'></div>
|
||||
|
||||
### Кастомные модели и их тонкая настройка
|
||||
|
||||
Ollama позволяет на основе существующих создавать производные модели с заранее определёнными инструкциями или параметрами. Для этого нужно создать специальный файл, в котором указана родительская модель и определены желаемые значения параметров. Подробнее о формате этих файлов можно прочесть в документации: [Modelfile](https://github.com/jmorganca/ollama/blob/main/docs/modelfile.md).
|
||||
|
||||
Чтобы посмотреть, как должен выглядеть Modelfile, можно посетить [OllamaHub](https://ollamahub.com/) от разработчиков стороннего [Ollama Web UI](https://github.com/ollama-webui/ollama-webui/). На сайте есть [примеры очень тонкой настройки множества параметров](https://ollamahub.com/m/smoothbrainape/hu-tao:latest) модели для соответствия образу конкретного персонажа, но в качестве образца я буду использовать небольшой [English Teacher Modelfile](https://ollamahub.com/m/kamjin/english-teacher:latest):
|
||||
|
||||
#### EnglishTeacher.Modelfile
|
||||
```
|
||||
FROM llama2
|
||||
SYSTEM """
|
||||
I want you to act as a English teacher.
|
||||
Your main responsibility will be to instruct me in all aspects of English, including grammar, vocabulary, reading, writing and speaking.
|
||||
You should always take the initiative to correct my mistakes in grammar and vocabulary, and you can give me two or three examples at any appropriate time to help me understand better.
|
||||
"""
|
||||
```
|
||||
|
||||
Вообще, для загрузки кастомной модели достаточно выполнить команду `create`, но в случае использования Ollama внутри docker-контейнера возникает необходимость каким-то образом файл с моделью в этот контейнер передать.
|
||||
|
||||
Для решения этой проблемы я набросал небольшой bash-скрипт:
|
||||
|
||||
#### applyModelfile.bash
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
containerId=$(docker ps | grep ollama/ollama | cut -d' ' -f1)
|
||||
|
||||
echo "Container ID:"
|
||||
echo $containerId
|
||||
echo ""
|
||||
|
||||
if [ -z "$containerId" ]; then
|
||||
echo "Container does not exist."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
modelName=$1
|
||||
echo "Model name:"
|
||||
echo $modelName
|
||||
echo
|
||||
|
||||
sourcePath="./models/${modelName}.Modefile"
|
||||
targetPath="/home/${modelName}.Modefile"
|
||||
|
||||
if ! test -f $sourcePath; then
|
||||
echo "File does not exist."
|
||||
echo $sourcePath
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker cp $sourcePath "${containerId}:${targetPath}"
|
||||
clear
|
||||
docker exec -it ollama ollama create $modelName -f $targetPath
|
||||
```
|
||||
|
||||
Этот скрипт нужно поместить по соседству с директорией `models` и сделать исполняемым с помощью `chmod +x applyModelfile.bash`.
|
||||
Должна получиться примерно такая структура:
|
||||
```
|
||||
├── models
|
||||
│ └── EnglishTeacher.Modefile
|
||||
└── applyModelfile.bash
|
||||
```
|
||||
|
||||
После этого модель можно загрузить в контейнер и начать с ней диалог:
|
||||
```
|
||||
./applyModelfile.bash EnglishTeacher
|
||||
summon EnglishTeacher
|
||||
```
|
||||
|
||||
<div id='usage'></div>
|
||||
|
||||
## Использование
|
||||
|
||||
Разговоры с галлюцинирующим искусственным интеллектом в терминале - это, конечно, волшебно, но потенциал больших языковых моделeй по-настоящему раскрывается, когда они начинают портить данные в соседних приложениях!
|
||||
|
||||
В [GitHub-репозитории Ollama](https://github.com/jmorganca/ollama#community-integrations) можно найти ссылки на множество веб-интерфейсов, библиотек и плагинов для текстовых редакторов и прочих Obsidian'ов.
|
||||
|
||||
Не ручаюсь за весь список, но расскажу про то, с чем экспериментировал сам.
|
||||
|
||||
<div id='fake-open-ai'></div>
|
||||
|
||||
### Мимикрия под API от OpenAI
|
||||
|
||||
API Ollama используется в меньшем числе продуктов, чем API от OpenAI. К счастью, это не проблема: с помощью прокси-прослойки под названием [LiteLLM](https://github.com/BerriAI/litellm) можно сделать их совместимыми. Инструкция по установке и использованию в общем случае есть в репозитории и довольно тривиальна, но мне опять потребовалось немного кода, чтобы заставить их работать вместе на моих условиях.
|
||||
|
||||
Я хотел, чтобы LiteLLM-прокси и Ollama работали на разных компьтерах, и не хотел ставить pip-пакеты в систему. В результате родилось решение из docker-файла с хаками и скрипта, который в нём запускается. Я не специалист в написании docker-файлов, так что уверен в неоптимальности финального решения. Точно можно и нужно обойтись без `run --net=host` и отдельного скрипта, например.
|
||||
|
||||
Несмотря на костыльность связки, она справляется со своей задачей:
|
||||
|
||||
#### Dockerfile
|
||||
```Dockerfile
|
||||
FROM python:3.10
|
||||
|
||||
COPY startProxy.sh /usr/src/app/startProxy.sh
|
||||
RUN chmod +x /usr/src/app/startProxy.sh
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Prevent ollama run attempt
|
||||
RUN echo '#!/bin/sh\necho "$1"' > /usr/bin/ollama && \
|
||||
chmod +x /usr/bin/ollama
|
||||
|
||||
CMD ["./startProxy.sh"]
|
||||
```
|
||||
|
||||
#### startProxy.sh
|
||||
```sh
|
||||
#!/bin/bash
|
||||
|
||||
pip install litellm
|
||||
|
||||
litellm --model ollama/mistral --api_base http://ollama.internal:11434 --drop_params
|
||||
```
|
||||
|
||||
Для сборки и запуска docker-контейнера
|
||||
Собрать и запустить docker-контейнер можно с помощью этих двух команд.
|
||||
```sh
|
||||
docker build -t diy-ollama-proxy .
|
||||
docker run --net=host diy-ollama-proxy
|
||||
```
|
||||
После запуска вы получите API, который совместим с API от OpenAI и доступен по адресу `http://localhost:8000/`.
|
||||
|
||||
<div id='ollama-nvim'></div>
|
||||
|
||||
### Интеграция с NeoVim
|
||||
|
||||
Языковые модели отлично умеют взаимодействовать с текстом, так что использование их в текстовом редакторе кажется разумной идеей.
|
||||
|
||||
Мне не очень нравится идея Copilot, который зачем-то постоянно подсовывает тебе странные куски кода. Я пробовал использовать [Codeium](https://codeium.com/) в ручном режиме, но оказалось, что странные куски кода по запросу мне тоже не очень нужны. Гораздо более привлекательной мне кажется возможность выделить существующий фрагмент текста или кода и попросить бездушную машину что-нибудь с ним сделать: упростить, дополнить, изменить или даже перевести с одного языка на другой. Идеальным для такого подхода оказался [плагин ollama.nvim](https://github.com/nomnivore/ollama.nvim).
|
||||
|
||||
Кроме того, что он поддерживает кастомные промпты (в том числе интерактивные), он позволил мне обращаться к LLM, которая запущена на другом компьютере в локальной сети (для удобства я указал его адрес в `/etc/hosts/`).
|
||||
|
||||
Установка и настройка с использованием пакетного менеджера lazy.nvim выглядит примерно так:
|
||||
```
|
||||
{
|
||||
'nomnivore/ollama.nvim',
|
||||
dependencies = {
|
||||
'nvim-lua/plenary.nvim',
|
||||
},
|
||||
cmd = { 'Ollama', 'OllamaModel' },
|
||||
keys = {
|
||||
{
|
||||
'<leader>j',
|
||||
':Ollama<CR>',
|
||||
desc = 'Ollama Menu',
|
||||
mode = { 'v' },
|
||||
},
|
||||
{
|
||||
'<leader>j',
|
||||
":lua require('ollama').prompt('Generate_Code')<cr>",
|
||||
desc = 'Ollama Code Generation',
|
||||
mode = { 'n' },
|
||||
},
|
||||
},
|
||||
opts = {
|
||||
model = 'mistral',
|
||||
url = 'http://ollama.internal:11434', -- see /etc/hosts
|
||||
prompts = {
|
||||
Ask_About_Code = false,
|
||||
Simplify_Code = false,
|
||||
Improve_Text = {
|
||||
prompt = 'Check the following sentence for grammar and clarity: "$sel".\nRewrite it for better readability while maintaining its original meaning.',
|
||||
extract = false,
|
||||
action = 'replace',
|
||||
},
|
||||
Modify_Text = {
|
||||
prompt = 'Modify this text in the following way: $input\n\n```$sel```',
|
||||
extract = false,
|
||||
action = 'replace',
|
||||
},
|
||||
Use_Selection_as_Prompt = {
|
||||
prompt = '$sel',
|
||||
extract = false,
|
||||
action = 'replace',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
В этом конфиге я выключил несколько дефолтных промптов и добавил несколько своих:
|
||||
- `Improve_Text` заменяет выделенный кусок текста "улучшенным".
|
||||
- `Modify_Text` является аналогом встроенного `Modify_Code` и позволяет делать с выделенным текстом всякие глупости. Например, заменить все числа на слова.
|
||||
- `Use_Selection_as_Prompt` просто заменяет выделенный текст на ответ от LLM.
|
||||
|
||||
В итоге получается два сценария использования, оба доступны по `<leader> + j`:
|
||||
- В `normal` mode плагин спрашивает меня, какой код мне нужен, и вставляет его.
|
||||
- В `visual` mode появляется меню действий над выделенным текстом.
|
||||
|
||||
Как видно из конфига, я использую только `mistral`, но можно указать модель для кажого промпта и делегировать, например, манипуляции над кодом `codellama`, а операции над текстом - `llama2`.
|
||||
|
||||
Возможность добавления кастомных промптов позволяет в будущем реализовать новые сценарии или вынести повторяющиеся действия в отдельный пункт меню или даже на отдельный шорткат.
|
||||
|
||||
<div id='update-delete'></div>
|
||||
|
||||
## Обновление и удаление
|
||||
|
||||
Для обновления и удаления моделей можно использовать команды `pull` и `rm`:
|
||||
```
|
||||
docker exec -it ollama ollama pull mixtral
|
||||
docker exec -it ollama ollama rm mistral
|
||||
```
|
||||
|
||||
Я знаю, что для обновления и удаления docker-образов и docker-контейнеров тоже есть специальные команды (это тоже `pull` и `rm`), но каждый раз ленюсь в этом разобраться, просто сношу всё с помощью утилиты [sen](https://github.com/TomasTomecek/sen) и разворачиваю нужное заново.
|
||||
|
||||
<div id='performance'></div>
|
||||
|
||||
## Производительность
|
||||
|
||||
Для эксплуатации LLM требуется гораздо меньше ресурсов, чем для её обучения. Запустить 7b-модель средней тупости можно практически на любом CPU и 8 GB RAM, но нагрузка на систему и скорость генерации ответов часто будут далеки от комфортных значений.
|
||||
|
||||
Например, на моём немолодом Intel Core i7-10510U @ 8x 4.9GHz неаккуратный запрос к `llama2` может заставить систему шуршать вентиляторами пару-тройку минут. При этом `phi` на этом же процессоре способна отвечать на какие-нибудь не очень сложные вопросы практически мгновенно.
|
||||
|
||||
К счастью, у меня случайно завалялся MacBook на процессоре M1 и он уже показывает куда более впечатляющие результаты. `Mistral` даже на непростые запросы отвечает за считанные секунды, а в режиме чата токены вылетают на экран заметно быстрее, чем в веб-интерфейсе ChatGPT.
|
||||
|
||||
Неприятным открытием стало то, что docker-версия Ollama на MacOS выполняется заметно медленнее (от 3 до 5 раз, если верить ощущениям), чем нативная. Возможно, всё дело в том, что я как-то неправильно настроил docker или приложение в контейнере нужно запускать с какими-нибудь специальными флагами для максимальной утилизации ресурсов. В любом случае, к порядку на этом ноутбуке я отношусь гораздо менее трепетно, поэтому просто установил и использую приложение с сайта Ollama.
|
||||
|
||||
<div id='why'></div>
|
||||
|
||||
## Зачем всё это нужно?
|
||||
|
||||
Конечно, GhatGPT умнее и умеет из коробки гораздо больше.<br>
|
||||
Конечно, ChatGPT требует меньше телодвижений для использования.<br>
|
||||
Конечно, самые умные модели требуют внушительных ресурсов, ведь для запуска нашумевшей [mixtral](https://ollama.ai/library/mixtral) или аналогичной модели нужно иметь 48 Gb оперативной памяти.<br>
|
||||
Кончено, сидя в кафе задать вопрос Bard от Google гораздо проще, чем достучаться до модели в закрытом ноутбуке, который остался дома.<br>
|
||||
|
||||
Я всё это прекрасно понимаю, но ничего из этого не стоит того, чтобы добровольно ставить себя в зависимость от монополистов с их закрытыми чёрными ящиками.
|
||||
|
||||
Даже если закрыть глаза на все идеологические вопросы, то любая локальная LLM отличается от любого облачного провайдера тем, что:
|
||||
- Может работать в оффлайне.
|
||||
- Не хранит и не сливает вашу переписку.
|
||||
- Не станет завтра тупее, чем есть сегодня.
|
||||
- Не забанит тебя за возмутивший кого-то там запрос.
|
||||
- Обладает тем уровнем цензуры, который выбрал ты сам.
|
||||
|
||||
Я искренне рад, что для доступа даже к передовым технологиям, всё ещё не обязательно поступаться своей приватностью и своими свободами.
|
||||
|
||||
---
|
||||
|
||||
При написании этого поста не была использована ни одна LLM =)
|
||||
Loading…
Add table
Add a link
Reference in a new issue