- Модуль: crm
- Путь к файлу: ~/bitrix/modules/crm/lib/ml/agent/modeltrainer.php
- Класс: Bitrix\Crm\Ml\Agent\ModelTrainer
- Вызов: ModelTrainer::run
static function run($trainingId)
{
$trainingId = (int)$trainingId;
if (!Loader::includeModule("ml"))
{
return "";
}
$training = ModelTrainingTable::getRowById($trainingId);
if (
!$training
|| in_array($training['STATE'], [TrainingState::FINISHED, TrainingState::CANCELED], true)
)
{
return '';
}
$model = Scoring::getModelByName($training["MODEL_NAME"]);
$lastId = $training["LAST_ID"];
$rowIdField = $model->getRowIdField();
$targetField = $model->getTargetField();
if(!$rowIdField)
{
return "";
}
if(!Lock::get(static::getLockName()))
{
// repeat iteration later
return __CLASS__ . "::run({$trainingId});";
}
// create ml model if it does not exist yet
if($training["STATE"] == TrainingState::PENDING_CREATION)
{
if(!$model->getModelId())
{
$creationResult = \Bitrix\Ml\Model::create(
$model->getName(),
\Bitrix\Ml\Model::TYPE_BINARY,
$model->getPossibleFields()
);
if(!$creationResult->isSuccess())
{
// repeat iteration later
return __CLASS__ . "::run({$trainingId});";
}
$mlModel = $creationResult->getData()["model"];
$model->setMlModel($mlModel);
}
if($model->getModelId())
{
ModelTrainingTable::update(
$trainingId,
[
"STATE" => TrainingState::IDLE,
]
);
}
}
$trainingSet = $model->getTrainingSet($lastId, static::RECORDS_LIMIT);
$trainingSetWithFeatures = [];
$client = new Client();
if(count($trainingSet) > 0)
{
foreach ($trainingSet as $entityId)
{
// Writing LAST_ID before building features because buildFeaturesVector can run too long (or even fail)
// in some extreme cases, so we will skip broken entity next time.
ModelTrainingTable::update(
$trainingId,
[
"STATE" => TrainingState::GATHERING,
"LAST_ID" => $entityId
]
);
$trainingSetWithFeatures[] = $model->buildFeaturesVector($entityId);
}
$appendResult = $client->appendLearningData([
"modelName" => $model->getName(),
"records" => $trainingSetWithFeatures
]);
if($appendResult->isSuccess())
{
$successRecords = 0;
$failedRecords = 0;
foreach ($trainingSetWithFeatures as $trainingRecord)
{
if($trainingRecord[$targetField] === "Y")
{
$successRecords++;
}
else
{
$failedRecords++;
}
}
ModelTrainingTable::update(
$trainingId,
[
"STATE" => TrainingState::GATHERING,
"RECORDS_SUCCESS" => new SqlExpression("?# + ?i", "RECORDS_SUCCESS", $successRecords),
"RECORDS_FAILED" => new SqlExpression("?# + ?i", "RECORDS_FAILED", $failedRecords)
]
);
Details::onTrainingProgress($model, ModelTrainingTable::getRowById($trainingId));
}
// repeat iteration later
Lock::release(static::getLockName());
return __CLASS__ . "::run({$trainingId});";
}
else
{
$startResult = $client->startTraining([
"modelName" => $model->getName()
]);
if($startResult->isSuccess())
{
ModelTrainingTable::update(
$trainingId,
[
"STATE" => TrainingState::TRAINING
]
);
// success
Lock::release(static::getLockName());
return "";
}
else
{
// repeat iteration later
Lock::release(static::getLockName());
return __CLASS__ . "::run({$trainingId});";
}
}
}