change to NAS

This commit is contained in:
Victor Phan
2026-02-15 19:47:58 +07:00
parent 823fe03b01
commit aad6ad1e5e
5 changed files with 1315 additions and 257 deletions
+205
View File
@@ -764,6 +764,211 @@ async def upload_cloud_removal_model(
raise HTTPException(status_code=500, detail=f"Upload failed: {str(e)}")
@app.post("/api/land-classification/upload")
async def upload_land_classification_model(
file: UploadFile = File(...),
model_type: str = Form("mobilenet"), # mobilenet, cnn, swin, xgboost, etc.
epoch: int = Form(0),
train_accuracy: float = Form(0.0),
val_accuracy: float = Form(0.0),
train_loss: float = Form(0.0),
val_loss: float = Form(0.0),
num_classes: int = Form(10),
input_size: int = Form(64),
description: str = Form("")
):
"""Upload land classification model with metadata"""
# Debug logging
print(f"[Land Upload] Received parameters:")
print(f" File: {file.filename}")
print(f" Model Type: {model_type}")
print(f" Epoch: {epoch}")
print(f" Train Accuracy: {train_accuracy}")
print(f" Val Accuracy: {val_accuracy}")
print(f" Train Loss: {train_loss}")
print(f" Val Loss: {val_loss}")
print(f" Num Classes: {num_classes}")
print(f" Input Size: {input_size}")
print(f" Description: {description}")
# Validate file extension
valid_extensions = ['.pth', '.pkl', '.joblib', '.h5', '.keras']
if not any(file.filename.endswith(ext) for ext in valid_extensions):
raise HTTPException(
status_code=400,
detail=f"Only {', '.join(valid_extensions)} files are allowed"
)
# Security check
if ".." in file.filename or "/" in file.filename or "\\" in file.filename:
raise HTTPException(status_code=400, detail="Invalid filename")
try:
model_dir = Path("land_classification_model")
model_dir.mkdir(exist_ok=True)
# Save uploaded file
file_path = model_dir / file.filename
# Check if file already exists
if file_path.exists():
raise HTTPException(
status_code=400,
detail=f"Model {file.filename} already exists"
)
# Write file
with open(file_path, "wb") as f:
content = await file.read()
f.write(content)
file_size = file_path.stat().st_size
# Save metadata as JSON sidecar file
metadata_file = file_path.with_suffix(file_path.suffix + '.json')
metadata_dict = {
"filename": file.filename,
"model_type": model_type,
"epoch": epoch,
"train_accuracy": train_accuracy,
"val_accuracy": val_accuracy,
"train_loss": train_loss,
"val_loss": val_loss,
"num_classes": num_classes,
"input_size": input_size,
"description": description,
"uploaded_at": datetime.now().isoformat()
}
with open(metadata_file, 'w') as f:
json.dump(metadata_dict, f, indent=2)
print(f"[Land Upload] Saved model: {file_path}")
print(f"[Land Upload] Saved metadata: {metadata_file}")
return {
"message": f"Successfully uploaded {file.filename}",
"filename": file.filename,
"size_mb": round(file_size / 1024 / 1024, 2),
"path": str(file_path),
"metadata": metadata_dict
}
except HTTPException:
raise
except Exception as e:
print(f"[Land Upload] Error: {e}")
import traceback
traceback.print_exc()
raise HTTPException(status_code=500, detail=f"Upload failed: {str(e)}")
@app.get("/api/land-classification/models")
async def list_land_classification_models():
"""List all uploaded land classification models"""
model_dir = Path("land_classification_model")
if not model_dir.exists():
return {"models": [], "count": 0}
models = []
# Search for model files
for model_file in model_dir.rglob("*"):
# Skip JSON metadata files
if model_file.suffix == '.json':
continue
# Only include model files
valid_extensions = ['.pth', '.pkl', '.joblib', '.h5', '.keras']
if not any(model_file.name.endswith(ext) for ext in valid_extensions):
continue
try:
# Try to load metadata from JSON sidecar file
metadata_file = Path(str(model_file) + '.json')
if metadata_file.exists():
with open(metadata_file, 'r') as f:
metadata = json.load(f)
models.append({
"filename": model_file.name,
"path": str(model_file),
"relative_path": str(model_file.relative_to(model_dir)),
"model_type": metadata.get('model_type', 'unknown'),
"epoch": metadata.get('epoch', 0),
"train_accuracy": metadata.get('train_accuracy', 0),
"val_accuracy": metadata.get('val_accuracy', 0),
"train_loss": metadata.get('train_loss', 0),
"val_loss": metadata.get('val_loss', 0),
"num_classes": metadata.get('num_classes', 10),
"input_size": metadata.get('input_size', 64),
"description": metadata.get('description', ''),
"created": model_file.stat().st_mtime,
"size_mb": model_file.stat().st_size / (1024 * 1024),
"has_metadata": True
})
else:
# No metadata file, use defaults
models.append({
"filename": model_file.name,
"path": str(model_file),
"relative_path": str(model_file.relative_to(model_dir)),
"model_type": "unknown",
"epoch": 0,
"train_accuracy": 0,
"val_accuracy": 0,
"train_loss": 0,
"val_loss": 0,
"num_classes": 10,
"input_size": 64,
"description": "",
"created": model_file.stat().st_mtime,
"size_mb": model_file.stat().st_size / (1024 * 1024),
"has_metadata": False
})
except Exception as e:
print(f"[Land Models] Error loading {model_file}: {e}")
continue
# Sort by creation time (newest first)
models.sort(key=lambda x: x['created'], reverse=True)
return {
"models": models,
"count": len(models)
}
@app.delete("/api/land-classification/models/{filename}")
async def delete_land_classification_model(filename: str):
"""Delete a land classification model"""
model_dir = Path("land_classification_model")
model_path = model_dir / filename
# Security check
if ".." in filename or "/" in filename or "\\" in filename:
raise HTTPException(status_code=400, detail="Invalid filename")
if not model_path.exists():
raise HTTPException(status_code=404, detail=f"Model not found: {filename}")
try:
# Delete model file
model_path.unlink()
# Delete metadata file if exists
metadata_file = Path(str(model_path) + '.json')
if metadata_file.exists():
metadata_file.unlink()
return {
"success": True,
"message": f"Deleted land classification model: {filename}"
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to delete: {str(e)}")
@app.delete("/api/cloud-removal/models/{filename}")
async def delete_cloud_removal_model(filename: str):
"""Xóa cloud removal model"""