рекомендации

среда, 14 августа 2019 г.

Повышение эффективности торговой стратегии

Перевод. Оригинал: How to Increase Your Model's Performance



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

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

Обучающий набор

Это данные, которые вы используете для обучения или построения модели. Алгоритм будет пытаться найти взаимосвязи между вашими входными данными (нашими индикаторами) и выходными данными (независимо от того, будет цена на следующий день расти или падать). Обычно вы можете зарезервировать около 60% ваших данных для обучения.

Тестовый набор

Мы будем использовать тестовый набор для оценки эффективности модели по данным, которые не использовались для ее построения. Это набор, который мы будем использовать для сравнения различных моделей, поэтому важно, чтобы он представлял собой репрезентативный набор данных. Как правило, около 20% данных должны быть отложены для тестового набора.

Набор валидации

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

1. Понимание вашей модели

Прежде чем вы сможете улучшить свою модель, вам необходимо понять характеристики вашей модели; в частности, где она попадает в дилемму смещения–дисперсии? Смещение, также известное как недостаточное обучение, вызвано тем, что модель делает слишком упрощенные предположения о данных. С другой стороны, дисперсия - это то, что мы знаем как переобучение, или просто моделирование собственного шума в ваших данных. Хитрость заключается в том, чтобы захватить как можно большую часть базового сигнала в ваших данных, не подгоняя модель к случайному шуму.

Простой способ узнать, где ваша модель попадает в дилемму смещения–дисперсии, - это сравнить ошибку в вашем обучающем наборе с ошибкой вашего тестового набора. Если у вас большая ошибка в обучающем наборе и большая ошибка в тестовом наборе, вы, скорее всего, будете иметь большое смещение, тогда как если у вас малая ошибка в обучающем наборе и большая ошибка в тестовом наборе, вы страдаете от высокой дисперсии.



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

Высокая дисперсия (переобучение):

1. Упростите свою модель
2. Используйте больше данных
3. Регуляризация *
4. Ансамбль методов: бэггинг

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

Большое смещение (недостаточное обучение)

1. Увеличьте сложность вашей модели
2. Добавьте больше входов
3. Создавайте более сложные функции
4. Ансамбль методов: бустинг

Теперь, когда мы понимаем, где наша модель попадает в дилемму смещения–дисперсии, давайте рассмотрим другие способы улучшения нашей модели.

2. Создание признаков.

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

В то время как вы можете сказать «я использую 14-периодный RSI для торговли», на самом деле, есть еще много разных ньюансов. Скорее всего, вы также учитываете наклон RSI, есть ли локальный максимум или минимум, дивергенция от цены и многие другие факторы. Однако модель получает только одну часть информации: текущее значение RSI. Создавая функцию или вычисление, полученное из значения RSI, мы можем предоставить алгоритму гораздо больше информации в одном значении.

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

Во-первых, давайте установим нужные нам пакеты, настроим наши данные и рассчитаем исходные данные.

# дает нам доступ к данным и индикаторам
install.packages("quantmod")
library(quantmod)

# включает алгоритм Naive Bayes
install.packages("e1071")
library(e1071)

# задаем диапазон дат
startDate = as.Date('2009-01-01')
endDate = as.Date('2014-06-01')

# получаем данные OHLCV по Microsoft с Yahoo Finance
getSymbols("MSFT",src="yahoo", from=startDate, to=endDate)

# рассчитываем 5-периодное EMA и 14-периодный RSI
EMA5 = EMA(Op(MSFT),n=5)
RSI14 = RSI(Op(MSFT),n=14)

# Мы должны сделать лаг по объему, чтобы использовать вчерашний объем в качестве входных данных, чтобы 
# избежать заглядывание в будущее, тогда как мы избегаем этой проблемы, рассчитывая индикаторы по цене открытия
Volume = lag(MSFT[,5],1)

# Создаем переменную, которую мы будем предсказывать, будет ли сегодняшняя цена закрываться вверх или вниз
PriceChange = Cl(MSFT) - Op(MSFT)
Class = ifelse(PriceChange>0,"UP","DOWN")

# Как мы уже упоминали в предыдущей статье, нам нужно округлить входные данные до двух десятичных разрядов
# при использовании наивного байесовского алгоритма
BaselineDataSet = data.frame(EMA5,RSI14,Volume)
BaselineDataSet = round(BaselineDataSet,2)

# Создаем наш набор данных и удаляем периоды, когда рассчитываются наши показатели
BaselineDataSet = data.frame(BaselineDataSet,Class)
BaselineDataSet = BaselineDataSet[-c(1:14),]

#дадим имена столбцам и разделим данные на 60% обучающего набора, 20% тестового набора и 20% набора валидации
colnames(BaselineDataSet) = c("EMA5","RSI14","Volume","Class")
BaselineTrainingSet = BaselineDataSet[1:808,] 
BaselineTestSet = BaselineDataSet[809:1077,]
BaselineValSet = BaselineDataSet[1078:1347,]

А затем построим нашу базовую модель.

BaselineNB = naiveBayes(Class~EMA5+RSI14+Volume,data=BaselineTrainingSet)<-naivebayes data="BaselineTrainingSet)</p" lass="" olume="">

Давайте посмотрим, насколько хорошо наша модель справилась с тестовым набором.

table(predict(BaselineNB,BaselineTestSet),BaselineTestSet[,4],dnn=list('predicted','actual'))

         actual
predicted DOWN  UP
     DOWN   15   5
     UP    140 109

