PHP: Определение языка текста с помощью N-грамм. Часть 2
Вторая часть статьи Яна Барбера об определении языка текста с помощью PHP. Первую часть можно найти тут.
Разбить на две части пришлось из-за большого объема текста с форматированием («Some error… We know. ). К сожалению, на моем компьютере не было особо большого количества материалов для построения моделей, но для этого сгодились многоязычные словари OSX. Убрав с помощью strip_tags XML теги, я получил чистый текст.
$ lang = new LangDetector ( ) ; $ dir = " /Library/Dictionaries/Apple Dictionary.dictionary/Contents/Resources/ " ; $ dutch = strip_tags ( file_get_contents ( $ dir . " Dutch.lproj/Body.data " ) ) ; $ lang -> adddocument ( $ dutch , ' dutch ' ) ; $ english = strip_tags ( file_get_contents ( $ dir . " English.lproj/Body.data " ) ) ; $ lang -> adddocument ( $ english , ' english ' ) ; $ finnish = strip_tags ( file_get_contents ( $ dir . " fi.lproj/Body.data " ) ) ; $ lang -> adddocument ( $ finnish , ' finnish ' ) ; $ spanish = strip_tags ( file_get_contents ( $ dir . " Spanish.lproj/Body.data " ) ) ; $ lang -> adddocument ( $ spanish , ' spanish ' ) ; $ italian = strip_tags ( file_get_contents ( $ dir . " Italian.lproj/Body.data " ) ) ; $ lang -> adddocument ( $ italian , ' italian ' ) ; $ french = strip_tags ( file_get_contents ( $ dir . " French.lproj/Body.data " ) ) ; $ lang -> adddocument ( $ french , ' french ' ) ; $ swedish = strip_tags ( file_get_contents ( $ dir . " sv.lproj/Body.data " ) ) ; $ lang -> adddocument ( $ swedish , ' swedish ' ) ; ?>
С построенным индексом мы теперь можем протестировать большое количество текстов на различных языках, чтобы убедиться в точности распознавания. Огромное спасибо Lorenzo, Soila (который говорит на огромном количестве различных языков) и Ivo за предоставленные примеры:
$ italian = " Nel mezzo del cammin di nostra vita mi ritrovai per una selva oscura ché la diritta via era smarrita. " ; echo $ italian , " \n " , " is " , $ lang -> detect ( $ italian ) , " \n " ;
$ finnish = " Suomalainen on sellainen, joka vastaa kun ei kysytä, kysyy kun ei vastata, ei vastaa kun kysytään, sellainen, joka eksyy tieltä, huutaa rannalla ja vastarannalla huutaa toinen samanlainen. " ; echo $ finnish , " \n " , " is " , $ lang -> detect ( $ finnish ) , " \n " ;
$ dutch = " zoals het klokje thuis tikt, tikt het nergens " ; echo $ dutch , " \n " , " is " , $ lang -> detect ( $ dutch ) , " \n " ;
$ spanish = " Por qué los inmensos aviones No se pasean com sus hijos? Cuál es el pájaro amarillo Que llena el nido de limones? Por qué no enseñan a sacar Miel del sol a los helicópteros? " ; echo $ spanish , " \n " , " is " , $ lang -> detect ( $ spanish ) , " \n " ;
$ swedish = " Och knyttet tog av skorna och suckade och sa: hur kan det kännas sorgesamt fast allting är så bra? Men vem ska trösta knyttet med att säga: lilla vän, vad gör man med en snäcka om man ej får visa den? " ; echo $ swedish , " \n " , " is " , $ lang -> detect ( $ swedish ) , " \n " ; ?>
Как легко можно увидеть (результат работы скрипта слегка обрезан, чтобы сократить изложение), каждый язык был корректно определен:
Nel mezzo del cammin. is italian
Suomalainen on sellainen. is finnish
zoals het klokje thuis tikt, tikt het nergens is dutch
Por que los inmensos. is spanish
Och knyttet tog av. is swedish
Аналогичную операцию можно проделать с целымим вебсайтами, удалив HTML теги с помощью strip_tag. Целью послужили сайты трех локальных офисов Ibuildings:
$ nl = strip_tags ( file_get_contents ( ' www.ibuildings.nl ' ) ) ; echo " IB NL reads as: " . $ lang -> detect ( $ nl ) , " \n " ;
$ uk = strip_tags ( file_get_contents ( ' www.ibuildings.co.uk ' ) ) ; echo " IB Uk reads as: " . $ lang -> detect ( $ uk ) , " \n " ;
$ it = strip_tags ( file_get_contents ( ' www.ibuildings.it ' ) ) ; echo " IB IT reads as: " . $ lang -> detect ( $ it ) , " \n " ; ?>
Похоже, что на странице в домене NL все же больше английского текста, чем датского, поэтому он определелился как английский. Однако, с итальянским все прошло отлично:
IB NL reads as: english IB UK reads as: english IB IT reads as: italian
Другие методыНесмотря на то, что метод триграмм очень удобен и прост, он совершенно не обязательно является лучшим для использования в каждой ситуации. Например, если вам требуется метод, который работает без предварительного обучения или с минимальными затратами памяти, можно просто собрать список коротких, часто встречающихся слов в каждом языке (вроде артиклей и предлогов) и искать в заданном тексте только их.
Точно также, поиск Unicode символов, уникальных для заданного языка, может дать вам достаточную точность его определения.
PEAR: Text_LanguageDetectКогда мы с Лоренцо обсуждали эту проблему, он упомянул, что в библиотеку PEAR уже включен пакет для определения языка текста, хотя и в альфа-версии. Он тоже использует метод триграмм, но располагает несколько более богатыми возможностями. Как и ожидалось, он достаточно прост в использовании, поддерживает Unicode и поставляется уже с готовой базой триграмм для некоторых языков, так что почти не нуждается в обучении. Для полноты картины мы попытались определить с его помощью языки тех же фрагментов текста:
function detect ( $ text , $ l ) < $ result = $ l -> detect ( $ text , 1 ) ; if ( PEAR :: isError ( $ result ) ) < return $ result -> getMessage ( ) ; > else < return key ( $ result ) ; > >
$ l = new Text_LanguageDetect ( ) ;
$ italian = " Nel mezzo del cammin di nostra vita mi ritrovai per una selva oscura ché la diritta via era smarrita. " ; echo $ italian , " \n " , " is " , detect ( $ italian , $ l ) , " \n " ;
// . остаток удален для краткости, но результат аналогичен ?>
Как и ожидалось, результат оказался аналогичным. Пакет можно легко установить из PEAR, используя команду