在深度學習框架PyTorch一書的學習-第六章-實戰指南和pytorch Debug —交互式調試工具Pdb (ipdb是增強版的pdb)-1-在pytorch中使用 和 pytorch實現性別檢測三篇文章的基礎上寫的這篇文章
之前我們使用的是:
exp_lr_scheduler = optim.lr_scheduler.StepLR(optimizer_conv, step_size=3, gamma=0.1)
去自動遞減學習率,但是這種方法還是十分死板的,希望實現能夠手動根據收斂地效果去更改學習率的大小。所以在這里就是用了ipdb調試工具
1)
首先我們會使用argparse去實現命令行解析,實現通過設置--debugFile命令,然后通過生成和刪除指定文件夾去進入調試狀態:
def getArgs(): #設置解析 parser = argparse.ArgumentParser() parser.add_argument('--debugFile', nargs='?', default='None', type=str) args = parser.parse_args() return vars(args) #用vars()內建函數化為字典
2)然后在train_model()函數中添加:
# 進入debug模式 # print('args : ', args['debugFile']) if os.path.exists(args['debugFile']): import ipdb; ipdb.set_trace()
實現當讀取到該指定的文件夾后進入調試狀態
3)
整個代碼如下:

# coding:utf8 from torchvision import datasets, models from torch import nn, optim from torchvision import transforms as T from torch.utils import data import os import copy import time import torch import argparse #首先進行數據的處理 data_dir = './data' device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") #轉換圖片數據 normalize = T.Normalize([0.485, 0.456, 0.406],[0.229, 0.224, 0.225]) data_transforms ={ 'train': T.Compose([ T.RandomResizedCrop(224),#從圖片中心截取 T.RandomHorizontalFlip(),#隨機水平翻轉給定的PIL.Image,翻轉概率為0.5 T.ToTensor(),#轉成Tensor格式,大小范圍為[0,1] normalize ]), 'val': T.Compose([ T.Resize(256),#重新設定大小 T.CenterCrop(224), T.ToTensor(), normalize ]), } #加載圖片 #man的label為0, woman的label為1 image_datasets = {x : datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in ['train', 'val']} #得到train和val中的數據量 dataset_sizes = {x : len(image_datasets[x].imgs) for x in ['train', 'val']} dataloaders = {x : data.DataLoader(image_datasets[x], batch_size=4, shuffle=True,num_workers=4) for x in ['train', 'val']} #然后選擇使用的模型 model_conv = models.resnet18(pretrained=True) #凍結參數,不訓練卷積層網絡 #for param in model_conv.parameters(): # param.requires_grad = False #提取fc全連接層中固定的參數,后面的訓練只對全連接層的參數進行優化 fc_features = model_conv.fc.in_features #修改類別為2,即man和woman model_conv.fc = nn.Linear(fc_features, 2) model_conv.to(device) #定義使用的損失函數為交叉熵代價函數 criterion = nn.CrossEntropyLoss() #定義使用的優化器 #optimizer_conv = optim.SGD(model_conv.fc.parameters(), lr=0.0001, momentum=0.9) #optimizer_conv = optim.SGD(model_conv.parameters(), lr=0.0001, momentum=0.9) optimizer_conv = optim.Adam(model_conv.parameters(), lr=0.0001, betas=(0.9, 0.99)) #設置自動遞減的學習率,等間隔調整學習率,即在7個step時,將學習率調整為 lr*gamma # exp_lr_scheduler = optim.lr_scheduler.StepLR(optimizer_conv, step_size=3, gamma=0.1) #exp_lr_scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer_conv, mode='min', verbose=True) # 訓練模型 # 參數說明: # model:待訓練的模型 # criterion:評價函數 # optimizer:優化器 # scheduler:學習率 # num_epochs:表示實現完整訓練的次數,一個epoch表示一整個訓練周期 # def train_model(model, criterion, optimizer, scheduler, args, num_epochs=20): def train_model(model, criterion, optimizer, args, num_epochs=20): # 定義訓練開始時間 since = time.time() #用於保存最優的權重 best_model_wts = copy.deepcopy(model.state_dict()) #最優精度值 best_train_acc = 0.0 best_val_acc = 0.0 best_iteration = 0 # # meters,統計指標:平滑處理之后的損失,還有混淆矩陣 # loss_meter = meter.AverageValueMeter()#能夠計算所有數的平均值和標准差,用來統計一個epoch中損失的平均值 # confusion_matrix = meter.ConfusionMeter(2)#用來統計分類問題中的分類情況,是一個比准確率更詳細的統計指標 # 對整個數據集進行num_epochs次訓練 for epoch in range(num_epochs): print('Epoch {}/{}'.format(epoch, num_epochs - 1)) print('-' * 10) #用於存儲train acc還沒有與val acc比較之前的值 temp = 0 # Each epoch has a training and validation phase # 每輪訓練訓練包含`train`和`val`的數據 for phase in ['train', 'val']: if phase == 'train': # 學習率步進 # scheduler.step() # 設置模型的模式為訓練模式(因為在預測模式下,采用了`Dropout`方法的模型會關閉部分神經元) model.train() # Set model to training mode else: # 預測模式 model.eval() # Set model to evaluate mode running_loss = 0.0 running_corrects = 0 # Iterate over data. # 遍歷數據,這里的`dataloaders`近似於一個迭代器,每一次迭代都生成一批`inputs`和`labels`數據, # 一批有四個圖片,一共有dataset_sizes['train']/4或dataset_sizes['val']/4批 # 這里循環幾次就看有幾批數據 for inputs, labels in dataloaders[phase]: inputs = inputs.to(device) # 當前批次的訓練輸入 labels = labels.to(device) # 當前批次的標簽輸入 # print('input : ', inputs) # print('labels : ', labels) # 將梯度參數歸0 optimizer.zero_grad() # 前向計算 # track history if only in train with torch.set_grad_enabled(phase == 'train'): # 相應輸入對應的輸出 outputs = model(inputs) # print('outputs : ', outputs) # 取輸出的最大值作為預測值preds,dim=1,得到每行中的最大值的位置索引,用來判別其為0或1 _, preds = torch.max(outputs, 1) # print('preds : ', preds) # 計算預測的輸出與實際的標簽之間的誤差 loss = criterion(outputs, labels) # backward + optimize only if in training phase if phase == 'train': # 對誤差進行反向傳播 loss.backward() #scheduler.step(loss) #當使用的學習率遞減函數為optim.lr_scheduler.ReduceLROnPlateau時,使用在這里 # 執行優化器對梯度進行優化 optimizer.step() # loss_meter.add(loss.item()) # confusion_matrix.add(outputs.detach(), labels.detach()) # 進入debug模式 # print('args : ', args['debugFile']) if os.path.exists(args['debugFile']): import ipdb; ipdb.set_trace() # statistics # 計算`running_loss`和`running_corrects` #loss.item()得到的是此時損失loss的值 #inputs.size(0)得到的是一批圖片的數量,這里為4 #兩者相乘得到的是4張圖片的總損失 #疊加得到所有數據的損失 running_loss += loss.item() * inputs.size(0) #torch.sum(preds == labels.data)判斷得到的結果中有幾個正確,running_corrects得到四個中正確的個數 #疊加得到所有數據中判斷成功的個數 running_corrects += torch.sum(preds == labels.data) # 當前輪的損失,除以所有數據量個數得到平均loss值 epoch_loss = running_loss / dataset_sizes[phase] # 當前輪的精度,除以所有數據量個數得到平均准確度 epoch_acc = running_corrects.double() / dataset_sizes[phase] print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc)) # deep copy the model # 對模型進行深度復制 if phase == 'train' and epoch_acc > best_train_acc: temp = epoch_acc if phase =='val' and epoch_acc > 0 and epoch_acc < temp: best_train_acc = temp best_val_acc = epoch_acc best_iteration = epoch best_model_wts = copy.deepcopy(model.state_dict()) # 計算訓練所需要的總時間 time_elapsed = time.time() - since print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60)) print('Best epoch: {:4f}'.format(best_iteration)) print('Best train Acc: {:4f}'.format(best_train_acc)) print('Best val Acc: {:4f}'.format(best_val_acc)) # load best model weights # 加載模型的最優權重 model.load_state_dict(best_model_wts) return model def getArgs(): #設置解析 parser = argparse.ArgumentParser() parser.add_argument('--debugFile', nargs='?', default='None', type=str) args = parser.parse_args() return vars(args) #用vars()內建函數化為字典 if __name__ == '__main__': args_dist = getArgs() print(args_dist) # model_train = train_model(model_conv, criterion, optimizer_conv, exp_lr_scheduler, args_dist) model_train = train_model(model_conv, criterion, optimizer_conv, args_dist) torch.save(model_train, 'GenderTest1_18.pkl')
4)
然后運行:
(deeplearning) userdeMBP:resnet18 user$ python train.py --debugFile=./debug {'debugFile': './debug'} Epoch 0/19 ---------- train Loss: 0.7313 Acc: 0.6000 val Loss: 0.6133 Acc: 0.5500 Epoch 1/19 ---------- train Loss: 0.3051 Acc: 0.9500 val Loss: 0.5630 Acc: 0.7000 Epoch 2/19 ---------- train Loss: 0.1872 Acc: 0.9000 val Loss: 0.8300 Acc: 0.6500 Epoch 3/19 ---------- train Loss: 0.3791 Acc: 0.8500 val Loss: 1.1445 Acc: 0.6000 Epoch 4/19 ---------- train Loss: 0.4880 Acc: 0.8000 val Loss: 0.5832 Acc: 0.7000 Epoch 5/19 ----------
這時候在當前文件夾下生成一個名為debug的文件夾,就會進入調試模式:
Epoch 5/19 ---------- --Call-- > /anaconda3/envs/deeplearning/lib/python3.6/site-packages/torch/autograd/grad_mode.py(129)__exit__() 128 --> 129 def __exit__(self, *args): 130 torch.set_grad_enabled(self.prev) ipdb> u #進入上一條命令 > /Users/user/pytorch/gender_test_work/resnet18/train.py(151)train_model() 150 import ipdb; --> 151 ipdb.set_trace() 152 ipdb> for group in optimizer.param_groups: group['lr'] #查看當前的學習率值 0.0001 ipdb> for group in optimizer.param_groups: group['lr']=0.01 #更改為新的學習率值
然后這時候刪除debug文件夾,在調試中運行c命令繼續向下運行:
ipdb> c train Loss: 1.0321 Acc: 0.7500 val Loss: 8590.6042 Acc: 0.6000 Epoch 6/19 ---------- train Loss: 2.5962 Acc: 0.4000 val Loss: 23884.8344 Acc: 0.5000 Epoch 7/19 ---------- train Loss: 1.0793 Acc: 0.5500 val Loss: 65956.7039 Acc: 0.5000 Epoch 8/19 ---------- train Loss: 1.6199 Acc: 0.4500 val Loss: 16973.9813 Acc: 0.5000 Epoch 9/19 ---------- train Loss: 1.4478 Acc: 0.3500 val Loss: 1580.6444 Acc: 0.5000 Epoch 10/19 ----------
然后再生成debug文件夾進入調試命令,查看此時的學習率,可見此時的學習率果然為調后的0.01:
Epoch 10/19 ---------- > /Users/user/pytorch/gender_test_work/resnet18/train.py(151)train_model() 150 import ipdb; --> 151 ipdb.set_trace() 152 ipdb> for group in optimizer.param_groups: group['lr'] 0.01 ipdb>
上面的訓練結果什么的不要太在意,只是為了演示隨便跑的
5)
中間有出現一個問題:
SyntaxError: non-default argument follows default argument
這種錯誤原因是將沒有默認值的參數在定義時放在了有默認值的參數的后面
如:
def train_model(model, criterion, optimizer, scheduler, num_epochs=200, args_dist):
應該寫成:
def train_model(model, criterion, optimizer, scheduler, args_dist, num_epochs=200):