Архив метки: GPS

Рекомендование предметов

В прошлом посте мы с Вами разобрались, как ранжировать людей по вкусам и говорить кто на нас похож больше всех. Теперь займемся связанной задачей — рекомендацией фильмов!

Рекомендование предметов

Найти подходящего критика – это, конечно, неплохо, но в действительности-то я хочу, чтобы мне порекомендовали фильм. Можно было бы посмотреть, какие фильмы понравились человеку с похожими на мои вкусами, и выбрать из них те, что я еще не смотрел. Но при таком подходе можно было бы пропустить много классных фильмов. Или посмотреть фильм, который понравился только ему.

Чтобы разрешить эти проблемы, необходимо ранжировать сами фильмы, вычислив взвешенную сумму оценок критиков. Берем каждого из отобранных критиков и умножаем его оценку подобия со мной на оценку, которую он выставил каждому фильму. Вот пример из книжки, надеюсь будет понятно)

Смысл в том, чтобы мнение критика с похожими на мои вкусами вносило больший вклад в общую оценку, чем мнение критика, не похожего на меня. В строке «Итого» приведены суммы вычисленных таким образом величин.

Можно было бы использовать для ранжирования сами эти суммы, но тогда фильм, который просмотрело больше людей, получил бы преимущество. Чтобы исправить эту несправедливость, необходимо разделить полученную величину на сумму коэффициентов подобия для всех критиков, которые рецензировали фильм (строка «S подоб.» в таблице)
По мере решения этой задачи, можно столкнуться с интересными закономерностями. 
Например, я сделал ранжирование фильмов, которым я ставил оценку. Что это значит? Представьте, что вы поставили всем фильмам 7 баллов. На страницу вашего профиля заходит Ваш друг, чтобы выбрать что посмотреть и натыкается на то, что все фильмы имеют одинаковые оценки. Чтобы этого не возникло, можно ему показывать не просто оценки, а взвешенные(с учетом вкусов друга, а не ваших), т.е. с учетом оценок других критиков.
Привожу скриптик.  Примеры приведены с Евклидовым коэффициентом подобия, исходя из его лаконичности, но так мне больше нравится Пирса.

select v1.film_id, 
count(1),--количество людей тоже ставивших голоса фильму кроме меня
evkl.sum_koef -- сумма подобия людей(со мной) голосовавших за этот фильм
from votes v1
join votes v2 on v1.film_id=v2.film_id
join (
select sum(evkl.vote*evkl.koef)/sum(evkl.koef) sum_koef, evkl.film_id
from(
select
v1.user_id,
v1.vote,
v1.film_id,
1/(1+(|/sum((v1.vote-v2.vote)^2))) koef--евклидово не взвешенное расстояние
from votes v1
join votes v2 on v1.film_id=v2.film_id
where v1.user_id <> v2.user_id
and v2.user_id=4722023 -- мой id
group by v1.user_id, v1.film_id, v1.vote
) evkl
group by evkl.film_id
) evkl on v1.film_id=evkl.film_id
where v1.user_id <> v2.user_id
and v2.user_id=4722023 -- мой id
group by v1.film_id, evkl.sum_koef
order by evkl.sum_koef desc

Таким образом фильмы с одинаковыми оценками(которые поставил я), стали взвешенными и выстроились в порядке, которые по идее мне понравились чуточку больше)))

Ну что же — это уже, как минимум не плохо) Но и основная задача, рекомендации фильмов, решается не сложнее).

select
v1.film_id,
sum(v1.vote*evkl.koef)/sum(evkl.koef)
from votes v1
join (
select
v1.user_id,
1/(1+(|/sum((v1.vote-v2.vote)^2))) koef--евклидово не взвешенное расстояние
from votes v1
join votes v2 on v1.film_id=v2.film_id
where v1.user_id <> v2.user_id
and v2.user_id=4722023
group by v1.user_id
order by v1.user_id
) evkl on evkl.user_id=v1.user_id
--фильтруем, чтобы за фильм голосовало не менее 10 человек
join (
select film_id, count(1) from votes group by film_id having count(1)>10
) flms on flms.film_id=v1.film_id
group by v1.film_id
order by sum(v1.vote*evkl.koef)/sum(evkl.koef)

