Views
No views yet
| Model Size | Model Name | Base Model | License | Huggingface Link |
|---|---|---|---|---|
| 2B | NuExtract-2.0-2B | Qwen2-VL-2B-Instruct | MIT | 🤗 NuExtract-2.0-2B |
| 4B | NuExtract-2.0-4B | Qwen2.5-VL-3B-Instruct | Qwen Research License | 🤗 NuExtract-2.0-4B |
| 8B | NuExtract-2.0-8B | Qwen2.5-VL-7B-Instruct | MIT | 🤗 NuExtract-2.0-8B |
NuExtract-2.0-2B is based on Qwen2-VL rather than Qwen2.5-VL because the smallest Qwen2.5-VL model (3B) has a more restrictive, non-commercial license. We therefore include NuExtract-2.0-2B as a small model option that can be used commercially.
verbatim-string - instructs the model to extract text that is present verbatim in the input.string - a generic string field that can incorporate paraphrasing/abstraction.integer - a whole number.number - a whole or decimal number.date-time - ISO formatted date.["string"])enum - a choice from set of possible answers (represented in template as an array of options, e.g. ["yes", "no", "maybe"]).multi-label - an enum that can have multiple possible answers (represented in template as a double-wrapped array, e.g. [["A", "B", "C"]]).null or [] (for arrays and multi-labels).1{
2 "first_name": "verbatim-string",
3 "last_name": "verbatim-string",
4 "description": "string",
5 "age": "integer",
6 "gpa": "number",
7 "birth_date": "date-time",
8 "nationality": ["France", "England", "Japan", "USA", "China"],
9 "languages_spoken": [["English", "French", "Japanese", "Mandarin", "Spanish"]]
10}1{
2 "first_name": "Susan",
3 "last_name": "Smith",
4 "description": "A student studying computer science.",
5 "age": 20,
6 "gpa": 3.7,
7 "birth_date": "2005-03-01",
8 "nationality": "England",
9 "languages_spoken": ["English", "French"]
10}1import torch
2from transformers import AutoProcessor, AutoModelForVision2Seq
3
4model_name = "numind/NuExtract-2.0-2B"
5# model_name = "numind/NuExtract-2.0-8B"
6
7model = AutoModelForVision2Seq.from_pretrained(model_name,
8 trust_remote_code=True,
9 torch_dtype=torch.bfloat16,
10 attn_implementation="flash_attention_2",
11 device_map="auto")
12processor = AutoProcessor.from_pretrained(model_name,
13 trust_remote_code=True,
14 padding_side='left',
15 use_fast=True)
16
17# You can set min_pixels and max_pixels according to your needs, such as a token range of 256-1280, to balance performance and cost.
18# min_pixels = 256*28*28
19# max_pixels = 1280*28*28
20# processor = AutoProcessor.from_pretrained(model_name, min_pixels=min_pixels, max_pixels=max_pixels)1def process_all_vision_info(messages, examples=None):
2 """
3 Process vision information from both messages and in-context examples, supporting batch processing.
4
5 Args:
6 messages: List of message dictionaries (single input) OR list of message lists (batch input)
7 examples: Optional list of example dictionaries (single input) OR list of example lists (batch)
8
9 Returns:
10 A flat list of all images in the correct order:
11 - For single input: example images followed by message images
12 - For batch input: interleaved as (item1 examples, item1 input, item2 examples, item2 input, etc.)
13 - Returns None if no images were found
14 """
15 from qwen_vl_utils import process_vision_info, fetch_image
16
17 # Helper function to extract images from examples
18 def extract_example_images(example_item):
19 if not example_item:
20 return []
21
22 # Handle both list of examples and single example
23 examples_to_process = example_item if isinstance(example_item, list) else [example_item]
24 images = []
25
26 for example in examples_to_process:
27 if isinstance(example.get('input'), dict) and example['input'].get('type') == 'image':
28 images.append(fetch_image(example['input']))
29
30 return images
31
32 # Normalize inputs to always be batched format
33 is_batch = messages and isinstance(messages[0], list)
34 messages_batch = messages if is_batch else [messages]
35 is_batch_examples = examples and isinstance(examples, list) and (isinstance(examples[0], list) or examples[0] is None)
36 examples_batch = examples if is_batch_examples else ([examples] if examples is not None else None)
37
38 # Ensure examples batch matches messages batch if provided
39 if examples and len(examples_batch) != len(messages_batch):
40 if not is_batch and len(examples_batch) == 1:
41 # Single example set for a single input is fine
42 pass
43 else:
44 raise ValueError("Examples batch length must match messages batch length")
45
46 # Process all inputs, maintaining correct order
47 all_images = []
48 for i, message_group in enumerate(messages_batch):
49 # Get example images for this input
50 if examples and i < len(examples_batch):
51 input_example_images = extract_example_images(examples_batch[i])
52 all_images.extend(input_example_images)
53
54 # Get message images for this input
55 input_message_images = process_vision_info(message_group)[0] or []
56 all_images.extend(input_message_images)
57
58 return all_images if all_images else None1template = """{"names": ["string"]}"""
2document = "John went to the restaurant with Mary. James went to the cinema."
3
4# prepare the user message content
5messages = [{"role": "user", "content": document}]
6text = processor.tokenizer.apply_chat_template(
7 messages,
8 template=template, # template is specified here
9 tokenize=False,
10 add_generation_prompt=True,
11)
12
13print(text)
14""""<|im_start|>user
15# Template:
16{"names": ["string"]}
17# Context:
18John went to the restaurant with Mary. James went to the cinema.<|im_end|>
19<|im_start|>assistant"""
20
21image_inputs = process_all_vision_info(messages)
22inputs = processor(
23 text=[text],
24 images=image_inputs,
25 padding=True,
26 return_tensors="pt",
27).to("cuda")
28
29# we choose greedy sampling here, which works well for most information extraction tasks
30generation_config = {"do_sample": False, "num_beams": 1, "max_new_tokens": 2048}
31
32# Inference: Generation of the output
33generated_ids = model.generate(
34 **inputs,
35 **generation_config
36)
37generated_ids_trimmed = [
38 out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
39]
40output_text = processor.batch_decode(
41 generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
42)
43
44print(output_text)
45# ['{"names": ["John", "Mary", "James"]}']- on either side (for the sake of illustration). Usually providing multiple examples will lead to better results.1template = """{"names": ["string"]}"""
2document = "John went to the restaurant with Mary. James went to the cinema."
3examples = [
4 {
5 "input": "Stephen is the manager at Susan's store.",
6 "output": """{"names": ["-STEPHEN-", "-SUSAN-"]}"""
7 }
8]
9
10messages = [{"role": "user", "content": document}]
11text = processor.tokenizer.apply_chat_template(
12 messages,
13 template=template,
14 examples=examples, # examples provided here
15 tokenize=False,
16 add_generation_prompt=True,
17)
18
19image_inputs = process_all_vision_info(messages, examples)
20inputs = processor(
21 text=[text],
22 images=image_inputs,
23 padding=True,
24 return_tensors="pt",
25).to("cuda")
26
27# we choose greedy sampling here, which works well for most information extraction tasks
28generation_config = {"do_sample": False, "num_beams": 1, "max_new_tokens": 2048}
29
30# Inference: Generation of the output
31generated_ids = model.generate(
32 **inputs,
33 **generation_config
34)
35generated_ids_trimmed = [
36 out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
37]
38output_text = processor.batch_decode(
39 generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
40)
41print(output_text)
42# ['{"names": ["-JOHN-", "-MARY-", "-JAMES-"]}']{"type": "image", "image": "http://path/to/your/image.jpg"}) or base64 encoding (e.g. {"type": "image", "image": "data:image;base64,/9j/..."}).1template = """{"store": "verbatim-string"}"""
2document = {"type": "image", "image": "file://1.jpg"}
3
4messages = [{"role": "user", "content": [document]}]
5text = processor.tokenizer.apply_chat_template(
6 messages,
7 template=template,
8 tokenize=False,
9 add_generation_prompt=True,
10)
11
12image_inputs = process_all_vision_info(messages)
13inputs = processor(
14 text=[text],
15 images=image_inputs,
16 padding=True,
17 return_tensors="pt",
18).to("cuda")
19
20generation_config = {"do_sample": False, "num_beams": 1, "max_new_tokens": 2048}
21
22# Inference: Generation of the output
23generated_ids = model.generate(
24 **inputs,
25 **generation_config
26)
27generated_ids_trimmed = [
28 out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
29]
30output_text = processor.batch_decode(
31 generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
32)
33print(output_text)
34# ['{"store": "Trader Joe\'s"}']1inputs = [
2 # image input with no ICL examples
3 {
4 "document": {"type": "image", "image": "file://0.jpg"},
5 "template": """{"store_name": "verbatim-string"}""",
6 },
7 # image input with 1 ICL example
8 {
9 "document": {"type": "image", "image": "file://0.jpg"},
10 "template": """{"store_name": "verbatim-string"}""",
11 "examples": [
12 {
13 "input": {"type": "image", "image": "file://1.jpg"},
14 "output": """{"store_name": "Trader Joe's"}""",
15 }
16 ],
17 },
18 # text input with no ICL examples
19 {
20 "document": {"type": "text", "text": "John went to the restaurant with Mary. James went to the cinema."},
21 "template": """{"names": ["string"]}""",
22 },
23 # text input with ICL example
24 {
25 "document": {"type": "text", "text": "John went to the restaurant with Mary. James went to the cinema."},
26 "template": """{"names": ["string"]}""",
27 "examples": [
28 {
29 "input": "Stephen is the manager at Susan's store.",
30 "output": """{"names": ["STEPHEN", "SUSAN"]}"""
31 }
32 ],
33 },
34]
35
36# messages should be a list of lists for batch processing
37messages = [
38 [
39 {
40 "role": "user",
41 "content": [x['document']],
42 }
43 ]
44 for x in inputs
45]
46
47# apply chat template to each example individually
48texts = [
49 processor.tokenizer.apply_chat_template(
50 messages[i], # Now this is a list containing one message
51 template=x['template'],
52 examples=x.get('examples', None),
53 tokenize=False,
54 add_generation_prompt=True)
55 for i, x in enumerate(inputs)
56]
57
58image_inputs = process_all_vision_info(messages, [x.get('examples') for x in inputs])
59inputs = processor(
60 text=texts,
61 images=image_inputs,
62 padding=True,
63 return_tensors="pt",
64).to("cuda")
65
66generation_config = {"do_sample": False, "num_beams": 1, "max_new_tokens": 2048}
67
68# Batch Inference
69generated_ids = model.generate(**inputs, **generation_config)
70generated_ids_trimmed = [
71 out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
72]
73output_texts = processor.batch_decode(
74 generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
75)
76for y in output_texts:
77 print(y)
78# {"store_name": "WAL-MART"}
79# {"store_name": "Walmart"}
80# {"names": ["John", "Mary", "James"]}
81# {"names": ["JOHN", "MARY", "JAMES"]}1xml_template = """<SportResult>
2 <Date></Date>
3 <Sport></Sport>
4 <Venue></Venue>
5 <HomeTeam></HomeTeam>
6 <AwayTeam></AwayTeam>
7 <HomeScore></HomeScore>
8 <AwayScore></AwayScore>
9 <TopScorer></TopScorer>
10</SportResult>"""
11
12messages = [
13 {
14 "role": "user",
15 "content": [{"type": "text", "text": xml_template}],
16 }
17 ]
18
19text = processor.apply_chat_template(
20 messages, tokenize=False, add_generation_prompt=True,
21)
22
23image_inputs = process_all_vision_info(messages)
24inputs = processor(
25 text=[text],
26 images=image_inputs,
27 padding=True,
28 return_tensors="pt",
29).to("cuda")
30
31generated_ids = model.generate(
32 **inputs,
33 **generation_config
34)
35generated_ids_trimmed = [
36 out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
37]
38output_text = processor.batch_decode(
39 generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
40)
41
42print(output_text[0])
43# {
44# "Date": "date-time",
45# "Sport": "verbatim-string",
46# "Venue": "verbatim-string",
47# "HomeTeam": "verbatim-string",
48# "AwayTeam": "verbatim-string",
49# "HomeScore": "integer",
50# "AwayScore": "integer",
51# "TopScorer": "verbatim-string"
52# }1description = "I would like to extract important details from the contract."
2
3messages = [
4 {
5 "role": "user",
6 "content": [{"type": "text", "text": description}],
7 }
8 ]
9
10text = processor.apply_chat_template(
11 messages, tokenize=False, add_generation_prompt=True,
12)
13
14image_inputs = process_all_vision_info(messages)
15inputs = processor(
16 text=[text],
17 images=image_inputs,
18 padding=True,
19 return_tensors="pt",
20).to("cuda")
21
22generated_ids = model.generate(
23 **inputs,
24 **generation_config
25)
26generated_ids_trimmed = [
27 out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
28]
29output_text = processor.batch_decode(
30 generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
31)
32
33print(output_text[0])
34# {
35# "Contract": {
36# "Title": "verbatim-string",
37# "Description": "verbatim-string",
38# "Terms": [
39# {
40# "Term": "verbatim-string",
41# "Description": "verbatim-string"
42# }
43# ],
44# "Date": "date-time",
45# "Signatory": "verbatim-string"
46# }
47# }vllm serve numind/NuExtract-2.0-8B --trust_remote_code --limit-mm-per-prompt image=6 --chat-template-content-format openai--max-model-len accordingly.1import json
2from openai import OpenAI
3
4openai_api_key = "EMPTY"
5openai_api_base = "http://localhost:8000/v1"
6
7client = OpenAI(
8 api_key=openai_api_key,
9 base_url=openai_api_base,
10)
11
12chat_response = client.chat.completions.create(
13 model="numind/NuExtract-2.0-8B",
14 temperature=0,
15 messages=[
16 {
17 "role": "user",
18 "content": [{"type": "text", "text": "Yesterday I went shopping at Bunnings"}],
19 },
20 ],
21 extra_body={
22 "chat_template_kwargs": {
23 "template": json.dumps(json.loads("""{\"store\": \"verbatim-string\"}"""), indent=4)
24 },
25 }
26)
27print("Chat response:", chat_response)"content" as they appear in the prompt (i.e. any in-context examples before the main input).1import base64
2
3def encode_image(image_path):
4 """
5 Encode the image file to base64 string
6 """
7 with open(image_path, "rb") as image_file:
8 return base64.b64encode(image_file.read()).decode('utf-8')
9
10base64_image = encode_image("0.jpg")
11base64_image2 = encode_image("1.jpg")
12
13chat_response = client.chat.completions.create(
14 model="numind/NuExtract-2.0-8B",
15 temperature=0,
16 messages=[
17 {
18 "role": "user",
19 "content": [
20 {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}}, # first ICL example image
21 {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image2}"}}, # real input image
22 ],
23 },
24 ],
25 extra_body={
26 "chat_template_kwargs": {
27 "template": json.dumps(json.loads("""{\"store\": \"verbatim-string\"}"""), indent=4),
28 "examples": [
29 {
30 "input": "<image>",
31 "output": """{\"store\": \"Walmart\"}"""
32 }
33 ]
34 },
35 }
36)
37print("Chat response:", chat_response)