В SubitoLabs мы работаем над очень крутым проектом по анализу сообщений в чатах. По сути, нам нужно определить какое-то ключевое слово в разговоре и какой-то рабочий процесс, чтобы понять, чего хочет пользователь.

Поскольку данные представляют собой 100% пользовательский контент, мы видим как минимум 40% слов с неправильным написанием: / «not =› nnot »и т. Д. Не очень круто, если мы хотим сделать какое-то простое регулярное выражение или использовать инструмент глубокого обучения, такой как FastText. из Facebook.

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

Первая версия будет работать только пословно (без n-грамма, я перечислю некоторые термины, которые хочу проверить орфографию («скайп», «да», «уи»…) и построю словарь («скайп =› skp skipe skyp kype ”…), то простая замена строки исправит опечатки, так что давайте найдем кандидатов!

Word2vec

У меня есть шанс получить массу данных для работы, поэтому первое, что я сделал, - это преобразовал весь текст в модель word2vec (если вы не используете word2vec, я позволю вам найти ее на Qwant = › https: // www.qwant.com/?q=word2vec ).

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

Я использую библиотеку gensim python, она проста и просто работает:

from gensim.models import Word2Vec
sentences = [for line in open("toto.txt")]
model = Word2Vec(sentences=sentences, size=100, window=5, min_count=5, workers=8, sg=1)
model.save("w2c")

Теперь мы можем немного поиграть с моделью:

from gensim.models import KeyedVectors
model = KeyedVectors.load_word2vec_format(“w2c.vec”)
model.wv.most_similar(“yes”)
('yesx', 0.9025881290435791), ('yesv', 0.9015781283378601), ('yess', 0.892906129360199), ('yes7', 0.8882982730865479), ('yes§', 0.8841457962989807), ('yesb', 0.8841107487678528), ('yesç', 0.883908212184906), ('yesds', 0.8773638010025024), ('yesw', 0.8769819140434265), ('ye4s', 0.8758268356323242)]
model.wv.most_similar("yyeess")
[('yeess', 0.9566314220428467), ('yyess', 0.9314475059509277), ('yeeess', 0.9195664525032043), ('yeesss', 0.8953560590744019), ('ohhyess', 0.8813329935073853), ('ohyess', 0.8793594837188721), ('myess', 0.8710075616836548), ('mmyess', 0.8670203685760498), ('yyesss', 0.8551628589630127), ('yyeah', 0.8548532724380493)]

круто, мы обнаружили много потенциальных опечаток («hoyess» следует преобразовать в «hoyess»)! Но:

>>> model.wv.most_similar(“not”)
[(‘but’, 0.8423370122909546), (‘nnot’, 0.8417038321495056), (‘amnot’, 0.8262171149253845), (‘whynot’, 0.8115596175193787), (‘unfortunetly’, 0.8044883608818054), (‘reasonably’, 0.8031128644943237), (‘either’, 0.8030785322189331), (‘noty’, 0.8023890852928162), (‘noithing’, 0.7982239127159119), (‘notthing’, 0.79536372423172)]

Gloups! но это не опечатка для нет, мне нужен способ проверить расстояние между двумя словами не так уж и много.

Расстояние Левенштейна

Это даст расстояние между двумя словами из http://www.nltk.org/howto/metrics.html.

Слова-кандидаты

Я не хочу проверять все слова, только несколько, поэтому я сделал словарь JSON, который хочу «расширить»:

[
  {
    "fr": ["oui", "ouais"],
    "en": ["yes", "yyeess", "yeah"]
  },
  {
    "*": ["skype"]
  },
  {
    "*" : ["paypal", "ppal"]
  },
  {
    "en": ["not", "nope"],
    "fr" : ["non"]
  }
]

Это мой формат по языку, поэтому я могу написать другую задачу Python для его автоматического перевода.

Окончательный код

import json, nltk
from gensim.models import KeyedVectors
def spellcheck_generate(model_path=””, dict_path=””, save_path=None):
 dict_data = json.loads(open(dict_path).read())
 w2c_model_data = KeyedVectors.load_word2vec_format(model_path, binary=model_path.endswith(“.bin”))
def find_terms(language, terms):
 buffer = []
def is_ok(term, w2c_item):
 return w2c_item[1] > 0.6 and nltk.edit_distance(term, w2c_item[0]) < 5
for term in terms:
 buffer.append(term)
 buffer.extend([w2c_item[0] for w2c_item in w2c_model_data.wv.most_similar(term) if is_ok(term, w2c_item)])
return buffer
results = {}
for item in dict_data:
 for language, terms in item.items():
 results[terms[0]] = find_terms(language, terms)
print(results)

это дает новый словарь:

{
 “oui”: [
 “oui”,
 “oui7”,
 “ouiµ”,
 “ouii”,
 “ouiu”,
 “ouij”,
 “ouio”,
 “ouis”,
 “ouais”,
 “ouaip”,
 “ouaiss”,
 “ouai”,
 “mouais”,
 “jouais”,
 “ouaii”,
 “ouaiiis”,
 “ouaisss”,
 “oué”,
 “oui”
 ],
 “yes”: [
 “yes”,
 “yesx”,
 “yesv”,
 “yess”,
 “yes7”,
 “yes§”,
 “yesb”,
 “yesç”,
 “yesw”,
 “ye4s”,
 “yyeess”,
 “yeess”,
 “yyess”,
 “yeeess”,
 “yeesss”,
 “ohyess”,
 “myess”,
 “mmyess”,
 “yyesss”,
 “yyeah”,
 “yeah”,
 “yeahj”,
 “yeah1”,
 “yeahg”,
 “yeahs”,
 “yeaha”,
 “yeahbb”,
 “yea”,
 “yyeah”,
 “owyeah”
 ],
 “skype”: [
 “skype”,
 “skyppe”,
 “kype”,
 “skypen”,
 “ype”,
 “sk”,
 “skye”
 ],
 “paypal”: [
 “paypal”,
 “paypall”,
 “paypl”,
 “pypal”,
 “ppal”,
 “payd”,
 “payout”,
 “pal”,
 “ppal”,
 “pypal”,
 “pal”,
 “paypal”,
 “ps4”,
 “paypl”
 ],
 “not”: [
 “not”,
 “nnot”,
 “noty”,
 “nope”,
 “nopee”,
 “nops”,
 “nopt”,
 “nopg”,
 “noope”
 ],
 “non”: [
 “non”,
 “nonc”,
 “nnon”
 ]
}

«Oué» - хороший синоним «oui».