Разделить предложение на отдельные слова

Мне нужно разделить китайское предложение на отдельные слова. Проблема с китайцами в том, что нет пробелов. Например, предложение может выглядеть так: 主楼怎么走 (с пробелами это будет: 主楼 怎么 走 ).

На данный момент я могу думать о одном решении. У меня есть словарь с китайскими словами (в базе данных). Сценарий будет:

  1. попробуйте найти первые два символа предложения в базе данных ( 主楼 ),

  2. если 主楼 на самом деле является словом, и он находится в базе данных, скрипт попытается найти первые три символа ( 主楼怎 ). 主楼怎 не слово, поэтому его нет в базе данных => теперь мое приложение знает, что 主楼 является отдельным словом ,

  3. попробуйте сделать это с остальными персонажами.

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

Есть ли другие решения?

Спасибо всем за помощь!

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

  1. Класс PHP ( http://www.phpclasses.org/browse/package/2431.html )

  2. Модуль Drupal, в основном другое PHP-решение с 4 различными алгоритмами сегментации (довольно легко понять, как это работает) ( http://drupal.org/project/csplitter )

  3. Расширение PHP для сегментации китайских слов ( http://code.google.com/p/phpcws/ )

  4. Есть некоторые другие решения, доступные, если вы попробуете найти baidu.com для «中文 分 词»,

С уважением,

Equ

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

У вас есть текст ввода, предложение, абзац что угодно. Так что да, для его обработки вам нужно будет запросить вашу БД для каждой проверки.

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

Сказав это, насколько велик этот словарь? В конце концов, вам понадобятся только слова, а не их определения, чтобы проверить, действительно ли это слово. Поэтому, если это вообще возможно (в зависимости от размера), наличие огромной карты памяти / хеш-таблицы / словаря с помощью только клавиш (фактические слова) может быть опцией и будет быстрым, как молния.

При 15 миллионах слов, скажем, в среднем 7 символов @ 2 байта, каждый из которых работает вокруг отметки 200 мегабайт. Не слишком сумасшедший.

Изменить: «Всего» 1 миллион слов, вы просматриваете чуть более 13 мегабайт, скажем, 15 с некоторыми накладными расходами. Я бы сказал, что это нелегко.

Другой, который хорошо работает, – http://www.itgrass.com/phpanalysis/index.html

Это единственное, что я нашел, что работает правильно с utf-8. Остальное работало только для меня в gb18030, что вызвало массу проблем позже. Я думал, что мне придется начать все сначала, но это спасло меня много времени.

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

Чтобы повысить эффективность этого процесса, не можете ли вы выполнить все эти проверки перед тем, как вставить предложение в базу данных и сами добавить пробелы?

(с помощью ABCDE для представления китайских символов для простоты)

Предположим, у вас есть вход ABCDE «предложение», и ваш словарь содержит эти слова, которые начинаются с A : AB , ABC , AC , AE и ABB . И предположим, что слово CDE существует, но DE , и E нет.

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

Прокрутите эти результаты, захватив следующие несколько символов из входной строки, чтобы получить правильное сравнение:

 AB ?= AB : True ABC ?= ABC: True AC ?= AB : False AE ?= AB : False ABB ?= ABC: False 

На этом этапе программа разворачивает две «истинные» ветви, которые она обнаружила. Во-первых, он предполагает, что AB является первым словом и пытается найти слова C- starting. CDE , так что ветка возможна. Вниз по другой ветке ABC – это первое слово, но DE невозможно, так что ветка недействительна, то есть первая должна быть истинной интерпретацией.

Я думаю, что этот метод минимизировал количество вызовов в базе данных (хотя он мог бы возвращать более крупные наборы из базы данных, поскольку вы извлекаете множество слов, начинающихся с одного и того же символа). Если ваша база данных была проиндексирована для такого рода поиска, я думаю, что это будет работать лучше, чем письмо с письмом. Теперь, глядя на весь этот процесс, и на другие ответы, я думаю, что это фактически структура trie (предполагая, что искаженный персонаж является корнем дерева), как предложил другой плакат. Ну, вот реализация этой идеи!

Я действительно понимаю, что проблема сегментации китайского слова очень сложна, но в некоторых случаях этот тривиальный алгоритм может быть достаточным: поиск самого длинного слова w, начинающегося с i-го символа, затем снова начните для символа i + length (w) -го символа ,

Вот реализация Python:

 #!/usr/bin/env python # encoding: utf-8 import re import unicodedata import codecs class ChineseDict: def __init__(self,lines,rex): self.words = set(rex.match(line).group(1) for line in lines if not line.startswith("#")) self.maxWordLength = max(map(len,self.words)) def segmentation(self,text): result = [] previousIsSticky = False i = 0 while i < len(text): for j in range(i+self.maxWordLength,i,-1): s = text[i:j] if s in self.words: break sticky = len(s)==1 and unicodedata.category(s)!="Lo" if previousIsSticky or (result and sticky): result[-1] += s else: result.append(s) previousIsSticky = sticky i = j return u" | ".join(result) def genWords(self,text): i = 0 while i < len(text): for j in range(i+self.maxWordLength,i,-1): s = text[i:j] if s in self.words: yield s break i = j if __name__=="__main__": cedict = ChineseDict(codecs.open("cedict_ts.u8",'r','utf-8'),re.compile(r"(?u)^.+? (.+?) .+")) text = u"""33. 你可以叫我夏尔戴高乐将军和夫人在科隆贝双教堂村过周末。星期日早晨,伊冯娜无意中走进浴室,正巧将军在洗盆浴。她感到非常意外,不禁大叫一声:“我的上帝!”戴高乐于是转过身,看见妻子因惊魂未定而站立在门口。他继续用香皂擦身,不紧不慢地说:“伊冯娜,你知道,如果是我们之间的隐私,你可以叫我夏尔,用不着叫我上帝……” """ print cedict.segmentation(text) print u" | ".join(cedict.genWords(text)) 村过周末星期日早晨#!/usr/bin/env python # encoding: utf-8 import re import unicodedata import codecs class ChineseDict: def __init__(self,lines,rex): self.words = set(rex.match(line).group(1) for line in lines if not line.startswith("#")) self.maxWordLength = max(map(len,self.words)) def segmentation(self,text): result = [] previousIsSticky = False i = 0 while i < len(text): for j in range(i+self.maxWordLength,i,-1): s = text[i:j] if s in self.words: break sticky = len(s)==1 and unicodedata.category(s)!="Lo" if previousIsSticky or (result and sticky): result[-1] += s else: result.append(s) previousIsSticky = sticky i = j return u" | ".join(result) def genWords(self,text): i = 0 while i < len(text): for j in range(i+self.maxWordLength,i,-1): s = text[i:j] if s in self.words: yield s break i = j if __name__=="__main__": cedict = ChineseDict(codecs.open("cedict_ts.u8",'r','utf-8'),re.compile(r"(?u)^.+? (.+?) .+")) text = u"""33. 你可以叫我夏尔戴高乐将军和夫人在科隆贝双教堂村过周末。星期日早晨,伊冯娜无意中走进浴室,正巧将军在洗盆浴。她感到非常意外,不禁大叫一声:“我的上帝!”戴高乐于是转过身,看见妻子因惊魂未定而站立在门口。他继续用香皂擦身,不紧不慢地说:“伊冯娜,你知道,如果是我们之间的隐私,你可以叫我夏尔,用不着叫我上帝……” """ print cedict.segmentation(text) print u" | ".join(cedict.genWords(text))意外 #!/usr/bin/env python # encoding: utf-8 import re import unicodedata import codecs class ChineseDict: def __init__(self,lines,rex): self.words = set(rex.match(line).group(1) for line in lines if not line.startswith("#")) self.maxWordLength = max(map(len,self.words)) def segmentation(self,text): result = [] previousIsSticky = False i = 0 while i < len(text): for j in range(i+self.maxWordLength,i,-1): s = text[i:j] if s in self.words: break sticky = len(s)==1 and unicodedata.category(s)!="Lo" if previousIsSticky or (result and sticky): result[-1] += s else: result.append(s) previousIsSticky = sticky i = j return u" | ".join(result) def genWords(self,text): i = 0 while i < len(text): for j in range(i+self.maxWordLength,i,-1): s = text[i:j] if s in self.words: yield s break i = j if __name__=="__main__": cedict = ChineseDict(codecs.open("cedict_ts.u8",'r','utf-8'),re.compile(r"(?u)^.+? (.+?) .+")) text = u"""33. 你可以叫我夏尔戴高乐将军和夫人在科隆贝双教堂村过周末。星期日早晨,伊冯娜无意中走进浴室,正巧将军在洗盆浴。她感到非常意外,不禁大叫一声:“我的上帝!”戴高乐于是转过身,看见妻子因惊魂未定而站立在门口。他继续用香皂擦身,不紧不慢地说:“伊冯娜,你知道,如果是我们之间的隐私,你可以叫我夏尔,用不着叫我上帝……” """ print cedict.segmentation(text) print u" | ".join(cedict.genWords(text)) 他继续用香皂擦身 #!/usr/bin/env python # encoding: utf-8 import re import unicodedata import codecs class ChineseDict: def __init__(self,lines,rex): self.words = set(rex.match(line).group(1) for line in lines if not line.startswith("#")) self.maxWordLength = max(map(len,self.words)) def segmentation(self,text): result = [] previousIsSticky = False i = 0 while i < len(text): for j in range(i+self.maxWordLength,i,-1): s = text[i:j] if s in self.words: break sticky = len(s)==1 and unicodedata.category(s)!="Lo" if previousIsSticky or (result and sticky): result[-1] += s else: result.append(s) previousIsSticky = sticky i = j return u" | ".join(result) def genWords(self,text): i = 0 while i < len(text): for j in range(i+self.maxWordLength,i,-1): s = text[i:j] if s in self.words: yield s break i = j if __name__=="__main__": cedict = ChineseDict(codecs.open("cedict_ts.u8",'r','utf-8'),re.compile(r"(?u)^.+? (.+?) .+")) text = u"""33. 你可以叫我夏尔戴高乐将军和夫人在科隆贝双教堂村过周末。星期日早晨,伊冯娜无意中走进浴室,正巧将军在洗盆浴。她感到非常意外,不禁大叫一声:“我的上帝!”戴高乐于是转过身,看见妻子因惊魂未定而站立在门口。他继续用香皂擦身,不紧不慢地说:“伊冯娜,你知道,如果是我们之间的隐私,你可以叫我夏尔,用不着叫我上帝……” """ print cedict.segmentation(text) print u" | ".join(cedict.genWords(text)) 

В последней части используется копия словаря CCEDICT для сегментирования (упрощенного) китайского текста в двух вариантах (соответственно, с символами без слов):

 33. 你 | 可以 | 叫 | 我 | 夏 | 尔戴高乐 | 将军 | 和 | 夫人 | 在 | 科隆 | 贝 | 双 | 教堂 | 村 | 过 | 周末。星期日 | 早晨,伊 | 冯 | 娜 | 无意中 | 走进 | 浴室,正巧 | 将军 | 在 | 洗 | 盆浴。她 | 感到 | 非常 | 意外,不禁 | 大 | 叫 | 一声:“我的 | 上帝!”戴高乐 | 于是 | 转 | 过 | 身,看见 | 妻子 | 因 | 惊魂 | 未定 | 而 | 站立 | 在 | 门口。他 | 继续 | 用 | 香皂 | 擦 | 身,不 | 紧 | 不 | 慢 | 地 | 说:“伊 | 冯 | 娜,你 | 知道,如果 | 是 | 我们 | 之间 | 的 | 隐私,你 | 可以 | 叫 | 我 | 夏 | 尔,用不着 | 叫 | 我 | 上帝……”你 | 可以 | 叫 | 我 | 夏 | 尔 | 戴高乐 | 将军 | 和 | 夫人 | 在 | 科隆 | 贝 | 双 | 教堂 | 村 | 过 | 周末 | 星期日 | 早晨 | 伊 | 冯 | 娜 | 无意中 | 走进 | 浴室 | 正巧 | 将军 | 在 | 洗 | 盆浴 | 她 | 感到 | 非常 | 意外 | 不禁 | 大 | 叫 | 一声 | 我的 | 上帝 | 戴高乐 | 于是 | 转 | 过 | 身 | 看见 | 妻子 | 因 | 惊魂 | 未定 | 而 | 站立 | 在 | 门口 | 他 | 继续 | 用 | 香皂 | 擦 | 身 | 不 | 紧 | 不 | 慢 | 地 | 说 | 伊 | 冯 | 娜 | 你 | 知道 | 如果 | 是 | 我们 | 之间 | 的 | 隐私 | 你 | 可以 | 叫 | 我 | 夏 | 尔 | 用不着 | 叫 | 我 | 上帝 

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

Подробнее об этом читайте здесь: http://technology.chtsai.org/mmseg/

Это метод, который я использую в своем текстовом анализаторе (Duzhe) ( http://duzhe.aaginskiy.com ). Я не использую базу данных, на самом деле я предварительно загружаю список слов в массив, который занимает около ~ 2 МБ ОЗУ, но выполняется очень быстро.

Если вы изучаете использование лексической сегментации по статистике (хотя статистический метод может быть таким же точным, как ~ 97%, согласно некоторым исследованиям), очень хорошим инструментом сегментации является ADSOtrans, который можно найти здесь:

http://www.adsotrans.com

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

Надеюсь это поможет.

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

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

Вы можете построить очень длинное регулярное выражение.

Изменить: я хотел построить его автоматически с помощью скрипта из БД. Не писать это вручную.