Skip to content

Commit 2387ce5

Browse files
committed
2 parents e8cf3e2 + 4c3f7d0 commit 2387ce5

14 files changed

Lines changed: 654 additions & 181 deletions

src/neuralnetwork/activation.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -392,21 +392,23 @@ double activation::calculate_mish_derivative(double x, double) noexcept
392392
return tanh_sp + x * sigmoid_x * (1 - tanh_sp * tanh_sp);
393393
}
394394

395-
double activation::calculate_swish(double x, double) noexcept
395+
double activation::calculate_swish(double x, double alpha) noexcept
396396
{
397397
MYODDWEB_PROFILE_FUNCTION("activation");
398398
constexpr double MAX_EXP_INPUT = 60.0;
399-
const double exp_term = std::exp(std::clamp(-x, -MAX_EXP_INPUT, MAX_EXP_INPUT));
399+
const double z = alpha * x;
400+
const double exp_term = std::exp(std::clamp(-z, -MAX_EXP_INPUT, MAX_EXP_INPUT));
400401
return x / (1.0 + exp_term);
401402
}
402403

403-
double activation::calculate_swish_derivative(double x, double) noexcept
404+
double activation::calculate_swish_derivative(double x, double alpha) noexcept
404405
{
405406
MYODDWEB_PROFILE_FUNCTION("activation");
406407
constexpr double MAX_EXP_INPUT = 60.0;
407-
double clamped_x = std::clamp(x, -MAX_EXP_INPUT, MAX_EXP_INPUT);
408-
double sigmoid = 1.0 / (1.0 + std::exp(-clamped_x));
409-
return sigmoid + x * sigmoid * (1.0 - sigmoid);
408+
const double z = alpha * x;
409+
const double clamped_z = std::clamp(z, -MAX_EXP_INPUT, MAX_EXP_INPUT);
410+
const double sigmoid = 1.0 / (1.0 + std::exp(-clamped_z));
411+
return sigmoid + alpha * x * sigmoid * (1.0 - sigmoid);
410412
}
411413

412414
double activation::calculate_gelu(double x, double) noexcept

src/neuralnetwork/examples/repro_issue_softmax.h

Lines changed: 110 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ class ExampleReproIssueSoftmax
2929
MYODDWEB_PROFILE_FUNCTION("ExampleReproIssueSoftmax");
3030

3131
// 17 inputs, 80 LSTM, 40 LSTM, 7 total outputs (2 in branch 0, 5 in branch 1)
32-
std::vector<unsigned> topology = { 17, 80, 40, 7 };
32+
std::vector<unsigned> topology = { 17, 64, 32, 7 };
3333

3434
std::vector<LayerDetails> trunk_hidden_layers =
3535
{
36-
LayerDetails(Layer::Architecture::Lstm, 80, activation(activation::method::tanh, 0.01), 0.15, 0.0001, OptimiserType::NadamW, 0.95),
37-
LayerDetails(Layer::Architecture::Lstm, 40, activation(activation::method::tanh, 0.01), 0.15, 0.0001, OptimiserType::NadamW, 0.95)
36+
LayerDetails(Layer::Architecture::Gru, 64, activation(activation::method::tanh, 0.1), 0.1, 0.0001, OptimiserType::NadamW, 0.9),
37+
LayerDetails(Layer::Architecture::Gru, 32, activation(activation::method::tanh, 0.1), 0.1, 0.0001, OptimiserType::NadamW, 0.9)
3838
};
3939

4040
std::vector<MultiOutputLayerDetails> multi_output_layer_details;
@@ -43,38 +43,39 @@ class ExampleReproIssueSoftmax
4343
MultiOutputLayerDetails b0
4444
(
4545
{
46-
LayerDetails(Layer::Architecture::FF, 16, activation(activation::method::tanh, 0.01), 0.15, 0.0001, OptimiserType::NadamW, 0.95),
47-
LayerDetails(Layer::Architecture::FF, 32, activation(activation::method::tanh, 0.01), 0.15, 0.0001, OptimiserType::NadamW, 0.95)
46+
LayerDetails(Layer::Architecture::FF, 32, activation(activation::method::tanh, 0.01), 0, 0.0001, OptimiserType::NadamW, 0.9),
47+
LayerDetails(Layer::Architecture::FF, 16, activation(activation::method::tanh, 0.01), 0, 0.0001, OptimiserType::NadamW, 0.9)
4848
},
49-
OutputLayerDetails(2, activation(activation::method::tanh, 0.01), ErrorCalculation::type::huber_direction_loss, EvaluationConfig(0.01, 0.15, 0.3, 0.3, false, 1.0), 0.0, OptimiserType::NadamW, 0.95)
49+
OutputLayerDetails(2, activation(activation::method::tanh, 0.1), ErrorCalculation::type::huber_direction_loss, EvaluationConfig(0.01, 0.15, 0.3, 0.3, false, 1.0), 0.0, OptimiserType::NadamW, 0.99)
5050
);
5151

5252
// Branch 1: 5 outputs, Softmax
5353
MultiOutputLayerDetails b1
5454
(
5555
{
56-
LayerDetails(Layer::Architecture::FF, 16, activation(activation::method::tanh, 0.01), 0.15, 0.0001, OptimiserType::NadamW, 0.95),
57-
LayerDetails(Layer::Architecture::FF, 32, activation(activation::method::tanh, 0.01), 0.15, 0.0001, OptimiserType::NadamW, 0.95)
56+
LayerDetails(Layer::Architecture::FF, 32, activation(activation::method::tanh, 0.01), 0, 0.0001, OptimiserType::NadamW, 0.9),
57+
LayerDetails(Layer::Architecture::FF, 16, activation(activation::method::tanh, 0.01), 0, 0.0001, OptimiserType::NadamW, 0.9)
5858
},
59-
OutputLayerDetails(5, activation(activation::method::softmax, 0.01), ErrorCalculation::type::cross_entropy, EvaluationConfig(0.0, 0.2, 1.0, 0.3, false, 1.0), 0.0, OptimiserType::NadamW, 0.95)
59+
OutputLayerDetails(5, activation(activation::method::softmax, 0.01), ErrorCalculation::type::cross_entropy, EvaluationConfig(0.0, 0.2, 1.0, 0.3, false, 1.0), 0.0, OptimiserType::NadamW, 0.99)
6060
);
6161

6262
multi_output_layer_details.push_back(b0);
6363
multi_output_layer_details.push_back(b1);
6464

