AI&GameDev

AI와 게임개발에 관련된 이야기

Ollama #5: LLaVA모델과 벡터DB를 활용한 멀티모달 스마트 이미지 검색 시스템

Ollama LLaVA 멀티모달 Rag

멀티모달 (multimodal) 기술은 텍스트, 이미지, 오디오 등 다양한 유형의 정보를 통합하여 처리합니다. 이 기술은 자율주행차, 로봇공학, 인공지능 분야에서 특히 유용하며 다양한 데이터 소스를 결합하여 보다 정확한 분석과 이해를 가능하게 합니다. 이번 포스팅에서는 Ollama에서 작동하는 LLaVA 모델을 활용하여 이미지를 텍스트 정보로 변환하고 이를 ChromaDB에 저장고 검색하는 기능을 구현해 보도록 하겠습니다.

Ollama에 대한 내용은 Ollama #1: 비용 걱정 없이 내 컴퓨터에서 제약 없는 LLM 실행하기Ollama #2: 내 손으로 만드는 ChatGPT 로컬LLM – 커스텀 모델 설치부터 실용 앱까지 글들을 확인해 보시기 바랍니다.

LLaVA: 대규모 언어 및 비전 어시스턴트

LLaVA: Large Language and Vision Assistant는 비전 인코더와 Vicuna 언어 모델을 결합한 대규모 언어 및 비전 멀티모달 AI 모델입니다. 이 모델은 이미지와 텍스트를 이해하고 처리하는 데 필요한 일반적인 시각 및 언어 이해 능력을 갖추고 있습니다.

Ollama + LLaVA: 통합 멀티모달 플랫폼으로 이미지 정보 처리

LLaVA 모델은 Ollama에서 쉽게 설치하여 사용할 수 있습니다. 이번에는 13b 모델을 설치해 사용해 보겠습니다.

ollama run llava:13b

이미지의 경로를 입력하는 것만으로 이미지에 대한 정보를 쉽게 확인할 수 있습니다. 다음은 이미지 검색의 예시입니다.

Ollama LLaVA
Ollama LLaVA

한글로 번역해 보았습니다. 나름 이미지를 잘 이해하고 설명해 주고 있는 것을 확인할 수 있습니다.

 이 이미지에는 사무실과 같은 전문적인 환경에서 대화에 참여하는 두 명의 캐릭터가 등장합니다. 한 캐릭터는 서류와 노트북으로 보이는 물건이 놓인 책상에 앉아 있고, 다른 캐릭터는 그 옆에 서서 적극적으로 토론에 참여하고 있습니다. 두 사람 모두 집중하고 토론에 참여하고 있는 것으로 보아 업무와 관련된 문제를 협업하거나 논의하고 있는 것으로 보입니다. 배경은 현대적이고 조명이 밝아 현대적인 사무실 환경으로 보입니다.

스마트 이미지 검색 시스템

이미지 정보를 LLaVA를 통해 텍스트화 하여 DB에 저장한 후, 이를 검색하는 방법으로 이미지 검색 기능을 구현해 보겠습니다.

Ollama를 활용한 LLaVA 모델: 멀티모달 이미지 처리 기능

Ollama에 설치된 LLaVA 모델을 사용하기 위해 Ollama Python Library 설치합니다.

pip install ollama

이미지 처리를 위해 PIL(Pillow) 라이브러를 설치합니다.

pip install pillow

이미지를 로딩한 후 PNG 형식의 bytes 값으로 가져와 generate를 사용하여 llava:13b 모델을 사용하여 이미지에 대한 요약을 생성합니다. stream값을 True로 설정하고 response를 실시간으로 출력합니다.

def get_info_from_image(image_file):
    print(f"\nProcessing {image_file}\n")  # 처리 중인 이미지 파일 이름을 출력합니다.
    with Image.open(image_file) as img:  # 이미지 파일을 열고, 이를 img라는 변수에 할당합니다.
        with BytesIO() as buffer:  # BytesIO 객체를 buffer라는 변수에 할당합니다.
            img.save(buffer, format='PNG')  # img 객체(이미지)를 PNG 형식으로 buffer에 저장합니다.
            image_bytes = buffer.getvalue()  # buffer의 바이트 값을 가져와 image_bytes 변수에 할당합니다.
            full_response = ''
            for response in generate(model='llava:13b', 
                            prompt=image_summary_prompt, 
                            images=[image_bytes], 
                            stream=True):
                # 콘솔에 응답을 출력하고 full_response에 추가합니다.
                print(response['response'], end='', flush=True)
                full_response += response['response']
    return full_response

결과를 json형식으로 출력하도록 prompt를 작성하여 결과는 다음과 같은 형식으로 출력됩니다. 이미지는 [‘Cartoon’, ‘Illustration’, ‘Photo’] 중 하나로 분류하여 Image의 값으로 추가하였습니다.

get_info_from_image('images/103.jpg')
{
  "Image": "Photo",
  "Overall Scene": "A realistic photograph of an urban street scene with signage for the London Underground, indicating its location at a station.",
  "Notable Features": "The signage is prominent and clearly identifies the location as being part of the London Underground public transportation system.",
  "Text in the Image": "London Underground. Public Transport Services",
  "Interpretation": "The image captures a common urban scene, emphasizing the role of public transportation in daily life and travel within a large city like London.",
  "Tags": "#Photo #Signage #PublicTransport #UrbanLife #London"
}

ChromaDB를 사용한 이미지 정보 관리 및 검색

ChromaDB에서 image_info_db collection의 존재 여부를 확인합니다.

# Persistent Chroma Client 시작
persistent_client = chromadb.PersistentClient()

# 이미 collection_name 이름의 컬렉션이 있는지 확인
collections = persistent_client.list_collections()
is_collection_exist = any(collection.name == 'image_info_db' for collection in collections)

기존에 생성된 collection이 존재하면 그대로 사용합니다.

# 기존에 생성된 collection이 존재
collection = persistent_client.get_collection(collection_name)

image_info_db라는 이름의 collection이 없는 경우 images 폴더의 모든 이미지 파일의 정보를 collection에 추가합니다.
file_nametagsimage는 값을 별도록 가져와 metadata에 입력하였습니다. 이는 차후 검색을 용이하게 하는데 사용됩니다.
ids에는 한 폴더만 테스트하기 때문에 중복될 일이 없어 해당 이미지의 파일명을 그대로 넣었습니다.
documents에는 파일 정보를 모두 입력하였습니다. 테스트를 위한 것이므로 metadata와 중복되는 값이 있어도 그대로 진행하였습니다.

# 새로운 collection을 생성
collection = persistent_client.get_or_create_collection(collection_name)
# data 폴더에 있는 모든 이미지 파일의 경로를 가져옴
image_files = glob.glob('images/*.*')

for image_file in image_files:
    result = get_info_from_image(image_file)
    json_obj = clean_json_text(result)
    if json_obj is None:  # JSON 객체가 None인 경우, 한 번 더 시도합니다.
        result = get_info_from_image(image_file)
        json_obj = clean_json_text(result)

    if json_obj:
        tags = json_obj['Tags']  # 'Tags' 필드를 가져옵니다.
        image_type = json_obj['Image'] # 'Image' 필드를 가져옵니다.

        metadata = {'filename': image_file, 'tags': tags, 'image': image_type}  # 메타데이터를 생성합니다.
        collection.add(ids=[image_file], metadatas=metadata, documents=json.dumps(json_obj)) # collection에 데이터 추가

collection에 저장된 이미지 정보 개수

print('count: ', collection.count())

collection 생성 시 9개 이미지의 정보가 추가되었습니다.

count:  9

collection에 저장된 있는 모든 이미지 정보 출력

