Алгоритм проектировался под рекомендации, если эта тема интересует можете посмотреть мою публикацию на медиуме "Easy way to build recommendation in Neo4J"
MATCH (c:Classifier) WHERE rand() > 0.5 WITH apoc.coll.shuffle(COLLECT(c)) AS classifiers UNWIND classifiers AS classifier WITH classifier, toFloat(rand()*100) AS rnd WITH COLLECT(classifier.id) AS ux_classifiers, COLLECT(rnd) AS ux_features, COLLECT(classifier.keyword + '(' + toInteger(rnd) + ')') AS ux_title UNWIND range(0, 10) AS it MATCH (c:Classifier) WHERE rand() > 0.7 WITH it, ux_classifiers, ux_features, ux_title, COLLECT(c.id) AS classifiers, COLLECT(1.0) AS features, COLLECT(c.keyword) AS title RETURN ux_title, title, alg.classifiers.similar(ux_classifiers, ux_features, classifiers, features) AS score ORDER BY score DESC
Отвечаю на ваш первый вопрос: Да все верно, сложность только для одного сравнения. В моем случае, все посты имели в среднем 76 классификаторов, а у пользователя в качестве опыта их было 45 Количество постов было ~10000 и рекомендация отдавалась за 600ms, что меня полностью устраивало.
Отвечаю на ваш второй вопрос: В сравнении участвуют только классификаторы отдельно взятого поста и пользователя
Да, все верно, алгоритм сводится к косинусному расстоянию по фиксированному набору признаков. Вообще-то сложность O(4N), так как в нем четыре независимых цикла, но в описании сложности принято константы отбрасывать.
Отличная статья. Реальность такова, что без ТЗ получается ХЗ, все как в реальной жизни.
Алгоритм проектировался под рекомендации, если эта тема интересует можете посмотреть мою публикацию на медиуме "Easy way to build recommendation in Neo4J"
Нормализация происходила по max, чтобы в топ поднимались только те категории, у которых максимальный вес.
Вот простая демонстрация
Я это сделал с помощью следующего запроса:
CREATE (:Classifier{id: 0, keyword: 'sky'})
CREATE (:Classifier{id: 1, keyword: 'ocean'})
CREATE (:Classifier{id: 2, keyword: 'nature'})
CREATE (:Classifier{id: 3, keyword: 'alp'})
CREATE (:Classifier{id: 4, keyword: 'mountain'})
MATCH (c:Classifier) WHERE rand() > 0.5
WITH apoc.coll.shuffle(COLLECT(c)) AS classifiers
UNWIND classifiers AS classifier
WITH classifier, toFloat(rand()*100) AS rnd
WITH COLLECT(classifier.id) AS ux_classifiers, COLLECT(rnd) AS ux_features, COLLECT(classifier.keyword + '(' + toInteger(rnd) + ')') AS ux_title
UNWIND range(0, 10) AS it
MATCH (c:Classifier) WHERE rand() > 0.7
WITH it, ux_classifiers, ux_features, ux_title, COLLECT(c.id) AS classifiers, COLLECT(1.0) AS features, COLLECT(c.keyword) AS title
RETURN ux_title, title, alg.classifiers.similar(ux_classifiers, ux_features, classifiers, features) AS score
ORDER BY score DESC
Отвечаю на ваш первый вопрос:
Да все верно, сложность только для одного сравнения.
В моем случае, все посты имели в среднем 76 классификаторов, а у пользователя в качестве опыта их было 45
Количество постов было ~10000 и рекомендация отдавалась за 600ms, что меня полностью устраивало.
Отвечаю на ваш второй вопрос:
В сравнении участвуют только классификаторы отдельно взятого поста и пользователя
N - это количество итераций цикла, которое зависит от количества параметров, т.е чем больше параметров, тем больше итераций и тем выше latency.
Да, все верно, алгоритм сводится к косинусному расстоянию по фиксированному набору признаков. Вообще-то сложность O(4N), так как в нем четыре независимых цикла, но в описании сложности принято константы отбрасывать.