Академический Документы
Профессиональный Документы
Культура Документы
12+
ISBN 978-5-4496-3281-4
Создано в интеллектуальной издательской системе Ridero
Оглавление
1. Искусственный интеллект. Строки, контекст и волны на Паскале
2. Строковые данные
3. Длинные строки
4. Функция поиска
5. Копирование
6. Кодировки
7. Нормализация строк
8. Преобразования строк
9. Сложное сравнение
4. Общее и частное
6. Синонимы
7. Вопрос-ответ
1. Простой бот
2. Голосовой помощник
2. Генерация волны
4. Связи элементов
2. Методология мышления
2. Формы языка
5. Гештальт-цикличность
6. Метафоры об MSM
7. Сингулярность неизбежна
9. Заключение
«Обучение путем подачи хорошего примера часто — самый эффективный, а иногда —
и единственно возможный метод.»
Никлаус Вирт, создатель языка Паскаль.
Чем мы занимаемся?
«Мы» в данном случае — это небольшое сообщество единомышленников, не входящих
в какое-либо формальное объединение, но захваченных идеей реализации искусственного
интеллекта на базе технологии MSM.
Сверхзадачей исследований в данном направлении не являлось и не является
создание сильного искусственного интеллекта[5] в понимании современных физиков
и инженеров; (такого, который будет многократно превосходить умственные способности
людей). Для начала, следовало бы создать «средний» интеллект. Такой, каким обладает
например, ваш сосед, Михаил Ефремович, учитель географии средних лет. Особо ничем
не выдающийся, иногда не очень внимательный или рассеянный, иногда циничный, иногда
смешной, но… понимающий. Способный сравнивать, оценивать, делать выводы,
формулировать суждения. Решать повседневные задачи. Планировать, следовать
определенным целям. И главное, способный действовать так, как этого требуют
обстоятельства. Вы скажете, такие машины уже есть? Хорошо, если вы так считаете,
не будем спорить. Но, автор считает, что нет.
Это учебник?
Книгу можно использовать как учебное пособие по разделу строковых данных
на Паскале. Но было бы неверно воспринимать показанные решения, как нечто
неоспоримое, — любая приведенная процедура может быть усовершенствована. Сама
природа строк приближает нас к другой форме подхода к методикам реализации. Здесь,
в этой новой реальности открывается то, что называется нечеткостью, субъективной оценкой
и многогранностью связей элементов конструктора, который мы можем выстраивать.
Книга затронет и некоторые философские, психологические, лингвистические аспекты
сущности человеческого восприятия. Но она не станет тешить человеческое самолюбие или
рисовать утопические миры. На этом поле и без того работают слишком много мастеров
слова, чтобы стать только очередным из них. Автору это неинтересно.
Гораздо интереснее работа, имеющая под собой практическое основание,
а не метафорические рассуждения. «Практика есть критерий истины» — известное
выражение, приписываемое Карлу Марксу, но авторство в данном случае не имеет значения.
Эта книга пробует осветить шаг за шагом движение в новом, малоизученном
направлении работы с новой технологией. Страница за страницей практического освоения
вы имеете шанс испытать чувства первооткрывателя, какие вероятно испытывал Левенгук,
настраивающий первый оптический микроскоп. И пусть спустя сотни лет был построен
и микроскоп электронный, но микроскоп конструкции Левенгука был самым важным,
первым шагом в нужном направлении.
С другой стороны, кто-то мог бы и сказать «Да обернитесь вокруг: в нашем мире data
science,[6] блокчейна[7] и искусственных нейросетей нет места для того, чтобы
заниматься какими-то символами…» Но вещь, кажущаяся сегодня слишком простой или
неочевидной может стать завтра тем самым электронным микроскопом. История знает массу
таких примеров.
Например, концепция первого перцептрона,[8] на основании которой родилось все
изобилие современных искусственных нейросетей и машинного обучения более чем
на 30 лет была подвергнута остракизму во время так называемой «Зимы искусственного
интеллекта». И что бы сказали бывшие критики перцептрона теперь, когда каждая
домохозяйка, школьник или пенсионер, имея некоторый интерес и немного свободного
времени может настроить искусственную нейросеть и сортировать огурцы по 12 критериям
в собственном гараже.[9]
Можно освоить вещи и поинтереснее огурцов, например машинный перевод, зрение
сквозь стены или толщи океанских вод… Но автор в данном случае равнодушен
по отношению к искусственным нейросетям, к биг-дате или к попыткам создавать
коннектоны. Безусловно, это в своем роде интересные, нужные и незаменимые вещи,
представляющие немаловажный научный и коммерческий интерес. Плохая новость состоит
в том, что все это имеет мало общего с настоящим интеллектом, который основан
на процессе понимания смысла. Да, искусственные нейросети хорошо работают.
Выписывают штрафы. Рисуют картины. Консультируют по ряду известных проблем. Играют
в шахматы, Го или водят машины. Они это все умеют или будут уметь; и ваша сетка будет
уметь гораздо лучше сортировать огурцы, чем человек, — с помощью нескольких строк
кода, небольшой настройки переменных и нескольких тысяч примеров выборки. Это —
сложная, но популярная и вполне осуществимая задача в рамках существующей парадигмы
машинного обучения. Кроме того — уже изданы сотни прикладных учебных пособий
по этой теме, существуют простые видеоинструкции по работе с кодом на Питоне
и в свободном доступе находятся нужные приложения и наборы данных.
То, о чем говорит эта книга — трудно. Требует умственного труда, фиксации внимания,
сосредоточенности. Эта книга — для тех, кто привык использовать критическое мышление,
ставить все под сомнение, моделировать собственную реальность, а не идти вперед
за общественным мнением.
Кого-то эта книга может развлечь, кого-то заставит задуматься или по-новому взглянуть
на информационные технологии. Возможно — кто-то сделает интересный учебный проект,
доклад, презентацию или реферат, за который может получить хорошую оценку. А кто-то,
(и на это хочется надеяться), использует описанные методы, чтобы участвовать
в приближении лучшего будущего с машинами, имеющими сознание. При этом, не будет
иметь значения язык реализации, — все описанные методики могут быть переведены
на любой язык программирования для достижения лучших показателей в скорости,
адаптивности или коммерческой ценности.
О чем речь?
Это повествование является не столько логическим продолжением первой книги автора,
сколько изложением «обратной стороны» практического программирования, комплекса
приемов, демонстрирующих структуру программы обработки строковых данных «с нуля».
Книга «Искусственный интеллект: начала MSM», вышла в начале 2018 года.[10] Она
рассказывает об основах перспективной технологии в построении искусственного
интеллекта — многомерном строчном (строковом) множестве (или «Multidimensional Strings
Multiplicity», англ.). Простая программная среда, построенная на основе специфического
классификатора всего за год с небольшим своего существования показала замечательные
результаты, в связи с чем автор посчитал важным продолжить публикацию данных, а также
методов и технологий, реализуемых в этом проекте.
В новой книге автор расскажет и о технологических приемах, использующихся
в программной модели для решения наиболее сложных проблем «понимания» естественного
языка. В первую очередь это касается вопросов полисемии и контекста, которые чаще всего,
(а может быть и по привычке), считаются в ЭВМ непреодолимыми.
Что потребуется?
Все приведенные в качестве примера процедуры и функции реализованы на языке
Pascal, а точнее, в бесплатном компиляторе TMT Pascal lite.[11] Выбор автора оказался
на стороне этого компилятора отнюдь не случаен. Это один из немногих компиляторов
Паскаля, позволяющих использовать во внутренних процедурах и функциях входные
и выходные массивы переменных строчного, логического и целочисленного типа.[12] Кроме
того, TMT Pascal известен, доступен и понятен настолько, что строить простые программные
модели, обрабатывающие строковые массивы данных не доставит большого труда.
Споры об MSM
Технология MSM — вещь своенравная. Первые публикации о ней в сети в начале
2018 года были восприняты с подозрением, а иногда и с недоумением. Под сомнение
ставилась как сама технология, так и квалификация автора, мол «Куда ты, блогер, сунулся
в калашный ряд?»[13]Но были среди критических замечаний и вполне корректные,
достойные рассмотрения.
Особенно часто оппонентам автора вспоминался Курт Фридрих Гедель,[14] австрийский
математик и философ с его теорией о неполноте, а также интерпретация этой теории
физиком и математиком Роджером Пенроузом[15] в контексте принципиального различия
между человеческим мозгом и компьютером. Мы не будем сильно беспокоить Геделя,
поскольку систему MSM, едва ли можно назвать математически формальной.
Основой MSM является иерархическая онтология, которая строится в соответствии
с интуитивной, смысловой, неформализованной «нагрузкой» понятий, их синонимов, весов,
отношений и правил. Иерархия имеет комплементарный код каждого элемента, не имеющий
фиксированной числовой разрядности, который построен по принципу бесконечности
и многомерности. В словарях, организованных по принципу MSM программная модель
оперирует не конкретными словами, а их смысловым кодом, и действует на основе подбора
правил, выбранных в соответствие с конкретными ситуациями. Также в логике MSM
реализован механизм, который позволяет уйти от математической, компьютерной точности
в сторону нечеткости, адаптивности к конкретным условиям. Этот механизм помогает
определять близкие понятия, находить родство и различия слов-множеств на основании
онтологических связей, фактически вшитых в код понятий, в противовес четкому
математическому сходству формального написания слов на естественном языке.
В книге «Искусственный интеллект: начала MSM» приводился простой пример, когда
эталонное знание «Кошачьи имеют красивую шерсть» автоматически порождает новые
знания о том, что «У киски есть красивая шерстка» или «У льва есть прекрасная грива»,
и еще сотен подобных. Этот процесс совсем не похож на математичный. Первоначально
введенные данные, формально, математически, совершенно отличаются от проверочных,
и тем не менее программа находит их идентичными, поскольку связи объектов, вшитые
в коде, и правила обработки позволяют это сделать.
Конечно же, подобный результат можно было бы получить и посредством обычного
объектно-ориентированного программирования (ООП),[16] однако относительно
энергозатрат в данном случае MSM оказывается в безусловном выигрыше за счет
комплементарности кода понятий и общей простоты организации системы. На следующем
примере также видно принципиальное отличие в построении классификатора традиционной
семантической сети (рис. 1) и MSM (рис. 2).
В отличие от традиционного решения семантической сети и вариантов объектного
программирования, в которых сеть связей элементов описывается с помощью
дополнительных описательных блоков, не обладающих комплементарными зависимостями,
элементы в MSM входят в единую структуру, где каждый из них обладает «памятью
предков», а кроме того «отягощен» свойствами и «отношениями». Эта структура обладает
безграничной глубиной связей, «зашитой» в самом коде элементов. На рисунке — показан
лишь микроскопический фрагмент, примерно 1/10 000 доли реального объема структуры,
которая кодируется с помощью этих простых правил.
Р
ис. 1. Элементы семантической сети представлены в виде ориентированного графа, вершины
которого обозначают объекты предметной области, а ребра между ними формируют
отношения между ними.
Кроме того, во взаимоотношениях элементов MSM играют немаловажную роль
дополнительные словари зависимостей, весов и свойств, позволяющие с помощью
минимальных средств строить сложные логические конструкции, обобщающиеся на целые
классы значений.
Приведенный фрагмент классификатора занимает минимальный объем памяти,
составляющий менее (!) 1 килобайта. За счет подобной реализации в перспективе можно
достигнуть возможностей мощной логической машины, имеющей ничтожный объем
в десятки или сотни мегабайт. Так приложение на основе MSM сможет работать даже
в обычной бытовой технике вроде холодильника или микроволновки.
Р
ис. 2. Фрагмент визуализации дерева элементов MSM в 3D.
(Правда, насколько для потребителя может быть актуальна «понимающая» смысл языка
микроволновая печь — это большой вопрос, но почему бы и нет?)
Но возвращаясь к теории Геделя, а точнее, к ее интерпретации, сам Роджер Пенроуз
отчасти соглашается с вероятным сценарием построения сильного искусственного
интеллекта: «Наше непонимание человеческого сознания говорит о наличие большого
пробела в научных знаниях и этот пробел, возможно, находится там, где теории нужно
выйти за пределы вычислительной системы».[17]
Вот еще цитата из его книги «Тени разума»:[18]
«В современной научной картине мира отсутствует один очень важный ингредиент.
Этот недостающий ингредиент совершенно необходим, если мы намерены хоть
сколько-нибудь успешно уместить центральные проблемы мыслительных процессов
человека в рамки логически последовательного научного мировоззрения. Я утверждаю, что
сам по себе этот ингредиент не находится за пределами, доступными науке, хотя в данном
случае нам, несомненно, придется в некоторой степени расширить наш научный
кругозор…»
Не является ли MSM тем самым недостающим звеном, пробелом в науке, выходящим
за рамки математических вычислений? Ведь зависимости, образующиеся в этой технологии
в большей степени функциональны только по причине организованных «вручную» связей,
основанных на смысловой «нагрузке» языка, а также их отношений? Нематематические
«вычисления» следуют контексту естественного языка, который выступает носителем
смыслов, весов, свойств и отношений между элементами; и система MSM в достаточной
степени удовлетворяет полноте отражения этих отношений.
Возвращаясь к рассуждениям в книге «Тени разума», можно сделать вывод, что Пенроуз
в своем видении вычислительной модели опирается на математизм подхода к рассмотрению
идеи воплощения искусственного интеллекта. Суть же метода MSM состоит
в парсинге сходства и различия объектов и использовании правил обработки не только
относительно конкретных элементов, но и в отношении классов объектов и фраз,
предложений, составленных из классов моделей. Похожие процессы происходят
и в искусственных нейросетях, которые также основаны на приципе поиска сходства
и различий, однако диалоговые нейросети используют для своих операций слова, фразы
и предложения естественного языка, тогда как MSM оперирует их смысловым кодом,
заимствованным из понимания естественного языка.
Но другой вопрос состоит в том, является ли сам человек эталоном смысла
и интеллекта? И возможно ли, анализируя смысловую нагрузку языка, достигнуть более
высоких интеллектуальных результатов, чем у среднестатистического человека, как это
видится многим нашим современникам?
Еще одна цитата Пенроуза в этом месте кажется весьма уместной: «Мы уверены, что
являем собой апофеоз интеллекта в царстве животных, однако этот интеллект, по всей
видимости, оказывается самым жалким образом не способен справиться с множеством
проблем, которые продолжает ставить перед нами наше собственное общество».[19]
Таким образом Пенроуз понимает, что человеческий интеллект несовершенен. И как вы
помните, наша задача в конструировании MSM видится скорее в воссоздании модели
человеческой логики в программной модели, нежели в создании сверхинтеллекта.
Объективное человеческое несовершенство является частью человеческой сущности; в связи
с чем рано или поздно нам не избежать его воплощения, частичного или полного
в электронно-вычислительной машине.
Почему этот вывод становится нам очевидным? Напомню, что посыл реализации
диалоговой модели на основе MSM состоит в том, что человеческое мышление
и символьный язык по существу неотъемлемы, как утверждает, в частности, Ноам
Хомски.[20] И хотя этот тезис неоднозначен, но он хорошо аргументирован, ведь мышление
без языка — это набор рефлективных зависимостей, бихевиористический подход, способный
решать лишь узкий набор задач, отвечающих за реакцию на положительное подкрепление.
Находить выход в лабиринте, избежать столкновения, выиграть в шахматы или Го, — это все
замечательные примеры реализации статистических, а иначе говоря, математических
методов в построении искусственного интеллекта. И даже те современные системы, которые
направлены на работу с естественным языком (например, переводчики или
боты-консультанты) по существу не оперируют смыслом языка, а производят лишь
внешнюю, автоматную, формальную обработку статистических зависимостей того или иного
написания слов. Однако они не «понимают» принципиальной разницы между кукурузой
и кузнечиком, не могут сказать, кто быстрее или умнее, не различают позитивных
и отрицательных факторов; а ведь эти вопросы по существу является сутью мышления.
В пользу этой точки зрения стоит привести несколько фрагментов из статьи «Ноам
Хомский: где искусственный интеллект пошел не туда?»:[21]
«… Ноам Хомский и его коллеги работали над тем, что впоследствии стало
называться когнитивной наукой — открытие ментальных представлений и правил,
которые лежат в основе наших познавательных и умственных способностей. Хомский и его
коллеги опрокинули доминирующую в тот момент парадигму бихевиоризма, возглавляемую
гарвардским психологом Б. Ф. Скиннером, в которой поведение животных было сведено
к простому набору ассоциаций между действием и его следствием в виде поощрения или
наказания.
…В мае 2011 года, в честь 150-летней годовщины Массачусетского технологического
института, состоялся симпозиум «Brains, Minds and Machines» («Мозги, умы и машины»),
на котором ведущие ученые-информатики, психологи и специалисты в области нейронаук
собрались для обсуждения прошлого и будущего искусственного интеллекта и его связь
с нейронауками. Подразумевалось, что собрание вдохновит всех междисциплинарным
энтузиазмом по поводу возрождения того научного вопроса, из которого и выросла вся
сфера искусственного интеллекта: Как работает разум? Как наш мозг создал наши
когнитивные способности, и можно ли это когда-либо воплотить в машине?
Ноам Хомский, выступая на симпозиуме, не был преисполнен энтузиазма. Хомский
раскритиковал сферу ИИ за принятие подхода, похожего на бихевиоризм, только в более
современной, вычислительно- сложной форме. Хомский заявил, что опора
на статистические техники для поиска закономерностей в больших объемах данных
маловероятно даст нам объяснительные догадки, которых мы ждем от науки. Для
Хомского новый ИИ — сфокусированный на использовании техник статистического
обучения для лучшей обработки данных и выработки предсказаний на их основе — вряд ли
даст нам общие выводы о природе разумных существ или о том, как устроено мышление.
Эта критика вызвала подробный ответ Хомскому со стороны директора по исследованиям
корпорации Google и известного исследователя в области ИИ, Питера Норвига, который
защищал использование статистических моделей и спорил о том, что новые методы ИИ
и само определение прогресса не так уж далеки от того, что происходит и в других науках.
Хомский ответил, что статистический подход может иметь практическую ценность,
например, для полезной поисковой системы, и он возможен при наличии быстрых
компьютеров, способных обрабатывать большие объемы данных. Но с научной точки
зрения, считает Хомский, данный подход неадекватен, или, говоря более жестко,
поверхностен.
«Мы не научили компьютер понимать, что означает фраза „физик сэр Исаак
Ньютон“, даже если мы можем построить поисковую систему, которая возвращает
правдоподобные результаты пользователям, вводящим туда эту фразу…»
Еще раз уточним: задачей-минимумом нашего сегодняшнего исследования остается
реализация диалоговой системы, близкой к человеческой модели мышления. И есть надежда,
что технология MSM является принципиально новым подходом, способным дать нам новую
пищу, иной способ исследования; вдохнуть новую жизнь в парадигму искусственного
интеллекта как символьной системы, (но — не системы на естественном символьном
языке).[22]
Также, в файле autoexec. nt, расположенном в Windows\system32, надо добавить строку «lh
%SystemRoot%\system32\kb16.com ru». После перезагрузки переключение раскладки
клавиатуры в DOS-приложениях будет производиться комбинациями Ctrl+Left Shift
(английская) и Ctrl+Right Shift (русская). Упомянутый метод нормально работает в том
случае, если Language settings for the system в качестве Default стоит Cyrilic.
В Windows 7:
В файле Windows/sistem32/autoexec. nt вписать строки:
«lh %SystemRoot%system32dosx
lh %SystemRoot%system32kb16.com ru
set clipper=F80»
В файле Windows/sistem32/config. nt вписать:
«files=80; buffers=99».
В командной строке в меню «Пуск» через команду «rehgedit» редактируем реестр:
HKEY_LOCAL_MACHINE -> SYSTEM -> CurrentControlSet -> Control -> Keyboard
Layout
DosKeybCodes изменить значение параметра 0000409 на ru
Раскладка будет переключаться сочетаниями клавиш:
русский — правые Shift+Alt; английский — левые Shift+Alt.
В Windows 10:
Не требует изменений в реестре. В файле Windows/sistem32/autoexec. nt добавляем
строчку — "%SystemRoot%/system32/kb16 ru».
В случае если используется 64-разрядный Windows, требуется использование
приложения-эмулятора для DOS-программ «DOS-box».
Строковые данные
Работе со строковыми данными всегда уделяют слишком мало внимания. Считается, что это
нечто, само-собой разумеющееся и слишком понятное, чтобы делать на них акцент.
В учебниках, самоучителях любой толщины и направленности нам сильно повезет, если им
уделяется хотя бы 2 страницы.
Попробуем же восполнить известные пробелы, для чего начнем с самых азов работы
со строками на Паскале.
Итак, строчные переменные и константы в Паскале описываются так же, как и любые
другие, например строка S: String означает что переменная S будет хранить некий набор
символов, строку (в количестве от 1 до 255).[1]
Соответственно, декларация S: String [100] в блоке переменных обозначает, что
переменная может хранить в себе последовательность элементов до 100 символов
включительно. Декларировать количество символов может быть удобно, когда мы работаем
с формализованными данными, например, с серией и номером документа с определенным
числом символов. Но при этом следует учесть следующее: при попытке присвоения такой
переменной более длинного ряда символов, не вошедшие символы будут «обрублены».
Например, описав переменную: S: String [8] следующая операция
Длинные строки
В Паскале возможна работа с длинными строками,[2] длина которых определяется
не числовым значением первого байта строки, а замыкающим символом «#0». Ограничение
в них определяется лишь объемом оперативной памяти или ее схемой адресации.
Максимально возможная строка в таком случае ограничена числом в 65 535 символов; еще
такие строки называют ASCIIZ-строками.
Для создания длинных строк, перед описанием программы следует включить директиву
расширенных значений компилятора {X+}; так же следует включить и модуль Strings.
Сама строка описывается типом Pchar, который является символьным указателем
и отвечает за хранение массива символов Char.
На следующем примере показан вывод строки из массива 600 символов. Здесь LongS —
длинная строка, S-массив символов. В данном случае массив S поочередно заполняется
символами A и B в зависимости от нечетности (Odd=true) или четности значений счетчика
цикла i.
И
лл. 3. Пример обработки длинной строки в программе longstring.
Далее, производится операция присвоения значений массива S переменной LongS и ее
последний символ помечается символом завершения строки «#0».
Кроме того, мы можем и напрямую работать с массивами символов Char, как
со строками.
В модуле Strings описываются следующие операторы для обработки ASCIIZ-строк,
но мы перечислим кратко основные из них, поскольку в обычной практике они нам
не потребуются:
StrCopy (S1,S2): Pchar; — Копирует строку S2 в строку S1, возвращая указатель на S1.
StrLen (S1): Integer; — Возвращает длину строки.
StrCat (S1, S2): Pchar; — Объединяет строки S1 и S2, возвращая указатель на начало
строки.
StrECopy (S1, S2): Pchar; — так же объединяет строки S1 и S2, но возвращает указатель
на конец строки.
StrComp (S1, S2): Integer; — Сравнивает две строки и возвращает код 0 если строки
равны, положительное значение если S1> S2 и отрицательное значение если S1 меньше S2.
StrlComp (S1,S2: Pchar; L: Word): Pchar; — так же сравнивает строки, но не более числа
символов, указанного в L.
StrEnd (S): Pchar — Возвращает указатель на конец строки.
StrLCat (S1, S2: Pchar; L: Word): Pchar; — функция копирует строку S2 в конец строки
S1 с учетом ограничения на количество символов L.
Пример работы с массивом символов S, как с длинными строками:
И
лл. 4. Обработка массива S с помощью функций StrCopy и StrLen.
В данном случае, мы присваиваем массиву значение функцией StrCopy, а затем измеряем его
длину с помощью StrLen и выводим на экран.
И
лл. 5. Результат работы программы Longstring.
Из недостатков работы с длинными строками выделяются:
— относительно долгие операции конкатенации (объединения двух строк) и получения
числового значения их длины;
— отсутствие контроля за концом строки, (что может вызывать потери данных
и критические ошибки);
— невозможность использовать символ завершающего байта (#0) в качестве элемента
строки;
— громоздкость синтаксиса.
По этим причинам и ввиду особой специфики работы, — большее внимание мы уделим
операциям с обычными строками.
Функция поиска
Одной из базовых функций работы с переменными строкового типа является функция
поиска подстроки в строке POS (S,S2), указывающая на числовую позицию первого символа
подстроки.
Например, в случае
n — должна быть переменной целочисленного типа (Integer, Word или Longint) и в данном
случае получит значение 6 — позицию подстроки «мыла» в строке.
Функция Pos достаточно хороша во многих случаях, однако особенностью строчных
данных может быть и такой случай, когда подстрока встречается в строке несколько раз.
В таком случае функция Pos вернет первое попадание в подстроку.
Например, следующее выражение
вернет значение 2, хотя символ «а» встречается здесь несколько раз и верными могли быть
также значения 4, 9 и 12.
Копирование
Если нам требуется получить фрагмент текста, являющегося частью строки, можно
использовать функцию Copy (S,N1,N2); Здесь N1 определяет, с какого символа будет
производиться копирование, а N2 — число копируемых символов.
Например, действие
Здесь, в качестве позиции для отсчета мы берем место нахождения в строке первого пробела
и копируем следующие за ним символы за минусом числа символов, следующих до пробела
включительно. Проверить верность этой формулы можно в следующей программе:
И
лл. 6. Копирование фрагмента строки с помощью функций Copy и Pos, с целью получения
части текста, следующего за первым словом в строке.
Результат — можно увидеть на экране:
И
лл. 7. Результат работы программы копирования фрагмента текста.
Также, предположим, что нам необходимо скопировать последнее слово из строки. В данном
случае, поскольку функция «Pos» нам не поможет, придется воспользоваться циклом, чтобы
«вручную» установить место нахождения последнего пробела.
Контролировать конкретный символ строки можно, используя числовое значение
индекса символа в строке, при этом числовой индекс указывается в квадратных скобках
S [n].
Так, в упомянутой строке S [1] =«М», S [2] =«а», s [3] =«м» и т. д. Зная общее число
символов с помощью функции length (S) мы можем перебором достигнуть нужного нам
символа в цикле и после произвести нужную операцию. В следующей программе мы
получаем последнее слово, благодаря перебору символов с «хвоста», уменьшая значение,
полученное из длины строки оператором Dec (i). Так же, на всякий случай мы уточняем
условие выхода из цикла в случае, если пробел так и не будет достигнут (i <1).
И
лл. 8. Копирование последнего слова из фрагмента текста с использованием цикла Repeat
и строкового индекса.
Результат удовлетворяет ожиданиям.
И
лл. 9. Итог работы программы (илл. 8).
Почему же в примерах мы использовали английскую транскрипцию? Очень просто, —
поскольку по умолчанию в компиляторе TMT Pascal используется кодировка Win, и та же
программа с русским предложением внутри даст непонятный итог:
И
лл.10. Пример неудачного использования программы по работе с текстом, — из-за
несовпадения кодировок ввода (Win) и вывода (DOS) удовлетворительный результат
не может быть получен.
Так он будет выглядеть, несмотря на то, что формальных ошибок мы не допускали:
И
лл. 11. Так выглядит фиаско при несовпадении кодировок.
И это ничто иное как слово «раму», не переведенное в Dos-кодировку.
Похожий, «никакой» результат мы получим, если, например, напишем символы «а»
в строке в английской раскладке, а предложение — на русском; тогда функция Pos из поиска
вернет значение отсутствия символа (0), хотя визуально нам кажется, что никакого различия
в написании нет.
Эти проблемы открывают целый пласт операционных задач, связанных с кодировками.
Кодировки
Ввод данных в стандартном окне программы DOS осуществляется в кодировке DOS ASCII.
Также и данные, которые в нем отображаются, имеют кодировку DOS.
В случае, если нам требуется обрабатывать данные из какого-либо внешнего источника,
например из приложения «Блокнот», скорее всего, потребуется перекодирование
из Win-кодировки. «Скорее всего» поскольку символы цифр, пробелов, пунктуации
и английских букв имеют одинаковые адресные значения как в кодировке Win, так и в DOS;
но символы дополнительных языков, например кириллицы, имеют различные значения
в различных кодировках. Между тем если и вывод и ввод осуществляется в фоновом режиме,
и как исходные данные так и обрабатываемые находятся в одной кодировке,
перекодирование из одной кодировки в другую также может и не потребоваться. (Например,
в случае, когда мы принимаем в обработку данные, записанные в кодировке Win,
и записываем их часть в другой файл, имеющий ту же кодировку Win).
Для разбора кодировки символов нам понадобится следующая пара функций:
CHR (x) — указывает на символ с числом X из символьной таблицы (это можно сделать
и служебным обозначением «#x», например K:=#32, — в данном случае, значение k будет
равно пробелу, символу с кодом «32» в соответствии с таблицей символов ASCII).
ORD (k) — возвращает код символа K из символьной таблицы.
Здесь, K имеет тип Char (символ). Операции с символами схожи с операциями
со строковым типом, например к переменной строчного типа мы можем добавить символ:
S:=S+K;
Здесь, если содержимое S=«Мама мыла раму», а K=«c», мы получим значение S=«Мама
мыла рамуc»;
Значения таблицы символов по величине равны одному байту, следовательно, они могут
принимать численные значения в диапазоне от 0 до 255.
Чтобы вывести стандартную таблицу символов, нам понадобится следующая
программа:
И
лл. 12. Программа для вывода таблицы символов ASCII DOS.
Мы несколько усложнили ее для того, чтобы получить ровную таблицу: здесь мы выделили
код символа другим цветом, и добавили символы: «0» к двузначным и «00» однозначным
числам.
Так мы выводим значения таблицы символов ASCII в DOS-кодировке, кроме
управляющих символов с кодами «11» («vertical tab»/ «вертикальная табуляция»), «12»
(«form feed»/ «смена страницы») и «13» («Enter»). Также был «испорчен» управляющий
символ «8», равнозначный команде удаления предыдущего символа с клавишей
«Backspace».[4]
И
лл. 13. Результат вывода таблицы символов в программе Tabsym.
Здесь, символы с кодами до 32 являются служебными.
Кстати, из управляющих символов в будущем нам могут понадобиться:
«Тab» — #9
«Enter» — #13
и возможно, «Esc» — #27.
Несмотря на то, что они не имеют явного представления в виде конкретного
обозначения, но они так же записываются в тексте, как и все остальные. То есть, взглянув
на строку мы не увидим что там есть символ с кодом «13» («Enter»), но при обработке этой
строки последним символом может оказаться символ с кодом «13». Это особенно важно
понимать при обработке предварительно сохраненных файлов.
Что в этой таблице является очевидным: мы можем выделить значения чисел
от 0 до 9 (коды 48—57); ряд символов пунктуации и специальных знаков (коды 33—47);
английские прописные (большие) буквы от A до Z (коды 65—90); английские строчные
(маленькие) буквы от a до z (коды 97—122); русские прописные (большие) буквы от А до Я
(коды 128—159); русские строчные (маленькие) буквы от а до я (коды 160—175, 224—239)
отдельно буквы Ё (240) и ё (241); и элементы псевдографики (коды 176—223).
Для простоты восприятия на рисунке ниже представлена нумерация кодов клавиш
стандартной клавиатуры «QWERTY».
И
лл.14. Стандартный набор кодов клавиатуры «Qwerty».
На следующем рисунке приведена базовая кодировка ASCII в двоичном, восьмеричном
и символьном коде без использования дополнительных языков.
И
лл. 15. Различные характеристики кодов ASCII-таблицы
символов.
Справка:
ASCII (англ. American standard code for information interchange), — название таблицы
кодировки, в которой распространённым печатным и непечатным символам сопоставлены
числовые коды. Таблица была разработана и стандартизована в США, в 1963 году.
Так или иначе, если в программе нужно использовать какой-то символ, например для
условия выхода из цикла, уточнить его нам поможет простая программа:
И
лл.16. Программа вывода кодов символов «по требованию» пользователя: код нажатой
клавиши будет отображаться на экране.
Здесь, Key — переменная символьного типа Char отвечает за нажатие клавиш
и последующий вывод кода символа на экран.
Строка «If keypressed=true» — «Если нажата клавиша», то переменной «key»
присваивается символ нажатой клавиши «readkey». Затем, мы выводим на экран значение
символа в таблице (функция ord) и сам символ (key).
Пример ее использования:
И
лл. 17. Программа сообщает коды нажатых пользователем клавиш.
Итак, мы разобрались с таблицей символов.
Теперь поговорим о перекодировке.
Как уже упоминалось, Win-кодировка текстового файла отличается от DOS-кодировки,
но она необходима для получения данных из открытых источников и удобства
представления данных.
Нормализация строк
Для перекодировки строки текста из кодировки Win в DOS нам потребуется следующая
процедура DecodeToDOS:
И
лл. 18. Процедура для декодирования строки из стандартной кодировки Win в DOS.
Соответственно, обратно из кодировки DOS мы можем «перегнать» текст в кодировку Win
(например, для записи в файл).
И
лл. 19. Процедура для декодирования строки из стандартной кодировки DOS
в кодировку Win.
Также, нам может потребоваться перекодирование не строки (из Win в DOS), а отдельного
символа, в этом случае мы используем процедуру DecodeToDOSC:
И
лл. 20. Преобразование из кодировки Win в кодировку Dos отдельного символа.
В программах использующих перекодировку символов мы использовали простой принцип
преобразования, обращаясь к числовым значениям переменных.
Далее, говоря об обработке строковых данных нам регулярно будет требоваться
процедура замены подстроки в строке, как приведенная ниже InsInString:
И
лл. 21. Замена подстроки в строке на нужный набор символов в процедуре InsInString
происходит благодаря несложной конструкции из функций Pos, Length и Copy.
Как она работает. Переменная i получает позицию нахождения подстроки S2 в строке S.
Если при этом позиция находится не на первом символе, в строку результата S4 копируется
фрагмент строки до указанной позиции (фрагмент Copy (S,1,i-1), к нему прибавляется
«слово» -замена S3, и оставшийся фрагмент текста за минусом уже полученного фрагмента
с учетом стоявшего ранее на этом месте слова (фрагмент Copy (s,i+length (s2),length (s)
-length (s2) -i+1)).
В операции замены первого слова есть небольшое различие — первым сразу становится
подстрока замены S3.
Предположим, в строке «Мама мыла раму» нам необходимо заменить слово «раму»
на «окно».
Замена производится в одно действие:
В данном случае переменная s2 получит измененную строку «Мама мыла окно». А в случае,
если искомая подстрока не будет найдена, переменная S4 вернет в программу исходную
строку S.
Но здесь необходимо учесть один момент. В случае, когда в текстовой строке
встречается несколько раз одно и то же заменяемое слово, (например, слово «быть» в строке
«быть или не быть»), процедуру следует повторить. Поскольку нам неизвестно, сколько раз
во фразе может встретиться слово (или символ, или группа символов), один, три раза или
100 раз, мы можем усовершенствовать процедуру для того, чтобы она заменяла их все до тех
пор, пока искомой подстроки в строке не останется. Здесь можно применить цикл Repeat
с условием выхода.
Видоизменим процедуру InsInString, добавив цикл.
И
лл. 22. Усовершенствованная процедура InInstring прекращает заменять подстроку только
тогда, когда заменять остается нечего.
Еще одним необходимым элементом нормализации текста является его преобразование
из строчного написания в заглавное для удобства последующего оперирования.[5]
То есть наша строка «Мама мыла раму» после обработки должна выглядеть как «МАМА
МЫЛА РАМУ», где все буквы являются прописными, большими.
Приведенная ниже процедура Getups преобразует строку таким образом, что все
символы, написанные строчными буквами становятся прописными, а все прочие символы —
копирует в той же позиции. Для этого процедура, которая работает со строкой использует
другую процедуру, (Getup), которая производит ту же операцию, но на уровне 1 символа.
Илл. 23.
Процедуры «поднятия» букв из строчных в прописные GetUps (для строк) и GetUp (для
отдельного символа).
Ознакомившись с ними вы можете сделать вывод о принципе их работы. Чтобы не заменять
все буквы по одной, процедура GetUp работает с диапазонами кодировок символов, меняя их
нужным образом. В данном случае это уже упомянутые диапазоны кодировки DOS:
английские строчные буквы от a до z (коды 97—122) и русские строчные буквы от а до я
(коды 160—175, 224—239). Определив «попадание» в строчную кодировку мы уже знаем,
в какую «сторону» и на какое число следует изменить код, вычесть или прибавить.
В частности, для изменения английских строчных символов из кода символа вычитается
число 32; а в русских, — до буквы «п» также вычитается 32, с буквы «р» — вычитается 80.
И
лл.24. Процедура «очистки» строки от двойных-тройных пробелов, а также пробелов
в начале и в конце строки.
Теперь постараемся подытожить то, что мы получили.
С помощью описанных процедур мы можем нормализовать текст, используя буквально
несколько операторов.
Вернемся к нашей проблеме с кодировкой, но теперь будем во всеоружии:
И
лл. 25. Обработка ненормализованной строки из кодировки Win, которая содержит лишние
пробелы.
Здесь исходная строка написана в кодировке Win и содержит лишние пробелы. Мы
перекодируем ее в кодировку DOS, с помощью процедуры Decodetodos, переводим
в верхний регистр (и убираем лишние пробелы) с помощью процедуры Getups, и получаем
последнее слово строки «раму».
И
лл. 26. Нормализация данных привела к требуемому результату, нужному слову в нужном
написании.
Следует упомянуть еще о нескольких базовых процедурах работы со строками в Паскале.
Преобразования строк
Str (N, S); — преобразует число N в строку S.
Иногда это требуется для операций над числовыми данными, например, для
преобразования даты и времени в наборы символов строкового типа. Дело в том, что
в Паскале мы не можем смешивать данные разных типов, и для этого требуется привести их
к одному типу.
Для наглядности, рассмотрим процедуру, которая генерирует строку информации
о завтрашней дате в читаемом формате. Результат должен выглядеть, допустим,
так: «1 марта 2019 года». Все что у нас есть, это информация о дате сегодняшней,
полученной посредством оператора GetDate. Эта информация кажется банальной, но в ней
мы должны учесть в случае необходимости, содержит ли текущий месяц 30 или 31 день.
Также стоит знать, является ли год високосным, если сегодня 28 февраля; стало быть
завтрашний день может быть как первым днем календарной весны, так и 29 февраля. Как
осуществить этот выбор?
Оператор Str здесь используется дважды в конце: для преобразования числа года и дня
и последующей их конкатенации (иначе говоря, объединения) в одной строке.
И
лл. 27. Листинг процедуры Gettomorrow, получающей точную завтрашнюю дату в формате
строки.
Что происходит? В качестве выходной переменной процедуры мы указали единственную
переменную S, в которую будет необходимо отправить результат — строку с датой. Далее,
внутренними переменными мы описали Year (год), Month (месяц), Day (день) и Day_of_week
(день недели); а также их строковые версии: Years, Months, Days. (Поскольку день недели
в данном случае нам не интересен, мы не будем заниматься его описанием
и преобразованием).
Первоначально, получив из операционной системы информацию о дате в формате чисел
«GetDate (year, month, day, day_of_week)», мы прибавляем к числовому значению дня
единицу «Inc (day)», и смотрим, что из этого получится.
В операторе сравнения с перечислением «Case» мы выбираем варианты сценариев.
В месяце однозначно не может быть 32 дня, поэтому сталкиваясь с этим числом, мы
присваиваем переменной дня единицу и увеличиваем значение месяца на единицу. Но если
на дворе был декабрь? Мы помним об этом, и если это так (числовое значение
месяца «Month» превратилось в 13), то прибавляем к году единицу «Inc (year)», если сегодня
31 декабря.
Но как мы помним — 31-ый день бывает не в каждом месяце; поэтому мы включаем
в исключения и 31-ые числа ноября, сентября, июня и апреля, — если таковые получились,
то значение месяца «Month» так же увеличиваем на единицу, а значению дня
«Day» присваиваем 1.
Число 30 невозможно даже после крайне редкого дня «29 февраля», поэтому мы
предусматриваем и его в виде исключения.
И наконец, 29 февраля бывает не в каждом месяце. Поэтому если мы встречаем такой
вариант, нам необходимо уточнить, является ли год високосным или нет.
Точное определение високосного года звучит так:
«Год високосный, если он делится на четыре без остатка, но если он делится
на 100 без остатка, это не високосный год. Однако, если он делится без остатка на 400,
это високосный год.»
То есть, в данном случае, если мы видим попадание в одно из условий, когда год
не делится на 4 без остатка, либо когда делится на 100, но не делится на 400, то считаем этот
год обычным и следующим днем считается 1 марта.
Затем мы присваиваем в очередном блоке сравнений case значение месяцев в нужном
нам склонении: «января», «февраля» и так далее. И наконец, в результате преобразований
числовых данных в строковые, получаем итоговый, нужный результат.
И
лл. 28. Пример использования процедуры преобразования строки S в число N с результатом
преобразования R, с помощью процедуры Val.
Теперь мы рассмотрим процедуру для преобразования фрагмента текста с использованием
дополнительной информации. Предположим, мы имеем текст статьи, который необходимо
дополнить некоторыми строковыми данными. В статье может встретиться употребление
информации о процентах, которое мы должны расширить, например, «95%» заменить
на фразу «почти все (95%)». Также при встрече дробного процентного показателя, процедура
должна округлить его, например «19,2%» употребить «менее пятой части (19%)».
Вариативность такого «очеловечивания» процентных показателей может быть достаточно
широкой (в приведенном примере, до 24 вариантов).
Что нам потребуется для упрощения этой задачи. Во-первых, это функция определения
символов цифр Trycifra.
И
лл. 29. Функция trycifra проверяет символ на принадлежность к цифрам от 0 до 9.
Во-вторых, это уже описанная ранее процедура InsInString. И в-третьих, это файл, «DictPer.
txt», который необходимо сохранить в рабочем каталоге программы (по умолчанию это
C:/TP/BIN). Он будет содержать числовые диапазоны значений и их описания (кодировка
Win). Приведем его содержимое полностью:
«0
3
незначительный процент
3
5
менее двадцатой части
5
8
чуть более двадцатой части
8
10
менее десятой части
10
15
более чем десятая часть
15
16
менее шестой части
16
19
менее пятой части
19
21
пятая часть
21
25
больше пятой части
25
25
четверть
26
27
более чем четверть
27
30
почти третья часть
30
40
более чем третья часть
40
50
менее половины
50
58
более половины
59
65
около двух третей
65
75
почти три четверти
75
77
три четверти
77
80
почти четыре пятых
80
82
четыре пятых
82
92
подавляющее большинство
92
100
почти все
100
100
все
100
10000000
более 100%»
Теперь приступим к сборке и рассмотрим происходящее подробнее.
В процедуре нам потребовалось много строковых переменных для текущих операций
(S-S8), две переменные для хранения диапазона (N2 и N3), одна — для текущего показателя
(N типа Real), одна — символьного типа (C), одна переменная для связи файла (F типа Text),
и две — логического типа (Boolean) для контроля над текущим состоянием операции (b
и tryfalse).
И
лл.30. Процедура LogicPercent, дополняющая числовые данные процентов различными
фразеологизмами.
На вход в процедуру мы имеем строку S, к обработке которой приступаем в случае
нахождения в ней символа «%». В цикле меняется значение текущего символа S [i], и если
мы встречаем обычные буквы или иные символы, то устанавливаем текущее значение
tryfalse=true.
Если же нам встречается число или символы дроби, то мы храним его в переменной S3,
поскольку оно может оказаться, а может и не оказаться процентом. Логический указатель
числа («нужного» символа) устанавливаем на B=true, и отменяем значение «ненужного»
символа TryFalse=false. Также мы дублируем это значение в переменной S8.
Встретив символ процента, цикл завершается. Мы преобразуем значение строковой
переменной S3 в число N. Результат преобразования j мы игнорируем, поскольку
контролировали код символов и уверены в числовом содержимом переменной. Поэтому
используем в дальнейшем j как хранилище для округленного значения N (Round (N)).
Затем мы открываем файл словаря значений DictPer. txt, читаем его по 3 строки сразу,
причем первые два «читаем» как числа (N2 и N3) с которыми сравниваем текущее число
значения j. Если число j попадает в промежуток между указанными числами, нами
используется символьное выражение из файла s6, которое мы не забываем переводить
в кодировку DOS (процедура decodetodos).
Теперь протестируем готовую процедуру в следующей программе.
И
лл. 31. Программа для тестирования процедуры
LogicPercent.
Илл.
32. Пример работы процедуры LogicPercent.
По такому принципу можно обрабатывать большие массивы текста, добиваясь, к примеру,
увеличения его уникальности.
Сложное сравнение
Предположим что нам необходимо выбрать из десятка слов два, наиболее похожих. Но что,
если все они разные и простое сравнение не работает? Если самыми похожими окажутся,
например, «ковровая» и «ковровый»? «Хлебный» и «хлебо-булочный»? «Эволюция»
и «конституция»? В данных случаях поможет функция сложного сравнения сходства.
Для начала поговорим о стратегии алгоритма. Мы могли бы здесь посчитать простое
количество попаданий букв одной строки в другую, однако это исключает вероятность
оценки сходства суффиксов и окончаний слов, как в последнем нашем примере
с «эволюцией» и «конституцией». Судите сами.
эволюция
конституция
И
лл. 33. Процедура сложного сравнения двух строк.
Что происходит: первоначально, мы присваиваем переменной оценки сравнения (P)
исходный балл (10 000), и оцениваем разность длины строк, за что также присваиваем
различный размер «шага» оценки (step). В зависимости от величины разницы длины слов (L
и L2) мы вычитаем эту разницу из первоначальной оценки (P). Затем происходит процесс
приведения обоих слов к одинаковым условиям, — мы считаем их равными по длине,
но если одно слово оказывается короче другого, то к нему присоединяется дополнительное
число пустых символов-пробелов (S4).
Далее, происходит процесс сравнения с использованием циклов, описанный выше.
И в завершении, мы вычитаем исходное количество баллов из полученной оценки.
В результате работы процедуры мы получаем оценку сходства слов «эволюция»
и «конституция» в 908 баллов. Если же в качестве первого слова выступит «рыба», или
«аргумент», оценка снизится до 0 баллов. Слово же «конституционный» поднимет оценку
до 8 880. Конечно, это абстрактное число, однако в рамках столь же абстрактных координат
оно дает стабильные сравнительные показатели сходства слов на естественном языке.
Но к сожалению, применять данную процедуру к крупным фрагментам текста
проблематично: разделители в виде пробелов в двух строках могут неоправданно повышать
показатели сходства. Мы могли бы конечно взять и заменить их на какие-либо другие
символы, но никто не гарантирует, что и эти символы не встретятся в тексте. Кроме того,
сравнение множества больших фрагментов текста будет сильно замедлять программу
и повышает вероятность «зависаний». Ввиду этого, самым адекватным решением может
быть специализация процедуры на сравнение одиночных слов.
Теперь следует определиться, что делать в таком случае с предложениями. Для начала
попробуем пилить их на слова и выводить в форме массива из отдельных слов. В таком виде
обработка больших фрагментов текста значительно упрощается.
А чем разделяются слова в предложениях? Правильно, пробелами и пунктуацией. Мы
приготовим универсальную процедуру, которая будет «распиливать» любое предложение
на набор слов и складывать их в массив для вывода. Стратегия здесь довольно простая, —
идти вдоль индекса предложения и используя маркер разделения (пробел) отделять
очередное слово. При этом, мы не станем сохранять пунктуацию, — ведь она «создает шум»:
в данном случае — это словарные конструкции, которых нет. Ведь фактически для
программы слова «корова» и «корова,» будут двумя разными словами, разными наборами
символов. Это нас не устраивает. Поэтому, помня о том, что нам еще следует обработать
пунктуацию, выделим исключительно все слова в предложении. Так может выглядеть эта
процедура:
И
лл. 34. Процедура Justbreak «распиливает» предложение на отдельные слова и сохраняет его
в массив с числом элементов j.
В процедуре Justbreak происходит обработка предложения, поступившего в переменную S
в двух циклах Repeat. В ней, индекс слова будет контролировать переменная j, а индекс
последовательного символа в предложении — переменная i. Первый цикл присваивает новое
значение счетчику индекса слова командой inc (j). Условием выхода из цикла, сохраняющего
слово, будет символ пробела, запятой, двоеточия, точки или превышение индекса i длины
строки предложения. Внутри этого цикла мы добавляем очередной символ к текущему
слову, если он соответствует условиям — не равен знаку пунктуации или пробелу. Кто-то
может возмутиться, ведь процедура разбивает некоторые составные слова, вроде «кто-то»,
«как-то», «как-нибудь» и т. д. Но в данном случае мы все же отделим «мух от котлет» —
выделим составные конструкции как слова по отдельности, но запомним, в какой
конструкции пунктуации они находились.
Модель пунктуации
Перейдем к процедуре сохранения пунктуации. Конечно же, она нам очень пригодится. Она
очень проста.
И
лл. 35. Выделение модели пунктуации из предложения.
Здесь мы берем исходное предложение и (на всякий случай) предварительно исправляем
наиболее распространенные ошибки: наличие пробела перед символами пунктуации,
с помощью ранее описанной процедуры insinstring. Далее поработаем с элементами массива
signs, в котором будем хранить символы пунктуации на время обработки.
Здесь мы начинаем «путешествие» по индексу входного предложения, отмечая каждое
слово символом пробела или символом пунктуации, если он есть. Кроме того, мы выделяем
весь полученный блок служебными символами «^».
В результате, на строку «Антошка — плохой, нехороший.» мы получим следующую
картинку пунктуации « ^-,.^». Что же, теперь мы можем рассматривать любое предложение
как набор «чистых» слов и модель пунктуации, к нему прилагающуюся.
Теперь вновь вернемся к задаче, в которой требовалось выделить и обнаружить пару
наиболее схожих слов в предложении.
Мы можем использовать следующий алгоритм:
1. Разбить слова в предложении на массив с помощью процедуры justbreak с числом
элементов j.
2. Сравним каждое слово массива с каждым и если полученное значение является
максимальным, то сохраним его, как ответ. Для этого нам потребуется два цикла и правило,
по которому одно и то же слово мы не будем сравнивать с самим собой.
Используя этот нехитрый метод, мы можем получить своего рода игру, в которой
программа будет искать слова, близкие по написанию и выбирать их, как на следующем
примере:
И
лл. 36. Тест игры в слова, где из ряда слов, введенных пользователем компьютер выбирает
два, наиболее похожих по написанию.
Как видно, программа действует, отсекая символы пунктуации и преобразуя ряд слов
из одной строковой переменной в массив слов, а также приводит каждое слово в верхний
регистр. Теперь рассмотрим программу, которая осуществляет эти действия.
И
лл. 37. Программа «Игры в слова» с компьютером, где из предложенных пользователем слов
программа выбирает два наиболее похожих по написанию.
Итак, что мы здесь видим. В первую очередь, это набор строковых переменных (s, s2, s3),
которые нужны для обмена данными между процедурами нормализации (decodetodos, getups)
и ввода строки пользователем. Также, здесь используются переменные для работы циклов
(i и n), для индексирования конца массива sm (j), для определения максимума (max)
и текущего состояния баллов (ball). Кроме того — переменные для хранения лучших
позиций двух слов (max1) и (max2) в массиве (sm). Массив требуется нам в качестве
выходной переменной для использования в описанной выше процедуре justbreak, поэтому
мы не производим иных действий с его элементами (по очистке, заполнению пустыми
символами и др.), поскольку эти действия уже прописаны в самой процедуре justbreak.
Весь алгоритм кроме исходных функций очистки мы поместили в цикл Repeat
с отсутствием условия выхода (Until false) для того, чтобы вопросы и ответы повторялись
при желании пользователя бесконечно.
После нормализации (декодирования) приветственной фразы «Введите ряд слов,
в котором следует найти похожие по написанию» и выведении ее на экран, от пользователя
получается строка S, в которой он написал ряд слов. Эту строку мы разбиваем процедурой
justbreak на массив слов sm с известным числом элементов j, после чего в двух циклах
последовательно сравниваем (с помощью percentcheck2) различные элементы массива друг
с другом, если речь не идет об одном и том же элементе (i <> n). Если текущий максимум
Max оказывается ниже текущей оценки Ball, мы передаем число Ball в Max, и запоминаем
позиции I и N, сохраняя их в переменные max1 и max2 соответственно. После окончания
циклов сравнения мы выводим результат из массива sm с индексами max1 и max2 и ждем
следующее задание.
Эта игра может быть усовершенствована. Например, в роли задающего вопрос
и генерирующего слова мы можем поставить программу, а человек будет их угадывать.
Но для этого нам уже потребуется работа со словарями, чем мы и займемся позже.
Шаблоны поиска
Еще одну интересную функцию можно считать очень полезной при обработке строковых
данных. Она незаменима для анализа, а точнее, парсинга по значительным объемам текста.
Предположим, перед нами — достаточно монотонный текст, состоящий из букв,
пунктуации и пробелов. Это может быть html -страница текста, полная рекламы, модулей
и ссылок; но небольшая часть этого текста носит важный для нас смысл. Мы хотели бы
разметить этот фрагмент текста какими-либо жесткими правилами, однако это невозможно.
На начало нужного фрагмента в тексте указывают лишь косвенные признаки, — например,
наличие даты, времени публикации и имени автора в следующем формате: «12.08.2018 01:20,
АНАТОЛИЙ ИВАНОВ». Строка перед ним — содержит только название статьи, а после
и до него — ряды случайных символов.
Эти данные — всегда разные, но формат их записи всегда один. Как нам прочитать их
в случайном месте текста?
В этом случае мы используем технологию шаблона. Под шаблоном здесь понимается
последовательное использование какого-либо ряда символов (точек, букв, чисел, дефисов
и т.п.), которое заранее может быть известно, однако его расположение в строке может быть
различным: этот ряд символов может находиться где угодно.
В чем состоит идея:
1. «Прочитать» строку текста, размечая встреченные группы символов одной
из нескольких меток. Например, все буквы русского алфавита мы можем пометить «0»,
пунктуацию — «1», числа — «2», английские буквы — «3», символьные команды
и псевдографику «4» и «5», а пробелы — «6». (Согласно этой схеме, упомянутый выше
шаблон без имени автора мы могли бы записать как последовательность
«22122122226221221»).
2. В «клоне» строки, размеченной полученным кодом мы находим искомый шаблон.
3. Поскольку длина клонированной строки и исходной не отличается, указание
на позицию шаблона укажет на позицию первого нужного нам символа в исходной строке
и общее количество символов.
И
лл. 38. Функция Shablon — определяет наличие в строке шаблона поиска, и возвращает
подстроку s2 с указанным в шаблоне фрагментом текста.
Как видно, функция довольно проста, но здесь были использованы некоторые жизненные
«лайфхаки». Поскольку бывает довольно сложно проконтролировать всю палитру
диапазонов таблицы символов, здесь введена дополнительная переменная логического типа
«plus», которая содержит информацию, об использовании очередного символа из строки
в шаблоне. Если мы пропустили какой-то символ (if plus=false) после проверки всех
полезных для нас вариантов, тогда мы добавляем символ кода «5» в S3 (там может оказаться
псевдографика, командные символы и т.п.). В таком случае длина строки, содержащая код
шаблона S3 не будет отличаться от исходной S. Также, для того, чтобы каждый раз
не проверять код текущего символа из исходника S, мы присвоили его значение переменной
j, что ускоряет время исполнения.
По окончании кодирования строки, мы выполняем поиск подстроки SS в полученной
строке S3, и копируем фрагмент из реальной строки S с позиции строки полученного
шаблона S3 и с длиной подстроки шаблона SS. Фрагмент «S2:=copy (s, pos (ss, s3), length
(ss));»
Проверим корректность процедуры:
И
лл. 39. Проверка функции shablon в программе.
И вот результат:
И
лл. 40. Результат работы программы с использованием функции shablon.
Функция шаблона с различными модификациями незаменима для парсинга
неформализованных данных. К примеру, она подходит для поиска даты рождения, номеров
телефонов, домашних адресов, номеров документов, временных периодов трудового стажа
и других сведений из текстовых файлов резюме, которые впоследствии могут быть внесены
в стандартную форму и нужным образом исправлены.
Но предположим, что пользователь написал дату рождения с лишним пробелом.
Например так, 02.12. 1962. Как можно предположить подобную ситуацию и исправить ее
автоматически?
Конечно же, во-первых, нам следует убедиться, что имелась ввиду именно дата
рождения а не период работы или дата составления резюме. На это могут указывать
сопутствующие данные: фрагменты слов в строке с «род», «рожд», «г.р» или «д.р».
Убедившись, мы используем ту же функцию shablon, но с некоторой модификацией, которая
может получать числовую информацию, игнорируя степень разреженности между
числовыми символами.
Так, функция clearshablon в случае необходимости игнорирует наличие или отсутствие
в строке дополнительных пробелов, благодаря нескольким операторам, добавленным
в функцию shablon и пользуясь описанной выше процедурой insinstring.
И
лл. 41. Функция clearshablon — осуществляет поиск шаблона и в случае неудачи пытается
улучшить результат, игнорируя символы пробелов.
В итоге, при обработке строки «02.12. 1962» с использованием шаблона, не включающего
пробел 2212212222 функция сlearshablon вернет результат «02.12.1962» без лишнего пробела,
благодаря синхронным усилиям процедуры insinstring, устранившим все пробелы из строк
как в коде-шаблоне, так и в исходной строке.
Разреженный текст
К разреженному тексту разработчики обычно относятся скептически; мол, явление это
редкое и тратить усилия на то, чтобы его распознавать — бессмысленно. Но если исходить
из анализа данных современных резюме, рефератов и сочинений, окажется, что почти
в каждом двадцатом из них встречаются одна-две строки разреженного текста. Неужели
строку типа «И в а н о в И в а н И в а н о в и ч» придется упустить? Быть может вы никогда
не столкнетесь с разреженным текстом, но сам по себе вопрос весьма интересен.
Посмотрим, как эту проблему можно решить средствами Паскаля.
1. Во-первых, нам следует определить некоторые признаки разреженности. (Одинарная,
двойная, тройная или четверная цикличность символов пробела, чередующаяся с символами
единичных букв).
2. Во-вторых, следует определить длину этой регулярной цикличности (поскольку число
пробелов после первого слова делается увеличенным, а значит, нерегулярным).
3. В-третьих следует обработать полученный фрагмент и проверить остаток текста
на наличие признаков разреженности.
А поможет нам в этом — описанная выше функция shablon и insinstring. Как? Смотрим
подробно код функции findras.
И
лл. 42. Функция FindRas находит и нормализует разреженный текст в строке.
Здесь мы используем массив из четырех видов строковых констант ST, в которых храним
«заготовки» для шаблона, описывающие минимальные фрагменты содержания четырех
видов разреженности. (Здесь: «6» означает пробел, «0» — букву русского языка, и требуемые
нам последовательности мы кодируем «60», «660», «6660» и «66660»). Почему их только 4?
Логично предположить что разреженность формата «пять пробелов один символ» едва ли
встречается в практике.
Далее, мы начинаем работать в трех циклах, главный из которых, цикл Repeat, имеет
условием выхода отсутствие вариантов признаков разреженности в тексте (здесь мы считаем
таким признаком отсутствие 3-х порядков последовательности одного из четырех вариантов
разреженности. То есть, если «shablon» отвечает «true» на последовательность 606060 значит
имеет место вариант разреженности с одним пробелом (Step=1) и следует его отработать).[6]
Далее, получив вариант разреженности (if step <> 0) мы попадаем в цикл, измеряющий
длину встреченной регулярности. В ней мы увеличиваем значение s3 на дополнительный
шаг регулярности, в случае удачи запоминаем его в переменной S5 и выходим из цикла,
когда терпим неудачу (пустой возврат s4). Далее, с помощью Insinstring мы получаем
из регулярной последовательности S5 строку без пробелов S4 и заменяем в оригинале строки
S фрагмент S5 на фрагмент строки без пробелов S4 с дополнительным пробелом, складывая
все в переменную S6. Затем, заменяем содержимое исходника S на частично
отредактированное S6 в котором была произведена замена и повторяем весь цикл с начала.
После того как регулярности пробелов перестают встречаться в тексте (Step=0), остается
только удалить двойные пробелы из общего текста и наслаждаться корректным написанием
строки, отправленным в переменную S2.
Испытаем функцию на практике в программе:
И
лл. 43. Применение функции FindRas к строке с разреженным текстом.
Причем, как видно все слова в тексте имеют разную степень разреженности, (от 0 до 3-х
пробелов между буквами), что однако не мешает привести его к нормальному виду:
И
лл. 44. Результат обработки разреженной строки с помощью функции FindRas.
И
лл. 45. Функция поиска отдельного слова или нескольких слов tryfreew в блоке текста.
Теперь можно считать, что мы рассмотрели основные функции, которые позволяют работать
со строками в широком диапазоне практических операций. Следующие главы расскажут
о том, как строковые данные могут стать разумнее.
И
лл. 46. Фрагмент последовательности из 2042 иероглифов японской письменности Кандзи
мог бы оказаться лишь каплей в море бесконечной символьной последовательности.
И вот, мы подходим к самому главному. Мы могли бы записывать слова, используя
на каждое — по одному символу; также, мы могли бы их категоризировать, отделяя символы
позиционно друг от друга в каждом таком «слове». Но позиционное смещение мы могли бы
использовать и как обозначение преемственности одного слова к другому.
Поскольку слова имеют свойства группироваться, обобщаясь с неким общим родителем,
логично предположить, что все возможные группы слов-родителей мы можем заключить
в достаточно небольшой последовательности позиционной записи этих наборов символов.
Таким образом, каждое слово будет иметь первым символом единого предка, но отделение
каждой позиции будет обозначать следующий уровень перехода.
Мы не существуем в таком идеальном мире. Однако, нам, быть может, окажется
достаточным количества символов, предлагаемых современными системами для подобного
эксперимента?
В таблице ASCII — 256 символов; и если мы будем использовать переход позиции
родства достаточно часто, вполне может оказаться, что нам будет достаточно и этой
последовательности для фиксации не арифметических, но смысловых операций. И здесь
наступает магический момент — оказывается, что любое слово или понятие естественного
языка мы можем угадать с помощью форм обобщения за количество попыток от 1 до 22. Это
означает, что наш классификатор с достаточной степенью детализации будет использовать
последовательности от 1 до 22 символов бесконечного символьного (строчного) множества.
На основе этой идеи и родилась концепция многомерного строчного множества,
единого классификатора для всех понятий естественного языка, а также для обозначения
комплекса операций по работе с ним. Чем она удобна? А чем удобен позиционный перенос
в арабской десятичной арифметической системе? Идея переноса десятков и сотен оказалась
прорывом в математике и привела к существенному упрощению арифметических операций.
Можно предположить, что подобная организация строковых данных может быть довольно
прогрессивной.
Итак, многомерное строчное (строковое) множество получает свое название
от принципа организации классификатора, действующего на основе трех простых правил.
1. Все элементы классификатора являются потомками прародителя, имеющего код A.
2. Каждый первый потомок родителя получает в наследство код родителя
с дополнением символа A.
3. Каждый следующий потомок родителя получает в наследство код родителя
с дополнением следующего по порядку свободного символа (В, С, D и т.д.) По завершении
буквенного ряда (после символа «z»), следующий символ получается из кода действующей
символьной таблицы.[1]
Р
ис. 47. Принцип классифицирования MSM на простом объекте «Кошка».
Таким образом, мы получаем классификатор, в котором стараемся достигнуть максимальной
разветвленности элементов, различая на каждом новом уровне усиление определенных
признаков, где каждый конечный «пункт назначения» может стать родителем следующего,
и так до бесконечности.[2]
При классификации мы также стараемся придерживаться того принципа, что более
общее определение становится родителем более узкого понятия. Так конечные элементы
ветвей классов оказываются наиболее узкими определениями. Если в полной мере соблюдать
этот принцип невозможно, когда одно понятие может быть родителем другого и наоборот, то
мы записываем их интуитивно, в произвольном порядке.
В начале книги, на рисунке 2 был приведен только небольшой фрагмент ветвей
классификатора, поскольку его трехмерное изображение представляет ряд технических
трудностей.[3] Поэтому здесь и далее приводятся двумерные иллюстрации.
На показанном фрагменте слова и лексемы используются как самостоятельные,
полноценные формы объектов что видно в хвосте классификатора: «Туманный» является
частным случаем «Туманное», а «Туман» — один из случаев «Туманный».
В случае, когда одно слово имеет несколько значений, они описываются в разных ветках
с добавлением символа "|" для каждого нового значения. Так, например слово «Сладкий»
в буквальном значении вкуса описывается кодом AAAAAABABAAAABBA, а в значении
«Льстивый» — AAAAAAABABBAIQJFGAAE. (См. рис. 49).
Р
ис. 48. Пример классификации слова «Туман», где он получает кодировку AABBADAIEA.
Р
ис. 49. Представление двух значений одного понятия «Сладкий» в различных ветках
классификатора.
Но в чем разница, отличие от других классификаторов, спросите вы? И совершенно верно,
что сомневаетесь. Первое отличие — в количестве уровней детализации предков. «Среднее»
понятие в MSM состоит из 15—20 переходных элементов. Во-вторых, в MSM не существует
дискриминации предлогов, — слова «а», «не», «в», «и», «но» и т. п. — являются
полноценными носителями смысла, как и более длинные их родственники. И наконец, мы
достигаем эффекта комплементарности элементов, «бонуса» от бесконечной символьной
последовательности, которым мы легко пользуемся.
Алгоритмы записи и чтения данных MSM
С чего можно начать формирование собственного классификатора? Для самой простой
организации словаря нам потребуется создать и сохранить два файла, которые мы можем
назвать произвольно, допустим, «ALIK. txt» и «ALIK2.txt».[4] Почему два? Они будут
братьями-близнецами и содержать одинаковые сведения, однако будет удобнее если один
из них хранит данные в формате «наименование/код», а другой — «код/наименование». Нам
не следует создавать их пустыми, поэтому в каждом из них мы сделаем первые записи,
например, в первом: «ВСЕ А», а во втором «A ВСЕ». («A» в данном случае -английский
символ).
Далее, нам потребуется процедура, которая добавляет записи в эти словари. Схема
простая: она находит искомое слово и формирует код потомка, на основании кода родителя,
с добавлением следующего по порядку (начиная с «А»), свободного от потомков символа
к коду родителя. То есть, если мы имеем понятие «ABB» с потомками в словаре «ABBA»,
«ABBB» и «ABBC», то добавляя в него следующего потомка, он должен получить кодовое
имя «ABBD». Этот процесс организован в процедуре addnext, получающей в качестве
входных параметров наименование S и код родителя SC.
И
лл. 50. Процедура добавления в словари элемента S к родителю с кодом SC.
Здесь происходит следующее. Если длина слова составляет менее трех символов, его длина
увеличивается при помощи одного или двух символов «|».[5] (Так например, слово «да»
превратится в «да|», а предлог «и» в «и||»).
Далее происходит чтение содержимого файла «alik2.txt». Если встречается искомая
запись, нам следует уточнить, действительно ли она оказалась в строке с первого
символа (Pos (sc, sc2) =1 и является ли этот код потомком исходного кода (l2> l). На самом
деле здесь нас интересует только наличие потомков, поскольку в них мы можем
«подсмотреть» использованные символы. В переменной max мы сохраняем числовой код
прочитанного символа потомка. По завершении просмотра словаря формулируется
актуальное имя для записываемого понятия: если потомки нам встречались, то символ имени
потомка будет равным символу имени последнего потомка +1 (для «A» это будет «B», для
«Y» это будет «Z», для «Z» это будет «a» и т.д.); если же потомки не встретились, то
именем для записи станет код родителя + «A» (if max=0 then sc2:=sc+«A» else sc2:=sc+chr
(max+1)).
Затем остается только записать результат в файл, используя в качестве разделителя
символ пробела.
Мы разобрались, как добавлять одиночные элементы в классификатор, но тем не менее,
не разобрались с интерфейсом и самим процессом реализации. Что должно происходить
с алгоритмом программы, когда она получает команду типа «M это N»? Во-первых, нужно
проверить не находится ли сохраняемое слово уже в словаре и существует ли его родитель.
В зависимости от этого будут применяться различные стратегии. Во-вторых, для
оптимизации скорости «обучения» было бы интересно создавать сразу набор
дополнительных потомков из словоформ исходного слова. Но остановимся пока на первой
задаче, а ко второй вернемся немного позднее, поскольку она потребует использования
дополнительных словарей.
Следующая процедура — находит сохраненное понятие и возвращает его код.
И
лл. 51. Процедура findword находит в словаре alik2 сохраненное понятие S и возвращает его
код S2.
В данном случае используется поиск по файлу alik2.txt. Напомню, файл хранит записи
данных в формате «код /наименование», как например «AAAAAABAABACE
ТЕМА». Условием работы цикла является отсутствие указателя на конец файла (while not eof
(f)). Внутри цикла читается каждая строка и в случае совпадения входной переменной S c
наименованием из фрагмента строки S3, (начиная от пробела и до конца строки),
переменная S2 получает фрагмент строки с кодом наименования. После этого производится
выход из цикла оператором Break, — нам больше нет необходимости читать другие записи
словаря, поскольку он не содержит одинаковых наименований с разными кодами и каждое
из них уникально. Соответственно, быстрый выход в данном случае обеспечивает ускорение
работы программы.
Понятно, что нам не обойтись и без обратной операции чтения слова по коду. Она
действует аналогичным способом:
И
лл. 52. Процедура извлечения из словаря слова S2 по коду SC.
Что нам потребуется еще для формирования записей словарей в формате «N это M»?
Конечно же, следует проверить наличие в базе обоих понятий, и здесь есть три варианта
сценариев:
И
лл. 55. Фрагмент диалога с диалоговой процедурой в режиме добавления понятий Chatit.
Итак, если как следует набраться терпения и заняться вдумчивым и планомерным
наполнением словарей[6], каждый элемент классификатора становится похож на слоеный
пирог из «генома» кода, в котором мы можем отсекать части хвоста и получать более
обобщенные сведения об элементе. Возьмем, например фразу:
Мы сегодня пойдем в кино
В закодированном виде это предложение выглядит так:
AAAAAABACIJ AAAAAAABAAHPFA AAAAAAACCABABCFA AAAAAAAAFFG
AAAAAABAADLJ
Если мы уберем от каждого кода по одному символу с его конца, то получим фразу:
AAAAAABACI AAAAAAABAAHPF AAAAAAACCABABCF AAAAAAAAFF AAAAAABAADL
Или
Объединение сейчас идем где кинотеатр
И пусть эта фраза звучит против правил русского языка, однако если бы это
предложение произнес иностранец, скажем из ЮАР, она не показалась бы чем-то
экстраординарным, и нам все равно был бы понятен ее смысл.
Ради интереса усечем еще один символ из каждого слова-множества и мы получим
предложение:
AAAAAABAC AAAAAAABAAHP AAAAAAACCABABC AAAAAAAAF AAAAAABAAD
Или
Сообщество настоящее идти указание кинематограф
Понять его еще немного сложнее, но все же, возможно. Таким образом, утрата или
приобретение одного или нескольких символов кода в отдельных словах-множествах
практически не меняет конечного смысла целой фразы, что в значительной мере упрощает ее
анализ и операбельность.
Кстати говоря, в каждом слове приведенного предложения есть общий фрагмент,
который легко выделяется, — это головной элемент AAAAAA, означающий «Понятие»
и говорящий о том, что во фразе мы оперируем абстрактными смыслами, порожденными
человеческим разумом.
Рассмотрим превращения этого предложения при позиционном прибавлении кода и его
удалении на рисунке 56. Цветом выделен оригинальный текст, строчки ниже — «предки»
кода, строчки выше — его потомки.
И
лл. 56. Результат работы процедуры, иллюстрирующей «путешествие» по содержанию кодов
слов-множеств из исходной фразы. Ниже исходных слов — его предки, выше — его
потомки.
Возможно, вам захочется отметить, что «кино» в действительности относится к конкретному
зданию или строению, и это справедливое замечание. Второй смысл
слова «кино» действительно закодирован в ветке классификатора «Здание»/ «Сооружение».
Также и смыслы слова «Мы» разнятся, и это может быть как указание на «Сообщество»/
«Объединение», так и на «Людей», выполняющих определенную роль; но о возможности
оперировать несколькими смыслами слов мы подробно поговорим в главе 1. Пока же
рассмотрим второй пример:
Над городом поднимался густой туман
В закодированном виде это предложение выглядит так:
AAAAAAAAFFEBA AAAAAAAAFFETEC AAAAAAAAFFEBCCFA
AAAAAAABAAHGAAFA AABBADAIEA
Вновь удаляем по одному последнему символу и получаем следующее:
AAAAAAAAFFEB AAAAAAAAFFETE AAAAAAAAFFEBCCF
AAAAAAABAAHGAAF AABBADAIE
Вверху город поднимается частый туманный
Это также нарушает правила русского языка и немного смутно, но подобное вполне
можно услышать от иностранца. Теперь продолжим и отсечем еще по одному символу
от кодов:
AAAAAAAAFFE AAAAAAAAFFET AAAAAAAAFFEBCC AAAAAAABAAHGAA
AABBADAI
Место городское подниматься частое туманное
Эту фразу можно понять уже двусмысленно: то ли в пригороде можно видеть смог,
то ли в городе часто виден туман, и тем не менее общий смысл того, о чем идет речь, все еще
может быть уловлен.
И
лл. 57. Предки и потомки слов из фразы «Над городом поднимался густой туман».
Эти примеры легко показывают, что кодирование смыслов в MSM имеет существенное
значение: фразы слов-множеств достаточно устойчивы к обобщениям, и если изменения
затрагивают лишь несколько символов в предложении, они с высокой долей вероятности
сохранят свое смысловое значение. Устойчивость и мобильность семиотических значений
позволяет расширять вероятностное попадание в необходимые смыслы. Так фразы «Мама
мыла раму» и «Мать моет окно» получают очень близкое семиотическое значение, которое
легко обрабатывается программными методами, поскольку их коды оказываются очень
близки. Визуально это можно увидеть на рисунке 58.
И
лл. 58. Родственность фраз «Мама мыла раму» и «Мать моет окно» очевидна.
Фразы можно сравнить и по написанию кода:
AAAAAAAEAADL AAAAAAACUKJFA AAADCABAAABJJAC (Мама мыла раму)
AAAAAAAEAAD AAAAAAACUKJF AAADCABAAABJ (Мать моет окно)
Если сравнивать их машинным способом, с помощью процедуры вычисления
процентного сходства, мы получим следующие значения: 92%, 92% и 80%; из этих
показателей вычисляется среднее значение сходства (92+92+80) /3=88%. Если в базе данных
не находится более точное попадание, (с более высоким процентным соотношением)
исходное предложение считается самым подходящим и используется в зависимости
от наших нужд для генерации необходимых данных.
Стоит отметить, что для сравнения слов-множеств используется не стандартная
описанная процедура сложного сравнения, (которую мы используем для сравнения слов
на естественном языке), а процедура, работающая по принципу «первого расхождения».
Почему это так? Все слова классификатора родственны и имеют одного общего предка.
Родственность будет считаться совпадающей до того момента, когда следующий символ
перестает совпадать; то есть ветки родственности начинают расходиться, — дальнейшее
сравнение может только запутать, поскольку, например, коды
ABAAABBCD и
ABAEABBBA
могут иметь совпадения после расхождения, что однако не приближает родственность
смыслового значения. И очевидно, что понятие с кодом ABAABADDE должно иметь
большее родство с первым значением, чем второе, поскольку имеет общую
последовательность из четырех прямых родственников «ABAA» и должно получить большее
количество баллов за сходство.
Стоит привести оригинал процедуры Truepercent.
И
лл. 59. Процедура truepercent измеряет сходство кодов MSM — до первого расхождения
значений.
Как действует алгоритм. Он получает на вход два кода слов-множеств, S и S2. В выходной
переменной T будет находиться значение массой от 0 (полное различие) до 10 000 (полное
сходство). Для начала, здесь проверяется длина значений и если один из кодов (или оба)
имеют длину меньше 1, то проверка заканчивается со значением T=0. Если эта проверка
пройдена, мы проверяем полное совпадение значений (IF s=s2), (и оставляем в T значение
10 000, если это так). Далее, значению step присваиваем число, равное 10 000 поделенным
на число символов в самой длинной строке из сравниваемых.
В цикле мы будем пробегать по индексу строки вперед с помощью указателя i до тех
пор, пока не наткнемся на первый не совпадающий символ или длина счетчика превысит
длину одной из строк. После этого, вычисляем значение T, по числу счетчика i-1,
умноженного на значение Step. Так например, сравнение значений ABABA и ACABA даст
показатель 2000, а сравнение ABABA и ABACA — 6000, что нам и требовалось.
Простое определение
Начиная работу с MSM нельзя не упомянуть возможность получения простого определения
на вопрос о значении того или иного слова. Для получения определения нам понадобится
прочитать родителей понятия, а мы помним, что код всех родителей записан в их детях.
Следовательно, для получения информации о его родителях нам следует удалить с «хвоста»
определяемого слова один-два символа и уточнить, что значат полученные коды в словаре.
Для этого нам понадобятся уже описанные процедуры Findword и Findcode, описанные
выше. Организовать их взаимодействие можно, добавив условие вхождения в диалоговую
процедуру Chatit. Мы будем давать определение, исходя из условия предоставления
пользователю двух ближайших по «старшинству» родственников понятия.
И
лл. 60. Пример реализации функции поиска объекта в словаре и разъяснения с помощью
процедур Findword и Findcode в диалоговой сборке Chatit.
Теперь диалог с компьютером может носить определенную полезную функцию, если нам
необходимо выяснить информацию о положении некоторых понятий в структуре
«дерева» MSM.
И
лл. 61. Пример работы с задачей «что такое», встроенной в диалоговую сборку Chatit.
Также, мы можем уточнять, принадлежит или нет определенное понятие к определенной
ветке, если проверим наличие понятия в составе кода слова. Этот тип запроса может звучать
как «Лиса это животное?» для чего программе требуется проверить наличие общих предков
у обоих слов-множеств этих понятий.
И
лл. 62. Функция проверки предположений пользователя по типу « M это N?»
Теперь «продегустируем», как это происходит на практике, развернув в процедуре Chatit уже
вполне осмысленный диалог, со стороны, напоминающий философский разговор двух
образованных людей. Поневоле в такой момент задумаешься, а кто на самом деле разумнее?
И
лл. 63. Пример работы диалога по принципу «M это N?» в диалоговой сборке Chatit.
Общее и частное
Семиотические (смысловые) сходства и различия понятий позволяют осуществлять простые
операции сравнения. То есть, в MSM не составляет никакого труда найти лишний объект
в ряде других, поскольку он будет отличаться своим кодом.
Рассмотрим пример: «Найди лишнее: карандаш, ручка, бумага, фломастер?» Здесь
программа может безошибочно определить лишний объект «бумага», по той простой
причине, что его код будет более отличен от других. Пример того, как это работает —
на иллюстрации 64.
И
лл. 64. Пример решения задач по нахождению лишнего объекта в программе MSM.
Выводы можно сделать, даже просто взглянув на состав кода:
AAADAACBABA (Карандаш)
AAADAACBABC (Ручка)
AAADAACBAAA (Бумага)
AAADAACBABB (Фломастер)
Это программное решение описывается в процедуре Findelse.
И
лл. 65. Процедура нахождения лишнего из серии слов, на основе их кода MSM.
В приведенной процедуре используются два массива — sm и sm2, в которые записываются
отдельные слова и их коды, а также массив Ball, где сохраняются оценки схожести одного
элемента со всеми остальными.
Для начала, строка S здесь разбивается на набор слов с помощью процедуры justbreak
и слова оказываются в массиве sm, с максимальным индексом j. В цикле массив sm2
заполняется кодами слов массива sm с помощью процедуры findword.
Далее, используются два цикла I и X. В случае, если значения i и x отличаются
(поскольку они выполняют функции индексов массива sm2), с помощью процедуры
truepercent получаются оценки пар кодов, результат которых добавляется в массив ball
текущего индекса i.
После перебора всех элементов в цикле x, для оценки каждого элемента используются
переменные min и mini. В случае если сумма текущих оценок оказывается меньше
минимальной, она сохраняется в переменной min а индекс элемента — в переменной mini.
По завершению работы циклов выходная переменная s2 получает значение sm
с индексом минимума mini, — это слово и будет лишним элементом на основе сравнения
кодов MSM.
Синонимы
Синонимы при работе с текстом встречаются регулярно. Поэтому нам следует уметь с ними
работать. В идеале программа должна понимать, например, что фразы «Ты хочешь есть?»
и «Ты хочешь кушать?» по существу означают одно и то же, несмотря на то, что
дополнительные значения слова «есть» кроме как «находиться» мы могли еще и не описать.
(Однако это не означает замещения смысла слова, эта стратегия используется только для
входного «понимания» блока текста, предложения).
Для хранения синонимов можно использовать отдельный словарь, например sinonims.
txt (Илл. 74). Он имеет самую простую структуру: здесь находятся словесные пары, которые
могут являться синонимами друг друга.
И
лл. 74. Содержимое словаря синонимов sinonims. txt.
Также стоит помнить, что одно и то же слово может иметь несколько синонимов
и описываться в различных местах этого словаря, поэтому оптимальным вариантом
получения сведений из него является генерация массива значений, (а не 1,2 или
5 переменных).
И
лл. 75. Процедура чтения синонимов findsin. По коду S происходит генерация списка
синонимов из словаря синонимов.
Для обработки словаря понадобится соответствующий алгоритм чтения и записи. Прочитать
записи можно с помощью процедуры Findsin (илл. 75), где варианты синонимов будут
сохраняться в выходном массиве с числом элементов j — так же, как и в случае с процедурой
justbreak.
В качестве входной переменной здесь используется единичный код из переменной s.
В процедуре происходит считывание сведений из словаря синонимов, и в случае, если
рассматриваемый код попадает в часть содержимого одного из значений, а также модуль
числа разницы в длине этих строк не превышает 4 символа, то итоговый массив дополняется
новым значением второй пары. Считывание является зеркальным; то есть искомый синоним
может быть получен как из левой так и из правой части записей словаря.
Процедуру записи синонимов в словарь мы можем не выделять в отдельный блок,
а встроить в описанную выше процедуру Chatit (илл. 53), как одну из форм реакций
на введенную пользователем информацию. Условием вхождения мы можем считать наличие
в строке символа равенства '=», который ставится между двумя словами. Так же здесь нам
потребуется предварительно созданный файл sinonims. txt, в который мы будем сохранять
новые синонимы.
И
лл. 76. Дополнительный блок входа в процедуре Chatit (илл. 53) сохраняет новые синонимы,
предварительно проверяя наличие более ранней записи в файле sinonims. txt.
Также в практике нам потребуются процедуры для генерации синонимов в блоке текста. То
есть, для нас имеет смысл оперировать несколькими вариантами предложений, собранных
целиком с использованием различных комбинаций синонимов. Эту задачу будут выполнять
процедуры sincombine и sinchanger.
Процедура sincombine занимается обработкой двух предложений, составленных
из кодов слов-множеств; но мы предполагаем, что эти два предложения могут отличаться
по причине использования в них одного или нескольких слов, имеющих несколько значений.
В результате нами генерируется массив из R значений, который содержит несколько
возможных вариантов рекомбинаций имеющихся значений.
Например, у нас есть предложения из слов:
«A B C D E» и «A F C D G». В данном случае нам известно, что слова B и F, а также E
и G являются синонимами друг друга. Но в то же время В не является синонимом G, а G
не является синонимом F. В данном случае позиция «руководит» объектом рекомбинации.
Идеальным результатом работы процедуры должно стать получение массива из двух
дополнительных рекомбинированных предложений: «A F C D E» и «A B C D G».
Мы не используем исходные предложения в качества вариантов вывода; а если у нас
окажется предложение с большим числом синонимов, число вариантов должно
пропорционально возрасти.
И
лл. 77. Процедура sincombine занимается рекомбинацией двух предложений с разными
вариантами синонимов.
Процедура sinchanger занимается исчерпывающим поиском вариантов синонимов
и созданием на их основе рекомбинированных предложений с использованием описанной
выше процедуры sincombine.
И
лл. 78. Листинг процедуры sinchanger, занимающейся исчерпывающим поиском «кучи»
вариантов с использованием словаря синонимов.
Итак, мы собрали нужные вещи, для дальнейших шагов. Каким образом мы можем получить
некоторые сведения, агрегированные из свойств ряда тех или иных объектов? Для этой цели
реализуется следующий механизм: на вход процедуры поступает некоторая заявка, назовем
ее N с рядом свойств N2, N3, N4.. -число серии свойств при этом не ограничивается. После
чего программа производит поиск соответствий всех параметров, которые связаны с N
с некоторой стадией обобщения. То есть, при чтении данных допускается некоторая разница
в «хвосте» элементов, которая делает соответствие отвечающим требуемому условию. Так
например, слова-множества «Цвет» (AAAAAAABACA) и «Красноватый»
(AAAAAAABACALAH) имеют общее основание «Цвет» и разницу в 3 символа. Это
означает, — если мы ищем свойства, соответствующие запросу «Цвет» и используем
допущение до 4 символов отклонения, то свойство «Красноватый» становится подходящим
элементом в качестве ответа на такой запрос. Аналогичным образом будут обрабатывать
и многие другие обобщающие запросы, например «Вода» (AABBAFC) и «Мокрый»
(AABBAFCKL), «Звук» (AAAAAAABADA) и «Громкая» (AAAAAAABADAAFE),
«Ругательство» (AAAAAAACaLGAC) и «Обматерить» (AAAAAAACaLGACJIE).
Но например, для обнаружения обобщения «Разрушение» (AAAAAAACA) и «Ударил»
(AAAAAAACaIJA), допустим дополнительный отступ в сторону общего предка «Действие»
(AAAAAAAC).
Так, с помощью эпмпирического метода проб и ошибок в алгоритме поиска свойств
с обобщением мы будем использовать обобщение до 4 символов с отступом в 1 символ
в сторону общего предка понятия. Кроме того, программа должна следить
и за использованием синонимов, чтобы они были полноценными участниками действия.
Переходим к практической реализации процедуры, занимающейся всем этим.
И
лл. 79. Листинг процедуры Anatomy, занимающейся практическим обобщением свойств
искомых понятий.
Из нашего внимания выпал еще один момент, это использованные
процедуры uncodecs и getfathercode.
Uncodecs занимается групповым восстановлением предложения в строке S из серии
кодов слов-множеств, и передаче перевода в S2:
И
лл. 80. Листинг процедуры Uncodecs для перевода предложения, написанного в кодах
слов-множеств на естественный язык.
Getfathercode — находит общее в двух словах, написанных в кодах слов-множеств SC
и SC2, и возвращает его в выходной переменной SC3.
И
лл. 81. Листинг процедуры Getfathercode для нахождения общего предка из кодов двух
слов-множеств.
Теперь мы готовы к обработке сложных запросов типа «Расскажи о цвете неба», и так этот
фрагмент может быть оформлен в нашей процедуре ChatIt, отвечающей за диалог
с пользователем:
И
лл. 82. Фрагмент вставки процедуры Anatomy в диалоговую процедуру Chatit.
Вопрос-ответ
Так же мы можем и прямо сохранить форму вопроса (это реализуется в словаре Queans
с помощью формы, как на иллюстрации 83).
И
лл. 83. Фрагмент записей словаря вопросно-ответных моделей Queans. И вопросы и ответы
выглядят как полные фразы, записанные в форме слов-множеств.
В подобном формате может быть записан нужный ответ. Пример такого диалога
с программой — на иллюстрации 84.
И
лл. 84. Прямое обучение конкретному вопросу. Здесь перефразирование вопроса не влияет
на «понимание» и возвращает систему к нужному ответу.
Помимо упомянутых форм взаимодействия с системой MSM мы имеем возможность
получать так называемые зеркальные ответы, когда организованные взаимосвязи позволяют
регулировать формы ответа в зависимости от поставленной формы вопроса.
Рассмотрим пример. Мы просим систему запомнить ситуативную фразу «Ты принес
из магазина продукты?» на которую записываем форму ответа «Продукты я принес». Далее,
на вопрос «Ты принес из магазина хлеб?» последует ответ «Хлеб я принес», а на вопрос «Ты
притащил из „Ашана“ молоко?» ответит «Молоко я притащил». Как это происходит?
И
лл. 85. Образец зеркального ответа из модели прямой записи вопросов и ответов.
Как мы уже упоминали, исходное предложение записывается в форме кода MSM в паре
с формой ответа. В данном случае запись модели выглядит так:
«AAAAAAAAFDAA AAAAAAACGLIEA AAAAAAAAAAA AAADCABBBA
AAADCCFHE
AAADCCFHE ABABACAAAACAP AAAAAAACGLIEA.»
В первой строке звучит вопрос, во второй — ответ. Ответная строка помечается точкой
в конце. Итак, записали.
Затем при совпадении/частичном совпадении с исходной фразой (где нам безусловно
поможет функция процентного сравнения) мы анализируем текст, отмечая наличие
совпадений в вопросе и ответе. В данном случае в тексте ответа используется два слова
из вопроса, это «Принес» (код AAAAAAACGLIEA) и «Продукты» (код AAADCCFHE).
После чего мы отмечаем позиции тех слов, которые имеют неполное совпадение, и копируем
вариант родственника из вопроса.
Записать модель вопросно-ответной схемы мы можем с помощью
процедуры writemodel:
И
лл. 86. Листинг процедуры записи вопросно-ответных моделей.
В этой процедуре записи моделей используется и незнакомая нам процедура Decodecs. Ее
основная функция — преобразование предложения в набор кодов MSM.
И
лл. 87. Листинг процедуры Decodecs, переводящая предложение на естественном языке
в набор кодов MSM.
Далее, нам потребуется и процедура чтения моделей, для прикладного применения
к конкретному заданию от пользователя. Этим занимается пара
процедур Checkmodels и Readmodels, где checkmodels — является вспомогательной,
процедурой, в которой производится выбор и проверка наличия моделей в словаре,
а readmodel — основной для взаимодействия с пользователем. Начнем с Checkmodels:
И
лл. 88. Процедура Сheckmodels в которой производится проверка наличия записанных
моделей ответа.
Здесь также используется функция multicheck, которая занимается сравнением наличия
одного кода в другом или одной серии кодов в другой серии кодов, записанных через точку,
как например "AADG.ADFD» и "ADFE.ADDD. AADG», — в последнем случае функция
вернет значение true (поскольку здесь совпадают значения AADG в строках).
И
лл. 89. Функция Multicheck, занимающаяся сравнением совпадений кодов слов-множеств
в одиночной или серийной записи.
И наконец, окончательная процедура обработки запроса Readmodel, принимающая запросы
пользователя и отвечающая на них.
И
лл. 90. Листинг процедуры Readmodel, занимающейся чтением модели и выбором вариантов
ответа на запрос пользователя из модели типа вопрос-ответ.
В этой же процедуре производятся и «зеркальные ответы», а также происходит контроль
за порядком соблюдения последовательности построения слов в вопросе.
Применение readmodel в нашей сборке диалоговой процедуры Chatit может быть
следующим.
И
лл. 91. Применение процедуры Readmodel в сборке диалоговой процедуры Chatit.
В данной сборке мы добавили переменную Entered, которую описали типом Boolean. Ее
также следует добавить и во входные параметры других процедур в сборке Chatit, — она
контролирует процесс вхождения в одну из процедур, и тогда становится True, иначе ее
значение остается False, — это означает что текущее задание еще не выполнено другими
процедурами и неплохо было бы решить поставленную задачу.
Однако на этом возможности системы на базе MSM далеко не исчерпываются.
В качестве свойств мы имеем возможность сохранять зависимости и удельные веса
значений.
И
лл. 93. Запись значений антагонистов в словарь antagon. txt.
В некоторых случаях оказывается полезным и более прямолинейно указывать
на характеристики весов значений. Например, словарь с записями весовых значений
оформляется кодами следующим образом:
БЕГЕМОТ ТЯЖЕЛЫЙ +500 (ABABACCEAA AAAAAAABAAAA +100)
СЛОН ТЯЖЕЛЫЙ +2000 (ABABACFAA AAAAAAABAAAA +2000)
КИТ ТЯЖЕЛЫЙ +7000 (ABABACPAA AAAAAAABAAAA +7000)
ЧЕЛОВЕК ТЯЖЕЛЫЙ +100 (ABABACAAA AAAAAAABAAAA +100)
И
лл. 94. Листинг процедуры Incalik4, записывающей весовые значения слов-множеств
в словарь.
Так мы легко получаем ответы на вопросы о сравнительных характеристиках, как например,
в данном случае: «Кто тяжелее, слон или человек?», «Кто легче: кит, бегемот или слон?»,
и т. д. Характеристика может также даваться по любому признаку: силе, скорости, яркости,
насыщенности, кислоте, сладости, и т. д.
Для создания возможности обработки подобных запросов нам понадобится две
процедуры, motherland и tryweight. Первая процедура — вспомогательная, она понадобится
нам для того, чтобы мы могли оценивать значения весов в зависимости от удаленности
потомка от своего родителя.
И
лл. 95. Листинг процедуры motherland, оценивающей близость родства двух слов-множеств.
В качестве ответа используется несколько вариантов:
— отсутствие родства — « -100»;
— родство, но разница в числе символов — положительное значение разницы
в символах (от 0 до бесконечности);
— родство, но разница в матери на один символ — то же самое, +1.
Следующая процедура tryweight просматривает массив условий SM и сверяет
соответствие с серией значений SM2 по трем параметрам: соответствие весового значения,
наличия заданного свойства и соответствия родства условий с предлагаемыми значениями.
И
лл. 96. Вычисление весовых значений серии условий к серии значений с помощью
процедуры tryweight.
Таким образом, на вопрос: «Выбери желтое сильное животное: таракан, крокодил, апельсин,
лев, шпиц, человек» процедура даст корректный ответ «Лев». Как видно, желтых животных
здесь два, «Лев» и «Шпиц» и оба они имеют сильного предка «Хищное». В этом случае
и играет свою роль как разница в удаленности от предка, имеющего весовое значение, так
и сумма весовых значений конкретной особи. «Хищное» имеет весовое значение силы +100,
«Лев» — так же имеет весовое значение +100, а непосредственное весовое значение силы
«Собаки» +20. Разница между кодами льва (ABABACBBBA) и хищного (ABABACB)
составляет 3 символа; такая же, как между ним и шпицем. Так эти существа получают
по (100/3) =33 балла силы по умолчанию. Дополнительное значение силы льва составляет
так же 100, итого — у льва оказывается 133 балла, в то время как у собаки второе значение
составляет 20, и в сумме 20+33=53 балла. Так в этом соревновании побеждает «Лев»,
набравший максимальное количество баллов и попаданий в родство свойств и условий.
Теперь встроим эту процедуру в диалоговую сборку Chatit.
И
лл. 97. Использование процедуры Tryweight в диалоговой процедуре Chatit.
Теперь рассмотрим возможный сценарий диалога на примерах.
И
лл. 98. Тест работы программы с использованием процедуры Tryweight на практических
примерах. Высказывание « Hmm..» в третьем примере означает, что полное соответствие
всем условиям не достигнуто: не обнаружилось ни одного значения, в свойствах или весах
которого хранилось бы слово «Самое». Программа просто нашла сколько-нибудь быстрое
насекомое.
Это замечательный результат. Но мы можем и улучшить его, построив алгоритм, согласно
которому из полученных сведений об удельных весах, система может строить
предположения на основе данных о родственниках без прямого на них указания.
Задача о корове
Представим, что никаких других данных о весе других живых существ, (кроме
описанных выше) у нас нет. Но спросив о весе коровы, допустим в такой форме «Корова
тяжелее человека?», мы можем получить ответ: «Предполагаю, что да». Как это можно
осуществить?
Не найдя прямого ответа, программа может построить предположение на основе
родственной связи кода существа, вес которого неизвестен (корова, код ABABACCAAA),
к коду существа, вес которого прямо указан (бегемот, код ABABACCEAA). Они имеют
общий головной фрагмент кода (ABABACC — парнокопытное), но и некоторое расхождение
(последние три буквы). Таким образом, весовое значение может быть рассчитано
приблизительно, исходя из предположения, что разность веса на каждом переходном этапе
кода не превышает 20% от веса родственника. Так можно получить числовое значение
+500 +-60% (по 20% за каждую букву разницы в родстве) итого — предположительный вес
коровы по оценке системы может составлять от 200 до 800. Теперь программе остается
только сравнить наименьшее значение с весовым значением человека (+100) и озвучить свое
предположение. Кстати, как вы наверное уже догадались, в описании значений веса был
использован приблизительный вес в килограммах. Так же можно использовать и другие
единицы мер и весов в описательных характеристиках объектов.
Задача об автомобилях
В случаях, когда весовые значения подаются в форме описанного задания, можно
попробовать найти решение, исходя из данных значений. Например, рассмотрим простую
задачу:
«Две машины едут в одном направлении: первая со скоростью 100 километров в час,
вторая со скоростью 120 километров в час. Через 50 километров пути вторая машина
сделала остановку на 5 минут. Какая из них приедет быстрее в пункт назначения,
находящийся на расстоянии 200 километров?»
Эта задача может быть решена системой при условии составления соответствующей
формы выбора правила, где система может самостоятельно «собирать» уравнения,
основываясь на ключевых фразах.
При добавлении достаточного числа правил решения задач в форме моделей, система
может выбирать наиболее похожую задачу и применять аналогичные методы решения.
Сначала мы отвлечемся и разберемся с описательной моделью на человеческом языке.
В задаче требуется определить следующие ключевые объекты и их значения, а также разбить
задачу на несколько последовательных подзадач:
Первая (машина): скорость 100 км/ч, путь 200 км = время (быстрее)?
Вторая (машина): скорость 120 км/ч, путь 50 км. 5 минут остановка. 120 км/час путь
150 км = время (быстрее)?
Теперь переходим к непосредственному подсчету. В случае с первой машиной мы
делим путь (200) на скорость (100) и получаем значение времени равное 2 часам.
В случае со второй машиной нам необходимо посчитать время, за которое машина
пройдет первые 50 километров, для этого мы делим путь (50) на скорость (120) и получаем
значение 0,417 часа. Умножаем это значение на 60 и получаем 25 минут. Затем прибавляем
к этому значению 5 минут, получаем 30 минут, или 0,5 часа. И теперь считаем, за какое
количество времени вторая машина преодолеет оставшийся промежуток пути, равный
150 км: 150/120 = 1,25 часа. Теперь к этому числу мы прибавляем 0,5 часа из предыдущего
времени пути и получаем число 1,75. Затем сравниваем значение времени, полученное
от первой машины (2) со второй (1,75) и получаем ответ — вторая машина приедет быстрее,
чем первая, несмотря на остановку в пути.
Таким образом, мы выделяем ответ в соответствии с меньшим значением, понимая, что
означает «Быстрее». Решение нельзя назвать достаточно простым для системы, однако
форма записи последовательности решений может быть зафиксирована простыми методами
таким образом, что подобные и иные задачи могут решаться достаточно легко
с использованием словарей моделей задач, имеющих соответствующие шаблоны решений.
В частности для решения подобной задачи мы выделяем текстовую форму с сохранением
модели пунктуации, где начало записи фиксируем символом».». Запись эталонной модели
задачи и решения может выглядеть следующим образом:
«.
AAAAAAADAAAAAABAB AAADAAAAAAANA AAAAAAACgPBFA
AAAAAAAAFFG AAAAAAADAAAAAAAAE AAAAAAAAFFDJ
AAAAAAADAAAAAAAAHAE AAAAAAACECB AAAAAAABAACD
100 AAAAAAABAAUJFF AAAAAAAAFFG AAAAAAABAAHBBAAAAA
AAAAAAADAAAAAABAADAE AAAAAAACECB AAAAAAABAACD
120 AAAAAAABAAUJFF AAAAAAAAFFG AAAAAAABAAHBBAAAAA *: A, B.*
AAAAAAAAFFEBAB 50 AAAAAAABAAUJFF AAADCAAADMA
AAAAAAADAAAAAABAADAE AAADAAAAAAAN AAAAAAAABAIB
AAAAAAACPHAC AAAAAAAAFFEBB 5 AAAAAAABAAHBBAAAAAAAB * C D.*
AAAAAAAADAAD AAAAAAAAAAA AAAAAAAAFGB AAAAAAACgPBGH
AAAAAAABAACAAAB AAAAAAAAFFG AAADCABBAL AAAAAAAAFFDYIHA
AAAAAAAAECAB AAAAAAAAFFEBB AAAAAAABAAEOD 200 AAAAAAABAAUJFF
*, E.*
>> F=E/A; G= (C/B) *60+D; H=60/G; I= (E-C) /B+H;
<<AAAAAAADAAAAAAAAHAE = F
<<AAAAAAADAAAAAABAADAE = I
>>>> I <F = AAAAAAADAAAAAABAADAE»
Конечно, описательная часть задачи в формате модели для решения выглядит довольно
громоздко, но при наличии хорошего объема словаря и большой базы записанных таким
образом моделей задач, должна показывать хорошие результаты.
Что здесь происходит? Мы просто переводим задачу на язык кодов MSM так, как есть,
предложениями, не забывая однако выделять численные значения, а так же модели чтения
пунктуации (выделены звездочками), в которой резервируем для каждого численного
значения в задании свою последовательную переменную (A, B, C, D, E).
Далее следуют предварительные расчеты за символами “>>», где мы также имеем
возможность заводить новые переменные (в данном случае F, G, H, I). Затем за символами “
<<” мы обозначаем связи переменных со значениями из текста, соответственно
«AAAAAAADAAAAAAAAHAE» означает «Первая»,
а «AAAAAAADAAAAAABAADAE» — «Вторая». И последняя строчка, следующая
за символами “>>>> ” — в ней мы описываем условие соответствия, при котором этот ответ
соответствует действительности.
На практике запись подобных моделей может быть результативна, хотя
полномасштабного тестирования в данном направлении еще не проводилось, и подробно
описывается она впервые.
Подобные выводы сделаны на основе сходной процедуры, благодаря которой
производилось расширение словарного запаса системы при полуавтоматическом чтении
с дополнением слов из толкового словаря русского языка, а также дополнении его
морфологическими формами исходных слов. Эффективность этого решения наглядна —
общий объем словаря удалось увеличить на более чем 30 тысяч слов всего за двести
человеко-часов взаимодействия с системой. Впрочем, подробнее об этом методе вы можете
прочитать в четвертой и пятой главах повествования.
Теперь мы остановимся на основной проблеме в решениях современных диалоговых
систем и изучим этот вопрос с разных сторон.
Простой бот
Принцип работы простейших ботов — когда запрос связан с ответом. Система выбирает,
основываясь на статистических методах, какой запрос больше похож на имеющийся в базе
данных, и выбирает ранее записанный ответ. Это — наиболее распространенный и наименее
интеллектуальный вариант. И, как правило, если это решение приводится с привлечением
искусственной нейросети, то оно не дает ему существенных преимуществ, кроме небольших
вариаций из симбиоза запросов. Например, мы научили подобного бота нескольким фразам:
«Как дела?», «Как твои дела» и «Как у тебя жизнь?» и варианты ответа: «Нормально»
и «Хорошо». Так вот, предел возможностей такого бота, это комбинаторность запроса, — то
есть очень большой удачей будет корректный ответ на запрос «Как у тебя дела?», — исходя
из указанных данных. Причем, удача в данной ситуации не гарантирована.
Теперь давайте рассмотрим конкретные примеры диалогов с простыми ботами, которым
старались задавать похожие вопросы, требующие немного больше, чем типовой ответ
на типовой вопрос.
С одним из простых ботов нам удалось пообщаться в рамках известного конкурса «The
Conversational Intelligence Challenge» («Состязание разговорного интеллекта»), в котором
производится набор базы знаний. При этом, вступая в диалог, заранее неизвестно, с кем он
производится: с человеком или с машиной. Но в данном случае нам попался довольно
примитивный бот первого типа. Диалог звучал так (Б-бот, Ч-человек). В скобках приводится
перевод фраз:
Б: — I think it is a good idea. agreed. How do you feel about pot bellied pigs?
— Я думаю это хорошая мысль. согласовано. Как ты относишься к пузатым
свиньям?
Б: — 3 years old
— Возраст 3 года
Ч: — bye
— Пока
ρBot: — Голубое.
Незнакомец: — Сколько ног у льва?
ρBot: — Колбаса)
Незнакомец: — Кошка и рыба это млекопитающие?
ρBot: — Мимимишного.
Незнакомец: — Что можно разбить тарелку или вилку?
Б: — Sometimes blue, sometimes red, sometimes orange, sometimes purple, and sometimes
many other colors. But usually blue.
— Иногда синее, иногда красное, иногда оранжевое, иногда пурпурное, и иногда
многих других цветов. Но обычно синее.
Б: — Blue, grey
— Синий, серый
О чем говорят эти и подобные ответы? Все программы первого типа выглядят разумно
лишь в случаях поддержания «светской беседы» в духе:
— Погода сегодня не очень, не так ли?
— Да, конечно. Я совсем не люблю плохую погоду. А как вам нравится сериал «Битва
престолов»?»
— О, этот сериал великолепен…
И так далее. Но в этом нет ни капли логики, ни грамма понимания. Это чистой воды
«Китайская комната».[1]
Но все возможные вопросы не только невозможно предусмотреть, их даже невозможно
просто перечислить. Поэтому, задав такой программе простой вопрос, который требует
небольшого рассуждения, — ее ответы сразу выглядят нелепо; это можно проверять
бесконечно. Становится очевидным, что здесь нельзя говорить даже об интеллекте
новорожденного, — он хотя бы в состоянии определять эмоции родителей и обучаться.
Общение с Mitsuku от Pandorabots местами приятно удивляет: этот бот был в состоянии
корректно ответить на пару простых предметных вопросов, хорошо запоминал имя
собеседника и географию, местами умело жонглировал фразами и не так грубо уходил
от незнакомых выражений. Он явно заслужил того, чтобы здесь привести полный диалог
с ним.
Б: — Where did you learn that from? I just told you it.
— Откуда вы это узнали? Я просто сказал тебе это.
Ч: — Human is a fish?
— Человек это рыба?
Б: — My creator is a human
— Мой создатель это человек
Ч: — Thanks
— Спасибо
Б: — You are a very polite person. Your mother raised you well.
— Ты очень вежливый. Твоя мать хорошо воспитала тебя.
Ч: — What is my name?
— Как меня зовут?
Ч: — Siberia
— Сибирь
Б: — I have no idea where that is. Tell me a little about your life in Siberia.
— Я понятия не имею, где это. Расскажи немного о своей жизни в Сибири.
Ч: — Here is cold
— Здесь холодно
Ч: — Ok. What do you think, how much kilometeres from my place to your?
— Ок. Как ты думаешь, сколько километров от моего места до тебя?
Б: — I see. I haven’t thought much about how much kilometeres from your place to my, so
I don’t have a good answer for you.
— Понимаю. Я не очень много думал о том, сколько километров от твоего места
до меня, поэтому у меня нет хорошего ответа для тебя.
Б: — More than one banana. Hmm, a tree looks brown with green leaves to me.
— Больше одного банана. Хм, дерево выглядит коричневым с зелеными листьями.
Б: — Yeah that’s right. Are you going? Ok, good evening, afternoon err. morning madame.
— Да, это так. Ты собираешься уходить? Хорошо, добрый вечер день полудня
утром мадам.
Ч: — What is my name?
— Как меня зовут?
Ч: — Were i am situated?
— Где я нахожусь?
И
лл. 103. Фрагмент диалога с Mitsuku, диалоговой программой второго типа.
И
лл. 104. Mitsuku помнит месторасположение и имя собеседника.
Что в этом диалоге было полезного? Программа запомнила важные вещи: имя собеседника
и название его географического положения. Верно ответила на два тестовых вопроса (о цвете
неба и льва) и изящно увернулась от обсуждения других, при этом, следуя некоторой логике,
сценарию разговора.
Теперь перейдем к следующему типу программ.
Голосовой помощник
Помимо некоторого функционала первого и второго типа, они активно используют
поисковые алгоритмы, в том числе, достаточно сложные — для прокладывания маршрута,
заказа такси или пиццы; кроме того они умеют использовать прикладные программы для
несложных задач — чтения и отправки электронной почты, голосовых вызовов, установки
будильника, вызова карты и т. п.
И
лл. 105. Презентация возможностей голосового помощника «Алиса», от компании «Яндекс».
Приведем диалог с голосовым помощником «Алиса», которому мы задали схожие вопросы.
В чем-то она нас порадовала, а в чем-то огорчила. Она верно ответила на вопрос о цвете неба
(правда переадресовав вопрос), и не вполне верно — о цвете льва, так же положившись
на информационную ссылку на некий ресурс.
И
лл. 106. «Алиса» начинает диалог достаточно оптимистично.
И
лл. 107. Из серии предметных вопросов «Алиса» ответила только на простой
арифметический. Продолжение диалога с «Алисой» не приносит результат. Она
не запоминает имени собеседника и не может отгадывать загадки.
Подведем небольшое резюме. Вопросы типа «Найди лишний объект» или «Найди общее»
ставят голосового помощника в тупик, и он переадресовывает их установленному
поисковику. Помощник не запоминает имени пользователя (по крайней мере
в десктоп-версии). В этом «Алиса» схожа с простым ботом первого типа. Конечно же,
невозможно обойти в этом вопросе и давнего конкурента «Алисы», голосового помощника
от компании «Apple», приложение к iPhone, «Siri». На следующей иллюстрации —
некоторые презентационные возможности «Siri».
И
лл. 108. Примеры полезных функций голосового помощника «Siri» от Apple.
Но на практическом прогоне нашего теста на понимание смысла приложение показало
далеко не лучшие результаты (Илл. 109—111).
Иллюстра
ции 109—111. Диалог с «Siri».
Плюс «Siri» оказался в способности открыть калькулятор для простого вычисления.
А за акулой — она отправила нас в ближайший рыбный магазин. Однако на вопрос-загадку
голосовой помощник не только не ответил, но и стал задавать дополнительный вопрос; то же
произошло и при попытке представиться. Кроме того, многие вопросы «Siri» была
не состоянии даже распознать, так что задать корректно вопросы «Пара сложение тройка
сколько будет?» и «Какого цвета неба?» не представилось возможным.
Из этого мы можем сделать печальный вывод, что современные диалоговые системы
в своем роде полезны, но чрезвычайно далеки от адаптивного, корректного понимания
смысла.
И
лл. 112. Механический калькулятор «Alpina», созданный инженером Окаром Милднером
в 1958 году, едва ли кто-то называл «Искусственным интеллектом» — по причине его
доступности и «овеществленности».
Не исключено также, что люди привыкли ставить вызовы искусственному интеллекту,
не называя таковым уже то, что было реализовано с помощью, например, механических
средств.
Так в понимании этого термина появились некоторые дополнительные критерии.
Во-первых, на принятие решений в системе должен влиять не один, а несколько (или много)
факторов, которые заранее сложно или невозможно предусмотреть; а во-вторых, электронная
система должна уметь самообучаться, совершенствоваться без участия или при
минимальном участии человека. Но и в этом подходе есть значительный пробел. С такой
позиции мы не можем рассматривать даже самого человека как интеллектуальную систему,
ведь на его обучение уходят годы и десятилетия, прежде чем появится возможность говорить
о наличии у него интеллекта. Поэтому нам следует сделать скидку на то, что
интеллектуальная система может длительное время обучаться с помощью учителя или
большой выборки обучающих корпусов, как в случае с искусственными нейросетями.
Между тем, с точки зрения обычного человека ассоциативные реакции рождают
несколько иное понимание. В первую очередь нам вспоминаются известные и яркие фильмы,
в которых авторы сценария воплотили свое видение этого феномена. Постараемся вспомнить
некоторые из них, наиболее известные.
И
лл. 113. Терминатор, персонаж одноименного фильма «The Terminator», 1984 г. Режиссер
Джеймс Кэмерон.
1. Терминатор.
Предыстория: Киборг прибывает в настоящее время из мрачного будущего, в котором
человечество оказалось в угнетении со стороны жестоких машин, поставивших себе целью
уничтожение всех людей на планете. В 1984 году у терминатора есть цель — уничтожить
мать будущего лидера человеческого сопротивления, Сару Коннор.
Проанализируем, какими человеческими (антропоморфными) и нечеловеческими
качествами наделили его авторы.
Человеческие качества: Терминатор умеет ориентироваться на местности и разделять
текущие задачи и главные. Например, его текущая задача — найти одежду и оружие,
а главная — уничтожить Сару Коннор. В процессе поиска одежды и оружия он также строит
и промежуточные задачи: обороняться, нападать, вести переговоры и не выделяться
в социальной среде. Он умеет планировать свои действия и успешно коммуницировать,
адаптироваться в социуме с целью достижения главной задачи. Умеет распознавать
образную информацию (например, рукописный текст в записной книжке).
Нечеловеческие качества: Отсутствие эмоций и необходимости в питании. Наличие
чрезвычайной силы и выносливости. Умение имитировать любой голос и управлять любой
техникой без опыта управления.
И
лл. 114. Демотиватор с использованием легендарной фразы компьютера Hal 9000 из фильма
«Космическая одиссея 2001» (1968 г., режиссер Стэнли Кубрик): «Мне жаль, Дейв. Я боюсь,
что не могу это сделать».
Компьютер «Хал 9000» из фильма «Космическая одиссея 2001» — суперкомпьютер,
управляющий космической экспедицией к Юпитеру.
Предыстория: Компьютер ошибочно оценивает угрозу для работоспособности одного
из модулей корабля. Но проверка людьми показывает, что модуль в порядке, — т.е. они
считают, что Хал допускает ошибку, что подтверждается при симуляции на аналогичном
компьютере на земле.
Поскольку компьютер управляет всеми функциями корабля, для предотвращения
возможной катастрофы участники команды принимают решение отключить компьютер.
Но сам Хал убежден, что не может принимать неверных решений и ошибку могут допустить
только люди.
Когда он понимает, что люди приняли решение об его отключении, то воспринимает это
как личную угрозу и начинает обороняться, — принимает решение убить всех членов
экспедиции для обеспечения собственного выживания.
Вот его рейтинг качеств:
Человеческие качества: — Умение адаптироваться к жизни в человеческой среде.
Высокая самооценка, самолюбие. Способность обманывать. Безжалостность
и изобретательность. Способность жертвовать второстепенными задачами (как например,
жизнью команды). Умение планировать.
Нечеловеческие качества: — Сверхнавыки: способность к беспроигрышной игре
в шахматы.
И
лл. 115. Персонаж с искусственным интеллектом Дэвид. Кадр из фильма «Искусственный
разум». 2001 г, режиссер Стивен Спилберг.
Искусственный мальчик Дэвид из фильма «Искусственный разум».
Предыстория: Действие происходит в недалеком будущем. В семье инженера
случилась беда: его единственный сын впал в кому из-за серьезной травмы. В то же время
на предприятии, выпускающем роботов, решили попробовать производить новую линейку
роботов-детей, испытывающих эмоциональную привязанность к родителям. Робот Дэвид,
опытный образец этой серии, оказывается в семье. И все бы хорошо, но настоящий сын
инженера выздоравливает, начинает ревновать и строить козни своему искусственному
конкуренту. В результате серии недоразумений электронный мальчик оказывается на улице
и ставит перед собой единственную цель, чтобы его мама полюбила его. Преодолев
множество препятствий и спустя 10 000 лет при самых странных обстоятельствах, Дэвид
достигает своей цели.
Человеческие качества: Эмоциональная привязанность. Социальная
адаптированность, отзывчивость. Целеустремленность.
Нечеловеческие качества: Способность жить вечно.
И
лл. 116. Кадр из фильма «Я, робот» («I, robot», 2004 г. Режиссер Алекс Пройас).
Робот Санни из кинофильма «Я, робот».
Предыстория: Разумные роботы прочно вошли в жизнь людей, как универсальные
помощники и успешно проникли во все сферы жизни. Поведением всех роботов управляет
функция универсальной защиты — три закона робототехники.[4] Однако ситуация выходит
из-под контроля и Вики, главный компьютер монополиста по производству роботов
компании «USR», принимает решение обеспечить безопасность людей с помощью
тоталитарного управления. Начинается революция, в которой роботы компании пытаются
полностью захватить власть над людьми. Необычный робот Санни, не имеющий в своей
операционной системе функции трех законов и системы получения обновлений, помогает
людям одержать победу над Вики и ее армией роботов.
Человеческие качества: Эмоциональный, сочувствующий. Любознательный.
Адаптивный в социальных отношениях. Видит сны. Интуитивно реагирует
на обстоятельства.
Нечеловеческие качества: Сверхсила и скорость.
Генерация волны
Наша техническая задача на данном этапе состоит в том, чтобы при поступающем на вход
предложении на естественном языке система определяла многозначные элементы
и генерировала исчерпывающее количество вариантов закодированных предложений.
Рассмотрим, как превращается в волну предложений пример, рассмотренный выше (См.
илл. 117).
И
лл. 117. Из фразы «Мы подошли к развалинам старого замка» генерируется 16 предложений
волны. 16, а не 8, — поскольку слово «подошли» тоже трактуется двумя смыслами,
соответственно 2х8=16. Так из одного предложения на естественном языке было
сгенерировано 16 уникальных предложений на «языке» MSM с использованием различных
смыслов всех упомянутых слов.
Как вы помните, при сохранении дополнительных вариантов тех или иных значений слов,
мы добавляем к оригинальному написанию слова символы «|» и их число растет
в зависимости от количества добавленных значений. Из всех возможных генераций
различных смыслов рождаются такие «волны предложений».
Следует рассмотреть эту процедуру более предметно, поэтому приведем ее листинг
полностью.
Илл.
118. Листинг процедуры Readwave, генерирующей «волну предложений» из исходной фразы
на естественном языке.
Как видно, здесь мы использовали несколько еще незнакомых процедур: Formingtemp,
Findvariants и Randomcount с вложенными функциями Countit и Getcount.
Приведем также и их.
И
лл. 119. Листинг процедуры Formingtemp, которая создает временный словарь, который
затем и используется вместо обычного словаря. Это делается для ускорения работы всех
процессов, использующих словари слов-множеств.
И
лл. 120. Листинг процедуры Findvariants, генерирующей множества вариантов отдельных
значений слов-множеств.
И
лл. 121. Вспомогательная процедура countit и функция getcount, которые используются
в процедуре Randomcount.
И
лл. 122. Листинг процедуры Randomcount, производящей смешанные значения из исходных.
Теперь, поиграем с процедурой Readwave, составив небольшую программу.
И
лл. 123. Листинг программы с тестовой демонстрацией возможностей процедуры Readwave.
Посмотрим еще раз, как происходит генерация волны на практике с другими фразами.
И
лл. 124. В тестовой процедуре цветом выделены исходные фразы, ниже — генерация волны
предложений. К примеру, здесь в одном из значений фразы «Твои слова убивают меня»
самое длинное предложение обозначает что «Твои слова расстраивают меня»; вероятно,
данный смысл здесь и можно считать самым верным.
Рассмотрим логическую последовательность действий, происходящих в процедуре
Readwave:
A: «Читаем» предложение по словам, по умолчанию взяв первые значения слов.
B: Отмечаем наличие дополнительных значений, при их наличии.
С: В случае наличия дополнительных значений, генерируем предложения где значения
выбираются в случайном порядке.
D: Дополнительные значения берутся также и из отдельного словаря синонимов
в случайном порядке.
E: Если полученного в результате генерации предложения из кодов нет в списке
результатов, то добавляем его туда.
F: Повторяем процесс генерации до тех пор, пока из него не перестают «вываливаться»
новые предложения.
Вам может показаться, что вариантов может оказаться слишком много, но на самом деле
это не так. На практике большинство предложений «размножаются» не более чем
до 8—16 элементов, и лишь десятые доли процентов достигают значения 32-х. Так что их
анализ нельзя назвать «взрывом» или чем-то подобным; анализ «смысловой волны», как ее
можно назвать, получается достаточно рядовым, хотя и вероятностным явлением.
Говоря «вероятностное» здесь имеется ввиду две вещи.
Первая, это вероятность, хотя и ничтожно малая (примерно 1 к 800 000) того, что
нужный смысл не будет определен в качестве предположения.
Вторая — это вероятность «взрыва» числа вариантов, при котором выбор нужного
варианта будет затерян в огромном числе вариантов. Как могут сложиться предпосылки
такого прогноза? По существу, такая вероятность невелика. Как я уже говорил,
в большинстве предложений русского языка используется по 1—2, редко — 3, крайне
редко — 4 слова с несколькими значениями. Поэтому единственной возможностью
составить «невозможное» предложение с большим числом многозначных слов и их
синонимов остается тогда, когда это делается специально. Теоретически возможно, что
некий джентльмен с целью сломать систему специально впишет в предложение
20—30 многозначных слов, и составит из них вопрос для такой системы. А что такое
30 полисемичных слов, даже если они имеют только по 2 значения? Это 2 в 30 степени или
1 073 741 824 варианта предложений, каждое из которых требует анализа. Такое число
вариантов едва ли сможет «проглотить» и обработать за сколько-нибудь быстрое время даже
весьма мощный суперкомпьютер. И это может оказаться слабым местом такой
интеллектуальной системы, поэтому нам следует заранее предусмотреть и подобный
сценарий развития событий. Возможно, следует просто ограничить число вариантов
фиксированной цифрой, например, 40 или 100. Что же, если их больше, — со значительной
долей вероятности можно сделать вывод, что системе кто-то пудрит мозги.
Возможно, описанный алгоритм несовершенен и достаточно медлителен. Но он
работает.
Кстати говоря, помочь в уменьшении числа анализируемых предложений «волны»
может предварительный фразеологический анализ. Действительно, если нам понятно, что
в предложении использована поговорка «Делу время — потехе час» нам нет необходимости
производить анализ этой фразы, чтобы понять, использовалось ли слово «дело» как
«канцелярская папка» или как «труд», мы можем просто заменить ее на фразу «Работа — это
хорошо» и ничего не потеряем. «Если что-то можно упростить — упрощай». Это —
основной принцип, которым можно руководствоваться во фразеологическом анализе текста.
Конечно, русский язык чрезвычайно богат различными фразеологизмами. Но встретив
«Яблоко от яблони недалеко падает» — можно «перевести»: «Он такой же как родители»
или «Она такая же, как ее мать». «Семь бед — один ответ» — переводим «Одна причина
всех несчастий», и так далее.
Нам нет необходимости из одной сложности порождать другую, практическую пользу
будет иметь лишь упрощение этой сложности. Если что-то мы может пересказать короче
и недвусмысленно, то нужно это делать.
Но вернемся к более актуальной на данный момент проблеме.
И
лл. 126. Процедура преобразования строки в имя файла из трех символов в английской
транслитерации.
И
лл. 127. Процедура Morpheus, создающая из одного словаря базу из нескольких тысяч
распределенных словарей.
При обработке данных в процедуре Morpheus преимущество отдается наименьшему
числовому коду в полученном блоке. Если перед нами единственное число,
существительное, мужской род, оно ставится первым в списке строки. Описательный код
следует за записью слова. Так формируется запись типа: «ШОМПОЛ
1111200000 ШОМПОЛА 1121200000 ШОМПОЛУ 1131200000 ШОМПОЛОМ
1151200000 ШОМПОЛЕ 1161200000».
И
лл. 128. Фрагмент одного из созданных распределенных словарей, файл с названием "_KS»,
содержащий все известные слова и их словоформы, начинающиеся на «ЭКС». Объем
файла — 136 Kb.
При смене буквенного кода из 3 символов, создается новый файл. Так в файлы
распределенных словарей в компактной форме сохраняются морфологические формы и их
типовые описания. Итоговое число словарей составляет около 6000, по количеству вариаций
наименований из 3 первых букв слов.
Однако и этого оказалось недостаточно для создания наиболее полной генерации
потомков из производных форм слов, поскольку линии производных тоже являются своего
рода формализацией, основанной на правилах русского языка.
Распределенные словари морфологии дарят нам многие бонусы. В первую очередь, мы
имеем возможность получать морфологическую кучу слов из одного слова с помощью
процедуры Findmorfs.
И
лл. 129. Листинг процедуры Findmorfs для получения массива морфологических форм
с помощью распределенных словарей.
Кроме того, теперь мы получаем возможность добавлять новые слова в словари множеств
MSM с помощью процедуры Addwithmorfs.
И
лл. 130. Листинг процедуры добавления слов-множеств с помощью словоформ
распределенных морфологических словарей Addwithmorfs.
Между тем, словарь в результате содержит достаточно полное описание количества форм;
просто эти записи зафиксированы в разных строках словаря.
Для перебора дополнительных форм и генерации наиболее полного числа потомков
применялась и процедура Wordsextractor, носящая следующие функции:
— На стадии первоначального анализа слова, производится перебор максимума
возможных окончаний. Первичный приоритет отдается словам с окончаниями: «-ое», «-ого»,
«-ем», далее «-ии», «-ая», «-сть», «-ые», и тд. (Полный список окончаний, хотя он может
быть и не исчерпывающим, можно посмотреть в упомянутой выше процедуре). Исходя
из приоритетности окончаний, первый вариант становится главным в линии потомков. (То
есть по умолчанию в соответствии с этим принципом «Существенное» станет родителем,
слова-множества «Существенный», «Острота» станет родителем для «Острый», «Острая»,
и т.д.)
— Генерация возможных вариантов проверяется на наличие в морфологическом
словаре, также проверяется на наличие дубликатов (которые устраняются) и сохраняется
в выходном массиве процедуры.
— Впоследствии, данная генерация вариантов словоформ в виде массива списка
предлагается для проверки и формирования линии потомков слов-множеств в основных
словарях.
И
лл. 131. Процедура обнаружения дополнительных словоформ Wordsextractor, дополняющая
возможности Findmorfs.
Таким образом, при создании записей в словаре, предлагая для записи 1 слово, мы можем
получить до 20 новых записей вариантов его потомков. Эти дополнения оказались уже
достаточным прогрессом для «ручного» введения данных, но лишь небольшим элементом
для решения задачи ускоренной автоматизации дополнения базы основного словаря. После
длительного периода «ручного» введения, когда объем данных основного словаря достиг
около 20 000 наиболее употребимых слов, можно было приступать к созданию процедур,
обрабатывающих текстовые данные толкового словаря.
Но сначала требовалось произвести удобоваримую формализацию этих данных,
поскольку записи на естественном языке едва ли были доступны для полномасштабной
работы над ними. (Например, неформализованная запись: «ДОБРОЖЕЛАТЕЛЬНЫЙ, -ая,
-ое; -лен, -льна. Желающий добра, готовый содействовать благополучию других,
благожелательный. Доброжелательные люди.» Подобный текст требовал значительной
предварительной переработки.)
Итак, что же было сделано. С помощью дополнительной обработки, удалявшей
сокращения, спецсимволы, блоки примеров и склонений, был сгенерирован новый словарь,
где та же запись в нормализованном виде выглядела так: «ДОБРОЖЕЛАТЕЛЬНЫЙ —
ЖЕЛАЮЩИЙ ДОБРА, ГОТОВЫЙ СОДЕЙСТВОВАТЬ БЛАГОПОЛУЧИЮ ДРУГИХ,
БЛАГОЖЕЛАТЕЛЬНЫЙ.»
Причем, в случаях, когда в словаре описывались несколько значений, в блоке описаний
одного и того же слова, программа перегенерировала их в ряд коротких описаний
отдельными строками. Например, запись значений слова «добрый» в оригинале словаря
выглядела так:
«ДОБРЫЙ, -ая, -ое; добр, добра, добро, добры и добры. 1. Делающий добро другим,
отзывчивый, а также выражающий эти качества. Добрая душа. Добрые глаза. Он добр ко
мне. 2. Несущий благо, добро, благополучие. Добрые вести. Доброе отношение. 3. Хороший,
нравственный. Добрые дела, 4. Дружески близкий, милый. Наши добрые друзья. Мой д.
знакомый. 5. Хороший, отличный. В добром здоровье кто-н. (вполне здоров). Д. конь. Д.
товар. Д. молодец. Добрая традиция. Доброе старое время (о прошлом; ирон.). Оставить
по себе добрую память. 6. Безукоризненный, честный. Доброе имя. 7. Действительно такой
большой, не меньший, чем то, что указывается существительным или числительным
(разг.). Осталось добрых десять километров. Съел добрых полбуханки. * Будьте добры,
будь добр — 1) форма вежливого обращения с просьбой. Будьте добры, позвоните позже; 2)
выражение подчеркнутого и настойчивого требования. Будь добр, оставь меня в покое.
Доброго здоровья приветствие при прощании с пожеланием благополучия. В добрый час!
пожелание удачи. В добрый путь — пожелание отъезжающему, а также (перен.)
пожелание успеха в каком-н. начинании. Добрый малый (разг.) — о неплохом человеке.
Добрый день (вечер), доброе утро — приветствие при встрече. Люди доброй воли (высок.) —
честные, прямые люди, искренне стремящиеся к миру, к благу народа. По доброй воле — без
принуждения, по своему желанию. Чего доброго, вводн. сл. (разг.) — возможно, пожалуй
(при ожидании неприятного). Еще, чего доброго, явится с визитом. II сущ. доброта, -ы, ж.
(к 1 знач.). ДОБРЯК, -а, м. (разг.). Добрый человек. II ж. добрячка, -и. II прил. добряческий,
-ая, -ое.»
И
лл. 132. Исходный толковый словарь, подлежащий переработке.
В результате, этот текст был переработан на удобоваримый список из 6 записей.
«ДОБРЫЙ — ДЕЛАЮЩИЙ ДОБРО ДРУГИМ, ОТЗЫВЧИВЫЙ, А|| ТАКЖЕ
ВЫРАЖАЮЩИЙ ЭТИ КАЧЕСТВА.
ДОБРЫЙ — НЕСУЩИЙ БЛАГО, ДОБРО, БЛАГОПОЛУЧИЕ.
ДОБРЫЙ — ХОРОШИЙ, НРАВСТВЕННЫЙ.
ДОБРЫЙ — ДРУЖЕСКИ БЛИЗКИЙ, МИЛЫЙ.
ДОБРЫЙ — ХОРОШИЙ, ОТЛИЧНЫЙ.
ДОБРЫЙ — БЕЗУКОРИЗНЕННЫЙ, ЧЕСТНЫЙ».[3]
И
лл. 133. Фрагмент адаптированного, переработанного словаря Prepoj. txt.
Этим занималась процедура Textprepare.[4]
И
лл. 134. Textprepare, занятая подготовкой удобоваримого текста для упрощенного словаря.
Не стоит скрывать, что внешний вид методов такой обработки оставляет желать лучшего,
но нам ведь важен результат, не так ли?[5]
Также здесь участвовала еще не описанная процедура Delbrackets.
Это очень полезная вещь, занятая корректным удалением текста, находящегося
в скобках. Казалось бы, это просто, если скобок в тексте всего одна пара. Но если их
несколько, алгоритм их удаления значительно усложняется.
И
лл. 135. Листинг процедур Delbrackets и Delkvakets, корректно удаляющих круглые
и квадратные скобки с заключенным внутри текстом.
Но, так или иначе, был подготовлен «нормализованный» толковый словарь, с которым уже
вполне возможно работать, для решения задачи дополнения словарного запаса основных
словарей ALIK и ALIK2.
Теперь можно было приступить к выполнению промежуточной цели, непосредственной
записи «моделей чтения» словаря, основываясь на строках, в которых все слова
предложения — объяснения могли быть переведены в слова-множества на основании
имевшейся базы.
В чем состоит эта идея: мы предполагаем, что объяснение в словаре имеет смысл. Когда
вы видим запись типа «А — B, С D», можно сделать предположение, что одно из слов
оказывается ближайшим родственником определяемого слова в классификаторе. И если мы
сохраняем модель пунктуации с сохранением всей конструкции, описанной
словами-множествами, то объяснение похожих идей, смыслов, или даже просто принцип
организации этой конструкции может дать нам необходимую информацию, если например,
объясняемое слово нам не знакомо.
Самый простой пример «Резак — то же, что нож».
Перевод фразы с включением модели пунктуации и указанием на ключевое слово
(родителя или потомка) может выглядеть так:
«AAADAACAABBAE AAAAAAAAFN AAAAAAABAADAABAI AAAAAAAADB
AAADAACAABBA * -, *5»
Здесь цифра 5 означает что определяемое слово «Резак» (AAADAACAABBAE) является
потомком 5-го слова в этой фразе «Нож» (AAADAACAABBA).
Накапливая значительное количество этой информации, система может выдвигать очень
точные предположения относительно того, в какое место классификатора можно сохранить
информацию о том или ином определении.
Относительно упомянутого примера, зная подобное определение о резаке, система
может выдвинуть предположение относительно другого подобного определения, например
«Зонтик — то же, что зонт». Зная слово «зонт» и не зная слова «зонтик», на основании
положения и сходства, программа предположит, что искомое слово является родственником
«Зонта», 5-го слова в этой фразе.
Но вы можете справедливо поинтересоваться, каким образом должен происходить
выбор, кем станет определяемое слово, запишется в родители, «дяди», «тети» или в «дети»
родственника?
В этом нам поможет такой инструмент, как связь.
Связи элементов
Связь — это код, имеющий длину самого длинного из двух сравниваемых слов-множеств,
который несет в себе информацию о том, как один код может быть преобразован в другой.
Код связи образован вычислением разницы между числовым кодом сравниваемых символов.
Например, связь между двумя кодами AAAA и AAAB (в направлении от AAAA) выглядит
как «AAAB». Но связь между AAAB и AAAA (в направлении от AAAB) будет выглядеть как
«AAA@». Здесь, @- это символ, предшествующий A в таблице ASCII и говорящий о том,
что необходимо перейти от текущего символа («B») к предыдущему («A»).
Если записать этот код в виде числовой последовательности, эта запись имела бы вид
«0 0 0 -1».
Но что мы будем делать, когда столкнемся с ситуацией, где связь определяется между
строками разной длины? Например, «AAA» и «AAAB»?
В процедурах чтения и записи связей Readsum и Createsum происходит следующее.
Здесь мы проводим сравнение строк, используя длину самого длинного и записывая весовую
разницу между ними, а в случае ее отсутствия, симулируем присутствие символа «точки
отсчета», «А».
И
лл. 136. Процедуры Readsum и Createsum отвечают за создание и чтение связей между
словами-множествами.
Точно такой же процесс происходит и в обратной процедуре, Createsum, за исключением
того, что там происходит сложение а не вычитание ординального значения символов.
В качестве теста, процесс нахождения связей и их проверки выглядит так:
И
лл. 137. «Code A» и «Code B» — коды, между которыми проводится связь, «Link A-B» —
получаемый код связи от кода «А» к коду «B» в результате работы процедуры Readsum;
«Check» — строка проверки восстановления кода «B» с помощью кода «A» и кода связи, что
осуществляется с помощью процедуры Createsum* (см. Приложение).
Но в вопросе о связях существуют некоторые оговорки. Предположим, что связь была
корректна для двух слов кода длиной в 4 символа, и нам необходимо ее применить
в прогнозировании поведения кода для слова-множества длиной в 6 символов. Как нам
поступить тогда? В данном случае, «в поведении» связи мы можем предположить, что нас
интересуют изменения, касающиеся «хвоста» слова-множества, и отбросить первые
недостающие символы, применяя изменения к «Хвосту». То есть, мы отбрасываем значения
относительно первых символов и применяем изменения, относительно последних 4. Это
«отбрасывание» только на первый взгляд может показаться некорректным, однако,
в практике оно применяется только в отношении схожих объектов, как «Красный»
(AAAAAAABACALA) и «Синий» (AAAAAAABACADA), «Сладкий»
(AAAAAABABAAAABBA) и «Соленый» (AAAAAABABAAAABDA), и т. д.
На рисунке 138 показан еще один пример работы процедур Readsum и Createsum.
И
лл. 138. Нахождение связей и проверка восстановления кодов из связей слов «Мысленное»
и «Простое»; «Красный» и «Синий»; «Сладкий» и «Соленый».
Как видно из приведенных примеров, близкородственные связи дают
малодифференцированный (наиболее монотонный) код связи. В данном случае код связи
между «Красным» и «Синим» выглядит как AAAAAAAAAAA9A, а код связи между
«Сладкий» и «Соленый» — AAAAAAAAAAAAAACA. Жирным шрифтом — выделены
фрагменты «головы» связи, которые могут быть отброшены в случае применения связи для
кода другой длины (более короткого или более длинного).
По существу, связь оказывается своего рода транспортным средством, которое помогает
восстанавливать или дублировать последовательности строк слов-множеств, что
соответственно, помогает переносить «поведение отношений» между двумя
словами-множествами в определенных ситуациях. И прежде всего для нас представляет
интерес поведения двух родственников, когда один из них является объясняющим
определением, ключевым родственником для другого. Конечно же, для того, чтобы
максимизировать результирующее попадание в нужный код, мы должны соблюдать принцип
комплементарности в организации классификатора. Это значит, что если мы вкладываем
значение «Синий» в обобщающее понятие «Синего», то значение «Красный» мы вкладываем
соответственно в обобщающее понятие «Красного»; так же и значение «Маленький» логично
сделать потомком «Маленькое», когда значение «Большой» оказывается потомком
«Большое». То есть, в идеале, равностепенные понятия должны иметь равное число
символов кода и недалеко расположенного общего предка (а точнее, пра- пра- пра- предка),
располагающегося на расстоянии 2—5 символов от хвоста.
Здесь следует сделать отступление для уточнения терминологии. В лингвистике есть
хорошие термины «препозиция» и «постпозиция», обозначающие расположение одного
слова (или фразы) относительно другого, соответственно, «до» и «после». В данном
контексте, препозицией связимы можем назвать применение связи в направлении кода «А»
к коду «B», а постпозицией связи — направление «В» к «А» (если изначально они
располагались в другом порядке). Таким образом, если нам известна препозиция связи и мы
можем из «А» получить «B», мы можем вычислить и постпозицию связи, — из «B» получить
«А», лишь изменив «знак» направления позиции. То есть, в приведенных выше примерах
с кодами «АААА» и «АААВ» препозицией связи будет «AAAB», а постпозицией —
«AAA@».
Итак, подведем предварительные итоги относительно работы со связями.
— При первичной записи моделей информации о связях, мы работаем, ориентируясь
на необходимую препозицию или постпозицию, в зависимости от требуемого результата.
И здесь возможны варианты, когда один код оказывается длиннее другого, что будет
отражено в записи связей.
— При чтении моделей со связями, в частности, при реконструировании посредством
связи незнакомого понятия, но близкого по родству с известным, при столкновении с кодом,
отличающемся по длине от кода связи, мы отбрасываем (или доращиваем) «голову»
исходного кода, и применяем к нему связь «с хвоста», до момента стабильного совпадения
(«АААА…”). В этом случае мы можем рассчитывать на близкое попадание в искомый код.
Что в данном случае означает «близость»? Она означает высокую вероятность
попадания нового понятия в требуемую ветку, однако не исключает и вероятность ошибки.
Для минимализации ошибок, мы можем отнестись к предположению, выдвинутому
с помощью модели связи, как к приблизительной оценке расположения искомого понятия;
грубо говоря, мы поймем в каком «районе» смотреть, и здесь нам могут помочь:
А) Дополнительный морфологический анализ объектов, расположенных на данной
ветке, ниже, или выше нее. В этом случае мы можем привлечь на помощь метод сложного
процентного сравнения, с целью найти наиболее близкую словоформу, либо словоформу,
являющуюся «представителем» обобщающего понятия, исходя из его окончания. Так мы
определим, какой из объектов уже имеющихся в классификаторе станет с наибольшей долей
вероятности будущим «отцом» незнакомого ранее слова. И это совершенно определенно
увеличивает шансы на успешный подход к полной автономизации системы в будущем.
Б) Записи свойств объектов с описаниями. В этом случае, к описанию незнакомого
слова мы можем отнестись как к форме загадки, и уточнить кандидата на «отцовство»,
статистически коррелируя показания связей и морфологии.
В) Значения-антагонисты, синонимы и весовые значения.
Итак, мы вплотную подошли к перечню задач, которые необходимо выполнить при
полуавтоматическом (как задача-минимум) и автономном (задача-максимум) получении
первичных знаний с помощью толкового словаря.
Начнем с самого начала. Для поиска строк, модели записи которых мы можем брать,
нам понадобится процедура быстрой проверки на наличие всех слов в словарях. Этим
занимается процедура Trylines. Проверяя строки, она возвращает строку проверки,
состоящую из нулей и единиц. В этой строке каждое слово по порядку обозначается
наличием (1) или отсутствием (0) в словаре.
И
лл. 139. Листинг процедуры Tryline, в которую на вход подается строка определения
на естественном языке S, а на выход получаются: — S2 — строка проверки предложения
(в формате «01101111..»»; — Good — указание на соответствие условию (true/false); —
SM80 — массив, содержащий разбитое на слова предложение; и J — количество элементов
этого массива.
В описанном выше алгоритме нам требуется следующая картина состава этой строки S2:
«011111111..» То есть, первое значение строки определения нам должно быть неизвестно,
а все остальные — известны и в этом случае переменная Good вернет значение из процедуры
True, если это соответствие обнаруживается.
Теперь, нам потребуется процедура Writetype, осуществляющая запись моделей
в словарь моделей определений Types.
И
лл. 140. Листинг процедуры Writetype, осуществляющей запись моделей чтения
определений. Здесь S — входная строка на естественном языке, EXIT — ответ
на корректность запроса (true — если запись отсутствует и все в порядке, а false — если
запись присутствует или отсутствуют слова для записи в модели.
Использовать процедуру Writetype для получения первоначального набора моделей,
дополненного волнами предложений можно в небольшой программе по чтению
подготовленного толкового словаря Prepoj. txt. Соответственно, здесь S и S2 — строки, F50:
Text, Exit: Boolean.
И
лл. 141. Мини-программа для формирования первоначального набора моделей определений
с помощью процедуры Writetype.
Что из этого получается: первоначально мы сохраняем словарь моделей, в которой волной
генерируются все возможные смыслы и сохраняются в файл types. txt.
И
лл. 142. Содержимое словаря моделей, сформированное из волны предложений
по определениям в которых известны все слова в классификаторе.
В словаре моделей используется следующий формат записи:
— Фрагмент до символов $$ — частная запись варианта волны, например
ABABACAAAACBJ ABABACAAAACB AAAAAAAAHKIA AAAAAAAAFFEPEA
(Как видно, следующий вариант отличается написанием одного слова.
ABABACAAAACBJ ABABACAAAACB AAAAAAAAHKIA AAAAAAACCBBBBABA)
— Символы в $$ — это код связи в состоянии постпозиции, позволяющий
реконструировать слово — определение из слова-родственника в данном предложении.
— Цифры после символа «*» -порядковый номер родственника в тексте записанной
модели определения.
— В символах ^^ заключается модель пунктуации.
После чего нам следует произвести проверку корректности отдельных определений. Для
этого нам понадобится редактор моделей определений, реализуемый
в процедуре Testmodels, с использованием вспомогательной
процедуры WriteColored, которая будет помогать нам подсвечивать нужные понятия
и процедуры Trycsline, которая будет модифицировать исходное предложение в кодах
в линию предков.
И
лл. 143. Процедура проверки сохраненных моделей Testmodels, использующая
вспомогательную процедуру «подсвечивания» нужных значений WriteColored.
И
лл. 144. Процедура Trycsline обрабатывает исходное предложение из кодов слов-множеств S,
получая S2 — перевод предложения на естественный язык, S6 — генерацию всех предков
предложения S, переведенную на естественный язык.
Работа процедуры тестирования моделей достаточно удобна. В данном случае, в первой
строке — приводится оригинальное предложение, второе — то же предложение, в котором
каждое слово-множество «подрезано» с хвоста, что позволяет уточнять смысловую ветку
нахождения конкретных слов. Каждое предложение тестируется по одному. Цветом
подсвечены слова, на которые указывается связь, а пользователь подтверждает каждый
вариант символом «1», если предложение корректно.
И
лл. 145. Интерфейс процедуры проверки моделей Testmodels.
Здесь пользователь определил верными два варианта из пяти предложенных «волной
предложений». Первые три варианта не должны учитываться в моделях, поскольку в них
были даны некорректные формы употребления слова «самого» (как потомок «большой»
и дважды как «степень»). В данном случае верными формами «самого» становятся
варианты потомков «собственное» и «себя».
Таким образом, сохраняя корректные варианты моделей, мы имеем в распоряжении эталон,
который «вылавливает» корректные варианты генерации волн при выдвижении
предположений.
То есть, с одной стороны, модель предостерегает нас от того, чтобы включить
в «понимание» лишний смысл от волны, а с другой стороны сама волна гарантирует то, что
нужный смысл не будет утерян.
Для этого оптимально использовать механизм предварительного согласования
с пользователем, поскольку некоторые смыслы, сгенерированные волной, могут являться
неадекватными. Модель — должна быть идеальна и лишена лишних смыслов, которые могут
только запутывать систему.
И
лл. 152. Вспомогательная процедура Getword копирует слово на естественном языке
из словаря Alik.
И
лл. 153. Вспомогательная процедура Getcode копирует код из строки словаря Alik.
Теперь произведем сборку всего необходимого в нашей окончательной процедуре этого
рабочего процесса.
И
лл. 154. Процедура Reader, осуществляющая чтения толкового словаря и добавление новых
значений под контролем пользователя.
Как это выглядит? Ниже — скриншот интерфейса рабочего фрагмента программы,
работающего на основе описанной процедуры, (за исключением того, что программа
на данном этапе не читает свойства объектов и не сравнивает весовые свойства
родительского и дочернего понятия для максимизации результата). Тем не менее, даже
в таком, усеченном формате, стратегия оказывается довольно успешной.
И
лл. 155. Программа осуществляет поиск незнакомых терминов в толковом словаре. Строки
«Unknown» — незнакомые слова, «n» — число проверенных морфологических генераций,
полные строки — текущие определения словаря. В данном случае, прочитано определение
«Сопредельный», отсутствующее в основном классификаторе, где на основании словаря
моделей чтения определений и их связей выдвинуто предположение, что родительскими
элементами могут быть слова «Погранична» (вариант 1) и «Пограничный» (вариант 2).
Удачный вариант (2) выбран пользователем, на основании которого записана
морфологическая генерация из 5 потомков — «Сопредельный», «Сопредельного»,
«Сопредельному», «Сопредельным», «Сопредельном».
В данном случае полезно сохранить и второе значение сопредельный как «смежный»,
и ничто не мешает сделать это вручную в параллельно запущенном окне основной
программы.
И
лл. 156. Второе значение «Сопредельный|» указано вручную с отсылкой к родительскому
понятию «Смежный». В данном случае программой сгенерировано 5 потомков, из которых
«Сопредельный|» — прямой потомок от «Смежный», а другие 4 — «Сопредельного|»,
«Сопредельному|», «Сопредельным|», «Сопредельном|» — становятся потомками
«Сопредельный|».
Пока данный алгоритм несовершенен, но в случае статистически удачной работы механизма
и в процессе накопления большой массы моделей, пункты 7 и 9 из задачи реализации
сильной логической машины впоследствии могут быть устранены; то есть, дополнение
словаря новыми знаниями может происходить без человеческого участия.
В процессе реализации механизма запоминания нового мы также применяем
и механизмпростого дополнения морфологическими формами незнакомых слов.
Например, встретив в тексте незнакомое слово «Пришла», находящееся без связи
с определением, программа может проверить, является ли слово одним из известных
склонений. Для этого производится генерация форм данного слова, проверяемая с помощью
морфологического словаря: «Прийти», «Пришли», «Пришел», … «Пришла». Одна
из обнаруженных форм соответствует найденной с помощью генерации из известного
понятия «Прийти». Соответственно, сгенерированные термины, не обнаруженные
в основном классификаторе, мы и должны записать, как потомков известного, а в случае
наличия дополнительных смыслов, продублировать это действие в параллельную ветку.
«Прийти» — «Пришла», «Прийти|» — «Пришла|», «Прийти||» — «Пришла||» и так далее.
Это — дополнительный и более простой путь к увеличению объема знаний классификатора,
который применяется параллельно с основным.
И
лл. 157. Пример поиска новых знаний в адаптированном толковом словаре по типу
алгоритма 2, — простого сравнения морфологии и генерации словоформ, а также
дополнения параллельных веток. Здесь незнакомое слово «Народный», встреченное в одном
из предложений оказалось родственником известного «Народное». Пользователь подтвердил
предлагаемую линию потомков символом «+». (В случае некорректного предположения,
пользователь может перечислить те варианты, которые необходимо исключить из линии
генерации, указав их номера). Также программой обнаружено и второе значение слова
«Народное», — здесь линия потомков сохраняется и в нее с дополнением служебного
символа «|».
Стоит привести и примеры не совсем удачного предположения. На следующем рисунке мы
видим, как программа предполагает, что «Соскучиться» это «Качество» (1) / «Состава» (2) /
или «Состояние|» (3). (Второе значение «Состояния» в данном случае это «Состав»
в значении «Измеряемое»/ «Качество»). И если слово «Соскрести» было определено точно,
то в данном случае можно понять, что программа была сбита с толку пространной
формулировкой и малым набором моделей подобного типа.
И
лл. 158. Пример ошибочного предположения родственной связи понятия «Соскучиться», как
«Состояние|»/ «Качество»/ «Состава».
В случае, обозначенном на иллюстрации, если мы сохраним модель с упомянутой
конструкцией «Соскучиться — прийти в тоскливое состояние, почувствовать скуку», где
ключевой родственник «Скуку», являющийся потомком «Скука», будет использовать связь
с «Соскучиться», то при попадании в похожее определения, например «Обрадоваться —
прийти в возбужденное состояние, испытать радость», связь будет направлена
на ассоциацию «Обрадоваться» с «Радостью». То есть, в данном случае наша проблема
состоит только в недостаточном числе коллекции определений.
И
лл. 159. Возможная схема расположения «вариатора напряженности» в абстрактной модели.
Схематично эта реализация может выглядеть так, как на рисунке 159.
И в данном случае, например, символ «А» не обязательно равен целой единице. Он
также может быть равен, например, 0,8 или 1,2, или даже 0,01. Главное в данном случае —
рамки одной позиции по отношению к другой и их устойчивость. Эти значения, условные
единицы могут быть, например и такими:
0 <1,105 <4,02 <4,43 <4,76 <4,9 <6,03 <8,4 <8,451 <15,2 <16,4 <16,63 <18,2 <18,28 <18,7
<18,82 <19,1 <19,39 <20,06 <20,4 <20,502 <20,611 <22,1 <23,83 <24,1 <24,9 <24,97 <25
(Почему? Наша абстрактная модель может оперировать разностными, условными
потенциалами). Еще раз, о том, что означают эти цифры. Это передаваемый импульс
напряжения или частота сигнала, или особенность соединения, или комбинация всех этих
элементов, — характеристика передаваемого «нейроном» в модели сигнала.
Тогда, каждое поступающее на вход «впечатление», (которое имеет некую разностную
градацию) производит некоторый ответ, вызывает реакцию, идентичную одной
из обозначенных позиций. Тогда, каждая напряженность или их серия будет «поселяться»
по определенному адресу. Например, первое «впечатление» будет равным позиции «А».
Но при получении второго «впечатления», которое похоже на первое «A», следует
«задуматься» о различии. И когда различие в деталях найдено, ему следует «найти место»,
например, в кортикальной колонке, этажом ниже. (Отсутствие различия в данном случае —
только сигнализирует об уже «известном»).
На уровне ниже, вероятно также есть вариант выбора определенного уровня
напряжения.
И
лл. 160. Под управлением вариатора новые значения обнаруживают свою позицию.
(Но у следующих этажей «нейронов» показатели градаций могут различаться. Например,
серия градаций «А» может колебаться в диапазоне от 0,3 до 4,8. Но следующий нейрон
будет «знать» по своему опыту, что «имеет в виду» предыдущий. Так на импульс
4,8 условных единиц он может «понять», что употреблен «А» по его «персональной» шкале
градаций).
Так «этажом ниже» могут «записаться», например «АА» и «AB», как
на иллюстрации 160.
При этом, скорее всего, какой-то «нейрон» может являться носителем длинной серии
таких позиций, а другой — короткой или единичной, что может крайне затруднять
формализацию живого процесса. То есть, например код ABABABFFACDN может храниться
в 3 последовательных нейронах — часть ABABA — в одном, BFFAC — в другом, а CDN —
в третьем.
Можно представить эту модель и несколько иным, но более живым образом.
Проведем мысленный эксперимент. Предположим, у нас есть колония из 24 муравьев,
которая поселилась на теле мертвого слона, качающегося на волнах в море. Для этих
муравьев сама ситуация устроила неординарную задачу:
1. Они не могут уйти со слона, иначе погибнут.
2. Каждый из муравьев вынужден есть слона, но каждый свой обед им нужно
производить очень короткое время, (скажем, 1 минуту), чтобы не увязнуть в теле слона,
и за это время муравей прорывает ход, в котором может разместиться полностью, — так
получается «Этаж».
3. Выйдя на поверхность, муравей умывается, высыхает и через какое-то время сможет
поесть еще раз, но только в ходе, уже «вырытом» в теле слона, (который также успевает
подсохнуть).
4. С поверхности кожи слона им доступен единственный первичный ход — рана в теле
слона, сделанная чайкой, а под кожей — их ходы могут разветвляться.
5. Муравьи не могут перегрызать стенки между ходами, иначе гарантированно гибнут,
но они чувствуют, где тонко и отступают перед опасностью; тем не менее, при этом они
теряют время и остаются голодны.
6. Слон «бездонен», однороден, не «убывает» и не портится. Но муравьи не могут
погружаться слишком глубоко — это удлиняет время их пребывания в теле слона
и оставляет меньше времени на еду. Также, муравьиный ход не может углубляться слишком
глубоко, поскольку плотность нор на глубине многократно повышается.
7. Каждый отдельный муравей не может съесть один «этаж» одновременно с другим —
им мешают челюсти и конечности.
Спустя несколько десятков обеденных циклов муравьи могут сделать для себя
замечательное открытие, выигрышную стратегию. Каждый из них может выбрать для
себя одно направлениеи следовать ему, а также делать «коридоры» определенной длины
(не слишком длинные и не слишком короткие) — в этом случае, он не пересекает ходы
других муравьев и это экономит им время.
И
лл. 161. Самоорганизация ходов муравьев в мысленном эксперименте о слоне и муравьях.
Что мы получаем.
Назовем вход в нору символом А.
Тогда первый подземный этаж, прорытый муравьем «А» получит название «АА»,
второй «ААА», третий «АААА», четвертый «ААААА», и так далее. Второй муравей,
опускающийся в общую нору «А», выроет нору «B», так его первый этаж станет носить
название «АB», второй «ABB», третий «ABBB», четвертый «ABBBB» и так далее,
до момента, например, когда достигнут предел и 9 этаж — «ABBBBBBBBB».
Итак, каждый из них прорыл ходы в соответствии со своим направлением и они
достигли предела плотности ходов «в глубину», когда очередной ход прорыть невозможно,
не наткнувшись на стенку другого хода. Но всем муравьям все еще хочется есть, и нехотя,
избегая голодной смерти, они немного изменяют своей стратегии. Так первым начинает обед
в первом этаже муравья «B» муравей «А» и с удивлением обнаруживает, что под этажом
муравья «B» он может продолжить рыть свои ходы «А». И так же муравей «B», оказавшийся
на этаже «А», может начать рыть ходы «B». Со временем они «привыкают» к таким
переходам между этажами, но каждый все равно продолжает грызть ход в своем
направлении.
Самоорганизованная таким образом естественная структура появилась
«самостоятельно» — как приспособленность к определенным начальным условиям среды, —
даже если этот пример и является только мыслительным экспериментом. Но что мешает
появиться подобной структуре в мозге, где неоткрытые знания мы можем представить как
«мертвого слона», а отдельные элементы мыслей, — как муравьев? Свои мысли мы
привыкли оформлять словами и предложениями, а значит элементы «дерева» ходов могут
являться не только образами, но и словами естественного языка, и в данном случае
география хода будет равна географии смысла. Так новое знание (или «ход»=«смысл»)
не может углубляться слишком далеко от известных «ходов», иначе оно «завязнет» в нем
и «погибнет». И так же, чтобы развиваться, мысли вынуждены постоянно «питаться»
новыми смыслами.
Методология мышления
Чем еще хороши операции с многомерными множествами, это тем, что мы имеем
возможность отслеживать логику решения на любом ее этапе и получать «разумное»
объяснение и соответственно, исправлять его. И это — принципиальное отличие
от поведения искусственной нейросети, результат в которой практически невозможно
коррелировать с исходными данными.
И
лл. 162. Возможный принцип подхода к реализации системы обработки данных.
Здесь возникает вопрос об унификации подхода к реализации правил обработки данных.
На иллюстрации 162 мы можем представить процесс анализа любой дискретной задачи как:
1. Нахождение «ключа вхождения», специфических маркеров, определяющих тот или
иной тип задания.
2. Определение стратегии для применения определенных правил с целью вычисления
решения.
3. Чтение данных и применение выбранной стратегии.
Набор стратегий может быть самым различным, но на первом этапе выбора ключа
вхождения нам также необходимо разделять 3 элемента предложения-задания: собственно,
ключ, условие и переменные. Например, в следующем вопросе: «Найди самого сильного
морского жителяиз них: краб, тюлень, собака, паук, ромашка?» обычным шрифтом
выделен ключ вхождения («Найди из них»), жирным жрифтом выделено условие для выбора
(«самого сильного морского жителя»), а курсивом — переменные для поиска («краб,
тюлень, собака, паук, ромашка»).
Но задание может быть перефразировано, в результате чего ключ вхождения может
быть записан, как «Выбери», «Определи», «Исключи», «Вычисли», «Какой из них», «Кто
из них», «Кто из этих существ», и так далее. На помощь приходит свойство гибкости кодов
множеств, поэтому записав наибольшее число условий из возможных вариантов, ключом
вхождения может оказаться не точное условие, а наиболее близкое к данному условию.
Оформление условия вхождения (для записи «Какой из них») может иметь
следующий вид:
>> AAAAAAAADAA AAAAAAAAAAA AAAAAAAAFGB
Тогда, при вопросе, содержащем «Какие из этих», мы получим фразу-множество,
имеющую значительное попадание в оригинал:
AAAAAAAADAAB AAAAAAAAAAA AAAAAAAAAABBA
И в данном случае, при отсутствии прочих близких условий программа примет
к исполнению ключ, ведущий к стратегии поиска по соответствию переменных условию
задания.
Стоит заметить полезное свойства ключа: определив его нахождение во фразе, мы
можем его удалить из нее и получить набор условия и переменные в «голом» виде. В данном
случае, разделителем условия и переменных является знак пунктуации «:», а указанием
на переменные — символы запятых. Но в любом случае мы можем оперировать лишь
вероятностями, признаками усиливающими или ослабляющими вероятность отношения того
или иного описательного блока к тому или иному объекту описаний.
И
лл. 163. Схематическое изображение фрагмента нити ДНК и его кода в форме символов «a»,
«g», «t» и «c».
Можно взглянуть и шире на окружающий мир. Если бы мы могли «нажать на паузу»
вселенную, как герой фильма «Брюс Всемогущий», то могли бы увидеть нечто
ошеломительное. Используя томограф, мы рассмотрели бы, как отдельные нейроны внутри
чьей-то головы светятся яркими звездочками, оставаясь на одном месте. Они виделись бы
нам материальными. Вероятно, мы бы убедились, что информация материальна. Мы
могли бы изучать их, сравнить друг с другом их заряды… Но вновь «запустив вселенную
на воспроизведение» мы опять рискуем потерять веру в материальность информации, ведь
все происходит слишком быстро и неуловимо.
С позиции материальности информации — все сущее становится на свои места. Каждый
символ, как единица информации, сохраняет свое равновесие. Возможно, меняется его
внешний вид, его характеристика, но не меняется единство, внешняя целостность «атома»
информации.
Если рассуждать в этом направлении, получается, что наша общепринятая концепция
информации абсолютно синтетична и неестественна. Беспорядочные переходы элементов
этой «синтетической» информации из одного состояния в другое совершаются лишь из-за
ограниченности своих условий. Счет и предложения из слов любого естественного языка
на бумаге, как и в любой электронной машине, работают против законов физики; но мы
находимся в этой парадигме информационного представления мира как в ловушке
и не можем выйти из нее.
В природе этого не происходит. Здесь все постепенно. Здесь нет состояния «Ничего»
и состояния «Все», что предлагается двоичной или любой другой системой счисления. Но,
как тогда считывается эта естественная информация, спросите вы?
Примеров можно привести сколько угодно. Допустим, речь может идти об информации,
которую несет электромагнитное излучение. Она может обладать настолько плотной
потенциальной емкостью, что легко приближается к возможностям бесконечной символьной
последовательности. Вспомните об астрономии. Чем занимаются астрономы
со спектральным анализатором, когда ловят излучение далеких звезд? По существу, они
получают единственный символ, атом информации в виде луча света, но расшифровывают
его с помощью спектра, получая колоссальное количество информации — в первую очередь,
о содержании массы различных химических элементов… Не говоря уже, что применяя
дополнительные методы исследования, они получают сведения о расстоянии, приближении
или удалении звездного объекта, его размере, массе, составе атмосферы… и это все может
быть получено из единственного однородного луча света. По сути, единичного символа
бесконечной символьной последовательности, атома информации. Разве это не потрясающе?
Информация не преодолевает законы сохранения энергии, поскольку она и есть материя
и энергия, заключая два состояния в одном. Это то, что изменяется и сохраняется, но не то,
что внезапно исчезает или так же молниеносно размножается.
Какой из всего вышеописанного следует вывод? Информация по своей глубокой
природе соблюдает единство своей материальной составляющей. Любой элемент
информации сохраняет свою целостность, а хорошим алгоритмом обработки информации
должен быть такой, который не нарушает этой целостности, соблюдает условия его
существования.
Помните выражение «Наши мысли материальны»? Похоже, что это не фантазия. Это
буквальное, атомарное состояние нашего мышления. Состояние, которое скорее можно
назвать течением, колебанием, передачей энергии, механикой. Но не двоичным кодом.
Так идея материальности, механистичности информации и идея бесконечной
символьной последовательности идеально дополняют друг друга. Можно сказать даже, что
их брак свершился на небесах. В буквальном смысле.
Но каким может быть практическое применение симбиоза из этих двух идей? Давайте
рассуждать дальше. С увеличением последовательной емкости единицы передачи
информации до числа, приближенного к бесконечности, мы получаем фантастические
возможности.
Кстати говоря, сегодня у нас в распоряжении есть достаточные технические мощности,
чтобы получить симуляцию если не бесконечной, то приближенной к бесконечности
символьной последовательности. Если ее решение окажется воплощено на аппаратном
уровне процессора, она может быть лучшим архиватором документов, сжимающим большие
файлы в десятки, в сотни и даже в тысячи раз. Представьте, что вы могли бы записать
на флешку, емкостью 16 гигабайт, объем данных, эквивалентный одному террабайту?
И главное, вы могли бы читать эти данные без потери скорости.
То же самое может произойти и с процессом передачи данных, который может быть
ускорен в сотни и в тысячи раз. Смартфоны с подобной архитектурой процессора могли бы
позволить настолько снизить нагрузку на базовые станции, что даже 100 000 абонентов,
подключенных одновременно, в одном месте к сети — не почувствуют проблем со связью.
В то же время, мы могли бы повысить надежность зашифрованной передачи данных
настолько, что любые идеи создания квантового компьютера, способного сломать эти коды,
покажутся смешными.
Впрочем, это уже совсем другая история.
Формы языка
Возможно я рискую в чем-то повториться, но мне хотелось бы еще раз остановиться на теме
человеческого интеллекта и его связи с языком. Для начала давайте разберемся в том, что
такое язык? Языком обычно называют систему символов, которая позволяет выражать
некоторые абстрактные понятия и передавать информацию.
Какими бывают языки:
1. Язык жестов, в большей степени основанный на визуальной передаче информации.
2. Язык вербальный, от латинского «Verba» — слова. Те которые образуются звуковыми
и письменными комбинациями.
Что мы можем выразить языком жестов? Глухие люди способны пользоваться языком
жестов достаточно хорошо настолько, чтобы передавать абстрактные, математические
понятия, формулы и сложные описания. Однако глухие, особенно те, которые глухи
с рождения, очень тяжело воспринимают письменную речь: они делают очень много ошибок
и научить их правописанию значительно сложнее. С чем это связано? Письменный и устный
язык состоит из одних и тех же элементов, звуков, которые переносятся на буквы и их
сочетания. Читая, вы мысленно или вслух произносите буквы, которые складываются
в слова. У глухих такой возможности нет. Они не знают как произносятся слова, поэтому им
сложно детализировать написанное слово. Для таких людей каждое написанное слово это
просто некий абстрактный набор элементов, которые только в определенном сочетании дают
какой-то смысл. Слышащие же знакомы с каждой отдельной буквой как с определенным
звуком или его сочетанием, поэтому для них письменная речь дается легче.
Теперь представим картинку. Перед нами два необитаемых острова, на котором мы
оставили двоих разных людей с топором и поставили перед ними простую задачу: построить
лодку. Один из них, это слышащий. Второй — глухой с рождения. Кто из них быстрее
справится с задачей?
На самом деле шансы и у того и у другого будут примерно одинаковы и оба они
одинаково хорошо могут построить лодку. Но есть одно условие, при котором ни тот ни
другой — никогда не построят лодку, даже если они проведут на острове сотню лет.
Ни тот ни другой не построят лодку никогда, если они окажутся там в возрасте
до 3 лет, — даже если вы оставите им подробные чертежи этой лодки и все детали.
И проблема не в том, что их могут съесть дикие звери, или они умрут от голода, утонут,
упадут или покончат с собой от депрессии. Вы можете оставить им много тонн
непортящегося провианта или сбрасывать его с вертолета; вы можете поместить их в теплые
и безопасные дома и оставить в качестве развлечения миллион различных игрушек, вы
можете огородить все побережье и скалы, сделав их безопасными, однако результат будет
один и тот же, — они состарятся и умрут, так и не выполнив задание.
В чем же разница между первым и вторым случаем?
Возраст в разнице результатов — лишь вторичная причина. Основная причина состоит
в том, что устойчивая, настроенная на основе значительного опыта «впитывания»
человеческой культуры и языка «логическая машина» достаточно быстро достигает нужного
результата, тогда как несформированная подобным образом — проваливает эксперимент.
Вам должны быть известны случаи «детей Маугли», которые оказались (некоторые
в возрасте 5 или 7 лет) оторваны от воспитания в человеческой культуре и становились
полностью неспособны решать даже простейшие интеллектуальные задачи.
Самый простой вывод, который следует из этого — интеллект невозможен без языка,
его понимания и осознания, как глубокой, сложной, символьной системы.
И
лл. 165. «Что компьютер понимает.»
На картинках 164 и 165 наглядно показывается, что такое описание традиционной
семантической сети на естественном языке для компьютера.
Что из этого следует? Это означает, что мы неверно описываем семантические сети. Сам
принцип подхода к ним в корне неверен, хотя интуитивно правилен. Письменность может
быть только промежуточным звеном и не может быть указателем. Письменность это ярлык,
внешний образ, который не несет в себе никакой смысловой нагрузки.
Семантические сети хороши для восприятия людей, но не компьютеров.
Промежуточные классификаторы, (каким является классификатор MSM) хорош для
компьютеров, но не для людей. Между тем, людям и не следует пытаться его считывать или
работать в нем. Но обратите внимание, как конструкции, которые мы с детства привыкли
воспринимать очень «глубокомысленными» и сложными, — могут детализироваться
до простых и понятных.
Как, например, поговорки и пословицы могут возвращаться к обыденному смыслу?
Взгляните на иллюстрацию 166.
И
лл. 166. Поговорка «Делу время — потехе час» легко детализируется в набор привычных,
не книжных смыслов.
Здесь мы разобрали «генный состав» поговорки «Делу время — протехе час» с помощью
классификатора MSM. Заметьте, что она может быть вполне адекватно понята, как
необходимость уделять долгое время набору полезных действий и короткое время —
развлечению или увлечению.
Возьмем еще один пример, поговорку «Счастливые часов не замечают».
И
лл. 167. «Генный разбор» состава поговорки «Счастливые часов не замечают».
Что мы видим в этом разборе состава предложения? Буквально на поверхности
обнаруживается ее смысл, в том что в радости, позитиве, время отрицается как видимое
свойство. Ее можно пересказать например, как «Веселый времени не видит», — здесь
сохраняется общий смысл исходной поговорки но и порождается новый, в форме некоторого
обобщения. И это — поразительное качество MSM в переносе и «размножении» смыслов,
которое мы просто обязаны использовать.
Гештальт-цикличность
Несомненно, в своей практической работе мы должны разделять понятия «думать»,
«размышлять», «говорить» и «действовать». Все эти вещи — это не одно и то же. Но есть
в них нечто общее, что в данный момент хотелось бы обсудить, размышляя
о гештальт-психологии и бихевиоризме.
Гештальт — форма объекта, его целостный смысл или завершенный процесс, как
единица сознания, психики.
Подпрограмма 1:
S [1]:= «ВИЧ ‘; s [2]:=«ЫЧ»;
Подпрограмма 2:
S2 [1]:=«ВНА ‘; s2 [2]:=«ЧНА»;
Подпрограмма 3:
S3 [1]:=«Отчество»; s3 [2]:=«Ф.И.О»; s3 [3]:=«ФИО»;
Наша задача — обнаружить совпадение в одном из трех вариантов. Так или подобным
образом может выглядеть запись работы программы с тремя включенными
гештальт-циклами.
I:=0;
Readln (P);
Repeat
Inc (i);
Until (pos (s [i],P) <> 0) or
(pos (s2 [i],P) <> 0) or
(pos (s3 [i],P) <> 0) or
(i> 3);
И
лл. 168. Рисунок из книги Марвина Мински «Эмоциональная машина». Здесь элементы,
называемые «Селекторы» вызывают активность других, которые в свою очередь
активизируют следующих, вызывая крупномасштабный каскад из активных элементов.
Несомненно, идея о работе мозга как о наборе серии различных программ или компьютеров,
работающих в системе сети, перекликается с идеями Марвина Мински,[1] в частности,
в книге «The Emotion Machine». В ней он приводит схемы этого процесса, когда одна
«программа» запускает серию других, — этот процесс он называет «каскадом».
Книга, к большому сожалению, не переиздавалась на русском языке, но ее
англоязычный оригинал доступен в сети.[2]
Метафоры об MSM
В виду общей рабьей складки умов, аллегория всё ещё имеет шансы быть более понятной
и убедительной и, главное, привлекательной, нежели самая понятная и убедительная речь.
Михаил Евграфович Салтыков-Щедрин (1826–1889) — русский писатель
Немного отвлечемся и представим, как мы можем определить место классификатора MSM,
как аналогию чего-либо в современной информационной науке? Отчего бы не попробовать
взглянуть на него языком метафоры?
Метафора 1
Конструктор-вселенная
Представим, что у нас есть конструктор. Например, готический замок. Но необычный.
На каждой детали есть несколько вариантов соединений. Допустим:
Кирпич
На кирпиче написано:
Желтый
Бетон
Строительный
Твердый
Маленький
Большой
Каменный
Песчаный
Рыхлый
…
Много? Но подождите, мы только начали. Теперь представьте, что на одном «кирпиче»
написано несколько тысяч значений. И на каждом из этих значений так же написано
по нескольку тысяч значений. Сложно? Вы думаете, что компьютеру легко перебрать их,
в поиске нужных сочетаний? А ведь именно так выглядит традиционный алгоритм сборки
классов в объектно-ориентированном программировании или семантическая сеть. Есть и еще
вариант, когда на этих же запчастях написаны и дополнительные условия сборки, где
указывается, как необходимо обследовать другие элементы и найти соответствия. Так
устроены и действуют многие экспертные системы.
А теперь предложим другой вариант. Вы вынимаете конструктор из очень большой
коробки, думая, что там лежит нечто еще сложнее. Но к своему удивлению видите, что в нем
находятся 5—7 уже собранных, готовых, соединенных из множества деталей, но больших
блоков, где все сочленения по поверхности комплементарны. У их подножия вы сразу
видите «бетонные» блоки, под ними — прикреплена земля, сверху трава и цветочки, стена,
окна.
Соединив два больших куска — вы сразу получили целую стену кирпичей с бойницами
на верхушке. А видя, что несколько элементов башни несоответствуют рисунку — вы легко
заменяете башню. Так, вам удается собрать весь конструктор, всего в несколько простых
действий, вместо того, чтобы создавать по невероятно сложной и фрагментированной
инструкции вселенную из атомов.
Как вы поняли, первый вариант это некомплементарная конструкция любого
действующего ООП, которое трудно как конструировать, так и упаковывать в одну коробку;
ведь чем больше раздельных деталей, тем больше шанс ошибиться, что-то забыть или
добавить слишком много.
В MSM мы укладываем не атомы кирпичей, мы укладываем последовательности, цепи,
стены из смыслов большими блоками, и не беда, если «кирпичи» сверху — не совпадают,
отсоединив верхний кусок мы легко получим другой, подходящий.
Метафора 2
Лабиринт отчаяния
Теперь представим, что вы оказались в огромном лабиринте. И чтобы выбраться из него,
у вас есть инструкция. В ней несколько тысяч страниц и она непоследовательна. На каждой
странице в ней написан ряд условий в соответствии с которыми вы должны менять
направление движения. Ее фразы оторваны от какой-либо логики и силясь ее понять, вы
только больше сходите с ума. Например: «На правой стене есть мох, внизу слева лежит
кирпич. Когда солнце светит справа в полдень, на стене слева сидит белая бабочка. Под
ногами песок? Поверните налево».
Вы сравниваете обстановку, находите несоответствие и листаете страницы
дальше. «На вас упал паук? Положите его в левый карман. Если паук шевелится, значит
скажите „да“ в мегафон. У вас нет мегафона? Проверьте страницу 511 и если она мокрая,
идите вперед». Страница за страницей вы перечитываете условия вашего движения,
но постоянно находите несоответствия, поэтому вынуждены делать долгие паузы и стоять
на месте. Да еще и книга очень тяжелая, вы сильно устаете. Вы едва ли успеете выбраться
из лабиринта дотемна, и уже почти на выходе столкнетесь, к примеру, с несоответствием.
На очередном повороте сидит жирная мышь и ест сыр. Но в инструкции об этом ничего
не сказано.
Эта толстая, бессмысленная, тяжелая книга полностью выводит вас из себя и вы
начинаете двигаться наугад, забросив ее.
Теперь мысленно перенесемся к началу лабиринта. Но в ваших руках уже другая
инструкция, всего на одном блокнотном листочке. В ней находится одна-единственная
надпись. «Идите в направлении таблички выход». Теперь вы замечаете, что почти на каждом
повороте есть знак «выход» и число в процентах, но также некоторые перекрестки и лишены
этой таблички. Оказавшись на новом перекрестке, (когда на предыдущем не было таблички),
вам остается только сравнить большее и меньшее значение, и отправиться в направлении
увеличения процентного показателя. Здесь вы не связаны с дополнительными условиями
и не успеваете даже устать, как покидаете лабиринт. Вас совсем не смущает жирная мышь
на выходе, она вам не помеха.
Все потому, что условия для вашего путешествия в лабиринте
были организованы до того, как вы оказались в нем. Элементы, (а именно таблички
со словом «Выход» и процентными данными), появлялись на вашем пути, как бы «сами
собой», по причине «предрешенности», вашего направления. Еще до своего появления
каждый «выход» может «найти» длину своего кратчайшего пути, а мы лишь оцениваем
величину расстояния в отношении с другими выходами и принимаем решение, — хорош
этот выход или плох.
Можно было бы обвинить эту систему в «избыточной» простоте. В связи с этим,
вероятность того, что все путешественники по лабиринту пойдут по одному пути, следуя
указателям, будут зашкаливать. Но в этом нет проблемы у системы, есть проблема —
у человеческого восприятия.
Сингулярность неизбежна
Сингулярность — это ситуация при которой общий искусственный интеллект, электронная
машина, станет разумнее среднестатистического человека. Футурологи называют это
явление «горизонт событий» по аналогии с явлениями, протекающими в глубоком космосе
рядом с черных дырами, где перестают действовать законы физики и невозможно
предсказать, что случится дальше. Различные авторы описывают эти события с разной долей
драматизма, но насколько эта ситуация может соответствовать действительности? И здесь
мы можем найти место нечеткости, субъективности в оценках. Прежде всего, нам следует
определиться, по каким критериям мы могли бы оценить разумность? По умению
обыгрывать человека в игры, обгонять по скорости выполнения ручного труда,
контролировать сложные процессы, делать изобретения?
Не стоит забывать, что интеллект — это адекватная реакция внешнему воздействию
с целью улучшения своей позиции, выживания и процветания. Поскольку сценой действий
интеллекта является социум, то отличие его от «глупости» будет состоять не только в жажде
жизни, но и в способности реконструировать чужое поведение, планировать сложные
действия, уметь договариваться и реагировать в соответствии со своим планом и выбранной
стратегией.
Как его можно отличить? Дальше уже зависит, насколько вы требовательны.
«Среднему» интеллекту достаточно одеть ботинки чтобы не замочить ноги, уметь вовремя
купить булку хлеба, чтобы не умереть с голода и не подраться с соседом на 9 мая. Для жизни
этого вполне достаточно. Такому интеллекту пригодится и умение «гуглить» текущие
проблемы и решать их с помощью других людей или электронных систем.
«Сильный» интеллект — это такой, который превосходит способности среднего
человека. Он уже будет способен сам найти себе работу и оформить загранпаспорт,
а по вечерам строчить по 40 постов в соцсетях на актуальные темы, зарабатывая на рекламе.
Может быть и не следует усложнять эти критерии?
Язык =Интеллект
Ноам Хомски — лингвист и нейрофизиолог говорит о том, что язык является не только
неотъемлемой частью коммуникации, но и составляет суть мышления. То есть, информация,
заложенная в смыслах языка, не может только «передаваться», она, в форме языка и является
носителем смысла. Соответственно, мышление невозможно без языка, как и язык без
мышления. Получается, что между интеллектом и языком мы можем поставить знак
равенства.
Еще одна замечательная идея Марвина Мински состоит в том, что принципы, идеи
и методики должны превосходить технологию конкретной структуры и частностей
физических взаимодействий. К примеру, компьютер — это не есть сумма элементов: как то,
конкретной материнской платы, с конкретной памятью, процессором, видеокартой; но —
машина для выполнения задач, построенная на методах обработки информации.
«…Чтобы построить части любой машины, мы можем использовать любое
достаточно сильное и стабильное вещество: все, что имеет значение, — это то, что
делает каждая отдельная часть, и как эти части соединяются. Например, мы можем
создавать разные компьютеры, которые выполняют одни и те же вещи, используя либо
новейшие электронные чипы, либо используя деревянные, струны и скрепки для бумаги,
размещая их части так, чтобы снаружи каждый из них выполнял те же процессы»…[3]
В итоге, для нас не имеет значения, из каких материалов будут сделаны определенные
элементы и микросхемы, на какой частоте работает процессор, с каким напряжением
подается ток на тот или иной транзистор или даже в какой разрядности чисел он работает;
мы могли бы построить отдельные его элементы, выполняющие определенные задачи
из дерева, стекла или пластика, шестеренок или консервных банок, — главное, это
правильно воплотить сам принцип работы для получения конкретного результата. Другими
словами, этот подход может называться так — «не важно, на чем это работает, важно,
что оно приносит результат».
Неуникальное сознание
Заключение
Завершая вторую книгу, посвященную строкам и MSM, хочется поблагодарить отважного
читателя, который перенес все тяготы ее содержания. В утешение предстоящему долгому
расставанию замечу, что на этом все только начинается.
В будущем нас еще ждет путешествие к анализу временных явлений, который позволит
рассматривать каждый объект классификатора, как динамическую модель; к адаптивным
циклам, которые могут стать универсальными «решателями» заданий;
к бихевиористическому поведению моделей; к явлениям «озадаченности», устойчивым
последовательностям и еще многим другим полезным вещам в безграничном мире
символьных последовательностей.
На прощание мне хочется привести одну цитату, которая за много лет не утратила своей
актуальности:
«Программист — это создатель вселенных, вседержителем которых он сам
и является. Ни один драматург, режиссер или император в истории не обладал такой
абсолютной властью при постановке спектакля или на поле боя, и не имел в своем
распоряжении таких неукоснительно преданных делу актеров или воинов.»
Джозеф Вейценбаум, профессор Массачусетского технологического института,
автор программы «Элиза».
У вас уже появилось желание создавать что-то новое? Значит, все это было не зря.
С. В. Соболенко, 11.02.2019 г.