Think World

从个人成长角度来说,从经历中学点什么总是重要的

0%

主要内容: 引用,指针,数组,动态内存分配

引用

  • <类型>&<变量> = <对象或变量>

  • 定义

    引用就是变量的另一个别名,对应的变量/对象必须存在

    1
    2
    3
    int x;
    int& rx = x;
    // int x,&rx = x;
  • 性质

    ​ 1.通过引用所做的读写操作实际上是作用于原变量上的

    ​ 2.引用必须在声明时,开始初始化

    ​ 3.引用一旦初始化,引用的名字不能再指定给其他变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int a { 0 }, b { 1 };
    int& r { a }; // 引用变量 r 在声明的同时就要初始化,r是a的别名

    r = 42; // 相当于 a = 42
    r = b; // 相当于 a = b; 执行后 a 的值是1
    // 此处不是让r变成b的引用
    // 引用r声明并初始化后,r就不能再改为其它变量的引用
    int& r2 = a; // 继续给a起别名
    int& r3 = r; // 声明引用变量 r3,用r初始化
    // 这个相当于 int& r3 = a; 因为 r 是 a 的别名
  • 指针和引用的差异

    指针和引用的应用场景是函数参数传递

    void f(int* pa ,int* pb); void f(int& pa,int& pb);

    从软件设计角度来看,使用引用更好,因为调用方不需要额外传参数时进行取地址运算.

    • 存取值的方式
      • 对指针变量需要使用*来读取相应内存的内容
    • 初始化
      • 指针没有要求
    • 对象或变量的存在性
      • 引用有要求
  • const和引用

    **const <类型>&**常用于返回值以及参数传递(保证不能修改相应的变量)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //变量的引用
    int val =100;
    int & myval1=var;
    const int & myval2 = var;
    myval1 = 300;//合法,可以通过myval2对var进行修改,此时myval2,myval3,var同时被修改
    myval2 = 100;//非法

    //常量的引用
    int b=100;
    const int& aa=b;//b的引用,可以选择用const的修饰
    const int& bb=1;//这里必须用const修饰
    aa = 10//非法
    bb = 20;//非法
  • 引用在参数传递中的使用

    如果在函数中不修改变量,建议使用 const T&

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int var =100;  
    const int cvar =200;
    /*形参:
    void f(int a1,int &a2, const int& a3)
    { }*/
    f(1,2,3);//2和a2 不合法
    f(1,var,var);

    f(1,cvar,cvar);//cvar与a2 不合法
    f(1,var,cvar);

    f(1,cvar,var);//cvar和a2 不合法
    f(var,var,var);
    f(cvar,var,cvar);

二维数组的初始化方式

空指针和动态内存分配

