hey almost done
parent
1e3ed0755a
commit
f84919ccb5
|
@ -1,5 +1,5 @@
|
|||
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)
|
||||
|
||||
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
|
||||
|
|
|
@ -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/
|
|
@ -44,7 +44,8 @@ namespace assign3
|
|||
case shape_t::GRID_OFFSET_WRAP:
|
||||
for (blt::size_t j = 0; j < height; j++)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,13 +39,13 @@ namespace assign3
|
|||
};
|
||||
|
||||
inline std::array<std::string, 4> shape_names{
|
||||
"Grid",
|
||||
"Edge Wrapped Grid",
|
||||
"Honey Comb Grid",
|
||||
"Edge Wrapped Honey Comb"
|
||||
"Grid",
|
||||
"Edge Wrapped Grid",
|
||||
"Honey Comb Grid",
|
||||
"Edge Wrapped Honey Comb"
|
||||
};
|
||||
|
||||
enum class debug_type
|
||||
enum class debug_t
|
||||
{
|
||||
DATA_POINT,
|
||||
DISTANCE
|
||||
|
@ -55,6 +55,25 @@ namespace assign3
|
|||
"Distance to Datapoint",
|
||||
"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
|
||||
|
|
|
@ -107,7 +107,8 @@ namespace assign3
|
|||
}
|
||||
error_plotting.clear();
|
||||
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]));
|
||||
}
|
||||
|
||||
|
@ -129,12 +130,15 @@ namespace assign3
|
|||
|
||||
int currently_selected_network = 0;
|
||||
int selected_som_mode = 0;
|
||||
int selected_init_type = 0;
|
||||
bool normalize_init = false;
|
||||
bool debug_mode = false;
|
||||
bool draw_colors = true;
|
||||
bool draw_data_lines = false;
|
||||
bool running = false;
|
||||
int debug_state = 0;
|
||||
int selected_data_point = 0;
|
||||
int selected_neuron = 0;
|
||||
|
||||
float requested_activation = 0.5;
|
||||
float at_distance_measurement = 2;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <assign3/fwdecl.h>
|
||||
#include "blt/std/types.h"
|
||||
#include <assign3/functions.h>
|
||||
#include <assign3/file.h>
|
||||
|
||||
namespace assign3
|
||||
{
|
||||
|
@ -34,7 +35,7 @@ namespace assign3
|
|||
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);
|
||||
|
||||
|
|
|
@ -29,7 +29,8 @@ namespace assign3
|
|||
class som_t
|
||||
{
|
||||
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);
|
||||
|
||||
|
|
|
@ -48,8 +48,8 @@ namespace assign3
|
|||
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_diff = std::abs(x[0] - y[0]);
|
||||
Scalar y_diff = std::abs(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);
|
||||
|
|
|
@ -144,10 +144,16 @@ namespace assign3
|
|||
ImGui::SetNextItemOpen(true, ImGuiCond_Appearing);
|
||||
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(),
|
||||
static_cast<int>(shape_names.size())))
|
||||
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) ||
|
||||
ImGui::InputInt("Max Epochs", &max_epochs))
|
||||
regenerate_network();
|
||||
|
@ -160,14 +166,15 @@ namespace assign3
|
|||
if (debug_mode)
|
||||
{
|
||||
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 Lines", &draw_data_lines);
|
||||
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))
|
||||
names.push_back("#" + std::to_string(i) + " (" + (v.is_bad ? "Bad)" : "Good)"));
|
||||
ImGui::Text("Select Data Point");
|
||||
|
@ -175,8 +182,15 @@ namespace assign3
|
|||
static_cast<int>(names.size()));
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -262,9 +276,9 @@ namespace assign3
|
|||
|
||||
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> neuron_positions;
|
||||
|
@ -300,7 +314,8 @@ namespace assign3
|
|||
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);
|
||||
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_width = text.getAssociatedText().getTextWidth();
|
||||
auto text_height = text.getAssociatedText().getTextHeight();
|
||||
|
@ -317,9 +332,34 @@ namespace assign3
|
|||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -23,12 +23,59 @@
|
|||
|
||||
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};
|
||||
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();
|
||||
|
||||
min_values.resize(data.size());
|
||||
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;
|
||||
}
|
||||
|
||||
for (auto& v : data)
|
||||
v = static_cast<Scalar>(rand.get_double(-1, 1));
|
||||
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;
|
||||
}
|
||||
|
@ -36,14 +83,14 @@ namespace assign3
|
|||
// apply the distance based on the update neuron function
|
||||
neuron_t& neuron_t::update(const std::vector<Scalar>& new_data, Scalar dist, Scalar eta)
|
||||
{
|
||||
static thread_local std::vector<Scalar> diff;
|
||||
diff.clear();
|
||||
// static thread_local std::vector<Scalar> diff;
|
||||
// diff.clear();
|
||||
|
||||
// for (auto [v, x] : blt::in_pairs(data, new_data))
|
||||
// diff.push_back(x - v);
|
||||
|
||||
for (auto [v, x] : blt::in_pairs(data, new_data))
|
||||
diff.push_back(x - v);
|
||||
|
||||
for (auto [v, d] : blt::in_pairs(data, diff))
|
||||
v += eta * dist * d;
|
||||
for (auto [v, d] : blt::in_pairs(data, new_data))
|
||||
v += eta * dist * (d - v);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue