5 декабря 2013 в 14:05

Эволюция алгоритма Test The Text

Test The Text выделяет стоп-слова в тексте. Стоп-слова делают текст тяжелее, слабее, длиннее.

Стоп-слова делятся на несколько категорий:
— модальные глаголы;
— усиляющие и обобщенные определения и наречия;
— клише и канцеляризмы;
— гиперонимы;
— паразиты времени;
— отглагольные существительные;
— пассивный залог;
— наречия;
— причастные обороты.

Прототип выделял модальные глаголы, используя список «мочь», «долженствовать» и «нуждаться» во всех формах:

    'modal': {
        'can': u"""могу, мог, могла, можешь, может, могло, можем, можете, могли, могут, 
                        смог, смогла, смогли, сможет, можно, нужен""",
        'need': u'нуждаться, нуждается, нужно, нужна, нужны',
        'should': u'должен, должна, должны, должно',
        'other': u'стоит, обязан, следует, необходимо, требуется'
    },



Текст разбивался на слова регулярным выражением (?:[\s,.:]|\A|\Z), каждое слово сравнивалось со стоп-словами. Совпадения помечались в исходном тексте <span class=«класс стоп-слова»>, размеченный текст возвращался обратно и заменял текст на странице.

Прототип работал, список стоп-слов рос, но когда я добрался до клише, понял, что дальше перечислять стоп-слова во всех формах я не смогу. Каждое стоп-слово превращалось в 63. Три рода × три времени × семь падежей. Я подключил pystemmer.

Стеммер убирает окончание и суффикс слова, приводя его к нормальной форме.
нуждаться → нужда
инновационное → инновацион

высокий → высок
высокая → высок
высокую → высок
Pystemmer работает по алгоритму Snowball.

Теперь алгоритм пробегался по стоп словам, отбрасывая у них окончания и суффиксы, затем по словам текста. Начальные формы стоп-слов и слов в тексте сравнивались. Стоп-фразы, вроде, «сомнительное удовольствие», алгоритм разбирает на слова, стеммирует и собирает обратно. При поиске стоп-фразы в списке слов текста, берется проверяемое слово и несколько слов за ним.

К сожалению, через словари стоп-слов невозможно проверить наречия, пассивный залог и причастные обороты. Представляете себе список всех наречий русского языка? Пришло время подключать морфологический анализатор. Выбора для python, кроме pymorphy2 от kmike нет, поэтому останавливаюсь на нем.

Морфологический анализатор определяет для слова часть речи (существительное, глагол, прилагательное, ...), пол, единственное/множественное число, падеж, лицо, время, залог для глаголов. Полный список в исходниках. Захватывающая статья как устроен pymoprhy2.
[Parse(word=u'доставлен', tag=OpencorporaTag('PRTS,perf,past,pssv masc,sing'), normal_form=u'доставить', score=1.0, methods_stack=((<DictionaryAnalyzer>, u'доставлен', 745, 71),))]

[Parse(word=u'пищущие', tag=OpencorporaTag('ADJF plur,nomn'), normal_form=u'пищущий', score=0.212962962962963, methods_stack=((<DictionaryAnalyzer>, u'ищущие', 162, 20), (<UnknownPrefixAnalyzer>, u'п'))), 
Parse(word=u'пищущие', tag=OpencorporaTag('ADJF inan,plur,accs'), normal_form=u'пищущий', score=0.212962962962963, methods_stack=((<DictionaryAnalyzer>, u'ищущие', 162, 24), (<UnknownPrefixAnalyzer>, u'п'))), 
Parse(word=u'пищущие', tag=OpencorporaTag('PRTF,impf,tran,pres,actv plur,nomn'), normal_form=u'пискать', score=0.212962962962963, methods_stack=((<DictionaryAnalyzer>, u'ищущие', 1609, 33), (<UnknownPrefixAnalyzer>, u'п'))), 
Parse(word=u'пищущие', tag=OpencorporaTag('PRTF,impf,tran,pres,actv inan,plur,accs'), normal_form=u'пискать', score=0.212962962962963, methods_stack=((<DictionaryAnalyzer>, u'ищущие', 1609, 37), (<UnknownPrefixAnalyzer>, u'п'))), 
Parse(word=u'пищущие', tag=OpencorporaTag('PRTF,impf,intr,pres,actv plur,nomn'), normal_form=u'питать', score=0.03703703703703704, methods_stack=((<FakeDictionary>, u'пищущие', 1670, 33), (<KnownSuffixAnalyzer>, u'щущие'))), 
Parse(word=u'пищущие', tag=OpencorporaTag('PRTF,impf,intr,pres,actv inan,plur,accs'), normal_form=u'питать', score=0.03703703703703704, methods_stack=((<FakeDictionary>, u'пищущие', 1670, 37), (<KnownSuffixAnalyzer>, u'щущие'))), 
Parse(word=u'пищущие', tag=OpencorporaTag('PRTF,impf,tran,pres,actv plur,nomn'), normal_form=u'пистать', score=0.03703703703703704, methods_stack=((<FakeDictionary>, u'пищущие', 2631, 33), (<KnownSuffixAnalyzer>, u'щущие'))), 
Parse(word=u'пищущие', tag=OpencorporaTag('PRTF,impf,tran,pres,actv inan,plur,accs'), normal_form=u'пистать', score=0.03703703703703704, methods_stack=((<FakeDictionary>, u'пищущие', 2631, 37), (<KnownSuffixAnalyzer>, u'щущие')))]