1.1 0带来的二义性问题

  • C语言中,空指针使用(void *)0来表示, 有时候,用“NULL”来表示空指针(由编译器决定实现方式,一种可能的实现方式是#define NULL 0)
  • C++03中,空指针使用“0”来表示。0既是一个常量整数,也是一个常量空指针。
  • C++11中引入保留字“nullptr”作为空指针

c++中string,字符指针,字符数组之间的区别

常量

c/c++中设置有常量:在程序运行过程中,其值不能改变的量称为常量

常量分为不同的类型:整型常量(3),浮点型常量(3.12),字符型常量(‘a’),字符串常量(“abc”)

常量一般有两种表现形式:

  • 直接常量:直接以值的形式表示的常量称之为直接常量
  • 符号常量:用标识符命名的常量称为符号常量,也就是在直接常量上再取一个名字,方便程序后续维护.习惯用大写字母和下划线来命名
    • 两种定义方式
      • const 类型 符号常量名字=常量值
      • #define 符号常量名 常量值

字符指针和字符数组的区别

c/c++中每个字符串都以字符’/0’作为结尾.为了节省内存,c/c++把常量字符串放到单独的一个内存区域,当几个指针赋值给相同的常量字符串时,它们实际会指向相同的内存地址.但是用常量内存初始化数组时,情况却有所不同.

1
2
3
4
5
6
7
8
9
10
char str1[] = "hello world";
char str2[] = "hello world";
//str1!=str2
//c++会为str1,str2分配两个长度为12个字节的空间,并把"hello world"的内容分别复制到数组中.
//str1,str2是两个不同的字符数组,所以初始地址不同


char* str3 = "hello world";
char* str4 = "helo world";
//str3==str4

参考资料

<剑指offer

< http://c.biancheng.net/view/2236.html

==本篇主要介绍一些 实验过程中用到的一些基础python知识点==

python面向对象

命名前缀及其含义

  • 前置单下划线_var:命名约定,用来表示该名称仅在内部使用。一般对Python解释器没有特殊含义(通配符导入除外),只能作为对程序员的提示。
  • 后置单下划线var_:命名约定,用于避免与Python关键字发生命名冲突。
  • 前置双下划线__var:在类环境中使用时会触发名称改写,对Python解释器有特殊含义。
  • 前后双下划线__var__:表示由Python语言定义的特殊方法。在自定义的属性中要避免使用这种命名方式。
  • 单下划线_:有时用作临时或无意义变量的名称(“不关心”)。此外还能表示Python REPL会话中上一个表达式的结果。

==首先先给出一些python面向对象的一些基础知识==

子类中初始化父类的相关问题

pytorch中网络构造的深入理解

官方中文文档

pytorch.nn.module

current layer

api:

Container

torch.nn.Sequential

A sequential container.

Modules will be added to it in the order they are passed in the constructor. Alternatively, an ordered dict of modules can also be passed in.

1
2
3
4
5
6
7
# Example of using Sequential
model = nn.Sequential(
nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU()
)

parameters

module 保存所有需要计算的参数

自己在设计网络时,要通过nn.Parameter()加入module的优化器管理

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyLiner(nn.Module):

def __int__(self, inp, outp):
super(MyLinear , self).__init__()

#
self.w = nn.Parameter(torch.randn(outp,inp))#这时生成参数不需要 requires_grad =True
self.b = nn.Parameter(torch.randn(outp))

def forward(self,x ):
x = x@self.w.t() +self.b
return x

modules

1
2
3
4
modules

children

GPU

1
2
3
device = torch.device('cuda')
net = Net()
net.to(device)

save and load

save

1
2
3
cn = MyNet()
......
torch.save(cn.state_dict(), "your_model_path.pth")

load

1
2
3
4
5
6
cn = MyNet()

state_dict = torch.load("your_model_path.pth")

#加载训练好的参数
cn.load_state_dict(state_dict)

train/test

1
2
cn = cn.eval()#转换成测试状态
cn = cn.train()#转换成 训练状态

了解基本知识之后,以下从构建自己网络的思路开始讲述

mynet

https://blog.csdn.net/qq_27825451/article/details/90550890

https://blog.csdn.net/qq_27825451/article/details/90705328

貌似是需要自己实验forward过程

常用的网络模块

Flatten

1
2
3
4
5
6
7
class Flatten(nn.Module ):

def __init__(self):
super(Flatten, self ).__init__()

def forward(self, input):
return input.view(input.size(0),-1) #将数据打平作为卷积层和全连接层之间的过渡

卷积神经网络

本文罗列一些简单的api

api官方文档连接

首先先补一个知识点:关于torch.nn.Xxxx 和 torch.nn.functional.xxx的 api的区别和使用建议

推荐博客: https://www.zhihu.com/question/66782101/answer/579393790

结论:

1
2
3
4
5
6
PyTorch官方推荐:具有学习参数的(例如,conv2d, linear, batch_norm)采用nn.Xxx方式,没有学习参数的(例如,maxpool, loss func, activation func)等根据个人选择使用nn.functional.xxx或者nn.Xxx方式。但关于dropout,个人强烈推荐使用nn.Xxx方式,因为一般情况下只有训练阶段才进行dropout,在eval阶段都不会进行dropout。使用nn.Xxx方式定义dropout,在调用model.eval()之后,model中所有的dropout layer都关闭,但以nn.function.dropout方式定义dropout,在调用model.eval()之后并不能关闭dropout。

作者:有糖吃可好
链接:https://www.zhihu.com/question/66782101/answer/579393790
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

卷积

api介绍(2维卷积)

1
2
3
4
5
6
7
8
9
10
torch.nn.Conv2d(
in_channels: int,
out_channels: int,
kernel_size: Union[T, Tuple[T, T]],
stride: Union[T, Tuple[T, T]] = 1,
padding: Union[T, Tuple[T, T]] = 0,
dilation: Union[T, Tuple[T, T]] = 1,# atrous算法
groups: int = 1,
bias: bool = True,
padding_mode: str = 'zeros')

实例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import torch
import torch.functional as F
import torch.nn as nn
#conv

conv = nn.Conv2d(3,5,kernel_size=3,stride =1,padding=0)#5个 3×(3×3)size=3,channel=3卷积核
x = torch.rand(1,3,28,28)#表示一张尺寸为28×28的rgb图像

#forward()
out = conv.forward(x)
print(out.shape)
#torch.Size([1, 5, 26, 26]);一张 channels = 5,size=26*26的image
# weigth && bias
print(conv.weight.shape)#这个就是kernel,卷积核
print(conv.bias.shape)

pooling

使用 F.avg_pool2dF.max_pool2d

upsampling

F.interpolate

实例代码

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
import torch
import torch.nn.functional as F
import torch.nn as nn

conv = nn.Conv2d(3,5,kernel_size=3,stride =1,padding=0)#5个 3×(3×3)size=3,channel=3卷积核
x = torch.rand(1,3,28,28)#表示一张尺寸为28×28的rgb图像
out_c= conv.forward(x)
print(out_c.shape)
# layer =
# out_p = F.avg_pool2d(x,5,stride=2)
out_p = F.max_pool2d(x,5,stride=2)

print(out_p.shape)

out_up = F.interpolate(out_p,scale_factor=2,mode='nearest')
#scale_factor指出了上采样的尺寸
print(out_up.shape)

out_final = torch.sigmoid(out_up)
print(out_final.shape)

'''

torch.Size([1, 5, 26, 26])

torch.Size([1, 3, 12, 12])

torch.Size([1, 3, 24, 24])

torch.Size([1, 3, 24, 24])

'''

BatchNorm操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
x = torch.rand(100,16,28,28)#表示一张尺寸为28×28的rgb图像
x = x.view(100,16,28*28)
print(x.shape)
#batch_norminize是针对每个通道进行计算的, input [batch_size ,channel, W*H] ,计算出每个channel上的均值和方差
layer = nn.BatchNorm1d(16) #指出channel size
out = layer(x) #进行norm操作
###可以查看参数
print(layer.running_mean.shape)#计算得到的每个channel上的均值
print(layer.running_var.shape)#计算得到每个channel上的方差
print(layer.weight)#在单个channel上线性变换的w,在baclkword过程中需要训练的参数
print(layer.bias)#

'''
torch.Size([100, 16, 784])
torch.Size([16])
torch.Size([16])
'''

常见的激活函数等

torch.nn.functional.softmax( input, dim= None)

torch.nn.functional.relu(input, inplace = False )

torch.nn.functional.sigmoid()

回顾基本训练过程(MINIST)

数据准备

导包、设计训练参数

1
2
3
4
5
6
7
8
9
import  torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms

batch_size=200
learning_rate=0.01
epochs=10

下载数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=True, download=True,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))对数据进行正则化
])),
batch_size=batch_size, shuffle=True)


