VS2017编译caffe

caffe的windows分支支持的VS版本是2013和2015,而我安装的是Visual Studio Community 2017, 直接使用cmake是不行的。

根据报错信息,凡是不行的地方让VS2017直接使用VS2015的配置,就可以使用caffe工程提供的预编译的二进制的依赖库了。

CMake脚本的修改

cmake/WindowsDownloadPrebuiltDependencies.cmake需要增加

set(DEPENDENCIES_URL_1912_27 "${DEPENDENCIES_URL_BASE}/v${DEPENDENCIES_VERSION}/${DEPENDENCIES_NAME_1900_27}${DEPENDENCIES_FILE_EXT}")
set(DEPENDENCIES_SHA_1912_27 "17eecb095bd3b0774a87a38624a77ce35e497cd2")

1912就是VS2017的,1900是2015的。

依赖包中OpenCV的配置

预编译的OpenCV中也缺少VS2017的信息,.caffe\dependencies\libraries\OpenCVConfig.cmake中增加VS2017的信息

  elseif(MSVC_VERSION EQUAL 1900)
    set(OpenCV_RUNTIME vc14)
  elseif(MSVC_VERSION EQUAL 1912)
    set(OpenCV_RUNTIME vc14)
  endif()

BLAS选择OpenBLAS,我这苏菲也没有N记卡,不用CUDA。选择VS2017 amd64编译起,cmake搞起,成功编译出工程, VS打开sln,编译也成功。Debug配置下
C:\Users\renwe\Projects\caffe\build>.\tools\Debug\caffe-d.exe --help,Release配置下C:\Users\renwe\Projects\caffe\build>.\tools\Release\caffe.exe --help成功运行。

YEAH!对了,

python版本选择

我看了caffe仅支持2.7和3.5版本的python,那VS2016里安装的python3.6我就放弃吧,有miniconda这种神器我何必自己折腾呢。选择了安装了miniconda2版本(想着Caffe2还不支持py3)。

mnist走一波

caffe工程目录下(我的C:\Users\renwe\Projects\caffe> )开PowerShell:

.\data\mnist\get_mnist.ps1
.\examples\mnist\create_mnist.ps1
.\examples\mnist\train_lenet.ps1

注意1:PowerShell运行脚本的权限,默认可能是不能执行的,修改命令Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
注意2:VS工程配置的问题,可执行文件都在各自的Debug和Release字目录中。适当修改下ps1脚本就OK了。
最后训练完测试完

I0331 02:00:46.701665 15772 solver.cpp:310] Iteration 10000, loss = 0.00280334
I0331 02:00:46.701665 15772 solver.cpp:330] Iteration 10000, Testing net (#0)
I0331 02:00:56.045547 19868 data_layer.cpp:73] Restarting data prefetching from start.
I0331 02:00:56.420552 15772 solver.cpp:397]     Test net output #0: accuracy = 0.9901
I0331 02:00:56.420552 15772 solver.cpp:397]     Test net output #1: loss = 0.0309957 (* 1 = 0.0309957 loss)
I0331 02:00:56.420552 15772 solver.cpp:315] Optimization Done.
I0331 02:00:56.420552 15772 caffe.cpp:260] Optimization Done.

WordPress搜索自定义文章类型post_type等

post_type直接使用 Custom Post Type Maker 创建和管理,可以控制是否对默认全站搜索可见。

如何限定只在自定义post_type中进行搜索呢?

WordPress搜索是支持post_type参数的,我们可以通过定制theme里的searchform.php定制下搜索框给加上这个post_type参数即可,可以通过下拉菜单或者radio标签等来控制这个参数。

比如下面这样

<?php $post_type_q = get_query_var ( 'post_type' ); ?>
<input type="radio" name="post_type" value="any" <?php echo $post_type_q=='any'?'checked':'';?> >全站
<input type="radio" name="post_type" value="post" <?php echo $post_type_q=='post'?'checked':'';?> >文章
<input type="radio" name="post_type" value="product" <?php echo $post_type_q=='product'?'checked':'';?> >产品

如何合并全文搜索和某个自定义post_meta搜索呢?

用下面三个filter控制吧,仅在搜索product时搜索这个barcode这个meta信息。