После подключения морфологического анализатора Test The Text выделяет наречия, страдательный залог, причастный оборот и, заодно, междометия.

Осталось разобраться с проблемами на клиенте. Я и не подозревал, что с простым полем ввода текста может быть столько проблем. Пришлось написать java-script очистки текста при вставке и код вставки <br> по нажатию enter. В новой версии я заменил свой код на Wysihtml5. Wysihtml5 это легковесный html-редактор. Нашел его, изучая как сделали редактор в Basecamp.

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

Вместо этого сервер стал возвращать список стоп-слов с их классом, начальной и конечной позицией. А разметка уже происходила на клиенте. Если слово на позиции не совпадает с ответом от сервера, слово не помечается. На тот случай, если пользователь поменял текст и позиция слова сместилась.

Планы по развитию:
— Выделение предложений больше 17 слов, их трудно читать.
— Выделение абзацев длиннее 8 строчек. Скорее всего такие абзацы нужно разбить.
— Слежение за «ритмом» текста. В хорошем тексте длинные предложения чередуются со средними и короткими, текст становится не монотонным. Читатель не засыпает.
— Повторение слов в соседних предложениях.
— Сослагательное наклонение.
— Публичное API.


Подписывайтесь на наш Хабра-блог и полезные письма. Пишем об информационном стиле, разбираем чужие посты.
Автор: @TestTheText
Test The Text
рейтинг 21,08
Компания прекратила активность на сайте

Комментарии (13)

  • –2
    Этот пост:
    Качество текста: 36%
    Символов: 6374
    Слов: 864
    Стоп-слов: 82
    • +2
      Стоп-слов: 82
      из них — половина примеры этих самых слов
    • 0
      Если из текста вырезать куски кода и примеры стоп-слов, все станет гораздо лучше.
    • +2
      Открою вам секрет. Хороший текст редактируется три-четыре раза с перерывом в пару часов. Итого целый день.
      Я не успеваю довести до идеала все тексты :(
  • –4
    Причастные обороты, услажняют язык, но могут нести смысловую нагрузку.
    «Чепмен, убившый Леннона, читал книгу»
  • 0
    С дичью дело, мы полагаем, закончено. Глава предприятия Хадсон, по сведениям, рассказал о мухобойках всё. Фазаньих курочек берегитесь.

    Забил свой привычный пример и получил подсветку только одного слова — «закончено». Гм. Результаты отличаются от ожидаемых, — скажем так.
    • 0
      Остаётся надеяться, что автор не бросит развитие алгоритма и продолжит его совершенствовать. Меня заинтересовало. =)
    • 0
      Списки стоп-слов постоянно пополняются. Я стараюсь в день редактировать по тексту и вручную проверять алгоритм.

      Подскажите, что выделить в вашем примере?
      • 0
        Шерлок Холмс, разгадывая шифр, оставил лишь каждое третье слово. Если же подходить к этой записке как к обычному тексту, нужно выделить «сомневающиеся» вводные обороты — «мы полагаем» и «по сведениям».
  • +1
    Может еще «Оценку понятности и удобочитаемости»?
    frontender.info/designing-reading-experience/
    • 0
      ух-ты спасибо! Утащил продолжительность чтения и «Оценку понятности и удобочитаемости» в план развития
  • 0
    Подскажите, а pyMorphy умеет работать с текстом целиком или же, как и phpMorphy, только с отдельными словами?
    • +1
      по словам

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка