train CNN thành công

This commit is contained in:
Victor Phan
2026-04-04 02:16:36 +00:00
parent 93c7ae2ef3
commit 646b9300e1
34 changed files with 22636 additions and 0 deletions
@@ -0,0 +1,416 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "72723821",
"metadata": {},
"source": [
"# Prepare Data on Server - Load Raw Data Only\n",
"Chuẩn bị dữ liệu trên server: **Chỉ tải dữ liệu thô từ S3, không xử lý**\n",
"\n",
"**Workflow:**\n",
"1. Server: Tải S2 (red, nir, scl) + S1 (VH, VV) thô → Lưu NetCDF\n",
"2. Local: Tải xuống → Tính NDVI → Cloud mask → Fill NaN → Aggregation → Train model\n",
"\n",
"**Sau khi chạy xong, tải các file data xuống máy cá nhân để xử lý + train model**"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f502b085",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"%matplotlib inline\n",
"\n",
"import importlib\n",
"import new_import_ODC \n",
"\n",
"importlib.reload(new_import_ODC)\n",
"\n",
"from new_import_ODC import *"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "379dd847",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"# Cấu hình Daskgateway\n",
"cluster, client = notebook_utils.initialize_dask(use_gateway=True, workers=(1, 10))\n",
"# Khai báo 1 Datacube là dc\n",
"dc = datacube.Datacube()\n",
"\n",
"# Cấu hình truy cập dịch vụ S3\n",
"configure_s3_access(aws_unsigned=False, requester_pays=True, client=client)\n",
"\n",
"client"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "da3e2180",
"metadata": {},
"outputs": [],
"source": [
"## Cấu hình thời gian lấy ảnh và tọa độ\n",
"date_range = (\"2022-09-01\", \"2023-10-01\")\n",
"longtitude_range = (105.5, 106.4)\n",
"latitude_range = (9.2, 10.0)\n",
"\n",
"coordinates = (longtitude_range, latitude_range)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3f521c79",
"metadata": {},
"outputs": [],
"source": [
"## DEBUG: Inspect what datacube wants to load\n",
"print(\"🔍 DIAGNOSTIC: Checking datacube metadata...\\n\")\n",
"\n",
"# Check available products\n",
"available_products = dc.list_products()\n",
"s2_products = available_products[available_products['name'].str.contains('s2', case=False)]\n",
"print(f\"Available S2 products:\\n{s2_products[['name', 'description']].to_string()}\\n\")\n",
"\n",
"# Query to check what would be loaded\n",
"test_query = {\n",
" 'product': 's2_l2a',\n",
" 'x': longtitude_range,\n",
" 'y': latitude_range,\n",
" 'time': (\"2023-01-01\", \"2023-02-01\"), # Just 1 month for testing\n",
"}\n",
"\n",
"print(f\"Test query: {test_query}\")\n",
"\n",
"try:\n",
" # This queries metadata only, doesn't load data\n",
" test_datasets = dc.find_datasets(**test_query)\n",
" print(f\"\\n📊 Metadata check for Jan 2023:\")\n",
" print(f\" Found {len(test_datasets)} scenes\")\n",
" if test_datasets:\n",
" first_ds = test_datasets[0]\n",
" print(f\" First scene: {first_ds.center_time}\")\n",
" print(f\" Bounds: {first_ds.bounds}\")\n",
" print(f\" CRS: {first_ds.crs}\")\n",
"except Exception as e:\n",
" print(f\"❌ Error: {e}\")\n",
"\n",
"print(\"\\n\" + \"=\"*60 + \"\\n\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8cf560b0",
"metadata": {},
"outputs": [],
"source": [
"## SENTINEL-2 LOADING: Monthly chunks to prevent OOM\n",
"print(\"📡 Tải dữ liệu Sentinel-2 L2A từ S3...\")\n",
"print(f\" AOI: {longtitude_range}, {latitude_range}\")\n",
"print(f\" Time range: {date_range}\\n\")\n",
"\n",
"data = None\n",
"\n",
"# Strategy: Load 13 monthly chunks instead of 396 scenes at once\n",
"# This keeps memory usage manageable (~5-15 GB per month)\n",
"\n",
"date_ranges = [\n",
" (\"2022-09-01\", \"2022-10-01\"),\n",
" (\"2022-10-01\", \"2022-11-01\"),\n",
" (\"2022-11-01\", \"2022-12-01\"),\n",
" (\"2022-12-01\", \"2023-01-01\"),\n",
" (\"2023-01-01\", \"2023-02-01\"),\n",
" (\"2023-02-01\", \"2023-03-01\"),\n",
" (\"2023-03-01\", \"2023-04-01\"),\n",
" (\"2023-04-01\", \"2023-05-01\"),\n",
" (\"2023-05-01\", \"2023-06-01\"),\n",
" (\"2023-06-01\", \"2023-07-01\"),\n",
" (\"2023-07-01\", \"2023-08-01\"),\n",
" (\"2023-08-01\", \"2023-09-01\"),\n",
" (\"2023-09-01\", \"2023-10-01\"),\n",
"]\n",
"\n",
"product = 's2_l2a'\n",
"measurements = ['red', 'nir', 'scl']\n",
"\n",
"# Get native CRS once\n",
"try:\n",
" query_crs = {\n",
" 'product': product,\n",
" 'x': longtitude_range,\n",
" 'y': latitude_range,\n",
" 'time': date_range,\n",
" }\n",
" native_crs = notebook_utils.mostcommon_crs(dc, query_crs)\n",
" print(f\"✅ Native CRS: {native_crs}\\n\")\n",
"except Exception as e:\n",
" print(f\"⚠️ Could not determine CRS: {e}\")\n",
" native_crs = 'EPSG:32648' # Fallback for UTM Zone 48N\n",
"\n",
"data_list = []\n",
"\n",
"for i, (start_date, end_date) in enumerate(date_ranges):\n",
" print(f\"[{i+1:2d}/13] {start_date} → {end_date} \", end=\"\", flush=True)\n",
" \n",
" try:\n",
" monthly_query = {\n",
" 'product': product,\n",
" 'x': longtitude_range,\n",
" 'y': latitude_range,\n",
" 'time': (start_date, end_date),\n",
" }\n",
" \n",
" load_params = {\n",
" 'measurements': measurements,\n",
" 'output_crs': native_crs,\n",
" 'resolution': (-10, 10),\n",
" 'group_by': 'solar_day',\n",
" 'dask_chunks': {'x': 512, 'y': 512, 'time': 1},\n",
" 'skip_broken_datasets': True,\n",
" }\n",
" \n",
" monthly_data = load_s2l2a_with_offset(dc, monthly_query | load_params)\n",
" \n",
" n_scenes = monthly_data.sizes['time']\n",
" if n_scenes > 0:\n",
" data_list.append(monthly_data)\n",
" print(f\"✓ {n_scenes} scenes\")\n",
" else:\n",
" print(\"⚠️ 0 scenes\")\n",
" \n",
" except MemoryError as e:\n",
" print(f\"❌ OOM: {str(e)[:60]}\")\n",
" break\n",
" except Exception as e:\n",
" print(f\"❌ {str(e)[:60]}\")\n",
" continue\n",
"\n",
"# Combine all monthly chunks\n",
"if data_list:\n",
" print(f\"\\n🔗 Combining {len(data_list)} monthly chunks...\")\n",
" data = xr.concat(data_list, dim='time')\n",
" print(f\"✅ Success! Shape: {dict(data.dims)}\")\n",
" print(f\" Memory: {notebook_utils.xarray_object_size(data)}\")\n",
" display(data)\n",
"else:\n",
" print(\"\\n❌ Failed to load any scenes\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cef198a0",
"metadata": {},
"outputs": [],
"source": [
"## Tải dữ liệu Sentinel-1 (VH, VV) - Raw data, không xử lý\n",
"print(\"📡 Tải dữ liệu Sentinel-1 từ S3...\")\n",
"try:\n",
" dsvh, dsvv = load_data_sen1(dc, date_range, coordinates)\n",
" print(f\"✅ VH shape: {dsvh.shape}\")\n",
" print(f\"✅ VV shape: {dsvv.shape}\")\n",
"except Exception as e:\n",
" print(f\"⚠️ Error loading S1: {e}\")\n",
" dsvh = None\n",
" dsvv = None"
]
},
{
"cell_type": "markdown",
"id": "5ba52958",
"metadata": {},
"source": [
"## Lưu dữ liệu RAW thành file NetCDF (chưa xử lý)\n",
"Dữ liệu này sẽ được tải xuống máy local để tiếp tục xử lý và training"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "06bf3f40",
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"import os\n",
"\n",
"# Close Dask client FIRST to avoid permission issues\n",
"print(\"🔌 Closing Dask client to free resources...\")\n",
"client.close()\n",
"cluster.close()\n",
"print(\"✅ Dask closed\\n\")\n",
"\n",
"# Now save data locally without Dask interference\n",
"data_dir = \"data_for_training\"\n",
"if not os.path.exists(data_dir):\n",
" os.makedirs(data_dir)\n",
" print(f\"✅ Tạo thư mục {data_dir}\\n\")\n",
"\n",
"print(\"💾 Lưu dữ liệu RAW từ S2 (red, nir, scl)...\")\n",
"\n",
"# FIX: Loại bỏ metadata problematic trước khi save\n",
"data_clean = data.copy()\n",
"\n",
"# 1. Loại bỏ dict attributes từ variables\n",
"for var_name in data_clean.data_vars:\n",
" attrs_to_remove = []\n",
" for attr_name, attr_value in data_clean[var_name].attrs.items():\n",
" if isinstance(attr_value, dict):\n",
" attrs_to_remove.append(attr_name)\n",
" \n",
" for attr_name in attrs_to_remove:\n",
" print(f\" ⚠️ Removing problematic attr: {var_name}.{attr_name}\")\n",
" del data_clean[var_name].attrs[attr_name]\n",
"\n",
"# 2. Xóa attrs từ coordinates\n",
"for coord_name in data_clean.coords:\n",
" attrs_to_remove = []\n",
" for attr_name, attr_value in data_clean[coord_name].attrs.items():\n",
" if attr_name in ['units', 'calendar', 'long_name', 'standard_name']:\n",
" attrs_to_remove.append(attr_name)\n",
" elif isinstance(attr_value, dict):\n",
" attrs_to_remove.append(attr_name)\n",
" \n",
" for attr_name in attrs_to_remove:\n",
" print(f\" ⚠️ Removing coord attr: {coord_name}.{attr_name}\")\n",
" del data_clean[coord_name].attrs[attr_name]\n",
"\n",
"# Compute to memory BEFORE closing Dask\n",
"print(\"\\n ⏳ Loading data into memory...\")\n",
"try:\n",
" data_computed = data_clean.compute()\n",
" print(\" ✅ Data loaded to memory\\n\")\n",
" \n",
" # Save from memory (no Dask involved)\n",
" s2_path = os.path.join(data_dir, \"sentinel2_raw.nc\")\n",
" print(f\" - Saving to {s2_path}...\")\n",
" data_computed.to_netcdf(s2_path, engine='netcdf4')\n",
" s2_size = os.path.getsize(s2_path) / 1024**3\n",
" print(f\" ✅ Sentinel-2 saved ({s2_size:.2f} GB)\")\n",
"except Exception as e:\n",
" print(f\" ❌ Error: {e}\")\n",
" print(f\" ⚠️ Data may still be in memory, proceed to next step\")\n",
"\n",
"# Lưu raw Sentinel-1 (nếu thành công)\n",
"if dsvh is not None and dsvv is not None:\n",
" print(f\"\\n💾 Lưu dữ liệu RAW từ S1 (VH, VV)...\")\n",
" \n",
" # Remove problematic attrs từ S1 data\n",
" if hasattr(dsvh, 'attrs'):\n",
" for attr in list(dsvh.attrs.keys()):\n",
" if attr in ['units', 'calendar'] or isinstance(dsvh.attrs[attr], dict):\n",
" del dsvh.attrs[attr]\n",
" \n",
" if hasattr(dsvv, 'attrs'):\n",
" for attr in list(dsvv.attrs.keys()):\n",
" if attr in ['units', 'calendar'] or isinstance(dsvv.attrs[attr], dict):\n",
" del dsvv.attrs[attr]\n",
" \n",
" s1_data = xr.Dataset({\n",
" 'VH': dsvh,\n",
" 'VV': dsvv,\n",
" })\n",
" \n",
" s1_path = os.path.join(data_dir, \"sentinel1_raw.nc\")\n",
" print(f\" - Saving to {s1_path}...\")\n",
" \n",
" try:\n",
" s1_data.to_netcdf(s1_path, engine='netcdf4')\n",
" s1_size = os.path.getsize(s1_path) / 1024**3\n",
" print(f\" ✅ Sentinel-1 saved ({s1_size:.2f} GB)\")\n",
" except Exception as e:\n",
" print(f\" ❌ Error saving S1: {e}\")\n",
"else:\n",
" print(\"⚠️ Sentinel-1 not saved\")\n",
"\n",
"print(f\"\\n✅ Data ready in '{data_dir}'\")\n",
"print(f\"\\n📥 **Download to local machine using rsync:**\")\n",
"print(f\" rsync -avz --progress user@server:~/CSIROBoeingPhase5-Vietnam/{data_dir}/ ./{data_dir}/\")\n",
"print(f\"\\n🔧 Next: Process data locally (NDVI, cloud mask, aggregation, training)\")\n",
"\n",
"# Hiển thị tổng kích thước\n",
"import subprocess\n",
"try:\n",
" result = subprocess.run(['du', '-sh', data_dir], capture_output=True, text=True)\n",
" print(f\"\\n📊 Data size on server: {result.stdout.strip()}\")\n",
"except:\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "93df03e7",
"metadata": {},
"outputs": [],
"source": [
"# Lưu training data\n",
"train_path = \"train/ST_training data_updated_1130points_new.shp\"\n",
"import shutil\n",
"\n",
"print(f\"📋 Copy training data...\")\n",
"# Copy toàn bộ các file liên quan đến shapefile\n",
"train_dir = \"train\"\n",
"train_output = os.path.join(data_dir, \"train_data\")\n",
"if not os.path.exists(train_output):\n",
" os.makedirs(train_output)\n",
"\n",
"for file in os.listdir(train_dir):\n",
" if \"1130points_new\" in file:\n",
" src = os.path.join(train_dir, file)\n",
" dst = os.path.join(train_output, file)\n",
" shutil.copy2(src, dst)\n",
" print(f\"✅ {file}\")\n",
"\n",
"print(f\"\\n✅ Training data đã copy vào '{train_output}'\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8bb935d9",
"metadata": {},
"outputs": [],
"source": [
"# Đóng client, cluster\n",
"print(\"\\n🔌 Đóng kết nối...\")\n",
"client.close()\n",
"cluster.close()\n",
"print(\"✅ Xong!\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,405 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "912ed572-1658-406b-976c-cd6de2d4e89e",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [],
"source": [
"%%time\n",
"%matplotlib inline\n",
"\n",
"import importlib\n",
"import new_import_ODC \n",
"\n",
"importlib.reload(new_import_ODC)\n",
"\n",
"from new_import_ODC import *"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d824dc4f-994b-4d1c-8d24-ce6674da141c",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"%%time\n",
"# Cấu hình Daskgateway\n",
"cluster, client = notebook_utils.initialize_dask(use_gateway=True, workers=(1, 10))\n",
"# Khai báo 1 Datacube là dc\n",
"dc = datacube.Datacube()\n",
"\n",
"# Cấu hình truy cập dịch vụ S3\n",
"configure_s3_access(aws_unsigned=False, requester_pays=True, client=client)\n",
"\n",
"client"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fbed4c80-bbf8-4ea8-aa45-2460b2ba04c7",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"## cấu hình thời gian lấy ảnh và tọa độ\n",
"date_range = (\"2022-09-01\", \"2023-10-01\")\n",
"longtitude_range = (105.5, 106.4)\n",
"latitude_range = (9.2, 10.0)\n",
"\n",
"coordinates = (longtitude_range, latitude_range)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6b90c49b-0665-4478-a23b-d111ef88eb79",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"## truy vấn ảnh vệ tinh sen2\n",
"data = load_data(dc, date_range, longtitude_range, latitude_range)\n",
"notebook_utils.heading(notebook_utils.xarray_object_size(data))\n",
"display(data)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2f6938b6-82e2-4916-bc1d-719c169e25e4",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"%%time\n",
"# Tiến hành loại bỏ các vị trí bị mây ảnh hưởng\n",
"result = mask_clean(data)\n",
"progress(result)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "435f9f78-a9a4-4226-86ca-d4bec42d454e",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"# Tiến hành tính toán NDVI\n",
"ds1 = calculate_indices(result, index=\"NDVI\", satellite_mission=\"s2\")\n",
"ndvi = ds1[\"NDVI\"]\n",
"display(ndvi)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "84992d28-8e3f-468e-be08-ded511f2c662",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"## Hiển thị ảnh NDVI chưa điền các giá trị mây (chưa fill nan)\n",
"plt.imshow(ndvi.isel(time=6))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "72318b60-532a-4f08-a5f7-94762d08a42c",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"# Thiết lập giá trị trung bình mùa vụ để xử lý các điểm ảnh bị mây dựa vào sự thay đổi theo mùa\n",
"time_split = [\n",
" slice(\"2022-09-01\", \"2023-01-01\"),\n",
" slice(\"2023-01-01\", \"2023-05-01\"),\n",
" slice(\"2023-05-01\", \"2023-07-01\"),\n",
" slice(\"2023-07-01\", \"2023-10-01\"),\n",
"]\n",
"\n",
"# Điền mây ở các vị trí mang giá trị nan (fill nan)\n",
"fill_nan_ndvi = fill_nan(ndvi, time_split)\n",
"\n",
"# In kết quả ảnh NDVI đã điền mây (đã fill nan)\n",
"plt.imshow(fill_nan_ndvi.isel(time=6))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "375b1cfb-37f5-49fe-8061-ea32eb47f9f6",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"%%time\n",
"## tính ndvi theo tháng\n",
"average_ndvi = fill_nan_ndvi.resample(time=\"1M\").mean().persist()\n",
"progress(average_ndvi)\n",
"\n",
"# compute average_ndvi\n",
"average_ndvi = average_ndvi.compute()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "187cc640-aef9-476b-91fc-b63f4d3ff2e3",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"#Load dữ liệu ảnh Sentinel 1\n",
"dsvh, dsvv = load_data_sen1(dc, date_range, coordinates)\n",
"average_vv = calculate_average(dsvv, time_pattern='1M')\n",
"average_vh = calculate_average(dsvh, time_pattern='1M')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d2585562-88aa-4c7d-bf70-1f6affcf65d4",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"## cấu hình bộ dữ liệu điểm huấn luyện mô hình (train file)\n",
"train_path = \"train/ST_training data_updated_1130points_new.shp\" # đường dẫn shp file train\n",
"\n",
"## load dữ liệu điểm huấn luyện mô hình (train file)\n",
"train = load_train_data(train_path)\n",
"train.head()\n",
"\n",
"# cấu hình nhãn dữ liệu \n",
"label_mapping = {\n",
" \"Lua tom\": \"0\",\n",
" \"Lua\": \"1\",\n",
" \"CHN\": \"2\",\n",
" \"CLN\": \"3\",\n",
" \"TS\": \"4\",\n",
" \"Song\": \"5\",\n",
" \"Dat xay dung\": \"6\",\n",
" \"Rung\": \"7\",\n",
"}\n",
"\n",
"# xây dựng tập dữ liệu (dataset) chứa dữ liệu VH, VV, NDVI\n",
"datasets = get_data_sen1_and_sen2(train, average_ndvi, average_vh, average_vv)\n",
"\n",
"# chia tập dữ liệu thành các phần theo tỉ lệ 80(80-20)-20 tương ứng với tập train, validate, test\n",
"X_train, X_val, X_test, y_train, y_val, y_test = split_train_data(\n",
" train, label_mapping, datasets\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2e955884-d4af-422d-a8e6-d436199540e0",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"%%time\n",
"# Import XGBoost\n",
"import xgboost as xgb\n",
"from sklearn.metrics import accuracy_score\n",
"import numpy as np\n",
"\n",
"# Convert to numpy arrays\n",
"X_train_np = np.asarray(X_train, dtype=np.float32)\n",
"X_val_np = np.asarray(X_val, dtype=np.float32)\n",
"y_train_np = np.asarray(y_train, dtype=np.int32)\n",
"y_val_np = np.asarray(y_val, dtype=np.int32)\n",
"\n",
"print(\"🚀 Training XGBoost model...\")\n",
"print(f\" Train samples: {len(X_train_np)}\")\n",
"print(f\" Val samples: {len(X_val_np)}\")\n",
"print(f\" Features: {X_train_np.shape[1]}\")\n",
"print(f\" Classes: 8\\n\")\n",
"\n",
"# XGBoost parameters\n",
"params = {\n",
" 'objective': 'multi:softmax', # Multi-class classification\n",
" 'num_class': 8, # 8 land use classes\n",
" 'max_depth': 6, # Maximum tree depth\n",
" 'learning_rate': 0.1, # Learning rate\n",
" 'n_estimators': 200, # Number of trees\n",
" 'subsample': 0.8, # Subsample ratio\n",
" 'colsample_bytree': 0.8, # Feature sampling ratio\n",
" 'random_state': 42,\n",
" 'n_jobs': -1, # Use all CPU cores\n",
" 'eval_metric': 'mlogloss' # Multi-class log loss\n",
"}\n",
"\n",
"# Train XGBoost model\n",
"model = xgb.XGBClassifier(**params)\n",
"\n",
"model.fit(\n",
" X_train_np, y_train_np,\n",
" eval_set=[(X_train_np, y_train_np), (X_val_np, y_val_np)],\n",
" verbose=True\n",
")\n",
"\n",
"# Validation accuracy\n",
"y_val_pred = model.predict(X_val_np)\n",
"val_accuracy = accuracy_score(y_val_np, y_val_pred)\n",
"print(f\"\\n✅ Training completed!\")\n",
"print(f\" Validation Accuracy: {val_accuracy:.4f} ({val_accuracy*100:.2f}%)\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b2a1e42c-cf1b-4d82-a6af-b06e3496918f",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"%%time\n",
"# Evaluate on test set\n",
"X_test_np = np.asarray(X_test, dtype=np.float32)\n",
"y_test_np = np.asarray(y_test, dtype=np.int32)\n",
"\n",
"print(\"📊 Evaluating XGBoost model on test set...\\n\")\n",
"\n",
"# Predictions\n",
"y_pred_test = model.predict(X_test_np)\n",
"\n",
"# Metrics\n",
"from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix\n",
"\n",
"test_accuracy = accuracy_score(y_test_np, y_pred_test)\n",
"precision = precision_score(y_test_np, y_pred_test, average='weighted', zero_division=0)\n",
"recall = recall_score(y_test_np, y_pred_test, average='weighted', zero_division=0)\n",
"f1 = f1_score(y_test_np, y_pred_test, average='weighted', zero_division=0)\n",
"\n",
"print(f\"📈 Test Results:\")\n",
"print(f\" Accuracy: {test_accuracy:.4f} ({test_accuracy*100:.2f}%)\")\n",
"print(f\" Precision: {precision:.4f}\")\n",
"print(f\" Recall: {recall:.4f}\")\n",
"print(f\" F1-Score: {f1:.4f}\\n\")\n",
"\n",
"# Confusion Matrix\n",
"from sklearn.metrics import ConfusionMatrixDisplay\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# Create figure first\n",
"fig, ax = plt.subplots(figsize=(10, 8))\n",
"\n",
"class_names = list(label_mapping.keys())\n",
"cm = confusion_matrix(y_test_np, y_pred_test)\n",
"disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=class_names)\n",
"disp.plot(cmap='Blues', ax=ax)\n",
"plt.xticks(rotation=45, ha='right')\n",
"plt.title('XGBoost Confusion Matrix')\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f1a14379-ed6e-4897-9ca4-2669743fab40",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"# Lưu mô hình huấn luyện\n",
"import json\n",
"import joblib\n",
"\n",
"# Save XGBoost model\n",
"model_path = \"model_xgboost.joblib\"\n",
"joblib.dump(model, model_path)\n",
"print(f\"✅ Model saved to {model_path}\")\n",
"\n",
"# Save model info\n",
"info = {\n",
" \"model_type\": \"XGBoost\",\n",
" \"num_classes\": 8,\n",
" \"classes\": list(label_mapping.keys()),\n",
" \"num_features\": X_train_np.shape[1],\n",
" \"params\": params,\n",
" \"accuracy\": float(test_accuracy),\n",
" \"precision\": float(precision),\n",
" \"recall\": float(recall),\n",
" \"f1_score\": float(f1),\n",
"}\n",
"\n",
"with open(\"model_xgboost_info.json\", \"w\") as f:\n",
" json.dump(info, f, indent=2)\n",
"\n",
"print(f\"✅ Model info saved to model_xgboost_info.json\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "33dd516d-9824-499e-96b9-5cd9224c194c",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"# đóng client, cluster\n",
"client.close()\n",
"cluster.close()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ee0ecd6a-733b-4655-8864-bc037a539ce2",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,571 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "a58466bf",
"metadata": {},
"source": [
"# Train CNN Model on Local Machine\n",
"Huấn luyện mô hình CNN với PyTorch trên máy cá nhân\n",
"\n",
"**Yêu cầu**: Đã tải dữ liệu từ server vào thư mục `data_for_training/`"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0e5a5a39",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import numpy as np\n",
"import xarray as xr\n",
"import torch\n",
"import torch.nn as nn\n",
"import torch.optim as optim\n",
"from torch.utils.data import DataLoader, TensorDataset\n",
"import matplotlib.pyplot as plt\n",
"from sklearn.preprocessing import LabelEncoder\n",
"from sklearn.model_selection import train_test_split\n",
"import geopandas as gpd\n",
"import joblib\n",
"from utils import load_data_geo\n",
"\n",
"print(f\"PyTorch version: {torch.__version__}\")\n",
"print(f\"GPU available: {torch.cuda.is_available()}\")\n",
"if torch.cuda.is_available():\n",
" print(f\"GPU device: {torch.cuda.get_device_name(0)}\")"
]
},
{
"cell_type": "markdown",
"id": "ff3f92e3",
"metadata": {},
"source": [
"## Load dữ liệu từ server"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "880dba25",
"metadata": {},
"outputs": [],
"source": [
"# Đường dẫn dữ liệu\n",
"data_dir = \"data_for_training\"\n",
"\n",
"print(\"📂 Kiểm tra các file dữ liệu...\")\n",
"if not os.path.exists(data_dir):\n",
" raise FileNotFoundError(f\"❌ Thư mục '{data_dir}' không tồn tại. Hãy tải dữ liệu từ server trước.\")\n",
"\n",
"# Load dữ liệu\n",
"print(\"\\n📥 Load dữ liệu...\")\n",
"average_ndvi = xr.open_dataarray(os.path.join(data_dir, \"average_ndvi.nc\"))\n",
"average_vv = xr.open_dataarray(os.path.join(data_dir, \"average_vv.nc\"))\n",
"average_vh = xr.open_dataarray(os.path.join(data_dir, \"average_vh.nc\"))\n",
"\n",
"print(f\"✅ NDVI shape: {average_ndvi.shape}\")\n",
"print(f\"✅ VV shape: {average_vv.shape}\")\n",
"print(f\"✅ VH shape: {average_vh.shape}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2236b22d",
"metadata": {},
"outputs": [],
"source": [
"# Load training data\n",
"train_data_path = os.path.join(data_dir, \"train_data\", \"ST_training data_updated_1130points_new.shp\")\n",
"print(f\"\\n📋 Load training points...\")\n",
"train = load_data_geo(train_data_path)\n",
"print(f\"✅ Training points: {len(train)}\")\n",
"print(f\"✅ Columns: {train.columns.tolist()}\")\n",
"train.head()"
]
},
{
"cell_type": "markdown",
"id": "8b1800e2",
"metadata": {},
"source": [
"## Chuẩn bị dữ liệu huấn luyện"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "128d1492",
"metadata": {},
"outputs": [],
"source": [
"# Cấu hình nhãn dữ liệu\n",
"label_mapping = {\n",
" \"Lua tom\": 0,\n",
" \"Lua\": 1,\n",
" \"CHN\": 2,\n",
" \"CLN\": 3,\n",
" \"TS\": 4,\n",
" \"Song\": 5,\n",
" \"Dat xay dung\": 6,\n",
" \"Rung\": 7,\n",
"}\n",
"\n",
"num_classes = len(label_mapping)\n",
"print(f\"🏷️ Số lớp: {num_classes}\")\n",
"for label, idx in label_mapping.items():\n",
" print(f\" {idx}: {label}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ec87566f",
"metadata": {},
"outputs": [],
"source": [
"# Trích xuất dữ liệu từ các điểm training\n",
"print(\"\\n🔧 Trích xuất dữ liệu từ các điểm...\")\n",
"X = []\n",
"y = []\n",
"\n",
"for idx, point in train.iterrows():\n",
" try:\n",
" # Lấy tọa độ\n",
" x_coord = point.geometry.x\n",
" y_coord = point.geometry.y\n",
" \n",
" # Trích xuất giá trị từ mỗi band\n",
" ndvi_data = average_ndvi.sel(x=x_coord, y=y_coord, method='nearest').values\n",
" vv_data = average_vv.sel(x=x_coord, y=y_coord, method='nearest').values\n",
" vh_data = average_vh.sel(x=x_coord, y=y_coord, method='nearest').values\n",
" \n",
" # Concatenate các band\n",
" data_point = np.concatenate((ndvi_data, vv_data, vh_data))\n",
" X.append(data_point)\n",
" \n",
" # Lấy nhãn\n",
" label_text = point.Hientrang\n",
" label_idx = label_mapping[label_text]\n",
" y.append(label_idx)\n",
" \n",
" except Exception as e:\n",
" print(f\" ⚠️ Point {idx}: {e}\")\n",
"\n",
"X = np.array(X)\n",
"y = np.array(y)\n",
"\n",
"print(f\"✅ Dữ liệu trích xuất: {X.shape}\")\n",
"print(f\"✅ Nhãn: {y.shape}\")\n",
"print(f\"✅ Phân phối nhãn: {np.bincount(y)}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2587a67c",
"metadata": {},
"outputs": [],
"source": [
"# Chia dữ liệu\n",
"print(\"\\n📊 Chia dữ liệu (train/val/test = 60/20/20)...\")\n",
"X_train, X_temp, y_train, y_temp = train_test_split(\n",
" X, y, test_size=0.4, random_state=42, stratify=y\n",
")\n",
"X_val, X_test, y_val, y_test = train_test_split(\n",
" X_temp, y_temp, test_size=0.5, random_state=42, stratify=y_temp\n",
")\n",
"\n",
"print(f\"✅ Train: {X_train.shape}\")\n",
"print(f\"✅ Val: {X_val.shape}\")\n",
"print(f\"✅ Test: {X_test.shape}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "359761e4",
"metadata": {},
"outputs": [],
"source": [
"# Normalize dữ liệu\n",
"print(\"\\n🔧 Normalize dữ liệu...\")\n",
"mean = X_train.mean()\n",
"std = X_train.std()\n",
"X_train = (X_train - mean) / (std + 1e-8)\n",
"X_val = (X_val - mean) / (std + 1e-8)\n",
"X_test = (X_test - mean) / (std + 1e-8)\n",
"\n",
"print(f\"✅ Mean: {mean:.4f}\")\n",
"print(f\"✅ Std: {std:.4f}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0667150b",
"metadata": {},
"outputs": [],
"source": [
"# Convert to PyTorch tensors\n",
"print(\"\\n🔧 Convert sang PyTorch tensors...\")\n",
"X_train_tensor = torch.FloatTensor(X_train).unsqueeze(1) # (N, 1, features)\n",
"X_val_tensor = torch.FloatTensor(X_val).unsqueeze(1)\n",
"X_test_tensor = torch.FloatTensor(X_test).unsqueeze(1)\n",
"\n",
"y_train_tensor = torch.LongTensor(y_train)\n",
"y_val_tensor = torch.LongTensor(y_val)\n",
"y_test_tensor = torch.LongTensor(y_test)\n",
"\n",
"print(f\"✅ X_train shape: {X_train_tensor.shape}\")\n",
"print(f\"✅ y_train shape: {y_train_tensor.shape}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "be2a4179",
"metadata": {},
"outputs": [],
"source": [
"# Tạo DataLoaders\n",
"print(\"\\n🔧 Tạo DataLoaders...\")\n",
"batch_size = 32\n",
"\n",
"train_dataset = TensorDataset(X_train_tensor, y_train_tensor)\n",
"train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\n",
"\n",
"val_dataset = TensorDataset(X_val_tensor, y_val_tensor)\n",
"val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)\n",
"\n",
"test_dataset = TensorDataset(X_test_tensor, y_test_tensor)\n",
"test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)\n",
"\n",
"print(f\"✅ Train batches: {len(train_loader)}\")\n",
"print(f\"✅ Val batches: {len(val_loader)}\")\n",
"print(f\"✅ Test batches: {len(test_loader)}\")"
]
},
{
"cell_type": "markdown",
"id": "5231acf3",
"metadata": {},
"source": [
"## Xây dựng CNN Model"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b279fe09",
"metadata": {},
"outputs": [],
"source": [
"# Định nghĩa CNN model\n",
"class CNNClassifier(nn.Module):\n",
" def __init__(self, input_size, num_classes=8):\n",
" super(CNNClassifier, self).__init__()\n",
" \n",
" # Conv blocks\n",
" self.conv1 = nn.Conv1d(1, 64, kernel_size=3, padding=1)\n",
" self.bn1 = nn.BatchNorm1d(64)\n",
" self.conv2 = nn.Conv1d(64, 64, kernel_size=3, padding=1)\n",
" self.bn2 = nn.BatchNorm1d(64)\n",
" self.pool1 = nn.MaxPool1d(2)\n",
" self.drop1 = nn.Dropout(0.25)\n",
" \n",
" self.conv3 = nn.Conv1d(64, 128, kernel_size=3, padding=1)\n",
" self.bn3 = nn.BatchNorm1d(128)\n",
" self.conv4 = nn.Conv1d(128, 128, kernel_size=3, padding=1)\n",
" self.bn4 = nn.BatchNorm1d(128)\n",
" self.pool2 = nn.MaxPool1d(2)\n",
" self.drop2 = nn.Dropout(0.25)\n",
" \n",
" self.conv5 = nn.Conv1d(128, 256, kernel_size=3, padding=1)\n",
" self.bn5 = nn.BatchNorm1d(256)\n",
" self.conv6 = nn.Conv1d(256, 256, kernel_size=3, padding=1)\n",
" self.bn6 = nn.BatchNorm1d(256)\n",
" self.global_avg_pool = nn.AdaptiveAvgPool1d(1)\n",
" self.drop3 = nn.Dropout(0.25)\n",
" \n",
" # FC layers\n",
" self.fc1 = nn.Linear(256, 128)\n",
" self.bn_fc1 = nn.BatchNorm1d(128)\n",
" self.drop_fc1 = nn.Dropout(0.5)\n",
" \n",
" self.fc2 = nn.Linear(128, 64)\n",
" self.bn_fc2 = nn.BatchNorm1d(64)\n",
" self.drop_fc2 = nn.Dropout(0.5)\n",
" \n",
" self.fc3 = nn.Linear(64, num_classes)\n",
" \n",
" self.relu = nn.ReLU()\n",
" \n",
" def forward(self, x):\n",
" # Block 1\n",
" x = self.relu(self.bn1(self.conv1(x)))\n",
" x = self.relu(self.bn2(self.conv2(x)))\n",
" x = self.pool1(x)\n",
" x = self.drop1(x)\n",
" \n",
" # Block 2\n",
" x = self.relu(self.bn3(self.conv3(x)))\n",
" x = self.relu(self.bn4(self.conv4(x)))\n",
" x = self.pool2(x)\n",
" x = self.drop2(x)\n",
" \n",
" # Block 3\n",
" x = self.relu(self.bn5(self.conv5(x)))\n",
" x = self.relu(self.bn6(self.conv6(x)))\n",
" x = self.global_avg_pool(x)\n",
" x = x.squeeze(-1)\n",
" x = self.drop3(x)\n",
" \n",
" # FC layers\n",
" x = self.relu(self.bn_fc1(self.fc1(x)))\n",
" x = self.drop_fc1(x)\n",
" x = self.relu(self.bn_fc2(self.fc2(x)))\n",
" x = self.drop_fc2(x)\n",
" x = self.fc3(x)\n",
" \n",
" return x\n",
"\n",
"# Khởi tạo model\n",
"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
"model = CNNClassifier(input_size=X_train.shape[1], num_classes=num_classes)\n",
"model = model.to(device)\n",
"\n",
"print(f\"✅ Model created\")\n",
"print(f\"📍 Device: {device}\")\n",
"print(f\"\\n📋 Model architecture:\")\n",
"print(model)"
]
},
{
"cell_type": "markdown",
"id": "ff42829a",
"metadata": {},
"source": [
"## Huấn luyện Model"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "de90a544",
"metadata": {},
"outputs": [],
"source": [
"# Loss function và optimizer\n",
"criterion = nn.CrossEntropyLoss()\n",
"optimizer = optim.Adam(model.parameters(), lr=0.001)\n",
"scheduler = optim.lr_scheduler.ReduceLROnPlateau(\n",
" optimizer, mode='min', factor=0.5, patience=5, verbose=True\n",
")\n",
"\n",
"epochs = 100\n",
"early_stop_patience = 15\n",
"best_val_loss = float('inf')\n",
"early_stop_counter = 0\n",
"\n",
"train_losses = []\n",
"val_losses = []\n",
"train_accs = []\n",
"val_accs = []\n",
"\n",
"print(\"🚀 Bắt đầu huấn luyện...\\n\")\n",
"\n",
"for epoch in range(epochs):\n",
" # Training\n",
" model.train()\n",
" train_loss = 0\n",
" train_correct = 0\n",
" train_total = 0\n",
" \n",
" for X_batch, y_batch in train_loader:\n",
" X_batch = X_batch.to(device)\n",
" y_batch = y_batch.to(device)\n",
" \n",
" optimizer.zero_grad()\n",
" outputs = model(X_batch)\n",
" loss = criterion(outputs, y_batch)\n",
" loss.backward()\n",
" optimizer.step()\n",
" \n",
" train_loss += loss.item()\n",
" _, predicted = torch.max(outputs.data, 1)\n",
" train_correct += (predicted == y_batch).sum().item()\n",
" train_total += y_batch.size(0)\n",
" \n",
" train_loss /= len(train_loader)\n",
" train_acc = 100 * train_correct / train_total\n",
" train_losses.append(train_loss)\n",
" train_accs.append(train_acc)\n",
" \n",
" # Validation\n",
" model.eval()\n",
" val_loss = 0\n",
" val_correct = 0\n",
" val_total = 0\n",
" \n",
" with torch.no_grad():\n",
" for X_batch, y_batch in val_loader:\n",
" X_batch = X_batch.to(device)\n",
" y_batch = y_batch.to(device)\n",
" \n",
" outputs = model(X_batch)\n",
" loss = criterion(outputs, y_batch)\n",
" val_loss += loss.item()\n",
" _, predicted = torch.max(outputs.data, 1)\n",
" val_correct += (predicted == y_batch).sum().item()\n",
" val_total += y_batch.size(0)\n",
" \n",
" val_loss /= len(val_loader)\n",
" val_acc = 100 * val_correct / val_total\n",
" val_losses.append(val_loss)\n",
" val_accs.append(val_acc)\n",
" \n",
" # Print progress\n",
" if (epoch + 1) % 10 == 0:\n",
" print(f'Epoch [{epoch+1}/{epochs}]')\n",
" print(f' Train Loss: {train_loss:.4f}, Acc: {train_acc:.2f}%')\n",
" print(f' Val Loss: {val_loss:.4f}, Acc: {val_acc:.2f}%')\n",
" \n",
" # Early stopping\n",
" scheduler.step(val_loss)\n",
" \n",
" if val_loss < best_val_loss:\n",
" best_val_loss = val_loss\n",
" early_stop_counter = 0\n",
" # Lưu best model\n",
" torch.save(model.state_dict(), 'model_cnn_pytorch_best.pt')\n",
" else:\n",
" early_stop_counter += 1\n",
" if early_stop_counter >= early_stop_patience:\n",
" print(f'\\n⛔ Early stopping at epoch {epoch+1}')\n",
" break\n",
"\n",
"print(f'\\n✅ Huấn luyện hoàn thành!')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0868ab12",
"metadata": {},
"outputs": [],
"source": [
"# Load best model\n",
"model.load_state_dict(torch.load('model_cnn_pytorch_best.pt'))\n",
"\n",
"# Evaluate on test set\n",
"model.eval()\n",
"test_correct = 0\n",
"test_total = 0\n",
"test_loss = 0\n",
"\n",
"with torch.no_grad():\n",
" for X_batch, y_batch in test_loader:\n",
" X_batch = X_batch.to(device)\n",
" y_batch = y_batch.to(device)\n",
" \n",
" outputs = model(X_batch)\n",
" loss = criterion(outputs, y_batch)\n",
" test_loss += loss.item()\n",
" _, predicted = torch.max(outputs.data, 1)\n",
" test_correct += (predicted == y_batch).sum().item()\n",
" test_total += y_batch.size(0)\n",
"\n",
"test_loss /= len(test_loader)\n",
"test_acc = 100 * test_correct / test_total\n",
"\n",
"print(f\"📊 Test Results:\")\n",
"print(f\" Test Loss: {test_loss:.4f}\")\n",
"print(f\" Test Accuracy: {test_acc:.2f}%\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c2067227",
"metadata": {},
"outputs": [],
"source": [
"# Vẽ đồ thị huấn luyện\n",
"fig, axes = plt.subplots(1, 2, figsize=(15, 5))\n",
"\n",
"# Loss\n",
"axes[0].plot(train_losses, label='Train Loss')\n",
"axes[0].plot(val_losses, label='Val Loss')\n",
"axes[0].set_xlabel('Epoch')\n",
"axes[0].set_ylabel('Loss')\n",
"axes[0].set_title('Model Loss')\n",
"axes[0].legend()\n",
"axes[0].grid(True)\n",
"\n",
"# Accuracy\n",
"axes[1].plot(train_accs, label='Train Accuracy')\n",
"axes[1].plot(val_accs, label='Val Accuracy')\n",
"axes[1].set_xlabel('Epoch')\n",
"axes[1].set_ylabel('Accuracy (%)')\n",
"axes[1].set_title('Model Accuracy')\n",
"axes[1].legend()\n",
"axes[1].grid(True)\n",
"\n",
"plt.tight_layout()\n",
"plt.savefig('training_history.png', dpi=150, bbox_inches='tight')\n",
"plt.show()\n",
"\n",
"print(\"✅ Đồ thị đã lưu vào 'training_history.png'\")"
]
},
{
"cell_type": "markdown",
"id": "d8402a21",
"metadata": {},
"source": [
"## Lưu Model"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c12de183",
"metadata": {},
"outputs": [],
"source": [
"# Lưu model state dict\n",
"print(\"💾 Lưu model...\")\n",
"torch.save(model.state_dict(), 'model_cnn_pytorch.pt')\n",
"print(f\"✅ Model state dict đã lưu: model_cnn_pytorch.pt\")\n",
"\n",
"# Lưu thông tin model\n",
"model_info = {\n",
" 'state_dict': model.state_dict(),\n",
" 'num_classes': num_classes,\n",
" 'input_size': X_train.shape[1],\n",
" 'label_mapping': label_mapping,\n",
" 'mean': mean,\n",
" 'std': std,\n",
" 'test_accuracy': test_acc,\n",
" 'test_loss': test_loss\n",
"}\n",
"\n",
"torch.save(model_info, 'model_cnn_pytorch_full.pt')\n",
"print(f\"✅ Full model info đã lưu: model_cnn_pytorch_full.pt\")\n",
"\n",
"print(f\"\\n✅ Model ready for prediction!\")"
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@@ -0,0 +1,312 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "d2134b50",
"metadata": {},
"source": [
"# Training CNN Model with PyTorch for Land Use Classification\n",
"Huấn luyện mô hình CNN với PyTorch cho phân loại sử dụng đất từ dữ liệu Sentinel-1 và Sentinel-2"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5b9ca134",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"%matplotlib inline\n",
"\n",
"import importlib\n",
"import new_import_ODC \n",
"\n",
"importlib.reload(new_import_ODC)\n",
"\n",
"from new_import_ODC import *"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "709c2518",
"metadata": {},
"outputs": [],
"source": [
"# Kiểm tra GPU availability\n",
"print(f\"PyTorch version: {torch.__version__}\")\n",
"print(f\"CUDA available: {torch.cuda.is_available()}\")\n",
"if torch.cuda.is_available():\n",
" print(f\"CUDA device: {torch.cuda.get_device_name(0)}\")\n",
" device = 'cuda'\n",
"else:\n",
" print(\"Using CPU for training\")\n",
" device = 'cpu'\n",
"\n",
"print(f\"\\nDevice sẽ dùng: {device}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "15f073ca",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"# Cấu hình Daskgateway\n",
"cluster, client = notebook_utils.initialize_dask(use_gateway=True, workers=(1, 10))\n",
"# Khai báo 1 Datacube là dc\n",
"dc = datacube.Datacube()\n",
"\n",
"# Cấu hình truy cập dịch vụ S3\n",
"configure_s3_access(aws_unsigned=False, requester_pays=True, client=client)\n",
"\n",
"client"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "74a229f6",
"metadata": {},
"outputs": [],
"source": [
"## cấu hình thời gian lấy ảnh và tọa độ\n",
"date_range = (\"2022-09-01\", \"2023-10-01\")\n",
"longtitude_range = (105.5, 106.4)\n",
"latitude_range = (9.2, 10.0)\n",
"\n",
"coordinates = (longtitude_range, latitude_range)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1ebdade4",
"metadata": {},
"outputs": [],
"source": [
"## truy vấn ảnh vệ tinh sen2\n",
"data = load_data(dc, date_range, longtitude_range, latitude_range)\n",
"notebook_utils.heading(notebook_utils.xarray_object_size(data))\n",
"display(data)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c444f3cc",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"# Tiến hành loại bỏ các vị trí bị mây ảnh hưởng\n",
"result = mask_clean(data)\n",
"progress(result)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f0df6581",
"metadata": {},
"outputs": [],
"source": [
"# Tiến hành tính toán NDVI\n",
"ds1 = calculate_indices(result, index=\"NDVI\", satellite_mission=\"s2\")\n",
"ndvi = ds1[\"NDVI\"]\n",
"display(ndvi)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "430302e3",
"metadata": {},
"outputs": [],
"source": [
"## Hiển thị ảnh NDVI chưa điền các giá trị mây (chưa fill nan)\n",
"plt.imshow(ndvi.isel(time=6))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6798b0d2",
"metadata": {},
"outputs": [],
"source": [
"# Thiết lập giá trị trung bình mùa vụ để xử lý các điểm ảnh bị mây dựa vào sự thay đổi theo mùa\n",
"time_split = [\n",
" slice(\"2022-09-01\", \"2023-01-01\"),\n",
" slice(\"2023-01-01\", \"2023-05-01\"),\n",
" slice(\"2023-05-01\", \"2023-07-01\"),\n",
" slice(\"2023-07-01\", \"2023-10-01\"),\n",
"]\n",
"\n",
"# Điền mây ở các vị trí mang giá trị nan (fill nan)\n",
"fill_nan_ndvi = fill_nan(ndvi, time_split)\n",
"\n",
"# In kết quả ảnh NDVI đã điền mây (đã fill nan)\n",
"plt.imshow(fill_nan_ndvi.isel(time=6))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0e10d6e6",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"## tính ndvi theo tháng\n",
"average_ndvi = fill_nan_ndvi.resample(time=\"1M\").mean().persist()\n",
"progress(average_ndvi)\n",
"\n",
"# compute average_ndvi\n",
"average_ndvi = average_ndvi.compute()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7b3b7086",
"metadata": {},
"outputs": [],
"source": [
"#Load dữ liệu ảnh Sentinel 1\n",
"dsvh, dsvv = load_data_sen1(dc, date_range, coordinates)\n",
"average_vv = calculate_average(dsvv, time_pattern='1M')\n",
"average_vh = calculate_average(dsvh, time_pattern='1M')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6d3f9ab1",
"metadata": {},
"outputs": [],
"source": [
"## cấu hình bộ dữ liệu điểm huấn luyện mô hình (train file)\n",
"train_path = \"train/ST_training data_updated_1130points_new.shp\" # đường dẫn shp file train\n",
"\n",
"## load dữ liệu điểm huấn luyện mô hình (train file)\n",
"train = load_train_data(train_path)\n",
"train.head()\n",
"\n",
"# cấu hình nhãn dữ liệu \n",
"label_mapping = {\n",
" \"Lua tom\": \"0\",\n",
" \"Lua\": \"1\",\n",
" \"CHN\": \"2\",\n",
" \"CLN\": \"3\",\n",
" \"TS\": \"4\",\n",
" \"Song\": \"5\",\n",
" \"Dat xay dung\": \"6\",\n",
" \"Rung\": \"7\",\n",
"}\n",
"\n",
"# xây dựng tập dữ liệu (dataset) chứa dữ liệu VH, VV, NDVI\n",
"datasets = get_data_sen1_and_sen2(train, average_ndvi, average_vh, average_vv)\n",
"\n",
"# chia tập dữ liệu thành các phần theo tỉ lệ 80(80-20)-20 tương ứng với tập train, validate, test\n",
"X_train, X_val, X_test, y_train, y_val, y_test = split_train_data(\n",
" train, label_mapping, datasets\n",
")"
]
},
{
"cell_type": "markdown",
"id": "c0188e1b",
"metadata": {},
"source": [
"## Huấn luyện mô hình CNN với PyTorch"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ba38ce6a",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"# Huấn luyện mô hình CNN với PyTorch\n",
"print(\"🔷 Bắt đầu huấn luyện CNN model với PyTorch...\\n\")\n",
"\n",
"cnn_model, history, scaler = train_cnn_pytorch(\n",
" X_train, X_val, X_test, \n",
" y_train, y_val, y_test,\n",
" num_classes=8,\n",
" epochs=100,\n",
" batch_size=32,\n",
" learning_rate=1e-3,\n",
" device=device,\n",
" verbose=True\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fd19df06",
"metadata": {},
"outputs": [],
"source": [
"# Vẽ đồ thị huấn luyện\n",
"plot_pytorch_training_history(history)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e16d8c20",
"metadata": {},
"outputs": [],
"source": [
"# Lưu mô hình CNN PyTorch\n",
"save_pytorch_model(cnn_model, scaler, model_name=\"model_cnn_pytorch.pth\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "adb17862",
"metadata": {},
"outputs": [],
"source": [
"# Hiển thị model architecture\n",
"print(\"\\n📐 Model Architecture:\")\n",
"print(cnn_model)\n",
"\n",
"# Đếm số parameters\n",
"total_params = sum(p.numel() for p in cnn_model.parameters())\n",
"trainable_params = sum(p.numel() for p in cnn_model.parameters() if p.requires_grad)\n",
"print(f\"\\n📊 Model Parameters:\")\n",
"print(f\" Total parameters: {total_params:,}\")\n",
"print(f\" Trainable parameters: {trainable_params:,}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b4963d6f",
"metadata": {},
"outputs": [],
"source": [
"# đóng client, cluster\n",
"client.close()\n",
"cluster.close()"
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+337
View File
@@ -0,0 +1,337 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "ac069784-fbb6-48c8-a547-11b96cade97b",
"metadata": {
"collapsed": true,
"jupyter": {
"outputs_hidden": true,
"source_hidden": true
},
"scrolled": true
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/tmp/ipykernel_218/1243805045.py:22: UserWarning: pandas only supports SQLAlchemy connectable (engine/connection) or database string URI or sqlite3 DBAPI2 connection. Other DBAPI2 objects are not tested. Please consider using SQLAlchemy.\n",
" df = pd.read_sql(sql, conn)\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
" product_name total_images\n",
"0 s2_l2a 2074896\n",
"1 sentinel_2_c1_l2a 1137446\n",
"2 landsat7_c2l2_sr 169065\n",
"3 landsat7_c2l2_st 168410\n",
"4 landsat8_c2l2_sr 122846\n",
"5 landsat8_c2l2_st 120778\n",
"6 landsat5_c2l2_sr 118583\n",
"7 landsat5_c2l2_st 118406\n",
"8 landsat8_c2l1 111513\n",
"9 nasa_aqua_l2_oc 37232\n",
"10 landsat9_c2l2_sr 30214\n",
"11 landsat9_c2l2_st 29709\n",
"12 landsat9_c2l1 18875\n",
"13 nasa_aqua_l2_sst 12070\n",
"14 ga_ls_mangrove_cover_cyear_3 5580\n",
"15 sentinel1_grd_gamma0_10m_unsmooth 5488\n",
"16 sentinel1_grd_gamma0_20m 4260\n",
"17 copernicus_dem_30 1334\n",
"18 global_mangrove_soc_2020_30m 784\n",
"19 global_mangrove_soc_2000_30m 781\n",
"20 global_mangrove_canopy_height_2015_12m 761\n",
"21 sentinel1_grd_gamma0_10m 291\n",
"22 lpdaac_nasadem 264\n",
"23 cci_biomass_annual_v51 248\n",
"24 esa_worldcover_2020 184\n",
"25 esa_worldcover_2021 184\n",
"26 global_tidal_marsh_soc_30m 41\n",
"27 global_tidal_marsh_distribution_2020_10m 35\n",
"28 copernicus_dem_fiji 26\n",
"29 lpdaac_mod11a1v061_lste 2\n",
"30 global_mangrove_soc_2000_100m 1\n"
]
}
],
"source": [
"import psycopg2\n",
"import os\n",
"import pandas as pd\n",
"\n",
"# Kết nối (như cũ)\n",
"conn = psycopg2.connect(\n",
" host=os.environ.get('DB_HOSTNAME'),\n",
" user=os.environ.get('DB_USERNAME'),\n",
" password=os.environ.get('DB_PASSWORD'),\n",
" dbname=os.environ.get('DB_DATABASE')\n",
")\n",
"\n",
"# Đếm số lượng ảnh theo từng loại sản phẩm\n",
"sql = \"\"\"\n",
"SELECT t.name as product_name, count(*) as total_images\n",
"FROM agdc.dataset d\n",
"JOIN agdc.dataset_type t ON d.dataset_type_ref = t.id\n",
"GROUP BY t.name\n",
"ORDER BY total_images DESC;\n",
"\"\"\"\n",
"\n",
"df = pd.read_sql(sql, conn)\n",
"print(df)\n",
"conn.close()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a0ae4051-3bfd-49fd-83fe-4c31c74338e2",
"metadata": {},
"outputs": [],
"source": [
"import psycopg2\n",
"import os\n",
"\n",
"# 1. Lấy thông tin kết nối\n",
"db_host = os.environ.get('DB_HOSTNAME')\n",
"db_user = os.environ.get('DB_USERNAME')\n",
"db_pass = os.environ.get('DB_PASSWORD')\n",
"db_name = os.environ.get('DB_DATABASE')\n",
"\n",
"# 2. Danh sách các bảng cần lấy\n",
"tables = [\n",
" 'agdc.metadata_type',\n",
" 'agdc.dataset_type',\n",
" 'agdc.dataset_location',\n",
" 'agdc.dataset'\n",
"]\n",
"\n",
"print(\"Đang kết nối trực tiếp tới PostgreSQL...\")\n",
"\n",
"try:\n",
" # Kết nối trực tiếp (Bỏ qua Pandas/SQLAlchemy)\n",
" conn = psycopg2.connect(\n",
" host=db_host,\n",
" user=db_user,\n",
" password=db_pass,\n",
" dbname=db_name\n",
" )\n",
" cur = conn.cursor()\n",
" print(\"Kết nối thành công!\\n\")\n",
"\n",
" for table_name in tables:\n",
" print(f\"--> Đang xuất bảng: {table_name}\")\n",
" \n",
" # Tên file CSV đầu ra\n",
" file_name = table_name.split('.')[1] + \".csv\"\n",
" \n",
" # Sử dụng lệnh COPY chuyên dụng của Postgres (Cực nhanh và chuẩn)\n",
" # SQL: COPY (SELECT * FROM table) TO STDOUT WITH CSV HEADER\n",
" sql = f\"COPY (SELECT * FROM {table_name}) TO STDOUT WITH CSV HEADER\"\n",
" \n",
" with open(file_name, 'w') as f:\n",
" cur.copy_expert(sql, f)\n",
" \n",
" print(f\" Đã lưu xong: {file_name}\")\n",
"\n",
" cur.close()\n",
" conn.close()\n",
" print(\"\\nHOÀN TẤT! Bạn hãy tải 4 file CSV về máy.\")\n",
"\n",
"except Exception as e:\n",
" print(f\"\\nCÓ LỖI XẢY RA: {e}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d139879e-c350-4986-a2e6-0d59099eb770",
"metadata": {
"collapsed": true,
"jupyter": {
"outputs_hidden": true,
"source_hidden": true
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Đang kết nối...\n",
"Kết nối thành công! Đang ước lượng nhanh...\n",
"\n",
"--- BẢNG DỰ TÍNH DUNG LƯỢNG (ƯỚC LƯỢNG) ---\n",
"NĂM | SỐ ẢNH (ƯỚC TÍNH) | SIZE (CSV) \n",
"------------------------------------------------------------\n",
"2020 | ~ 21,448 | ~ 83.78 MB\n",
"2021 | ~ 21,448 | ~ 83.78 MB\n",
"2022 | ~ 21,448 | ~ 83.78 MB\n",
"2023 | ~ 21,448 | ~ 83.78 MB\n",
"2024 | ~ 21,448 | ~ 83.78 MB\n",
"------------------------------------------------------------\n",
"TỔNG CỘNG : ~ 107,240 ảnh (Khoảng 418.91 MB)\n",
"------------------------------------------------------------\n",
"(Lưu ý: Số liệu này là ước tính của Database, độ chính xác khoảng 80-90%)\n"
]
},
{
"name": "stdin",
"output_type": "stream",
"text": [
"\n",
"Bạn có muốn bắt đầu tải không? (y/n): y\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
">>> Đang tải bảng định nghĩa...\n",
"\n",
">>> Đang tải dữ liệu chính...\n",
"--> Năm 2020...\n"
]
}
],
"source": [
"import psycopg2\n",
"import os\n",
"import re # Thư viện xử lý chuỗi để lấy số liệu\n",
"\n",
"# --- CẤU HÌNH ---\n",
"YEARS = [2020, 2021, 2022, 2023, 2024]\n",
"AVG_ROW_SIZE_KB = 4 \n",
"\n",
"db_host = os.environ.get('DB_HOSTNAME')\n",
"db_user = os.environ.get('DB_USERNAME')\n",
"db_pass = os.environ.get('DB_PASSWORD')\n",
"db_name = os.environ.get('DB_DATABASE')\n",
"\n",
"print(\"Đang kết nối...\")\n",
"\n",
"try:\n",
" conn = psycopg2.connect(\n",
" host=db_host, user=db_user, password=db_pass, dbname=db_name\n",
" )\n",
" cur = conn.cursor()\n",
" print(\"Kết nối thành công! Đang ước lượng nhanh...\\n\")\n",
"\n",
" # ==========================================\n",
" # BƯỚC 1: ƯỚC LƯỢNG SIÊU TỐC (INSTANT ESTIMATE)\n",
" # ==========================================\n",
" print(\"--- BẢNG DỰ TÍNH DUNG LƯỢNG (ƯỚC LƯỢNG) ---\")\n",
" print(f\"{'NĂM':<10} | {'SỐ ẢNH (ƯỚC TÍNH)':<20} | {'SIZE (CSV)':<20}\")\n",
" print(\"-\" * 60)\n",
"\n",
" total_est_rows = 0\n",
"\n",
" for year in YEARS:\n",
" # Mẹo: Dùng EXPLAIN để lấy số liệu ước tính từ Query Planner\n",
" # Nó sẽ trả về chuỗi kiểu: \"Seq Scan on dataset ... (rows=12345 ...)\"\n",
" sql_estimate = f\"\"\"\n",
" EXPLAIN SELECT 1 FROM agdc.dataset\n",
" WHERE (metadata->'properties'->>'datetime')::timestamp >= '{year}-01-01'\n",
" AND (metadata->'properties'->>'datetime')::timestamp <= '{year}-12-31 23:59:59'\n",
" \"\"\"\n",
" cur.execute(sql_estimate)\n",
" explain_result = cur.fetchone()[0] # Lấy dòng đầu tiên của kết quả EXPLAIN\n",
" \n",
" # Dùng Regex để bắt lấy con số sau chữ \"rows=\"\n",
" match = re.search(r\"rows=(\\d+)\", explain_result)\n",
" if match:\n",
" count = int(match.group(1))\n",
" else:\n",
" count = 0 # Không bắt được số\n",
" \n",
" total_est_rows += count\n",
" est_size_mb = (count * AVG_ROW_SIZE_KB) / 1024\n",
" \n",
" print(f\"{year:<10} | ~ {count:<18,} | ~ {est_size_mb:.2f} MB\")\n",
"\n",
" print(\"-\" * 60)\n",
" print(f\"TỔNG CỘNG : ~ {total_est_rows:,} ảnh (Khoảng {(total_est_rows * AVG_ROW_SIZE_KB)/1024:.2f} MB)\")\n",
" print(\"-\" * 60)\n",
" print(\"(Lưu ý: Số liệu này là ước tính của Database, độ chính xác khoảng 80-90%)\")\n",
"\n",
" # ==========================================\n",
" # QUYẾT ĐỊNH TẢI\n",
" # ==========================================\n",
" check = input(\"\\nBạn có muốn bắt đầu tải không? (y/n): \")\n",
" if check.lower() != 'y':\n",
" print(\"Đã hủy.\")\n",
" exit()\n",
"\n",
" # ==========================================\n",
" # BƯỚC 2 & 3: TẢI DỮ LIỆU (Giữ nguyên logic cũ)\n",
" # ==========================================\n",
" # ... (Phần code tải small_tables và tải dữ liệu chính giữ nguyên như cũ) ...\n",
" # Để code gọn, mình viết tiếp phần tải ở dưới đây:\n",
" \n",
" # 2. Tải bảng nhỏ\n",
" print(\"\\n>>> Đang tải bảng định nghĩa...\")\n",
" for tb in ['agdc.metadata_type', 'agdc.dataset_type']:\n",
" f_name = tb.split('.')[1] + \".csv\"\n",
" with open(f_name, 'w') as f:\n",
" cur.copy_expert(f\"COPY (SELECT * FROM {tb}) TO STDOUT WITH CSV HEADER\", f)\n",
" \n",
" # 3. Tải dữ liệu chính\n",
" print(\"\\n>>> Đang tải dữ liệu chính...\")\n",
" for year in YEARS:\n",
" print(f\"--> Năm {year}...\")\n",
" \n",
" # Dataset\n",
" f_ds = f\"dataset_{year}.csv\"\n",
" sql_ds = f\"\"\"\n",
" COPY (SELECT * FROM agdc.dataset \n",
" WHERE (metadata->'properties'->>'datetime')::timestamp >= '{year}-01-01' \n",
" AND (metadata->'properties'->>'datetime')::timestamp <= '{year}-12-31 23:59:59'\n",
" ) TO STDOUT WITH CSV HEADER\"\"\"\n",
" with open(f_ds, 'w') as f: cur.copy_expert(sql_ds, f)\n",
" \n",
" # Location\n",
" f_loc = f\"dataset_location_{year}.csv\"\n",
" sql_loc = f\"\"\"\n",
" COPY (SELECT l.* FROM agdc.dataset_location l JOIN agdc.dataset d ON l.dataset_ref = d.id\n",
" WHERE (d.metadata->'properties'->>'datetime')::timestamp >= '{year}-01-01' \n",
" AND (d.metadata->'properties'->>'datetime')::timestamp <= '{year}-12-31 23:59:59'\n",
" ) TO STDOUT WITH CSV HEADER\"\"\"\n",
" with open(f_loc, 'w') as f: cur.copy_expert(sql_loc, f)\n",
" \n",
" print(\"\\nHOÀN TẤT TOÀN BỘ!\")\n",
" cur.close()\n",
" conn.close()\n",
"\n",
"except Exception as e:\n",
" print(f\"\\nCÓ LỖI: {e}\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@@ -0,0 +1,6 @@
{
"cells": [],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 5
}
File diff suppressed because one or more lines are too long