Deep Learning

GPT-4를 활용하여 여러 장의 이미지를 사용한 few-shot learning 하기

makeitworth 2024. 8. 9. 17:05

GPT-4 API를 활용한 이미지 분석 가이드

소개

GPT-4(GPT-4-turbo, GPT-4o) 모델은 인풋으로 텍스트 뿐 아니라 이미지도 사용 가능하다는 특징이 있습니다. 이 가이드는 GPT-4 API를 사용하여 이미지를 분석하고 원하는 정보를 추출하는 방법을 소개합니다. 새롭게 GPT API를 활용해보고자 하는 분들을 위해 작성되었으며, API 설정부터 간단한 이미지 분석 예시, 그리고 몇가지 input-output 쌍을 활용한 few-shot 프롬프팅까지 단계 별로 설명합니다.

few-shot prompting 이란?

AI 모델에게 작업을 수행하는 방법을 가르치는 효과적인 기술입니다. 모델에게 몇 가지 예시(보통 2-5개)를 제공하여 원하는 출력 형식이나 작업 수행 방식을 보여줍니다.

1. 환경 설정

먼저 필요한 라이브러리를 설치하고 임포트합니다.
구글 코랩 환경이라면, openai 라이브러리를 매번 설치해야 합니다.

!pip install openai
import json, os
from openai import OpenAI
import pandas as pd

openai의 api key는 사용하여 호출시 돈이 나가기 때문에 반드시 다른 곳에 안전하게 보관하고, load해서 사용하자

2. API 키 설정

OpenAI API를 사용하기 위해서는 API 키가 필요합니다. 보안을 위해 API 키는 별도의 파일에 저장하고 불러오는 것이 좋습니다.

OPENAI_API_KEY = json.load(open(['내 api_key 저장 경로']))['openai_api_key']
client = OpenAI(api_key=OPENAI_API_KEY)

3. API 연결 테스트

API가 정상적으로 작동하는지 간단한 테스트를 수행합니다.

client.chat.completions.create(
            model="gpt-4o-2024-05-13",
            max_tokens=4096,
            messages=[
                {"role": "user", "content": "Hi, I'm Jay. Who are you?"},
            ]
        )
ChatCompletion(id='chatcmpl-9uB4MXQ35JONWlGdnppjYOPv2k8Qd', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content="Hello, Jay! I'm an AI assistant here to help you with information, answer questions, and assist with various tasks. How can I assist you today?", refusal=None, role='assistant', function_call=None, tool_calls=None))], created=1723175946, model='gpt-4o-2024-05-13', object='chat.completion', service_tier=None, system_fingerprint='fp_3aa7262c27', usage=CompletionUsage(completion_tokens=31, prompt_tokens=16, total_tokens=47))

4. 유틸리티 함수 정의

보통 우리가 관심이 있는 것은 위와 같은 긴 response 중에서 content="Hello Jay! I'm an AI developed by OpenAI, and I'm here to help you with information, answer questions, or assist with various tasks. How can I assist you today?" 뿐이기 때문에 간단하게 API를 호출하고, 응답 처리를 하기 위한 유틸리티 함수를 정의합ㄴ디ㅏ.

# 요청 하여 응답 얻는 함수
def get_response(client, model, r_format, messages):
    completion = client.beta.chat.completions.parse(
        model=model,
        temperature=0.1,
        stop=['</json>',],
        response_format=r_format,
        messages=messages
    )
    return completion
# 응답 중 메시지 내용만 파싱하는 함수 만들기
def get_contents(response):
    return response.choices[0].message.content

분석할 이미지를 표시하기 위한 함수를 정의합니다. 이 함수는 이미지의 원본 비율을 유지하면서 크기를 조정합니다.

# 원본 비율을 유지하면서 이미지를 출력하려면, 사용자가 지정한 width 또는 height 중 하나만 설정하고, 다른 값을 자동으로 계산해야 합니다. 이렇게 하면 이미지가 원본 비율을 유지하면서 크기 조정됩니다.
from IPython.display import Image, display
from PIL import Image as PILImage
import requests
from io import BytesIO

