Zamień ten tekst na URL Webhooka

Full-stack i Programowanie

Eksperymenty z GPT-3 i OpenAI API i Node.js

Wydanie nr
20
opublikowane
2023-01-13
Daniel Noworyta
Daniel Noworyta
Full stack Developer
No items found.

Cześć,

bardzo się cieszę, że nadszedł w końcu dzień w którym powracamy do regularnej wysyłki newsletterów branżowych. Wydanie, które właśnie czytasz, powiązane jest ze ścieżką "Full-stack i Programowanie". Znajdziesz w nim dłuższą wiadomość na wybrany temat oraz powiązane z nim linki. Motywem przewodnim będą nowości z branży, najnowsze technologie, frameworki, narzędzia i techniki pracy. Moim celem jest projektowanie wydań w taki sposób, aby ułatwić Ci pozostawanie na bieżąco. Pamiętaj, że zawsze chętnie wysłucham Twoich opinii. Jeżeli wpadniesz na pomysły, które mogą mi pomóc w prowadzeniu tego newslettera, będę Ci za nie niezwykle wdzięczny. 

Jak nie trudno było zauważyć, w ciągu ostatnich dni eksplorowałem możliwości GPT-3 tworząc "overmentAI" — projekt umożliwiający zadanie mi pytania i otrzymanie wygenerowanej odpowiedzi w oparciu o moje publikacje. W tym wydaniu pokażę jak to wyglądało "od drugiej strony" i podzielę się moimi wnioskami i przemyśleniami.


Eksperyment

overmentAI to eksperyment, który zrealizowałem po to aby lepiej zrozumieć działanie GPT-3. Zależało mi na tym aby wyjść poza "wykorzystanie tego, co już jest" w formie ChatGPT a konkretnie, jak mogę wykorzystać własne dane. Początkowo cała terminologia była dla mnie tak złożona (np. embedding, wektory, prompt engineering czy nieznane mi narzędzia), że zacząłem wątpić czy uda mi się to wszystko ogarnąć.

Podjąłem próbę trenowania własnego modelu wykorzystując fine-tuning. Szybko jednak okazało się, że potrzebuję ogromnych zestawów danych aby miało to jakikolwiek sens. Potrzebowałem czegoś, co odpowie na moje potrzeby ale nie będzie wymagało inwestycji czasowej.

Wtedy wpadłem na dwa projekty: https://huberman.rile.yt oraz https://all-in-on-ai.vercel.app, które wykorzystały istniejące dane aby stworzyć coś w rodzaju wyszukiwarki. Pomyślałem, że fajnie byłoby zrobić coś więcej niż samą wyszukiwarkę — chat umożliwiający rozmowę ze mną. Wtedy trafiłem na wpis o tworzeniu chatu na Discord, który zainspirował mnie do przygotowania warsztatu na eduweb ale z wykorzystaniem node.js. W tym momencie dowiedziałem się już czym jest embedding i jak wykorzystać wektorową bazę do budowania kontekstu dla zapytania (zaraz wyjaśnię).

W GPT-3 najważniejsze są dane

Uczący się chatbot brzmi interesująco. Ponownie jednak nie miałem możliwości poświęcać czasu na uczenie go wszystkiego od podstaw, tym bardziej że brain.overment.com i dziesiątki napisanych przeze mnie artykułów i newsletterów jasno sugerują, że mam już dane z którymi mogę pracować. Pozostało pytanie: w jaki sposób wykorzystać je z openAI?

Okazało się, że dosłownie wszystkie pisane przeze mnie publikacje tworzyłem w iA Writer. W większości z nich miałem nawet tagi, które pozwoliły mi szybko odnaleźć treści, które publikowałem (w dodatku w składni markdown). Od razu wpadły mi do głowy pomysły jak mogę wykorzystać GPT-3 aby je skategoryzować i przygotować bardzo solidną bazę. Ponownie jednak na tym etapie wydawało się to zbyt czasochłonne a nadal nie wiedziałem czy ma to jakikolwiek sens. Dlatego przygotowałem bardzo prosty skrypt, który podzieli wskazany przeze mnie plik na mniejsze części. Zamiast pisać taką funkcję samodzielnie, poprosiłem ChatGPT o napisanie jej dla mnie:

Powyższy kod w bardzo mało inteligentny sposób dzieli tekst z jednego pliku na akapity i każdy fragment wrzuca do drugiego pliku w odpowiednim formacie. Na tej podstawie otrzymałem bardzo długi zestaw danych, który bot mógł przeczytać.

