在煉丹師的路上越走越遠,開始入手pytorch框架的學習,越煉越熟吧。。。html
1. 張量的建立和操做git
建立爲初始化矩陣,並初始化github
a = torch.empty(5, 3) #建立一個5*3的未初始化矩陣 nn.init.zeros_(a) #初始化a爲0 nn.init.constant_(a, 3) # 初始化a爲3 nn.init.uniform_(a) #初始化爲uniform分佈
隨機數矩陣網絡
torch.rand(5, 3) # 5*3 , [0, 1)的隨機數
torch.rand_like(m) #建立和m的size同樣的隨機數矩陣 torch.rand(3, 3) # 5*3 , mean=0, variance=1,的正態分佈 torch.randint(1, 10, (3,3)) #3*3的整數矩陣(1-10之間)
tensor類型和形狀架構
a = torch.Tensor([1, 2, 3]) #經過列表建立Tensor
a = torch.eye(3,4) #對角矩形
a = torch.ones(3,4)
b = torch.ones_like(a) #建立和a同樣size a = torch.zeros(5, 3, dtype = torch.long) a.dtype #查看數據類型 32位浮點型:torch.Float (默認的就是這種類型, float32) 64位整型:torch.Long (int64 ) 32位整型:torch.Int (int32) 16位整型:torch.Short (int16) 64位浮點型:torch.Double (float64) a.size() h, w = torch.Size([5, 3]) #5*3維 ,h=5, w=3 a.view(3, 5) #將a進行reshape成3*5的矩陣 a.view(-1, 15) # -1表示自動計算, 即轉變爲1*15
tensor和numpy(array)的相互轉換app
b = a.numpy() #Tensor轉numpy c = np.ones((3,3)) d = torch.from_numpy(c) #numpy 轉tensor
2. 張量的操做框架
索引: 支持numpy的經常使用索引和切片操做dom
加法:和numpy相似的廣播原則ide
索引操做 y = torch.rand(5,3) y[1:, 2] 切片和索引 y[y>0.5] 花式索引 加法操做(和numpy同樣的廣播原則) result = x+y reslut = torch.add(x, y) y.add_(x) #直接對y的值進行修改, (以_結尾的方法會直接在原地修改變量, 如x.copy_(y), x.t_()會修改x.) result = torch.empty(5,3) torch.add(x, y, out=result) #這裏的result必須先定義 對於一個元素的張量,能夠直接經過x.item()拿到元素值 x = torch.ones(3,4) y = torch.sum(x) print(y.item(0)) #獲得整數12.0
cuda Tensor: pytorch 支持Gpu操做,能夠在Gpu上建立tensor,經過to()方法能夠在cpu和Gpu上間轉換tensor函數
if torch.cuda.is_available(): device = torch.device("cuda") y = torch.ones_like(x, device=device) #直接在Gpu上建立tensor x = x.to(device) #從cpu上轉移到gpu z = x+y print(z.to("cpu", torch.double)) #轉回到cpu,並改變數據類型
3. 自動求導(Autograd)
在pytorch搭建的神經網絡中,Tensor 和Function爲最主要的兩個類,一塊兒組成了一個無環圖。 在前向傳播時,Function操做tensor的值,而進行反向傳播時,須要計算function的導數來更新參數tensor, pytorch爲咱們自動實現了求導。每個tensor都有一個requires_grad屬性,若tensor.requires_grad=True, 則無環圖會記錄對該tensor的全部操做,當進行backward時,pytorch就會自動計算其導數值,並保存在tensor的grad屬性中。
x = torch.ones(2, 2, requires_grad=True) #設置requires_grad=True, backward時會計算導數 y = x+2 屬性值 y.requirs_grad 是否autograd, 會自動繼承x的requires_grad y.grad 導數或梯度值 y.grad_fn 對x的操做function,grad_fn=<AddBackward0> tensor.detach() 將tensor從計算曆史(無環圖)中脫離出來? with torch.no_grad(): 從計算曆史(無環圖)中脫離, backward時不求導 with torch.set_grad_enabled(phase == 'train'): (phase == 'train')爲True時求導 tensor.backward() #反向傳播, 計算梯度,若是tensor只包含一個數時,backward不須要參數, 不然須要指明參數
backward:
#out爲標量,因此backward時不帶參數 x = torch.ones(2, 2, requires_grad=True) y = x+2 z = y*y*3 out = z.mean() out.backward() print(x.grad) #tensor([[4.5000, 4.5000],[4.5000, 4.5000]]) print(y.grad) #None
backward計算過程以下:
#y不爲爲標量,backward時須要帶參數 x = torch.ones(2, 2, requires_grad=True) y = 2*x+2 y.backward(torch.tensor([[1,1],[1,1]], dtype=torch.float)) #能夠理解爲tensor([1, 1, 1, 1]) * dy/dx print(x.grad) # tensor([[2.,2.],[2.,2.]]) #y不爲爲標量,backward時須要帶參數 x = torch.ones(2, 2, requires_grad=True) y = 2*x+2 y.backward(torch.tensor([[1,0.1],[1,0.1]], dtype=torch.float)) #能夠理解爲tensor([1, 0.1, 1, 0.1]) * dy/dx print(x.grad) # tensor([[2.0000,0.2000],[2.0000,0.2000]])
4. 神經網絡(Neutral Networks)
訓練神經網絡的典型步驟以下:
定義神經網絡(權重參數)
在數據集上進行迭代
前向傳播,神經網絡逐層計算輸入的數據
計算loss(神經網絡的計算值和正確值的距離)
計算梯度,反向傳遞到神經網絡中,更新神經網絡的權重(weight = weight - learning_rate * gradient)
4.1 定義神經網絡
定義一個神經網絡,須要繼承torch.nn.Module, 並實現初始化方法和前向傳播。下面代碼爲AlexNet的實現(經過三種方式定義網絡結構):
#coding:utf-8 #pytorch implementation of AlexNet import torch.nn as nn import torch.nn.functional as F import torch class AlexNet(nn.Module): def __init__(self): # image size 227*227 super(AlexNet, self).__init__() self.features = nn.Sequential( nn.Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2,2)), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False), # when ceil_mode is False, floor will be used to calculate the shape, else ceil nn.Conv2d(64, 192, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2)), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False), nn.Conv2d(192, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(inplace=True), nn.Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(inplace=True), nn.Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) ) # self.avgpool = nn.AdaptiveAvgPool2d(output_size=(6,6)) #使輸出的形狀變成6*6 self.classifier=nn.Sequential( nn.Dropout(p=0.5), nn.Linear(in_features=9216, out_features=4096, bias=True), nn.ReLU(inplace=True), nn.Dropout(p=0.5), nn.Linear(in_features=4096, out_features=4096, bias=True), nn.ReLU(inplace=True), nn.Linear(in_features=4096, out_features=1000, bias=True) ) def forward(self, x): x = self.features(x) x = x.view(-1, 256*6*6) x = self.classifier(x) return x class AlexNet1(nn.Module): def __init__(self): super(AlexNet1, self).__init__() self.conv1 = nn.Sequential( nn.Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2, 2)), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False), ) self.conv2 = nn.Sequential( nn.Conv2d(64, 192, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2)), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False), ) self.conv3 = nn.Sequential( nn.Conv2d(192, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(inplace=True), ) self.conv4 = nn.Sequential( nn.Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(inplace=True), ) self.conv5 = nn.Sequential( nn.Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) ) self.classifier=nn.Sequential( nn.Dropout(p=0.5), nn.Linear(in_features=9216, out_features=4096, bias=True), nn.ReLU(inplace=True), nn.Dropout(p=0.5), nn.Linear(in_features=4096, out_features=4096, bias=True), nn.ReLU(inplace=True), nn.Linear(in_features=4096, out_features=1000, bias=True) ) def forward(self, x): x = self.conv1(x) x = self.conv2(x) x = self.conv3(x) x = self.conv4(x) x = self.conv5(x) x = x.view(-1, 256*6*6) x = self.classifier(x) return x class AlexNet2(nn.Module): def __init__(self): super(AlexNet2, self).__init__() self.conv1 = nn.Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4), padding=(2,2)) self.relu1 = nn.ReLU(inplace=True) self.pool1 = nn.MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False) # when ceil_mode is False, floor will be used to calculate the shape, else ceil self.conv2 = nn.Conv2d(64, 192, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2)) self.relu2 = nn.ReLU(inplace=True) self.pool2 = nn.MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=False) self.conv3 = nn.Conv2d(192, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) self.relu3 = nn.ReLU(inplace=True) self.conv4 = nn.Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) self.relu4 = nn.ReLU(inplace=True) self.conv5 = nn.Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) self.relu5 = nn.ReLU(inplace=True) self.pool5 = nn.MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) self.dropout = nn.Dropout(p=0.5) self.linear1 = nn.Linear(in_features=9216, out_features=4096, bias=True) self.relu6 = nn.ReLU(inplace=True) self.linear2 = nn.Linear(in_features=4096, out_features=4096, bias=True) self.relu7 = nn.ReLU(inplace=True) self.linear3 = nn.Linear(in_features=4096, out_features=1000, bias=True) def forward(self, x): x = self.pool1(self.relu1(self.conv1(x))) x = self.pool2(self.relu2(self.conv2(x))) x = self.relu3(self.conv3(x)) x = self.relu4(self.conv4(x)) x = self.pool5(self.relu5(self.conv5(x))) x = x.view(-1, 256*6*6) x = self.dropout(x) x = self.dropout(self.relu6(self.linear1(x))) x = self.relu7(self.linear2(x)) x = self.linear3(x) return x if __name__=="__main__": # net = AlexNet() # net = AlexNet1() net = AlexNet2() input = torch.randn(1,3,227,227) #torch中net輸入爲四維的: batch_size*channel*W*H output = net(input) print(output)
4.2 定義loss函數
pytorch中經常使用的loss函數有:
nn.L1Loss()
nn.L1Loss(): 取預測值和真實值差的絕對值,最後平均數 x = torch.tensor([[1,1],[2,2]], dtype=torch.float) #計算loss時須要float類型 y = torch.tensor([[3,3],[4,4]], dtype=torch.float) criterion = nn.L1Loss() loss = criterion(x, y) #(|3-1|+|3-1|+|4-2|+|4-2|)/4=2.0 print(loss.item()) # 2.0
nn.SmoothL1Loss(size_average=None, reduce=True,reduction='mean')
#reduce爲False時, 返回向量, 返回整個bacth的每個loss
# reduce 默認爲True, 返回標量, size_average爲True時返回batch_loss的平均值, 爲False時返回batch_loss的和(size_average廢棄,由reduction取代)
nn.SmoothL1Loss(): 在(-1, 1)範圍內是平方loss(L2 loss), 其餘範圍內是L1 loss x = torch.tensor([[1,1],[2,2]], dtype=torch.float) #計算loss時須要float類型 y = torch.tensor([[1.5,1.5],[4,4]], dtype=torch.float) criterion = nn.SmoothL1Loss() loss = criterion(x, y) #(((1.5-1)**2)/2+((1.5-1)**2)/2+|4-2|-0.5+|4-2|-0.5)/4=2.0 print(loss.item()) # 0.8125
nn.MSELoss()
nn.MSELoss(): 平方loss(L2 loss), 最後平均數 x = torch.tensor([[1,1],[2,2]], dtype=torch.float) #計算loss時須要float類型 y = torch.tensor([[3,3],[4,4]], dtype=torch.float) criterion = nn.MSELoss() loss = criterion(x, y) #((3-1)**2+(3-1)**2+(4-2)**2+4-2)**2)/4=4.0 print(loss.item()) # 4
nn.NLLLoss() : 負對數似然損失函數(Negative Log Likelihood)
和CrossEntropyLoss()的惟一區別是,不會對輸入值進行softmax計算。(所以model計算輸出時,最後一層須要加上LogSoftmax)
(假如x=[1, 2, 3], class=0; 則f=x[0]=1)
nn.NLLLoss() : 負對數似然損失函數(Negative Log Likelihood) input = torch.randn(3, 5, requires_grad=True) target = torch.empty(3, dtype=torch.long).random_(5) #必須是torch.long類型 criterion = nn.NLLLoss() loss = criterion(nn.LogSoftmax(dim=1)(input), target) print(loss.item())
nn.CrossEntropyLoss() 交叉熵函數
nn.LogSoftmax()和nn.NLLLoss()結合體:會對輸入值使用softmax,再進行計算(所以model計算輸出時不須要進行softmax)
(參考: https://www.cnblogs.com/marsggbo/p/10401215.html)
loss = nn.CrossEntropyLoss() #input = torch.randn(3, 5, requires_grad=True) #target = torch.empty(3, dtype=torch.long).random_(5) #必須是torch.long類型 loss = nn.CrossEntropyLoss() input = torch.tensor([[-0.7678, 0.2773, -0.9249, 1.4503, 0.5256], [-0.8529, -1.4283, -0.3284, 1.8608, -0.3206], [ 0.1201, -0.7239, 0.6798, -0.8335, -2.1710]], requires_grad=True) target = torch.tensor([0, 0, 1], dtype=torch.long) #必須是torch.long類型, 且input中每一行對應的target中的一個數字(不是one-hot) output = loss(input, target) print(output.item()) #nn.LogSoftmax()和nn.NLLLoss()分開計算以下: input = torch.tensor([[-0.7678, 0.2773, -0.9249, 1.4503, 0.5256], [-0.8529, -1.4283, -0.3284, 1.8608, -0.3206], [ 0.1201, -0.7239, 0.6798, -0.8335, -2.1710]], requires_grad=True) target = torch.tensor([0, 0, 1], dtype=torch.long) #必須是torch.long類型 sft = nn.LogSoftmax(dim=1)(input) nls = nn.NLLLoss()(sft, target)
numpy實現交叉熵函數
def label_encoder(target, nclass): label = np.zeros((target.shape[0],nclass)) for i in range(target.shape[0]): label[i][target[i]]=1 print(label) return label def cross_entropy_loss(pred, target): target = label_encoder(target, pred.shape[1]) #one-hot編碼 pred_exp = np.exp(pred) pred_sft = pred_exp/(np.sum(pred_exp, axis=1)[:,None]) print(np.log(pred_sft)) loss = -np.sum(np.log(pred_sft)*target)/pred.shape[0] #取一個batch的平均值 print(loss) return loss if __name__=="__main__": input = np.array([[-0.7678, 0.2773, -0.9249, 1.4503, 0.5256], [-0.8529, -1.4283, -0.3284, 1.8608, -0.3206], [ 0.1201, -0.7239, 0.6798, -0.8335, -2.1710]]) target = np.array([0, 0, 1]) loss = cross_entropy_loss(input,target)
nn.BCELoss() 二分類時的交叉熵(Bianry cross entropy),
nn.CrossEntropyLoss()的特例,即分類限定爲二分類,label必須爲0,1; 模型輸出最後一層須要用sigmoid函數
criterion = nn.BCELoss() input = torch.randn(5, 1, requires_grad=True) target = torch.empty(5,1).random_(2) pre = nn.Sigmoid()(input) loss = criterion(pre, target) print(loss.item())
nn.BCEWithLogitsLoss(): 將nn.sigmoid()和nn.BCELoss()結合
criterion = nn.BCEWithLogitsLoss() input = torch.randn(5, 1, requires_grad=True) target = torch.empty(5,1).random_(2) loss = criterion(input, target) print(loss.item())
4.3 定義優化器
經過loss函數計算出網絡的預測值和真實值之間的loss後,loss.backward()能將梯度反向傳播,須要根據梯度來更新網絡的權重係數。優化器能幫咱們實現權重係數的更新。
不採用優化器:
#不用優化器,更新系數
criterion = nn.CrossEntropyLoss() input = torch.randn(5, 2, requires_grad=True) target = torch.empty(5,1).random_(2) pre = net(input) loss = criterion(pre, target) net.zero_grad() loss.backward() learing_rate=0.01 for f in net.parameters(): f.data.sub_(f.grad.data*learing_rate) #更新系數
採用優化器:
#採用優化器,更新系數 import torch.optim as optim optimizer = optim.SGD(net.parameters(), lr=0.01) criterion = nn.CrossEntropyLoss() input = torch.randn(5, 2, requires_grad=True) target = torch.empty(5,1).random_(2) optimizer.zero_grad() pre = net(input) loss = criterion(pre, target) loss.backward() optimizer.step() #更新系數
4.4 定義DataLoader
進行網絡訓練時,能夠一次性導入全部數據(BGD,batch gradient descent), 能夠一次導入一條數據(SGD, stochastic gradient descent),還能夠一次導入部分數據(MBGD, Mini-batch gradient descent), 目前MBGD爲較爲經常使用的方法。並且通常狀況,無特殊說明時,論文裏面說起SGD,都指代的時MGBD的方式進行數據導入和網絡訓練。
採用pytorch自帶的數據集:
#採用pytorch自帶的數據集 import torchvision import torchvision.transform as transforms transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5),(0.5, 0.5, 0.5)) ] ) trainset = torchvision.datasets.CIFAR10(root="./data", train=True, download=True, transform = transform) #下載數據集 trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=2) #每個mini-batch,導入4條數據
採用自定義的數據集:
class RandomDataset(Dataset): def __init__(self, size, length): self.len = length self.data = torch.randn(length, size) def __getitem__(self, index): return self.data[index] def __len__(self): return self.len rand_loader = DataLoader(dataset=RandomDataset(input_size, data_size), batch_size=batch_size, shuffle=True)
class MyDataset(Dataset): def __init__(self, root_dir, annotations_file, transform=None): self.root_dir = root_dir self.annotations_file = annotations_file self.transform = transform if not os.path.isfile(self.annotations_file): print(self.annotations_file + 'does not exist!') self.file_info = pd.read_csv(annotations_file, index_col=0) self.size = len(self.file_info) def __len__(self): return self.size def __getitem__(self, idx): image_path = self.file_info['path'][idx] if not os.path.isfile(image_path): print(image_path + ' does not exist!') return None image = Image.open(image_path).convert('RGB') label_species = int(self.file_info.iloc[idx]['species']) sample = {'image': image, 'species': label_species} if self.transform: sample['image'] = self.transform(image) return sample train_transforms = transforms.Compose([transforms.Resize((600, 600)), transforms.RandomCrop(500), transforms.RandomHorizontalFlip(), transforms.ToTensor(), ]) val_transforms = transforms.Compose([transforms.Resize((500, 500)), transforms.ToTensor() ]) train_dataset = MyDataset(root_dir= ROOT_DIR + TRAIN_DIR, annotations_file= TRAIN_ANNO, transform=train_transforms) test_dataset = MyDataset(root_dir= ROOT_DIR + VAL_DIR, annotations_file= VAL_ANNO, transform=val_transforms) train_loader = DataLoader(dataset=train_dataset, batch_size=8, shuffle=True) test_loader = DataLoader(dataset=test_dataset)
4.5 神經網絡訓練:
準備好數據集,定義好模型,loss函數,優化器,數據迭代器,即可以進行網絡訓練了。下面是一個簡單的圖片分類器,將圖片分紅三類:兔子,老鼠,雞
A. 準備數據集的label文件:
import pandas as pd import os from PIL import Image # ROOTS = '../Dataset/' ROOTS = '/home/ai/project/data/project_I/Dataset/' PHASE = ['train', 'val'] SPECIES = ['rabbits', 'rats', 'chickens'] # [0,1,2] DATA_info = {'train': {'path': [], 'species': []}, 'val': {'path': [], 'species': []} } for p in PHASE: for s in SPECIES: DATA_DIR = ROOTS + p + '/' + s DATA_NAME = os.listdir(DATA_DIR) for item in DATA_NAME: try: img = Image.open(os.path.join(DATA_DIR, item)) except OSError: pass else: DATA_info[p]['path'].append(os.path.join(DATA_DIR, item)) if s == 'rabbits': DATA_info[p]['species'].append(0) elif s == 'rats': DATA_info[p]['species'].append(1) else: DATA_info[p]['species'].append(2) ANNOTATION = pd.DataFrame(DATA_info[p]) ANNOTATION.to_csv('Species_%s_annotation.csv' % p) print('Species_%s_annotation file is saved.' % p)
生成的label文件格式以下,包括圖片路徑,以及對應的分類(0,1, 2依次表明'rabbits', 'rats', 'chickens')
B. 定義網絡結構:
能夠本身搭建全新的網絡結構, 也能夠採用成熟的網絡架構,如AlexNet,VGG,GoogleNet, Resnet等。下面分別展現了自定義和借用Resnet網絡結構:
import torch.nn as nn import torchvision import torch.nn.functional as F class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(3, 3, 3) self.maxpool1 = nn.MaxPool2d(kernel_size=2) self.relu1 = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(3, 6, 3) self.maxpool2 = nn.MaxPool2d(kernel_size=2) self.relu2 = nn.ReLU(inplace=True) self.conv3 = nn.Conv2d(6,12,3) self.fc1 = nn.Linear(12 * 123 * 123, 150) self.relu3 = nn.ReLU(inplace=True) self.drop = nn.Dropout2d() self.fc2 = nn.Linear(150, 3) # self.softmax1 = nn.Softmax(dim=1) def forward(self, x): x = self.conv1(x) x = self.maxpool1(x) x = self.relu1(x) x = self.conv2(x) x = self.maxpool2(x) x = self.relu2(x) x =nn.ReLU(nn.MaxPool2d(self.conv3(x),kernel_size=3)) # print(x.shape) x = x.view(-1, 12 * 123 * 123) x = self.fc1(x) x = self.relu3(x) x = F.dropout(x, training=self.training) x_species = self.fc2(x) # x_species = self.softmax1(x_species) return x_species
import torch.nn as nn import torchvision import torch.nn.functional as F from torchvision import models class Net(nn.Module): def __init__(self, model): super(Net, self).__init__() # self.conv1 = nn.Conv2d(3, 64, 3, padding=1) # self.conv2 = nn.Conv2d(64, 64, 3, padding=1) # self.conv3 = nn.Conv2d(64, 128, 3, padding=1) # self.conv4 = nn.Conv2d(128, 128, 3, padding=1) # self.conv5 = nn.Conv2d(128, 256, 3, padding=1) # self.conv6 = nn.Conv2d(256, 256, 3, padding=1) # self.conv7 = nn.Conv2d(256, 256, 3, padding=1) self.resnet18_layer = nn.Sequential(*list(model.children())[:-1]) self.fc1 = nn.Linear(512 * 1 * 1, 150) self.relu3 = nn.ReLU(inplace=True) # self.drop = nn.Dropout2d() self.fc2 = nn.Linear(150, 3) self.softmax1 = nn.Softmax(dim=1) def forward(self, x): # x = F.relu(self.conv1(x)) # x = F.max_pool2d(F.relu(self.conv2(x)),2) # x = F.relu(self.conv3(x)) # x = F.max_pool2d(F.relu(self.conv4(x)),2) # x = F.relu(self.conv5(x)) # x = F.relu(self.conv6(x)) # x = F.max_pool2d(F.relu(self.conv7(x)),2) x = self.resnet18_layer(x) # x = F.dropout(x, self.training) #print(x.shape) x = x.view(-1, 512 * 1 * 1) x = self.fc1(x) x = self.relu3(x) # x = F.dropout(x, training=self.training) x_species = self.fc2(x) #x_species = self.softmax1(x_species) return x_species
C. 定義訓練主函數:
訓練網絡時,能夠對自定義的網絡從頭訓練,也能夠採用在ImageNet上預訓練好的網絡,進行finetune,這裏採用預訓練好的Resnet16.
import os import matplotlib.pyplot as plt from torch.utils.data import DataLoader import torch from Species_Network import * from torchvision.transforms import transforms from PIL import Image import pandas as pd import random from torch import optim from torch.optim import lr_scheduler import copy ROOT_DIR = '../Dataset/' TRAIN_DIR = 'train/' VAL_DIR = 'val/' TRAIN_ANNO = 'Species_train_annotation.csv' VAL_ANNO = 'Species_val_annotation.csv' CLASSES = ['Mammals', 'Birds'] SPECIES = ['rabbits', 'rats', 'chickens'] class MyDataset(): def __init__(self, root_dir, annotations_file, transform=None): self.root_dir = root_dir self.annotations_file = annotations_file self.transform = transform if not os.path.isfile(self.annotations_file): print(self.annotations_file + 'does not exist!') self.file_info = pd.read_csv(annotations_file, index_col=0) self.size = len(self.file_info) def __len__(self): return self.size def __getitem__(self, idx): image_path = self.file_info['path'][idx] if not os.path.isfile(image_path): print(image_path + ' does not exist!') return None image = Image.open(image_path).convert('RGB') label_species = int(self.file_info.iloc[idx]['species']) sample = {'image': image, 'species': label_species} if self.transform: sample['image'] = self.transform(image) return sample train_transforms = transforms.Compose([transforms.Resize((500, 500)), transforms.RandomHorizontalFlip(), transforms.ToTensor(), ]) val_transforms = transforms.Compose([transforms.Resize((500, 500)), transforms.ToTensor() ]) train_dataset = MyDataset(root_dir= ROOT_DIR + TRAIN_DIR, annotations_file= TRAIN_ANNO, transform=train_transforms) test_dataset = MyDataset(root_dir= ROOT_DIR + VAL_DIR, annotations_file= VAL_ANNO, transform=val_transforms) train_loader = DataLoader(dataset=train_dataset, batch_size=128, shuffle=True) test_loader = DataLoader(dataset=test_dataset) data_loaders = {'train': train_loader, 'val': test_loader} device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") print(device) def visualize_dataset(): print(len(train_dataset)) idx = random.randint(0, len(train_dataset)) sample = train_loader.dataset[idx] print(idx, sample['image'].shape, SPECIES[sample['species']]) img = sample['image'] plt.imshow(transforms.ToPILImage()(img)) plt.show() visualize_dataset() def train_model(model, criterion, optimizer, scheduler, num_epochs=50): Loss_list = {'train': [], 'val': []} Accuracy_list_species = {'train': [], 'val': []} best_model_wts = copy.deepcopy(model.state_dict()) best_acc = 0.0 for epoch in range(num_epochs): print('Epoch {}/{}'.format(epoch, num_epochs - 1)) print('-*' * 10) # Each epoch has a training and validation phase for phase in ['train', 'val']: if phase == 'train': model.train() else: model.eval() running_loss = 0.0 corrects_species = 0 for idx,data in enumerate(data_loaders[phase]): #print(phase+' processing: {}th batch.'.format(idx)) inputs = data['image'].to(device) labels_species = data['species'].to(device) optimizer.zero_grad() with torch.set_grad_enabled(phase == 'train'): x_species = model(inputs) x_species = x_species.view(-1,3) _, preds_species = torch.max(x_species, 1) loss = criterion(x_species, labels_species) if phase == 'train': loss.backward() optimizer.step() running_loss += loss.item() * inputs.size(0) corrects_species += torch.sum(preds_species == labels_species) epoch_loss = running_loss / len(data_loaders[phase].dataset) Loss_list[phase].append(epoch_loss) epoch_acc_species = corrects_species.double() / len(data_loaders[phase].dataset) epoch_acc = epoch_acc_species Accuracy_list_species[phase].append(100 * epoch_acc_species) print('{} Loss: {:.4f} Acc_species: {:.2%}'.format(phase, epoch_loss,epoch_acc_species)) if phase == 'val' and epoch_acc > best_acc: best_acc = epoch_acc_species best_model_wts = copy.deepcopy(model.state_dict()) print('Best val species Acc: {:.2%}'.format(best_acc)) model.load_state_dict(best_model_wts) torch.save(model.state_dict(), 'best_model.pt') print('Best val species Acc: {:.2%}'.format(best_acc)) return model, Loss_list,Accuracy_list_species network = Net().to(device) optimizer = optim.SGD(network.parameters(), lr=0.005, momentum=0.9, weight_decay=1e-6) #weight_decay:L2正則項懲罰 criterion = nn.CrossEntropyLoss() exp_lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=1, gamma=0.1) # Decay LR by a factor of 0.1 every 1 epochs model, Loss_list, Accuracy_list_species = train_model(network, criterion, optimizer, exp_lr_scheduler, num_epochs=100) x = range(0, 100) y1 = Loss_list["val"] y2 = Loss_list["train"] plt.plot(x, y1, color="r", linestyle="-", marker="o", linewidth=1, label="val") plt.plot(x, y2, color="b", linestyle="-", marker="o", linewidth=1, label="train") plt.legend() plt.title('train and val loss vs. epoches') plt.ylabel('loss') plt.savefig("train and val loss vs epoches.jpg") plt.close('all') # 關閉圖 0 y5 = Accuracy_list_species["train"] y6 = Accuracy_list_species["val"] plt.plot(x, y5, color="r", linestyle="-", marker=".", linewidth=1, label="train") plt.plot(x, y6, color="b", linestyle="-", marker=".", linewidth=1, label="val") plt.legend() plt.title('train and val Species acc vs. epoches') plt.ylabel('Species accuracy') plt.savefig("train and val Species acc vs epoches.jpg") plt.close('all') ######################################## Visualization ################################## def visualize_model(model): model.eval() with torch.no_grad(): for i, data in enumerate(data_loaders['val']): inputs = data['image'] labels_species = data['species'].to(device) x_species = model(inputs.to(device)) x_species = x_species.view( -1,2) _, preds_species = torch.max(x_species, 1) print(inputs.shape) plt.imshow(transforms.ToPILImage()(inputs.squeeze(0))) plt.title('predicted species: {}\n ground-truth species:{}'.format(SPECIES[preds_species],SPECIES[labels_species])) plt.show() visualize_model(model)
import os import matplotlib.pyplot as plt from torch.utils.data import Dataset, DataLoader import torch from Spe_Network import * from torchvision.transforms import transforms from PIL import Image import pandas as pd import random from torch import optim from torch.optim import lr_scheduler import copy from torchvision import models import logging logging.basicConfig(level=logging.DEBUG, filename="train.log", filemode="a+") ROOT_DIR = '/home/ai/project/data/project_I/Dataset/' TRAIN_DIR = 'train/' VAL_DIR = 'val/' TRAIN_ANNO = 'Species_train_annotation.csv' VAL_ANNO = 'Species_val_annotation.csv' CLASSES = ['Mammals', 'Birds'] SPECIES = ['rabbits', 'rats', 'chickens'] class MyDataset(Dataset): def __init__(self, root_dir, annotations_file, transform=None): self.root_dir = root_dir self.annotations_file = annotations_file self.transform = transform if not os.path.isfile(self.annotations_file): print(self.annotations_file + 'does not exist!') self.file_info = pd.read_csv(annotations_file, index_col=0) self.size = len(self.file_info) def __len__(self): return self.size def __getitem__(self, idx): image_path = self.file_info['path'][idx] if not os.path.isfile(image_path): print(image_path + ' does not exist!') return None image = Image.open(image_path).convert('RGB') label_species = int(self.file_info.iloc[idx]['species']) sample = {'image': image, 'species': label_species} if self.transform: sample['image'] = self.transform(image) return sample train_transforms = transforms.Compose([transforms.Resize((600, 600)), transforms.RandomCrop(500), transforms.RandomHorizontalFlip(), transforms.ToTensor(), ]) val_transforms = transforms.Compose([transforms.Resize((500, 500)), transforms.ToTensor() ]) train_dataset = MyDataset(root_dir= ROOT_DIR + TRAIN_DIR, annotations_file= TRAIN_ANNO, transform=train_transforms) test_dataset = MyDataset(root_dir= ROOT_DIR + VAL_DIR, annotations_file= VAL_ANNO, transform=val_transforms) train_loader = DataLoader(dataset=train_dataset, batch_size=8, shuffle=True) test_loader = DataLoader(dataset=test_dataset) data_loaders = {'train': train_loader, 'val': test_loader} device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") print(device) def visualize_dataset(): print(len(train_dataset)) idx = random.randint(0, len(train_dataset)) sample = train_loader.dataset[idx] print(idx, sample['image'].shape, SPECIES[sample['species']]) img = sample['image'] plt.imshow(transforms.ToPILImage()(img)) plt.show() visualize_dataset() def train_model(model, criterion, optimizer, scheduler, num_epochs=50): Loss_list = {'train': [], 'val': []} Accuracy_list_species = {'train': [], 'val': []} best_model_wts = copy.deepcopy(model.state_dict()) best_acc = 0.0 for epoch in range(num_epochs): print('Epoch {}/{}'.format(epoch, num_epochs - 1)) print('-*' * 10) # Each epoch has a training and validation phase for phase in ['train', 'val']: if phase == 'train': model.train() else: model.eval() running_loss = 0.0 corrects_species = 0 for idx,data in enumerate(data_loaders[phase]): #print(phase+' processing: {}th batch.'.format(idx)) inputs = data['image'].to(device) labels_species = data['species'].to(device) optimizer.zero_grad() with torch.set_grad_enabled(phase == 'train'): x_species = model(inputs) x_species = x_species.view(-1,3) _, preds_species = torch.max(x_species, 1) loss = criterion(x_species, labels_species) if phase == 'train': loss.backward() optimizer.step() running_loss += loss.item() * inputs.size(0) corrects_species += torch.sum(preds_species == labels_species) epoch_loss = running_loss / len(data_loaders[phase].dataset) Loss_list[phase].append(epoch_loss) epoch_acc_species = corrects_species.double() / len(data_loaders[phase].dataset) epoch_acc = epoch_acc_species Accuracy_list_species[phase].append(100 * epoch_acc_species) print('{} Loss: {:.4f} Acc_species: {:.2%}'.format(phase, epoch_loss,epoch_acc_species)) logging.info('{} Loss: {:.4f} Acc_species: {:.2%}'.format(phase, epoch_loss,epoch_acc_species)) if phase == 'val' and epoch_acc > best_acc: best_acc = epoch_acc_species best_model_wts = copy.deepcopy(model.state_dict()) print('Best val species Acc: {:.2%}'.format(best_acc)) logging.info('Best val species Acc: {:.2%}'.format(best_acc)) model.load_state_dict(best_model_wts) torch.save(model.state_dict(), 'best_model.pt') print('Best val species Acc: {:.2%}'.format(best_acc)) logging.info('Best val species Acc: {:.2%}'.format(best_acc)) return model, Loss_list,Accuracy_list_species # vgg16 = models.vgg16(pretrained=True) res18 = models.resnet18(pretrained=True) network = Net(res18).to(device) optimizer = optim.SGD(network.parameters(), lr=0.005, momentum=0.9, weight_decay=1e-4) #weight_decay:L2正則項懲罰 criterion = nn.CrossEntropyLoss() exp_lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=1, gamma=0.1) # Decay LR by a factor of 0.1 every 1 epochs model, Loss_list, Accuracy_list_species = train_model(network, criterion, optimizer, exp_lr_scheduler, num_epochs=100) # x = range(0, 100) # y1 = Loss_list["val"] # y2 = Loss_list["train"] # plt.plot(x, y1, color="r", linestyle="-", marker="o", linewidth=1, label="val") # plt.plot(x, y2, color="b", linestyle="-", marker="o", linewidth=1, label="train") # plt.legend() # plt.title('train and val loss vs. epoches') # plt.ylabel('loss') # plt.savefig("train and val loss vs epoches.jpg") # plt.close('all') # 關閉圖 0 # y5 = Accuracy_list_species["train"] # y6 = Accuracy_list_species["val"] # plt.plot(x, y5, color="r", linestyle="-", marker=".", linewidth=1, label="train") # plt.plot(x, y6, color="b", linestyle="-", marker=".", linewidth=1, label="val") # plt.legend() # plt.title('train and val Species acc vs. epoches') # plt.ylabel('Species accuracy') # plt.savefig("train and val Species acc vs epoches.jpg") # plt.close('all') ######################################## Visualization ################################## def visualize_model(model): model.eval() with torch.no_grad(): for i, data in enumerate(data_loaders['val']): inputs = data['image'] labels_species = data['species'].to(device) x_species = model(inputs.to(device)) x_species = x_species.view( -1,2) _, preds_species = torch.max(x_species, 1) print(inputs.shape) plt.imshow(transforms.ToPILImage()(inputs.squeeze(0))) plt.title('predicted species: {}\n ground-truth species:{}'.format(SPECIES[preds_species],SPECIES[labels_species])) plt.show() # visualize_model(model)
參考:
https://github.com/Hong-Bo/hands-on-pytorch/blob/master/alex_net/alex_net.py
https://pytorch.org/tutorials/