文章目录[隐藏]
目标检测—全卷积实现
首先照例分享学习资源:
带你逐行手写单目标检测算法,从数据到模型搭建、训练、预测_哔哩哔哩_bilibili
一.相关知识点的学习
二分类交叉熵:
其实现的公式:
上式中带上标的y表示预测值(0-1之间),是网络的预测结果,y是真实值,因为是二分类,所以y的值只分0和1
MSE:
KSE(均方误差)函数一般用来检测模型的预测值和真实值之间的偏差。
其实现公式为:
其值越大,表面预测的效果越差,MSE的值越小,说明预测模型描述实验数据具有更好的精确度。
多分类交叉熵:
其实现公式为:
在上式中,C是损失值
n是求平均用的,所以是样本数量,也就是batchsize(每批训练的数量);
x是预测向量维度,因为需要在输出的特征向量维度上一个个计算并求和;
y是onehot编码后的真实值 对应x维度上的标签,是1或0;
a是onehot格式输出的预测标签,是0~1的值,a经过了softmax激活,所以a的和值为1。
其中(onehot编码)独热编码即 One-Hot 编码,又称一位有效编码。其方法是使用 N位 状态寄存器来对 N个状态 进行编码,每个状态都有它独立的寄存器位,并且在任意时候,其中只有一位有效。
FCN网络:
首先,FCN和CNN的区别在于把CNN最后的全连接转换成卷积层,其次,FCN网络可以接受任意尺寸的输入图像,并采用反卷积层对最后一个卷积层的feature map(特征图)进行上采样, 使它恢复到输入图像相同的尺寸,从而可以对每个像素都产生了一个预测, 同时保留了原始输入图像中的空间信息, 最后在上采样的特征图上进行逐像素分类。
二.代码实现目标检测
二分类:sigmoid函数
回归问题:不需要激活
多分类问题:输出层使用softmax
(1)数据预处理
#数据集处理
import os.path
import torch
import cv2
from torch.utils.data import Dataset
import numpy as np
class MyDataset(Dataset):
def __init__(self,root,is_Train = True):
self.dataset = [] #数组存储图片路径
dir = 'train' if is_Train else "test" #通过is_Train的判断,来确定访问的文件路径(train还是test)
sub_dir = os.path.join(root,dir) #拼接文件路径,进入test或train文件
print("The current picture is from ",sub_dir)
img_list = os.listdir(sub_dir) #返回指定的文件夹包含的文件或文件夹的名字的列表
for i in img_list:
img_dir = os.path.join(sub_dir,i)
self.dataset.append(img_dir) #将拼接后图片路径存入数组中
def __len__(self):
return len(self.dataset) #返回图片个数
def __getitem__(self,index):
data = self.dataset[index]
img = cv2.imread(data)/255 #将图片格式由H,W,C格式转换到C,H,W格式,并归一化,其中C为通道数、W为宽度、H为高度
new_img = torch.tensor(img).permute(2,0,1) #将图片tensor维度转换
data_list = data.split('.') #将文件名按.划分
label = int(data_list[1]) #标签
position = data_list[2:6] #位置
position = [int(i)/300 for i in position]
sort = int(data_list[6])-1 #因为标签为0时是没有小黄人,就无法给到0分类,而算法需要以0开始,所以我们将其减1,将1-20的数转换为0-19
return np.float32(new_img),np.float32(label),np.float32(position),np.int(sort)
if __name__ == '__main__':
data = MyDataset('F:\Artificial Intelligence\Target detection\yellow_data\yellow_data',is_Train=False)
for i in data:
print(i)
上述代码中,主要注意讲sort的标签从1-20转换为0-19
(2).网络搭建
#搭建网络
from torch import nn
import torch
class My_net(nn.Module):
def __init__(self):
super(My_net,self).__init__()
#nn.Sequential()操作打包
self.layers = nn.Sequential(
nn.Conv2d(3,11,3), #二维卷积
nn.LeakyReLU(), #大于0的值保留,小于零的值赋值非负斜率
nn.MaxPool2d(3), #最大池化,对邻域内特征点取最大
nn.Conv2d(11, 22, 3),
nn.LeakyReLU(),
nn.MaxPool2d(2),
nn.Conv2d(22, 32, 3),
nn.LeakyReLU(),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 3),
nn.LeakyReLU(),
nn.Conv2d(64, 128, 3),
nn.LeakyReLU(),
)
self.label_layer = nn.Sequential(
nn.Conv2d(128,1,19),
nn.LeakyReLU()
)
self.position_layer = nn.Sequential(
nn.Conv2d(128,4,19),
nn.LeakyReLU()
)
self.sort_layer = nn.Sequential(
nn.Conv2d(128,20,19),
nn.LeakyReLU()
)
def forward(self,x):
out = self.layers(x)
label = self.label_layer(out)
label = torch.squeeze(label,dim = 2) #torch.squeeze对数据进行压缩,dim指维度
label = torch.squeeze(label, dim=2)
label = torch.squeeze(label, dim=1)
position = self.position_layer(out)
position = torch.squeeze(position,dim = 2)
position = torch.squeeze(position,dim = 2)
sort = self.sort_layer(out)
sort = torch.squeeze(sort,dim = 2)
sort = torch.squeeze(sort, dim=2)
return label,position,sort
if __name__ == '__main__':
net = My_net()
x = torch.randn(3,3,300,300)
print(net(x)[0].shape)
print(net(x)[1].shape)
print(net(x)[2].shape)
注意对应维度的转换。
(3).训练函数编写
from net import My_net
from data import MyDataset
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from torch import nn,optim
import torch
import datetime
import os
import warnings
warnings.filterwarnings('ignore') #不加这一句,代码运行前会报warning
Device = 'cuda' #使用GPU
class train:
def __init__(self,root,weight): #若没有权重文件,则去除weight进行训练
self.summaryWriter = SummaryWriter('logs')
#SummaryWriter()创建一个tensorboard文件,log为存储路径
self.train_dataset = MyDataset(root = root,is_Train=True)
self.test_dataset = MyDataset(root = root,is_Train=False)
#引入训练、测试集数据
#Dataloder构建可迭代的数据装载器, 我们在训练的时候,每一个for循环,每一次iteration,就是从DataLoader中获取一个batch_size大小的数据的。
self.train_dataLoader = DataLoader(self.train_dataset,batch_size=50,shuffle=True)
self.test_dataLoader = DataLoader(self.test_dataset,batch_size=50,shuffle=True)
self.net = My_net().to(Device) #将网络导入GPU
if os.path.exists(weight): #调用训练好的权重,若没有此文件,请注释这一句以及下一句,进行训练产生文件
self.net.load_state_dict(torch.load(weight))
self.opt = optim.Adam(self.net.parameters()) #调用优化器
self.label_loss_fun = nn.BCEWithLogitsLoss() #二分类交叉熵损失
self.position_loss_fun = nn.MSELoss() #均方误差;函数一般用来检测模型的预测值和真实值之间的偏差。
self.sort_loss_fun = nn.CrossEntropyLoss() #交叉熵
self.train = True
self.text = True
def __call__(self):
index1,index2 = 0,0 #步长
for epoch in range(1000):
if self.train:
# enumerate枚举
for i,(img,label,position,sort) in enumerate(self.train_dataLoader):
self.net.train()
img,label,position,sort = img.to(Device),label.to(Device),position.to(Device),sort.to(Device)
# 变量传入GPU内
out_label,out_position,out_sort = self.net(img)
# 获取网络输出值
label_loss = self.label_loss_fun(out_label,label)
position_loss = self.position_loss_fun(out_position,position)
# torch.where 合并两个tensor类型,合并0-19的sort
sort = sort[torch.where(sort>=0)]
out_sort = out_sort[torch.where(sort>=0)]
sort_loss = self.sort_loss_fun(out_sort,sort)
train_loss = 0.2 * label_loss + position_loss * 0.6 + 0.2 * sort_loss
# 计算损失函数 全局优化
self.opt.zero_grad()
train_loss.backward()
# 反向传播
self.opt.step()
# 根据梯度更新网络参数
if i%10 == 0:
print('train_loss{i}=====>',train_loss.item())
self.summaryWriter.add_scalar('train_loss',train_loss,index1)
# 将标量添加到 summary , 将损失画在图中
index1+=1
date_time = str(datetime.datetime.now()).replace(' ', '-').replace(':', '_').replace('.', '_')
# 获取当前时间
torch.save(self.net.state_dict(), f'param/{date_time}-{epoch}.pt')
# 保存权重模型
if self.text:
sum_sort_acc,sum_label_acc = 0,0
for i, (img, label, position, sort) in enumerate(self.train_dataLoader):
self.net.train()
img, label, position, sort = img.to(Device), label.to(Device), position.to(Device), sort.to(Device)
out_label, out_position, out_sort = self.net(img)
label_loss = self.label_loss_fun(out_label, label)
position_loss = self.position_loss_fun(out_position, position)
sort = sort[torch.where(sort >= 0)]
out_sort = out_sort[torch.where(sort >= 0)]
sort_loss = self.sort_loss_fun(out_sort, sort)
test_loss = label_loss + position_loss + sort_loss
out_label = torch.tensor(torch.sigmoid(out_label))
# 调用激活函数后转换为张量
out_label[torch.where(out_label>=0.5)] = 1
out_label[torch.where(out_label < 0.5)] = 0
out_sort = torch.argmax(torch.softmax(out_sort,dim = 1))
# 返回概率最大的下标
label_acc=torch.mean(torch.eq(out_label,label).float())
sum_label_acc+=label_acc
sort_acc = torch.mean(torch.eq(out_sort,sort).float())
sum_sort_acc += sort_acc
if i % 10 == 0:
print('test_loss{i}=====>', test_loss.item())
self.summaryWriter.add_scalar('test_loss', test_loss, index2)
index2 += 1
avg_sort_acc = sum_sort_acc/i
avg_label_acc = sum_label_acc/i
# 以上操作进行准确率的计算
print(f'avg_sort_acc{epoch}===>',avg_sort_acc)
print(f'avg_label_acc{epoch}===>', avg_sort_acc)
self.summaryWriter.add_scalar('avg_sort_acc',avg_sort_acc,epoch)
self.summaryWriter.add_scalar('avg_label_acc', avg_label_acc, epoch)
if __name__ == '__main__':
train = train('F:\Artificial Intelligence\Target detection\yellow_data\yellow_data','param/2021-10-19-23_38_55_992635-0.pt')
train() # 给予文件路径进行训练
若未有weight文件的话,先注释掉weight部分,否则代码无法运行,具体可以看分享的学习资料。
(4).预测函数
import os
import torch
import cv2
from net import My_net
if __name__ == '__main__':
img_name = os.listdir(r'F:\Artificial Intelligence\\Target detection\yellow_data\yellow_data\\test')
for i in img_name:
img_dir = os.path.join(r'F:\Artificial Intelligence\\Target detection\yellow_data\yellow_data\\test',i)
# 拼接路径 访问图片
img = cv2.imread(img_dir)
# 读取图片信息
position = i.split('.')[2:6]
# 将position信息分离
sort = i.split('.')[6]
position = [int(j) for j in position]
cv2.rectangle(img,(position[0],position[1]),(position[2],position[3]),(0,255,0),thickness = 2)
cv2.putText(img,sort,(position[0],position[1]-3),cv2.FONT_HERSHEY_SIMPLEX,2,(0,255,0),thickness = 2)
# 对识别到的小黄人进行画框
model = My_net()
# 导入模型
model.load_state_dict(torch.load('param/2021-10-20-08_13_42_378776-0.pt'))
# 保存模型
new_img = torch.tensor(img).permute(2,0,1)
# 转换图片维度
new_img = torch.unsqueeze(new_img,dim = 0)/255
# 将图片拓展
out_label,out_sort,out_position = model(new_img)
out_label = torch.sigmoid(out_label)
out_sort = torch.argmax(torch.softmax(out_sort,dim = 1))
# 调用激活函数
out_position = out_position[0]*300
out_position = [int(i) for i in out_position]
if out_label.item()>0.5:
cv2.rectangle(img,(out_position[0],out_position[1]),(out_position[2],out_position[3]),(0,255,0))
cv2.putText(img,str(out_sort.item()),(out_position[0],out_position[1]),cv2.FONT_HERSHEY_SIMPLEX,2,(0,0,255))
cv2.imshow('img',img)
cv2.waitKey(500)
cv2.destroyAllWindows()
# 每500ms 换检测一次图片
10.20学习总结
版权声明:本文为CSDN博主「风声向寂」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_52533790/article/details/120872424
暂无评论