làm mịn các điểm ảnh

This commit is contained in:
Victor Phan
2026-01-06 12:25:42 +07:00
parent ae3e5fffdd
commit d6ba6d8db0
4 changed files with 975 additions and 202 deletions
+209 -7
View File
@@ -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,