Skip to content

Commit

Permalink
Merge pull request #316 from deephealthproject/develop
Browse files Browse the repository at this point in the history
Fixes 1.0
  • Loading branch information
salvacarrion authored Dec 1, 2021
2 parents c0e988b + ea3f24e commit be52b54
Show file tree
Hide file tree
Showing 26 changed files with 450 additions and 131 deletions.
8 changes: 1 addition & 7 deletions cmake/eigen.CMakeLists.txt.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@ project(eigen-download NONE)
# Set base dir
SET(EP_BASE_DIR @EP_BASE_DIR@)

if(CMAKE_GENERATOR MATCHES "Visual Studio")
# Visual Studio needs a more recent version of Eigen3
# Version 3.3.90
SET(TAG ba9d18b9388acdf27a3900a4f981fab587e59b0c)
else()
SET(TAG "3.3.7") # Eigen3 must be >= 3.3.7
endif()
SET(TAG "3.4.0")

include(ExternalProject)
ExternalProject_Add(eigen
Expand Down
11 changes: 7 additions & 4 deletions docs/markdown/eddl_progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@
| Expand | 🟢️️| 🟢️️ |🟢️️ | 🟢️ | Returns a layer with singleton dimensions expanded to a larger size |
| Where | 🟢️️ | 🟢️️ | 🟢️️ | ️🔴️ | Return elements chosen from x or y depending on condition |
| Resize | 🟢️️ | 🟢️️ |🟢️️ | 🟢️️ | Resize the input image to the given size. `[height, width]` |
| Clamp / Clip | 🟢️️ | 🟢️️ |🟢️️ | 🔴️️ | Clamps all elements in input into the range `[min, max]`. |
| Repeat | 🟢️️ | 🟢️️ |🟢️️ | 🔴️️ | Repeats the elements of a tensor along the specified dimension. |


| Clamp / Clip | 🟢️️ | 🟢️️ |🟢️️ | 🟢️ | Clamps all elements in input into the range `[min, max]`. |
| Repeat | 🟢️️ | 🟢️️ |🟢️️ | 🔴️️ | Repeats the elements of a tensor along the specified dimension. (Elements in an axis can be repeated independently) |
| Tile | 🔴️️ | 🔴 |🔴️ | 🔴️️ | Repeats the elements of a tensor along the specified dimensions. |
| Round | 🔴️️ | 🔴️️ | 🔴️️ | 🔴️️ | Round of the elements of input |
| Ceil | 🔴️ | 🔴️️ | 🔴️ | 🔴️️ | Ceil of the elements of input |
| Floor | 🔴️️ | 🔴 |🔴️ | 🔴️️ | Floor of the elements of input |
| Broadcast | 🔴️️ | 🔴 |🔴️ | 🔴️️ | Produce an object that mimics broadcasting. |


## Activations
Expand Down
3 changes: 2 additions & 1 deletion docs/markdown/eddl_progress_tensor.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,9 @@ Numpy-like operations over a raw-tensor object

| Functionality | CPU | GPU | Comments |
| ------------- | ---- | ---- | -------- |
| tile | ⚫️ | ⚫️ | Construct an array by repeating A the number of times given by reps |
| repeat | 🟢️ | 🟢️️️ ️ | Repeats the elements of a tensor along the specified dimension |
| tile | 🔴️️ | 🔴️️️ ️ | Repeats the elements of a tensor along the specified dimensions |
| broadcast | 🔴️️ | 🔴️️️ ️ | Produce an object that mimics broadcasting. |


### Adding and removing elements
Expand Down
5 changes: 4 additions & 1 deletion docs/sphinx/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,7 @@

# html_sidebars = {
# '**': ['searchbox.html'],
# }
# }


github_url = "https://github.com/deephealthproject/eddl"
2 changes: 2 additions & 0 deletions docs/sphinx/source/intro/build-options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ If you decide to manually install these dependencies in your system (make sure t
- sphinx_rtd_theme==1.0.*
- sphinx-tabs==3.2.*
- breathe==4.31.*
- onnx-simplifier==0.3.*
- protobuf==3.19.*
.. note::

Expand Down
12 changes: 12 additions & 0 deletions docs/sphinx/source/intro/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ You can also install ``EDDL`` from source with cmake.
# -DCMAKE_C_COMPILER=$(which gcc-7) \
# -DCMAKE_CXX_COMPILER=$(which g++-7) \
make -j$(nproc) # The -j flag is to parallelize the compilation
make install
.. tab:: MacOS
Expand All @@ -142,6 +143,7 @@ You can also install ``EDDL`` from source with cmake.
cd build
cmake .. -DCMAKE_PREFIX_PATH=$CONDA_PREFIX -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX
make -j$(sysctl -n hw.logicalcpu) # The -j flag is to parallelize the compilation
make install
.. tab:: Windows
Expand Down Expand Up @@ -186,6 +188,16 @@ See the :doc:`build-options` section for more details about cmake options.
cmake .. -DBUILD_SUPERBUILD=ON -DBUILD_TARGET=CUDNN -DCMAKE_CUDA_COMPILER=/usr/local/cuda/bin/nvcc -DCMAKE_CXX_COMPILER=/usr/bin/g++-7
6. To update/remote the eddl environment type:

.. code:: bash
# Update existing environment
conda env update --file environment.yml --prune
# Remove environment
conda remove --name eddl --all
Including EDDL in your project
---------------------------------
Expand Down
2 changes: 2 additions & 0 deletions docs/sphinx/source/model/onnx.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ you try to "standardize" or "simplifying" using *onnx_simplifier_*.

**Installation**

This is only needed is you are not using the conda environment for the EDDL, as we ship ONNX simplifier with it.

.. code-block:: bash
pip3 install -U pip && pip3 install onnx-simplifier
Expand Down
4 changes: 3 additions & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ dependencies: # https://docs.conda.io/projects/conda-build/en/latest/resources/
- sphinx==4.3.*
- sphinx_rtd_theme==1.0.*
- sphinx-tabs==3.2.*
- breathe==4.31.*
- breathe==4.31.*
- onnx-simplifier==0.3.*
- protobuf==3.19.*
3 changes: 3 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ if(BUILD_PROTOBUF)
add_executable(app_image_classification_resnet34 "applications/1_image_classification_resnet34.cpp")
target_link_libraries(app_image_classification_resnet34 eddl)

add_executable(app_test_model "applications/app_test_model.cpp")
target_link_libraries(app_test_model eddl)


# EXAMPLES: RAW ONNX **************************************************************
add_executable(onnx_pointer "onnx/1_onnx_pointer.cpp")
Expand Down
181 changes: 181 additions & 0 deletions examples/applications/app_test_model.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@

#include <iostream>
#include "eddl/apis/eddl.h"
#include "eddl/serialization/onnx/eddl_onnx.h"

using namespace eddl;


Tensor* preprocess_input(Tensor* input, const vector<int> &target_size, bool normalize=true, bool standarize=true){
// Define preprocessing constants
auto* mean_vec = new Tensor( {0.485, 0.456, 0.406}, {3, 1}, input->device);
auto* std_vec = new Tensor( {0.229, 0.224, 0.225}, {3, 1}, input->device);

// ==========================================================================
// ====== SANITY CHECKS =====================================================
// ==========================================================================
// Check dimension. Input must be a 3D or 4D tensor
if(!(input->ndim == 3 || input->ndim == 4)){
throw std::runtime_error("A 3D or 4D tensor is expected. " + std::to_string(input->ndim) + "D tensor received.");
}

// Convert from 3D to 4D (if needed)
if(input->ndim == 3){
input->unsqueeze_(0);
}
// ==========================================================================


// ==========================================================================
// ====== NORMALIZATION =====================================================
// ==========================================================================

// Resize tensor (creates a new instance)
Tensor* new_input = input->scale(target_size); // (height, width)

// Normalization [0..1]
if(normalize){
new_input->mult_(1/255.0f);
}

// Standarization: (X-mean)/std
if(standarize){
// Tensor* mean = Tensor::broadcast(mean_vec, new_input);
// Tensor* std = Tensor::broadcast(std_vec, new_input);
// 1) [There is no broadcasting...] Repeat dimensions => Temp!
Tensor* mean = Tensor::repeat(mean_vec, target_size[0]*target_size[1], 1); mean->reshape_(new_input->shape);
Tensor* std = Tensor::repeat(std_vec, target_size[0]*target_size[1], 1); std->reshape_(new_input->shape);
new_input->sub_(mean);
new_input->div_(std);

// Free memory
delete mean;
delete std;
}
// ==========================================================================

// Free memory
delete mean_vec;
delete std_vec;

return new_input;
}


int main(int argc, char **argv) {
// ==========================================================================
// ====== SET DEFAULT VARIABLES =============================================
// ==========================================================================

// Step 0: Download the model, the classes and the image we want to classify
string image_fname = "../../examples/data/elephant.jpg";
string class_names_file = "../../examples/data/imagenet_class_names.txt";

// Image Classification
string model_path = "models/resnet34-v1-7.onnx"; // 3x224x224 // okay
// string model_path = "models/mobilenetv2-7.onnx"; // 3x224x224 // Signal: SIGSEGV (Segmentation fault)
// string model_path = "models/vgg16-7.onnx"; // 3xHxW // okay
// string model_path = "models/bvlcalexnet-3.onnx"; // 3x224x224 // The onnx node 'LRN' is not supported yet
// string model_path = "models/bvlcalexnet-12.onnx"; // 3x224x224 // The onnx node 'LRN' is not supported yet
// string model_path = "models/googlenet-3.onnx"; // 3x224x224 // The onnx node 'LRN' is not supported yet
// string model_path = "models/densenet-3.onnx"; // 3x224x224 // okay
// string model_path = "models/inception-v1-3.onnx"; // 3x224x224 // The onnx node 'LRN' is not supported yet
// string model_path = "models/efficientnet-lite4-11.onnx"; // 224x224x3 // The onnx node 'LRN' is not supported yet

// Object Detection & Image Segmentation
// string model_path = "models/tinyyolov2-7.onnx"; // 3x416x416 //Error in Add node Add. The first dimension (batch) of the constant operator must be 1, got 3 (ONNX::ImportNet) ⚠️
// string model_path = "models/ssd-10_simp.onnx"; // 3x1200x1200 // Signal: SIGSEGV (Segmentation fault)
// string model_path = "models/FasterRCNN-10_simp.onnx"; // 3xHxW // Signal: SIGSEGV (Segmentation fault)
// string model_path = "models/yolov4_simp.onnx"; // 416x416x3 // [ONNX::LOG] Detected a padding asymmetry + The onnx node 'Shape' is not supported yet
// string model_path = "models/MaskRCNN-10_simp.onnx"; // 3xHxW // Signal: SIGSEGV (Segmentation fault)
// string model_path = "models/retinanet-9.onnx"; // 3xHxW // okay
// string model_path = "models/yolov3-10_simp.onnx"; // 3x416x416 // The imported model has more than 1 input layer and the shape provided to reshape the model can only be appliedif the model has only one input layer. (ONNX::ImportNet)
// string model_path = "models/fcn-resnet50-11_simp.onnx"; // 3xHxW // The onnx node 'Shape' is not supported yet
// string model_path = "models/ResNet101-DUC-7.onnx"; // 3xHxW // okay. Error with plot

// Image Manipulation
// string model_path = "models/super-resolution-10.onnx"; // 3x224x224 // Tensors with different size (Tensor::copy)
// string model_path = "models/mosaic-9.onnx"; // 3xHxW // Error importing layer . Only "constant" mode is supported (passed "reflect"). (ONNX::ImportNet)

// Step 1: Specify input
int in_channels = 3;
int in_height = 224;
int in_width = 224;
vector<int> input_shape = {in_channels, in_height, in_width};
vector<int> dimensions_order = {0, 1, 2, 3};
// ==========================================================================


// ==========================================================================
// ====== LOAD ONNX MODEL ===================================================
// ==========================================================================

// Import ONNX model
std::cout << "Importing ONNX..." << std::endl;
Net *net = import_net_from_onnx_file(model_path, input_shape);

// ==========================================================================
// Print and plot our model
net->summary();
net->plot("mymodel.pdf");

// ==========================================================================
// ====== APPLY POSTPROCESSING ==============================================
// ==========================================================================

// Step 4 (optional): Add a softmax layer to get probabilities directly from the model, since it
// does not include the softmax layer.
// layer input = net->lin[0]; // getLayer(net,"input_layer_name");
// layer output = net->lout[0]; // getLayer(net,"output_layer_name");
// layer new_output = Softmax(output);
//
// // Create model
// net = Model({input},{new_output});
// ==========================================================================

// ==========================================================================
// ====== COMPILE MODEL =====================================================
// ==========================================================================

// Build model
build(net,
adam(0.001f), // Optimizer (not used for prediction)
{"softmax_cross_entropy"},// Losses (not used for prediction)
{"categorical_accuracy"}, // Metrics (not used for prediction)
CS_GPU({1}), // Use one GPU
false // Disable model initialization, since we want to use the onnx weights
);
// ==========================================================================


// ==========================================================================
// ====== INFERENCE =========================================================
// ==========================================================================
// Load test image
Tensor *image = Tensor::load(image_fname);

// Step 3: Preprocess input. (Look up the preprocessing required at the model's page)
Tensor* image_preprocessed = preprocess_input(image, {in_height, in_width});
image_preprocessed->permute_(dimensions_order);

// Predict image. Returns a vector of tensors (here one).
vector<Tensor*> outputs = net->predict({image_preprocessed});
// ==========================================================================


// ==========================================================================
// ====== PRINT TOP K CLASSES ===============================================
// ==========================================================================
// Read imagenet class names from txt file
std::cout << "Reading imagenet class names..." << std::endl;
vector<string> class_names = eddl::read_txt_file(class_names_file);

// Print top K predictions
int top_k = 5;
std::cout << "Top " << top_k << " predictions:" << std::endl;
std::cout << eddl::get_topk_predictions(outputs[0], class_names, top_k) << std::endl;
// ==========================================================================

return 0;
}

5 changes: 4 additions & 1 deletion examples/test_internals/onnx/test_onnx_auxiliar_layers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ int main(int argc, char **argv) {
layer l = in; // Aux var

l = Reshape(l, {1, 28, 28});
l = Repeat(l, 3, 0);
l = Repeat(l, 2, 1);
l = Repeat(l, 2, 2);
l = Pad(l, {1, 1, 1, 1}, 0.0);
l = MaxPool2D(ReLu(Conv2D(l, 30, {3, 3}, {1, 1}, "valid")), {3, 3}, {1, 1}, "same");

Expand Down Expand Up @@ -194,4 +197,4 @@ int main(int argc, char **argv) {
return EXIT_SUCCESS;
else
return EXIT_FAILURE;
}
}
29 changes: 29 additions & 0 deletions include/eddl/serialization/onnx/layers/core/repeat_onnx.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#if defined(cPROTO)
#ifndef EDDL_REPEAT_ONNX_H
#define EDDL_REPEAT_ONNX_H
#include "eddl/serialization/onnx/onnx.pb.h"
#include "eddl/serialization/onnx/utils_onnx.h"
#include "eddl/layers/core/layer_core.h"

/*
* ONNX IMPORT
*/

// OPSET: 13, 6
Layer* build_repeat_layer(onnx::NodeProto *node,
map<string, onnx::NodeProto *> &constant_node_map,
map<string, vector<float>> &map_init_values,
map<string, Layer *> &output_node_map,
LOG_LEVEL log_level,
int dev,
int mem);

/*
* ONNX EXPORT
*/

// OPSET: 13, 6
void build_tile_node(LRepeat *layer, onnx::GraphProto *graph);

#endif // EDDL_REPEAT_ONNX_H
#endif // cPROTO
3 changes: 2 additions & 1 deletion include/eddl/serialization/onnx/layers/layers_onnx.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ enum ONNX_LAYERS {
SLICE, // OPSET: 13, 11, 10
SPLIT, // OPSET: 13, 11, 2
EXPAND, // OPSET: 13, 8
CONSTANT // OPSET: 13, 12, 11, 9, 1
CONSTANT, // OPSET: 13, 12, 11, 9, 1
REPEAT // OPSET: 13, 6
};

map<string, ONNX_LAYERS> create_enum_map();
Expand Down
12 changes: 8 additions & 4 deletions include/eddl/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,27 @@ bool pathExists(const std::string &s);

string get_parent_dir(const string& fname);

string replace_str(const string& value, const string& oldvalue, const string& newvalue);

string normalize_layer_name(const string& value);

vector<int> compute_squeeze(vector<int> shape, int axis, bool ignore_batch=false);
vector<int> compute_unsqueeze(vector<int> shape, int axis, bool ignore_batch=false);

vector<int> address2indices(unsigned int address, const vector<int>& shape, const vector<int>& strides);
vector<int> address2indices(int address, const vector<int>& shape, const vector<int>& strides);
unsigned int indices2address(const vector<int>& indices, const vector<int>& strides);

// https://isocpp.org/wiki/faq/inline-functions#inline-member-fns
inline int fast_indices2address(const unsigned int* indices, const unsigned int* strides, unsigned int ndim){
unsigned int address = 0;
inline int fast_indices2address(const int* indices, const int* strides, int ndim){
int address = 0;
for (int i=0; i< ndim; i++){
address += indices[i] * strides[i];
}
return address;
}


inline void fast_address2indices( int address, unsigned int* indices, const unsigned int* shape, const unsigned int* strides, unsigned int ndim){
inline void fast_address2indices( int address, int* indices, const int* shape, const int* strides, int ndim){
for(int i=0; i<ndim; i++) {
indices[i] = address / strides[i] % shape[i];
}
Expand Down
Loading

0 comments on commit be52b54

Please sign in to comment.