grid don't work
parent
45ccb3647e
commit
1e3ed0755a
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.25)
|
||||
project(COSC-4P80-Assignment-3 VERSION 0.0.18)
|
||||
project(COSC-4P80-Assignment-3 VERSION 0.0.19)
|
||||
include(FetchContent)
|
||||
|
||||
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
|
||||
|
|
|
@ -29,12 +29,24 @@ namespace assign3
|
|||
class array_t
|
||||
{
|
||||
public:
|
||||
explicit array_t(blt::size_t dimensions, blt::size_t width, blt::size_t height):
|
||||
explicit array_t(blt::size_t dimensions, blt::size_t width, blt::size_t height, shape_t shape):
|
||||
width(static_cast<blt::i64>(width)), height(static_cast<blt::i64>(height))
|
||||
{
|
||||
for (blt::size_t i = 0; i < width; i++)
|
||||
switch (shape)
|
||||
{
|
||||
case shape_t::GRID:
|
||||
case shape_t::GRID_WRAP:
|
||||
for (blt::size_t j = 0; j < height; j++)
|
||||
for (blt::size_t i = 0; i < width; i++)
|
||||
map.emplace_back(dimensions, i, j);
|
||||
break;
|
||||
case shape_t::GRID_OFFSET:
|
||||
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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] blt::vec2ul from_index(blt::size_t index) const
|
||||
|
|
|
@ -30,15 +30,19 @@ namespace assign3
|
|||
inline constexpr blt::i32 RENDER_2D = 0x0;
|
||||
inline constexpr blt::i32 RENDER_3D = 0x1;
|
||||
|
||||
enum class shape : blt::i32
|
||||
enum class shape_t : blt::i32
|
||||
{
|
||||
GRID = RENDER_2D,
|
||||
GRID_WRAP = RENDER_2D,
|
||||
GRID_OFFSET = RENDER_2D,
|
||||
GRID_OFFSET_WRAP = RENDER_2D,
|
||||
GAUSSIAN_DIST = RENDER_2D,
|
||||
TOROIDAL = RENDER_3D,
|
||||
CYLINDER = RENDER_3D
|
||||
GRID,
|
||||
GRID_WRAP,
|
||||
GRID_OFFSET,
|
||||
GRID_OFFSET_WRAP
|
||||
};
|
||||
|
||||
inline std::array<std::string, 4> shape_names{
|
||||
"Grid",
|
||||
"Edge Wrapped Grid",
|
||||
"Honey Comb Grid",
|
||||
"Edge Wrapped Honey Comb"
|
||||
};
|
||||
|
||||
enum class debug_type
|
||||
|
|
|
@ -92,31 +92,43 @@ namespace assign3
|
|||
|
||||
void render();
|
||||
|
||||
void update_graphics();
|
||||
|
||||
void regenerate_network()
|
||||
{
|
||||
som = std::make_unique<som_t>(motor_data.files[currently_selected_network].normalize(), som_width, som_height, max_epochs);
|
||||
switch (static_cast<shape_t>(selected_som_mode))
|
||||
{
|
||||
case shape_t::GRID:
|
||||
case shape_t::GRID_OFFSET:
|
||||
distance_function = std::make_unique<euclidean_distance_function_t>();
|
||||
break;
|
||||
case shape_t::GRID_OFFSET_WRAP:
|
||||
case shape_t::GRID_WRAP:
|
||||
distance_function = std::make_unique<toroidal_euclidean_distance_function_t>(som_width, som_height);
|
||||
break;
|
||||
}
|
||||
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));
|
||||
error_plotting.push_back(som->topological_error(motor_data.files[currently_selected_network]));
|
||||
}
|
||||
|
||||
private:
|
||||
motor_data_t& motor_data;
|
||||
std::unique_ptr<som_t> som;
|
||||
std::unique_ptr<topology_function_t> topology_function;
|
||||
std::unique_ptr<distance_function_t> distance_function;
|
||||
|
||||
std::vector<Scalar> error_plotting;
|
||||
|
||||
blt::gfx::font_renderer_t fr2d{};
|
||||
blt::gfx::batch_renderer_2d br2d;
|
||||
|
||||
float draw_width = 0;
|
||||
float draw_height = 0;
|
||||
float neuron_scale = 35;
|
||||
|
||||
blt::i32 som_width = 5;
|
||||
blt::i32 som_height = 5;
|
||||
blt::i32 max_epochs = 100;
|
||||
Scalar initial_learn_rate = 0.1;
|
||||
blt::i32 max_epochs = 2000;
|
||||
Scalar initial_learn_rate = 1;
|
||||
|
||||
int currently_selected_network = 0;
|
||||
int selected_som_mode = 0;
|
||||
bool debug_mode = false;
|
||||
bool draw_colors = true;
|
||||
bool draw_data_lines = false;
|
||||
|
|
|
@ -42,17 +42,30 @@ namespace assign3
|
|||
|
||||
[[nodiscard]] Scalar dist(const std::vector<Scalar>& X) const;
|
||||
|
||||
[[nodiscard]] inline const std::vector<Scalar>& get_data() const
|
||||
neuron_t& set_activation(Scalar act)
|
||||
{
|
||||
activation = act;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void activate(Scalar act)
|
||||
{ activation += act; }
|
||||
|
||||
[[nodiscard]] const std::vector<Scalar>& get_data() const
|
||||
{ return data; }
|
||||
|
||||
[[nodiscard]] inline Scalar get_x() const
|
||||
[[nodiscard]] Scalar get_x() const
|
||||
{ return x_pos; }
|
||||
|
||||
[[nodiscard]] inline Scalar get_y() const
|
||||
[[nodiscard]] Scalar get_y() const
|
||||
{ return y_pos; }
|
||||
|
||||
[[nodiscard]] Scalar get_activation() const
|
||||
{ return activation; }
|
||||
|
||||
private:
|
||||
Scalar x_pos, y_pos;
|
||||
Scalar activation = 0;
|
||||
std::vector<Scalar> data;
|
||||
};
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ 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);
|
||||
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);
|
||||
|
||||
blt::size_t get_closest_neuron(const std::vector<Scalar>& data);
|
||||
|
||||
|
@ -39,6 +39,8 @@ namespace assign3
|
|||
|
||||
blt::vec2 get_topological_position(const std::vector<Scalar>& data);
|
||||
|
||||
Scalar topological_error(const data_file_t& data);
|
||||
|
||||
[[nodiscard]] const array_t& get_array() const
|
||||
{ return array; }
|
||||
|
||||
|
@ -53,6 +55,7 @@ namespace assign3
|
|||
data_file_t file;
|
||||
blt::size_t current_epoch = 0;
|
||||
blt::size_t max_epochs;
|
||||
distance_function_t* dist_func;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -97,7 +97,6 @@ namespace assign3
|
|||
topology_function = std::make_unique<gaussian_function_t>();
|
||||
|
||||
regenerate_network();
|
||||
update_graphics();
|
||||
}
|
||||
|
||||
void renderer_t::cleanup()
|
||||
|
@ -142,8 +141,13 @@ namespace assign3
|
|||
ImGui::Checkbox("Run to completion", &running);
|
||||
ImGui::Text("Epoch %ld / %ld", som->get_current_epoch(), som->get_max_epochs());
|
||||
}
|
||||
ImGui::SetNextItemOpen(true, ImGuiCond_Appearing);
|
||||
if (ImGui::CollapsingHeader("SOM Settings"))
|
||||
{
|
||||
ImGui::Text("Network Shape");
|
||||
if (ImGui::ListBox("##NetworkShape", &selected_som_mode, get_selection_string, shape_names.data(),
|
||||
static_cast<int>(shape_names.size())))
|
||||
regenerate_network();
|
||||
if (ImGui::InputInt("SOM Width", &som_width) || ImGui::InputInt("SOM Height", &som_height) ||
|
||||
ImGui::InputInt("Max Epochs", &max_epochs))
|
||||
regenerate_network();
|
||||
|
@ -195,13 +199,22 @@ namespace assign3
|
|||
ImPlotPoint(som_width, som_height), ImPlotHeatmapFlags_ColMajor);
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
ImPlot::SetNextAxesLimits(0, max_epochs, 0, 1, ImPlotCond_Once);
|
||||
if (ImPlot::BeginPlot("Error"))
|
||||
{
|
||||
ImPlot::PlotLine("##error", error_plotting.data(), static_cast<int>(error_plotting.size()));
|
||||
ImPlot::EndPlot();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (running)
|
||||
{
|
||||
if (som->get_current_epoch() < som->get_max_epochs())
|
||||
{
|
||||
som->train_epoch(initial_learn_rate, topology_function.get());
|
||||
error_plotting.push_back(som->topological_error(current_data_file));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -220,24 +233,6 @@ namespace assign3
|
|||
fr2d.render();
|
||||
}
|
||||
|
||||
void renderer_t::update_graphics()
|
||||
{
|
||||
// find the min x / y for the currently drawn som as positions may depend on type.
|
||||
const auto x_comparator = [](const auto& a, const auto& b) {
|
||||
return a.get_x() < b.get_x();
|
||||
};
|
||||
const auto y_comparator = [](const auto& a, const auto& b) {
|
||||
return a.get_y() < b.get_y();
|
||||
};
|
||||
const auto& som_neurons = som->get_array().get_map();
|
||||
auto min_x = std::min_element(som_neurons.begin(), som_neurons.end(), x_comparator)->get_x();
|
||||
auto max_x = std::max_element(som_neurons.begin(), som_neurons.end(), x_comparator)->get_x();
|
||||
auto min_y = std::min_element(som_neurons.begin(), som_neurons.end(), y_comparator)->get_y();
|
||||
auto max_y = std::max_element(som_neurons.begin(), som_neurons.end(), y_comparator)->get_y();
|
||||
draw_width = (max_x - min_x);
|
||||
draw_height = (max_y - min_y);
|
||||
}
|
||||
|
||||
std::vector<float> renderer_t::get_neuron_activations(const data_file_t& file)
|
||||
{
|
||||
static std::vector<float> closest_type;
|
||||
|
@ -248,6 +243,9 @@ namespace assign3
|
|||
for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
|
||||
{
|
||||
auto half = som->find_closest_neighbour_distance(i) / at_distance_measurement;
|
||||
// auto sigma = std::sqrt(-(half * half) / (2 * std::log(requested_activation)));
|
||||
// auto r = 1 / (2 * sigma * sigma);
|
||||
//
|
||||
auto scale = topology_function->scale(half, requested_activation);
|
||||
for (const auto& data : file.data_points)
|
||||
{
|
||||
|
|
46
src/som.cpp
46
src/som.cpp
|
@ -26,8 +26,9 @@
|
|||
namespace assign3
|
||||
{
|
||||
|
||||
som_t::som_t(const data_file_t& file, blt::size_t width, blt::size_t height, blt::size_t max_epochs):
|
||||
array(file.data_points.begin()->bins.size(), width, height), file(file), max_epochs(max_epochs)
|
||||
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):
|
||||
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{}());
|
||||
|
@ -57,8 +58,7 @@ namespace assign3
|
|||
{
|
||||
if (i == v0_idx)
|
||||
continue;
|
||||
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);
|
||||
auto dist = basis_func->call(neuron_t::distance(dist_func, v0, n), time_ratio * scale);
|
||||
n.update(current_data.bins, dist, eta);
|
||||
}
|
||||
}
|
||||
|
@ -84,12 +84,11 @@ namespace assign3
|
|||
|
||||
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();
|
||||
for (const auto& [i, n] : blt::enumerate(array.get_map()))
|
||||
{
|
||||
if (i != v0)
|
||||
distance_min = std::min(distance_min, neuron_t::distance(&dist_func, array.get_map()[v0], n));
|
||||
distance_min = std::min(distance_min, neuron_t::distance(dist_func, array.get_map()[v0], n));
|
||||
}
|
||||
return distance_min;
|
||||
}
|
||||
|
@ -140,5 +139,40 @@ namespace assign3
|
|||
return (dp1 * p_1) + (dp2 * p_2) + (dp3 * p_3);
|
||||
}
|
||||
|
||||
Scalar som_t::topological_error(const data_file_t& data)
|
||||
{
|
||||
Scalar total = 0;
|
||||
std::vector<std::pair<blt::size_t, Scalar>> distances;
|
||||
|
||||
for (const auto& x : data.data_points)
|
||||
{
|
||||
distances.clear();
|
||||
for (const auto& [i, n] : blt::enumerate(array.get_map()))
|
||||
distances.emplace_back(i, n.dist(x.bins));
|
||||
|
||||
std::pair<blt::size_t, Scalar> min1 = {0, std::numeric_limits<Scalar>::max()};
|
||||
std::pair<blt::size_t, Scalar> min2 = {0, std::numeric_limits<Scalar>::max()};
|
||||
|
||||
for (const auto& elem : distances)
|
||||
{
|
||||
if (elem.second < min1.second)
|
||||
{
|
||||
min2 = min1;
|
||||
min1 = elem;
|
||||
} else if (elem.second < min2.second)
|
||||
min2 = elem;
|
||||
}
|
||||
|
||||
// we can assert the neurons are neighbours if the distance between the BMUs and the nearest neighbour are equal.
|
||||
auto min_distances = neuron_t::distance(dist_func, array.get_map()[min1.first], array.get_map()[min2.first]);
|
||||
auto neighbour_distances = find_closest_neighbour_distance(min1.first);
|
||||
|
||||
if (!blt::f_equal(min_distances, neighbour_distances))
|
||||
total += 1;
|
||||
}
|
||||
|
||||
return total / static_cast<Scalar>(data.data_points.size());
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue