LLaVA-o1: 비전-언어 모델의 구조적 추론 고도화
LLaVA-o1가 구조화된 문제 해결을 통해 멀티모달 AI의 추론 과제를 어떻게 해결하는지 알아보세요. 해당 모델의 데이터셋, 기능, 그리고 W&B Weave를 통한 성능 분석을 확인할 수 있습니다. 이 글은 AI가 번역한 기사입니다. 오역이 있을 수 있으니 댓글로 알려주세요.
Created on September 12|Last edited on September 12
Comment
비전-언어 모델 분야는 멀티모달 입력을 이해하고 해석하는 능력에서 큰 진전을 이루었습니다. 그럼에도 불구하고, 많은 기존 VLM은 체계적이고 구조화된 추론을 요구하는 과제에서 상당한 어려움에 직면합니다. 예를 들어, 다음과 같은 모델들은 OpenAI’s GPT-4o Vision 이와 유사한 시스템들은 직접적인 답변 생성에서는 좋은 성능을 보이지만, 논리적 사고, 세밀한 분석, 또는 수학적 추론이 요구되는 복잡한 과제에서는 종종 부족함을 드러냅니다.
이러한 한계는 특히 추론이 많이 요구되는 상황에서 오류로 이어질 수 있습니다.
이러한 과제에 대응하여 연구자들은 개발했습니다 LLaVA-o1LLaVA-o1는 구조화된 다단계 추론 형식으로 응답하도록 명시적으로 학습되어, 기존 멀티모달 시스템의 한계를 보완하도록 설계되었습니다. 이 글에서는 다음을 사용해 해당 모델을 실험해 보겠습니다 Weave 평가LLaVA-o1의 성능을 더 깊이 이해하기 위해.

목차
LLaVA-o1가 비전-언어 모델로 동작하는 방식LLaVA-o1 데이터셋 LLaVA-o1의 단계별 빔 서치LLaVA-o1과 기반 모델 비교LLaVA-o1의 한계 LLaVA-o1로 추론 실행하기 Weave 평가로 LLaVA-o1 분석하기 Weave로 결과 분석하기 Weave로 버그 잡기결론: LLaVA는 비전-언어 모델의 진전을 이끌었다
LLaVA-o1가 비전-언어 모델로 동작하는 방식
학습 과정에서 LLaVA-o1는 응답을 네 가지 뚜렷한 단계로 구성하도록 학습되어, 명확성과 체계적인 문제 해결을 보장합니다.
네 가지 단계는 다음과 같습니다:
- 첫 번째 단계(요약)은(는) 해결해야 할 대상에 대한 상위 수준의 이해를 제공하면서 과제를 개괄하고 문제의 맥락을 설정합니다.
- 다음으로, 캡션 단계에서 모델은 이미지를 분석하여 관련 시각적 요소를 식별하고, 과제에 핵심적인 구성 요소들을 상세히 서술합니다.
- The 추론 이어서 추론 단계가 진행되며, 모델은 단계별 논리 분석을 통해 중간 통찰을 도출하고, 응답이 체계적인 사고에 기반하도록 보장합니다.
- 마지막으로, 결론 단계에서 모델은 이전 단계의 결과를 종합하여 과제나 질의에 직접 답하는 일관되고 정확한 답변을 도출합니다.
다음은 논문에서 발췌한 스크린샷으로, 베이스 모델과 LLaVA-o1 모델의 응답을 함께 보여줍니다:

