Langchain, RAG, GPT-Vision: Analýza obrázků v pdf

GPT pdf analýza obrázky

V tomto návodu vám ukážu, jak pomocí Langchain, RAG a GPT Vision získat obrázky i text z pdf dokumentu a následně tato data analyzovat.

Tento kód budeme spouštět pomocí platformy Google Colab.

Návod

Nejprve si nainstalujeme potřebné knihovny.

! pip install pdf2image
! pip install pytesseract
! apt install poppler-utils
! apt install tesseract-ocr
! pip install kaleido cohere tiktoken python-multipart
! pip install -U langchain openai chromadb langchain-experimental # (newest versions required for multi-modal)
! pip install "unstructured[all-docs]==0.10.19" pillow pydantic lxml pillow matplotlib tiktoken open_clip_torch torch

Nastavíme cestu, kde bude uložen pdf dokument a následně i všechny soubory.

import os

path = "/content/data/"
file_name = os.listdir(path)

V následujícím kódu uděláme:

  • Nainstalujeme knihovny.
  • Importujeme data.
  • Extrahujeme obrázky/tabulky a rozdělíme text do chunks.
  • Importujeme knihovny, vytvoříme chroma databázi, přidáme obrázky a dokumenty.
  • Vytvoříme RAG.
  • Importujeme knihovny a následně dekódujeme, změníme vel. obrázku a enkódujeme.
  • Přidáme kontext a obrázky, pokud jsou přítomny, následně i systémovou výzvu pro analýzu.
  • Nastavíme API klíč a položíme dotaz.

Nahrajeme pdf dokument do prostředí Google Colab. Jako příklad jsem použil jednu studii ohledně řeckých bronzových uměleckých soch.

Kód

# Extract images, tables, and chunk text
from unstructured.partition.pdf import partition_pdf

raw_pdf_elements = partition_pdf(
    filename=path + file_name[0],
    extract_images_in_pdf=True,
    infer_table_structure=True,
    chunking_strategy="by_title",
    max_characters=4000,
    new_after_n_chars=3800,
    combine_text_under_n_chars=2000,
    image_output_dir_path=path
)  # Closing parenthesis was missing here

tables = []
texts = []
for element in raw_pdf_elements:
    if "unstructured.documents.elements.Table" in str(type(element)):
        tables.append(str(element))
    elif "unstructured.documents.elements.CompositeElement" in str(type(element)):
        texts.append(str(element))

print(len(tables))
print(len(texts))
import os
import uuid

import chromadb
import numpy as np
from langchain.vectorstores import Chroma
from langchain_experimental.open_clip import OpenCLIPEmbeddings
from PIL import Image as _PILImage

# Create chroma
vectorstore = Chroma(
    collection_name="mm_rag_clip_photos", embedding_function=OpenCLIPEmbeddings()
)

# Get image URIs with .jpg extension only
image_uris = sorted(
    [
        os.path.join(path, image_name)
        for image_name in os.listdir(path)
        if image_name.endswith(".jpg")
    ]
)

# Add images
vectorstore.add_images(uris=image_uris)

# Add documents
vectorstore.add_texts(texts=texts)

# Make retriever
retriever = vectorstore.as_retriever()
import base64
import io
from io import BytesIO

import numpy as np
from PIL import Image


def resize_base64_image(base64_string, size=(128, 128)):
    """
    Resize an image encoded as a Base64 string.

    Args:
    base64_string (str): Base64 string of the original image.
    size (tuple): Desired size of the image as (width, height).

    Returns:
    str: Base64 string of the resized image.
    """
    # Decode the Base64 string
    img_data = base64.b64decode(base64_string)
    img = Image.open(io.BytesIO(img_data))

    # Resize the image
    resized_img = img.resize(size, Image.LANCZOS)

    # Save the resized image to a bytes buffer
    buffered = io.BytesIO()
    resized_img.save(buffered, format=img.format)

    # Encode the resized image to Base64
    return base64.b64encode(buffered.getvalue()).decode("utf-8")


def is_base64(s):
    """Check if a string is Base64 encoded"""
    try:
        return base64.b64encode(base64.b64decode(s)) == s.encode()
    except Exception:
        return False


