未完成版本。

背景

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

感觉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

2015-12-09 by philokey

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),保留分最高的,舍弃其它。

由于

  1. CNN共享参数
  2. the feature vectors computed by the CNN are low-dimensional when compared to other common approaches

所以速度比较快。 13s ...

read more

Scala学习笔记

2014-10-28 by philokey

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 ...
read more

C++中的类

2014-10-15 by philokey

公有继承

通过 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 ...
read more

C++中的static

2014-09-01 by philokey

静态全局变量

在全局变量前,加上关键字static,该变量就被定义成为一个静态全局变量,存放在全局数据区。

全局变量和全局静态变量的区别

  • 全局变量是不显式用static修饰的全局变量,但全局变量默认是动态的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern 全局变量名的声明,就可以使用全局变量。
  • 全局静态变量是显式用static修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用extern声明也不能使用。

静态局部变量

在局部变量前,加上关键字static,该变量就被定义成为一个静态局部变量。

有时候需要在两次调用之间对变量的值进行保存。但是全局变量不易维护,所以可以使用静态局部变量。

静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束。

静态函数

在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用。

定义静态函数的好处:

  • 静态函数不能被其它文件所用;
  • 其它文件中可以定义相同名字的函数,不会发生冲突;

class中的静态成员

定义

对于类中的非静态成员,每个对象都有自己的该成员。而静态成员只创建一个副本。静态成员可以是函数。

class A {
private ...
read more

ubuntu字体

2014-08-19 by philokey

基础知识

Sans-serif=无衬线体=黑体:并不是具体一款字体,而是一类字体,选择它其实等于选择这类字体中优先级最高的那款字体。

Serif=衬线体=白体:同上

Monospace=等宽字体,用于终端下面以及编程使用。这类字体的要求是能区分 1 i l,o 0 O。:同上

点阵字体=位图字体

无衬线体更适合电脑屏幕阅读,衬线体适合打印。——因为衬线可以使得人视线平齐于一行。也就是说不会读破行。

中文显示时有不同的方式,一方面因为中文本身拥有的横和同高度就可以导致这种平齐。行距对中文更重要。

Linux字体

字体文件存放路径

/usr/share/fonts/  #系统字体
~/.fonts #用户字体

配置文件路径

/etc/fonts/fonts.conf #系统配置文件,每类字体下的第一种字体优先级最高
~/.fonts.conf #用户配置文件,只对当前用户运行的程序有效 ...
read more

Search in Rotated Sorted Array

2014-08-16 by philokey

解法:二分法,分别讨论左边单调递增还是右边单调递增。当有重复元素时, 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 ...
read more

Longest Valid Parentheses @ LeetCode

2014-08-14 by philokey

题意: 给出括号序列,问最长的连续合法括号子序列长度是多少

解法:用一个栈记录左括号, 右括号和在数组中的下标, 如果当前括号是右括号且栈顶是左括号, 则弹栈并更新答案

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 ...
read more

Unique Paths II @ LeetCode

2014-08-14 by philokey

题意:给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 ...
read more

Unique Binary Search Trees @ LeetCode

2014-08-13 by philokey

题意:给定 1-n 共n个数, 问可以组成多少种不同的二分查找树。

解法:动态规划。

dp[n] 表示有n个数时,构成不同二分查找树的个数。转移方程为:

$$ dp[n] = \sum_{i=0}^{n-1}dp[i]*dp[n-i-1] $$
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 ...
read more