目錄
1.遷移學(xué)習(xí)概念
2.數(shù)據(jù)預(yù)處理
?3.訓(xùn)練模型(基于遷移學(xué)習(xí))
3.1選擇網(wǎng)絡(luò),這里用resnet
3.2如果用GPU訓(xùn)練,需要加入以下代碼
3.3卷積層凍結(jié)模塊
3.4加載resnet152模
3.5解釋initialize_model函數(shù)
3.6遷移學(xué)習(xí)網(wǎng)絡(luò)搭建
3.7優(yōu)化器
3.8訓(xùn)練模塊(可以理解為主函數(shù))
3.9開始訓(xùn)練
3.10微調(diào)
4.測試模型
4.1加載訓(xùn)練好的模型
4.2測試數(shù)據(jù)預(yù)處理
4.3數(shù)據(jù)展示
4.4提取測試數(shù)據(jù)集
4.5計(jì)算提取數(shù)據(jù)集的預(yù)測結(jié)果
4.6展示預(yù)測結(jié)果
參考文獻(xiàn)
1.遷移學(xué)習(xí)概念
先說一下深度學(xué)習(xí)常見的問題:
? ? ? ? 1.數(shù)據(jù)集不夠,通常用數(shù)據(jù)增強(qiáng)解決。
? ? ? ? 2.參數(shù)難以確定,訓(xùn)練時間長,這就需要用遷移學(xué)習(xí)來解決
什么叫遷移學(xué)習(xí)呢:比方說有一個對100w的自行車數(shù)據(jù)集,并用VGG模型訓(xùn)練好的網(wǎng)絡(luò),而此時你想訓(xùn)練一個1w自行車數(shù)據(jù)集(雖然對象一樣,但采集的數(shù)據(jù)會不同),也用VGG模型進(jìn)行訓(xùn)練,你發(fā)現(xiàn),你們數(shù)據(jù)集的對象一樣,選用的網(wǎng)絡(luò)模型一樣,此時在初始化自己模型權(quán)重(就是卷積層,池化層和全連接層的參數(shù))時,可以用人家訓(xùn)練好的模型參數(shù)(如果不這樣就需要隨機(jī)初始化模型權(quán)重),這樣做可以節(jié)省大量尋找最優(yōu)參數(shù)的時間,又可以保證參數(shù)的準(zhǔn)確。
總結(jié):遷移學(xué)習(xí)就是用別人的東西訓(xùn)練自己的東西,但要注意,為了使用別人的模型參數(shù),要保證自己的數(shù)據(jù)對象、網(wǎng)絡(luò)結(jié)構(gòu)、輸入和輸出數(shù)據(jù)的結(jié)構(gòu)和別人相同。比方說,別人識別狗,你不能識別 貓,別人用VGG你不能用resnet,別人輸入和輸入圖像大小是224×224.你不能是256×256。
進(jìn)一步理解遷移學(xué)習(xí)的使用1:看下圖最大的紅框,表示卷積層,當(dāng)用別人的模型時,對卷積層的兩種處理方式。
? ? ? ? A:作為自己模型權(quán)重的初始化參數(shù)。
? ? ? ? B:凍結(jié)卷積層網(wǎng)絡(luò),意思是直接用別人的參數(shù),不再更新。凍結(jié)卷積層網(wǎng)絡(luò)又分幾種情況。
? ? ? ? ? ? ? ? B1:當(dāng)數(shù)據(jù)量小時,凍結(jié)第二大紅框表示的卷積層,剩下卷積層進(jìn)行更新。因?yàn)閿?shù)據(jù)量小時,容易過擬合,直接用別人呢參數(shù)最好。
? ? ? ? ? ? ? ? B2:當(dāng)數(shù)據(jù)量中等時凍結(jié)最小紅框表示的卷積層,剩下的卷積層進(jìn)行更行。
? ? ? ? ? ? ? ? B3:當(dāng)數(shù)據(jù)量足夠大時,不凍結(jié)卷積層,用A的方法,只作為自己模型權(quán)重的初始化參數(shù)。數(shù)據(jù)量大時,雖然對象一樣,但畢竟數(shù)據(jù)不同,會有一定差異,更新參數(shù)是最優(yōu)選擇。
?進(jìn)一步理解遷移學(xué)習(xí)的使用2:說完卷積層,在說一下全連接層,必須要注意不管卷積層選A還是B,全連接層都是要更新的,原因在于,別人模型進(jìn)行圖像分類可能是進(jìn)行1000個分類,而你只進(jìn)行100或者999個分類,那么全連接層的參數(shù)肯定是不同的。
2.數(shù)據(jù)預(yù)處理
上接該文:pytorch實(shí)戰(zhàn)-圖像分類(一)(數(shù)據(jù)預(yù)處理)
?3.訓(xùn)練模型(基于遷移學(xué)習(xí))
3.1選擇網(wǎng)絡(luò),這里用resnet
model_name = 'resnet' #可選的比較多 ['resnet', 'alexnet', 'vgg', 'squeezenet', 'densenet', 'inception']
#是否用人家訓(xùn)練好的特征來做
feature_extract = True
3.2如果用GPU訓(xùn)練,需要加入以下代碼
# 是否用GPU訓(xùn)練
train_on_gpu = torch.cuda.is_available()
if not train_on_gpu:
print('CUDA is not available. Training on CPU ...')
else:
print('CUDA is available! Training on GPU ...')
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
3.3卷積層凍結(jié)模塊
def set_parameter_requires_grad(model, feature_extracting):
if feature_extracting:
for param in model.parameters():
param.requires_grad = False
3.4加載resnet152模
注意:resnet152模型就是別人的模型。
model_ft = models.resnet152()
model_ft
3.5解釋initialize_model函數(shù)
本小節(jié)只是截取pytorch官網(wǎng)的一個例子,用initialize_model說明在pytoch中遷移學(xué)習(xí)怎么使用,不屬于本文代碼
具體操作如下:
? ? ? ? 1.下載別人的模型參數(shù),這里下載restnet152模型
? ? ? ? 2.選擇需要凍結(jié)的卷積層
? ? ? ? 3.改變?nèi)B接層的輸出個數(shù),這里將1000改為102
def initialize_model(model_name, num_classes, feature_extract, use_pretrained=True):
# 選擇合適的模型,不同模型的初始化方法稍微有點(diǎn)區(qū)別
model_ft = None
input_size = 0
if model_name == "resnet":
""" Resnet152
"""
model_ft = models.resnet152(pretrained=use_pretrained) #下載resnet152模型
set_parameter_requires_grad(model_ft, feature_extract) #選擇凍結(jié)哪部分卷積層
num_ftrs = model_ft.fc.in_features #全連接層的輸入比方說全連接層是2048×1000,這就是2048.
model_ft.fc = nn.Sequential(nn.Linear(num_ftrs, 102),
nn.LogSoftmax(dim=1)) #原resnet152的全連接層輸出是1000,自己模型需要的輸出是102,進(jìn)行改動。
input_size = 224
return model_ft, input_size
3.6遷移學(xué)習(xí)網(wǎng)絡(luò)搭建
model_ft, input_size = initialize_model(model_name, 102, feature_extract, use_pretrained=True)
#GPU計(jì)算
model_ft = model_ft.to(device)
#?模型保存
filename='checkpoint.pth'
# 是否訓(xùn)練所有層
params_to_update = model_ft.parameters()
print("Params to learn:")
if feature_extract:
params_to_update = []
for name,param in model_ft.named_parameters():
if param.requires_grad == True:
params_to_update.append(param)
print("\t",name)
else:
for name,param in model_ft.named_parameters():
if param.requires_grad == True:
print("\t",name)
3.7優(yōu)化器
就是用該方法更新模型參數(shù)
# 優(yōu)化器設(shè)置
optimizer_ft = optim.Adam(params_to_update, lr=1e-2)
scheduler = optim.lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)#學(xué)習(xí)率每7個epoch衰減成原來的1/10
#最后一層已經(jīng)LogSoftmax()了,所以不能nn.CrossEntropyLoss()來計(jì)算了,nn.CrossEntropyLoss()相當(dāng)于logSoftmax()和nn.NLLLoss()整合
criterion = nn.NLLLoss()
3.8訓(xùn)練模塊(可以理解為主函數(shù))
def train_model(model, dataloaders, criterion, optimizer, num_epochs=25, is_inception=False,filename=filename):
since = time.time() #
best_acc = 0
"""
checkpoint = torch.load(filename)
best_acc = checkpoint['best_acc']
model.load_state_dict(checkpoint['state_dict'])
optimizer.load_state_dict(checkpoint['optimizer'])
model.class_to_idx = checkpoint['mapping']
"""
model.to(device)
val_acc_history = []
train_acc_history = []
train_losses = []
valid_losses = []
LRs = [optimizer.param_groups[0]['lr']]
best_model_wts = copy.deepcopy(model.state_dict())
for epoch in range(num_epochs):
print('Epoch {}/{}'.format(epoch, num_epochs - 1))
print('-' * 10)
# 訓(xùn)練和驗(yàn)證
for phase in ['train', 'valid']:
if phase == 'train':
model.train() # 訓(xùn)練
else:
model.eval() # 驗(yàn)證
running_loss = 0.0
running_corrects = 0
# 把數(shù)據(jù)都取個遍
for inputs, labels in dataloaders[phase]:
inputs = inputs.to(device)
labels = labels.to(device)
# 清零
optimizer.zero_grad()
# 只有訓(xùn)練的時候計(jì)算和更新梯度
with torch.set_grad_enabled(phase == 'train'):
if is_inception and phase == 'train':
outputs, aux_outputs = model(inputs)
loss1 = criterion(outputs, labels)
loss2 = criterion(aux_outputs, labels)
loss = loss1 + 0.4*loss2
else:#resnet執(zhí)行的是這里
outputs = model(inputs)
loss = criterion(outputs, labels)
_, preds = torch.max(outputs, 1)
# 訓(xùn)練階段更新權(quán)重
if phase == 'train':
loss.backward()
optimizer.step()
# 計(jì)算損失
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)
epoch_loss = running_loss / len(dataloaders[phase].dataset)
epoch_acc = running_corrects.double() / len(dataloaders[phase].dataset)
time_elapsed = time.time() - since
print('Time elapsed {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))
# 得到最好那次的模型
if phase == 'valid' and epoch_acc > best_acc:
best_acc = epoch_acc
best_model_wts = copy.deepcopy(model.state_dict())
state = {
'state_dict': model.state_dict(),
'best_acc': best_acc,
'optimizer' : optimizer.state_dict(),
}
torch.save(state, filename)
if phase == 'valid':
val_acc_history.append(epoch_acc)
valid_losses.append(epoch_loss)
scheduler.step(epoch_loss)
if phase == 'train':
train_acc_history.append(epoch_acc)
train_losses.append(epoch_loss)
print('Optimizer learning rate : {:.7f}'.format(optimizer.param_groups[0]['lr']))
LRs.append(optimizer.param_groups[0]['lr'])
print()
time_elapsed = time.time() - since
print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
print('Best val Acc: {:4f}'.format(best_acc))
# 訓(xùn)練完后用最好的一次當(dāng)做模型最終的結(jié)果
model.load_state_dict(best_model_wts)
return model, val_acc_history, train_acc_history, valid_losses, train_losses, LRs
3.9開始訓(xùn)練
model_ft, val_acc_history, train_acc_history, valid_losses, train_losses, LRs = train_model(model_ft, dataloaders, criterion, optimizer_ft, num_epochs=20, is_inception=(model_name=="inception"))
3.10微調(diào)
在2.9中得到的模型,是凍結(jié)了卷積層,只訓(xùn)練了全連接層,所以此時希望在此基礎(chǔ)上再對卷積層進(jìn)行訓(xùn)練。
for param in model_ft.parameters():
param.requires_grad = True
# 再繼續(xù)訓(xùn)練所有的參數(shù),學(xué)習(xí)率調(diào)小一點(diǎn)
optimizer = optim.Adam(params_to_update, lr=1e-4)
scheduler = optim.lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)
# 損失函數(shù)
criterion = nn.NLLLoss()
# Load the checkpoint,加載自己的模型
checkpoint = torch.load(filename)
best_acc = checkpoint['best_acc']
model_ft.load_state_dict(checkpoint['state_dict'])
optimizer.load_state_dict(checkpoint['optimizer'])
#model_ft.class_to_idx = checkpoint['mapping']
model_ft, val_acc_history, train_acc_history, valid_losses, train_losses, LRs = train_model(model_ft, dataloaders, criterion, optimizer, num_epochs=10, is_inception=(model_name=="inception"))
4.測試模型
4.1加載訓(xùn)練好的模型
model_ft, input_size = initialize_model(model_name, 102, feature_extract, use_pretrained=True)
# GPU模式
model_ft = model_ft.to(device)
#?保存文件的名字
filename='seriouscheckpoint.pth'
# 加載模型
checkpoint = torch.load(filename)
best_acc = checkpoint['best_acc']
model_ft.load_state_dict(checkpoint['state_dict'])
4.2測試數(shù)據(jù)預(yù)處理
? ? ? ? 1.測試數(shù)據(jù)處理方法需要跟訓(xùn)練時一直才可以
? ? ? ? 2.crop操作的目的是保證輸入的大小是一致的
? ? ? ? 3.標(biāo)準(zhǔn)化操作也是必須的,用跟訓(xùn)練數(shù)據(jù)相同的mean和std,但是需要注意一點(diǎn)訓(xùn)練數(shù)據(jù)是在0-1上進(jìn)行標(biāo)準(zhǔn)化,所以測試數(shù)據(jù)也需要先歸一化
? ? ? ? 4.PyTorch中顏色通道是第一個維度,跟很多工具包都不一樣,需要轉(zhuǎn)換文章來源:http://www.zghlxwxcb.cn/news/detail-630700.html
def process_image(image_path):
# 讀取測試數(shù)據(jù)
img = Image.open(image_path)
# Resize,thumbnail方法只能進(jìn)行縮小,所以進(jìn)行了判斷
if img.size[0] > img.size[1]:
img.thumbnail((10000, 256))
else:
img.thumbnail((256, 10000))
# Crop操作
left_margin = (img.width-224)/2
bottom_margin = (img.height-224)/2
right_margin = left_margin + 224
top_margin = bottom_margin + 224
img = img.crop((left_margin, bottom_margin, right_margin,
top_margin))
# 相同的預(yù)處理方法
img = np.array(img)/255
mean = np.array([0.485, 0.456, 0.406]) #provided mean
std = np.array([0.229, 0.224, 0.225]) #provided std
img = (img - mean)/std
# 注意顏色通道應(yīng)該放在第一個位置
img = img.transpose((2, 0, 1))
return img
4.3數(shù)據(jù)展示
def imshow(image, ax=None, title=None):
"""展示數(shù)據(jù)"""
if ax is None:
fig, ax = plt.subplots()
# 顏色通道還原
image = np.array(image).transpose((1, 2, 0))
# 預(yù)處理還原
mean = np.array([0.485, 0.456, 0.406])
std = np.array([0.229, 0.224, 0.225])
image = std * image + mean
image = np.clip(image, 0, 1)
ax.imshow(image)
ax.set_title(title)
return ax
4.4提取測試數(shù)據(jù)集
# 得到一個batch的測試數(shù)據(jù)
dataiter = iter(dataloaders['valid'])
images, labels = dataiter.next()
model_ft.eval()
if train_on_gpu:
output = model_ft(images.cuda())
else:
output = model_ft(images)
4.5計(jì)算提取數(shù)據(jù)集的預(yù)測結(jié)果
_, preds_tensor = torch.max(output, 1)
preds = np.squeeze(preds_tensor.numpy()) if not train_on_gpu else np.squeeze(preds_tensor.cpu().numpy())
preds
4.6展示預(yù)測結(jié)果
fig=plt.figure(figsize=(20, 20))
columns =4
rows = 2
for idx in range (columns*rows):
ax = fig.add_subplot(rows, columns, idx+1, xticks=[], yticks=[])
plt.imshow(im_convert(images[idx]))
ax.set_title("{} ({})".format(cat_to_name[str(preds[idx])], cat_to_name[str(labels[idx].item())]),
color=("green" if cat_to_name[str(preds[idx])]==cat_to_name[str(labels[idx].item())] else "red"))
plt.show()
參考文獻(xiàn)
1.6-訓(xùn)練結(jié)果與模型保存_嗶哩嗶哩_bilibili文章來源地址http://www.zghlxwxcb.cn/news/detail-630700.html
到了這里,關(guān)于pytorch實(shí)戰(zhàn)-圖像分類(二)(模型訓(xùn)練及驗(yàn)證)(基于遷移學(xué)習(xí)(理解+代碼))的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!