الذكاء الاصطناعي والاستشعار عن بعد
شهد مجال الاستشعار عن بُعد تطورًا ملحوظًا بفضل دمج تقنيات الذكاء الاصطناعي الحديثة، لا سيما خوارزميات التعلم العميق مثل الشبكات العصبية الالتفافية (CNN) والشبكات التكرارية (RNN)، التي أثبتت فعاليتها في تحليل الصور الفضائية واستخلاص المعلومات منها بدقة عالية.
تُستخدم هذه التقنيات في تطبيقات متعددة مثل تصنيف استخدامات الأراضي، واكتشاف التغيرات البيئية، ورصد الكوارث الطبيعية كحرائق الغابات والفيضانات. ومن الأمثلة البارزة على الاستخدامات الحديثة، تقنيات تحسين دقة الصور (Super-Resolution) المعتمدة على الذكاء الاصطناعي، والتي تُستخدم لرفع جودة الصور منخفضة الدقة الملتقطة من الأقمار الصناعية، مما يُمكّن من الحصول على تفاصيل أدق دون الحاجة إلى أجهزة تصوير أعلى تكلفة.
كما بدأت نماذج مثل Vision Transformers (ViTs) وGenerative Adversarial Networks (GANs) تُستخدم لتحسين نتائج التصنيف، وتوليد بيانات اصطناعية للتدريب، وتسهيل عملية التفسير الآلي للصور. هذه الأساليب تُسهم في تعزيز دقة المراقبة البيئية، ودعم اتخاذ القرار في مجالات مثل الزراعة الذكية، وإدارة الموارد الطبيعية، والتخطيط الحضري.

بيانات الاستشعار عن بعد
تعتمد تقنيات الذكاء الاصطناعي في مجال الاستشعار عن بُعد بشكل كبير على توفر مجموعات بيانات عالية الجودة ومتنوعة. من أبرز هذه المجموعات UC-Merced، التي تُعد مرجعًا شهيرًا لتصنيف الصور الجوية بدقة مكانية تبلغ 0.3 متر، وتغطي 21 فئة مختلفة.
هناك أيضًا مجموعة OPTIMAL-31، التي تحتوي على صور عالية الجودة لتقييم النماذج في سياقات معقدة. من المجموعات الأخرى المهمة: EuroSAT، والتي تستند إلى صور القمر الصناعي Sentinel-2 وتغطي 10 فئات استخدام أراضٍ أوروبية، وBigEarthNet، وهي من أكبر مجموعات البيانات متعددة الأطياف وتعتمد أيضًا على Sentinel-2، وتُستخدم في تصنيف الصور والتعلم متعدد التسمية.
كذلك تُعد NWPU-RESISC45 من أكثر المجموعات شيوعًا، حيث تضم 31,500 صورة تغطي 45 فئة مختلفة بدقة 0.2 إلى 0.3 متر.

بالنسبة لمصادر الصور الفضائية، هناك تنوع واسع في أنظمة الاستشعار. يوفر Sentinel-1 بيانات رادارية (SAR) بدقة مكانية تتراوح بين 5 إلى 20 مترًا حسب النطاق والمنتج، ويُستخدم لرصد التغيرات الأرضية والتضاريس في جميع الظروف الجوية والليلية. أما Sentinel-2، فيوفر صورًا متعددة الأطياف بدقة مكانية تصل إلى 10 أمتار في بعض الحزم، ويُستخدم بكثرة في تطبيقات الزراعة، واكتشاف الغطاء النباتي، والتغيرات البيئية.
من الأقمار الصناعية التجارية المتقدمة، هناك WorldView-3، الذي يوفر صورًا بدقة مكانية تصل إلى 30 سنتيمترًا (بانكروماتيكية)، ويشمل نطاقات متعددة الطيف وفائقة الطيف، مما يجعله مناسبًا للتطبيقات الدقيقة مثل مراقبة البنية التحتية وتحليل المناطق الحضرية. كما يوجد أقمار صناعية أخرى مثل Pleiades (بدقة تصل إلى 50 سم)، وPlanetScope التابع لشركة Planet Labs، والذي يوفر صورًا يومية بدقة 3-5 أمتار، مما يجعله مناسبًا للمراقبة المستمرة وتطبيقات الزراعة الدقيقة.

