Перейти до основного вмісту

Запустіть VLM за допомогою Direct API

ПолеЗначення
КатегоріяGenAI
СкладністьСередній
Орієнтовний час читання10-15 minutes
Міткиgenai, vlm, image, cache, multimodal

Моделі, що поєднують обробку зображень і тексту, можуть приймати текст разом із тензорами зображень. Для одного запиту додайте зображення безпосередньо до GenerationRequest.images. Для повторюваних запитів, закодуйте зображення один раз і повторно використовуйте кешовані вкладення зображень у наступних запитах.

Покроковий огляд

Завантаження VLM та зображення

Завантажте VisionLanguageModel з розгорнутої директорії моделі LLiMa і декодуйте зображення з диска.

Використовуйте OpenCV для читання зображення. Neat розглядає триканальні вхідні дані cv::Mat як BGR і внутрішньо перетворює їх на RGB.

tutorials/020_run_a_vlm/run_a_vlm.cpp
genai::VisionLanguageModel model(args.model);
cv::Mat image = cv::imread(args.image.string(), cv::IMREAD_COLOR);
if (image.empty()) {
throw std::runtime_error("failed to read image: " + args.image.string());
}

Запит із прямим зображенням

Додайте зображення безпосередньо до першого запиту. Це найпростіший спосіб, і часто його достатньо для одноразових візуальних запитів.

tutorials/020_run_a_vlm/run_a_vlm.cpp
genai::GenerationRequest direct;
direct.prompt = "Describe this image in one sentence.";
direct.images = {image};
direct.max_new_tokens = 96;

const genai::GenerationResult first = model.run(direct);
std::cout << "direct image: " << first.text << "\n\n";

Кешування вкладення зображення

Викличте encode(...), щоб кешувати вкладення зображень у моделі. Виклик повертає true, коли зображення було прийнято та кешовано.

tutorials/020_run_a_vlm/run_a_vlm.cpp
if (!model.encode(image)) {
throw std::runtime_error("VLM did not accept the image for caching");
}
std::cout << "cached_images=" << model.cached_image_count() << "\n";

Задавайте додаткові запитання

У кожному запиті, який має повторно використовувати кешоване зображення, встановіть значення use_cached_images = true. Ви можете задавати кілька запитань щодо одного й того ж кешованого зображення. Запити без цього прапора працюють у звичайному режимі: запити, що містять лише текст, не використовують зображення, запити, що містять пряме зображення, використовують власні images, а інший виклик encode(...) замінює кешоване зображення.

tutorials/020_run_a_vlm/run_a_vlm.cpp
genai::GenerationRequest cached;
cached.prompt = "What details should I inspect more closely?";
cached.use_cached_images = true;
cached.max_new_tokens = 96;

const genai::GenerationResult follow_up = model.run(cached);
std::cout << "cached image: " << follow_up.text << "\n\n";

genai::GenerationRequest second_cached;
second_cached.prompt = "Summarize the image in three keywords.";
second_cached.use_cached_images = true;
second_cached.max_new_tokens = 48;

const genai::GenerationResult second_follow_up = model.run(second_cached);
std::cout << "cached image keywords: " << second_follow_up.text << "\n\n";

Додайте зображення до повідомлення в чаті

Під час використання messages додавайте зображення до повідомлення користувача, якому вони потрібні. Це дозволить розмістити зображення поруч із відповідним текстом.

tutorials/020_run_a_vlm/run_a_vlm.cpp
genai::ChatMessage image_message;
image_message.role = "user";
image_message.content = "What is the main subject of this image?";
image_message.images = {image};

genai::GenerationRequest message_request;
message_request.messages = {image_message};
message_request.max_new_tokens = 96;

const genai::GenerationResult message_result = model.run(message_request);
std::cout << "message image: " << message_result.text << "\n";

Запуск

На Modalix DevKit завантажте VLM LFM2-VL 1.6B з Hugging Face за допомогою CLI LLiMa:

llima pull LFM2-VL-1.6B-a16w4

Запустіть навчальний посібник на Modalix з локальною директорією моделі DevKit і локальним зображенням:

C++ (prebuilt):

