Как построить основанную на контенте систему рекомендаций, которая использует несколько атрибутов?
Я новичок в машинном обучении. Я хочу построить основанную на контенте систему рекомендаций в Python, которая использует несколько атрибутов, чтобы решить, похожи ли два элемента. В моем случае "элементы" - это пакеты, размещаемые менеджером пакетов C# ( пример), которые имеют различные атрибуты, такие как имя, описание, теги, которые могут помочь идентифицировать подобные пакеты.
У меня есть система рекомендаций прототипа, которая в настоящее время использует только один атрибут, описание, чтобы решить, похожи ли пакеты. Он вычисляет рейтинги TF-IDF для описаний и выводит 10 лучших рекомендаций на основании этого:
# Code mostly stolen from http://blog.untrod.com/2016/06/simple-similar-products-recommendation-engine-in-python.html
def train(dataframe):
tfidf = TfidfVectorizer(analyzer='word',
ngram_range=(1, 3),
min_df=0,
stop_words='english')
tfidf_matrix = tfidf.fit_transform(dataframe['description'])
cosine_similarities = linear_kernel(tfidf_matrix, tfidf_matrix)
for idx, row in dataframe.iterrows():
similar_indices = cosine_similarities[idx].argsort()[:-10:-1]
similar_items = [(dataframe['id'][i], cosine_similarities[idx][i])
for i in similar_indices]
id = row['id']
similar_items = [it for it in similar_items if it[0] != id]
# This 'sum' is turns a list of tuples into a single tuple:
# [(1,2), (3,4)] -> (1,2,3,4)
flattened = sum(similar_items, ())
try_print("Top 10 recommendations for %s: %s" % (id, flattened))
Как я могу совместить cosine_similarities
с другими мерами сходства (на основе того же автора, схожих имен, общих тегов и т. д.), чтобы дать больше контекста моим рекомендациям?
3 ответа
Для некоторого контекста моя работа с рекомендациями, основанными на контенте, вращалась вокруг необработанного текста и категориальных данных / функций. Вот подход высокого уровня, который я выбрал, который хорошо сработал и довольно прост в реализации.
Предположим, у меня есть три функциональных столбца, которые я могу потенциально использовать для выработки рекомендаций: description
, name
, а также tags
, Для меня путь наименьшего сопротивления влечет за собой сочетание этих трех наборов функций в полезной форме.
Вы хорошо начали, используя TF-IDF для кодирования description
, Так почему бы не лечить name
а также tags
аналогичным образом, создавая "корпус", состоящий из description
, name
, а также tags
? В буквальном смысле это будет означать объединение содержимого каждого из трех столбцов в один длинный текстовый столбец.
Будьте внимательны в отношении конкатенации, так как, вероятно, в ваших интересах сохранить, из какого столбца происходит данное слово, в случае таких функций, как name
а также tag
которые, как предполагается, имеют гораздо меньшую мощность, чем description
, Чтобы выразить это более явно: вместо того, чтобы просто создать столбец корпуса, как это:
df['corpus'] = (pd.Series(df[['description', 'name', 'tags']]
.fillna('')
.values.tolist()
).str.join(' ')
Вы можете попытаться сохранить информацию о том, где конкретные данные указывают на name
а также tags
родом из. Что-то вроде этого:
df['name_feature'] = ['name_{}'.format(x) for x in df['name']]
df['tags_feature'] = ['tags_{}'.format(x) for x in df['tags']]
И после того, как вы это сделаете, я бы пошел дальше, рассмотрев, как работает токенайзер по умолчанию (который вы используете выше) в TfidfVectorizer
, Предположим, у вас есть имя автора данного пакета: "Джонни" Молния "Громовержец". Если вы просто объедините эту литеральную строку, токенизатор разделит ее на части и свернет каждый из "Джонни", "Молния" и "Тандерсмит" в отдельные функции, которые потенциально могут уменьшить информацию, добавляемую значением этой строки для name
, Я думаю, что лучше попытаться сохранить эту информацию. Поэтому я хотел бы сделать что-то подобное для каждого из ваших текстовых столбцов с меньшим количеством элементов (например, name
или же tags
):
def raw_text_to_feature(s, sep=' ', join_sep='x', to_include=string.ascii_lowercase):
def filter_word(word):
return ''.join([c for c in word if c in to_include])
return join_sep.join([filter_word(word) for word in text.split(sep)])
def['name_feature'] = df['name'].apply(raw_text_to_feature)
Такое же критическое мышление должно применяться к tags
, Если у вас есть разделенный запятыми "список" тегов, вам, вероятно, придется проанализировать их по отдельности и найти правильный способ их использования.
В конечном итоге, как только вы получите все свои <x>_feature
После создания столбцов вы можете создать свой окончательный "корпус" и подключить его к вашей рекомендательной системе в качестве входных данных.
Конечно, вся эта система требует некоторой разработки, но я обнаружил, что это самый простой способ ввода новой информации из других столбцов, имеющих разный кардинал.
Насколько я понимаю ваш вопрос, это можно сделать двумя способами:
1) Объедините другие функции с tfidf_matrix
а затем рассчитать косинус сходства
2) Рассчитайте сходство других признаков, используя другие методы, а затем каким-то образом объедините их с косинусным сходством tfidf_matrix
чтобы получить значимую метрику.
Я говорил о первом.
Например, скажем, для ваших данных, tfidf_matrix
(только для 'description'
колонна) имеет форму (3000, 4000)
где 3000 - строки в данных, а 4000 - уникальные слова (словарь), найденные TfidfVectorizer.
Теперь предположим, что вы выполняете некоторую обработку объектов в других столбцах ("авторы", "идентификатор" и т. Д.), И получается 5 столбцов. Таким образом, форма этих данных (3000, 5)
,
Я говорил объединить две матрицы (объединить столбцы) так, чтобы новая форма ваших данных была (3000, 4005)
и затем вычислите косинус сходства.
Смотрите ниже пример:
from scipy import sparse
# This is your original matrix
tfidf_matrix = tfidf.fit_transform(dataframe['description'])
# This is the other features
other_matrix = some_processing_on_other_columns()
sparse.hstack((tfidf_matrix, other_matrix))
cosine_similarities = linear_kernel(combined_matrix, combined_matrix)
У вас есть вектор для пользователя $\gamma_u$ и элемент $\gamma_i$. Функция оценки по вашей рекомендации:
Прямо сейчас вы сказали, что у вашего векторного элемента есть только 1 элемент, но как только вы получите больше, эта модель будет масштабироваться для этого.
В этом случае вы уже спроектировали свои векторы, но, как правило, в рекомендациях, функция изучается посредством матричной факторизации. Это называется моделью скрытого фактора, тогда как у вас есть модель, созданная вручную.