làm mịn các điểm ảnh
This commit is contained in:
+209
-7
@@ -254,6 +254,96 @@ class SwinUNetClassifier(nn.Module):
|
||||
return np.mean(predictions == y)
|
||||
|
||||
|
||||
# MobileNetV3 + LR-ASPP Classifier
|
||||
class MobileNetLRASPPClassifier(nn.Module):
|
||||
"""
|
||||
MobileNetV3 backbone with LR-ASPP (Lite Reduced Atrous Spatial Pyramid Pooling) for semantic segmentation
|
||||
Lightweight architecture optimized for efficiency and speed
|
||||
"""
|
||||
def __init__(self, n_features, n_classes):
|
||||
super(MobileNetLRASPPClassifier, self).__init__()
|
||||
self.n_features = n_features
|
||||
self.n_classes = n_classes
|
||||
|
||||
# Feature extraction layers (MobileNetV3-inspired)
|
||||
self.feature_extractor = nn.Sequential(
|
||||
nn.Linear(n_features, 128),
|
||||
nn.BatchNorm1d(128),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.Dropout(0.2),
|
||||
|
||||
nn.Linear(128, 256),
|
||||
nn.BatchNorm1d(256),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.Dropout(0.3),
|
||||
|
||||
nn.Linear(256, 512),
|
||||
nn.BatchNorm1d(512),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.Dropout(0.3),
|
||||
)
|
||||
|
||||
# LR-ASPP head (simplified for feature vectors)
|
||||
# Branch 1: Global average pooling
|
||||
self.global_pool = nn.AdaptiveAvgPool1d(1)
|
||||
self.global_conv = nn.Sequential(
|
||||
nn.Linear(512, 128),
|
||||
nn.ReLU(inplace=True)
|
||||
)
|
||||
|
||||
# Branch 2: 1x1 convolution equivalent
|
||||
self.branch_conv = nn.Sequential(
|
||||
nn.Linear(512, 128),
|
||||
nn.BatchNorm1d(128),
|
||||
nn.ReLU(inplace=True)
|
||||
)
|
||||
|
||||
# Fusion and classification
|
||||
self.classifier = nn.Sequential(
|
||||
nn.Linear(256, 128), # 128 from global + 128 from branch
|
||||
nn.BatchNorm1d(128),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.Dropout(0.4),
|
||||
nn.Linear(128, n_classes)
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
# x shape: (batch, n_features)
|
||||
features = self.feature_extractor(x)
|
||||
|
||||
# LR-ASPP head
|
||||
# Branch 1: Global pooling
|
||||
global_feat = self.global_pool(features.unsqueeze(-1)).squeeze(-1)
|
||||
global_feat = self.global_conv(global_feat)
|
||||
|
||||
# Branch 2: Direct features
|
||||
branch_feat = self.branch_conv(features)
|
||||
|
||||
# Concatenate branches
|
||||
fused = torch.cat([global_feat, branch_feat], dim=1)
|
||||
|
||||
# Classification
|
||||
output = self.classifier(fused)
|
||||
return output
|
||||
|
||||
def predict(self, X):
|
||||
"""Scikit-learn style predict"""
|
||||
self.eval()
|
||||
with torch.no_grad():
|
||||
if isinstance(X, np.ndarray):
|
||||
X = torch.FloatTensor(X)
|
||||
outputs = self(X)
|
||||
_, predicted = torch.max(outputs, 1)
|
||||
return predicted.cpu().numpy()
|
||||
|
||||
def score(self, X, y):
|
||||
"""Scikit-learn style score"""
|
||||
predictions = self.predict(X)
|
||||
if isinstance(y, torch.Tensor):
|
||||
y = y.cpu().numpy()
|
||||
return np.mean(predictions == y)
|
||||
|
||||
|
||||
# Microsoft Planetary Computer imports
|
||||
import planetary_computer
|
||||
from pystac_client import Client
|
||||
@@ -1055,16 +1145,128 @@ def train_model(
|
||||
model = model.cpu()
|
||||
model.device_used = str(device)
|
||||
|
||||
elif model_type == 'mobilenet-lraspp':
|
||||
if not PYTORCH_AVAILABLE:
|
||||
raise ImportError("PyTorch is required for MobileNetV3 + LR-ASPP. Install: pip install torch torchvision")
|
||||
|
||||
n_features = X_train.shape[1]
|
||||
n_classes = len(np.unique(y_train))
|
||||
|
||||
device = torch.device('cuda' if torch.cuda.is_available() and use_gpu else 'cpu')
|
||||
update_status(f"Building MobileNetV3 + LR-ASPP model on {device}...", 75)
|
||||
|
||||
model = MobileNetLRASPPClassifier(n_features, n_classes).to(device)
|
||||
|
||||
# Convert to PyTorch tensors
|
||||
X_train_tensor = torch.FloatTensor(X_train)
|
||||
y_train_tensor = torch.LongTensor(y_train)
|
||||
X_test_tensor = torch.FloatTensor(X_test)
|
||||
y_test_tensor = torch.LongTensor(y_test)
|
||||
|
||||
# Create data loaders
|
||||
train_dataset = TensorDataset(X_train_tensor, y_train_tensor)
|
||||
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True) # Larger batch for efficiency
|
||||
|
||||
# Calculate class weights for imbalanced data
|
||||
class_counts = np.bincount(y_train)
|
||||
class_weights = 1.0 / (class_counts + 1e-6)
|
||||
class_weights = class_weights / class_weights.sum() * len(class_counts)
|
||||
class_weights_tensor = torch.FloatTensor(class_weights).to(device)
|
||||
|
||||
print(f"[MOBILENET] Class distribution: {class_counts}")
|
||||
print(f"[MOBILENET] Class weights: {class_weights}")
|
||||
|
||||
# Loss with class weights
|
||||
criterion = nn.CrossEntropyLoss(weight=class_weights_tensor)
|
||||
optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=0.0001)
|
||||
|
||||
# LR scheduler
|
||||
scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.5, patience=5)
|
||||
|
||||
# Early stopping
|
||||
best_val_loss = float('inf')
|
||||
patience = 10
|
||||
patience_counter = 0
|
||||
|
||||
# Train MobileNetV3 + LR-ASPP
|
||||
update_status("Training MobileNetV3 + LR-ASPP model with PyTorch...", 80)
|
||||
epochs = min(60, n_estimators // 2)
|
||||
|
||||
# Validation dataset
|
||||
val_dataset = TensorDataset(X_test_tensor, y_test_tensor)
|
||||
val_loader = DataLoader(val_dataset, batch_size=64, shuffle=False)
|
||||
|
||||
model.train()
|
||||
for epoch in range(epochs):
|
||||
# Training phase
|
||||
model.train()
|
||||
epoch_loss = 0.0
|
||||
for batch_X, batch_y in train_loader:
|
||||
batch_X, batch_y = batch_X.to(device), batch_y.to(device)
|
||||
|
||||
optimizer.zero_grad()
|
||||
outputs = model(batch_X)
|
||||
loss = criterion(outputs, batch_y)
|
||||
loss.backward()
|
||||
|
||||
# Gradient clipping
|
||||
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
|
||||
|
||||
optimizer.step()
|
||||
epoch_loss += loss.item()
|
||||
|
||||
# Validation phase
|
||||
model.eval()
|
||||
val_loss = 0.0
|
||||
correct = 0
|
||||
total = 0
|
||||
with torch.no_grad():
|
||||
for batch_X, batch_y in val_loader:
|
||||
batch_X, batch_y = batch_X.to(device), batch_y.to(device)
|
||||
outputs = model(batch_X)
|
||||
loss = criterion(outputs, batch_y)
|
||||
val_loss += loss.item()
|
||||
|
||||
_, predicted = torch.max(outputs, 1)
|
||||
total += batch_y.size(0)
|
||||
correct += (predicted == batch_y).sum().item()
|
||||
|
||||
avg_train_loss = epoch_loss / len(train_loader)
|
||||
avg_val_loss = val_loss / len(val_loader)
|
||||
val_acc = 100 * correct / total
|
||||
|
||||
# Update learning rate
|
||||
scheduler.step(avg_val_loss)
|
||||
lr = optimizer.param_groups[0]['lr']
|
||||
|
||||
if (epoch + 1) % 5 == 0:
|
||||
update_status(f"MobileNet Epoch {epoch+1}/{epochs}, Train Loss: {avg_train_loss:.4f}, Val Loss: {avg_val_loss:.4f}, Val Acc: {val_acc:.2f}%, LR: {lr:.6f}", 80 + (epoch / epochs) * 10)
|
||||
print(f"[MOBILENET] Epoch {epoch+1}/{epochs} - Train Loss: {avg_train_loss:.4f}, Val Loss: {avg_val_loss:.4f}, Val Acc: {val_acc:.2f}%")
|
||||
|
||||
# Early stopping
|
||||
if avg_val_loss < best_val_loss:
|
||||
best_val_loss = avg_val_loss
|
||||
patience_counter = 0
|
||||
else:
|
||||
patience_counter += 1
|
||||
if patience_counter >= patience:
|
||||
print(f"[MOBILENET] Early stopping at epoch {epoch+1} (best val loss: {best_val_loss:.4f})")
|
||||
update_status(f"MobileNet early stopped at epoch {epoch+1}", 90)
|
||||
break
|
||||
|
||||
model = model.cpu()
|
||||
model.device_used = str(device)
|
||||
|
||||
else:
|
||||
raise ValueError(f"Unknown model type: {model_type}. Choose: xgboost, random_forest, decision_tree, svm, cnn, swin-unet")
|
||||
raise ValueError(f"Unknown model type: {model_type}. Choose: xgboost, random_forest, decision_tree, svm, cnn, swin-unet, mobilenet-lraspp")
|
||||
|
||||
# Fit non-neural-network models
|
||||
if model_type not in ['cnn', 'swin-unet']:
|
||||
if model_type not in ['cnn', 'swin-unet', 'mobilenet-lraspp']:
|
||||
model.fit(X_train, y_train)
|
||||
|
||||
# Evaluate
|
||||
update_status("Evaluating model...", 90)
|
||||
if model_type in ['cnn', 'swin-unet']:
|
||||
if model_type in ['cnn', 'swin-unet', 'mobilenet-lraspp']:
|
||||
# PyTorch models evaluation
|
||||
train_score = model.score(X_train, y_train)
|
||||
test_score = model.score(X_test, y_test)
|
||||
@@ -1111,10 +1313,10 @@ def train_model(
|
||||
"test_accuracy": float(test_score),
|
||||
"model_type": model_type,
|
||||
"device": device if model_type == 'xgboost' else 'cpu',
|
||||
"n_estimators": n_estimators if model_type in ['xgboost', 'random_forest', 'cnn', 'swin-unet'] else None,
|
||||
"max_depth": max_depth if model_type not in ['cnn', 'swin-unet'] else None,
|
||||
"learning_rate": learning_rate if model_type in ['xgboost', 'swin-unet'] else None,
|
||||
"epochs": min(50, n_estimators // 2) if model_type == 'cnn' else (min(60, n_estimators // 2) if model_type == 'swin-unet' else None),
|
||||
"n_estimators": n_estimators if model_type in ['xgboost', 'random_forest', 'cnn', 'swin-unet', 'mobilenet-lraspp'] else None,
|
||||
"max_depth": max_depth if model_type not in ['cnn', 'swin-unet', 'mobilenet-lraspp'] else None,
|
||||
"learning_rate": learning_rate if model_type in ['xgboost', 'swin-unet', 'mobilenet-lraspp'] else None,
|
||||
"epochs": min(50, n_estimators // 2) if model_type == 'cnn' else (min(60, n_estimators // 2) if model_type in ['swin-unet', 'mobilenet-lraspp'] else None),
|
||||
"n_features": X_train.shape[1],
|
||||
"n_classes": len(np.unique(y_train)),
|
||||
"class_names": class_names,
|
||||
|
||||
Reference in New Issue
Block a user