Strange Rin
34 supporters
Векторні бази даних для самих маленьких ...

Векторні бази даних для самих маленьких - ч2: Electric Boogaloo

Feb 01, 2024

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

Давайте спробуємо скласти із SDK Weaviate, JS простенький процес роботи із векторною базою.

  • Розгорнемо локально Docker контейнер з локальним інстансом, власне, бази

  • Подивимось як до неї підключитись

  • Як створити колекцію для робочих сутностей

  • Як записати векторну репрезентацію сутності в БДʼ

  • Як проходить пошук

Все це буде у якості покрокової інструкції з картиночками, плюс буде репозиторій зі скриптами, тому дуже раджу спробувати повторювати етапи під час прочитання, особливого розуміння технічних деталей від вас не потребуватиме.

Перш за все давайте спробуємо скласти в голові історію з ембеддінгами і векторним простором, яким власне і є векторна БД.

Уявіть собі сферу, у сфери є уявний центр по осі Х та У, від цього центру по всій площі, і різноманітних напрямках розходяться "промені", власне вектори, уявний набір координат, напрямку всередині цієї абстрактної сфери.

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

Відтак, коли ми робимо запит і векторизуємо його, ми по-суті створюємо новий "промінь", і шукаємо його найближчих сусідів, чи навіть співпадіння. Тому пошук умовного кошеняти виведе вас до частини бази, де вже зберігаються векторизовані тварини, а не овочевий кошик із "Сільпо".

ВАЖЛИВО

Чому ж ми не використовуємо векторні БД в якості основних, якщо вони швидкі, скалюються і по-суті, можуть приймати на вхід human-readable штуки?

Векторні БД паршиво підходять в якості primary data source ("source of truth"), через свою природу. Це стохастична система, як і все що має під капотом нейронку, вона працює з вірогідностями на фундаментальному рівні. Тобто однаковий запит з однаковими вхідними даними НЕ означає однаковий результат щоразу.

Векторні БД не забезпечують data consistency як трансакційні БД, але добре підходять для пошукових систем - семантичних і мішаних (семантика та статистика разом, про це в наступній частині коротко), чи рекомендаційних систем. Тобто використовувати як шар, чи сервіс - так, інакше - ні.

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

I have become a Hello Kitty, creator of Cringe

Ну шо ж, настав час щось поскладати - і почати треба з установки деякого програмного забезпечення.

  • Docker Desktop, або ваш улюблений присмак Докеру - тут і далі ми будемо використовувати його плюс-мінус постійно. Можете повірити мені на слово, без софту для віртуалізації працювати відносно складно, особливо зі всім дотичним до машинного навчання. Набори залежностей інколи абсолютно безумні, і можуть викликати геморой, понос, золотуху, чуму і сифіліс. Водночас! Вам не потрібно розуміти чим віртуалізація в контейнерах відрізняється від повноцінних віртуальних машин. Так, я знаю, шо у вас ж venv, poetry, flavor of the month Conda env. Просто поставте Докер.

  • Для сьогоднішнього експерименту потрібен буде NPM, якщо не розбираєтесь який ставити - ставте просто останній, для наших цілей заведеться. Це дасть вам рантайм JS/Node і менеджер пакетів, і те, і те нам потрібне буде для роботи.

  • Працювати будемо із векторною БД Weaviate, тому переходимо на їхній сайт з докою і тримаємо його відкритим фоном весь час.
    Link:
    https://weaviate.io/developers/weaviate/client-libraries/typescript#installation-and-setup

  • Також на їхньому сайті вас цікавить їх конфігуратор Докер імеджів, який простим візардом дасть вам накидати те що вам потрібно. How to install -> Docker Compose -> Configurator

  • Далі залишаємо опції переважно дефолтні, крім тих, які будуть вказані інакше. Перше що ми обираємо не за замовчуванням - включити модулі. Використовувати Weaviate для будь-яких абстрактних векторів - потужно, але абсолютно не потрібно нам зараз.

  • Далі обираємо тип даних - image, те що потрібно нам під наш юзкейс. Загалом всі основні вендори дозволяють векторизувати абсолютно різні типи даних, і навіть їм змішувати - текст та зображення, сенсорні дані та відео-аудіо потоки, тощо. Все впирається у фантазію та бізнес логіку певну.

  • Наступний пункт буде з невеликою розвилочкою. Якщо ваша машина має відеокарту від nVidia на борту, яка підтримує CUDA - обирайте перший варіант. Якщо такого нема - тоді другий, для наших тестів різниця у швидкодії буде зовсім несуттєвою.

  • Далі цілий пакован інтеграцій з різноманітними сервісами, ми це робити поки не будемо, але уявіть наприклад що можна було б зкормити результат вашого пошуку умовній GPT-4 і згенерувати на її основі щось цікаве. Інтеграція буде зовсім тривіальна з інженерної точки зору, ще один SDK, і клієнт з ключем, але в якості вправи - дуже раджу.

  • Далі ви отримаєте curl команду з вашим контейнером, скопіюйте її і виконайте у терміналі вашої IDE чи системному терміналі, по аналогії з тим як ставили пакет на NPM

  • Після введіть команду
    docker-compose up -d
    (d - detach, щоб контейнер почав крутитись фоном, а не залочив вам вікно вашого терміналу)
    І ваш екземпляр БД буде крутитись за адресою localhost:8080

  • Вимикаємо відповідно командою
    docker-compose down

