feat(nisps-core,wasm): add per-sample weights to MLP training
Add optional sample_weights parameter to MLP::Train() and the WASM nisps_mlp_train binding. When provided, weights replace the uniform 1/N scaling per sample — enabling recency, spatial, or any custom importance weighting without changing the training interface.
This commit is contained in:
parent
456c426cb1
commit
c8d7779699
4 changed files with 24 additions and 20 deletions
|
|
@ -130,7 +130,8 @@ public:
|
||||||
float learning_rate,
|
float learning_rate,
|
||||||
int max_iterations = 5000,
|
int max_iterations = 5000,
|
||||||
float min_error_cost = 0.001,
|
float min_error_cost = 0.001,
|
||||||
bool output_log = true);
|
bool output_log = true,
|
||||||
|
const std::vector<T>* sample_weights = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Training with batch support
|
* @brief Training with batch support
|
||||||
|
|
|
||||||
|
|
@ -516,39 +516,33 @@ T MLP<T>::Train(const training_pair_t& training_sample_set_with_bias,
|
||||||
float learning_rate,
|
float learning_rate,
|
||||||
int max_iterations,
|
int max_iterations,
|
||||||
float min_error_cost,
|
float min_error_cost,
|
||||||
bool) {
|
bool,
|
||||||
|
const std::vector<T>* sample_weights) {
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
T current_iteration_cost_function = 0.f;
|
T current_iteration_cost_function = 0.f;
|
||||||
|
|
||||||
T sampleSizeReciprocal = 1.f / training_sample_set_with_bias.first.size();
|
const size_t n_samples = training_sample_set_with_bias.first.size();
|
||||||
|
T sampleSizeReciprocal = 1.f / n_samples;
|
||||||
|
|
||||||
for (i = 0; i < max_iterations; i++) {
|
for (i = 0; i < max_iterations; i++) {
|
||||||
current_iteration_cost_function = 0.f;
|
current_iteration_cost_function = 0.f;
|
||||||
|
|
||||||
auto training_features = training_sample_set_with_bias.first;
|
auto training_features = training_sample_set_with_bias.first;
|
||||||
auto training_labels = training_sample_set_with_bias.second;
|
auto training_labels = training_sample_set_with_bias.second;
|
||||||
auto t_feat = training_features.begin();
|
|
||||||
auto t_label = training_labels.begin();
|
|
||||||
|
|
||||||
while (t_feat != training_features.end() || t_label != training_labels.end()) {
|
for (size_t s = 0; s < n_samples; s++) {
|
||||||
|
T weight = sample_weights ? (*sample_weights)[s] : sampleSizeReciprocal;
|
||||||
|
|
||||||
// Payload
|
|
||||||
current_iteration_cost_function +=
|
current_iteration_cost_function +=
|
||||||
_TrainOnExample(*t_feat, *t_label, learning_rate, sampleSizeReciprocal);
|
_TrainOnExample(training_features[s], training_labels[s], learning_rate, weight);
|
||||||
|
|
||||||
// \Payload
|
|
||||||
if (t_feat != training_features.end())
|
|
||||||
{
|
|
||||||
++t_feat;
|
|
||||||
}
|
|
||||||
if (t_label != training_labels.end())
|
|
||||||
{
|
|
||||||
++t_label;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
current_iteration_cost_function *= sampleSizeReciprocal;
|
// When using custom weights (already normalized to sum to 1), loss is already scaled.
|
||||||
|
// With uniform weights, multiply by sampleSizeReciprocal for backward compat.
|
||||||
|
if (!sample_weights) {
|
||||||
|
current_iteration_cost_function *= sampleSizeReciprocal;
|
||||||
|
}
|
||||||
|
|
||||||
ReportProgress(true, 100, i, current_iteration_cost_function);
|
ReportProgress(true, 100, i, current_iteration_cost_function);
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -90,6 +90,7 @@ EMSCRIPTEN_KEEPALIVE
|
||||||
float nisps_mlp_train(void* ptr,
|
float nisps_mlp_train(void* ptr,
|
||||||
float* features_flat, int n_samples, int feature_dim,
|
float* features_flat, int n_samples, int feature_dim,
|
||||||
float* labels_flat, int label_dim,
|
float* labels_flat, int label_dim,
|
||||||
|
float* sample_weights,
|
||||||
float learning_rate, int max_iterations, float min_error) {
|
float learning_rate, int max_iterations, float min_error) {
|
||||||
|
|
||||||
auto* mlp = static_cast<nisps::MLP<float>*>(ptr);
|
auto* mlp = static_cast<nisps::MLP<float>*>(ptr);
|
||||||
|
|
@ -104,8 +105,16 @@ float nisps_mlp_train(void* ptr,
|
||||||
labels_flat + (i + 1) * label_dim);
|
labels_flat + (i + 1) * label_dim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build weights vector if provided (non-null pointer)
|
||||||
|
std::vector<float> weights_vec;
|
||||||
|
const std::vector<float>* weights_ptr = nullptr;
|
||||||
|
if (sample_weights) {
|
||||||
|
weights_vec.assign(sample_weights, sample_weights + n_samples);
|
||||||
|
weights_ptr = &weights_vec;
|
||||||
|
}
|
||||||
|
|
||||||
nisps::MLP<float>::training_pair_t data(features, labels);
|
nisps::MLP<float>::training_pair_t data(features, labels);
|
||||||
return mlp->Train(data, learning_rate, max_iterations, min_error, false);
|
return mlp->Train(data, learning_rate, max_iterations, min_error, false, weights_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Weight manipulation with spread ----
|
// ---- Weight manipulation with spread ----
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue