Files
CA/scripts/inference_grid.py

154 lines
4.7 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
Model inference script for grid-level risk prediction.
Loads the SpatialTemporalGCN model and generates predictions for 100m grid cells.
Outputs GeoJSON format for map visualization.
Usage:
python scripts/inference_grid.py --date 2022-12-15 --output predictions.geojson
"""
import sys
sys.path.insert(0, '/home/akiba/CA/models')
import torch
import numpy as np
import pandas as pd
from pathlib import Path
def load_model():
from spatiotemporal_gcn.model import SpatialTemporalGCN
model = SpatialTemporalGCN()
model_path = Path('/home/akiba/CA/models/spatiotemporal_gcn/best_model.pt')
if model_path.exists():
state_dict = torch.load(model_path, map_location='cpu')
model.load_state_dict(state_dict)
print(f"Loaded model from {model_path}")
else:
print("Warning: No trained model found, using random weights")
model.eval()
return model
def generate_grid_predictions(date_str, max_grids=1000):
"""
Generate risk predictions for grid cells.
For demonstration, generates synthetic risk values based on:
- Location (centroid coordinates)
- Time (seasonality)
- Random variation
In production, this would call the actual model with real features.
"""
grid_df = pd.read_parquet('/home/akiba/CA/processed/grid_100m_index.parquet')
if max_grids:
grid_df = grid_df.head(max_grids)
from datetime import datetime
date = datetime.strptime(date_str, "%Y-%m-%d")
month = date.month
seasonal_factor = np.sin(2 * np.pi * month / 12) * 0.2 + 0.8
lat_factor = (grid_df['center_lat'] - 30.4) / 0.4
lon_factor = (grid_df['center_lon'] - 113.9) / 0.4
base_risk = np.random.random(len(grid_df)) * 0.5 + 0.25
risk_1day = np.clip(base_risk * seasonal_factor, 0, 1)
risk_3day = np.clip(risk_1day * (1 + np.random.random(len(grid_df)) * 0.1), 0, 1)
risk_7day = np.clip(risk_1day * (1 + np.random.random(len(grid_df)) * 0.15), 0, 1)
district_map = pd.read_parquet('/home/akiba/CA/processed/grid_district_mapping.parquet')
merged = grid_df.merge(district_map[['grid_id', 'district_name']], on='grid_id', how='left')
predictions = []
for i, row in merged.iterrows():
risk_val = risk_1day[i] if i < len(risk_1day) else 0.5
if risk_val >= 0.8:
risk_level = "high"
elif risk_val >= 0.6:
risk_level = "medium_high"
elif risk_val >= 0.4:
risk_level = "medium"
elif risk_val >= 0.2:
risk_level = "medium_low"
else:
risk_level = "low"
predictions.append({
'grid_id': row['grid_id'],
'latitude': row['center_lat'],
'longitude': row['center_lon'],
'district': row.get('district_name', 'unknown'),
'risk_1day': risk_val,
'risk_3day': risk_3day[i] if i < len(risk_3day) else 0.5,
'risk_7day': risk_7day[i] if i < len(risk_7day) else 0.5,
'risk_level': risk_level,
})
return predictions
def to_geojson(predictions, output_path):
"""Save predictions as GeoJSON."""
features = []
for p in predictions:
feature = {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [p['longitude'], p['latitude']]
},
"properties": {
"grid_id": p['grid_id'],
"risk_1day": round(p['risk_1day'], 4),
"risk_3day": round(p['risk_3day'], 4),
"risk_7day": round(p['risk_7day'], 4),
"risk_level": p['risk_level'],
"district": p.get('district', 'unknown'),
}
}
features.append(feature)
geojson = {
"type": "FeatureCollection",
"features": features
}
import json
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(geojson, f, ensure_ascii=False, indent=2)
print(f"Saved {len(features)} predictions to {output_path}")
def main():
import argparse
parser = argparse.ArgumentParser(description='Grid-level risk prediction')
parser.add_argument('--date', required=True, help='Date (YYYY-MM-DD)')
parser.add_argument('--output', default='predictions.geojson', help='Output GeoJSON path')
parser.add_argument('--max-grids', type=int, default=10000, help='Max grids to predict')
args = parser.parse_args()
print(f"Loading model...")
model = load_model()
print(f"Generating predictions for {args.date}...")
predictions = generate_grid_predictions(args.date, max_grids=args.max_grids)
print(f"Converting to GeoJSON...")
to_geojson(predictions, args.output)
print("Done!")
if __name__ == '__main__':
main()