توفر هذه المجموعات والأنظمة البيئية المتكاملة بيئة مثالية لتطوير وتدريب نماذج الذكاء الاصطناعي على مجموعة واسعة من المهام، من تصنيف الغطاء الأرضي إلى اكتشاف التغيرات والتنبؤ بالكوارث.
تقنيات حديثة
في السنوات الأخيرة، شهد مجال الاستشعار عن بُعد تطورات ملحوظة بفضل دمج تقنيات الذكاء الاصطناعي المتقدمة، مما أدى إلى تحسين دقة وكفاءة تحليل الصور الفضائية. أحد هذه التطورات يتمثل في استخدام نماذج المحولات (Transformers) لمعالجة المهام المعقدة مثل تصنيف المشاهد الطبيعية، حيث تم تطوير نموذج يجمع بين المحولات وتقنيات التعلم العميق لتحقيق دقة تصنيف عالية عبر مجموعات بيانات متعددة.


في مجال تحسين دقة الصور (Super-Resolution)، تم تقديم نموذج EDiffSR الذي يعتمد على نموذج الانتشار الاحتمالي (Diffusion Probabilistic Model) لتحسين جودة الصور الفضائية منخفضة الدقة. يتميز هذا النموذج بقدرته على إنتاج صور ذات جودة بصرية عالية مع تقليل التعقيد الحسابي.

علاوة على ذلك، تم تطوير نموذج MA-GAN (شبكة توليدية تنافسية متعددة الانتباه) لتحسين دقة الصور في الاستشعار عن بُعد. يستخدم هذا النموذج كتلتي بناء رئيسيتين: كتلة التلافيف الهرمية في الكتلة الكثيفة المتبقية (PCRDB)، وكتلة الترقية المعتمدة على الانتباه (AUP). تعمل هاتان الكتلتان معًا لتحسين جودة الصور المنتجة، وقد أظهر النموذج تفوقًا على عدة طرق حديثة في مجموعة بيانات مشاهد الاستشعار عن بُعد.

