Overview

끊임없이 진화하는 인공 지능 환경에서 보다 인간과 유사한 대화 에이전트를 만드는 것이 중심 초점이었습니다. 텍스트 기반 상호 작용이 크게 발전했지만 텍스트, 이미지, 문서 등 다양한 양식을 통합하면 사용자 경험이 더욱 풍부해지고 에이전트의 효과적인 이해 및 대응 능력이 향상됩니다. 이 영역에서 유망한 접근 방식 중 하나는 다중 모드 표현의 기능과 생성 모델의 기능을 통합하는 다중 모드 검색 증강 생성(Multi-modal Retrieval-Augmented Generative, RAG) 모델을 활용하는 것입니다.

이 글에는 angchain’s cookbook에 있는 Multi-Modal RAG의 재구현과 심층적인 설명이 포함되어 있습니다.

 

Understanding Multi-modal RAG

검색 강화 생성 모델(Retrieval-Augmented Generative models) 또는 RAG의 개념은 transformer와 같은 생성 모델의 강점과 검색 기반(retrieval-based) 기술을 결합할 수 있는 능력 덕분에 주목받고 있습니다.

검색기 구성 요소(retriever components)로 생성 모델을 강화함(augmenting)으로써 RAG 모델은 대규모 텍스트 말뭉치(corpora)기존 지식을 효과적으로 활용하여 응답을 향상시킬 수 있습니다.

 

이제 이 패러다임을 다중 모드 설정으로 확장한 Multi-modal RAG는 텍스트, 이미지, 문서와 같은 다양한 형식을 검색 및 생성 프로세스에 통합합니다. 이를 통해 대화 에이전트는 텍스트 입력뿐만 아니라 그에 수반되는 시각적 또는 문맥 정보를 기반으로 응답을 이해하고 생성할 수 있습니다.

 

설치

pip install "unstructured[all-docs]"

% pip list | grep unstructured
unstructured                             0.13.2
unstructured-client                      0.18.0
unstructured-inference                   0.7.25
unstructured.pytesseract                 0.3.12

 

Lets dive into technicalities

Multi-modal RAG 기반 대화 에이전트를 달성하기 위해 단계:

https://github.com/SunGajiwala/langchain/blob/master/cookbook/Multi_modal_RAG.ipynb

 

  1. Extract text, tables, and images from PDF files using partitioning techniques and document structure analysis.
    분할 기술과 문서 구조 분석을 사용하여 PDF 파일에서 텍스트, 표 및 이미지를 추출

  2. Categorize extracted elements into text and tables based on their type.
    추출된 요소를 유형에 따라 텍스트와 테이블로 분류

  3. Generate summaries for text elements using an OpenAI model, optionally splitting long texts into manageable chunks.
    OpenAI 모델을 사용하여 텍스트 요소에 대한 요약을 생성하고 선택적으로 긴 텍스트를 관리 가능한 덩어리로 분할

  4. Encode images as base64 strings and summarize them using an OpenAI Vision model.
    이미지를 base64 문자열로 인코딩하고 OpenAI Vision 모델을 사용하여 요약

  5. Create a multi-vector retriever to index summaries and raw contents of text, tables, and images.
    텍스트, 표, 이미지의 요약과 원시 콘텐츠를 색인화하는 다중 벡터 검색기를 생성

  6. Initialize a vector store using the Chroma vector store with OpenAI embeddings.
    OpenAI 임베딩이 포함된 Chroma 벡터 저장소를 사용하여 벡터 저장소를 초기화

  7. Construct a multi-modal RAG chain for processing user questions with both textual and visual context.
    텍스트 및 시각적 컨텍스트를 모두 사용하여 사용자 질문을 처리하기 위한 다중 모드 RAG 체인을 구축

  8. Retrieve relevant documents based on a user query using the multi-vector retriever.
    다중 벡터 검색기를 사용하여 사용자 쿼리를 기반으로 관련 문서를 검색

  9. Invoke the multi-modal RAG chain to generate a response to the user query.
    다중 모드 RAG 체인을 호출하여 사용자 쿼리에 대한 응답을 생성
from langchain.text_splitter import CharacterTextSplitter
from unstructured.partition.pdf import partition_pdf


# Extract elements from PDF
def extract_pdf_elements(path, fname):
    """
    Extract images, tables, and chunk text from a PDF file.
    path: File path, which is used to dump images (.jpg)
    fname: File name
    """
    return partition_pdf(fname,
                        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
    )

# Categorize elements by type
def categorize_elements(raw_pdf_elements):
    """
    Categorize extracted elements from a PDF into tables and texts.
    raw_pdf_elements: List of unstructured.documents.elements
    """
    tables = []
    texts = []
    for element in raw_pdf_elements:
        #print(element)
        #print(str(type(element)))
        if "unstructured.documents.elements.Table" in str(type(element)):
            print(str(type(element)))
            tables.append(str(element))
        elif "unstructured.documents.elements.CompositeElement" in str(type(element)):
            print(str(type(element)))
            texts.append(str(element))
    return texts, tables


# File path
fpath = "/Users/dongsik/github/Multi-Modal-using-RAG/Attention_is_all_you_need.pdf"
fname = "Attention_is_all_you_need.pdf"

# Get elements
raw_pdf_elements = extract_pdf_elements(fpath, fname)
#print(raw_pdf_elements)

# Get text, tables
texts, tables = categorize_elements(raw_pdf_elements)

# Optional: Enforce a specific token size for texts
text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
    chunk_size=4000, chunk_overlap=0
)
joined_texts = " ".join(texts)
texts_4k_token = text_splitter.split_text(joined_texts)

 

1. extract_pdf_elements 함수는 unstructured.partition.pdf 모듈의 partition_pdf 메서드를 활용하여 PDF 파일에서 이미지, 테이블 및 청크 텍스트를 추출합니다.

 

2. categorize_elements 함수는 추출된 요소를 유형에 따라 텍스트와 테이블로 분류합니다.

 

3. 선택적으로 추출된 텍스트를 CharacterTextSplitter 클래스를 사용하여 특정 토큰 크기의 청크로 분할할 수 있습니다. 이 단계에는 청크 크기 및 겹침과 같은 매개변수를 사용하여 텍스트 분할기를 설정한 다음 결합된 텍스트를 청크로 분할하는 작업이 포함됩니다.

 

pdf에서 추출된 요소항목을 확인해보겠습니다.

print("raw_pdf_elements : ", raw_pdf_elements)
print(raw_pdf_elements[0].metadata)

import pandas as pd
# Convert JSON output into Pandas DataFrame
data = []
for c in raw_pdf_elements:
    #print(type(c).__name__)
    row = {}
    row['Element Type'] = type(c).__name__
    row['Filename'] = c.metadata.filename
    row['Date Modified'] = c.metadata.last_modified
    row['Filetype'] = c.metadata.filetype
    row['Page Number'] = c.metadata.page_number
    row['text'] = c.text
    data.append(row)