function add_join_include_barcode($joins) {
 if ( !is_admin()) {
  global $wpdb;
  if ( isset( $_GET['post_type'] ) && !empty( $_GET['post_type'] ) && $_GET['post_type'] == 'product' ) {
   $joins = $joins . " INNER JOIN {$wpdb->postmeta} ON ({$wpdb->posts}.ID = {$wpdb->postmeta}.post_id)" ;
  }
 }
 return $joins;
}
function add_where_include_barcode($where) {
 if ( !is_admin()) {
  global $wpdb;
  if ( isset( $_GET['post_type'] ) && !empty( $_GET['post_type'] ) && $_GET['post_type'] == 'product' ) {
   $where.= " OR ({$wpdb->postmeta}.meta_key='barcode' AND {$wpdb->postmeta}.meta_value like '%".esc_sql( $_GET['s'])."%')";
  }
 }
 return $where;
}
add_filter('posts_join','add_join_include_barcode');
add_filter('posts_where','add_where_include_barcode');
function search_distinct() {
 return "DISTINCT";
}
add_filter('posts_distinct', 'search_distinct');

新建文章自动添加meta信息字段

add_action( 'add_meta_boxes_product', 'add_product_meta_data' );
function add_product_meta_data( WP_Post $cf ) {
    // Add the meta data you want the custom post type to have
    $cf_meta_data = [
        'barcode',
    ];

    foreach ( $cf_meta_data as $meta ) {
        add_post_meta( $cf->ID, $meta, '', true );
    }
}

用单选框复选框形式修改某taxonomy

先通过注册add_meta_boxes这个action创建输入框,还需要注册save_post这个action来保存此meta信息。


function product_seller_meta_box_markup($object)
{
    wp_nonce_field(basename(__FILE__), "meta-box-nonce");
    ?>
<div>
<form>
    <?php 
    $sellerterms = get_the_terms( $post->ID, 'seller' );
    $seller = null;
    if ( ! empty( $sellerterms ) && ! is_wp_error( $sellerterms ) ) {
        $seller = $sellerterms[0]->name;
    }
    $allsellers = array('狗家','猫家','狮家');
    for($i = 0; $i < count($allprices); ++$i) {
        echo '<input type="radio" name="product_seller" value="'.$allsellers [$i].'"'.($seller==$allsellers[$i]?' checked ':'').'>'.$allsellers[$i].'<br>';
    }
    ?>
    </form>
</div>
<?php
}
function add_meta_box_seller() {
  add_meta_box("seller-meta-box", "卖家", "product_seller_meta_box_markup", "product", "side", "low", null);
}
add_action("add_meta_boxes", "add_meta_box_seller"); 

function save_product_seller($post_id, $post, $update) {
  if (!isset($_POST["meta-box-nonce"]) || !wp_verify_nonce($_POST["meta-box-nonce"], basename(__FILE__))) return $post_id;
  if(!current_user_can("edit_post", $post_id)) return $post_id;
  if(defined("DOING_AUTOSAVE") && DOING_AUTOSAVE) return $post_id;

  if('product' != $post->post_type)
    return $post_id;

  $seller = null;

  if(isset($_POST["product_seller"]))
  {
    $seller = $_POST["product_seller"];
  }
  if ($seller!==null) wp_set_object_terms( $post_id, $seller , 'seller' );
}

add_action("save_post", "save_product_seller", 10, 3);

 

caffe输入层怎么编写?

caffe的输入层总结

前面已经总结了下caffe已内置的输入层,但是除了python layer外,其余的局限性都比较大,比如大家常用的BoundingBox数据就很不统一,py-faster-rcnn里面使用python layer作输入层直接读取从PASCAL VOC格式数据(其实是从它构建的roidb并缓存了),而SSD自己构建了AnnotatedDataLayer来处理保存在lmdb中的数据,caffe-yolo也自己构建了BoxDataLayer供自己使用,大家都很不统一,caffe官方也迟迟不统一一个BBoxDataLayer出来,局面很分裂。本文就算是总结下阅读这些代码的经验吧。

那输入层应该怎么写?先来搞清除这几个概念。

一些基础概念

Datum

Datum是LMDB/LevelDB每个条目的值。但是Datum的内容是在caffe.proto里规定好了的,原版只有bytesdatafloatfloat_data两个字段可供保存数据。如果想保存多种数据,没办法,只能先组装到一块再放到datum中。

DataReader

yolo和ssd的DataReader完全一样啊。本对象还实例化了一个继承自InternalThread的类负责启动一个线程单独的读取磁盘数据。

如果输入层直接继承InternalThread类,则可以不实现DataReader类,相当于自己就是读入类,线程入口是InternalThreadEntry

Caffe原装的DataLayer就已继承InternalThread,只需要实现load_batch即可。

Batch

Bath就是每次让DataReader读到的数据,这个数据在输入层一般直接就直接想下一层传递了。这个caffe默认的只有data_label_两个Blob成员,如果我们有多种类型数据,caffe默认的恐怕也不够用。

Prefetch队列

  virtual void load_batch(Batch<Dtype>* batch);
  vector<shared_ptr<Batch<Dtype> > > prefetch_;
  BlockingQueue<Batch<Dtype>*> prefetch_free_;
  BlockingQueue<Batch<Dtype>*> prefetch_full_;

load_batch函数的用途:batch_size次从数据库中读取出共计batch_sizedatum数据,将这些datum转成Batch对象。

InternalThreadEntry每次从prefetch_free_pop一个Batch对象出来,然后调用load_batch装满这个Batch对象,再把这个Batch对象pushprefetch_full_中去。

XXXDataLayer::Forward_cpu的作用就是将当前的prefetch_current_指向的Batch对象pushprefetch_free_中去,然后再用从prefetch_full_pop取出一个对象到prefetch_current_上来。

总结下方法

简单标签

如果你的数据只是一些图,每张图对应一个int型可以表示的label,那你直接把图文件名和label组成文本文件用ImageDataLayer读就可以了,想用LMDB的就把数据存到LMDB中也可以。

bbox标签

因为原版datum字段有限,特别是label只有一个,大家只好各显神通。

caffe-yolo的做法是,datumdata存放图片,float_data存放bbox六元组(标签,难易标识,四个坐标)。ssd的做法则是对datum做了更大的更改以支持bbox数据。我为了方便,直接给原版datum定义了扩展字段,允许自行扩展若干自定义字段。

也可以将datum视为最简单的容器,我们在其内的data字段内另建容器,把数据结构先序列化存到datum的data字段,使用时再反序列化出来。

protobuf extension的使用

在proto中扩展Datum

// message Datum 需要先声明扩展字段区间,比如 {
//   extensions 11 to 20;
// }
// 然后想扩展datum时,只需要另外如此声明即可
extend Datum {
  optional string imgfilename = 15;
}

python代码中对消息扩展字段的使用

datum.Extensions[caffe.proto.caffe_pb2.imgfilename] = "/path/to/imgfile"

CPP对对消息扩展字段的使用

// get
datum.GetExtension(imgfilename)
// set
datum.SetExtension(imgfilename, "/path/to/imgfile");

 

方法1:直接继承BasePrefetchingDataLayer

需要实现load_batch函数,这个非常简单,队列和线程方法都已定义,仅需自己实现load_bactchLayerSetup相关的函数即可。

有些继承自此方法的层还定义了DataReader另外起个线程来读数据,其实没什么必要,因为load_batch就已经是在单独的线程里的。

方法2:直接继承Layer和InternalThread来实现

需要实现InternalThread里的InternalThreadEntry方法,并定义数据缓冲队列供使用。这个线程里的方法来调用一些方法实现文件输入并放在队列里供Layer的Forward方法取用。这个方法等于自己实现了一遍BasePrefetchingDataLayerLayerSetup函数最后启动这个线程即可。

附:各输入层的继承关系



python argparse 要点

写个需要参数的python脚本时,这个argparse模块就是相当顺手的,已默认支持常用的参数书写形式。

比如:

parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('--foo')
parser.add_argument('-b','--bar')
# 那么下面的结果都是一样
# PROG --foo 2 --bar 3
parser.parse_args(['--foo','2', '--bar', '3'])
# PROG --fo 2 -b 3
parser.parse_args(['--fo','2', '-b', '3']) # 支持长参数只书写前面能区分出的部分即可,ArgumentParser的参数allow_abbrev=False可以关闭这个特性
# PROG --fo=2 -b=3
parser.parse_args(['--fo=2', '-b=3']) # 长短参数都可以使用等于号形式赋值
# PROG --foo=2 -b3
parser.parse_args(['--foo=2', '-b3']) # 短参数还支持连写形式

继续阅读python argparse 要点

给googletest输出的xml报告文件穿上花衣

给xml文件穿花衣就是XSLT的事情,那我们就来给googletest输出的xml报告穿上花衣服吧。

这是好多年前我创建的一套xslt文件,一直能用。

给googletest输出的xml报告文件穿上花衣,我这件衣服是高仿CUnit的。

我的这个googletest分支上已携带了xml-stylesheet声明,直接使用即可。

# cmake 构建googletest
cd googletest
mkdir build
cd build
cmake -Dgtest_build_samples=ON ..

# 编译googletest及样例
make

# 运行样例让它直接将结果输出到我们的xslt文件目录。
./sample1_unittest --gtest_output=xml:/home/renwei/gtest/gtxslt/sample1_ut_detail.xml

# firefox打开你就能看到穿花衣的gtest xml结果。
firefox /home/renwei/gtest/gtxslt/sample1_ut_detail.xml

这个有什么用?

用途大大的啊,自动化单元测试结果直接发布就不是孤零零的xml文件啊。那自动化单元测试有啥用?嗯,这个,少年,我该怎么回答你呢,一大篇文章。

python logging 要点

print大法固然随手就能方便写出来,但是某些情况还是用的不爽,需要更为专业的日志记录,那就看下python内置的logging模块吧。另外还有google出品的glog等第三方的日志模块,本问不涉及了。

一些基本概念

logger 记录器,记录器可以指定一个或多个handler处理器,以便同时记录到多个位置或者别的什么的区别记录。
handler 实际的处理器,logging模块里的日志最终处理者。
filter 过滤器,logger和handler上都可以指定filter。
formatter 格式化器,这个概念简单,就是决定最终输出格式的,关联与handler上。
loggers树 所有的loggers被整合成一棵以root为根的树,以定义时的名字为信息构建这个树,定义时以.符号分级。另外,某logger生成的record记录完后会自动向其父级logger传播,一直传播到root节点或遇到某级配置propagate = False的logger。
root根logger 默认会被创建的记录器。直接使用logging.warn()模块级日志函数使用的记录器就是它, basicConfig() 的设置也是针对它的, logging.getLogger() 不制定名字获得的logger也是它。

一些使用建议

配置方法

logging以子模块logging.config支持下面三种配置方式:

fileConfig 文件形式配置,文件格式就是configparser所支持的格式,基本就是ini格式。
dictConfig 直接用一个dict字典描述的配置信息,除了方便代码中直接写dict配置外,也方便导入以其他诸如json、yaml等配置文件描述的配置。
listen 启动一个socket server监听配置信息。
*basicConfig 这个basicConfig并不属于logging.config,但它可以配置最顶级的root节点。
*代码配置 对啊,我们当让可以直接在代码中创建logging中的各种对象添加到loggers树中,也可以用代码再对树中的各项予以再配置。

一些使用tips

getLogger(__name__)

使用模块方法logging.getLogger方法来获取或创建logger,多次以同一名称调用该方法获得的logger是同一个对象,不用担心重复或重新创建,安心使用。一般直接用模块名称__name__创建模块内使用的logger。

msg延迟格式化

尽量使用logging.info("info: %s",param)的方式而不是已格式化的字符串logging.info("info: {}".format(param)),因为msg可能被配置过滤掉无需输出了啊。

已内置的Handlers

FileHandler 基本的文件日志处理器。
WatchedFileHandler 它会监视文件,如果日志文件被其他程序改变,会自动关闭并重新打开文件。
RotatingFileHandler,
TimedRotatingFileHandler
以一组序列的文件名来循环记录日志,以文件大小或者时间间隔为限。
SocketHandler,
DatagramHandler
分别用TCP和UDP方式发布日志。
SMTPHandler,HTTPHandler 以SMTP和HTTP方法发布日志。
MemoryHandler 先缓存日志在内容中,后再flush到目标handler上。
NullHandler no-op。
SysLogHandler,
NTEventLogHandler
发布日志到unix syslog和NT event上。
StreamHandler 支持stdout stderr,以及其他支持write()flush()方法的所有文件或类文件对象。

python的单元测试和文档测试模块

unittest模块

用法炒鸡简单,基本用法如下

#!/usr/bin/env python  
#encoding: utf-8  
import unittest

def sum(x, y):
    return x+y

