基于Pytorch深度學習的衛(wèi)星圖像分類(Kaggle比賽項目實戰(zhàn))
一、主題介紹:
在Pytorch框架下,實戰(zhàn)Kaggle比賽:衛(wèi)星圖像分類(Satellite Remote Sensing Image -RSI-CB256),利用Pycharm調(diào)試運行代碼。
二、環(huán)境配置要求:
python版本:3.7
pycharm版本:2020.3.5
pytorch版本:1.12.1
numpy版本:1.21.6
matplotlib版本:3.2.2
tqdm版本:4.65.0

把需要的包下載好,在你的有cuda或者cpu的pytorch環(huán)境中下載
三、主要步驟與涉及知識:
3.1 主要步驟:
- 啟用pycharm,新建實驗項目,選擇編譯環(huán)境
- 下載數(shù)據(jù)集
- 預處理數(shù)據(jù)集
- 定義網(wǎng)絡
- 模型訓練及預測
3.2涉及知識:常見Anaconda終端命令的使用
1.Python編程語言
2.數(shù)據(jù)處理分析庫pandas、科學計算庫numpy,進度條庫tqdm,畫圖庫matplotlib,python機器學習庫等使用,torch中nn、loss、optimizer模塊使用
四、實驗步驟:
4.1 數(shù)據(jù)下載
在pycharm中新建實驗三項目,名為“deep_learning_experiments_3”,在該項目文件夾路徑下下載項目數(shù)據(jù)。
方法 網(wǎng)址下載 Satellite Image Classification | Kaggle


衛(wèi)星圖像分類數(shù)據(jù)集包含來自傳感器和谷歌地圖快照的大約5600張圖像。它有屬于4個不同類別的衛(wèi)星圖像。
cloudy:從衛(wèi)星拍攝的1500張云圖像。desert:從衛(wèi)星拍攝的1131張沙漠圖像。
green_area:主要是森林覆蓋的衛(wèi)星圖像1500張圖片。water:1500張湖泊和其他水體的衛(wèi)星圖像。

4.2數(shù)據(jù)預處理
4.2.1導入所需的PyTorch模塊定義一些常量
import torch from torch.utils.data import DataLoader, Subset from torchvision import datasets, transforms # 驗證集比例 valid_split = 0.2 # 批量大小 batch_size = 64 # 數(shù)據(jù)根目錄路徑 root_dir = 'D:/pycharm projects/deep_learning_experiments/experiment_4/data'
使用20%的數(shù)據(jù)進行驗證,批大小為64,如果本地機器上訓練面臨OOM(內(nèi)存不足)問題,那么降低批大小32或16。
batch_size(批次大?。?/strong>是機器學習和深度學習訓練中的一個核心超參數(shù),用于控制每次迭代時模型處理的樣本數(shù)量。簡單來說,當你有一個包含 1000 張圖片的訓練集,若設置
batch_size=100,則模型會:
- 每次從訓練集中取 100 張圖片
- 計算這 100 張圖片的總損失(通過前向傳播)
- 根據(jù)根據(jù)總損失反向傳播更新一次模型參數(shù)
- 重復這一過程,直到所有 1000 張圖片都被處理完(即完成 1 個 epoch 的訓練)
作用與影響:
內(nèi)存占用:
batch_size越大,一次需要加載到內(nèi)存(或 GPU 顯存)的數(shù)據(jù)越多,可能導致內(nèi)存不足(OOM 錯誤)。訓練效率:較大的
batch_size可以利用 GPU 并行的并行計算能力加速訓練(單次處理處理大批次數(shù)據(jù)的效率更高),但單次迭代的計算時間會更長。模型收斂:
- 過小的
batch_size(如batch_size=1,即隨機梯度下降 SGD):訓練波動大,收斂路徑曲折,但可能更容易跳出局部最優(yōu)。- 適中的
batch_size(如 32、64、128):在收斂穩(wěn)定性和效率間取得平衡,是最常用的選擇。- 過大的
batch_size:訓練更穩(wěn)定,但可能收斂到較差的局部最優(yōu),且需要更大的學習率配合。
4.2.2訓練集和測試集轉(zhuǎn)換
#訓練集數(shù)據(jù)增強,以及將圖像數(shù)據(jù)類型轉(zhuǎn)化為張量做歸一化
train_transform = transforms.Compose([
transforms.Resize(224),
transforms.RandomHorizontalFlip(p=0.5),
transforms.RandomVerticalFlip(p=0.5),
transforms.GaussianBlur(kernel_size=(5, 9), sigma=(0.1, 5)), #kernel_size:高斯卷積核大小, sigma:標準差(min,max)
transforms.RandomRotation(degrees=(30, 70)),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])
valid_transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])
#空間變換增強
#翻轉(zhuǎn) → 位置不變性
#旋轉(zhuǎn) → 方向不變性
#組合使用 → 強大的幾何不變性
#外觀變換增強
#高斯模糊 → 對圖像質(zhì)量的魯棒性
#標準化??增強效果可視化
假設原始圖像經(jīng)過這個流水線可能產(chǎn)生:
原圖 → 清晰、正立的圖像
增強1 → 水平翻轉(zhuǎn) + 輕微模糊
增強2 → 旋轉(zhuǎn)45° + 垂直翻轉(zhuǎn)
增強3 → 強烈模糊 + 旋轉(zhuǎn)60°
4.2.3定義數(shù)據(jù)加載器
# 加載數(shù)據(jù)集
dataset = datasets.ImageFolder(root_dir, transform=train_transform)
dataset_test = datasets.ImageFolder(root_dir, transform=valid_transform)
# 數(shù)據(jù)集信息
print(f"Classes: {dataset.classes}") # 輸出類別名稱
dataset_size = len(dataset)
valid_size = int(valid_split * dataset_size)
# 隨機劃分訓練集和驗證集
indices = torch.randperm(len(dataset)).tolist() # 隨機打亂索引
dataset_train = Subset(dataset, indices[:-valid_size]) # 訓練集:前80%
dataset_valid = Subset(dataset_test, indices[-valid_size:]) # 驗證集:后20%
print(f"Total training images: {len(dataset_train)}")
print(f"Total valid_images: {len(dataset_valid)}")
train_loader = DataLoader(
dataset_train, batch_size=batch_size, shuffle=True # 訓練時打亂
)
valid_loader = DataLoader(
dataset_valid, batch_size=batch_size, shuffle=False # 驗證時不打亂
)4. 3構(gòu)建模型(ResNet34)
4.3.1訓練腳本train.py
導入所有庫模塊以及上面編寫的模塊,還有參數(shù)解析器,控制--epochs
import torch
import argparse
import torch.nn as nn
import torch.optim as optim
from model import build_model
from utils import save_model, save_plots
from datasets import train_loader, valid_loader, dataset
from tqdm.auto import tqdm
# 通過命令行控制訓練輪數(shù)
parser = argparse.ArgumentParser()
parser.add_argument('-e', '--epochs', type=int, default=20,
help='number of epochs to train our network for')
args = vars(parser.parse_args())4.3.2定義學習率、計算設備,構(gòu)建ResNet34模型并定義優(yōu)化器和損失函數(shù)
# 學習參數(shù)設置
#學習率: 0.001 (Adam優(yōu)化器的常用值)
#設備: 自動檢測GPU/CPU
lr = 0.001
epochs = args['epochs']
device = ('cuda' if torch.cuda.is_available() else 'cpu')
print(f"Computation device: {device}\n")
# 構(gòu)建模型
model = build_model(
pretrained=True, fine_tune=False, num_classes=len(dataset.classes)
).to(device)
#總參數(shù)量: 所有參數(shù)的數(shù)量
#可訓練參數(shù): 僅計算需要梯度的參數(shù)
total_params = sum(p.numel() for p in model.parameters())
print(f"{total_params:,} total parameters.")
total_trainable_params = sum(
p.numel() for p in model.parameters() if p.requires_grad)
print(f"{total_trainable_params:,} training parameters.\n")
# 優(yōu)化器: Adam,適合大多數(shù)場景
optimizer = optim.Adam(model.parameters(), lr=lr)
# 損失函數(shù): 交叉熵損失,用于多分類
criterion = nn.CrossEntropyLoss()4.3.3訓練與驗證函數(shù)
4.3.3.1訓練函數(shù)
# 訓練過程
def train(model, trainloader, optimizer, criterion):
model.train()
print('Training')
train_running_loss = 0.0
train_running_correct = 0
counter = 0
for i, data in tqdm(enumerate(trainloader), total=len(trainloader)):
counter += 1
image, labels = data
image = image.to(device)
labels = labels.to(device)
# 梯度清零
optimizer.zero_grad()
# 前向傳播
outputs = model(image)
# 計算損失
loss = criterion(outputs, labels)
train_running_loss += loss.item()
# 計算準確率
_, preds = torch.max(outputs.data, 1)
train_running_correct += (preds == labels).sum().item()
# 反向傳播
loss.backward()
# 更新參數(shù)
optimizer.step()
# 統(tǒng)計一批次的損失率和準確率
epoch_loss = train_running_loss / counter
epoch_acc = 100. * (train_running_correct / len(trainloader.dataset))
return epoch_loss, epoch_acc在每個epoch之后,該函數(shù)返回該epoch的損失和準確度。
epoch(輪次)是深度學習訓練中的核心概念,指模型完整遍歷一次所有訓練數(shù)據(jù)的過程,可以理解為 “訓練一輪”。簡單來說,假設你有 1000 張訓練圖片,無論
batch_size(批次大?。┦嵌嗌?,只要模型把這 1000 張圖片全部處理完一次,就算完成了 1 個epoch。比如之前的代碼設置了20個epoch,就意味著模型要把 4502 張訓練圖完整訓練 20 遍。
4.3.3.2驗證函數(shù)
def validate(model, testloader, criterion, class_names):
# 設置為評估模式(禁用dropout和batchnorm的隨機性)
model.eval()
print('驗證中...')
# 累計損失值
valid_running_loss = 0.0
# 累計正確預測數(shù)量
valid_running_correct = 0
# 批次計數(shù)器
counter = 0
# 初始化列表來跟蹤每個類別的準確率
# class_correct: 每個類別的正確預測數(shù)
# class_total: 每個類別的總樣本數(shù)
class_correct = list(0. for i in range(len(class_names)))
class_total = list(0. for i in range(len(class_names)))
# 禁用梯度計算以節(jié)省內(nèi)存和加速計算
with torch.no_grad():
# 遍歷驗證集中的所有批次
for i, data in tqdm(enumerate(testloader), total=len(testloader)):
counter += 1
# 獲取圖像數(shù)據(jù)和標簽
image, labels = data
# 將數(shù)據(jù)轉(zhuǎn)移到指定設備(GPU或CPU)
image = image.to(device)
labels = labels.to(device)
# 前向傳播:通過模型獲取預測輸出
outputs = model(image)
# 計算損失值
loss = criterion(outputs, labels)
# 累加損失值
valid_running_loss += loss.item()
# 計算準確率:獲取預測類別(最大概率的索引)
_, preds = torch.max(outputs.data, 1)
# 累加正確預測的數(shù)量
valid_running_correct += (preds == labels).sum().item()
# 計算每個類別的準確率
# 獲取每個樣本的預測是否正確(布爾張量)
correct = (preds == labels).squeeze()
# 遍歷當前批次中的每個樣本
for i in range(len(preds)):
# 獲取當前樣本的真實標簽
label = labels[i]
# 如果預測正確,則對應類別的正確數(shù)加1
class_correct[label] += correct[i].item()
# 對應類別的總樣本數(shù)加1
class_total[label] += 1
# 計算整個epoch的平均損失(總損失除以批次數(shù)量)
epoch_loss = valid_running_loss / counter
# 計算整體準確率(正確預測數(shù)除以總樣本數(shù),轉(zhuǎn)換為百分比)
epoch_acc = 100. * (valid_running_correct / len(testloader.dataset))
# 打印每個類別的準確率
print('\n')
for i in range(len(class_names)):
# 計算并打印每個類別的準確率
print(f"類別 {class_names[i]} 的準確率: {100 * class_correct[i] / class_total[i]:.3f}%")
print('\n')
# 返回平均損失和整體準確率
return epoch_loss, epoch_acc4.3.4訓練循環(huán)
# 初始化列表來跟蹤損失和準確率
# train_loss: 訓練損失列表
# valid_loss: 驗證損失列表
# train_acc: 訓練準確率列表
# valid_acc: 驗證準確率列表
train_loss, valid_loss = [], []
train_acc, valid_acc = [], []
# 開始訓練循環(huán)
for epoch in range(epochs):
# 打印當前訓練輪次信息
print(f"[INFO]: 第 {epoch+1} 輪 / 共 {epochs} 輪")
# 訓練一個epoch并獲取訓練損失和準確率
train_epoch_loss, train_epoch_acc = train(model, train_loader,
optimizer, criterion)
# 驗證一個epoch并獲取驗證損失和準確率
valid_epoch_loss, valid_epoch_acc = validate(model, valid_loader,
criterion, dataset.classes)
# 將當前epoch的結(jié)果添加到列表中
train_loss.append(train_epoch_loss)
valid_loss.append(valid_epoch_loss)
train_acc.append(train_epoch_acc)
valid_acc.append(valid_epoch_acc)
# 打印當前epoch的訓練和驗證結(jié)果
print(f"訓練損失: {train_epoch_loss:.3f}, 訓練準確率: {train_epoch_acc:.3f}%")
print(f"驗證損失: {valid_epoch_loss:.3f}, 驗證準確率: {valid_epoch_acc:.3f}%")
print('-'*50) # 打印分隔線
# 保存訓練好的模型權(quán)重(每個epoch都保存)
save_model(epochs, model, optimizer, criterion)
# 訓練完成后保存損失和準確率圖表
save_plots(train_acc, valid_acc, train_loss, valid_loss)
print('訓練完成')4.3.5模型測試
4.3.5.1測試參數(shù)設置
# 導入必要的庫
import torch # PyTorch深度學習框架
import cv2 # OpenCV計算機視覺庫,用于圖像處理
import torchvision.transforms as transforms # PyTorch圖像變換模塊
import argparse # 命令行參數(shù)解析庫
# 構(gòu)建參數(shù)解析器
# 創(chuàng)建ArgumentParser對象,用于處理命令行參數(shù)
parser = argparse.ArgumentParser()
# 添加輸入?yún)?shù)
# -i 或 --input: 指定輸入圖像路徑,默認值為'input/test_data/cloudy.jpeg'
parser.add_argument('-i', '--input',
default='input/test_data/cloudy.jpeg',
help='輸入圖像的路徑')
# 解析輸入?yún)?shù)并將其轉(zhuǎn)換為字典格式
args = vars(parser.parse_args())
# 計算設備設置
# 指定使用CPU進行計算(如果需要GPU可改為'cuda')
device = 'cpu'4.3.5.2加載模型處理轉(zhuǎn)換
對于預處理只需要將圖像轉(zhuǎn)換為 PIL 圖像格式,調(diào)整其大小,將其轉(zhuǎn)換為張量,然后應用歸一化。
# 定義所有類別標簽的列表
# 對應模型的4個輸出類別:多云、沙漠、綠地、水域
labels = ['cloudy', 'desert', 'green_area', 'water']
# 初始化模型并加載訓練好的權(quán)重
# pretrained=False: 不加載預訓練權(quán)重
# fine_tune=False: 不進行微調(diào)
# num_classes=4: 設置輸出類別數(shù)為4
model = build_model(
pretrained=False, fine_tune=False, num_classes=4
).to(device) # 將模型移動到指定設備(CPU)
print('[INFO]: 正在加載自定義訓練權(quán)重...')
# 加載訓練好的模型檢查點文件
# map_location=device: 確保權(quán)重加載到正確的設備上
checkpoint = torch.load('outputs/model.pth', map_location=device)
# 將訓練好的權(quán)重加載到模型中
model.load_state_dict(checkpoint['model_state_dict'])
# 將模型設置為評估模式(禁用dropout和batchnorm的隨機性)
model.eval()
# 定義圖像預處理變換流程
transform = transforms.Compose([
transforms.ToPILImage(), # 將numpy數(shù)組或tensor轉(zhuǎn)換為PIL圖像
transforms.Resize(224), # 調(diào)整圖像大小為224x224像素
transforms.ToTensor(), # 將PIL圖像轉(zhuǎn)換為tensor,并歸一化到[0,1]
transforms.Normalize( # 標準化處理,使用ImageNet數(shù)據(jù)集的均值和標準差
mean=[0.485, 0.456, 0.406], # RGB通道的均值
std=[0.229, 0.224, 0.225] # RGB通道的標準差
)
])4.3.5.3讀取圖像與前饋
# 讀取并預處理輸入圖像
# 使用OpenCV讀取圖像文件
image = cv2.imread(args['input'])
# 從文件路徑中提取真實類別(ground truth)
# 例如:'input/test_data/cloudy.jpeg' -> 'cloudy'
gt_class = args['input'].split('/')[-1].split('.')[0]
# 保存原始圖像的副本用于后續(xù)顯示
orig_image = image.copy()
# 將圖像從BGR格式轉(zhuǎn)換為RGB格式(OpenCV默認使用BGR,但模型需要RGB)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 對圖像應用預處理變換(調(diào)整大小、標準化等)
image = transform(image)
# 添加批次維度(將3D張量[H,W,C]轉(zhuǎn)換為4D張量[1,H,W,C])
# 因為模型期望輸入具有批次維度
image = torch.unsqueeze(image, 0)
# 在不計算梯度的情況下進行推理(節(jié)省內(nèi)存和計算資源)
with torch.no_grad():
# 將圖像數(shù)據(jù)移動到設備并通過模型進行前向傳播
outputs = model(image.to(device))
# 獲取模型輸出的最高概率類別
# torch.topk返回前k個最大值和對應的索引,這里k=1表示取最高概率
output_label = torch.topk(outputs, 1)
# 將預測的索引轉(zhuǎn)換為對應的類別名稱
pred_class = labels[int(output_label.indices)]
# 在原始圖像上添加真實類別文本(綠色)
cv2.putText(orig_image,
f"GT: {gt_class}", # 顯示真實類別
(10, 25), # 文本位置坐標(x,y)
cv2.FONT_HERSHEY_SIMPLEX, # 字體類型
1, # 字體大小
(0, 255, 0), # 字體顏色(綠色)
2, # 字體粗細
cv2.LINE_AA # 抗鋸齒線型
)
# 在原始圖像上添加預測類別文本(紅色)
cv2.putText(orig_image,
f"Pred: {pred_class}", # 顯示預測類別
(10, 55), # 文本位置坐標(x,y),在真實類別下方
cv2.FONT_HERSHEY_SIMPLEX, # 字體類型
1, # 字體大小
(0, 0, 255), # 字體顏色(紅色)
2, # 字體粗細
cv2.LINE_AA # 抗鋸齒線型
)
# 在控制臺打印真實類別和預測類別
print(f"真實類別: {gt_class}, 預測類別: {pred_class}")
# 顯示帶有標注的結(jié)果圖像
cv2.imshow('Result', orig_image)
# 等待按鍵操作(0表示無限等待)
cv2.waitKey(0)
# 將結(jié)果圖像保存到outputs目錄
# 文件名格式:outputs/真實類別.png
cv2.imwrite(f"outputs/{gt_class}.png", orig_image)4.4訓練測試終端命令
4.4.1訓練命令
python train.py --epochs 100
4.4.2測試命令
python inference.py --input test_data/cloudy.jpg
或者直接點運行也可以
運行中……

最終結(jié)果展示:

再運行下inference.py文件驗證下:
隨便放個圖片:


驗證結(jié)果完全正確?。?!是綠洲圖像


又測試了下cloudy的,也完全正確:

下面的是準確率和損失率變化圖像,很直觀的展示:
準確率:隨著訓練輪數(shù)的增加準確率顯著上升?。?!且訓練集和驗證集的準確率最終逐漸趨于一致

損失率:訓練集和驗證集的損失率顯著下降,最終訓練集的損失率降低到了0.05左右

五、訓練總結(jié)
5.1 最終性能指標
驗證準確率: 96.53%
訓練準確率: 98.22%
驗證損失: 0.163
訓練損失: 0.061
5.2 各類別準確率進展
| 類別 | 第1輪 | 第20輪 | 提升幅度 |
|---|---|---|---|
| cloudy | 87.42% | 98.43% | +11.01% |
| desert | 49.30% | 95.78% | +46.48% |
| green_area | 79.28% | 97.04% | +17.76% |
| water | 98.28% | 94.48% | -3.80% |
5.3關(guān)鍵觀察
5.3.1 優(yōu)秀表現(xiàn)
desert類巨大進步 :從49%提升到96%,說明模型成功學會了識別沙漠特征
整體性能卓越 :96.53%的驗證準確率非常優(yōu)秀
訓練穩(wěn)定 :損失持續(xù)下降,沒有明顯過擬合
收斂良好 :最后幾輪性能穩(wěn)定在高水平
5.3.2 需要注意
water類輕微下降 :從98%降到94%,但仍保持高水平
訓練時間波動 : 某些epoch訓練時間較長(可能受系統(tǒng)資源影響)
5.4 訓練動態(tài)分析
5.4.1關(guān)鍵轉(zhuǎn)折點
第5輪: desert類突破78%,整體準確率超過92%
第9輪: 首次達到95%+驗證準確率
第18輪: 達到峰值96.53%,desert類突破96%
5.4.2 收斂情況
最后5輪性能穩(wěn)定在95-96.5%之間,說明模型已經(jīng)充分訓練。
這還只是考慮性能和速度將epoch設置在了20的情況下,如果訓練次數(shù)設置在32/64,效果應該會更好(根據(jù)你的電腦性能決定)~~~
到此這篇關(guān)于基于深度學習的衛(wèi)星圖像分類(Kaggle比賽實戰(zhàn))的文章就介紹到這了,更多相關(guān)基于深度學習的衛(wèi)星圖像Kaggle比賽內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Django中ajax發(fā)送post請求 報403錯誤CSRF驗證失敗解決方案
這篇文章主要介紹了Django中ajax發(fā)送post請求 報403錯誤CSRF驗證失敗解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-08-08
一文秒懂python讀寫csv xml json文件各種騷操作
多年來,數(shù)據(jù)存儲的可能格式顯著增加,但是,在日常使用中,還是以 CSV 、 JSON 和 XML 占主導地位。 在本文中,我將與你分享在Python中使用這三種流行數(shù)據(jù)格式及其之間相互轉(zhuǎn)換的最簡單方法,需要的朋友可以參考下2019-07-07
最新解決'nvidia-smi' 不是內(nèi)部或外部命令也不是可運行的程序
使用cmd查看電腦顯卡的信息,調(diào)用nvidia-smi查看顯卡使用情況報錯,提示'nvidia-smi' 不是內(nèi)部或外部命令,也不是可運行的程序,本文給大家分享完美解決方案,感興趣的朋友跟隨小編一起看看吧2023-01-01
Python從Excel讀取數(shù)據(jù)并使用Matplotlib繪制成二維圖像
本課程實現(xiàn)使用 Python 從 Excel 讀取數(shù)據(jù),并使用 Matplotlib 繪制成二維圖像。這一過程中,將通過一系列操作來美化圖像,最終得到一個可以出版級別的圖像。本課程對于需要書寫實驗報告,學位論文,發(fā)表文章,做報告的學員具有較大價值2023-02-02

