feat: implement comprehensive land cover classification pipeline with model benchmarking and experiment logging
This commit is contained in:
+26
-43
@@ -510,6 +510,7 @@ def train_model(
|
||||
bbox=bbox,
|
||||
patch_url=planetary_computer.sign,
|
||||
fail_on_error=False,
|
||||
chunks={"time": 1, "x": 2048, "y": 2048}
|
||||
)
|
||||
|
||||
# Debug: Print S2 data info
|
||||
@@ -556,6 +557,7 @@ def train_model(
|
||||
bbox=bbox,
|
||||
patch_url=planetary_computer.sign,
|
||||
fail_on_error=False,
|
||||
chunks={"time": 1, "x": 2048, "y": 2048}
|
||||
)
|
||||
|
||||
# Convert to dB
|
||||
@@ -734,8 +736,8 @@ def train_model(
|
||||
labels = np.array(labels)
|
||||
|
||||
elif feature_mode in ['odc', 'extended']:
|
||||
# For odc/extended: Extract features for full raster first, then sample at points
|
||||
update_status(f"Extracting {feature_mode} features from full raster...", 62)
|
||||
# For odc/extended: Extract points FIRST, then compute features to save RAM
|
||||
update_status(f"Extracting points from {feature_mode} raster before computing features...", 62)
|
||||
|
||||
# Apply cloud mask first
|
||||
if 'SCL' in ds_s2:
|
||||
@@ -745,68 +747,49 @@ def train_model(
|
||||
if band != 'SCL':
|
||||
ds_s2[band] = ds_s2[band].where(~cloud_mask)
|
||||
|
||||
# Extract features using FeatureExtractor for entire raster
|
||||
# Use advanced indexing to extract exactly the 1130 points
|
||||
x_coords = xr.DataArray(train_gdf.geometry.x.values, dims="point")
|
||||
y_coords = xr.DataArray(train_gdf.geometry.y.values, dims="point")
|
||||
|
||||
update_status("Downloading and extracting point data from Dask array (this is fast)...", 65)
|
||||
points_s2 = ds_s2.sel(x=x_coords, y=y_coords, method='nearest').compute()
|
||||
|
||||
update_status("Computing spectral indices for extracted points...", 66)
|
||||
# Extract features using FeatureExtractor for ONLY the extracted points
|
||||
raster_features = extractor.extract(
|
||||
s2_data=ds_s2,
|
||||
s2_data=points_s2,
|
||||
vh_data=None, # ODC/extended don't use radar in aggregate
|
||||
vv_data=None
|
||||
)
|
||||
|
||||
print(f"[DEBUG] Extracted raster features: shape={raster_features.shape}")
|
||||
print(f"[DEBUG] Feature range: [{raster_features.min()}, {raster_features.max()}]")
|
||||
print(f"[DEBUG] Extracted point features: shape={raster_features.shape}")
|
||||
print(f"[DEBUG] Feature range: [{np.nanmin(raster_features)}, {np.nanmax(raster_features)}]")
|
||||
|
||||
# Now sample at each training point
|
||||
features = []
|
||||
labels = []
|
||||
failed_extractions = 0
|
||||
|
||||
# Get spatial dimensions
|
||||
y_coords = ds_s2.y.values
|
||||
x_coords = ds_s2.x.values
|
||||
|
||||
print(f"[DEBUG] S2 spatial grid: x=[{x_coords.min()}, {x_coords.max()}], y=[{y_coords.min()}, {y_coords.max()}]")
|
||||
|
||||
for idx, row in train_gdf.iterrows():
|
||||
point = row.geometry
|
||||
x_coord = point.x
|
||||
y_coord = point.y
|
||||
label = row[label_column]
|
||||
|
||||
try:
|
||||
# Find nearest pixel indices
|
||||
x_idx = np.argmin(np.abs(x_coords - x_coord))
|
||||
y_idx = np.argmin(np.abs(y_coords - y_coord))
|
||||
if idx < len(raster_features):
|
||||
feature_vec = raster_features[idx]
|
||||
|
||||
# Get features at this pixel
|
||||
# raster_features shape: (n_pixels, n_features)
|
||||
# Need to convert 2D (y, x) index to 1D pixel index
|
||||
pixel_idx = y_idx * len(x_coords) + x_idx
|
||||
if idx < 3:
|
||||
print(f"[DEBUG] Point {idx}: features={feature_vec[:3]}...")
|
||||
|
||||
if pixel_idx < len(raster_features):
|
||||
feature_vec = raster_features[pixel_idx]
|
||||
|
||||
if idx < 3:
|
||||
print(f"[DEBUG] Point {idx}: coords=({x_coord:.2f}, {y_coord:.2f}) -> pixel[{y_idx},{x_idx}] -> idx={pixel_idx}, features={feature_vec[:3]}...")
|
||||
|
||||
if not np.isnan(feature_vec).any():
|
||||
features.append(feature_vec)
|
||||
labels.append(label)
|
||||
else:
|
||||
failed_extractions += 1
|
||||
if idx < 3:
|
||||
print(f"[DEBUG] Point {idx} has NaN features")
|
||||
if not np.isnan(feature_vec).any():
|
||||
features.append(feature_vec)
|
||||
labels.append(label)
|
||||
else:
|
||||
failed_extractions += 1
|
||||
if idx < 3:
|
||||
print(f"[DEBUG] Point {idx} pixel_idx {pixel_idx} out of range (max={len(raster_features)})")
|
||||
except Exception as e:
|
||||
print(f"[DEBUG] Point {idx} has NaN features")
|
||||
else:
|
||||
failed_extractions += 1
|
||||
if idx < 3:
|
||||
print(f"[DEBUG] Point {idx} extraction failed: {e}")
|
||||
continue
|
||||
|
||||
if failed_extractions > 0:
|
||||
update_status(f"⚠️ {failed_extractions}/{len(train_gdf)} points had NaN/missing data", 65)
|
||||
update_status(f"⚠️ {failed_extractions}/{len(train_gdf)} points had NaN/missing data", 68)
|
||||
|
||||
features = np.array(features)
|
||||
labels = np.array(labels)
|
||||
|
||||
Reference in New Issue
Block a user