順著文檔理解代碼,加備注
import os, sys, glob, shutil, json
os.environ["CUDA_VISIBLE_DEVICES"]= '0'
import cv2
from PILimport Image
import numpyas np
from tqdmimport tqdm, tqdm_notebook
import torch
torch.manual_seed(0)
torch.backends.cudnn.deterministic= False
torch.backends.cudnn.benchmark= True
import torchvision.modelsas models
import torchvision.transformsas transforms
import torchvision.datasetsas datasets
import torch.nnas nn
import torch.nn.functionalas F
import torch.optimas optim
from torch.autogradimport Variable
from torch.utils.data.datasetimport Dataset
#定義好讀取圖像的Dataset的類
#屬性img_path, img_label, transform,方法__getitem__,__len__
class SVHNDataset(object):
? ? def __init__(self,img_path,img_label,transform=None):
? ? ? ? self.img_path= img_path
? ? ? ? self.img_label= img_label
? ? ? ? if transform is not None:
? ? ? ? ? ? self.transform= transform
? ? ? ? else:
? ? ? ? ? ? self.transform= None
? ? #返回index的圖像和標(biāo)簽
? ? def __getitem__(self,index):
? ? ? ? img= Image.open(self.img_path[index]).convert('RGB')
if self.transformis? not None :
? ? ? ? ? ? img= self.transform(img)
# 設(shè)置最?的字符?度為5個(gè)
? ? ? ? lbl= np.array(self.img_label[index],dtype= np.int)
#組合標(biāo)簽時(shí)用list隨意加入元素——np.array類型——張量
? ? ? ? lbl= list(lbl)+ (5-len(lbl))* [10]
return img,torch.from_numpy(np.array(lbl[:5]))
def __len__(self):
? ? ? ? return len(self.img_path)
#定義訓(xùn)練和驗(yàn)證數(shù)據(jù)的Dataset
def getDataSet():
? ? # 訓(xùn)練數(shù)據(jù)的Dataset
? ? #查找符合特定規(guī)則的文件路徑名,獲取所有的匹配路徑(用完全的路徑別用../)
? ? train_path= glob.glob(r'D:/competition/tianchi/cv/SVHN/input/mchar_train/mchar_train/*.png')
train_path.sort()
train_json= json.load(open('D:/competition/tianchi/cv/SVHN/input/train.json'))
#json格式:"000000.png": {"height": [219, 219], "label": [1, 9], "left": [246, 323], "top": [77, 81], "width": [81, 96]},
? ? #train_json[x]是dict,索引:D2['name'] = 'Bob'
? ? train_label= [train_json[x]['label']for xin train_json]
print(len(train_path),len(train_label))
#dataloader對(duì)dataset封裝以批量迭代讀取,圖像和標(biāo)簽讀取成SVHNDataset類碗殷,數(shù)據(jù)擴(kuò)充,
? ? #數(shù)據(jù)加載器。組合數(shù)據(jù)集和采樣器抹镊,并在數(shù)據(jù)集上提供單進(jìn)程或多進(jìn)程迭代器。
? ? train_loader= torch.utils.data.DataLoader(
SVHNDataset(train_path, train_label,#(input, target)
? ? transforms.Compose([#將多種變換組合在一起
? ? ? ? transforms.Resize((64,128)),#指定大小,縮放到固定尺?
? ? ? ? transforms.RandomCrop((60,120)),#在一個(gè)隨機(jī)的位置進(jìn)行裁
? ? ? ? transforms.ColorJitter(0.3,0.3,0.2),#隨機(jī)改變圖像的亮度對(duì)比度和飽和度
? ? ? ? transforms.RandomRotation(5),# 加?隨機(jī)旋轉(zhuǎn)
#convert a PIL image to tensor (HWC) in range [0,255] then to a torch.Tensor(CHW) in the range [0.0,1.0]
? ? ? ? transforms.ToTensor(),# 將圖?轉(zhuǎn)換為pytorch 的tesntor
#用給定的均值和標(biāo)準(zhǔn)差分別對(duì)每個(gè)通道的數(shù)據(jù)進(jìn)行正則化荤傲。均值(M1,…,Mn)垮耳,給定標(biāo)準(zhǔn)差(S1,…,Sn)
#output[channel] = (input[channel] - mean[channel]) / std[channel],如((0,1)-0.5)/0.5=(-1,1)
? ? ? ? transforms.Normalize([0.485,0.456,0.406], [0.229,0.224,0.225])
])),
batch_size=40,#每個(gè)batch加載多少個(gè)樣本
? ? #shuffle=True,? ? #在每個(gè)epoch重新打亂數(shù)據(jù),默認(rèn)false
? ? #num_workers=10,? #用多少個(gè)子進(jìn)程加載數(shù)據(jù)
? ? )
#驗(yàn)證數(shù)據(jù)的dataset
? ? val_path= glob.glob('D:/competition/tianchi/cv/SVHN/input/mchar_val/mchar_val/*.png')
val_path.sort()
val_json= json.load(open('D:/competition/tianchi/cv/SVHN/input/val.json'))
val_label= [val_json[x]['label']for xin val_json]
print(len(val_path),len(val_label))
val_loader= torch.utils.data.DataLoader(
SVHNDataset(val_path, val_label,
transforms.Compose([
transforms.Resize((60,120)),
# transforms.ColorJitter(0.3, 0.3, 0.2),
# transforms.RandomRotation(5),
? ? ? ? transforms.ToTensor(),
transforms.Normalize([0.485,0.456,0.406], [0.229,0.224,0.225])
])),
batch_size=40,
shuffle=False,
num_workers=10,)
return train_loader,val_loader
#output
train_loader,val_loader= getDataSet()#輸出應(yīng)當(dāng)為30000 30000 10000 10000
#構(gòu)建CNN模型
class SVHN_model_1(nn.Module):
? ? #super指代父類,繼承的時(shí)候遂黍,調(diào)用含super的各個(gè)的基類__init__函數(shù)
? ? def __init__(self):
? ? ? ? super(SVHN_model_1,self).__init__()
#cnn提取特征模塊
? ? ? ? #一個(gè)有序的容器终佛,神經(jīng)網(wǎng)絡(luò)模塊將按照在傳入構(gòu)造器的順序依次被添加到計(jì)算圖中執(zhí)行,同時(shí)以神經(jīng)網(wǎng)絡(luò)模塊為元素的有序字典也可以作為傳入?yún)?shù)雾家。
? ? ? ? self.cnn= nn.Sequential(
nn.Conv2d(3,16,kernel_size=(3,3),stride=(2,2)),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(16,32,kernel_size=(3,3),stride=(2,2)),
nn.ReLU(),
nn.MaxPool2d(2),
)
#對(duì)傳入數(shù)據(jù)應(yīng)用線性變換:y = A x+ b,(每個(gè)輸入樣本的大小,出大小,bias=False則圖層不會(huì)學(xué)習(xí)附加偏差铃彰。)
? ? ? ? self.fc1= nn.Linear(32 * 3 * 7,11)
self.fc2= nn.Linear(32 * 3 * 7,11)
self.fc3= nn.Linear(32 * 3 * 7,11)
self.fc4= nn.Linear(32 * 3 * 7,11)
self.fc5= nn.Linear(32 * 3 * 7,11)
self.fc6= nn.Linear(32 * 3 * 7,11)
#輸出經(jīng)過(guò)所有神經(jīng)網(wǎng)絡(luò)層的結(jié)果
? ? def forward(self,img):
? ? ? ? feat= self.cnn(img)#nn.Sequential
? ? ? ? #將一個(gè)多行的Tensor,拼接成一行
? ? ? ? feat= feat.view(feat.shape[0],-1)
#并聯(lián)6全連接,進(jìn)行分類
? ? ? ? c1= self.fc1(feat)
c2= self.fc2(feat)
c3= self.fc3(feat)
c4= self.fc4(feat)
c5= self.fc5(feat)
c6= self.fc6(feat)
return c1, c2, c3, c4, c5, c6
#model已經(jīng)經(jīng)過(guò)了cnn,有forward()方法出6個(gè)并聯(lián)全連接結(jié)果
model1= SVHN_model_1()
#使用在imageNet數(shù)據(jù)集上的與訓(xùn)練模型
class SVHN_model_2(nn.Module):
? ? def __init__(self):
? ? ? ? #?芯咧?
? ? ? ? super(SVHN_model_2,self).__init__()
model_conv= models.resnet18(pretrained=True)
#平均池化:輸出特征的個(gè)數(shù)等于輸入平面的個(gè)數(shù)牙捉,都為1*1的tensor,strides敬飒,paddings等參數(shù)都自適應(yīng)好了
? ? ? ? model_conv.avgpool= nn.AdaptiveAvgPool2d(1)
#迭代器:model.modules()會(huì)遍歷model中所有的子層邪铲,而model.children()僅會(huì)遍歷當(dāng)前層[[1, 2], 3]--[1, 2], 3。
? ? ? ? model_conv= nn.Sequential(*list(model_conv.children())[:-1])
self.cnn= model_conv
self.fc1= nn.Linear(512,11)
self.fc2= nn.Linear(512,11)
self.fc3= nn.Linear(512,11)
self.fc4= nn.Linear(512,11)
self.fc5= nn.Linear(512,11)
#與model1一樣
? ? def forward(self,img):
? ? ? ? feat= self.cnn(img)
# print(feat.shape)
? ? ? ? feat= feat.view(feat.shape[0],-1)
c1= self.fc1(feat)
c2= self.fc2(feat)
c3= self.fc3(feat)
c4= self.fc4(feat)
c5= self.fc5(feat)
return c1, c2, c3, c4, c5
model2= SVHN_model_2()
#訓(xùn)練數(shù)據(jù)
def trainData(train_loader,model):
? ? #s=nn.Softmax(dim=1)一列的和為1–NLLLoss --i=torch.log(s(input))
? ? #nn.NLLLoss(i,target) 把上面的輸出與Label對(duì)應(yīng)的那個(gè)值拿出來(lái)驶拱,再去掉負(fù)號(hào)霜浴,再求均值。
? ? criterion= nn.CrossEntropyLoss()#二分類損失函數(shù),是上面三步的綜合
? ? #優(yōu)化器對(duì)象Optimizer蓝纲,用來(lái)保存當(dāng)前的狀態(tài)阴孟,并能夠根據(jù)計(jì)算得到的梯度來(lái)更新參數(shù)晌纫。
? ? optimizer= torch.optim.Adam(model.parameters(),0.005)
loss_plot= []
c0_plot= []
#epoch訓(xùn)練過(guò)程
? ? for epochin range(1):
? ? ? ? for datain train_loader:
? ? ? ? ? ? c0, c1, c2, c3, c4, c5= model(data[0])
data[1]= data[1].long()
loss= criterion(c0, data[1][:,0])+\
criterion(c1, data[1][:,1])+\
criterion(c2, data[1][:,2])+\
criterion(c3, data[1][:,3])+\
criterion(c4, data[1][:,4])
#報(bào)錯(cuò)下面的data[1][:, 5]不存在
? ? ? ? ? ? ? ? ? #criterion(c5, data[1][:, 5])
? ? ? ? ? ? loss/= 5
? ? ? ? ? ? optimizer.zero_grad()
loss.backward()
optimizer.step()
loss_plot.append(loss.item())
c0_plot.append((c0.argmax(1)==data[1][:,0].sum().item()*1.0/c0.shape[0]))
print(epoch)
#trainData(train_loader,model1)
#每次epoch訓(xùn)練的過(guò)程
def train(train_loader,model,criterion,optimizer,epoch):
? ? # 切換模型為訓(xùn)練模式
? ? model.train()
#enumerate()將一個(gè)可遍歷的數(shù)據(jù)對(duì)象組合為一個(gè)索引序列,同時(shí)列出下標(biāo)和數(shù)據(jù)
? ? for i, datain enumerate(train_loader):
? ? ? ? c0, c1, c2, c3, c4, c5= model(data[0])
data[1]= data[1].long()
loss= criterion(c0, data[1][:,0])+ \
criterion(c1, data[1][:,1])+ \
criterion(c2, data[1][:,2])+ \
criterion(c3, data[1][:,3])+ \
criterion(c4, data[1][:,4])
# criterion(c5, data[1][:, 5])
? ? ? ? loss/= 5
? ? ? ? optimizer.zero_grad()#把模型中參數(shù)的梯度設(shè)為0
? ? ? ? loss.backward()#反向傳播(grad_fn就是Tensor專門保存其進(jìn)行過(guò)的數(shù)學(xué)運(yùn)算
? ? ? ? optimizer.step()#模型更新
def validate(val_loader,model,criterion):
? ? # 切換模型為預(yù)測(cè)模型
? ? model.eval()
val_loss= []
# 不記錄模型梯度信息
? ? with torch.no_grad():
? ? ? ? for i, datain enumerate(val_loader):
? ? ? ? ? ? c0, c1, c2, c3, c4, c5= model(data[0])
loss= criterion(c0, data[1][:,0])+ \
criterion(c1, data[1][:,1])+ \
criterion(c2, data[1][:,2])+ \
criterion(c3, data[1][:,3])+ \
criterion(c4, data[1][:,4])+ \
criterion(c5, data[1][:,5])
loss/= 6
? ? ? ? ? ? #不記錄模型梯度信息
? ? ? ? ? ? val_loss.append(loss.item())
return np.mean(val_loss)
def valCal(train_loader,val_loader,model):
? ? criterion= nn.CrossEntropyLoss()
optimizer= torch.optim.Adam(model.parameters(),0.001)
best_loss= 1000.0
? ? for epochin range(3):
? ? ? ? print('Epoch: ', epoch)
#每一次epoch都對(duì)訓(xùn)練集和VAL在同一個(gè)模型進(jìn)行計(jì)算
? ? ? ? train(train_loader,model, criterion, optimizer, epoch)
print('train_over')
val_loss= validate(val_loader,model, criterion)
print('val_over')
# 記錄下驗(yàn)證集精度
? ? ? ? if val_loss< best_loss:
? ? ? ? ? ? best_loss= val_loss
torch.save(model.state_dict(),'./model.pt')
valCal(train_loader,val_loader,model1)