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,
|
||||
int max_iterations = 5000,
|
||||
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
|
||||
|
|
|
|||
|
|
@ -516,39 +516,33 @@ T MLP<T>::Train(const training_pair_t& training_sample_set_with_bias,
|
|||
float learning_rate,
|
||||
int max_iterations,
|
||||
float min_error_cost,
|
||||
bool) {
|
||||
bool,
|
||||
const std::vector<T>* sample_weights) {
|
||||
|
||||
int i = 0;
|
||||
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++) {
|
||||
current_iteration_cost_function = 0.f;
|
||||
|
||||
auto training_features = training_sample_set_with_bias.first;
|
||||
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 +=
|
||||
_TrainOnExample(*t_feat, *t_label, learning_rate, sampleSizeReciprocal);
|
||||
|
||||
// \Payload
|
||||
if (t_feat != training_features.end())
|
||||
{
|
||||
++t_feat;
|
||||
}
|
||||
if (t_label != training_labels.end())
|
||||
{
|
||||
++t_label;
|
||||
}
|
||||
_TrainOnExample(training_features[s], training_labels[s], learning_rate, weight);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -90,6 +90,7 @@ EMSCRIPTEN_KEEPALIVE
|
|||
float nisps_mlp_train(void* ptr,
|
||||
float* features_flat, int n_samples, int feature_dim,
|
||||
float* labels_flat, int label_dim,
|
||||
float* sample_weights,
|
||||
float learning_rate, int max_iterations, float min_error) {
|
||||
|
||||
auto* mlp = static_cast<nisps::MLP<float>*>(ptr);
|
||||
|
|
@ -104,8 +105,16 @@ float nisps_mlp_train(void* ptr,
|
|||
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);
|
||||
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 ----
|
||||
|
|
|
|||
Loading…
Reference in a new issue