Подтвердить что ты не робот

Pythonic реализация байесовских сетей для конкретного приложения

Вот почему я задаю этот вопрос: В прошлом году я сделал некоторый код на С++ для вычисления задних вероятностей для определенного типа модели (описанной байесовской сетью). Модель работала неплохо, и некоторые другие люди начали использовать мое программное обеспечение. Теперь я хочу улучшить свою модель. Поскольку я уже кодирую несколько разные алгоритмы вывода для новой модели, я решил использовать python, потому что время выполнения не критично важно, и python может позволить мне сделать более элегантный и управляемый код.

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

Я уже нашел отличный модуль python для сетевых графиков (networkx), который позволяет вам прикрепить словарь к каждому node и к каждому ребру. По сути, это позволило мне дать свойства узлов и краев.

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

Например, в классической сети "Азия" (http://www.bayesserver.com/Resources/Images/AsiaNetwork.png), с состояниями "XRay Result" и "Dyspnea", Известно, что мне нужно написать функцию для вычисления вероятности того, что другие переменные имеют определенные значения (согласно некоторой модели).

Вот мой вопрос программирования: Я собираюсь попробовать несколько моделей, и в будущем это возможно, я захочу попробовать другую модель после этого. Например, одна модель может выглядеть точно так же, как сеть в Азии. В другой модели, направленный край может быть добавлен из "Посещения в Азию" на "Имеет рак легких". Другая модель может использовать исходный направленный график, но вероятностная модель для "Одышки" node с учетом узлов "Туберкулез или рак" и "Имеет бронхит" может отличаться. Все эти модели будут вычислять вероятность по-другому.

Все модели будут иметь существенное совпадение; например, множественные ребра, входящие в "Or" node, всегда будут делать "0", если все входы "0" и "1" в противном случае. Но некоторые модели будут иметь узлы, которые принимают целочисленные значения в некотором диапазоне, в то время как другие будут логическими.

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

Некоторые параметры:

  • Я уже делал это правильно. Код сначала, задавайте вопросы позже. Быстрее копировать и вставлять код и иметь один класс для каждой модели. Мир - темное и дезорганизованное место...
  • Каждая модель является ее собственным классом, но также и подклассом общей байесовской модели. Эта общая модель будет использовать некоторые функции, которые будут переопределены. Страуструп гордился бы.
  • Сделайте несколько функций в одном классе, которые вычисляют разные вероятности.
  • Кодируйте общую библиотеку BayesianNetwork и реализуйте мои проблемы вывода как конкретные графики, читаемые этой библиотекой. Узлам и ребрам должны быть заданы такие свойства, как "Boolean" и "OrFunction", которые, учитывая известные состояния родительского node, могут быть использованы для вычисления вероятностей разных результатов. Эти строки свойств, такие как "OrFunction", могут даже использоваться для поиска и вызова правильной функции. Может быть, через пару лет я сделаю нечто похожее на версию Mathematica 1988 года!

Большое спасибо за вашу помощь.

Update: Объектно-ориентированные идеи очень помогают здесь (каждый node имеет назначенный набор узлов-предшественников определенного подтипа node, и каждый node имеет функцию правдоподобия, которая вычисляет его вероятность различных состояний результата, учитывая состояния предшественника узлы и т.д.). OOP FTW!

4b9b3361

Ответ 1

Я долгое время занимался этим делом в свободное время. Я думаю, что сейчас на третьей или четвертой версии этой же проблемы. Я действительно готов к выпуску другой версии Fathom (https://github.com/davidrichards/fathom/wiki) с включенными динамическими байесовскими моделями и другим уровнем сохранения.

Как я пытался сделать свой ответ ясным, это получилось довольно долго. Я прошу прощения за то. Вот как я атаковал проблему, которая, кажется, отвечает на некоторые ваши вопросы (несколько косвенно):

Я начал с перерыва в распространении веры в Иудейскую жемчужину в Байесовской сети. То есть, это график с предшествующими коэффициентами (причинно-следственная поддержка), исходящими от родителей и вероятностями (диагностическая поддержка), поступающими от детей. Таким образом, базовый класс - это просто BeliefNode, как и то, что вы описали с помощью дополнительного node между BeliefNodes, LinkMatrix. Таким образом, я явно выбираю тип вероятности, который я использую по типу LinkMatrix, который я использую. Это упрощает объяснение того, что делает сеть убеждений после этого, а также упрощает вычисления.

Любые подклассы или изменения, которые я внес бы в базовый BeliefNode, будут для непрерывных переменных binning, а не для изменения правил распространения или ассоциаций node.

Я решил сохранить все данные внутри BeliefNode и только фиксированные данные в LinkedMatrix. Это связано с тем, что я поддерживаю чистые обновления убеждений с минимальной сетевой активностью. Это означает, что в моем хранилище BelieveNode:

  • массив ссылок на детей, а также отфильтрованные вероятности от каждого дочернего элемента и матрицу ссылок, которая выполняет фильтрацию для этого дочернего
  • массив родительских ссылок, а также отфильтрованные предыдущие коэффициенты, исходящие от каждого родителя, и матрица ссылок, которая выполняет фильтрацию для этого родителя
  • общая вероятность node
  • объединенные ранее коэффициенты node
  • вычисленное убеждение или задняя вероятность
  • упорядоченный список атрибутов, в котором все предыдущие коэффициенты и вероятность соответствуют

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

Я использую MongoDB для сохранения и кэширования. Я получаю доступ к этим данным внутри создаваемой модели для скорости и асинхронного доступа. Это делает сеть довольно эффективной, а также имеет возможность быть очень большой, если это необходимо. Кроме того, поскольку я использую Mongo таким образом, я могу легко создать новый контекст для той же базы знаний. Так, например, если у меня есть диагностическое дерево, часть диагностической поддержки для диагностики будет исходить из симптомов и тестов пациента. То, что я делаю, создает контекст для этого пациента, а затем распространяет мои убеждения, основываясь на доказательствах этого конкретного пациента. Аналогичным образом, если врач сказал, что пациент, вероятно, испытывает два или более заболеваний, тогда я мог бы изменить некоторые из моих матриц ссылок, чтобы размножать изменения веры по-разному.

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

Моя работа с открытым исходным кодом, поэтому вы можете следить за ней, если хотите. Все это Ruby, так что это будет похоже на ваш Python, но не обязательно на замену. Одна вещь, которая мне нравится в моем дизайне, заключается в том, что вся информация, необходимая людям для интерпретации результатов, может быть найдена в самих узлах, а не в коде. Это можно сделать в качественных описаниях или в структуре сети.

Итак, вот некоторые важные отличия, которые у меня есть с вашим дизайном:

  • Я не вычисляю модель правдоподобия внутри класса, а скорее между узлами внутри матрицы ссылок. Таким образом, у меня нет проблемы объединения нескольких функций правдоподобия внутри одного класса. У меня также нет проблемы с одной моделью против другой, я могу просто использовать два разных контекста для одной и той же базы знаний и сравнить результаты.
  • Я добавляю много прозрачности, делая человеческие решения очевидными. I.e., если я решаю использовать по умолчанию или-gate между двумя узлами, я знаю, когда я добавил это и что это было просто решение по умолчанию. Если я вернусь позже и изменим матрицу ссылок и пересчитаю базу знаний, у меня есть заметка о том, почему я сделал это, а не просто приложение, которое выбрало один метод над другим. Вы могли бы заставить своих потребителей делать заметки об этом. Однако вы решаете это, вероятно, неплохо получить пошаговое диалоговое окно от аналитика о том, почему они настраивают вещи друг на друга.
  • Возможно, я более подробно расскажу о предыдущих шансах и вероятностях. Я точно не знаю, я просто видел, что вы использовали разные модели для изменения ваших вероятностных чисел. Многое из того, что я говорю, может быть совершенно неуместным, если ваша модель для вычисления задних убеждений не разрушается таким образом. У меня есть возможность сделать три асинхронных шага, которые могут быть вызваны в любом порядке: передать измененные вероятности сети, пропустить измененные ранее шансы вниз по сети и перерасчитать объединенное мнение (задняя вероятность) node сам.

Одно большое предостережение: некоторые из того, о чем я говорю, еще не выпущены. Я работал над вещами, о которых я говорю примерно до 2:00 сегодня утром, поэтому он определенно актуальен и определенно получает регулярное внимание от меня, но пока еще не доступен для публики. Поскольку это моя страсть, я был бы рад ответить на любые вопросы или работать вместе над проектом, если вы хотите.

Ответ 2

система вывода на основе ограничений Mozart/Oz3 решает аналогичную проблему: вы описываете свою проблему с точки зрения ограничений на конечные переменные домена, ограничение распределители и распределители, функции затрат. Если больше не существует вывода, но есть все еще несвязанные переменные, он использует ваши функции затрат для разделения пространства проблем на несвязанной переменной, что, скорее всего, снижает затраты на поиск: то есть, если X между [a, c] является такой переменной, и c (a < b < c) является точкой, наиболее вероятно уменьшающей стоимость поиска, вы получаете два случая проблемы, где X находится между [a, b], а в другом случае X находится между [b, с]. Моцарт делает это довольно изящно, поскольку он переопределяет привязку переменной как объект первого класса (это очень полезно, поскольку Mozart распространен параллельно и распределен, чтобы переместить проблемное пространство в другой node). В своей реализации я подозреваю, что он использует стратегию копирования на запись.

Конечно, вы можете реализовать схему копирования на запись в графической библиотеке (tip: numpy использует различные стратегии для минимизации копирования; если вы основываете свое графическое представление на нем, вы можете получить семантику copy-on-write для бесплатно) и достичь ваших целей.

Ответ 3

Я не слишком хорошо знаком с Bayesian Networks, поэтому надеюсь, что следующее полезно:

В прошлом у меня была, казалось бы, схожая проблема с регрессором Gaussian Process, а не байесовский классификатор.

В итоге я использовал наследование, которое получилось хорошо. Все модельные пареметры устанавливаются вместе с конструктором. Функции calculate() являются виртуальными. Каскадирование различных методов (например, сумма-метод, который сочетает в себе произвольное количество других методов) также хорошо работает таким образом.

Ответ 4

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

  • Как часто вы добавляете модели?
  • Ожидают ли пользователи вашей библиотеки добавлять новые модели?
  • Какой процент пользователей будет добавлять модели против того, какой процент будет использовать существующие?

Если большая часть времени будет потрачена на существующие модели, а новые модели будут менее распространены, то наследование, вероятно, будет использоваться мной. Это упрощает сбор документации, и код, который ее использует, будет легко понять.

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

Независимо от того, к какому окончательному дизайну вы работаете, я бы начал с простого проекта по созданию одного класса. Получите это, пройдя множество автоматических тестов, а затем реорганизуйте в более полный дизайн после этого. Кроме того, не забывайте об управлении версиями; -)