В 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».