Нашли ошибку?
Скачать чистый
мини-проект≈ 20 минупражнения: 0 из 5

Мини-проект: ближайшие соседи

Метод k ближайших соседей на NumPy — порекомендовать напиток по оценкам похожих гостей и предсказать продажи по похожим дням; почему перед расстояниями признаки нужно нормализовать.

Python запустится при первом выполнении

Как сервисы угадывают, что вам понравится? Один из самых простых и понятных способов — k ближайших соседей (k-nearest neighbors, kNN): найти k самых похожих на вас людей и посмотреть, что нравится им. «Похожие» — это близкие по расстоянию, если каждого человека записать строкой чисел. Метод хорошо разобран, например, в книге «Грокаем алгоритмы» (глава 10). Здесь соберём его сами из того, что уже знаем: разности с транслированием, np.linalg.norm, np.argsort.

Шаг 1. Похожие гости

Восемь постоянных гостей кофейни оценили шесть напитков по шкале от 1 до 5. Столбцы: эспрессо, капучино, латте, раф, чай, какао. Новая гостья Оля оценила первые пять, а какао не пробовала. Понравится ли оно ей?

[ ]Оценки гостей
import numpy as np

drinks = np.array(["эспрессо", "капучино", "латте", "раф", "чай", "какао"])
ratings = np.array([
    [5, 4, 2, 1, 2, 1],
    [2, 3, 5, 5, 3, 5],
    [4, 5, 3, 2, 1, 2],
    [1, 2, 4, 5, 4, 4],
    [5, 5, 3, 2, 2, 1],
    [3, 3, 3, 3, 5, 3],
    [2, 2, 5, 4, 3, 5],
    [4, 3, 2, 2, 4, 2],
])
olya = np.array([2, 3, 5, 4, 3])     # без какао
print(ratings.shape, olya.shape)
print(drinks[:5])
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
(8, 6) (5,)
['эспрессо' 'капучино' 'латте' 'раф' 'чай']
[ ]Упражнение 1Расстояние до каждого гостя решено

Сохраните в dists расстояние от оценок Оли olya до оценок каждого гостя — только по первым пяти напиткам, которые Оля пробовала.

dists = ...

Шаг 2. Три ближайших

np.argsort возвращает номера в порядке возрастания — первые k из них и есть ближайшие соседи.

[ ]Упражнение 2Соседи Оли решено

Сохраните в neighbors номера трёх гостей, ближайших к Оле.

neighbors = ...

Шаг 3. Предсказание

Прогноз для Оли — средняя оценка какао у её соседей. Массив номеров выбирает строки таблицы так же, как элементы одномерного массива в уроке о сортировке: ratings[neighbors] — таблица из трёх строк.

[ ]Упражнение 3Понравится ли какао решено

Сохраните в cocoa_pred среднюю оценку какао (столбец 5) у трёх соседей Оли.

cocoa_pred = ...

Прогноз 4.7 из 5 — какао Оле стоит предложить. Для сравнения, средняя оценка какао у всех гостей — около 2.9: соседи знают об Оле больше, чем «средний гость».

Пояснение с ответом появится, когда решите упражнение «Понравится ли какао».

Ниже первая строка — средние оценки трёх соседей Оли по всем шести напиткам, вторая — средние всех гостей:

[ ]Вкусы соседей по всем напиткам
print(np.round(ratings[neighbors].mean(axis=0), 1))
print(np.round(ratings.mean(axis=0), 1))
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
[1.7 2.3 4.7 4.7 3.3 4.7]
[3.2 3.4 3.4 3.  3.  2.9]
Вывод появится, когда решите упражнение «Понравится ли какао», — иначе он подскажет ответ. Ячейку можно и выполнить.

Шаг 4. Прогноз продаж

Тот же метод предсказывает числа. Кофейня записывала для каждого дня три признака — температуру, выходной ли это (1 или 0) и была ли акция (1 или 0) — и сколько чашек продали. Завтра будний день, +17 °C, без акции. Сколько варить?

[ ]Двенадцать прошлых дней
days = np.array([
    # температура, выходной, акция
    [5, 0, 0],
    [12, 0, 1],
    [18, 1, 0],
    [22, 1, 1],
    [8, 1, 0],
    [25, 0, 0],
    [15, 0, 1],
    [19, 0, 0],
    [3, 1, 1],
    [16, 1, 0],
    [28, 1, 0],
    [10, 0, 0],
])
cups = np.array([140, 190, 260, 330, 250, 150, 200, 155, 280, 265, 240, 145])
tomorrow = np.array([17, 0, 0])
print(days.shape, cups.shape)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
(12, 3) (12,)
[ ]Упражнение 4Соседи по сырым признакам решено

Сохраните в naive_dists расстояние от завтрашнего дня tomorrow до каждого прошлого дня по признакам days, в naive_nb — номера четырёх ближайших дней, а в naive_pred — среднее число чашек cups в эти дни.

naive_dists = ...
naive_nb = ...
naive_pred = ...

[ ]Кто оказался соседом
print(days[naive_nb])
print(cups[naive_nb])
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
[[18  1  0]
 [16  1  0]
 [19  0  0]
 [15  0  1]]
[260 265 155 200]
Вывод появится, когда решите упражнение «Соседи по сырым признакам», — иначе он подскажет ответ. Ячейку можно и выполнить.

Среди «похожих» дней оказались выходные с продажами 260 и 265 чашек — хотя завтра будний день. Почему? Температура меняется от 3 до 28 — разница в 5 градусов даёт в расстоянии 5, а разница «выходной или будний» — всего 1. Признак с большими числами заглушает остальные.

Пояснение с ответом появится, когда решите упражнение «Соседи по сырым признакам».

Шаг 5. Нормализация

Решение — привести каждый признак к одной шкале 0–1, как в уроке о нормализации: (x - min) / (max - min) по каждому столбцу. Минимум и максимум берутся по прошлым дням, и завтрашний день приводится теми же числами.

[ ]Упражнение 5Соседи после нормализации решено

Приведите признаки к 0–1: lo и hi — минимум и максимум каждого столбца days, days_n — нормализованная таблица, tomorrow_n — нормализованный завтрашний день. Затем сохраните в scaled_nb номера четырёх ближайших дней по нормализованным признакам, а в scaled_pred — среднее число чашек в эти дни.

lo = ...
hi = ...
days_n = ...
tomorrow_n = ...
scaled_nb = ...
scaled_pred = ...

[ ]Два прогноза
print("соседи без нормализации:", days[naive_nb][:, 1].sum(), "выходных из 4 →", naive_pred, "чашек")
print("соседи после нормализации:", days[scaled_nb][:, 1].sum(), "выходных из 4 →", scaled_pred, "чашек")
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
соседи без нормализации: 2 выходных из 4 → 220.0 чашек
соседи после нормализации: 0 выходных из 4 → 147.5 чашек
Итог появится, когда решите все упражнения урока, — в нём ответы. Ячейку можно и выполнить.

Выводы

После нормализации соседями стали будние дни без акции — те, что действительно похожи на завтрашний, — и прогноз упал с 220 до 148 чашек: сырые признаки завысили бы заказ почти в полтора раза. Метод ближайших соседей — это всего три операции NumPy: разность с транслированием, норма по строкам и argsort. А главный урок проекта — перед тем как измерять расстояния, признаки приводят к одной шкале.

Выводы с ответами появятся, когда решите все упражнения урока.

Подробнее в справочнике

Практика по модулю

Задачи по NumPy для самостоятельной практики появятся в разделе «Задачи → NumPy».

Нашли ошибку?