RomanticQq

保持对生活的热爱,唯有生活不可被辜负

0%

  1. NMS的详细过程;

    答案

  2. 激活函数都有哪些,以及它们的值域;

    答案

  3. 梯度消失和梯度爆炸的原因;

  4. BP算法;

  5. 对于平均池化层和最大池化层的BP算法的推导;

  6. 过拟合的损失函数会呈现一个什么样的状态;

在缺少有效预防欠拟合和过拟合措施的情况下,随着模型拟合能力的增强,错误率在训练集上逐渐减小,而在验证集上先减小后增大;当两者的误差率都较大时,处于欠拟合状态(high bias, low variance);当验证集误差率达到最低点时,说明拟合效果最好,由最低点增大时,处与过拟合状态(high variance, low bias)。(参考于https://blog.csdn.net/txpp520/article/details/105347189)

查准率和查全率

查准率:就是预测是真的中,实际是真的的比例;

查全率:就是标签是真的中,预测是真的的比例;

预测真 预测假
标签真 TP FN
标签假 FP TN

T/F:True/False;

P/N:Positive/Negative

Precision(查准率或精确率) = TP / (TP+FP)

Recall(查全率或召回率) = TP / (TP+FN)

IOU

IOU就是预测框和真实框两则的交集和并集的比值;

置信度

confidence表示它是一个物体的可能性有多大;

AP和mAP

map指标综合衡量检测效果;

求AP值首先需要设定一个IOU阈值,按照置信度的大小对所有的预测框进行排序;

然后分别按照每个置信度的值为阈值求出P和R,有多少个预测框就有多少对P、R值(P、R值可能会相同);

把P-R值绘制到P-R图像上面;

对PR曲线进行平滑处理,即对PR曲线上的每个点,Precision的值取该点右侧最大的Precision的值;

求出PR曲线平滑后围成图形的面积,即为AP值;

求出各个类的AP,然后求平均,即为mAP(通常来说AP是在单个类别下的,mAP是AP值在所有类别下的均值);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
import os
import matplotlib.pyplot as plt
import numpy as np
import torch
from torch import nn
import torchvision
import torch.optim as optim
from torchvision import transforms,models,datasets
import imageio
import time
import warnings
import random
import sys
import copy
import json
from PIL import Image

# 数据读取与预处理操作
data_dir = './flower_data/'
train_dir = data_dir + '/train'
valid_dir = data_dir + '/valid'
num_classes = 102

# 制作好数据源

# 数据增强(Data Augmentation)
# 数据不够怎么办?如何更高效的利用数据?
# 对图像进行镜像,翻转,旋转,放大,缩小

# 制作好数据源
# data_transforms中指定了所有图像预处理操作
# ImageFolder假设所有的文件按文件夹保存好,每个文件夹下面存同一类别的图片,文件夹的名字为分类的名字
data_transfroms = {
'train':transforms.Compose([ # transforms.Compose()类用来组合多个torchvision.transforms操作。
transforms.RandomRotation(45),# 随机旋转,-45到45度之间随机选
transforms.CenterCrop(224), #从中心开始裁剪 留下224*224的图像区域 另外还有随机裁剪
transforms.RandomHorizontalFlip(p=0.5), # 随机水平翻转,选择一个概率 做镜像
transforms.RandomVerticalFlip(p=0.5), # 随机垂直翻转 做镜像
transforms.ColorJitter(brightness=0.2,contrast=0.1,saturation=0.1,hue=0.1), # 参数1为亮度,参数2为对比度,参数3为饱和度,参数4为色相
transforms.RandomGrayscale(p=0.025), # 概率转换成灰度率,3通道就是R=G=B
transforms.ToTensor(), # ???归一化
# PIL Image或numpy.ndarray(形状为HxWxC)数据范围是[0,255]到一个Torch.FloatTensor

transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]) # 均值,标准差
# 为了使迁移学习效果更好,用人家模型的均值和标准差
]),
# 除了数据增强外,训练集和验证集的数据处理要一样
'valid':transforms.Compose([
transforms.Resize(256), #调整图片的大小,使图片
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]) # 均值,标准差 和训练时参数设置一样
# 因为这里是预测图像的结果,不需要考虑训练时的一些参数设置
]),
}

# Batch数据制作
batch_size = 8 #根据内存适当的调整大小

