上一節(jié)使用的是官方數(shù)據(jù)集fashionminist進(jìn)行訓(xùn)練,這節(jié)課使用自己搜集的數(shù)據(jù)集來進(jìn)行數(shù)據(jù)的獲取和訓(xùn)練。
所需資源
教學(xué)視頻:https://www.bilibili.com/video/BV1by4y1b7hX/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=e482aea0f5ebf492c0b0220fb64f98d3
pytorch進(jìn)階學(xué)習(xí)(一):https://blog.csdn.net/weixin_45662399/article/details/129737499?spm=1001.2014.3001.5501
課程準(zhǔn)備:本節(jié)課需要用到3個(gè)Python文件和一個(gè)數(shù)據(jù)集文件,代碼后面我都會(huì)給出,zip需要自己下載,,數(shù)據(jù)集zip文件和所需的三個(gè)代碼文件可以在“l(fā)eo在這”進(jìn)行下載。
一、數(shù)據(jù)集文件準(zhǔn)備
1.1 項(xiàng)目文件結(jié)構(gòu)
下圖為我的項(xiàng)目文件在遠(yuǎn)程服務(wù)器上的目錄,需要新建一個(gè)dataset中為上傳的自己的數(shù)據(jù)集。

1.2 上傳數(shù)據(jù)集到服務(wù)器
數(shù)據(jù)集文件解壓后如下所示,有6個(gè)子文件夾,對應(yīng)6個(gè)類別。

我們先把dataset.zip上傳到服務(wù)器中的代碼項(xiàng)目文件夾中。一定要找到服務(wù)器中項(xiàng)目的路徑,不要傳錯(cuò)位置!

我的項(xiàng)目目錄在服務(wù)器中的路徑為“tmp/pycharm_932”,數(shù)據(jù)集zip文件即下載到tmp/pycharm_932/dataset目錄下。

1.3 解壓zip文件
回到服務(wù)器控制臺在紅,先使用cd命令定位到tmp/pycharm_932/dataset路徑下,然后使用unzip 'dataset.zip'命令解壓壓縮文件。

可以看到服務(wù)器完成了解壓。

最后把zip文件從dataset文件夾中刪去即可,最終解壓好的文件如下。

1.4 代碼框架解讀
'CreateDataset.py' 用于把數(shù)據(jù)集文件夾中的所有圖片文件生成一個(gè)TXT文件,其中存放著所有圖片的路徑和圖片對應(yīng)的標(biāo)簽。
'CreateDataLoader.py' 用于把上一步生成的TXT文件中的信息提取出來,進(jìn)行圖片信息的打包,生成一個(gè)dataloader
最后在'加載自己的數(shù)據(jù).py' 文件中對dataloader進(jìn)行使用,并且完成網(wǎng)絡(luò)的訓(xùn)練和測試。
二、運(yùn)行CreateDataset.py
作用:該代碼可以生成訓(xùn)練集和測試集中每張圖片的路徑和標(biāo)簽,保存在TXT文件中。后續(xù)即可以從文件中調(diào)用每一張圖片,進(jìn)行讀取。
2.1 代碼實(shí)現(xiàn)
'''
生成訓(xùn)練集和測試集,保存在txt文件中
'''
import os
import random
#60%當(dāng)訓(xùn)練集
train_ratio = 0.6
#剩下的當(dāng)測試集
test_ratio = 1-train_ratio
rootdata = r"dataset"
train_list, test_list = [],[]
data_list = []
class_flag = -1
for a,b,c in os.walk(rootdata):
print(a)
for i in range(len(c)):
data_list.append(os.path.join(a,c[i]))
for i in range(0,int(len(c)*train_ratio)):
train_data = os.path.join(a, c[i])+'\t'+str(class_flag)+'\n'
train_list.append(train_data)
for i in range(int(len(c) * train_ratio),len(c)):
test_data = os.path.join(a, c[i]) + '\t' + str(class_flag)+'\n'
test_list.append(test_data)
class_flag += 1
print(train_list)
random.shuffle(train_list)
random.shuffle(test_list)
with open('train.txt','w',encoding='UTF-8') as f:
for train_img in train_list:
f.write(str(train_img))
with open('test.txt','w',encoding='UTF-8') as f:
for test_img in test_list:
f.write(test_img)
2.2 運(yùn)行結(jié)果
可以看到此時(shí)服務(wù)器中文件管理器中已經(jīng)有了test.txt和train.txt兩個(gè)文件。

