178 lines
5.9 KiB
Python
178 lines
5.9 KiB
Python
import os
|
|
import glob
|
|
import time
|
|
import json
|
|
import itertools
|
|
import numpy as np
|
|
import joblib
|
|
import torch
|
|
import torch.nn as nn
|
|
import torch.optim as optim
|
|
from torch.utils.data import TensorDataset, DataLoader
|
|
from sklearn.model_selection import train_test_split
|
|
from sklearn.metrics import classification_report, accuracy_score
|
|
|
|
from train_module import SwinUNetClassifier
|
|
|
|
def load_data():
|
|
cache_files = glob.glob('dataset_cache/training_data_*.joblib')
|
|
if not cache_files:
|
|
raise FileNotFoundError("No cache files found in dataset_cache/")
|
|
|
|
# Get the latest cache file
|
|
cache_file = max(cache_files, key=os.path.getctime)
|
|
print(f"Loading data from {cache_file}...")
|
|
data = joblib.load(cache_file)
|
|
features = data['features']
|
|
labels = data['labels']
|
|
|
|
# Map labels to 0..N-1
|
|
unique_labels = sorted(list(np.unique(labels)))
|
|
label_map = {lbl: idx for idx, lbl in enumerate(unique_labels)}
|
|
mapped_labels = np.array([label_map[l] for l in labels])
|
|
|
|
return features, mapped_labels, unique_labels
|
|
|
|
def train_evaluate(features, labels, embed_dim, lr, weight_decay, epochs, patience, device):
|
|
X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.2, random_state=42)
|
|
|
|
n_features = X_train.shape[1]
|
|
n_classes = len(np.unique(labels))
|
|
|
|
model = SwinUNetClassifier(n_features, n_classes, embed_dim=embed_dim).to(device)
|
|
|
|
X_train_t = torch.FloatTensor(X_train)
|
|
y_train_t = torch.LongTensor(y_train)
|
|
X_test_t = torch.FloatTensor(X_test)
|
|
y_test_t = torch.LongTensor(y_test)
|
|
|
|
train_dataset = TensorDataset(X_train_t, y_train_t)
|
|
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
|
|
|
|
# Class weights
|
|
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_t = torch.FloatTensor(class_weights).to(device)
|
|
|
|
criterion = nn.CrossEntropyLoss(weight=class_weights_t)
|
|
optimizer = optim.AdamW(model.parameters(), lr=lr, weight_decay=weight_decay)
|
|
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=50)
|
|
|
|
best_acc = 0.0
|
|
patience_counter = 0
|
|
best_model_state = None
|
|
|
|
model.train()
|
|
for epoch in range(epochs):
|
|
model.train()
|
|
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()
|
|
optimizer.step()
|
|
|
|
scheduler.step()
|
|
|
|
# Eval
|
|
model.eval()
|
|
with torch.no_grad():
|
|
outputs = model(X_test_t.to(device))
|
|
_, preds = torch.max(outputs, 1)
|
|
acc = accuracy_score(y_test, preds.cpu().numpy())
|
|
|
|
if acc > best_acc:
|
|
best_acc = acc
|
|
best_model_state = model.state_dict()
|
|
patience_counter = 0
|
|
else:
|
|
patience_counter += 1
|
|
|
|
if patience_counter >= patience:
|
|
break
|
|
|
|
# Restore best
|
|
if best_model_state:
|
|
model.load_state_dict(best_model_state)
|
|
|
|
return model, best_acc, X_test_t, y_test
|
|
|
|
def main():
|
|
print("🚀 BẮT ĐẦU TÌM KIẾM SIÊU THAM SỐ CHO SWIN-UNET")
|
|
features, labels, unique_labels = load_data()
|
|
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
|
print(f"Using device: {device}")
|
|
|
|
param_grid = {
|
|
'embed_dim': [64, 128, 256, 512],
|
|
'lr': [1e-3, 5e-4, 1e-4],
|
|
'weight_decay': [0.01, 0.001],
|
|
'epochs': [200, 500]
|
|
}
|
|
|
|
keys = param_grid.keys()
|
|
combinations = list(itertools.product(*(param_grid[k] for k in keys)))
|
|
|
|
best_global_acc = 0.0
|
|
best_params = None
|
|
best_model = None
|
|
|
|
# Ensure dir exists
|
|
os.makedirs('land_classification_model', exist_ok=True)
|
|
os.makedirs('model_train', exist_ok=True)
|
|
|
|
for i, values in enumerate(combinations):
|
|
params = dict(zip(keys, values))
|
|
print(f"\n[{i+1}/{len(combinations)}] Training with params: {params}")
|
|
|
|
model, acc, X_test_t, y_test = train_evaluate(
|
|
features, labels,
|
|
embed_dim=params['embed_dim'],
|
|
lr=params['lr'],
|
|
weight_decay=params['weight_decay'],
|
|
epochs=params['epochs'],
|
|
patience=30,
|
|
device=device
|
|
)
|
|
|
|
print(f"Test Accuracy: {acc:.4f}")
|
|
|
|
if acc > best_global_acc:
|
|
best_global_acc = acc
|
|
best_params = params
|
|
best_model = model
|
|
|
|
print(f"🌟 NEW BEST ACCURACY: {acc:.4f}")
|
|
|
|
if acc >= 0.95:
|
|
print("🎯 ĐẠT MỤC TIÊU >95%! DỪNG TÌM KIẾM.")
|
|
break
|
|
|
|
if best_model is not None:
|
|
model_path = 'land_classification_model/model_swin-unet_optimized_95.joblib'
|
|
best_model = best_model.cpu()
|
|
joblib.dump(best_model, model_path)
|
|
print(f"\n✅ Đã lưu mô hình tốt nhất (Acc: {best_global_acc:.4f}) vào {model_path}")
|
|
print(f"Cấu hình tốt nhất: {best_params}")
|
|
|
|
# generate report
|
|
best_model.eval()
|
|
with torch.no_grad():
|
|
outputs = best_model(X_test_t)
|
|
_, preds = torch.max(outputs, 1)
|
|
clf_rep = classification_report(y_test, preds.cpu().numpy(), output_dict=True)
|
|
|
|
info = {
|
|
"model_type": "swin-unet",
|
|
"test_accuracy": float(best_global_acc),
|
|
"params": {"n_estimators": best_params['epochs'], "max_depth": best_params['embed_dim']},
|
|
"classification_report": clf_rep
|
|
}
|
|
with open('model_train/model_swin-unet_auto_info.json', 'w') as f:
|
|
json.dump(info, f, indent=2)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|