--- base_model: llava-hf/llava-1.5-7b-hf library_name: peft pipeline_tag: image-text-to-text tags: - base_model:adapter:llava-hf/llava-1.5-7b-hf - lora - transformers - medical - vision-language-model - QLoRA language: - en --- # LLaVA-1.5-7B Skin Disease Fine-tuned Model (LoRA) ## Model Description 이 모델은 `llava-hf/llava-1.5-7b-hf` 베이스 모델을 기반으로 파인튜닝된 LoRA 어댑터입니다. 안면 피부 질환을 진단하고, 관련 케어 가이드를 제공하는 데 특화되어 있습니다. 기존 범용 모델이 동양인(한국인) 피부 임상 데이터 학습이 부족해 발생하는 오진율과 할루시네이션(임의 처방)을 해결하고자 개발되었습니다. - **Base Model:** `llava-hf/llava-1.5-7b-hf` - **Finetuning Method:** 8-bit QLoRA & SFT - **Primary Use Case:** 안면 피부 질환 추론 및 다중 턴(Multi-turn) 질의응답 (자가 진단 챗봇) --- ## Training Details - **Training Data:** AI Hub의 '안면부 피부질환 이미지 합성 데이터' 9,600장을 기반으로, 이미지 당 4개의 단일 턴 질의응답을 하나의 대화 세션으로 묶는 **멀티턴 세션 체이닝(Multi-turn Session Chaining)** 기법을 적용하여 38,400 Turn 대화셋(QA쌍)으로 가공했습니다. - **Results:** 피부 질환 진단 Accuracy 약 60% 향상 (0.093 ➔ 0.148), Macro F1-Score 약 65% 향상 (0.126 ➔ 0.208). 반복 생성 루프(Repetition Loop) 버그를 데이터 전처리 레벨에서 원천 해결했습니다. ### 📌 Multi-turn Chaining 특성 본 모델은 이미지 1장당 4개의 연속적인 질의응답(QA)을 하나의 대화 세션으로 묶는 **멀티턴 세션 체이닝(Multi-turn Session Chaining)** 기법으로 훈련되었습니다. **[학습 데이터셋 예시]** ```text USER: \nWhat skin disease is visible in this image? ASSISTANT: Psoriasis USER: What part of the body is this image of? ASSISTANT: Face USER: What symptoms are visible in this image? ASSISTANT: itching USER: Describe this disease. ASSISTANT: An inflammatory skin condition that presents as red papules or plaques covered with scales. ``` **⚠️ 추론 시 주의사항 (Repetition Behavior)** 모델이 위와 같은 '멀티턴 흐름'에 완벽하게 적응(과적합)되어 있으므로, 단순히 1개의 질문만 던져도 **모델 스스로 다음 질문(`USER:`)을 상상하여 전체 대본을 끝까지 출력하려는 특징**을 보입니다. 따라서 추론 시에는 파이썬 코드를 통해 문자열을 적절히 슬라이싱(Slicing)하여 원하는 답변만 추출하는 후처리(Post-processing)가 필요합니다. --- ## 🚀 How to Get Started ### 1. Requirements (라이브러리 버전) 최신 `peft` 라이브러리의 어댑터 로드 호환성을 위해 아래 라이브러리들의 버전이 필요합니다. (특히 `torchao >= 0.16.0` 필수) ```bash pip install -U transformers peft accelerate bitsandbytes requests Pillow "torchao>=0.16.0" ``` ### 2. 모델 및 어댑터 로드 (Model Loading) 모델은 8-bit QLoRA로 튜닝되었으므로, 데이터 타입 충돌을 방지하기 위해 베이스 모델을 8-bit로 로드해야 합니다. ```python from transformers import AutoProcessor, LlavaForConditionalGeneration, BitsAndBytesConfig from peft import PeftModel import torch # 8-bit 양자화 설정 quantization_config = BitsAndBytesConfig( load_in_8bit=True, llm_int8_threshold=200.0, llm_int8_skip_modules=["lm_head", "vision_tower", "multi_modal_projector"] ) # Base Model 로드 base_model_id = "llava-hf/llava-1.5-7b-hf" base_model = LlavaForConditionalGeneration.from_pretrained( base_model_id, quantization_config=quantization_config, device_map="auto" ) # 파인튜닝된 LoRA Adapter 로드 (경고 방지 옵션 추가) adapter_id = "jun47/llava-7b-skin" model = PeftModel.from_pretrained(base_model, adapter_id, ensure_weight_tying=True) # Processor 로드 (커스텀 템플릿 보존을 위해 어댑터 경로에서 로드) processor = AutoProcessor.from_pretrained(adapter_id) ``` ### 3. 텍스트 추론 및 파싱 (Inference & Parsing) 학습 데이터의 특성을 살려 **영어 프롬프트**를 사용해야 가장 정확한 진단을 얻을 수 있습니다. ```python from PIL import Image import requests # 테스트 이미지 로드 image_url = "https://example.com/your_skin_image.jpg" image = Image.open(requests.get(image_url, stream=True).raw) # 첫 번째 질문 프롬프트 prompt = "USER: \nWhat skin disease is visible in this image?\nASSISTANT:" inputs = processor(text=prompt, images=image, return_tensors="pt").to("cuda") # 텍스트 생성 (모델이 전체 대화를 생성하도록 넉넉한 토큰 부여) outputs = model.generate(**inputs, max_new_tokens=200, temperature=0.2, do_sample=True) input_length = inputs["input_ids"].shape[1] raw_full_text = processor.decode(outputs[0][input_length:], skip_special_tokens=True) ``` #### 방법 1: 진단명만 깔끔하게 추출하기 모델이 다음 질문(`USER:`)을 상상해내기 전까지만 텍스트를 자릅니다. ```python diagnosis = raw_full_text.split("USER:")[0].strip() print("==== AI 진단 결과 ====") print(f"진단명: {diagnosis}") # 출력 예시: Rosacea ``` #### 방법 2: 모델의 멀티턴 학습 특성을 역이용하여 진단명 + 상세 설명 한 번에 추출하기 모델이 스스로 생성한 전체 4턴 대화(환각) 스크립트 속에서 정규식 파싱을 통해 진단명과 최종 설명을 모두 낚아채는 최적화 방식입니다. ```python # 1. 진단명 추출 diagnosis = raw_full_text.split("USER:")[0].strip() # 2. 증상 설명 추출 ('Describe this disease' 이후의 답변만 가져옴) try: description = raw_full_text.split("Describe this disease. ASSISTANT:")[-1].split("USER:")[0].strip() except Exception: description = "상세 설명을 추출할 수 없습니다." print("==== AI 복합 진단 결과 ====") print(f"진단명: {diagnosis}") print(f"상세 설명: {description}") ```