Тема 2. Поиск и логический вывод
В предыдущей теме мы отметили, что символьный ИИ исторически предшествовал машинному обучению. Прежде чем перейти к построению моделей на данных, стоит разобраться в методах, которые составляли ядро ИИ на протяжении нескольких десятилетий, — и которые до сих пор применяются в задачах планирования, конфигурирования и автоматического рассуждения. Речь пойдёт о двух больших классах методов: поиске в пространстве состояний и логическом выводе на основе баз знаний.
Понимание этих методов полезно не только как историческая справка. Алгоритмы поиска лежат в основе навигационных сервисов, игровых движков, систем автоматического планирования. Экспертные системы продолжают использоваться в медицинской диагностике и промышленной автоматизации. Кроме того, ограничения символьного подхода, с которыми мы столкнёмся в конце темы, — это именно те ограничения, ответом на которые стало машинное обучение.
Задачи поиска в пространстве состояний
Формализация задачи поиска
Многие задачи ИИ можно свести к поиску пути в некотором абстрактном пространстве. Рассмотрим классический учебный пример — головоломку «восьмёрка» (англ. 8-puzzle). Имеется поле с восемью пронумерованными фишками и одной пустой клеткой. За один ход разрешается переместить фишку, соседнюю с пустой клеткой, на освободившееся место. Требуется привести фишки в стандартный порядок. На таком масштабе пространство достижимых конфигураций обозримо — состояний, и в учебных схемах далее мы пользуемся именно «восьмёркой» для наглядности. Её старший вариант, «пятнашки» (англ. 15-puzzle) на поле с пятнадцатью фишками, даёт уже состояний — на этом масштабе полный перебор исключён.
Чтобы решить эту задачу алгоритмически, её нужно формализовать. Обозначим конфигурацию поля как состояние (англ. state). Начальная конфигурация — начальное состояние , упорядоченная конфигурация — целевое состояние . Каждый допустимый ход — это оператор перехода, переводящий одно состояние в другое. Множество всех достижимых состояний вместе с переходами между ними образует пространство состояний (англ. state space).
Формально задача поиска задаётся четвёркой: где — множество состояний, — начальное состояние, — множество операторов (действий), — множество целевых состояний. Решением является последовательность операторов , переводящая в некоторое .
Пространство состояний естественно представляется в виде графа: вершины — состояния, рёбра — допустимые переходы. При этом алгоритм поиска обычно работает не с полным графом (он может быть слишком велик или даже бесконечен), а строит дерево поиска (англ. search tree) «на лету», раскрывая вершины по мере необходимости. Корень дерева — начальное состояние; дочерние вершины порождаются применением операторов; лист (англ. leaf) — вершина без детей, которая либо ещё не раскрыта алгоритмом, либо является тупиком, у которого нет применимых операторов (рис. 2.1).
Слева на рисунке — фрагмент графа пространства состояний: начальное состояние и четыре его соседа по одному ходу (B, C, D, E). Рёбра графа двунаправленные: любой ход «восьмёрки» обратим — фишку, передвинутую в пустую клетку, можно тут же передвинуть назад. Справа тот же фрагмент развёрнут в дерево поиска из корня . Среди потомков корня снова появляется (красный лист в легенде), и это прямое следствие обратимости ходов: каждый ребёнок корня — это плюс один ход; среди четырёх возможных ходов из этого ребёнка один обязательно возвращает обратно в . На рисунке такие дубликаты показаны под B и E; под C и D они точно такие же (там, где в дереве стоит «…»). Если алгоритм не ведёт список уже посещённых вершин, дубликаты раскрываются повторно, и дерево раздувается в разы по сравнению с самим графом.
Чтобы оценить, как масштабируется такой перебор, удобно сравнить «восьмёрку» с «пятнашками»: 181 440 против — отличие в семь порядков при увеличении поля всего на одну строку и один столбец. Это типичная картина для задач поиска: пространство состояний растёт комбинаторно от размера задачи, и выбор стратегии обхода дерева оказывается решающим уже на скромных масштабах.
Алгоритмы поиска оценивают по четырём критериям:
- Полнота (англ. completeness): гарантирует ли алгоритм нахождение решения, если оно существует?
- Оптимальность (англ. optimality): находит ли алгоритм решение с минимальной стоимостью?
- Временная сложность (англ. time complexity): сколько вершин алгоритм раскрывает в худшем случае?
- Пространственная сложность (англ. space complexity): сколько вершин алгоритм одновременно хранит в памяти?
Дальнейший анализ алгоритмов опирается на три параметра дерева поиска. Фактор ветвления — среднее число дочерних вершин при раскрытии. Глубина решения — длина кратчайшего пути от корня до целевой вершины, то есть число ходов в оптимальном решении. Для «восьмёрки» полным перебором установлено, что : никакая решаемая начальная конфигурация не требует больше 31 хода до цели. Максимальная глубина дерева — длина самой длинной ветви, по которой алгоритм может спуститься; в графах с циклами без отсечения повторов она может оказаться сколь угодно большой.
Неинформированный поиск
Неинформированные (слепые) алгоритмы не используют никаких сведений о том, насколько текущее состояние «близко» к цели. Они различаются лишь порядком, в котором раскрывают вершины.
Поиск в ширину (англ. breadth-first search, BFS) обходит дерево слоями. Глубина вершины — это длина пути от корня до неё: корень имеет глубину 0, его дети — глубину 1, их дети — глубину 2, и так далее. BFS сначала раскрывает корень, затем все вершины на глубине 1, затем все на глубине 2, и пока слой не разобран до конца, к слою алгоритм не переходит (рис. 2.2). Реализуется это очередью FIFO (англ. first-in-first-out): новые порождённые вершины кладутся в хвост очереди, а на раскрытие берётся та, что стоит в голове, — то есть попавшая туда раньше всех. Поскольку дети любой вершины оказываются в очереди позже самой вершины, послойный порядок выдерживается автоматически.
BFS полон: если решение существует, оно лежит на какой-то конечной глубине, и алгоритм гарантированно до неё доходит — он не оставляет ни одного слоя необработанным. BFS также оптимален при одинаковой стоимости переходов: если каждый ход стоит одинаково (например, любой ход в «восьмёрке» — это один шаг), то первое же найденное решение имеет минимальное число шагов; алгоритм просто не успевает спуститься глубже, пока на текущем слое есть нерассмотренные вершины. Если стоимости разные (скажем, длины участков дороги в задаче навигации), это уже не гарантировано: путь с наименьшим числом ходов не обязательно самый дешёвый, и BFS перестаёт быть оптимальным.
Платит BFS за свои гарантии памятью: на глубине при факторе ветвления в очереди может оказаться до вершин. Для «восьмёрки» с и оптимальной длиной решения хранение нескольких десятков миллионов вершин ещё допустимо в памяти настольной машины. Для «пятнашек» с и оценка выходит за пределы реалистичных ресурсов — BFS становится практически неприменимым.
Поиск в глубину (англ. depth-first search, DFS) идёт в противоположном направлении. Алгоритм раскрывает вершину и сразу переходит к одному из её детей, углубляясь по выбранной ветви, пока не достигнет листа или тупика; только после этого возвращается к ближайшему непросмотренному ветвлению и продолжает оттуда (рис. 2.3). Реализуется это стеком LIFO (англ. last-in-first-out): новые порождённые вершины кладутся на верх стека, а на раскрытие берётся та, что положена туда последней — самая «свежая». Естественной альтернативой стеку служит обычная рекурсия: рекурсивный вызов сам кладёт состояние на стек выполнения, а возврат из вызова — снимает.
В отличие от BFS, DFS не полон в общем случае: если в пространстве состояний есть бесконечная ветвь (например, из-за неотсекаемых циклов), алгоритм может уйти в неё навсегда и так и не добраться до решения, даже если оно существует на конечной глубине. Оптимальность тоже не гарантируется — даже если решение найдено, оно может оказаться сколь угодно длиннее оптимального: DFS возвращает первое решение, которое попалось, а оно лежит на той ветви, куда алгоритм случайно свернул раньше других.
Зато по памяти DFS радикально экономнее BFS: на стеке в каждый момент хранится только текущая ветвь и не раскрытые ещё соседи каждой её вершины — в худшем случае , где — максимальная глубина дерева. Для «пятнашек» это уже терпимо. Поэтому в задачах с гигантским пространством состояний DFS остаётся применимым там, где BFS не помещается в память, — ценой потерь в полноте и оптимальности.
На практике выбор между BFS и DFS — это выбор между гарантией результата и расходом памяти. Существует ли компромисс?
Поиск с итеративным углублением (англ. iterative deepening depth-first search, IDDFS) пытается совместить достоинства BFS и DFS 1. Алгоритм выполняет серию запусков DFS с возрастающим ограничением глубины: сначала просматривает только корень (предел 0), затем корень и его детей (предел 1), потом — до глубины 2, и так далее, пока на каком-то пределе не найдёт целевую вершину (рис. 2.4). Каждая отдельная итерация — это обычный DFS, но с жёстким запретом спускаться ниже текущего предела глубины.
На первый взгляд кажется, что повторное раскрытие верхних уровней — расточительство: при поиске на глубине 3 алгоритм заново обходит все вершины глубин 1 и 2, которые уже раскрывались. Однако при факторе ветвления основная масса вершин сосредоточена на последнем уровне (там их в раз больше, чем на предпоследнем), и накладные расходы на повторы составляют не более от стоимости однократного обхода. Для это всего лишь полуторакратное увеличение.
IDDFS наследует преимущества обоих родительских алгоритмов. Он полон: ограничение глубины растёт неограниченно, и любое решение на конечной глубине рано или поздно будет найдено. Он оптимален при одинаковой стоимости переходов: глубина увеличивается шагами по единице, и первая нашедшаяся цель неизбежно лежит на минимальной возможной глубине. И при этом по памяти он экономен как DFS — , — потому что каждая итерация это обычный DFS. Сочетание этих свойств делает IDDFS предпочтительным неинформированным алгоритмом для задач с большим пространством состояний.
Сравнение трёх алгоритмов на одной задаче поучительно. Положим задачу нахождения решения в дереве глубиной с . BFS гарантированно найдёт оптимальное решение, удерживая в памяти около вершин. DFS уложится в вершин памяти, но может уйти в неоптимальную ветвь и вернуть решение длиной, скажем, 17 вместо 12. IDDFS повторит часть верхних уровней, раскрыв в полтора раза больше вершин, чем BFS, — но при тех же единицах памяти и с гарантией оптимальности. Когда задача исчерпывает оперативную память при BFS, IDDFS обычно становится единственно реалистичным неинформированным выбором.
Информированный (эвристический) поиск
Неинформированные алгоритмы «слепы» — они не знают, в какой стороне находится цель, и потому обходят пространство состояний механически. Если же у нас есть возможность оценить, насколько текущее состояние далеко от целевого, поиск можно направить значительно эффективнее.
Эвристическая функция оценивает стоимость пути от вершины до ближайшего целевого состояния. Разумеется, точное значение этой стоимости нам неизвестно (иначе задача была бы уже решена), поэтому — приближённая оценка. Для «восьмёрки» и «пятнашек» типичная эвристика — манхэттенское расстояние: сумма расстояний каждой фишки от её целевой позиции по горизонтали и вертикали. Эта оценка никогда не превышает реального числа ходов, и такое свойство оказывается ключевым.
Жадный поиск (англ. greedy best-first search) всегда раскрывает вершину с наименьшим . Он устремляется к цели напрямик и может найти решение быстро, но без гарантии оптимальности: алгоритм легко попадает в локальные «ловушки», выбирая путь, который выглядит многообещающим, но ведёт в тупик.
Алгоритм A* (англ. A-star) использует более взвешенную стратегию 2. Для каждой вершины вычисляется оценочная функция где — стоимость пути от начальной вершины до (известна точно), а — эвристическая оценка оставшегося пути. Алгоритм раскрывает вершину с наименьшим , то есть учитывает и уже понесённые затраты, и прогнозируемый остаток.
A* гарантирует нахождение оптимального решения при выполнении двух условий. Первое — допустимость (англ. admissibility) эвристики: для всех , где — истинная стоимость оптимального пути от до цели. Иначе говоря, эвристика не должна переоценивать расстояние до цели. Манхэттенское расстояние для «восьмёрки» — допустимая эвристика, поскольку каждая фишка должна сделать не менее стольких ходов, сколько клеток отделяет её от целевой позиции. Второе условие — состоятельность (англ. consistency, иногда monotonicity): для любой вершины и её потомка , полученного действием , выполняется , где — стоимость перехода. Состоятельность — более сильное требование, чем допустимость, и оно гарантирует, что A* не станет повторно раскрывать уже обработанные вершины.
Качество эвристики на практике определяет, сколько вершин раскроет A*. Эвристика доминирует над , если для всех при сохранении допустимости. Доминирующая эвристика никогда не приводит к раскрытию большего числа вершин. Классический пример для «восьмёрки»: тривиальная эвристика «число фишек не на своих местах» допустима, но слабая; манхэттенское расстояние её доминирует и на типичных задачах сокращает число раскрываемых вершин в десятки раз. Это объясняет, почему инженерия эвристики — отдельный пункт при проектировании систем планирования.
Разница между жадным поиском и A* наглядна на примере навигации. Положим, требуется проложить маршрут из точки А в точку Б на карте. Жадный поиск выберет дорогу, которая «смотрит» в сторону Б (минимальное расстояние по прямой), — но эта дорога может оказаться просёлочной, петляющей и в итоге длинной. A* учтёт и уже пройденный путь , и оставшееся расстояние , и предпочтёт скоростную магистраль, даже если она сначала ведёт в сторону от цели.
На практике A* применяется в навигационных приложениях, компьютерных играх (поиск пути для персонажей), робототехнике (планирование движения), логистике (оптимизация маршрутов). Ограничение алгоритма — требования к памяти: как и BFS, A* хранит все раскрытые вершины, и при больших пространствах состояний это может стать проблемой. Существуют модификации, смягчающие это ограничение: IDA* 1 комбинирует A* с итеративным углублением и потребляет памяти, SMA* 3 работает в фиксированном бюджете памяти, отбрасывая наименее перспективные вершины. Подробное рассмотрение этих модификаций выходит за рамки курса; систематическое изложение — в 4.
Логический вывод и представление знаний
Базы знаний и экспертные системы
Поиск в пространстве состояний — мощный инструмент, но он предполагает, что задача уже формализована: определены состояния, операторы, цель. А что если задача состоит не в нахождении пути, а в принятии решения на основе неполной информации? Врач ставит диагноз, инженер определяет причину неисправности, консультант подбирает конфигурацию оборудования — во всех этих случаях рассуждение строится не как поиск пути, а как последовательность выводов из имеющихся фактов и правил.
Именно для таких задач в 1970–1980-х годах были разработаны экспертные системы (англ. expert systems) 56. Архитектура классической экспертной системы включает три компонента:
- База знаний (англ. knowledge base) — хранилище фактов о предметной области и правил, связывающих факты с выводами. Факты описывают известные свойства объектов («температура пациента — 38.5°C», «бактерия грамположительная»). Правила задают логику вывода.
- Механизм вывода (англ. inference engine) — программный модуль, который применяет правила к фактам и порождает новые заключения.
- Пользовательский интерфейс — обеспечивает диалог с пользователем: запрашивает недостающие факты и объясняет ход рассуждения.
Правила в экспертных системах чаще всего записываются в форме продукций (англ. production rules): «ЕСЛИ условие, ТО действие». Рассмотрим фрагмент, стилизованный под систему диагностики:
ЕСЛИ инфекция является первичной бактериемией
И окраска по Граму положительная
И морфология — кокки в цепочках
ТО с вероятностью 0.7 возбудитель — Streptococcus
Подобных правил в реальных системах могли быть сотни. MYCIN содержала около 450 правил и показывала точность диагностики, сопоставимую с экспертами-инфекционистами.
Механизм вывода может работать в двух режимах. Прямой вывод (англ. forward chaining) отталкивается от известных фактов: система просматривает правила, проверяет, у каких из них выполнены условия, и активирует соответствующие действия, порождая новые факты. Процесс повторяется, пока не будет достигнута цель или не останется применимых правил. Прямой вывод естественен для задач мониторинга и автоматического реагирования: поступили новые данные — система делает выводы.
Обратный вывод (англ. backward chaining) действует в противоположном направлении. Система начинает с гипотезы (например, «возбудитель — Streptococcus») и ищет правила, которые могли бы её подтвердить. Если условия правила не подтверждены имеющимися фактами, они становятся новыми подцелями, которые система пытается доказать рекурсивно. Обратный вывод характерен для диагностических систем: есть подозрение — нужно проверить, подтверждается ли оно данными.
На практике разница между режимами проявляется в том, какие вопросы система задаёт пользователю. Система с прямым выводом обрабатывает все доступные данные и выдаёт заключение. Система с обратным выводом спрашивает только то, что нужно для проверки текущей гипотезы, — и в этом её преимущество при большой базе знаний, когда сбор полной информации дорог или невозможен.
Рассмотрим миниатюрный пример. Пусть база знаний содержит два правила: «ЕСЛИ лихорадка И кашель, ТО грипп» и «ЕСЛИ грипп, ТО назначить покой». Известный факт — «лихорадка». Прямой вывод раскрутит цепочку так: ищем правила, у которых выполнены условия. Первое правило требует ещё и «кашель» — система спросит у пользователя про кашель; получив подтверждение, добавит «грипп» в базу фактов, затем сработает второе правило и появится «назначить покой». Обратный вывод стартует с гипотезы «назначить покой»: ищет правила, которые её выводят, находит второе; чтобы оно сработало, нужен «грипп». Грипп не факт — ищется правило, его выводящее: первое. Его условия — «лихорадка» (есть в фактах) и «кашель» (неизвестно — спрашиваем). Дальше — как в прямом выводе. Различие — в порядке вопросов: прямой обходит правила «снизу вверх» от фактов, обратный — «сверху вниз» от целей. На небольшой базе различие незаметно, но при сотнях правил разница в числе бесполезных запросов к пользователю определяет, удобна ли система.
Ограничения символьного подхода
Экспертные системы продемонстрировали, что формализованное знание позволяет решать практические задачи. Но их массовое внедрение обнажило несколько фундаментальных ограничений, которые не удалось преодолеть в рамках символьной парадигмы.
Первое — проблема приобретения знаний (англ. knowledge acquisition bottleneck). Правила приходилось извлекать из голов экспертов вручную, в ходе длительных интервью. Эксперт далеко не всегда способен объяснить, как именно он принимает решения: значительная часть профессионального опыта существует в форме интуиции и неявного знания. Создание базы знаний для одной предметной области занимало месяцы и годы, а при изменении области — например, при появлении нового оборудования или новых стандартов — базу приходилось перерабатывать заново.
Второе — хрупкость (англ. brittleness). Экспертная система работает уверенно ровно в тех ситуациях, которые предусмотрены её правилами. Стоит столкнуться с незнакомой комбинацией фактов — и система либо даёт некорректный ответ, либо не даёт никакого. У неё нет механизма обобщения, позволяющего перенести опыт из одной ситуации в другую. Человек-эксперт может рассуждать по аналогии; продукционная система — нет.
Третье — комбинаторный взрыв. При увеличении числа правил и фактов количество возможных цепочек вывода растёт экспоненциально. Для простых систем с десятками правил это не проблема, но при масштабировании до тысяч правил время работы механизма вывода может стать неприемлемым.
Эти ограничения не означают, что символьный подход бесполезен. В задачах с чёткой структурой — конфигурирование, планирование, формальная верификация — он остаётся эффективным. Но для задач, где закономерности трудно формализовать в виде правил (распознавание образов, обработка естественного языка, прогнозирование на основе больших объёмов данных), потребовался принципиально иной подход.
Ответом стало машинное обучение: вместо ручного формирования правил — автоматическое извлечение закономерностей из данных. Вместо явной базы знаний — модель, параметры которой настраиваются в ходе обучения. Этот переход — от знаний к данным, от правил к статистике — составляет содержание следующих тем курса, начиная с темы 3.
Литература
- Korf R. E. Depth-First Iterative-Deepening: An Optimal Admissible Tree Search. — Artificial Intelligence, 1985, С. 97–109, DOI: 10.1016/0004-3702(85)90084-0.
- Hart P. E., Nilsson N. J., Raphael B. A Formal Basis for the Heuristic Determination of Minimum Cost Paths. — IEEE Transactions on Systems Science and Cybernetics, 1968, С. 100–107, DOI: 10.1109/TSSC.1968.300136.
- Russell S. J. Efficient Memory-Bounded Search Methods. — Proceedings of the 10th European Conference on Artificial Intelligence (ECAI), 1992, С. 1–5.
- Russell S., Norvig P. Artificial Intelligence: A Modern Approach. — Pearson, 2021.
- Shortliffe E. H. Computer-Based Medical Consultations: MYCIN. — Elsevier, 1976.
- McDermott J. R1: A Rule-Based Configurer of Computer Systems. — Artificial Intelligence, 1982, С. 39–88, DOI: 10.1016/0004-3702(82)90021-2.