Skip to content

Commit

Permalink
增加手推卷积神经网络反向传播公式
Browse files Browse the repository at this point in the history
  • Loading branch information
GYee committed Aug 14, 2020
1 parent 9fa67ba commit 7b0b68a
Show file tree
Hide file tree
Showing 16 changed files with 185 additions and 26 deletions.
3 changes: 2 additions & 1 deletion C++基础/04_C++11常用新特性.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ using namespace std;
//}

int main() {
vector<int> nums({ 13, 5, 3, 7, 43 });
vector<int> nums{ 13, 5, 3, 7, 43 };
//sort(nums.begin(), nums.end(), cmp); // 1.使用函数来定义,需要自定义一个cmp函数来调用
//2.直接使用lambda表达式
sort(nums.begin(), nums.end(), [](int a, int b)-> int { return a > b; });
Expand Down Expand Up @@ -217,6 +217,7 @@ int main() {
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ C++的标准模板库(STL)中提供了4种智能指针:`auto_ptr`、`uniqu

## 1. auto_ptr

智能指针 `auto_=tr` 由C++98引入,定义在头文件 `<memory>` 中,在C++11中已经被弃用了,因为它不够安全,而且可以被 `unique_ptr` 代替。那它为什么会被 `unique_ptr` 代替呢?先看下面这段代码:
智能指针 `auto_ptr` 由C++98引入,定义在头文件 `<memory>` 中,在C++11中已经被弃用了,因为它不够安全,而且可以被 `unique_ptr` 代替。那它为什么会被 `unique_ptr` 代替呢?先看下面这段代码:

```C++
#include <iostream>
Expand All @@ -43,7 +43,7 @@ int main() {

## 2. unique_ptr

`unique_ptr``auto_ptr` 一样也是采用所有权模式,即同一时间智能有一个智能指针可以指向某个对象 ,但之所以说**使用 `unique_ptr` 智能指针更加安全**,是因为它相比于 `auto_ptr` 而言**禁止了拷贝操作**`auto_ptr`支持拷贝赋值,前面代码示例就是个例子), `unique_ptr` 采用了移动赋值 `std::move()`函数来进行控制权的转移。例如:
`unique_ptr``auto_ptr` 一样也是采用所有权模式,即同一时间只能有一个智能指针可以指向某个对象 ,但之所以说**使用 `unique_ptr` 智能指针更加安全**,是因为它相比于 `auto_ptr` 而言**禁止了拷贝操作**`auto_ptr`支持拷贝赋值,前面代码示例就是个例子), `unique_ptr` 采用了移动赋值 `std::move()`函数来进行控制权的转移。例如:

```C++
#include <iostream>
Expand Down Expand Up @@ -99,7 +99,7 @@ p2 = unique_ptr<string>(new string("hello world")); //正确,临时右值可
>
> ②当智能指针类的对象作为另一个对象的副本时(即进行了拷贝操作),引用计数加1;
>
> ③当使用赋值操作符对一个智能指针类对象进行赋值时(如`p2=p1`),左操作数(即`p2`)引用先减1,因为它已经指向了别的地方,如果减1后引用计数为0,则释放指针所指对象的内存;然后右操作数(即`p1`)引用加1,因此此时左操作数指向了右操作数的对象
> ③当使用赋值操作符对一个智能指针类对象进行赋值时(如`p2=p1`),左操作数(即`p2`)引用先减1,因为它已经指向了别的地方,如果减1后引用计数为0,则释放指针所指对象的内存;然后右操作数(即`p1`)引用加1,因为此时左操作数指向了右操作数的对象
>
> ④调用析构函数时,析构函数先使引用计数减1,若减至0则delete对象。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ int *p = new int[2]; //编译器会自动计算

● malloc分配成功后返回的是**void*指针**,需要强制类型转换成需要的类型;而new**直接就返回了对应类型的指针**

● new和delete使用时会分别调用构造函数和析构函数,而malloc和free只能申请和释放内存空间,不会调用构造 函数和析构函数
● new和delete使用时会分别调用构造函数和析构函数,而malloc和free只能申请和释放内存空间,不会调用构造函数和析构函数



Expand Down
1 change: 0 additions & 1 deletion C++基础/24_sizeof与strlen的区别.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ cout<<sizeof(stu)<<endl; // 输出 12
这个例子是结构体的内存对齐所导致的,计算结构变量的大小就必须讨论数据对齐问题。为了CPU存取的速度最快(这同CPU取数操作有关,详细的介绍可以参考一些计算机原理方面的书),C语言在处理数据时经常把结构变量中的成员的大小按照4或8的倍数计算,这就叫**数据对齐**(data alignment)。这样做可能会浪费一些内存,但理论上速度快了。当然这样的设置会在读写一些别的应用程序生成的数据文件或交换数据时带来不便。
## 参考资料
[sizeof和strlen的区别及使用详解](https://blog.csdn.net/magic_world_wow/article/details/80500473)
10 changes: 5 additions & 5 deletions C++基础/74_sort自定义排序.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
## 问题

算法题当中对原始数据进行排序后,很大概率可以使得解题变得简单。一般情况下都是对 vector 等基本类型进行排序,这时直接使用 sort 函数即可,但是有时候我们想对自定义的结构体等类型进行排序,这时候直接调用sort就行不通了,需要另外定义一个排序规则,然后传给sort函数才行,具体怎么实现嗯
算法题当中对原始数据进行排序后,很大概率可以使得解题变得简单。一般情况下都是对 vector 等基本类型进行排序,这时直接使用 sort 函数即可,但是有时候我们想对自定义的结构体等类型进行排序,这时候直接调用sort就行不通了,需要另外定义一个排序规则,然后传给sort函数才行,具体怎么实现呢

## sort内部使用的是什么排序?

sort并不是简单的快速排序,它对普通的快速排序进行了优化,此外,它还结合了插入排序和推排序。系统会根据你的数据形式和数据量自动选择合适的排序方法,这并不是说它每次排序只选择一种方法,它是在一次完整排序中不同的情况选用不同方法,比如给一个数据量较大的数组排序,开始采用快速排序,分段递归,分段之后每一段的数据量达到一个较小值后它就不继续往下递归,而是选择插入排序,如果递归的太深,他会选择推排序
sort并不是简单的快速排序,它对普通的快速排序进行了优化,此外,它还结合了插入排序和堆排序。系统会根据你的数据形式和数据量自动选择合适的排序方法,这并不是说它每次排序只选择一种方法,它是在一次完整排序中不同的情况选用不同方法,比如给一个数据量较大的数组排序,开始采用快速排序,分段递归,分段之后每一段的数据量达到一个较小值后它就不继续往下递归,而是选择插入排序,如果递归的太深,他会选择堆排序

## sort的基本使用

要使用该函数需要先包含:`#include <algorithm>`
sort的函数原型为:`sort(first_pointer,first_pointer+n,cmp)`
sort的函数原型为:`sort(first_pointer, first_pointer + n, cmp)`

**参数1**:第一个参数是数组的首地址,一般写上数组名就可以,因为数组名是一个指针常量。(左闭)
**参数2**:第二个参数相对较好理解,即首地址加上数组的长度n(代表尾地址的下一地址)。(右开)
Expand Down Expand Up @@ -106,7 +106,7 @@ bool cmp(struct Dog a,struct Dog b)
}
int main()
{
struct Dog dog[3];
Dog dog[3];
dog[0].sex="male";
dog[0].age=3;
dog[0].weight=20;
Expand Down Expand Up @@ -138,7 +138,7 @@ typedef struct student{
char name[20];
int math;
// 重载 < 运算符
bool operator < (const student &x) const {
bool operator < (const student &x){
return math > x.math ;
}
}Student;
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
本仓库汇总CV算法岗重要的一些知识点和面试问答,主要分为**计算机视觉、机器学习、图像处理和、C++基础** 四大块,每一块都有几十个问题,每个问题都会努力给出详细完整的解答,帮助你理清这些知识点细节。

为了刺激下大家的神经,激发斗志,在往下看之前可以先看看这篇经验分享:[算法岗必须人手一篇顶会?超详细面经:无论文、无实习拿下腾讯CV算法岗](https://mp.weixin.qq.com/s?__biz=MzI5MDUyMDIxNA==&mid=2247494712&idx=1&sn=2c906e0c4062955adb8bf4bbda7cb1a8&chksm=ec1c01c1db6b88d7e1f4b8ff2b2f084d1e7961ffdcb29c9de03328cb9c2a14fe68522b7b0c2f&mpshare=1&scene=1&srcid=&sharer_sharetime=1588779616413&sharer_shareid=40621009b5a320f1873da9d6e9a820a7#rd)。创建本仓库的灵感也就是来源于此文,感谢大佬的分享~

## 食用方法

github上直接看的话很多公式和图片看不了,**请 clone 到本地然后下载安装Typora查看**,Typora 是一款支持实时预览的 Markdown 文本编辑器。它有 OS X、Windows、Linux 三个平台的版本,完全免费,体验非常好,推荐你使用。

知识不是看几遍就会的,好记性不如烂笔头,最好看了我们的总结后,你也自己敲出一个适合你的版本,或删减或添加或修改为适合你的表达方式,这样会大大提升学习的效果。
Expand Down Expand Up @@ -45,7 +48,7 @@ github上直接看的话很多公式和图片看不了,**请 clone 到本地
| 18 | | 有哪些修改、调试模型的经验分享 |
| 19 | | 目标检测评价指标mAP的计算 |
| 20 | | 实例分割中的评价指标 |
| 21 | | 手推反向传播公式BP |
| 21 | | 手推反向传播公式BP |
| 22 || 各层 FLOPs 和参数量 paras 的计算 |
| 23 || 感受野怎么计算 |
| 24 || CV中的卷积操作是互相关还是卷积 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Roberts算子是一种利用**局部交叉差分**寻找边缘的算子,常用

**优点:**从图像处理的实际效果来看,边缘定位较准,对噪声敏感。适用于边缘明显且噪声较少的图像分割。

**缺点:**对边缘的定位不太准确,提取的边缘线条较粗。
**缺点:**提取的边缘线条较粗。

Roberts算子的模板分为水平方向和垂直方向,如下式所示,从其模板可以看出,**Roberts算子能较好的增强正负45度的图像边缘。**

Expand All @@ -72,7 +72,7 @@ Prewitt算子采用 3x3 模板对区域内的像素值进行计算,而Robert

**优点:**Prewitt算子对噪声有抑制作用,抑制噪声的原理是通过像素平均。

**缺点:**该算子具有平滑的作用,自但是像素平均相当于对图像的低通滤波,所以Prewitt算子对边缘的定位不如Roberts算子。
**缺点:**该算子具有平滑的作用,但是像素平均相当于对图像的低通滤波,所以Prewitt算子对边缘的定位不如Roberts算子。

同样,下面给出 Prewitt 算子的模板,在像素点 P5 处 $x$ 和 $y$ 方向上的梯度大小 $g_x$ 和 $g_y$ 分别计算如下:

Expand Down
2 changes: 1 addition & 1 deletion 图像处理/12_常见的三种图像插值方法.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ FPN网络的上采样过程用到了最近邻插值(转置卷积也是一种

这种方法计算量小,但可能会造成插值生成的图像灰度上的不连续,在灰度变化的地方可能会出现明显的锯齿感。

上面是在数字图像处理中,遇到像素坐标为小数时的取值问题,最开始我们也提到了 FPN 的从上往下的旁路中的上采样也使用到了最近邻插值方法,这里的上采样是使得特征图的宽与高变成原来的 2 倍,那根我们这里提到的小数坐标运算问题不太一样,它是直接先逐行复制,再逐列复制,具体的表现我们使**用 keras 中的 UpSampling2D 函数**来可视化下就很清晰了:
上面是在数字图像处理中,遇到像素坐标为小数时的取值问题,最开始我们也提到了 FPN 的从上往下的旁路中的上采样也使用到了最近邻插值方法,这里的上采样是使得特征图的宽与高变成原来的 2 倍,那跟我们这里提到的小数坐标运算问题不太一样,它是直接先逐行复制,再逐列复制,具体的表现我们使**用 keras 中的 UpSampling2D 函数**来可视化下就很清晰了:

```python
#!/usr/bin/env/python3
Expand Down
2 changes: 1 addition & 1 deletion 图像处理/18_图像配准.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@

为了定位局部最大值和最小值,仔细检查图像中的每个像素,并将其与相邻像素进行比较。

当我说“邻近”时,它不仅包括该图像的周围像素(像素所在的像素),还包括八度中上一张和下一张图像的九个像素。
当我说“邻近”时,它不仅包括该图像的周围像素(像素所在的尺度),还包括八度中上一张和下一张图像的九个像素。

这意味着将每个像素值与其他26个像素值进行比较,以确定是否为局部最大值/最小值。例如,在下图中,我们从第一个八度获得了三个图像。将标记为x的像素与相邻像素(绿色)进行比较,如果它是相邻像素中最高或最低的像素,则将其选择为关键点:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Bagging对样本重采样,对每一重采样得到的子样本集训练一个

另一方面,若各子模型独立,则有 $Var(\frac{\sum X_i}{n}) = \frac{Var(X_i)}{n}$ ,此时可以显著降低variance。若各子模型完全相同,则 $Var(\frac{\sum X_i}{n}) = Var(X_i)$ ,此时不会降低variance。bagging方法得到的各子模型是有一定相关性的,属于上面两个极端状况的中间态,因此可以一定程度降低variance。

为了进一步降低variance,Random forest 通过随机选取特征子集,进一步减少了模型之间的香相关性,从而使得variance进一步降低。
为了进一步降低variance,Random forest 通过随机选取特征子集,进一步减少了模型之间的相关性,从而使得variance进一步降低。

## 参考资料

Expand Down
6 changes: 3 additions & 3 deletions 机器学习/57_机器学习中常用的损失函数一览.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ $$
1. MSE 相比 MAE 通常可以更快地收敛
2. MAE 相比 MSE 对于离群点更加健壮,包容性更强,即更加不易受到离群点 outlier 的影响。

**MSE 相比 MAE 通常可以更快地收敛**:当使用梯度下降算法时,MSE 损失的梯度为 $-\hat{y_i}$ ,而 MAE 损失的梯度为正负 1,即 MSE 的梯度的 scale 会随误差大小变化,而 MAE 的梯度的 scale 则一直保持为 1,即便在绝对误差 $|y_i-\hat{y_i}|$ 很小的时候 MAE 的梯度 scale 也同样为 1,这实际上是非常不利于模型的训练的。当然你可以通过在训练过程中动态调整学习率缓解这个问题,但是总的来说,损失函数梯度之间的差异导致了 MSE 在大部分时候比 MAE 收敛地更快。**这个也就是回归问题中一般采用 MSE 而不是 MAE 的原因。**
**MSE 相比 MAE 通常可以更快地收敛**:当使用梯度下降算法时,MSE 损失的梯度为 $-(y_i - \hat{y_i})$ ,而 MAE 损失的梯度为正负 1,即 MSE 的梯度的 scale 会随误差大小变化,而 MAE 的梯度的 scale 则一直保持为 1,即便在绝对误差 $|y_i-\hat{y_i}|$ 很小的时候 MAE 的梯度 scale 也同样为 1,这实际上是非常不利于模型的训练的。当然你可以通过在训练过程中动态调整学习率缓解这个问题,但是总的来说,损失函数梯度之间的差异导致了 MSE 在大部分时候比 MAE 收敛地更快。**这个也就是回归问题中一般采用 MSE 而不是 MAE 的原因。**

**MAE 相比 MSE 对于离群点更加健壮**:这点可以这样直观地理解,下图是 MAE 和 MSE 损失画到同一张图里面,由于MAE 损失与绝对误差之间是线性关系,MSE 损失与误差是平方关系,当误差非常大的时候,MSE 损失会远远大于 MAE 损失。因此当数据中出现一个误差非常大的 outlier 时,MSE 会产生一个非常大的损失,对模型的训练会产生较大的影响。

Expand Down Expand Up @@ -194,7 +194,7 @@ $$
$$
KL(p,q) = \sum_{k=1}^{K}p^klog(p^k)-\sum_{k=1}^{K}p^klog(q^k)
$$
其中的第一项为分布 $p$ 的信息熵,第二项为分布 $p$ 和分布 $q$ 的交叉熵。将最优分布 $y_i^*$ 和输出分布 $\hat{y_i}$ 代入 $p$ 和 $q$ 得到:
其中的第一项为分布 $p$ **信息熵**,第二项为分布 $p$ 和分布 $q$ **交叉熵**。将最优分布 $y_i^*$ 和输出分布 $\hat{y_i}$ 代入 $p$ 和 $q$ 得到:
$$
KL(y_i^*,\hat{y_i}) = \sum_{k=1}^{K}{y_i^*}^klog({y_i^*}^k)-\sum_{k=1}^{K}{y_i^*}^klog(\hat{y_i^k})
$$
Expand Down Expand Up @@ -258,7 +258,7 @@ $$

## 指数损失

运用指数损失的典型分类器是 AdaBoost 算法。AdaBoost 可以看作是前向分步加法算法的特例,是又基本分类器组成的加法模型,损失函数为指数函数。
运用指数损失的典型分类器是 AdaBoost 算法。AdaBoost 可以看作是前向分步加法算法的特例,是由基本分类器组成的加法模型,损失函数为指数函数。

**指数函数形式:**
$$
Expand Down
Loading

0 comments on commit 7b0b68a

Please sign in to comment.