Я думаю, что идея k-ближайших соседей — это самая чистая и ясная идея Алгоритм k-ближайших соседей (KNN) — это просто применение этой идеи в области данных.
Вашу зарплату определяют окружающие.
Ваш уровень определяется уровнем самых близких вам людей.
Мир, который вы видите, определяется окружающими вас людьми.
Идеи есть идеи, и то, что нельзя закодировать, нельзя применить к науке о данных.
Мы углубляем наше понимание метода, задавая вопросы, а затем применяя метод для их решения.
проблема:Предположим, вы владелец платформы airbnb. Как вы устанавливаете арендную плату за собственный дом?
анализировать:Арендаторы выбирают дом, которым они довольны, в соответствии с информацией об аренде на платформе airbnb, включая цену, количество спален, тип дома, местоположение и т. д. Установление арендной платы за дом тесно связано с динамикой рынка. Для одного и того же типа дома мы взимаем слишком высокую арендную плату, и арендатор не будет сдавать его. Если плата слишком низкая, доход не является хорошим.
отвечать:Соберите некоторую информацию о домах с похожими условиями на наш дом, определите те, которые находятся ближе всего к нашему дому, а затем найдите среднее значение их цен как арендную плату за наш дом.
Это K-ближайшие соседи (KNN), алгоритм k-ближайших соседей. Основная идея KNN заключается в том, что за класс неразмеченного образца голосуют его k ближайших соседей.
В данной статье рассматривается весь процесс применения алгоритма на основе задачи ценообразования ренты, включая следующие части.
- читать данные
- обработка данных
- предсказание кода рукописного алгоритма
- Предсказание модели с помощью sklearn
- Оптимизация гиперпараметров
- Перекрестная проверка
- Суммировать
Заявляю заранее, что этот набор данных является общедоступным, и вы можете найти много материалов по смежным темам в Интернете. В этой статье мы попытаемся объяснить это полностью и точно. Было бы здорово, если бы вы нашли более подробные учебные материалы.
1. Чтение данных
Сначала прочитайте данные, изучите данные и найдите целевую переменную.price
,а такжеcleaning_fee
иsecurity_deposit
Формат немного проблематичен, а некоторые переменные являются символьными типами, которые необходимо обрабатывать. Я транспонировал фрейм данных для удобства просмотра.
2. Обработка данных
Давайте просто разберемся сprice
, попробуйте сосредоточиться на самой алгоритмической идее.
# 处理下目标变量price,并转换成数值型
stripped_commas = dc_listings['price'].str.replace(',', '')
stripped_dollars = stripped_commas.str.replace('$', '')
dc_listings['price'] = stripped_dollars.astype('float')
# k近邻算法也是模型,需要划分训练集和测试集
sample_num = len(dc_listings)
# 在这我们先把数据随机打散,保证数据集的切分随机有效
dc_listings = dc_listings.loc[np.random.permutation(len(sample_num))]
train_df = dc_listings.iloc[0:int(0.7*sample_num)]
test_df = dc_listings.iloc[int(0.7*sample_num):]
3. Предсказание кода рукописного алгоритма
Напишите код непосредственно в соответствии с определением алгоритма k ближайших соседей.С точки зрения простоты и эффективности мы делаем прогнозы только для одиночных переменных.
Количество жильцов должно быть тесно связано с арендной платой, как и площадь. Здесь мы используем первое.
Наша цель — понять алгоритмическую логику. На практике обычно не учитывается только одна переменная.
# 注意,这儿是train_df
def predict_price(new_listing):
temp_df = train_df.copy()
temp_df['distance'] = temp_df['accommodates'].apply(lambda x: np.abs(x - new_listing))
temp_df = temp_df.sort_values('distance')
nearest_neighbor_prices = temp_df.iloc[0:5]['price']
predicted_price = nearest_neighbor_prices.mean()
return(predicted_price)
# 这儿是test_df
test_df['predicted_price'] = test_df['accommodates'].apply(predict_price)
# MAE(mean absolute error), MSE(mean squared error), RMSE(root mean squared error)
test_df['squared_error'] = (test_df['predicted_price'] - test_df['price'])**(2)
mse = test_df['squared_error'].mean()
rmse = mse ** (1/2)
Стоит подчеркнуть, что конструкция моделей алгоритма основана на учебном наборе, а оценка прогнозирования основана на тестовом наборе. Существует также класс образцов, строго для оценки применения, OOT: образцы во времени.
Судя по результатам, хотя мы использовали только количество жильцовaccommodates
Эта переменная выполняет выбор ближайшего соседа, и результат прогнозирования также очень эффективен.
4. Используйте sklearn для предсказания модели
На этот раз мы будем использовать больше переменных, удалим только строки и необъяснимые переменные и воспользуемся оставшимися переменными, которые можно использовать.
Когда используется несколько переменных, размеры этих инвариантов различны, и нам необходимо их стандартизировать. Это обеспечивает различия в распределении соответствующих переменных и в то же время гарантирует, что переменные могут быть наложены друг на друга.
# 剔掉非数值型变量和不合适的变量
drop_columns = ['room_type', 'city', 'state', 'latitude', 'longitude', 'zipcode', 'host_response_rate', 'host_acceptance_rate', 'host_listings_count']
dc_listings = dc_listings.drop(drop_columns, axis=1)
# 剔掉缺失比例过高的列(变量)
dc_listings = dc_listings.drop(['cleaning_fee', 'security_deposit'], axis=1)
# 剔掉有缺失值的行(样本)
dc_listings = dc_listings.dropna(axis=0)
# 多个变量的量纲不一样,需要标准化
normalized_listings = (dc_listings - dc_listings.mean())/(dc_listings.std())
normalized_listings['price'] = dc_listings['price']
# 于是我们得到了可用于建模的数据集,7:3划分训练集测试集
train_df = normalized_listings.iloc[0:int(0.7*len(normalized_listings))]
test_df = normalized_listings.iloc[int(0.7*len(normalized_listings)):]
# price是y,其余变量都是X
features = train_df.columns.tolist()
features.remove('price')
Обработанный набор данных выглядит следующим образом, гдеprice
— это цель, которую мы хотим предсказать, а остальные — доступные переменные.
from sklearn.neighbors import KNeighborsRegressor
from sklearn.metrics import mean_squared_error
knn = KNeighborsRegressor(n_neighbors=5, algorithm='brute')
knn.fit(train_df[features], train_df['price'])
predictions = knn.predict(test_df[features])
mse = mean_squared_error(test_df['price'], predictions)
rmse = mse ** (1/2)
Полученное среднеквадратичное значение = 111,9 меньше, чем 117,4 одномерного knn, и результат оптимизируется. Строго говоря, это сравнение не совсем справедливо, потому что мы отбрасываем небольшое количество образцов с отсутствующими признаками.
5. Оптимизация гиперпараметров
В частях 3 и 4 мы заранее установили k=5, но это нормально. Является ли значение разумным или нет, оптимальным оно или нет, необходимо дополнительно определить.
Среди них это k является гиперпараметром. Для любого набора данных, если вы используете knn, вам необходимо определить это значение k.
Значение k не изучается моделью на основе данных, а определяется предустановкой, а затем обратно выбирается в соответствии с результатом. Так определяется любой гиперпараметр, как и другие алгоритмы.
import matplotlib.pyplot as plt
%matplotlib inline
hyper_params = [x for x in range(1,21)]
rmse_values = []
features = train_df.columns.tolist()
features.remove('price')
for hp in hyper_params:
knn = KNeighborsRegressor(n_neighbors=hp, algorithm='brute')
knn.fit(train_df[features], train_df['price'])
predictions = knn.predict(test_df[features])
mse = mean_squared_error(test_df['price'], predictions)
rmse = mse**(1/2)
rmse_values.append(rmse)
plt.plot(hyper_params, rmse_values,c='r',linestyle='-',marker='+')
Мы обнаружили, что чем больше k, тем точнее отклонение прогнозируемой цены от истинной цены от тренда. Однако следует отметить, что чем больше k, тем больше вычислительные затраты.
Когда мы определяем значение k, мы можем использовать метод локтя, то есть смотреть на точку перегиба на рисунке выше, которая на изображении является локтем локтя.
k=7 или 10 могут быть лучшими результатами, чем k=5.
6. Перекрестная проверка
Результаты наших расчетов выше полностью зависят от обучающей и тестовой выборок, хотя мы учитывали случайность в их разбиении. Но результат по-прежнему спорадический, особенно когда размер выборки недостаточно велик.
Перекрестная проверка призвана решить эту проблему. Мы можем выполнять разные обучающие наборы и разделы тестовых наборов для одного и того же набора образцов. Переучивайте и прогнозируйте после каждого сплита, а потом вместе смотрите на результаты.
Наиболее широко используется n-кратная перекрестная проверка, процесс которой заключается в случайном разделении набора данных на n частей, использовании n-1 подмножеств в качестве обучающего набора, а оставшегося 1 подмножества в качестве тестового набора. Это позволяет провести в общей сложности n тренировок и прогнозов.
Мы можем написать логику напрямую следующим образом.
sample_num = len(normalized_listings)
normalized_listings.loc[normalized_listings.index[0:int(0.2*sample_num)], "fold"] = 1
normalized_listings.loc[normalized_listings.index[int(0.2*sample_num):int(0.4*sample_num)], "fold"] = 2
normalized_listings.loc[normalized_listings.index[int(0.4*sample_num):int(0.6*sample_num)], "fold"] = 3
normalized_listings.loc[normalized_listings.index[int(0.6*sample_num):int(0.8*sample_num)], "fold"] = 4
normalized_listings.loc[normalized_listings.index[int(0.8*sample_num):], "fold"] = 5
fold_ids = [1,2,3,4,5]
def train_and_validate(df, folds):
fold_rmses = []
for fold in folds:
# Train
model = KNeighborsRegressor()
train = df[df["fold"] != fold]
test = df[df["fold"] == fold].copy()
model.fit(train[features], train["price"])
# Predict
labels = model.predict(test[features])
test["predicted_price"] = labels
mse = mean_squared_error(test["price"], test["predicted_price"])
rmse = mse**(1/2)
fold_rmses.append(rmse)
return(fold_rmses)
rmses = train_and_validate(normalized_listings, fold_ids)
avg_rmse = np.mean(rmses)
Инженерия, мы хотим в полной мере использовать инструменты и ресурсы. Библиотека sklearn содержит реализацию наших часто используемых алгоритмов машинного обучения, которые можно напрямую использовать для проверки.
from sklearn.model_selection import cross_val_score, KFold
kf = KFold(5, shuffle=True, random_state=1)
model = KNeighborsRegressor()
mses = cross_val_score(model, normalized_listings[features], normalized_listings["price"], scoring="neg_mean_squared_error", cv=kf)
rmses = np.sqrt(np.absolute(mses))
avg_rmse = np.mean(rmses)
Результаты перекрестной проверки будут более надежными, особенно для небольших наборов данных. Потому что это может в определенной степени уменьшить количество случайных ошибок.
Сочетая перекрестную проверку и оптимизацию гиперпараметров, мы обычно получаем наилучшие результаты, предсказанные алгоритмом knn для этого набора данных.
# 超参优化
num_folds = [x for x in range(2,50,2)]
rmse_values = []
for fold in num_folds:
kf = KFold(fold, shuffle=True, random_state=1)
model = KNeighborsRegressor()
mses = cross_val_score(model, normalized_listings[features], normalized_listings["price"], scoring="neg_mean_squared_error", cv=kf)
rmses = np.sqrt(np.absolute(mses))
avg_rmse = np.mean(rmses)
std_rmse = np.std(rmses)
rmse_values.append(avg_rmse)
plt.plot(num_folds, rmse_values,c='r',linestyle='-',marker='+')
Мы получаем тот же тренд, чем больше k, тем лучше эффект тренда. В то же время, поскольку перекрестная проверка в определенной степени решает проблему переобучения, чем больше идеальное значение k, тем сложнее может быть модель.
7. Резюме
Из основной идеи алгоритма k-ближайших соседей и описанного выше процесса кодирования видно, что алгоритм представляет собой метод обучения на основе экземпляров, поскольку он полностью полагается на экземпляры в обучающем наборе.
Алгоритм требует немного математики и прост для понимания. Однако он очень непригоден для применения к большим наборам данных, потому что для каждого прогноза алгоритма k-ближайших соседей необходимо вычислять расстояние от данных всего обучающего набора до прогнозируемых данных, а затем располагать их в порядке возрастания. порядка, что приводит к огромному количеству вычислений.
Если математическую функцию можно использовать для описания взаимосвязи между переменными признаков набора данных и целевой переменной, то после получения представления функции с помощью обучающего набора прогнозирование становится простой задачей математического расчета. Сложность вычислений значительно снижается.
Другие классические алгоритмы машинного обучения в основном представляют собой проблему выражения функции. Посмотрим позже.