print(json.dumps(collection.get(), indent=2))
{
  "ids": [
    "images/001.png",
    "images/002.png",
    "images/003.webp",
    "images/004.webp",
    "images/101.jpg",
    "images/102.png",
    "images/103.jpg",
    "images/H2_001.jpg",
    "images/H2_002.jpg"
  ],
  "embeddings": null,
  "metadatas": [
    {
      "filename": "images/001.png",
      "image": "Illustration",
      "tags": "#Illustration #HolidayMood #StylizedCharacters #BuildingArchitecture #CelebratoryAtmosphere"
    },
    {
      "filename": "images/002.png",
      "image": "Illustration",
      "tags": "#Illustration #WarmColorPalette #Sunset #Couple #Winter"
    },
    {
      "filename": "images/003.webp",
      "image": "Illustration",
      "tags": "#Illustration #OfficeSetting #ProfessionalInteraction #LightingEffects #ArtisticStyle"
    },
    {
      "filename": "images/004.webp",
      "image": "Illustration",
      "tags": "None"
    },
    {
      "filename": "images/101.jpg",
      "image": "Photo",
      "tags": "#Photo #Cityscape #HollywoodSign #FilmIndustry #TouristAttraction"
    },
    {
      "filename": "images/102.png",
      "image": "Illustration",
      "tags": "#Illustration #CityScape #Landmarks #Tourism #ModernDesign"
    },
    {
      "filename": "images/103.jpg",
      "image": "Photo",
      "tags": "None"
    },
    {
      "filename": "images/H2_001.jpg",
      "image": "Cartoon",
      "tags": "#Cartoon #Comic #Conversation #Emotions #Japanese"
    },
    {
      "filename": "images/H2_002.jpg",
      "image": "Cartoon",
      "tags": "#Cartoon #CharacterDesign #Manga #AdachiMitsuru #Chapcomics"
    }
  ],
  "documents": [
    "{\"Image\": \"Illustration\", \"Overall Scene\": \"A whimsical, artistic illustration of a snowy street scene during the holiday season, featuring stylized buildings and characters.\", \"Notable Features\": \"The playful and exaggerated styles in character design and building architecture that give it a festive charm.\", \"Text in the Image\": \"No visible text\", \"Interpretation\": \"The illustration evokes a sense of warmth and celebration, showcasing the spirit of holiday traditions in an imaginative and artistic way.\", \"Tags\": \"#Illustration #HolidayMood #StylizedCharacters #BuildingArchitecture #CelebratoryAtmosphere\"}",
    "{\"Image\": \"Illustration\", \"Overall Scene\": \"A tranquil winter scene where a couple is walking hand in hand down a path surrounded by snow-covered trees.\", \"Notable Features\": \"The warm colors used to depict the sunset and the contrast between the couple's figures and the cooler tones of the snow-covered landscape.\", \"Text in the Image\": \"No visible text\", \"Interpretation\": \"The illustration conveys a sense of companionship and shared warmth amidst a peaceful, cold environment, symbolizing the strength of human connection and love even in harsh conditions.\", \"Tags\": \"#Illustration #WarmColorPalette #Sunset #Couple #Winter\"}",
    "{\"Image\": \"Illustration\", \"Overall Scene\": \"Two adults are sitting across from each other at a table in an office environment, engaged in what appears to be a professional discussion or interview.\", \"Notable Features\": \"The use of lighting and shadows suggests the setting might be at sunrise or sunset, giving the scene a warm, inviting atmosphere. The characters are depicted with exaggerated features typical of cartoon styles, but the overall style is more illustrative than cartoony.\", \"Text in the Image\": \"No visible text\", \"Interpretation\": \"The image likely represents a professional meeting or interview between two individuals, possibly sharing ideas, collaborating, or discussing work matters. The warm lighting suggests a positive and approachable atmosphere, indicative of a productive or supportive conversation.\", \"Tags\": \"#Illustration #OfficeSetting #ProfessionalInteraction #LightingEffects #ArtisticStyle\"}",
    "{\"Image\": \"Illustration\", \"Overall Scene\": \"A modern, well-lit office space with individuals working at various stations.\", \"Notable Features\": \"The use of a limited color palette and the stylistic choice to represent people in silhouette form.\", \"Text in the Image\": \"Text on wall says 'Automa\\u00e7\\u00e3o' and 'Ethoson', suggesting a brand or concept related to automation.\", \"Interpretation\": \"The image may depict a concept of modern workplace automation, where technology is integrated into everyday work processes, as symbolized by the silhouetted figures engaged in their tasks.\"}",
    "{\"Image\": \"Photo\", \"Overall Scene\": \"A realistic photograph of a city skyline with the Hollywood sign prominently displayed.\", \"Notable Features\": \"The clear daytime sky and the distinct shape of the Hollywood sign against the backdrop of hills in the distance.\", \"Text in the Image\": \"No visible text\", \"Interpretation\": \"The photograph captures a quintessential scene from Hollywood, evoking themes of celebrity, glamour, and the allure of the entertainment industry.\", \"Tags\": \"#Photo #Cityscape #HollywoodSign #FilmIndustry #TouristAttraction\"}",
    "{\"Image\": \"Illustration\", \"Overall Scene\": \"An illustration of a city skyline featuring iconic London landmarks such as the Big Ben and Gherkin building.\", \"Notable Features\": \"The clean lines and simple shapes used to represent the cityscape, giving it a modern and sleek appearance.\", \"Text in the Image\": \"London\", \"Interpretation\": \"The illustration showcases London's most recognizable landmarks as a way to symbolize the city's rich history and global influence.\", \"Tags\": \"#Illustration #CityScape #Landmarks #Tourism #ModernDesign\"}",
    "{\"Image\": \"Photo\", \"Overall Scene\": \"An urban street scene with a prominent sign for the London Underground subway system.\", \"Notable Features\": \"The bright blue and white color scheme of the sign, indicative of the London Underground logo design.\", \"Text in the Image\": \"Sign reads 'UNDERGROUND' at the top and 'PUBLIC SUBWAY' below, with a subway train silhouette between them. The sign is blue and white, against a backdrop of buildings typical of a European city street.\"}",
    "{\"Image\": \"Cartoon\", \"Overall Scene\": \"A manga page depicts two characters having a conversation.\", \"Notable Features\": \"The use of bold lines and shading to convey the expressions and emotions of the characters.\", \"Text in the Image\": \"Korean text. It reads '\\uc88b\\uc544' (Japanese) which translates to 'Good' or 'I like it'.\", \"Interpretation\": \"The image suggests a moment of agreement or positive interaction between the two characters, potentially reflecting their relationship or communication in the narrative.\", \"Tags\": \"#Cartoon #Comic #Conversation #Emotions #Japanese\"}",
    "{\"Image\": \"Cartoon\", \"Overall Scene\": \"Four characters from the anime/manga series 'H2' by Adachi Mitsuru, depicted in a cover art style.\", \"Notable Features\": \"The stylized character design and expressive features typical of Japanese manga illustrations.\", \"Text in the Image\": \"There is text on the image indicating it is a 'Chapcomics' publication and the issue number '24'.\", \"Interpretation\": \"This cover art captures the essence of the series, featuring the main characters and conveying an element of their personalities or the story's theme.\", \"Tags\": \"#Cartoon #CharacterDesign #Manga #AdachiMitsuru #Chapcomics\"}"
  ],
  "uris": null,
  "data": null
}

