Распределение обучающих данных по сравнению с распределением теста / прогноза


Должно ли распределение, представленное обучающими данными, отражать распределение тестовых данных и данных, которые вы прогнозируете? Могу ли я измерить качество обучающих данных, посмотрев на распределение каждой функции и сравнив это распределение с данными, которые я прогнозирую или тестирую? В идеале учебные данные должны быть достаточно репрезентативными для реального распределения в мире.

2 2

2 ответа:

Краткий ответ: подобные диапазоны были бы хорошей идеей. Длинный ответ: иногда это не будет проблемой (редко), но давайте рассмотрим, когда.

В идеальной ситуации ваша модель будет точно отражать истинное явление . Представьте себе простейший случай: линейная модель y = x. Если обучающие данные бесшумны (или имеют допустимый шум). Ваша линейная регрессия, естественно, приведет к модели, приблизительно равной y = x. обобщение модели будет работать почти идеально даже за пределами тренировочного полигона. Если бы ваши данные о поезде были {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8, 9:9, 10:10}. Тестовая точка 500, будет красиво отображена на функцию, возвращающую 500.

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

Проблема в том, что мало функций ( и соответствующих им природных явлений) - особенно когда мы рассматриваем нелинейные функции -- распространитесь на данные за пределами тренировочного диапазона так чисто. Представьте себе, что температура в офисе сравнивается с комфортом сотрудников. Если вы только посмотрите на температуру от 40 до 60 градусов. Линейная функция будет блестяще вести себя в обучающих данных. Как ни странно, если вы тестируете на 60-80, отображение будет разрушаться. Здесь вопрос заключается в уверенности в вашем утверждении, что данные являются достаточно репрезентативными.

Теперь рассмотрим шум. Представьте, что вы точно знаете, что такое реальный мир. есть: синусоидальная волна. А еще лучше, вам скажут его амплитуду и фазу. Чего вы не знаете, так это его частоты. У вас есть действительно солидная выборка между 1 и 100, функция, которую вы хорошо сопоставляете с обучающими данными. Теперь, если будет достаточно шума, вы можете неверно оценить частоту на волосок. Когда вы тестируете вблизи тренировочного полигона, результаты не так уж плохи. за пределами тренировочного полигона все начинает шататься. По мере продвижения все дальше и дальше от тренинга диапазон, реальная функция и функция расходятся и сходятся на основе их относительных частот. Иногда остатки кажутся прекрасными, иногда они ужасны.

Существует проблема с вашей идеей изучения распределения переменных: взаимодействие между переменными. Даже если каждая переменная должным образом сбалансирована в обучении и тестировании, возможно, что отношения между переменными будут отличаться (совместные распределения). Для чисто надуманного примера рассмотрим вы предсказывали вероятность беременности индивидуума в любой момент времени. В вашем тренировочном наборе были женщины в возрасте от 20 до 30 лет и мужчины в возрасте от 30 до 40 лет. При тестировании у вас был одинаковый процент мужчин и женщин, но возрастные диапазоны были перевернуты. Независимо друг от друга переменные выглядят очень хорошо подобранными! Но в своем учебном наборе вы можете очень легко сделать вывод: "беременеют только люди моложе 30 лет.- Как ни странно, ваш тестовый набор продемонстрировал бы прямо противоположное! Беда в том, что ваш предсказания делаются из многомерного пространства, но распределения, о которых вы думаете, одномерны. Однако рассмотрение совместного распределения непрерывных переменных относительно друг друга (и рассмотрение категориальных переменных соответствующим образом) является хорошей идеей. В идеале ваша модель fit должна иметь доступ к аналогичному диапазону данных тестирования.

По сути, речь идет об экстраполяции из ограниченного тренировочного пространства. Если модель вписывается в тренировочное пространство обобщает, вы можете обобщать; в конечном счете, обычно безопаснее всего иметь действительно хорошо распределенный набор обучения, чтобы максимизировать вероятность того, что вы захватили сложность основной функции.

Действительно интересный вопрос! Я надеюсь, что ответ был несколько проницательным; я буду продолжать строить на нем, как ресурсы приходят на ум! Дайте мне знать, если какие-то вопросы останутся!

EDIT: замечание, сделанное в комментариях, которое, я думаю, должно быть прочитано будущими читателями. В идеале, обучающие данные никогда не следует каким-либо образом влиять на данные тестирования. Это включает в себя изучение распределений, совместных распределений и т. д. При достаточном количестве данных распределения в обучающих данных должны сходиться с распределениями в тестовых данных (вспомните среднее значение, закон больших чисел). Манипуляции с распределениями соответствия (например, Z-scoring перед разделением train / test) существенно искажают показатели производительности в вашу пользу. Подходящим методом для разделения данных о поездах и тестах было бы что-то вроде стратифицированного K-образного сгиба для перекрестной проверки.