class TestFunc(unittest.TestCase):
    def setUp(self):
        pass
    def tearDown(self):  
        pass

    def test_sum_3_4(self):
        self.assertEqual(sum(3,4), 7)

    def test_sum_3_4_NE6(self):
        self.assertNotEqual(sum(3,4), 6)

    @unittest.expectedFailure
    def test_sum_3_4_EF(self):
        self.assertEqual(sum(3,4), 6)


if __name__ == '__main__':
    unittest.main()
    1. 首先引入 unittest 模块
    2. 定义 TestCase 类,类的 setUp 和 tearDown 函数按需使用,比如测试的一个类的话,在 setUp 中可实例化出一个被测类供测试函数使用,然后再写一堆 test_xxx 函数即可。
    3. 最后,main 模块中使用 unittest.main() 启动整个测试即可。
    4. 自动寻找文件夹中所有的单元测试文件并执行它python -m unittest discover -s ut_dir

doctest模块

doctest模块主要用于对文档字符串写的用法示例进行测试。

怎么写可以进行doctest的示例代码呢?直接实例吧

#!/usr/bin/env python  
#encoding: utf-8  

def sum( x, y):
    """sum function.
    
    Example:
    >>> sum(3,4)
    7
    """
    return x+y

if __name__ == '__main__':
    import doctest
    doctest.testmod()

直接运行这个py文件,嗯,啥结果也没有看见,那就对了,说明doctest通过了。doctest默认是不显示任何结果的,除非使用-v参数:python myfile.py -v 就可以看到doctest执行过程和结果了。

另外,doctest还提供了unittest API以方便unittest直接把doctest一块执行了。
只需要再测试模块中定义个`load_tests()`函数即可

def load_tests(loader, tests, ignore):
    tests.addTests(doctest.DocTestSuite())
    return tests

这般以后,unittest.main()会把doctest也一块给做了。

python正则表达式总结

match/search会缓存最近的对象

re.compile返回的是一个正则表达式对象。
下面两种方式等效,在使用正则较少的情况下不用太担心直接使用re.match的性能问题,因为match/search会缓存最近的对象。

pat = re.compile(pattern)
result = pat.match(string)
# is equivalent to
result = re.match(pattern, string)

re对象的方法

Method/Attribute Purpose
match() Determine if the RE matches at the beginning of the string.
search() Scan through a string, looking for any location where this RE matches.
findall() Find all substrings where the RE matches, and returns them as a list.
finditer() Find all substrings where the RE matches, and returns them as an iterator.

matchsearch在没有找到匹配时返回None,找到时返回match object。

match object的方法

Method/Attribute Purpose
group() Return the string matched by the RE
start() Return the starting position of the match
end() Return the ending position of the match
span() Return a tuple containing the (start, end) positions of the match

match还是search?还是find*?

match只检查字符串从0位置开始是否符合,而search则会搜索整个字符串。
search只搜索自一次匹配,而findall则会搜索完整个字符串提供所有匹配。

贪婪和非贪婪方式

*,+,?等都是贪婪方式。但是后面加个?号改为匹配尽可能少的字符,即*?,+?,??,{m,n}?

标识

Flag Meaning
DOTALL, S Make . match any character, including newlines
IGNORECASE, I Do case-insensitive matches
LOCALE, L Do a locale-aware match
MULTILINE, M Multi-line matching, affecting ^ and $
VERBOSE, X Enable verbose REs, which can be organized more cleanly and understandably.
UNICODE, U Makes several escapes like \w, \b, \s and \d dependent on the Unicode character database.

一些定位元字符

Metacharacter Meaning
| or
^ 行首
$ 行位
\A 字符串首
\Z 字符串尾
\b 单词边界
\B 非单词边界

group分组

group()group(0)是匹配串,group(n)[n>0]是第n个子分组, group(L,m,n)可以接受多个index返回元祖,groups()返回所有匹配子组。

非捕获组(?:…)和命名组(?P…)

非捕获组不能后向引用了,比如只需要单词或的匹配但是又不想捕获它时。
(...)\1使用序号后向引用,(?P<name>...)(?P=name)后向引用命名组。

Lookahead Assertions

(?=...)确定性断言,(?!...)否定性断言。

简单分支判断

(?(id/name)yes-pattern|no-pattern)提供一个简单的分支判断,如果id/name组存在匹配,就检索匹配yes-pattern,否则检索匹配no-pattern,否定项可选可省略。

git中的各种后悔药

FAQ 常用后悔药