Podział w tym przypadku jest o tyle istotny, że zapytanie wysyłane do GPT-3 ma ograniczoną liczbę znaków. Podział na małe fragmenty pozwala mi na późniejsze wykorzystywanie ich do budowania kontekstu (za chwilę stanie się to jasne).

Na tym etapie bardzo jasne okazało się to, że w pracy z AI istotne są nie tylko dane ale także sposób ich organizacji, tagowania i kategoryzacji. Jest to bardzo czasochłonne zajęcie i łatwo popełnić błąd, który później dość trudno naprawić i który może doprowadzać do zwracania całkowicie błędnych wyników.

Prompt Design

Użytkownik może zadawać pytania i powinien otrzymać sensowną odpowiedź zbudowaną wyłącznie na podstawie publikowanych przeze mnie treści. Dlatego musiałem przygotować odpowiedni prompt, który połączy trzy rzeczy: instrukcję, kontekst i pytanie użytkownika.

Wymyśliłem coś takiego:

_Instrukcja #1: "..."
Kontekst: "..." (budowany na podstawie danych)
Instrukcja #2: "..."
Pytanie: "" (wprowadzany przez użytkownika)
Odpowiedź: (generowana przez GPT-3)_

Przykład:

_Na podstawie poniższego kontekstu, zrealizuj instrukcję.

Kontekst: "Adam lubi słuchać muzyki."

Wykorzystując tylko i wyłącznie powyższy kontekst odpowiedz wyczerpująco na poniższe pytanie tak szczerze jak to możliwe, mówiąc jak do przyjaciela, zwracając się bezpośrednio. Nie dodawaj żadnych informacji nie uwzględnionych w podanym kontekście. Napisz odpowiedź poprawnie gramatycznie i czytelnie. Jeżeli odpowiedź nie znajduje się bezpośrednio w kontekście lub nie zadano pytania, napisz "Nie potrafię się do tego odnieść." Użytkownik nie widzi kontekstu. Jeżeli możesz, dodaj obrazki i linki w formacie HTML. Odpowiedź zwróć w formacie HTML i popraw składnię i czytelność tekstu, uwzględniając odpowiednie formatowanie. Nie dodawaj żadnych informacji, co nie znajduje się w kontekście tylko odpowiedz "Nie wiem".

Pytanie: "Kto lubi muzykę?"

Odpowiedź: "Adam."_

Oczywiście prompt można dowolnie modyfikować a nawet wykorzystywać GPT-3 aby go optymalizować. Jednak widać tutaj jasno koncepcję o którą mi chodziło. Zależało mi na tym aby bot odpowiadał wykorzystując wyłącznie moje dane. Teraz wystarczyło je dostarczyć.

Pinecone i embedding

To najbardziej technicznie złożone zadanie, które w pierwszej chwili było trudno zrozumieć. W uproszczeniu chodzi o to aby zamieniać zebrane przeze mnie dane na tzw. wektory. Coś takiego można zrobić z pomocą openAI a konkretnie modelu text-embedding-ada-002. Wygenerowane wektory muszą trafić do wektorowej bazy (np. Pinecone) i to właśnie tam ma miejsce główna koncepcja budowania kontekstu.

W bardzo dużym uproszczeniu embedding to zestaw wektorów. Pinecone wizualizuje je w ten sposób:

Początkowo w bazie mamy "kropki" powiązane z danymi, które już się w niej znajdują. W momencie gdy użytkownik zdaje pytanie, jego treść również zamieniamy na wektor i odpytujemy bazę aby zwróciła tylko te dane, które leżą najbliżej pytania zadanego przez użytkownika. W oparciu o zebrane w ten sposób informacje możemy zbudować kontekst.

Dlatego jeżeli ktoś zapyta o książki, zostaną odnalezione wszystkie moje wzmianki o tym jakie książki czytam, jak to robię itd. a na ich podstawie GPT-3 generuje odpowiedź, zgodnie z instrukcją zapisaną w prompt.

Pomimo tego, że brzmi to bardzo skomplikowanie, w praktyce chodzi o wykorzystanie 4 funkcji:

  • zamiany tekstu na embedding (openAI)
  • wgranie danych do bazy wektorowej (pinecone)
  • wyszukanie danych w bazie wektorowej (pinecone)
  • przygotowanie odpowiedzi, tzw. completion (openAI)

Największa trudność ponownie polega tylko na odpowiednim zebraniu danych. W moim przypadku było to dość łatwe. Przygotowanie całej aplikacji w oparciu o wcześniej opracowany przeze mnie warsztat, zajęło mi jeden wieczór.

