8 #include "trg/cdc/modules/neurotrigger/CDCTriggerNeuroTrainerModule.h"
10 #include <parallel_fann.hpp>
15 #include <framework/datastore/StoreArray.h>
16 #include <mdst/dataobjects/MCParticle.h>
17 #include <tracking/dataobjects/RecoTrack.h>
18 #include <trg/cdc/dataobjects/CDCTriggerSegmentHit.h>
19 #include <trg/cdc/dataobjects/CDCTriggerTrack.h>
20 #include <framework/datastore/StoreObjPtr.h>
21 #include <framework/dataobjects/EventMetaData.h>
22 #include <framework/core/ModuleParam.templateDetails.h>
24 #include <cdc/geometry/CDCGeometryPar.h>
25 #include <framework/gearbox/Unit.h>
41 "The NeuroTriggerTrainer module of the CDC trigger.\n"
42 "Takes track segments and 2D track estimates to prepare input data\n"
43 "for the training of a neural network.\n"
44 "Networks are trained after the event loop and saved.\n\n"
45 "Data preparation is done in two steps:\n"
46 "1. The MLP uses hits from a limited range around the 2D track. "
47 "To find this range, a histogram with the distance of hits to the 2D track "
48 "is prepared. The relevant ID range is determined by a threshold on "
49 "the hit counters or on the sum of the hit counters over the relevant range.\n"
50 "2. Input data is calculated from the hits, the 2D tracks and the ID ranges. "
51 "Target data is collected from a MCParticle or RecoTrack related to the 2D track."
54 addParam(
"hitCollectionName", m_hitCollectionName,
55 "Name of the input StoreArray of CDCTriggerSegmentHits.",
57 addParam(
"EventTimeName", m_EventTimeName,
58 "Name of the event time object.",
60 addParam(
"inputCollectionName", m_inputCollectionName,
61 "Name of the StoreArray holding the 2D input tracks.",
62 string(
"TRGCDC2DFinderTracks"));
63 addParam(
"trainOnRecoTracks", m_trainOnRecoTracks,
64 "If true, use RecoTracks as targets instead of MCParticles.",
66 addParam(
"targetCollectionName", m_targetCollectionName,
67 "Name of the MCParticle/RecoTrack collection used as target values.",
68 string(
"MCParticles"));
69 addParam(
"filename", m_filename,
70 "Name of the root file where the NeuroTrigger parameters will be saved.",
71 string(
"NeuroTrigger.root"));
72 addParam(
"trainFilename", m_trainFilename,
73 "Name of the root file where the generated training samples will be saved.",
74 string(
"NeuroTrigger.root"));
75 addParam(
"logFilename", m_logFilename,
76 "Base name of the text files where the training logs will be saved "
77 "(two for each sector, named logFilename_BestRun_i.log "
78 "and logFilename_AllOptima_i.log).",
79 string(
"NeuroTrigger"));
80 addParam(
"arrayname", m_arrayname,
81 "Name of the TObjArray to hold the NeuroTrigger parameters.",
83 addParam(
"trainArrayname", m_trainArrayname,
84 "Name of the TObjArray to hold the training samples.",
86 addParam(
"saveDebug", m_saveDebug,
87 "If true, save parameter distribution of training data "
88 "in train file and training curve in log file.",
true);
89 addParam(
"load", m_load,
90 "Switch to load saved parameters if existing. "
91 "Take care not to duplicate training sets!",
false);
93 addParam(
"nMLP", m_parameters.nMLP,
94 "Number of expert MLPs.", m_parameters.nMLP);
95 addParam(
"nHidden", m_parameters.nHidden,
96 "Number of nodes in each hidden layer for all networks "
97 "or factor to multiply with number of inputs (1 list or nMLP lists). "
98 "The number of layers is derived from the shape.", m_parameters.nHidden);
99 addParam(
"multiplyHidden", m_parameters.multiplyHidden,
100 "If true, multiply nHidden with number of input nodes.",
101 m_parameters.multiplyHidden);
102 addParam(
"targetZ", m_parameters.targetZ,
103 "Train one output of MLP to give z.", m_parameters.targetZ);
104 addParam(
"targetTheta", m_parameters.targetTheta,
105 "Train one output of MLP to give theta.", m_parameters.targetTheta);
106 addParam(
"outputScale", m_parameters.outputScale,
107 "Output scale for all networks (1 value list or nMLP value lists). "
108 "Output[i] of the MLP is scaled from [-1, 1] "
109 "to [outputScale[2*i], outputScale[2*i+1]]. "
110 "(units: z[cm] / theta[degree])", m_parameters.outputScale);
111 addParam(
"phiRange", m_parameters.phiRange,
112 "Phi region in degree for which experts are trained. "
113 "1 value pair, nMLP value pairs or nPhi value pairs "
114 "with nPhi * nPt * nTheta * nPattern = nMLP.", m_parameters.phiRange);
115 addParam(
"invptRange", m_parameters.invptRange,
116 "Charge / Pt region in 1/GeV for which experts are trained. "
117 "1 value pair, nMLP value pairs or nPt value pairs "
118 "with nPhi * nPt * nTheta * nPattern = nMLP.", m_parameters.invptRange);
119 addParam(
"thetaRange", m_parameters.thetaRange,
120 "Theta region in degree for which experts are trained. "
121 "1 value pair, nMLP value pairs or nTheta value pairs "
122 "with nPhi * nPt * nTheta * nPattern = nMLP.", m_parameters.thetaRange);
123 addParam(
"phiRangeTrain", m_parameters.phiRangeTrain,
124 "Phi region in degree from which training events are taken. "
125 "Can be larger than phiRange to avoid edge effect.", m_parameters.phiRangeTrain);
126 addParam(
"invptRangeTrain", m_parameters.invptRangeTrain,
127 "Charge / Pt region in 1/GeV from which training events are taken. "
128 "Can be larger than phiRange to avoid edge effect.", m_parameters.invptRangeTrain);
129 addParam(
"thetaRangeTrain", m_parameters.thetaRangeTrain,
130 "Theta region in degree from which training events are taken. "
131 "Can be larger than phiRange to avoid edge effect.", m_parameters.thetaRangeTrain);
132 addParam(
"maxHitsPerSL", m_parameters.maxHitsPerSL,
133 "Maximum number of hits in a single SL. "
134 "1 value or same as SLpattern.", m_parameters.maxHitsPerSL);
135 addParam(
"SLpattern", m_parameters.SLpattern,
136 "Super layer pattern for which experts are trained. "
137 "1 value, nMLP values or nPattern values "
138 "with nPhi * nPt * nTheta * nPattern = nMLP.", m_parameters.SLpattern);
139 addParam(
"SLpatternMask", m_parameters.SLpatternMask,
140 "Super layer pattern mask for which experts are trained. "
141 "1 value or same as SLpattern.", m_parameters.SLpatternMask);
142 addParam(
"tMax", m_parameters.tMax,
143 "Maximal drift time (for scaling, unit: trigger timing bins).", m_parameters.tMax);
144 addParam(
"et_option", m_parameters.et_option,
145 "option on how to obtain the event time. Possibilities are: "
146 "'etf_only', 'fastestpriority', 'zero', 'etf_or_fastestpriority', 'etf_or_zero', 'etf_or_fastest2d', 'fastest2d'.",
147 m_parameters.et_option);
148 addParam(
"T0fromHits", m_parameters.T0fromHits,
149 "Deprecated, kept for backward compatibility. If true, the event time is "
150 "determined from all relevant hits in a sector, if there is no valid event "
151 "time from the event time finder. If false, no drift times are used if "
152 "there is no valid event time.",
153 m_parameters.T0fromHits);
154 addParam(
"selectSectorByMC", m_selectSectorByMC,
155 "If true, track parameters for sector selection are taken "
156 "from MCParticle instead of CDCTriggerTrack.",
false);
158 addParam(
"nTrainPrepare", m_nTrainPrepare,
159 "Number of samples for preparation of relevant ID ranges "
160 "(0: use default ranges).", 1000);
161 addParam(
"IDranges", m_IDranges,
162 "If list is not empty, it will replace the default ranges. "
163 "1 list or nMLP lists. Set nTrainPrepare to 0 if you use this option.",
165 addParam(
"relevantCut", m_relevantCut,
166 "Cut for preparation of relevant ID ranges.", 0.02);
167 addParam(
"cutSum", m_cutSum,
168 "If true, relevantCut is applied to the sum over hit counters, "
169 "otherwise directly on the hit counters.",
false);
170 addParam(
"nTrainMin", m_nTrainMin,
171 "Minimal number of training samples "
172 "or factor to multiply with number of weights. "
173 "If the minimal number of samples is not reached, "
174 "all samples are saved but no training is started.", 10.);
175 addParam(
"nTrainMax", m_nTrainMax,
176 "Maximal number of training samples "
177 "or factor to multiply with number of weights. "
178 "When the maximal number of samples is reached, "
179 "no further samples are added.", 10.);
180 addParam(
"multiplyNTrain", m_multiplyNTrain,
181 "If true, multiply nTrainMin and nTrainMax with number of weights.",
183 addParam(
"nValid", m_nValid,
184 "Number of validation samples for training.", 1000);
185 addParam(
"nTest", m_nTest,
186 "Number of test samples to get resolution after training.", 5000);
187 addParam(
"stopLoop", m_stopLoop,
188 "If true, stop event loop when maximal number of samples "
189 "is reached for all sectors.",
true);
190 addParam(
"rescaleTarget", m_rescaleTarget,
191 "If true, set target values > outputScale to 1, "
192 "else skip them.",
true);
194 addParam(
"wMax", m_wMax,
195 "Weights are limited to [-wMax, wMax] after each training epoch "
196 "(for convenience of the FPGA implementation).",
198 addParam(
"nThreads", m_nThreads,
199 "Number of threads for parallel training.", 1);
200 addParam(
"checkInterval", m_checkInterval,
201 "Training is stopped if validation error is higher than "
202 "checkInterval epochs ago, i.e. either the validation error is increasing "
203 "or the gain is less than the fluctuations.", 500);
204 addParam(
"maxEpochs", m_maxEpochs,
205 "Maximum number of training epochs.", 10000);
206 addParam(
"repeatTrain", m_repeatTrain,
207 "If >1, training is repeated several times with different start weights. "
208 "The weights which give the best resolution on the test samples are kept.", 1);
209 addParam(
"NeuroTrackInputMode", m_neuroTrackInputMode,
210 "When using real tracks, use neurotracks instead of 2dtracks as input to the neurotrigger",
219 m_tracks.isRequired(m_inputCollectionName);
220 if (m_trainOnRecoTracks) {
222 targets.isRequired(m_targetCollectionName);
225 targets.isRequired(m_targetCollectionName);
229 !loadTraindata(m_trainFilename, m_trainArrayname) ||
230 !m_NeuroTrigger.load(m_filename, m_arrayname)) {
231 m_NeuroTrigger.initialize(m_parameters);
234 for (
unsigned iMLP = 0; iMLP < m_NeuroTrigger.nSectors(); ++iMLP) {
237 for (
int iSL = 0; iSL < 9; ++iSL) {
238 m_trainSets[iMLP].addCounters(cdc.nWiresInLayer(layerId));
239 layerId += (iSL > 0 ? 6 : 7);
243 m_NeuroTrigger.initializeCollections(m_hitCollectionName, m_EventTimeName, m_parameters.et_option);
245 if (m_NeuroTrigger.nSectors() != m_trainSets.size())
246 B2ERROR(
"Number of training sets (" << m_trainSets.size() <<
") should match " <<
247 "number of sectors (" << m_NeuroTrigger.nSectors() <<
")");
248 if (m_nTrainMin > m_nTrainMax) {
249 m_nTrainMin = m_nTrainMax;
250 B2WARNING(
"nTrainMin set to " << m_nTrainMin <<
" (was larger than nTrainMax)");
253 if (m_IDranges.size() > 0) {
254 if (m_IDranges.size() == 1 || m_IDranges.size() == m_NeuroTrigger.nSectors()) {
255 B2DEBUG(50,
"Setting relevant ID ranges from parameters.");
256 for (
unsigned isector = 0; isector < m_NeuroTrigger.nSectors(); ++isector) {
257 unsigned iranges = (m_IDranges.size() == 1) ? 0 : isector;
258 if (m_IDranges[iranges].size() == 18)
259 m_NeuroTrigger[isector].relevantID = m_IDranges[iranges];
261 B2ERROR(
"IDranges must contain 18 values (sector " << isector
262 <<
" has " << m_IDranges[iranges].size() <<
")");
264 if (m_nTrainPrepare > 0)
265 B2WARNING(
"Given ID ranges will be replaced during training. "
266 "Set nTrainPrepare = 0 if you want to give ID ranges by hand.");
268 B2ERROR(
"Number of IDranges should be 0, 1, or " << m_NeuroTrigger.nSectors());
274 for (
unsigned iMLP = 0; iMLP < m_NeuroTrigger.nSectors(); ++iMLP) {
275 phiHistsMC.push_back(
276 new TH1D((
"phiMC" + to_string(iMLP)).c_str(),
277 (
"MC phi in sector " + to_string(iMLP)).c_str(),
278 100, -2 * M_PI, 2 * M_PI));
280 new TH1D((
"ptMC" + to_string(iMLP)).c_str(),
281 (
"MC charge / pt in sector " + to_string(iMLP)).c_str(),
283 thetaHistsMC.push_back(
284 new TH1D((
"thetaMC" + to_string(iMLP)).c_str(),
285 (
"MC theta in sector " + to_string(iMLP)).c_str(),
288 new TH1D((
"zMC" + to_string(iMLP)).c_str(),
289 (
"MC z in sector " + to_string(iMLP)).c_str(),
291 phiHists2D.push_back(
292 new TH1D((
"phi2D" + to_string(iMLP)).c_str(),
293 (
"2D phi in sector " + to_string(iMLP)).c_str(),
294 100, -2 * M_PI, 2 * M_PI));
296 new TH1D((
"pt2D" + to_string(iMLP)).c_str(),
297 (
"2D charge / pt in sector " + to_string(iMLP)).c_str(),
306 for (
int itrack = 0; itrack < m_tracks.getEntries(); ++itrack) {
309 float phi0Target = 0;
310 float invptTarget = 0;
311 float thetaTarget = 0;
313 if (m_trainOnRecoTracks) {
317 B2DEBUG(150,
"Skipping CDCTriggerTrack without relation to RecoTrack.");
323 bool foundValidRep =
false;
324 for (
unsigned irep = 0; irep < reps.size() && !foundValidRep; ++irep) {
332 reps[irep]->extrapolateToLine(state, TVector3(0, 0, -1000), TVector3(0, 0, 2000));
335 if (state.getMom().Dot(m_tracks[itrack]->getDirection()) < 0) {
336 state.setPosMom(state.getPos(), -state.getMom());
337 state.setChargeSign(-state.getCharge());
340 phi0Target = state.getMom().Phi();
341 invptTarget = state.getCharge() / state.getMom().Pt();
342 thetaTarget = state.getMom().Theta();
343 zTarget = state.getPos().Z();
348 foundValidRep =
true;
350 if (!foundValidRep) {
351 B2DEBUG(150,
"No valid representation found for RecoTrack, skipping.");
358 B2DEBUG(150,
"Skipping CDCTriggerTrack without relation to MCParticle.");
368 m_NeuroTrigger.updateTrack(*m_tracks[itrack]);
371 float phi0 = m_tracks[itrack]->getPhi0();
372 float invpt = m_tracks[itrack]->getKappa(1.5);
373 float theta = atan2(1., m_tracks[itrack]->getCotTheta());
374 if (m_selectSectorByMC) {
379 vector<int> sectors = m_NeuroTrigger.selectMLPs(phi0, invpt, theta);
380 if (sectors.size() == 0)
continue;
382 vector<float> targetRaw = {};
383 if (m_parameters.targetZ)
384 targetRaw.push_back(zTarget);
385 if (m_parameters.targetTheta)
386 targetRaw.push_back(thetaTarget);
387 for (
unsigned i = 0; i < sectors.size(); ++i) {
388 int isector = sectors[i];
389 vector<float> target = m_NeuroTrigger[isector].scaleTarget(targetRaw);
391 bool outOfRange =
false;
392 for (
unsigned itarget = 0; itarget < target.size(); ++itarget) {
393 if (fabs(target[itarget]) > 1.) {
395 target[itarget] /= fabs(target[itarget]);
398 if (!m_rescaleTarget && outOfRange)
continue;
400 if (m_nTrainPrepare > 0 &&
401 m_trainSets[isector].getTrackCounter() < m_nTrainPrepare) {
405 if (m_trainOnRecoTracks) {
411 double relId = m_NeuroTrigger.getRelId(hit);
412 m_trainSets[isector].
addHit(hit.getISuperLayer(), round(relId));
420 double relId = m_NeuroTrigger.getRelId(hit);
421 m_trainSets[isector].addHit(hit.getISuperLayer(), round(relId));
424 m_trainSets[isector].countTrack();
426 if (m_trainSets[isector].getTrackCounter() >= m_nTrainPrepare) {
427 updateRelevantID(isector);
431 float nTrainMax = m_multiplyNTrain ? m_nTrainMax * m_NeuroTrigger[isector].nWeights() : m_nTrainMax;
432 if (m_trainSets[isector].nSamples() > (nTrainMax + m_nValid + m_nTest)) {
436 m_NeuroTrigger.getEventTime(isector, *m_tracks[itrack], m_parameters.et_option, m_neuroTrackInputMode);
438 unsigned long hitPattern = m_NeuroTrigger.getInputPattern(isector, *m_tracks[itrack], m_neuroTrackInputMode);
439 unsigned long sectorPattern = m_NeuroTrigger[isector].getSLpattern();
440 B2DEBUG(250,
"hitPattern " << hitPattern <<
" sectorPattern " << sectorPattern);
441 if (sectorPattern > 0 && (sectorPattern & hitPattern) != sectorPattern) {
442 B2DEBUG(250,
"hitPattern not matching " << (sectorPattern & hitPattern));
446 vector<unsigned> hitIds;
447 if (m_neuroTrackInputMode) {
448 hitIds = m_NeuroTrigger.selectHitsHWSim(isector, *m_tracks[itrack]);
450 hitIds = m_NeuroTrigger.selectHits(isector, *m_tracks[itrack]);
452 m_trainSets[isector].addSample(m_NeuroTrigger.getInputVector(isector, hitIds), target);
454 phiHistsMC[isector]->Fill(phi0Target);
455 ptHistsMC[isector]->Fill(invptTarget);
456 thetaHistsMC[isector]->Fill(thetaTarget);
457 zHistsMC[isector]->Fill(zTarget);
458 phiHists2D[isector]->Fill(m_tracks[itrack]->getPhi0());
459 ptHists2D[isector]->Fill(m_tracks[itrack]->getKappa(1.5));
461 if (m_trainSets[isector].nSamples() % 1000 == 0) {
462 B2DEBUG(50, m_trainSets[isector].nSamples() <<
" samples collected for sector " << isector);
470 for (
unsigned isector = 0; isector < m_trainSets.size(); ++isector) {
471 float nTrainMax = m_multiplyNTrain ? m_nTrainMax * m_NeuroTrigger[isector].nWeights() : m_nTrainMax;
472 if (m_trainSets[isector].nSamples() < (nTrainMax + m_nValid + m_nTest)) {
478 B2INFO(
"Training sample preparation for NeuroTrigger finished, stopping event loop.");
480 eventMetaData->setEndOfData();
489 saveTraindata(m_trainFilename, m_trainArrayname);
491 for (
unsigned isector = 0; isector < m_NeuroTrigger.nSectors(); ++isector) {
493 if (m_NeuroTrigger[isector].isTrained())
495 float nTrainMin = m_multiplyNTrain ? m_nTrainMin * m_NeuroTrigger[isector].nWeights() : m_nTrainMin;
496 if (m_trainSets[isector].nSamples() < (nTrainMin + m_nValid + m_nTest)) {
497 B2WARNING(
"Not enough training samples for sector " << isector <<
" (" << (nTrainMin + m_nValid + m_nTest)
498 <<
" requested, " << m_trainSets[isector].nSamples() <<
" found)");
502 m_NeuroTrigger[isector].trained =
true;
504 vector<unsigned> indices = m_NeuroTrigger.getRangeIndices(m_parameters, isector);
505 vector<float> phiRange = m_parameters.phiRange[indices[0]];
506 vector<float> invptRange = m_parameters.invptRange[indices[1]];
507 vector<float> thetaRange = m_parameters.thetaRange[indices[2]];
513 m_NeuroTrigger[isector].phiRange = phiRange;
514 m_NeuroTrigger[isector].invptRange = invptRange;
515 m_NeuroTrigger[isector].thetaRange = thetaRange;
517 m_NeuroTrigger.save(m_filename, m_arrayname);
524 B2DEBUG(50,
"Setting relevant ID ranges for sector " << isector);
525 vector<float> relevantID;
526 relevantID.assign(18, 0.);
529 for (
unsigned iSL = 0; iSL < 9; ++iSL) {
530 int nWires = cdc.nWiresInLayer(layerId);
531 layerId += (iSL > 0 ? 6 : 7);
532 B2DEBUG(90,
"SL " << iSL <<
" (" << nWires <<
" wires)");
534 unsigned maxCounter = 0;
536 unsigned counterSum = 0;
537 for (
int iTS = 0; iTS < nWires; ++iTS) {
538 if (m_trainSets[isector].getHitCounter(iSL, iTS) > 0)
539 B2DEBUG(90, iTS <<
" " << m_trainSets[isector].getHitCounter(iSL, iTS));
540 if (m_trainSets[isector].getHitCounter(iSL, iTS) > maxCounter) {
541 maxCounter = m_trainSets[isector].getHitCounter(iSL, iTS);
544 counterSum += m_trainSets[isector].getHitCounter(iSL, iTS);
547 if (maxId > nWires / 2) maxId -= nWires;
548 relevantID[2 * iSL] = maxId;
549 relevantID[2 * iSL + 1] = maxId;
553 double cut = m_relevantCut * counterSum;
554 B2DEBUG(50,
"Threshold on counterSum: " << cut);
555 unsigned relevantSum = maxCounter;
556 while (counterSum - relevantSum > cut) {
557 int prev = m_trainSets[isector].getHitCounter(iSL, relevantID[2 * iSL] - 1);
558 int next = m_trainSets[isector].getHitCounter(iSL, relevantID[2 * iSL + 1] + 1);
561 (relevantID[2 * iSL + 1] - maxId) > (maxId - relevantID[2 * iSL]))) {
562 --relevantID[2 * iSL];
564 if (relevantID[2 * iSL] <= -nWires)
break;
566 ++relevantID[2 * iSL + 1];
568 if (relevantID[2 * iSL + 1] >= nWires - 1)
break;
573 double cut = m_relevantCut * m_trainSets[isector].getTrackCounter();
574 B2DEBUG(50,
"Threshold on counter: " << cut);
575 while (m_trainSets[isector].getHitCounter(iSL, relevantID[2 * iSL] - 1) > cut) {
576 --relevantID[2 * iSL];
577 if (relevantID[2 * iSL] <= -nWires)
break;
579 while (m_trainSets[isector].getHitCounter(iSL, relevantID[2 * iSL + 1] + 1) > cut) {
580 ++relevantID[2 * iSL + 1];
581 if (relevantID[2 * iSL + 1] >= nWires - 1)
break;
585 relevantID[2 * iSL] -= 0.5;
586 relevantID[2 * iSL + 1] += 0.5;
587 B2DEBUG(50,
"SL " << iSL <<
": "
588 << relevantID[2 * iSL] <<
" " << relevantID[2 * iSL + 1]);
590 m_NeuroTrigger[isector].relevantID = relevantID;
597 B2INFO(
"Training network for sector " << isector <<
" with OpenMP");
599 B2INFO(
"Training network for sector " << isector <<
" without OpenMP");
602 unsigned nLayers = m_NeuroTrigger[isector].nLayers();
603 unsigned* nNodes =
new unsigned[nLayers];
604 for (
unsigned il = 0; il < nLayers; ++il) {
605 nNodes[il] = m_NeuroTrigger[isector].nNodesLayer(il);
607 struct fann* ann = fann_create_standard_array(nLayers, nNodes);
611 unsigned nTrain = m_trainSets[isector].
nSamples() - m_nValid - m_nTest;
612 struct fann_train_data* train_data =
613 fann_create_train(nTrain, nNodes[0], nNodes[nLayers - 1]);
614 for (
unsigned i = 0; i < nTrain; ++i) {
615 vector<float> input = currentData.
getInput(i);
616 for (
unsigned j = 0; j < input.size(); ++j) {
617 train_data->input[i][j] = input[j];
619 vector<float> target = currentData.
getTarget(i);
620 for (
unsigned j = 0; j < target.size(); ++j) {
621 train_data->output[i][j] = target[j];
625 struct fann_train_data* valid_data =
626 fann_create_train(m_nValid, nNodes[0], nNodes[nLayers - 1]);
627 for (
unsigned i = nTrain; i < nTrain + m_nValid; ++i) {
628 vector<float> input = currentData.
getInput(i);
629 for (
unsigned j = 0; j < input.size(); ++j) {
630 valid_data->input[i - nTrain][j] = input[j];
632 vector<float> target = currentData.
getTarget(i);
633 for (
unsigned j = 0; j < target.size(); ++j) {
634 valid_data->output[i - nTrain][j] = target[j];
638 fann_set_activation_function_hidden(ann, FANN_SIGMOID_SYMMETRIC);
639 fann_set_activation_function_output(ann, FANN_SIGMOID_SYMMETRIC);
640 fann_set_training_algorithm(ann, FANN_TRAIN_RPROP);
641 double bestRMS = 999.;
643 vector<double> bestTrainLog = {};
644 vector<double> bestValidLog = {};
646 vector<double> trainOptLog = {};
647 vector<double> validOptLog = {};
649 for (
int irun = 0; irun < m_repeatTrain; ++irun) {
650 double bestValid = 999.;
651 vector<double> trainLog = {};
652 vector<double> validLog = {};
653 trainLog.assign(m_maxEpochs, 0.);
654 validLog.assign(m_maxEpochs, 0.);
657 vector<fann_type> bestWeights = {};
658 bestWeights.assign(m_NeuroTrigger[isector].nWeights(), 0.);
659 fann_randomize_weights(ann, -0.1, 0.1);
661 for (
int epoch = 1; epoch <= m_maxEpochs; ++epoch) {
663 double mse = parallel_fann::train_epoch_irpropm_parallel(ann, train_data, m_nThreads);
665 double mse = fann_train_epoch(ann, train_data);
667 trainLog[epoch - 1] = mse;
669 for (
unsigned iw = 0; iw < ann->total_connections; ++iw) {
670 if (ann->weights[iw] > m_wMax)
671 ann->weights[iw] = m_wMax;
672 else if (ann->weights[iw] < -m_wMax)
673 ann->weights[iw] = -m_wMax;
678 double valid_mse = parallel_fann::test_data_parallel(ann, valid_data, m_nThreads);
680 double valid_mse = fann_test_data(ann, valid_data);
682 validLog[epoch - 1] = valid_mse;
684 if (valid_mse < bestValid) {
685 bestValid = valid_mse;
686 for (
unsigned iw = 0; iw < ann->total_connections; ++iw) {
687 bestWeights[iw] = ann->weights[iw];
692 if (epoch > m_checkInterval && valid_mse > validLog[epoch - m_checkInterval]) {
693 B2INFO(
"Training run " << irun <<
" stopped in epoch " << epoch);
694 B2INFO(
"Train error: " << mse <<
", valid error: " << valid_mse <<
695 ", best valid: " << bestValid);
700 if (epoch == 1 || (epoch < 100 && epoch % 10 == 0) || epoch % 100 == 0) {
701 B2INFO(
"Epoch " << epoch <<
": Train error = " << mse <<
702 ", valid error = " << valid_mse <<
", best valid = " << bestValid);
705 if (breakEpoch == 0) {
706 B2INFO(
"Training run " << irun <<
" finished in epoch " << m_maxEpochs);
707 breakEpoch = m_maxEpochs;
709 trainOptLog.push_back(trainLog[bestEpoch - 1]);
710 validOptLog.push_back(validLog[bestEpoch - 1]);
712 vector<float> oldWeights = m_NeuroTrigger[isector].getWeights();
713 m_NeuroTrigger[isector].weights = bestWeights;
714 vector<double> sumSqr;
715 sumSqr.assign(nNodes[nLayers - 1], 0.);
716 for (
unsigned i = nTrain + m_nValid; i < m_trainSets[isector].nSamples(); ++i) {
717 vector<float> output = m_NeuroTrigger.runMLP(isector, m_trainSets[isector].getInput(i));
718 vector<float> target = m_trainSets[isector].getTarget(i);
719 for (
unsigned iout = 0; iout < output.size(); ++iout) {
720 float diff = output[iout] - m_NeuroTrigger[isector].unscaleTarget(target)[iout];
721 sumSqr[iout] += diff * diff;
724 double sumSqrTotal = 0;
725 if (m_parameters.targetZ) {
726 sumSqrTotal += sumSqr[m_NeuroTrigger[isector].zIndex()];
727 B2INFO(
"RMS z: " << sqrt(sumSqr[m_NeuroTrigger[isector].zIndex()] / m_nTest) <<
"cm");
729 if (m_parameters.targetTheta) {
731 sumSqrTotal += sumSqr[m_NeuroTrigger[isector].thetaIndex()];
732 B2INFO(
"RMS theta: " << sqrt(sumSqr[m_NeuroTrigger[isector].thetaIndex()] / m_nTest) <<
"deg");
734 double RMS = sqrt(sumSqrTotal / m_nTest / sumSqr.size());
735 B2INFO(
"RMS on test samples: " << RMS <<
" (best: " << bestRMS <<
")");
738 bestTrainLog.assign(trainLog.begin(), trainLog.begin() + breakEpoch);
739 bestValidLog.assign(validLog.begin(), validLog.begin() + breakEpoch);
741 m_NeuroTrigger[isector].weights = oldWeights;
747 ofstream logstream(m_logFilename +
"_BestRun_" + to_string(isector) +
".log");
748 for (
unsigned i = 0; i < bestTrainLog.size(); ++i) {
749 logstream << bestTrainLog[i] <<
" " << bestValidLog[i] << endl;
753 ofstream logstreamOpt(m_logFilename +
"_AllOptima_" + to_string(isector) +
".log");
754 for (
unsigned i = 0; i < trainOptLog.size(); ++i) {
755 logstreamOpt << trainOptLog[i] <<
" " << validOptLog[i] << endl;
757 logstreamOpt.close();
760 fann_destroy_train(train_data);
761 fann_destroy_train(valid_data);
769 B2INFO(
"Saving traindata to file " << filename <<
", array " << arrayname);
770 TFile datafile(filename.c_str(),
"UPDATE");
771 TObjArray* trainSets =
new TObjArray(m_trainSets.size());
772 for (
unsigned isector = 0; isector < m_trainSets.size(); ++isector) {
773 trainSets->Add(&m_trainSets[isector]);
775 phiHistsMC[isector]->Write(phiHistsMC[isector]->GetName(), TObject::kOverwrite);
776 ptHistsMC[isector]->Write(ptHistsMC[isector]->GetName(), TObject::kOverwrite);
777 thetaHistsMC[isector]->Write(thetaHistsMC[isector]->GetName(), TObject::kOverwrite);
778 zHistsMC[isector]->Write(zHistsMC[isector]->GetName(), TObject::kOverwrite);
779 phiHists2D[isector]->Write(phiHists2D[isector]->GetName(), TObject::kOverwrite);
780 ptHists2D[isector]->Write(ptHists2D[isector]->GetName(), TObject::kOverwrite);
783 trainSets->Write(arrayname.c_str(), TObject::kSingleKey | TObject::kOverwrite);
787 for (
unsigned isector = 0; isector < phiHistsMC.size(); ++ isector) {
788 delete phiHistsMC[isector];
789 delete ptHistsMC[isector];
790 delete thetaHistsMC[isector];
791 delete zHistsMC[isector];
792 delete phiHists2D[isector];
793 delete ptHists2D[isector];
797 thetaHistsMC.clear();
806 TFile datafile(filename.c_str(),
"READ");
807 if (!datafile.IsOpen()) {
808 B2WARNING(
"Could not open file " << filename);
811 TObjArray* trainSets = (TObjArray*)datafile.Get(arrayname.c_str());
814 B2WARNING(
"File " << filename <<
" does not contain key " << arrayname);
818 for (
int isector = 0; isector < trainSets->GetEntriesFast(); ++isector) {
820 if (samples) m_trainSets.push_back(*samples);
821 else B2WARNING(
"Wrong type " << trainSets->At(isector)->ClassName() <<
", ignoring this entry.");
826 B2DEBUG(100,
"loaded " << m_trainSets.size() <<
" training sets");
Struct for training data of a single MLP for the neuro trigger.
unsigned nSamples() const
get number of samples (same for input and target)
const std::vector< float > & getInput(unsigned i) const
get input vector of sample i
const std::vector< float > & getTarget(unsigned i) const
get target value of sample i
The trainer module for the neural networks of the CDC trigger.
bool loadTraindata(const std::string &filename, const std::string &arrayname="trainSets")
Load saved training samples.
virtual void initialize() override
Initialize the module.
virtual void event() override
Called once for each event.
void updateRelevantID(unsigned isector)
calculate and set the relevant id range for given sector based on hit counters of the track segments.
virtual void terminate() override
Do the training for all sectors.
void train(unsigned isector)
Train a single MLP.
void saveTraindata(const std::string &filename, const std::string &arrayname="trainSets")
Save all training samples.
Combination of several CDCHits to a track segment hit for the trigger.
The Class for CDC Geometry Parameters.
static CDCGeometryPar & Instance(const CDCGeometry *=nullptr)
Static method to get a reference to the CDCGeometryPar instance.
A Class to store the Monte Carlo particle information.
TVector3 getMomentum() const
Return momentum.
float getCharge() const
Return the particle charge defined in TDatabasePDG.
TVector3 getProductionVertex() const
Return production vertex position.
This is the Reconstruction Event-Data Model Track.
bool wasFitSuccessful(const genfit::AbsTrackRep *representation=nullptr) const
Returns true if the last fit with the given representation was successful.
const std::vector< genfit::AbsTrackRep * > & getRepresentations() const
Return a list of track representations. You are not allowed to modify or delete them!
bool addHit(const HitType *hit, Args &&... params)
Add a generic hit with the given parameters for the reco hit information.
const genfit::MeasuredStateOnPlane & getMeasuredStateOnPlaneClosestTo(const TVector3 &closestPoint, const genfit::AbsTrackRep *representation=nullptr)
Return genfit's MasuredStateOnPlane, that is closest to the given point useful for extrapolation of m...
RelationVector< TO > getRelationsTo(const std::string &name="", const std::string &namedRelation="") const
Get the relations that point from this object to another store array.
TO * getRelatedTo(const std::string &name="", const std::string &namedRelation="") const
Get the object to which this object has a relation.
Accessor to arrays stored in the data store.
Type-safe access to single objects in the data store.
static const double deg
degree to radians
#StateOnPlane with additional covariance matrix.
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Abstract base class for different kinds of events.