# datasets.ImageFolder(root,transform,...)
# root 图片存储的根路径 transform 图片进行的变换
# 返回值 self.classes:用一个list保存类别名称;...
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir,x),data_transfroms[x]) for x in ['train','valid']}
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x],batch_size=batch_size,shuffle=True) for x in ['train','valid']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['train','valid']}
class_names = image_datasets['train'].classes
print(class_names)
# print(dataset_sizes)

# 读取标签对应的名字
# with as 操作已经打开的文件对象(本身就是上下文管理器),无论期间是否抛出异常,都能保证 with as 语句执行完毕后自动关闭已经打开的文件。
# target 参数用于指定一个变量,该语句会将 expression 指定的结果保存到该变量中
with open('cat_to_name.json','r') as f:
cat_to_name = json.load(f) # json.load读取文件类型,返回的数据类型是字典
print(cat_to_name)

# 展示下数据
# 注意tensor的数据需要转化成numpy的格式,而且还需要还原回标准化的结果
def im_convert(tensor):
# 展示数据
image = tensor.to("cpu").clone().detach()
image = image.numpy().squeeze()
image = image.transpose(1,2,0) # h,w,c 在tensor中c,h,w
image = image * np.array((0.229,0.224,0.225)) + np.array((0.485,0.456,0.406)) # 把数据还原回去
image = image.clip(0,1)
return image

fig = plt.figure(figsize=(20,12))
# plt.figure设置画布的大小和背景 figsize:设置画布大小
columns = 4
rows = 2

dataiter = iter(dataloaders['train']) # iter的输入是支持迭代的集合对象
print(dataiter)
inputs,classes = dataiter.next()
print(classes)

for idx in range(columns * rows):
# add_subplot(rows, columns, idx) 将画布分成rows行,columns列,图像画在从左到右从上到下的第idx块
ax = fig.add_subplot(rows, columns, idx+1, xticks=[], yticks=[])
ax.set_title(cat_to_name[str(int(class_names[classes[idx]]))])
plt.imshow(im_convert(inputs[idx]))
# plt.show()

# 加载models中提供的模型,并且直接用训练好的权重当做初试化参数
# 第一次执行需要下载,可能会比较慢

model_name = 'resnet' # 可选的比较多['ResNet','alexnet','vgg','squeezenet','densenet','inception']
# 是否用人家训练好的特征来做
feature_extract = True
# 是否用GPU训练
train_on_gpu = torch.cuda.is_available()
if not train_on_gpu:
print('GPU false')
else:
print('GPU True')

#torch.device包含一个设备类型('cpu'或'cuda'设备类型)和可选的设备的序号
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# print(device)

# 设置不更新的部分
def set_parameter_requires_grad(model, feature_extracting):
if feature_extracting:
for param in model.parameters():
param.requires_grad = False

model_ft = models.resnet152()
# print(model_ft)

def initialize_model(model_name,num_classes,feature_extract,use_pretrained=True):
# 选择合适的模型,不同模型的初始化方法稍微有点区别
model_ft = None
input_size = 0

if model_name == 'resnet':
model_ft = models.resnet152(pretrained=use_pretrained) # pretrained为true 会下载训练好的模型
set_parameter_requires_grad(model_ft,feature_extract) # 选择性的冻住哪些层
num_ftrs = model_ft.fc.in_features # 用fc.in_features得到最后一层的输出,即下一层的输入
# 重写fc层
# softmax和logsoftmax
# softmax是 该数求指数 / 所有数求指数的和
# logsoftmax是 log(softmax) 把softmax的结果log一下
model_ft.fc = nn.Sequential(nn.Linear(num_ftrs,num_classes),nn.LogSoftmax(dim=1))
input_size = 224

return model_ft, input_size

# 设置哪些层需要训练
model_ft, input_size = initialize_model(model_name,num_classes,feature_extract,use_pretrained=True)
# GPU计算
# 构造好的模型加入GPU
model_ft = model_ft.to(device)

# 模型保存
filename = 'checkpoint.pth'

# 是否训练所有层
params_to_update = model_ft.parameters()
print('Params to learn:')
# 这个if else是为了查看哪些参数是需要被训练的
if feature_extract:
params_to_update = []
for name,param in model_ft.named_parameters():
if param.requires_grad == True:
params_to_update.append(param)
print("\t",name)
else:
for name,param in model_ft.named_parameters():
if param.requires_grad == True:
print("\t",name)

