fix(modeling): device-agnostic MPS/CPU support — replace .cuda() with .to(device), fix masked_scatter_ broadcast, and use device-type-aware autocast

#5
Files changed (1) hide show
  1. 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[idx].masked_scatter_(images_seq_mask[idx].unsqueeze(-1).cuda(), images_in_this_batch)
 
 
 
 
 
 
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).cuda(),
1004
- images=[(images_crop.cuda(), images_ori.cuda())],
1005
- images_seq_mask=images_seq_mask.unsqueeze(0).cuda(),
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("cuda", dtype=torch.bfloat16):
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).cuda(),
1029
- images=[(images_crop.cuda(), images_ori.cuda())],
1030
- images_seq_mask=images_seq_mask.unsqueeze(0).cuda(),
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("cuda", dtype=torch.bfloat16):
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).cuda().shape[1]:])
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).cuda().shape[1]:])
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).cuda().shape[1]:])
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("cuda", dtype=torch.bfloat16):
1239
  with torch.no_grad():
1240
  gen_kwargs = dict(
1241
- input_ids=input_ids.unsqueeze(0).cuda(),
1242
- images=[(dummy_crop.cuda(), images_ori.cuda())],
1243
- images_seq_mask=images_seq_mask.unsqueeze(0).cuda(),
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).cuda().shape[1]:])
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)]