Вот и все!) Что нам нужно знать об этой ф-ии. Ну во первых мы ввели ограничение на минимальное кол-во голосов за фильм в 10, чтобы не вышло так, что кому-то понравился фильм, у вас с этим критиком коэффициент близкий у 1 и соответственно фильм имеет хороший вес.

И теперь про отличие этой цифры от той, что выставлена на самом сайте kinopoisk.ru. А отличие простое: там просто средне арифметическая по голосам и кол-ву голосовавших, а эта чисто ваша, рассчитанная исходя из сходства вкусов Вас и тех кто голо
совал. Поэтому при таком подходе, у разных людей у одного и того же фильма будут разные оценки, иногда абсолютно разные!)))

Фильтрация по схожести образцов

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

Техника, которую мы применяли до сих пор, называется коллаборативной фильтрацией по схожести пользователей. Альтернатива известна под названием «коллаборативная фильтрация по схожести образцов». Когда набор данных очень велик, коллаборативная фильтрация по схожести образцов может давать лучшие результаты, причем многие вычисления можно выполнить заранее, поэтому пользователь получит рекомендации быстрее.

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

Итак, создадим табличку, куда будем записывать коэффициенты схожести фильмов между собой.

CREATE TABLE movies_similarity
(
id serial NOT NULL,
film_id1 integer NOT NULL,
film_id2 integer NOT NULL,
pirson_koef double precision NOT NULL,
CONSTRAINT movies_similarity_pk PRIMARY KEY (id)
)

Туда при помощи запроса и конструкции INSERT INTO запишем данные фильмов, которые мы оценили уже на сайте.

Выдача рекомендаций 

Теперь вы готовы выдавать рекомендации, пользуясь словарем данных о схожести образцов без обращения ко всему набору данных. Необходимо получить список всех образцов, которым пользователь выставлял оценки, найти похожие и взвесить их с учетом коэффициентов подобия. Давайте разберем пример:

В каждой строке указан фильм, который я смотрел, и оценка, которую я ему выставил. Для каждого фильма, который я не смотрел, имеется столбец, где показано, насколько он похож на виденные мной фильмы. Например, коэффициент подобия между фильмами «Superman» и «The Night Listener» равен 0,103. В столбцах с названиями, начинающимися с О.x, показана моя оценка, умноженная на коэффициент подобия; поскольку я поставил фильму «Superman» оценку 4,0, то, умножая число на пересечении строки «Superman» и столбца «Night» на 4,0, получаем: 4,0 × 0,103 = 0,412.

В строке «Итого» просуммированы коэффициенты подобия и значения в столбцах «О.x» для каждого фильма. Чтобы предсказать мою оценку фильма, достаточно разделить итог для колонки «О.x» на суммарный коэффициент подобия. Так, для фильма «The Night Listener» прогноз моей оценки равен 1,378/0,433 = 3,183. 

Если алгоритм понятен, давайте реализуем его:

select
ms.film_id1,
case when sum(ms.pirson_koef)<>0
then
sum(v.vote*ms.pirson_koef)/sum(ms.pirson_koef)
else
0
end
from votes v
join movies_similarity ms on ms.film_id2=v.film_id
where v.user_id=4722023
group by ms.film_id1
order by case when sum(ms.pirson_koef)<>0
then
sum(v.vote*ms.pirson_koef)/sum(ms.pirson_koef)
else
0
end desc

В общем из тех 14 фильмов, что я лайкнул мне посоветовали посмотреть — «Голова-ластик»
Ну что же…думаю этим и займусь) А вы найдите себе фильм на вечер!)

Автор: Pavel Petropavlov

Подбор предметов

Теперь вы знаете, как искать похожих людей и рекомендовать предметы данному человеку. Но что если нужно узнать, какие предметы похожи друг на друга? В данном случае вы можете определить степень сходства, выявив людей, которым понравился данный товар, и посмотрев, что еще им понравилось. По существу, это тот же метод, которым мы уже пользовались для определения похожести людей, – нужно лишь вместо людей всюду подставить товары. Излогаясь математическим языком — транспонировать)

Подбор схожих предметов

Произвидя поиск таким образом логика будет следующей: сравниваем 2 фильма и находим всех людей, которые смотрели оба фильма и смотрим оценки выставленные второму, если они хорошие, а лучше все 10ки)

Практика Евклида