هذه الابتكارات تسلط الضوء على الاتجاه المتزايد نحو استخدام تقنيات الذكاء الاصطناعي المتقدمة، مثل المحولات والشبكات التوليدية التنافسية، لتعزيز قدرات الاستشعار عن بُعد، مما يفتح آفاقًا جديدة لتحليل أكثر دقة وفعالية للبيانات الفضائية.
التطبيق العملي الأول
سنتعرف على كيفية بناء نموذج ذكاء اصطناعي بسيط يقوم بعمل تقسيم الصور باستخدام صور ملتقطة من طائرة درون لمناطق تعرضت للفيضانات.
سنستخدم مكتبة المبنية على PyTorch لتنفيذ هذا المشروع.
البيانات المستخدمة مأخوذة من مجموعة بيانات ، والتي تحتوي على صور وفيه التوصيف (Masks) لها. يمكنك تحميل البيانات المستخدمة في هذا الدرس من خلال الزر أدناه
ويمكنك تجربة الكود البرمجي على منصة Google Colab بشكل مجاني، مرفق بنهاية الدرس ملف معد مسبقاً فيه جميع الشفرات.
ما هي مهمة التقسيم (Segmentation) في الرؤية الحاسوبية؟
في عالم الرؤية الحاسوبية، واحدة من أهم المهام هي تقسيم الصور ، إذ تهدف هذه المهمة إلى تحديد كل بكسل في الصورة إلى أي فئة ينتمي. بخلاف تصنيف الصور (Image Classification) الذي يعطي فئة واحدة للصورة ككل، أو حتى الكشف عن الأجسام (Object Detection) الذي يرسم مربعات حول الأجسام، فإن التقسيم يُعطي مستوى تفصيليًا لكل كائن في الصورة.
كيف يعمل التقسيم عمليًا؟
عند إدخال صورة إلى نموذج تقسيم، يقوم النموذج بإنتاج صورة جديدة (تُسمى: mask أو التوصيف). هذه الصورة الجديدة تحتوي على أرقام تمثل فئات مختلفة (مثلاً: ماء = 5، مبنى = 1، شجرة = 6...). بهذا الشكل، يصبح لدينا لكل بكسل في الصورة الأصلية توصيف دقيق لهويته.
علاقة التقسيم بالاستشعار عن بُعد
في سياق الاستشعار عن بُعد ، تكون الصور عادة مأخوذة من الأقمار الصناعية أو الطائرات بدون طيار (درون). وتكمن القوة في استخدام التقسيم هنا في القدرة على تحليل الأوضاع على الأرض بشكل دقيق:
هل هذا المبنى غمره الماء؟
هل الطريق سالك أم مغمور؟
هل هذه المنطقة مغطاة بالعشب أم بالماء؟
وهكذا.
ماذا سنفعل في هذا الدرس؟
في هذا الدليل التطبيقي، نستخدم نموذج ذكاء اصطناعي مبني على معمارية لتطبيق مهمة تقسيم متعددة الفئات على مجموعة بيانات حقيقية تُسمى . هذه البيانات تم جمعها من طائرة درون فوق مناطق تعرضت للفيضانات، وتشمل فئات متعددة مثل:
مباني مغمورة وغير مغمورة
طرق مغمورة وغير مغمورة
ماء، شجر، عشب، برك، مركبات، وغيرها ...
سنقوم بــــــــــ
تجهيز البيانات وتوصيفها.
تدريب نموذج ذكاء اصطناعي لفهم هذه الفئات.
تقييم النموذج وعرض نتائجه بصريًا.
لماذا هذا مهم؟
مهمة مثل هذه يمكن أن تُستخدم في حالات الطوارئ لإرسال فرق إنقاذ إلى الأماكن المتضررة، أو تقييم الأضرار بعد الكوارث الطبيعية، أو حتى التخطيط العمراني الذكي.
المتطلبات الأساسية
قبل أن نبدأ، تأكد من تثبيت المكتبة التالية:
# Prerequisites / requirements:
!pip install segmentation-models-pytorch
استيراد المكتبات اللازمة:
#import needed libraries:
import torch
import torchvision
from PIL import Image
import torchvision.transforms as transforms
from torch.utils.data import Dataset, DataLoader
import numpy as np
import matplotlib.pyplot as plt
import segmentation_models_pytorch as smp
import os
from matplotlib.colors import ListedColormap
التأكد من الجهاز المستخدم (GPU أو CPU):
#Check mounted device type:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
إذا كنت تستخدم Google Colab ولديك GPU مفعّل، يمكنك فحصه باستخدام الأمر التالي:
#Check GPU details if found:
!nvidia-smi
تحميل بيانات FloodNet
أولاً، نحتاج إلى وظيفة تساعدنا في جمع كافة الملفات داخل مجلد معين:
#Function to extract list of dataset files from location in storage mounted in Colab/GDrive:
def list_files_in_directory(directory):
file_paths = []
for root, dirs, files in os.walk(directory):
for file in files:
file_paths.append(os.path.join(root, file))
return file_paths
تحديد مسارات البيانات للتدريب والاختبار:
#Define paths to dataset location (train & test):
train_image_path = "/content/drive/MyDrive/datasets/FloodNet-Supervised_v1.0/train/train-org-img"
train_label_path = "/content/drive/MyDrive/datasets/FloodNet-Supervised_v1.0/train/train-label-img"
test_image_path = "/content/drive/MyDrive/datasets/FloodNet-Supervised_v1.0/test/test-org-img"
test_label_path = "/content/drive/MyDrive/datasets/FloodNet-Supervised_v1.0/test/test-label-img"
#extract list of images and masks from defined paths:
train_image_list = list_files_in_directory(train_image_path)
train_label_list = list_files_in_directory(train_label_path)
test_image_list = list_files_in_directory(test_image_path)
test_label_list = list_files_in_directory(test_label_path)
تعريف كلاس Dataset مخصص
الآن سنقوم بإنشاء كائن Dataset مخصص لـــ FloodNet:
# Custom defined PyTorch ImageFolder dataset
class FloodNetDataset(Dataset):
"""FloodNet dataset."""
def __init__(self, objects_list, labels_list, transform=None):
"""
Args:
objects_list (list): list of image paths to be used with the dataloader.
labels_list (list): list of segmentation image labels paths to be used with the dataloader.
transform (callable, optional): Optional transform to be applied
on a sample.
"""
self.objects_list = objects_list
self.labels_list = labels_list
self.transform = transform
def __len__(self):
return len(self.objects_list)
def __getitem__(self, idx):
image = Image.open(self.objects_list[idx])
label = Image.open(self.labels_list[idx])
if self.transform:
image = self.transform(image)
label = self.transform(label)
return image, label
تجهيز البيانات وتحميلها في PyTorch
التحويلات (Transforms)
نحتاج إلى تحويل الصور لتكون مناسبة للتدريب. سنقوم بتحويل الصور من PIL إلى Tensor، ثم نعيد تحجيمها إلى أبعاد موحدة (64×64).
# Transform:
transform = transforms.Compose([
transforms.PILToTensor(),
transforms.Resize((64, 64))
])
تعريف مجموعات البيانات
الآن نستخدم الكلاس FloodNetDataset الذي أنشأناه سابقًا لقراءة الصور وملفات التوصيف من المسارات:
# Define datasets:
train_dataset = FloodNetDataset(objects_list=train_image_list, labels_list=train_label_list, transform=transform)
test_dataset = FloodNetDataset(objects_list=test_image_list, labels_list=test_label_list, transform=transform)
تحميل البيانات باستخدام DataLoader
DataLoader يسهل علينا التعامل مع البيانات أثناء التدريب، مثل تقسيمها إلى دفعات (Batches):
# Define Dataloader:
batch_size = 16
floodnet_dataloader_train = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=0)
floodnet_dataloader_test = DataLoader(test_dataset, batch_size=batch_size, shuffle=True, num_workers=0)
إعداد الألوان والتسميات للفئات (للعرض)
لكي نستطيع عرض التوصيفات (Masks) بشكل مرئي ومفهوم، نحتاج إلى تعيين لون لكل فئة (class) من الفئات الموجودة في FloodNet. يتم ذلك باستخدام قائمة ألوان hex وقائمة بأسماء الفئات.
# QoL Setup:
# Specify colors for each class using hex codes
class_colors = [
"#000000", # Background
"#ff001e", # Building-Flooded
"#ff8f9c", # Building-Non-Flooded
"#56a100", # Road-Flooded
"#b3b3b3", # Road-non-Flooded
"#3bfcff", # Water
"#0004ff", # Tree
"#ff00ff", # Vehicle
"#f2ff00", # Pool
"#00ff1e" # Grass
]
class_names = [
"Background", "Building-Flooded", "Building-Non-Flooded", "Road-Flooded",
"Road-non-Flooded", "Water", "Tree", "Vechile",
"Pool", "Grass"
]
colormap = ListedColormap(class_colors)
عرض عينة من البيانات
قبل ما نبدأ التدريب، خلينا نعرض صورة وحدة مع توصيفها لنتأكد من تنسيق البيانات:
#Show sample of dataset:
sample = next(iter(floodnet_dataloader_train))
plt.figure(figsize=(20, 5))
plt.subplot(1,2,1)
plt.imshow(sample[0][0].permute(1, 2, 0)) # for visualization we have to transpose back to HWC
plt.subplot(1,2,2)
plt.imshow(sample[1][0].permute(1, 2, 0), cmap=colormap) # for visualization we have to remove 3rd dimension of mask
cbar = plt.colorbar(ticks=range(10), format='%1i', cmap=colormap)
cbar.ax.set_yticklabels(class_names)
plt.show()
تدريب نموذج UNet لتقسيم الصور
تعريف دالة الدقة (Accuracy)
نستخدم دالة بسيطة لحساب دقة النموذج من خلال مقارنة التوقعات بالملصقات الحقيقية (Masks):
#Define Accuracy metric:
def acc(label, predicted):
seg_acc = (label.cpu() == torch.argmax(predicted, axis=1).cpu()).sum() / torch.numel(label.cpu())
return seg_acc
إعداد نموذج UNet من مكتبة segmentation_models_pytorch
N_EPOCHS = 3
model = smp.Unet(
encoder_name="resnet34", # choose encoder, e.g. mobilenet_v2 or efficientnet-b7
encoder_weights="imagenet", # use `imagenet` pre-trained weights for encoder initialization
in_channels=3, # model input channels (1 for gray-scale images, 3 for RGB, etc.)
classes=10, # model output channels (number of classes in your dataset)
)
if str(device) == 'cuda':
print("model is cuda")
model = model.cuda()
تهيئة دالة الخسارة، المحسن، وجدولة معدل التعلم
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
criterion = smp.losses.DiceLoss('multiclass', from_logits=True)
lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=1, gamma=0.5)
min_loss = torch.tensor(float('inf'))
os.makedirs('./saved_models', exist_ok=True)
تدريب وتقييم النموذج
نبدأ الآن بحلقة التدريب على عدة عصور (Epochs):
import copy
plot_losses = []
scheduler_counter = 0
best_model = None
for epoch in range(N_EPOCHS):
# training
model.train()
loss_list = []
acc_list = []
for batch_i, (x, y) in enumerate(floodnet_dataloader_train):
pred_mask = model(x.to(device).float())
loss = criterion(pred_mask, y.to(device).type(torch.int64))
optimizer.zero_grad()
loss.backward()
optimizer.step()
loss_list.append(loss.cpu().detach().numpy())
acc_list.append(acc(y,pred_mask).numpy())
print(
"\r[Epoch %d/%d] [Batch %d/%d] [Loss: %f (%f)]"
% (
epoch,
N_EPOCHS,
batch_i,
len(floodnet_dataloader_train),
loss.cpu().detach().numpy(),
np.mean(loss_list),
)
)
scheduler_counter += 1
# testing
model.eval()
val_loss_list = []
val_acc_list = []
for batch_i, (x, y) in enumerate(floodnet_dataloader_test):
with torch.no_grad():
pred_mask = model(x.to(device).float())
val_loss = criterion(pred_mask, y.to(device).type(torch.int64))
val_loss_list.append(val_loss.cpu().detach().numpy())
val_acc_list.append(acc(y,pred_mask).numpy())
print(' epoch {} - loss : {:.5f} - acc : {:.2f} - val loss : {:.5f} - val acc : {:.2f}'.format(epoch,
np.mean(loss_list),
np.mean(acc_list),
np.mean(val_loss_list),
np.mean(val_acc_list)))
plot_losses.append([epoch, np.mean(loss_list), np.mean(val_loss_list)])
compare_loss = np.mean(val_loss_list)
is_best = compare_loss < min_loss
if is_best == True:
scheduler_counter = 0
min_loss = min(compare_loss, min_loss)
torch.save(model.state_dict(), './saved_models/unet_epoch_{}_{:.5f}.pt'.format(epoch,np.mean(val_loss_list)))
best_model = copy.deepcopy(model)
if scheduler_counter > 5:
lr_scheduler.step()
print(f"lowering learning rate to {optimizer.param_groups[0]['lr']}")
scheduler_counter = 0
رسم منحنى الخسارة:
# plot loss
plot_losses = np.array(plot_losses)
plt.figure(figsize=(12,8))
plt.plot(plot_losses[:,0], plot_losses[:,1], color='b', linewidth=4)
plt.plot(plot_losses[:,0], plot_losses[:,2], color='r', linewidth=4)
plt.title("Crossentropy", fontsize=20)
plt.xlabel('epoch',fontsize=20)
plt.ylabel('loss',fontsize=20)
plt.grid()
plt.legend(['training', 'validation']) # using a named size
plt.savefig('loss_plots.png')
عرض عينات من نتائج النموذج
عرض صور من بيانات الاختبار مع نتائج النموذج:
# lets look at some samples
floodnet_dataloader_sample = DataLoader(test_dataset, batch_size=1, shuffle=True, num_workers=0)
num_samples = 5
# Specify colors for each class using hex codes
class_colors = [
"#000000", "#ff001e", "#ff8f9c", "#56a100",
"#b3b3b3", "#3bfcff", "#0004ff", "#ff00ff",
"#f2ff00", "#00ff1e"
]
class_names = [
'Background', 'Building-Flooded', 'Building-Non-Flooded', 'Road-Flooded',
'Road-non-Flooded', 'Water', 'Tree', 'Vechile',
'Pool', 'Grass'
]
# Create a colormap with the given colors
colormap = ListedColormap(class_colors)
for i in range(num_samples):
plt.figure(figsize=(20, 5))
sample = next(iter(floodnet_dataloader_sample))
model.eval()
output = model(sample[0].float().to(device)).detach().type(torch.int64)
plt.subplot(1,3,1)
plt.title("Input Image")
plt.imshow(sample[0][0].permute(1, 2, 0)) # for visualization we have to transpose back to HWC
plt.subplot(1,3,2)
plt.title("Mask Ground Truth")
plt.imshow(sample[1].squeeze(), cmap=colormap) # for visualization we have to remove 3rd dimension of mask
plt.subplot(1,3,3)
plt.title("Model's mask")
plt.imshow(output.argmax(dim=1).squeeze().cpu(), cmap=colormap) # for visualization we have to remove 3rd dimension of mask
cbar = plt.colorbar(ticks=range(10), format='%1i', cmap=colormap)
cbar.ax.set_yticklabels(class_names)
plt.show()
🎉 ختامًا
في هذا الدرس، تعلمنا:
-
كيفية التعامل مع بيانات الاستشعار عن بعد المصنفة مثل FloodNet.
-
تجهيز بيانات الصور والتوصيفات وتحميلها في PyTorch.
-
استخدام نموذج UNet من مكتبة segmentation_models_pytorch.
-
تدريب النموذج، حساب الخسارة والدقة، وحفظ أفضل نسخة منه.
-
عرض نتائج النموذج بصريًا على صور حقيقية من بيانات الاختبار.