working closer

main
Brett 2024-11-16 18:13:17 -05:00
parent 6480c060c6
commit 45ccb3647e
9 changed files with 128 additions and 29 deletions

View File

@ -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)

View File

@ -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

View File

@ -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;
}; };
} }

View File

@ -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;

View File

@ -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);
}
} }

View File

@ -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));

View File

@ -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};
data_positions.push_back(pos);
auto color = blt::make_color(1, 1, 1); auto color = blt::make_color(1, 1, 1);
float z_index = 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;
}
} }

View File

@ -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()});
} }
} }

View File

@ -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;
} }