yatinece commited on
Commit
4e78d70
Β·
1 Parent(s): 64f629d

Add application file

Browse files
.dockerignore ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.pyd
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ .venv/
9
+ venv/
10
+ .git/
11
+ .cursor/
12
+ terminals/
13
+ agent-transcripts/
14
+ *.ipynb
15
+ *.png
16
+ *.jpg
17
+ *.jpeg
18
+ *.gif
19
+ *.webp
20
+ *.mp4
21
+ *.mov
22
+ *.avi
23
+ *.mkv
24
+ *.zip
25
+ *.tar
26
+ *.tar.gz
27
+ *.7z
28
+ .DS_Store
29
+
__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ """AI vs Real detector app package."""
2
+
app - Copy (2).py ADDED
@@ -0,0 +1,606 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+ from torchvision import transforms, models
6
+ from PIL import Image
7
+ import gradio as gr
8
+ import numpy as np
9
+ import pandas as pd
10
+ import requests
11
+ from io import BytesIO
12
+ import warnings
13
+ warnings.filterwarnings('ignore')
14
+
15
+ # Global variables
16
+ detector = None
17
+ current_model_path = None
18
+
19
+ class AIDetectorModel(nn.Module):
20
+ def __init__(self, num_classes=2, dropout_prob=0.3, load_pretrained=True):
21
+ super(AIDetectorModel, self).__init__()
22
+
23
+ # Smart loading: only download RegNet if needed
24
+ if load_pretrained:
25
+ print("πŸ“₯ Loading RegNet with pre-trained weights (319MB download)...")
26
+ self.backbone = models.regnet_y_16gf(weights=models.RegNet_Y_16GF_Weights.IMAGENET1K_SWAG_E2E_V1)
27
+ else:
28
+ print("πŸš€ Creating RegNet architecture without pre-trained weights...")
29
+ self.backbone = models.regnet_y_16gf(weights=None)
30
+
31
+ # Freeze most layers (only relevant for pre-trained)
32
+ for param in self.backbone.parameters():
33
+ param.requires_grad = False
34
+
35
+ # Unfreeze last block
36
+ if hasattr(self.backbone, 'trunk_output') and hasattr(self.backbone.trunk_output, 'block4'):
37
+ self.backbone.trunk_output.block4.requires_grad_(True)
38
+
39
+ # Replace average pooling with max pooling
40
+ self.backbone.avgpool = nn.AdaptiveMaxPool2d(output_size=(1, 1))
41
+
42
+ # Get feature dimension
43
+ num_ftrs = self.backbone.fc.in_features
44
+
45
+ # Replace classifier with custom layers
46
+ self.backbone.fc = nn.Sequential(
47
+ nn.Linear(num_ftrs, 2048),
48
+ nn.SiLU(),
49
+ nn.Dropout(dropout_prob),
50
+ nn.Linear(2048, 1024),
51
+ nn.SiLU(),
52
+ nn.Dropout(dropout_prob),
53
+ nn.Linear(1024, 512),
54
+ nn.SiLU(),
55
+ nn.Dropout(dropout_prob),
56
+ nn.Linear(512, num_classes)
57
+ )
58
+
59
+ def forward(self, x):
60
+ return self.backbone(x)
61
+
62
+ def analyze_checkpoint(checkpoint_path):
63
+ """Analyze checkpoint to determine if backbone weights are included"""
64
+ try:
65
+ print(f"πŸ” Analyzing checkpoint: {checkpoint_path}")
66
+ checkpoint = torch.load(checkpoint_path, map_location='cpu')
67
+
68
+ # Extract state dict
69
+ if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint:
70
+ state_dict = checkpoint['model_state_dict']
71
+ print("πŸ“¦ Found structured checkpoint with model_state_dict")
72
+ else:
73
+ state_dict = checkpoint
74
+ print("πŸ“¦ Found direct state_dict checkpoint")
75
+
76
+ # Analyze parameters
77
+ all_keys = list(state_dict.keys())
78
+ backbone_params = [k for k in all_keys if k.startswith('backbone.')]
79
+ backbone_conv_params = [k for k in backbone_params if 'conv' in k]
80
+ backbone_block_params = [k for k in backbone_params if 'block' in k]
81
+ classifier_params = [k for k in all_keys if 'fc' in k]
82
+
83
+ total_params = len(all_keys)
84
+
85
+ print(f"πŸ“Š Parameter analysis:")
86
+ print(f" β€’ Total parameters: {total_params}")
87
+ print(f" β€’ Backbone parameters: {len(backbone_params)}")
88
+ print(f" β€’ Backbone conv layers: {len(backbone_conv_params)}")
89
+ print(f" β€’ Backbone blocks: {len(backbone_block_params)}")
90
+ print(f" β€’ Classifier parameters: {len(classifier_params)}")
91
+
92
+ # Determine if full backbone is included
93
+ # RegNet has many backbone parameters, so if we have 100+ backbone params, it's likely complete
94
+ has_full_backbone = len(backbone_params) > 100 and len(backbone_conv_params) > 10
95
+
96
+ if has_full_backbone:
97
+ print("βœ… COMPLETE MODEL DETECTED - backbone weights included!")
98
+ print("πŸš€ Will skip RegNet download for faster loading")
99
+ else:
100
+ print("⚠️ Incomplete model detected - backbone weights missing")
101
+ print("πŸ“₯ Will download RegNet pre-trained weights")
102
+
103
+ return has_full_backbone, checkpoint
104
+
105
+ except Exception as e:
106
+ print(f"❌ Error analyzing checkpoint: {e}")
107
+ print("πŸ“₯ Will fallback to downloading RegNet weights")
108
+ return False, None
109
+
110
+ class AIImageDetector:
111
+ def __init__(self, model_path, device=None):
112
+ """Initialize AI Image Detector with smart loading"""
113
+ self.device = device if device else ('cuda' if torch.cuda.is_available() else 'cpu')
114
+ print(f"πŸš€ Initializing AI Image Detector on {self.device}")
115
+
116
+ # Analyze checkpoint to determine loading strategy
117
+ has_backbone, checkpoint = analyze_checkpoint(model_path)
118
+
119
+ # Load model with optimized strategy
120
+ self.model = self._load_model(model_path, has_backbone, checkpoint)
121
+ self.model.eval()
122
+
123
+ # Define image preprocessing transforms
124
+ self.transform = transforms.Compose([
125
+ transforms.Resize(224, interpolation=transforms.InterpolationMode.BICUBIC),
126
+ transforms.CenterCrop(224),
127
+ transforms.ToTensor(),
128
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
129
+ ])
130
+
131
+ print("βœ… Model loaded and ready for inference!")
132
+
133
+ def _load_model(self, model_path, has_backbone, checkpoint):
134
+ """Load model with optimized backbone loading"""
135
+ try:
136
+ # Load checkpoint if not already loaded
137
+ if checkpoint is None:
138
+ checkpoint = torch.load(model_path, map_location=self.device)
139
+
140
+ # Determine whether to load pre-trained weights
141
+ load_pretrained = not has_backbone
142
+
143
+ # Create model
144
+ if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint:
145
+ # Structured checkpoint
146
+ model = AIDetectorModel(
147
+ num_classes=checkpoint.get('num_classes', 2),
148
+ dropout_prob=checkpoint.get('dropout_prob', 0.3),
149
+ load_pretrained=load_pretrained
150
+ )
151
+ model.load_state_dict(checkpoint['model_state_dict'])
152
+
153
+ # Show additional info if available
154
+ if 'epoch' in checkpoint:
155
+ print(f"πŸ“Š Loaded model from epoch {checkpoint['epoch']}")
156
+ if 'val_loss' in checkpoint:
157
+ print(f"πŸ“Š Validation loss: {checkpoint['val_loss']:.4f}")
158
+
159
+ else:
160
+ # Direct state dict
161
+ model = AIDetectorModel(load_pretrained=load_pretrained)
162
+ model.load_state_dict(checkpoint)
163
+
164
+ return model.to(self.device)
165
+
166
+ except Exception as e:
167
+ print(f"❌ Error loading model: {e}")
168
+ print("πŸ”„ Attempting fallback loading...")
169
+
170
+ # Fallback: try with pre-trained weights
171
+ try:
172
+ model = AIDetectorModel(load_pretrained=True)
173
+ if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint:
174
+ model.load_state_dict(checkpoint['model_state_dict'])
175
+ else:
176
+ model.load_state_dict(checkpoint)
177
+ return model.to(self.device)
178
+ except Exception as fallback_error:
179
+ print(f"❌ Fallback loading also failed: {fallback_error}")
180
+ raise e
181
+
182
+ def preprocess_image(self, image):
183
+ """Preprocess image for model input"""
184
+ try:
185
+ # Ensure RGB format
186
+ if image.mode != 'RGB':
187
+ image = image.convert('RGB')
188
+
189
+ # Apply transforms and add batch dimension
190
+ tensor = self.transform(image).unsqueeze(0)
191
+ return tensor.to(self.device)
192
+
193
+ except Exception as e:
194
+ print(f"❌ Error preprocessing image: {e}")
195
+ raise e
196
+
197
+ def predict(self, image):
198
+ """
199
+ Predict if image is real or AI-generated
200
+
201
+ Returns:
202
+ tuple: (prediction, confidence, probabilities)
203
+ """
204
+ try:
205
+ # Preprocess image
206
+ input_tensor = self.preprocess_image(image)
207
+
208
+ # Run inference
209
+ with torch.no_grad():
210
+ outputs = self.model(input_tensor)
211
+ probabilities = F.softmax(outputs, dim=1)
212
+ confidence, predicted = torch.max(probabilities, 1)
213
+
214
+ # Convert to numpy for easier handling
215
+ probs = probabilities.cpu().numpy()[0]
216
+ pred_class = predicted.cpu().item()
217
+ conf_score = confidence.cpu().item()
218
+
219
+ # Map to class names
220
+ class_names = ['REAL', 'FAKE']
221
+ prediction = class_names[pred_class]
222
+
223
+ return prediction, conf_score, probs
224
+
225
+ except Exception as e:
226
+ print(f"❌ Error during prediction: {e}")
227
+ raise e
228
+
229
+ def predict_from_url(self, url):
230
+ """Download image from URL and make prediction"""
231
+ try:
232
+ # Download image with timeout
233
+ headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}
234
+ response = requests.get(url, timeout=15, headers=headers)
235
+ response.raise_for_status()
236
+
237
+ # Load image
238
+ image = Image.open(BytesIO(response.content))
239
+
240
+ # Make prediction
241
+ prediction, confidence, probabilities = self.predict(image)
242
+
243
+ return prediction, confidence, probabilities, image
244
+
245
+ except requests.exceptions.RequestException as e:
246
+ print(f"❌ Error downloading image: {e}")
247
+ raise Exception(f"Failed to download image from URL: {str(e)}")
248
+ except Exception as e:
249
+ print(f"❌ Error processing image from URL: {e}")
250
+ raise e
251
+
252
+ def get_available_models():
253
+ """Get list of available model files"""
254
+ # Define possible model files
255
+ model_options = [
256
+ 'best_ai_detector.pth',
257
+ 'best_ai_detector_new.pth'
258
+ ]
259
+
260
+ # Check which files actually exist
261
+ available_models = []
262
+ for model in model_options:
263
+ if os.path.exists(model):
264
+ available_models.append(model)
265
+
266
+ # If no models found, return the default options anyway
267
+ if not available_models:
268
+ available_models = model_options
269
+ print("⚠️ No model files found in current directory, showing default options")
270
+ else:
271
+ print(f"βœ… Found {len(available_models)} model file(s): {available_models}")
272
+
273
+ return available_models
274
+
275
+ def load_detector(model_path=None):
276
+ """Load the detector model with specified path"""
277
+ global detector, current_model_path
278
+
279
+ try:
280
+ # Use specified path or default
281
+ if model_path is None:
282
+ model_path = 'best_ai_detector.pth'
283
+
284
+ # Check if the same model is already loaded
285
+ if detector is not None and current_model_path == model_path:
286
+ return f"βœ… Model '{model_path}' is already loaded and ready!"
287
+
288
+ # Check if file exists
289
+ if not os.path.exists(model_path):
290
+ available_models = get_available_models()
291
+ error_msg = f"❌ Model file '{model_path}' not found!\n\n"
292
+ error_msg += f"Available models in current directory:\n"
293
+ for model in available_models:
294
+ exists = "βœ…" if os.path.exists(model) else "❌"
295
+ error_msg += f" {exists} {model}\n"
296
+ error_msg += f"\nPlease ensure your trained model file is uploaded to the Space."
297
+ return error_msg
298
+
299
+ print(f"🎯 Loading model: {model_path}")
300
+
301
+ # Initialize detector
302
+ detector = AIImageDetector(model_path)
303
+ current_model_path = model_path
304
+
305
+ return f"βœ… Model '{model_path}' loaded successfully!\nπŸš€ Ready for image analysis."
306
+
307
+ except Exception as e:
308
+ error_msg = f"❌ Error loading model '{model_path}': {str(e)}\n\n"
309
+ error_msg += "This might be due to:\n"
310
+ error_msg += "β€’ Incompatible model file\n"
311
+ error_msg += "β€’ Corrupted checkpoint\n"
312
+ error_msg += "β€’ Missing dependencies\n"
313
+ return error_msg
314
+
315
+ def predict_image(image):
316
+ """Handle image upload and prediction"""
317
+ global detector
318
+
319
+ # Check if detector is loaded
320
+ if detector is None:
321
+ return "❌ Please load a model first using the dropdown and 'Load Model' button!", None, None, None
322
+
323
+ if image is None:
324
+ return "❌ Please upload an image first!", None, None, None
325
+
326
+ try:
327
+ # Make prediction
328
+ prediction, confidence, probabilities = detector.predict(image)
329
+
330
+ # Format detailed results
331
+ result_text = f"πŸ” **Prediction: {prediction}**\n\n"
332
+ result_text += f"πŸ“Š **Confidence: {confidence:.1%}**\n\n"
333
+ result_text += f"πŸ“ˆ **Detailed Probabilities:**\n"
334
+ result_text += f"β€’ 🟒 REAL (Human-made): {probabilities[0]:.1%}\n"
335
+ result_text += f"β€’ πŸ”΄ FAKE (AI-generated): {probabilities[1]:.1%}\n\n"
336
+
337
+ # Add interpretation
338
+ if confidence > 0.8:
339
+ result_text += f"πŸ’ͺ **High confidence prediction**"
340
+ elif confidence > 0.6:
341
+ result_text += f"πŸ€” **Moderate confidence prediction**"
342
+ else:
343
+ result_text += f"⚠️ **Low confidence - uncertain prediction**"
344
+
345
+ # Create chart data
346
+ prob_df = pd.DataFrame({
347
+ 'Category': ['REAL (Human)', 'FAKE (AI)'],
348
+ 'Probability': [float(probabilities[0]), float(probabilities[1])]
349
+ })
350
+
351
+ # Quick status
352
+ color = "🟒" if prediction == "REAL" else "πŸ”΄"
353
+ status = f"{color} {prediction} - {confidence:.1%} confidence"
354
+
355
+ return result_text, prob_df, prediction, status
356
+
357
+ except Exception as e:
358
+ error_msg = f"❌ Prediction failed: {str(e)}"
359
+ print(error_msg)
360
+ return error_msg, None, None, None
361
+
362
+ def predict_from_url(url):
363
+ """Handle URL input and prediction"""
364
+ global detector
365
+
366
+ # Check if detector is loaded
367
+ if detector is None:
368
+ return "❌ Please load a model first using the dropdown and 'Load Model' button!", None, None, None, None
369
+
370
+ if not url or not url.strip():
371
+ return "❌ Please enter a valid image URL!", None, None, None, None
372
+
373
+ try:
374
+ # Download and predict
375
+ prediction, confidence, probabilities, image = detector.predict_from_url(url.strip())
376
+
377
+ # Format results (same as upload)
378
+ result_text = f"πŸ” **Prediction: {prediction}**\n\n"
379
+ result_text += f"πŸ“Š **Confidence: {confidence:.1%}**\n\n"
380
+ result_text += f"πŸ“ˆ **Detailed Probabilities:**\n"
381
+ result_text += f"β€’ 🟒 REAL (Human-made): {probabilities[0]:.1%}\n"
382
+ result_text += f"β€’ πŸ”΄ FAKE (AI-generated): {probabilities[1]:.1%}\n\n"
383
+
384
+ # Add interpretation
385
+ if confidence > 0.8:
386
+ result_text += f"πŸ’ͺ **High confidence prediction**"
387
+ elif confidence > 0.6:
388
+ result_text += f"πŸ€” **Moderate confidence prediction**"
389
+ else:
390
+ result_text += f"⚠️ **Low confidence - uncertain prediction**"
391
+
392
+ # Create chart data
393
+ prob_df = pd.DataFrame({
394
+ 'Category': ['REAL (Human)', 'FAKE (AI)'],
395
+ 'Probability': [float(probabilities[0]), float(probabilities[1])]
396
+ })
397
+
398
+ # Quick status
399
+ color = "🟒" if prediction == "REAL" else "πŸ”΄"
400
+ status = f"{color} {prediction} - {confidence:.1%} confidence"
401
+
402
+ return result_text, prob_df, prediction, status, image
403
+
404
+ except Exception as e:
405
+ error_msg = f"❌ URL processing failed: {str(e)}"
406
+ print(error_msg)
407
+ return error_msg, None, None, None, None
408
+
409
+ def create_interface():
410
+ """Create the Gradio web interface"""
411
+
412
+ # Simplified CSS for better compatibility
413
+ css = """
414
+ .gradio-container {
415
+ background-color: #1a1a1a !important;
416
+ color: white !important;
417
+ }
418
+
419
+ .main-header {
420
+ text-align: center;
421
+ background: linear-gradient(135deg, #eb001b 0%, #ff5f00 100%);
422
+ color: white !important;
423
+ padding: 30px;
424
+ border-radius: 15px;
425
+ margin-bottom: 25px;
426
+ }
427
+
428
+ .model-section {
429
+ background: #2a2a2a !important;
430
+ padding: 25px;
431
+ border-radius: 15px;
432
+ margin-bottom: 25px;
433
+ border: 2px solid #ff5f00;
434
+ }
435
+ """
436
+
437
+ with gr.Blocks(css=css, title="AI Image Detector") as demo:
438
+ # Main header
439
+ gr.HTML("""
440
+ <div class="main-header">
441
+ <h1>πŸ€– AI Image Detector</h1>
442
+ <p>Upload an image or provide a URL to detect if it's REAL (human-made) or FAKE (AI-generated)</p>
443
+ </div>
444
+ """)
445
+
446
+ # Model selection section
447
+ with gr.Group():
448
+ gr.HTML("""
449
+ <div class="model-section">
450
+ <h3>πŸ”§ Model Selection & Loading</h3>
451
+ </div>
452
+ """)
453
+
454
+ with gr.Row():
455
+ with gr.Column(scale=2):
456
+ model_dropdown = gr.Dropdown(
457
+ choices=get_available_models(),
458
+ value='best_ai_detector.pth',
459
+ label="🎯 Select Model",
460
+ info="Choose which trained model to load"
461
+ )
462
+ with gr.Column(scale=1):
463
+ load_btn = gr.Button("πŸ”„ Load Model", variant="secondary")
464
+
465
+ model_status = gr.Textbox(
466
+ label="πŸ“Š Model Status",
467
+ value="Select a model and click 'Load Model' to initialize the AI detector",
468
+ interactive=False,
469
+ lines=2
470
+ )
471
+
472
+ with gr.Tabs():
473
+ # Image upload tab
474
+ with gr.TabItem("πŸ“ Upload Image"):
475
+ with gr.Row():
476
+ with gr.Column(scale=1):
477
+ input_image = gr.Image(
478
+ label="πŸ“· Upload Image for Analysis",
479
+ type="pil",
480
+ height=400
481
+ )
482
+ predict_btn = gr.Button(
483
+ "πŸ” Analyze Image",
484
+ variant="primary"
485
+ )
486
+
487
+ with gr.Column(scale=1):
488
+ prediction_status = gr.Textbox(
489
+ label="🎯 Quick Result",
490
+ interactive=False,
491
+ lines=1
492
+ )
493
+
494
+ prediction_output = gr.Markdown(
495
+ label="πŸ“Š Detailed Analysis",
496
+ value="Load a model and upload an image to get AI detection results"
497
+ )
498
+
499
+ probability_chart = gr.BarPlot(
500
+ label="πŸ“ˆ Probability Distribution",
501
+ x="Category",
502
+ y="Probability",
503
+ width=400,
504
+ height=250
505
+ )
506
+
507
+ # URL input tab
508
+ with gr.TabItem("🌐 Image URL"):
509
+ with gr.Row():
510
+ with gr.Column(scale=1):
511
+ url_input = gr.Textbox(
512
+ label="πŸ”— Image URL",
513
+ placeholder="https://example.com/image.jpg",
514
+ lines=2
515
+ )
516
+ url_predict_btn = gr.Button(
517
+ "πŸ” Analyze from URL",
518
+ variant="primary"
519
+ )
520
+
521
+ downloaded_image = gr.Image(
522
+ label="πŸ“₯ Downloaded Image",
523
+ type="pil",
524
+ height=300
525
+ )
526
+
527
+ with gr.Column(scale=1):
528
+ url_prediction_status = gr.Textbox(
529
+ label="🎯 Quick Result",
530
+ interactive=False,
531
+ lines=1
532
+ )
533
+
534
+ url_prediction_output = gr.Markdown(
535
+ label="πŸ“Š Detailed Analysis",
536
+ value="Load a model and enter an image URL to get AI detection results"
537
+ )
538
+
539
+ url_probability_chart = gr.BarPlot(
540
+ label="πŸ“ˆ Probability Distribution",
541
+ x="Category",
542
+ y="Probability",
543
+ width=400,
544
+ height=250
545
+ )
546
+
547
+ # Information section
548
+ with gr.Accordion("ℹ️ About This AI Detector", open=False):
549
+ gr.Markdown("""
550
+ ### 🎯 What This Model Does
551
+
552
+ This AI detector analyzes images to determine if they are:
553
+ - **🟒 REAL**: Created by humans (photographs, traditional digital art, etc.)
554
+ - **πŸ”΄ FAKE**: Generated by AI systems (DALL-E, Midjourney, Stable Diffusion, etc.)
555
+
556
+ ### πŸ“Š Understanding the Results
557
+
558
+ - **Prediction**: The model's best guess (REAL or FAKE)
559
+ - **Confidence**: How certain the model is (higher = more confident)
560
+ - **Probabilities**: Breakdown showing likelihood for each category
561
+ """)
562
+
563
+ # Connect event handlers
564
+ load_btn.click(
565
+ fn=load_detector,
566
+ inputs=[model_dropdown],
567
+ outputs=model_status
568
+ )
569
+
570
+ predict_btn.click(
571
+ fn=predict_image,
572
+ inputs=[input_image],
573
+ outputs=[prediction_output, probability_chart, gr.State(), prediction_status]
574
+ )
575
+
576
+ url_predict_btn.click(
577
+ fn=predict_from_url,
578
+ inputs=[url_input],
579
+ outputs=[url_prediction_output, url_probability_chart, gr.State(), url_prediction_status, downloaded_image]
580
+ )
581
+
582
+ return demo
583
+
584
+ if __name__ == "__main__":
585
+ print("πŸš€ Starting AI Image Detector with Model Selection...")
586
+ print("πŸ“ Available models can be selected from dropdown")
587
+
588
+ # Check available models at startup
589
+ available_models = get_available_models()
590
+ print(f"🎯 Available models: {available_models}")
591
+
592
+ # Create interface
593
+ demo = create_interface()
594
+
595
+ # Don't auto-load model - let user choose
596
+ print("πŸ’‘ Select a model from the dropdown and click 'Load Model' to begin")
597
+
598
+ # Launch interface
599
+ print("🌐 Launching interface...")
600
+ demo.launch(
601
+ server_name="0.0.0.0",
602
+ server_port=7860,
603
+ share=True,
604
+ show_error=True,
605
+ debug=False
606
+ )
app - Copy.py ADDED
@@ -0,0 +1,626 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+ from torchvision import transforms, models
6
+ from PIL import Image
7
+ import gradio as gr
8
+ import numpy as np
9
+ import pandas as pd
10
+ import requests
11
+ from io import BytesIO
12
+ import warnings
13
+ warnings.filterwarnings('ignore')
14
+
15
+ # Global variables
16
+ detector = None
17
+
18
+ class AIDetectorModel(nn.Module):
19
+ def __init__(self, num_classes=2, dropout_prob=0.3, load_pretrained=True):
20
+ super(AIDetectorModel, self).__init__()
21
+
22
+ # Smart loading: only download RegNet if needed
23
+ if load_pretrained:
24
+ print("πŸ“₯ Loading RegNet with pre-trained weights (319MB download)...")
25
+ self.backbone = models.regnet_y_16gf(weights=models.RegNet_Y_16GF_Weights.IMAGENET1K_SWAG_E2E_V1)
26
+ else:
27
+ print("πŸš€ Creating RegNet architecture without pre-trained weights...")
28
+ self.backbone = models.regnet_y_16gf(weights=None)
29
+
30
+ # Freeze most layers (only relevant for pre-trained)
31
+ for param in self.backbone.parameters():
32
+ param.requires_grad = False
33
+
34
+ # Unfreeze last block
35
+ if hasattr(self.backbone, 'trunk_output') and hasattr(self.backbone.trunk_output, 'block4'):
36
+ self.backbone.trunk_output.block4.requires_grad_(True)
37
+
38
+ # Replace average pooling with max pooling
39
+ self.backbone.avgpool = nn.AdaptiveMaxPool2d(output_size=(1, 1))
40
+
41
+ # Get feature dimension
42
+ num_ftrs = self.backbone.fc.in_features
43
+
44
+ # Replace classifier with custom layers
45
+ self.backbone.fc = nn.Sequential(
46
+ nn.Linear(num_ftrs, 2048),
47
+ nn.SiLU(),
48
+ nn.Dropout(dropout_prob),
49
+ nn.Linear(2048, 1024),
50
+ nn.SiLU(),
51
+ nn.Dropout(dropout_prob),
52
+ nn.Linear(1024, 512),
53
+ nn.SiLU(),
54
+ nn.Dropout(dropout_prob),
55
+ nn.Linear(512, num_classes)
56
+ )
57
+
58
+ def forward(self, x):
59
+ return self.backbone(x)
60
+
61
+ def analyze_checkpoint(checkpoint_path):
62
+ """Analyze checkpoint to determine if backbone weights are included"""
63
+ try:
64
+ print(f"πŸ” Analyzing checkpoint: {checkpoint_path}")
65
+ checkpoint = torch.load(checkpoint_path, map_location='cpu')
66
+
67
+ # Extract state dict
68
+ if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint:
69
+ state_dict = checkpoint['model_state_dict']
70
+ print("πŸ“¦ Found structured checkpoint with model_state_dict")
71
+ else:
72
+ state_dict = checkpoint
73
+ print("πŸ“¦ Found direct state_dict checkpoint")
74
+
75
+ # Analyze parameters
76
+ all_keys = list(state_dict.keys())
77
+ backbone_params = [k for k in all_keys if k.startswith('backbone.')]
78
+ backbone_conv_params = [k for k in backbone_params if 'conv' in k]
79
+ backbone_block_params = [k for k in backbone_params if 'block' in k]
80
+ classifier_params = [k for k in all_keys if 'fc' in k]
81
+
82
+ total_params = len(all_keys)
83
+
84
+ print(f"πŸ“Š Parameter analysis:")
85
+ print(f" β€’ Total parameters: {total_params}")
86
+ print(f" β€’ Backbone parameters: {len(backbone_params)}")
87
+ print(f" β€’ Backbone conv layers: {len(backbone_conv_params)}")
88
+ print(f" β€’ Backbone blocks: {len(backbone_block_params)}")
89
+ print(f" β€’ Classifier parameters: {len(classifier_params)}")
90
+
91
+ # Determine if full backbone is included
92
+ # RegNet has many backbone parameters, so if we have 100+ backbone params, it's likely complete
93
+ has_full_backbone = len(backbone_params) > 100 and len(backbone_conv_params) > 10
94
+
95
+ if has_full_backbone:
96
+ print("βœ… COMPLETE MODEL DETECTED - backbone weights included!")
97
+ print("πŸš€ Will skip RegNet download for faster loading")
98
+ else:
99
+ print("⚠️ Incomplete model detected - backbone weights missing")
100
+ print("πŸ“₯ Will download RegNet pre-trained weights")
101
+
102
+ return has_full_backbone, checkpoint
103
+
104
+ except Exception as e:
105
+ print(f"❌ Error analyzing checkpoint: {e}")
106
+ print("πŸ“₯ Will fallback to downloading RegNet weights")
107
+ return False, None
108
+
109
+ class AIImageDetector:
110
+ def __init__(self, model_path, device=None):
111
+ """Initialize AI Image Detector with smart loading"""
112
+ self.device = device if device else ('cuda' if torch.cuda.is_available() else 'cpu')
113
+ print(f"πŸš€ Initializing AI Image Detector on {self.device}")
114
+
115
+ # Analyze checkpoint to determine loading strategy
116
+ has_backbone, checkpoint = analyze_checkpoint(model_path)
117
+
118
+ # Load model with optimized strategy
119
+ self.model = self._load_model(model_path, has_backbone, checkpoint)
120
+ self.model.eval()
121
+
122
+ # Define image preprocessing transforms
123
+ self.transform = transforms.Compose([
124
+ transforms.Resize(224, interpolation=transforms.InterpolationMode.BICUBIC),
125
+ transforms.CenterCrop(224),
126
+ transforms.ToTensor(),
127
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
128
+ ])
129
+
130
+ print("βœ… Model loaded and ready for inference!")
131
+
132
+ def _load_model(self, model_path, has_backbone, checkpoint):
133
+ """Load model with optimized backbone loading"""
134
+ try:
135
+ # Load checkpoint if not already loaded
136
+ if checkpoint is None:
137
+ checkpoint = torch.load(model_path, map_location=self.device)
138
+
139
+ # Determine whether to load pre-trained weights
140
+ load_pretrained = not has_backbone
141
+
142
+ # Create model
143
+ if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint:
144
+ # Structured checkpoint
145
+ model = AIDetectorModel(
146
+ num_classes=checkpoint.get('num_classes', 2),
147
+ dropout_prob=checkpoint.get('dropout_prob', 0.3),
148
+ load_pretrained=load_pretrained
149
+ )
150
+ model.load_state_dict(checkpoint['model_state_dict'])
151
+
152
+ # Show additional info if available
153
+ if 'epoch' in checkpoint:
154
+ print(f"πŸ“Š Loaded model from epoch {checkpoint['epoch']}")
155
+ if 'val_loss' in checkpoint:
156
+ print(f"πŸ“Š Validation loss: {checkpoint['val_loss']:.4f}")
157
+
158
+ else:
159
+ # Direct state dict
160
+ model = AIDetectorModel(load_pretrained=load_pretrained)
161
+ model.load_state_dict(checkpoint)
162
+
163
+ return model.to(self.device)
164
+
165
+ except Exception as e:
166
+ print(f"❌ Error loading model: {e}")
167
+ print("πŸ”„ Attempting fallback loading...")
168
+
169
+ # Fallback: try with pre-trained weights
170
+ try:
171
+ model = AIDetectorModel(load_pretrained=True)
172
+ if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint:
173
+ model.load_state_dict(checkpoint['model_state_dict'])
174
+ else:
175
+ model.load_state_dict(checkpoint)
176
+ return model.to(self.device)
177
+ except Exception as fallback_error:
178
+ print(f"❌ Fallback loading also failed: {fallback_error}")
179
+ raise e
180
+
181
+ def preprocess_image(self, image):
182
+ """Preprocess image for model input"""
183
+ try:
184
+ # Ensure RGB format
185
+ if image.mode != 'RGB':
186
+ image = image.convert('RGB')
187
+
188
+ # Apply transforms and add batch dimension
189
+ tensor = self.transform(image).unsqueeze(0)
190
+ return tensor.to(self.device)
191
+
192
+ except Exception as e:
193
+ print(f"❌ Error preprocessing image: {e}")
194
+ raise e
195
+
196
+ def predict(self, image):
197
+ """
198
+ Predict if image is real or AI-generated
199
+
200
+ Returns:
201
+ tuple: (prediction, confidence, probabilities)
202
+ """
203
+ try:
204
+ # Preprocess image
205
+ input_tensor = self.preprocess_image(image)
206
+
207
+ # Run inference
208
+ with torch.no_grad():
209
+ outputs = self.model(input_tensor)
210
+ probabilities = F.softmax(outputs, dim=1)
211
+ confidence, predicted = torch.max(probabilities, 1)
212
+
213
+ # Convert to numpy for easier handling
214
+ probs = probabilities.cpu().numpy()[0]
215
+ pred_class = predicted.cpu().item()
216
+ conf_score = confidence.cpu().item()
217
+
218
+ # Map to class names
219
+ class_names = ['REAL', 'FAKE']
220
+ prediction = class_names[pred_class]
221
+
222
+ return prediction, conf_score, probs
223
+
224
+ except Exception as e:
225
+ print(f"❌ Error during prediction: {e}")
226
+ raise e
227
+
228
+ def predict_from_url(self, url):
229
+ """Download image from URL and make prediction"""
230
+ try:
231
+ # Download image with timeout
232
+ headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}
233
+ response = requests.get(url, timeout=15, headers=headers)
234
+ response.raise_for_status()
235
+
236
+ # Load image
237
+ image = Image.open(BytesIO(response.content))
238
+
239
+ # Make prediction
240
+ prediction, confidence, probabilities = self.predict(image)
241
+
242
+ return prediction, confidence, probabilities, image
243
+
244
+ except requests.exceptions.RequestException as e:
245
+ print(f"❌ Error downloading image: {e}")
246
+ raise Exception(f"Failed to download image from URL: {str(e)}")
247
+ except Exception as e:
248
+ print(f"❌ Error processing image from URL: {e}")
249
+ raise e
250
+
251
+ def load_detector(model_path=None):
252
+ """Load the detector model with automatic path detection"""
253
+ global detector
254
+
255
+ if detector is None:
256
+ try:
257
+ # Define possible model file locations
258
+ possible_paths = [
259
+ 'best_ai_detector.pth',
260
+ 'ai_detector_complete.pth',
261
+ './models/best_ai_detector.pth',
262
+ './models/ai_detector_complete.pth',
263
+ './checkpoints/best_ai_detector.pth',
264
+ './checkpoints/ai_detector_complete.pth'
265
+ ]
266
+
267
+ # Use specified path or find available file
268
+ if model_path and os.path.exists(model_path):
269
+ target_path = model_path
270
+ print(f"🎯 Using specified model: {target_path}")
271
+ else:
272
+ target_path = None
273
+ print(f"πŸ” Searching for model files...")
274
+
275
+ for path in possible_paths:
276
+ if os.path.exists(path):
277
+ target_path = path
278
+ print(f"βœ… Found model file: {path}")
279
+ break
280
+
281
+ if target_path is None:
282
+ # List available files for debugging
283
+ current_files = [f for f in os.listdir('.') if f.endswith('.pth')]
284
+ error_msg = f"❌ No model file found!\n\n"
285
+ error_msg += f"Searched locations:\n"
286
+ for path in possible_paths:
287
+ error_msg += f" β€’ {path}\n"
288
+ error_msg += f"\nAvailable .pth files in current directory:\n"
289
+ for file in current_files:
290
+ error_msg += f" β€’ {file}\n"
291
+ error_msg += f"\nPlease ensure your trained model file is uploaded to the Space."
292
+ return error_msg
293
+
294
+ # Initialize detector
295
+ detector = AIImageDetector(target_path)
296
+ return f"βœ… Model loaded successfully from {target_path}!"
297
+
298
+ except Exception as e:
299
+ error_msg = f"❌ Error loading model: {str(e)}\n\n"
300
+ error_msg += "This might be due to:\n"
301
+ error_msg += "β€’ Incompatible model file\n"
302
+ error_msg += "β€’ Corrupted checkpoint\n"
303
+ error_msg += "β€’ Missing dependencies\n"
304
+ return error_msg
305
+
306
+ return "βœ… Model already loaded and ready!"
307
+
308
+ def predict_image(image):
309
+ """Handle image upload and prediction"""
310
+ global detector
311
+
312
+ # Ensure detector is loaded
313
+ if detector is None:
314
+ load_result = load_detector()
315
+ if "❌" in load_result:
316
+ return load_result, None, None, None
317
+
318
+ if image is None:
319
+ return "❌ Please upload an image first!", None, None, None
320
+
321
+ try:
322
+ # Make prediction
323
+ prediction, confidence, probabilities = detector.predict(image)
324
+
325
+ # Format detailed results
326
+ result_text = f"πŸ” **Prediction: {prediction}**\n\n"
327
+ result_text += f"πŸ“Š **Confidence: {confidence:.1%}**\n\n"
328
+ result_text += f"πŸ“ˆ **Detailed Probabilities:**\n"
329
+ result_text += f"β€’ 🟒 REAL (Human-made): {probabilities[0]:.1%}\n"
330
+ result_text += f"β€’ πŸ”΄ FAKE (AI-generated): {probabilities[1]:.1%}\n\n"
331
+
332
+ # Add interpretation
333
+ if confidence > 0.8:
334
+ result_text += f"πŸ’ͺ **High confidence prediction**"
335
+ elif confidence > 0.6:
336
+ result_text += f"πŸ€” **Moderate confidence prediction**"
337
+ else:
338
+ result_text += f"⚠️ **Low confidence - uncertain prediction**"
339
+
340
+ # Create chart data
341
+ prob_df = pd.DataFrame({
342
+ 'Category': ['REAL (Human)', 'FAKE (AI)'],
343
+ 'Probability': [float(probabilities[0]), float(probabilities[1])]
344
+ })
345
+
346
+ # Quick status
347
+ color = "🟒" if prediction == "REAL" else "πŸ”΄"
348
+ status = f"{color} {prediction} - {confidence:.1%} confidence"
349
+
350
+ return result_text, prob_df, prediction, status
351
+
352
+ except Exception as e:
353
+ error_msg = f"❌ Prediction failed: {str(e)}"
354
+ print(error_msg)
355
+ return error_msg, None, None, None
356
+
357
+ def predict_from_url(url):
358
+ """Handle URL input and prediction"""
359
+ global detector
360
+
361
+ # Ensure detector is loaded
362
+ if detector is None:
363
+ load_result = load_detector()
364
+ if "❌" in load_result:
365
+ return load_result, None, None, None, None
366
+
367
+ if not url or not url.strip():
368
+ return "❌ Please enter a valid image URL!", None, None, None, None
369
+
370
+ try:
371
+ # Download and predict
372
+ prediction, confidence, probabilities, image = detector.predict_from_url(url.strip())
373
+
374
+ # Format results (same as upload)
375
+ result_text = f"πŸ” **Prediction: {prediction}**\n\n"
376
+ result_text += f"πŸ“Š **Confidence: {confidence:.1%}**\n\n"
377
+ result_text += f"πŸ“ˆ **Detailed Probabilities:**\n"
378
+ result_text += f"β€’ 🟒 REAL (Human-made): {probabilities[0]:.1%}\n"
379
+ result_text += f"β€’ πŸ”΄ FAKE (AI-generated): {probabilities[1]:.1%}\n\n"
380
+
381
+ # Add interpretation
382
+ if confidence > 0.8:
383
+ result_text += f"πŸ’ͺ **High confidence prediction**"
384
+ elif confidence > 0.6:
385
+ result_text += f"πŸ€” **Moderate confidence prediction**"
386
+ else:
387
+ result_text += f"⚠️ **Low confidence - uncertain prediction**"
388
+
389
+ # Create chart data
390
+ prob_df = pd.DataFrame({
391
+ 'Category': ['REAL (Human)', 'FAKE (AI)'],
392
+ 'Probability': [float(probabilities[0]), float(probabilities[1])]
393
+ })
394
+
395
+ # Quick status
396
+ color = "🟒" if prediction == "REAL" else "πŸ”΄"
397
+ status = f"{color} {prediction} - {confidence:.1%} confidence"
398
+
399
+ return result_text, prob_df, prediction, status, image
400
+
401
+ except Exception as e:
402
+ error_msg = f"❌ URL processing failed: {str(e)}"
403
+ print(error_msg)
404
+ return error_msg, None, None, None, None
405
+
406
+ def create_interface():
407
+ """Create the Gradio web interface"""
408
+
409
+ # Custom CSS for better styling
410
+ css = """
411
+ .main-header {
412
+ text-align: center;
413
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
414
+ color: white;
415
+ padding: 20px;
416
+ border-radius: 10px;
417
+ margin-bottom: 20px;
418
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
419
+ }
420
+ .status-box {
421
+ font-size: 18px;
422
+ font-weight: bold;
423
+ text-align: center;
424
+ }
425
+ """
426
+
427
+ with gr.Blocks(css=css, title="AI Image Detector", theme=gr.themes.Soft()) as demo:
428
+ # Main header
429
+ gr.HTML("""
430
+ <div class="main-header">
431
+ <h1>πŸ€– AI Image Detector</h1>
432
+ <p>Upload an image or provide a URL to detect if it's REAL (human-made) or FAKE (AI-generated)</p>
433
+ <p><small>Powered by RegNetY-16GF with custom classification layers</small></p>
434
+ </div>
435
+ """)
436
+
437
+ # Model loading section
438
+ with gr.Row():
439
+ with gr.Column(scale=3):
440
+ model_status = gr.Textbox(
441
+ label="πŸ”§ Model Status",
442
+ value="Click 'Load Model' to initialize the AI detector",
443
+ interactive=False,
444
+ lines=2
445
+ )
446
+ with gr.Column(scale=1):
447
+ load_btn = gr.Button("πŸ”„ Load Model", variant="secondary", size="lg")
448
+
449
+ # Main functionality tabs
450
+ with gr.Tabs():
451
+ # Image upload tab
452
+ with gr.TabItem("πŸ“ Upload Image"):
453
+ with gr.Row():
454
+ with gr.Column(scale=1):
455
+ input_image = gr.Image(
456
+ label="πŸ“· Upload Image for Analysis",
457
+ type="pil",
458
+ height=400
459
+ )
460
+ predict_btn = gr.Button(
461
+ "πŸ” Analyze Image",
462
+ variant="primary",
463
+ size="lg"
464
+ )
465
+
466
+ with gr.Column(scale=1):
467
+ prediction_status = gr.Textbox(
468
+ label="🎯 Quick Result",
469
+ interactive=False,
470
+ lines=1,
471
+ elem_classes=["status-box"]
472
+ )
473
+
474
+ prediction_output = gr.Markdown(
475
+ label="πŸ“Š Detailed Analysis",
476
+ value="Upload an image and click 'Analyze Image' to get AI detection results"
477
+ )
478
+
479
+ probability_chart = gr.BarPlot(
480
+ label="πŸ“ˆ Probability Distribution",
481
+ x="Category",
482
+ y="Probability",
483
+ width=400,
484
+ height=250,
485
+ color="Category"
486
+ )
487
+
488
+ # URL input tab
489
+ with gr.TabItem("🌐 Image URL"):
490
+ with gr.Row():
491
+ with gr.Column(scale=1):
492
+ url_input = gr.Textbox(
493
+ label="πŸ”— Image URL",
494
+ placeholder="https://example.com/image.jpg",
495
+ lines=2
496
+ )
497
+ url_predict_btn = gr.Button(
498
+ "πŸ” Analyze from URL",
499
+ variant="primary",
500
+ size="lg"
501
+ )
502
+
503
+ downloaded_image = gr.Image(
504
+ label="πŸ“₯ Downloaded Image",
505
+ type="pil",
506
+ height=300
507
+ )
508
+
509
+ with gr.Column(scale=1):
510
+ url_prediction_status = gr.Textbox(
511
+ label="🎯 Quick Result",
512
+ interactive=False,
513
+ lines=1,
514
+ elem_classes=["status-box"]
515
+ )
516
+
517
+ url_prediction_output = gr.Markdown(
518
+ label="πŸ“Š Detailed Analysis",
519
+ value="Enter an image URL and click 'Analyze from URL' to get AI detection results"
520
+ )
521
+
522
+ url_probability_chart = gr.BarPlot(
523
+ label="πŸ“ˆ Probability Distribution",
524
+ x="Category",
525
+ y="Probability",
526
+ width=400,
527
+ height=250,
528
+ color="Category"
529
+ )
530
+
531
+ # Information and help section
532
+ with gr.Accordion("ℹ️ About This AI Detector", open=False):
533
+ gr.Markdown("""
534
+ ### 🎯 What This Model Does
535
+
536
+ This AI detector analyzes images to determine if they are:
537
+ - **🟒 REAL**: Created by humans (photographs, traditional digital art, etc.)
538
+ - **πŸ”΄ FAKE**: Generated by AI systems (DALL-E, Midjourney, Stable Diffusion, etc.)
539
+
540
+ ### πŸ“Š Understanding the Results
541
+
542
+ - **Prediction**: The model's best guess (REAL or FAKE)
543
+ - **Confidence**: How certain the model is (higher = more confident)
544
+ - **Probabilities**: Breakdown showing likelihood for each category
545
+
546
+ ### 🧠 Model Architecture
547
+
548
+ - **Backbone**: RegNetY-16GF (efficient computer vision model)
549
+ - **Input Size**: 224Γ—224 pixels
550
+ - **Classes**: Binary classification (REAL vs FAKE)
551
+ - **Training**: Fine-tuned on diverse real and AI-generated images
552
+
553
+ ### ⚠️ Important Limitations
554
+
555
+ - This is a research tool and may not be 100% accurate
556
+ - Performance varies depending on image type and AI generation method
557
+ - Always use human judgment alongside model predictions
558
+ - New AI generation techniques may not be detected accurately
559
+
560
+ ### πŸ’‘ Tips for Best Results
561
+
562
+ - Use clear, high-quality images
563
+ - Avoid heavily compressed or low-resolution images
564
+ - Be aware that the model may struggle with:
565
+ - Very recent AI generation techniques
566
+ - Heavily edited or filtered images
567
+ - Images with mixed real/AI content
568
+ """)
569
+
570
+ # Connect event handlers
571
+ load_btn.click(
572
+ fn=load_detector,
573
+ outputs=model_status
574
+ )
575
+
576
+ predict_btn.click(
577
+ fn=predict_image,
578
+ inputs=[input_image],
579
+ outputs=[prediction_output, probability_chart, gr.State(), prediction_status]
580
+ )
581
+
582
+ url_predict_btn.click(
583
+ fn=predict_from_url,
584
+ inputs=[url_input],
585
+ outputs=[url_prediction_output, url_probability_chart, gr.State(), url_prediction_status, downloaded_image]
586
+ )
587
+
588
+ # Optional: Add example images if you have them
589
+ # gr.Examples(
590
+ # examples=[
591
+ # ["path/to/example_real.jpg"],
592
+ # ["path/to/example_fake.jpg"],
593
+ # ],
594
+ # inputs=input_image,
595
+ # label="πŸ“· Try These Examples"
596
+ # )
597
+
598
+ return demo
599
+
600
+ if __name__ == "__main__":
601
+ # Initialize
602
+ detector = AIImageDetector('best_ai_detector.pth')
603
+ print("πŸš€ Starting Optimized AI Image Detector...")
604
+ print("πŸ“ This version will automatically detect if RegNet download is needed")
605
+
606
+ # Create interface
607
+ demo = create_interface()
608
+
609
+ # Try to auto-load model at startup (optional)
610
+ try:
611
+ print("πŸ”„ Attempting to auto-load model...")
612
+ load_result = load_detector()
613
+ print(f"πŸ”§ Startup result: {load_result}")
614
+ except Exception as e:
615
+ print(f"⚠️ Model not auto-loaded: {e}")
616
+ print("πŸ’‘ Use the 'Load Model' button in the interface")
617
+
618
+ # Launch interface optimized for Hugging Face Spaces
619
+ print("🌐 Launching interface...")
620
+ demo.launch(
621
+ server_name="0.0.0.0",
622
+ server_port=7860,
623
+ share=True, # Don't use share=True in Hugging Face Spaces
624
+ show_error=True,
625
+ debug=False,ssr_mode=False
626
+ )
app_v0.py ADDED
@@ -0,0 +1,351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+ from torchvision import transforms, models
6
+ from PIL import Image
7
+ import gradio as gr
8
+ import numpy as np
9
+ import pandas as pd
10
+ import warnings
11
+ import requests
12
+ from io import BytesIO
13
+
14
+ warnings.filterwarnings('ignore')
15
+
16
+ # =============================================================================
17
+ # MODEL FUNCTIONS
18
+ # =============================================================================
19
+
20
+ class AIDetectorModel(nn.Module):
21
+ def __init__(self, num_classes=2, dropout_prob=0.3):
22
+ super(AIDetectorModel, self).__init__()
23
+
24
+ self.backbone = models.regnet_y_16gf(weights=None)
25
+ self.backbone.avgpool = nn.AdaptiveMaxPool2d(output_size=(1, 1))
26
+
27
+ num_ftrs = self.backbone.fc.in_features
28
+ self.backbone.fc = nn.Sequential(
29
+ nn.Linear(num_ftrs, 2048),
30
+ nn.SiLU(),
31
+ nn.Dropout(dropout_prob),
32
+ nn.Linear(2048, 1024),
33
+ nn.SiLU(),
34
+ nn.Dropout(dropout_prob),
35
+ nn.Linear(1024, 512),
36
+ nn.SiLU(),
37
+ nn.Dropout(dropout_prob),
38
+ nn.Linear(512, num_classes)
39
+ )
40
+
41
+ def forward(self, x):
42
+ return self.backbone(x)
43
+
44
+ class AIDetector:
45
+ def __init__(self):
46
+ self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
47
+ self.model = None
48
+ self.model_loaded = False
49
+
50
+ self.transform = transforms.Compose([
51
+ transforms.Resize(224, interpolation=transforms.InterpolationMode.BICUBIC),
52
+ transforms.CenterCrop(224),
53
+ transforms.ToTensor(),
54
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
55
+ ])
56
+
57
+ def load_model(self, model_path):
58
+ try:
59
+ if not os.path.exists(model_path):
60
+ return f"Error: Model file '{model_path}' not found"
61
+
62
+ checkpoint = torch.load(model_path, map_location='cpu')
63
+
64
+ if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint:
65
+ self.model = AIDetectorModel(
66
+ num_classes=checkpoint.get('num_classes', 2),
67
+ dropout_prob=checkpoint.get('dropout_prob', 0.3)
68
+ )
69
+ self.model.load_state_dict(checkpoint['model_state_dict'])
70
+ else:
71
+ self.model = AIDetectorModel()
72
+ self.model.load_state_dict(checkpoint)
73
+
74
+ self.model.to(self.device)
75
+ self.model.eval()
76
+ self.model_loaded = True
77
+
78
+ file_size = os.path.getsize(model_path) / (1024*1024)
79
+ return f"βœ… Model '{model_path}' loaded successfully! ({file_size:.1f}MB)"
80
+
81
+ except Exception as e:
82
+ self.model_loaded = False
83
+ return f"❌ Failed to load model: {str(e)}"
84
+
85
+ def predict(self, image):
86
+ if not self.model_loaded:
87
+ raise RuntimeError("No model loaded")
88
+
89
+ if image.mode != 'RGB':
90
+ image = image.convert('RGB')
91
+
92
+ input_tensor = self.transform(image).unsqueeze(0).to(self.device)
93
+
94
+ with torch.no_grad():
95
+ outputs = self.model(input_tensor)
96
+ probabilities = F.softmax(outputs, dim=1)
97
+ confidence, predicted = torch.max(probabilities, 1)
98
+
99
+ probs = probabilities.cpu().numpy()[0]
100
+ pred_class = predicted.cpu().item()
101
+ conf_score = confidence.cpu().item()
102
+
103
+ class_names = ['REAL', 'FAKE']
104
+ prediction = class_names[pred_class]
105
+
106
+ return prediction, conf_score, probs
107
+
108
+ # =============================================================================
109
+ # GLOBAL VARIABLES AND UTILITY FUNCTIONS
110
+ # =============================================================================
111
+
112
+ detector = AIDetector()
113
+
114
+ def get_model_files():
115
+ """Get available .pth model files in current directory"""
116
+ try:
117
+ files = [f for f in os.listdir('.') if f.endswith('.pth')]
118
+ if not files:
119
+ files = ["No .pth files found"]
120
+ return files
121
+ except:
122
+ return ["Error reading directory"]
123
+
124
+ def refresh_models():
125
+ """Refresh the model list"""
126
+ return gr.Dropdown(choices=get_model_files())
127
+
128
+ # =============================================================================
129
+ # UI HANDLER FUNCTIONS
130
+ # =============================================================================
131
+
132
+ def load_model_handler(model_name):
133
+ if not model_name or model_name in ["No .pth files found", "Error reading directory"]:
134
+ return "Please select a valid model file"
135
+
136
+ return detector.load_model(model_name)
137
+
138
+ def predict_handler(image):
139
+ if not detector.model_loaded:
140
+ return "❌ No model loaded. Please load a model first.", None, "No Model", "0%"
141
+
142
+ if image is None:
143
+ return "❌ Please upload an image", None, "No Image", "0%"
144
+
145
+ try:
146
+ prediction, confidence, probabilities = detector.predict(image)
147
+
148
+ result_text = f"""
149
+ ## Analysis Results
150
+
151
+ **Prediction:** {prediction}
152
+ **Confidence:** {confidence:.1%}
153
+
154
+ **Detailed Probabilities:**
155
+ - REAL (Human-made): {probabilities[0]:.1%}
156
+ - FAKE (AI-generated): {probabilities[1]:.1%}
157
+
158
+ **Confidence Level:** {"High" if confidence > 0.8 else "Moderate" if confidence > 0.6 else "Low"}
159
+ """
160
+
161
+ chart_data = pd.DataFrame({
162
+ 'Category': ['REAL', 'FAKE'],
163
+ 'Probability': [probabilities[0], probabilities[1]]
164
+ })
165
+
166
+ return result_text, chart_data, prediction, f"{confidence:.1%}"
167
+
168
+ except Exception as e:
169
+ return f"❌ Prediction failed: {str(e)}", None, "Error", "0%"
170
+
171
+ def url_handler(url):
172
+ if not url or not url.strip():
173
+ return "❌ Please enter a URL", None, "No URL", "0%", None
174
+
175
+ try:
176
+ url = url.strip()
177
+ if not url.startswith(('http://', 'https://')):
178
+ url = 'https://' + url
179
+
180
+ response = requests.get(url, timeout=10, headers={'User-Agent': 'Mozilla/5.0'})
181
+ response.raise_for_status()
182
+
183
+ image = Image.open(BytesIO(response.content))
184
+ result_text, chart_data, prediction, confidence = predict_handler(image)
185
+
186
+ return result_text, chart_data, prediction, confidence, image
187
+
188
+ except Exception as e:
189
+ return f"❌ Failed to load image from URL: {str(e)}", None, "Error", "0%", None
190
+
191
+ def clear_handler():
192
+ return None, "Upload an image to analyze", None, "No Image", "0%"
193
+
194
+ def clear_url_handler():
195
+ return "", "Enter an image URL to analyze", None, "No URL", "0%", None
196
+
197
+ # =============================================================================
198
+ # GRADIO INTERFACE
199
+ # =============================================================================
200
+
201
+ def create_interface():
202
+ # Simple theme without custom CSS
203
+ theme = theme = gr.themes.Soft(
204
+ primary_hue="orange",
205
+ secondary_hue="gray"
206
+ ).set(
207
+ body_background_fill="*neutral_950",
208
+ block_background_fill="*neutral_900",
209
+ button_primary_background_fill="*orange_500",
210
+ button_primary_background_fill_hover="*orange_600"
211
+ )
212
+
213
+ with gr.Blocks(theme=theme, title="AI Image Detector") as demo:
214
+
215
+ gr.Markdown("# AI Image Detector")
216
+ gr.Markdown("Upload an image or provide a URL to detect if it's AI-generated or real.")
217
+
218
+ # Model Loading Section
219
+ with gr.Row():
220
+ with gr.Column(scale=3):
221
+ model_dropdown = gr.Dropdown(
222
+ label="Select Model File",
223
+ choices=get_model_files(),
224
+ value=None,
225
+ interactive=True
226
+ )
227
+ with gr.Column(scale=1):
228
+ load_btn = gr.Button("Load Model", variant="primary")
229
+ refresh_btn = gr.Button("Refresh List", variant="secondary")
230
+
231
+ model_status = gr.Textbox(
232
+ label="Model Status",
233
+ value="No model loaded",
234
+ interactive=False,
235
+ lines=2
236
+ )
237
+
238
+ # Main Interface
239
+ with gr.Tabs():
240
+ # Upload Tab
241
+ with gr.TabItem("Upload Image"):
242
+ with gr.Row():
243
+ with gr.Column():
244
+ upload_image = gr.Image(
245
+ label="Upload Image",
246
+ type="pil"
247
+ )
248
+ with gr.Row():
249
+ analyze_btn = gr.Button("Analyze Image", variant="primary")
250
+ clear_btn = gr.Button("Clear")
251
+
252
+ with gr.Column():
253
+ status_row = gr.Row()
254
+ with status_row:
255
+ result_status = gr.Textbox(label="Result", interactive=False)
256
+ confidence_status = gr.Textbox(label="Confidence", interactive=False)
257
+
258
+ results_text = gr.Markdown("Upload an image to analyze")
259
+ results_chart = gr.BarPlot(
260
+ x="Category",
261
+ y="Probability",
262
+ title="Prediction Probabilities",
263
+ height=300
264
+ )
265
+
266
+ # URL Tab
267
+ with gr.TabItem("Analyze URL"):
268
+ with gr.Row():
269
+ with gr.Column():
270
+ url_input = gr.Textbox(
271
+ label="Image URL",
272
+ placeholder="https://example.com/image.jpg"
273
+ )
274
+ with gr.Row():
275
+ url_btn = gr.Button("Load & Analyze", variant="primary")
276
+ url_clear_btn = gr.Button("Clear")
277
+
278
+ url_image = gr.Image(label="Loaded Image", type="pil")
279
+
280
+ with gr.Column():
281
+ url_status_row = gr.Row()
282
+ with url_status_row:
283
+ url_result_status = gr.Textbox(label="Result", interactive=False)
284
+ url_confidence_status = gr.Textbox(label="Confidence", interactive=False)
285
+
286
+ url_results_text = gr.Markdown("Enter an image URL to analyze")
287
+ url_results_chart = gr.BarPlot(
288
+ x="Category",
289
+ y="Probability",
290
+ title="URL Prediction Probabilities",
291
+ height=300
292
+ )
293
+
294
+ # Event Handlers
295
+ load_btn.click(
296
+ fn=load_model_handler,
297
+ inputs=[model_dropdown],
298
+ outputs=[model_status]
299
+ )
300
+
301
+ refresh_btn.click(
302
+ fn=refresh_models,
303
+ outputs=[model_dropdown]
304
+ )
305
+
306
+ analyze_btn.click(
307
+ fn=predict_handler,
308
+ inputs=[upload_image],
309
+ outputs=[results_text, results_chart, result_status, confidence_status]
310
+ )
311
+
312
+ url_btn.click(
313
+ fn=url_handler,
314
+ inputs=[url_input],
315
+ outputs=[url_results_text, url_results_chart, url_result_status, url_confidence_status, url_image]
316
+ )
317
+
318
+ clear_btn.click(
319
+ fn=clear_handler,
320
+ outputs=[upload_image, results_text, results_chart, result_status, confidence_status]
321
+ )
322
+
323
+ url_clear_btn.click(
324
+ fn=clear_url_handler,
325
+ outputs=[url_input, url_results_text, url_results_chart, url_result_status, url_confidence_status, url_image]
326
+ )
327
+
328
+ return demo
329
+
330
+ # =============================================================================
331
+ # MAIN FUNCTION
332
+ # =============================================================================
333
+
334
+ def main():
335
+ print("AI Image Detector Starting...")
336
+ print(f"PyTorch: {torch.__version__}")
337
+ print(f"CUDA Available: {torch.cuda.is_available()}")
338
+ print(f"Device: {'GPU' if torch.cuda.is_available() else 'CPU'}")
339
+
340
+ models = get_model_files()
341
+ print(f"Found models: {models}")
342
+
343
+ demo = create_interface()
344
+ demo.launch(
345
+ server_name="0.0.0.0",
346
+ server_port=7860,
347
+ share=False
348
+ )
349
+
350
+ if __name__ == "__main__":
351
+ main()
best_ai_detector.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f1cbd0d1efdf3c8f84622b519ff204e286d8ccfb90ffe106ec96b5efbbb34311
3
+ size 358207028
best_ai_detector_new.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:59044857b458fd807706133bf2d4cb2ac7089c10dc30c6f24ffaf3476a7611fa
3
+ size 358210596
saveFullModel.py ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from torchvision import models
4
+ import os
5
+
6
+ class AIDetectorModel(nn.Module):
7
+ def __init__(self, num_classes=2, dropout_prob=0.3, load_pretrained=True):
8
+ super(AIDetectorModel, self).__init__()
9
+
10
+ if load_pretrained:
11
+ print("πŸ“₯ Loading RegNet with pre-trained weights...")
12
+ self.backbone = models.regnet_y_16gf(weights=models.RegNet_Y_16GF_Weights.IMAGENET1K_SWAG_E2E_V1)
13
+ else:
14
+ self.backbone = models.regnet_y_16gf(weights=None)
15
+
16
+ # Freeze most layers
17
+ for param in self.backbone.parameters():
18
+ param.requires_grad = False
19
+
20
+ # Unfreeze last block
21
+ if hasattr(self.backbone, 'trunk_output') and hasattr(self.backbone.trunk_output, 'block4'):
22
+ self.backbone.trunk_output.block4.requires_grad_(True)
23
+
24
+ # Replace average pooling with max pooling
25
+ self.backbone.avgpool = nn.AdaptiveMaxPool2d(output_size=(1, 1))
26
+
27
+ # Get feature dimension
28
+ num_ftrs = self.backbone.fc.in_features
29
+
30
+ # Replace classifier
31
+ self.backbone.fc = nn.Sequential(
32
+ nn.Linear(num_ftrs, 2048),
33
+ nn.SiLU(),
34
+ nn.Dropout(dropout_prob),
35
+ nn.Linear(2048, 1024),
36
+ nn.SiLU(),
37
+ nn.Dropout(dropout_prob),
38
+ nn.Linear(1024, 512),
39
+ nn.SiLU(),
40
+ nn.Dropout(dropout_prob),
41
+ nn.Linear(512, num_classes)
42
+ )
43
+
44
+ def forward(self, x):
45
+ return self.backbone(x)
46
+
47
+ def verify_complete_backbone(model):
48
+ """Verify that model has complete RegNet backbone - CORRECTED"""
49
+ state_dict = model.state_dict()
50
+ all_keys = list(state_dict.keys())
51
+
52
+ # Check for actual RegNet components based on real structure
53
+ stem_keys = [k for k in all_keys if 'backbone.stem' in k]
54
+ block_keys = [k for k in all_keys if 'backbone.trunk_output.block' in k]
55
+ proj_keys = [k for k in all_keys if 'backbone.trunk_output.block' in k and 'proj' in k]
56
+ fc_keys = [k for k in all_keys if 'backbone.fc' in k]
57
+
58
+ print(f"πŸ” Backbone verification:")
59
+ print(f" β€’ Total keys: {len(all_keys)}")
60
+ print(f" β€’ Stem keys: {len(stem_keys)}")
61
+ print(f" β€’ Block keys: {len(block_keys)}")
62
+ print(f" β€’ Projection keys: {len(proj_keys)}")
63
+ print(f" β€’ FC keys: {len(fc_keys)}")
64
+
65
+ # CORRECTED: RegNet Y 16GF has: ~420 block params, ~24 proj layers, 6 stem params
66
+ is_complete = (
67
+ len(block_keys) > 400 and # Should have ~420 block parameters
68
+ len(stem_keys) > 5 and # Should have 6 stem parameters
69
+ len(proj_keys) > 20 # Should have ~24 projection parameters
70
+ )
71
+
72
+ if is_complete:
73
+ print("βœ… Complete backbone verified!")
74
+ print(f"βœ… Found {len(block_keys)} blocks, {len(stem_keys)} stem, {len(proj_keys)} proj layers")
75
+ else:
76
+ print("❌ Incomplete backbone detected!")
77
+ print(f"Expected: >400 blocks, >5 stem, >20 proj")
78
+ print(f"Found: {len(block_keys)} blocks, {len(stem_keys)} stem, {len(proj_keys)} proj")
79
+
80
+ return is_complete
81
+
82
+ def create_truly_complete_model(trained_checkpoint_path, output_path):
83
+ """Create a truly complete model with full RegNet weights"""
84
+
85
+ print("πŸ”„ Creating TRULY complete offline model...")
86
+ print("=" * 60)
87
+
88
+ # Step 1: Create fresh model WITH pretrained weights
89
+ print("πŸ“₯ Step 1: Loading fresh RegNet with ImageNet weights...")
90
+ fresh_model = AIDetectorModel(load_pretrained=True)
91
+
92
+ # Step 2: Verify fresh model is complete (FIXED VALIDATION)
93
+ print("\nπŸ” Step 2: Verifying fresh model completeness...")
94
+ if not verify_complete_backbone(fresh_model):
95
+ raise RuntimeError("❌ Fresh model is not complete!")
96
+
97
+ # Step 3: Load your trained weights
98
+ print(f"\nπŸ“‚ Step 3: Loading your trained weights from {trained_checkpoint_path}...")
99
+ trained_checkpoint = torch.load(trained_checkpoint_path, map_location='cpu')
100
+
101
+ if isinstance(trained_checkpoint, dict) and 'model_state_dict' in trained_checkpoint:
102
+ trained_state = trained_checkpoint['model_state_dict']
103
+ extra_info = {
104
+ 'num_classes': trained_checkpoint.get('num_classes', 2),
105
+ 'dropout_prob': trained_checkpoint.get('dropout_prob', 0.3),
106
+ 'epoch': trained_checkpoint.get('epoch', 0),
107
+ 'val_loss': trained_checkpoint.get('val_loss', 0.0)
108
+ }
109
+ else:
110
+ trained_state = trained_checkpoint
111
+ extra_info = {'num_classes': 2, 'dropout_prob': 0.3}
112
+
113
+ # Step 4: Merge weights intelligently
114
+ print("\nπŸ”€ Step 4: Merging backbone + trained classifier...")
115
+ fresh_state = fresh_model.state_dict()
116
+
117
+ # Keep ALL backbone weights from fresh model (includes RegNet pretrained)
118
+ # Replace ONLY classifier weights from trained model
119
+ merged_state = fresh_state.copy()
120
+
121
+ classifier_keys = [k for k in trained_state.keys() if 'backbone.fc' in k]
122
+ print(f" β€’ Replacing {len(classifier_keys)} classifier parameters")
123
+
124
+ for key in classifier_keys:
125
+ if key in trained_state:
126
+ merged_state[key] = trained_state[key]
127
+ print(f" βœ… Updated: {key}")
128
+
129
+ # Step 5: Create final model and verify
130
+ print("\nβœ… Step 5: Creating final complete model...")
131
+ final_model = AIDetectorModel(load_pretrained=False) # Empty architecture
132
+ final_model.load_state_dict(merged_state) # Load merged weights
133
+
134
+ # Final verification (should definitely pass now)
135
+ print("\nπŸ” Final verification:")
136
+ if not verify_complete_backbone(final_model):
137
+ raise RuntimeError("❌ Final model verification failed!")
138
+
139
+ # Step 6: Save complete model
140
+ print(f"\nπŸ’Ύ Step 6: Saving complete model to {output_path}...")
141
+ complete_checkpoint = {
142
+ 'model_state_dict': merged_state,
143
+ 'complete_model': True,
144
+ 'backbone_included': True,
145
+ 'offline_ready': True,
146
+ **extra_info
147
+ }
148
+
149
+ torch.save(complete_checkpoint, output_path)
150
+
151
+ file_size = os.path.getsize(output_path) / (1024*1024)
152
+ print(f"βœ… SUCCESS! Complete model saved:")
153
+ print(f" β€’ File: {output_path}")
154
+ print(f" β€’ Size: {file_size:.1f}MB")
155
+ print(f" β€’ Ready for offline use!")
156
+
157
+ return output_path
158
+
159
+ if __name__ == "__main__":
160
+ # Input and output paths
161
+ input_model = "2_block_best_ai_detector.pth" # Your trained model
162
+ output_model = "truly_complete_ai_detector_new.pth" # New complete model
163
+
164
+ if not os.path.exists(input_model):
165
+ print(f"❌ Trained model not found: {input_model}")
166
+ # Show available files
167
+ pth_files = [f for f in os.listdir('.') if f.endswith('.pth')]
168
+ if pth_files:
169
+ print("Available .pth files:")
170
+ for f in pth_files:
171
+ size = os.path.getsize(f) / (1024*1024)
172
+ print(f" β€’ {f} ({size:.1f}MB)")
173
+ else:
174
+ print("No .pth files found in current directory")
175
+ else:
176
+ try:
177
+ result_path = create_truly_complete_model(input_model, output_model)
178
+ print("\n" + "="*60)
179
+ print(f"πŸŽ‰ SUCCESS! Use this file in your offline app:")
180
+ print(f"πŸ“ {result_path}")
181
+ print("="*60)
182
+ except Exception as e:
183
+ print(f"❌ Failed to create complete model: {e}")
static/styles.css ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root{
2
+ --bg:#0b0f14;
3
+ --card:#121824;
4
+ --card2:#0f1520;
5
+ --text:#e8eef7;
6
+ --muted:#a9b6c7;
7
+ --accent:#ff5f00;
8
+ --accent2:#eb001b;
9
+ --border:rgba(255,255,255,.08);
10
+ }
11
+ *{box-sizing:border-box}
12
+ body{
13
+ margin:0;
14
+ font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";
15
+ background: radial-gradient(1200px 600px at 20% 0%, rgba(255,95,0,.25), transparent 50%),
16
+ radial-gradient(1000px 500px at 90% 10%, rgba(235,0,27,.18), transparent 55%),
17
+ var(--bg);
18
+ color:var(--text);
19
+ }
20
+ .container{max-width:1100px;margin:0 auto;padding:28px 18px 60px}
21
+ .header{
22
+ background: linear-gradient(135deg, rgba(235,0,27,.95), rgba(255,95,0,.95));
23
+ border-radius:16px;
24
+ padding:22px 22px;
25
+ border:1px solid rgba(255,255,255,.12);
26
+ }
27
+ h1{margin:0 0 6px 0;font-size:28px}
28
+ .sub{margin:0;color:rgba(255,255,255,.92)}
29
+ .grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-top:14px}
30
+ .card{
31
+ background: linear-gradient(180deg, rgba(18,24,36,.95), rgba(15,21,32,.95));
32
+ border:1px solid var(--border);
33
+ border-radius:16px;
34
+ padding:16px;
35
+ box-shadow: 0 10px 30px rgba(0,0,0,.25);
36
+ }
37
+ .card h2{margin:0 0 12px 0;font-size:16px;color:var(--text)}
38
+ .row{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap}
39
+ .field{display:flex;flex-direction:column;gap:6px;width:100%}
40
+ .field span{font-size:12px;color:var(--muted)}
41
+ select,input[type="url"],input[type="number"],input[type="file"]{
42
+ width:100%;
43
+ padding:10px 10px;
44
+ border-radius:10px;
45
+ border:1px solid var(--border);
46
+ background:#0b111b;
47
+ color:var(--text);
48
+ outline:none;
49
+ }
50
+ .btn{
51
+ appearance:none;
52
+ border:1px solid rgba(255,255,255,.14);
53
+ background: linear-gradient(135deg, var(--accent2), var(--accent));
54
+ color:white;
55
+ border-radius:12px;
56
+ padding:10px 14px;
57
+ font-weight:700;
58
+ cursor:pointer;
59
+ width:100%;
60
+ }
61
+ .btn.secondary{
62
+ background: transparent;
63
+ border:1px solid rgba(255,255,255,.18);
64
+ }
65
+ .btn:hover{filter:brightness(1.03)}
66
+ .status{margin-top:12px;padding:10px;border:1px dashed rgba(255,255,255,.16);border-radius:12px}
67
+ .label{font-size:12px;color:var(--muted);margin-bottom:6px}
68
+ .mono{font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;}
69
+ .result{margin-top:12px}
70
+ .pill{
71
+ display:inline-block;
72
+ padding:8px 10px;
73
+ border-radius:999px;
74
+ border:1px solid rgba(255,255,255,.14);
75
+ background: rgba(255,255,255,.06);
76
+ font-weight:700;
77
+ }
78
+ .hint{margin-top:8px;font-size:12px;color:var(--muted)}
79
+ .media-row{margin-top:10px}
80
+ .preview{
81
+ width:100%;
82
+ height:auto;
83
+ border-radius:14px;
84
+ border:1px solid rgba(255,255,255,.10);
85
+ background: rgba(0,0,0,.18);
86
+ }
87
+ .kv{
88
+ margin-top:10px;
89
+ display:grid;
90
+ grid-template-columns:1fr 1fr;
91
+ gap:8px 10px;
92
+ }
93
+ .kv .k{
94
+ display:block;
95
+ font-size:12px;
96
+ color:var(--muted);
97
+ margin-bottom:4px;
98
+ }
99
+ .kv .v{
100
+ display:block;
101
+ padding:8px 10px;
102
+ border-radius:12px;
103
+ border:1px solid rgba(255,255,255,.10);
104
+ background: rgba(0,0,0,.18);
105
+ }
106
+ .frame-grid{
107
+ margin-top:12px;
108
+ display:grid;
109
+ grid-template-columns: repeat(2, 1fr);
110
+ gap:10px;
111
+ }
112
+ .frame-card{
113
+ border:1px solid rgba(255,255,255,.10);
114
+ background: rgba(0,0,0,.12);
115
+ border-radius:14px;
116
+ overflow:hidden;
117
+ }
118
+ .frame{
119
+ width:100%;
120
+ height:auto;
121
+ display:block;
122
+ }
123
+ .frame-meta{
124
+ padding:10px;
125
+ color:var(--muted);
126
+ display:flex;
127
+ flex-direction:column;
128
+ gap:6px;
129
+ }
130
+ .history{
131
+ margin-top:10px;
132
+ display:flex;
133
+ flex-direction:column;
134
+ gap:10px;
135
+ }
136
+ .history-item{
137
+ border:1px solid rgba(255,255,255,.10);
138
+ background: rgba(0,0,0,.14);
139
+ border-radius:14px;
140
+ padding:10px;
141
+ }
142
+ .history-top{
143
+ display:flex;
144
+ justify-content:space-between;
145
+ gap:10px;
146
+ color:var(--muted);
147
+ font-size:12px;
148
+ }
149
+ .history-mid{
150
+ margin-top:6px;
151
+ display:flex;
152
+ flex-direction:column;
153
+ gap:4px;
154
+ }
155
+ .history-bottom{
156
+ margin-top:8px;
157
+ font-weight:700;
158
+ color:var(--text);
159
+ }
160
+ .error{
161
+ padding:10px;
162
+ border-radius:12px;
163
+ border:1px solid rgba(255,95,0,.35);
164
+ background: rgba(255,95,0,.12);
165
+ color: #ffd7bf;
166
+ }
167
+ .md{
168
+ margin-top:10px;
169
+ white-space:pre-wrap;
170
+ padding:10px;
171
+ border-radius:12px;
172
+ border:1px solid rgba(255,255,255,.10);
173
+ background: rgba(0,0,0,.18);
174
+ color: var(--text);
175
+ overflow:auto;
176
+ }
177
+ .footer{margin-top:18px}
178
+ @media (max-width: 980px){
179
+ .grid{grid-template-columns:1fr}
180
+ .btn{width:100%}
181
+ .frame-grid{grid-template-columns:1fr}
182
+ .kv{grid-template-columns:1fr}
183
+ }
184
+
templates/index.html ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
6
+ <title>AI Image Detector</title>
7
+ <link rel="stylesheet" href="/static/styles.css" />
8
+ </head>
9
+ <body>
10
+ <div class="container">
11
+ <header class="header">
12
+ <div>
13
+ <h1>AI Image Detector</h1>
14
+ <p class="sub">Detect whether an image looks REAL (human-made) or FAKE (AI-generated). Also supports quick video sampling.</p>
15
+ </div>
16
+ </header>
17
+
18
+ <section class="card">
19
+ <h2>Model</h2>
20
+ <form class="row" method="post" action="/load_model">
21
+ <label class="field">
22
+ <span>Model file</span>
23
+ <select name="model_path">
24
+ {% for m in available_models %}
25
+ <option value="{{ m }}" {% if current_model_path == m %}selected{% endif %}>{{ m }}</option>
26
+ {% endfor %}
27
+ </select>
28
+ </label>
29
+ <button type="submit" class="btn secondary">Load model</button>
30
+ </form>
31
+ <div class="status">
32
+ <div class="label">Status</div>
33
+ <div class="mono">{{ model_status }}</div>
34
+ </div>
35
+ </section>
36
+
37
+ <section class="card">
38
+ <h2>History (last 10)</h2>
39
+ {% if history and history|length > 0 %}
40
+ <div class="history">
41
+ {% for h in history %}
42
+ <div class="history-item">
43
+ <div class="history-top">
44
+ <div class="mono">{{ h.ts }}</div>
45
+ <div class="mono">{{ h.type }}</div>
46
+ </div>
47
+ <div class="history-mid">
48
+ <div class="hint">Model: <span class="mono">{{ h.model }}</span></div>
49
+ <div class="hint">Input: <span class="mono">{{ h.source }}</span></div>
50
+ </div>
51
+ <div class="history-bottom">
52
+ <div class="mono">{{ h.prediction }}{% if h.confidence is not none %} Β· {{ (h.confidence*100) | round(1) }}%{% endif %}</div>
53
+ </div>
54
+ </div>
55
+ {% endfor %}
56
+ </div>
57
+ {% else %}
58
+ <div class="hint">No checks yet.</div>
59
+ {% endif %}
60
+ </section>
61
+
62
+ <section class="grid">
63
+ <div class="card">
64
+ <h2>Upload image</h2>
65
+ <form method="post" action="/analyze_image_upload" enctype="multipart/form-data">
66
+ <label class="field">
67
+ <span>Image file</span>
68
+ <input type="file" name="file" accept="image/*" required />
69
+ </label>
70
+ <button type="submit" class="btn">Analyze image</button>
71
+ </form>
72
+
73
+ {% if image_result %}
74
+ <div class="result">
75
+ {% if image_result.error %}
76
+ <div class="error">Error: {{ image_result.error }}</div>
77
+ {% else %}
78
+ <div class="pill">{{ image_result.quick }}</div>
79
+ <div class="media-row">
80
+ <img class="preview" src="{{ image_result.image_data_uri }}" alt="Uploaded image preview" />
81
+ </div>
82
+ <div class="kv">
83
+ <div><span class="k">Prediction</span><span class="v mono">{{ image_result.prediction }}</span></div>
84
+ <div><span class="k">Confidence</span><span class="v mono">{{ (image_result.confidence*100) | round(1) }}%</span></div>
85
+ <div><span class="k">p_real</span><span class="v mono">{{ (image_result.probabilities[0]*100) | round(1) }}%</span></div>
86
+ <div><span class="k">p_fake</span><span class="v mono">{{ (image_result.probabilities[1]*100) | round(1) }}%</span></div>
87
+ </div>
88
+ {% endif %}
89
+ </div>
90
+ {% endif %}
91
+ </div>
92
+
93
+ <div class="card">
94
+ <h2>Image URL</h2>
95
+ <form method="post" action="/analyze_image_url">
96
+ <label class="field">
97
+ <span>Image URL</span>
98
+ <input type="url" name="url" placeholder="https://example.com/image.jpg" required />
99
+ </label>
100
+ <button type="submit" class="btn">Analyze URL</button>
101
+ </form>
102
+
103
+ {% if url_result %}
104
+ <div class="result">
105
+ {% if url_result.error %}
106
+ <div class="error">Error: {{ url_result.error }}</div>
107
+ {% else %}
108
+ <div class="pill">{{ url_result.quick }}</div>
109
+ <div class="hint">Source: <span class="mono">{{ url_result.url }}</span></div>
110
+ <div class="media-row">
111
+ <img class="preview" src="{{ url_result.image_data_uri }}" alt="URL image preview" />
112
+ </div>
113
+ <div class="kv">
114
+ <div><span class="k">Prediction</span><span class="v mono">{{ url_result.prediction }}</span></div>
115
+ <div><span class="k">Confidence</span><span class="v mono">{{ (url_result.confidence*100) | round(1) }}%</span></div>
116
+ <div><span class="k">p_real</span><span class="v mono">{{ (url_result.probabilities[0]*100) | round(1) }}%</span></div>
117
+ <div><span class="k">p_fake</span><span class="v mono">{{ (url_result.probabilities[1]*100) | round(1) }}%</span></div>
118
+ </div>
119
+ {% endif %}
120
+ </div>
121
+ {% endif %}
122
+ </div>
123
+
124
+ <div class="card">
125
+ <h2>Video analyzer (random frames)</h2>
126
+ <form method="post" action="/analyze_video_url">
127
+ <label class="field">
128
+ <span>Video URL</span>
129
+ <input type="url" name="video_url" placeholder="https://example.com/video.mp4" required />
130
+ </label>
131
+ <label class="field">
132
+ <span>Frames to sample</span>
133
+ <input type="number" name="num_frames" min="1" max="50" value="5" />
134
+ </label>
135
+ <button type="submit" class="btn">Analyze video</button>
136
+ <div class="hint">Downloads the video, samples random frames, and aggregates results into a report.</div>
137
+ </form>
138
+
139
+ {% if video_result %}
140
+ <div class="result">
141
+ {% if video_result.error %}
142
+ <div class="error">Error: {{ video_result.error }}</div>
143
+ {% else %}
144
+ <div class="pill">Overall: {{ video_result.overall }} (avg p_fake {{ (video_result.avg_p_fake*100) | round(1) }}%)</div>
145
+ <div class="hint">Source: <span class="mono">{{ video_result.video_url }}</span></div>
146
+ {% if video_result.is_youtube %}
147
+ <div class="hint">Detected YouTube URL (downloaded via <span class="mono">yt-dlp</span>).</div>
148
+ {% endif %}
149
+ <div class="frame-grid">
150
+ {% for f in video_result.frames %}
151
+ <div class="frame-card">
152
+ <img class="frame" src="{{ f.image_data_uri }}" alt="Frame {{ f.frame_index }}" />
153
+ <div class="frame-meta">
154
+ <div class="mono">frame {{ f.frame_index }}</div>
155
+ <div class="mono">{{ f.prediction }} Β· conf {{ (f.confidence*100) | round(1) }}%</div>
156
+ <div class="mono">p_fake {{ (f.p_fake*100) | round(1) }}%</div>
157
+ </div>
158
+ </div>
159
+ {% endfor %}
160
+ </div>
161
+ {% endif %}
162
+ </div>
163
+ {% endif %}
164
+ </div>
165
+ </section>
166
+
167
+ <footer class="footer">
168
+ <div class="hint">
169
+ Tip: on Hugging Face Spaces, run with <span class="mono">uvicorn ai_vs_real.app:app --host 0.0.0.0 --port 7860</span>.
170
+ </div>
171
+ </footer>
172
+ </div>
173
+ </body>
174
+ </html>
175
+
test.ipynb ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "id": "ab0628fc",
7
+ "metadata": {},
8
+ "outputs": [
9
+ {
10
+ "name": "stdout",
11
+ "output_type": "stream",
12
+ "text": [
13
+ "πŸ” Exploring RegNet Y 16GF structure...\n",
14
+ "πŸ“Š Total parameters: 428\n",
15
+ "\n",
16
+ "πŸ—οΈ RegNet Structure Analysis:\n",
17
+ "\n",
18
+ "πŸ“ stem.0: 1 parameters\n",
19
+ " β€’ stem.0.weight\n",
20
+ "\n",
21
+ "πŸ“ stem.1: 5 parameters\n",
22
+ " β€’ stem.1.weight\n",
23
+ " β€’ stem.1.bias\n",
24
+ " β€’ stem.1.running_mean\n",
25
+ " β€’ stem.1.running_var\n",
26
+ " β€’ stem.1.num_batches_tracked\n",
27
+ "\n",
28
+ "πŸ“ trunk_output.block1: 50 parameters\n",
29
+ " β€’ trunk_output.block1.block1-0.proj.0.weight\n",
30
+ " β€’ trunk_output.block1.block1-0.proj.1.weight\n",
31
+ " β€’ trunk_output.block1.block1-0.proj.1.bias\n",
32
+ " β€’ trunk_output.block1.block1-0.proj.1.running_mean\n",
33
+ " β€’ trunk_output.block1.block1-0.proj.1.running_var\n",
34
+ " ... and 45 more\n",
35
+ "\n",
36
+ "πŸ“ trunk_output.block2: 94 parameters\n",
37
+ " β€’ trunk_output.block2.block2-0.proj.0.weight\n",
38
+ " β€’ trunk_output.block2.block2-0.proj.1.weight\n",
39
+ " β€’ trunk_output.block2.block2-0.proj.1.bias\n",
40
+ " β€’ trunk_output.block2.block2-0.proj.1.running_mean\n",
41
+ " β€’ trunk_output.block2.block2-0.proj.1.running_var\n",
42
+ " ... and 89 more\n",
43
+ "\n",
44
+ "πŸ“ trunk_output.block3: 248 parameters\n",
45
+ " β€’ trunk_output.block3.block3-0.proj.0.weight\n",
46
+ " β€’ trunk_output.block3.block3-0.proj.1.weight\n",
47
+ " β€’ trunk_output.block3.block3-0.proj.1.bias\n",
48
+ " β€’ trunk_output.block3.block3-0.proj.1.running_mean\n",
49
+ " β€’ trunk_output.block3.block3-0.proj.1.running_var\n",
50
+ " ... and 243 more\n",
51
+ "\n",
52
+ "πŸ“ trunk_output.block4: 28 parameters\n",
53
+ " β€’ trunk_output.block4.block4-0.proj.0.weight\n",
54
+ " β€’ trunk_output.block4.block4-0.proj.1.weight\n",
55
+ " β€’ trunk_output.block4.block4-0.proj.1.bias\n",
56
+ " β€’ trunk_output.block4.block4-0.proj.1.running_mean\n",
57
+ " β€’ trunk_output.block4.block4-0.proj.1.running_var\n",
58
+ " ... and 23 more\n",
59
+ "\n",
60
+ "πŸ“ fc.weight: 1 parameters\n",
61
+ " β€’ fc.weight\n",
62
+ "\n",
63
+ "πŸ“ fc.bias: 1 parameters\n",
64
+ " β€’ fc.bias\n",
65
+ "\n",
66
+ "============================================================\n",
67
+ "πŸ” Searching for conv layers specifically:\n",
68
+ "Found 0 conv parameters:\n",
69
+ "\n",
70
+ "============================================================\n",
71
+ "πŸ” Looking for block structure:\n",
72
+ "Found 420 block parameters:\n",
73
+ " β€’ trunk_output.block1.block1-0.proj.0.weight\n",
74
+ " β€’ trunk_output.block1.block1-0.proj.1.weight\n",
75
+ " β€’ trunk_output.block1.block1-0.proj.1.bias\n",
76
+ " β€’ trunk_output.block1.block1-0.proj.1.running_mean\n",
77
+ " β€’ trunk_output.block1.block1-0.proj.1.running_var\n",
78
+ " β€’ trunk_output.block1.block1-0.proj.1.num_batches_tracked\n",
79
+ " β€’ trunk_output.block1.block1-0.f.a.0.weight\n",
80
+ " β€’ trunk_output.block1.block1-0.f.a.1.weight\n",
81
+ " β€’ trunk_output.block1.block1-0.f.a.1.bias\n",
82
+ " β€’ trunk_output.block1.block1-0.f.a.1.running_mean\n",
83
+ " ... and 410 more\n",
84
+ "\n",
85
+ "============================================================\n",
86
+ "πŸ’‘ ANALYSIS COMPLETE\n",
87
+ "Use the output above to understand the correct parameter structure\n",
88
+ "and update the validation logic accordingly.\n"
89
+ ]
90
+ }
91
+ ],
92
+ "source": [
93
+ "import torch\n",
94
+ "from torchvision import models\n",
95
+ "\n",
96
+ "def explore_regnet_structure():\n",
97
+ " \"\"\"Explore the actual RegNet structure to understand parameter names\"\"\"\n",
98
+ " print(\"πŸ” Exploring RegNet Y 16GF structure...\")\n",
99
+ " \n",
100
+ " # Load RegNet with pretrained weights\n",
101
+ " model = models.regnet_y_16gf(weights=models.RegNet_Y_16GF_Weights.IMAGENET1K_SWAG_E2E_V1)\n",
102
+ " \n",
103
+ " # Get all parameter names\n",
104
+ " param_names = list(model.state_dict().keys())\n",
105
+ " \n",
106
+ " print(f\"πŸ“Š Total parameters: {len(param_names)}\")\n",
107
+ " print(\"\\nπŸ—οΈ RegNet Structure Analysis:\")\n",
108
+ " \n",
109
+ " # Group parameters by component\n",
110
+ " components = {}\n",
111
+ " for name in param_names:\n",
112
+ " parts = name.split('.')\n",
113
+ " if len(parts) >= 2:\n",
114
+ " component = f\"{parts[0]}.{parts[1]}\"\n",
115
+ " if component not in components:\n",
116
+ " components[component] = []\n",
117
+ " components[component].append(name)\n",
118
+ " \n",
119
+ " # Display structure\n",
120
+ " for component, params in components.items():\n",
121
+ " print(f\"\\nπŸ“ {component}: {len(params)} parameters\")\n",
122
+ " # Show first few parameter names as examples\n",
123
+ " for i, param in enumerate(params[:5]):\n",
124
+ " print(f\" β€’ {param}\")\n",
125
+ " if len(params) > 5:\n",
126
+ " print(f\" ... and {len(params) - 5} more\")\n",
127
+ " \n",
128
+ " print(\"\\n\" + \"=\"*60)\n",
129
+ " print(\"πŸ” Searching for conv layers specifically:\")\n",
130
+ " conv_params = [name for name in param_names if 'conv' in name.lower()]\n",
131
+ " print(f\"Found {len(conv_params)} conv parameters:\")\n",
132
+ " for param in conv_params[:10]: # Show first 10\n",
133
+ " print(f\" β€’ {param}\")\n",
134
+ " if len(conv_params) > 10:\n",
135
+ " print(f\" ... and {len(conv_params) - 10} more\")\n",
136
+ " \n",
137
+ " print(\"\\n\" + \"=\"*60)\n",
138
+ " print(\"πŸ” Looking for block structure:\")\n",
139
+ " block_params = [name for name in param_names if 'block' in name.lower()]\n",
140
+ " print(f\"Found {len(block_params)} block parameters:\")\n",
141
+ " for param in block_params[:10]:\n",
142
+ " print(f\" β€’ {param}\")\n",
143
+ " if len(block_params) > 10:\n",
144
+ " print(f\" ... and {len(block_params) - 10} more\")\n",
145
+ " \n",
146
+ " return param_names, model\n",
147
+ "\n",
148
+ "if __name__ == \"__main__\":\n",
149
+ " try:\n",
150
+ " param_names, model = explore_regnet_structure()\n",
151
+ " \n",
152
+ " print(\"\\n\" + \"=\"*60)\n",
153
+ " print(\"πŸ’‘ ANALYSIS COMPLETE\")\n",
154
+ " print(\"Use the output above to understand the correct parameter structure\")\n",
155
+ " print(\"and update the validation logic accordingly.\")\n",
156
+ " \n",
157
+ " except Exception as e:\n",
158
+ " print(f\"❌ Error exploring RegNet: {e}\")\n",
159
+ " print(\"Make sure you have internet access to download RegNet weights\")"
160
+ ]
161
+ },
162
+ {
163
+ "cell_type": "code",
164
+ "execution_count": null,
165
+ "id": "d6a4fd46",
166
+ "metadata": {},
167
+ "outputs": [],
168
+ "source": []
169
+ }
170
+ ],
171
+ "metadata": {
172
+ "kernelspec": {
173
+ "display_name": ".venv",
174
+ "language": "python",
175
+ "name": "python3"
176
+ },
177
+ "language_info": {
178
+ "codemirror_mode": {
179
+ "name": "ipython",
180
+ "version": 3
181
+ },
182
+ "file_extension": ".py",
183
+ "mimetype": "text/x-python",
184
+ "name": "python",
185
+ "nbconvert_exporter": "python",
186
+ "pygments_lexer": "ipython3",
187
+ "version": "3.11.0"
188
+ }
189
+ },
190
+ "nbformat": 4,
191
+ "nbformat_minor": 5
192
+ }