在初学PyToch和神经网络时,面对众多的概念和函数,我们可能会很迷惑如雾里看花。这时最好的方法便是做一个简单的小项目理解这个项目的构成,虽然博主也是新手,但我们不妨一起通过PyTorch来编写一个可以区分蜜蜂和蚂蚁的net。这是蜜蜂和蚂蚁训练集测试集的下载地址,来源于B站视频评论区。
首先是我们需要的包:
import torch import torch.nn as nn import torch.nn.function as F import torchvision #读取图片 from torchvision import transforms然后第一步是录入我们训练和测试用的照片。先设定好照片的统一格式:
transform=transforms.Compose([ transforms.Resize((32,32)), #像素统一转换为32*32, #如果想让照片变得清晰点记得多加几个卷积层 transforms.Tensor #统一转化为张量 ])然后导入训练照片和测试照片:
trainset=torchvision.datasets.Imagefloder( root=r'训练照片储存地址',#Python中地址的'\'会和'\n'混淆所以一般在前面加个'r'或者用'\\' trainsform=transform ) trainloader=torch.utils.data.DataLoader( trainset, #定义如何给net图片 batch_size=4, #每次会给四张图片 shuffle=True, #每次完整的训练伦次会打乱照片顺序 num_wokers=0 #单进程运行 ) #同理定义测试集 testset=torchvision.datasets.Imagefloder( root=r'训练照片储存地址', trainsform=transform ) testloader=torch.utils.data.DataLoader( trainset, #定义如何给net图片 batch_size=4, #每次会给四张图片 shuffle=True, #每次完整的训练伦次会打乱照片顺序 num_wokers=0 #单进程运行 ) #定义种类 classes=('ants','bees')接下来开始定义net:
class Net(nn.Module): def __init__(self): super(Net,self).__init__ #标准格式 self.conv1=nn.Conv2d(3,6,5) #定义第一个卷积层,3代表彩色照片,6代表6个卷积核, #5代表每个卷积核尺寸为5*5 self.pool=nn.MaxPool2d(2,2) #定义池化矩阵 self.conv2=nn.Conv2d(6,16,5) #定义第二个卷积层 self.fc1=nn.Linear(16*5*5,120)#16和最后一个卷积层输出对应,5*5由公式计算得到 self.fc2=nn.Linear(120,84) #这里先转换为120,再到84最后到2(ants和bees)是 self.fc3=nn.Linear(84,2) #公式写法,当然也可以直接由120到2 def forward(self,x): x=self.pool(F.relu(self.conv1(x))) #按顺序池化 x=self.pool(F.relu(self.conv2(x))) x=torch.flatten(x,1) #展平,fc只接收一维变量 x=F.relu(self.fc1(x)) x=F.relu(self.fc2(x)) x=self.fc3(x) return x #输出结果 net=Net()在fc1中的5*5原自公式:
其中W指照片原尺寸,K代表卷积核大小(或池化大小),P代表填充(默认为零),S代表步长(经过卷积核一般为1,经过池化矩阵我们这里为2)。也就是说如果我们统一的图片尺寸是256*256,那么经过两组卷积池化后会尺寸变成61*61,即fc1的括号变成(16*61*61,120),这会相当麻烦,所以如果大家想取大像素还请适当增加卷积层个数。
接下来我们开始梯度下降:
criterion=nn.CrossEntropyLoss() optimizer=torch.optim.SGD(net.parameters(),lr=0.01,momentum=0.9)#优化器选择带动量的SGD print("开始训练") for epoch in range(训练轮数): for i , data in enumerate(trainloader,0): inputs,labels = data outputs=net(inputs) optimitzer.zero_grad() #每次梯度记得归零 loss=criter(outputs,labels) loss.backward optimizer.step() print("训练完成") #储存训练结束后的参数 PATH=r'\-.pth' #储存的地址,记得提前建好.pth文件 torch.save(net.state_dict(),PATH)最后我们对训练完成后的net进行测试:
snet.load_state_dict(torch.load(PATH)) #读取上面储存的参数 net.eval() #结束学习(即下面运行测试集时不进行迭代) total=0 correct=0 with torch.no_grad(): #不计算梯度(节约算力) for data in testloader: images , labels = data outputs=net(images) _, predicted = torch.max(outputs,1)# '_,'指把返回的images值舍弃, #毕竟我们只在意labels对不对 #最后的1指的是一维 #不直接使用total=len(testset)的原因是电脑会舍弃总数对每组照片的余数 total +=labels.size(0) correct +=(predicted == labels).sum().itrm() print(f"正确率为:{100*correct//total}%。")至此我们的分辨蜜蜂和蚂蚁的神经网络就此写完了,接下来我们可以随便测试一张照片,同时想要直接读取存好的地址不重新训练(浪费时间),可以这样写:
import os PATH=r'-\.pth' #训练后参数储存的地址 if os.path.exists(PATH): net.load_state_dict(torch.load(PATH)) else: #训练过程 #…………然后:
from PIL import Image image_path=r'你想测试的照片地址' img=Image.open(image_path).convert('RGB') img_tensor=transfrom(img) #将我们选择的照片变为3*32*32的张量 img_tensor=img_tensor.unsqueeze(0) #在这个三维张量前再插入一个维度(批次1) #因为Conv2d只支持四维变量 with torch.no_grad: outputs=net(img) -, predicted = torch.max(outputs,1) result=predicted.item() print(classes[result]) #输出具体类别最后是如何运用显卡(GPU)加速我们的训练:
device=torch.device("cuda:0") #首先把net转移进GPU net=Net() net.to(device) #最后记得把数据也转移进GPU,net和数据必须同时在CPU或者同时在GPU #……训练过程…… inputs = inputs.to(device) labels = labels.to(device) #……正常后续流程……最后说说题外话,我感觉用PyCharm编写Python中的会方便很多,它会直接提醒你括号里的数字代表什么: