I'm trying to get an output from a trained model which has a classification, the input node count is 1, and the output node count is 2. However, I'm not quite sure where the classification lands and how exactly do I handle it.
for(size_t idx = 0; idx < input_node_count; idx++)
{
float* data_ptr = interpreter->typed_input_tensor<float>(idx);
memcpy(data_ptr, my_input.data(), input_elem_size[idx]);
}
if (kTfLiteOk != interpreter->Invoke())
{
return false;
}
for(size_t idx = 0; idx < output_node_count; idx++)
{
float* output = interpreter->typed_output_tensor<float>(idx);
output_buffer[idx] = std::vector<float> (output,
output + output_elem_size[idx]);
}
result = output_buffer[1];
classification_result = output_buffer[0]; // Best way to approach this
As of now, I can just print out the sizes and see that result is 196.608 elements and classification_result is 2, as it should. My problem is I hard-coded this to be index 1 and 0 but this might not always be the case in my program which runs all sorts of models. So sometimes classification might be index 1, which causes the above code to fall apart.
I've tried to check the sizes of the buffers however that is also not guaranteed since the classification size and the result size is different for each input. Is there a way for me to know for certain which index is which? Am I approaching this the right way?
Use Tensorflow lite signatures for this. Signature defs can help you with that by accessing inputs/outputs using names defined in the original model.
See conversion and inference example here
Python example
# Load the TFLite model in TFLite Interpreter
interpreter = tf.lite.Interpreter(TFLITE_FILE_PATH)
# There is only 1 signature defined in the model,
# so it will return it by default.
# If there are multiple signatures then we can pass the name.
my_signature = interpreter.get_signature_runner()
# my_signature is callable with input as arguments.
output = my_signature(x=tf.constant([1.0], shape=(1,10), dtype=tf.float32))
# 'output' is dictionary with all outputs from the inference.
# In this case we have single output 'result'.
print(output['result'])
For C++
# To run
auto my_signature = interpreter_->GetSignatureRunner("my_signature");
# Set your inputs and allocate tensors
auto* input_tensor_a = my_signature->input_tensor("input_a");
...
# Execute
my_signature->Invoke();
# output
auto* output_tensor_x = my_signature->output_tensor("output_x");
Related
I'm trying to implement an image based regression using a CNN in libtorch. The problem is, that my images got different sizes, which will cause an Exception batching the images.
First things first, I create my dataset:
auto set = MyDataSet(pathToData).map(torch::data::transforms::Stack<>());
Then I create the dataLoader:
auto dataLoader = torch::data::make_data_loader(
std::move(set),
torch::data::DataLoaderOptions().batch_size(batchSize).workers(numWorkersDataLoader)
);
The exception will be thrown batching data in the train loop:
for (torch::data::Example<> &batch: *dataLoader) {
processBatch(model, optimizer, counter, batch);
}
with a batch size greater than 1 (with a batch size of 1 everything works well because there isn't any stacking involved). For example I'll get the following error using a batch size of 2:
...
what(): stack expects each tensor to be equal size, but got [3, 1264, 532] at entry 0 and [3, 299, 294] at entry 1
I read that one could for example use collate_fn in order to implement some padding (for example here), I just do not get where to implement it. For example torch::data::DataLoaderOptions does not offer such a thing.
Does anyone know how to do this?
I've got a solution now. In summary, I'm split my CNN in Conv- and Denselayers and use the output of a torch::nn::AdaptiveMaxPool2d in the batch construction.
In order to do so, I have to modify my Dataset, Net and train/val/test-methods. In my Net I added two additional forward-functions. The first one passes data through all Conv-Layers and returns the output of an AdaptiveMaxPool2d-Layer. The second one passes the data through all Dense-Layers. In practice this looks like:
torch::Tensor forwardConLayer(torch::Tensor x) {
x = torch::relu(conv1(x));
x = torch::relu(conv2(x));
x = torch::relu(conv3(x));
x = torch::relu(ada1(x));
x = torch::flatten(x);
return x;
}
torch::Tensor forwardDenseLayer(torch::Tensor x) {
x = torch::relu(lin1(x));
x = lin2(x);
return x;
}
Then I override the get_batch method and use forwardConLayer to compute every batch entry. In order to train (correctly), I call zero_grad() before I construct a batch. All in all this looks like:
std::vector<ExampleType> get_batch(at::ArrayRef<size_t> indices) override {
// impl from bash.h
this->net.zero_grad();
std::vector<ExampleType> batch;
batch.reserve(indices.size());
for (const auto i : indices) {
ExampleType batchEntry = get(i);
auto batchEntryData = (batchEntry.data).unsqueeze(0);
auto newBatchEntryData = this->net.forwardConLayer(batchEntryData);
batchEntry.data = newBatchEntryData;
batch.push_back(batchEntry);
}
return batch;
}
Lastly I call forwardDenseLayer at all places where I normally would call forward, e.g.:
for (torch::data::Example<> &batch: *dataLoader) {
auto data = batch.data;
auto target = batch.target.squeeze();
auto output = model.forwardDenseLayer(data);
auto loss = torch::mse_loss(output, target);
LOG(INFO) << "Batch loss: " << loss.item<double>();
loss.backward();
optimizer.step();
}
Update
This solution seems to cause an error if the number of the dataloader's workers isn't 0. The error is:
terminate called after thro9wing an instance of 'std::runtime_error'
what(): one of the variables needed for gradient computation has been modified by an inplace operation: [CPUFloatType [3, 12, 3, 3]] is at version 2; expected version 1 instead. ...
This error does make sense because the data is passing the CNN's head during the batching process. The solution to this "problem" is to set the number of workers to 0.
I am trying to create a mask for torch in C++ of type BoolTensor. The first n elements in dimension one need to be False and the rest need to be True.
This is my attempt but I do not know if this is correct (size is the number of elements):
src_mask = torch::BoolTensor({6, 1});
src_mask[:size,:] = 0;
src_mask[size:,:] = 1;
I'm not sure to understand exactly your goal here, so here is my best attempt to convert into C++ you pseudo-code .
First, with libtorch you declare the type of your tensor through the torch::TensorOptions struct (types names are prefixed with a lowercase k)
Second, your python-like slicing is possible thanks to the torch::Tensor::slicefunction (see here and there).
Finally, that gives you something like :
// Creates a tensor of boolean, initially all ones
auto options = torch::TensorOptions().dtype(torch::kBool));
torch::Tensor bool_tensor = torch::ones({6,1}, options);
// Set the slice to 0
int size = 3;
bool_tensor.slice(/*dim=*/0, /*start=*/0, /*end=*/size) = 0;
std::cout << bool_tensor << std::endl;
Please not that this will set the first size rows to 0. I assumed that's what you meant by "first elements in dimension x".
Another way to do it:
using namespace torch::indexing; //for using Slice(...) function
at::Tensor src_mask = at::empty({ 6, 1 }, at::kBool); //empty bool tensor
src_mask.index_put_({ Slice(None, size), Slice() }, 0); //src_mask[:size,:] = 0
src_mask.index_put_({ Slice(size, None), Slice() }, 1); //src_mask[size:,:] = 0
How would one get a view of a PyArrayObject* similar to the following python code?
# n-column array x
# d is the length of each column
print(x.shape) # => (d, n)
by_column = [x[::,i] for i in range(x.shape[1])]
assert len(by_column) == n
print(by_column[n-1].shape) # => (d,)
So far my code is this:
// my_array is a PyArrayObject*
std::vector<PyArrayObject*> columns = {};
npy_intp* dims = my_array->dimensions;
npy_intp* strides = my_array->strides;
std::vector<int> shape = {};
for (int i = 0; &dims[i] != strides; i++){
shape.push_back(dims[i]);
}
switch (shape.size()) {
case 1: {
// handle 1D array by simply iterating
}
case 2: {
int columns = shape[1];
// What now?
}
}
I'm having trouble finding any reference to do this in C/C++ in both the documentation and the source code, could you give an example of how one would do this?
The C/C++ API for numpy seems really convoluted when compared to something like std::vector, and the documentation isn't very beginner-friendly either, so any references to easier guides would be appreciated too.
You should access the internal structure of PyArrayObject via the PyArray_XXX functions like PyArray_NDIM. To get the contents of a sequence, you use PyObject_GetItem with a tuple key, where in your use case the tuple will have a PySliceObject as the first element.
I have a C++ application with an SQL backend, and have been storing the row id of any retrieved columns (an integer, primary key bigint in the database) with SetItemData() on list control rows as required. This is then retrieved with GetItemData() if that ID needs to be queried.
I am now getting a weird problem in that, in this one scenario, GetItemData() is returning a random 7-digit number instead of the stored ID. When I add the row I use the following code:
CListCtrl& lc = GetListCtrl();
for (int i = 0; i < vInsertItems.size(); i++) {
int j = lc.InsertItem(i,i.strName);
DWORD dwdRowID = (DWORD)cammms,nRowID;
lc.SetItemData(j,dwdRowID);
}
To retrieve and check the value I can do the following (where I have determined that nCurrentlySelectedIndex is correct):
CListCtrl& lc = GetListCtrl();
int msgID = lc.GetItemData(nCurrentlySelectedIndex);
CString debugInt; debugInt.Format(_T("debugInt = %d"),msgID);
AfxMessageBox(debugInt);
What is bizarre, is that if I run the second batch of code directly after the first, it is all fine. But if I run it in a separate function, msgID becomes set to a set of random 7 digits, different every time.
Does anyone have any idea what could be causing this?
I everybody,
I'm trying to parse a text file into matlab: it consists of several blocks (START_BLOCK/END_BLOCK) where are allocated strings (variable) and values (associated to the previous variables).
An example is this:
START_BLOCK_EXTREMEWIND
velocity_v1 29.7
velocity_v50 44.8
velocity_vred1 32.67
velocity_vred50 49.28
velocity_ve1 37.9
velocity_ve50 57
velocity_vref 50
END_BLOCK_EXTREMEWIND
Currently, my code is:
fid = fopen('test_struct.txt','rt');
C = textscan(fid,'%s %f32 %*[^\n]','CollectOutput',true);
C{1} = reshape(C{1},1,numel(C{1}));
C{2} = reshape(C{2},1,numel(C{2}));
startIdx = find(~cellfun(#isempty, regexp(C{1}, 'START_BLOCK_', 'match')));
endIdx = find(~cellfun(#isempty, regexp(C{1}, 'END_BLOCK_', 'match')));
assert(all(size(startIdx) == size(endIdx)))
extract_parameters = #(n)({C{1}{startIdx(n)+1:endIdx(n) - 1}});
parameters = arrayfun(extract_parameters, 1:numel(startIdx), 'UniformOutput', false);
s = cell2struct(cell(size(parameters{1})),parameters{1}(1:numel(parameters{1})),2);
s.velocity_v1 = C{2}(2);
s.velocity_v50 = C{2}(3);
s.velocity_vred1 = C{2}(4);
s.velocity_vred50 = C{2}(5);
s.velocity_ve1 = C{2}(6);
s.velocity_ve50 = C{2}(7);
s.velocity_vref = C{2}(8);
It works, but it's absolutely static. I would rather have a code able to:
1. check the existence of blocks --> as already implemented;
2. the strings are to be taken as fields of the structure;
3. the numbers are meant to be the attributes of each field.
Finally, if there is more than one block, there should be and iteration about those blocks to get the whole structure.
It's the first time I approach structure coding at all, so please be patient.
I thank you all in advance.
Kindest regards.
It sounds like you will want to make use of dynamic field names. If you have a struct s, a string fieldName that stores the name of a field, and fieldVal which holds the value that you'd like to set for this field, then you can use the following syntax to perform the assignment:
s.(fieldName) = fieldVal;
This MATLAB doc provides further info.
With this in mind, I took a slightly different approach to parse the text. I iterated through the text with a for loop. Although for loops are sometimes frowned upon in MATLAB (since MATLAB is optimized for vectorized operations), I think in this case it helps to make the code cleaner. Furthermore, my understanding is that if you are having to make use of arrayfun, then replacing this with a for loop probably won't really cause much of a performance hit, anyway.
The following code will convert each block in the text to a struct with the specified fields and values. These resulting "block" structs are then added to a higher-level "result" struct.
fid = fopen('test_struct.txt','rt');
C = textscan(fid,'%s %f32 %*[^\n]','CollectOutput',true);
fclose(fid);
paramNames = C{1};
paramVals = C{2};
curBlockName = [];
inBlock = 0;
blockCount = 0;
%// Iterate through all of the entries in "paramNames". Each block will be a
%// new struct that is then added to a high-level "result" struct.
for i=1:length(paramNames)
curParamName = paramNames{i};
isStart = ~isempty(regexp(curParamName, 'START_BLOCK_', 'match'));
isEnd = ~isempty(regexp(curParamName, 'END_BLOCK_', 'match'));
%// If at the start of a new block, create a new struct with a single
%// field - the BlockName (as specified by the text after "START_BLOCK_"
if(isStart)
assert(inBlock == 0);
curBlockName = curParamName(length('START_BLOCK_') + 1:end);
inBlock = 1;
blockCount = blockCount + 1;
s = struct('BlockName', curBlockName);
%// If at the end of a block, add the struct that we've just populated to
%// our high-level "result" struct.
elseif(isEnd)
assert(inBlock == 1);
inBlock = 0;
%// EDIT - storing result in "structure of structures"
%// rather than array of structs
%// s_array(blockCount) = s;
result.(curBlockName) = s;
%// Otherwise, assume that we are inside of a block, so add the current
%// parameter to the struct.
else
assert(inBlock == 1);
s.(curParamName) = paramVals(i);
end
end
%// Results stored in "result" structure
Hopefully this answers your question... or at least provides some helpful hints.
I edited my code today and now it almost works as meant to be:
clc, clear all, close all
%Find all row headers
fid = fopen('test_struct.txt','r');
row_headers = textscan(fid,'%s %*[^\n]','CommentStyle','%','CollectOutput',1);
row_headers = row_headers{1};
fclose(fid);
%Find all attributes
fid1 = fopen('test_struct.txt','r');
attributes = textscan(fid1,'%*s %s','CommentStyle','%','CollectOutput',1);
attributes = attributes{1};
fclose(fid1);
%Collect row headers and attributes in a single cell
parameters = [row_headers,attributes];
%Find all the blocks
startIdx = find(~cellfun(#isempty, regexp(parameters, 'BLOCK_START_', 'match')));
endIdx = find(~cellfun(#isempty, regexp(parameters, 'BLOCK_END_', 'match')));
assert(all(size(startIdx) == size(endIdx)))
%Extract fields between BLOCK_START_ and BLOCK_END_
extract_fields = #(n)(parameters(startIdx(n)+1:endIdx(n)-1,1));
struct_fields = arrayfun(extract_fields, 1:numel(startIdx), 'UniformOutput', false);
%Extract attributes between BLOCK_START_ and BLOCK_END_
extract_attributes = #(n)(parameters(startIdx(n)+1:endIdx(n)-1,2));
struct_attributes = arrayfun(extract_attributes, 1:numel(startIdx), 'UniformOutput', false);
for i = 1:numel(struct_attributes)
s{i} = cell2struct(struct_attributes{i},struct_fields{i},1);
end
Now, in the end, I get a cell of stuctures that could, let's say, fulfill my requirements. The only point that I would like to improve is:
- Give each structure the name of the respective block.
Does anybody have valuable hints?
Thank you all for supporting me.
Regards,
Francesco