# 优化器设置
optimizer_ft = optim.Adam(model_ft.parameters(),lr=1e-2)
scheduler = optim.lr_scheduler.StepLR(optimizer_ft,step_size=7,gamma=0.1) # 学习率每7个epoch衰减成原来的1/10
# 最后一层已经LogSoftmax()了,所以不能nn.CrossEntropyLoss()来计算了,nn.CrossEntropyLoss()相当于logSoftmax()和nn.NLLLoss()整合
criterion = nn.NLLLoss()

# 训练模型
def train_model(model, dataloaders, criterion, optimizer, num_epochs=10, is_inception=False, filename=filename):
since = time.time()
best_acc = 0 # 最好的准确率
# !!!!不是保存最后一个模型,而是保存准确率较高的模型

# 训练前把模型加入GPU
model.to(device)

val_acc_history = []
train_acc_history = []
train_losses = []
valid_losses = []

# optimizer.param_groups: 是长度为2的list,其中的元素是2个字典;
# optimizer.param_groups[0]: 长度为6的字典,包括[‘amsgrad’, ‘params’, ‘lr’, ‘betas’, ‘weight_decay’, ‘eps’]这6个参数;
# optimizer.param_groups[1]: 好像是表示优化器的状态的一个字典;
LRs = [optimizer.param_groups[0]['lr']] #学习率数组
# copy.deepcopy() python中的函数进行深拷贝
best_model_wts = copy.deepcopy(model.state_dict())

for epoch in range(num_epochs):
print("Epoch {}/{}".format(epoch,num_epochs-1))
print('-'*10)

# 训练和验证
for phase in ['train','valid']:
if phase == 'train':
model.train() # 训练
else:
model.eval() # 验证

running_loss = 0.0
running_corrects = 0

# 把数据都取个遍
for inputs,labels in dataloaders[phase]:
# 把训练的数据加入GPU
inputs = inputs.to(device)
labels = labels.to(device)

# 清零
optimizer.zero_grad()
# 只有训练的时候计算和更新梯度
# set_grad_enabled 用于设置梯度计算打开或关闭状态的上下文管理器.
with torch.set_grad_enabled(phase == 'train'):
if is_inception and phase == 'train':
outputs, aux_outputs = model(inputs)
loss1 = criterion(outputs,labels)
loss2 = criterion(aux_outputs,labels)
loss = loss1 + 0.4*loss2
else:
outputs = model(inputs)
loss = criterion(outputs,labels)
# print(type(outputs))
_,preds = torch.max(outputs,1)

# 训练阶段更新权重
if phase == 'train':
loss.backward()
optimizer.step()

# 计算损失
running_loss +=loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)

epoch_loss = running_loss / len(dataloaders[phase].dataset)
epoch_acc = running_corrects.double() / len(dataloaders[phase].dataset)

