feat: add configurable scoring criteria for Qwen VLM verifier
- Add score_type parameter to QwenVLMVerifier - Update documentation with new scoring options - Add support for Qwen 3B and 72B models - Update README with clearer verifier usage notes Fixes #2
This commit is contained in:
@@ -149,7 +149,7 @@ def score_candidates(candidate_tensors: List[torch.Tensor], text_prompt: str, ve
|
||||
elif verifier_name == "image_reward":
|
||||
score = verifier.score(text_prompt, pil_img)
|
||||
elif verifier_name == "qwen_vlm_verifier":
|
||||
score = verifier.get_overall_score(pil_img, text_prompt)
|
||||
score = verifier.score(pil_img, text_prompt)
|
||||
else:
|
||||
logger.warning(f"Unknown verifier type: {verifier_name}")
|
||||
continue
|
||||
@@ -435,13 +435,19 @@ class LoadQwenVLMVerifier:
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"qwen_verifier_id": (["Qwen/Qwen2.5-VL-7B-Instruct", None], {
|
||||
"qwen_verifier_id": (["Qwen/Qwen2.5-VL-7B-Instruct", "Qwen/Qwen2.5-VL-3B-Instruct", "Qwen/Qwen2.5-VL-72B-Instruct"], {
|
||||
"default": "Qwen/Qwen2.5-VL-7B-Instruct",
|
||||
"tooltip": "Identifier for the Qwen VLM model."
|
||||
}),
|
||||
"device": ("STRING", {
|
||||
"default": "cuda" if torch.cuda.is_available() else "cpu",
|
||||
"tooltip": "Device to load the model onto."
|
||||
}),
|
||||
"score_type": (["overall_score", "accuracy_to_prompt", "creativity_and_originality",
|
||||
"visual_quality_and_realism", "consistency_and_cohesion",
|
||||
"emotional_or_thematic_resonance"], {
|
||||
"default": "overall_score",
|
||||
"tooltip": "Type of score to return from the Qwen model evaluation."
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -452,7 +458,7 @@ class LoadQwenVLMVerifier:
|
||||
CATEGORY = "InferenceTimeScaling"
|
||||
DESCRIPTION = "Loads the Qwen VLM verifier model. Downloads it if necessary."
|
||||
|
||||
def execute(self, qwen_verifier_id, device):
|
||||
def execute(self, qwen_verifier_id, device, score_type):
|
||||
# Construct a local comfyui checkpoint path for the model
|
||||
model_checkpoint = os.path.join(folder_paths.models_dir, "LLM", os.path.basename(qwen_verifier_id))
|
||||
if not os.path.exists(model_checkpoint):
|
||||
@@ -461,7 +467,7 @@ class LoadQwenVLMVerifier:
|
||||
local_dir=model_checkpoint,
|
||||
local_dir_use_symlinks=False,
|
||||
)
|
||||
verifier_instance = QwenVLMVerifier(qwen_verifier_id, device)
|
||||
verifier_instance = QwenVLMVerifier(model_checkpoint, device, score_type=score_type)
|
||||
return (verifier_instance,)
|
||||
|
||||
|
||||
@@ -489,7 +495,10 @@ class LoadCLIPScoreVerifier:
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"clip_verifier_id": (["openai/clip-vit-base-patch32", "openai/clip-vit-large-patch14", None], {
|
||||
"clip_verifier_id": (["openai/clip-vit-base-patch32",
|
||||
"openai/clip-vit-large-patch14",
|
||||
"openai/clip-vit-base-patch16",
|
||||
"openai/clip-vit-large-patch14-336"], {
|
||||
"default": "openai/clip-vit-base-patch32",
|
||||
"tooltip": "Identifier for the CLIP model."
|
||||
}),
|
||||
@@ -528,7 +537,7 @@ class LoadImageRewardVerifier:
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"ir_verifier_id": (["ImageReward-v1.0", None], {
|
||||
"ir_verifier_id": (["ImageReward-v1.0"], {
|
||||
"default": "ImageReward-v1.0",
|
||||
"tooltip": "Identifier for the ImageReward model."
|
||||
}),
|
||||
|
||||
@@ -82,11 +82,25 @@ class Grading(BaseModel):
|
||||
|
||||
class QwenVLMVerifier():
|
||||
|
||||
def __init__(self, model_name, device='cpu'):
|
||||
def __init__(self, model_name, device='cpu', score_type='overall_score'):
|
||||
logger.info(f"Initializing QwenVLMVerifier with model {model_name} on device {device}")
|
||||
self.model_name = model_name
|
||||
self.device = device
|
||||
self.dtype = torch.float16 if "cuda" in self.device else torch.float16
|
||||
self.dtype = torch.float16
|
||||
|
||||
# Validate score_type
|
||||
valid_score_types = {
|
||||
'accuracy_to_prompt',
|
||||
'creativity_and_originality',
|
||||
'visual_quality_and_realism',
|
||||
'consistency_and_cohesion',
|
||||
'emotional_or_thematic_resonance',
|
||||
'overall_score'
|
||||
}
|
||||
if score_type not in valid_score_types:
|
||||
raise ValueError(f"Invalid score_type. Must be one of: {valid_score_types}")
|
||||
|
||||
self.score_type = score_type
|
||||
self.load_model()
|
||||
|
||||
|
||||
@@ -96,28 +110,14 @@ class QwenVLMVerifier():
|
||||
min_pixels = 256 * 28 * 28
|
||||
max_pixels = 1280 * 28 * 28
|
||||
|
||||
logger.debug(f"Loading model from {self.model_name}")
|
||||
model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
|
||||
self.model_name,
|
||||
torch_dtype=self.dtype,
|
||||
device_map="auto",
|
||||
low_cpu_mem_usage=True
|
||||
)
|
||||
logger.info("Model loaded successfully")
|
||||
|
||||
logger.debug("Loading processor")
|
||||
processor = AutoProcessor.from_pretrained(
|
||||
self.model_name, min_pixels=min_pixels, max_pixels=max_pixels
|
||||
)
|
||||
logger.info("Processor loaded successfully")
|
||||
|
||||
logger.debug("Initializing transformers vision")
|
||||
self.qwen_model = transformers_vision(
|
||||
self.model_name,
|
||||
model_class=model.__class__,
|
||||
model_class=Qwen2_5_VLForConditionalGeneration,
|
||||
device=self.device,
|
||||
model_kwargs={"torch_dtype": self.dtype},
|
||||
processor_class=processor.__class__,
|
||||
processor_class=AutoProcessor,
|
||||
processor_kwargs={"min_pixels": min_pixels, "max_pixels": max_pixels}
|
||||
)
|
||||
logger.info("Transformers vision initialized")
|
||||
|
||||
@@ -125,8 +125,6 @@ class QwenVLMVerifier():
|
||||
self.structured_qwen_generator = outlines.generate.json(self.qwen_model, Grading)
|
||||
logger.info("Structured generator setup complete")
|
||||
|
||||
del model
|
||||
del processor
|
||||
torch.cuda.empty_cache()
|
||||
gc.collect()
|
||||
logger.info("Memory cleanup completed")
|
||||
@@ -173,21 +171,20 @@ class QwenVLMVerifier():
|
||||
raise
|
||||
|
||||
|
||||
def get_overall_score(self, image, prompt: str, max_tokens: int = None, seed: int = 42) -> float:
|
||||
# TODO Extend to handle any score key (instead of just overall score) as input
|
||||
logger.info("Getting overall score")
|
||||
def score(self, image, prompt: str, max_tokens: int = None, seed: int = 42) -> float:
|
||||
logger.info(f"Getting {self.score_type} score")
|
||||
try:
|
||||
outputs = self.query_model(image, prompt, max_tokens, seed)
|
||||
overall_score = outputs["overall_score"]["score"]
|
||||
score = outputs[self.score_type]["score"]
|
||||
|
||||
if overall_score:
|
||||
logger.debug(f"Overall score calculated: {overall_score}")
|
||||
return float(overall_score)
|
||||
if score is not None:
|
||||
logger.debug(f"{self.score_type} score calculated: {score}")
|
||||
return float(score)
|
||||
|
||||
logger.warning("Overall score not found in model output")
|
||||
logger.warning(f"{self.score_type} score not found in model output")
|
||||
return 0.0
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting overall score: {str(e)}", exc_info=True)
|
||||
logger.error(f"Error getting {self.score_type} score: {str(e)}", exc_info=True)
|
||||
return 0.0
|
||||
|
||||
|
||||
@@ -196,35 +193,15 @@ if __name__ == "__main__":
|
||||
|
||||
model_name = "Qwen/Qwen2.5-VL-7B-Instruct"
|
||||
|
||||
model = QwenVLMVerifier(model_name=model_name, device=device)
|
||||
# model.load_model()
|
||||
model = QwenVLMVerifier(model_name=model_name, device=device, score_type='visual_quality_and_realism')
|
||||
|
||||
image_path = "596F6DF4-2856-436E-A981-649ABFB15F1B.jpeg"
|
||||
image = Image.open(image_path).convert("RGB")
|
||||
|
||||
test_prompt = "A red bird and a fish."
|
||||
|
||||
response = model.query_model(image, test_prompt)
|
||||
print("Model Response:", response)
|
||||
|
||||
aspect_keys = [
|
||||
"accuracy_to_prompt",
|
||||
"creativity_and_originality",
|
||||
"visual_quality_and_realism",
|
||||
"consistency_and_cohesion",
|
||||
"emotional_or_thematic_resonance"
|
||||
]
|
||||
|
||||
scores = []
|
||||
for key in aspect_keys:
|
||||
if key in response and "score" in response[key]:
|
||||
scores.append(response[key]["score"])
|
||||
|
||||
if scores:
|
||||
average_score = sum(scores) / len(scores)
|
||||
print("Average Score:", average_score)
|
||||
else:
|
||||
print("No scores found to average.")
|
||||
visual_quality_score = model.score(image, test_prompt)
|
||||
print(f"Visual quality score: {visual_quality_score}")
|
||||
|
||||
# model.to_device('cpu')
|
||||
# model.to_device('cuda')
|
||||
@@ -232,5 +209,5 @@ if __name__ == "__main__":
|
||||
response = model.query_model(image, test_prompt)
|
||||
print("Model Response:", response)
|
||||
|
||||
overall_score = model.get_overall_score(image, test_prompt)
|
||||
overall_score = model.score(image, test_prompt)
|
||||
print(f"Overall score: {overall_score}")
|
||||
|
||||
Reference in New Issue
Block a user