working closer
parent
6480c060c6
commit
45ccb3647e
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.25)
|
cmake_minimum_required(VERSION 3.25)
|
||||||
project(COSC-4P80-Assignment-3 VERSION 0.0.17)
|
project(COSC-4P80-Assignment-3 VERSION 0.0.18)
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
|
|
||||||
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
|
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
|
||||||
|
|
|
@ -65,6 +65,18 @@ namespace assign3
|
||||||
[[nodiscard]] Scalar distance(blt::span<const Scalar> x, blt::span<const Scalar> y) const final;
|
[[nodiscard]] Scalar distance(blt::span<const Scalar> x, blt::span<const Scalar> y) const final;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct toroidal_euclidean_distance_function_t : public distance_function_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
toroidal_euclidean_distance_function_t(blt::i32 width, blt::i32 height): width(width), height(height)
|
||||||
|
{}
|
||||||
|
|
||||||
|
[[nodiscard]] Scalar distance(blt::span<const Scalar> x, blt::span<const Scalar> y) const final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
blt::i32 width, height;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //COSC_4P80_ASSIGNMENT_3_FUNCTIONS_H
|
#endif //COSC_4P80_ASSIGNMENT_3_FUNCTIONS_H
|
||||||
|
|
|
@ -84,6 +84,8 @@ namespace assign3
|
||||||
|
|
||||||
std::vector<float> get_neuron_activations(const data_file_t& file);
|
std::vector<float> get_neuron_activations(const data_file_t& file);
|
||||||
|
|
||||||
|
static std::vector<float> normalize_data(const std::vector<float>& data);
|
||||||
|
|
||||||
void draw_som(neuron_render_info_t info, const std::function<blt::vec4(render_data_t)>& color_func);
|
void draw_som(neuron_render_info_t info, const std::function<blt::vec4(render_data_t)>& color_func);
|
||||||
|
|
||||||
void draw_debug(const data_file_t& file);
|
void draw_debug(const data_file_t& file);
|
||||||
|
@ -116,9 +118,14 @@ namespace assign3
|
||||||
|
|
||||||
int currently_selected_network = 0;
|
int currently_selected_network = 0;
|
||||||
bool debug_mode = false;
|
bool debug_mode = false;
|
||||||
|
bool draw_colors = true;
|
||||||
|
bool draw_data_lines = false;
|
||||||
bool running = false;
|
bool running = false;
|
||||||
int debug_state = 0;
|
int debug_state = 0;
|
||||||
int selected_data_point = 0;
|
int selected_data_point = 0;
|
||||||
|
|
||||||
|
float requested_activation = 0.5;
|
||||||
|
float at_distance_measurement = 2;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace assign3
|
||||||
|
|
||||||
neuron_t& update(const std::vector<Scalar>& new_data, Scalar dist, Scalar eta);
|
neuron_t& update(const std::vector<Scalar>& new_data, Scalar dist, Scalar eta);
|
||||||
|
|
||||||
static Scalar distance(const neuron_t& n1, const neuron_t& n2);
|
static Scalar distance(distance_function_t* dist_func, const neuron_t& n1, const neuron_t& n2);
|
||||||
|
|
||||||
[[nodiscard]] Scalar dist(const std::vector<Scalar>& X) const;
|
[[nodiscard]] Scalar dist(const std::vector<Scalar>& X) const;
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <assign3/functions.h>
|
#include <assign3/functions.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include "blt/iterator/zip.h"
|
#include "blt/iterator/zip.h"
|
||||||
|
#include <blt/std/assert.h>
|
||||||
|
|
||||||
namespace assign3
|
namespace assign3
|
||||||
{
|
{
|
||||||
|
@ -43,4 +44,14 @@ namespace assign3
|
||||||
}
|
}
|
||||||
return std::sqrt(dist);
|
return std::sqrt(dist);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Scalar toroidal_euclidean_distance_function_t::distance(blt::span<const Scalar> x, blt::span<const Scalar> y) const
|
||||||
|
{
|
||||||
|
BLT_ASSERT(x.size() == 2 && y.size() == 2);
|
||||||
|
Scalar x_diff = x[0] - y[0];
|
||||||
|
Scalar y_diff = x[1] - y[1];
|
||||||
|
Scalar x_min = std::min(x_diff, static_cast<Scalar>(width) - x_diff);
|
||||||
|
Scalar y_min = std::min(y_diff, static_cast<Scalar>(height) - y_diff);
|
||||||
|
return std::sqrt(x_min * x_min + y_min * y_min);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -58,6 +58,8 @@ int main(int argc, const char** argv)
|
||||||
auto args = parser.parse_args(argc, argv);
|
auto args = parser.parse_args(argc, argv);
|
||||||
|
|
||||||
data.files = assign3::data_file_t::load_data_files_from_path(args.get<std::string>("file"));
|
data.files = assign3::data_file_t::load_data_files_from_path(args.get<std::string>("file"));
|
||||||
|
for (auto& v : data.files)
|
||||||
|
v = v.normalize();
|
||||||
data.update();
|
data.update();
|
||||||
|
|
||||||
blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, destroy}.setSyncInterval(1).setMaximized(true));
|
blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, destroy}.setSyncInterval(1).setMaximized(true));
|
||||||
|
|
108
src/manager.cpp
108
src/manager.cpp
|
@ -20,6 +20,7 @@
|
||||||
#include <blt/math/log_util.h>
|
#include <blt/math/log_util.h>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <implot.h>
|
#include <implot.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
static void HelpMarker(const std::string& desc)
|
static void HelpMarker(const std::string& desc)
|
||||||
{
|
{
|
||||||
|
@ -38,6 +39,23 @@ const char* get_selection_string(void* user_data, int selection)
|
||||||
return (reinterpret_cast<std::string*>(user_data))[selection].c_str();
|
return (reinterpret_cast<std::string*>(user_data))[selection].c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<assign3::Scalar> rotate90Clockwise(const std::vector<assign3::Scalar>& input, int width, int height)
|
||||||
|
{
|
||||||
|
std::vector<assign3::Scalar> rotated(width * height);
|
||||||
|
|
||||||
|
for (int row = 0; row < height; ++row)
|
||||||
|
{
|
||||||
|
for (int col = 0; col < width; ++col)
|
||||||
|
{
|
||||||
|
int newRow = col;
|
||||||
|
int newCol = height - row - 1;
|
||||||
|
rotated[newRow * height + newCol] = input[row * width + col];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rotated;
|
||||||
|
}
|
||||||
|
|
||||||
namespace assign3
|
namespace assign3
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -142,7 +160,9 @@ namespace assign3
|
||||||
{
|
{
|
||||||
case debug_type::DATA_POINT:
|
case debug_type::DATA_POINT:
|
||||||
{
|
{
|
||||||
auto current_data_file = motor_data.files[currently_selected_network].normalize();
|
ImGui::Checkbox("Data Type Color", &draw_colors);
|
||||||
|
ImGui::Checkbox("Data Lines", &draw_data_lines);
|
||||||
|
auto current_data_file = motor_data.files[currently_selected_network];
|
||||||
std::vector<std::string> names;
|
std::vector<std::string> names;
|
||||||
for (const auto& [i, v] : blt::enumerate(current_data_file.data_points))
|
for (const auto& [i, v] : blt::enumerate(current_data_file.data_points))
|
||||||
names.push_back("#" + std::to_string(i) + " (" + (v.is_bad ? "Bad)" : "Good)"));
|
names.push_back("#" + std::to_string(i) + " (" + (v.is_bad ? "Bad)" : "Good)"));
|
||||||
|
@ -160,17 +180,34 @@ namespace assign3
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
|
auto current_data_file = motor_data.files[currently_selected_network];
|
||||||
|
auto closest_type = get_neuron_activations(current_data_file);
|
||||||
|
|
||||||
|
if (ImGui::Begin("Plots & Data"))
|
||||||
|
{
|
||||||
|
ImPlot::SetNextAxesLimits(0, som_width, 0, som_height, ImPlotCond_Always);
|
||||||
|
if (ImPlot::BeginPlot("Activations", ImVec2(-1, 0), ImPlotFlags_NoInputs))
|
||||||
|
{
|
||||||
|
auto rev = rotate90Clockwise(closest_type, som_width, som_height);
|
||||||
|
// auto rev = closest_type;
|
||||||
|
// std::reverse(rev.begin(), rev.end());
|
||||||
|
ImPlot::PlotHeatmap("##data_map", rev.data(), som_height, som_width, 0, 0, "%.1f", ImPlotPoint(0, 0),
|
||||||
|
ImPlotPoint(som_width, som_height), ImPlotHeatmapFlags_ColMajor);
|
||||||
|
ImPlot::EndPlot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
|
||||||
if (running)
|
if (running)
|
||||||
{
|
{
|
||||||
if (som->get_current_epoch() < som->get_max_epochs())
|
if (som->get_current_epoch() < som->get_max_epochs())
|
||||||
som->train_epoch(initial_learn_rate, topology_function.get());
|
som->train_epoch(initial_learn_rate, topology_function.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto current_data_file = motor_data.files[currently_selected_network].normalize();
|
|
||||||
|
|
||||||
if (!debug_mode)
|
if (!debug_mode)
|
||||||
{
|
{
|
||||||
auto closest_type = get_neuron_activations(current_data_file);
|
closest_type = normalize_data(closest_type);
|
||||||
draw_som(neuron_render_info_t{}.set_base_pos({370, 145}).set_neuron_scale(120).set_neuron_padding({5, 5}),
|
draw_som(neuron_render_info_t{}.set_base_pos({370, 145}).set_neuron_scale(120).set_neuron_padding({5, 5}),
|
||||||
[&closest_type](render_data_t context) {
|
[&closest_type](render_data_t context) {
|
||||||
auto type = closest_type[context.index];
|
auto type = closest_type[context.index];
|
||||||
|
@ -207,27 +244,20 @@ namespace assign3
|
||||||
closest_type.clear();
|
closest_type.clear();
|
||||||
closest_type.resize(som->get_array().get_map().size());
|
closest_type.resize(som->get_array().get_map().size());
|
||||||
|
|
||||||
|
|
||||||
for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
|
for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
|
||||||
{
|
{
|
||||||
auto half = som->find_closest_neighbour_distance(i);
|
auto half = som->find_closest_neighbour_distance(i) / at_distance_measurement;
|
||||||
auto scale = topology_function->scale(half * 0.5f, 0.5);
|
auto scale = topology_function->scale(half, requested_activation);
|
||||||
for (const auto& data : file.data_points)
|
for (const auto& data : file.data_points)
|
||||||
{
|
{
|
||||||
auto dist = v.dist(data.bins);
|
auto ds = topology_function->call(v.dist(data.bins), scale);
|
||||||
auto ds = topology_function->call(dist, scale);
|
|
||||||
if (data.is_bad)
|
if (data.is_bad)
|
||||||
closest_type[i] -= ds;
|
closest_type[i] -= ds;
|
||||||
else
|
else
|
||||||
closest_type[i] += ds;
|
closest_type[i] += ds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto min_act = *std::min_element(closest_type.begin(), closest_type.end());
|
|
||||||
auto max_act = *std::max_element(closest_type.begin(), closest_type.end());
|
|
||||||
for (auto& v : closest_type)
|
|
||||||
{
|
|
||||||
auto n = 2 * (v - min_act) / (max_act - min_act) - 1;
|
|
||||||
v = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
return closest_type;
|
return closest_type;
|
||||||
}
|
}
|
||||||
|
@ -239,24 +269,41 @@ namespace assign3
|
||||||
case debug_type::DATA_POINT:
|
case debug_type::DATA_POINT:
|
||||||
{
|
{
|
||||||
std::vector<blt::vec2> data_positions;
|
std::vector<blt::vec2> data_positions;
|
||||||
|
std::vector<blt::vec2> neuron_positions;
|
||||||
for (const auto& [i, v] : blt::enumerate(file.data_points))
|
for (const auto& [i, v] : blt::enumerate(file.data_points))
|
||||||
{
|
{
|
||||||
auto pos = som->get_topological_position(v.bins) * 120 + blt::vec2{370, 145};
|
auto pos = som->get_topological_position(v.bins) * 120 + blt::vec2{370, 145};
|
||||||
auto color = blt::make_color(1,1,1);
|
data_positions.push_back(pos);
|
||||||
float z_index = 1;
|
auto color = blt::make_color(1, 1, 1);
|
||||||
|
float z_index = 2;
|
||||||
|
if (draw_colors)
|
||||||
|
{
|
||||||
|
if (v.is_bad)
|
||||||
|
color = blt::make_color(1, 0, 0);
|
||||||
|
else
|
||||||
|
color = blt::make_color(0, 1, 0);
|
||||||
|
}
|
||||||
if (i == static_cast<blt::size_t>(selected_data_point))
|
if (i == static_cast<blt::size_t>(selected_data_point))
|
||||||
|
{
|
||||||
|
if (!draw_colors)
|
||||||
{
|
{
|
||||||
color = blt::make_color(1, 0, 1);
|
color = blt::make_color(1, 0, 1);
|
||||||
z_index = 2;
|
z_index = 3;
|
||||||
}
|
}
|
||||||
br2d.drawRectangleInternal(color, blt::gfx::rectangle2d_t{pos, blt::vec2{8,8}}, z_index);
|
}
|
||||||
|
br2d.drawRectangleInternal(color, blt::gfx::rectangle2d_t{pos, blt::vec2{8, 8}}, z_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& data_point = file.data_points[selected_data_point];
|
const auto& data_point = file.data_points[selected_data_point];
|
||||||
auto closest_type = get_neuron_activations(file);
|
auto closest_type = normalize_data(get_neuron_activations(file));
|
||||||
draw_som(neuron_render_info_t{}.set_base_pos({370, 145}).set_neuron_scale(120).set_neuron_padding({0, 0}),
|
draw_som(neuron_render_info_t{}.set_base_pos({370, 145}).set_neuron_scale(120).set_neuron_padding({0, 0}),
|
||||||
[this, &data_point, &closest_type](render_data_t context) {
|
[this, &neuron_positions, &data_point, &closest_type](render_data_t context) {
|
||||||
auto& text = fr2d.render_text(std::to_string(context.neuron.dist(data_point.bins)), 18).setColor(0.2, 0.2, 0.8);
|
auto half = som->find_closest_neighbour_distance(context.index) / at_distance_measurement;
|
||||||
|
auto scale = topology_function->scale(half, requested_activation);
|
||||||
|
auto ds = topology_function->call(context.neuron.dist(data_point.bins), scale);
|
||||||
|
|
||||||
|
neuron_positions.push_back(context.neuron_padded);
|
||||||
|
auto& text = fr2d.render_text(std::to_string(ds), 18).setColor(0.2, 0.2, 0.8);
|
||||||
auto text_width = text.getAssociatedText().getTextWidth();
|
auto text_width = text.getAssociatedText().getTextWidth();
|
||||||
auto text_height = text.getAssociatedText().getTextHeight();
|
auto text_height = text.getAssociatedText().getTextHeight();
|
||||||
text.setPosition(context.neuron_padded - blt::vec2{text_width / 2.0f, text_height / 2.0f}).setZIndex(1);
|
text.setPosition(context.neuron_padded - blt::vec2{text_width / 2.0f, text_height / 2.0f}).setZIndex(1);
|
||||||
|
@ -264,6 +311,12 @@ namespace assign3
|
||||||
auto type = closest_type[context.index];
|
auto type = closest_type[context.index];
|
||||||
return type >= 0 ? blt::make_color(0, type, 0) : blt::make_color(-type, 0, 0);
|
return type >= 0 ? blt::make_color(0, type, 0) : blt::make_color(-type, 0, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (draw_data_lines)
|
||||||
|
{
|
||||||
|
for (const auto& neuron : neuron_positions)
|
||||||
|
br2d.drawLineInternal(blt::make_color(1, 1, 0), blt::gfx::line2d_t{neuron, data_positions[selected_data_point]}, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case debug_type::DISTANCE:
|
case debug_type::DISTANCE:
|
||||||
|
@ -273,4 +326,17 @@ namespace assign3
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<float> renderer_t::normalize_data(const std::vector<float>& data)
|
||||||
|
{
|
||||||
|
std::vector<float> return_data;
|
||||||
|
auto min_act = *std::min_element(data.begin(), data.end());
|
||||||
|
auto max_act = *std::max_element(data.begin(), data.end());
|
||||||
|
for (auto& v : data)
|
||||||
|
{
|
||||||
|
auto n = 2 * (v - min_act) / (max_act - min_act) - 1;
|
||||||
|
return_data.push_back(n);
|
||||||
|
}
|
||||||
|
return return_data;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -56,9 +56,8 @@ namespace assign3
|
||||||
}
|
}
|
||||||
|
|
||||||
// distance between two neurons, in 2d
|
// distance between two neurons, in 2d
|
||||||
Scalar neuron_t::distance(const neuron_t& n1, const neuron_t& n2)
|
Scalar neuron_t::distance(distance_function_t* dist_func, const neuron_t& n1, const neuron_t& n2)
|
||||||
{
|
{
|
||||||
euclidean_distance_function_t dist_func;
|
return dist_func->distance({n1.get_x(), n1.get_y()}, {n2.get_x(), n2.get_y()});
|
||||||
return dist_func.distance({n1.get_x(), n1.get_y()}, {n2.get_x(), n2.get_y()});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -57,7 +57,8 @@ namespace assign3
|
||||||
{
|
{
|
||||||
if (i == v0_idx)
|
if (i == v0_idx)
|
||||||
continue;
|
continue;
|
||||||
auto dist = basis_func->call(neuron_t::distance(v0, n), time_ratio * scale);
|
toroidal_euclidean_distance_function_t dist_func{static_cast<blt::i32>(array.get_width()), static_cast<blt::i32>(array.get_height())};
|
||||||
|
auto dist = basis_func->call(neuron_t::distance(&dist_func, v0, n), time_ratio * scale);
|
||||||
n.update(current_data.bins, dist, eta);
|
n.update(current_data.bins, dist, eta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,11 +84,12 @@ namespace assign3
|
||||||
|
|
||||||
Scalar som_t::find_closest_neighbour_distance(blt::size_t v0)
|
Scalar som_t::find_closest_neighbour_distance(blt::size_t v0)
|
||||||
{
|
{
|
||||||
|
toroidal_euclidean_distance_function_t dist_func{static_cast<blt::i32>(array.get_width()), static_cast<blt::i32>(array.get_height())};
|
||||||
Scalar distance_min = std::numeric_limits<Scalar>::max();
|
Scalar distance_min = std::numeric_limits<Scalar>::max();
|
||||||
for (const auto& [i, n] : blt::enumerate(array.get_map()))
|
for (const auto& [i, n] : blt::enumerate(array.get_map()))
|
||||||
{
|
{
|
||||||
if (i != v0)
|
if (i != v0)
|
||||||
distance_min = std::min(distance_min, neuron_t::distance(array.get_map()[v0], n));
|
distance_min = std::min(distance_min, neuron_t::distance(&dist_func, array.get_map()[v0], n));
|
||||||
}
|
}
|
||||||
return distance_min;
|
return distance_min;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue