mirror of
https://github.com/zebrajr/pytorch.git
synced 2025-12-07 12:21:27 +01:00
Summary: Pull Request resolved: https://github.com/pytorch/pytorch/pull/18886 Expose tensor filler util to Python and add a unit test (both C++/Python) Reviewed By: salexspb Differential Revision: D14784470 fbshipit-source-id: bb8e013d1755c27c166e87d5a8491a97c65d3d8d
259 lines
8.7 KiB
C++
259 lines
8.7 KiB
C++
#include "caffe2/predictor/emulator/data_filler.h"
|
|
#include "caffe2/predictor/emulator/utils.h"
|
|
|
|
namespace caffe2 {
|
|
namespace emulator {
|
|
|
|
void DataNetFiller::fill_parameter(Workspace* ws) const {
|
|
// As we use initial parameter initialization for this BenchmarkState,
|
|
// we can just run the init_net
|
|
CAFFE_ENFORCE(
|
|
ws->RunNetOnce(init_net_),
|
|
"Failed running the init_net: ",
|
|
ProtoDebugString(init_net_));
|
|
}
|
|
|
|
void DataNetFiller::fill_input_internal(TensorList_t* input_data) const {
|
|
Workspace ws;
|
|
CAFFE_ENFORCE(ws.RunNetOnce(data_net_));
|
|
for (const auto& name : input_names_) {
|
|
input_data->emplace_back(
|
|
BlobGetMutableTensor(ws.GetBlob(name), CPU)->Clone());
|
|
}
|
|
}
|
|
|
|
static void fill_with_type(
|
|
const TensorFiller& filler,
|
|
const std::string& type,
|
|
TensorCPU* output) {
|
|
CPUContext context;
|
|
if (type == "float") {
|
|
filler.Fill<float>(output, &context);
|
|
} else if (type == "double") {
|
|
filler.Fill<double>(output, &context);
|
|
} else if (type == "uint8_t" || type == "unsigned char") {
|
|
filler.Fill<uint8_t>(output, &context);
|
|
} else if (type == "uint16_t") {
|
|
filler.Fill<uint16_t>(output, &context);
|
|
} else if (type == "int8_t") {
|
|
filler.Fill<int8_t>(output, &context);
|
|
} else if (type == "int16_t") {
|
|
filler.Fill<int16_t>(output, &context);
|
|
} else if (type == "int32_t" || type == "int") {
|
|
filler.Fill<int32_t>(output, &context);
|
|
} else if (type == "int64_t" || type == "long") {
|
|
filler.Fill<int64_t>(output, &context);
|
|
} else if (type == "bool") {
|
|
auto mutable_filler = filler;
|
|
mutable_filler.Min(0).Max(2).Fill<uint8_t>(output, &context);
|
|
} else {
|
|
throw std::invalid_argument("filler does not support type " + type);
|
|
}
|
|
}
|
|
|
|
DataRandomFiller::DataRandomFiller(
|
|
const NetDef& run_net,
|
|
const std::vector<std::vector<std::vector<int64_t>>>& input_dims,
|
|
const std::vector<std::vector<std::string>>& input_types) {
|
|
// parse dimensions
|
|
CAFFE_ENFORCE_EQ(input_dims.size(), run_net.op_size());
|
|
CAFFE_ENFORCE_EQ(input_types.size(), run_net.op_size());
|
|
|
|
// load op inputs and outputs
|
|
std::unordered_set<std::string> output_names;
|
|
for (size_t i = 0; i < run_net.op_size(); ++i) {
|
|
const auto& op = run_net.op(i);
|
|
const auto& op_dims = input_dims[i];
|
|
const auto& op_types = input_types[i];
|
|
CAFFE_ENFORCE(
|
|
op_dims.size() == op.input_size(),
|
|
op.name() + " has " + c10::to_string(op.input_size()) +
|
|
" inputs; while the input dimension size is " +
|
|
c10::to_string(op_dims.size()));
|
|
CAFFE_ENFORCE(
|
|
op_types.size() == op.input_size(),
|
|
op.name() + " has " + c10::to_string(op.input_size()) +
|
|
" inputs; while the input type size is " +
|
|
c10::to_string(op_types.size()));
|
|
|
|
for (size_t j = 0; j < op.input_size(); ++j) {
|
|
inputs_[op.input(j)] =
|
|
std::make_pair(get_tensor_filler(op, j, op_dims), op_types[j]);
|
|
}
|
|
|
|
// Hack, we normal have a path of
|
|
// length -> LengthsiRangeFill -> Gather -> w -> SparseLengthsWeighted*
|
|
// \---------------------------------------/
|
|
// So when we generate the value of length, we need to bound it to the size
|
|
// of weight input of Gather too
|
|
if (op.type().find("SparseLengthsWeighted") == 0 && i > 0) {
|
|
const auto& prev_op = run_net.op(i - 1);
|
|
if (prev_op.type() == "Gather") {
|
|
const auto& prev_dims = input_dims[i - 1];
|
|
VLOG(1) << "Setting max length value to " << prev_dims[0].front()
|
|
<< " for " << op.input(3);
|
|
inputs_[op.input(3)].first.Max(prev_dims[0].front());
|
|
}
|
|
}
|
|
|
|
for (size_t j = 0; j < op.output_size(); ++j) {
|
|
output_names.emplace(op.output(j));
|
|
}
|
|
}
|
|
|
|
// load parameters
|
|
std::unordered_set<std::string> parameters;
|
|
for (size_t i = 0; i < run_net.arg_size(); ++i) {
|
|
const auto& arg = run_net.arg(i);
|
|
// TODO: replace "PredictorParameters" with the constant in OSS bbp
|
|
if (arg.has_name() && arg.name() == "PredictorParameters") {
|
|
parameters.reserve(arg.strings_size());
|
|
for (size_t j = 0; j < arg.strings_size(); ++j) {
|
|
parameters.emplace(arg.strings(j));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (parameters.size() == 0) {
|
|
VLOG(1) << "Fail to find any parameters";
|
|
}
|
|
for (const auto& param : parameters) {
|
|
// remove unused parameters
|
|
if (inputs_.find(param) != inputs_.end()) {
|
|
// inputs_[param] will be erase from inputs_ in the next step
|
|
parameters_.emplace(param, inputs_[param]);
|
|
}
|
|
}
|
|
|
|
for (const auto& param : parameters_) {
|
|
inputs_.erase(param.first);
|
|
}
|
|
for (const auto& name : output_names) {
|
|
inputs_.erase(name);
|
|
}
|
|
CAFFE_ENFORCE(inputs_.size() > 0, "Empty input for run net");
|
|
|
|
// generate input names
|
|
for (const auto& input : inputs_) {
|
|
input_names_.push_back(input.first);
|
|
}
|
|
}
|
|
|
|
void DataRandomFiller::fill_parameter(Workspace* ws) const {
|
|
for (auto& param : parameters_) {
|
|
Blob* blob = ws->CreateBlob(param.first);
|
|
fill_with_type(
|
|
param.second.first,
|
|
param.second.second,
|
|
BlobGetMutableTensor(blob, CPU));
|
|
CAFFE_ENFORCE(ws->GetBlob(param.first)->GetRaw());
|
|
}
|
|
}
|
|
|
|
void DataRandomFiller::fill_input_internal(TensorList_t* input_data) const {
|
|
for (auto& name : input_names_) {
|
|
input_data->emplace_back(CPU);
|
|
const auto& it = inputs_.find(name);
|
|
CAFFE_ENFORCE(it != inputs_.end());
|
|
fill_with_type(it->second.first, it->second.second, &input_data->back());
|
|
}
|
|
}
|
|
|
|
TestDataRandomFiller::TestDataRandomFiller(
|
|
const NetDef& net,
|
|
const std::vector<std::vector<std::vector<int64_t>>>& inputDims,
|
|
const std::vector<std::vector<std::string>>& inputTypes)
|
|
: DataRandomFiller() {
|
|
std::unordered_set<std::string> outputNames;
|
|
// Determine blobs that are outputs of some ops (intermediate blobs).
|
|
for (auto opIdx = 0; opIdx < net.op_size(); ++opIdx) {
|
|
const auto& op = net.op(opIdx);
|
|
for (auto outputIdx = 0; outputIdx < op.output_size(); ++outputIdx) {
|
|
outputNames.emplace(op.output(outputIdx));
|
|
}
|
|
}
|
|
// Determine ops that have non-intermediate inputs.
|
|
std::unordered_set<size_t> opWithRequiredInputs;
|
|
for (auto opIdx = 0; opIdx < net.op_size(); ++opIdx) {
|
|
const auto& op = net.op(opIdx);
|
|
for (auto inputIdx = 0; inputIdx < op.input_size(); ++inputIdx) {
|
|
if (!outputNames.count(op.input(inputIdx))) {
|
|
opWithRequiredInputs.emplace(opIdx);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CAFFE_ENFORCE_EQ(inputDims.size(), opWithRequiredInputs.size());
|
|
CAFFE_ENFORCE_EQ(inputTypes.size(), opWithRequiredInputs.size());
|
|
|
|
int counter = 0;
|
|
for (auto opIdx = 0; opIdx < net.op_size(); ++opIdx) {
|
|
if (!opWithRequiredInputs.count(opIdx)) {
|
|
// Skip intermediate ops.
|
|
continue;
|
|
}
|
|
const auto& op = net.op(opIdx);
|
|
const auto& op_dims = inputDims[counter];
|
|
const auto& op_types = inputTypes[counter];
|
|
++counter;
|
|
|
|
int countRequiredInputs = 0;
|
|
for (auto inputIdx = 0; inputIdx < op.input_size(); ++inputIdx) {
|
|
if (!outputNames.count(op.input(inputIdx))) {
|
|
++countRequiredInputs;
|
|
}
|
|
}
|
|
|
|
CAFFE_ENFORCE(
|
|
op_dims.size() == countRequiredInputs,
|
|
op.name() + " has " + c10::to_string(op.input_size()) +
|
|
" (required) inputs; while the input dimension size is " +
|
|
c10::to_string(op_dims.size()));
|
|
CAFFE_ENFORCE(
|
|
op_types.size() == countRequiredInputs,
|
|
op.name() + " has " + c10::to_string(op.input_size()) +
|
|
" (required) inputs; while the input type size is " +
|
|
c10::to_string(op_types.size()));
|
|
|
|
int dimCounter = 0;
|
|
for (auto inputIdx = 0; inputIdx < op.input_size(); ++inputIdx) {
|
|
auto inputName = op.input(inputIdx);
|
|
if (outputNames.count(inputName)) {
|
|
// Skip intermediate inputs.
|
|
continue;
|
|
}
|
|
inputs_[inputName] = std::make_pair(
|
|
get_tensor_filler(op, dimCounter, op_dims), op_types[dimCounter]);
|
|
++dimCounter;
|
|
}
|
|
}
|
|
CAFFE_ENFORCE(inputs_.size() > 0, "Empty input for run net");
|
|
// generate input names
|
|
for (const auto& input : inputs_) {
|
|
input_names_.push_back(input.first);
|
|
}
|
|
}
|
|
|
|
void TestDataRandomFiller::fillInputToWorkspace(Workspace* workspace) const {
|
|
for (auto& name : input_names_) {
|
|
const auto& it = inputs_.find(name);
|
|
CAFFE_ENFORCE(it != inputs_.end());
|
|
auto* tensor =
|
|
BlobGetMutableTensor(workspace->CreateBlob(name), caffe2::CPU);
|
|
fill_with_type(it->second.first, it->second.second, tensor);
|
|
}
|
|
}
|
|
|
|
void fillRandomNetworkInputs(
|
|
const NetDef& net,
|
|
const std::vector<std::vector<std::vector<int64_t>>>& inputDims,
|
|
const std::vector<std::vector<std::string>>& inputTypes,
|
|
Workspace* workspace) {
|
|
TestDataRandomFiller(net, inputDims, inputTypes)
|
|
.fillInputToWorkspace(workspace);
|
|
}
|
|
|
|
} // namespace emulator
|
|
} // namespace caffe2
|