Гетерогенные переменные

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

Так как все переменные попадают в один и тот же диапазон, то имеет смысл вычислять сразу все расстояния между ними. Представьте, однако, что имеется еще одна переменная, влияющая на цену, скажем объем бутылки в миллилитрах. В отличие от остальных переменных, которые изменяются в диапазоне от 0 до 100, эта может принимать значения до 1500. На рис. 8.6 показано, как это отразится на выборе ближайшего соседа и на вычислении весов, соответствующих расстояниям.

Рис. 8.6. Наличие гетерогенных переменных приводит к проблемам с расстоянием

Очевидно, что новая переменная оказывает куда большее влияние на вычисленные расстояния, чем исходные; именно она вносит в расстояние подавляющий вклад, то есть остальные переменные фактически не принимаются в расчет вовсе.

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

Расширение набора данных

Чтобы смоделировать эти эффекты, давайте включим в набор данных еще несколько переменных. Можете скопировать код функции wineset1, назвать новую функцию wineset2 и модифицировать ее, добавив строки, выделенные полужирным шрифтом:

def wineset2( ): rows=[]

for i in range(300): rating=random( )*50+50 age=random( )*50 aisle=float(randint(1,20))

bottlesize=[375.0,750.0,1500.0,3000.0][randint(0,3)]

price=wineprice(rating,age) price*=(bottlesize/750)

price*=(random( )*0.9+0.2)

rows.append({‘input’:(rating,age,aisle,bottlesize), ‘result’:price})

return rows

Теперь можно создать новые наборы данных, содержащие номер ряда и объем бутылки:

>>> reload(numpredict)

<module ‘numpredict’ from ‘numpredict.py’> >>> data=numpredict.wineset2( )

Чтобы понять, как это повлияло на прогностические способности различных вариантов алгоритма kNN, прогоните их на новых наборах, задав оптимальные параметры, которые удалось подобрать в предыдущих тестах:

>>> numpredict.crossvalidate(knn3,data)

1427.3377833596137

>>> numpredict.crossvalidate(numpredict.weightedknn,data)

1195.0421231227463

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

Масштабирование измерений

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

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

Рис. 8.7. Масштабирование осей решает проблему расстояний

Как видите, масштаб по оси, соответствующей объему бутылки, уменьшен в 10 раз, следовательно, ближайшие соседи для некоторых образцов изменились. Это решает проблему, возникающую, когда диапазон значений одних переменных намного шире, чем других. Но как быть с несущественными переменными? Посмотрим, что произойдет, если все значения по одной из осей умножить на 0 (рис. 8.8). Теперь все точки имеют одну и ту же координату по оси номера ряда, поэтому расстояния между ними зависят только от положения по оси возраста. Таким образом, номер ряда перестал принимать участие в вычислении ближайших соседей и полностью исключен из рассмотрения. Если для всех несущественных переменных установить значение 0, то алгоритмы станут работать гораздо точнее.

Рис. 8.8. Несущественные оси свернуты

Функция rescale принимает список образцов и параметр scale, представляющий собой список вещественных чисел. Она возвращает новый набор данных, в котором все значения умножены на числа из набора scale. Добавьте эту функцию в файл numpredict.py:

def rescale(data,scale): scaleddata=[] for row in data: scaled=[scale[i]*row[‘input’][i] for i in range(len(scale))] scaleddata.append({‘input’:scaled,’result’:row[‘result’]}) return scaleddata

Протестируем эту функцию, применив к набору данных масштабирование с разумно выбранными коэффициентами, и посмотрим, позволит ли это улучшить прогноз: >>> reload(numpredict)

<module ‘numpredict’ from ‘numpredict.py’> >>> sdata=numpredict.rescale(data,[10,10,0,0.5]) >>> numpredict.crossvalidate(knn3,sdata)

660.9964024835578

>>> numpredict.crossvalidate(numpredict.weightedknn,sdata)

852.32254222973802

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

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100