hamxaameer commited on
Commit
1cad0fe
·
1 Parent(s): 4b1bc71

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +83 -86
app.py CHANGED
@@ -35,98 +35,95 @@ def load_model_from_pickle(pickle_path="best_model.pkl"):
35
  if not os.path.exists(pickle_path):
36
  return f"❌ Model file not found: {pickle_path}\n\nPlease ensure best_model.pkl is uploaded to the HuggingFace Space."
37
 
38
- # METHOD 1: Set environment variable BEFORE any CUDA operations
39
- # This prevents PyTorch from seeing ANY CUDA devices
40
- import os as os_module
41
- old_cuda_visible = os_module.environ.get('CUDA_VISIBLE_DEVICES', None)
42
- os_module.environ['CUDA_VISIBLE_DEVICES'] = '-1' # Disable all CUDA devices
43
 
44
- # Also set other CUDA-disabling flags
45
- os_module.environ['CUDA_LAUNCH_BLOCKING'] = '0'
46
-
47
- try:
48
- # METHOD 2: Use pickle with restricted globals to prevent CUDA imports
49
- import io
50
- import pickle
51
-
52
- class CPUOnlyUnpickler(pickle.Unpickler):
53
- def find_class(self, module, name):
54
- # Allow transformers and torch imports
55
- if module.startswith('transformers') or module.startswith('torch'):
56
- return super().find_class(module, name)
57
- return super().find_class(module, name)
 
 
 
 
 
 
 
58
 
59
- def persistent_load(self, pid):
60
- # Intercept torch storage and force CPU
61
- if isinstance(pid, tuple) and len(pid) > 0:
62
- if pid[0] == 'storage':
63
- # Format: ('storage', storage_type, key, location, size)
64
- storage_type = pid[1]
65
- key = pid[2]
66
- location = 'cpu' # Force CPU location
67
- size = pid[4] if len(pid) > 4 else pid[3]
68
- # Rebuild with CPU location
69
- return super().persistent_load(('storage', storage_type, key, location, size))
70
- return super().persistent_load(pid)
71
-
72
- # Load using our custom unpickler
73
- with open(pickle_path, 'rb') as f:
74
- # First try: Custom unpickler with CUDA disabled
75
  try:
76
- model_package = CPUOnlyUnpickler(f).load()
77
- except Exception as e1:
78
- # Second try: Standard torch.load with map_location
79
- f.seek(0) # Reset file pointer
80
- try:
81
- model_package = torch.load(
82
- f,
83
- map_location=torch.device('cpu'),
84
- weights_only=False
85
- )
86
- except Exception as e2:
87
- # Third try: Load with pickle directly and extract weights only
88
- f.seek(0)
89
- raw_package = pickle.load(f)
90
 
91
- # Try to extract model from various package formats
92
- if isinstance(raw_package, dict):
93
- if 'model' in raw_package:
94
- model_obj = raw_package['model']
95
- elif 'state_dict' in raw_package:
96
- return (f"❌ The pickle contains only state_dict. Please save the full model object.\n\n"
97
- f"Use: torch.save({{'model': model, 'tokenizer': tokenizer, 'config': config}}, 'file.pkl')")
98
- else:
99
- return f"❌ Unknown pickle format. Keys found: {list(raw_package.keys())}"
100
 
101
- # Move model to CPU recursively
102
- def recursive_cpu(obj):
103
- if hasattr(obj, 'cpu'):
104
- return obj.cpu()
105
- elif isinstance(obj, dict):
106
- return {k: recursive_cpu(v) for k, v in obj.items()}
107
- elif isinstance(obj, (list, tuple)):
108
- return type(obj)(recursive_cpu(item) for item in obj)
109
- return obj
110
 