test_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=False, transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=batch_size, shuffle=True)

前向传播

参数初始化

1
2
3
4
5
6
7
8
9
10
11
w1, b1 = torch.randn(200, 784, requires_grad=True),\
torch.zeros(200, requires_grad=True)
w2, b2 = torch.randn(200, 200, requires_grad=True),\
torch.zeros(200, requires_grad=True)
w3, b3 = torch.randn(10, 200, requires_grad=True),\
torch.zeros(10, requires_grad=True)#class_size = 10,最后分为10类

#利用何恺明大佬的初始化方法
torch.nn.init.kaiming_normal_(w1)
torch.nn.init.kaiming_normal_(w2)
torch.nn.init.kaiming_normal_(w3)

搭建网络

1
2
3
4
5
6
7
def fforward():
x = x@w1 + b1
x =F. relu(x)
x = x@w2 + b2
x = F.relu(x)
x = x@w3+ b3
return x

定义加速器和loss函数

1
2
optimizer = optim.SGD([w1,b1,w2,b2,w3,b3],lr = learning_rate)#传入需要计算梯度的参数和learning_rate
criteon = nn.CrossEntropyLoss()

反向传播

1
2
3
4
5
6
7
8
9
10
11
12
13
for epoch in len(epochs):
for batch_idx , (data,target) in enumerate(train_loader):
data= data.view(-1,28*28)#view(-1,28*28)中-1表示不确定/不关心的数,只要求dim=0的size是28×28就行
logits = fforward(data)

