hey almost done

main
Brett 2024-11-18 16:49:56 -05:00
parent 1e3ed0755a
commit f84919ccb5
11 changed files with 153 additions and 35 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.19) project(COSC-4P80-Assignment-3 VERSION 0.0.20)
include(FetchContent) include(FetchContent)
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)

5
build_emscripten.sh Executable file
View File

@ -0,0 +1,5 @@
#!bash
mkdir cmake-build-emscript
emcmake cmake -DCMAKE_BUILD_TYPE=Release -S ./ -B ./cmake-build-emscript
emmake cmake --build ./cmake-build-emscript -j 32
scp ./cmake-build-emscript/COSC-4P80-Assignment-3.* administrator@supercomputer:/var/www/tpgc.me/playground/self_organizing_maps/motors_galore/

View File

@ -44,7 +44,8 @@ namespace assign3
case shape_t::GRID_OFFSET_WRAP: case shape_t::GRID_OFFSET_WRAP:
for (blt::size_t j = 0; j < height; j++) for (blt::size_t j = 0; j < height; j++)
for (blt::size_t i = 0; i < width; i++) for (blt::size_t i = 0; i < width; i++)
map.emplace_back(dimensions, (j % 2 == 0 ? static_cast<Scalar>(i) : static_cast<Scalar>(i) + 0.5f), j); map.emplace_back(dimensions, (j % 2 == 0 ? static_cast<Scalar>(i) : static_cast<Scalar>(i) + 0.5f),
static_cast<Scalar>(static_cast<double>(j) * (std::sqrt(3) / 2.0)));
break; break;
} }
} }

View File

@ -39,13 +39,13 @@ namespace assign3
}; };
inline std::array<std::string, 4> shape_names{ inline std::array<std::string, 4> shape_names{
"Grid", "Grid",
"Edge Wrapped Grid", "Edge Wrapped Grid",
"Honey Comb Grid", "Honey Comb Grid",
"Edge Wrapped Honey Comb" "Edge Wrapped Honey Comb"
}; };
enum class debug_type enum class debug_t
{ {
DATA_POINT, DATA_POINT,
DISTANCE DISTANCE
@ -55,6 +55,25 @@ namespace assign3
"Distance to Datapoint", "Distance to Datapoint",
"Distance to Neighbours" "Distance to Neighbours"
}; };
enum class init_t
{
COMPLETELY_RANDOM,
RANDOM_DATA,
SAMPLED_DATA
};
inline std::array<std::string, 3> init_names{
"Completely Random",
"Random Data Based",
"Sample Based"
};
inline std::array<std::string, 3> init_helps{
"Initializes weights randomly between -1 and 1",
"Find min and max of each data element, then initialize weights between that range",
"Initialize weights based on the input data"
};
} }
#endif //COSC_4P80_ASSIGNMENT_3_FWDECL_H #endif //COSC_4P80_ASSIGNMENT_3_FWDECL_H

View File

@ -107,7 +107,8 @@ namespace assign3
} }
error_plotting.clear(); error_plotting.clear();
som = std::make_unique<som_t>(motor_data.files[currently_selected_network], som_width, som_height, max_epochs, som = std::make_unique<som_t>(motor_data.files[currently_selected_network], som_width, som_height, max_epochs,
distance_function.get(), static_cast<shape_t>(selected_som_mode)); distance_function.get(), static_cast<shape_t>(selected_som_mode),
static_cast<init_t>(selected_init_type), normalize_init);
error_plotting.push_back(som->topological_error(motor_data.files[currently_selected_network])); error_plotting.push_back(som->topological_error(motor_data.files[currently_selected_network]));
} }
@ -129,12 +130,15 @@ namespace assign3
int currently_selected_network = 0; int currently_selected_network = 0;
int selected_som_mode = 0; int selected_som_mode = 0;
int selected_init_type = 0;
bool normalize_init = false;
bool debug_mode = false; bool debug_mode = false;
bool draw_colors = true; bool draw_colors = true;
bool draw_data_lines = false; 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;
int selected_neuron = 0;
float requested_activation = 0.5; float requested_activation = 0.5;
float at_distance_measurement = 2; float at_distance_measurement = 2;

