網(wǎng)站seo診斷評分63淘寶指數(shù)查詢
人臉表情分類任務
- 注意:整個項目來自阿里云天池,下面是開發(fā)人員的聯(lián)系方式,本人僅作為學習記錄!!!
- 該文章原因,學習該項目,完善注釋內容,針對新版本的Pytorch進行部分代碼調整
- 本文章采用pytorch2.0.1版本,python3.10版本
源碼鏈接
這是一個使用pytorch實現(xiàn)的簡單的2分類任務
項目結構:- net.py: 網(wǎng)絡定義腳本- train.py:模型訓練腳本- inference.py:模型推理腳本- run_train.sh 訓練可執(zhí)行文件- run_inference.sh 推理可執(zhí)行文件# Copyright 2019 longpeng2008. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# If you find any problem,please contact us longpeng2008to2012@gmail.com
1. 網(wǎng)絡結構
# coding:utf8import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np# 3層卷積神經網(wǎng)絡simpleconv3定義
# 包括3個卷積層,3個BN層,3個ReLU激活層,3個全連接層class simpleconv3(nn.Module):# 初始化函數(shù)def __init__(self, nclass):# 繼承父類super(simpleconv3, self).__init__()# 3通道 輸入圖片大小為3*48*48,輸出特征圖大小為12*23*23,卷積核大小為3*3,步長為2'''輸出特征圖大小 = [(輸入大小 - 卷積核大小) / 步長] + 1輸入大小是 48x48卷積核大小是 3x3步長是 2將這些值代入公式,您將得到輸出特征圖的大小:輸出特征圖大小 = [(48 - 3) / 2] + 1 = (45 / 2) + 1 = 22.5 + 1 = 23'''self.conv1 = nn.Conv2d(3, 12, 3, 2)# 批量標準化操作 12個特征通道self.bn1 = nn.BatchNorm2d(12)# 輸入圖片大小為12*23*23,輸出特征圖大小為24*11*11,卷積核大小為3*3,步長為2'''輸出特征圖大小 = [(輸入大小 - 卷積核大小) / 步長] + 1輸入大小是 23x23卷積核大小是 3x3步長是 2將這些值代入公式,您將得到輸出特征圖的大小:輸出特征圖大小 = [(23 - 3) / 2] + 1 = (20 / 2) + 1 = 10 + 1 = 11'''self.conv2 = nn.Conv2d(12, 24, 3, 2)# 批量標準化操作 24個特征通道self.bn2 = nn.BatchNorm2d(24)# 輸入圖片大小為24*11*11,輸出特征圖大小為48*5*5,卷積核大小為3*3,步長為2'''輸出特征圖大小 = [(輸入大小 - 卷積核大小) / 步長] + 1輸入大小是 11x11卷積核大小是 3x3步長是 2將這些值代入公式,您將得到輸出特征圖的大小:輸出特征圖大小 = [(11 - 3) / 2] + 1 = (8 / 2) + 1 = 4 + 1 = 5'''self.conv3 = nn.Conv2d(24, 48, 3, 2)# 批量標準化操作 48個特征通道self.bn3 = nn.BatchNorm2d(48)# 輸入向量長為48*5*5=1200,輸出向量長為1200 展平self.fc1 = nn.Linear(48 * 5 * 5, 1200)# 1200 -> 128self.fc2 = nn.Linear(1200, 128) # 輸入向量長為1200,輸出向量長為128# 128 -> 類別數(shù)self.fc3 = nn.Linear(128, nclass) # 輸入向量長為128,輸出向量長為nclass,等于類別數(shù)# 前向函數(shù)def forward(self, x):# relu函數(shù),不需要進行實例化,直接進行調用# conv,fc層需要調用nn.Module進行實例化# 先卷積后標準化再激活x = F.relu(self.bn1(self.conv1(x)))x = F.relu(self.bn2(self.conv2(x)))x = F.relu(self.bn3(self.conv3(x)))# 更改形狀 改為1維x = x.view(-1, 48 * 5 * 5)# 全連接再激活x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = self.fc3(x)return xif __name__ == '__main__':import torchx = torch.randn(1, 3, 48, 48)model = simpleconv3(2)y = model(x)print(model)'''simpleconv3((conv1): Conv2d(3, 12, kernel_size=(3, 3), stride=(2, 2))(bn1): BatchNorm2d(12, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(conv2): Conv2d(12, 24, kernel_size=(3, 3), stride=(2, 2))(bn2): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(conv3): Conv2d(24, 48, kernel_size=(3, 3), stride=(2, 2))(bn3): BatchNorm2d(48, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(fc1): Linear(in_features=1200, out_features=1200, bias=True)(fc2): Linear(in_features=1200, out_features=128, bias=True)(fc3): Linear(in_features=128, out_features=2, bias=True))'''
2. 訓練函數(shù)
部分代碼內容與作者不同
- scheduler.step()與optimizer.step()修改前后順序
- RandomSizedCrop改為RandomCrop
- transforms.Scale修改為transforms.Resize
# coding:utf8
from __future__ import print_function, division
import os
import torch
import torch.nn as nn
import torch.optim as optim
# 使用tensorboardX進行可視化
from tensorboardX import SummaryWriter
from torch.optim import lr_scheduler
from torchvision import datasets, transformsfrom net import simpleconv3writer = SummaryWriter('logs') # 創(chuàng)建一個SummaryWriter的示例,默認目錄名字為runs# 訓練主函數(shù)
def train_model(model, criterion, optimizer, scheduler, num_epochs=25):"""訓練模型Args:model: 模型criterion: loss函數(shù)optimizer: 優(yōu)化器scheduler: 學習率調度器num_epochs: 訓練輪次Returns:"""# 開始訓練for epoch in range(num_epochs):# 打印訓練輪次print(f'Epoch {epoch+1}/{num_epochs}')for phase in ['train', 'val']:if phase == 'train':# 設置為訓練模式model.train(True)else:# 設置為驗證模式model.train(False)# 損失變量running_loss = 0.0# 精度變量running_accs = 0.0number_batch = 0# 從dataloaders中獲得數(shù)據(jù)for data in dataloaders[phase]:inputs, labels = dataif use_gpu:inputs = inputs.cuda()labels = labels.cuda()# 清空梯度optimizer.zero_grad()# 前向運行outputs = model(inputs)# 使用max()函數(shù)對輸出值進行操作,得到預測值索引_, preds = torch.max(outputs.data, 1)# 計算損失loss = criterion(outputs, labels)if phase == 'train':# 誤差反向傳播loss.backward()# 參數(shù)更新optimizer.step()running_loss += loss.data.item()running_accs += torch.sum(preds == labels).item()number_batch += 1# 調整學習率scheduler.step()# 得到每一個epoch的平均損失與精度epoch_loss = running_loss / number_batchepoch_acc = running_accs / dataset_sizes[phase]# 收集精度和損失用于可視化if phase == 'train':writer.add_scalar('data/trainloss', epoch_loss, epoch)writer.add_scalar('data/trainacc', epoch_acc, epoch)else:writer.add_scalar('data/valloss', epoch_loss, epoch)writer.add_scalar('data/valacc', epoch_acc, epoch)print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))writer.close()return modelif __name__ == '__main__':# 圖像統(tǒng)一縮放大小image_size = 60# 圖像裁剪大小,即訓練輸入大小crop_size = 48# 分類類別數(shù)nclass = 2# 創(chuàng)建模型model = simpleconv3(nclass)# 數(shù)據(jù)目錄data_dir = './data'# 模型緩存接口if not os.path.exists('models'):os.mkdir('models')# 檢查GPU是否可用,如果是使用GPU,否使用CPUuse_gpu = torch.cuda.is_available()if use_gpu:model = model.cuda()print(model)# 創(chuàng)建數(shù)據(jù)預處理函數(shù),訓練預處理包括隨機裁剪縮放、隨機翻轉、歸一化,驗證預處理包括中心裁剪,歸一化data_transforms = {'train': transforms.Compose([transforms.RandomCrop(48), # 隨機大小、長寬比裁剪圖片size=48 RandomSizedCrop改為RandomCroptransforms.RandomHorizontalFlip(), # 隨機水平翻轉 默認概率p=0.5transforms.ToTensor(), # 將原始的PILImage格式或者numpy.array格式的數(shù)據(jù)格式化為可被pytorch快速處理的張量類型transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) # 數(shù)據(jù)標準化 要將圖像三個通道的數(shù)據(jù) 整理到 [-1,1] 之間 ,可以加快模型的收斂]),'val': transforms.Compose([transforms.Resize(64), # Scale用于調整圖像的大小,現(xiàn)在采用transforms.Resize()代替transforms.CenterCrop(48), # 從圖像中心裁剪圖片尺寸size=48transforms.ToTensor(),transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])]),}# 使用torchvision的dataset ImageFolder接口讀取數(shù)據(jù)image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in ['train', 'val']}# 創(chuàng)建數(shù)據(jù)指針,設置batch大小,shuffle,多進程數(shù)量dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x],batch_size=16, # 每個小批次包含16個樣本shuffle=True, # 是否隨機打亂數(shù)據(jù)num_workers=4) # 加載數(shù)據(jù)的子進程數(shù)for x in ['train', 'val']}# 獲得數(shù)據(jù)集大小dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}# 優(yōu)化目標使用交叉熵,優(yōu)化方法使用帶動量項的SGD,學習率迭代策略為step,每隔100個epoch,變?yōu)樵瓉淼?.1倍criterion = nn.CrossEntropyLoss()# 優(yōu)化器 傳入權重閾值,學習率0.1 動量(momentum)是一個控制梯度下降方向的超參數(shù)。# 它有助于加速訓練,特別是在存在平坦區(qū)域或局部極小值時。動量的值通常在0到1之間。較大的動量值會使參數(shù)更新更平滑。在這里,動量設置為0.9。optimizer_ft = optim.SGD(model.parameters(), lr=0.1, momentum=0.9)'''lr_scheduler.StepLR 是PyTorch中的學習率調度器(learning rate scheduler),用于在訓練神經網(wǎng)絡時動態(tài)調整學習率。lr_scheduler.StepLR 允許您在訓練的不同階段逐步減小學習率,以幫助優(yōu)化過程。optimizer_ft:這是您用于優(yōu)化模型參數(shù)的優(yōu)化器,通常是 optim.SGD 或其他PyTorch優(yōu)化器的實例。學習率調度器將監(jiān)控這個優(yōu)化器的狀態(tài),并根據(jù)其規(guī)則更新學習率。step_size=100:這是學習率更新的周期,也稱為學習率下降步數(shù)。在每個 step_size 個訓練周期之后,學習率將減小。gamma=0.1:這是學習率減小的因子。在每個 step_size 個訓練周期之后,學習率將乘以 gamma。這意味著學習率將以 gamma 的倍數(shù)逐步減小。'''exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=100, gamma=0.1)model = train_model(model=model,criterion=criterion,optimizer=optimizer_ft,scheduler=exp_lr_scheduler,num_epochs=10)torch.save(model.state_dict(), 'models/model.pt')
3. 預測
執(zhí)行以下內容,或者自行安排數(shù)據(jù)集
## 使用方法 python3 inference.py 模型路徑 圖片路徑
python3 inference.py models/model.pt data/train/0/1neutral.jpg
python3 inference.py models/model.pt data/train/1/1smile.jpg
# coding:utf8import sys
import numpy as np
import torch
from PIL import Image
from torchvision import transforms# 全局變量
# sys.argv[1] 權重文件
# sys.argv[2] 圖像文件夾testsize = 48 # 測試圖大小
from net import simpleconv3# 定義模型
net = simpleconv3(2)
# 設置推理模式,使得dropout和batchnorm等網(wǎng)絡層在train和val模式間切換
net.eval()
# 停止autograd模塊的工作,以起到加速和節(jié)省顯存
torch.no_grad()# 載入模型權重
modelpath = sys.argv[1]
net.load_state_dict(torch.load(modelpath, map_location=lambda storage, loc: storage))# 定義預處理函數(shù)
data_transforms = transforms.Compose([transforms.Resize(48),transforms.ToTensor(),transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])])# 讀取3通道圖片,并擴充為4通道tensor
imagepath = sys.argv[2]
image = Image.open(imagepath)
imgblob = data_transforms(image).unsqueeze(0)# 獲得預測結果predict,得到預測的標簽值label
predict = net(imgblob)
index = np.argmax(predict.detach().numpy())
# print(predict)
# print(index)if index == 0:print('the predict of ' + sys.argv[2] + ' is ' + str('none'))
else:print('the predict of ' + sys.argv[2] + ' is ' + str('smile'))
4. TensorBoardX的使用
TensorBoardX 是一個用于在 PyTorch 中可視化訓練過程和結果的工具。它是 TensorBoard 的 Python 版本,用于創(chuàng)建交互式、實時的訓練和評估圖表。以下是一些使用 TensorBoardX 的一般步驟:
-
安裝 TensorBoardX:首先,您需要安裝 TensorBoardX 庫。您可以使用以下命令安裝它:
pip install tensorboardX
-
導入庫:在您的 PyTorch 代碼中,導入 TensorBoardX 庫:
from tensorboardX import SummaryWriter
-
創(chuàng)建 SummaryWriter:創(chuàng)建一個
SummaryWriter
對象,以將日志數(shù)據(jù)寫入 TensorBoard 日志目錄。writer = SummaryWriter()
-
記錄數(shù)據(jù):在訓練循環(huán)中,使用
writer.add_*
方法來記錄各種數(shù)據(jù),例如標量、圖像、直方圖等。以下是一些示例:-
記錄標量數(shù)據(jù):
writer.add_scalar('loss', loss, global_step)
-
記錄圖像數(shù)據(jù):
writer.add_image('image', image, global_step)
-
記錄直方圖數(shù)據(jù):
writer.add_histogram('weights', model.conv1.weight, global_step)
-
記錄文本數(shù)據(jù):
writer.add_text('description', 'This is a description.', global_step)
-
-
啟動 TensorBoard 服務器:在命令行中,使用以下命令啟動 TensorBoard 服務器:
tensorboard --logdir=/path/to/log/directory
其中
/path/to/log/directory
是存儲 TensorBoardX 日志的目錄。 -
查看可視化結果:在瀏覽器中打開 TensorBoard 的 Web 界面,通常位于
http://localhost:6006
,您可以在該界面上查看可視化結果。
請注意,您可以根據(jù)需要記錄不同類型的數(shù)據(jù),并根據(jù)訓練過程的不同階段定期記錄數(shù)據(jù)。TensorBoardX 提供了豐富的可視化工具,以幫助您監(jiān)視和分析模型的訓練過程。
確保在訓練循環(huán)中適時記錄數(shù)據(jù),并使用 TensorBoardX 查看結果,以更好地理解和改進您的深度學習模型。
這是 TensorBoard 啟動時的一般信息,表明TensorBoard運行在本地主機(localhost)。如果您想使 TensorBoard 可以在網(wǎng)絡上訪問,可以采取以下幾種方法:
-
使用代理:您可以使用代理服務器來將 TensorBoard 的端口暴露到網(wǎng)絡上。這通常需要在代理服務器上進行一些配置,以便外部用戶可以訪問 TensorBoard。代理服務器可以是諸如 Nginx 或 Apache 之類的 Web 服務器。
-
使用 --bind_all 參數(shù):在啟動 TensorBoard 時,您可以使用
--bind_all
參數(shù),以將 TensorBoard 綁定到所有網(wǎng)絡接口。這樣,TensorBoard 將可以在本地網(wǎng)絡上的任何 IP 地址上訪問,而不僅僅是本地主機。例如:tensorboard --logdir=/path/to/log/directory --bind_all
-
使用 --host 參數(shù):您還可以使用
--host
參數(shù)來指定 TensorBoard 的主機名(hostname),以使其在指定的主機上可用。例如:tensorboard --logdir=/path/to/log/directory --host=0.0.0.0
這將允許 TensorBoard 在所有網(wǎng)絡接口上運行,從而在網(wǎng)絡上的任何 IP 地址上訪問。
請根據(jù)您的需求和網(wǎng)絡設置選擇適當?shù)姆椒?。如果只需要在本地訪問 TensorBoard,無需進行任何更改。如果需要在網(wǎng)絡上訪問,可以使用上述選項之一。不過,請注意,為了安全起見,最好將 TensorBoard 限制在受信任的網(wǎng)絡上,或者使用身份驗證和授權來保護訪問。
效果展示