def show_img(img_url, width=None, height=None):
    # 원본 이미지의 크기 가져오기
    response = requests.get(img_url)
    img = PILImage.open(BytesIO(response.content))
    orig_width, orig_height = img.size

    # 비율 유지하면서 크기 계산
    if width is not None and height is None:
        height = int((width / orig_width) * orig_height)
    elif height is not None and width is None:
        width = int((height / orig_height) * orig_width)
    elif width is None and height is None:
        width, height = orig_width, orig_height

    display(Image(url=img_url, width=width, height=height))

함수가 잘 동작하는지 테스트 해보는 텍스트-투-텍스트 예제

messages = [{"role": "user", "content": "Hi, I'm Jay. Who are you? Please answer in json format {'answer':''}"}]
json_format = {"type": "json_object"}
response = get_response(client, "gpt-4-turbo", json_format, messages)
get_contents(response)
'{"answer":"Hello Jay, I\'m an AI here to help you with your questions and tasks!"}'

5. 이미지 분석 (멀티모달, 이미지 투 텍스트)

GPT-4의 이미지 분석 능력을 테스트합니다. 이미지를 인풋으로 받아서 텍스트와 시각적 요소를 분석한 결과를 생성합니다.

system_prompt = "Please Extract Text, and image elements in the input image and elebolate the details. Please answer in json format {'Text':'','Image':''}"
img_url = "https://cdn.pixabay.com/photo/2015/09/04/23/00/vintage-922963_1280.jpg"
show_img(img_url, width=300)

messages=[
    {"role": "system", "content": system_prompt},
    {"role": "user", "content": [{
                                "type": "image_url",
                                "image_url": {"url": img_url}
                                }]}
    ]
json_format = {"type": "json_object"}
response = get_response(client, "gpt-4-turbo", json_format, messages)
get_contents(response)
'{\n  "Text": "VANITY FAIR, June, 1914 - Price 25 cts.",\n  "Image": "The image is a vintage cover of the Vanity Fair magazine from June 1914. It features an illustration of five stylishly dressed figures, likely at a social event. The characters are depicted in early 20th-century fashion, with elaborate hats and vibrant, patterned clothing. The setting appears to be outdoors, suggested by the faint outline of buildings and a street lamp in the background. The artwork uses a pastel color palette and has a playful, elegant vibe typical of magazine covers from that era."\n}'

6. Few-shot 프롬프팅을 통한 이미지 레이아웃 분석

few-shot prompting 이란 AI 모델에게 작업을 수행하는 방법을 가르치는 효과적인 기술입니다. 모델에게 몇 가지 예시(보통 2-5개)를 제공하여 원하는 출력 형식이나 작업 수행 방식을 보여줍니다.

Few-shot 프롬프팅의 장점:

  • 빠른 적응: 모델이 새로운 작업을 빠르게 이해하고 수행할 수 있습니다.
  • 일관성: 원하는 출력 형식을 명확하게 지정할 수 있습니다.
  • 맞춤형 응답: 특정 도메인이나 상황에 맞는 응답을 유도할 수 있습니다.

사용 방법:

  • 시스템 메시지에 작업 설명을 포함합니다.
  • 2-3개의 예시 쌍(입력과 원하는 출력)을 제공합니다.
  • 실제 쿼리를 제시합니다.

이를 통해 모델은 프롬프트에 포함된 예시를 학습하고, 새로운 입력에 대해 유사한 방식으로 응답하게 됩니다.

다음은 몇 가지 예시를 프롬프팅에 입력한 퓨샷 프롬프팅의 사용 방법과, 이를 통한 응답의 변화를 보여주는 간단한 예시입니다.

system_prompt = "Graphic design usually has layout composition. What layout is the following image has? Answer in json format [Example]{'Title': 'Big Sale', 'Body':{'image': 'large image of plastic boxes','text':'$2 for 2'}}"
img_url = "https://cdn.pixabay.com/photo/2015/09/04/23/00/vintage-922963_1280.jpg"
messages=[
    {"role": "system", "content": system_prompt},
    {"role": "user", "content": [{
                                "type": "image_url",
                                "image_url": {"url": img_url}
                                }]}
    ]
json_format = {"type": "json_object"}
response = get_response(client, "gpt-4-turbo", json_format, messages)
get_contents(response)
'{\n  "Title": "Vanity Fair",\n  "Subtitle": "June, 1914 - Price 25 cts.",\n  "Body": {\n    "image": "Illustration of four fashionable women and a small dog on a street, depicted in a stylized, early 20th-century art style",\n    "text": "The image features a group of elegantly dressed women in various chic outfits, including wide-brimmed hats and striped garments, suggesting a scene of leisure and social interaction typical of the era\'s upper class. A small dog is also present, adding a playful element to the scene."\n  },\n  "Artist": "Plummer"\n}'

굉장히 잘 묘사한 것 같지만, 자세히 살펴보면 아쉬운 부분이 있다.

  1. 중심 이미지의 패셔너블한 인물들은 5명이 아니라 네명이다. (물어볼 때 마다 4명, 5명 바뀜) 이처럼 gpt-4는 '이미지 이해'에 있어서 대략적인 부분은 잘 하지만, counting, obeject detection 같은 부분은 인간이 느끼기에 쉬운 과제 임에도 아직 오류가 많다고 알려져 있다.
  2. 오른쪽 아래의 "June, 1914 - Price 25 cts." 부분은 글씨도 작고, 아래쪽에 많이 쳐져 있게 때문에 "Subtitle" 보다는 "Footer"로 넣고 싶다.
    예시 이미지 1 출처: 사진: UnsplashMuseums Victoria
# 퓨샷 이미지 예시
show_img("https://images.unsplash.com/photo-1571847140471-1d7766e825ea?q=80&w=3133&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",width=300)

messages=[
    {"role": "system", "content": system_prompt},
    {"role": "user", "content": [{
                                "type": "image_url" ,
                                "image_url": {"url": "https://images.unsplash.com/photo-1571847140471-1d7766e825ea?q=80&w=3133&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"}
                                }]},
    {"role": "assistant", "content": "{'Title': 'LEAVE YOUR FILMS HERE', 'Body':{'image': 'Two packs of photos with letter Kodak Verichrome Film','text':'KODAK PRINT', 'text':'WE USE VELOX'}, 'FOOTER': 'SPECIAL QUICK SERVICE'}"},
    {"role": "user", "content": [{
                            "type": "image_url",
                            "image_url": {"url":"https://images.unsplash.com/photo-1580130579792-c4f6ce908069?q=80&w=2979&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" }
                            }]},
    {"role": "assistant", "content": "{'Title': 'KEEPING UP', 'Body':{'image': 'abstract representation of a face with geometric shapes and a prominent eye'}, 'FOOTER': 'with SCIENCE'}"},
    {"role": "user", "content": [{
                            "type": "image_url",
                            "image_url": {"url": img_url}
                            }]}
    ]
response = get_response(client, "gpt-4-turbo", json_format, messages)
get_contents(response)
'{\n  "Title": "VANITY FAIR",\n  "Body": {\n    "image": "Illustration of four fashionable individuals in early 20th-century attire, walking with a small dog",\n    "text": "June, 1914 - Price 25 cts."\n  }\n}'

"June, 1914 - Price 25 cts." 문구의 분류가 기대하던대로 Footer에 넣어주진 않았지만, 이제 Subtitle이 아닌 이미지로 내려갔다. 그리고 이미지 묘사도 위에 퓨샷 인풋이 없었을 때는 장황하게 길고, 텍스트 키의 값도 이미지에 대한 묘사가 들어가 있었는데, 퓨샷 예시를 같이 넣어준 다음에는, 예시와 유사하게 image의 묘사도 간결해지고, 텍스트의 값은 이미지에 있는 글자의 추출 값만 들어가게 되었다.

7. 결론

GPT-4 API를 사용하여 이미지를 분석하는 방법을 배웠습니다. 단순한 이미지 분석부터 few-shot 학습을 통한 복잡한 레이아웃 분석까지 다양한 기법을 살펴보았습니다. GPT-4는 이미지 이해에 있어 대체로 우수한 성능을 보이지만, 세부적인 요소 카운팅이나 객체 감지 등에서는 아직 개선의 여지가 있습니다.
또한 system prompt 입력에 따라 결과물의 퀄리티가 달라집니다. 프롬프트에 있었던 스펠링 오류를 바로잡는 것 만으로도 더 좋은 결과물을 얻을 수 있기 때문에 항상 프롬프트를 테스트해보세요. gpt에게 나의 프롬프트를 평가하고 개선하게 만드는 것도 좋은 방법입니다!