View File

@ -23,6 +23,7 @@
#include <assign3/fwdecl.h> #include <assign3/fwdecl.h>
#include "blt/std/types.h" #include "blt/std/types.h"
#include <assign3/functions.h> #include <assign3/functions.h>
#include <assign3/file.h>
namespace assign3 namespace assign3
{ {
@ -34,7 +35,7 @@ namespace assign3
data.resize(dimensions); data.resize(dimensions);
} }
neuron_t& randomize(blt::size_t seed); neuron_t& randomize(blt::size_t seed, init_t init, bool normalize, const data_file_t& file);
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);

View File

@ -29,7 +29,8 @@ namespace assign3
class som_t class som_t
{ {
public: public:
som_t(const data_file_t& file, blt::size_t width, blt::size_t height, blt::size_t max_epochs, distance_function_t* dist_func, shape_t shape); som_t(const data_file_t& file, blt::size_t width, blt::size_t height, blt::size_t max_epochs, distance_function_t* dist_func,
shape_t shape, init_t init, bool normalize);
blt::size_t get_closest_neuron(const std::vector<Scalar>& data); blt::size_t get_closest_neuron(const std::vector<Scalar>& data);

View File

@ -48,8 +48,8 @@ namespace assign3
Scalar toroidal_euclidean_distance_function_t::distance(blt::span<const Scalar> x, blt::span<const Scalar> y) const 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); BLT_ASSERT(x.size() == 2 && y.size() == 2);
Scalar x_diff = x[0] - y[0]; Scalar x_diff = std::abs(x[0] - y[0]);
Scalar y_diff = x[1] - y[1]; Scalar y_diff = std::abs(x[1] - y[1]);
Scalar x_min = std::min(x_diff, static_cast<Scalar>(width) - x_diff); 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); Scalar y_min = std::min(y_diff, static_cast<Scalar>(height) - y_diff);
return std::sqrt(x_min * x_min + y_min * y_min); return std::sqrt(x_min * x_min + y_min * y_min);

View File