loss = criteon(logits,target)

optimizer.zero_grad()#所有参数上一轮的梯度信息都清除
loss.backward()
optimizer.step()#用梯度更新参数
if( batch_idx %100 ==0):
print("train epoch :{}, batch:{} ,loss{:.6f}".format(epoch,batch_idx,loss.item() ))
#torch.item() 表示如果多维的torch中只包含一个元素,取出来

测试

1
2
3
4
5
6
7
8
9
10
test_loss = 0
correct =0
for data,target in test_loader :
data = data.view(-1,28*28)
logits = fforward(data)#得到预测值
test_loss += criteon(logits,target).item()

pre = logits.argmax(dim=1)
correct = pred.eq(target.data)

GPU加速

比较好的博客:

http://www.feiguyunai.com/index.php/2019/04/30/python-ml-25-pytorch-gpu/

[自己的实验环境应该是单GPU环境]

创建一个GPU实例

1
2
3
device = torch.device('cuda: 0' if torch.cuda.is_available() else'cpu')#首先要先判断是否有可用的GPU
#'0'表示GPU的编号

把需要计算的量加载到设备上

1
2
3
4
5
6
7
8
net = MLP().to(deivce) 
optimizer = optim.SGD(net.parameters() , lr = learning_rate)
criteon = nn.CrossEntropyLoss().to(device)#to(device) ,如果数据类型是model,则返回类型是 cpu上数据的引用

for epoch in range(epochs):
for batch_idx , (data,target) in enumerate(train_loader):
data = data.view(-1,28*28)
data, target = data.to(device),target.cuda()#如果数据类型是 tensor,返回类型是cpu上数据的copy()

BN

参考文献:

wiki

https://www.cnblogs.com/guoyaohua/p/8724433.html

问题背景:

梯度消失梯度爆炸的问题

一般解决方法汇总

drop_out:这里可以参考吴恩达老师的deep learning课件

要解决的问题

内部协变量移位现象(Internal Covariate shift)

机器学习中有一个比较重要的假设:独立同分布,就是假设训练数据和测试数据是同时满足相同分布;

​ 在网络的训练阶段,由于前几层的参数发生变化,因此当前层的输入分布也会相应变化,因此当前层需要不断调整以适应新的分布。对于较深的网络,此问题尤为严重,因为较浅的隐藏层的细微变化将在它们在网络中传播时被放大,从而导致较深的隐藏层发生显着变化。因此,提出了批量标准化的方法,以减少这些不必要的偏移,以加快训练速度并生成更可靠的模型。

​ NP的基本思想就是让每个隐层节点的激活输入分布固定下来(一般是放置在激活层之前);类似于白化操作:对深层神经网络的每个隐层神经元的激活值做简化版本的白化操作.

​ 同时,我们可以发现BN还有其他的用途

​ 1.会使网络使用更高的学习速率而不会出现梯度消失或者梯度爆炸

​ 2.似乎存在正则化效果从而改善了归一化属性,可以不使用drop out 

本质思想

算法步骤

①不仅仅极大提升了训练速度,收敛过程大大加快;

②还能增加分类效果,一种解释是这是类似于 Dropout 的一种防止过拟合的正则化表达方式,所以不用 Dropout 也能达到相当的效果;

③另外调参过程也简单多了,对于初始化要求没那么高,而且可以使用大的学习率等。

ResNet

综述

网络结构

在原论文中,作者不断加深网络结构来研究随着网络层次的加深,残差模块的表现

所以存在 ResNet-34, ResNet-50, ResNet-101, ResNet-152

这里只以ResNet-34为主

可以看到整个网络分为两部分Plain Network 和Residual Network

Plain Network

参考最左侧VGG网络的框架,都是采用3×3的卷积核

(i) for the same output feature map size, the layers have the same number of filters; 而且stride=1, padding =1,

(ii) if the feature map size is halved, the number of filters is doubled

so as to preserve the time complexity per layer.[池化层使用步长为2,然后3×3的卷积核]

可以看出来网络比较工整

Residual Network

基本结构

最右侧的网络即是残差网络,在plainNet的基础上增加了Residual模块

实弧线,表示这里shortcuts是正常的恒等映射
$$
Y = F(X,{w_i}) + X
$$
虚弧线,表示这里恒等映射(identity mapping / shortcuts)要进行增加维度;原文中提到了两种方式

1. with extra zero entries padded for increasing dimensions;我理解的是仅仅是用0来填充多余的维度
2. 采用 projection shortcut (采用1*1 的卷积核),公式为,$W_s$用来调整维度和尺寸。

$$
Y = F(X,{w_i}) + W_sX
$$

对于以上两种方式,stride=2

残差结构的原理

==自己的数学比较鸡肋,感觉应该是重点理解残差结构(而不仅仅是保证梯度不会很小)

本片论文的introduction中提到了,增加学习网络的深度确实有利于提高神经网络的能,但是会因为一些意料之外的原因导致出现在较深网络层次中准确率降低的情况;

其中比较重要的影响因素是 较深的网络会出现梯度消失和梯度爆炸的问题

1.梯度消失:导致深层的梯度不能传递到浅层,不能更新参数

2.梯度爆炸的:导致网络不稳定

有一些其他的解决方法 比如 normalized initialization, intermediate normalization layers[NP]

本文采用使用 残差结构:在深层网路,训练 F(x)的梯度趋向0,保证不出现梯度下降和梯度爆炸的现象

利用公式,我们可以简单验证一下梯度更新的情况

$$
Y = F(X,{ W_i }) +X\
F = W_2\sigma(W_1 X) \
$$
这里忽略了 偏移量$b_i$, $\sigma$ 表示relu函数

实际上我们训练的就是$F(x)$,而且在网络结构的深层我们趋向于将它的梯度训练为0

我们设损失函数为$\Epsilon$

$$
\
X_{l+1} = X_{l} + F(X_l,{ W_i })\即上一个残差模块的输出作为下一个残差模块的输入\
X_{l+2 } = X_{l+1}+F(X_{l+1},{W_I}) = X_l + F(X_l,{ W_i })+F(X_{l+1},{W_I})\
…\
X_L = X_l + \sum_{i =l}^{L-1} F(X_i,W_i)\
\ X_l表示第l个残差模块的输入,X_L表示第L个残差模块的输入;满足l<L\
$$

$$
\frac{\partial \Epsilon}{\partial X_l } = \frac{\partial \Epsilon }{\partial X_L}\times \frac {\part X_L} {X_l}= \frac{\partial \Epsilon }{\partial X_L}(1+\frac{\part}{\part X_l}(\sum_{i=l}^{L-1} F(X_i,W_i)))
$$
注:这里恒等映射没有经过1×1的卷积运算

可以看出来,较为深层的网络,我们也可以保证梯度不为消失,可以较为无损地传播梯度

其他优秀博主的意见:[可以换个角度思考一下]

残差网络的其他设计

1.由浅层的网络到深层的网络,将两层(3*3)转换成3层(1×1,3×3,1×1),减少参数,便于训练(1×1卷积,用来调整维度的;第1个降低维度到原来的1/2,第二个再恢复原来的维度)

2.关于涉及维度变换的残差模块的恒等映射,作者比较了三种实现

比较结果

但是考虑到训练的成本,建议选择方案A,即Zero-padding shortcut,毕竟重点是残差结构

实验部分

1
2
3
4
5
(1)使用color augmentation做数据扩增
(2)在每个卷积层之后,激活函数之前使用batch normalization (BN)
(3)SGD作优化,weight decay =0.0001,momentum=0.9
(4)learning rate=0.1,当错误率停滞时除以10
(5)不使用dropout

参考一些博客,博主们都提到了训练过程中 BN位置的设置等问题

参考博客:

实验:https://juejin.im/entry/6844903564590972936

原理:https://www.itread01.com/content/1544868722.html

https://blog.csdn.net/u014296502/article/details/80438616

综述:https://zhuanlan.zhihu.com/p/31852747