Извините за задержку ответа. После нескольких месяцев итераций я внедрил и запустил в производство следующее решение, и оно работает довольно хорошо.

Проблема здесь сводится к тому, как можно уменьшить дисперсию баллов обучения/теста при выполнении перекрестной проверки. Это важно, так как если ваша дисперсия высока, то уверенность в выборе лучшей модели падает. Чем более репрезентативны тестовые данные для данных поезда, тем меньше отклонений вы получаете в своем тесте. результаты тестов по всему набору перекрестной проверки. Стратифицированная перекрестная валидация решает эту проблему, особенно при наличии значительного дисбаланса классов, обеспечивая сохранение пропорций классов меток во всех наборах тестов / поездов. Однако это не решает проблему с распределением функций.

В моем случае у меня было несколько особенностей, которые были очень сильными предикторами, но также очень искаженными в их распределении. Это вызвало значительное расхождение в моих тестовых оценках, что затруднило выберите модель с любой уверенностью. По существу, решение состоит в том, чтобы обеспечить совместное распределение метки с набором признаков по наборам тестов/поездов. Существует много способов сделать это, но очень простой подход состоит в том, чтобы просто взять каждый диапазон колонок (если непрерывный) или метку (если категориальный) по одному и выборку из этих колонок при создании тестовых и обучающих наборов. Обратите внимание, что ведра быстро становятся очень разреженными, особенно когда у вас есть много категориальных переменных. Кроме того, порядок столбцов, в котором вы ведете, сильно влияет на выходные данные выборки. Ниже приведено решение, в котором я сначала отбираю ярлык (такой же, как стратифицированный CV), а затем Пример 1 другой функции (наиболее важной функции (называемой score_percentage), которая известна заранее).
def train_test_folds(self, label_column="label"):
    # train_test is an array of tuples where each tuple is a test numpy array and train numpy array pair.
    # The final iterator would return these individual elements separately.

    n_folds = self.n_folds
    label_classes = np.unique(self.label)

    train_test = []
    fmpd_copy = self.fm.copy()
    fmpd_copy[label_column] = self.label
    fmpd_copy = fmpd_copy.reset_index(drop=True).reset_index()
    fmpd_copy = fmpd_copy.sort_values("score_percentage")

    for lbl in label_classes:
        fmpd_label = fmpd_copy[fmpd_copy[label_column] == lbl]
        # Calculate the fold # using the label specific dataset
        if (fmpd_label.shape[0] < n_folds):
            raise ValueError("n_folds=%d cannot be greater than the"
                             " number of rows in each class."
                             % (fmpd_label.shape[0]))
        # let's get some variance -- shuffle within each buck
        # let's go through the data set, shuffling items in buckets of size nFolds
        s = 0
        shuffle_array = fmpd_label["index"].values
        maxS = len(shuffle_array)
        while s < maxS:
            max = min(maxS, s + n_folds) - 1
            for i in range(s, max):
                j = random.randint(i, max)
                if i < j:
                    tempI = shuffle_array[i]
                    shuffle_array[i] = shuffle_array[j]
                    shuffle_array[j] = tempI
            s = s + n_folds
#        print("shuffle s =",s," max =",max, " maxS=",maxS)
        fmpd_label["index"] = shuffle_array
        fmpd_label = fmpd_label.reset_index(drop=True).reset_index()
        fmpd_label["test_set_number"] = fmpd_label.iloc[:, 0].apply(
            lambda x: x % n_folds)
        print("label ", lbl)

        for n in range(0, n_folds):
            test_set = fmpd_label[fmpd_label["test_set_number"]
                                  == n]["index"].values
            train_set = fmpd_label[fmpd_label["test_set_number"]
                                   != n]["index"].values
            print("for label ", lbl, " test size is ",
                  test_set.shape, " train size is ", train_set.shape)
            print("len of total size", len(train_test))

            if (len(train_test) != n_folds):
                # Split doesnt exist. Add it in.
                train_test.append([train_set, test_set])
            else:
                temp_arr = train_test[n]
                temp_arr[0] = np.append(temp_arr[0], train_set)
                temp_arr[1] = np.append(temp_arr[1], test_set)
                train_test[n] = [temp_arr[0], temp_arr[1]]

    return train_test