time_elapsed = time.time() - since
print('Time elapsed {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))

# 得到最好那次的模型
if phase == 'valid' and epoch_acc > best_acc:
best_acc = epoch_acc
best_model_wts = copy.deepcopy(model.state_dict())
# pytorch中model.parameters()和model.state_dict()使用时的区别
# model.parameters()方法返回的是一个生成器generator,里面只有模型的参数,而没有对应的网络层名称;
# 并且 如果有些层的参数没有被训练,那么它只保存那些被训练的参数
# model.state_dict() 返回的则是一个字典 {key:value},key 是网络层名称,value 则是该层的参数

# state_dict()是一个状态字典,⼀个简单的python的字典对象,将每⼀层与它的对应参数建⽴映射关系
state = {
'state_dict': model.state_dict(),
'best_acc': best_acc,
# 优化器参数:有时保存模型的参数需要稍后接着训练,那么就必须保存优化器的状态和其所使用的超参数
'optimizer': optimizer.state_dict(), # 包含了优化器的状态,以及被使用的超参数
}
torch.save(state, filename)
if phase == 'valid':
val_acc_history.append(epoch_acc)
valid_losses.append(epoch_loss)
# scheduler.step()按照Pytorch的定义是用来更新优化器的学习率的,一般是按照epoch为单位进行更换,即多少个epoch后更换一次学习率
# ???epoch_loss这个参数有什么意义呢
scheduler.step(epoch_loss)
if phase == 'train':
train_acc_history.append(epoch_acc)
train_losses.append(epoch_loss)

print('Optimizer learning rate : {:.7f}'.format(optimizer.param_groups[0]['lr']))
LRs.append(optimizer.param_groups[0]['lr'])
print()

time_elapsed = time.time() - since
print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
print('Best val Acc: {:4f}'.format(best_acc))

# 训练完后用最好的一次当做模型最终的结果
model.load_state_dict(best_model_wts)
return model, val_acc_history, train_acc_history, valid_losses, train_losses, LRs

model_ft, val_acc_history, train_acc_history, valid_losses, train_losses, LRs = train_model(model_ft, dataloaders, criterion, optimizer_ft, num_epochs=10, is_inception=(model_name=="inception"))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import numpy as np
import torch
import torch.nn as nn

x_values = [i for i in range(11)]
x_train = np.array(x_values, dtype=np.float32)
x_train = x_train.reshape(-1, 1)
print(x_train.shape)

y_values = [2 * i + 1 for i in x_values]
y_train = np.array(y_values, dtype=np.float32)
y_train = y_train.reshape(-1, 1)
print(y_train.shape)


# 线性回归模型(其实线性回归就是一个不加激活函数的全连接层)

class LinearRegressionModel(nn.Module): # nn.Module父类
# 前面介绍了如何自定义一个模型——通过继承nn.Module类来实现,
# 在__init__构造函数中申明各个层的定义,在forward中实现层之间的连接关系,实际上就是前向传播的过程。
def __init__(self, input_dim, output_dim):
super(LinearRegressionModel, self).__init__()
self.linear = nn.Linear(input_dim, output_dim)

# 实例化一个对象中传入对应的参数就可以自动调用forward函数,是在进行前向传播时调用的,而不是在__init__后就直接调用的
def forward(self, x):
out = self.linear(x)
return out


# input_dim和output_dim是输入数据的维度和输出数据的维度
input_dim = 1
output_dim = 1
model = LinearRegressionModel(input_dim, output_dim)

epochs = 1000
learning_rate = 0.01
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate) # optimizer 优化器
# 损失函数一般看任务来说吧 分类任务一般都是交叉熵,回归任务一般条件下是MSE
criterion = nn.MSELoss() # criterion 标准

# 训练模型
for epoch in range(epochs):
epoch += 1
# 注意转换成tensor
inputs = torch.from_numpy(x_train)
labels = torch.from_numpy(y_train)

# 梯度要清零每一次迭代
optimizer.zero_grad()

# 前向传播
outputs = model(inputs)

# 计算损失
loss = criterion(outputs, labels)

# 反向传播
loss.backward()

# 更新权重参数
optimizer.step()

if epoch % 50 == 0:
print("epoch {},loss {}".format(epoch, loss.item()))

# 利用模型去预测结果
predicted = model(torch.from_numpy(x_train).requires_grad_()).data.numpy()
print(predicted)

小王子遇见了一只狐狸。狐狸告诉小王子,“如果你驯服我,我们便属于彼此。”

小王子每天在同一个时间来,渐渐地他驯服了狐狸。

可是小王子却要离开了。

在小王子要离开的时候,狐狸让他再去看一眼那五千多朵玫瑰。

小王子回到了玫瑰园。

他对玫瑰们说,“你们很美丽,但也很空虚,不会有人为你们去死。当然,寻常的路人会认为我的玫瑰花和你们差不多。但她比你们全部加起来还重要,因为我给她浇过水。因为我给她盖过玻璃罩。因为我为她挡过风。因为我为她消灭过毛毛虫。因为我倾听过她的抱怨和吹嘘,甚至有时候也倾听她的沉默。因为她是我的玫瑰。“

小王子最后与狐狸告了别。

狐狸告诉了小王子一个秘密:”你的玫瑰之所以如此宝贵,是因为你曾为她付出的时间。“

