Image-Text-to-Text
Transformers
Safetensors
multilingual
unlimited-ocr
feature-extraction
baidu
vision-language
ocr
custom_code
Eval Results
Instructions to use baidu/Unlimited-OCR with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use baidu/Unlimited-OCR with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="baidu/Unlimited-OCR", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("baidu/Unlimited-OCR", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use baidu/Unlimited-OCR with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "baidu/Unlimited-OCR" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "baidu/Unlimited-OCR", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/baidu/Unlimited-OCR
- SGLang
How to use baidu/Unlimited-OCR with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "baidu/Unlimited-OCR" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "baidu/Unlimited-OCR", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "baidu/Unlimited-OCR" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "baidu/Unlimited-OCR", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use baidu/Unlimited-OCR with Docker Model Runner:
docker model run hf.co/baidu/Unlimited-OCR
fix(modeling): device-agnostic MPS/CPU support — replace .cuda() with .to(device), fix masked_scatter_ broadcast, and use device-type-aware autocast
#5
by kushdab - opened
- modeling_unlimitedocr.py +25 -17
modeling_unlimitedocr.py
CHANGED
|
@@ -579,7 +579,13 @@ class UnlimitedOCRModel(DeepseekV2Model):
|
|
| 579 |
images_in_this_batch = torch.cat(images_in_this_batch, dim=0)
|
| 580 |
# exit()
|
| 581 |
|
| 582 |
-
inputs_embeds
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 583 |
|
| 584 |
idx += 1
|
| 585 |
|
|
@@ -785,6 +791,7 @@ class UnlimitedOCRForCausalLM(DeepseekV2ForCausalLM):
|
|
| 785 |
|
| 786 |
|
| 787 |
def infer(self, tokenizer, prompt='', image_file='', output_path = '', base_size=1024, image_size=640, crop_mode=True, test_compress=False, save_results=False, eval_mode=False, max_length=32768, tps_interval=0, no_repeat_ngram_size=0, ngram_window=0, temperature=0.0):
|
|
|
|
| 788 |
self.disable_torch_init()
|
| 789 |
|
| 790 |
os.makedirs(output_path, exist_ok=True)
|
|
@@ -1000,9 +1007,9 @@ class UnlimitedOCRForCausalLM(DeepseekV2ForCausalLM):
|
|
| 1000 |
self.config.sliding_window = None
|
| 1001 |
# Build logits processors for ngram
|
| 1002 |
gen_kwargs = dict(
|
| 1003 |
-
input_ids=input_ids.unsqueeze(0).
|
| 1004 |
-
images=[(images_crop.
|
| 1005 |
-
images_seq_mask=images_seq_mask.unsqueeze(0).
|
| 1006 |
images_spatial_crop=images_spatial_crop,
|
| 1007 |
do_sample=temperature > 0,
|
| 1008 |
temperature=temperature if temperature > 0 else None,
|
|
@@ -1015,7 +1022,7 @@ class UnlimitedOCRForCausalLM(DeepseekV2ForCausalLM):
|
|
| 1015 |
gen_kwargs['logits_processor'] = [SlidingWindowNoRepeatNgramProcessor(no_repeat_ngram_size, ngram_window)]
|
| 1016 |
elif no_repeat_ngram_size > 0:
|
| 1017 |
gen_kwargs['no_repeat_ngram_size'] = no_repeat_ngram_size
|
| 1018 |
-
with torch.autocast(
|
| 1019 |
with torch.no_grad():
|
| 1020 |
output_ids = self.generate(**gen_kwargs)
|
| 1021 |
self.config.sliding_window = _orig_sw
|
|
@@ -1025,9 +1032,9 @@ class UnlimitedOCRForCausalLM(DeepseekV2ForCausalLM):
|
|
| 1025 |
self.config._ring_window = _orig_sw
|
| 1026 |
self.config.sliding_window = None
|
| 1027 |
gen_kwargs = dict(
|
| 1028 |
-
input_ids=input_ids.unsqueeze(0).
|
| 1029 |
-
images=[(images_crop.
|
| 1030 |
-
images_seq_mask=images_seq_mask.unsqueeze(0).
|
| 1031 |
images_spatial_crop=images_spatial_crop,
|
| 1032 |
do_sample=temperature > 0,
|
| 1033 |
temperature=temperature if temperature > 0 else None,
|
|
@@ -1039,14 +1046,14 @@ class UnlimitedOCRForCausalLM(DeepseekV2ForCausalLM):
|
|
| 1039 |
gen_kwargs['logits_processor'] = [SlidingWindowNoRepeatNgramProcessor(no_repeat_ngram_size, ngram_window)]
|
| 1040 |
elif no_repeat_ngram_size > 0:
|
| 1041 |
gen_kwargs['no_repeat_ngram_size'] = no_repeat_ngram_size
|
| 1042 |
-
with torch.autocast(
|
| 1043 |
with torch.no_grad():
|
| 1044 |
output_ids = self.generate(**gen_kwargs)
|
| 1045 |
self.config.sliding_window = _orig_sw
|
| 1046 |
|
| 1047 |
|
| 1048 |
if '<image>' in conversation[0]['content'] and eval_mode:
|
| 1049 |
-
outputs = tokenizer.decode(output_ids[0, input_ids.unsqueeze(0).
|
| 1050 |
stop_str = '<|end▁of▁sentence|>'
|
| 1051 |
if outputs.endswith(stop_str):
|
| 1052 |
outputs = outputs[:-len(stop_str)]
|
|
@@ -1056,7 +1063,7 @@ class UnlimitedOCRForCausalLM(DeepseekV2ForCausalLM):
|
|
| 1056 |
return outputs
|
| 1057 |
|
| 1058 |
if '<image>' in conversation[0]['content'] and test_compress:
|
| 1059 |
-
outputs = tokenizer.decode(output_ids[0, input_ids.unsqueeze(0).
|
| 1060 |
pure_texts_outputs_token_length = len(text_encode(tokenizer, outputs, bos=False, eos=False))
|
| 1061 |
print('='*50)
|
| 1062 |
print('image size: ', (w, h))
|
|
@@ -1067,7 +1074,7 @@ class UnlimitedOCRForCausalLM(DeepseekV2ForCausalLM):
|
|
| 1067 |
|
| 1068 |
|
| 1069 |
if '<image>' in conversation[0]['content'] and save_results:
|
| 1070 |
-
outputs = tokenizer.decode(output_ids[0, input_ids.unsqueeze(0).
|
| 1071 |
stop_str = '<|end▁of▁sentence|>'
|
| 1072 |
|
| 1073 |
print('='*15 + 'save results:' + '='*15)
|
|
@@ -1137,6 +1144,7 @@ class UnlimitedOCRForCausalLM(DeepseekV2ForCausalLM):
|
|
| 1137 |
|
| 1138 |
|
| 1139 |
def infer_multi(self, tokenizer, prompt='', image_files=None, output_path='', image_size=640, save_results=False, max_length=32768, tps_interval=0, no_repeat_ngram_size=0, ngram_window=0, temperature=0.0):
|
|
|
|
| 1140 |
"""
|
| 1141 |
Multi-image inference. Does NOT support crop mode.
|
| 1142 |
Prompt uses a single <image> token (e.g. "<image>Multi page parsing.").
|
|
@@ -1235,12 +1243,12 @@ class UnlimitedOCRForCausalLM(DeepseekV2ForCausalLM):
|
|
| 1235 |
_orig_sw = getattr(self.config, 'sliding_window_size', None) or getattr(self.config, 'sliding_window', None)
|
| 1236 |
self.config._ring_window = _orig_sw # Save for ring buffer to read
|
| 1237 |
self.config.sliding_window = None
|
| 1238 |
-
with torch.autocast(
|
| 1239 |
with torch.no_grad():
|
| 1240 |
gen_kwargs = dict(
|
| 1241 |
-
input_ids=input_ids.unsqueeze(0).
|
| 1242 |
-
images=[(dummy_crop.
|
| 1243 |
-
images_seq_mask=images_seq_mask.unsqueeze(0).
|
| 1244 |
images_spatial_crop=images_spatial_crop,
|
| 1245 |
do_sample=temperature > 0,
|
| 1246 |
temperature=temperature if temperature > 0 else None,
|
|
@@ -1256,7 +1264,7 @@ class UnlimitedOCRForCausalLM(DeepseekV2ForCausalLM):
|
|
| 1256 |
output_ids = self.generate(**gen_kwargs)
|
| 1257 |
self.config.sliding_window = _orig_sw # Restore
|
| 1258 |
|
| 1259 |
-
outputs = tokenizer.decode(output_ids[0, input_ids.unsqueeze(0).
|
| 1260 |
stop_str = '<|end▁of▁sentence|>'
|
| 1261 |
if outputs.endswith(stop_str):
|
| 1262 |
outputs = outputs[:-len(stop_str)]
|
|
|
|
| 579 |
images_in_this_batch = torch.cat(images_in_this_batch, dim=0)
|
| 580 |
# exit()
|
| 581 |
|
| 582 |
+
# Expand mask to match inputs_embeds shape and move to the same device.
|
| 583 |
+
# masked_scatter_ requires the mask to be broadcastable to the target tensor;
|
| 584 |
+
# unsqueeze(-1) alone leaves shape [seq_len, 1] which triggers a broadcast
|
| 585 |
+
# error on MPS. expand_as() makes it explicit, and .to(device) replaces
|
| 586 |
+
# the hardcoded .to(device) that crashes on Apple Silicon and CPU.
|
| 587 |
+
_mask = images_seq_mask[idx].unsqueeze(-1).expand_as(inputs_embeds[idx]).to(inputs_embeds.device)
|
| 588 |
+
inputs_embeds[idx].masked_scatter_(_mask, images_in_this_batch)
|
| 589 |
|
| 590 |
idx += 1
|
| 591 |
|
|
|
|
| 791 |
|
| 792 |
|
| 793 |
def infer(self, tokenizer, prompt='', image_file='', output_path = '', base_size=1024, image_size=640, crop_mode=True, test_compress=False, save_results=False, eval_mode=False, max_length=32768, tps_interval=0, no_repeat_ngram_size=0, ngram_window=0, temperature=0.0):
|
| 794 |
+
device = next(self.parameters()).device # MPS / CUDA / CPU
|
| 795 |
self.disable_torch_init()
|
| 796 |
|
| 797 |
os.makedirs(output_path, exist_ok=True)
|
|
|
|
| 1007 |
self.config.sliding_window = None
|
| 1008 |
# Build logits processors for ngram
|
| 1009 |
gen_kwargs = dict(
|
| 1010 |
+
input_ids=input_ids.unsqueeze(0).to(device),
|
| 1011 |
+
images=[(images_crop.to(device), images_ori.to(device))],
|
| 1012 |
+
images_seq_mask=images_seq_mask.unsqueeze(0).to(device),
|
| 1013 |
images_spatial_crop=images_spatial_crop,
|
| 1014 |
do_sample=temperature > 0,
|
| 1015 |
temperature=temperature if temperature > 0 else None,
|
|
|
|
| 1022 |
gen_kwargs['logits_processor'] = [SlidingWindowNoRepeatNgramProcessor(no_repeat_ngram_size, ngram_window)]
|
| 1023 |
elif no_repeat_ngram_size > 0:
|
| 1024 |
gen_kwargs['no_repeat_ngram_size'] = no_repeat_ngram_size
|
| 1025 |
+
with torch.autocast(device.type, dtype=torch.bfloat16):
|
| 1026 |
with torch.no_grad():
|
| 1027 |
output_ids = self.generate(**gen_kwargs)
|
| 1028 |
self.config.sliding_window = _orig_sw
|
|
|
|
| 1032 |
self.config._ring_window = _orig_sw
|
| 1033 |
self.config.sliding_window = None
|
| 1034 |
gen_kwargs = dict(
|
| 1035 |
+
input_ids=input_ids.unsqueeze(0).to(device),
|
| 1036 |
+
images=[(images_crop.to(device), images_ori.to(device))],
|
| 1037 |
+
images_seq_mask=images_seq_mask.unsqueeze(0).to(device),
|
| 1038 |
images_spatial_crop=images_spatial_crop,
|
| 1039 |
do_sample=temperature > 0,
|
| 1040 |
temperature=temperature if temperature > 0 else None,
|
|
|
|
| 1046 |
gen_kwargs['logits_processor'] = [SlidingWindowNoRepeatNgramProcessor(no_repeat_ngram_size, ngram_window)]
|
| 1047 |
elif no_repeat_ngram_size > 0:
|
| 1048 |
gen_kwargs['no_repeat_ngram_size'] = no_repeat_ngram_size
|
| 1049 |
+
with torch.autocast(device.type, dtype=torch.bfloat16):
|
| 1050 |
with torch.no_grad():
|
| 1051 |
output_ids = self.generate(**gen_kwargs)
|
| 1052 |
self.config.sliding_window = _orig_sw
|
| 1053 |
|
| 1054 |
|
| 1055 |
if '<image>' in conversation[0]['content'] and eval_mode:
|
| 1056 |
+
outputs = tokenizer.decode(output_ids[0, input_ids.unsqueeze(0).to(device).shape[1]:])
|
| 1057 |
stop_str = '<|end▁of▁sentence|>'
|
| 1058 |
if outputs.endswith(stop_str):
|
| 1059 |
outputs = outputs[:-len(stop_str)]
|
|
|
|
| 1063 |
return outputs
|
| 1064 |
|
| 1065 |
if '<image>' in conversation[0]['content'] and test_compress:
|
| 1066 |
+
outputs = tokenizer.decode(output_ids[0, input_ids.unsqueeze(0).to(device).shape[1]:])
|
| 1067 |
pure_texts_outputs_token_length = len(text_encode(tokenizer, outputs, bos=False, eos=False))
|
| 1068 |
print('='*50)
|
| 1069 |
print('image size: ', (w, h))
|
|
|
|
| 1074 |
|
| 1075 |
|
| 1076 |
if '<image>' in conversation[0]['content'] and save_results:
|
| 1077 |
+
outputs = tokenizer.decode(output_ids[0, input_ids.unsqueeze(0).to(device).shape[1]:])
|
| 1078 |
stop_str = '<|end▁of▁sentence|>'
|
| 1079 |
|
| 1080 |
print('='*15 + 'save results:' + '='*15)
|
|
|
|
| 1144 |
|
| 1145 |
|
| 1146 |
def infer_multi(self, tokenizer, prompt='', image_files=None, output_path='', image_size=640, save_results=False, max_length=32768, tps_interval=0, no_repeat_ngram_size=0, ngram_window=0, temperature=0.0):
|
| 1147 |
+
device = next(self.parameters()).device # MPS / CUDA / CPU
|
| 1148 |
"""
|
| 1149 |
Multi-image inference. Does NOT support crop mode.
|
| 1150 |
Prompt uses a single <image> token (e.g. "<image>Multi page parsing.").
|
|
|
|
| 1243 |
_orig_sw = getattr(self.config, 'sliding_window_size', None) or getattr(self.config, 'sliding_window', None)
|
| 1244 |
self.config._ring_window = _orig_sw # Save for ring buffer to read
|
| 1245 |
self.config.sliding_window = None
|
| 1246 |
+
with torch.autocast(device.type, dtype=torch.bfloat16):
|
| 1247 |
with torch.no_grad():
|
| 1248 |
gen_kwargs = dict(
|
| 1249 |
+
input_ids=input_ids.unsqueeze(0).to(device),
|
| 1250 |
+
images=[(dummy_crop.to(device), images_ori.to(device))],
|
| 1251 |
+
images_seq_mask=images_seq_mask.unsqueeze(0).to(device),
|
| 1252 |
images_spatial_crop=images_spatial_crop,
|
| 1253 |
do_sample=temperature > 0,
|
| 1254 |
temperature=temperature if temperature > 0 else None,
|
|
|
|
| 1264 |
output_ids = self.generate(**gen_kwargs)
|
| 1265 |
self.config.sliding_window = _orig_sw # Restore
|
| 1266 |
|
| 1267 |
+
outputs = tokenizer.decode(output_ids[0, input_ids.unsqueeze(0).to(device).shape[1]:])
|
| 1268 |
stop_str = '<|end▁of▁sentence|>'
|
| 1269 |
if outputs.endswith(stop_str):
|
| 1270 |
outputs = outputs[:-len(stop_str)]
|