# LLaVA

Общайтесь с изображениями с помощью LLaVA — открытой альтернативы GPT-4V.

{% hint style="success" %}
Все примеры можно запускать на GPU-серверах, арендуемых через [CLORE.AI Marketplace](https://clore.ai/marketplace).
{% endhint %}

## Аренда на CLORE.AI

1. Посетите [CLORE.AI Marketplace](https://clore.ai/marketplace)
2. Отфильтруйте по типу GPU, объему VRAM и цене
3. Выберите **On-Demand** (фиксированная ставка) или **Spot** (цена по ставке)
4. Настройте ваш заказ:
   * Выберите Docker-образ
   * Установите порты (TCP для SSH, HTTP для веб-интерфейсов)
   * Добавьте переменные окружения при необходимости
   * Введите команду запуска
5. Выберите способ оплаты: **CLORE**, **BTC**, или **USDT/USDC**
6. Создайте заказ и дождитесь развертывания

### Доступ к вашему серверу

* Найдите данные для подключения в **Моих заказах**
* Веб-интерфейсы: используйте URL HTTP-порта
* SSH: `ssh -p <port> root@<proxy-address>`

## Что такое LLaVA?

LLaVA (Large Language and Vision Assistant) может:

* Понимать и описывать изображения
* Отвечать на вопросы о визуальном содержимом
* Анализировать графики, диаграммы, скриншоты
* Оптическое распознавание текста (OCR) и понимание документов

## Варианты моделей

| Модель        | Размер | VRAM   | Качество  |
| ------------- | ------ | ------ | --------- |
| LLaVA-1.5-7B  | 7B     | 8GB    | Хорошо    |
| LLaVA-1.5-13B | 13B    | 16GB   | Лучше     |
| LLaVA-1.6-34B | 34B    | 40GB   | Лучшее    |
| LLaVA-NeXT    | 7-34B  | 8-40GB | Последняя |

## Быстрое развертывание

**Docker-образ:**

```
pytorch/pytorch:2.5.1-cuda12.4-cudnn9-devel
```

**Порты:**

```
22/tcp
8000/http
```

**Команда:**

```bash
pip install llava torch transformers accelerate gradio && \
python -m llava.serve.cli --model-path liuhaotian/llava-v1.5-7b --load-4bit
```

## Доступ к вашему сервису

После развертывания найдите ваш `http_pub` URL в **Моих заказах**:

1. Перейдите на **Моих заказах** страницу
2. Нажмите на ваш заказ
3. Найдите `http_pub` URL (например, `abc123.clorecloud.net`)

Используйте `https://YOUR_HTTP_PUB_URL` вместо `localhost` в примерах ниже.

## Установка

```bash
git clone https://github.com/haotian-liu/LLaVA.git
cd LLaVA
pip install -e .
pip install flash-attn --no-build-isolation
```

## Базовое использование

### Python API

```python
from llava.model.builder import load_pretrained_model
from llava.mm_utils import get_model_name_from_path
from llava.eval.run_llava import eval_model
from PIL import Image

model_path = "liuhaotian/llava-v1.5-7b"
tokenizer, model, image_processor, context_len = load_pretrained_model(
    model_path=model_path,
    model_base=None,
    model_name=get_model_name_from_path(model_path)
)

# Простой вывод (inference)
args = type('Args', (), {
    "model_path": model_path,
    "model_base": None,
    "model_name": get_model_name_from_path(model_path),
    "query": "Опишите это изображение подробно",
    "conv_mode": None,
    "image_file": "photo.jpg",
    "sep": ",",
    "temperature": 0.2,
    "top_p": None,
    "num_beams": 1,
    "max_new_tokens": 512
})()

output = eval_model(args)
print(output)
```

### Использование Transformers

```python
from transformers import LlavaNextProcessor, LlavaNextForConditionalGeneration
import torch
from PIL import Image

processor = LlavaNextProcessor.from_pretrained("llava-hf/llava-v1.6-mistral-7b-hf")
model = LlavaNextForConditionalGeneration.from_pretrained(
    "llava-hf/llava-v1.6-mistral-7b-hf",
    torch_dtype=torch.float16,
    device_map="auto"
)

# Загрузить изображение
image = Image.open("photo.jpg")

# Создать беседу
conversation = [
    {
        "role": "user",
        "content": [
            {"type": "image"},
            {"type": "text", "text": "Что показано на этом изображении?"}
        ]
    }
]

prompt = processor.apply_chat_template(conversation, add_generation_prompt=True)
inputs = processor(prompt, image, return_tensors="pt").to("cuda")

output = model.generate(**inputs, max_new_tokens=200)
response = processor.decode(output[0], skip_special_tokens=True)
print(response)
```

## Интеграция с Ollama (рекомендуется)

Самый простой способ запустить LLaVA на CLORE.AI:

```bash
# Установите Ollama
curl -fsSL https://ollama.com/install.sh | sh

# Загрузить модель LLaVA
ollama pull llava:7b

# Запуск с изображением (CLI)
ollama run llava:7b "Опишите это изображение: /path/to/image.jpg"
```

### API LLaVA через Ollama

{% hint style="warning" %}
**Важно:** Зрение LLaVA работает **только** через `/api/generate` эндпоинт с параметром `images` параметр. `/api/chat` и совместимые с OpenAI эндпоинты **не** поддерживают изображения с LLaVA.
{% endhint %}

#### Рабочий метод: /api/generate

```bash
# Сначала кодировать изображение в base64
BASE64_IMAGE=$(base64 -i photo.jpg | tr -d '\n')

# Отправить запрос по зрению
curl https://your-http-pub.clorecloud.net/api/generate -d "{
  \"model\": \"llava:7b\",
  \"prompt\": \"Что вы видите на этом изображении? Опишите подробно.\",
  \"images\": [\"$BASE64_IMAGE\"],
  \"stream\": false
}"
```

Ответ:

```json
{
  "model": "llava:7b",
  "response": "Изображение показывает прекрасный закат над горами...",
  "done": true
}
```

#### НЕ РАБОТАЕТ: /api/chat (возвращает null для зрения)

```bash
# Это НЕ работает для запросов по зрению:
curl https://your-http-pub.clorecloud.net/api/chat -d '{
  "model": "llava:7b",
  "messages": [{"role": "user", "content": "describe", "images": ["..."]}]
}'
# Возвращает null для ответов, связанных с изображением
```

### Python с Ollama

```python
import requests
import base64

def encode_image(image_path):
    with open(image_path, "rb") as f:
        return base64.b64encode(f.read()).decode()

# Для зрения используйте /api/generate (НЕ /api/chat!)
response = requests.post(
    "https://your-http-pub.clorecloud.net/api/generate",
    json={
        "model": "llava:7b",
        "prompt": "Что вы видите на этом изображении?",
        "images": [encode_image("photo.jpg")],
        "stream": False
    }
)

print(response.json()["response"])
```

### Полный рабочий пример

```python
import requests
import base64
import sys

def analyze_image(ollama_url, image_path, question):
    """Анализ изображения с помощью LLaVA через Ollama"""

    # Кодирование изображения
    with open(image_path, "rb") as f:
        image_base64 = base64.b64encode(f.read()).decode()

    # Используйте /api/generate (единственный рабочий эндпоинт для зрения)
    response = requests.post(
        f"{ollama_url}/api/generate",
        json={
            "model": "llava:7b",
            "prompt": question,
            "images": [image_base64],
            "stream": False
        }
    )

    return response.json()["response"]

# Пример использования
url = "https://your-http-pub.clorecloud.net"
result = analyze_image(url, "photo.jpg", "Опишите это изображение подробно")
print(result)
```

## Сценарии использования

### Описание изображения

```python
prompt = "Опишите это изображение подробно, включая цвета, объекты и атмосферу."
```

### OCR / Извлечение текста

```python
prompt = "Извлеките весь текст, видимый на этом изображении. Оформите его ясно."
```

### Анализ графиков

```python
prompt = "Проанализируйте этот график. Какие ключевые тенденции и выводы?"
```

### Код со скриншота

```python
prompt = "Извлеките код, показанный на этом скриншоте. Предоставьте только код."
```

### Обнаружение объектов

```python
prompt = "Перечислите все объекты, видимые на изображении, с их приблизительными местоположениями."
```

## Интерфейс Gradio

```python
import gradio as gr
from transformers import LlavaNextProcessor, LlavaNextForConditionalGeneration
import torch

processor = LlavaNextProcessor.from_pretrained("llava-hf/llava-v1.6-mistral-7b-hf")
model = LlavaNextForConditionalGeneration.from_pretrained(
    "llava-hf/llava-v1.6-mistral-7b-hf",
    torch_dtype=torch.float16,
    device_map="auto"
)

def analyze_image(image, question):
    conversation = [
        {
            "role": "user",
            "content": [
                {"type": "image"},
                {"type": "text", "text": question}
            ]
        }
    ]

    prompt = processor.apply_chat_template(conversation, add_generation_prompt=True)
    inputs = processor(prompt, image, return_tensors="pt").to("cuda")

    output = model.generate(**inputs, max_new_tokens=500)
    response = processor.decode(output[0], skip_special_tokens=True)

    # Извлечь ответ ассистента
    return response.split("[/INST]")[-1].strip()

demo = gr.Interface(
    fn=analyze_image,
    inputs=[
        gr.Image(type="pil", label="Изображение"),
        gr.Textbox(label="Вопрос", value="Опишите это изображение подробно")
    ],
    outputs=gr.Textbox(label="Ответ"),
    title="Ассистент зрения LLaVA"
)

demo.launch(server_name="0.0.0.0", server_port=8000)
```

## API-сервер

```python
from fastapi import FastAPI, UploadFile, File, Form
from transformers import LlavaNextProcessor, LlavaNextForConditionalGeneration
import torch
from PIL import Image
import io

app = FastAPI()

processor = LlavaNextProcessor.from_pretrained("llava-hf/llava-v1.6-mistral-7b-hf")
model = LlavaNextForConditionalGeneration.from_pretrained(
    "llava-hf/llava-v1.6-mistral-7b-hf",
    torch_dtype=torch.float16,
    device_map="auto"
)

@app.post("/analyze")
async def analyze(
    image: UploadFile = File(...),
    question: str = Form(default="Опишите это изображение")
):
    img = Image.open(io.BytesIO(await image.read()))

    conversation = [
        {
            "role": "user",
            "content": [
                {"type": "image"},
                {"type": "text", "text": question}
            ]
        }
    ]

    prompt = processor.apply_chat_template(conversation, add_generation_prompt=True)
    inputs = processor(prompt, img, return_tensors="pt").to("cuda")

    output = model.generate(**inputs, max_new_tokens=500)
    response = processor.decode(output[0], skip_special_tokens=True)

    return {"response": response.split("[/INST]")[-1].strip()}

# Запуск: uvicorn server:app --host 0.0.0.0 --port 8000
```

## Пакетная обработка

```python
from transformers import LlavaNextProcessor, LlavaNextForConditionalGeneration
import torch
from PIL import Image
import os

processor = LlavaNextProcessor.from_pretrained("llava-hf/llava-v1.6-mistral-7b-hf")
model = LlavaNextForConditionalGeneration.from_pretrained(
    "llava-hf/llava-v1.6-mistral-7b-hf",
    torch_dtype=torch.float16,
    device_map="auto"
)

def analyze_image(image_path, question):
    image = Image.open(image_path)

    conversation = [
        {"role": "user", "content": [
            {"type": "image"},
            {"type": "text", "text": question}
        ]}
    ]

    prompt = processor.apply_chat_template(conversation, add_generation_prompt=True)
    inputs = processor(prompt, image, return_tensors="pt").to("cuda")

    output = model.generate(**inputs, max_new_tokens=300)
    return processor.decode(output[0], skip_special_tokens=True).split("[/INST]")[-1].strip()

# Обработка папки с изображениями
image_folder = "./images"
results = []

for filename in os.listdir(image_folder):
    if filename.endswith(('.jpg', '.png', '.jpeg')):
        path = os.path.join(image_folder, filename)
        description = analyze_image(path, "Кратко опишите это изображение")
        results.append({"file": filename, "description": description})
        print(f"{filename}: {description[:100]}...")

# Сохранить результаты
import json
with open("descriptions.json", "w") as f:
    json.dump(results, f, indent=2)
```

## Оптимизация памяти

### Квантование 4-бит

```python
from transformers import BitsAndBytesConfig

quantization_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_compute_dtype=torch.float16
)

model = LlavaNextForConditionalGeneration.from_pretrained(
    "llava-hf/llava-v1.6-mistral-7b-hf",
    quantization_config=quantization_config,
    device_map="auto"
)
```

### Выгрузка на CPU (CPU Offload)

```python
model = LlavaNextForConditionalGeneration.from_pretrained(
    "llava-hf/llava-v1.6-mistral-7b-hf",
    torch_dtype=torch.float16,
    device_map="auto",
    offload_folder="offload"
)
```

## Производительность

| Модель        | GPU      | Токенов/с |
| ------------- | -------- | --------- |
| LLaVA-1.5-7B  | RTX 3090 | \~30      |
| LLaVA-1.5-7B  | RTX 4090 | \~45      |
| LLaVA-1.6-7B  | RTX 4090 | \~40      |
| LLaVA-1.5-13B | A100     | \~35      |

## Устранение неполадок

### Недостаточно памяти

```python

# Использовать 4-битную квантизацию

# Или использовать меньшую модель (7B вместо 13B)

# Или обрабатывать меньшие изображения
image = image.resize((336, 336))
```

### Медленная генерация

* Использовать flash attention
* Уменьшить max\_new\_tokens
* Использовать квантизированную модель

### Плохое качество

* Используйте модель побольше
* Лучшие подсказки с контекстом
* Изображения более высокого разрешения

## Оценка стоимости

Типичные ставки на маркетплейсе CLORE.AI (по состоянию на 2024):

| GPU       | Почасовая ставка | Дневная ставка | Сессия 4 часа |
| --------- | ---------------- | -------------- | ------------- |
| RTX 3060  | \~$0.03          | \~$0.70        | \~$0.12       |
| RTX 3090  | \~$0.06          | \~$1.50        | \~$0.25       |
| RTX 4090  | \~$0.10          | \~$2.30        | \~$0.40       |
| A100 40GB | \~$0.17          | \~$4.00        | \~$0.70       |
| A100 80GB | \~$0.25          | \~$6.00        | \~$1.00       |

*Цены варьируются в зависимости от провайдера и спроса. Проверьте* [*CLORE.AI Marketplace*](https://clore.ai/marketplace) *для текущих тарифов.*

**Экономьте деньги:**

* Используйте **Spot** рынок для гибких рабочих нагрузок (часто на 30–50% дешевле)
* Платите с помощью **CLORE** токенов
* Сравнивайте цены у разных провайдеров

## Дальнейшие шаги

* LLMы Ollama — Запуск LLaVA с Ollama
* RAG + LangChain — Зрение + RAG
* vLLM Inference - Продакшен-сервинг


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.clore.ai/guides/guides_v2-ru/modeli-zreniya/llava-vision-language.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
