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

Машинное обучение — это упражнение «мусор на входе» и «мусор на выходе» («GIGO»). Если вы подадите слишком сложные данные в алгоритм, который выполняет много логики и математики, велика вероятность, что вы получите какой-то бессмысленный результат, поскольку алгоритмы в конце дней представляют собой просто количественное представление информации.

Поэтому в этой статье я собираюсь углубиться в часть предварительной обработки. Для турнира тренировочные и тестовые наборы имеют метку «эра», которая определяет нашу оценку согласованности. Если наш прогноз непротиворечив во всех эпохах, он будет иметь высокий показатель согласованности, и наоборот. Действительная отправка должна иметь согласованность не менее 75 %.

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

def eraStandardize(self,dataset,model):
        placeholder = pd.DataFrame()
        era = set([''.join(x for x in element if x.isdigit()) for element in dataset['era']])
        era.discard('')
        era = [int(i) for i in era]
        maxera,minera = max(era)+1,min(era)
        for i in range(minera,maxera):
            data = dataset[dataset['era']=='era{}'.format(i)][[f for f in list(dataset) if "feature" in f]]
            placeholder = placeholder.append(pd.DataFrame(model(data)))
        return placeholder

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

self.X_train = Preprocess().eraStandardize(self.training_data, Preprocess().StandardScaler)
self.X_test = Preprocess().eraStandardize(self.test_data, Preprocess().StandardScaler)
self.X_prediction = Preprocess().StandardScaler(self.X_prediction)

Это пример реализации. как обучающие, так и тестовые данные передаются в пользовательскую функцию eraStandardize с использованием функции scikitlearn StandardScalar, в то время как данные прогноза передаются напрямую в функцию StandardScalar, поскольку у нас нет метки «эра» для данных прогноза.

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

def advisory_screen(self,portion,train_x):
    
    model = RandomForestClassifier(n_estimators=50)
    
    X_test = self.x_prediction
    sample_size_test = X_test.shape[0]
    idholder = pd.DataFrame()
    
    for i in range(1,97):
        X_train = train_x[train_x['era']=='era{}'.format(i)][[f for f in list(train_x) if "feature" in f]]
        X_train_id = train_x[train_x['era']=='era{}'.format(i)].id.reset_index()
        sample_size_train = X_train.shape[0]
        X_data = pd.concat([X_train, X_test])
        Y_data = np.array(sample_size_train*[0] + sample_size_test*[1])
        
        model.fit(X_data,Y_data)
        pre_train = pd.DataFrame(data={'wrong-score':model.predict_proba(X_train)[:,1]})
        pre_test = pd.DataFrame(data={'right-score':model.predict_proba(X_test)[:,1]})
        num_data = round(portion * X_train.shape[0])
        test_alike_data = pd.concat([X_train_id,pre_train],axis=1)
        test_alike_data = test_alike_data.sort_values(by='wrong-score',ascending=False)[:num_data]
    ##############for control only#####
        print('out of {0} training sample and {1} testing sample'.format(sample_size_train,sample_size_test))
        print('correct for training: {}'.format(sum([1 for i in model.predict_proba(X_train)[:,1] if i<0.5])))
        #print('correct for validation: {}'.format(sum([1 for i in model.predict_proba(X_test)[:,1] if i>0.5])))
        #print(pd.concat([test_alike_data.head(n=5),test_alike_data.tail(n=5)]))
        #print(pd.concat([test_class.head(n=5),test_class.tail(n=5)]))
    #################################
        idholder = idholder.append(pd.DataFrame(test_alike_data.id),ignore_index=True)
    return train_x[train_x.id.isin(idholder.id)]

Код в основном указывает тестовые данные с меткой 1 и указывает обучающие данные с меткой 0. Затем мы применяем классификатор для обучения данных по эпохам, выбираем только определенный верхний диапазон обучающих данных. RandomForest с n_estimator 30 выше работает довольно хорошо в классификации, обычно теряя только несколько точек данных, но занимает почти полчаса, чтобы завершить один цикл (все эпохи за один раз) на моем Mac, когда я использую только одно ядро.

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

self.X = self.training_data
    while self.X.shape[0] >= 75000:
        self.X = Preprocess().advisory_screen(0.9,self.X)
        print(self.X.shape[0])

Дайте мне знать, если у вас есть какие-либо вопросы/комментарии по приведенному выше коду. Я также ищу товарищей по команде для Kaggle/Numerai или вообще приятелей, чтобы вместе изучать машинное обучение. Свяжитесь со мной, если вы заинтересованы в программировании и машинном обучении.

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