@ -144,10 +144,16 @@ namespace assign3
ImGui::SetNextItemOpen(true, ImGuiCond_Appearing); ImGui::SetNextItemOpen(true, ImGuiCond_Appearing);
if (ImGui::CollapsingHeader("SOM Settings")) if (ImGui::CollapsingHeader("SOM Settings"))
{ {
ImGui::Text("Network Shape"); ImGui::SeparatorText("Network Shape");
if (ImGui::ListBox("##NetworkShape", &selected_som_mode, get_selection_string, shape_names.data(), if (ImGui::ListBox("##NetworkShape", &selected_som_mode, get_selection_string, shape_names.data(),
static_cast<int>(shape_names.size()))) static_cast<int>(shape_names.size())))
regenerate_network(); regenerate_network();
ImGui::SeparatorText("Init Type");
if (ImGui::ListBox("##InitType", &selected_init_type, get_selection_string, init_names.data(), static_cast<int>(init_names.size())))
regenerate_network();
ImGui::TextWrapped("Help: %s", init_helps[selected_init_type].c_str());
ImGui::Separator();
ImGui::Checkbox("Normalize Init Data", &normalize_init);
if (ImGui::InputInt("SOM Width", &som_width) || ImGui::InputInt("SOM Height", &som_height) || if (ImGui::InputInt("SOM Width", &som_width) || ImGui::InputInt("SOM Height", &som_height) ||
ImGui::InputInt("Max Epochs", &max_epochs)) ImGui::InputInt("Max Epochs", &max_epochs))
regenerate_network(); regenerate_network();
@ -160,14 +166,15 @@ namespace assign3
if (debug_mode) if (debug_mode)
{ {
ImGui::ListBox("##DebugStateSelect", &debug_state, get_selection_string, debug_names.data(), debug_names.size()); ImGui::ListBox("##DebugStateSelect", &debug_state, get_selection_string, debug_names.data(), debug_names.size());
switch (static_cast<debug_type>(debug_state)) switch (static_cast<debug_t>(debug_state))
{ {
case debug_type::DATA_POINT: case debug_t::DATA_POINT:
{ {
ImGui::Checkbox("Data Type Color", &draw_colors); ImGui::Checkbox("Data Type Color", &draw_colors);
ImGui::Checkbox("Data Lines", &draw_data_lines); ImGui::Checkbox("Data Lines", &draw_data_lines);
auto current_data_file = motor_data.files[currently_selected_network]; auto current_data_file = motor_data.files[currently_selected_network];
std::vector<std::string> names; static std::vector<std::string> names;
names.clear();
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)"));
ImGui::Text("Select Data Point"); ImGui::Text("Select Data Point");
@ -175,8 +182,15 @@ namespace assign3
static_cast<int>(names.size())); static_cast<int>(names.size()));
} }
break; break;
case debug_type::DISTANCE: case debug_t::DISTANCE:
{
static std::vector<std::string> names;
names.clear();
for (blt::size_t i = 0; i < som->get_array().get_map().size(); i++)
names.push_back("Neuron " + std::to_string(i));
ImGui::Text("Select Neuron");
ImGui::ListBox("##SelectNeuron", &selected_neuron, get_selection_string, names.data(), static_cast<int>(names.size()));
}
break; break;
} }
} }
@ -262,9 +276,9 @@ namespace assign3
void renderer_t::draw_debug(const data_file_t& file) void renderer_t::draw_debug(const data_file_t& file)
{ {
switch (static_cast<debug_type>(debug_state)) switch (static_cast<debug_t>(debug_state))
{ {
case debug_type::DATA_POINT: case debug_t::DATA_POINT:
{ {
std::vector<blt::vec2> data_positions; std::vector<blt::vec2> data_positions;
std::vector<blt::vec2> neuron_positions; std::vector<blt::vec2> neuron_positions;
@ -300,7 +314,8 @@ namespace assign3
auto scale = topology_function->scale(half, requested_activation); auto scale = topology_function->scale(half, requested_activation);
auto ds = topology_function->call(context.neuron.dist(data_point.bins), scale); auto ds = topology_function->call(context.neuron.dist(data_point.bins), scale);
neuron_positions.push_back(context.neuron_padded); if (draw_data_lines)
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 = 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();
@ -317,9 +332,34 @@ namespace assign3
} }
} }
break; break;
case debug_type::DISTANCE: case debug_t::DISTANCE:
{ {
auto closest_type = normalize_data(get_neuron_activations(file));
auto& selected_neuron_ref = som->get_array().get_map()[selected_neuron];
static std::vector<Scalar> distances_2d;
static std::vector<Scalar> distances_nd;
distances_2d.clear();
distances_nd.clear();
for (const auto& n : som->get_array().get_map())
{
distances_2d.push_back(neuron_t::distance(distance_function.get(), selected_neuron_ref, n));
distances_nd.push_back(selected_neuron_ref.dist(n.get_data()));
}
draw_som(neuron_render_info_t{}.set_base_pos({370, 145}).set_neuron_scale(120).set_neuron_padding({0, 0}),
[this, &closest_type](render_data_t context) {
auto& text = fr2d.render_text(
"2D: " + std::to_string(distances_2d[context.index]) + "\nND: " +
std::to_string(distances_nd[context.index]), 18).setColor(0.2, 0.2, 0.8);
auto text_width = text.getAssociatedText().getTextWidth();
text.setPosition(context.neuron_padded - blt::vec2{text_width / 2.0f, 0}).setZIndex(1);
if (static_cast<blt::size_t>(selected_neuron) == context.index)
return blt::make_color(0, 0, 1);
auto type = closest_type[context.index];
return type >= 0 ? blt::make_color(0, type, 0) : blt::make_color(-type, 0, 0);
});
} }
break; break;
} }