df = pd.DataFrame(data)
print(len(df))
df.to_excel('attention_is_all_you_need.xlsx', index=False)

 

raw_pdf_elements :  
[<unstructured.documents.elements.CompositeElement object at 0x35524b590>, 
 <unstructured.documents.elements.CompositeElement object at 0x30a24a650>, 
 <unstructured.documents.elements.CompositeElement object at 0x3557f9090>, 
 <unstructured.documents.elements.CompositeElement object at 0x355704650>, 
 <unstructured.documents.elements.CompositeElement object at 0x355704fd0>, 
 <unstructured.documents.elements.CompositeElement object at 0x3557055d0>, 
 <unstructured.documents.elements.Table object at 0x30ef5d3d0>, 
 <unstructured.documents.elements.CompositeElement object at 0x355706e10>, 
 <unstructured.documents.elements.CompositeElement object at 0x30ef75290>, 
 <unstructured.documents.elements.CompositeElement object at 0x30ef77650>, 
 <unstructured.documents.elements.Table object at 0x30ef76290>, 
 <unstructured.documents.elements.CompositeElement object at 0x3449ed510>, 
 <unstructured.documents.elements.CompositeElement object at 0x3449edc50>, 
 <unstructured.documents.elements.Table object at 0x3449efd10>, 
 <unstructured.documents.elements.CompositeElement object at 0x355b57350>, 
 <unstructured.documents.elements.Table object at 0x355b54c90>, 
 <unstructured.documents.elements.CompositeElement object at 0x3559d2250>, 
 <unstructured.documents.elements.CompositeElement object at 0x3559d1b50>, 
 <unstructured.documents.elements.CompositeElement object at 0x107cd4350>, 
 <unstructured.documents.elements.CompositeElement object at 0x307a4a010>]
 
<unstructured.documents.elements.ElementMetadata object at 0x32255e810>

20

 

unstructured.documents.elements.Table : Table

unstructured.documents.elements.CompositeElement : Text

 

추출된 요소를 dataframe에 넣고 excel로 출력해보면 Chunking 옵션에 맞게 추출된것을 확인할수있습니다.

 

원본 pdf (10 page에 있는 table)

 

pdf에서 추출된 요소(elements)

 

 

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
import os

# OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
# Generate summaries of text elements
def generate_text_summaries(texts, tables, summarize_texts=False):
    """
    Summarize text elements
    texts: List of str
    tables: List of str
    summarize_texts: Bool to summarize texts
    """

    # Prompt
    prompt_text = """You are an assistant tasked with summarizing tables and text for retrieval. \
    These summaries will be embedded and used to retrieve the raw text or table elements. \
    Give a concise summary of the table or text that is well optimized for retrieval. Table or text: {element} """
    prompt = ChatPromptTemplate.from_template(prompt_text)

    # Text summary chain
    model = ChatOpenAI(temperature=0, model="gpt-4")
    summarize_chain = {"element": lambda x: x} | prompt | model | StrOutputParser()

    # Initialize empty summaries
    text_summaries = []
    table_summaries = []

    # Apply to text if texts are provided and summarization is requested
    if texts and summarize_texts:
        text_summaries = summarize_chain.batch(texts, {"max_concurrency": 5})
    elif texts:
        text_summaries = texts

    # Apply to tables if tables are provided
    if tables:
        table_summaries = summarize_chain.batch(tables, {"max_concurrency": 5})

    return text_summaries, table_summaries


# Get text, table summaries
text_summaries, table_summaries = generate_text_summaries(
    texts_4k_token, tables, summarize_texts=True
)

 

 

1. generate_text_summaries 함수는 텍스트 요소를 요약할지 여부를 나타내는 bool 플래그 summarize_texts와 함께 텍스트 및 테이블 요소 목록을 입력으로 사용합니다. 요약 작업을 위한 프롬프트 템플릿을 설정합니다.

 

2. Prompt template에는 검색에 최적화된 간결한 요약을 제공하도록 안내하는 assistant에 대한 instructions이 포함되어 있습니다.

 

3. 이 코드는 temperature 및 model variant("gpt-3.5-turbo-16k")과 같은 매개변수를 지정하여 OpenAI 모델과의 채팅 기반 상호 작용을 초기화합니다. prompt template, OpenAI 모델, 모델의 응답을 처리하기 위한 output parser로 구성된 요약 체인이 구성됩니다.

 

4. text element가 제공되고 요약이 요청되면 요약 체인이 텍스트 요소에 일괄적으로 적용됩니다. max_concurrency 매개변수는 OpenAI API에 대한 최대 동시 요청 수를 제어합니다.

 

5. table element가 제공되면 요약 체인이 유사하게 적용됩니다.

 

6. 마지막으로, 전처리된 text element (texts_4k_token), 테이블 및 텍스트 요소를 요약하는 플래그와 함께 generate_text_summaries 함수를 호출합니다.

 

import base64
import os

from langchain_core.messages import HumanMessage


def encode_image(image_path):
    """Getting the base64 string"""
    with open(image_path, "rb") as image_file:
        return base64.b64encode(image_file.read()).decode("utf-8")


def image_summarize(img_base64, prompt):
    """Make image summary"""
    chat = ChatOpenAI(model="gpt-4-vision-preview", max_tokens=200)

    msg = chat.invoke(
        [
            HumanMessage(
                content=[
                    {"type": "text", "text": prompt},
                    {
                        "type": "image_url",
                        "image_url": {"url": f"data:image/jpeg;base64,{img_base64}"},
                    },
                ]
            )
        ]
    )
    return msg.content


def generate_img_summaries(path):
    """
    Generate summaries and base64 encoded strings for images
    path: Path to list of .jpg files extracted by Unstructured
    """

    # Store base64 encoded images
    img_base64_list = []

    # Store image summaries
    image_summaries = []

    # Prompt
    from tqdm import tqdm
    import time

    prompt = """You are an assistant tasked with summarizing images for retrieval. \
        These summaries will be embedded and used to retrieve the raw image. \
        Give a concise summary of the image that is well optimized for retrieval."""

    count = 0

    for img_file in tqdm(sorted(os.listdir(path)), desc="Processing images"):
        if img_file.endswith(".jpg"):
            img_path = os.path.join(path, img_file)

            try:
                base64_image = encode_image(img_path)
                img_base64_list.append(base64_image)
                image_summaries.append(image_summarize(base64_image, prompt))
                count += 1

            except Exception as e:
                print(f"Error processing image {img_file}: {e}")

    return img_base64_list, image_summaries


# Image summaries
img_base64_list, image_summaries = generate_img_summaries("figures/")

 

