1. Вступление
В этом руководстве мы узнаем все об алгоритме Slope One в Java.
Мы также покажем пример реализации проблемы Collaborative Filtering (CF) - техники машинного обучения , используемой системами рекомендаций .
Это может быть использовано, например, для прогнозирования интересов пользователей для конкретных элементов.
2. Совместная фильтрация
Алгоритм наклона Один - это система совместной фильтрации на основе элементов.
Это означает, что он полностью основан на рейтинге элементов пользователя. Когда мы вычисляем сходство между объектами, мы знаем только историю рейтингов, а не сам контент. Это сходство затем используется для прогнозирования потенциальных рейтингов пользователей для пар «пользователь-элемент», отсутствующих в наборе данных.
Https://commons.wikimedia.org/wiki/File:Collaborative__filtering.gif[image]ниже показывают полный процесс получения и расчета рейтинга для конкретного пользователя:
ссылка:/uploads/Collaborative filtering new.gif[]
Сначала пользователи оценивают различные элементы в системе. Далее алгоритм вычисляет сходства. После этого система делает прогнозы для оценок элементов пользователя, которые пользователь еще не оценил.
Для получения более подробной информации о теме совместной фильтрации мы можем обратиться к t к статье Википедии
3. Алгоритм наклона 1
Slope One был назван самой простой формой нетривиальной совместной фильтрации на основе элементов на основе рейтингов. Он учитывает как информацию всех пользователей, которые оценили один и тот же элемент, так и другие элементы, оцененные одним и тем же пользователем, для расчета матрицы сходства.
В нашем простом примере мы собираемся предсказать пользовательский рейтинг товаров в магазине.
Давайте начнем с простой модели Java для нашей проблемы и области.
3.1. Java Model
В нашей модели у нас есть два основных объекта - предметы и пользователи. Класс Item содержит название элемента:
private String itemName;
С другой стороны, класс User содержит имя пользователя:
private String username;
Наконец, у нас есть класс InputData , который будет использоваться для инициализации данных. Предположим, мы создадим в магазине пять разных продуктов:
List<Item> items = Arrays.asList(
new Item("Candy"),
new Item("Drink"),
new Item("Soda"),
new Item("Popcorn"),
new Item("Snacks")
);
Более того, мы создадим трех пользователей, которые случайным образом оценили некоторые из вышеупомянутых, используя шкалу от 0,0 до 1,0, где 0 означает отсутствие интереса, 0,5 - как-то заинтересовано, а 1,0 - как полностью заинтересованное. В результате инициализации данных мы получим Map с данными пользовательского рейтинга:
Map<User, HashMap<Item, Double>> data;
3.2. Дифференциалы и матрицы частот
На основе имеющихся данных мы рассчитаем отношения между элементами, а также количество экземпляров элементов. Для каждого пользователя мы проверяем его/ее рейтинг предметов:
for (HashMap<Item, Double> user : data.values()) {
for (Entry<Item, Double> e : user.entrySet()) {
//...
}
}
На следующем шаге мы проверяем, существует ли элемент в наших матрицах. Если это первое вхождение, мы создаем новую запись на картах:
if (!diff.containsKey(e.getKey())) {
diff.put(e.getKey(), new HashMap<Item, Double>());
freq.put(e.getKey(), new HashMap<Item, Integer>());
}
Первая матрица используется для расчета различий между пользовательскими рейтингами. Его значения могут быть положительными или отрицательными (поскольку разница между рейтингами может быть отрицательной) и сохраняются как Double . С другой стороны, частоты сохраняются как значения Integer .
На следующем шаге мы собираемся сравнить рейтинги всех предметов:
for (Entry<Item, Double> e2 : user.entrySet()) {
int oldCount = 0;
if (freq.get(e.getKey()).containsKey(e2.getKey())){
oldCount = freq.get(e.getKey()).get(e2.getKey()).intValue();
}
double oldDiff = 0.0;
if (diff.get(e.getKey()).containsKey(e2.getKey())){
oldDiff = diff.get(e.getKey()).get(e2.getKey()).doubleValue();
}
double observedDiff = e.getValue() - e2.getValue();
freq.get(e.getKey()).put(e2.getKey(), oldCount + 1);
diff.get(e.getKey()).put(e2.getKey(), oldDiff + observedDiff);
}
Если кто-то оценил предмет раньше, мы увеличиваем частоту на единицу. Кроме того, мы проверяем среднюю разницу между рейтингами товара и вычисляем новый observedDiff .
-
Обратите внимание, что мы добавляем сумму oldDiff и observedDiff в качестве нового значения элемента. **
Наконец, мы рассчитываем оценки сходства внутри матриц:
for (Item j : diff.keySet()) {
for (Item i : diff.get(j).keySet()) {
double oldValue = diff.get(j).get(i).doubleValue();
int count = freq.get(j).get(i).intValue();
diff.get(j).put(i, oldValue/count);
}
}
Основная логика состоит в том, чтобы разделить вычисленную разницу рейтинга предмета на количество его вхождений. После этого шага мы можем распечатать нашу окончательную матрицу различий.
3.3. Predictions
Будучи основной частью Склона Один, мы собираемся прогнозировать все недостающие рейтинги на основе существующих данных. Для этого нам нужно сравнить рейтинги пользовательских элементов с матрицей различий, рассчитанной на предыдущем шаге:
for (Entry<User, HashMap<Item, Double>> e : data.entrySet()) {
for (Item j : e.getValue().keySet()) {
for (Item k : diff.keySet()) {
double predictedValue =
diff.get(k).get(j).doubleValue() + e.getValue().get(j).doubleValue();
double finalValue = predictedValue ** freq.get(k).get(j).intValue();
uPred.put(k, uPred.get(k) + finalValue);
uFreq.put(k, uFreq.get(k) + freq.get(k).get(j).intValue());
}
}
//...
}
После этого нам нужно подготовить «чистые» прогнозы, используя код ниже:
HashMap<Item, Double> clean = new HashMap<Item, Double>();
for (Item j : uPred.keySet()) {
if (uFreq.get(j) > 0) {
clean.put(j, uPred.get(j).doubleValue()/uFreq.get(j).intValue());
}
}
for (Item j : InputData.items) {
if (e.getValue().containsKey(j)) {
clean.put(j, e.getValue().get(j));
} else {
clean.put(j, -1.0);
}
}
Хитрость, которую следует рассмотреть с большим набором данных, состоит в том, чтобы использовать только записи элементов, которые имеют большое значение частоты (например,> 1). Обратите внимание, что если прогноз невозможен, его значение будет равно -1.
-
Наконец, очень важное примечание. Если наш алгоритм работал правильно, мы должны получить прогнозы для предметов, которые пользователь не оценил, а также повторные оценки для предметов, которые он оценил ** . Эти повторные оценки не должны изменяться, в противном случае это означает, что в реализации вашего алгоритма есть ошибка.
3.4. Подсказки
Есть несколько основных факторов, которые влияют на алгоритм Slope One. Вот несколько советов, как повысить точность и время обработки:
-
рассмотреть возможность получения пользовательских оценок на стороне БД для больших данных
наборы ** установить временные рамки для получения рейтингов, так как интересы людей могут
изменяться со временем - это также сократит время, необходимое для обработки входные данные ** разбить большие наборы данных на более мелкие - вам не нужно рассчитывать
прогнозы для всех пользователей каждый день; Вы можете проверить, взаимодействовал ли пользователь с предсказанным элементом, а затем добавить/удалить его из очереди обработки на следующий день.
4. Заключение
В этом уроке мы смогли узнать об алгоритме Slope One.
Кроме того, мы ввели проблему совместной фильтрации для систем рекомендаций по элементам.
-
Полная реализация ** этого руководства может быть найдена в the проект GitHub .