Не очень хорошо. Точность только 46%, и у нас довольно сильное восходящее смещение. Это явный признак того, что наша модель недостаточно сложна для моделирования наших данных и недостаточно обучена.

Теперь давайте создадим некоторые более сложные функции и посмотрим, сможем ли мы уменьшить это смещение.

# Давайте рассмотрим расстояние между нашей 5-периодной EMA и ценой открытия и трехпериодному  rate of changes (ROC) нашего RSI
EMA5Cross = EMA5-Op(MSFT)
RSI14ROC3 = ROC(RSI14, 3, type="discrete")

# и 1-периодный ROC для нашего объема
VolumeROC1 = ROC(Volume,1,type="discrete")<-roc olume="" p="" type="discrete">

# округляем значения индикаторов
FeatureDataSet = data.frame(EMA5Cross,RSI14ROC3,VolumeROC1)
FeatureDataSet = round(FeatureDataSet,2)

# Удаляем периоды, когда рассчитываются наши показатели
FeatureDataSet = data.frame(FeatureDataSet, Class)
FeatureDataSet = FeatureDataSet[-c(1:17),]

# назовем наши столбцы и разделим данные на наборы для обучения, тестирования и валидации
colnames(FeatureDataSet) = c("EMA5Cross","RSI14ROC3","VolumeROC1","Class")
FeatureTrainingSet = FeatureDataSet[1:806,]
FeatureTestSet = FeatureDataSet[807:1075,]
FeatureValSet = FeatureDataSet[1076:1344,]

И, наконец, построим нашу новую модель.

FeatureNB=naiveBayes(Class~EMA5Cross+RSI14ROC3+VolumeROC1,data=FeatureTrainingSet)<-naivebayes data="FeatureTrainingSet)</p" lass="" olumeroc1="" ross="">

Давайте посмотрим, смогли ли мы уменьшить смещение нашей модели.

table(predict(FeatureNB,FeatureTestSet),FeatureTestSet[,4],dnn=list('predicted','actual'))


         actual
predicted DOWN UP
     DOWN   64 37
     UP     90 78

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

3. Техники ансамблей

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

Существует три основных метода составления ансамблей:

Бэггинг

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

Бустинг

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

Стогование

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

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

Сначала мы построим дерево решений, используя те же функции, которые мы создали для наивного Байеса.

#Включает дерево решений
install.packages("rpart")
library(rpart)

#Содержит функцию, необходимую для расчета бэггинга
install.packages("foreach")
library(foreach)

BaselineDecisionTree=rpart(Class~EMA5Cross+RSI14ROC3+VolumeROC1,data=FeatureTrainingSet, cp=.001)<-rpart cp=".001)</p" data="FeatureTrainingSet," lass="" olumeroc1="" ross="">

И посмотрите, насколько хорошо он справляется с тестовым набором

table(predict(BaselineDecisionTree,FeatureTestSet,type="class"),FeatureTestSet[,4],dnn=list('predicted','actual'))


         actual
predicted DOWN UP
     DOWN   79 58
     UP     75 57

Около 51%. Сравнивая это с точностью в нашем обучающем наборе, которая составляла 73%, мы можем видеть, что наше необрезанное дерево решений значительно переобучено.

Теперь давайте посмотрим, поможет ли бэггинг ансамбля уменьшить это. Хотя R предоставляет несколько различных алгоритмов бэггинга, давайте создадим алгоритм сами, чтобы мы могли лучше контролировать основной алгоритм и параметры бэггинга.

# Это определяет, как мы создаем подмножества обучающего набора 
# и сколько моделей включить в ансамбль. Здесь 
#мы строим 1501 различных моделей из случайной выборки 1/10 данных
length_divisor=10
iterations=1501

Это наш алгоритм ручного бэггинга. Спасибо Вику Паручури за него.

BaggedDecisionTree = foreach(m=1:iterations,.combine=cbind) %do% 
training_positions = sample(nrow(FeatureTrainingSet), 
size=floor((nrow(FeatureTrainingSet)/length_divisor))) train_pos=1:nrow(FeatureTrainingSet) %in% training_positions

BaselineDecisionTree=rpart(Class~EMA5Cross+RSI14ROC3+VolumeROC1,
data=FeatureTrainingSet[train_pos,]) predict(BaselineDecisionTree,newdata=FeatureTestSet) }

Теперь нам нужно агрегировать прогнозы нашего 1501 дерева решений 

CumulativePredictions=apply(BaggedDecisionTree[,1:iterations],1,function(x){s=(sum(x)/iterations)round(s,0)})

Возвращаемся из двоичного вывода в наши классификационные метки

FinalPredictions=ifelse(CumulativePredictions==1,"DOWN","UP")<-ifelse p="" umulativepredictions="=1,">

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

table(FinalPredictions,FeatureTestSet[,4],dnn=list('predicted','actual'))

         actual
predicted DOWN  UP
     DOWN  118  85
     UP     36  30

Намного лучше! Уменьшая дисперсию, мы смогли увеличить нашу эффективность до 54%.

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

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

По этой причине давайте в качестве нашей окончательной модели примем наш Feature Naive Bayes.

И теперь, чтобы проверим его по набору валидации:

(Скрестив пальцы…..)

table(predict(FeatureNB,FeatureValSet),FeatureValSet[,4],dnn=list('predicted','actual'))

         actual
predicted DOWN UP
     DOWN   48 45
     UP     80 96

Совсем неплохо и очень хорошо согласуется с ошибкой на обучающем и тестовом наборах! Наша окончательная точность составила 54%. Похоже, мы построили довольно надежную модель!

Заключение

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

Комментариев нет:

Отправить комментарий