順著文檔理解代碼,加備注
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的圖像和標簽
? ? def __getitem__(self,index):
? ? ? ? img= Image.open(self.img_path[index]).convert('RGB')
if self.transformis? not None :
? ? ? ? ? ? img= self.transform(img)
# 設置最?的字符?度為5個
? ? ? ? lbl= np.array(self.img_label[index],dtype= np.int)
#組合標簽時用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)
#定義訓練和驗證數(shù)據(jù)的Dataset
def getDataSet():
? ? # 訓練數(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對dataset封裝以批量迭代讀取,圖像和標簽讀取成SVHNDataset類,數(shù)據(jù)擴充,
? ? #數(shù)據(jù)加載器。組合數(shù)據(jù)集和采樣器,并在數(shù)據(jù)集上提供單進程或多進程迭代器。
? ? train_loader= torch.utils.data.DataLoader(
SVHNDataset(train_path, train_label,#(input, target)
? ? transforms.Compose([#將多種變換組合在一起
? ? ? ? transforms.Resize((64,128)),#指定大小,縮放到固定尺?
? ? ? ? transforms.RandomCrop((60,120)),#在一個隨機的位置進行裁
? ? ? ? transforms.ColorJitter(0.3,0.3,0.2),#隨機改變圖像的亮度對比度和飽和度
? ? ? ? transforms.RandomRotation(5),# 加?隨機旋轉
#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(),# 將圖?轉換為pytorch 的tesntor
#用給定的均值和標準差分別對每個通道的數(shù)據(jù)進行正則化。均值(M1,…,Mn),給定標準差(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,#每個batch加載多少個樣本
? ? #shuffle=True,? ? #在每個epoch重新打亂數(shù)據(jù),默認false
? ? #num_workers=10,? #用多少個子進程加載數(shù)據(jù)
? ? )
#驗證數(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()#輸出應當為30000 30000 10000 10000
#構建CNN模型
class SVHN_model_1(nn.Module):
? ? #super指代父類,繼承的時候,調用含super的各個的基類__init__函數(shù)
? ? def __init__(self):
? ? ? ? super(SVHN_model_1,self).__init__()
#cnn提取特征模塊
? ? ? ? #一個有序的容器,神經網(wǎng)絡模塊將按照在傳入構造器的順序依次被添加到計算圖中執(zhí)行,同時以神經網(wǎng)絡模塊為元素的有序字典也可以作為傳入?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),
)
#對傳入數(shù)據(jù)應用線性變換:y = A x+ b,(每個輸入樣本的大小,出大小,bias=False則圖層不會學習附加偏差。)
? ? ? ? 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)
#輸出經過所有神經網(wǎng)絡層的結果
? ? def forward(self,img):
? ? ? ? feat= self.cnn(img)#nn.Sequential
? ? ? ? #將一個多行的Tensor,拼接成一行
? ? ? ? feat= feat.view(feat.shape[0],-1)
#并聯(lián)6全連接,進行分類
? ? ? ? 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已經經過了cnn,有forward()方法出6個并聯(lián)全連接結果
model1= SVHN_model_1()
#使用在imageNet數(shù)據(jù)集上的與訓練模型
class SVHN_model_2(nn.Module):
? ? def __init__(self):
? ? ? ? #??
? ? ? ? super(SVHN_model_2,self).__init__()
model_conv= models.resnet18(pretrained=True)
#平均池化:輸出特征的個數(shù)等于輸入平面的個數(shù),都為1*1的tensor,strides,paddings等參數(shù)都自適應好了
? ? ? ? model_conv.avgpool= nn.AdaptiveAvgPool2d(1)
#迭代器:model.modules()會遍歷model中所有的子層,而model.children()僅會遍歷當前層[[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()
#訓練數(shù)據(jù)
def trainData(train_loader,model):
? ? #s=nn.Softmax(dim=1)一列的和為1–NLLLoss --i=torch.log(s(input))
? ? #nn.NLLLoss(i,target) 把上面的輸出與Label對應的那個值拿出來,再去掉負號,再求均值。
? ? criterion= nn.CrossEntropyLoss()#二分類損失函數(shù),是上面三步的綜合
? ? #優(yōu)化器對象Optimizer,用來保存當前的狀態(tài),并能夠根據(jù)計算得到的梯度來更新參數(shù)。
? ? optimizer= torch.optim.Adam(model.parameters(),0.005)
loss_plot= []
c0_plot= []
#epoch訓練過程
? ? 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])
#報錯下面的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訓練的過程
def train(train_loader,model,criterion,optimizer,epoch):
? ? # 切換模型為訓練模式
? ? model.train()
#enumerate()將一個可遍歷的數(shù)據(jù)對象組合為一個索引序列,同時列出下標和數(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ù)的梯度設為0
? ? ? ? loss.backward()#反向傳播(grad_fn就是Tensor專門保存其進行過的數(shù)學運算
? ? ? ? optimizer.step()#模型更新
def validate(val_loader,model,criterion):
? ? # 切換模型為預測模型
? ? 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都對訓練集和VAL在同一個模型進行計算
? ? ? ? train(train_loader,model, criterion, optimizer, epoch)
print('train_over')
val_loss= validate(val_loader,model, criterion)
print('val_over')
# 記錄下驗證集精度
? ? ? ? if val_loss< best_loss:
? ? ? ? ? ? best_loss= val_loss
torch.save(model.state_dict(),'./model.pt')
valCal(train_loader,val_loader,model1)