./lib/sima-neat/tutorials/tutorial_020_run_a_vlm \
--model /media/nvme/llima/models/LFM2-VL-1.6B-a16w4 \
--image tests/images/people.jpg

C++ (build from source):

./build.sh --target tutorial_020_run_a_vlm
./build/tutorials-standalone/tutorial_020_run_a_vlm \
--model /media/nvme/llima/models/LFM2-VL-1.6B-a16w4 \
--image tests/images/people.jpg

Очікуваний результат: одна відповідь на запит із прямим зображенням, кілька додаткових відповідей, які повторно використовують кешоване зображення, і одна відповідь на запит із зображенням на рівні повідомлення.

На практиці

Використовуйте кешування зображень, коли користувач задає кілька запитань щодо одного й того ж кадру, зображення продукту, діаграми або сторінки документа. Уникайте кешування, коли кожен запит використовує інше зображення, оскільки прямий шлях до зображення простіший і зберігає стан запиту в наочній формі.

Деякі моделі можуть не підтримувати повторне використання кешованих зображень. У такому разі використовуйте прямі зображення для кожного запиту.

Використовуйте ChatMessage.images, коли ви створюєте розмову, і лише одне повідомлення повинно містити зображення. Використовуйте GenerationRequest.images для простішої форми, що містить лише один запит.

Повний початковий код

Показати повні програми
tutorials/020_run_a_vlm/run_a_vlm.cpp
#include "neat/genai.h"

#include <opencv2/imgcodecs.hpp>

#include <filesystem>
#include <iostream>
#include <stdexcept>
#include <string>

namespace genai = simaai::neat::genai;

struct Args {
std::filesystem::path model;
std::filesystem::path image;
};

Args parse_args(int argc, char** argv) {
Args args;
for (int i = 1; i < argc; ++i) {
const std::string arg = argv[i];
if (arg == "--model" && i + 1 < argc) {
args.model = argv[++i];
} else if (arg == "--image" && i + 1 < argc) {
args.image = argv[++i];
} else {
throw std::runtime_error("usage: run_a_vlm --model <vlm_model_dir> --image <image>");
}
}
if (args.model.empty() || args.image.empty()) {
throw std::runtime_error("missing required --model <vlm_model_dir> or --image <image>");
}
return args;
}

int main(int argc, char** argv) {
try {
const Args args = parse_args(argc, argv);

genai::VisionLanguageModel model(args.model);
cv::Mat image = cv::imread(args.image.string(), cv::IMREAD_COLOR);
if (image.empty()) {
throw std::runtime_error("failed to read image: " + args.image.string());
}

genai::GenerationRequest direct;
direct.prompt = "Describe this image in one sentence.";
direct.images = {image};
direct.max_new_tokens = 96;

const genai::GenerationResult first = model.run(direct);
std::cout << "direct image: " << first.text << "\n\n";

if (!model.encode(image)) {
throw std::runtime_error("VLM did not accept the image for caching");
}
std::cout << "cached_images=" << model.cached_image_count() << "\n";

genai::GenerationRequest cached;
cached.prompt = "What details should I inspect more closely?";
cached.use_cached_images = true;
cached.max_new_tokens = 96;

const genai::GenerationResult follow_up = model.run(cached);
std::cout << "cached image: " << follow_up.text << "\n\n";

genai::GenerationRequest second_cached;
second_cached.prompt = "Summarize the image in three keywords.";
second_cached.use_cached_images = true;
second_cached.max_new_tokens = 48;

const genai::GenerationResult second_follow_up = model.run(second_cached);
std::cout << "cached image keywords: " << second_follow_up.text << "\n\n";

genai::ChatMessage image_message;
image_message.role = "user";
image_message.content = "What is the main subject of this image?";
image_message.images = {image};

genai::GenerationRequest message_request;
message_request.messages = {image_message};
message_request.max_new_tokens = 96;

const genai::GenerationResult message_result = model.run(message_request);
std::cout << "message image: " << message_result.text << "\n";

return 0;
} catch (const std::exception& e) {
std::cerr << "error: " << e.what() << "\n";
return 1;
}
}

Джерело