diff --git a/CMakeLists.txt b/CMakeLists.txt index b94938c..cc7c100 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(COSC-4P80-Assignment-3 VERSION 0.0.22) +project(COSC-4P80-Assignment-3 VERSION 0.0.23) include(FetchContent) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) @@ -14,12 +14,6 @@ FetchContent_Declare(implot FIND_PACKAGE_ARGS) FetchContent_MakeAvailable(implot) -FetchContent_Declare(matplotplusplus - GIT_REPOSITORY https://github.com/alandefreitas/matplotplusplus - GIT_TAG v1.2.1 - FIND_PACKAGE_ARGS) -FetchContent_MakeAvailable(matplotplusplus) - add_subdirectory(lib/blt-with-graphics) include_directories(include/) @@ -32,7 +26,7 @@ add_executable(COSC-4P80-Assignment-3 ${PROJECT_BUILD_FILES} ${IM_PLOT_FILES}) target_compile_options(COSC-4P80-Assignment-3 PRIVATE -Wall -Wextra -Wpedantic -Wno-comment) target_link_options(COSC-4P80-Assignment-3 PRIVATE -Wall -Wextra -Wpedantic -Wno-comment) -target_link_libraries(COSC-4P80-Assignment-3 PRIVATE BLT_WITH_GRAPHICS matplot) +target_link_libraries(COSC-4P80-Assignment-3 PRIVATE BLT_WITH_GRAPHICS) if (${ENABLE_ADDRSAN} MATCHES ON) target_compile_options(COSC-4P80-Assignment-3 PRIVATE -fsanitize=address) diff --git a/errors25.png b/errors25.png new file mode 100644 index 0000000..81b4f6d Binary files /dev/null and b/errors25.png differ diff --git a/heatmap.png b/heatmap.png new file mode 100644 index 0000000..9ef3a28 Binary files /dev/null and b/heatmap.png differ diff --git a/include/assign3/manager.h b/include/assign3/manager.h index cd398de..32f2130 100644 --- a/include/assign3/manager.h +++ b/include/assign3/manager.h @@ -94,7 +94,6 @@ namespace assign3 som = std::make_unique(motor_data.files[currently_selected_network], som_width, som_height, max_epochs, distance_function.get(), topology_function.get(), static_cast(selected_som_mode), static_cast(selected_init_type), normalize_init); - som->compute_neuron_activations(); } private: diff --git a/include/assign3/som.h b/include/assign3/som.h index dca8871..701c008 100644 --- a/include/assign3/som.h +++ b/include/assign3/som.h @@ -44,6 +44,8 @@ namespace assign3 Scalar quantization_error(); + void compute_errors(); + void compute_neuron_activations(Scalar distance = 2, Scalar activation = 0.5); void write_activations(std::ostream& out); diff --git a/plot_heatmap.py b/plot_heatmap.py new file mode 100644 index 0000000..79db893 --- /dev/null +++ b/plot_heatmap.py @@ -0,0 +1,30 @@ +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +import matplotlib +import matplotlib as mpl +import sys + +filename = sys.argv[1] +size = sys.argv[2] + +df = pd.read_csv(filename, header=None) + +height, width = df.shape + +data = df.to_numpy() + +plt.imshow(data, cmap='coolwarm_r', interpolation='nearest') + +plt.xticks(np.arange(width), np.arange(width)) +plt.yticks(np.arange(height), np.arange(height)) + +plt.xlabel('X Pos') +plt.ylabel('Y Pos') +plt.title('Heatmap of Motor Data (Bins: {})'.format(size)) + +plt.gca().invert_yaxis() + +plt.colorbar() + +plt.savefig("heatmap.png") diff --git a/plot_line_graph.py b/plot_line_graph.py new file mode 100644 index 0000000..ef4596b --- /dev/null +++ b/plot_line_graph.py @@ -0,0 +1,41 @@ +import pandas as pd +import matplotlib.pyplot as plt +import numpy as np +import sys + +file1 = sys.argv[1] +file2 = sys.argv[2] +bins = sys.argv[3] +split = sys.argv[4] + +df1 = pd.read_csv(file1) +df2 = pd.read_csv(file2) + +data1 = df1.to_numpy() +data2 = df2.to_numpy() + +y_min = np.min(data2) +y_max = np.max(data2) + +if split.lower() == "false": + fig, ax1 = plt.subplots() + + ax1.plot(data1, color='b', label='Topological Error') + ax1.set_xlabel('Epochs') + ax1.set_ylabel('Error %', color='b') + ax1.tick_params(axis='y', labelcolor='b') + ax1.set_ylim(0, 1) + #ax1.set_xlim(0, data1.size) + + ax2 = ax1.twinx() + + ax2.plot(data2, color='r', label='Quantization Error') + ax2.set_ylabel('Incorrect BMU', color='r') + ax2.tick_params(axis='y', labelcolor='r') + ax2.set_ylim(y_min, y_max) + + ax1.set_title('Topological and Quantization Error (Bins: {})'.format(bins)) + + plt.savefig("errors{}.png".format(bins)) +else: + plt.plot(data1, color='b', label='Topological Error') diff --git a/src/main.cpp b/src/main.cpp index 13187c4..d1fbc93 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,7 +10,6 @@ #include #include #include -#include using namespace assign3; @@ -78,11 +77,6 @@ void action_start_graphics(const std::vector& argv_vector) blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, destroy}.setSyncInterval(1).setMaximized(true)); } -struct activation -{ - Scalar x, y, act; -}; - struct task_t // NOLINT { data_file_t* file; @@ -101,7 +95,7 @@ struct task_t // NOLINT gaussian_function_t topology_func{}; std::vector> topological_errors{}; std::vector> quantization_errors{}; - std::vector> activations{}; + std::vector> activations{}; }; void action_test(const std::vector& argv_vector) @@ -125,7 +119,7 @@ void action_test(const std::vector& argv_vector) static blt::size_t runs = 30; - for (blt::size_t i = 0; i < std::thread::hardware_concurrency(); i++) + for (blt::size_t _ = 0; _ < std::thread::hardware_concurrency(); _++) { threads.emplace_back([&task_mutex, &tasks]() { do @@ -139,7 +133,7 @@ void action_test(const std::vector& argv_vector) tasks.pop_back(); } - for (blt::size_t i = 0; i < runs; i++) + for (blt::size_t run = 0; run < runs; run++) { gaussian_function_t func{}; auto dist = distance_function_t::from_shape(task.shape, task.width, task.height); @@ -147,13 +141,13 @@ void action_test(const std::vector& argv_vector) &task.topology_func, task.shape, task.init, false); while (som->get_current_epoch() < som->get_max_epochs()) som->train_epoch(task.initial_learn_rate); - som->compute_neuron_activations(); task.topological_errors.push_back(som->get_topological_errors()); task.quantization_errors.push_back(som->get_quantization_errors()); - std::vector acts; - for (const auto& neuron : som->get_array().get_map()) - acts.push_back({neuron.get_x(), neuron.get_y(), neuron.get_activation()}); + + std::vector acts; + for (const auto& v : som->get_array().get_map()) + acts.push_back(v.get_activation()); task.activations.emplace_back(std::move(acts)); } std::stringstream paths; @@ -170,7 +164,7 @@ void action_test(const std::vector& argv_vector) std::vector average_topological_errors; std::vector average_quantization_errors; - std::vector average_activations; + std::vector average_activations; average_topological_errors.resize(task.topological_errors.begin()->size()); average_quantization_errors.resize(task.quantization_errors.begin()->size()); @@ -184,30 +178,30 @@ void action_test(const std::vector& argv_vector) average_quantization_errors[index] += v; for (const auto& vec : task.activations) for (auto [index, v] : blt::enumerate(vec)) - average_activations[index].act += v.act; + average_activations[index] += v; - for (auto& v : average_topological_errors) - v /= static_cast(runs); - for (auto& v : average_quantization_errors) - v /= static_cast(runs); - for (auto& v : average_activations) - v.act /= static_cast(runs); + std::ofstream topological{path + "topological_avg.csv"}; + std::ofstream quantization{path + "quantization_avg.csv"}; + std::ofstream activations{path + "activations_avg.csv"}; - auto f = matplot::figure(); - f->tiledlayout(2, 1); - auto axis = f->add_axes(); - axis->hold(true); - axis->plot(matplot::linspace(0, static_cast(task.max_epochs)), average_topological_errors)->display_name("Topological Error"); - axis->title("Error"); - axis->xlabel("Epoch"); - axis->ylabel("Error"); - axis->grid(true); - axis->plot(matplot::linspace(0, static_cast(task.max_epochs)), average_quantization_errors)->display_name("Quantization Error"); - f->title("Topological and Quantization Errors, " + std::to_string(runs) + " Runs"); - - f->save((path + "errors_plot.eps"), "postscript"); - f->save((path + "errors_plot.tex"), "epslatex"); - f->save((path + "errors_plot.png"), "png"); + topological << "error\n"; + quantization << "error\n"; + for (auto [i, v] : blt::enumerate(average_topological_errors)) + { + topological << v / static_cast(runs) << '\n'; + } + for (auto [i, v] : blt::enumerate(average_quantization_errors)) + { + quantization << v / static_cast(runs) << '\n'; + } + for (auto [i, v] : blt::enumerate(average_activations)) + { + activations << v / static_cast(runs); + if (i % task.width == task.width-1) + activations << '\n'; + else + activations << ','; + } BLT_INFO("Task '%s' Complete", path.c_str()); diff --git a/src/manager.cpp b/src/manager.cpp index 9f4b974..4288741 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -242,10 +242,7 @@ namespace assign3 if (running) { if (som->get_current_epoch() < som->get_max_epochs()) - { som->train_epoch(initial_learn_rate); - som->compute_neuron_activations(); - } } diff --git a/src/som.cpp b/src/som.cpp index a6a33f7..3cdc8ca 100644 --- a/src/som.cpp +++ b/src/som.cpp @@ -34,8 +34,7 @@ namespace assign3 { for (auto& v : array.get_map()) v.randomize(std::random_device{}(), init, normalize, file); - topological_errors.push_back(topological_error()); - quantization_errors.push_back(quantization_error()); + compute_errors(); } void som_t::train_epoch(Scalar initial_learn_rate) @@ -67,8 +66,7 @@ namespace assign3 } } current_epoch++; - topological_errors.push_back(topological_error()); - quantization_errors.push_back(quantization_error()); + compute_errors(); } blt::size_t som_t::get_closest_neuron(const std::vector& data) @@ -265,9 +263,16 @@ namespace assign3 continue; incorrect++; } - + return incorrect; } + void som_t::compute_errors() + { + compute_neuron_activations(); + topological_errors.push_back(topological_error()); + quantization_errors.push_back(quantization_error()); + } + } \ No newline at end of file