原文地址:

Faster R-CNN

主要参考博文

原文地址

在原论文的摘要中提出:

在之前SPP-Net 和Fast R-CNN提出依靠region proposal algorithms去预测物体的位置会减少很多计算量,但是这也暴露了区域提议计算的瓶颈

本文关键提出了RPN(region proposal network),使用卷积层,减少参数的数量,有较快的速度和较高的准确率

RPN ==shares full-image convolutional features== with the detection network, thus enabling nearly ==cost-free== region proposals.

​ An RPN is a fully-convolutional network that simultaneously ==predicts object bounds and objectness scores== at each position. RPNs are trained end-to-end to generate highquality region proposals, which are used by Fast R-CNN for detection.

softmax交叉熵损失函数:https://blog.csdn.net/chaipp0607/article/details/73392175

网络框架

![2020-08-28_16-35](Faster R-CNN/2020-08-28_16-35.png)

主要分为四部分:

  1. Conv layers: 作为一种CNN网络目标检测方法,使用conv+pooling+relu来提取feature maps,被之后的RPN 和全连接层共享

  2. RPN(region proposal network )

    ​ ==用于生成region proposals==,该层主要工作:

    ​ a. 通过softmax判断anchors属于positive , negative,

    ​ b. 再利用bounding box regression 修正anchors获得较为准确的 proposals

  3. roi pooling

    ​ 该层收集输入的feature maps和proposals,综合这些信息提区proposal feature maps,

  4. classification

    ​ 利用 proposal feature maps 计算 proposal 的类别,同时再次 bounding box regression 获得检测框最终的精确位置

下图为python的VGG16模型中的faster_rcnn_test.pt网络结构

![2020-08-28_16-34](Faster R-CNN/2020-08-28_16-34.png)

Conv layers

在Conv layers中

  1. 所有的conv层都是 kernel_size = 3, pad =1 ,stride =1
  2. 所有的pooling层都是 kernel_size =2, pad =0, stride =2

导致 Conv layers中conv层不改变输入输出矩阵的大小,只有在pooling层中M×N的矩阵变成(M/2)×(N/2)的大小。所以从Conv layers输出的矩阵的尺寸为M×N

目的是为了在ROI Pooling的输入层中proposal(M×N)与 feature maps尺寸一致

Region Proposal Network

这一层的主要任务: 获取有效的proposals,完成目标定位

![2020-08-28_17-42](Faster R-CNN/2020-08-28_17-42.png)

![2020-08-28_18-50](Faster R-CNN/2020-08-28_18-50.png)

主要流程:生成 anchors -> softmax 分类器提取 positvie anchors -> bbox reg 回归 positive anchors -> Proposal Layer 生成 proposals

这是主要的原理就是:利用3×3的卷积核的中心作为anchor的中心,在每个点设置9个anchor作为候选区,之后再有cnn来判断anchor是negitive or positive anchor,目前这里只是二分类,而且后面还有 2 次 bounding box regression 可以修正检测框位置

1.生成anchors

略(思路是比较简单的,可以参考博客)

2.softmax分类器提取positive anchors

输入:anchors,输出:rpn_cls_score

3.bbox 回归

bounding box regression原理

大致思想:我们目标是像通过 bounding box regression,对anchors进行调整,让他接近GT(但是我们anchor的选取按照固定的方法,没有结合GT的位置信息);所以我们学习的是anchors和GT之间变换(每张图片的GT是固定的,而且anchors的选取也是固定的)

​ 所以本文学习的是预测的窗口(anchor)=>GT窗口之间的一种变换(先平移后进行缩放)(如果相差比较小,即positive可以看作是线性回归模型)

​ 并设计相应的loss函数对其进行约束

原理

![2020-08-28_18-29](Faster R-CNN/2020-08-28_18-29.png)

![2020-08-28_18-35](Faster R-CNN/2020-08-28_18-35.png)

![2020-08-28_18-36](Faster R-CNN/2020-08-28_18-36.png)

![2020-08-28_21-22](Faster R-CNN/2020-08-28_21-22.png)

而在原论文中是这样子记录的

![](Faster R-CNN/2020-08-28_21-21.png)

![2020-08-28_20-51](Faster R-CNN/2020-08-28_20-51.png)