1. encode_image 함수는 지정된 경로에서 이미지 파일을 읽고 이를 base64 문자열로 인코딩합니다. 인코딩된 문자열은 UTF-8 형식으로 디코딩된 후 반환됩니다.

 

2. image_summarize 함수는 base64로 인코딩된 이미지와 프롬프트를 입력으로 사용합니다. GPT-4 Vision 모델로 채팅 세션을 초기화하고 프롬프트와 base64 형식으로 인코딩된 이미지 URL을 모두 포함하는 메시지를 구성합니다.

 

3. generate_img_summaries 함수는 JPEG 이미지가 포함된 디렉터리를 처리합니다. 각 이미지 파일을 반복하여 base64 형식으로 인코딩하고 image_summarize 함수를 사용하여 요약을 생성합니다.

 

4. 프롬프트는 generate_img_summaries 함수 내에 정의되어 검색에 최적화된 간결한 요약을 제공하도록 assistant에게 지시합니다. tqdm 라이브러리는 이미지 처리 중에 진행률 표시줄을 표시하는 데 사용됩니다.

 

5. 마지막으로 이미지가 포함된 디렉터리 경로를 사용하여 generate_img_summaries 함수가 호출됩니다. 이는 이미지 요약을 위해 GPT-4 Vision 모델을 활용하여 각 이미지에 대해 base64로 인코딩된 이미지와 요약을 생성합니다.

 

import uuid

from langchain.retrievers.multi_vector import MultiVectorRetriever
from langchain.storage import InMemoryStore
from langchain_community.vectorstores import Chroma
from langchain_core.documents import Document
from langchain_openai import OpenAIEmbeddings


def create_multi_vector_retriever(
    vectorstore, text_summaries, texts, table_summaries, tables, image_summaries, images
):
    """
    Create retriever that indexes summaries, but returns raw images or texts
    """

    # Initialize the storage layer
    store = InMemoryStore()
    id_key = "doc_id"

    # Create the multi-vector retriever
    retriever = MultiVectorRetriever(
        vectorstore=vectorstore,
        docstore=store,
        id_key=id_key,
    )

    # Helper function to add documents to the vectorstore and docstore
    def add_documents(retriever, doc_summaries, doc_contents):
        doc_ids = [str(uuid.uuid4()) for _ in doc_contents]
        summary_docs = [
            Document(page_content=s, metadata={id_key: doc_ids[i]})
            for i, s in enumerate(doc_summaries)
        ]
        retriever.vectorstore.add_documents(summary_docs)
        retriever.docstore.mset(list(zip(doc_ids, doc_contents)))

    # Add texts, tables, and images
    # Check that text_summaries is not empty before adding
    if text_summaries:
        add_documents(retriever, text_summaries, texts)
    # Check that table_summaries is not empty before adding
    if table_summaries:
        add_documents(retriever, table_summaries, tables)
    # Check that image_summaries is not empty before adding
    if image_summaries:
        add_documents(retriever, image_summaries, images)

    return retriever


# The vectorstore to use to index the summaries
vectorstore = Chroma(
    collection_name="rag-storage", embedding_function=OpenAIEmbeddings()
)

# Create retriever
retriever_multi_vector_img = create_multi_vector_retriever(
    vectorstore,
    text_summaries,
    texts,
    table_summaries,
    tables,
    image_summaries,
    img_base64_list,
)

 

1. create_multi_Vector_retriever 함수는 텍스트 요약, 텍스트 내용, 테이블 요약, 테이블 내용, 이미지 요약 및 이미지 base64 인코딩 문자열을 포함하여 다양한 요약 및 해당 원시 콘텐츠를 입력으로 사용합니다.

 

2. 메모리 내 문서 저장소(store)를 초기화하고 지정된 벡터 저장소(vectorstore)를 사용하여 다중 벡터 검색기(retriever)를 설정합니다. id_key 매개변수는 검색기 내에서 문서를 식별하는 데 사용되는 키를 결정합니다.

 

3. 함수 내부에는 벡터 저장소와 문서 저장소 모두에 문서를 추가하기 위한 도우미 함수 add_documents가 정의되어 있습니다. 각 문서에 대해 고유한 UUID를 생성하고 요약이 포함된 문서 개체를 페이지 콘텐츠로 생성하여 벡터 저장소에 추가합니다. 또한 UUID를 키로 사용하여 원시 문서 내용을 문서 저장소에 추가합니다.

 

4. 코드는 해당 유형의 문서를 검색기에 추가하기 전에 각 유형의 요약이 비어 있지 않은지 확인합니다.

 

5. OpenAI 임베딩 기능이 포함된 Chroma 벡터 저장소를 사용하여 벡터 저장소(vectorstore)를 초기화합니다.

 

6. create_multi_Vector_retriever 함수를 사용하면 텍스트, 테이블 및 이미지에 대한 벡터 저장소와 요약/컨텐츠를 제공하여 다중 벡터 검색기(retriever_multi_Vector_img)가 생성됩니다.

 

import io
import re

from IPython.display import HTML, display
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
from PIL import Image


def plt_img_base64(img_base64):
    """Disply base64 encoded string as image"""
    # 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))


def looks_like_base64(sb):
    """Check if the string looks like base64"""
    return re.match("^[A-Za-z0-9+/]+[=]{0,2}$", sb) is not None


def is_image_data(b64data):
    """
    Check if the base64 data is an image by looking at the start of the data
    """
    image_signatures = {
        b"\xFF\xD8\xFF": "jpg",
        b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A": "png",
        b"\x47\x49\x46\x38": "gif",
        b"\x52\x49\x46\x46": "webp",
    }
    try:
        header = base64.b64decode(b64data)[:8]  # Decode and get the first 8 bytes
        for sig, format in image_signatures.items():
            if header.startswith(sig):
                return True
        return False
    except Exception:
        return False


def resize_base64_image(base64_string, size=(128, 128)):
    """
    Resize an image encoded as a Base64 string
    """
    # 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 split_image_text_types(docs):
    """
    Split base64-encoded images and texts
    """
    b64_images = []
    texts = []
    for doc in docs:
        # Check if the document is of type Document and extract page_content if so
        if isinstance(doc, Document):
            doc = doc.page_content
        if looks_like_base64(doc) and is_image_data(doc):
            doc = resize_base64_image(doc, size=(1300, 600))
            b64_images.append(doc)
        else:
            texts.append(doc)
    return {"images": b64_images, "texts": texts}


