• Модуль: 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});";
		}
	}
}