==在bbox-regression 中要训练的参数就是,我们需要预测的box(x,y,w,h)==

的4. proposal layer 生成 proposals

1
2
3
4
5
根据bbox回归得到的[dx(A), dy(A), dw(A) ,dh(A)]对所有的anchors进行修正和微调
按照输入的positive softmax scores 由大到小排序anchors,提取前pre_nms_topN,即选择较好的修正后的 positive anchors
限定超出图像边界的 positive anchors 为图像边界,防止后续 roi pooling 时 proposal 超出图像边界
剔除尺寸非常小的 positive anchors
对剩余的 positive anchors 进行 NMS(nonmaximum suppression)

注意由于第三步生成的proposal要和原图像进行对比,所以它的尺寸是M*N

NMS:非极大值抑制

![2020-08-28_19-07](Faster R-CNN/2020-08-28_19-07.png)

消除冗余的边界框

1
2
3
4
5
6
根据置信度得分进行排序
选择置信度最高的比边界框添加到最终输出列表中,将其从边界框列表中删除
计算所有边界框的面积
计算置信度最高的边界框与其它候选框的IoU。
删除IoU大于阈值的边界框
重复上述过程,直至边界框列表为空

RoI Pooling

是一个简单的SPP-Net, 属于卷积层和连接层之间的过渡层,将大小不一的proposals变成固定大小(之后classifier 模块是利用全连接层进行分类,所以需要固定大小)

存在其他方法:crop,warp,但是他们会破坏图像原有信息

![2020-08-28_21-44](Faster R-CNN/2020-08-28_21-44.png)

Classification

![2020-08-28_19-14](Faster R-CNN/2020-08-28_19-14.png)

loss函数

在训练过程和bbox回归中都有涉及

![2020-08-28_20-51](Faster R-CNN/2020-08-28_20-51.png)

训练过程

交替训练过程

![2020-08-28_19-38](Faster R-CNN/2020-08-28_19-38.png)

![2020-08-28_19-40](Faster R-CNN/2020-08-28_19-40.png)

公共卷积层的不是已经训练好了么??

1
2
3
4
5
6
参数说明:
rpn_cls_prob_reshape: positive vs negative anchors 分类器结果

rpn_bbox_pred: 对应的 bbox reg 的 变换量[dx(A,dy(A),dw(A),dh(A))]

im_info: 对于一副任意大小 PxQ 图像,传入 Faster RCNN 前首先 reshape 到固定 MxN,im_info=[M, N, scale_factor] 则保存了此次缩放的所有信息。这样做的是为了方便之后训练网络[历史遗留问题]

![2020-08-28_20-08](Faster R-CNN/2020-08-28_20-08.png)

![2020-08-28_20-26](Faster R-CNN/2020-08-28_20-26.png)

rpn-train1

![2020-08-28_20-25](Faster R-CNN/2020-08-28_20-25.png)

![2020-08-28_20-29](Faster R-CNN/2020-08-28_20-29.png)

![2020-08-28_20-02](Faster R-CNN/2020-08-28_20-02.png)

RPN-test

![2020-08-28_20-11](Faster R-CNN/2020-08-28_20-11.png)

![2020-08-28_20-02_1](Faster R-CNN/2020-08-28_20-02_1.png)

faster rcnn

![2020-08-28_20-10](Faster R-CNN/2020-08-28_20-10.png)

![2020-08-28_20-15](Faster R-CNN/2020-08-28_20-15.png)

第二轮训练方式与第一轮大同小异。

端到端方式

仍然存在的问题

1。读不懂代码,意味不知道网络需要什么样子的输入以及标注,而且看论文只能看个大概,不是特别清楚得了解每层网络之间的尺寸,维度等等的变换

2.不是特别明白训练的过程

3.不是特别明白 这里关于faster-rcnn 和 rpn 之间的参数共享的问题

  1. 要训练的参数都有哪些?? 反向传播,参数更新的过程

目标检测summary

本文在 主要描述目标检测的问题以及解决方法

目标检测的任务本质只有两个问题:图像识别,定位

这里着重介绍一下目标检测的评价方式

评价指标

数据集不平衡时, precision和recall等指标就不具备参考性,所以引出了AP
,它的相关介绍用例子介绍比较合适