I HATE JAVASCRIPT I HATE JAVASCRIPT

Ну шо, настав час накидати ваш офігенний скрипт, на щастя для нас всіх, він простий як двері, а я бекендер і МЛ-мавпа, тому ніяка шизофазія нам (поки) не грозить.

Одразу посилання на репо, якщо вам буде дуже ліниво набирати щось руками, просто позапускайте степи один за одним, по порядку. Так як це демо скрипт, щоб показати як воно працює, закоментовуйте частини із створенням колекції і наповненням БД після першого відпрацювання.

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

Репо:

https://github.com/strangerin/ariadna/tree/development

Проєкти з нормальною структурою теж є, якщо комусь цікаво більше пітоняча сторона речей, полуркайте по репо.

Не відволікаючись особливо далі:

Ініціалізуємо проєкт

npm init -y

І встановлюємо необхідний SDK

npm i weaviate-ts-client

Трішки поганого коду і для чого він треба

Далі коротко і тезово - для роботи з векторною базою потрібно оголосити schema нашої колекції даних.

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

У випадку з текстовими векторами, я б використав умовну cosine similarity, тут інший алгоритм, для зображень -hnsw.

const schemaCfg = {
    // generic name
    'class': 'Image',
    // declared in docker compose, self-explanatory
    'vectorizer': 'img2vec-neural',
    // https://arxiv.org/abs/1603.09320 - used search algorithm
    'vectorIndexType': 'hnsw',
    'moduleConfig': {
        'img2vec-neural': {
            'imageFields': [
                'image'
            ]
        }
    },
    'properties': [
        {
            // primary data description
            'name': 'image',
            'dataType': ['blob']
        },
        {
            // aux data description
            'name': 'text',
            'dataType': ['string']
        }
    ]

Власне робота з базою не відрізняється нічим від роботи зі звичайним БД через умовну ORM. Створюємо екземпляр клієнту з яким і будемо взаємодіяти надалі, в сигнатуру виклику засовуємо, в нашому випадку, дані для локальної роботи.

const client = weaviate.client({
    scheme: 'http',
    host: 'localhost:8080',
});

Далі все можна розділити на три умовні етапи, теж нічим незвичним не відрізняються

  1. Створюємо колекцію в БД за нашою схемою, де schemaCfg власне конфіг який ми вже створили.

await client.schema
    .classCreator()
    .withClass(schemaCfg)
    .do();


Перевірити роботу можна викликом власне схеми на вашій локальній БД

const schemaQry = await client.schema.getter().do();
console.log(schemaQry);
  1. Завантажуємо контент у векторну БД, кастимо всі сутності у base64 та передаємо нашому обєкту клієнта weaviate, де той вже формує із них внутрішню image сутність використовуючи модель для векторизації та інші супутні налаштування які ми вказали у конфіг файлі.

    По своїй природі операція доволі важка, тому займе найдовше у процесі. Ви не хочете це робити на кожному умовному імпорті даних, тому задумайтесь про систему контролю версій даних, кешування, тощо на живому проєкті з будь-якою подібною системою.


    const files = readdirSync(imgFolder); const batchProcess = files.map(async (imgFile) => { // Convert the image content to base64 const b64 = toBase64(join(imgFolder, imgFile)); // Create Image instance in collection for each image await client.data.creator() .withClassName('Image') .withProperties({ image: b64, text: imgFile.split('.')[0], }) .do(); }); await Promise.all(batchProcess);

  2. Для пошуку той же процес у мініатюрі, векторизуємо нашу картинку на вхід, і в випадку демо - записуємо новим зображенням найбільш підходящу з картинок у БД, для простоти повертаємо 1, але в живих системах часто повертаємо Н найближчих співпадінь, потім можливо їх треба буде ранжирувати наново за допомогою іншого алгоритму.

    const test = Buffer.from( readFileSync('./deathwatch_meme_test.jpeg') ).toString('base64'); const resImage = await client.graphql.get() .withClassName('Image') .withFields(['image']) .withNearImage({ image: test }) .withLimit(1) .do(); // Write result to filesystem const result = resImage.data.Get.Image[0].image; writeFileSync('./result.jpg', result, 'base64');


Тепер хвилинка смішнявок - зображення яке лежало в базі та зображення яке ми задали в якості пошукового запиту

І головний прикол - це далеко не єдина картинка з космодесантником, чи навіть космодесантником Караулу Смерті, яку ми векторизували, якщо цікаво - гляньте у відповідній папці в репо.

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

Останні, одначе теж мають місце бути і нікуди від вас не дінуться.

Загалом - це лише коротка і проста демонстрація, за бажання, ви можете створити простий інтерфейс, зробити сепарацію функціоналу, чи просто перевикористати ідею десь у себе.

Gefällt dir dieser Beitrag?

Kaufe Strange Rin einen Token

1 Kommentar

Mehr von Strange Rin