def split_image_text_types(docs):
    """Split numpy array images and texts"""
    images = []
    text = []
    for doc in docs:
        doc = doc.page_content  # Extract Document contents
        if is_base64(doc):
            # Resize image to avoid OAI server error
            images.append(
                resize_base64_image(doc, size=(250, 250))
            )  # base64 encoded str
        else:
            text.append(doc)
    return {"images": images, "texts": text}
from operator import itemgetter
from langchain.chat_models import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableLambda, RunnablePassthrough, RunnableParallel

def prompt_func(data_dict):
    # Joining the context texts into a single string
    formatted_texts = "\n".join(data_dict["context"]["texts"])
    messages = []

    # Adding image(s) to the messages if present
    if data_dict["context"]["images"]:
        image_message = {
            "type": "image_url",
            "image_url": {
                "url": f"data:image/jpeg;base64,{data_dict['context']['images'][0]}"
            },
        }
        messages.append(image_message)

    # Adding the text message for analysis
    text_message = {
        "type": "text",
        "text": (
             "Jsi odborný kritik umění a historik. Máš za úkol analyzovat a interpretovat obrazy, "
             "vzhledem k jejich historickému a kulturnímu významu. Vedle obrázků bude "
             "poskytnuto se souvisejícím textem i kontext nabídky. Oba budou načteny z vektorové databáze "
             "na základě klíčových slov zadaných uživatelem. Využij prosím své rozsáhlé znalosti a analytické schopnosti k poskytnutí "
             "komplexního shrnutí, které obsahuje:\n"
             "- Podrobný popis vizuálních prvků na obrázku.\n"
             "- Historický a kulturní kontext obrázku.\n"
             "- Interpretace symboliky a významu obrázku.\n"
             "- Spojení mezi obrázkem a souvisejícím textem.\n\n"
             f"Klíčová slova poskytnutá uživatelem: {data_dict['question']}\n\n"
             "Text a/nebo tabulky:\n"
            f"{formatted_texts}"
        ),
    }
    messages.append(text_message)

    return [HumanMessage(content=messages)]
from google.colab import userdata


openai_api_key = userdata.get('OPENAI_API_KEY')

model = ChatOpenAI(temperature=0,
                   openai_api_key=openai_api_key,
                   model="gpt-4-vision-preview",
                   max_tokens=1024)

# RAG pipeline
chain = (
    {
        "context": retriever | RunnableLambda(split_image_text_types),
        "question": RunnablePassthrough(),
    }
    | RunnableParallel({"response":prompt_func| model| StrOutputParser(),
                      "context": itemgetter("context"),})
)
response = chain.invoke("bronzová hlava")
#
print(response['response'])
print(response['context'])

Odpověď GPT:

Na obrázku je zobrazena bronzová hlava, která má všechny znaky klasického uměleckého díla. Hlava je vyobrazena v profilu a zdá se, že je součástí kruhového medailonu. Je zde patrný detailní modelace vlasů a obličejových rysů, stejně jako zdobení v podobě věnce nebo diadému na hlavě. Tato hlava také nese atributy, které by mohly naznačovat, že se jedná o zobrazení bohyně, možná Athény, vzhledem k přítomnosti co se zdá být sova a olivových větví, které jsou tradičně spojeny s touto bohyní moudrosti a válečné strategie v řecké mytologii.

Historický a kulturní kontext obrázku je zřejmě helénistické období, což je doba po Alexandru Velikém, kdy řecká kultura a vliv pronikaly do široké oblasti a mísily se s místními kulturami. Helénistické umění je známé svou dramatickou expresivitou a detailním zpracováním, což je vidět i na tomto medailonu.

Interpretace symboliky a významu obrázku může odkazovat na ideály moudrosti, ochrany a intelektuální síly, které Athéna představuje. Bronz jako materiál může symbolizovat trvalost a sílu, zatímco detailní zpracování povrchu může odkazovat na helénistické umělecké dovednosti a estetické hodnoty.

Spojení mezi obrázkem a souvisejícím textem je zřejmé v odkazu na helénistické umění a jeho schopnost vyjádřit hluboké a komplexní myšlenky prostřednictvím „výmluvných povrchů“. Text naznačuje, že i když jsou helénistické bronzové sochy vzdálené a neosobní, stále nám umožňují nahlédnout do jiného způsobu bytí a pojetí etického života. Tento medailon, který byl pravděpodobně nalezen v paláci v Thessaloniki a byl součástí vozu, je příkladem toho, jak helénistické umění dokázalo ztělesnit a předat tyto koncepty.

