RomanticQq

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

0%

  1. 指针的定义

    1
    2
    3
    int a=10;
    int* p;//定义一个指针
    p = &a;//p的值是一个地址,可以通过*把p位置的值取到,即*p(*p是一个数值,p是地址)
  2. 指针所占内存空间

    指针所占用内存空间是固定的,所有指针类型在32位操作系统下是4个字节,在64位操作系统下是8个字节;

  3. 空指针和野指针

    • 空指针指向内存中编号为0的空间;
    • 野指针指向非法的内存空间;
    • 空指针和野指针指向的内存空间都是不可访问的;
  4. const修饰指针

    1
    2
    3
    4
    int a=10;
    const int* p;//此时*p是常量,且*p是数值,若写*p=a就会报错,p=&a正常;
    int* const p;//此时p的常量,且p的值是地址,若写p=&a就会报错,*p=a正常;
    const int* const p;//地址和值都不可修改
  5. 指针和数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include<iostream>
    using namespace std;
    //参数1 数组的首地址;参数2 数组的长度
    //void sortt(int arr[], int len)
    //void sortt(int* arr, int len)
    //这是两种不同的写法,都可以,传的都是地址
    void sortt(int arr[], int len)
    {
    cout << arr << endl;
    cout << arr[0] << endl;
    }
    int main()
    {
    int arr[] = { 1,3,5,2,4 };
    int len = 5;
    sortt(arr, len);//数组名就是数组的首地址
    for (int j = 0; j < len; j++)
    cout << arr[j] << endl;
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include<iostream>
    using namespace std;
    //参数1 数组的首地址;参数2 数组的长度
    void sortt(int* arr, int len)
    {
    cout << *arr << endl;//1,对首地址进行解析
    cout << arr << endl;//0000007E2EF2FB28,传过来的是地址
    //[]前面必须紧跟着一个地址或者一个字母(这个字母用于简化这个地址)
    cout << arr[2] << endl;//5,可以这样直接取,原因上一行
    }
    int main()
    {
    int arr[] = {1,3,5,2,4};
    int len = 5;
    sortt(arr, len);//数组名就是数组的首地址
    return 0;
    }
  1. 利用指针进行数组遍历

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include<iostream>
    using namespace std;
    int main()
    {
    int arr[] = { 1,2,3,4,5 };
    int* p = arr;
    for (int i = 0; i < 5; i++)
    {
    cout << *p << endl;
    p++;
    }
    return 0;
    }
  2. 数组指针和指针数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #include<iostream>
    using namespace std;
    int main()
    {
    int a[] = { 1,2,3 };
    int b[] = { 4,5,6 };
    int c[] = { 7,8,9 };
    int* arr[] = { a,b,c }; //这时a,b,c存放在arr里面的都是地址
    cout << arr[0] << endl;
    for (int i = 0; i < 3; i++)
    {
    for (int j = 0; j < 3; j++)
    {
    //对于一维数组来说,arr[i]取到的是值,对于二维数组来说arr[i]取到的是地址;
    //对于指针数组来说,它是开辟了新的地址,在新地址中把原来的地址存下来了
    cout << arr[i] + j << endl;//输出地址
    //下面两行输出的都是数值,可以对数组进行取值,这两种方法都可以
    cout << *(arr[i]+j) << endl;
    cout << arr[i][j] << endl;
    }
    }
    return 0;
    }

南京百家云科技有限公司

  1. 自我介绍;

    (第一个项目)

  2. 介绍使用YOLOv3的项目并介绍YOLOv3;

  3. YOLOv3的图像输入大小是什么;

  4. YOLOv5、YOLOv8都出来了为什么要用YOLOv3;

  5. 对YOLOv5熟悉吗,说一下YOLOv3与YOLOv5的区别,YOLOv5中加了哪些结构,介绍一下;

  6. YOLOv3最后模型的输出是什么;

  7. 多分类目标和多目标分类一样吗;多分类和多标签一样吗,若不一样有什么区别;

  8. softmax的作用是什么;为什么进行目标分类时要用softmax;

  9. 若需要给一个目标打多个标签时,应该怎么做;

  10. 更加熟悉YOLOv3还是faster-RCNN,介绍一下(回答的是faster-RCNN);

  11. faster-RCNN中图像的输入大小是什么;

  12. 介绍一下faster-RCNN的发展史;

  13. 你看的faster-RCNN源码中,都进行了哪些数据处理(包括在图像尺寸不满足输入大小时,图像上的标注框应该如何进行缩放);

    (第二个项目)

  14. 介绍另外一下另外一个项目;

  15. 该项目中所使用的数据集是什么,有多少张图片,共有多少种类,类别都是什么;

  16. 数据集使用了哪些数据增强,使用了什么库,为什么要这样做;知道这些数据增强的底层实现吗;

  17. 在项目中使用了什么方式读取图片?OpenCV和PIL这两种读取图片的方式有什么区别;

  18. 项目中使用了深度可分离卷积,介绍一下深度可分离卷积,并介绍一下它的作用;

  19. 在该项目中模型共有多少层,输入的图像大小是多少,进行了多少次下采样;

  20. 在该项目中使用了注意力机制,介绍一下;

  21. 在该项目中最后的输出是怎么样的;

    (深度学习)

  22. 介绍一下深度学习的激活函数,有哪些优缺点;

  23. 梯度下降函数有哪些;在哪会用到梯度下降;梯度下降的参数有哪些;

  24. 学习率的作用是什么;

  25. 如何设置batch_size;图像处理在什么位置做;batch_size会调用什么函数;

  26. 目前在做什么,介绍一下它的原理,代码是如何实现的(说的是NF);

  27. 生成对抗网络的生成器和判别器的关系;

  28. tensorRT在转换的时候会转换成什么类型的模型;

  29. 在转换的过程中遇到了什么问题,是如何解决的;

  30. 为什么tensorRT会减少显存,增加计算量;

  31. 一阶段和两阶段的目标检测模型有什么区别;

    (算法)

  32. 快排;

    (其他)

  33. 你从上一次实习经历中得到了什么;

  34. 在上家公司实习你做了什么工作;
  35. 在上家公司做线材的缺陷检测使用了什么相机,光源;用了几个相机;相机和光源的位置是如何摆放的;是否用到了图像拼接;
  36. 在上家公司使用的可视化系统是自己开发的吗;
  37. 在上家公司实习的项目中遇到了什么问题;是如何解决的;
  38. 学习过flask框架吗;

总结:这次面试时间长达100分钟,感觉比较好,虽然问的问题比较多,有一部分没有回答上来,快排也写的稀碎,但总体还是好的,能够感觉到面试官特别重视。

梅卡曼德机器人(上海)

  1. 自我介绍;
  2. 防止过拟合的方法有哪些;
  3. 介绍一下你的项目(三个都介绍了一下);
  4. 你多次提到了残差结构,介绍一下残差结构是怎么样的;
  5. 学习率一般设置多少,有没有自己的调参经验;过大或过小会导致什么后果;
  6. 介绍一下图像分类,语义分割和实例分割有什么区别;

总结:这个面试很快就结束了,面试官介绍说梅卡曼德的研发主要是在北京,上海这边主要是做项目,比如采集图像2D或3D图像,交给专门的标注公司去做,标注公司做完后检查他们做的是否合格,然后通过项目区去选择合适的方法,用现成的软件设置一下超参数就可以去训练。

武汉TCL工业研究院

  1. 自我介绍;
  2. 介绍一下项目(几乎没有发散去问);
  3. 介绍一下YOLOv3;
  4. YOLOv3的三种不同尺寸的特征图分别是下采样多少;预测何种类型的物体;
  5. YOLOv3的一个grid cell产生几个anchor;
  6. YOLOv3中正样本和负样本是如何划分的;
  7. 介绍一下faster-RCNN;
  8. 在faster-RCNN中正负样本是如何选择的;

哈啰

  1. 介绍一下做过比较有意义的项目;

  2. focal loss的公式,以及各个参数的含义;

  3. 残差结构的作用;

    回答:可以有效的防止过拟合。

    为什么可以有效的防止过拟合?

    回答:残差结构的分支结构,当网络层数过深时,残差结构可以使带有卷积操作的分支短路,从而减少模型的深度。(其实这样回家是不对的)

    应该是因为当是普通结构时y=f(x),而当是残差结构时y=f(x)+x,…..;

  4. 熟悉的目标检测网络有哪些;

  5. 归一化层的作用;
  6. 目标检测网络中,一阶段的网络和两阶段的网络主要的差别在哪;
  7. 介绍一下在faster-RCNN中的RPN层;
  8. 在faster-RCNN中,坐标误差用的什么损失函数;
  9. smooth L1损失背后的原理;
  10. 在YOLOv3中,如果预测框超出了图像区域应该怎么处理;
  11. 在YOLOv3中,预测的xywh的偏移量的值的范围是什么;
  12. 在YOLOv3中的损失函数是什么;
  13. 在YOLOv3中,计算坐标时log和e是怎么计算的;

总结:面试官特别好,每提问一个问题在回答完后都会说出他对回答的评价,以及他觉得的答案,态度也特别好;在面试完以后介绍了一下如果面试通过后的工作内容,并从在校生的视角进行对比,就纠正认识上的差距;并且还给出了今后学习上的一些建议。

问他实习生的核心竞争力是什么:1.实习时间的长短;2.技术;3.态度

正式找工作的核心竞争力:1.技术;2.学习能力(他说这个面试很难体现出来,但也会采用一些方法)

最后还说知道学生在学校基本上只学习python,要多学一下C++,不然以后找工作很不利,尽量多知道一些算法的底层逻辑,这样更有利于以后的发展。

其他面试提出的有价值的问题:

  1. 在对缺陷进行语义分割时,若缺陷的面积很小且不规则,如何进行测量;

需要尽快学习的地方

  1. 学习传统的图像处理方法;
  2. 学习C++;
  3. 学习OpenCV;
  4. 学习3D视觉技术;
  5. 学习PCL点云库;
  6. 学习关于视觉相关的C++技术;
  7. 对YOLO系列和faster-RCNN系列的原理要理清楚,可以不细看代码,但原理一点要搞清楚;
  8. 找关于以上技术的开源项目进行学习,并掌握;
  9. 学习transformer、VAE、NF等模型的原理代码;
  10. 掌握mobilnetv2和unet的模型结构和设计思想;
  11. 完善简历上的项目细节;

部分参考:https://zhuanlan.zhihu.com/p/35709485

二分类交叉熵损失

image-20221215212558197

多分类交叉熵损失

在多分类交叉熵损失函数中,预测值要先经过softmax,然后再送入交叉熵公式。

image-20221215212849429

总结:经过测试,在pytorch的BCELoss中并没有做sigmod或softmax,在多分类交叉熵损失函数中做了softmax。

二分类损失和多分类损失一定不能混用,使用二分类损失时的数据维度应该是一维,使用多分类损失函数时的数据维度应该是二维。

虽然二维数据时仍然可以使用二分类交叉熵损失,但是这样是不对的。同理,一维数据也可以使用多分类交叉熵损失,但是这样也是不对的。

参考

代码:https://github.com/bubbliiiing/yolo3-pytorch

b站:https://www.bilibili.com/video/BV1Hp4y1y788/?spm_id_from=333.337.search-card.all.click&vd_source=0a0fc78c4f646862160e365c9909f8f7

博客:https://blog.csdn.net/weixin_44791964/article/details/105310627

模型

yolov3

  1. 主干提取网络采用的是darknet52;
  2. 在darknet52中,优点是采用了残差连接和1×1和3×3的卷积核连接使用,1×1和3×3的卷积核连接使用可以有效的减少参数量;
  3. 在检测网络中采用了多尺度特征融合,能够有效提高算法的检测精度;
  4. 在yolov3中有3种在不同尺寸特征图上的预测结果,以输入图像是416×416大小为例,分别会生成尺寸为52×52、26×26、13×13的特征图,分别预测小、中、大目标;

损失函数

ab14c47c98f3d0400a3e5d44ff53426

损失函数包括四部分,分别为正样本坐标损失、正样本类别损失、正样本置信度损失和负样本置信度损失。

注:所有样本=正样本+负样本+忽略样本

正样本:真正预测物体的预测框;

忽略样本:不是真正预测物体的预测框,但是与真实框的最大IOU大于0.5

负样本:不是真正预测物体的预测框,但是与真实框的最大IOU小于0.5

正样本坐标损失

  1. 坐标是用x,y,w,h进行表示,分别为中心点坐标和框的宽高的偏移量;
  2. 在代码中计算损失是用的BCELoss;
  3. 在对标签数据进行处理时,对于没有物体的anchor,x,y,w,h,conf和cls都设置为0;
  4. 对小框的惩罚项:对真正预测物体的anchor进行设置,设置的值为该anchor真实的w和h的乘积;其余没有真实物体的值设置为0;
  5. 对4中求出的最终求出的结果,要用2减去;
  6. 在代码中,对小框的惩罚项到乘到标签数据的x,y,w和h上面;
  7. 如何惩罚小框:惩罚小框就是使损失值变大,对于存在物体的anchor的框较小时,对应的w和h也更小(因为w和h代表宽高的偏移量),因此乘积也就更小,当用2减去该值是,所得到的的值就越大,乘到损失函数上就会使损失变大;

正样本类别损失

  1. 正样本类别损失的计算方式采用的BCELoss;
  2. 计算时每个真实目标物体的类别采用的是one_hot编码的数据,预测的类别是采用的预测数据,如[0,0,1]和[0.3,0.5,0.2];

正样本置信度损失和负样本置信度损失

  1. 正样本置信度损失和负样本置信度损失都直接采用BCELoss进行计算;
  2. 该处的置信度是该bbox是否有物体的置信度;

训练阶段

  1. 通过dataloader获取在图片大小是1×1的条件下的x,y,w,h;
  2. 然后转化为在当前特征图上的相对应的偏移量traget;
  3. 把9种不同大小的anchor和真实框进行IOU(该处的IOU是两个矩形的中心点重合),获取每个真实框与anchor最大IOU的索引;
  4. 通过target获取真实框所在特征图的grid cell的位置;
  5. 分别对y_true和noobj_mask进行设置(noobj_mask表示没有物体的索引);
  6. 通过预测值计算在当前特征图上的x,y,w,h,得出预测值;
  7. 在当前特征图下计算真实框和anchor的IOU(普通IOU;若真实框为3,anchor为100,则结果的shape为[3,100]);
  8. 通过7获取每个先验框与真实框的最大重合度;若最大重合度的IOU大于0.5,则忽略该预测框;
  9. 将以上得到的值交给损失函数去计算;

预测阶段

  1. 预测出偏移量;
  2. 得到中心点的坐标和宽高(在当前特征图上);
  3. 将2中得到的坐标进行归一化(即相当于放在1×1的特征图上);
  4. 通过处理得到预测框左上角和右下角的坐标;
  5. 通过置信度进行抑制(具体细节:让每个anchor的置信度和它预测出各个类别的概率相乘的结果去和置信度分数去比较,大于置信度分数则保留,否则则剔除);
  6. 然后按照类别进行非极大值抑制nms(nms只要抑制IOU,但是要传conf),获取最终的结果;
  7. 通过对坐标进行计算,得到在原图(真正图片)上的中心点和宽高;

    目标检测常见名词区分

bbox:指预测框(在anchor的基础上调整过后的预测框);

grid cell:指图像划分成的小格格;

ground truth:指人工标注框;

anchor:指初始化的预测框;

nn.DataParallel是pytorch使用多gpu训练时所使用的方法,但是使用nn.DataParallel之后,模型的读取就会有所不同。

当训练时没有使用nn.DataParallel,而测试时使用

这种情况就会报错,应该先把模型加载进来,在使用nn.DataParallel

1
2
model.load_state_dict(torch.load(save_path))
model = nn.DataParallel(model, device_ids=[0, 1])

造成这些问题的根本原因是因为模型经过nn.DataParallel后,权重参数的形式会发生变化

1
2
3
4
module.features.0.weight # 经过nn.DataParallel
features.0.weight # 没有经过nn.DataParallel
# 没有经过nn.DataParallel的模型,没有model.module这个属性
# 经过nn.DataParallel的模型,给模型传参时使用model.module(params),比如给forward传数据时

torch.permute

permute作用为调换Tensor的维度,参数为调换的维度。例如对于一个二维Tensor来说,调用tensor.permute(1,0)意为将1轴(列轴)与0轴(行轴)调换,相当于进行转置

torch.reshape

reshape和view得到的tensor并不是转置的效果,而是相当于将原tensor的元素按行取出,然后按行放入到新形状的tensor中。

torch.reshape和torch.view的区别

torch的view()与reshape()方法都可以用来重塑tensor的shape,区别就是使用的条件不一样。

view()方法只适用于满足连续性条件的tensor,并且该操作不会开辟新的内存空间,只是产生了对原存储空间的一个新别称和引用,返回值是视图。

reshape()即可以处理连续内存的数据,也可以处理不是连续内存空间的数据;当内存空间连续时和view()相同;当数据内存空间不连续时,就会对数据进行复制到连续内存空间上,然后再进行维度的改变。

image-20221102120054647

Batch Norm

  • 按照不同的通道进行批归一化;
  • 比如输入是【2,3,4,5】,输入两张三通道的图像,取这两张图像的第一个通道组合起来做一次归一化,取这两张图像的第二个通道组合起来做一次归一化……
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import torch

x = torch.randn([2, 3, 2, 2])
bn = torch.nn.BatchNorm2d(3)
res = bn(x)
res1 = res[:, 0, ...]
print(res1)

x1 = x[:, 0, ...]
u = x1.mean()
s = (x1-u).pow(2).mean()
res2 = (x1-u)/torch.sqrt(s+1e-5)
print(res2)
# res1 == res2

Layer Norm

image-20221102122020184

假设我们的输入为(1, 3, 5, 5)的变量,并对其进行LayerNorm,一般来说有两种归一化的方式。如下图所示,左边为第一种归一化方法,对所有channel所有像素计算;右边为第二种归一化方法,对所有channel的每个像素分别计算

  • 计算一个batch中所有channel中所有参数的均值和方差,然后进行归一化,即(3, 5, 5)
  • 计算一个batch中所有channel中的每一个参数的均值和方差进行归一化,即(3, 1, 1),计算25次

参考

  1. 下载cuda、cudnn、tensorrt;
  2. 把cudnn下的lib、bin、include对应复制到cudn下面;
  3. 把tensorrt的lib加到环境变量中;
  4. 安装pycuda时去官网下载whl文件,直接装可能会报错;如果cuda版本太高,用pycuda最新的版本就可以,不用回退cuda版本;
  5. 安装下载好的tensorrt;

显卡:NVIDIA GeForce GTX 1070

batch-size:4

GPU内存:8G

模型:resnet101

数据集:flower_data

img.shpe: (3, 512, 512)

测试情况

默认(不使用混合精度、jit、tensorRT,函数:test_model)
GPU内存占用 运行时间(s)
6.6G 25s
混合精度(只使用混合精度,函数:test_autocast_model)
GPU内存占用 运行时间(s)
4.2G 27s
jit(只使用jit,函数:test_jit_model)
GPU内存占用 运行时间(s)
2.0G 25s
jit+混合精度(test_antocast_jit_model)
GPU内存占用 运行时间(s)
2.0G 25s
tensorRT(只使用tensorRT,函数:test_trt_model)
GPU内存占用 运行时间(s)
3.3G 5s
混合精度+tensorRT(函数:test_autocast_trt_model)
GPU内存占用 运行时间(s)
3.3G 5s
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
def test_model():
model = models.resnet101(pretrained=True).eval().to(device)
for inputs, labels in tqdm(dataloaders['valid']):
inputs = inputs.to(device)
outputs = model(inputs)


def test_autocast_model():
model = models.resnet101(pretrained=True).eval().to(device)
with torch.autocast(device_type="cuda"):
for inputs, labels in tqdm(dataloaders['valid']):
inputs = inputs.to(device)
outputs = model(inputs)


def test_jit_model():
model = models.resnet101(pretrained=True).eval().to(device)
model = torch.jit.script(model)
model = torch.jit.freeze(model)
for inputs, labels in tqdm(dataloaders['valid']):
inputs = inputs.to(device)
outputs = model(inputs)


def test_antocast_jit_model():
model = models.resnet101(pretrained=True).eval().to(device)
with torch.cuda.amp.autocast(cache_enabled=False):
model = torch.jit.script(model)
model = torch.jit.freeze(model)
for inputs, labels in tqdm(dataloaders['valid']):
inputs = inputs.to(device)
outputs = model(inputs)


def test_trt_model():
model = models.resnet101(pretrained=True).eval().to(device)
x = torch.ones((1, 3, 512, 512)).cuda()
model_trt = torch2trt(model, [x])
model = TRTModule()
model.load_state_dict(model_trt.state_dict())
for inputs, labels in tqdm(dataloaders['valid']):
inputs = inputs.to(device)
outputs = model(inputs)


def test_autocast_trt_model():
model = models.resnet101(pretrained=True).eval().to(device)
x = torch.ones((1, 3, 512, 512)).cuda()
model_trt = torch2trt(model, [x])
model = TRTModule()
model.load_state_dict(model_trt.state_dict())
with torch.autocast(device_type="cuda"):
for inputs, labels in tqdm(dataloaders['valid']):
inputs = inputs.to(device)
outputs = model(inputs)

  1. YOLO系列;
  2. faster-rcnn系列;
  3. 元组和列表的区别;
  4. 列表去重;
  5. 静态方法;
  6. 项目经验;
  7. dataset和dataloader;
  8. opencv;