LLM이 여러분의 PDF 문제를 해결해드립니다 – Ollama-Gemma2와 함께하는 프로젝트 이야기

여러분, 혹시 문서 더미 속에서 중요한 정보를 찾아야 했던 적 있나요? 직접 검사하고, 요약하는 일이 얼마나 힘든지 잘 아실 겁니다. 오늘은 이런 문제를 Python과 머신러닝(LMM)으로 해결하는 방법을 나눠보려 합니다. 프로젝트의 하이라이트는 Local 모델인 Ollama-Gemma2를 사용하여 PDF 데이터를 RAG(Retrieval-Augmented Generation) 모델로 검색하고 요약하는 것입니다.

PDF 데이터를 SQLite와 PostgreSQL에 저장하기!

여러분이 알고 계셔야 할 첫 번째 개념은 PDF 파일을 데이터베이스에 저장하는 것입니다. 여기선 SQLite와 PostgreSQL을 사용했습니다.

1. PDF 파일 로드

PyPDFLoader를 사용하여 PDF 파일을 로드합니다. 이 단계에서는 파일을 읽기만 합니다.

from langchain_community.document_loaders import PyPDFLoader

def load_pdf(file_path):
    loader = PyPDFLoader(file_path)
    documents = loader.load()
    return documents

file_path는 읽어들일 PDF 파일의 경로를 말합니다. documents는 PDF의 내용을 담고 있습니다.

2. 텍스트 분할

이어서, PDF에서 얻은 텍스트를 청크로 나눕니다. RecursiveCharacterTextSplitter를 사용하면 이 작업이 가능합니다.

from langchain.text_splitter import RecursiveCharacterTextSplitter

def split_text(documents):
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
    texts = text_splitter.split_documents(documents)
    return texts

chunk_size는 각 청크의 최대 크기를, chunk_overlap은 청크들 간의 중복 범위를 뜻합니다.

3. 텍스트를 데이터베이스에 저장하기

저장한 텍스트 청크를 데이터베이스에 넣는 것은 꽤 중요합니다. 아래는 이를 SQLite에 저장하는 코드입니다.

from langchain_community.vectorstores import SQLiteVSS

def save_to_db(texts, embeddings):
    vector_store = SQLiteVSS.from_texts(
        texts=texts,
        embedding=embeddings,
        table="documents"
    )
    return vector_store

위 코드에서 texts는 저장하려는 텍스트, embeddings는 임베딩 모델이며 모든 텍스트 청크는 ‘documents’ 테이블에 저장됩니다.

4. 데이터베이스로부터 읽어오기

이제 저장한 데이터를 다시 불러오는 방법에 대해 알아봅니다.

def load_from_db(embeddings):
    conn = SQLiteVSS.create_connection(db_file="vss.db")
    db = SQLiteVSS(table="documents", embedding=embeddings, connection=conn)
    return db

db_file은 SQLite 데이터베이스 파일의 이름입니다. 이는 필요한 데이터를 효율적으로 가져오기 위한 연결을 만듭니다.

Ollama-Gemma2: 문서 요약의 마법사

이제 Ollama-Gemma2 모델을 사용하여 데이터를 검색하고 요약해 봅시다. 이 모델은 사용자가 입력한 쿼리에 대해 문서를 이해하고 요약해 줍니다.

1. 임베딩 생성

위에서 사용한 텍스트 청크를 위한 임베딩을 생성합니다. 이 임베딩은 텍스트를 숫자 벡터로 변환하여 효율적으로 검색하는 데 사용됩니다.

from langchain_community.embeddings import HuggingFaceEmbeddings

embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")

2. 검색 및 요약

Ollama-Gemma2 모델을 사용하여 벡터 저장소에서 가장 유사한 텍스트 청크를 검색하고 이를 요약합니다.

from langchain.chains import RetrievalQA
from langchain_community.llms import Ollama

def query_to_db(query, vector_store, llm, chain_type):
    retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 2})
    qa = RetrievalQA.from_chain_type(llm=llm, chain_type=chain_type, retriever=retriever)
    response = qa.invoke(query)
    return response