select
v1.film_id, v2.film_id,
1/(1+(|/sum((v1.vote-v2.vote)^2))),--евклидово не взвешенное расстояние
count(1)--кол-во общих фильмов(вес для ф-ии)
from votes v1
join votes v2 on v1.user_id=v2.user_id
where v1.film_id <> v2.film_id
and v2.film_id=326
group by v1.film_id, v2.film_id
--having count(1)>10
order by 1/(1+(|/sum((v1.vote-v2.vote)^2))) desc, count(1) desc

Для сравнения возьмем фильм «Побег из Шоушенка». Метод Евклида посчитал, что на него больше всего похож фильм «Реал Мадрид». Спросите чем? Отвечу:

select
v1.film_id, v2.film_id,
v1.vote vote1, v2.vote vote2, v1.user_id, v2.user_id
from votes v1
join votes v2 on v1.user_id=v2.user_id
where v1.film_id <> v2.film_id
and v2.film_id=326
and v1.film_id=155080

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

Практика Пирсона

select
v1.film_id,
case when ((sum(v1.vote^2)+1)-(sum(v1.vote)^2)/count(1)::float)*((sum(v2.vote^2)+1)-(sum(v2.vote)^2)/count(1)::float) <> 0
then
(sum(v1.vote*v2.vote)-(sum(v1.vote)*sum(v2.vote))/count(1)::float)/(|/((sum(v1.vote^2)+1)-(sum(v1.vote)^2)/count(1)::float)*((sum(v2.vote^2)+1)-(sum(v2.vote)^2)/count(1)::float))
else
0
end,
count(1)--кол-во общих критиков(вес для ф-ии)
from votes v1
join votes v2 on v1.user_id=v2.user_id
where v1.film_id <> v2.film_id
and v2.film_id=326 --фильм для сравнения
group by v1.film_id, v2.film_id
--having count(1)>10
order by
case when ((sum(v1.vote^2)+1)-(sum(v1.vote)^2)/count(1)::float)*((sum(v2.vote^2)+1)-(sum(v2.vote)^2)/count(1)::float) <> 0
then
(sum(v1.vote*v2.vote)-(sum(v1.vote)*sum(v2.vote))/count(1)::float)/(|/((sum(v1.vote^2)+1)-(sum(v1.vote)^2)/count(1)::float)*((sum(v2.vote^2)+1)-(sum(v2.vote)^2)/count(1)::float))
else
0
end
desc,
count(1) desc

Запустив пирсона, получим совсем другой фильм — «Крезанутые». И это всего при 2х общих критиках. В общем советую Вам обратить внимание на закомментированную строку с having, считаю что нужно ее применить для более точного совпадения)

Автор: Pavel Petropavlov

Машинное обучение. Начало.

Предисловие

Итак, начнем цикл статей про машинное обучение) В основном он будет основан на материале из различных книг, но основная идея цикла — это подача!) А подавать будем, попытавшись написать реальный проектик и попрактиковавшись в различных интересных штукенциях)

В поисках данных

Итак, нам нужны данные — много данных!) И желательно интересных. Не знаю, как вы, но я долго думать не стал и воспользовался сайтом kinopoisk.ru, надеюсь сильно бить не будут, ведь мы будем взращивать их будущие кадры(т.е. Вас!)))

Начнем с конфигурации. В разных книгах примеры данных хранятся во всяких rss, plain files и прочей не современной чепухе) Мы то с Вами живем в то время, когда даже слово sql стыдно произносить в приличном обществе, без приставки no!) Но мы произнесем — PostgreSQL! Начнем с установки(ищем, где ищется)) а закончим созданием таблички votes.

CREATE TABLE votes
(
id serial NOT NULL, --уникальный идентификатор
user_id integer NOT NULL, --id пользователя с сайта
film_id integer NOT NULL, --id фильма с сайта
vote smallint NOT NULL, --оценка фильму пользователем от 1 до 10
CONSTRAINT vote_id PRIMARY KEY (id)
)

Объяснять столбцы не буду!!!))) Если все получилось, давайте добавим данных. Бить меня не нужно, можно только корректировать, я для Вас написал следующее чудо, запускаем и…не ждем, а приступаем к работе, он и сам справится, за несколько лет)) У меня на момент написания этих строк обработано 9000 пользователей из как минимум 4722023 (спалился:)

import re, psycopg2
from lxml.html import parse
from lxml.cssselect import CSSSelector

def get_user_voites():

user_start_num = 200
user_end_num = 4722023

try:
conn=psycopg2.connect("dbname='postgres' user='postgres' password='120789' host='localhost'")
cur = conn.cursor()
page_num_sel = CSSSelector('div.pagesFromTo')

for user_num in xrange(user_start_num, user_end_num):

vote_film_list = []
loop_bool = True
page_num = 1

while loop_bool:
try:
page = parse('http://www.kinopoisk.ru/user/%s/votes/list/ord/date/page/%s/' % (user_num, page_num))

vote_count_div = int(page_num_sel(page)[0].text.split()[-1])

items = page.xpath("//div[contains(@class, 'item')]")
for item in items:
try:
film_div = item.find("div[@class='info']").find("div[@class='nameRus']").find("a")

vote_num = int(item.find("div[@class='num']").text)
film_id = re.search('film/(d)+/$', film_div.values()[0]).group(0)[5:-1]
film_name = film_div.text
vote = item.find("div[@class='vote']").text

if vote and film_id:
vote_film_list.append({'vote': vote, 'user_id': user_num, 'film_id': film_id})
except:
pass

if vote_num <= vote_count_div:
page_num += 1

print vote_num, page_num, vote_count_div, len(vote_film_list)
cur.executemany("""INSERT INTO votes(user_id, film_id, vote) VALUES (%(user_id)s, %(film_id)s, %(vote)s)""", vote_film_list)
conn.commit()
vote_film_list = []

except Exception, e:
print e
loop_bool = False


except Exception, e:
print "I am unable to connect to the database.", e

finally:
if conn:
conn.close()

Автор: Pavel Petropavlov

Линейный поиск

Линейный, последовательный поиск — алгоритм нахождения заданного значения произвольной функции на некотором отрезке. Данный алгоритм является простейшим алгоритмом поиска и в отличие, например, от двоичного поиска, не накладывает никаких ограничений на функцию и имеет простейшую реализацию. Поиск значения функции осуществляется простым сравнением очередного рассматриваемого значения (как правило поиск происходит слева направо, то есть от меньших значений аргумента к большим) и, если значения совпадают (с той или иной точностью), то поиск считается завершённым.

Читать

Сортировка слиянием

Сортировка слиянием (англ. merge sort) — алгоритм сортировки, который упорядочивает списки (или другие структуры данных, доступ к элементам которых можно получать только последовательно, например — потоки) в определённом порядке. Сначала задача разбивается на несколько подзадач меньшего размера. Затем эти задачи решаются с помощью рекурсивного вызова или непосредственно, если их размер достаточно мал. Наконец, их решения комбинируются, и получается решение исходной задачи. Алгоритм был изобретён Джоном фон Нейманом в 1945 году.

Странно, но мне показалось, что данный алгоритм не так сильно освещён в интернете в целом и на python  в частности. А ведь именно этот алгоритм чаще всего применяется для сортировки данных не помещающихся в оперативке, т.е. хранящихся в файлах(внешние сортировки). Как и в случае с быстрой сортировкой Хоара, существует 2 реализации данного алгоритма. Рекурсивная и нет) Скажу по секрету, не рекурсивную реализацию мне предоставил так же мой преподаватель Деон. Более того, она ещё и не требует дополнительной памяти, что в классическом варианте относят к её основным минусам!

Для решения задачи сортировки эти три этапа выглядят так:
  1. Сортируемый массив разбивается на две части примерно одинакового размера;
  2. Каждая из получившихся частей сортируется отдельно, например — тем же самым алгоритмом;
  3. Два упорядоченных массива половинного размера соединяются в один.
Рекурсивное разбиение задачи на меньшие происходит до тех пор, пока размер массива не достигнет единицы (любой массив длины 1 можно считать упорядоченным).
Нетривиальным этапом является соединение двух упорядоченных массивов в один. Основную идею слияния двух отсортированных массивов можно объяснить на следующем примере. Пусть мы имеем две стопки карт, лежащих рубашками вниз так, что в любой момент мы видим верхнюю карту в каждой из этих стопок. Пусть также, карты в каждой из этих стопок идут сверху вниз в неубывающем порядке. Как сделать из этих стопок од

Курсы во Львове

14-15 сентября буду читать немного ужатый вариант курсов по асинхронному сетевому программированию.
Подробности здесь.

Автор: Andrew Svetlov