Вы здесь

Модели информации и данных. Атом и универсум информации. Построение информационной системы (А. М. Горбачев)

Построение информационной системы

Вариации архитектуры и требования к системе

Мы очень многое знаем про определенность, статичность, четкую последовательность действий и строгое определение структур данных в программных системах. И наоборот, не так много сказано про возможности вариаций – не про девиации, то есть отклонения от норм, а про наличие общей формы и вариации в реализации обработок и структур данных, которые формируют индивидуальность каждой из экземпляров программных систем. То есть, с одной стороны, мы декларируем основные принципы систем, с другой стороны, мы должны понимать, насколько гибкими являются эти системы. Гибкость достигается за счёт вариативности структур этих систем. Тремя столпами любой информационной системы являются данные, обработка и коммуникация. Коммуникацию сейчас оставим без рассмотрения как производную от обработки данных.

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

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

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

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

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

Данное выше описание как нельзя лучше подходит под системы класса BPM (Business Process Management, управление бизнес-процессами) и технологии SOA (Service Oriented Architecture, сервисно-ориентированная архитектура), когда конечная система складывается из компонентов как из кубиков. Но здесь есть одно значительное «НО!», которое заключается в том, что компоненты в системах с такой архитектурой являются статичными, и они не обладают достаточной гибкостью, чтобы именно собирать их как кубики, а не переписывать и проектировать составляющие их компоненты каждый раз заново, как это и делается на практике.

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

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

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

При этом реализация требования универсальности для агента требует не только гибких правил обработки данных, но также и гибких структур данных, которые способны модифицировать и преобразовывать не только сами данные, но и структуры данных.


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

Вариации структур данных и операций

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

Например, система управления персоналом позволяет вам создавать новые поля в карточке сотрудника. Но при этом, система определяет, что должна существовать определенная таблица «карточка сотрудника», а каждый сотрудник должен быть идентифицирован табельным номером.

Таблица «карточка сотрудника», поля «табельный номер», «ФИО» и пр. – это и есть предопределенная структура данных. Возможность увеличения или уменьшения универсальности – добавление полей, удаление и изменение полей, реструктурирование таблицы и пр. и является реализацией универсальности системы, а её возможность к изменениям является свойством вариативности системы.

Вариативность касается как возможностей в обработке данных, так и возможностей в использовании структур данных. Рассмотрим эти две составляющие раздельно.

Означает ли максимальная гибкость в операциях, что система должна отойти от какой-либо структурированности программного кода? Нет, не означает. Это вопрос относится к понятию полноты вычислимости по Тьюрингу. Известно, что машина Тьюринга обладает полнотой вычислимости, то есть она может реализовать любой алгоритм. Следовательно, чтобы достичь достаточной вариативности по обрабатываемым алгоритмам, наш агент должен иметь возможность реализовать машину Тьюринга.

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

Крайним примером статичности информации является выбитая в камне надпись. Физическая сущность носителя таких данных гарантирует его долговечность и неизменность. Максимальную гибкость в структуре данных мы стремимся получить через лёгкость изменения данных и через простоту в определении структур данных. Для надписей структурой является естественный язык, в информационных системах структура данных определяется платформой – языком программирования (типы данных), аппаратной структурой (например, размер машинного слова), реализацией языка программирования (различные виды языка Basic определяют различный размер для типа int), СУБД и пр.

Поскольку мы делаем упор на данные, мы должны обратить внимание на три важные составляющие, связанные с ними – хранение, извлечение и запись, обработка и обмен (коммуникация). Для передачи данных с их предварительной упаковкой используется языка разметки XML и JSON, который дает возможность «завернуть» всё, что угодно. Для хранения большого объема данных со сложной структурой обычно используется СУБД (системах управления базами данных) на основе реляционной модели данных либо, что реже, базы данных, основывающиеся на иных моделях данных. Для обработки данных используется несчетное множество программных технологий – языков, платформ, framework’ов, библиотек и т. п.

Предопределенная (априорная) информация агента

Любая программа предполагает определенный сценарий или несколько сценариев выполнения операций и действий. Например, в программах управления персоналом одним из сценариев является ввод данных о сотруднике, ввод данных о позициях штатного расписания, прием сотрудника на работу.

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

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

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

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

Парадокс Рассела для операций, автогенерация кода

