core
from caffe2.python import core :
Operators
Caffe2中的Operators有点像函数, 从C++的角度来说, 它们都是从一个通用接口派生而来的, 并且通过类型注册(什么意思?), 因此我们可以在runtime下调用不同的operators. 关于operators的接口定义在 caffe2/proto/caffe2.proto
中. 通常情况下, 它会接受一大批输入, 同时会返回一大批输出.
记住, 当我们说在 Caffe2 Python 中创建一个operator时, 还没有任何东西开始运行. 这仅仅是创建了一个protocal buffer
(协议缓冲区), 用于指定具体的operator, 在稍后的一段时间内, 它将会被送入到C++后台去执行.(protobuf仅仅是一个针对结构化数据的json-like的序列化工具).
Relu
下面的代码创建了一个operator
:1
2
3
4
5
6
7
8
9
10
11
12op = core.CreateOperator(
"Relu", # 我希望运行的operator的类型
["X"], # 一个列表, 里面存放的是输入的blobs的名字
["Y"] # 仍然是一个列表, 里面存放的是输出的blobs的名字
)
print(type(op)) # <class 'caffe2.proto.caffe2_pb2.OperatorDef'>
print(op)
# input: "X"
# output: "Y"
# name: ""
# type: "Relu"
接下来, 尝试使用operator, 首先将输入blobs添加到workspace中, 然后使用最简单的运行operator的方法, 即调用workspace.RunOperatorOnce(operator)
:1
2workspace.FeedBlob("X", np.random.randn(2,3).astype(np.float32))
workspace.RunOperatorOnce(op) # 指定需要运行的operator变量
执行了RunOperatorOnce
以后, workspace中会多出一个名字为name="Y"
的blobs, 用FetchBlobs()
获取该blobs以后, 可以看到Y的值实际上就是对X执行Relu函数以后的结果(对应位置大于0的元素保留, 其他位置归0). 注意, 只有在执行了RunOperatorOnce以后, 名字为Y的blobs才会存在于workspace中, 此时使用FetchBlobs()才能够获取到对应值, 否则, Y是不存在的
CreateOperator
operators同时也可以接受一些可选参数, 这些参数可以通过键值的方式来指定:1
2
3
4
5
6
7
8op = core.CreateOperator(
"GaussianFill",
[], # 填充时不需要输入任何数据
["Z"], # 指定输出的名字
shape=[100,100], # 指定shape, 这里是一个100×100的二维列表
mean=1.0, # 指定平均值
std=1.0 # 指定标准差
)
Nets
Nets本质上就是计算图. 一个Net由多个operators组成, 当我们谈到Nets的时候, 我们同时也会谈到 BlobReference, 这是一个对象, 通过它我们可以轻易的将各种operators连接起来.
在创建网络时, 暗示着protocal buffer除了name以外其余都是空的1
2
3my_net = core.Net("my_first_net") # 重复调用时, 会不断创建net, 每次都会在后面加上一个后缀以区别不同的net, 如my_first_net, my_first_net_1, my_first_net_2等等
print(my_net.Proto()) # name: "my_first_net"
print(type(my_net)) # <class 'caffe2.python.core.Net'>
接下来, 向net中创建一些blobs1
X = my_net.GaussianFill([], ["X"], mean=0.0, shape=[2,3], std=1.0, run_once=0)
此时, my_net
中就创建了一个operator, 其类型为”GaussianFill”. 在这里, 对象X
的类型为: <class 'caffe2.python.core.BlobReference'>
, 它会记录两个值, 一个是blob的名字, 另一个记录该对象是从哪个net中来的(_from_net
).
上面没有使用core
, 而是直接利用my_net
进行operator的创建, 同样也可以使用core
中的CreateOperator
来创建operator, 并将其添加到对应的net中, 如下所示:1
2op = core.CreateOperator("op_name", ... )
net.Proto().op.append(op)
继续创建W和b:1
2W = net.GaussianFill([], ["W"], mean=0.0, std=1.0, shape=[5,3], run_once=0)
b = net.ConstantFill([], ["b"], shape=[5,], value=1.0, run_once=0)
下面利用了一个简单的语法糖, 由于BlobReference
对象知道自己是从哪个net中来的, 因此, 可以直接利用BlobReference
对象来创建operators, 下面显示了如何创建FC operator:1
Y = X.FC([W,b], ["Y"])
如果利用net创建则是下面的形式:1
Y = my_net.FC([X,W,b], ["Y"])
此时, 如果利用my_net.Proto()
来查看net信息的话, 由于参数变多, 难以观察, 因此 Caffe2 提供了一个精简的minimal graph视图来帮助观察net:1
2
3
4from caffe2.python import net_drawer
from IPython import display
graph = net_drawer.GetPydotGraph(net, rankdir="LR")
display.Image(graph.create_pgn(), width=800)
通过上面的代码, 我们创建一个Net, 但是还没有任何东西被运行, 当我们运行network的时候, 会发生下面两件事情:
- 一个C++的net对象会从protobuf中实例化
- 实例化的网络的Run()函数会被调用
再做其他事情之前, 我们需要先用ResetWorkspace()
清空早前的workspace, 然后有两种方式可以用来run net, 首先看看第一种:1
2
3
4
5
6workspace.ResetWorkspace()
print(workspace.Blobs()) # [] 可以看到, 列表为空, 因为X,Y,W,b均是operators
workspace.RunNetOnce(net) # 只执行一次, 无需调用CreateNet
print(workspace.Blobs()) # ['W','X','Y','b'], 在执行了RunNetOnce以后, workspace内被添加了有关operators的blobs.
for name in workspace.Blobs():
print(name, workspace.FetchBlobs(name))
下面是第二种创建并执行net的方式, 首先, 清空workspace里面的blobs, 然后创建net对象, 最后, 执行net1
2
3
4
5
6
7workspace.ResetWorkspace()
print(workspace.Blobs()) #[] 之前的blobs又被清空了
workspace.CreateNet(net) # 先创建net
workspace.RunNet(net.Proto().name) # 执行net, 传入net.Proto().name参数
print(workspace.Blobs()) # ['W','X','Y','b']
for name in workspace.Blobs():
print(name, workspace.FetchBlob(name))
RunNetOnce
和 RunNet
之间有一点小区别, 最重要的区别就是计算开销的不同. 因为RunNetOnce
包含了序列化protobuf, 同时还要将其在Python和C中传递, 并对network进行初始化, 因此它需要更长的执行时间.
net.FC()
1 | model.net.FC() |
net.Softmax()
net.SoftmaxWithLoss()
参数:logits
: Unscaled log probabilitieslabels
:weight_tensor
:
返回值:softmax
: Tensor with softmax cross entropy loss (也可以认为是概率)loss
: Average loss
示例:1
model.net.SoftmaxWithLoss()
net.LabelCrossEntropy()
示例:1
xent = model.LabelCrossEntropy([softmax, label], 'xent')
net.AveragedLoss(xent, “loss”)
示例:1
loss = model.AveragedLoss(xent, "loss")
net.Cast()
示例:1
data = model.net.Cast(data_uint8, "data", to=core.DataType.FLOAT)
net.Scale()
示例:1
2# 将 data 从 [0, 255] 放缩到 [0,1]
data = model.Scale(data, data, scale=float(1./256))
net.StopGradient()
示例:1
2# 避免计算数据的梯度
data = model.StopGradient(data, data)
net.Checkpoint()
workspace
from caffe2.python import workspace
1 | import cv2 # NOQA(Must import before importing caffe2 due to bug in cv2) |
GlobalInit()
1 | workspace.GlobalInit( |
初始化caffe2的全局环境, 如果希望看到更加详细的初始化信息, 可以将--caffe2_log_level
设置为1. 当初始化成功时, 返回 True
, 否则, 返回 False
.
caffe2的工作空间由你创建的blobs组成, 并且将其存储在内存当中. 将blob当做是numpy中的一个N维数组, 可以从Basics.ipynb
中看出, blob实际上是一个指针, 它指向可以存储任意类型的一个C++对象, 但是最常存储的类型仍然是 Tensor
类型.
Blobs()
workspace.Blobs()
方法可以打印出workspace中的所有存在的blobs (只会显示出blobs的名称).
HasBlob()
workspace.HasBlob("blob_name")
用于查询workspace中是否存在名为blob_name
的blob, 返回一个布尔值
workspace.FeedBlob()
用于向workspace中添加blobs1
2
3
4X = np.random.randn(2,3).astype(np.float32)
print("Generated X from numpy:\n{}".format(X))
workspace.FeedBlob("X", X) # 若添加成功, 则返回True, 否则, 返回False
# 如果调用该函数时, 名字已经在workspace中存在, 则后添加的值会覆盖之前添加的值
workspace.FetchBlob("X")
用来获取对应名字的blob的值, 如果获取的名字不在workspace中, 那么就会抛出错误, 我们可以用利用try except
语句来输出错误信息:1
2
3
4try:
workspace.FetchBlob("not_exists_name")
except RuntimeError as err:
print(err) # Can't find blob:...
workspace.ResetWorkspace()
说明:
重置工作空间, 如果root_folder
为空, 则会保持当前的目录设置.
参数:root_folder(str)
: 路径
返回:
无
定义:
备注:
返回
workspace.SwitchWorkspace()
同时, 你可以拥有多个不同的workspaces, 每一个workspace都有不同的名字, 你可以调用workspace.SwitchWorkspace("name")
来在这些不同的名字之间交换. 不同workspace中的blobs是相互独立的, 你可以使用workspace.CurrentWorkspace
来查询当前所有的workspace:1
2
3
4
5
6# Switch the workspace. The second argument "True" means creating
# the workspace if it is not exists
workpsace.SwitchWorkspace("new_workspace_name", True) # 如果名为"new_workspace_name"的workspace存在, 则切换当前workspace到该workspace, 如果不存在, 那么就创建它.
print("Current workspace:{}".format(workspace.CurrentWorkspace())) # 默认的workspace的名称为: default
print("Current blobs in the workspace:{}".format(workspace.Blobs()))
利用workspace.ResetWorkspace()
可以清空当前workspace中的所有东西, 谨慎使用1
workspace.ResetWorkspace()
ModelHelper
源码: model_helper.py
1 | from caffe2.python import model_helper |
param_init()
XavierFill()
1 | m = model_helper.ModelHelper(name="my model") |
ConstantFill()
1 | bias = m.param_init_net.ConstantFill([], "fc_b", shape=[10, ]) |
net
net.Accuracy()
net.FC()
1 | fc_1 = m.net.FC(["data", "fc_w", "fc_b"], "fc1") # "data", "fc_w", "fc_b" 都是workspace中的blobs |
net.Sigmoid()
1 | pred = m.net.Sigmoid(fc_1, "pred") |
net.SoftmaxWithLoss()
1 | softmax, loss = m.net.SoftmaxWithLoss([pred, "label"], ["softmax", "loss"]) |
brew
源码: brew.py, caffe2/python/helpers/
1 | from caffe2.python.helpers.algebra import * |
示例:
1 | from caffe2.python import brew |
brew.conv()
参数:model
:blobs_in
:blobs_out
:dim_in
:dim_out
:kernel
:stride
:pad
:
返回值:blobs_out
的引用
示例:1
2
3
4conv1 = brew.conv(
model, data, 'conv1', dim_in=image_channels, dim_out=32,
kernel=5, stride=1, pad=2
)
brew.max_pool()
参数:model
:blobs_in
:blobs_out
:kernel
:stride
:
返回值:blobs_out
的引用
池化层:1
pool1 = brew.max_pool(model, conv1, "pool1", kernel=2, stride=2)
brew.average_pool()
示例:1
pool2 = brew.average_pool(model, relu2, "pool2", kernel=3, stride=2)
brew.relu()
示例:1
relu1 = brew.relu(model, pool1, "relu1")
brew.accuracy()
参数:model
: 模型对象blob_in
: 输入的blobblob_out
: 输出的blob**kwargs
:kwargs['device_option']
:kwargs['top_k']
:kwargs['accuracy']
:
返回值:model.net.Accuracy(...,blob_out,...)
源码: caffe2/python/helpers/train.py1
2
3
4
5
6
7
8
9
10
11
12# brew.py
# rows 55
class HelperWrapper(object):
_registry = {
'accuracy': accuracy
}
# caffe2/python/helpers/train.py
# rows 37
def accuracy(model, blob_in, blob_out, **kwargs):
# ...
model.net.Accuracy(...)
示例:1
2
3
4# blobs_in 为 [softmax, label]
# blobs_out 为 "accuracy"
def AddAccuracy(model, softmax, label):
accuracy = brew.accuracy(model, [softmax, label], "accuracy")
brew.fc()
参数:model
blob_in
blob_out
dim_in
dim_out
weight_init=None
bias_init=None
WeightInitializer=None
BiasInitializer=None
enable_tensor_core=False
float16_compute=False
**kwargs
返回值:_
: blob_out
的引用
源码: caffe2/python/helpers/fc.py1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22# brew.py
# rows 34
from caffe2.python.helpers.fc import *
class HelperWrapper(object):
_registry={
'fc':fc
}
# caffe2/python/helpers/fc.py
# rows 57
def fc(model, *args, **kwargs):
return _FC_or_packed_FC(model, model.net.FC, *args, **kwargs)
# rows 13
def _FC_or_packed_FC(
model, op_call, blob_in, blob_out, dim_in, dim_out,
weight_init=None, bias_init=None, WeightInitializer=None, BiasInitializer=None,
enable_tensor_core=False, float16_compute=False, **kwargs
):
# ...
return op_call([blob_in, weight, bias], blob_out, **kwargs)
brew.softmax()
参数:model
blob_in
blob_out=None
use_cudnn=False
**kwargs:
kwargs['engine']
: 'CUDNN'
返回值:
源码: caffe2/python/helpers/normalization.py
1 | # brew.py |
brew.db_input()
数据集输入层处理函数
参数:model(ModelHelper)
:blobs_out
:batch_size(int)
: eg: 64db(str)
: 数据文件夹的路径, eg: /home/zerozone/caffe2_notebooks/tutorial_data/cifar10/training_lmdb
db_type(str)
: eg: 'lmdb'
返回值:
源码: caffe2/python/helpers/db_input.py1
2
3
4
5
6
7
8
9
10def db_input(model, blobs_out, batch_size, db, db_type):
dbreader_name = "dbreader_" + db
dbreader = model.param_init_net.CreateDB(
[],
dbreader_name,
db=db,
db_type=db_type,
)
return model.net.TensorProtosDBInput(
dbreader, blobs_out, batch_size=batch_size)
示例:1
2
3
4
5
6
7data_uint8, label = brew.db_input(
model, # ModelHelper 实例
blobs_out=["data_uint8", "label"],
batch_size=batch_size,
db=db,
db_type=db_type,
)
optimizer
1 | from caffe2.python import optimizer |
源码: caffe2/python/optimizer.py
optimizer.build_sgd()
参数:model
:base_learning_rate(float)
:max_gradient_norm=None
:allow_lr_injection=False
:**kwargs
:kwargs['policy'](str)
: eg "fixed"
kwargs['momentum'](float)
: eg 0.9kwargs['weight_decay'](float)
: eg 0.004
返回值:
优化器实例
源码:
1 | # caffe2/python/optimizer.py |
CNNModelHelper
源码: cnn.py
1 | from caffe2.python import cnn |
CNNModelHelper.FC()
1 | # cnn.py |
CNNModelHelper.Softmax()
1 | # cnn.py |
caffe2_pb2
1 | from caffe2.proto import caffe2_pb2 |
源码: caffe2.proto
caffe2_pb2.NetDef()
示例:1
2
3
4
5
6
7
8# 这里的 init_net_proto 也可以替换成 predict_net_proto
# 相应的应该将 .param_init_net 替换成 .net
init_net_proto = caffe2_pb2.NetDef()
with open(init_net_path, "rb") as f:
init_net_proto.ParseFromString(f.read())
test_model.param_init_net = test_model.param_init_net.AppendNet(
core.Net(init_net_proto)
)
caffe2_pb2.TensorProtos()
定义:1
2
3
4
5// caffe2/proto/caffe2.proto
// rows 134
message TensorProtos{
repeated TensorProto protos = 1;
}
备注:1
2
3tensor_protos = caffe2_pb2.TensorProtos()
img_tensor = tensor_protos.protos.add()
caffe2_pb2.TensorProto()
TensorProto.dims
定义:1
2
3// caffe2/proto/caffe2.proto
// rows 44
repeated int64 dims = 1;
备注:1
2
3
4
5print(type(img_tensor.dims))
# <class 'google.protobuf.pyext._message.RepeatedScalarContainer'>
img_tensor.dims.extend((32,32,3))
print(img_tensor.dims) # [32, 32,3]
TensorProto.data_type
定义:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// caffe2/proto/caffe2.proto
// rows 47
enum DataType {
UNDEFINED = 0;
// Basic types
FLOAT = 1; // float
INT32 = 2; // int
BYTE = 3; // byte, when deserialized, is going to be restored as uint8
STRING = 4; // string
// Less-commonly used data types
BOOL = 5; // bool
UINT8 = 6; // uint8_t
INT8 = 7; // int8_t
UINT16 = 8; // uint16_t
INT16 = 9; // int16_t
INT64 = 10; // int64_t
FLOAT16 = 12; // at::Half
DOUBLE = 13; // double
}
optional DataType data_type = 2 [default = FLOAT];
备注:1
2
3
4
5print(type(img_tensor.data_type))
# <class 'int'>
img_tensor.data_type = 1 # 默认值为2
print(img_tensor.data_type)
# 1
TensorProto.float_data
定义:1
2
3// caffe2/proto/caffe2.proto
// rows 85
repeated float float_data = 3 [packed = true];
备注:1
img_tensor.float_data.extend(flatten_img) # flatten_img 是 3072 长的一维向量(32×32×3)
TensorProto.int32_data
定义:1
2
3// caffe2/proto/caffe2.proto
// rows 89
repeated int32 int32_data = 4 [packed = true];
备注:1
label_tensor.int32_data.append(5)
Caffe2 模型结果可视化
下面的网站可以对 .Prototxt
格式的文件或者代码可视化显示
http://ethereon.github.io/netscope/quickstart.html
http://ethereon.github.io/netscope/#/editor