生成了train.txt和test.txt兩個(gè)文件,里面保存了每張圖片的對應(yīng)相對路徑和類別標(biāo)簽,標(biāo)簽是以int型進(jìn)行存儲(chǔ)。打開test.txt文件可以發(fā)現(xiàn)里面的內(nèi)容為測試集所有圖片路徑以及其標(biāo)簽。

2.3 代碼要點(diǎn)解析
對訓(xùn)練集和測試集的劃分比例為6:4
rootdata為數(shù)據(jù)集文件保存的根目錄,為dataset文件夾。
#60%當(dāng)訓(xùn)練集
train_ratio = 0.6
#剩下的當(dāng)測試集
test_ratio = 1-train_ratio
rootdata = r"dataset"
2. 讀取文件夾
a讀取文件夾根目錄,再使用c[i]讀取每個(gè)圖片的名稱,使用os.path.join進(jìn)行拼接,實(shí)現(xiàn)每張圖片的相對路徑的path存取。可以看到a為dataset加上下面類別子文件夾。
dataset/擦花
dataset/桔皮
dataset/碰傷
dataset/橫條壓凹
dataset/不導(dǎo)電
dataset/漏底
和c[i]進(jìn)行拼接后即可完成每一張圖片的定位。
dataset/碰傷/碰傷20180906142721對照樣本.jpg
然后使用class_flag進(jìn)行圖片類別標(biāo)簽的存儲(chǔ),從0開始依次增加,一共6個(gè)類別,故取值為【0,5】。
path和label之間使用\t進(jìn)行分割,即一個(gè)tab的距離。
for a,b,c in os.walk(rootdata):
print(a)
for i in range(len(c)):
data_list.append(os.path.join(a,c[i]))
for i in range(0,int(len(c)*train_ratio)):
train_data = os.path.join(a, c[i])+'\t'+str(class_flag)+'\n'
train_list.append(train_data)
for i in range(int(len(c) * train_ratio),len(c)):
test_data = os.path.join(a, c[i]) + '\t' + str(class_flag)+'\n'
test_list.append(test_data)
class_flag += 1
3. 寫入文件
使用shuffle打亂數(shù)據(jù)集的順序,把上面生成的每一張圖片的“路徑+標(biāo)簽”轉(zhuǎn)為字符串形式,然后存入txt文件中。
因?yàn)槲募兄形?,所以使用utf-8編碼形式。
random.shuffle(train_list)
random.shuffle(test_list)
with open('train.txt','w',encoding='UTF-8') as f:
for train_img in train_list:
f.write(str(train_img))
with open('test.txt','w',encoding='UTF-8') as f:
for test_img in test_list:
f.write(test_img)
三、CreateDataLoader.py
作用:完成對上一步dataset中生成的txt文件中對圖片和標(biāo)簽信息的讀取,將圖片進(jìn)行打包放入圖片加載器DataLoader中。
使用系統(tǒng)帶的數(shù)據(jù)集如下代碼所示,將帶的數(shù)據(jù)集存在training_data中,將training_data作為參數(shù)傳入DataLoader中。
training_data = datasets.FashionMNIST(
root="data",
train=True,
download=True,
transform=ToTensor(),
)
# 下面是測試集,同樣需要下載
test_data = datasets.FashionMNIST(
root="data",
train=False,
download=True,
transform=ToTensor(),
)
train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)
可以看到我們代碼是使用LoadData類自己新建了一個(gè)數(shù)據(jù)集train_dataset,然后把train_dataset傳入DataLoader中。
train_dataset = LoadData("train.txt", True)
# 傳入dataloader中
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,batch_size=10,shuffle=True)
3.1 代碼實(shí)現(xiàn)
import torch
from PIL import Image
import torchvision.transforms as transforms
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True
from torch.utils.data import Dataset
# 數(shù)據(jù)歸一化與標(biāo)準(zhǔn)化
# 圖像標(biāo)準(zhǔn)化
transform_BZ= transforms.Normalize(
mean=[0.5, 0.5, 0.5],# 取決于數(shù)據(jù)集
std=[0.5, 0.5, 0.5]
)
#讀取TXT文件
class LoadData(Dataset):
def __init__(self, txt_path, train_flag=True):
self.imgs_info = self.get_images(txt_path)
self.train_flag = train_flag
self.train_tf = transforms.Compose([
transforms.Resize(224),
transforms.RandomHorizontalFlip(),
transforms.RandomVerticalFlip(),
transforms.ToTensor(),
transform_BZ
])
self.val_tf = transforms.Compose([
transforms.Resize(224),
transforms.ToTensor(),
transform_BZ
])
def get_images(self, txt_path):
with open(txt_path, 'r', encoding='utf-8') as f:
imgs_info = f.readlines()
imgs_info = list(map(lambda x:x.strip().split('\t'), imgs_info))
return imgs_info
def padding_black(self, img):
w, h = img.size
scale = 224. / max(w, h)
img_fg = img.resize([int(x) for x in [w * scale, h * scale]])
size_fg = img_fg.size
size_bg = 224
img_bg = Image.new("RGB", (size_bg, size_bg))
img_bg.paste(img_fg, ((size_bg - size_fg[0]) // 2,
(size_bg - size_fg[1]) // 2))
img = img_bg
return img
def __getitem__(self, index):
img_path, label = self.imgs_info[index]
img = Image.open(img_path)
img = img.convert('RGB')
img = self.padding_black(img)
if self.train_flag:
img = self.train_tf(img)
else:
img = self.val_tf(img)
label = int(label)
return img, label
def __len__(self):
return len(self.imgs_info)
if __name__ == "__main__":
train_dataset = LoadData("train.txt", True)
print("數(shù)據(jù)個(gè)數(shù):", len(train_dataset))
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=10,
shuffle=True)
for image, label in train_loader:
print(image.shape)
print(image)
# img = transform_BZ(image)
# print(img)
print(label)
# test_dataset = Data_Loader("test.txt", False)
# print("數(shù)據(jù)個(gè)數(shù):", len(test_dataset))
# test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
# batch_size=10,
# shuffle=True)
# for image, label in test_loader:
# print(image.shape)
# print(label)
3.2 運(yùn)行結(jié)果
我學(xué)校服務(wù)器跑大約等了兩分鐘,然后跑完后出現(xiàn)如下結(jié)果。


可以看到數(shù)據(jù)集中有380張圖片,圖片大小為224*224,dataloader中圖片每10個(gè)為一組,tensor中為10個(gè)圖片的標(biāo)簽。
3.3 代碼要點(diǎn)解析
3.3.1 class LoadData init方法
該類的初始化方法,定義了兩個(gè)變量,img_info為get_images方法獲取的信息,是一個(gè)list,保存著圖片的路徑和標(biāo)簽;train_flag為標(biāo)志點(diǎn),標(biāo)志是否為訓(xùn)練集,TRUE為時(shí)=是,否則為測試集。
train_tf和val_tf使用compose完成對圖片樣式的變換,如定義大小為224*224,隨機(jī)水平展平,正則化等。
3.3.2 get_images方法
通過txt文件的路徑讀取到txt中信息,使用‘\t’分割圖片路徑和圖片標(biāo)簽,并且保存在imgs)info的列表中。
3.3.3 padding_black方法
如果圖片過小,使用padding填充該圖片,使其能夠成為224*224大小。
3.3.4 getitem方法
該方法為class loaddata的主方法,使用index下標(biāo)獲取到每一張圖片的path和label后,用flag判斷為訓(xùn)練集還是驗(yàn)證集,并且采用對應(yīng)的圖片處理措施(train_tf/val_tf)。
返回的是圖片和對應(yīng)標(biāo)簽。
3.3.5 main方法
把LoadData生成的數(shù)據(jù)存入train_dataset中,作為數(shù)據(jù)集傳入torch的data.DataLoader類中,設(shè)置batchsize=10.
10張圖片為一組,輸出每張圖片和標(biāo)簽。
四、加載自己的數(shù)據(jù).py
4.1 代碼實(shí)現(xiàn)
大部分和第一節(jié)中的模型一樣。
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda, Compose
import matplotlib.pyplot as plt
from CreateDataloader import LoadData
# 定義網(wǎng)絡(luò)模型
class NeuralNetwork(nn.Module):
def __init__(self):
super(NeuralNetwork, self).__init__()
# 碾平,將數(shù)據(jù)碾平為一維
self.flatten = nn.Flatten()
# 定義linear_relu_stack,由以下眾多層構(gòu)成
self.linear_relu_stack = nn.Sequential(
# 全連接層
nn.Linear(3*224*224, 512),
# ReLU激活函數(shù)
nn.ReLU(),
# 全連接層
nn.Linear(512, 512),
nn.ReLU(),
nn.Linear(512, 6),
nn.ReLU()
)
# x為傳入數(shù)據(jù)
def forward(self, x):
# x先經(jīng)過碾平變?yōu)?維
x = self.flatten(x)
# 隨后x經(jīng)過linear_relu_stack
logits = self.linear_relu_stack(x)
# 輸出logits
return logits
# 定義訓(xùn)練函數(shù),需要
def train(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset)
# 從數(shù)據(jù)加載器中讀取batch(一次讀取多少張,即批次數(shù)),X(圖片數(shù)據(jù)),y(圖片真實(shí)標(biāo)簽)。
for batch, (X, y) in enumerate(dataloader):
# 將數(shù)據(jù)存到顯卡
X, y = X.cuda(), y.cuda()
# 得到預(yù)測的結(jié)果pred
pred = model(X)
# 計(jì)算預(yù)測的誤差
# print(pred,y)
loss = loss_fn(pred, y)
# 反向傳播,更新模型參數(shù)
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 每訓(xùn)練100次,輸出一次當(dāng)前信息
if batch % 100 == 0:
loss, current = loss.item(), batch * len(X)
print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]")
def test(dataloader, model):
size = len(dataloader.dataset)
print("size = ",size)
# 將模型轉(zhuǎn)為驗(yàn)證模式
model.eval()
# 初始化test_loss 和 correct, 用來統(tǒng)計(jì)每次的誤差
test_loss, correct = 0, 0
# 測試時(shí)模型參數(shù)不用更新,所以no_gard()
# 非訓(xùn)練, 推理期用到
with torch.no_grad():
# 加載數(shù)據(jù)加載器,得到里面的X(圖片數(shù)據(jù))和y(真實(shí)標(biāo)簽)
for X, y in dataloader:
# 將數(shù)據(jù)轉(zhuǎn)到GPU
X, y = X.cuda(), y.cuda()
# 將圖片傳入到模型當(dāng)中就,得到預(yù)測的值pred
pred = model(X)
# 計(jì)算預(yù)測值pred和真實(shí)值y的差距
test_loss += loss_fn(pred, y).item()
# 統(tǒng)計(jì)預(yù)測正確的個(gè)數(shù)
correct += (pred.argmax(1) == y).type(torch.float).sum().item()
test_loss /= size
correct /= size
print("correct = ",correct)
print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
if __name__=='__main__':
batch_size = 16
# # 給訓(xùn)練集和測試集分別創(chuàng)建一個(gè)數(shù)據(jù)集加載器
train_data = LoadData("train.txt", True)
valid_data = LoadData("test.txt", False)
train_dataloader = DataLoader(dataset=train_data, num_workers=4, pin_memory=True, batch_size=batch_size, shuffle=True)
test_dataloader = DataLoader(dataset=valid_data, num_workers=4, pin_memory=True, batch_size=batch_size)
for X, y in test_dataloader:
print("Shape of X [N, C, H, W]: ", X.shape)
print("Shape of y: ", y.shape, y.dtype)
break
# 如果顯卡可用,則用顯卡進(jìn)行訓(xùn)練
device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))
# 調(diào)用剛定義的模型,將模型轉(zhuǎn)到GPU(如果可用)
model = NeuralNetwork().to(device)
print(model)
# 定義損失函數(shù),計(jì)算相差多少,交叉熵,
loss_fn = nn.CrossEntropyLoss()
# 定義優(yōu)化器,用來訓(xùn)練時(shí)候優(yōu)化模型參數(shù),隨機(jī)梯度下降法
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3) # 初始學(xué)習(xí)率
# 一共訓(xùn)練5次
epochs = 5
for t in range(epochs):
print(f"Epoch {t+1}\n-------------------------------")
train(train_dataloader, model, loss_fn, optimizer)
test(test_dataloader, model)
print("Done!")
# 讀取訓(xùn)練好的模型,加載訓(xùn)練好的參數(shù)
model = NeuralNetwork()
model.load_state_dict(torch.load("model.pth"))
4.2 運(yùn)行結(jié)果
首先打印出來了網(wǎng)絡(luò)的結(jié)構(gòu)。

之后是訓(xùn)練模型的結(jié)果,一共有5個(gè)epoch,可以看到準(zhǔn)確率不是很高。文章來源:http://www.zghlxwxcb.cn/news/detail-492938.html

4.3 代碼解析

num_workers為cpu使用多線程讀取數(shù)據(jù),pin_memory為不寫入虛擬內(nèi)存文章來源地址http://www.zghlxwxcb.cn/news/detail-492938.html
到了這里,關(guān)于pytorch進(jìn)階學(xué)習(xí)(二):使用DataLoader讀取自己的數(shù)據(jù)集的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!