View File

@ -23,12 +23,59 @@
namespace assign3 namespace assign3
{ {
neuron_t& neuron_t::randomize(blt::size_t seed) neuron_t& neuron_t::randomize(blt::size_t seed, init_t init, bool normalize, const data_file_t& file)
{ {
blt::random::random_t rand{seed}; blt::random::random_t rand{seed};
switch (init)
{
case init_t::COMPLETELY_RANDOM:
for (auto& v : data)
v = static_cast<Scalar>(rand.get_double(-1, 1));
break;
case init_t::RANDOM_DATA:
{
static std::vector<Scalar> min_values, max_values;
min_values.clear();
max_values.clear();
for (auto& v : data) min_values.resize(data.size());
v = static_cast<Scalar>(rand.get_double(-1, 1)); max_values.resize(data.size());
for (const auto& [min, max] : blt::in_pairs(min_values, max_values))
{
min = std::numeric_limits<Scalar>::max();
max = std::numeric_limits<Scalar>::min();
}
for (const auto& point : file.data_points)
{
for (const auto& [i, bin] : blt::enumerate(point.bins))
{
min_values[i] = std::min(min_values[i], bin);
max_values[i] = std::max(max_values[i], bin);
}
}
for (const auto& [i, v] : blt::enumerate(data))
v = static_cast<Scalar>(rand.get_double(min_values[i], max_values[i]));
}
break;
case init_t::SAMPLED_DATA:
{
auto selected = rand.select(file.data_points);
std::memcpy(data.data(), selected.bins.data(), data.size() * sizeof(Scalar));
}
break;
}
if (normalize)
{
Scalar total = 0;
for (auto v : data)
total += v * v;
Scalar mag = std::sqrt(total);
for (auto& v : data)
v /= mag;
}
return *this; return *this;
} }
@ -36,14 +83,14 @@ namespace assign3
// apply the distance based on the update neuron function // apply the distance based on the update neuron function
neuron_t& neuron_t::update(const std::vector<Scalar>& new_data, Scalar dist, Scalar eta) neuron_t& neuron_t::update(const std::vector<Scalar>& new_data, Scalar dist, Scalar eta)
{ {
static thread_local std::vector<Scalar> diff; // static thread_local std::vector<Scalar> diff;
diff.clear(); // diff.clear();
for (auto [v, x] : blt::in_pairs(data, new_data)) // for (auto [v, x] : blt::in_pairs(data, new_data))
diff.push_back(x - v); // diff.push_back(x - v);
for (auto [v, d] : blt::in_pairs(data, diff)) for (auto [v, d] : blt::in_pairs(data, new_data))
v += eta * dist * d; v += eta * dist * (d - v);
return *this; return *this;
} }

View File

@ -27,11 +27,11 @@ namespace assign3
{ {
som_t::som_t(const data_file_t& file, blt::size_t width, blt::size_t height, blt::size_t max_epochs, distance_function_t* dist_func, som_t::som_t(const data_file_t& file, blt::size_t width, blt::size_t height, blt::size_t max_epochs, distance_function_t* dist_func,
shape_t shape): shape_t shape, init_t init, bool normalize):
array(file.data_points.begin()->bins.size(), width, height, shape), file(file), max_epochs(max_epochs), dist_func(dist_func) array(file.data_points.begin()->bins.size(), width, height, shape), file(file), max_epochs(max_epochs), dist_func(dist_func)
{ {
for (auto& v : array.get_map()) for (auto& v : array.get_map())
v.randomize(std::random_device{}()); v.randomize(std::random_device{}(), init, normalize, file);
} }
void som_t::train_epoch(Scalar initial_learn_rate, topology_function_t* basis_func) void som_t::train_epoch(Scalar initial_learn_rate, topology_function_t* basis_func)