From f8c931c04aa032aa4a8a860c480e5ba569f4b6ea Mon Sep 17 00:00:00 2001 From: Semyon1104 <129722895+Semyon1104@users.noreply.github.com> Date: Fri, 6 Dec 2024 20:22:19 +0300 Subject: [PATCH] sketch building graph (#155) #152 --------- Co-authored-by: AndreySorokin7 <129724280+AndreySorokin7@users.noreply.github.com> --- app/AlexNet/parser.py | 38 ++++--- app/AlexNet/reader_weights_sample.cpp | 15 ++- app/CMakeLists.txt | 2 +- app/Graph/CMakeLists.txt | 46 ++++++++ app/Graph/build.cpp | 132 ++++++++++++++++++++++ app/Graph/build.hpp | 18 +++ app/Graph/graph_build.cpp | 40 +++++++ include/Weights_Reader/reader_weights.hpp | 3 +- include/graph/graph.hpp | 1 + include/layers/Layer.hpp | 2 + include/layers/Shape.hpp | 4 + include/layers/Tensor.hpp | 1 + src/Weights_Reader/reader_weights.cpp | 15 +-- src/layers/Shape.cpp | 9 ++ test/model_read/model_read.cpp | 10 +- 15 files changed, 294 insertions(+), 42 deletions(-) create mode 100644 app/Graph/CMakeLists.txt create mode 100644 app/Graph/build.cpp create mode 100644 app/Graph/build.hpp create mode 100644 app/Graph/graph_build.cpp diff --git a/app/AlexNet/parser.py b/app/AlexNet/parser.py index eb0a953c..9389baef 100644 --- a/app/AlexNet/parser.py +++ b/app/AlexNet/parser.py @@ -4,28 +4,34 @@ import numpy as np import os +# Пути к модели и JSON файлу BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) MODEL_PATH = os.path.join(BASE_DIR, 'docs', 'AlexNet-model.h5') MODEL_DATA_PATH = os.path.join(BASE_DIR, 'docs', 'model_data_alexnet_1.json') - # Загрузка модели model = load_model(MODEL_PATH) -# Получение весов модели -weights = model.get_weights() - - -# Сохранение имен слоев и весов модели -layer_weights = {} -for layer in model.layers: +# Получение весов модели и информации о порядке слоев +layer_info = [] +for index, layer in enumerate(model.layers): layer_name = layer.name + layer_type = type(layer).__name__ # Тип слоя (например, Conv2D, Dense и т.д.) + # Преобразование весов в списки для совместимости с JSON - layer_weights[layer_name] = [w.tolist() for w in layer.get_weights()] + layer_weights = [w.tolist() for w in layer.get_weights()] + + # Сохранение информации о слое: его тип, имя и веса + layer_info.append({ + 'index': index, # Порядковый номер слоя + 'name': layer_name, + 'type': layer_type, + 'weights': layer_weights + }) # Сохранение данных в JSON файл with open(MODEL_DATA_PATH, 'w') as f: - json.dump(layer_weights, f, indent=2) # добавляем отступы для лучшей читаемости + json.dump(layer_info, f, indent=2) print(f"Model data saved to {MODEL_DATA_PATH}") @@ -34,13 +40,13 @@ loaded_model_data = json.load(f) # Преобразование данных обратно в numpy массивы -for layer_name, weights in loaded_model_data.items(): - loaded_model_data[layer_name] = [np.array(w) for w in weights] +for layer_data in loaded_model_data: + layer_data['weights'] = [np.array(w) for w in layer_data['weights']] # Вывод данных -print("Model layers and weights:") -for layer_name, weights in loaded_model_data.items(): - print("Layer:", layer_name) - for weight in weights: +print("Model layers and weights with order:") +for layer_data in loaded_model_data: + print(f"Layer {layer_data['index']} ({layer_data['type']}, {layer_data['name']}):") + for weight in layer_data['weights']: print(weight) print() diff --git a/app/AlexNet/reader_weights_sample.cpp b/app/AlexNet/reader_weights_sample.cpp index b7ba0c35..4d77c57c 100644 --- a/app/AlexNet/reader_weights_sample.cpp +++ b/app/AlexNet/reader_weights_sample.cpp @@ -6,13 +6,18 @@ int main() { std::string json_file = MODEL_PATH; json model_data = read_json(json_file); - for (const auto& layer : model_data.items()) { - const std::string& layer_name = layer.key(); - std::cout << "Layer: " << layer_name << std::endl; + for (const auto& layer_data : model_data) { + int layer_index = layer_data["index"]; + std::string layer_name = layer_data["name"]; + std::string layer_type = layer_data["type"]; + + std::cout << "Layer " << layer_index << " (" << layer_type << ", " + << layer_name << "):" << std::endl; try { - Tensor tensor = create_tensor_from_json(layer.value(), Type::kFloat); - std::cout << tensor << std::endl; + Tensor tensor = + create_tensor_from_json(layer_data["weights"], Type::kFloat); + // std::cout << tensor << std::endl; } catch (const std::exception& e) { std::cerr << "Error processing layer " << layer_name << ": " << e.what() << std::endl; diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index f9590f8b..3d88e031 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(ReaderImage) - add_subdirectory(Accuracy) add_subdirectory(AlexNet) +add_subdirectory(Graph) diff --git a/app/Graph/CMakeLists.txt b/app/Graph/CMakeLists.txt new file mode 100644 index 00000000..264c8fb8 --- /dev/null +++ b/app/Graph/CMakeLists.txt @@ -0,0 +1,46 @@ +file(MAKE_DIRECTORY "${CMAKE_SOURCE_DIR}/3rdparty/opencv/build") + +execute_process( + COMMAND ${CMAKE_COMMAND} -S "${CMAKE_SOURCE_DIR}/3rdparty/opencv" -B "${CMAKE_SOURCE_DIR}/3rdparty/opencv/build" -DCMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER} -DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DBUILD_PERF_TESTS=OFF -DBUILD_TESTS=OFF -DBUILD_opencv_apps=OFF + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/3rdparty/opencv/build" +) +execute_process( + COMMAND ${CMAKE_COMMAND} --build "${CMAKE_SOURCE_DIR}/3rdparty/opencv/build" --config "${CMAKE_BUILD_TYPE}" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/3rdparty/opencv/build" +) +set(INCLUDE_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/build.hpp") +set(SRC_FILES "${CMAKE_CURRENT_SOURCE_DIR}/build.cpp") +add_library(BuildGraph STATIC ${INCLUDE_HEADERS} ${SRC_FILES}) + +set_target_properties(BuildGraph PROPERTIES LINKER_LANGUAGE CXX) + +find_package(OpenCV REQUIRED PATHS "${CMAKE_SOURCE_DIR}/3rdparty/opencv/build") +include_directories(${OpenCV_INCLUDE_DIRS}) + +target_link_libraries(BuildGraph PUBLIC ${OpenCV_LIBS}) +target_link_libraries(BuildGraph PUBLIC reader_lib) +target_link_libraries(BuildGraph PUBLIC TBB::tbb) +target_link_libraries(BuildGraph PUBLIC layers_lib) +target_link_libraries(BuildGraph PUBLIC gtest_main) + +target_include_directories(BuildGraph PUBLIC ${CMAKE_SOURCE_DIR}/3rdparty/Json/include) + +add_executable(Graph_Build graph_build.cpp) +target_link_libraries(Graph_Build BuildGraph) + +if (WIN32) +add_custom_command(TARGET Graph_Build POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_SOURCE_DIR}/3rdparty/opencv/build/bin/${CMAKE_BUILD_TYPE}" + "${CMAKE_BINARY_DIR}/app/ReaderImage/${CMAKE_BUILD_TYPE}/") +endif() + +file(DOWNLOAD + "https://raw.githubusercontent.com/opencv/opencv/4.x/samples/data/lena.jpg" + "${CMAKE_CURRENT_BINARY_DIR}/image.jpg" + SHOW_PROGRESS + STATUS status_code + LOG log_file +) +add_definitions(-DIMAGE1_PATH="${CMAKE_CURRENT_BINARY_DIR}/image.jpg") +add_definitions(-DMODEL_PATH="${CMAKE_SOURCE_DIR}/docs/model_data_alexnet_1.json") diff --git a/app/Graph/build.cpp b/app/Graph/build.cpp new file mode 100644 index 00000000..a799558f --- /dev/null +++ b/app/Graph/build.cpp @@ -0,0 +1,132 @@ +#include "build.hpp" + +void build_graph(Tensor& input, Tensor& output) { + std::vector> layers; + + std::string json_file = MODEL_PATH; + json model_data = read_json(json_file); + + std::cout << "Loaded model data from JSON." << std::endl; + + for (const auto& layer_data : model_data) { + std::string layer_type = layer_data["type"]; + std::cout << "Processing layer of type: " << layer_type << std::endl; + + Tensor tensor = + create_tensor_from_json(layer_data["weights"], Type::kFloat); + + if (layer_type.find("Conv") != std::string::npos) { + Shape shape = tensor.get_shape(); + std::cout << "PoolingLayer shape: "; + for (size_t i = 0; i < shape.dims(); ++i) { + std::cout << shape[i] << " "; + } + std::cout << std::endl; + + Tensor tmp_values = tensor; + Tensor tmp_bias = make_tensor(tensor.get_bias()); + + auto conv_layer = + std::make_shared(1, 0, 1, tmp_values, tmp_bias); + conv_layer->setName(kConvolution); + layers.push_back(conv_layer); + std::cout << "ConvLayer added to layers." << std::endl; + } + + if (layer_type.find("Dense") != std::string::npos) { + Tensor tmp_values = tensor; + std::vector values_vector = *tensor.as(); + std::vector> values_vector_2d( + tensor.get_shape()[0], + std::vector(tensor.get_shape()[1], 0.0F)); + int q = 0; + for (size_t i = 0; i < values_vector.size(); i++) { + values_vector_2d[q][i - (q * tensor.get_shape()[1])] = values_vector[i]; + if ((i + 1) % tensor.get_shape()[1] == 0) { + q++; + } + } + std::vector> values_vector_2d_2( + tensor.get_shape()[1], + std::vector(tensor.get_shape()[0], 0.0F)); + + for (size_t i = 0; i < tensor.get_shape()[0]; ++i) { + for (size_t j = 0; j < tensor.get_shape()[1]; ++j) { + values_vector_2d_2[j][i] = values_vector_2d[i][j]; + } + } + std::vector values_vector_1d( + tensor.get_shape()[0] * tensor.get_shape()[1], 0.0F); + int index_1d = 0; + + for (size_t j = 0; j < tensor.get_shape()[1]; ++j) { + for (size_t k = 0; k < tensor.get_shape()[0]; ++k) { + values_vector_1d[index_1d++] = values_vector_2d_2[j][k]; + } + } + + Shape shape_fc({tensor.get_shape()[1], tensor.get_shape()[0]}); + Tensor values = make_tensor(values_vector_1d, shape_fc); + Tensor tmp_bias = make_tensor(tensor.get_bias()); + + auto fc_layer = std::make_shared(values, tmp_bias); + fc_layer->setName(kFullyConnected); + layers.push_back(fc_layer); + std::cout << "DenseLayer added to layers." << std::endl; + } + + if (layer_type.find("Pool") != std::string::npos) { + Shape shape = {2, 2}; + std::cout << "PoolingLayer shape: " << shape[0] << "x" << shape[1] + << std::endl; + auto pool_layer = std::make_shared(shape); + pool_layer->setName(kPooling); + layers.push_back(pool_layer); + std::cout << "PoolingLayer added to layers." << std::endl; + } + + if (layer_type.find("Flatten") != std::string::npos) { + auto flatten_layer = std::make_shared(); + flatten_layer->setName(kFlatten); + layers.push_back(flatten_layer); + std::cout << "FlattenLayer added to layers." << std::endl; + } + + if (layer_type.find("Dropout") != std::string::npos) { + auto dropout_layer = std::make_shared(0.5); + dropout_layer->setName(kDropout); + layers.push_back(dropout_layer); + std::cout << "DropOutLayer added to layers with probability 0.5." + << std::endl; + } + } + std::cout << "number of layers - " << layers.size() + 1 << std::endl; + Graph graph(static_cast(layers.size())); + InputLayer a1(kNhwc, kNchw, 1, 2); + + std::cout << "InputLayer created." << std::endl; + + graph.setInput(a1, input); + std::cout << "Input set in graph." << std::endl; + + graph.makeConnection(a1, *layers[0]); + std::cout << "Connection made between InputLayer and first layer." + << std::endl; + + for (size_t i = 0; i < layers.size() - 1; ++i) { + graph.makeConnection(*layers[i], *layers[i + 1]); + } + + graph.setOutput(*layers.back(), output); + std::cout << "Output set in graph." << std::endl; + + std::cout << "Starting inference..." << std::endl; + graph.inference(); + std::cout << "Inference completed." << std::endl; + + std::vector tmp = *output.as(); + std::vector tmp_output = softmax(*output.as()); + for (float i : tmp) { + std::cout << i << " "; + } +} \ No newline at end of file diff --git a/app/Graph/build.hpp b/app/Graph/build.hpp new file mode 100644 index 00000000..fb5a82bf --- /dev/null +++ b/app/Graph/build.hpp @@ -0,0 +1,18 @@ +#include +#include +#include +#include +#include + +#include "Weights_Reader/reader_weights.hpp" +#include "graph/graph.hpp" +#include "layers/ConvLayer.hpp" +#include "layers/DropOutLayer.hpp" +#include "layers/EWLayer.hpp" +#include "layers/FCLayer.hpp" +#include "layers/FlattenLayer.hpp" +#include "layers/InputLayer.hpp" +#include "layers/OutputLayer.hpp" +#include "layers/PoolingLayer.hpp" + +void build_graph(Tensor& input, Tensor& output); \ No newline at end of file diff --git a/app/Graph/graph_build.cpp b/app/Graph/graph_build.cpp new file mode 100644 index 00000000..7949b23f --- /dev/null +++ b/app/Graph/graph_build.cpp @@ -0,0 +1,40 @@ +#include "build.cpp" +#include "build.hpp" + +using namespace itlab_2023; + +int main() { + std::string image_path = IMAGE1_PATH; + cv::Mat image = cv::imread(image_path); + if (image.empty()) { + throw std::runtime_error("Failed to load image"); + } + cv::Mat resized_image; + cv::cvtColor(image, image, cv::COLOR_BGR2GRAY); + cv::resize(image, resized_image, cv::Size(28, 28)); + std::vector channels; + + cv::split(resized_image, channels); + + int count_pic = 1; + std::vector res(count_pic * 28 * 28); + + for (int i = 0; i < 28; ++i) { + for (int j = 0; j < 28; ++j) { + res[i * 28 + j] = channels[0].at(i, j); + } + } + Shape sh({static_cast(count_pic), 28, 28, 1}); + Tensor t = make_tensor(res, sh); + Tensor input = t; + + Shape sh1({1, 5, 5, 3}); + std::vector vec; + vec.reserve(75); + for (int i = 0; i < 75; ++i) { + vec.push_back(3); + } + Tensor output = make_tensor(vec, sh1); + + build_graph(input, output); +} diff --git a/include/Weights_Reader/reader_weights.hpp b/include/Weights_Reader/reader_weights.hpp index 34eecfd3..e31e0138 100644 --- a/include/Weights_Reader/reader_weights.hpp +++ b/include/Weights_Reader/reader_weights.hpp @@ -10,7 +10,6 @@ using namespace itlab_2023; json read_json(const std::string& filename); void extract_values_without_bias(const json& j, std::vector& values); void extract_values_from_json(const json& j, std::vector& values); -void parse_json_shape(const json& j, std::vector& shape, - size_t dim = 0); +void parse_json_shape(const json& j, std::vector& shape, size_t dim); Tensor create_tensor_from_json(const json& j, Type type); void extract_bias_from_json(const json& j, std::vector& bias); diff --git a/include/graph/graph.hpp b/include/graph/graph.hpp index 2c4800b3..e129a516 100644 --- a/include/graph/graph.hpp +++ b/include/graph/graph.hpp @@ -1,3 +1,4 @@ + #pragma once #include #include diff --git a/include/layers/Layer.hpp b/include/layers/Layer.hpp index 5c84a7c7..a7bcd85c 100644 --- a/include/layers/Layer.hpp +++ b/include/layers/Layer.hpp @@ -18,12 +18,14 @@ enum LayerType { kElementWise, kConvolution, kFullyConnected, + kFlatten, kOutput, }; class Layer { public: Layer() = default; + virtual ~Layer() = default; int getID() const { return id_; } void setID(int id) { id_ = id; } LayerType getName() const { return type_; } diff --git a/include/layers/Shape.hpp b/include/layers/Shape.hpp index be90b0cf..693a1512 100644 --- a/include/layers/Shape.hpp +++ b/include/layers/Shape.hpp @@ -2,7 +2,9 @@ #include #include +#include #include +#include #include #include @@ -37,8 +39,10 @@ class Shape { } size_t dims() const noexcept { return dims_.size(); } size_t get_index(const std::vector& coords) const; + friend std::ostream& operator<<(std::ostream& os, const Shape& shape); private: std::vector dims_; }; + } // namespace itlab_2023 diff --git a/include/layers/Tensor.hpp b/include/layers/Tensor.hpp index f6c2c71f..998bde1e 100644 --- a/include/layers/Tensor.hpp +++ b/include/layers/Tensor.hpp @@ -106,6 +106,7 @@ class Tensor { } const std::vector& get_bias() const { return bias_; } + const std::vector& get_values() const { return values_; } bool empty() const { return values_.empty(); } auto begin() { return values_.begin(); } diff --git a/src/Weights_Reader/reader_weights.cpp b/src/Weights_Reader/reader_weights.cpp index 4f811665..938da566 100644 --- a/src/Weights_Reader/reader_weights.cpp +++ b/src/Weights_Reader/reader_weights.cpp @@ -49,12 +49,10 @@ void extract_values_without_bias(const json& j, std::vector& values) { if (j.is_array() && !j.empty() && j.back().is_array()) { bias_size = j.back().size(); } - std::cout << "Temp values size: " << temp_values.size() << std::endl; std::cout << "Bias size: " << bias_size << std::endl; if (temp_values.size() >= bias_size) { values.assign(temp_values.begin(), temp_values.end() - bias_size); } - std::cout << "Values size after extraction: " << values.size() << std::endl; } void parse_json_shape(const json& j, std::vector& shape, size_t dim) { @@ -100,19 +98,10 @@ Tensor create_tensor_from_json(const json& j, Type type) { extract_values_without_bias(j, vals); std::cout << "Extracted values size: " << vals.size() << std::endl; - parse_json_shape(j, shape); - std::cout << "Parsed shape: "; - size_t expected_size = 1; - for (const auto& dim : shape) { - std::cout << dim << " "; - expected_size *= dim; - } + parse_json_shape(j, shape, 0); + std::cout << std::endl; - if (expected_size == 1 && shape.empty()) { - expected_size = 0; - } - std::cout << "Expected size: " << expected_size << std::endl; extract_bias_from_json(j, bias); std::cout << "Extracted bias size: " << bias.size() << std::endl; Shape sh(shape); diff --git a/src/layers/Shape.cpp b/src/layers/Shape.cpp index f2989c21..e2ce7d0f 100644 --- a/src/layers/Shape.cpp +++ b/src/layers/Shape.cpp @@ -19,4 +19,13 @@ size_t Shape::get_index(const std::vector& coords) const { } return res; } +std::ostream& operator<<(std::ostream& os, const Shape& shape) { + for (size_t i = 0; i < shape.dims(); ++i) { + os << shape[i]; + if (i < shape.dims() - 1) { + os << " "; + } + } + return os; +} } // namespace itlab_2023 diff --git a/test/model_read/model_read.cpp b/test/model_read/model_read.cpp index a2b8e71a..19701a09 100644 --- a/test/model_read/model_read.cpp +++ b/test/model_read/model_read.cpp @@ -68,7 +68,7 @@ TEST(ExtractValuesFromJsonTests, HandlesNestedArray) { TEST(ParseJsonShapeTests, HandlesEmptyArray) { json j = json::array({}); std::vector shape; - parse_json_shape(j, shape); + parse_json_shape(j, shape, 0); std::vector expected = {0}; EXPECT_EQ(shape, expected); } @@ -76,7 +76,7 @@ TEST(ParseJsonShapeTests, HandlesEmptyArray) { TEST(ParseJsonShapeTests, HandlesSimpleArray) { json j = json::array({{1.0, 2.0, 3.0}, {1.0, 2.0, 3.0}}); std::vector shape; - parse_json_shape(j, shape); + parse_json_shape(j, shape, 0); std::vector expected = {3}; EXPECT_EQ(shape, expected); } @@ -84,7 +84,7 @@ TEST(ParseJsonShapeTests, HandlesSimpleArray) { TEST(ParseJsonShapeTests, HandlesNestedArray) { json j = json::array({{{1.0, 2.0}, {3.0, 4.0}}, {3.0, 4.0}}); std::vector shape; - parse_json_shape(j, shape); + parse_json_shape(j, shape, 0); std::vector expected = {2, 2}; EXPECT_EQ(shape, expected); } @@ -157,7 +157,7 @@ TEST(CreateTensorFromJsonTest, SimpleTensorCheckNoBias) { TEST(CreateTensorFromJsonTest, EmptyShape) { json j = json::array({}); std::vector shape; - parse_json_shape(j, shape); + parse_json_shape(j, shape, 0); std::vector expected = {0}; EXPECT_EQ(shape, expected); } @@ -171,7 +171,7 @@ TEST(ParseJsonShapeTests, HandlesNonArrayJson) { json j = "not_an_array"; std::vector shape; - parse_json_shape(j, shape); + parse_json_shape(j, shape, 0); std::vector expected = {0}; EXPECT_EQ(shape, expected);