训练测试精度
通过修改一部分参数可以通过修改一部分参数(学习率或批量或epoch)、网络结构、卷积核大小、卷积步长、填零、通道数,以提升精度
精度要求达到70%以上
import torch
import torchvision
import torch.nn as nn
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import os
print(os.path.abspath("."))
C:\Users\ASUS\Desktop\测试精度
#超参数
num_classes = 10 #类别个数
num_epochs = 20 #迭代次数/训练次数
learning_rate = 0.001 #学习率,方法更新的速率
batch_size = 64 #批次,批量大小,每次传输图片的个数
#训练集
train_dataset = torchvision.datasets.CIFAR10(root = './data',
train = True,
download = True,
transform = transforms.ToTensor())
Files already downloaded and verified
#测试集
test_dataset = torchvision.datasets.CIFAR10(root = './data',
train = False,
download = True,
transform = transforms.ToTensor())
Files already downloaded and verified
#加载训练数据,对图片进行分组、打乱顺序
train_loader = torch.utils.data.DataLoader(dataset = train_dataset,batch_size = batch_size,shuffle = True)
#加载测试数据,对图片进行分组、打乱顺序
test_loader = torch.utils.data.DataLoader(dataset = test_dataset,batch_size = batch_size,shuffle = True)
# 定义网络结构
class network(nn.Module):
def __init__(self,num_classes):
super(network, self).__init__()
#继承父类,即此处的nn.Module
self.conv1 = nn.Sequential(nn.Conv2d(3,16,3,1,1),nn.ReLU(),nn.MaxPool2d(2,2))
#第一个卷积后的大小是:int((32-3+2*1)/1+1) = 32,第一个最大池化结果:int((32-2+2*0)/2+1) = 16
self.conv2 = nn.Sequential(nn.Conv2d(16,32,3,1,1),nn.ReLU(),nn.MaxPool2d(2,2))
#第二个卷积后的大小:int((16-3 + 2* 1)/1+1) = 16 第二个最大池化结果:int((16-2+2*0)/2 + 1) = 8
self.conv3 = nn.Sequential(nn.Conv2d(32,64,3,1,1),nn.ReLU(),nn.MaxPool2d(2,2))
#第三个卷积后的大小:int((8-3 + 2* 1)/1+1) = 8, 第三个最大池化结果:int((8-2+2*0)/2 + 1) = 4
#最终的图片size是(64,4,4)
self.fc1 = nn.Linear(64*4*4,256)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(256,128)
self.fc3 = nn.Linear(128,num_classes)
#前向传播
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = self.conv3(x)
x = x.view(x.size(0), -1)
#x.view(x.size()[0], -1)将前面多维度的tensor展平成一维
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
x = self.relu(x)
x = self.fc3(x)
return x
model = network(num_classes).cpu() #让网络模型在cpu上运行
loss = nn.CrossEntropyLoss() #交叉熵损失函数,返回值是误差,用以表示模型算法模型对图片识别的结果与图片本身的标签之间的偏差
optimizer = torch.optim.Adam(model.parameters(),learning_rate)
#优化器:优化参数,包括w和b,更新w和吧,使得算法的输出结果更贴近正确结果
#训练
group = len(train_loader) #获取图片组数
epoch_arr = []
loss_arr = []
for epoch in range(num_epochs): #num_epochs = 10,epoch是0-9,range(10)是0到9的数字序列
# i = 0
epoch_arr.append(epoch)
for i,(images,labels) in enumerate(train_loader):
images = images.cpu()
labels = labels.cpu()
#forward前向传播
output = model(images) #获取输出
cost = loss(output,labels) #获取误差
#反向传播
optimizer.zero_grad() #清空梯度存储空间b
cost.backward() #对loss进行求偏导(求梯度) ,偏w,偏b
optimizer.step() # w = w - learning_rate * 偏w , b = b - learning_rate * 偏b
if (i + 1) == 400:
loss_arr.append(cost.item())
print("epoch:[{}/{}],step:[{}/{}],loss:{:.4f}".format(epoch+1, num_epochs,i + 1, group,cost.item()))
# i += 1
# cost返回结果:tensor()
epoch:[1/20],step:[400/782],loss:1.5827
epoch:[2/20],step:[400/782],loss:1.3340
epoch:[3/20],step:[400/782],loss:1.0577
epoch:[4/20],step:[400/782],loss:0.9202
epoch:[5/20],step:[400/782],loss:0.8304
epoch:[6/20],step:[400/782],loss:0.8964
epoch:[7/20],step:[400/782],loss:0.6270
epoch:[8/20],step:[400/782],loss:0.6364
epoch:[9/20],step:[400/782],loss:0.6819
epoch:[10/20],step:[400/782],loss:0.7096
epoch:[11/20],step:[400/782],loss:0.6670
epoch:[12/20],step:[400/782],loss:0.5065
epoch:[13/20],step:[400/782],loss:0.4616
epoch:[14/20],step:[400/782],loss:0.3293
epoch:[15/20],step:[400/782],loss:0.2835
epoch:[16/20],step:[400/782],loss:0.2574
epoch:[17/20],step:[400/782],loss:0.3087
epoch:[18/20],step:[400/782],loss:0.3039
epoch:[19/20],step:[400/782],loss:0.2828
epoch:[20/20],step:[400/782],loss:0.2881
#测试
with torch.no_grad(): #测试阶段不需要梯度更新(参数更新),即:w和b不需要求导求梯度,因此不需要grad
correct = 0 # 存储识别正确的图片张数
for images,labels in test_loader:
images = images.cpu()
labels = labels.cpu()
#前向传播
output = model(images) #output仅仅是模型的输出,形式是10个张量类型的数据tensor([])
_,predict = torch.max(output.data,1) # predict是确切的某一个类,预测结果
#torch.max(x,1) 返回的是两个结果,第一个是x里的最大值,第二个是最大值所在的位置(索引)
#最大值所在的位置(索引)才是预测的类
#比如图片0,torch.max(output,1)结果是tensor([22,13,10,11,5,19,14,0,4,1]),最大值是22,22所在位置是0,则predict = 0
correct += (predict == labels).sum().item() # (predict == labels).sum().item()的结果是每一组图片中预测正确的图片数(识别正确的数量)
accruacy = correct / len(test_dataset)
print("Accuracy:{:.2f}%".format(accruacy * 100))
Accuracy:72.29%
实践练习总结
手写体识别、口罩识别和卷积神经网络
全链接算子实现手写体识别和口罩识别:
在这个项目中,我学习了全链接算子的原理和实现方式,并将其应用于手写体识别和口罩识别。通过对手写数字和口罩图像的训练和测试,我成功实现了对手写数字和佩戴口罩的识别。这个项目让我深入了解了全链接算子的工作原理,以及如何使用它来解决实际问题。
在手写体识别方面,我首先搜集了大量的手写数字样本,并对它们进行了预处理,包括图片的灰度化、二值化等。然后,我将处理后的图像输入到全链接算子中进行训练和测试。通过逐步调整参数和优化模型,我取得了不错的识别准确率。
在口罩识别方面,我使用了与手写体识别类似的方法。我收集了戴口罩和不戴口罩的图像样本,并对它们进行了相应的预处理。然后,我将处理后的图像输入到全链接算子中进行训练和测试。通过不断调整参数和改进模型,我实现了对佩戴口罩的识别。
卷积神经网络:
在这个项目中,我学习了卷积神经网络(CNN)的原理和应用。我了解了CNN在图像处理和识别方面的优势,并尝试将其应用于手写体识别和口罩识别。
在手写体识别方面,我设计了一个简单的CNN模型,并使用手写数字数据集进行了训练和测试。通过不断调整网络结构和参数,我取得了比全链接算子更好的识别效果。这个项目让我深入理解了CNN的工作原理和训练过程,并为我今后在图像处理领域的学习和实践打下了坚实的基础。
在口罩识别方面,我同样使用了CNN模型。我收集了更多的口罩图像,并通过数据增强等方法扩充了数据集。通过对CNN模型进行训练和测试,我得到了较好的识别结果。这次实践让我深刻认识到了CNN在图像识别方面的强大能力,并激发了我进一步探索深度学习的兴趣。
# Load in relevant libraries, and alias where appropriate
import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
import numpy as np
# Define relevant variables for the ML task
batch_size = 64 #批量大小,用于将训练集分成一组一组的图片组,每组64张图片
num_classes = 10 #图片类别个数
learning_rate = 0.001 #学习率,规定优化器更新梯度的速率/幅度
num_epochs = 10 #训练次数(又称为迭代次数)
# Device will determine whether to run the training on GPU or CPU.选择设备,cpu或者gpu
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
prepare the data
#Loading the dataset and preprocessing
#训练集图片60000张,测试集图片10000张。
train_dataset = torchvision.datasets.MNIST(root = './data',
train = True,
transform = transforms.Compose([
transforms.Resize((28,28)),
transforms.ToTensor()]),
download = True)
test_dataset = torchvision.datasets.MNIST(root = './data',
train = False,
transform = transforms.Compose([
transforms.Resize((28,28)),
transforms.ToTensor()]),
download=True)
#对训练集和测试集进行分批处理,缩短训练时间,同时处理64图片。
train_loader = torch.utils.data.DataLoader(dataset = train_dataset,
batch_size = batch_size,
shuffle = True)
test_loader = torch.utils.data.DataLoader(dataset = test_dataset,
batch_size = batch_size,
shuffle = True)
define the model class
#Defining the convolutional neural network
class Network(nn.Module):
def __init__(self, num_classes):
super(Network, self).__init__() #重写父类nn.Module,用子类对象调用父类已被覆盖的方法
self.fc1 = nn.Linear(28*28, 64) #self+变量名:定义实例变量。 第一层全连接神经网络,输入大小28*28=784,输出大小64
self.relu = nn.ReLU() #激活层
self.fc2 = nn.Linear(64, num_classes) #第二层全连接神经网络,输入大小64,输出大小类别个数,本实验中是10
def forward(self, x):
out = x.view(x.size(0),-1) #x.size(0)是去掉第零维,也就是(64,1,28,28)里的“64”,这一维。64代表的是batch_size = 64
out = self.fc1(out) #第一层输出
out = self.relu(out) #激活层输出
out = self.fc2(out) #第二层输出
return out
model = Network(num_classes).to(device) #向Network这个类传参,并且规定跑模型所用的设备
#Setting the loss function,损失函数
cost = nn.CrossEntropyLoss()
#Setting the optimizer with the model parameters and learning rate,优化器
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
#this is defined to print how many steps are remaining when training,计算训练集的批量
total_step = len(train_loader)
train the model
#训练部分
for epoch in range(num_epochs): #外循环,遍历训练次数(迭代10次)
for i, (images, labels) in enumerate(train_loader): #内循环,遍历训练集每个批次的图片和对应的标签,一共遍历938次。
#enumerate()函数是枚举的作用,i是从0到937。
images = images.to(device) #将图片转化成张量数据,对数据使用device就是将数据转化为张量格式。
labels = labels.to(device) #将标签转化成张量数据
#Forward pass,前向传播
outputs = model(images) #将张量格式的图片数据传到模型当中,得到输出结果
loss = cost(outputs, labels) #运用损失函数,传入模型输出结果和标签,作为参数,得到损失值
# Backward and optimize #反向传播,此处类似于数学里的求导(求梯度:gradient)
optimizer.zero_grad() #每次计算一组图片数据的梯度是,需要将内存中原来存储的梯度数据进行清空。
loss.backward() #对求得的损失进行求梯度
optimizer.step() #更新梯度的同时,也更新了梯度。
if (i + 1) == 400:
print("epoch:[{}/{}],step:[{}/{}],loss:{:.4f}".format(epoch+1, num_epochs, i+1, total_step, loss.item()))
#分别输出当前迭代次数,总迭代次数,当前训练图片组数,总的图片批量,损失结果。
#"" .format()为python定义输出格式
epoch:[1/10],step:[400/938],loss:0.2036
epoch:[2/10],step:[400/938],loss:0.2228
epoch:[3/10],step:[400/938],loss:0.1541
epoch:[4/10],step:[400/938],loss:0.1869
epoch:[5/10],step:[400/938],loss:0.0997
epoch:[6/10],step:[400/938],loss:0.0965
epoch:[7/10],step:[400/938],loss:0.0547
epoch:[8/10],step:[400/938],loss:0.0437
epoch:[9/10],step:[400/938],loss:0.0407
epoch:[10/10],step:[400/938],loss:0.0077
# Test the model,测试部分
# In test phase, we don't need to compute gradients (for memory efficiency)
with torch.no_grad(): #由于是测试,不必再对参数进行更新,因此也不必更新梯度,所以强调no_grad
correct = 0 #用于存储总的测试正确个数
total = 0 #获取总的图片个数(此处是获取测试集总图片数,也就是10000)
for images, labels in test_loader: #遍历分批处理后的测试集
images = images.to(device)
labels = labels.to(device)
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
#torch.max(outputs.data, 1)的返回值是两个,第一个是outputs.data的最大值,第二个是该最大值的索引(也就是所在位置,类似于数组里的最大值索引)
#由于最大值的索引对应着测试结果(也就是训练后模型的预测结果),所以只需要第二个返回值即可,第一个返回值是什么都无所谓。
#下划线也可以换成别的变量名,无实义
total += labels.size(0)
correct += (predicted == labels).sum().item() #.item()用于获取张量数据中的数据值。此处获取的是每组图片预测正确的总数
print('Accuracy of the network on the 10000 test images: {} %'.format(100 * correct / total))
model structure
from torchsummary import summary
summary(Network(num_classes),input_size=[(1,28,28)])
save the model
torch.save(model, 'model.pth')
model_path = "model.pth"
network = torch.load(model_path)
parm = {}
for name,parameters in network.state_dict().items():
parm[name] = parameters.detach().numpy()``
parm['fc1.weight'].transpose().shape
口罩识别
import os
def train_val(labels_path, data_path, ratio=0.3):
nomask_num = 0#计数nomask的数量
mask_num = 0#计数mask的数量
image_dir = "\\".join(data_path.split("\\")[-3:]) + "\\images"#根据yolo2coco要求制定路径
txt_files = os.listdir(labels_path)
f_train = open("train.txt", "w")
f_val = open("val.txt", "w")
m = 0
n = 0
for txt in txt_files:
f_txt = open(os.path.join(labels_path, txt), 'r')#打开txt文件
if f_txt.read()[0] == "0":#读取每个文件的第一行,判断是nomask(0)还是mask(1)
nomask_num += 1#不戴口罩加1
else:
mask_num += 1#戴口罩加1
f_txt.close()
for txt in txt_files:
f_txt = open(os.path.join(labels_path, txt), 'r')
if f_txt.read()[0] == "0":#读取每个文件的第一行,判断是nomask(0)还是mask(1)
n += 1
if n >= int(nomask_num * ratio):
f_train.writelines(image_dir+"\\" + txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
m += 1
if m >= int(mask_num * ratio):
f_train.writelines(image_dir+"\\" + txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
f_txt.close()
f_train.close()
f_val.close()
if __name__ == "__main__":
data_path = os.path.join(os.getcwd(), "mask")#获取文件夹mask的绝对路径
labels_path = os.path.join(data_path, "labels")#获取labels文件夹的绝对路径
train_val(labels_path=labels_path, data_path=data_path, ratio=0.3)
结果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/863b70e9f0cd44bab02d402a3a4c52d6.png#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/765ac66fd34146ffb314c6bc9d8062d2.png#pic_center)
# 总结
通过这两个项目的实践,我不仅学到了很多关于全链接算子和卷积神经网络的知识和技能,还提高了自己的编程能力和解决问题的能力。这些经验对我的职业发展具有重要意义,我将继续深入学习和实践,不断提升自己在图像处理和机器学习领域的能力。