工作区的代码想撤销: git checkout <file name>
add到暂存区的代码想撤销: git reset HEAD [<file name>]
提交到本地仓库的代码想撤销: git reset --hard <版本号>
把刚刚的提交撤回到暂存区: git reset --soft HEAD~1
revert某修改: git revert <id>
回滚后反悔怎么办?: git reflog 记录了我们的每一次命令( commit、merge 等信息), 根据这命令来查出我们的历史 commit id,然后 git reset即可
另外,git rebase -i也可以撤销之前的提交。 edit修改,drop丢弃,squash合并压缩

修改历史提交中的用户和邮箱

修改已提交的某一次的信息, 这个使用git rebasegit commit --amend 配合一步一步完成修改。
修改git全部已提交的用户名和邮箱 , 此方案直接全部过滤修改的方式,方便快捷。

删除分支、远程跟踪分支和远程分支

https://stackoverflow.com/questions/2003505/how-do-i-delete-a-git-branch-both-locally-and-remotely?rq=1

# Deleting a remote branch,删除远程分支
git push origin --delete <branch>  # Git version 1.7.0 or newer
git push origin :<branch>          # Git versions older than 1.7.0
 
# Deleting a local branch,删除本地分支
git branch --delete <branch>
git branch -d <branch> # Shorter version
git branch -D <branch> # Force delete un-merged branches
 
# Deleting a local remote-tracking branch,删除本地的远程跟踪分支
git branch --delete --remotes <remote>/<branch>
git branch -dr <remote>/<branch> # Shorter
# only deleted his local remote-tracking branch origin/bugfix, and not the actual remote branch bugfix on origin
# 注:只会删除本地分支对应的远程跟踪分支,不会删除远程服务器上的分支

git fetch <remote> --prune # Delete multiple obsolete tracking branches
git fetch <remote> -p      # Shorter

# delete local tag
git tag -d <tagname>
# delete remote tag
git push origin :tagname #1
git push --delete origin tagname #2
git push origin :refs/tags/<tagname> #3 

建立空的新分支

这里以github的操作为例,创建一个名为gh-pages的空分支

# 创建一个独立的orphan的分支
git checkout --orphan gh-pages

# 删除原来的所有文件
git rm -rf .

注意这个时候你用git branch命令是看不见当前分支的名字的,除非你进行了一次commit。

caffe的输入层总结

来自图片:ImageDataLayer

最简单的输入方式,适用于分类任务,输入就是一个文件文件,而文本文件每行就是一个图片保存路径名称和它的数值标签即可。如果图片都已以文件夹或文件名保存了,可以非常容易地使用lsawk sed等命令建立其要求的文件文件。

来自LMDB或LevelDB数据库:DataLayper

每个条目记录值就是一个Datum,具体代码中就是使用datum.ParseFromString(cursor_->value())获取的。

但是原始的Datum只有一个data项和一个label项和可供使用的float_data项(直接保存的浮点数数据替代bytes表示的data)。如果您的输入数据除了图片还有别的,想自己新写输入层,除了使用这个float_data项外,您可以使用我pCaffe,对Datum定义了扩展条目,可以通过设置扩展字段的方式增加别的data条目。

来自HDF5数据文件:HDF5DataLayer

HDF5数据层参数中不能包含transform参数,不支持数据的图像预处理,直接拷贝数据使用。如果数据确实需要预处理,可以在其后增加reshape crop等层。

来自内存:MemoryDataLayer

每个batch开始前,数据必须使用MemoryDataLayer::Reset方法将内存地址传给网络,caffe会免拷贝地使用它。

自动切图:WindowData

主要是根据窗口定义文件自动地切图贴标签,与基准窗口overlap大于阈值的设为前景窗,否则设为背景窗。 窗口文件里可根据每张图手工控制这些指标,比如可以通过增大小样本类的窗口数以平衡样本不均衡现象。

DummyDataLayer

DummyData有Filler参数,设置来可以自动根据需要填充数据,在构建测试代码时相当好用。

InputLayer

这层参数只有shape,一般仅用于deploy网络中。替代旧式的位于网络参数中的input_shape参数。

PythonLayer

python层用来书写自定义数据的输入层是相当方便的。比如py-faster-rcnn就使用python层来搞定 GroundTruth BoundingBox 的输入。

支持动态并行和源码级plugins的Caffe——pCaffe