训练阶段

  1. 图片处理成大小为448*448,喂给神经网络模型;

  2. 经过神经网络后,会输出7 7 30大小的矩阵;

  3. 30是指每个grid cell可以预测出两个bbox,每个bbox会有四个参数来表示这个bbox具体的位置和大小,每个框还有一个confidence score参数,共10个参数;另外,每个gird cell可以预测出20个类别的概率,共20个参数;共30个参数;
    (x1,y1,w1,h1,confidence1,x2,y2,h2,w2,confidence2,…)

  4. (x, y)坐标代表bbox的中心相对于网格单元的边界;w,h是相对于整个图像的预测。

  5. 一个标记框的中心点落在哪个grid cell中,就让哪个gird cell的一个bbox(IOU较大)去拟合这个框,并且这个grid cell输出的类别也应该是正确的类别;

  6. 每个grid cell只能预测出一个物体,49个grid cell只能预测出49个物体;这也是YOLO v1在预测小目标和密集目标较差的原因;

  7. 在训练过程中confidence score参数是 IOU和Pr(Object)的乘积,Pr(Object)是指bbox是否是被选中的预测框的概率,非0即1;IOU是某个gird cell的bbox和打标的真是框的面积交并比;

  8. 每个grid cell预测出的两个bbox,则会产生两个IOU;若该grid cell有预测物体,则选择IOU较大的bbox去拟合;若该grid cell没有预测物体,则confidence score尽可能的小,接近于0;

  9. 最后一层预测类概率和边界框坐标。我们根据图像的宽度和高度使边界框的宽度和高度标准化,使它们在0和1之间。我们将边界盒和坐标参数化,使其成为特定网格单元格位置的偏移量,因此它们也被限定在0和1之间。

  10. 损失函数的设计:

  • 带^的数据是真实值;
  • λcoord和λnoobj的作用是为了平衡负样本和正样本比例不均衡的问题;
  • Ci的标签值非0即1;
  • pi的标签值非0即1;
  1. 对于中心点的损失直接用了均方误差,但是对于宽高为什么用了平方根呢?