query는 사용자가 입력하는 질문입니다. vector_store는 텍스트 청크가 저장된 데이터베이스 연결을, llm은 Ollama-Gemma2 모델을 나타냅니다. 마지막으로 chain_type은 검색 방식입니다.

예제: 문서를 한글로 요약하기

프로젝트의 진짜 재미는 데이터를 입력하고 요약 결과를 확인하는 순간입니다. 예제를 통해 마지막 단계를 보겠습니다.

if __name__ == "__main__":
    # 데이터 불러오기
    texts = load_pdf_process("example.pdf") 
    embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
    vector_store = load_from_db(embeddings)

    # Ollama-Gemma2 모델 생성
    ollama_model = Ollama(model="gemma2")

    # 쿼리 수행
    query = "문서를 한글로 요약해주세요."
    response = query_to_db(query, vector_store, ollama_model, "stuff")
    print(response)

위의 코드는 PDF 파일을 로드하고, 청크로 분할하고, 데이터베이스에 저장하며, Ollama-Gemma2 모델을 사용해 요약 결과를 반환합니다.

전체 코드는 아래 링크에서 참조 할 수 있습니다.

https://github.com/bipark/Ollama-Gemma2-PDF-RAG

마무리

이번 프로젝트를 통해 PDF 파일을 읽고, 청크로 나누며, 데이터베이스에 저장한 후, 이를 LLM 모델로 분석하고 요약하는 다양한 과정을 알아봤습니다. Ollama-Gemma2 모델은 오늘날의 문서 분석 및 요약 작업을 한층 더 효율적이고 쉽게 만들어줍니다. 여러분도 이 방법을 사용하여 문서 더미 속에서 중요한 정보를 빠르고 쉽게 찾아 보세요!

이 글은 Github 문서를 토대로 제목부터 모두 AI가 작성 했습니다.

무서운 세상입니다. 이건 제가 썼습니다. ^^;

MirrorOn

LLM(Large Language Model)은 이제 없으면 아쉬운 존재입니다. 개발자 입장에서는 벌써 2년째 Github Copilot을 코딩도우미로 잘 쓰고 있었지만 ChatGPT, Claude가 좀 더 좋은 코드를 만들어 주는 상황이 되었고, 최근 검색증강생성(RAG – Retrieval-Augmented Generation)이 개발자 입장에서 중요한 키워드가 되어서 MML의 구조와 Api에 대한 공부를 하지 않을수 없게 되어서 공부를 시작했습니다.

공부에 가장 빠른 방법은 가르치거나 만들어 보는것이라고 늘 생각해서 뭔가 공부 하면 새 프로젝트를 하나 시작해서 적용해 보는 편인데, 이번에 LLM을 공부하며 만든 프로젝트가 MirrorOn 입니다.

다중 LLM 질의 도구 MirrorOn

MirrorOn은 한번 질문을 던져서 여러개의 LLM으로 부터 답을 얻는 질의도구 입니다. 이름은 ‘거울아~ 거울아~’ 에서 착안한겁니다. ^^ 거울을 켜(On!)면 누가 세상에서 가장 아름다운지 알려줄겁니다.

현재(V 0.1.0)에서는 ChatGPT, Claude, Ollama를 사용할 수 있습니다. ChatGPT, Claude는 API key 가 필요합니다. 사용자가 직접 API key를 발급받고 설정에서 키를 입력하고 질문을 입력하면 API 질의결과를 조회할 수 있습니다. 대화의 맥락을 이어갈수 있도록 구성되어 있고, 메뉴를 통하여 복사,삭제, 공유등을 실행할 수 있습니다.

로컬 LLM