id를 기반으로 이미지 정보 검색

id를 이미지 파일명으로 설정했기 때문에 다음과 같이 id를 검색하는 방법으로 이미지를 검색할 수 있습니다.

json_text = collection.get(ids=['images/H2_002.jpg'])
print(json.dumps(json_text, indent=2))
{
  "ids": [
    "images/H2_002.jpg"
  ],
  "embeddings": null,
  "metadatas": [
    {
      "filename": "images/H2_002.jpg",
      "image": "Cartoon",
      "tags": "#Cartoon #CharacterDesign #Manga #AdachiMitsuru #Chapcomics"
    }
  ],
  "documents": [
    "{\"Image\": \"Cartoon\", \"Overall Scene\": \"Four characters from the anime/manga series 'H2' by Adachi Mitsuru, depicted in a cover art style.\", \"Notable Features\": \"The stylized character design and expressive features typical of Japanese manga illustrations.\", \"Text in the Image\": \"There is text on the image indicating it is a 'Chapcomics' publication and the issue number '24'.\", \"Interpretation\": \"This cover art captures the essence of the series, featuring the main characters and conveying an element of their personalities or the story's theme.\", \"Tags\": \"#Cartoon #CharacterDesign #Manga #AdachiMitsuru #Chapcomics\"}"
  ],
  "uris": null,
  "data": null
}

‘Illustration’ 유형의 이미지 데이터

# collection에 있는 image가 'Illustration'인 data를 출력
json_text = collection.get(
    where={'image': 'Illustration'}
)
print(json.dumps(json_text, indent=2))

metadata의 image값이 ‘Illustration’ 이미지를 모두 찾습니다.