LLaVA-o1 데이터셋
LLaVA-o1은 추론 중심 과제를 강화하도록 정교하게 설계된 멀티모달 데이터셋인 LLaVA-o1-100k로 학습되었습니다. 논문에 따르면, 이 데이터셋은 MMStar, MathVista, MMVet 등 다양한 고난도 시각 질의응답(VQA) 벤치마크에서 예제를 수집·통합하고 주석을 추가하여 구성되었습니다. 이러한 데이터셋들은 시각적 콘텐츠를 체계적으로 해석하고, 경향을 분석하며, 논리적 문제 해결을 수행해야 하는 추론 중심 문항에 초점을 맞춘 점을 기준으로 선정되었습니다.
LLaVA-o1-100k 데이터셋을 만들기 위해, 저자들은 활용했습니다 GPT-4o 각 과제에 대해 단계별로 세밀한 주석을 생성하기 위해서입니다. 이 구조화된 주석 과정은 LLaVA-o1에서 사용하는 추론 프레임워크를 반영하여 Summary, Caption, Reasoning, Conclusion의 네 가지 뚜렷한 단계로 나뉜 추론 체인을 만들어냈습니다. 이러한 주석은 모델이 학습 중에 익힐 수 있는 명확하고 체계적인 문제 해결 템플릿을 제공합니다.
저자들이 데이터셋을 구성하는 데 사용한 프롬프트는 다음과 같습니다:
"I have an image and a question that I want you to answer. I need you to strictly follow the format with fourspecific sections: SUMMARY, CAPTION, REASONING, and CONCLUSION. It is crucial that you adhere to this structure exactlyas outlined and that the final answer in the CONCLUSION matches the standard correct answer precisely.To explain further: In SUMMARY, briefly explain what steps you'll take to solve the problem. In CAPTION,describe the contents of the image, specifically focusing on details relevant to the question.In REASONING, outline a step-by-step thought process you would use to solve the problem based on the image.In CONCLUSION, give the final answer in a direct format, and it must match the correct answer exactly.If it's a multiple choice question, the conclusion should only include the option without repeating what the option is.Here's how the format should look: <SUMMARY> [Summarize how you will approach the problem and explain the steps you willtake to reach the answer.] </SUMMARY> <CAPTION> [Provide a detailed description of the image, particularly emphasizingthe aspects related to the question.] </CAPTION> <REASONING> [Provide a chain-of-thought, logical explanation of the problem.This should outline step-by-step reasoning.] </REASONING> <CONCLUSION> [State the final answer in a clear and direct format.It must match the correct answer exactly.] </CONCLUSION> (Do not forget </CONCLUSION>!) Please apply this format meticulouslyto analyze the given image and answer the related question, ensuring that the answer matches the standard one perfectly."
이 데이터셋은 ShareGPT4V, A-OKVQA 같은 범용 VQA 벤치마크의 예제와 ScienceQA, AI2D 같은 과학 분야 특화 데이터셋을 결합합니다. 여기에 ChartQA, CLEVR-Math와 같은 전문 데이터셋에서 추론 난도가 높은 과제도 포함하여 다양한 추론 시나리오를 폭넓게 포괄합니다. 서로 다른 도메인의 과제를 통합함으로써, LLaVA-o1-100k 데이터셋은 견고한 학습 기반을 제공하여 모델이 복잡한 실제 멀티모달 추론 과제를 효과적으로 처리할 수 있도록 합니다.
이 정교하게 선별된 데이터셋은 체계적이고 구조화된 추론 과제에서 LLaVA-o1이 뛰어난 성능을 발휘하도록 하는 기반을 이루며, 전통적인 VQA 데이터셋으로 학습된 모델들과 LLaVA-o1을 뚜렷이 구분 짓습니다.
LLaVA-o1의 단계별 빔 서치
학습 측면의 혁신 외에도, LLaVA-o1은 추론 단계에서 새로운 단계별 빔 서치 방법을 도입합니다. 이 기법은 문제 해결 과정의 각 단계에서 여러 개의 추론 경로를 생성하고 평가하여, 가장 일관되고 정확한 경로를 선택합니다. 이 방법은 이론적으로 응답을 정제해 신뢰성과 정확도를 높일 수 있지만, 우리는 평가에서는 이를 사용하지 않기로 했습니다.
제 분석과 논문의 결과에 따르면, LLaVA-o1의 성능 향상의 대부분은 단계별 빔 서치가 아니라 LLaVA-o1-100k 데이터셋과 구조화된 추론 과정에서 비롯된 것으로 보입니다. 예를 들어, 논문에서는 구조화된 추론 프레임워크만으로도 여러 벤치마크에서 평균 6.9%의 향상을 달성했다고 보고하며, 단계별 빔 서치 도입은 MMVet, MathVista처럼 추론 난도가 높은 과제에서 1–2% 수준의 미미한 개선만을 추가했다고 밝힙니다.
또한, 단계별 빔 서치는 계산 오버헤드를 크게 증가시킵니다실시간 애플리케이션에서의 실용성을 떨어뜨립니다. 데이터셋과 추론 프레임워크가 성능 향상의 대부분을 차지하고, 빔 서치로 인한 추가 이득이 복잡성 증가를 정당화하지 못하기 때문에, 우리는 평가를 LLaVA-o1의 구조화된 추론과 데이터셋 기반 학습에 집중하기로 했습니다.
LLaVA-o1과 기반 모델 비교
LLaVA-o1은 기반으로 Llama-3.2-11B-Vision-Instruct 모델을 사용합니다. 이 기반 모델은 멀티모달 입력을 처리하고 직접적인 답변을 제공하도록 설계된 강력한 비전-언어 시스템입니다. 다만 일반적인 작업에서는 효과적이지만, 단순 예측 기법에 의존하는 특성 때문에 추론 난도가 높은 상황에서는 한계를 보입니다.
LLaVA-o1의 실제 영향을 평가하기 위해, 저자들은 원래의 기반 모델뿐 아니라 동일한 LLaVA-o1-100k 데이터셋으로 학습하되 구조화된 추론 단계 없이 직접적인 질문-응답에만 초점을 맞춘 기준용 파인튜닝 모델과도 비교를 수행했습니다.
이 파인튜닝된 기준 모델은 LLaVA-o1에서 도입된 추론 프레임워크의 효과를 분리해 확인할 수 있으므로, 공정한 평가를 위해 반드시 필요합니다.
💡
기준용 파인튜닝 모델도 추가 데이터의 이점을 얻지만, LLaVA-o1을 규정하는 구조화된 추론 과정은 갖추지 못했습니다. 반면 LLaVA-o1은 요약, 캡션, 추론, 결론으로 구성된 다단계 추론 프레임워크를 적용하여, 기준 모델로는 수행하기 어려운 방식으로 복잡한 문제를 체계적으로 처리합니다.
MMStar, MathVista, MMVet, AI2D와 같은 벤치마크에서의 평가를 통해, LLaVA-o1은 원래의 기반 모델과 Q/A 중심의 파인튜닝 기준 모델 모두에 비해 크게 향상된 성능을 보여주었습니다. 이러한 벤치마크 결과는 구조화되고 해석 가능한 출력을 생성하는 능력을 바탕으로, 논리적 추론, 수학적 문제 해결, 과학적 분석에서 뛰어난 역량을 갖추었음을 입증합니다.
LLaVA-o1의 한계
그러나 LLaVA-o1은 추론 난도가 높은 작업에서는 강력한 성능을 보였지만, 구조화된 추론이 꼭 필요하지 않은 단순한 작업에서는 효과가 떨어졌습니다. 예를 들어 MMBench 데이터셋처럼 비교적 단순한 질의를 다루는 일반 시각적 질의응답 과제에서는, LLaVA-o1의 구조화된 추론 방식이 성능을 약간만 개선했으며, 경우에 따라서는 소폭의 성능 저하로 이어지기도 했습니다.
마찬가지로 기본 도해 해석에 초점을 둔 AI2D 벤치마크에서도, 이 모델은 직접 학습 방식에 비해 성능이 떨어졌습니다. 다단계 추론 프레임워크가 이러한 단순한 문제들을 과도하게 복잡하게 만드는 경향을 보여, 추가적인 추론 단계가 항상 직접적인 답변을 요구하는 과제에 유리한 것은 아님을 시사합니다. 이러한 과제들은 비전-언어 모델을 해결하려는 문제의 복잡도에 맞춰 설계하는 것이 중요함을 강조하며, 구조화된 추론이 복잡한 상황에서는 뛰어나지만, 직관적인 과제에서는 오히려 성능을 저해할 수 있음을 보여줍니다.
LLaVA-o1로 추론 실행하기
LLaVA-o1로 추론을 실행하려면, 먼저 Python 3.10과 함께 필요한 Python 라이브러리들을 설치하세요.
pip install torch==2.4.0 transformers==4.45.0 pillow==10.4.0 datasets==3.1.0 weave==0.51.18 openai==1.55.1
코드가 사용자의 시스템에서 원활히 실행되도록 패키지의 정확한 버전을 명시하는 편입니다. 다만 이 튜토리얼이 작성된 시점보다 훨씬 나중에 이 글을 읽고 있다면, 패키지를 더 최신 버전으로 업그레이드해야 할 수도 있습니다.
💡
이제 다음 스크립트를 사용해 LLaVA-o1로 추론을 실행할 수 있습니다.
import torchfrom transformers import MllamaForConditionalGeneration, AutoProcessorfrom PIL import Imagefrom datasets import load_datasetimport weaveweave.init("llava_single_inference")@weave.op()def predict(question: str, image):"""Run inference with LLaVA on a single sample."""# Load model and processor inside the functionMODEL_ID = "Xkev/Llama-3.2V-11B-cot"model = MllamaForConditionalGeneration.from_pretrained(MODEL_ID, torch_dtype=torch.bfloat16, device_map="auto")processor = AutoProcessor.from_pretrained(MODEL_ID)pil_image = Image.open(image) if isinstance(image, str) else image# Prepare inputsmessages = [{"role": "user", "content": [{"type": "image"},{"type": "text", "text": question}]}]input_text = processor.apply_chat_template(messages, add_generation_prompt=True)inputs = processor(pil_image,input_text,add_special_tokens=False,return_tensors="pt").to(model.device)# Generate outputoutput = model.generate(**inputs,max_new_tokens=2048,return_dict_in_generate=True,output_scores=True)# Decode outputoutput_text = processor.decode(output.sequences[0], skip_special_tokens=True)return {"model_output": output_text}def create_single_sample(dataset_name: str, split: str = "test"):"""Load a single sample from the dataset."""dataset = load_dataset(dataset_name, split=split, cache_dir="./cache")example = dataset[0]question = example["qa"][0]["query"]image = example["image"]pil_image = Image.open(image) if isinstance(image, str) else imagereturn {"question": f"Based on the image, answer the following question: {question}","image": pil_image}if __name__ == "__main__":# Load a single sample and perform inferencesample = create_single_sample("TeeA/ChartQA")result = predict(sample["question"], sample["image"])# Print the resultprint(f"Question: {sample['question']}")print(f"Model Output: {result['model_output']}")
이 예제에서는 MllamaForConditionalGeneration transformers의 클래스를 사용해 LLaVA-o1 모델을 로드하는데, 이는 파인튜닝된 시각적 입력과 텍스트 입력이 모두 필요한 추론 과제용.
The AutoProcessor 클래스는 모델에 입력을 준비하는 데 사용됩니다. 이 클래스는 텍스트를 토크나이즈하고, 이미지를 호환 가능한 형식으로 변환하며, 원시 데이터와 모델 요구 사항 사이의 원활한 연계를 보장하는 모델 준비 입력을 생성합니다. 또한 이 스크립트는 머신러닝 작업에서 데이터셋의 로드와 관리를 단순화하는 datasets 라이브러리도 사용하여 ChartQA와 같은 벤치마크에 접근할 수 있게 합니다.
W&B Weave 은(는) 워크플로에 통합되어 추론 과정을 추적하고 분석합니다. The @weave.op 데코레이터는 predict 함수를 래핑하여 추론 중 입력과 출력을 자동으로 로깅할 수 있게 합니다. 이러한 로깅은 디버깅, 모델 평가, 특정 작업에서의 모델 동작을 심층 분석하는 데 유용합니다. 스크립트를 실행한 뒤에는 weave 내부에 새로운 트레이스가 표시됩니다:

이 트레이스는 로깅 메커니즘을 사용하지 않을 때보다 덜 눈에 띌 수 있는 버그를 포착하고, 프로덕션 환경에서 모델이 어떻게 동작하는지 시각화하는 데 유용합니다. 또한 트레이스에서는 LLaVA-o1이 답을 생성할 때 사용하는 추론 과정도 확인할 수 있습니다.
Weave 평가로 LLaVA-o1 분석하기
두 모델의 정성적 응답 차이를 살펴보기 위해, 전체 MMVet 데이터셋을 사용하여 LLaVA-o1과 기반 모델인 Llama-3.2-11B-Vision-Instruct를 종합적으로 비교 평가했습니다. 목적은 LLaVA-o1의 구조화된 추론 프레임워크가 기반 모델의 직접 응답 방식과 비교했을 때, 추론 난도가 높은 멀티모달 과제에서 성능에 어떤 영향을 미치는지 분석하는 것이었습니다.
이상적인 비교는 모델을 포함하겠지만 파인튜닝된 구조화된 추론 단계 없이, 즉 직접적인 Q/A 쌍에만 초점을 맞춰 MMVet 데이터셋에서 학습된 그러한 모델은 공개되지 않았습니다. 따라서 본 평가는 LLaVA-o1과 기반 모델 간의 성능 차이를 파악하는 데 초점을 둡니다.
이 비교가 전적으로 공정한 것은 아니라는 점을 유의해야 합니다. 기반 모델은 MMVet 데이터셋의 어떤 작업이나 Q/A 쌍에도 노출된 적이 없어, LLaVA-o1은 구조화된 추론 프레임워크와 데이터셋 특화 최적화를 모두 활용할 수 있는 이점을 갖습니다. 이러한 한계에도 불구하고, 본 평가는 구조화된 추론이 모델 성능에 어떤 영향을 미치는지에 대한 의미 있는 통찰을 제공하며, 성공과 실패의 구체적인 지점을 부각합니다.
💡
이번 평가에서는 여섯 가지 핵심 멀티모달 역량을 평가하는 전체 MMVet 데이터셋을 사용했습니다:
- 인식,
- 지식 추론,
- 언어 생성,
- 공간 추론,
- 수학
평가는 다음을 사용하여 수행되었습니다 Weave 평가이는 입력, 출력, 성능 지표를 나란히 비교할 수 있게 해 주었습니다. Weave의 상세 비교 뷰는 LLaVA-o1의 구조화된 추론 프레임워크가 특정 과제를 해결하는 데 어떻게 기여했는지, 그리고 베이스 모델의 보다 단순한 직접 응답 방식이 어디에서 격차를 초래했는지를 파악하는 데 매우 유용했습니다.
평가는 두 모델 모두에게 MMVet의 전체 이미지-질문 쌍에 대해 예측을 생성하도록 요구하여, 다양한 추론 능력을 입증하도록 했습니다. Weave를 사용해 각 예측을 해당 입력과 정답과 함께 기록했습니다. 이 과정은 Weave Evaluations 프레임워크를 통해 완전히 자동화되었으며, 파이프라인에 모델과 데이터셋을 통합하는 작업만 필요했습니다.
Weave의 내장 도구 덕분에 모든 입력, 출력, 예측이 꼼꼼하게 추적되었습니다. 이 설정은 정량 분석과 모델 동작에 대한 정성적 탐색을 모두 가능하게 했습니다. Weave의 비교 뷰를 통해 LLaVA-o1의 구조화된 추론 방식이 뛰어난 성과를 보인 경우와, 베이스 모델이 직접 응답 방식 때문에 어려움을 겪은 경우를 식별할 수 있었습니다. 전체 MMVet 데이터셋은 두 모델이 멀티모달 추론 과제를 어떻게 처리하는지 평가하기 위한 풍부하고 다양한 테스트베드를 제공했습니다.
LLaVA-o1과 베이스 모델인 Llama-3.2-11B-Vision-Instruct는 각자의 구현에서 가져왔으며 Hugging Face, 논문에 명시된 구성과 가중치를 그대로 사용해 일관성을 보장했습니다. 복합적이고 통합적인 비전-언어 추론을 위해 설계된 벤치마크인 MMVet은 LLaVA-o1의 구조화된 추론을 베이스 모델의 단순한 접근과 비교해 시험하기에 견고한 프레임워크를 제공했습니다. 이러한 설정은 Weave의 자동화된 평가 기능과 결합되어 두 모델을 공정하고 통찰력 있게 비교할 수 있게 했습니다.
import osimport jsonimport asyncioimport loggingfrom datasets import load_datasetfrom weave import Evaluation, Modelimport weavefrom PIL import Imagefrom io import BytesIOimport base64import torchfrom transformers import MllamaForConditionalGeneration, AutoProcessorfrom typing import Dict, Any, ClassVarfrom openai import OpenAIimport time# Set up logginglogging.basicConfig(level=logging.INFO)logger = logging.getLogger(__name__)weave.init("llava_model_eval")def get_pil_image(image):"""Convert various image formats to PIL Image"""try:if isinstance(image, str):return Image.open(image)elif isinstance(image, Image.Image):return imageelif isinstance(image, bytes):return Image.open(BytesIO(image))elif isinstance(image, str) and image.startswith('data:image'):base64_data = image.split(',')[1] if ',' in image else imageimage_bytes = base64.b64decode(base64_data)return Image.open(BytesIO(image_bytes))else:raise ValueError(f"Unsupported image type: {type(image)}")except Exception as e:logger.error(f"Error processing image: {e}")raisedef evaluate_chart_answer(question: str, ground_truth: str, predicted_answer: str):"""Use GPT-4 to evaluate model answers"""client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))# # Quick check for exact match in prediction# if str(ground_truth).lower() in str(predicted_answer).lower():# return {"correct": 1}messages = [{"role": "user","content": f"""You are a judge evaluating answers to chart questions. Return ONLY a JSON object with no additional text.Question: {question}Ground Truth Answer: {ground_truth} ###Predicted Answer: {predicted_answer}Compare these answers and return a JSON object in this exact format:{{"correct": <0 or 1>}}Rules for scoring:- Score 1 if the answer is correct (or correct within reasonable error margin)- Score 1 if the correct answer is contained somewhere in the model response- Score 1 if the answer is right but the formatting is not exactly the same- Score 0 if the answer is clearly incorrect"""}]retries = 0max_retries = 10base_delay = 10while retries < max_retries:try:response = client.chat.completions.create(model="gpt-4o-2024-08-06",messages=messages,max_tokens=1024,temperature=0)response_text = response.choices[0].message.content.strip()try:return json.loads(response_text)except json.JSONDecodeError:import rejson_match = re.search(r'\{.*\}', response_text, re.DOTALL)if json_match:try:return json.loads(json_match.group())except:logger.warning("Failed to parse JSON match")logger.warning("Failed to parse response as JSON")return {"correct": 0}except Exception as e:logger.error(f"Error during evaluation (attempt {retries + 1}): {e}")retries += 1if retries < max_retries:delay = base_delay * (2 ** (retries - 1))logger.info(f"Retrying in {delay} seconds...")time.sleep(delay)else:logger.error("Max retries reached")return {"correct": 0}@weave.opdef chart_qa_scorer(question: str, ground_truth: str, model_output: dict) -> Dict[str, Any]:"""Weave operation for scoring model outputs"""try:if not isinstance(model_output, dict) or 'model_output' not in model_output:logger.error("Invalid model output format")return {'score': 0}result = evaluate_chart_answer(question=question,ground_truth=ground_truth,predicted_answer=model_output['model_output'])return {'score': result.get('correct', 0)}except Exception as e:logger.error(f"Scoring error: {e}")return {'score': 0}def get_model_prediction(model, question: str, image):"""Get prediction from model for given question and image"""try:if not model.model or not model.processor:raise ValueError("Model or processor not initialized")pil_image = get_pil_image(image)messages = [{"role": "user", "content": [{"type": "image"},{"type": "text", "text": question}]}]try:input_text = model.processor.apply_chat_template(messages, add_generation_prompt=True)except Exception as e:logger.error(f"Error applying chat template: {e}")raiseinputs = model.processor(pil_image,input_text,add_special_tokens=False,return_tensors="pt").to(model.model.device)input_tokens = len(inputs.input_ids[0])output = model.model.generate(**inputs,max_new_tokens=2048,return_dict_in_generate=True,output_scores=True)output_tokens = len(output.sequences[0]) - input_tokensoutput_text = model.processor.decode(output.sequences[0], skip_special_tokens=True)return {'model_output': str(output_text),'model': model.__class__.__name__,'usage': {'prompt_tokens': input_tokens,'completion_tokens': output_tokens,'total_tokens': input_tokens + output_tokens}}except Exception as e:logger.error(f"Error during prediction: {e}")return {'model_output': f"Error during prediction: {str(e)}",'model': model.__class__.__name__,'usage': {'prompt_tokens': 0,'completion_tokens': 0,'total_tokens': 0}}class BaseModel(Model):model: ClassVar[MllamaForConditionalGeneration] = Noneprocessor: ClassVar[AutoProcessor] = None@staticmethoddef load_model():logger.info("Loading BaseModel...")model_id = "meta-llama/Llama-3.2-11B-Vision-Instruct"try:BaseModel.model = MllamaForConditionalGeneration.from_pretrained(model_id,torch_dtype=torch.bfloat16,device_map="auto",)BaseModel.processor = AutoProcessor.from_pretrained(model_id)if not BaseModel.model or not BaseModel.processor:raise ValueError("Failed to load model or processor")logger.info("BaseModel loaded successfully")except Exception as e:logger.error(f"Error loading BaseModel: {e}")raise@staticmethoddef clear_model():logger.info("Clearing BaseModel from memory...")BaseModel.model = NoneBaseModel.processor = Nonetorch.cuda.empty_cache()@weave.opdef predict(self, question: str, image):if not BaseModel.model or not BaseModel.processor:raise ValueError("BaseModel is not loaded.")out = get_model_prediction(BaseModel, question, image)try:if "assistant" in str(out['model_output']):out['model_output'] = str(out['model_output']).split("assistant")[1].strip()except Exception as e:logger.warning(f"Error splitting output on 'assistant': {e}")return outclass LlavaModel(Model):model: ClassVar[MllamaForConditionalGeneration] = Noneprocessor: ClassVar[AutoProcessor] = None@staticmethoddef load_model():logger.info("Loading LlavaModel...")model_id = "Xkev/Llama-3.2V-11B-cot"try:LlavaModel.model = MllamaForConditionalGeneration.from_pretrained(model_id,torch_dtype=torch.bfloat16,device_map="auto",)LlavaModel.processor = AutoProcessor.from_pretrained(model_id)if not LlavaModel.model or not LlavaModel.processor:raise ValueError("Failed to load model or processor")logger.info("LlavaModel loaded successfully")except Exception as e:logger.error(f"Error loading LlavaModel: {e}")raise@staticmethoddef clear_model():logger.info("Clearing LlavaModel from memory...")LlavaModel.model = NoneLlavaModel.processor = Nonetorch.cuda.empty_cache()@weave.opdef predict(self, question: str, image):if not LlavaModel.model or not LlavaModel.processor:raise ValueError("LlavaModel is not loaded.")out = get_model_prediction(LlavaModel, question, image)try:if "<CONCLUSION>" in str(out['model_output']):out['model_output'] = str(out['model_output']).split("<CONCLUSION>")[1].strip()except Exception as e:logger.warning(f"Error splitting output on '<CONCLUSION>': {e}")return outdef create_evaluation_dataset(dataset_name: str, split: str = "test", eval_size: int = 100):"""Create evaluation dataset from the HuggingFace MMVet dataset."""try:# Load and shuffle datasetdataset = load_dataset(dataset_name, split=split, cache_dir="./cache").shuffle(seed=42)# Select the last `eval_size` examplesstart_idx = max(0, len(dataset) - eval_size)eval_data = dataset.select(range(0, len(dataset)))evaluation_dataset = []for example in eval_data:question = example["question"]ground_truth = example["answer"]image = example["image"]# Ensure image is in PIL formatif isinstance(image, str):pil_image = Image.open(image)if pil_image.mode == "RGBA":pil_image = pil_image.convert("RGB")elif isinstance(image, Image.Image):pil_image = imageelse:raise ValueError(f"Unexpected image type: {type(image)}")# Append formatted example to evaluation datasetevaluation_dataset.append({"question": "Based on the image, answer the following question: " + question,"ground_truth": ground_truth,"image": pil_image})if not evaluation_dataset:raise ValueError("No examples were processed from the dataset")logger.info(f"Created evaluation dataset with {len(evaluation_dataset)} examples")return evaluation_datasetexcept Exception as e:logger.error(f"Error creating evaluation dataset: {e}")raiseasync def run_evaluations():"""Run evaluations on both models"""try:eval_dataset = create_evaluation_dataset("lmms-lab/MMVet")results = {}# Evaluate BaseModellogger.info("\nEvaluating BaseModel...")base_model = BaseModel()base_model.load_model()evaluation = Evaluation(dataset=eval_dataset,scorers=[chart_qa_scorer],name="BaseModel Evaluation")results["base_model"] = await evaluation.evaluate(base_model)base_model.clear_model()# Evaluate LlavaModellogger.info("\nEvaluating LlavaModel...")llava_model = LlavaModel()llava_model.load_model()evaluation = Evaluation(dataset=eval_dataset,scorers=[chart_qa_scorer],name="LlavaModel Evaluation")results["llava_model"] = await evaluation.evaluate(llava_model)llava_model.clear_model()return resultsexcept Exception as e:logger.error(f"Error during evaluation: {e}")raiseif __name__ == "__main__":try:results = asyncio.run(run_evaluations())logger.info("Evaluation completed successfully")logger.info(f"Results: {results}")except Exception as e:logger.error(f"Failed to run evaluations: {e}")
이 평가를 수행하기 위해 전체 MMVet 데이터셋을 준비했으며, 인식, OCR, 지식 기반 추론, 언어 생성, 공간 추론, 수학의 여섯 가지 핵심 멀티모달 역량에 걸친 과제가 모두 포함되도록 보장했습니다. 평가 프레임워크는 LLaVA-o1과 베이스 모델 모두에 대해 커스텀 래퍼를 사용했으며, 이를 통해 예측이 일관된 형식으로 생성되고 기록되도록 했습니다. 이 래퍼들은 전처리를 처리하여 필요할 경우 이미지를 PIL 형식으로 변환하고, 모델과의 호환성을 유지하기 위해 질문을 토크나이즈했습니다.
GPT-4o를 사용해 맞춤형 채점 메커니즘을 개발했으며, 이는 모델 출력의 정답 여부를 정답 데이터와 대조하여 평가했습니다. 이 스코어러는 부분 정답, 형식 불일치, 허용 오차 범위 내의 답변 등 경계 사례를 처리하기 위한 구조화된 규칙 집합을 구현했습니다. GPT-4o의 평가는 각 예측에 대해 응답의 정오를 표시하는 JSON 객체를 반환했습니다. 이 자동화된 채점 파이프라인은 일관성을 보장했을 뿐만 아니라, 모델 출력의 미묘한 추론과 논리적 응집성까지 고려했습니다.
결과는 입력, 예측, 점수를 나란히 비교할 수 있게 해 주는 도구인 Weave에 기록되고 시각화되었습니다. Weave의 강력한 비교 뷰는 모델 성능에 대한 정성적·정량적 분석을 상세하게 수행할 수 있게 해 주었으며, LLaVA-o1의 구조화된 추론 프레임워크가 뛰어난 영역과 베이스 모델의 직접 응답 방식이 효과적이었거나 부족했던 지점을 부각했습니다. 이 포괄적인 설정은 구조화된 추론과 계산 효율성 간 상호작용에 대한 통찰을 제공하며, 다양한 멀티모달 과제 전반에서 모델 동작을 보다 미세한 관점으로 조망할 수 있게 합니다.
Weave로 결과 분석하기
Weave Evaluations를 활용해 각 모델이 어디에서, 왜 성공하거나 어려움을 겪었는지 명확히 파악할 수 있었으며, 구조화된 추론이 모델 성능에 어떤 영향을 미치는지에 대한 미묘한 관점을 얻었습니다. 이번 실험에서 LLaVA-o1 모델은 LLaVA-o1-100k 데이터셋으로 어떤 형태의 파인튜닝도 적용하지 않은 베이스 모델보다 약간 더 좋은 성능을 보였습니다. 전반적으로 이 기법은 여러분의 모델 성능을 향상시킬 가능성이 있지만, 베이스라인 모델과 비교했을 때 성능이 실제로 개선되었는지 철저히 평가할 것을 강력히 권장합니다. 아래는 두 모델의 결과를 모두 확인할 수 있는 Weave 대시보드의 스크린샷입니다.


Weave 평가를 활용하면 각 모델이 생성한 정확한 응답을 깊이 들여다보고 정답과 빠르게 비교할 수 있습니다. 이를 통해 정답 여부뿐만 아니라, 모델이 그 답에 도달하기 위해 거친 추론 경로까지 분석할 수 있습니다. 예를 들어, 공간 이해나 수학적 추론이 필요한 복합 멀티모달 과제에서 정답으로 이어지는 LLaVA-o1의 구조화된 추론 패턴을 식별하는 한편, 같은 프레임워크가 더 단순한 과제에서는 과도하게 복잡해질 수 있는 지점도 동시에 관찰할 수 있습니다.
Weave의 상세한 시각화 도구를 사용하면 예측 답변과 정답 간의 불일치를 쉽게 발견하여 실패 사례를 정확히 짚어낼 수 있습니다. 오류가 문맥 이해 부족, 추론 깊이의 한계, 혹은 이미지나 텍스트 입력을 올바르게 파싱하는 모델의 능력 문제에서 비롯되는지 평가할 수 있습니다. 또한 토큰 사용량과 지연 시간 지표를 살펴보며 두 모델 모두에 대해 정확도와 계산 효율성 간의 균형을 평가할 수 있습니다.
Weave로 버그 잡기
LLaVA-o1과 베이스 모델의 성능을 평가하는 동안, 저는 대규모 언어 모델 (LLM) 예측을 채점하기 위한 심사자로 사용했습니다. 하지만 Weave에서 분석하는 동안 베이스 모델의 정답 예측이 오답으로 잘못 표시되는 문제가 있음을 발견했습니다. 아래 시각화에서 볼 수 있듯이, 베이스 모델이 정답을 정확히 맞혔음에도 채점기가 이를 오답으로 표시하는 사례에서 그 문제가 분명해졌습니다.