上图中,蓝色为bounding box,红色框为真实标注,如果W和h没有平方根的话,那么bounding box跟两个真实标注的位置loss是相同的。但是从面积看来B框是A框的25倍,C框是B框的81/25倍。B框跟A框的大小偏差更大,所以不应该有相同的loss。
如果W和h加上平方根,那么B对A的位置loss约为3.06,B对C的位置loss约为1.17,B对A的位置loss的值更大,这更加符合我们的实际判断。所以,算法对位置损失中的宽高损失加上了平方根。(参考于https://blog.csdn.net/x454045816/article/details/107527326/)
因为平方根对小数据敏感,对大数据不敏感,比如对于根号x,一阶导数随着x的增大而减小

预测阶段

  1. 直接把图片交给神经网络即可输出结果;

  2. 采用NMS非极大值抑制的方法,使低置信度的预测不显示出来;

  3. 并不是所有的gird cell的都可以预测出物体的位置;

(参考于https://blog.csdn.net/weixin_48200452/article/details/116453893)

论文

YOLO v1

代码

原理解析请看:点击1 点击2

当是二维时

直接对矩阵进行转置;

1
2
3
4
5
6
7
array([[0, 1],
[2, 3]])

transpose后

array([[0, 2],
[1, 3]])
当是三维时

交换x,y时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
A = array([[[ 0,  1,  2,  3],
[ 4, 5, 6, 7]],

[[ 8, 9, 10, 11],
[12, 13, 14, 15]]])

transpose后

A = array([[[ 0 1 2 3],
[ 8 9 10 11]],

[[ 4 5 6 7],
[12 13 14 15]]])
# 可以直接把三维数组看成二维数组,把一个一维数组看成一个数,继续使用二维数组转置的方式即可;

np.transpose(img, (1, 0, 2))操作,可以将图像逆时针翻转90度

问题

错误:[Errno socket error] [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed。

解决方案

全局取消证书验证(当项目对安全性问题不太重视时,推荐使用,可以全局取消证书的验证,简易方便)

1
2
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

视觉识别任务

  • 分类:不考虑空间的位置;
  • 语义分割:给我一个图像,告诉我图像上每一像素是什么语义;
  • 目标检测:图像上某个区域是什么类别(是目标还是背景,是目标的话是什么标签)
  • 实例分割:在同一个图像中,同一类别不同物品显示不同的颜色,即若一个图像中有两个猫,它会用不同的颜色表示两只猫;

实例分割与语义分割不同的是:语义分割只区分同一种类,不区分同一种类中的不同。

语义分割

定义

给每个像素分配类别标签不区分实例,只考虑像素类别。

语义分割思路

滑动窗口

具体过程:在一张图片上,通过滑动窗口的方式对每个点取周围的区域进行分类判断;

存在的问题:效率太低;

全卷积

C的大小为类别个数;

具体过程:给输入的特征图增加padding,使其保持原有大小;输出为C H w的特征图,可以看成H W个C维向量,每个C维向量只有一个类别;并且把标答也做成C H * W,利用交叉熵进行比较,并进行反向传播;

但同时也存在问题;

改进的全卷积

下采样

下采样可以通过padding或增大步长的方式实现;

上采样

把1,2,3,4放到5,6,7,8的位置;

转置卷积

x,y,z是三个参数;左右两图的两组x,y,z参数是不同的;

过程

  • 传统的,在上采样过程中为了防止信息的丢失,直接把下采样过程中对应的特征图和上采样过程中对应的特征图拼接起来;
  • 最优的,先把下采样过程中得到的特征图经过卷积进行卷积,然后再拼接起来,保证正向的一些信息流能够传递过来;

目标检测

定义

不光告诉我们图像中有什么目标,还要告诉我们它在什么位置;

思路

在倒数第二层的全连接层上分别接上分类器和确定位置的神经网络;

当做目标定位这种网络时,找到一个预先训练好的模型,然后直接把它分类,然后在这个训练的基础上,训练倒数第二层与定位层直接连接的参数,让它的总损失降到最低;

在实际的训练过程中可以这样,先训练好分类,然后所有参数不变去训练定位层的参数,然后再把所有参数都打开进行微调,使总损失下降;

损失值

因为有分类和定位,所有又称多任务损失;

分类的损失很容易计算,那么定位的损失如何计算呢?

x,y,h,w相差的平方和。

在计算两个损失和时,可以设置权重,来倾向于哪个更拟合正确;

多目标检测

当多个目标时,就难以知道标答的计算,采用单目标分类定位的方法是不可行的;只有当目标个数确定时,才能用单目标这种方法进行;

即因为不知道目标的个数,就难以确定输出的维度;

方法1:滑动窗口

存在的问题:CNN需要对图像中所有可能的区域(不同 位置、尺寸、长宽比)进行分类,计算量巨大!

方法2:R-CNN

  • 找出所有潜在可能包含目标的区域;
  • 运行速度需要相对较快;

利用区域建议方法产生感兴趣的区域,并对该区域进行缩放到224 224,因为输入要求是224 224,然后再把缩放后的图像进行特征提取;随后进行Bbox reg进行坐标回归和使用支持向量机对区域进行分类;

通过Bbox reg回归选取和实际的误差,调节选取窗口的参数,解决了选取区域不准确的问题;

存在的问题:计算效率低下;

解决方法:在特征图上进行区域扣取;

方法3:Fast R-CNN

思想

先对整图进行卷积操作,然后再进行区域裁剪和缩放特征;

要保证裁剪和缩放都可导,整个网络才能训练。

区域裁剪: RoI Pool

区域裁剪: RoI Align

与Rol Pool相比,保持映射过来的区域,平均选取四个点,进行计算该点的值,再采用最大池化的方式进行提取;

如何计算选点处的值?

方法4:Faster R-CNN

思路

在中间特征层后加入区域建议网络RPN( Region Proposal Network) 产生候选区域,其他部分保持与Fast R-CNN一致,即 扣取每个候选区域的特征,然后对其进行分类。

区域建议(Region Proposal Network)

对每一个像素都要在它外面圈一个框,这个框的分类是什么,这代表这个位置的分类就是什么(是不是目标);

问题:框选取多大,框长什么样?

在编程的时候选取的都是固定的区域,但是可以通过回归出来和真正需要选择区域的偏差量,调整区域框;

4k的4:每个像素回归4个数字;

对于选取的K 20 15 的边框,选取最合适的前300个边框进行目标检测;

四种损失

RPN分类损失只是判断该区域是不是目标;

应用

image-20211013212211838

整个训练过程:先对整个图像进行卷积,得到特征图,然后分别对每个像素点进行区域划分回归,选择就有可能是目标区域的300个区域进行区域裁剪,然后再进行分类和区域定位。

image-20211013212736996

YOLO是速度比较快的,虽然可能精度上没有Faster R-CNN高,但是性价比是比较高的;

影响因素

实例分割