main
Brett 2024-10-25 14:01:47 -04:00
parent 0b2e0ccce0
commit 1febcb48b3
6 changed files with 126 additions and 87 deletions

View File

@ -4,6 +4,10 @@
<mapping directory="" vcs="Git" /> <mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/blt" vcs="Git" /> <mapping directory="$PROJECT_DIR$/lib/blt" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/blt-graphics" vcs="Git" /> <mapping directory="$PROJECT_DIR$/lib/blt-graphics" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/blt-graphics/libraries/BLT" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/blt-graphics/libraries/BLT/libraries/parallel-hashmap" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/blt-graphics/libraries/imgui" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/blt-graphics/libraries/openal-soft" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/blt/libraries/parallel-hashmap" vcs="Git" /> <mapping directory="$PROJECT_DIR$/lib/blt/libraries/parallel-hashmap" vcs="Git" />
</component> </component>
</project> </project>

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.25) cmake_minimum_required(VERSION 3.25)
project(COSC-4P80-Assignment-2 VERSION 0.0.5) project(COSC-4P80-Assignment-2 VERSION 0.0.6)
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
option(ENABLE_UBSAN "Enable the ub sanitizer" OFF) option(ENABLE_UBSAN "Enable the ub sanitizer" OFF)

View File

@ -25,7 +25,7 @@ ENVIRONMENT_DATA_LOCATION = USER_HOME / ".brett_scripts.env"
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
CONFIG_FILE_DIRECTORY = Path(os.getenv('APPDATA') + "\BLT") CONFIG_FILE_DIRECTORY = Path(os.getenv('APPDATA') + "\BLT")
CONFIG_FILE_LOCATION = Path(CONFIG_FILE_DIRECTORY + "\commit_config.env") CONFIG_FILE_LOCATION = Path(CONFIG_FILE_DIRECTORY + "\commit_config.json")
else: else:
XDG_CONFIG_HOME = os.environ.get('XDG_CONFIG_HOME') XDG_CONFIG_HOME = os.environ.get('XDG_CONFIG_HOME')
if XDG_CONFIG_HOME is None: if XDG_CONFIG_HOME is None:
@ -36,7 +36,7 @@ else:
if len(str(XDG_CONFIG_HOME)) == 0: if len(str(XDG_CONFIG_HOME)) == 0:
XDG_CONFIG_HOME = USER_HOME XDG_CONFIG_HOME = USER_HOME
CONFIG_FILE_DIRECTORY = XDG_CONFIG_HOME / "blt" CONFIG_FILE_DIRECTORY = XDG_CONFIG_HOME / "blt"
CONFIG_FILE_LOCATION = CONFIG_FILE_DIRECTORY / "commit_config.env" CONFIG_FILE_LOCATION = CONFIG_FILE_DIRECTORY / "commit_config.json"
class Config: class Config:
def __init__(self): def __init__(self):

View File

@ -31,11 +31,11 @@ namespace assign2
friend layer_t; friend layer_t;
public: public:
// empty neuron for loading from a stream // empty neuron for loading from a stream
explicit neuron_t(weight_view weights): weights(weights) explicit neuron_t(weight_view weights, weight_view dw): dw(dw), weights(weights)
{} {}
// neuron with bias // neuron with bias
explicit neuron_t(weight_view weights, Scalar bias): bias(bias), weights(weights) explicit neuron_t(weight_view weights, weight_view dw, Scalar bias): bias(bias), dw(dw), weights(weights)
{} {}
Scalar activate(const Scalar* inputs, function_t* act_func) Scalar activate(const Scalar* inputs, function_t* act_func)
@ -47,6 +47,23 @@ namespace assign2
return a; return a;
} }
void back_prop(function_t* act, const std::vector<Scalar>& previous_outputs, Scalar next_error)
{
// delta for weights
error = act->derivative(z) * next_error;
for (auto [prev_out, d_weight] : blt::zip(previous_outputs, dw))
{
// dw / apply dw
d_weight = learn_rate * prev_out * error;
}
}
void update()
{
for (auto [w, d] : blt::in_pairs(weights, dw))
w += d;
}
template<typename OStream> template<typename OStream>
OStream& serialize(OStream& stream) OStream& serialize(OStream& stream)
{ {
@ -73,11 +90,13 @@ namespace assign2
float a = 0; float a = 0;
float bias = 0; float bias = 0;
float error = 0; float error = 0;
weight_view dw;
weight_view weights; weight_view weights;
}; };
class layer_t class layer_t
{ {
friend network_t;
public: public:
template<typename WeightFunc, typename BiasFunc> template<typename WeightFunc, typename BiasFunc>
layer_t(const blt::i32 in, const blt::i32 out, function_t* act_func, WeightFunc w, BiasFunc b): layer_t(const blt::i32 in, const blt::i32 out, function_t* act_func, WeightFunc w, BiasFunc b):
@ -87,62 +106,64 @@ namespace assign2
for (blt::i32 i = 0; i < out_size; i++) for (blt::i32 i = 0; i < out_size; i++)
{ {
auto weight = weights.allocate_view(in_size); auto weight = weights.allocate_view(in_size);
auto dw = weight_derivatives.allocate_view(in_size);
for (auto& v : weight) for (auto& v : weight)
v = w(i); v = w(i);
neurons.push_back(neuron_t{weight, b(i)}); neurons.push_back(neuron_t{weight, dw, b(i)});
} }
} }
std::vector<Scalar> call(const std::vector<Scalar>& in) const std::vector<Scalar>& call(const std::vector<Scalar>& in)
{ {
std::vector<Scalar> out; outputs.clear();
out.reserve(out_size); outputs.reserve(out_size);
#if BLT_DEBUG_LEVEL > 0 #if BLT_DEBUG_LEVEL > 0
if (in.size() != in_size) if (in.size() != in_size)
throw std::runtime_exception("Input vector doesn't match expected input size!"); throw std::runtime_exception("Input vector doesn't match expected input size!");
#endif #endif
for (auto& n : neurons) for (auto& n : neurons)
out.push_back(n.activate(in.data(), act_func)); outputs.push_back(n.activate(in.data(), act_func));
return out; return outputs;
} }
Scalar back_prop(const std::vector<Scalar>& prev_layer_output, Scalar error, const layer_t& next_layer, bool is_output) Scalar back_prop(const std::vector<Scalar>& prev_layer_output,
const std::variant<blt::ref<const std::vector<Scalar>>, blt::ref<const layer_t>>& data)
{ {
std::vector<Scalar> dw; return std::visit(blt::lambda_visitor{
// is provided if we are an output layer, contains output of this net (per neuron) and the expected output (per neuron)
[this, &prev_layer_output](const std::vector<Scalar>& expected) {
// this is close! i think the changes should be applied in the neuron since the slides show the change of weight PER NEURON PER INPUT Scalar total_error = 0;
// δ(h) for (auto [i, n] : blt::enumerate(neurons))
if (is_output) {
{ auto d = outputs[i] - expected[i];
// assign error to output layer auto d2 = 0.5f * (d * d);
for (auto& n : neurons) total_error += d2;
n.error = act_func->derivative(n.z) * error; // f'act(net(h)) * (error) n.back_prop(act_func, prev_layer_output, d2);
} else }
{ return total_error;
// first calculate and assign input layer error },
std::vector<Scalar> next_error; // interior layer
next_error.resize(next_layer.neurons.size()); [this, &prev_layer_output](const layer_t& layer) {
for (const auto& [i, w] : blt::enumerate(next_layer.neurons)) Scalar total_error = 0;
{ for (auto [i, n] : blt::enumerate(neurons))
for (auto wv : w.weights) {
next_error[i] += w.error * wv; Scalar weight_error = 0;
// needed? // TODO: this is not efficient on the cache!
next_error[i] /= static_cast<Scalar>(w.weights.size()); for (auto nn : layer.neurons)
} weight_error += nn.error * nn.weights[i];
Scalar w2 = 0.5f * weight_error * weight_error;
for (auto& n : neurons) total_error += w2;
{ n.back_prop(act_func, prev_layer_output, w2);
n.error = act_func->derivative(n.z); }
} return total_error;
} }
}, data);
for (const auto& v : prev_layer_output) }
{
void update()
} {
for (auto& n : neurons)
return error_at_current_layer; n.update();
} }
template<typename OStream> template<typename OStream>
@ -181,8 +202,10 @@ namespace assign2
private: private:
const blt::i32 in_size, out_size; const blt::i32 in_size, out_size;
weight_t weights; weight_t weights;
weight_t weight_derivatives;
function_t* act_func; function_t* act_func;
std::vector<neuron_t> neurons; std::vector<neuron_t> neurons;
std::vector<Scalar> outputs;
}; };
} }

View File

@ -29,8 +29,7 @@ namespace assign2
{ {
public: public:
template<typename WeightFunc, typename BiasFunc> template<typename WeightFunc, typename BiasFunc>
network_t(blt::i32 input_size, blt::i32 output_size, blt::i32 layer_count, blt::i32 hidden_size, WeightFunc w, BiasFunc b): network_t(blt::i32 input_size, blt::i32 output_size, blt::i32 layer_count, blt::i32 hidden_size, WeightFunc w, BiasFunc b)
input_size(input_size), output_size(output_size), hidden_count(layer_count), hidden_size(hidden_size)
{ {
if (layer_count > 0) if (layer_count > 0)
{ {
@ -50,8 +49,7 @@ namespace assign2
template<typename WeightFunc, typename BiasFunc, typename OutputWeightFunc, typename OutputBiasFunc> template<typename WeightFunc, typename BiasFunc, typename OutputWeightFunc, typename OutputBiasFunc>
network_t(blt::i32 input_size, blt::i32 output_size, blt::i32 layer_count, blt::i32 hidden_size, network_t(blt::i32 input_size, blt::i32 output_size, blt::i32 layer_count, blt::i32 hidden_size,
WeightFunc w, BiasFunc b, OutputWeightFunc ow, OutputBiasFunc ob): WeightFunc w, BiasFunc b, OutputWeightFunc ow, OutputBiasFunc ob)
input_size(input_size), output_size(output_size), hidden_count(layer_count), hidden_size(hidden_size)
{ {
if (layer_count > 0) if (layer_count > 0)
{ {
@ -69,28 +67,20 @@ namespace assign2
} }
} }
explicit network_t(std::vector<layer_t> layers): explicit network_t(std::vector<layer_t> layers): layers(std::move(layers))
input_size(layers.begin()->get_in_size()), output_size(layers.end()->get_out_size()),
hidden_count(static_cast<blt::i32>(layers.size()) - 1), hidden_size(layers.end()->get_in_size()), layers(std::move(layers))
{} {}
network_t() = default; network_t() = default;
std::vector<Scalar> execute(const std::vector<Scalar>& input) const std::vector<Scalar>& execute(const std::vector<Scalar>& input)
{ {
std::vector<Scalar> previous_output; std::vector<blt::ref<const std::vector<Scalar>>> outputs;
std::vector<Scalar> current_output; outputs.emplace_back(input);
for (auto [i, v] : blt::enumerate(layers)) for (auto& v : layers)
{ outputs.emplace_back(v.call(outputs.back()));
previous_output = current_output;
if (i == 0)
current_output = v.call(input);
else
current_output = v.call(previous_output);
}
return current_output; return outputs.back();
} }
std::pair<Scalar, Scalar> error(const std::vector<Scalar>& outputs, bool is_bad) std::pair<Scalar, Scalar> error(const std::vector<Scalar>& outputs, bool is_bad)
@ -108,19 +98,33 @@ namespace assign2
return {0.5f * (error * error), error}; return {0.5f * (error * error), error};
} }
Scalar train(const data_file_t& example) Scalar train_epoch(const data_file_t& example)
{ {
Scalar total_error = 0; Scalar total_error = 0;
Scalar total_d_error = 0; Scalar total_d_error = 0;
for (const auto& x : example.data_points) for (const auto& x : example.data_points)
{ {
print_vec(x.bins) << std::endl; execute(x.bins);
auto o = execute(x.bins); std::vector<Scalar> expected{x.is_bad ? 0.0f : 1.0f, x.is_bad ? 1.0f : 0.0f};
print_vec(o) << std::endl;
auto [e, de] = error(o, x.is_bad); for (auto [i, layer] : blt::iterate(layers).enumerate().rev())
total_error += e; {
total_d_error += -learn_rate * de; if (i == layers.size() - 1)
BLT_TRACE("\tError %f, %f, is bad? %s", e, -learn_rate * de, x.is_bad ? "True" : "False"); {
auto e = layer.back_prop(layers[i - 1].outputs, expected);
total_error += e;
} else if (i == 0)
{
auto e = layer.back_prop(x.bins, layers[i + 1]);
total_error += e;
} else
{
auto e = layer.back_prop(layers[i - 1].outputs, layers[i + 1]);
total_error += e;
}
}
for (auto& l : layers)
l.update();
} }
BLT_DEBUG("Total Errors found %f, %f", total_error, total_d_error); BLT_DEBUG("Total Errors found %f, %f", total_error, total_d_error);
@ -128,7 +132,6 @@ namespace assign2
} }
private: private:
blt::i32 input_size, output_size, hidden_count, hidden_size;
std::vector<layer_t> layers; std::vector<layer_t> layers;
}; };
} }

View File

@ -79,37 +79,46 @@ int main(int argc, const char** argv)
auto data_files = load_data_files(get_data_files(data_directory)); auto data_files = load_data_files(get_data_files(data_directory));
random_init randomizer{619}; random_init randomizer{619};
empty_init empty;
sigmoid_function sig; sigmoid_function sig;
relu_function relu; relu_function relu;
threshold_function thresh; threshold_function thresh;
layer_t layer1{16, 8, &sig, randomizer, randomizer}; layer_t layer1{16, 16, &sig, randomizer, empty};
layer1.debug(); layer1.debug();
layer_t layer2{8, 8, &sig, randomizer, randomizer}; layer_t layer2{16, 16, &sig, randomizer, empty};
layer2.debug(); layer2.debug();
layer_t layer3{8, 8, &sig, randomizer, randomizer}; layer_t layer3{16, 16, &sig, randomizer, empty};
layer3.debug(); layer3.debug();
layer_t layer_output{8, 2, &relu, randomizer, randomizer}; layer_t layer_output{16, 2, &sig, randomizer, empty};
layer_output.debug(); layer_output.debug();
network_t network{{layer1, layer2, layer3, layer_output}}; network_t network{{layer1, layer2, layer3, layer_output}};
std::vector<Scalar> input;
input.resize(16);
for (auto f : data_files) for (auto f : data_files)
{ {
if (f.data_points.begin()->bins.size() == 16) if (f.data_points.begin()->bins.size() == 16)
{ {
for (auto [i, b] : blt::enumerate(f.data_points.begin()->bins)) for (blt::size_t i = 0; i < 10; i++)
input[i] = b; {
network.train(f); network.train_epoch(f);
}
break; break;
} }
} }
BLT_INFO("Test Cases:");
auto output = network.execute(input); for (auto f : data_files)
print_vec(output) << std::endl; {
if (f.data_points.begin()->bins.size() == 16)
{
for (auto& d : f.data_points)
{
std::cout << "Good or bad? " << d.is_bad << " :: ";
print_vec(network.execute(d.bins)) << std::endl;
}
}
}
// for (auto d : data_files) // for (auto d : data_files)
// { // {
// BLT_TRACE_STREAM << "\nSilly new file:\n"; // BLT_TRACE_STREAM << "\nSilly new file:\n";