6565
auto options = NeuralNetworkOptions::create(topology)
66-
.with_batch_size(32) // Match REAL
66+
.with_batch_size(32)
6767
.with_output_layer_details(multi_output_layer_details)
6868
.with_log_level(log_level)
69-
.with_learning_rate(0.0015) // Match REAL
70-
.with_learning_rate_warmup(0.0003, 0.07) // Match REAL
71-
.with_learning_rate_decay_rate(0.985) // Match REAL
72-
.with_clip_threshold(1.5) // Match REAL
73-
.with_number_of_epoch(500)
69+
.with_learning_rate(0.0005)
70+
.with_learning_rate_warmup(0.0002, 0.07)
71+
.with_learning_rate_decay_rate(0.985)
72+
.with_clip_threshold(1.5)
73+
.with_number_of_epoch(5000)
7474
.with_hidden_layers(trunk_hidden_layers)
75-
.with_shuffle_training_data(true)
75+
.with_shuffle_training_data(false)
76+
.with_shuffle_bptt_batches(true)
7677
.with_enable_bptt(true)
77-
.with_bptt_max_ticks(24) // Match REAL
78+
.with_bptt_max_ticks(24)
7879
.with_final_error_calculation_types({
7980
ErrorCalculation::type::huber_loss,
8081
ErrorCalculation::type::huber_direction_loss,
@@ -191,110 +192,129 @@ class ExampleReproIssueSoftmax
191192
for (int i = 0; i < 5; ++i)
192193
Logger::info("Class ", i, ": ", (double)counts[i] / outputs.size() * 100.0, "%");
193194

194-
nn->train(inputs, outputs);
195+
try {
196+
nn->train(inputs, outputs);
195197

196-
// Metrics for both branches
197-
std::vector<ErrorCalculation::type> all_layer_metrics = {
198-
ErrorCalculation::type::huber_direction_loss,
199-
ErrorCalculation::type::cross_entropy,
200-
ErrorCalculation::type::directional_confidence_score,
201-
ErrorCalculation::type::prediction_coverage
202-
};
198+
// Metrics for both branches
199+
std::vector<ErrorCalculation::type> all_layer_metrics = {
200+
ErrorCalculation::type::huber_direction_loss,
201+
ErrorCalculation::type::cross_entropy,
202+
ErrorCalculation::type::directional_confidence_score,
203+
ErrorCalculation::type::prediction_coverage
204+
};
203205

204-
auto metrics_results = nn->calculate_forecast_metrics_all_layers(all_layer_metrics, true);
206+
auto metrics_results = nn->calculate_forecast_metrics_all_layers(all_layer_metrics, true);
205207

206-
Logger::info("Branch 0 Metrics (Regression/Huber):");
207-
if (metrics_results.size() > 0)
208-
{
209-
for (const auto& m : metrics_results[0])
208+
Logger::info("Branch 0 Metrics (Regression/Huber):");
209+
if (metrics_results.size() > 0)
210210
{
211-
Logger::info(" ", ErrorCalculation::type_to_string(m.error_type()), ": ", m.error());
211+
for (const auto& m : metrics_results[0])
212+
{
213+
Logger::info(" ", ErrorCalculation::type_to_string(m.error_type()), ": ", m.error());
214+
}
212215
}
213-
}
214216

215-
Logger::info("Branch 1 Metrics (Softmax/Cross-Entropy):");
216-
if (metrics_results.size() > 1)
217-
{
218-
for (const auto& m : metrics_results[1])
217+
Logger::info("Branch 1 Metrics (Softmax/Cross-Entropy):");
218+
if (metrics_results.size() > 1)
219219
{
220-
Logger::info(" ", ErrorCalculation::type_to_string(m.error_type()), ": ", m.error());
220+
for (const auto& m : metrics_results[1])
221+
{
222+
Logger::info(" ", ErrorCalculation::type_to_string(m.error_type()), ": ", m.error());
223+
}
221224
}
222-
}
223225

224-
// Detailed sample check
225-
Logger::info("Checking sample 0 results:");
226-
auto res0 = nn->think(inputs[0]);
227-
Logger::info("Think: ", Logger::vec_to_string(res0));
228-
Logger::info("Given: ", Logger::vec_to_string(outputs[0]));
226+
// Detailed sample check
227+
Logger::info("Checking sample 0 results:");
228+
auto res0 = nn->think(inputs[0]);
229+
Logger::info("Think: ", res0);
230+
Logger::info("Given: ", outputs[0]);
229231

230-
if (!save_nn_file.empty())
231-
{
232-
NeuralNetworkSerializer::save(*nn, save_nn_file);
232+
if (!save_nn_file.empty())
233+
{
234+
NeuralNetworkSerializer::save(*nn, save_nn_file);
235+
}
236+
} catch (const std::exception& e) {
237+
Logger::set_level(Logger::LogLevel::Error);
238+
Logger::error("ReproIssueMarketData failed with error: ", e.what());
233239
}
234240

235241
delete nn;
236242
TEST_END
237243
}
238244

239-
static void ReproIssueMarketData(const std::string& save_nn_file, const std::string& csv_file, Logger::LogLevel log_level, size_t max_rows = 0)
240-
{
241-
TEST_START("Repro Issue Market Data")
242-
NeuralNetwork* nn = create_neural_network(log_level);
243-
std::vector<std::vector<double>> inputs, outputs;
244-
245-
if (!load_csv_data(csv_file, inputs, outputs, max_rows))
245+
static void ReproIssueMarketData(const std::string& save_nn_file, const std::string& csv_file, Logger::LogLevel log_level, size_t max_rows = 0)
246246
{
247-
Logger::error("Failed to load CSV: ", csv_file);
248-
delete nn;
249-
return;
250-
}
247+
TEST_START("Repro Issue Market Data")
248+
NeuralNetwork* nn = create_neural_network(log_level);
249+
std::vector<std::vector<double>> inputs, outputs;
251250

252-
Logger::info("Loaded ", inputs.size(), " samples from ", csv_file);
253-
// Verify distribution
254-
std::vector<int> counts(5, 0);
255-
for (const auto& o : outputs)
256-
{
251+
if (!load_csv_data(csv_file, inputs, outputs, max_rows))
252+
{
253+
Logger::error("Failed to load CSV: ", csv_file);
254+
delete nn;
255+
return;
256+
}
257+
258+
Logger::info("Loaded ", inputs.size(), " samples from ", csv_file);
259+
// Verify distribution
260+
std::vector<int> counts(5, 0);
261+
for (const auto& o : outputs)
262+
{
263+
for (int i = 0; i < 5; ++i)
264+
{
265+
if (o[2 + i] > 0.5) { counts[i]++; break; }
266+
}
267+
}
257268
for (int i = 0; i < 5; ++i)
258269
{
259-
if (o[2 + i] > 0.5) { counts[i]++; break; }
270+
Logger::info("Class ", i, ": ", (double)counts[i] / outputs.size() * 100.0, "%");
260271
}
261-
}
262-
for (int i = 0; i < 5; ++i)
263-
Logger::info("Class ", i, ": ", (double)counts[i] / outputs.size() * 100.0, "%");
264272

265-
nn->train(inputs, outputs);
273+
try {
274+
nn->train(inputs, outputs);
266275

267-
// Monitoring
268-
//Logger::info("Final Softmax Weights Sum: ", nn->get_output_layer_weights_sum(1));
269-
//Logger::info("Final Logits (sample 0): ", Logger::vec_to_string(nn->get_output_layer_logits(1, inputs[0])));
276+
// Monitoring
277+
// Logger::info("Final Softmax Weights Sum: ", nn->get_output_layer_weights_sum(1));
278+
// Logger::info("Final Logits (sample 0): ", Logger::vec_to_string(nn->get_output_layer_logits(1, inputs[0])));
270279

271-
// Calculate metrics for both layers - request only appropriate metrics for each
272-
std::vector<ErrorCalculation::type> all_layer_metrics = { ErrorCalculation::type::huber_direction_loss, ErrorCalculation::type::cross_entropy, ErrorCalculation::type::directional_confidence_score, ErrorCalculation::type::prediction_coverage };
280+
// Calculate metrics for both layers - request only appropriate metrics for each
281+
std::vector<ErrorCalculation::type> all_layer_metrics = { ErrorCalculation::type::huber_direction_loss, ErrorCalculation::type::cross_entropy, ErrorCalculation::type::directional_confidence_score, ErrorCalculation::type::prediction_coverage };
273282

274-
auto all_layer_metrics_results = nn->calculate_forecast_metrics_all_layers(all_layer_metrics, true);
283+
auto all_layer_metrics_results = nn->calculate_forecast_metrics_all_layers(all_layer_metrics, true);
275284

276-
Logger::info("Layer 0 Metrics (Regression):");
277-
for (const auto& m : all_layer_metrics_results[0])
278-
{
279-
Logger::info(" ", ErrorCalculation::type_to_string(m.error_type()), ": ", m.error());
280-
}
285+
Logger::info("Layer 0 Metrics (Regression):");
286+
for (const auto& m : all_layer_metrics_results[0])
287+
{
288+
Logger::info(" ", ErrorCalculation::type_to_string(m.error_type()), ": ", m.error());
289+
}
281290

282-
Logger::info("Layer 1 Metrics (Softmax):");
283-
for (const auto& m : all_layer_metrics_results[1])
284-
{
285-
Logger::info(" ", ErrorCalculation::type_to_string(m.error_type()), ": ", m.error());
286-
}
291+
Logger::info("Layer 1 Metrics (Softmax):");
292+
for (const auto& m : all_layer_metrics_results[1])
293+
{
294+
Logger::info(" ", ErrorCalculation::type_to_string(m.error_type()), ": ", m.error());
295+
}
296+
297+
auto size = static_cast<size_t>(inputs.size() / 2.0);
287298

288-
auto res0 = nn->think(inputs[0]);
289-
Logger::debug("Think:", Logger::vec_to_string(res0));
290-
Logger::debug("Given:", Logger::vec_to_string(outputs[0]));
299+
auto res0 = nn->think(inputs[0]);
300+
Logger::debug("Think:", res0);
301+
Logger::debug("Given:", outputs[0]);
291302

292-
auto res1 = nn->think(inputs[1]);
293-
Logger::debug("Think:", Logger::vec_to_string(res1));
294-
Logger::debug("Given:", Logger::vec_to_string(outputs[1]));
303+
auto res = nn->think(inputs[size]);
304+
Logger::debug("Think:", res);
305+
Logger::debug("Given:", outputs[size]);
295306

307+
auto res1 = nn->think(inputs[1]);
308+
Logger::debug("Think:", res1);
309+
Logger::debug("Given:", outputs[1]);
296310

297-
NeuralNetworkSerializer::save(*nn, save_nn_file);
311+
NeuralNetworkSerializer::save(*nn, save_nn_file);
312+
}
313+
catch (const std::exception& e)
314+
{
315+
Logger::set_level(Logger::LogLevel::Error);
316+
Logger::error("ReproIssueMarketData failed with error: ", e.what());
317+
}
298318
delete nn;
299319
TEST_END
300320
}

0 commit comments

Comments
 (0)