Alp İpekçiler commited on
Commit
a89e608
·
1 Parent(s): db44cf4

Complete rewrite: Clean, simple backend for ConvNextV2-large-DogBreed

Browse files
Files changed (2) hide show
  1. app.py +48 -78
  2. requirements.txt +1 -1
app.py CHANGED
@@ -1,100 +1,73 @@
1
  """
2
- Production-ready Flask backend for deployment
3
- Optimized for Hugging Face Spaces / Railway / Render
4
  """
5
 
6
  from flask import Flask, request, jsonify
7
- import io
8
- from PIL import Image
9
  from flask_cors import CORS
10
- import logging
 
11
  import os
12
 
13
- # Set cache directory to /tmp for Hugging Face Spaces
14
  os.environ['HF_HOME'] = '/tmp/.cache'
15
  os.environ['TRANSFORMERS_CACHE'] = '/tmp/.cache'
16
  os.environ['TORCH_HOME'] = '/tmp/.cache'
17
 
18
  app = Flask(__name__)
19
- CORS(app, resources={r"/predict_pet": {"origins": "*"}})
20
-
21
- # Logging configuration
22
- logging.basicConfig(
23
- level=logging.INFO,
24
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
25
- )
26
- logger = logging.getLogger(__name__)
27
 
28
- # Model global variables
29
  model = None
30
- image_processor = None
31
 
32
  def load_model():
33
  """Load model on first request"""
34
- global model, image_processor
35
 
36
  if model is not None:
37
  return
38
 
39
- try:
40
- import torch
41
- from transformers import AutoImageProcessor, AutoModelForImageClassification
42
-
43
- logger.info("Loading ConvNextV2-large-DogBreed model...")
44
-
45
- model_name = "Pavarissy/ConvNextV2-large-DogBreed"
46
-
47
- # Detect device
48
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
49
- logger.info(f"Using device: {device}")
50
-
51
- # Load model
52
- image_processor = AutoImageProcessor.from_pretrained(model_name)
53
- model = AutoModelForImageClassification.from_pretrained(model_name)
54
- model = model.to(device)
55
- model.eval()
56
-
57
- logger.info(f"✓ Model loaded successfully on {device}")
58
-
59
- except Exception as e:
60
- logger.error(f"Failed to load model: {e}")
61
- raise
62
 
63
  @app.route('/', methods=['GET'])
64
- def health_check():
65
- """Health check endpoint"""
66
  return jsonify({
67
  'status': 'healthy',
68
- 'service': 'Dog Breed Prediction API',
69
  'model': 'ConvNextV2-large-DogBreed',
70
- 'accuracy': '91.39%',
71
- 'version': '1.0.0'
72
  })
73
 
74
  @app.route('/predict_pet', methods=['POST'])
75
  def predict_pet():
76
- """Predict dog breed from uploaded image"""
77
-
78
  try:
79
- # Load model if not loaded
80
  load_model()
81
 
82
- # Validate request
83
  if 'image' not in request.files:
84
- return jsonify({'error': 'No image file provided'}), 400
85
-
86
- file = request.files['image']
87
 
88
- # Read and validate image
89
- image_bytes = file.read()
90
- pil_image = Image.open(io.BytesIO(image_bytes))
91
-
92
- if pil_image.mode != 'RGB':
93
- pil_image = pil_image.convert('RGB')
94
 
95
- # Make prediction
96
  import torch
97
- inputs = image_processor(images=pil_image, return_tensors="pt")
98
 
99
  device = next(model.parameters()).device
100
  inputs = {k: v.to(device) for k, v in inputs.items()}
@@ -102,35 +75,32 @@ def predict_pet():
102
  with torch.no_grad():
103
  outputs = model(**inputs)
104
  logits = outputs.logits
105
-
106
- probs = torch.nn.functional.softmax(logits, dim=-1)[0].cpu()
107
- top_5_probs, top_5_indices = torch.topk(probs, 5)
108
 
109
- # Format results
110
- top_5_breeds = []
111
- for prob, idx in zip(top_5_probs, top_5_indices):
112
- top_5_breeds.append({
113
- 'breed': model.config.id2label[idx.item()],
114
- 'confidence': float(prob.item())
115
- })
116
 
117
- logger.info(f"Prediction: {top_5_breeds[0]['breed']} ({top_5_breeds[0]['confidence']:.2%})")
118
 
 
119
  return jsonify({
120
- 'predicted_label': top_5_breeds[0]['breed'],
121
- 'breed': top_5_breeds[0]['breed'],
122
- 'confidence': top_5_breeds[0]['confidence'],
123
- 'top_5': top_5_breeds,
124
- 'model': 'ConvNextV2-large-DogBreed',
125
- 'accuracy': '91.39%',
126
  'detection': {'box': {'x': 0, 'y': 0, 'width': 0, 'height': 0}},
127
  'gender': 'Unknown'
128
  })
129
 
130
  except Exception as e:
131
- logger.error(f"Error: {str(e)}", exc_info=True)
 
 
132
  return jsonify({'error': str(e)}), 500
133
 
134
  if __name__ == '__main__':
135
  port = int(os.environ.get('PORT', 7860))
 
136
  app.run(host='0.0.0.0', port=port, debug=False)
 
1
  """
2
+ Clean Pet Backend for Hugging Face Spaces
3
+ Using ConvNextV2-large-DogBreed model
4
  """
5
 
6
  from flask import Flask, request, jsonify
 
 
7
  from flask_cors import CORS
8
+ from PIL import Image
9
+ import io
10
  import os
11
 
12
+ # Set cache directories FIRST
13
  os.environ['HF_HOME'] = '/tmp/.cache'
14
  os.environ['TRANSFORMERS_CACHE'] = '/tmp/.cache'
15
  os.environ['TORCH_HOME'] = '/tmp/.cache'
16
 
17
  app = Flask(__name__)
18
+ CORS(app)
 
 
 
 
 
 
 
19
 
20
+ # Global model variables
21
  model = None
22
+ processor = None
23
 
24
  def load_model():
25
  """Load model on first request"""
26
+ global model, processor
27
 
28
  if model is not None:
29
  return
30
 
31
+ print("🔄 Loading ConvNextV2-large-DogBreed model...")
32
+
33
+ from transformers import AutoImageProcessor, AutoModelForImageClassification
34
+ import torch
35
+
36
+ model_name = "Pavarissy/ConvNextV2-large-DogBreed"
37
+
38
+ processor = AutoImageProcessor.from_pretrained(model_name)
39
+ model = AutoModelForImageClassification.from_pretrained(model_name)
40
+
41
+ device = "cuda" if torch.cuda.is_available() else "cpu"
42
+ model = model.to(device)
43
+ model.eval()
44
+
45
+ print(f"✅ Model loaded on {device}")
 
 
 
 
 
 
 
 
46
 
47
  @app.route('/', methods=['GET'])
48
+ def health():
 
49
  return jsonify({
50
  'status': 'healthy',
 
51
  'model': 'ConvNextV2-large-DogBreed',
52
+ 'accuracy': '91.39%'
 
53
  })
54
 
55
  @app.route('/predict_pet', methods=['POST'])
56
  def predict_pet():
 
 
57
  try:
58
+ # Load model if needed
59
  load_model()
60
 
61
+ # Get image
62
  if 'image' not in request.files:
63
+ return jsonify({'error': 'No image provided'}), 400
 
 
64
 
65
+ file = request.files['image']
66
+ image = Image.open(io.BytesIO(file.read())).convert('RGB')
 
 
 
 
67
 
68
+ # Process and predict
69
  import torch
70
+ inputs = processor(image, return_tensors="pt")
71
 
72
  device = next(model.parameters()).device
73
  inputs = {k: v.to(device) for k, v in inputs.items()}
 
75
  with torch.no_grad():
76
  outputs = model(**inputs)
77
  logits = outputs.logits
 
 
 
78
 
79
+ # Get top prediction
80
+ probs = torch.nn.functional.softmax(logits, dim=-1)[0]
81
+ top_prob, top_idx = torch.max(probs, dim=0)
82
+
83
+ predicted_breed = model.config.id2label[top_idx.item()]
84
+ confidence = float(top_prob.item())
 
85
 
86
+ print(f"✅ Predicted: {predicted_breed} ({confidence:.2%})")
87
 
88
+ # Return in format expected by frontend
89
  return jsonify({
90
+ 'predicted_label': predicted_breed,
91
+ 'breed': predicted_breed,
92
+ 'confidence': confidence,
 
 
 
93
  'detection': {'box': {'x': 0, 'y': 0, 'width': 0, 'height': 0}},
94
  'gender': 'Unknown'
95
  })
96
 
97
  except Exception as e:
98
+ print(f"Error: {str(e)}")
99
+ import traceback
100
+ traceback.print_exc()
101
  return jsonify({'error': str(e)}), 500
102
 
103
  if __name__ == '__main__':
104
  port = int(os.environ.get('PORT', 7860))
105
+ print(f"🚀 Starting server on port {port}")
106
  app.run(host='0.0.0.0', port=port, debug=False)
requirements.txt CHANGED
@@ -2,6 +2,6 @@ flask==3.0.0
2
  flask-cors==4.0.0
3
  transformers==4.35.0
4
  torch==2.1.0
 
5
  pillow==10.1.0
6
  accelerate==0.24.0
7
-
 
2
  flask-cors==4.0.0
3
  transformers==4.35.0
4
  torch==2.1.0
5
+ torchvision==0.16.0
6
  pillow==10.1.0
7
  accelerate==0.24.0