File indexing completed on 2026-08-16 09:21:05
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016 #ifndef ROOT_INTERNAL_ML_RDATALOADERENGINE
0017 #define ROOT_INTERNAL_ML_RDATALOADERENGINE
0018
0019 #include <condition_variable>
0020 #include <memory>
0021 #include <mutex>
0022 #include <string>
0023 #include <thread>
0024 #include <vector>
0025
0026 #include "ROOT/ML/RBatchLoader.hxx"
0027 #include "ROOT/ML/RClusterLoader.hxx"
0028 #include "ROOT/ML/RDatasetLoader.hxx"
0029 #include "ROOT/ML/RFlat2DMatrix.hxx"
0030 #include "ROOT/ML/RFlat2DMatrixOperators.hxx"
0031 #include "ROOT/ML/RSampler.hxx"
0032 #include "ROOT/RDF/InterfaceUtils.hxx"
0033
0034
0035 namespace ROOT::Experimental::ML {
0036 }
0037
0038 namespace ROOT::Experimental::Internal::ML {
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048 template <typename... Args>
0049 class RDataLoaderEngine {
0050 private:
0051 std::vector<std::string> fCols;
0052 std::vector<std::size_t> fVecSizes;
0053 std::size_t fBatchSize;
0054 std::size_t fSetSeed;
0055
0056
0057 std::size_t fBatchesInMemory;
0058 std::size_t fBufferCapacity;
0059 std::size_t fLowWatermark;
0060 std::size_t fHighWatermark;
0061
0062 std::size_t fTrainingClusterIdx{0};
0063 std::size_t fValidationClusterIdx{0};
0064
0065 float fTestSize;
0066
0067 std::unique_ptr<RDatasetLoader<Args...>> fDatasetLoader;
0068 std::unique_ptr<RClusterLoader<Args...>> fClusterLoader;
0069 std::unique_ptr<RBatchLoader> fTrainingBatchLoader;
0070 std::unique_ptr<RBatchLoader> fValidationBatchLoader;
0071 std::unique_ptr<RSampler> fTrainingSampler;
0072 std::unique_ptr<RSampler> fValidationSampler;
0073
0074 std::unique_ptr<RFlat2DMatrixOperators> fTensorOperators;
0075
0076 std::vector<ROOT::RDF::RNode> fRdfs;
0077
0078 std::unique_ptr<std::thread> fLoadingThread;
0079 std::condition_variable fLoadingCondition;
0080 std::mutex fLoadingMutex;
0081
0082 bool fDropRemainder;
0083 bool fShuffle;
0084 bool fLoadEager;
0085 std::string fSampleType;
0086 float fSampleRatio;
0087 bool fReplacement;
0088
0089 bool fIsActive{false};
0090
0091 bool fEpochActive{false};
0092 bool fTrainingEpochActive{false};
0093 bool fValidationEpochActive{false};
0094
0095 std::size_t fNumTrainingEntries;
0096 std::size_t fNumValidationEntries;
0097
0098
0099 std::vector<RFlat2DMatrix> fTrainingDatasets;
0100 std::vector<RFlat2DMatrix> fValidationDatasets;
0101
0102 RFlat2DMatrix fTrainingDataset;
0103 RFlat2DMatrix fValidationDataset;
0104
0105 RFlat2DMatrix fSampledTrainingDataset;
0106 RFlat2DMatrix fSampledValidationDataset;
0107
0108 std::size_t fTrainingEpochCount{0};
0109 std::size_t fValidationEpochCount{0};
0110
0111 public:
0112 RDataLoaderEngine(const std::vector<ROOT::RDF::RNode> &rdfs, const std::size_t batchSize,
0113 const std::size_t batchesInMemory, const std::vector<std::string> &cols,
0114 const std::vector<std::size_t> &vecSizes = {}, const float vecPadding = 0.0,
0115 const float testSize = 0.0, bool shuffle = true, bool dropRemainder = true,
0116 const std::size_t setSeed = 0, bool loadEager = false, std::string sampleType = "",
0117 float sampleRatio = 1.0, bool replacement = false)
0118 : fRdfs(rdfs),
0119 fCols(cols),
0120 fVecSizes(vecSizes),
0121 fBatchSize(batchSize),
0122 fBatchesInMemory(batchesInMemory),
0123 fTestSize(testSize),
0124 fDropRemainder(dropRemainder),
0125 fSetSeed(setSeed),
0126 fShuffle(shuffle),
0127 fLoadEager(loadEager),
0128 fSampleType(sampleType),
0129 fSampleRatio(sampleRatio),
0130 fReplacement(replacement)
0131 {
0132 fTensorOperators = std::make_unique<RFlat2DMatrixOperators>(fShuffle, fSetSeed);
0133
0134 if (fLoadEager) {
0135 fDatasetLoader = std::make_unique<RDatasetLoader<Args...>>(fRdfs, fTestSize, fCols, fVecSizes, vecPadding,
0136 fShuffle, fSetSeed);
0137 fDatasetLoader->SplitDatasets();
0138
0139 if (fSampleType == "") {
0140 fDatasetLoader->ConcatenateDatasets();
0141
0142 fTrainingDataset = fDatasetLoader->GetTrainingDataset();
0143 fValidationDataset = fDatasetLoader->GetValidationDataset();
0144
0145 fNumTrainingEntries = fDatasetLoader->GetNumTrainingEntries();
0146 fNumValidationEntries = fDatasetLoader->GetNumValidationEntries();
0147 }
0148
0149 else {
0150 fTrainingDatasets = fDatasetLoader->GetTrainingDatasets();
0151 fValidationDatasets = fDatasetLoader->GetValidationDatasets();
0152
0153 fTrainingSampler = std::make_unique<RSampler>(fTrainingDatasets, fSampleType, fSampleRatio, fReplacement,
0154 fShuffle, fSetSeed);
0155 fValidationSampler = std::make_unique<RSampler>(fValidationDatasets, fSampleType, fSampleRatio,
0156 fReplacement, fShuffle, fSetSeed);
0157
0158 fNumTrainingEntries = fTrainingSampler->GetNumEntries();
0159 fNumValidationEntries = fValidationSampler->GetNumEntries();
0160 }
0161 }
0162
0163 else {
0164
0165 fClusterLoader = std::make_unique<RClusterLoader<Args...>>(fRdfs, fCols, fVecSizes, vecPadding, fTestSize,
0166 fShuffle, fSetSeed);
0167
0168
0169 fBufferCapacity = fBatchSize * fBatchesInMemory;
0170 fLowWatermark = fBufferCapacity / 2;
0171 fHighWatermark = fBufferCapacity;
0172
0173
0174 fClusterLoader->SplitDataset();
0175 fNumTrainingEntries = fClusterLoader->GetNumTrainingEntries();
0176 fNumValidationEntries = fClusterLoader->GetNumValidationEntries();
0177 }
0178
0179 fTrainingBatchLoader = std::make_unique<RBatchLoader>(fBatchSize, fCols, fLoadingMutex, fLoadingCondition,
0180 fVecSizes, fNumTrainingEntries, fDropRemainder);
0181 fValidationBatchLoader = std::make_unique<RBatchLoader>(fBatchSize, fCols, fLoadingMutex, fLoadingCondition,
0182 fVecSizes, fNumValidationEntries, fDropRemainder);
0183 }
0184
0185 ~RDataLoaderEngine() { DeActivate(); }
0186
0187 void DeActivate()
0188 {
0189 {
0190 std::lock_guard<std::mutex> lock(fLoadingMutex);
0191 if (!fIsActive)
0192 return;
0193 fIsActive = false;
0194 }
0195
0196 fLoadingCondition.notify_all();
0197
0198 if (fLoadingThread) {
0199 if (fLoadingThread->joinable()) {
0200 fLoadingThread->join();
0201 }
0202 }
0203
0204 fLoadingThread.reset();
0205 }
0206
0207
0208 void Activate()
0209 {
0210 {
0211 std::lock_guard<std::mutex> lock(fLoadingMutex);
0212 if (fIsActive)
0213 return;
0214
0215 fIsActive = true;
0216 }
0217
0218 if (fLoadEager) {
0219 return;
0220 }
0221
0222 fLoadingThread = std::make_unique<std::thread>(&RDataLoaderEngine::LoadData, this);
0223 }
0224
0225
0226 void ActivateTrainingEpoch()
0227 {
0228 {
0229 std::lock_guard<std::mutex> lock(fLoadingMutex);
0230 fTrainingEpochActive = true;
0231 fTrainingClusterIdx = 0;
0232 if (!fLoadEager) {
0233
0234 fClusterLoader->ShuffleTrainingClusters(fTrainingEpochCount++);
0235 }
0236 }
0237
0238 fTrainingBatchLoader->Activate();
0239 fLoadingCondition.notify_all();
0240 }
0241
0242 void DeActivateTrainingEpoch()
0243 {
0244 {
0245 std::lock_guard<std::mutex> lock(fLoadingMutex);
0246 fTrainingEpochActive = false;
0247 }
0248
0249 fTrainingBatchLoader->Reset();
0250 fTrainingBatchLoader->DeActivate();
0251 fLoadingCondition.notify_all();
0252 }
0253
0254 void ActivateValidationEpoch()
0255 {
0256 {
0257 std::lock_guard<std::mutex> lock(fLoadingMutex);
0258 fValidationEpochActive = true;
0259 fValidationClusterIdx = 0;
0260 if (!fLoadEager) {
0261 fClusterLoader->ShuffleValidationClusters(fValidationEpochCount++);
0262 }
0263 }
0264
0265 fValidationBatchLoader->Activate();
0266 fLoadingCondition.notify_all();
0267 }
0268
0269 void DeActivateValidationEpoch()
0270 {
0271 {
0272 std::lock_guard<std::mutex> lock(fLoadingMutex);
0273 fValidationEpochActive = false;
0274 }
0275
0276 fValidationBatchLoader->Reset();
0277 fValidationBatchLoader->DeActivate();
0278 fLoadingCondition.notify_all();
0279 }
0280
0281
0282
0283
0284 void LoadData()
0285 {
0286 std::unique_lock<std::mutex> lock(fLoadingMutex);
0287
0288 while (true) {
0289
0290 fLoadingCondition.wait(lock, [&] {
0291 return !fIsActive ||
0292 (fTrainingEpochActive && fTrainingClusterIdx < fClusterLoader->GetNumTrainingClusters()) ||
0293 (fValidationEpochActive && fValidationClusterIdx < fClusterLoader->GetNumValidationClusters());
0294 });
0295
0296 if (!fIsActive) {
0297 break;
0298 }
0299
0300
0301 auto validationEmpty = [&] {
0302 if (!fValidationEpochActive || fValidationClusterIdx >= fClusterLoader->GetNumValidationClusters())
0303 return false;
0304 if (fValidationBatchLoader->isProducerDone())
0305 return false;
0306 return fValidationBatchLoader->GetNumBatchQueue() < fLowWatermark / fBatchSize;
0307 };
0308
0309
0310 if (fTrainingEpochActive) {
0311 const std::size_t numTrainingClusters = fClusterLoader->GetNumTrainingClusters();
0312
0313 while (true) {
0314
0315 if (!fIsActive || !fTrainingEpochActive)
0316 break;
0317
0318
0319 if (fTrainingClusterIdx >= numTrainingClusters) {
0320 fTrainingBatchLoader->MarkProducerDone();
0321 break;
0322 }
0323
0324
0325
0326
0327 if (validationEmpty()) {
0328 break;
0329 }
0330
0331
0332
0333 if (fTrainingBatchLoader->GetNumBatchQueue() >= fLowWatermark / fBatchSize) {
0334 fLoadingCondition.wait(lock, [&] {
0335 return !fIsActive || !fTrainingEpochActive ||
0336 fTrainingBatchLoader->GetNumBatchQueue() < (fLowWatermark / fBatchSize) ||
0337 validationEmpty();
0338 });
0339 continue;
0340 }
0341
0342
0343 std::vector<RClusterRange> trainClustersToLoad;
0344 auto accumulatedEntries = 0;
0345 const bool discovering = !fClusterLoader->IsSplitDiscovered();
0346 while (fTrainingClusterIdx < numTrainingClusters && accumulatedEntries < fBufferCapacity &&
0347 (!discovering || trainClustersToLoad.empty())) {
0348 const auto &cluster = fClusterLoader->GetTrainingClusters()[fTrainingClusterIdx++];
0349 trainClustersToLoad.push_back(cluster);
0350 accumulatedEntries += cluster.GetNumEntries();
0351 }
0352
0353 const bool isLastBuffer = (fTrainingClusterIdx >= numTrainingClusters);
0354
0355
0356
0357
0358 lock.unlock();
0359 RFlat2DMatrix stagingBuffer(accumulatedEntries, fClusterLoader->GetNumChunkCols());
0360 std::size_t rowOffset = 0;
0361
0362 for (auto &cluster : trainClustersToLoad) {
0363 auto loadedEntries = fClusterLoader->LoadTrainingClusterInto(stagingBuffer, cluster.rdfIdx,
0364 cluster.start, cluster.end, rowOffset);
0365 if (discovering) {
0366
0367
0368 cluster.SetNumEntries(loadedEntries);
0369 }
0370 rowOffset += cluster.GetNumEntries();
0371 }
0372
0373 if (discovering && fNumTrainingEntries == 0 && fClusterLoader->GetNumTrainingEntries() > 0) {
0374 fNumTrainingEntries = fClusterLoader->GetNumTrainingEntries();
0375 fNumValidationEntries = fClusterLoader->GetNumValidationEntries();
0376 fTrainingBatchLoader->RecalculateBatchCounts(fNumTrainingEntries);
0377 fValidationBatchLoader->RecalculateBatchCounts(fNumValidationEntries);
0378 }
0379
0380 if (rowOffset < static_cast<std::size_t>(accumulatedEntries)) {
0381 stagingBuffer.Resize(rowOffset, stagingBuffer.GetCols());
0382 }
0383
0384 RFlat2DMatrix shuffledStagingBuffer;
0385 fTensorOperators->ShuffleTensor(shuffledStagingBuffer, stagingBuffer);
0386 fTrainingBatchLoader->CreateBatches(shuffledStagingBuffer, isLastBuffer);
0387
0388
0389 lock.lock();
0390
0391 if (isLastBuffer && discovering) {
0392 fClusterLoader->FinaliseSplitDiscovery();
0393 }
0394 }
0395 }
0396
0397
0398 if (fValidationEpochActive) {
0399 const std::size_t numValidationClusters = fClusterLoader->GetNumValidationClusters();
0400
0401 while (true) {
0402
0403 if (!fIsActive || !fValidationEpochActive)
0404 break;
0405
0406
0407 if (fValidationClusterIdx >= numValidationClusters) {
0408 fValidationBatchLoader->MarkProducerDone();
0409 break;
0410 }
0411
0412
0413 if (fValidationBatchLoader->GetNumBatchQueue() >= (fLowWatermark / fBatchSize)) {
0414 fLoadingCondition.wait(lock, [&] {
0415 return !fIsActive || !fValidationEpochActive ||
0416 fValidationBatchLoader->GetNumBatchQueue() < (fLowWatermark / fBatchSize);
0417 });
0418 continue;
0419 }
0420
0421
0422 std::vector<RClusterRange> valClustersToLoad;
0423 auto accumulatedEntries = 0;
0424 while (fValidationClusterIdx < numValidationClusters && accumulatedEntries < fBufferCapacity) {
0425 const auto &cluster = fClusterLoader->GetValidationClusters()[fValidationClusterIdx++];
0426 valClustersToLoad.push_back(cluster);
0427 accumulatedEntries += cluster.GetNumEntries();
0428 }
0429
0430 const bool isLastBuffer = (fValidationClusterIdx >= numValidationClusters);
0431
0432 lock.unlock();
0433
0434 RFlat2DMatrix stagingBuffer(accumulatedEntries, fClusterLoader->GetNumChunkCols());
0435 std::size_t rowOffset = 0;
0436
0437 for (const auto &cluster : valClustersToLoad) {
0438 fClusterLoader->LoadValidationClusterInto(stagingBuffer, cluster.rdfIdx, cluster.start, cluster.end,
0439 rowOffset);
0440 rowOffset += cluster.GetNumEntries();
0441 }
0442
0443 RFlat2DMatrix shuffledStagingBuffer;
0444 fTensorOperators->ShuffleTensor(shuffledStagingBuffer, stagingBuffer);
0445 fValidationBatchLoader->CreateBatches(shuffledStagingBuffer, isLastBuffer);
0446
0447 lock.lock();
0448 }
0449 }
0450 }
0451 }
0452
0453
0454
0455 void CreateTrainBatches()
0456 {
0457 fTrainingBatchLoader->Activate();
0458
0459 if (fLoadEager) {
0460 if (fSampleType == "") {
0461 fTensorOperators->ShuffleTensor(fSampledTrainingDataset, fTrainingDataset);
0462 }
0463
0464 else {
0465 fTrainingSampler->Sampler(fSampledTrainingDataset);
0466 }
0467
0468 fTrainingBatchLoader->CreateBatches(fSampledTrainingDataset, true);
0469 fTrainingBatchLoader->MarkProducerDone();
0470 }
0471 }
0472
0473
0474
0475 void CreateValidationBatches()
0476 {
0477 fValidationBatchLoader->Activate();
0478
0479 if (fLoadEager) {
0480 if (fSampleType == "") {
0481 fTensorOperators->ShuffleTensor(fSampledValidationDataset, fValidationDataset);
0482 }
0483
0484 else {
0485 fValidationSampler->Sampler(fSampledValidationDataset);
0486 }
0487
0488 fValidationBatchLoader->CreateBatches(fSampledValidationDataset, true);
0489 fValidationBatchLoader->MarkProducerDone();
0490 }
0491 }
0492
0493
0494 RFlat2DMatrix GetTrainBatch()
0495 {
0496
0497 return fTrainingBatchLoader->GetBatch();
0498 }
0499
0500
0501 RFlat2DMatrix GetValidationBatch()
0502 {
0503
0504 return fValidationBatchLoader->GetBatch();
0505 }
0506
0507 std::size_t NumberOfTrainingBatches() { return fTrainingBatchLoader->GetNumBatches(); }
0508 std::size_t NumberOfValidationBatches() { return fValidationBatchLoader->GetNumBatches(); }
0509
0510 std::size_t TrainRemainderRows() { return fTrainingBatchLoader->GetNumRemainderRows(); }
0511 std::size_t ValidationRemainderRows() { return fValidationBatchLoader->GetNumRemainderRows(); }
0512
0513 bool IsActive()
0514 {
0515 std::lock_guard<std::mutex> lock(fLoadingMutex);
0516 return fIsActive;
0517 }
0518
0519 bool IsTrainingActive()
0520 {
0521 std::lock_guard<std::mutex> lock(fLoadingMutex);
0522 return fTrainingEpochActive;
0523 }
0524
0525 bool IsValidationActive()
0526 {
0527 std::lock_guard<std::mutex> lock(fLoadingMutex);
0528 return fValidationEpochActive;
0529 }
0530 };
0531
0532 }
0533
0534 #endif