Rezultaty "overmentAI"

Biorąc pod uwagę to, jak "na kolanie" przygotowałem dane, byłem przekonany, że odpowiedzi będą "przeciętne". Nie miałem z tym problemu bo i tak zależało mi wyłącznie na poznaniu mechanizmów i zasad pracy z GPT-3. Okazało się, że byłem w błędzie. Oczywiście nie wszystkie odpowiedzi wyglądały tak, jak te poniżej. Jednak nadal mówimy o zdecydowanej większości.

Podczas testów zapisywałem wszystkie zadawane pytania oraz generowane konteksty i odpowiedzi. Na ich podstawie zauważyłem, że można znacznie zwiększyć skuteczność bota poprzez nawet podstawowe tagowanie informacji. Podział ich na fragmenty był dobrym ruchem ale jeżeli np. opis sposobu czytania książek rozbity jest na 3 fragmenty i tylko jeden z nich wprost leży "blisko" pytania zadanego przez użytkownika, to naturalnie obniża to jakość odpowiedzi.

Ponownie jednak do samego tagowania treści można wykorzystać GPT-3 aby znacząco przyspieszyć cały proces. W moim przypadku już teraz pracuję na ~1.5 mln znaków tekstu a to oznacza, że każda większa operacja jest dość kosztowna i to dosłownie.

Przykładowo zbudowanie overmentAI kosztowało mnie $30:

Nie jest to dużo ale jeżeli oddałbym ten projekt w ręce szerszej publiczności, jestem pewien że przy odpowiedniej konfiguracji mówilibyśmy o bardzo wysokich kosztach. Można je jednak znacząco obniżyć a także przyspieszyć generowanie odpowiedzi poprzez wykorzystanie fine-tuning i lepszej optymalizacji danych.

Wnioski i przemyślenia

Praca z OpenAI API zrobiła na mnie nawet większe wrażenie niż ChatGPT. Okazało się, że udostępniony przez nich interfejs jest niesamowicie przyjazny w użyciu. Zdecydowanie brakuje mi jednak doświadczenia i wiedzy na temat budowy promptów oraz opracowania odpowiednich zestawów danych. Dużym wyzwaniem jest także jakakolwiek kontrola nad zwracanymi informacjami. Pomimo całkiem dobrze napisanego promptu, bot czasem "przeciekał" i próbował odpowiadać na pytania nie wykorzystując wskazanego kontekstu. Jednak największym ze wszystkich wyzwań jest niestabilność API, które regularnie zwracało błędy o przekroczeniu limitów lub niedostępności serwerów. Nie mam wątpliwości, że ten problem szybko zostanie rozwiązany, jednak aktualnie nie wyobrażam sobie wykorzystania openAI API na produkcji bez mechanizmów potrafiących radzić sobie z brakiem dostępu do API. No i ostatnim wątkiem są same dane i miejsce ich przechowywania. W teorii dane nie są zapisywane po ich stronie podczas embeddingu, jednak polityka prywatności nie jest dla mnie w 100% transparentna i tym samym w przypadku pracy z wrażliwymi danymi, zaprojektowałbym aplikację, która nie przekazuje ich jakkolwiek do OpenAI.

Zbudowanie overmentAI podsunęło mi także mnóstwo pomysłów na wykorzystanie GPT-3 w moich projektach. Zanim jednak do tego dojdzie, z całą pewnością konieczne będą dalsze eksperymenty z przygotowywaniem danych oraz optymalizacją działania aplikacji.

Oczywiste jest już jednak dla mnie to, że GPT-3 szybko weszło do TOP3 tematów wokół których skupi się moja uwaga w najbliższym czasie.

Kod źródłowy i dalsze kroki

overmentAI powstało w oparciu o kod źródłowy, który udostępniam tutaj wraz z opisem. Na jego podstawie opracowałem także warsztat dostępny na eduweb.pl. We wspomnianych materiałach nie poruszam jednak tematu dotyczącego samego przygotowania danych, jednak nawet na podstawie wyżej zdjęcia kodu źródłowego, można zauważyć, że nie ma w tym nic szczególnie złożonego. Myślę że na opisanie technik przygotowania zestawów informacji, przyjdzie jeszcze czas. Tymczasem bardzo zachęcam do pobrania repozytorium i eksplorowania GPT-3 samodzielnie. Warto tylko pamiętać o ustawieniu twardych limitów kosztów na naszym koncie aby nawet przypadkowo nie generować niepotrzebnych kosztów.