作爲一個只學習了一點點語義分割模型的小白,真正實戰起來還是一個鴻溝,最近遇到了很多小白初期肯定都會遇到的問題,在這裏一一說一下,希望能夠幫助到小白們。
1、實驗室服務器有GPU,我們如何使用服務器的GPU進行模型的訓練呢?
答:我這裏採用的方法是使用pycharm連接遠程服務器,網上有很多此類的教程,我其實只在pycharm裏面配置了遠程服務器,然後就可以可視化服務器的文件了,本地的文件可以和服務器的文件同步,也就是你把文件放進pycharm裏面,然後upload,就跑到服務器裏面了,這和你用xshell使用rz命令上傳文件是一樣的。
這裏需要注意一點:我們可視化遠程服務器文件後,可以在自己電腦的pycharm上編輯服務器文件,然後上傳,服務器的代碼就成功更改了,不過編輯是在本地,實際運行可不是讓你運行pycharm,而是用命令行連接服務器後,使用python 文件名.py運行代碼,在代碼裏面使用GPU就可以了。
- 將本地pycharm遠程連接帶有GPU的服務器
- 將本地編寫的文件upload到服務器
- 打開命令行,ssh連接到服務器
- cd進入代碼所在地址,輸入python 文件名.py運行代碼
GPU使用是在代碼裏面寫的,pytorch使用GPU代碼如下:
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
2、知道了如何使用GPU,那麼我們如何進行語義分割代碼的編寫和運行呢?
答:我看到網上此類的教程不多,於是自己將詳細的教程寫一下。(基於pytorch)
流程主要分類四個方面吧:
- 圖像處理
- 模型建立
- 訓練模型並保存最優模型
- 讀取模型進行測試
因爲我是小白,所以接下來我將詳細說明在pytorch中進行上面四個方面的具體過程。
一、圖像處理部分
我們進入pytorch官網,可以看到如下庫:
如果需要進行圖像處理,那麼我們使用Pytorch——計算機視覺工具包:torchvision
torchvision獨立於Pytorch,需通過pip install torchvision 安裝。
torchvision 主要包含以下三部分:
models : 提供深度學習中各種經典的網絡結構以及訓練好的模型,包括Alex Net, VGG系列、ResNet系列、Inception系列等;
datasets:提供常用的數據集加載,設計上都是繼承torch.utils.data.Dataset,主要包括MMIST、CIFAR10/100、ImageNet、COCO等;
transforms: 提供常用的數據預處理操作,主要包括對Tensor及PIL Image對象的操作。
具體操作過程如下:
1、首先寫transform,transform的輸入是圖片,輸出是對圖片的操作結果。(參見pytorchvison.transforms文檔)
例如這樣:
transform=transforms.Compose([
transforms.Resize(224), #縮放圖片(Image),保持長寬比不變,最短邊爲224像素
transforms.CenterCrop(224), #從圖片中間裁剪出224*224的圖片
transforms.ToTensor(), #將圖片Image轉換成Tensor,歸一化至【0,1】
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])
#標準化,規定均值和方差
])
2、採用cv2庫的imread、resize等函數對圖像進行一些早期處理。(參見cv2實現數據增強的博客)
例如這樣:
imgB = cv2.imread('bag_data_msk/' + img_name, 0)
imgB = cv2.resize(imgB, (160, 160))
imgB = imgB / 255
imgB = imgB.astype('uint8')
imgB = onehot(imgB, 2) # 因爲此代碼是二分類問題,即分割出手提包和背景兩樣就行,因此這裏參數是2
imgB = imgB.transpose(2, 0, 1) # imgB不經過transform處理,所以要手動把(H,W,C)轉成(C,H,W)
imgB = torch.FloatTensor(imgB)
3、對圖像進行transform處理。
例如這樣:
img_name = r'bag3.jpg' # 預測的圖片
imgA = cv2.imread(img_name)
imgA = cv2.resize(imgA, (160, 160))
imgA = transform(imgA)
4、將本地數據集轉化爲pytorch的Dataset
類。(參見Pytorch中正確設計並加載數據集方法)
也就是說要將我們自己的數據集轉化爲pytorch的數據集類,這樣才能將數據集加載進DataLoader中。
Dataset
類是Pytorch
中圖像數據集中最爲重要的一個類,也是Pytorch
中所有數據集加載類中應該繼承的父類。其中父類中的兩個私有成員函數必須被重載,否則將會觸發錯誤提示:
- def getitem(self, index):
- def len(self):
其中__len__
應該返回數據集的大小,而__getitem__
應該編寫支持數據集索引的函數,例如通過dataset[i]
可以得到數據集中的第i+1
個數據。
例如這樣:
class BagDataset(Dataset):
def __init__(self, transform=None):
self.transform = transform
def __len__(self):
return len(os.listdir('bag_data'))
def __getitem__(self, idx):
img_name = os.listdir('bag_data')[idx]
imgA = cv2.imread('bag_data/' + img_name)
imgA = cv2.resize(imgA, (160, 160))
# print(imgA.shape)
imgB = cv2.imread('bag_data_msk/' + img_name, 0)
imgB = cv2.resize(imgB, (160, 160))
imgB = imgB / 255
imgB = imgB.astype('uint8')
imgB = onehot(imgB, 2) # 因爲此代碼是二分類問題,即分割出手提包和背景兩樣就行,因此這裏參數是2
imgB = imgB.transpose(2, 0, 1) # imgB不經過transform處理,所以要手動把(H,W,C)轉成(C,H,W)
imgB = torch.FloatTensor(imgB)
if self.transform:
imgA = self.transform(imgA) # 一轉成向量後,imgA通道就變成(C,H,W)
return imgA, imgB
5、定義訓練集個數和測試集個數,並進行劃分。(參見Pytorch劃分數據集的方法)
例如這樣:
train_size = int(0.9 * len(bag)) # 整個訓練集中,百分之90爲訓練集
test_size = len(bag) - train_size
train_dataset, test_dataset = random_split(bag, [train_size, test_size]) # 按照給定的長度將數據集劃分成沒有重疊的新數據集組合
6、定義DataLoader。
Dataset是一個包裝類,用來將數據包裝爲Dataset類,然後傳入DataLoader中,我們再使用DataLoader這個類來更加快捷的對數據進行操作。
DataLoader是一個比較重要的類,它爲我們提供的常用操作有:batch_size(每個batch的大小), shuffle(是否進行shuffle操作), num_workers(加載數據的時候使用幾個子進程)
例如這樣:
train_dataloader = DataLoader(train_dataset, batch_size=4, shuffle=True, num_workers=4)
test_dataloader = DataLoader(test_dataset, batch_size=4, shuffle=True, num_workers=4)
到這裏,我們主要做了如下任務:
- 讀取圖像
- 圖像增強
- 圖像處理
- 數據集轉換
- 定義訓練集和測試集個數,並通過random_split切分得到
- 定義DataLoader,將圖像集傳入
到這裏,第一模塊:圖像處理部分大致就這樣了。
二、模型建立
網絡這部分就不多說了,因爲網上有各種語義分割的網絡結構,而且你從github上一搜索好多是。
三、訓練模型
- 設置GPU訓練
- 獲得模型,並將模型分配給GPU
- 設置損失函數評判標準
- 設置優化方法
- 第一個for循環epoch
- 第二個for循環從DataLoader中獲得圖像數據
- 將圖像傳入模型中,獲得結果,並將結果二值化
- 優化器梯度設置爲0,將輸出和真實結果計算損失,損失反向傳播,優化器.step()
- 設置輸出顯示,保存模型等
1、設置GPU訓練
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
2、獲得模型,並將模型分配給GPU
vgg_model = VGGNet(requires_grad=True, show_params=show_vgg_params)
fcn_model = FCNs(pretrained_net=vgg_model, n_class=2)
fcn_model = fcn_model.to(device)
3、設置損失函數評判標準
4、設置優化方法
criterion = nn.BCELoss().to(device)
optimizer = optim.SGD(fcn_model.parameters(), lr=1e-2, momentum=0.7)
5、第一個for循環epoch
for epo in range(epo_num):
6、第二個for循環從DataLoader中獲得圖像數據
for index, (bag, bag_msk) in enumerate(train_dataloader):
7、將圖像傳入模型中,獲得結果,並將結果二值化
output = fcn_model(bag)
output = torch.sigmoid(output)
8、優化器梯度設置爲0,將輸出和真實結果計算損失,損失反向傳播,優化器.step()
optimizer.zero_grad()
loss = criterion(output, bag_msk)
loss.backward()
optimizer.step()
9、設置輸出顯示,保存模型等
print('epoch train loss = %f, epoch test loss = %f, %s'
% (train_loss / len(train_dataloader), test_loss / len(test_dataloader), time_str))
if np.mod(epo, 5) == 0:
torch.save(fcn_model, 'checkpoints/fcn_model_{}.pt'.format(epo))
print('saveing checkpoints/fcn_model_{}.pt'.format(epo))
四、測試
- 加載模型
- 設置transform或者cv2將圖像轉化成想輸入的大小
- 傳入模型輸出顯示或保存
大致過程就是這樣的,如果你想要實現一下的話,可以參考網上的:
pytorch用FCN語義分割手提包數據集(訓練+預測單張輸入圖片代碼)
我已經試過了,能夠成功運行。