{
  "ids": [
    "images/001.png",
    "images/002.png",
    "images/003.webp",
    "images/004.webp",
    "images/102.png"
  ],
  "embeddings": null,
  "metadatas": [
    {
      "filename": "images/001.png",
      "image": "Illustration",
      "tags": "#Illustration #HolidayMood #StylizedCharacters #BuildingArchitecture #CelebratoryAtmosphere"
    },
    {
      "filename": "images/002.png",
      "image": "Illustration",
      "tags": "#Illustration #WarmColorPalette #Sunset #Couple #Winter"
    },
    {
      "filename": "images/003.webp",
      "image": "Illustration",
      "tags": "#Illustration #OfficeSetting #ProfessionalInteraction #LightingEffects #ArtisticStyle"
    },
    ...
    중략

‘snow two people’ 쿼리에 해당하는 ‘Illustration’ 유형 이미지의 결과 2개

  • query_texts: 제공된 query_texts와 가장 유사한 결과를 반환합니다.
  • n_results: 각 query_embedding 또는 query_texts에 대해 반환할 결과의 개수 입니다.
  • where: 결과를 필터링하는 데 사용되는 Where 유형 딕셔너리입니다. 예: {"$and": ["color" : "red", "price" : {"$gte": 4.20}]}.
# collection에 있는 image가 'Illustration'인 data 중에서 'snow two people'라는 쿼리에 대한 결과를 2개 출력
result = collection.query(
    query_texts=['snow two people'],
    n_results=2,
    where={'image': 'Illustration'}
)
print(result['ids'])
print('\n=====\n')
print(result['distances'])
print('\n=====\n')
print(result['documents'])

distances는 검색어와 각 데이터 간의 의미론적 거리를 표현합니다. result['distances']를 출력하면 작은 값부터 출력됩니다. images/002.png 이미지가 images/001.png보다 query_texts에 더 근접한 값임을 알 수 있습니다. 눈은 두 이미지 모두 포함되는 내용인데 두 사람이라는 부분에서 더 근접한 값으로 인지한 것으로 보입니다.

[['images/002.png', 'images/001.png']]

=====

[[1.4522385851548776, 1.7365406815639153]]

=====

[['{"Image": "Illustration", "Overall Scene": "A tranquil winter scene where a couple is walking hand in hand down a path surrounded by snow-covered trees.", "Notable Features": "The warm colors used to depict the sunset and the contrast between the couple\'s figures and the cooler tones of the snow-covered landscape.", "Text in the Image": "No visible text", "Interpretation": "The illustration conveys a sense of companionship and shared warmth amidst a peaceful, cold environment, symbolizing the strength of human connection and love even in harsh conditions.", "Tags": "#Illustration #WarmColorPalette #Sunset #Couple #Winter"}', '{"Image": "Illustration", "Overall Scene": "A whimsical, artistic illustration of a snowy street scene during the holiday season, featuring stylized buildings and characters.", "Notable Features": "The playful and exaggerated styles in character design and building architecture that give it a festive charm.", "Text in the Image": "No visible text", "Interpretation": "The illustration evokes a sense of warmth and celebration, showcasing the spirit of holiday traditions in an imaginative and artistic way.", "Tags": "#Illustration #HolidayMood #StylizedCharacters #BuildingArchitecture #CelebratoryAtmosphere"}']]

추가 검색 옵션 (Filter)

추가적인 필터 옵션에 관해서는 Filters – ChromaDB Cookbook | The Unofficial Guide to ChromaDB에서 확인하시기 바랍니다.

정리: Ollama와 LLaVA를 이용한 멀티모달 이미지 검색 시스템의 이점

Ollama와 LLaVA 모델을 활용한 로컬LLM으로 이미지를 인식하여 텍스트로 설명을 생성하고, 이를 ChromaDB에 입력하여 텍스트로 이미지를 검색하는 기능을 구현해 보았습니다. 대부분의 이미지들은 내 컴퓨터 저장공간에 쌓여만 가고 사용되지 못하고 있습니다. 단순히 파일명만으로 검색하는 것은 그 한계가 있어, 저장 공간이 낭비되는 경우가 많습니다. 그러나 이를 데이터베이스화하여 검색할 수 있다면 잠자고 있던 이미지들을 적극적으로 활용할 수 있을 것입니다.

이번에는 단순히 이미지를 검색하는 기능까지만 확인해 보았지만 이러한 데이터를 취합할 수 있다는 것은 멀티모달 로컬LLM을 사용하여 RAG(Retrieval-Augmented Generation) 시스템을 구성할 수 있다는 것에도 의미가 있습니다. 이 기술은 텍스트, 이미지 및 기타 데이터 소스를 결합하여 정보 검색과 생성 과정을 강화합니다. 특히, 단순한 텍스트나 숫자 데이터만을 활용하는 것이 아니라 이미지 데이터도 포함하여 RAG 모델을 구축할 수 있습니다. 사용자가 질문을 입력하면 시스템은 관련 이미지와 텍스트 데이터베이스를 검색하여 가장 관련성 높은 정보를 추출한 후 추출된 정보를 기반으로 답변을 생성하여 보다 정확하고 상세한 응답을 제공할 수 있습니다.

본 글은 GPTers에 게시되고 있습니다.
Ollama #5: LLaVA모델과 벡터DB를 활용한 멀티모달 스마트 이미지 검색 시스템 | 지피터스 GPTers

Ollama #5: LLaVA모델과 벡터DB를 활용한 멀티모달 스마트 이미지 검색 시스템

답글 남기기

Scroll to top