def img_prompt_func(data_dict):
    """
    Join the context 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"]:
        for image in data_dict["context"]["images"]:
            image_message = {
                "type": "image_url",
                "image_url": {"url": f"data:image/jpeg;base64,{image}"},
            }
            messages.append(image_message)

    # Adding the text for analysis
    text_message = {
        "type": "text",
        "text": (
            "You are a deep learning and machine learning specialist.\n"
            "You will be given a mixed of text, tables, and image(s) usually of charts.\n"
            "Use this information to provide quality information related to the user question. \n"
            f"User-provided question: {data_dict['question']}\n\n"
            "Text and / or tables:\n"
            f"{formatted_texts}"
        ),
    }
    messages.append(text_message)
    return [HumanMessage(content=messages)]


def multi_modal_rag_chain(retriever):
    """
    Multi-modal RAG chain
    """

    # Multi-modal LLM
    model = ChatOpenAI(temperature=0, model="gpt-4-vision-preview", max_tokens=1024)

    # RAG pipeline
    chain = (
        {
            "context": retriever | RunnableLambda(split_image_text_types),
            "question": RunnablePassthrough(),
        }
        | RunnableLambda(img_prompt_func)
        | model
        | StrOutputParser()
    )

    return chain


# Create RAG chain
chain_multimodal_rag = multi_modal_rag_chain(retriever_multi_vector_img)

 

1. looks_like_base64 함수는 주어진 문자열이 base64로 인코딩된 문자열과 유사한지 확인합니다.

 

2. is_image_data 함수는 헤더를 검사하여 base64로 인코딩된 문자열이 이미지 데이터를 나타내는지 확인합니다.

 

3. resize_base64_image 함수는 base64로 인코딩된 이미지 문자열을 디코딩하고 이미지 크기를 조정한 후 이를 base64 문자열로 다시 인코딩합니다.

 

4. split_image_text_types 함수는 문서 목록을 내용에 따라 이미지 유형과 텍스트 유형으로 분할합니다. 각 문서에서 이미지와 유사한 base64 문자열을 확인하고 그에 따라 크기를 조정합니다.

 

5. img_prompt_func 함수는 RAG 모델 입력에 적합한 형식으로 사용자가 제공한 질문, 상황별 텍스트 및 이미지를 포함하는 메시지를 구성합니다. 컨텍스트 텍스트의 형식을 지정하고 이미지가 있는 경우 이미지 메시지를 추가합니다.

 

6. multi_modal_rag_chain 함수는 다중 모드 RAG 파이프라인을 구성합니다. 관련 컨텍스트 데이터를 가져오는 검색기, 전처리를 위한 람다 함수, 이미지 프롬프트 함수, GPT-4 Vision 모델 및 출력 파서로 구성됩니다.

 

7. 마지막으로 앞서 생성한 검색기로 multi_modal_rag_chain 함수를 호출하여 RAG 체인(chain_multimodal_rag)을 생성합니다.

 

# Check retrieval
query = "Can you give me a brief description on the document."
docs = retriever_multi_vector_img.get_relevant_documents(query, limit=6)

# We get 4 docs
len(docs)

 

검색 쿼리는 다중 벡터 검색기(retriever_multi_Vector_img)를 사용하여 실행됩니다. 쿼리에서는 문서에 대한 간략한 설명을 요청합니다. 검색기의 get_relevant_documents 메소드는 쿼리를 기반으로 관련 문서를 검색하는 데 사용되며 최대 6개의 문서가 지정됩니다.

 

# Run RAG chain
response = chain_multimodal_rag.invoke(query)
response = response.split('.')

# Print each line in a new line
for line in response:
    print(line)

 

The images you have provided appear to be visualizations of attention patterns from a machine learning model, likely from a neural network that processes natural language, such as a transformer-based model
 These types of visualizations are used to understand how the model is paying attention to different parts of the input text when making predictions or generating text


Each image shows lines connecting words in a sentence or phrase
 The thickness and color intensity of the lines indicate the strength of the attention the model is giving to those connections
 In other words, they show which words the model considers important when interpreting the meaning of a particular word


The first image with red lines shows a sentence that reads "The law will never be perfect, but its application should be just - this is what we are missing in my opinion <EOS>
" The attention patterns are dense, suggesting that the model is considering many different connections between the words


The second image with purple and brown lines shows a sentence that reads "It is in this spirit that a majority of American governments have passed new laws since 2009 making the registration or voting process more difficult <EOS>
" The attention here is focused on the latter part of the sentence, particularly on the words "making the registration or voting process more difficult
"

The third image with purple lines is a less intense version of the first image, showing the same sentence with a different attention pattern, possibly from another layer or a different model


The fourth image with green lines is another visualization of the first sentence, again showing a different pattern of attention, which could be from yet another layer or model


The "<EOS>" token typically signifies "End Of Sentence" in natural language processing, indicating the end of the input text for the model


These visualizations are useful for researchers and engineers to debug and improve the model's understanding of language, as well as to gain insights into how the model makes decisions based on the input text

 

이 코드 조각은 다중 모드 RAG 체인(chain_multimodal_rag)을 호출하여 지정된 쿼리에 대한 응답을 생성합니다. 쿼리는 제공된 컨텍스트와 질문을 기반으로 응답 생성을 트리거하는 체인의 invoke 메서드로 전달됩니다.

 

대화형 에이전트를 위한 Multi-modal RAG의 장점:

1. Enhanced Understanding: 다중 모드를 통합함으로써 Multi-modal RAG 모델은 사용자 쿼리의 뉘앙스와 컨텍스트를 더 잘 파악할 수 있습니다. 이미지의 시각적 단서나 문서의 추가 정보는 보다 관련성이 높고 정확한 응답을 생성하는 데 유용한 컨텍스트를 제공할 수 있습니다.

 

2. Richer Responses: 다양한 양식을 활용할 수 있는 능력을 통해 대화 에이전트는 더욱 유익하고 매력적인 응답을 생성할 수 있습니다. 시각적 설명 제공, 관련 문서 참조, 멀티미디어 콘텐츠 통합 등 Multi-modal RAG는 대화 경험을 풍부하게 할 수 있습니다.

 

3. Improved User Interaction: 다중 모드 상호 작용은 실제 커뮤니케이션을 더욱 밀접하게 모방하여 사용자 경험을 더욱 직관적이고 자연스럽게 만듭니다. 사용자는 텍스트, 이미지 또는 문서의 조합을 사용하여 에이전트와 통신할 수 있으므로 보다 유연하고 표현력이 풍부한 상호 작용이 가능합니다.

 

4. Broader Knowledge Integration: 다중 모드 표현을 활용하면 대화 에이전트가 더 넓은 범위의 지식 소스를 활용할 수 있습니다. 상담원은 텍스트 데이터에만 의존하는 대신 시각적 소스와 문서의 정보를 통합하여 지식 기반을 확장하고 응답 품질을 향상시킬 수 있습니다.

 

과제와 향후 방향:

Multi-modal RAG는 대화형 AI 발전에 큰 가능성을 갖고 있지만 잠재력을 최대한 실현하려면 몇 가지 과제를 해결해야 합니다.

 

1. Data Quality and Diversity: 고품질의 다양한 다중 모드 데이터 세트의 가용성을 보장하는 것은 강력한 모델을 교육하는 데 중요합니다. 이러한 데이터 세트를 수집하고 관리하는 것은 특히 다중 모드 데이터가 부족한 영역에서 중요한 과제를 제기합니다.

 

2. Model Complexity and Scalability: 여러 양식을 단일 모델에 통합하면 복잡성과 계산 요구가 증가합니다. Multi-modal RAG 모델의 확장 가능한 배포를 가능하게 하려면 효율적인 아키텍처와 교육 절차를 개발해야 합니다.

 

3. Ethical and Privacy Concerns: 대화형 에이전트가 다양한 데이터 유형을 처리하는 데 더욱 능숙해짐에 따라 사용자 개인 정보 보호를 보장하고 민감한 정보를 윤리적으로 처리하는 것이 가장 중요해졌습니다. 데이터 익명화 및 동의 관리를 위한 강력한 메커니즘이 필수적입니다.

 

4. Evaluation Metrics and Benchmarks: 다중 모드 대화형 AI 시스템에 대한 적절한 평가 지표와 벤치마크를 설정하는 것은 성능을 정확하게 평가하는 데 필수적입니다. 측정항목은 응답 관련성, 일관성, 다중 양식 통합과 같은 요소를 고려해야 합니다.

 

결론

Multi-modal RAG는 대화형 AI의 진화에서 중요한 진전을 나타내며 보다 몰입적이고 상황에 맞게 풍부한 상호 작용을 생성할 수 있는 잠재력을 제공합니다. 텍스트, 이미지 및 문서를 완벽하게 통합함으로써 Multi-modal RAG 모델은 사용자 쿼리를 보다 효과적으로 이해하고 유익할 뿐만 아니라 매력적인 응답을 생성할 수 있습니다. 과제는 여전히 남아 있지만, 이 분야의 지속적인 연구와 개발은 기계와 인간 사이의 인간과 같은 의사소통의 새로운 지평을 열어줄 가능성을 갖고 있습니다.


참고

https://unstructured-io.github.io/unstructured/core/chunking.html

https://unstructured.io/blog/optimizing-unstructured-data-retrieval

https://unstructured-io.github.io/unstructured/best_practices/table_extraction_pdf.html

https://github.com/SunGajiwala/langchain/blob/master/cookbook/Multi_modal_RAG.ipynb

 

따라하기

https://sungajiwala54.medium.com/unlocking-conversational-ais-potential-a-deep-dive-into-multi-modal-rag-for-large-pdf-f6e48057e08d

https://github.com/dongshik/Multi-Modal-using-RAG/blob/test/Multi_modal_RAG.ipynb

 

 

테디님이 올려놓으신 유튜브 영상을 보고 Macbook M1에서 따라해봤습니다.  중간에 몇가지 설정에 맞게 약간 변경해가면서 테스트 합니다.

https://www.youtube.com/watch?v=VkcaigvTrug&t=23s

# GPU 모니터링
% sudo asitop

# LangServe 실행
% python server.py

# ngrok으로 external 서비스
% ngrok http --domain=humble-curiously-antelope.ngrok-free.app 8000

# PDF를 RAG
% streamlit run main.py

conda 가상환경 만들고 requirements.txt로 필요한 모듈 설치


conda create -n llm311 python=3.11

% conda env list
# conda environments:
#
base                  *  /Users/dongsik/miniconda
llm                      /Users/dongsik/miniconda/envs/llm
llm311                   /Users/dongsik/miniconda/envs/llm311

 

% conda activate llm311

% python -V
Python 3.11.9

% pip list
Package    Version
---------- -------
pip        23.3.1
setuptools 68.2.2
wheel      0.41.2

 

예제 github을 내 github으로 fork 한후 내 PC에 clone 받아서 내환경에 맞게 수정하면서 진행합니다.

 

teddy github : https://github.com/teddylee777/langserve_ollama

내 github : https://github.com/dongshik/langserve_ollama

% ll
total 1000
drwxr-xr-x@ 12 dongsik  staff     384 Apr 20 09:22 .
drwxr-xr-x   4 dongsik  staff     128 Apr 19 16:35 ..
drwxr-xr-x@ 14 dongsik  staff     448 Apr 19 16:40 .git
-rw-r--r--@  1 dongsik  staff      50 Apr 19 16:35 .gitignore
-rw-r--r--@  1 dongsik  staff    3343 Apr 19 16:35 README.md
drwxr-xr-x@  8 dongsik  staff     256 Apr 19 16:35 app
drwxr-xr-x@  8 dongsik  staff     256 Apr 19 16:35 example
drwxr-xr-x@  3 dongsik  staff      96 Apr 19 16:35 images
drwxr-xr-x@  4 dongsik  staff     128 Apr 19 16:35 ollama-modelfile
-rw-r--r--@  1 dongsik  staff  481043 Apr 19 16:35 poetry.lock
-rw-r--r--@  1 dongsik  staff     659 Apr 19 16:35 pyproject.toml
-rw-r--r--@  1 dongsik  staff   14983 Apr 19 16:35 requirements.txt

 

pip install -r requirements.txt

% pip install -r requirements.txt
Ignoring colorama: markers 'python_version >= "3.11.dev0" and python_version < "3.12.dev0" and platform_system == "Windows"' don't match your environment

 

% pip list | grep lang
langchain                  0.1.16
langchain-community        0.0.32
langchain-core             0.1.42
langchain-openai           0.1.3
langchain-text-splitters   0.0.1
langchainhub               0.1.15
langdetect                 1.0.9
langserve                  0.0.51
langsmith                  0.1.47

% pip list | grep huggingface
huggingface-hub            0.22.2

 

Huggingface에서 모델 Download 받고 Ollama에 EEVE Q5 모델 등록하고 구동

huggingface-cli download \
  heegyu/EEVE-Korean-Instruct-10.8B-v1.0-GGUF \
  ggml-model-Q5_K_M.gguf \
  --local-dir /Users/dongsik/GitHub/teddylee777/langserve_ollama/ollama-modelfile/EEVE-Korean-Instruct-10.8B-v1.0 \
  --local-dir-use-symlinks False
  
Consider using `hf_transfer` for faster downloads. This solution comes with some limitations. See https://huggingface.co/docs/huggingface_hub/hf_transfer for more details.
downloading https://huggingface.co/heegyu/EEVE-Korean-Instruct-10.8B-v1.0-GGUF/resolve/main/ggml-model-Q5_K_M.gguf to /Users/dongsik/.cache/huggingface/hub/tmpkuuur4ki
ggml-model-Q5_K_M.gguf:  37%|███████████████████████████▌                                               | 2.81G/7.65G [04:35<09:55, 8.13MB/s]

 

 

% ll -sh
total 14954512
       0 drwxr-xr-x@ 5 dongsik  staff   160B Apr 20 10:02 .
       0 drwxr-xr-x@ 4 dongsik  staff   128B Apr 20 10:02 ..
       8 -rw-r--r--@ 1 dongsik  staff   369B Apr 19 16:35 Modelfile
       8 -rw-r--r--@ 1 dongsik  staff   419B Apr 19 16:35 Modelfile-V02
14954496 -rw-r--r--  1 dongsik  staff   7.1G Apr 20 10:02 ggml-model-Q5_K_M.gguf

 

<경로>/langserve_ollama/ollama-modelfile/EEVE-Korean-Instruct-10.8B-v1.0/Modelfile

FROM ggml-model-Q5_K_M.gguf

TEMPLATE """{{- if .System }}
<s>{{ .System }}</s>
{{- end }}
<s>Human:
{{ .Prompt }}</s>
<s>Assistant:
"""

SYSTEM """A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions."""

PARAMETER TEMPERATURE 0
PARAMETER stop <s>
PARAMETER stop </s>

 

모델파일 설정하지 않으면 답변이 끝났을때 이상하게 대답할수도있기때문에 필요합니다. 

System prompt가 있다면 중간(.System) 위치에 넣어으라는 의미이며 여기서는 'SYSTEM'이 이자리를 치환하게 됩니다.

그다음 <s> 스페셜 토큰이 앞에 붙어서 사용자 즉 Human의 질문 .Prompt가 들어가게 됩니다.

그후 모델 Assistant가 받아서 답변하게 됩니다. 

 

※ Note!!

Modelfile에서 <s>는 문장의 시작을 나타내는 특수 토큰입니다. 이것은 "문장의 시작"을 나타내기 위해 사용됩니다. 예를 들어, 자연어 처리 작업에서 모델이 문장의 시작을 식별하고, 이에 따라 적절한 처리를 수행할 수 있도록 합니다. 이것은 토큰화된 데이터의 일부로서 모델에 제공됩니다.

 

 

tokenizer.chat_template

{% if messages[0]['role'] == 'system' %}{% set loop_messages = messages[1:] %}{% set system_message = messages[0]['content'] %}{% else %}{% set loop_messages = messages %}{% set system_message = 'You are a helpful assistant.' %}{% endif %}{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% for message in loop_messages %}{% if loop.index0 == 0 %}{{'<|im_start|>system ' + system_message + '<|im_end|> '}}{% endif %}{{'<|im_start|>' + message['role'] + ' ' + message['content'] + '<|im_end|>' + ' '}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant ' }}{% endif %}

 

https://huggingface.co/yanolja/EEVE-Korean-Instruct-10.8B-v1.0


Prompt Template

A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions.
Human: {prompt}
Assistant:

 

 

ollama 목록 확인 

% ollama list
NAME    	ID          	SIZE  	MODIFIED
eeve:q4 	68f4c2c2d9fe	6.5 GB	7 days ago
gemma:2b	b50d6c999e59	1.7 GB	10 days ago

 

ollama가 잘 구동되어 있는지 확인합니다.

% ps -ef | grep ollama
  501  3715  3691   0 Wed01PM ??         0:29.51 /Applications/Ollama.app/Contents/Resources/ollama serve
  501  4430     1   0 Wed01PM ??         0:00.03 /Applications/Ollama.app/Contents/Frameworks/Squirrel.framework/Resources/ShipIt com.electron.ollama.ShipIt /Users/dongsik/Library/Caches/com.electron.ollama.ShipIt/ShipItState.plist
  501 61608  3197   0 10:45AM ttys002    0:00.01 grep ollama

 

새로받은 모델을 ollama에 등록합니다

ollama create eeve:q5 -f ollama-modelfile/EEVE-Korean-Instruct-10.8B-v1.0/Modelfile

저는 위의 Modelfile로 ollama등록할려고 하니 "Error: unknown parameter 'TEMPERATURE'"가 발생했습니다. 
그래서 소문자 temperature로 변경해서 생성되었습니다.
만일 동일한 에러가 발생한다면 소문자 temperature로 변경해서 생성해보시기 바랍니다. 

 

% ollama create eeve:q5 -f ollama-modelfile/EEVE-Korean-Instruct-10.8B-v1.0/Modelfile

transferring model data
creating model layer
creating template layer
creating system layer
creating parameters layer
creating config layer
using already created layer sha256:b9e3d1ad5e8aa6db09610d4051820f06a5257b7d7f0b06c00630e376abcfa4c1
writing layer sha256:6b70a2ad0d545ca50d11b293ba6f6355eff16363425c8b163289014cf19311fc
writing layer sha256:1fa69e2371b762d1882b0bd98d284f312a36c27add732016e12e52586f98a9f5
writing layer sha256:3ab8c1bbd3cd85e1b39b09f5ff9a76e64da20ef81c22ec0937cc2e7076f1a81c
writing layer sha256:d86595b443c06710a3e5ba27700c6a93ded80100ff1aa808a7f3444ff529fa70
writing manifest
success

 

% ollama list
NAME    	ID          	SIZE  	MODIFIED
eeve:q4 	68f4c2c2d9fe	6.5 GB	7 days ago
eeve:q5 	0732d4a47219	7.7 GB	7 minutes ago
gemma:2b	b50d6c999e59	1.7 GB	10 days ago

 

ollama run eeve:q5

% ollama run eeve:q5
>>> 대한민국의 수도는 어디야?
안녕하세요! 대한민국의 수도에 대해 궁금해하시는군요. 서울이 바로 그 곳입니다! 서울은 나라의 북부에 위치해 있으며 정치, 경제, 문화의 중심지 역할을 하고 있습니다. 2019년 기준으로 약 970만 명의 인구를 가진 대도시로,
세계에서 가장 큰 도시 중 하나입니다. 또한 세계적인 금융 허브이자 주요 관광지로, 경복궁, 남산타워, 명동과 같은 다양한 역사적 및 현대적 명소를 자랑하고 있습니다. 서울은 활기찬 밤문화로도 유명하며, 많은 바와 클럽
관광객과 현지인 모두를 끌어들입니다. 대한민국의 수도에 대해 더 알고 싶으신 것이 있으신가요?

>>>

 

로컬 command로 실행한 모습

 

아래 문구로 질문해보겠습니다.

 

한국의 수도는 어디인가요? 아래 선택지 중 골라주세요.\n\n(A) 경성\n(B) 부산\n(C) 평양\n(D) 서울\n(E) 전주

>>> 한국의 수도는 어디인가요? 아래 선택지 중 골라주세요.\n\n(A) 경성\n(B) 부산\n(C) 평양\n(D) 서울\n(E) 전주
대한민국의 수도에 대한 질문에 답변해 주셔서 감사합니다! 정답은 (D) 서울입니다. 서울은 나라의 북부에 위치해 있으며 정치, 경제, 문화의 중심지 역할을 하고 있습니다. 2019년 기준으로 약 970만 명의 인구를
대도시로, 세계에서 가장 큰 도시 중 하나입니다. 또한 세계적인 금융 허브이자 주요 관광지로, 경복궁, 남산타워, 명동과 같은 다양한 역사적 및 현대적 명소를 자랑하고 있습니다. 서울은 활기찬 밤문화로도 유명하며,
많은 바와 클럽이 관광객과 현지인 모두를 끌어들입니다. 대한민국의 수도에 대해 더 알고 싶으신 것이 있으신가요?

 

질문과 동시에 답변이 나오는것처럼 작동합니다. 속도도 좋고 답변의 퀄리티도 좋습니다.

>>> 다음 지문을 읽고 문제에 답하시오.
...
... ---
...
... 1950년 7월, 한국 전쟁 초기에 이승만 대통령은 맥아더 장군에게 유격대원들을 북한군의 후방에 침투시키는 방안을 제안했다. 이후, 육군본부는 육본직할 유격대와 육본 독립 유격대를 편성했다. 국군은 포항과 인접한 장사동 지역에 상륙작
... 전을 수행할 부대로 독립 제1유격대대를 선정했다. 육군본부는 독립 제1유격대대에 동해안의 장사동 해안에 상륙작전을 감행하여 북한군 제2군단의 보급로를 차단하고 국군 제1군단의 작전을 유리하게 하기 위한 작전명령(육본 작명 제174호)
... 을 하달했다. 9월 14일, 독립 제1유격대대는 부산에서 LST 문산호에 승선하여 영덕군의 장사동으로 출항했다.
...
... 1950년 9월 15일, 독립 제1유격대대는 장사동 해안에 상륙을 시도하였으나 태풍 케지아로 인한 높은 파도와 안개로 인해 어려움을 겪었다. LST 문산호는 북한군의 사격과 파도로 인해 좌초되었고, 상륙부대는 09:00시경에 전원이
... 상륙을 완료하였다. 그 후, 15:00시경에 200고지를 점령하였고, 다양한 무기와 장비를 노획하였다. 9월 16일과 17일에는 독립 제1유격대대가 여러 위치에서 북한군과의 전투를 벌였으며, 미 구축함과의 연락 두절로 인해 추가적인
... 어려움을 겪었다.
...
... 장사동에서 위급한 상황에 처한 독립 제1유격대대를 구출하기 위해 해군본부는 LT-1(인왕호)를 급파했으나, LST 문산호의 구출에 실패했다. 해군본부는 상륙부대의 철수를 지원하기 위해 LST 조치원호를 현지로 보냈다. 9월 18일,
... 이명흠 부대장은 유엔 해군과의 협력 하에 부족한 식량과 탄약 지원을 받았다. 9월 19일, 유엔군의 함포지원과 함께 LST 조치원호가 도착하여 철수 작전을 시작했다. 스피어 소령은 직접 해안에 상륙하여 구조작전을 지시하였다. 9월 2
... 0일, 725명이 부산항으로 복귀했으나, 32명이 장사동 해안에 남아 북한군의 포로가 되었거나 탈출하여 국군에 합류하였다.
...
... 장사리 전투가 인천 상륙작전의 양동작전으로 알려졌으나, 이 전투가 드라마틱한 요소로 인해 과장되었으며, 실제로는 인천 상륙작전과 큰 관련이 없다. 또한, 북한이나 중국의 군사적 상황을 고려할 때, 장사리에서의 전투가 낙동강 전선에 영
... 향을 끼칠 가능성은 낮다.
...
... ---
...
... 문제
... 1. 지문에 나오는 지명을 모두 쓰시오.
... 2. 그중 대게로 유명한 곳은?
지문에 나오는 지명은 다음과 같습니다:
- 포항
- 장사동
- 영덕군
- 부산
- 문산호
- 조치원호
- 스피어 소령
- 낙동강 전선
대게로 유명한 곳은 영덕군입니다.

 

ollama 쉘에서 나올때는 Use Ctrl + d or /bye to exit.

 

2021년형 14인치 MacBook Pro의 M1 Pro CPU는 10코어이고 GPU는 16코어입니다. 

2020년형 13인치 MacBook Pro의 M1 CPU는 8코어이고 GPU는 8코어입니다. (저는 이겁니다)

 

 

cpu를 100% 까지 사용하면서 일했습니다. (수고했어)

 

 

LangServe로 모델 서빙

langserve_ollama % ll app
total 40
drwxr-xr-x@  8 dongsik  staff   256 Apr 19 16:35 .
drwxr-xr-x@ 12 dongsik  staff   384 Apr 20 09:22 ..
-rw-r--r--@  1 dongsik  staff     0 Apr 19 16:35 __init__.py
-rw-r--r--@  1 dongsik  staff   549 Apr 19 16:35 chain.py
-rw-r--r--@  1 dongsik  staff   723 Apr 19 16:35 chat.py
-rw-r--r--@  1 dongsik  staff   328 Apr 19 16:35 llm.py
-rw-r--r--@  1 dongsik  staff  1444 Apr 19 16:35 server.py
-rw-r--r--@  1 dongsik  staff   559 Apr 19 16:35 translator.py
(llm311) dongsik@dongsikleeui-MacBookPro langserve_ollama %

 

 

chat.py, chain.py, llm.py, translator.py 세개 파일의 llm 모델명을 내 환경에 맞게 수정합니다.

# LangChain이 지원하는 다른 채팅 모델을 사용합니다. 여기서는 Ollama를 사용합니다.
#llm = ChatOllama(model="EEVE-Korean-10.8B:latest")
llm = ChatOllama(model="eeve:q5")

 

server.py 실행

(llm311) dongsik@dongsikleeui-MacBookPro langserve_ollama % cd app
(llm311) dongsik@dongsikleeui-MacBookPro app % pwd
/Users/dongsik/GitHub/teddylee777/langserve_ollama/app
(llm311) dongsik@dongsikleeui-MacBookPro app % ll
total 40
drwxr-xr-x@  8 dongsik  staff   256 Apr 19 16:35 .
drwxr-xr-x@ 12 dongsik  staff   384 Apr 20 09:22 ..
-rw-r--r--@  1 dongsik  staff     0 Apr 19 16:35 __init__.py
-rw-r--r--@  1 dongsik  staff   584 Apr 20 13:15 chain.py
-rw-r--r--@  1 dongsik  staff   758 Apr 20 13:15 chat.py
-rw-r--r--@  1 dongsik  staff   363 Apr 20 13:15 llm.py
-rw-r--r--@  1 dongsik  staff  1444 Apr 19 16:35 server.py
-rw-r--r--@  1 dongsik  staff   594 Apr 20 13:15 translator.py
(llm311) dongsik@dongsikleeui-MacBookPro app % python server.py

 

http://0.0.0.0:8000/prompt/playground/

 

질문 과 답변

 

 

RemoteRunable로 LangServe를 호출 하도록 변경

 

<경로>/langserve_ollama/example

% ll
total 120
drwxr-xr-x@  9 dongsik  staff    288 Apr 20 13:50 .
drwxr-xr-x@ 12 dongsik  staff    384 Apr 20 09:22 ..
drwxr-xr-x@  3 dongsik  staff     96 Apr 19 16:35 .streamlit
-rw-r--r--@  1 dongsik  staff  12504 Apr 19 16:35 00-ollama-test.ipynb
-rw-r--r--@  1 dongsik  staff   4885 Apr 19 16:35 01-remote-invoke.ipynb
-rw-r--r--@  1 dongsik  staff   3775 Apr 19 16:35 02-more-examples.ipynb
-rw-r--r--@  1 dongsik  staff   6222 Apr 19 16:35 main.py
-rw-r--r--@  1 dongsik  staff  14708 Apr 19 16:35 requirements.txt

 

01-remote-invoke.ipynb의 로컬 LangServe 주소로 변경합니다

from langserve import RemoteRunnable

# ngrok remote 주소 설정

#chain = RemoteRunnable("NGROK 에서 설정한 본인의 도메인 주소/prompt/")
# chain = RemoteRunnable("https://poodle-deep-marmot.ngrok-free.app/prompt/")
chain = RemoteRunnable("http://0.0.0.0:8000/prompt/")

for token in chain.stream({"topic": "딥러닝에 대해서 알려줘"}):
    print(token, end="")

 

 

 

 

 

 

ngrok을 이용해서 로컬 LangServe 를 Port Forwarding하기 

ngrok 가입 

https://dashboard.ngrok.com/cloud-edge/domains

 

M1용 설치 파일을 다운로드 받아서 설치합니다.

https://dashboard.ngrok.com/get-started/setup/macos

 

무료 도메인 설정 

 

 

humble-curiously-antelope.ngrok-free.app

 

LangServe 구동된 포트로 ngok 도메인 지정해서 포트 포워딩

ngrok http --domain=humble-curiously-antelope.ngrok-free.app 8000

% ngrok http --domain=humble-curiously-antelope.ngrok-free.app 8000

ngrok                                                                                                                    (Ctrl+C to quit)

K8s Gateway API support available now: https://ngrok.com/r/k8sgb

Session Status                online
Account                       dongsik.lee (Plan: Free)
Version                       3.8.0
Region                        Japan (jp)
Latency                       45ms
Web Interface                 http://127.0.0.1:4040
Forwarding                    https://humble-curiously-antelope.ngrok-free.app -> http://localhost:8000

Connections                   ttl     opn     rt1     rt5     p50     p90
                              0       0       0.00    0.00    0.00    0.00

 

https://humble-curiously-antelope.ngrok-free.app/prompt/playground/

 

ngrok url로 질의를 해보면 local 서버의 GPU가 100%로 올라가면서 Output을 만들고있습니다.

 

01-remote-invoke.ipynb 파일의 RemoteRunnable 주소를 ngrok 주소로 변경하고 vscode로 실행해봅니다.

from langserve import RemoteRunnable

# ngrok remote 주소 설정

#chain = RemoteRunnable("NGROK 에서 설정한 본인의 도메인 주소/prompt/")
chain = RemoteRunnable("https://humble-curiously-antelope.ngrok-free.app/prompt/")
#chain = RemoteRunnable("http://0.0.0.0:8000/prompt/")

for token in chain.stream({"topic": "딥러닝에 대해서 알려줘"}):
    print(token, end="")

 

잘 작동됩니다.

 

추가 예제 

번역기 

from langchain_community.chat_models import ChatOllama
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

# LangChain이 지원하는 다른 채팅 모델을 사용합니다. 여기서는 Ollama를 사용합니다.
# llm = ChatOllama(model="EEVE-Korean-10.8B:latest")
llm = ChatOllama(model="eeve:q5")

# 프롬프트 설정
prompt = ChatPromptTemplate.from_template(
    "Translate following sentences into Korean:\n{input}"
)

# LangChain 표현식 언어 체인 구문을 사용합니다.
chain = prompt | llm | StrOutputParser()

 

LLM을 Runable로 실행

from langchain_community.chat_models import ChatOllama
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

# LangChain이 지원하는 다른 채팅 모델을 사용합니다. 여기서는 Ollama를 사용합니다.
# llm = ChatOllama(model="EEVE-Korean-10.8B:latest")
llm = ChatOllama(model="eeve:q5")

 

Streamlit으로 PDF rag 해보기

Embedding을 OpenAIEmbeddings을 사용하기위해서 OPENAI_API_KEY를 .env 파일에서 가져옵니다.

% pip install python-dotenv

 

main.py 내용중 OPEN API KEY세팅과 LANGSERVE_ENDPOINT를 ngrok주소로 업데이트 한후 실행합니다

% streamlit run main.py

  You can now view your Streamlit app in your browser.

  Local URL: http://localhost:8501
  Network URL: http://192.168.0.10:8501

  For better performance, install the Watchdog module:

  $ xcode-select --install
  $ pip install watchdog

 

예제 > SPRI_AI_Brief_2023년12월호_F.pdf

https://spri.kr/posts?code=AI-Brief

 

FileNotFoundError: [Errno 2] No such file or directory: 'pdfinfo'

% conda install poppler
Channels:
 - defaults
 - conda-forge
Platform: osx-arm64

% pip install pdftotext

 

FileNotFoundError: [Errno 2] No such file or directory: 'tesseract'

% brew install tesseract
==> Auto-updating Homebrew...
Adjust how often this is run with HOMEBREW_AUTO_UPDATE_SECS or disable with
HOMEBREW_NO_AUTO_UPDATE. Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).

% brew install tesseract-lang

 

UnicodeEncodeError: 'ascii' codec can't encode characters in position 22-23: ordinal not in range(128)

 

 

 

 

위 PDF에서 최종 질문 을 해보겠습니다.

 

실제해보니 내용이 엄청난 영상입니다. 

- Ollama

- EEVE 양자화 모델

- LangServe

- ngrok

- Streamlit RAG

- Asitop

 

감사합니다.

Asitop으로 내 M1 상태 모니터링


% pip install asitop

% sudo asitop 

sudo 패스워드 입력

+ Recent posts