Търсене във видео с Cosmos-Embed1 на GX10
Търсим в колекция от кратки клипове с обикновено изречение на английски, например „a car overtaking a white truck“, без етикети по файловете. Моделът Cosmos-Embed1 на NVIDIA превръща и клипове, и текст във вектори в общо пространство, а локална векторна база (Qdrant) намира най-близките клипове.
qdrant/qdrant:latest и публикуването на портове към цялата мрежа, както и вътрешните адреси и пътища. Добавихме: Cosmos-Embed1 — модел на NVIDIA точно за търсене текст↔видео (общо пространство, 8 кадъра на клип), закачена версия на Qdrant (образ за amd64 и arm64, проверен в Docker Hub на 03.10.2026), нов програмен интерфейс query_points, предупреждение за trust_remote_code, лицензът на NVIDIA накратко и кутия за личните данни. Не даваме числа за скорост и размер на колекцията — не сме ги мерили.01Какво ще научиш
- Какво е вектор за клип (embedding) и защо текст и видео трябва да са в едно пространство.
- Как да пуснеш Qdrant в Docker така, че да не е видим за мрежата.
- Как да вземеш вектор от клип и от изречение с Cosmos-Embed1.
- Как да индексираш клипове и да търсиш с текст.
- Как да събереш набор за преглед от резултатите на няколко заявки.
- Какво разрешава лицензът на NVIDIA и кога видеото е лични данни.
02Преди да започнеш
- Машина от класа NVIDIA GB10 (например ASUS Ascent GX10 или DGX Spark) с DGX OS; Docker, настроен по първото включване.
- Python 3.10 или по-нов и PyTorch, който работи на машината — виж урока PyTorch на GB10. Това не сме го пускали тук.
- Акаунт в Hugging Face (моделът се тегли оттам) и интернет за първото изтегляне.
- Папка с кратки клипове, които имаш право да обработваш. За тренировка са достатъчни 20–50 клипа.
- Заявките се пишат на английски, като кратки описания на действие или сцена.
| Вариант | Вход | Дължина на вектора |
|---|---|---|
| Cosmos-Embed1-224p | 8 кадъра · 224×224 | 256 |
| Cosmos-Embed1-336p | 8 кадъра · 336×336 | 768 |
| Cosmos-Embed1-448p | 8 кадъра · 448×448 | 768 |
03Стъпки
-
Схемата
Всеки клип → 8 равномерно избрани кадъра → вектор от Cosmos-Embed1 → Qdrant. Заявката (изречение) → вектор от същия модел → Qdrant връща най-близките клипове по косинусово сходство. Защо едно пространство? Само когато текстът и видеото са научени заедно, близост на вектори означава близост на смисъл.
-
Qdrant в Docker — затворен към мрежата
Закачената версия е
v1.19.1; образът е за amd64 и arm64 (Docker Hub, 03.10.2026). Портът е публикуван само към127.0.0.1, защото Qdrant по подразбиране е без удостоверяване. Данните са в именуван том. gRPC портът (6334) не се публикува.bashdocker run -d --name qdrant --restart unless-stopped \ -p 127.0.0.1:6333:6333 \ -v qdrant_storage:/qdrant/storage \ qdrant/qdrant:v1.19.1 curl -s http://localhost:6333/Очакваш кратък отговор с името и версията. От друг компютър стигаш до Qdrant през SSH тунел (като в урока за n8n):
ssh -L 6333:localhost:6333 <потребител>@<адрес-на-машината>. -
Среда за Python
Нужни са
transformers,einops, OpenCV (само за четене на кадри) и клиентът на Qdrant. PyTorch се слага по начина, който работи на твоята машина (урок 04-21) — не сме го пускали.bashpython3 -m venv .venv source .venv/bin/activate pip install transformers einops opencv-python-headless qdrant-client==1.19.1 -
Вектор от клип и от изречение
Кодът следва примера в картата на модела. Закачи ревизия (
revision) на хранилището:trust_remote_code=Trueизпълнява код от хранилището на модела — прегледай го и фиксирай конкретен commit, който си прегледал. Векторите са нормализирани; дължината им (256 или 768) се взема от резултата, а не се пише на ръка.python · embed.pyimport cv2, numpy as np, torch from transformers import AutoModel, AutoProcessor MODEL = "nvidia/Cosmos-Embed1-448p" REV = "<commit>" # закачи прегледан commit model = AutoModel.from_pretrained(MODEL, trust_remote_code=True, revision=REV) model = model.to("cuda", dtype=torch.bfloat16).eval() proc = AutoProcessor.from_pretrained(MODEL, trust_remote_code=True, revision=REV) def read_frames(path, n=8): cap = cv2.VideoCapture(path) total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) frames = [] for i in np.linspace(0, total - 1, n, dtype=int): cap.set(cv2.CAP_PROP_POS_FRAMES, int(i)) ok, f = cap.read() if not ok: cap.release() return None frames.append(cv2.cvtColor(f, cv2.COLOR_BGR2RGB)) cap.release() return np.stack(frames) # (T, H, W, C), uint8 @torch.no_grad() def video_vec(path): fr = read_frames(path) if fr is None: return None batch = np.transpose(fr[None], (0, 1, 4, 2, 3)) # (B, T, C, H, W) inp = proc(videos=batch).to("cuda", dtype=torch.bfloat16) return model.get_video_embeddings(**inp).visual_proj[0].float().cpu().numpy() @torch.no_grad() def text_vec(query): inp = proc(text=[query]).to("cuda", dtype=torch.bfloat16) return model.get_text_embeddings(**inp).text_proj[0].float().cpu().numpy()Първото изпълнение тегли модела от Hugging Face. Ако страницата на модела иска съгласие с условията, приеми ги с твоя акаунт (
hf auth login— токенът не се пише в скриптове). -
Индексиране в Qdrant
В полезния товар (payload) пазим относително име на файла и по желание етикети — не пълни пътища и не лични данни.
python · index.pyfrom pathlib import Path from qdrant_client import QdrantClient from qdrant_client.models import Distance, VectorParams, PointStruct from embed import video_vec client = QdrantClient(url="http://localhost:6333") NAME = "clips" CLIPS = Path("clips") paths = sorted(CLIPS.glob("**/*.mp4")) first = next(v for v in (video_vec(str(p)) for p in paths) if v is not None) if not client.collection_exists(NAME): client.create_collection( collection_name=NAME, vectors_config=VectorParams(size=len(first), distance=Distance.COSINE), ) points = [] for i, p in enumerate(paths): v = video_vec(str(p)) if v is None: print("пропуснат:", p.name) continue points.append(PointStruct(id=i, vector=v.tolist(), payload={"file": str(p.relative_to(CLIPS))})) client.upsert(collection_name=NAME, points=points) print("точки:", client.count(collection_name=NAME).count)За големи колекции вмъквай на партиди (например по няколкостотин точки) — оптималният размер не сме го мерили.
-
Търсене с текст
Заявката е кратко описание на действие или сцена на английски — моделът е обучен предимно с такива надписи (картата на модела предупреждава, че други формулировки може да съвпадат лошо). Оценката (
score) е косинусово сходство; не слагай фиксиран праг — избери го по свои примери.python · search.pyfrom qdrant_client import QdrantClient from embed import text_vec client = QdrantClient(url="http://localhost:6333") def search(query, k=10): res = client.query_points( collection_name="clips", query=text_vec(query).tolist(), limit=k, with_payload=True, ) return [(h.score, h.payload["file"]) for h in res.points] for score, name in search("a car overtaking a white truck"): print(round(score, 3), name)Търсене „видео към видео“: вземи вектора на един клип (
video_vec) и го подай катоquery— така намираш подобни клипове. -
Набор за преглед от няколко заявки
За да съберем набор от различни сцени, пускаме няколко заявки и записваме най-добрите резултати в списък. Човек преглежда списъка — векторното търсене подрежда, но не гарантира, че клипът е точно това, което искаш.
python · manifest.pyimport json from search import search QUERIES = { "overtaking": "a car overtaking a white truck", "night_road": "a vehicle driving on a wet road at night", "crossing": "a vehicle slowing down at a pedestrian crossing", } manifest = {name: [f for _, f in search(q, k=50)] for name, q in QUERIES.items()} with open("manifest_for_review.json", "w", encoding="utf-8") as f: json.dump(manifest, f, ensure_ascii=False, indent=2) -
Как да знаеш, че работи
Направи си малък контролен набор: 20 клипа, за които знаеш какво показват, и 5 заявки с очаквани резултати. Виж в кои позиции излизат очакваните клипове (например дали са в първите 5). Само така ще разбереш дали 224p, 336p или 448p ти е достатъчен — в картата на NVIDIA има резултати върху свои набори, но не върху твоите данни.
🌐Пълният референтен проект на NVIDIANVIDIA публикува и цялостен проект „Cosmos Dataset Search“: услуга Cosmos-embed (NIM), векторна база Milvus с ускорение cuVS, S3-съвместимо хранилище, уеб интерфейс и Docker Compose или Helm. Не сме го пускали и не знаем изискванията му към GB10 — виж документацията в хранилището му. Уроците тук ползват по-малкия път: един модел и Qdrant.
04Проверка
curlкъмlocalhost:6333отговаря, а портът не е публикуван към други интерфейси.- За един клип получаваш вектор; дължината му е размерът на колекцията.
- Индексирани са поне 20 клипа, а в payload има само относителни имена.
- Заявка с кратка английска фраза връща очакваните клипове в началото.
- Резултатите за няколко заявки са прегледани от човек и сверени с контролен набор.
- Прочел си лиценза и си решил въпроса с личните данни преди реални записи.
Тест
1. Защо в урока няма отделен текстов енкодер (CLIP) за заявките?
2. Как е публикуван Qdrant в стъпка 2 и защо?
3. Какво изисква лицензът на NVIDIA при разпространение на продукт, изграден върху модел на Cosmos?
4. На какъв език пишеш заявките за най-добър резултат?
05Какво следва
06Източници
- Cosmos-Embed1-448p — картата на модела: варианти, вход и изход, лиценз, ограничения.
- NVIDIA Open Model License — пълният текст на лиценза.
- Cosmos Dataset Search (GitHub) — референтният проект на NVIDIA.
- Cosmos Dataset Search (NVIDIA) — страницата на проекта.
- Qdrant в Docker Hub — тагове и архитектури (arm64).
- qdrant-client (PyPI) — версия на клиента.
- Qdrant — документация.
- Regulation (EU) 2016/679 (GDPR) — Регламент за защита на личните данни.