项目git 代码:
https://github.com/Boris-2021/Location_awareness-.git
流程图架构图
数据集的收集
- 分五类地点:
- 正门口前台前廊道。
- 男厕所门前。
- 资料室门前廊道。
- 会议室门前廊道。
- 办公室B区门前廊道。
- 收集数量计划:
- 站立姿势竖直拍摄方向
- 一点365度,50张照片
- 5点共250张照片
- 一个地点一个数据集文件夹
数据处理及拆分
创建图像处理类:
class RMB_dataset(Dataset):
def __init__(self, path="", transform=None):
# 输入:图像的存储位置
# 负责:根据指定的路径 将训练集 or 测试集 or 验证集 组织成如下形式
# train_data = [[图像名称1, 类别1],[图像名称2, 类别2],....[图像名称n, 类别n]]
self.img_info = []
for root, dir, files in os.walk(path):
for file in files:
file_name = os.path.join(root, file)
label = int(file_name.split('\\')[-2])
# print(file_name)
# 0: '办公室B区门前廊道', 1: '会议室门前廊道', 2: '男厕所门前', 3: '正门口前台前廊道', 4: '资料室门前廊道'
self.img_info.append([file_name, label])
self.transfrom = transform
# def __getitem__(self, item):
def __getitem__(self, index):
# 逐张读取图像
# 之后进行图像处理:旋转、缩放、模糊,翻转
# index 是个下标。范围【0,训练集的长度)
img_name, label = self.img_info[index] # 读图像
img = Image.open(img_name) # image mode=RGB size=3468x4624
if self.transfrom is not None:
img = self.transfrom(img)
return img, label
def __len__(self):
# 返回值是整个数据集的长度
return len(self.img_info)
该类及方法得作用是将图像像素点得RGB值,加载为网络可以输入得数据格式。[[图像1, 类别1],[图像2, 类别2],....[图像n, 类别n]]。
计算图像集合中图像RGB值得均差,标准差方法:
# 计算数据集的均值和标准差
def getStat(train_data):
'''
Compute mean and variance for training data
:param train_data: 自定义类Dataset(或ImageFolder即可)
:return: (mean, std)
'''
print('Compute mean and variance for training data.')
print(len(train_data))
train_loader = torch.utils.data.DataLoader(
train_data, batch_size=1, shuffle=False, num_workers=0,
pin_memory=True)
mean = torch.zeros(3)
std = torch.zeros(3)
for X, _ in train_loader:
for d in range(3):
mean[d] += X[:, d, :, :].mean()
std[d] += X[:, d, :, :].std()
mean.div_(len(train_data))
std.div_(len(train_data))
return list(mean.numpy()), list(std.numpy())
将数据集得目录输入进方法,得到数据集得mean及std。norm_std = [0.211, 0.223, 0.237],norm_mean = [0.472, 0.456, 0.425]。
transform模块将图像4维numpy矩阵进行缩放,灰度,归一化处理。
# 仅在训练集中增加大量的图像处理,灰度处理
trans_train = transforms.Compose([
transforms.Resize((Image_size, Image_size,)), # 缩放图像
transforms.RandomGrayscale(p=0.9), # 90%的数据灰度化
transforms.ToTensor(), # 将图像转换为tensor, 除255
transforms.Normalize(norm_mean, norm_std) # 归一化
])
最终得转化为网络可用得数据格式得图像数据,打乱分批(batch)神经网路模型一个批次一个批次得训练。
# 针对训练集
train_path = "dataset/train"
train_data = RMB_dataset(path=train_path, transform=trans_train)
train_loader = DataLoader(
dataset=train_data, # 数据类的对象
batch_size=Batch_size,
shuffle=True,
# num_workers=1
Batch_size = 20 每二十个图像数据为一个批次。
模型的建立(LeNet-5)
参考 https://blog.csdn.net/qq_42570457/article/details/81460807
使用pytorch模块建立网络结构:
# 网络结构
# 基本定义__init__, 前向传播forward
class LeNet(nn.Module):
def __init__(self, classes):
# 初始化函数中,定义每层
super(LeNet, self).__init__()
# 输入的通道数3,输出的通道数6,卷积核的宽和高都是5
# 卷积核:6*3*5*5
self.conv1 = nn.Conv2d(3, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5) # 卷积核 16*6*5*5
self.fc1 = nn.Linear(16*13*13, 120) # 全连接
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, classes)
self.relu = nn.ReLU(inplace=True)
def forward(self, x):
# X: B*C*H*W
# X: 1*3*64*64
out = F.relu(self.conv1(x)) # 1*6*64*64
out = F.max_pool2d(out, 2) # 核的大小2*2; 1*6*60*60
out = F.relu(self.conv2(out)) # 1* 16*30*30
out = F.max_pool2d(out, 2) # 核的大小2*2;1*16*26*26
out = out.view(out.size(0), -1) # 展平(1, 16*13*13)==(1, 2704)
out = F.relu(self.fc1(out)) # full connect (1, 120)
out = F.relu(self.fc2(out)) # (1, 84)
out = self.fc3(out) # (1, 类别数)
return out
网络识别图片过程:
LeNet-5是一个较简单的卷积神经网络。下图显示了其结构:输入的二维图像,先经过两次卷积层到池化层,再经过全连接层,最后输出层。
各层参数详解:
1)INPUT层-输入层,首先是数据 INPUT 层,输入图像的尺寸统一缩放并归一化为64*64。
2)conv1卷积层:
输入图片:64*64
卷积核大小:5*5
卷积核种类:6
输出featuremap大小:60*60 (64-5+1)=60
神经元数量:60*60*6
可训练参数:(5*5+1) * 6(每个滤波器5*5=25个unit参数和一个bias参数,一共6个滤波器)
连接数:(5*5+1)*6*60*60=129600
3)max_pool2d池化层:
输入:60*60
采样区域:2*2
采样方式:4个输入相加,乘以一个可训练参数,再加上一个可训练偏置。
采样种类:6
输出featureMap大小:30*30(60/2)
神经元数量:30*30*6
可训练参数:2*6(和的权+偏置)
连接数:(2*2+1)*6*30*30
S2中每个特征图的大小是C1中特征图大小的1/4。
4)conv2卷积层:
输入:30*30
卷积核大小:5*5
卷积核种类:16
输出featureMap大小:26*26 (30-5+1)=26
神经元数量:26*26*16
可训练参数:(5*5+1) * 6(每个滤波器5*5=25个unit参数和一个bias参数,一共6个滤波器)
连接数:(5*5+1)*6*26*26
5) max_pool2d池化层:
输入:26*26
采样区域:2*2
采样方式:4个输入相加,乘以一个可训练参数,再加上一个可训练偏置。
采样种类:16
输出featureMap大小:13*13(26/2)
神经元数量:13*13*6
可训练参数:2*16(和的权+偏置)
连接数:(2*2+1)*6*13*13
S2中每个特征图的大小是C1中特征图大小的1/4。
6)fc1全连接层:
输入:16*13*13 = (1, 2704)
计算方式:计算输入向量和权重向量之间的点积,再加上一个偏置。
可训练参数:120*( 2704+1)
输出:(1,120)
7)Fc2全连接层:
输入:(1,120)
计算方式:计算输入向量和权重向量之间的点积,再加上一个偏置。
可训练参数:84*( 120+1)
输出:84
8)Fc2全连接层:
84==>5
模型的训练过程
- 设置超参数:
max_epochs = 30 表示样集训练30轮。
LR = 0.01 初始学习率0.01。
Batch_size = 20 一批20个样本。
Image_size = 64 图片缩放尺寸64。
- loss_fun = nn.CrossEntropyLoss() 使用交叉熵损失函数。
- optimizer = Adam(model.parameters(), lr=LR) 梯度下降方法ADAM,牛顿法的一种升级方法,可以尽快的达到收敛效果。
- 保存模型:
if len(acc_rate_list) > 2 and acc_rate > acc_rate_list[-2]:
# 保存模型
torch.save(model.state_dict(), 'model/location.pth')
保证了模型保存准确率最高的那一个。
结果展示
预测方法:
def predicted(img_name):
img = Image.open(img_name).convert('RGB')
norm_mean = [0.472, 0.456, 0.425]
norm_std = [0.211, 0.223, 0.237]
img = F.resize(img, size=(64, 64)) # 缩放
img = F.to_tensor(img).to(device)
img = F.normalize(img, norm_mean, norm_std)
# print(img)
# print(img.shape)
# 将图片扩展为四维
img = img.expand(1, 3, 64, 64)
# print(img)
# print(img.shape)
output = model(img)
_, y_pred = torch.max(output, dim=1)
y_pred = y_pred.data.cpu().numpy()[0]
transition_dict = {0: '办公室B区门前廊道', 1: '会议室门前廊道', 2: '男厕所门前', 3: '正门口前台前廊道', 4: '资料室门前廊道'}
pred_location = transition_dict[y_pred] # 转化为位置信息
org_img = Image.open(img_name)
plt.imshow(org_img)
plt.title(pred_location)
plt.show()
将想要预测的图像路径传如预测方法。得到预测结果。
目前模型训练30轮,在测试集中现实的准确率达到80%左右。
项目git 代码:
https://github.com/Boris-2021/Location_awareness-.git