caffe & mxnet & fast-rcnn 学习笔记
2015-12-27 by philokey未完成版本。
背景
CV课的大作业,内容是做物体检测,一开始选择做dog detection,后来顺手又把猫加上了。挣扎了好久,觉得用hog或者其他什么特征是在太过时了,决定用cnn。之前读过rcnn的论文,但是代码是用matlab写的,出于某种偏见,不想读,抑制不住的想要自己实现一个Python版的。
接着是训练工具的选择,首先看了caffe,花了一天半配环境,然后发现不能用Python3,于是吐槽的好久愤然放弃。然后发现mxnet是个好东西,配置方便,文档齐全,跑跑classification的example觉得很开心,支持Python3。但是,用mxnet做detection简直是个噩梦,大概因为是新东西,还没人用来做detection。又冷静思考了一下实现rcnn的工作量,各种零零碎碎的东西加起来,我估计今年过完我都实现不了。所以向现实低头,还是用现成点的东西吧。
回去又看了眼rbg的github,好在ICCV2015上的fast-rcnn不仅又快又好,还是用python写的,还内置了修订版的caffe,于是拜读了代码,觉得人家一个科学家,代码还写这么好,真是不知道比我们高到哪里去了。所以最后的决定是:直接用fast-rcnn + caffe,调整已有的网络,训练猫狗检测模型。
fast-rcnn论文概述
感觉slides读起来很清晰,网络结构说的很清楚。
fast-rcnn是rcnn的升级版,mAP提高了4个点左右,训练速度提高了9倍,测试速度提高了213倍,能做到0.3s一张图。
整体架构
网络的整体框架如上图所示,以AlexNet为例。输入分两部分,第一部分是一张原图,先通过5个卷积层,第二部分是selective search生成的2000个ROI,两部分一起通过一个ROIPooling layer,生成长度固定的特征,再进过两个全连接层。作者之前分析了rcnn等方法慢得原因之一是因为采用了pipeline模式,为了加快速度,作者直接搞了连个平行的输出层,也就是说有两个损失函数。一个输出bounding box的predict,用的还是逻辑斯蒂回归,一个输出softmax分类的结果。两个损失函数具体怎么做梯度下降还要再看看源代码吧。
ROI Pooling layer
ROI Pooling layer是一个特殊的单层的SPP layer,文中的解释没有看的很懂(大概是因为SPP net没读懂)。这一层的作用就是就是把每个ROI变成H*W个grid,然后得到固定大小的feature传入全连接层。 总的来说,还是不太懂作者的意图。
fast-rcnn代码阅读和修改
概述
├── caffe-fast-rcnn
├── lib
│ ├── datasets
│ ├── fast_rcnn
│ ├── roi_data_layer
│ ├── setup.py
│ └── utils
├── models
│ ├── CaffeNet
│ ├── VGG16
│ └── VGG_CNN_M_1024
└── tools
├── _init_paths.py
├── compress_net.py
├── demo.py
├── reval.py
├── test_net.py
├── train_net.py
└── train_svms.py
fast-rcnn核心代码框架如上所示,其中caffe进过作者的改造,增加了两个层,一个是本文提出的Pooling Layer,另一个是做bounding box回归的Smooth L1 Loss Layer。lib基本包含了论文里面所有的核心代码,modle对应了论文中提到的三个规模的进过pretraining的网络模型,CaffeNet是S,VGG_CNN_M_1024是M,VGG16是L。tools是一些有用的示例代码,在上面改改基本就能训练自己的模型。
datasets
├── datasets
│ ├── __init__.py
│ ├── factory.py
│ ├── imdb.py
│ └── pascal_voc.py```
先从输入数据说起。
输入数据的处理在datasets这个模块中完成。imdb.py定义了输入数据的基本类imdb,全称是Image database。使用者可以继承这个类,训练自己的数据,pascal_voc.py给出了很好的例子。调用factory.py里面的函数可以生成和使用imdb。
接下来看要怎么生成自己的输入imdb,其实主要工作量也集中在了处理数据和生成数据上,都是体力活。
比如说我们创建一个新的类,叫做catdog
roi_data_layer
├── roi_data_layer
│ ├── __init__.py
│ ├── layer.py
│ ├── minibatch.py
│ └── roidb.py
用mxnet做图像分类
概述
看到kaggle上有个cat vs dog的比赛,于是顺手做了个猫狗分类。考虑到用fast-rcnn做分类有点杀鸡用牛刀,而且训练数据并没有标出区域,而且用selective search生成bounding box略麻烦,所以就直接用mxnet来作。
一开始还很naive的想用kaggle给的25000张图的训练数据来做,结果用Alexnet跑了十几个epochs发现validate的准确率还在50%徘徊,然后意识到了数据量的重要新,于是直接上了pretraining好的模型,果然是效果拔群。跑完第一个epoch,准确率是75%,跑完第二个88%,第三个93%,第四个95%。跑到第十几个的时候,训练数据的准确率已经到了99.6%,validate的数据准确率到96.2%,感觉还是有点overfitting了。交到kaggle上,跑出了96.1%的结果,然而第一名的98%多,如果没有pretraining的数据,能跑到那么高么,总之觉得好厉害。
mxnet使用细节
TO BE UPDATE
总结
这次project的主要收获
- 学习了两个深度学习的工具,caffe,mxnet。个人很看好mxnet。
- 拜读了Fast R-CNN的部分代码,写的好优雅,思路清晰好读,值得学习。
- 稍微跟进了CV领域比较前沿的论文,虽然因为缺少先验知识,没有特别懂。
论文笔记:Rich feature hierarchies for accurate object detection and semantic segmentation
Object detection with R-CNN
Module design
先用selective search生成category-independent region proposals,然后从每个region proposal中用CNN提取出4096维的特征向量。网络是用alex net 摘掉softmax层。
Test time detection
每张测试图片用selective search 的fast mode大概提取出2000个region proposal. 对每个region,用训练好的CNN提取特征,再用SVM分类,打分。如果region有重叠(IoU),保留分最高的,舍弃其它。
由于
- CNN共享参数
- the feature vectors computed by the CNN are low-dimensional when compared to other common approaches
所以速度比较快。 13s ...
read moreScala学习笔记
Functional Programming Principles in Scala
Programming Paradigms(编程范式)
Paradigm: In science, a paradigm describes distinct concepts or thought patterns in some scientific discipline.
Main programming paradigms:
- imperative programming(命令式编程)
- functional programming
- logic programming
Orthogonal to it:
- object-oriented programming(面向对象的程序设计)
Imperative programming
- modifying mutable(可变的) variables
- using assignments
- and control structures ...
C++中的类
公有继承
通过 is-a 的方式进行继承
- 基类先被创建
- 派生类通过成员初始化列表将基类信息传递给基类构造hanshu
- 如果省略成员初始化列表,基类将使用默认构造函数
- 派生类过期时,先调用派生类的析构函数
基类和派生类的特殊关系
class A {
private:
string name;
public:
void print() {
printf("A\n");
}
A(string st) {
name = st;
}
};
class B: public A {
public:
B(string st):A(st){}
};
基类指针可以再不进行显示类型转换的情况下指向派生类的对象; 基类引用可以再不进行显式类型转换的情况下引用派生类对象
B tp("abc");
A &rt = tp; //right
A *pt = &tp; //right ...
C++中的static
静态全局变量
在全局变量前,加上关键字static,该变量就被定义成为一个静态全局变量,存放在全局数据区。
全局变量和全局静态变量的区别
- 全局变量是不显式用static修饰的全局变量,但全局变量默认是动态的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern 全局变量名的声明,就可以使用全局变量。
- 全局静态变量是显式用static修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用extern声明也不能使用。
静态局部变量
在局部变量前,加上关键字static,该变量就被定义成为一个静态局部变量。
有时候需要在两次调用之间对变量的值进行保存。但是全局变量不易维护,所以可以使用静态局部变量。
静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束。
静态函数
在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用。
定义静态函数的好处:
- 静态函数不能被其它文件所用;
- 其它文件中可以定义相同名字的函数,不会发生冲突;
class中的静态成员
定义
对于类中的非静态成员,每个对象都有自己的该成员。而静态成员只创建一个副本。静态成员可以是函数。
class A {
private ...
ubuntu字体
基础知识
Sans-serif=无衬线体=黑体:并不是具体一款字体,而是一类字体,选择它其实等于选择这类字体中优先级最高的那款字体。
Serif=衬线体=白体:同上
Monospace=等宽字体,用于终端下面以及编程使用。这类字体的要求是能区分 1 i l,o 0 O。:同上
点阵字体=位图字体
无衬线体更适合电脑屏幕阅读,衬线体适合打印。——因为衬线可以使得人视线平齐于一行。也就是说不会读破行。
中文显示时有不同的方式,一方面因为中文本身拥有的横和同高度就可以导致这种平齐。行距对中文更重要。
Linux字体
字体文件存放路径
/usr/share/fonts/ #系统字体
~/.fonts #用户字体
配置文件路径
/etc/fonts/fonts.conf #系统配置文件,每类字体下的第一种字体优先级最高
~/.fonts.conf #用户配置文件,只对当前用户运行的程序有效 ...
Search in Rotated Sorted Array
解法:二分法,分别讨论左边单调递增还是右边单调递增。当有重复元素时, A[l] == A[m] 的情况要单独拿出来考虑, 因为有[l,m] => [1,3,1]这种情况
class Solution:
# @param A a list of integers
# @param target an integer
# @return a boolean
def search(self, A, target):
l, r = 0, len(A) - 1
while l <= r:
m = (l + r) / 2
if ...
Longest Valid Parentheses @ LeetCode
题意: 给出括号序列,问最长的连续合法括号子序列长度是多少
解法:用一个栈记录左括号, 右括号和在数组中的下标, 如果当前括号是右括号且栈顶是左括号, 则弹栈并更新答案
class Solution:
# @param s, a string
# @return an integer
def longestValidParentheses(self, s):
sLen, stack, ret = len(s), [(-1, ')')], 0
for i in range(sLen):
if stack[-1][1] == '(' and s[i] == ')':
stack.pop()
ret = max(ret, i - stack[-1][0])
else ...
Unique Paths II @ LeetCode
题意:给n*m的格子,1有障碍物,0表示没有障碍物,问从左上角到右下角有几种走法,其中有障碍物的格子不能通过。
解法:
动态规划。
初始化第一行和第一列时遇到障碍物就停止
dp[i][j] = dp[i-1][j] + dp[i][j-1], 如果grid[i][j]有障碍物, dp[i][j] = 0
class Solution:
# @param obstacleGrid, a list of lists of integers
# @return an integer
def uniquePathsWithObstacles(self, obstacleGrid):
n, m = len(obstacleGrid), len(obstacleGrid ...
Unique Binary Search Trees @ LeetCode
题意:给定 1-n 共n个数, 问可以组成多少种不同的二分查找树。
解法:动态规划。
dp[n] 表示有n个数时,构成不同二分查找树的个数。转移方程为:
class Solution:
# @return an integer
def numTrees(self, n):
dp = [0 for i in range(n+1)]
dp[0] = 1
for i in range(1,n+1):
for ...