Жизнеспособное решение для разделения слов кхмерский? - PullRequest
15 голосов
/ 01 февраля 2011

Я работаю над решением разбить длинные строки кхмерского (камбоджийский язык) на отдельные слова (в UTF-8). Кхмерский не использует пробелы между словами. Есть несколько решений, но они далеко не адекватны ( здесь и здесь ), и эти проекты отошли на второй план.

Вот пример строки кхмера, которую нужно разделить (они могут быть длиннее этой):

ចូរ សរសើរ ដល់ 100 ទ្រង់ បាន ទាំងអស់ ទាំងអស់ ទាំងអស់ ទាំងអស់ រូប រូប ព្រោះ អង្គ អង្គ អង្គ 101 101 101 101 101 101 101 101 101 101 101

Цель создания жизнеспособного решения, которое разделяет кхмерские слова, двояка: это побудит тех, кто использовал кхмерские устаревшие (не-Unicode) шрифты, переходить на Unicode (что имеет много преимуществ), и это позволит использовать старые кхмерские шрифты для импорта в Unicode для быстрого использования с проверкой орфографии (вместо того, чтобы вручную проходить и разбивать слова, что при большом документе может занять очень много времени).

Мне не нужна 100% точность, но важна скорость (особенно потому, что линия, которую нужно разделить на кхмерские слова, может быть довольно длинной). Я открыт для предложений, но в настоящее время у меня есть большой набор кхмерских слов, которые правильно разделены (с неразрывным пробелом), и я создал файл словаря вероятности слова (quency.csv) для использования в качестве словаря для разделитель слов.

Я нашел этот код Python здесь , который использует алгоритм Витерби , и он предположительно работает быстро.

import re
from itertools import groupby

def viterbi_segment(text):
    probs, lasts = [1.0], [0]
    for i in range(1, len(text) + 1):
        prob_k, k = max((probs[j] * word_prob(text[j:i]), j)
                        for j in range(max(0, i - max_word_length), i))
        probs.append(prob_k)
        lasts.append(k)
    words = []
    i = len(text)
    while 0 < i:
        words.append(text[lasts[i]:i])
        i = lasts[i]
    words.reverse()
    return words, probs[-1]

def word_prob(word): return dictionary.get(word, 0) / total
def words(text): return re.findall('[a-z]+', text.lower()) 
dictionary = dict((w, len(list(ws)))
                  for w, ws in groupby(sorted(words(open('big.txt').read()))))
max_word_length = max(map(len, dictionary))
total = float(sum(dictionary.values()))

Я также пытался использовать исходный код Java от автора этой страницы: Сегментация текста: разбиение слов по словарю , но оно выполнялось слишком медленно, чтобы его можно было использовать (потому что мой словарь вероятности превышен 100 тысяч терминов ...).

А вот еще один вариант в python из Определение наиболее вероятных слов из текста без пробелов / комбинированных слов :

WORD_FREQUENCIES = {
    'file': 0.00123,
    'files': 0.00124,
    'save': 0.002,
    'ave': 0.00001,
    'as': 0.00555
}

def split_text(text, word_frequencies, cache):
    if text in cache:
        return cache[text]
    if not text:
        return 1, []
    best_freq, best_split = 0, []
    for i in xrange(1, len(text) + 1):
        word, remainder = text[:i], text[i:]
        freq = word_frequencies.get(word, None)
        if freq:
            remainder_freq, remainder = split_text(
                    remainder, word_frequencies, cache)
            freq *= remainder_freq
            if freq > best_freq:
                best_freq = freq
                best_split = [word] + remainder
    cache[text] = (best_freq, best_split)
    return cache[text]

print split_text('filesaveas', WORD_FREQUENCIES, {})

--> (1.3653e-08, ['file', 'save', 'as'])

Я новичок, когда дело доходит до Python, и я действительно новичок во всем реальном программировании (за исключением веб-сайтов), поэтому, пожалуйста, потерпите меня. У кого-нибудь есть варианты, которые, по их мнению, будут работать хорошо?

Ответы [ 3 ]

3 голосов
/ 01 февраля 2011

Библиотека ICU (которая имеет Python и привязки Java) имеет класс DictionaryBasedBreakIterator , который можно использовать для этого.

1 голос
/ 03 февраля 2011

Я думаю, что это хорошая идея, как она есть.

Я предлагаю вам, когда у вас есть некоторый опыт работы с ней, вы добавляете некоторые правила, которые могут быть очень конкретными, например, в зависимости от словадо, в зависимости от слова после, в зависимости от окружающих слов, в зависимости от последовательности слов перед текущим словом, просто перечислить наиболее часто встречающиеся.Вы можете найти набор правил в проекте gposttl.sf.net, который является проектом pos-тегирования, в файле data / contextualrulefile.

Должны использоваться правила ПОСЛЕ того, как оценка статистики завершена, они производят некоторую подстройку и могут значительно улучшить точность.

1 голос
/ 01 февраля 2011

Питон с примером filesaveas, кажется, повторяется по всей входной строке (for i in xrange(1, len(text) + 1)), вставляя лучшие результаты в cache по пути; в каждом потенциальном слове оно затем начинает смотреть на слово next (которое, в свою очередь, будет смотреть на слово после этого и т. д.), и если это второе слово не выглядит очень хорошо, это не спасет тот конкретный. ощущается как время выполнения O (N!), Где N - длина входной строки.

Супер умный, но, вероятно, ужасный для всего, кроме простых задач. Какое самое длинное кхмерское слово у вас есть? Я надеюсь <20 символов. </p>

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

На совершенно другом уровне, сколько кхмерских слов образуется путем объединения двух или более законных кхмерских слов? (аналогично «перочинному ножу» или «баскетбольному мячу»). Если не слишком много, то может иметь смысл создать набор словарей, разделенных по длине слова и отображающих слово в вероятность использования.

Скажем, самое длинное кхмерское слово имеет длину 14 символов; введите 14 символов ввода в словарь len14, сохраните вероятность. Введите 13 символов в len13, сохраните вероятность. Введите 12 символов ... вплоть до 1 в len1. Затем выберите интерпретацию с наибольшей вероятностью, сохраните слово, удалите столько символов и повторите попытку.

Так что для входов, таких как «I» или «Изображение», он не будет плохо работать, возможно, более длинные входы должны иметь автоматически завышенные вероятности?

Спасибо за интересный вопрос;) Я не знал ни одного подобного языка, довольно круто.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...