pytorch/caffe2/python/utils.py
Lu Fang 664fe34e0a
[Caffe2][fbcode=>GH sync] Update from facebook 4323b18ce13c (#7116)
* [fix] Re-enable events in RNN ops

We have earlier added event disabling in RNN ops as back then we didn't use
events, with current use cases this is no longer true
(https://fburl.com/8vd0lp8y)

* use ops with cude impl

* Revert D7729695: [caffe2][fix] Re-enable events in RNN ops

This reverts commit 4b215c7496fb724656ff4c776933a15bdbbcde5e

@bypass-lint

An infra SEV is better than not reverting this diff.
If you copy this password, see you in SEV Review!
@cause_a_sev_many_files

* [observer] Clean up observer_config.h

#accept2ship

* [1/n] Refactor dataio_test.py

Replace code duplication with a common function

* Add barrier net that runs before training nets

Add a synchonize barrier net that is run before training nets.  With this net, shards that are faster will wait for other shards before start training.  This reduce chances of the faster shards timing out during GLOO AllReduce.

Removed explicit data_parallel_model.py.synchronize call in holmes workflow.  Similar change in speech/asr_training workflow will come in another diff.

* Support the dnnlowp backend in caffe2_benchmark

This is for SHARE operator latency evaluation

* Migrate integral_image_op to main caffe2

migrate integral_image_op(GPU version) given by https://fburl.com/yvqezigi
to caffe2/caffe2/operators and implement its CPU version. Write up a test
using the hypothesis_test mechanism

* [pos_disc, fbcode] Implement unjoined lr loss

As explained in https://our.intern.facebook.com/intern/wiki/Model_Based_Calibration/, when the dataset is an joined data set, where labels might change later, we need to use unjoined logloss.

The implementation is almost the same as in Sigrid (https://fburl.com/1trngsls), where
    loss = y (log(p) - log(1-p)) + (1-y)(log(1-p)) = xy - (1-y)x - (1-y)log(1+exp(-x))

For x < 0, to ensure stability and avoid overflow, we reformulate the above exp as
    loss = xy - (1-y)x - (1-y)x + (1-y)log(1+exp(x)) = xy + (1-y)log(1+exp(x))

Then the final expression becomes
    loss = xy + (y - 1) x (x >= 0) - (1 - y) log(1 + exp(x - 2 x (x >= 0)))

where y is the true label, x is the dot product and p = logistic(x).

This kind of implementation is align with the current implementation of the original cross entropy in
https://phabricator.intern.facebook.com/diffusion/FBS/browse/master/fbcode/caffe2/caffe2/operators/cross_entropy_op.cc;0bae3b5d0f825897c5e0dd0ff10f489d7271bf25$7-13

* Keep the array to fix the conflict

* [C2] Compute Adagrad effective LR

The AdagradWithLR op outputs an extra blob which is contains the average effective learning rate across all weights in this blob.

* Open-source extractMetaNetDef & runGlobalInitialization, add new Predictor constructor from db file, and add run_map_outputs

1. Open-source extractMetaNetDef and runGlobalInitialization, for use in
2. new Predictor constructor from db file.
3. Add new run function that returns outputs as TensorMap

* Disable eigen cpu

Disable eigen cpu in transpose and reduce

* Introduce request_only/object_only property of ModelLayer

by default this is False

* A simple TC Caffe2 benchmark

We can run tunner, get MappingOptions and then use them to
compare against cuBLAS

currently broken due to LLVM issues. How to run:

hg checkout eec1ab31b59c03b8deded1c755a9abaf8c45be01
add D7401202
add D7434625
add D7506031
add D7540728

buck run @mode/dev-nosan tc/tc/benchmarks_python:caffe2_benchmark

* Move Caffe2 feature_maps_ops to open source

Need feature maps operators in open source project facebookresearch/BlueWhale

* Manually fix the conflicts in channel shuffle op

* Fix the inconsistency between different gh and fbcode

* Skip Adagrad GPU Test (Because some gpu implementation is missing)

* Fix another test to make sure it won't run on gpu when implementation is not available yet
2018-05-01 20:49:00 -07:00

329 lines
11 KiB
Python

## @package utils
# Module caffe2.python.utils
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from caffe2.proto import caffe2_pb2
from future.utils import viewitems
from google.protobuf.message import DecodeError, Message
from google.protobuf import text_format
import sys
import copy
import collections
import functools
import numpy as np
from six import integer_types, binary_type, text_type
def OpAlmostEqual(op_a, op_b, ignore_fields=None):
'''
Two ops are identical except for each field in the `ignore_fields`.
'''
ignore_fields = ignore_fields or []
if not isinstance(ignore_fields, list):
ignore_fields = [ignore_fields]
assert all(isinstance(f, text_type) for f in ignore_fields), (
'Expect each field is text type, but got {}'.format(ignore_fields))
def clean_op(op):
op = copy.deepcopy(op)
for field in ignore_fields:
if op.HasField(field):
op.ClearField(field)
return op
op_a = clean_op(op_a)
op_b = clean_op(op_b)
return op_a == op_b
def CaffeBlobToNumpyArray(blob):
if (blob.num != 0):
# old style caffe blob.
return (np.asarray(blob.data, dtype=np.float32)
.reshape(blob.num, blob.channels, blob.height, blob.width))
else:
# new style caffe blob.
return (np.asarray(blob.data, dtype=np.float32)
.reshape(blob.shape.dim))
def Caffe2TensorToNumpyArray(tensor):
if tensor.data_type == caffe2_pb2.TensorProto.FLOAT:
return np.asarray(
tensor.float_data, dtype=np.float32).reshape(tensor.dims)
elif tensor.data_type == caffe2_pb2.TensorProto.DOUBLE:
return np.asarray(
tensor.double_data, dtype=np.float64).reshape(tensor.dims)
elif tensor.data_type == caffe2_pb2.TensorProto.INT32:
return np.asarray(
tensor.int32_data, dtype=np.int).reshape(tensor.dims) # pb.INT32=>np.int use int32_data
elif tensor.data_type == caffe2_pb2.TensorProto.INT16:
return np.asarray(
tensor.int32_data, dtype=np.int16).reshape(tensor.dims) # pb.INT16=>np.int16 use int32_data
elif tensor.data_type == caffe2_pb2.TensorProto.UINT16:
return np.asarray(
tensor.int32_data, dtype=np.uint16).reshape(tensor.dims) # pb.UINT16=>np.uint16 use int32_data
elif tensor.data_type == caffe2_pb2.TensorProto.INT8:
return np.asarray(
tensor.int32_data, dtype=np.int8).reshape(tensor.dims) # pb.INT8=>np.int8 use int32_data
elif tensor.data_type == caffe2_pb2.TensorProto.UINT8:
return np.asarray(
tensor.int32_data, dtype=np.uint8).reshape(tensor.dims) # pb.UINT8=>np.uint8 use int32_data
else:
# TODO: complete the data type: bool, float16, byte, int64, string
raise RuntimeError(
"Tensor data type not supported yet: " + str(tensor.data_type))
def NumpyArrayToCaffe2Tensor(arr, name=None):
tensor = caffe2_pb2.TensorProto()
tensor.dims.extend(arr.shape)
if name:
tensor.name = name
if arr.dtype == np.float32:
tensor.data_type = caffe2_pb2.TensorProto.FLOAT
tensor.float_data.extend(list(arr.flatten().astype(float)))
elif arr.dtype == np.float64:
tensor.data_type = caffe2_pb2.TensorProto.DOUBLE
tensor.double_data.extend(list(arr.flatten().astype(np.float64)))
elif arr.dtype == np.int or arr.dtype == np.int32:
tensor.data_type = caffe2_pb2.TensorProto.INT32
tensor.int32_data.extend(arr.flatten().astype(np.int).tolist())
elif arr.dtype == np.int16:
tensor.data_type = caffe2_pb2.TensorProto.INT16
tensor.int32_data.extend(list(arr.flatten().astype(np.int16))) # np.int16=>pb.INT16 use int32_data
elif arr.dtype == np.uint16:
tensor.data_type = caffe2_pb2.TensorProto.UINT16
tensor.int32_data.extend(list(arr.flatten().astype(np.uint16))) # np.uint16=>pb.UNIT16 use int32_data
elif arr.dtype == np.int8:
tensor.data_type = caffe2_pb2.TensorProto.INT8
tensor.int32_data.extend(list(arr.flatten().astype(np.int8))) # np.int8=>pb.INT8 use int32_data
elif arr.dtype == np.uint8:
tensor.data_type = caffe2_pb2.TensorProto.UINT8
tensor.int32_data.extend(list(arr.flatten().astype(np.uint8))) # np.uint8=>pb.UNIT8 use int32_data
else:
# TODO: complete the data type: bool, float16, byte, int64, string
raise RuntimeError(
"Numpy data type not supported yet: " + str(arr.dtype))
return tensor
def MakeArgument(key, value):
"""Makes an argument based on the value type."""
argument = caffe2_pb2.Argument()
argument.name = key
iterable = isinstance(value, collections.Iterable)
# Fast tracking common use case where a float32 array of tensor parameters
# needs to be serialized. The entire array is guaranteed to have the same
# dtype, so no per-element checking necessary and no need to convert each
# element separately.
if isinstance(value, np.ndarray) and value.dtype.type is np.float32:
argument.floats.extend(value.flatten().tolist())
return argument
if isinstance(value, np.ndarray):
value = value.flatten().tolist()
elif isinstance(value, np.generic):
# convert numpy scalar to native python type
value = np.asscalar(value)
if type(value) is float:
argument.f = value
elif type(value) in integer_types or type(value) is bool:
# We make a relaxation that a boolean variable will also be stored as
# int.
argument.i = value
elif isinstance(value, binary_type):
argument.s = value
elif isinstance(value, text_type):
argument.s = value.encode('utf-8')
elif isinstance(value, caffe2_pb2.NetDef):
argument.n.CopyFrom(value)
elif isinstance(value, Message):
argument.s = value.SerializeToString()
elif iterable and all(type(v) in [float, np.float_] for v in value):
argument.floats.extend(
v.item() if type(v) is np.float_ else v for v in value
)
elif iterable and all(
type(v) in integer_types or type(v) in [bool, np.int_] for v in value
):
argument.ints.extend(
v.item() if type(v) is np.int_ else v for v in value
)
elif iterable and all(
isinstance(v, binary_type) or isinstance(v, text_type) for v in value
):
argument.strings.extend(
v.encode('utf-8') if isinstance(v, text_type) else v
for v in value
)
elif iterable and all(isinstance(v, caffe2_pb2.NetDef) for v in value):
argument.nets.extend(value)
elif iterable and all(isinstance(v, Message) for v in value):
argument.strings.extend(v.SerializeToString() for v in value)
else:
if iterable:
raise ValueError(
"Unknown iterable argument type: key={} value={}, value "
"type={}[{}]".format(
key, value, type(value), set(type(v) for v in value)
)
)
else:
raise ValueError(
"Unknown argument type: key={} value={}, value type={}".format(
key, value, type(value)
)
)
return argument
def TryReadProtoWithClass(cls, s):
"""Reads a protobuffer with the given proto class.
Inputs:
cls: a protobuffer class.
s: a string of either binary or text protobuffer content.
Outputs:
proto: the protobuffer of cls
Throws:
google.protobuf.message.DecodeError: if we cannot decode the message.
"""
obj = cls()
try:
text_format.Parse(s, obj)
return obj
except text_format.ParseError:
obj.ParseFromString(s)
return obj
def GetContentFromProto(obj, function_map):
"""Gets a specific field from a protocol buffer that matches the given class
"""
for cls, func in viewitems(function_map):
if type(obj) is cls:
return func(obj)
def GetContentFromProtoString(s, function_map):
for cls, func in viewitems(function_map):
try:
obj = TryReadProtoWithClass(cls, s)
return func(obj)
except DecodeError:
continue
else:
raise DecodeError("Cannot find a fit protobuffer class.")
def ConvertProtoToBinary(proto_class, filename, out_filename):
"""Convert a text file of the given protobuf class to binary."""
proto = TryReadProtoWithClass(proto_class, open(filename).read())
with open(out_filename, 'w') as fid:
fid.write(proto.SerializeToString())
def GetGPUMemoryUsageStats():
"""Get GPU memory usage stats from CUDAContext. This requires flag
--caffe2_gpu_memory_tracking to be enabled"""
from caffe2.python import workspace, core
workspace.RunOperatorOnce(
core.CreateOperator(
"GetGPUMemoryUsage",
[],
["____mem____"],
device_option=core.DeviceOption(caffe2_pb2.CUDA, 0),
),
)
b = workspace.FetchBlob("____mem____")
return {
'total_by_gpu': b[0, :],
'max_by_gpu': b[1, :],
'total': np.sum(b[0, :]),
'max_total': np.sum(b[1, :])
}
def ResetBlobs(blobs):
from caffe2.python import workspace, core
workspace.RunOperatorOnce(
core.CreateOperator(
"Free",
list(blobs),
list(blobs),
device_option=core.DeviceOption(caffe2_pb2.CPU),
),
)
class DebugMode(object):
'''
This class allows to drop you into an interactive debugger
if there is an unhandled exception in your python script
Example of usage:
def main():
# your code here
pass
if __name__ == '__main__':
from caffe2.python.utils import DebugMode
DebugMode.run(main)
'''
@classmethod
def run(cls, func):
try:
return func()
except KeyboardInterrupt:
raise
except Exception:
import pdb
print(
'Entering interactive debugger. Type "bt" to print '
'the full stacktrace. Type "help" to see command listing.')
print(sys.exc_info()[1])
print
pdb.post_mortem()
sys.exit(1)
raise
def raiseIfNotEqual(a, b, msg):
if a != b:
raise Exception("{}. {} != {}".format(msg, a, b))
def debug(f):
'''
Use this method to decorate your function with DebugMode's functionality
Example:
@debug
def test_foo(self):
raise Exception("Bar")
'''
@functools.wraps(f)
def wrapper(*args, **kwargs):
def func():
return f(*args, **kwargs)
return DebugMode.run(func)
return wrapper