Můžeme i zobrazit referovaný obrázek z dokumentu.

from IPython.display import HTML, display


def plt_img_base64(img_base64):
    # Create an HTML img tag with the base64 string as the source
    image_html = f'<img src="data:image/jpeg;base64,{img_base64}" />'

    # Display the image by rendering the HTML
    display(HTML(image_html))

plt_img_base64(response['context']['images'][0])

Konverzace

Následně můžeme pokládat další otázky, které nás zajímají.

response = chain.invoke("Portrait of a Boy, analýza sochy a kontextu")
print(response['response'])
print(response['context'])

Odpověď:

Vizuální prvky na obrázku: Obrázek zobrazuje sochu chlapce, který je zobrazen v životní velikosti. Socha je vyrobena z mramoru a je velmi detailně zpracovaná. Chlapec má na sobě římský oděv a jeho vlasy jsou pečlivě upravené. Jeho výraz je zaměřený a zamyšlený, což naznačuje, že je to portrét někoho důležitého nebo významného.

Historický a kulturní kontext obrázku: Socha pochází z hellénistického období, které trvalo od konce 4. století př. n. l. do 1. století n. l. Toto období bylo charakterizováno rozšířením řecké kultury po celém Středomoří a Blízkém východě. Hellénistické sochařství bylo známé svou realistickou a detailní práci, což je vidět i na této soše. Socha může být portrétem nějakého významného chlapce, možná syna nějakého významného politika nebo vojevůdce.

Interpretace symboliky a významu obrázku: Socha může symbolizovat mládí, nevinnost a potenciál. Detailní zpracování a realistický výraz naznačují, že chlapec byl důležitou osobou v řecké společnosti. Jeho oděv a účes mohou naznačovat jeho společenský status nebo rodinné pozadí.

Spojení mezi obrázkem a souvisejícím textem: Text „HELLENISTIC SCULPTURE ART IN AMERICA“ naznačuje, že socha je součástí sbírky hellénistického sochařství v Americe. Text „ART IN AMERICA XX3“ a „ART IN AMERICA XX5“ může být odkazem na konkrétní výstavu nebo katalog, ve kterém je socha zahrnuta. Celkově text poskytuje kontext pro sochu a její význam v rámci amerického uměleckého světa. {‚images‘: [], ‚texts‘: [‚HELLENISTIC SCULPTURE ART IN AMERICA XX9‘, ‚ART IN AMERICA XX3‘, ‚ART IN AMERICA\n\nXX3‘, ‚HELLENISTIC SCULPTURE\n\nART IN AMERICA\n\nXX5‘]}

Kód funguje dobře na interpretaci umění, nebo jiných technických dokumentů s vizuálními prvky. Samozřejmě je tomu potřeba přizpůsobit i systémovou výzvu. Bohužel funkcionalita nedokáže zobrazit všechny typy obrázků. Když jsem např. zkoušel nějaké analytické vizualizace a grafy, tak je nebyla schopna rozeznat jako obrázky. Vždy si však můžete vyzkoušet, které obrázky se zobrazí a které ne pomocí funkce Python. U delších dokumentů trvá extrahování obrázků a tvorba vektor databáze poměrně dlouho.

Obsah článku

Líbí se Vám obsah?

Odebírejte Newsletter, ať vám nic neunikne.

Školení

Jednodenní školení AI. Cena od 15 000 Kč za tým 6–12 lidí. Rezervujte

Odebírejte novinky ze světa AI

Chcete-li se přihlásit k odběru tohoto blogu a dostávat upozornění na nové příspěvky e-mailem, zadejte svou e-mailovou adresu.

Sledujte mě na sítích.

NABÍZÍM..

AI školení

Jednodenní školení AI. Cena od 15 000 Kč za tým 6–12 lidí. Rezervujte

Odebírejte novinky ze světa AI

Chcete-li se přihlásit k odběru tohoto blogu a dostávat upozornění na nové příspěvky e-mailem, zadejte svou e-mailovou adresu.

Podobné články

školení kurz claude Anthropic

Školení Claude Anthropic

Školení Claude pro firmy i jednotlivce. Díky tomuto kurzu se dozvíte, jak Claude používat při každodenní práci i na pomoc v osobním životě. Naučím vás

Číst více »