Ollama(https://ollama.com)는 사용자의 컴퓨터에서 LLM을 실행할수 있도록 도와주는 프로그램입니다. 링크에서 Ollama를 설치하고 원하는 Model을 설치해서 질문을 무료(!)로 할 수 있습니다. 컴퓨터의 용량이 허락하는대로 다양한 Model을 실행할 수 있습니다.

MirrorOn을 Ollama 클라이언트로 쓰면 매월 LLM에 지불하는 비용을 아낄수 있습니다.

RAG – Retrieval-Augmented Generation

검색증강생성(RAG)는 애플리케이션 개발자의 입장에서는 향후 필수적으로 갖추어야 하는 기능이 될것같습니다. MML이 모든것을 다 알수없지만 사용자의 요구사항은 다양하고 거기에 맞추는 방법이 RAG입니다.

MirrorOn V0.1.0에서는 기본적인 기능을 만들어 봤습니다. 현재는 PDF 문서를 업로드 하고 요약, 검색하는 정도입니다만 추후 벡터화등의 기능을 추가할 예정입니다.

그리고 이미지를 업로드 해서 내용에 대한 질문을 하거나, LLM에게 인스트력션을 제공하는 등의 LLM을 사용하기 위한 특별한 기능들이 있습니다. 상세한 기능은 링크를 참고 하세요. 영어와 일본어도 지원합니다.

기능 설명

https://github.com/bipark/MirrorOn

다운로드

현재는 Mac 버전만 존재 합니다. 맥앱스토어에 업로드 하기위해 몇주를 시도했지만, API key를 입력하는 기능 문제로 애플에서 승인을 보류하고 있어서 Git Release에서 다운 받도록 업로드 했습니다. 추후 애플의 승인이 있으면 앱스토어 에서도 볼수 있기를 바랍니다. 원도우즈용 버전도 만들고 있습니다.

맥용 다운로드 링크

https://github.com/bipark/MirrorOn/releases/tag/MirrorOn

2024,7,13 Billy

크랙 디텍터 – Crack Detector

크랙디텍터(Crack Detector) V3가 앱스토어에 릴리즈되었습니다. 크랙디텍터는 스마트폰 카메라에서 촬영한 콘크리트 벽면의 벌어진틈(크랙)을 찾아내고 크랙의 두께와 길이를 이미지 프로세싱 방법으로 측정하는 소프트웨어 서비스 입니다.

V3 – 머신러닝

크랙디텍터를 만들기 시작한건 거의 10년이 넘었습니다만 상용화 가능한 수준의 제품이 된건 이번 버전이 처음입니다.

V3에서 가장 큰 변화는 사진에서 크랙을 찾아내는 방법을 기존의 룰베이스 이미지 프로세싱에서 세그멘테이션 머신러닝을 도입한 것입니다. 기존의 이미지 프로세싱 방법은 현장의 위치, 조명, 시간등에 따른 이미지 품질 차이로 인해서 크랙을 못찾거나 그림자를 크랙으로 인식하는 등의 문제가 있었습니다만 머신러닝 도입후 크랙디텍션 성능이 매우 향상되었습니다.

마커

앱을 다운 받고 콘크리트 벽에 크랙을 촬영하면 이미지를 분석해서 크랙을 찾아냅니다만 크랙의 크기를 Pixel 단위로 표시합니다. 마커를 활용하면 Pixel 단위를 mm 단위로 정확하게 변환 할 수 있습니다.

마커는 정밀측정이 가능한 4개의 마커를 가진 하드웨어 타입과 1개의 마커를 사용하는 스티커 타입을 쓸 수 있습니다.

앱다운로드

다운로드 - 아이폰
https://apps.apple.com/kr/app/crack-detector/id1530337221

무료다운로드 이며 안드로이드용은 준비 중입니다. 앱사용에 관한 문의는 rtlink.park@gmail.com

개발배경

Jetson Nano, 전원, Wifi, Case, Setup, Inference

엔비디아의 Jetson Nano를 구입했다. 최근 좀 저렴한 2GB 모델의 릴리즈 소식을 들었지만 기존 4GB 모델을 구입했다. 충동적으로 구입했기 때문에 특별한 사용 목적은 없고 그저 어떤 성능이 나오는지 Development Kit 수준이 어느정도 인지 알고 싶었다.

구입은 아래 링크에서 했는데, 그동안 읽었던 글들에서 기본 모델에는 Wifi 없고 4A짜리 전원 공급장치가 필요하다고 해서 구입하는김에 Wifi 장비와 별도의 4A 전원공급장치, 그리고 케이스도 구입했다. 케이스를 별도로 구입한 이유는 아래에 보면 알게 되겠지만 Wifi 안테나가 꽤 큰 사이즈인데 케이스가 없으면 마땅히 붙여놓을 곳이 없어보여서 였다.

https://www.devicemart.co.kr/goods/search?keyword_log_flag=Y&search_text=jetson&q=jetson&x=0&y=0

위와 같이 두개의 디바이스로 분리 할 수 있다. 나중에 조립을 마치고 MicroSD를 좀 더 큰 사이즈로 바꾸려고 보니 완전히 다시 분해를 해야만 했다. 라즈베리파이가 그렇게 쉽게 SD를 바꿀수 있는것에 비해 너무 불편했다. 설계 미스!

그리고 아래 Wifi 모듈 커넥터는 위의 두개 디바이스 사이에 숨겨져 있다. 이것도 설계 미스! 너무 불편하다.

아래 동영상을 참조해서 Wifi를 연결했다. 다행히 소프트웨어 셋팅을 해줄 필요는 없다. 자동으로 인식 했다. 다른 블로그에서는 다이소에 가면 오천원짜리 USB 랜카드를 연결해도 된다고 했다. 그게 나을 수도 있다. 랜카드와 안테나 가격이 3만원이다.

조립을 하는 김에 가지고 있던 라즈베리파이 카메라를 연결했다. 아무래도 동영상 인식에는 카메라가 꼭 필요할것 같다.

조립을 마치고 Micro-USB에 전원을 연결해서 부팅을 했는데 계속해서 전원이 부족하다는 메세지가 나와서 배럴잭으로 4A 아답터를 통해서 전원을 연결했는데도 연결이 되지 않았다. 알고보니 배럴잭 뒤에 있는 J48 핀을 연결해야 배럴잭을 통해서 전원이 연결되는 것이었다. 4A 아답터 연결후에는 전원이 부족하다는 메세지는 나오지 않았다.

배럴잭이 있는 별도의 직류전원 장치 4A – 7200원

메탈 케이스 28,000원 – 후면

케이스가 그리 효율적이지 않은 구조였지만

조립해놓고 나니 그래도 케이스를 사길 잘했다는 생각이 들었다.

일단 조립완료 하고 부팅을 해봤다.

아래 링크에서 시작하면 된다. 순서대로 따라서 MicroSD를 준비해서 디바이스에 꽂고 부팅하면 우분투 화면을 만날 수 있다.

https://developer.nvidia.com/embedded/learn/get-started-jetson-nano-devkit

실제 DNN 기반의 추론테스트를 위한 라이브러리 셋팅은 아래 링크에서 코드와 같이 진행해야 하는데 전체 설치까지 대략 1시간이 넘게 걸린다. 학습모델 선택을 바꾸면 좀 더 걸릴 수도 있다.

https://github.com/dusty-nv/jetson-inference/blob/master/docs/building-repo-2.md

$ sudo apt-get update 
$ sudo apt-get install git cmake libpython3-dev python3-numpy 
$ git clone --recursive https://github.com/dusty-nv/jetson-inference 
$ cd jetson-inference 
$ mkdir build $ cd build 
$ cmake ../ 
$ make -j$(nproc) 
$ sudo make install 
$ sudo ldconfig

설치가 완료되면 아래 링크의 코드를 이용해서 실제 추론 테스트를 해볼 수 있다.

https://github.com/dusty-nv/jetson-inference/blob/master/docs/imagenet-example-python-2.md

셈플로 제공되는 곰돌이 이미지는 당연히 잘 인식되었고 다른 이미지를 이용해서 추론을 해보니 꽤 빠르게 잘 인식되었다.