Обсуждение автогенерации кода начнем с описания парадокса Рассела, хорошо известного в теории множеств: пусть К – множество всех множеств, которые не содержат себя в качестве своего элемента. Содержит ли К само себя в качестве элемента? Обычно этот парадокс иллюстрируется несколькими, насколько занимательными, настолько и абсурдными задачами:

Парадокс брадобрея: деревенскому брадобрею приказали брить всякого, кто не бреется сам и не брить того, кто бреется сам. Может ли брадобрей брить самого себя?

Парадокс мэра: в стране вышел указ о том, что мэры городов должны жить не в своем городе, а в специальном городе мэров. Может ли мэр города мэров жить в городе мэров?

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

Аналогичный парадокс, связанный с модификацией программного кода, возникает в задачах искусственного интеллекта. А именно, может ли программный код модифицировать и выстраивать сам себя? Или в другой интерпретации: может ли программный код строить другой программный код, не содержащий сам себя?

Как же соотносятся множества из парадокса Рассела и генерируемый программный код? На элементарном уровне код состоит из команд и операций. Их стройная последовательность и является кодом, который требуется получить. Но на уровне постановки задачи генерации никакого программного кода еще нет, это обобщенная информация или мета-информация о том, каким должен быть генерируемый код. Это и есть множество (задач), не содержащее другое множество (команд). Задача – это поручение процессору выполнить определенную команду. Получается, что, создавая программу, и записывая её в память мы даем поручение процессору выполнить определенный набор команд. Когда же процессор идет по этой последовательности команд, он рассматривает каждую из них в текущем контексте выполнения (регистры, состояние памяти и пр.). Понятие множества, не содержащего само себя – это фактически мета-информация (задачи) о мета-информации (командах), в которой не содержатся задачи более низкого уровня, то есть представления задач с более детализированным видом, хотя формально этот более абстрактный вид мета-информации остается мета-информацией. Подробнее представление мета-информации будет рассмотрено ниже.

Разумеется, парадокс Рассела не является краеугольным камнем для возможности генерации кода. В нашем случае, наоборот, мы понимаем, что одна программа может сформировать программный код (об этом ниже), и сама программа – это код. Так может ли идти речь о воспроизведении кода как об аналоге всемогущества?

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

В связи с тем, что самомодифицирующиеся программы, за исключением специфических, указанных выше программ, сейчас фактически не встречаются, поднятый мной вопрос на основе парадокса Рассела не так уж наивен для сегодняшнего уровня развития информационных систем.

Существует вид программирования, основной задачей которого является генерация программного кода путем самомодификации либо путем создания новой программы. Такой вид программирования называется мета-программированием.

Мета-программирование, собственно, предлагает два варианта – это шаблоны и внеязыковые средства, такие как синтаксические и лексические анализаторы. Полиморфизм объекто-ориентированного подхода программирования также предлагается как один из инструментов мета-программирования. Частным случаем мета-программы является «квайн» (quine): программа, которая выдает на выходе точную копию своего исходного текста. Тем не менее, какие бы технологии программирования мы не перечисляли, созданием программ в мета-программировании занимаются люди, а подходы призваны лишь упрощать написание кода.

Что же формирует код или, говоря простым языком, заставляет программы исполняться? Крупным классом программ, порождающим программный код, являются компиляторы. Противоположностью генерации низкоуровневого исполняемого кода является исполнение кода высокого уровня при помощи программ-интерпретаторов, которые на ходу анализируют исходный код и исполняют инструкции программ. Существует и компромиссный вариант между компиляцией и интерпретацией программ. Это технологии, осуществляющие трансляцию исходного программного кода в байт-код или в p-код (пи-код). Байт-код является более низкоуровневым, p-код – в большей степени высокоуровневым. Внутри исполнимого модуля эти виды кода исполняются интерпретатором.

Существенным отличием интерпретаторов и p-кода от компилированного кода состоит в том, что, используя механизмы интерпретации, система «на ходу» может выполнять скрипты на том же языке программирования.

В качестве альтернативы внешние программные коды также могут встраиваться в систему. Например, Microsoft предлагает собственный SDK, основанный на Visual Basic for Applications (VBA), подобно тому, как VBA существует в офисных продуктах Microsoft – MS Word, MS Excel и пр.

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