111
- model_package = {
112
- 'model': recursive_cpu(model_obj) if model_obj else None,
113
- 'tokenizer': raw_package.get('tokenizer'),
114
- 'config': raw_package.get('config', {})
115
- }
116
- else:
117
- # Package is the model itself
118
- model_package = {
119
- 'model': recursive_cpu(raw_package),
120
- 'tokenizer': None,
121
- 'config': {}
122
- }
123
-
124
- finally:
125
- # Restore original CUDA_VISIBLE_DEVICES
126
- if old_cuda_visible is not None:
127
- os_module.environ['CUDA_VISIBLE_DEVICES'] = old_cuda_visible
128
- else:
129
- os_module.environ.pop('CUDA_VISIBLE_DEVICES', None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
  # Success! Model loaded with one of the strategies above
132
  # Handle a few common package shapes.
 
35
  if not os.path.exists(pickle_path):
36
  return f"❌ Model file not found: {pickle_path}\n\nPlease ensure best_model.pkl is uploaded to the HuggingFace Space."
37
 
38
+ # ULTIMATE FIX: Check if CPU version exists, if not convert it
39
+ cpu_pickle_path = pickle_path.replace('.pkl', '_cpu.pkl')
 
 
 
40
 
41
+ if not os.path.exists(cpu_pickle_path):
42
+ # Need to convert CUDA pickle to CPU pickle
43
+ try:
44
+ # Use torch.load with custom map_location that captures and remaps ALL devices
45
+ def smart_map_location(storage, location):
46
+ # This function is called for EACH tensor storage during unpickling
47
+ # It runs BEFORE the "CUDA device check", allowing us to remap
48
+ return storage.cpu()
49
+
50
+ # Load with our smart mapper
51
+ model_package = torch.load(pickle_path, map_location=smart_map_location)
52
+
53
+ # Now save it as a CPU-only pickle for future loads
54
+ torch.save(model_package, cpu_pickle_path)
55
+
56
+ return f"✅ Converted CUDA model to CPU! Loading from converted version...\n\nPlease wait, loading model..."
57
+
58
+ except Exception as convert_error:
59
+ # Conversion failed, try direct load with aggressive remapping
60
+ import io
61
+ import pickle as pkl
62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  try:
64
+ # Read the pickle bytes
65
+ with open(pickle_path, 'rb') as f:
66
+ buffer = io.BytesIO(f.read())
67
+
68
+ # Create custom unpickler with aggressive CPU forcing
69
+ class AggressiveCPUUnpickler(pkl.Unpickler):
70
+ def find_class(self, module, name):
71
+ # Remap any CUDA storage to CPU storage
72
+ if 'cuda' in name.lower():
73
+ name = name.replace('cuda', '').replace('Cuda', '')
74
+ return super().find_class(module, name)
 
 
 
75
 
76
+ def load_build(self):
77
+ # Override to catch tensor builds
78
+ stack = self.stack
79
+ state = stack.pop()
80
+ inst = stack[-1]
 
 
 
 
81
 
82
+ # If this is a tensor, force to CPU
83
+ if hasattr(inst, 'to'):
84
+ try:
85
+ inst = inst.cpu()
86
+ stack[-1] = inst
87
+ except:
88
+ pass
 
 
89
 
90
+ if hasattr(inst, '__setstate__'):
91
+ inst.__setstate__(state)
92
+ else:
93
+ for k, v in state.items():
94
+ setattr(inst, k, v)
95
+
96
+ def persistent_load(self, pid):
97
+ # Intercept ALL storage loads
98
+ if isinstance(pid, tuple) and len(pid) >= 5:
99
+ # Standard torch storage format
100
+ tag, storage_type, key, location, size = pid[0], pid[1], pid[2], pid[3], pid[4]
101
+ if tag == 'storage':
102
+ # Force location to CPU
103
+ return (tag, storage_type, key, 'cpu', size)
104
+ return pid
105
+
106
+ # Try to load with aggressive unpickler
107
+ unpickler = AggressiveCPUUnpickler(buffer)
108
+ model_package = unpickler.load()
109
+
110
+ # Save as CPU version for next time
111
+ torch.save(model_package, cpu_pickle_path)
112
+
113
+ except Exception as aggressive_error:
114
+ return (f"❌ Failed to convert CUDA pickle to CPU.\n\n"
115
+ f"Convert error: {str(convert_error)[:100]}\n"
116
+ f"Aggressive error: {str(aggressive_error)[:100]}\n\n"
117
+ f"Please re-save your model on a CPU machine:\n"
118
+ f"```python\n"
119
+ f"import torch\n"
120
+ f"# Load your model\n"
121
+ f"model = model.cpu() # Move to CPU\n"
122
+ f"torch.save({{'model': model, 'tokenizer': tokenizer, 'config': config}}, 'best_model.pkl')\n"
123
+ f"```")
124
+ else:
125
+ # CPU version exists, load it directly
126
+ model_package = torch.load(cpu_pickle_path, map_location='cpu')
127
 
128
  # Success! Model loaded with one of the strategies above
129
  # Handle a few common package shapes.