문제의 원인은 LLM 심사자의 채점 로직에 있었습니다. 정답을 정확히 인식하기보다, 답의 타당성에 영향을 주지 않는 사소한 표현이나 형식 차이에 과도하게 민감하게 반응했습니다. 예를 들어, 숫자 정답인 "2004"의 경우에도, 모델이 추가 문맥을 덧붙이거나 표현을 다르게 했다는 이유만으로, 기술적으로는 맞았음에도 오답으로 판정되는 일이 있었습니다.
Weave의 입력, 출력, 채점 지표를 나란히 비교해 주는 기능 덕분에 이 문제가 한눈에 드러났습니다. 심사자가 정답 예측을 오답으로 잘못 표시하는 이런 유형의 오류는, 수백 개의 예제를 다룰 때 ‘건초 더미에서 바늘 찾기’처럼 발견하기가 매우 어렵습니다. 일반적인 평가 파이프라인에서는 쉽게 눈에 띄지 않았을 수도 있습니다. 하지만 Weave는 돋보기처럼 작동하여, 하나의 인터페이스에서 예측 결과, 정답, 심사자의 점수를 명확하게 확인할 수 있게 해 주었습니다. 이런 불일치를 시각적으로 드러내 줌으로써, 문제의 원인이 모델의 출력이 아니라 평가 메커니즘에 있음을 즉시 파악할 수 있었습니다. 이와 같은 명확성은 문제를 식별하고 해결하는 데 결정적인 역할을 했고, 평가 과정이 정확하고 공정해지도록 보장했습니다.
문제를 해결하기 위해, 기술적으로는 정답인 보다 미세한 표현도 허용하도록 프롬프트를 약간 조정해 평가 로직을 개선했습니다. 이 간단한 조정으로 단순한 사례에서 불필요하게 LLM에 의존하던 부분을 제거하고, 채점 과정의 전반적인 정확도를 크게 높일 수 있었습니다. 또한 이 기본 로직을 Weave의 시각화 기능과 결합해 평가 파이프라인을 정교화하고, 모델의 벤치마킹이 공정하고 신뢰할 수 있도록 보장했습니다.
결론: LLaVA는 비전-언어 모델의 진전을 이끌었다
LLaVA-o1의 개발은 유용한 비전-언어 모델을 달성하기 위한 긴 여정에서 또 하나의 도약입니다. 구조화된 추론을 설계에 명시적으로 포함함으로써, LLaVA-o1은 전통적 모델의 핵심 한계를 보완하며, 단순한 질의응답 과제와 복잡하고 추론 집약적인 문제들. 문제를 체계적으로 여러 단계로 분해하는 이 모델의 능력은, 구조화된 사고 과정이 논리, 수학, 과학적 분석에서 AI 시스템의 역량을 어떻게 향상시킬 수 있는지를 보여준다.
LLaVA-o1의 구조화된 추론 프레임워크는 흥미로운 접근법이지만, 단순한 과제에서의 한계는 어떤 단일한 방법도 보편적으로 최적일 수 없음을 상기시킵니다. 이러한 실험에서 얻은 통찰을 활용해 연구자들은 접근법을 더욱 정교화하고, AI 기반 추론의 가능성을 한계까지 확장할 수 있습니다.
Training a KANFormer: KAN's Are All You Need?
We will dive into a new experimental architecture, replacing the MLP layers in transformers with KAN layers!
Building a real-time answer engine with Llama 3.1 405B and W&B Weave
Infusing llama 3.1 405B with internet search capabilities!!
YOLOv9 object detection tutorial
How to use one of the worlds fastest and most accurate object detectors to run inference, display on your webcam using OpenCV and tracking your results.
Add a comment