Метрики перекрестной проверки с помощью Pyspark - PullRequest
0 голосов
/ 03 декабря 2018

Когда мы проводим перекрестную проверку в k-кратном размере, мы проверяем, насколько хорошо модель ведет себя, когда речь идет о прогнозировании данных, которые она никогда не видела.

Если разделить мой набор данных на 90% обучения и 10% теста иЕсли проанализировать производительность модели, нет гарантии, что мой набор тестов не содержит только 10% «самых простых» или «самых сложных» точек для прогнозирования.

Проведя 10-кратную перекрестную проверку, я могу быть уверенчто каждый пункт будет хотя бы один раз использован для обучения.Так как (в данном случае) модель будет тестироваться 10 раз, мы можем провести анализ этих тестовых метрик, что позволит нам лучше понять, как модель работает при классификации новых данных.

Документация Spark ссылается на перекрестную проверку как способ оптимизации гиперпараметров алгоритма, когда целью должна быть проверка модели.

Делая это:

lr = LogisticRegression(maxIter=10, tol=1E-4)
ovr = OneVsRest(classifier=lr)
pipeline = Pipeline(stages=[... , ovr])

crossval = CrossValidator(estimator=pipeline,
                          estimatorParamMaps=paramGrid,
                          evaluator=MulticlassClassificationEvaluator(),
                          numFolds=10)

# Run cross-validation, and choose the best set of parameters.
cvModel = crossval.fit(df)

Я могу получить (в моем понимании) модель с лучшим набором параметров, определенных в paramGrid .Я понимаю ценность этой настройки гиперпараметра, но мне нужно проанализировать производительность модели, а не просто получить лучшую модель.

Вопрос заключается в том (для 10-кратной перекрестной проверки в данном случае):

Можно ли использовать CrossValidator для извлечения метрик (f1, точность, отзыв и т. Д.) Для каждого из 10 тестов (или в среднем из этих 10 тестов для каждой метрики) ?, , т. Е. ,Можно ли использовать CrossValidator для проверки модели вместо выбора модели?

Спасибо!


Обновление


Как указано user10465355 в комментариях, аналогичный вопрос можно найти здесь .Первое предложение состоит в том, чтобы установить значение collectSubModels в true перед установкой, и это вызвало ошибку, говорящую о том, что ключевое слово не существовало (честно говоря, я не тратил много времени, пытаясь выяснить, почему).

Пользователь Мак предлагает в своем ответе обходной путь для распечатки промежуточных результатов обучения.С помощью предоставленного им метода можно распечатать промежуточные результаты метрики оценки.Поскольку я хочу извлечь промежуточные результаты точности, вспоминания, f1 и матрицы путаницы, я внес некоторые изменения в метод, который он реализовал:

TestResult = collections.namedtuple("TestResult", ["params", "metrics"])

class CrossValidatorVerbose(CrossValidator):

    def _fit(self, dataset):
        folds = []
        est = self.getOrDefault(self.estimator)
        epm = self.getOrDefault(self.estimatorParamMaps)
        numModels = len(epm)

        eva = self.getOrDefault(self.evaluator)
        metricName = eva.getMetricName()
        nFolds = self.getOrDefault(self.numFolds)
        seed = self.getOrDefault(self.seed)
        h = 1.0 / nFolds

        randCol = self.uid + "_rand"
        df = dataset.select("*", rand(seed).alias(randCol))
        metrics = [0.0] * numModels

        for i in range(nFolds):
            folds.append([])
            foldNum = i + 1
            print("Comparing models on fold %d" % foldNum)

            validateLB = i * h
            validateUB = (i + 1) * h
            condition = (df[randCol] >= validateLB) & (df[randCol] < validateUB)
            validation = df.filter(condition)
            train = df.filter(~condition)

            for j in range(numModels):
                paramMap = epm[j]
                model = est.fit(train, paramMap)
                # TODO: duplicate evaluator to take extra params from input
                prediction = model.transform(validation, paramMap)
                metric = eva.evaluate(prediction)
                metrics[j] += metric

                avgSoFar = metrics[j] / foldNum
                print("params: %s\t%s: %f\tavg: %f" % (
                    {param.name: val for (param, val) in paramMap.items()},
                    metricName, metric, avgSoFar))

                predictionLabels = prediction.select("prediction", "label")
                allMetrics = MulticlassMetrics(predictionLabels.rdd)
                folds[i].append(TestResult(paramMap.items(), allMetrics))


        if eva.isLargerBetter():
            bestIndex = np.argmax(metrics)
        else:
            bestIndex = np.argmin(metrics)

        bestParams = epm[bestIndex]
        bestModel = est.fit(dataset, bestParams)
        avgMetrics = [m / nFolds for m in metrics]
        bestAvg = avgMetrics[bestIndex]
        print("Best model:\nparams: %s\t%s: %f" % (
            {param.name: val for (param, val) in bestParams.items()},
            metricName, bestAvg))

        return self._copyValues(CrossValidatorModel(bestModel, avgMetrics)), folds

Чтобы использовать его, просто замените метод CrossValidator на CrossValidatorVerbose и при выполненииПодгонка модели сделайте:

cvModel, folds  = crossval.fit(df)

Чтобы напечатать метрику определенного сгиба (1-й сгиб с 1-м набором гиперпараметров):

def printMetrics(metrics, df):
    labels = df.rdd.map(lambda lp: lp.label).distinct().collect()
    for label in sorted(labels):
        print("Class %s precision = %s" % (label, metrics.precision(label)))
        print("Class %s recall = %s" % (label, metrics.recall(label)))
        print("Class %s F1 Measure = %s" % (label, metrics.fMeasure(label, beta=1.0)))
        print ""

    # Weighted stats
    print("Weighted recall = %s" % metrics.weightedRecall)
    print("Weighted precision = %s" % metrics.weightedPrecision)
    print("Weighted F(1) Score = %s" % metrics.weightedFMeasure())
    print("Weighted F(0.5) Score = %s" % metrics.weightedFMeasure(beta=0.5))
    print("Weighted false positive rate = %s" % metrics.weightedFalsePositiveRate)
    print("Accuracy = %s" % metrics.accuracy)

printMetrics(folds[0][0].metrics, df)

Будет напечатано что-то вроде:

Class 0.0 precision = 0.809523809524
Class 0.0 recall = 0.772727272727
Class 0.0 F1 Measure = 0.790697674419

Class 1.0 precision = 0.857142857143
Class 1.0 recall = 0.818181818182
Class 1.0 F1 Measure = 0.837209302326

Class 2.0 precision = 0.875
Class 2.0 recall = 0.875
Class 2.0 F1 Measure = 0.875

...

Weighted recall = 0.808333333333
Weighted precision = 0.812411616162
Weighted F(1) Score = 0.808461689698
Weighted F(0.5) Score = 0.810428077222
Weighted false positive rate = 0.026335560185
Accuracy = 0.808333333333
...