Однако при автоматическом создании кода мы неизменно столкнемся с типичными проблемами, связанными с корректностью кода. Согласно статистики, в среднем 60% времени по разработке программного обеспечения тратится не на программирование, а на отладку и тестирование кода. Если человек сталкивается с подобными проблемами, то с ними столкнется и автоматическая система динамической генерации кода. То есть либо код должен быть лишен ошибок (что, очевидно, невозможно), либо ошибки при исполнении кода должны порождать исключения, которые в свою очередь должны возвращать исполнимый код на уровень регенерации кода. Коррекцию или регенерацию можно сравнить с проведением эксперимента, а последовательную отладку программы – с методом проб и ошибок.

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

Дилемма первичности для операций и данных, операции с операциями

В отношении информационных технологий мы можем поднять вопрос первичности: что возникло раньше – данные или операции? Эта дилемма возникает не из праздного интереса, это дилемма определения главенства и выстраивания зависимостей. Должны ли обработки подстраиваться под структуры данных либо структуры данных должны строиться, исходя из особенностей обработок, в том числе языка программирования, особенностей доступа к данным?

С точки зрения агентов, информация является первичной, поскольку в окружающем мире информация не является собственностью самого агента, а потому агент существует в среде, насыщенной информацией. Кроме того, сошлемся на понятие априорной информации, описанное выше: она должна существовать до того, как первый процесс начнет «работать».

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

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

Возвращаясь к вопросам, освещенным в предыдущей главе, мы можем рассмотреть программный код как данные. И в этом случае дилемма первичности приводит нас к задаче структурирования кода как данных и дальнейшему управлению кодом (хотя бы даже кодом различного уровня, не обязательно кодом низкого уровня – машинным кодом). И тогда дилемма первичности превращается в несколько иную дилемму: что первично – курица или курица из яйца? Не воспринимайте этот вопрос как игру слов с целью затуманить Ваш мозг, это лишь попытка привести рассуждение к рассмотрению кода как данных.

Для рассмотрения возможностей соединения двух полярных субстанций (данных и процессов) рассмотрим эту дилемму с точки зрения аппаратных архитектур.

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

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

Идеи, основанные на автоматном программировании, явно разделяют инструкции и память. Самые известные примеры автоматного программирования – это машина Тьюринга и машина Поста.

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

Ещё в 1946 году фон Нейманом при проектировании первых процессоров были сформированы несколько принципов построения ЭВМ. Одним из его принципов был принцип однородности памяти, в котором говорилось, что программы и данные хранятся в одной и той же памяти. С командами можно выполнять такие же действия, как и с данными. Альтернативой фон Неймановской архитектуре является гарвардская архитектура, которую отличает раздельное хранение команд и данных.

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

В высокоуровневых языках программирования транслятор обычно ограничивает доступ к памяти, где хранится исполняемый код, а в интерпретируемых языках и вовсе отсутствует возможность не то, что скорректировать текст программы, но и даже получить этот текст. Одним из исключений является инструкция List некоторых реализаций языка программирования Basic, выводящая на экран исходный код программы. В других языках программирования для этих целей специально придумывают специальные программы, называемые «квайн» (quine). Аналогично, в скриптовых языках типа bat-файлов в dos/windows и *sh в Unix-системах код существует в текстовых файлах, а соответственно и доступ к ним может осуществляться через операции с файлами. В части языков программирования такая возможность реализуется через возможности интерпретатора или p-кода (пи-кода).

Другим исключением является операции аналогичного вида со встраиваемыми языками и скриптами. Первые – это SDK VBA, которые позволяют вызывать интерпретируемые VBA-процедуры из программного кода и скрипты обычно относятся к собственным языкам программирования либо тому же интерпретируемому языку программирование (как, например, команда exec в T-SQL и eval () в PHP).

Но фактически эти возможности остаются невостребованными с точки зрения модификации кода.

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

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

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

С одной стороны, вместо определения константы с кодом мы можем брать его из отдельного справочника или элемента данных, привязанного к плану счетов. Но с другой стороны, мы не можем это сделать, поскольку структура данных является фиксированной, а, кроме того, в таком случае нас ждёт сложная процедура администрирования этих структур, включая организацию ссылки на план счетов, массовых данных при необходимости (например, вместо одного счета мы должны указывать ряд счетов) и визуализации данных. Такой комплекс процедур влечет за собой существенные накладные расходы при настройке отчета, поэтому от этого пути отказываются в пользу отказа от контроля над целостностью данных.

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

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