Расстояние Хэмминга (питон Симхаша), выдающее неожиданное значение
Я проверял модуль Simhash ( https://github.com/leonsim/simhash).
Я предполагаю, что расстояние Simhash ("String"). (Simhash ("Другая строка")) - это расстояние Хемминга между двумя строками. Теперь я не уверен, что полностью понимаю этот метод "get_features(string)", как показано в ( https://leons.im/posts/a-python-implementation-of-simhash-algorithm/).
def get_features(s):
width = 2
s = s.lower()
s = re.sub(r'[^\w]+', '', s)
return [s[i:i + width] for i in range(max(len(s) - width + 1, 1))]
Теперь, когда я пытаюсь вычислить расстояние между "aaaa" и "aaas", используя ширину 2, он выдает расстояние как 0.
from simhash import Simhash
Simhash(get_features("aaas")).distance(Simhash(get_features("aaaa")))
Я не уверен, что мне здесь не хватает.
1 ответ
Копаться в коде
Ширина, в вашем случае, является ключевым параметром в get_features()
, которые дают разные расщепленные слова. get_features()
в вашем случае будет выводиться как:
['аа', 'аа', 'аа']
['аа', 'аа', 'как']
Затем Simhash вычисляет эти списки как невзвешенные объекты (что означает, что вес каждой функции по умолчанию равен 1) и выводит примерно так:
86f24ba207a4912
86f24ba207a4912
Они одинаковые!
Причина в самом алгоритме simhash. Давайте посмотрим на код:
def build_by_features(self, features):
"""
`features` might be a list of unweighted tokens (a weight of 1
will be assumed), a list of (token, weight) tuples or
a token -> weight dict.
"""
v = [0] * self.f
masks = [1 << i for i in range(self.f)]
if isinstance(features, dict):
features = features.items()
for f in features:
if isinstance(f, basestring):
h = self.hashfunc(f.encode('utf-8'))
w = 1
else:
assert isinstance(f, collections.Iterable)
h = self.hashfunc(f[0].encode('utf-8'))
w = f[1]
for i in range(self.f):
v[i] += w if h & masks[i] else -w
ans = 0
for i in range(self.f):
if v[i] >= 0:
ans |= masks[i]
self.value = ans
Процесс вычисления можно разделить на 4 этапа: 1) хэшировать каждое разделенное слово (особенность), чтобы преобразовать строку в двоичные числа; 2) вес их; 3) принять взвешенные биты вместе; 4) изменить введенное число на двоичное и вывести его в качестве значения.
Теперь, в вашем случае, шаг 3 будет выводить как:
[-3, 3, -3, -3, 3, -3, -3, -3, 3, -3, -3, 3, -3, -3, 3, -3, -3, 3, -3, 3, 3, 3, 3, -3, -3, -3, -3, -3, -3, 3, -3, -3, -3, 3, -3, 3, 3, 3, -3, 3, -3, -3, 3, -3, -3, 3, -3, -3, 3, 3, 3, 3, -3, 3, 3, -3, -3, -3, -3, 3, -3, -3, -3, -3] [-1, 3, -3, -1, 3, -3, -3, -1, 3, -3, -3, 1, -1, -1, 1, -3, -3, 3, -1, 3, 1, 3, 1, -3, -1, -3, -3, -1, -1, 3, -1, -1, -1, 3, -1, 1, 3, 1, -1, 1, -3, -3, 1, -1, -3, 3, -3, -1, 1, 3, 3, 3, -3, 3, 3, -3, -1, -1, -1, 1, -3, -3, -3, -1]
И после шага 4, 2 выводят одинаковое значение.
Другой параметр
Если вы измените ширину с 2 на 1, 3, 4, вы получите другой результат
Simhash(get_features())
,
Ваш случай показывает ограничение simhash с коротким текстом.