Datalog를 이야기하면 왜 고정점과 온톨로지를 만나게 될까
by Justin Kim
Datalog를 설명하다 보면 이상하게도 고정점이라는 수학 용어를 피하기 어렵습니다. 사실과 규칙으로 지식을 표현하는 언어라고 소개했을 뿐인데, 어느새 최소 고정점이라는 말까지 꺼내게 됩니다. 처음 듣는 사람에게는 쉬운 이야기를 갑자기 어렵게 만드는 것처럼 보일 수 있습니다. 고정점이 등장하는 데에는 자연스러운 이유가 있습니다. 규칙으로 새로운 사실을 알아내기 시작하면 곧 마주치는 질문, “그러면 어디까지 알아내야 끝난 것인가?”에 대한 답입니다.
고양이 한 마리를 떠올려 봅시다. 이름은 나비입니다. 우리가 처음 적어 둔 것은 “나비는 고양이다”, “고양이는 포유류의 한 종류다”, “포유류는 동물의 한 종류다”라는 세 문장입니다. 사람은 이 문장들을 읽자마자 나비가 동물이라는 것을 압니다. 그러나 컴퓨터에 세 문장을 저장하는 것만으로는 충분하지 않습니다. 어떤 종류에 속한 것은 그보다 넓은 종류에도 속한다는 규칙이 있어야 합니다.
이를 Wirelog 문법으로 적고, Python 바인딩인 PyreWire에서 실행해 보겠습니다. 아래 코드에서 PROGRAM 안의 문자열이 Wirelog 프로그램입니다. .decl은 관계의 이름과 각 열의 자료형을 선언하고, symbol은 이름 같은 문자열 값을 나타냅니다. known_type은 처음 주어진 분류, type은 규칙을 적용한 분류, subclass는 종류 사이의 포함 관계입니다. 입력하는 사실과 도출하는 관계를 나누어 둔 것입니다. 대문자로 시작하는 X, A, B는 그 자리에 들어갈 값을 나타냅니다.
Python 환경에 python3 -m pip install pyrewire==1.1.2로 설치한 뒤, 아래 코드를 nabi.py로 저장하고 python3 nabi.py로 실행하면 됩니다. 이 예제는 PyreWire 1.1.2와 Wirelog 0.70.0에서 실행해 확인했습니다.
from pyrewire import EasySession
PROGRAM = """
.decl known_type(entity: symbol, category: symbol)
.decl subclass(child: symbol, parent: symbol)
.decl type(entity: symbol, category: symbol)
type(X, A) :- known_type(X, A).
type(X, B) :- type(X, A), subclass(A, B).
"""
with EasySession(PROGRAM) as session:
session.insert("known_type", ["나비", "고양이"])
session.insert("subclass", ["고양이", "포유류"])
session.insert("subclass", ["포유류", "동물"])
print(sorted(session.snapshot("type")))
insert는 처음의 세 문장을 사실로 넣고, snapshot("type")은 규칙을 평가한 결과를 읽습니다. 출력 순서를 일정하게 보여주기 위해 sorted를 썼습니다. 실제 출력은 다음과 같습니다.
[('나비', '고양이'), ('나비', '동물'), ('나비', '포유류')]
Wirelog 프로그램의 첫 번째 규칙은 처음 주어진 분류를 type으로 옮깁니다. 두 번째 규칙은 “X가 A에 속하고, A가 B의 한 종류라면, X는 B에도 속한다”라고 읽으면 됩니다. 눈여겨볼 점은 규칙의 결과에도 type이 있고, 조건에도 type이 있다는 것입니다. 방금 얻은 결론이 같은 규칙의 다음 재료가 됩니다. 이것이 이 예제의 재귀입니다. 고양이에서 포유류로 가는 규칙, 포유류에서 동물로 가는 규칙을 따로 늘어놓지 않아도 같은 규칙으로 분류의 사다리를 계속 올라갈 수 있습니다.
그 과정을 천천히 나누어 보겠습니다. 처음 주어진 분류를 type으로 옮긴 뒤, 각 단계는 그 단계가 시작할 때 알고 있던 사실만 사용한다고 합시다. 처음에는 나비가 고양이라는 사실을 이용해 “나비는 포유류다”를 얻습니다. 다음 단계에서는 방금 얻은 사실을 이용해 “나비는 동물이다”를 얻습니다. 다시 규칙을 적용해도 이제 새로 나오는 사실은 없습니다. 이미 아는 문장을 다시 얻을 수는 있지만, 우리가 아는 사실의 모음은 더 커지지 않습니다.
이렇게 같은 연산을 한 번 더 해도 결과가 변하지 않는 상태가 고정점(fixed point)입니다. 여기서 고정되는 것은 나비도, 분류 체계도 아닙니다. 주어진 규칙을 적용하는 연산에 대해 사실들의 집합이 더는 달라지지 않는 것입니다. “지금 아는 사실에 규칙으로 얻을 수 있는 사실을 보탠다”라는 연산을 F라고 부르면, 고정점에서는 F(사실 집합) = 사실 집합이 됩니다.
앞의 설명에서는 두 단계를 거쳐 나비가 동물이라는 결론을 얻었습니다. 그런데 여기에 “동물은 생물의 한 종류다”라는 사실을 하나 더 넣으면 어떨까요? 이제 “나비는 생물이다”라는 결론도 얻을 수 있습니다. 두 단계까지만 알아보고 멈추면 이 결론을 놓칩니다. 자료에 따라 이렇게 이어지는 길이가 달라지므로, 몇 단계면 충분할지 미리 정하기는 어렵습니다. 대신 “아직 새로 알아낼 사실이 있는가?”를 확인하면 됩니다. 새 사실이 나오면 그것을 바탕으로 계속 알아보고, 알고 있는 사실에 모든 규칙을 적용해도 새로 나오는 것이 없을 때 멈추는 것입니다.
고정점이 필요한 더 깊은 이유는 여기에 있습니다. Datalog의 규칙은 실행 순서를 지시하기보다 어떤 조건에서 어떤 결론이 따라야 하는지를 선언합니다. “나비는 포유류다”까지 얻고 끝낸 결과에는, 그 결과 자체를 조건으로 삼아 얻을 수 있는 “나비는 동물이다”가 빠져 있습니다. 규칙의 의미를 끝까지 반영하지 못한 셈입니다. 고정점은 단지 프로그램을 멈추는 요령이 아니라, 선언한 규칙에 비추어 결과가 충분한지를 판단하는 기준입니다.
여기서 사실을 입력에 넣는 것과, 계산 결과에만 문장을 덧붙이는 것을 구분해야 합니다. 먼저 “나비는 파충류다”를 입력 사실로 주었다고 합시다. 그렇다면 파충류도 결과에 포함되는 것이 맞습니다. 엔진은 주어진 사실을 전제로 계산합니다. 이 예제에는 고양이와 파충류를 동시에 입력하지 못하게 하는 규칙도, 입력의 진위를 검사하는 규칙도 없습니다.
직접 확인하려면 앞의 Python 코드에서 세 번의 insert 다음, print(sorted(session.snapshot("type"))) 바로 전에 아래 한 줄을 같은 들여쓰기로 추가하면 됩니다.
session.insert("known_type", ["나비", "파충류"])
다시 실행하면 다음 결과가 나옵니다. 파충류는 다른 분류에서 추론된 것이 아니라, 입력으로 주어졌으므로 포함됩니다. known_type을 type으로 옮기는 첫 번째 규칙이 이 사실에도 적용됩니다.
[('나비', '고양이'), ('나비', '동물'), ('나비', '파충류'), ('나비', '포유류')]
원래 입력의 결과는 “고양이, 포유류, 동물”이고, 파충류를 입력에 추가한 뒤의 결과는 “고양이, 포유류, 동물, 파충류”입니다. 둘 다 각자의 입력에 대한 최소 고정점입니다. 입력이 달라졌으므로 따라오는 결론도 달라진 것입니다. 최소 고정점에서 ‘최소’란 입력된 사실을 마음대로 빼서 작게 만든다는 뜻이 아닙니다. 주어진 사실을 모두 받아들이고, 그 사실과 규칙으로 따라오는 결론까지 담은 가장 작은 집합이라는 뜻입니다.
이와 별개로, 입력은 원래대로 두고 누군가 결과 목록에만 “파충류”를 써 넣었다면 어떨까요? 이 목록은 엔진이 원래 입력에서 계산한 결과가 아닙니다. 앞서 정의한 “현재 집합을 유지하면서 규칙의 결론을 보태는 연산”만 적용하면 이 목록도 그대로 남을 수 있습니다. 그 연산은 이미 들어 있는 문장을 지우지 않기 때문입니다. 따라서 더 이상 늘지 않는다는 조건만으로는 올바른 계산 결과인지 판단할 수 없습니다. 이 경우에는 결과에 덧쓴 파충류를 빼도 원래 입력과 규칙이 요구하는 결론이 모두 남습니다. 그래서 이 목록은 원래 입력에 대한 최소 고정점이 아닙니다.
반면 파충류를 입력 사실로 받아들인 경우에는 그 사실을 빼서는 안 됩니다. 같은 “파충류”라는 문장이어도 입력으로 주어진 것인지, 입력과 무관하게 결과에만 끼워 넣은 것인지에 따라 판단이 달라집니다. 최소 고정점은 사실의 진위를 심사하는 장치가 아니라, 정해진 입력과 규칙에 대한 답의 범위를 정하는 기준입니다. 이 최소 고정점과 규칙으로 증명 가능한 사실들이 일치한다는 것이 기본 Datalog 의미론의 핵심입니다. Datalog의 의미론을 정리한 자료의 2.2절에서도 이 관계를 설명합니다.
이 설명이 언제나 성립하려면 범위를 분명히 해야 합니다. 여기서는 부정이 없고, 함수나 계산으로 새 값을 끝없이 만들어내지 않는 기본 Datalog를 생각하고 있습니다. 입력과 규칙은 유한하고, 규칙의 변수는 조건에 나타난 사실의 값으로 채워집니다. 이런 범위에서는 만들 수 있는 서로 다른 사실의 수에도 한계가 있습니다. 이미 얻은 사실은 사라지지 않고, 같은 사실을 여러 번 얻어도 하나로 셉니다. 따라서 사실을 계속 보태는 과정은 결국 멈춥니다. 부정이나 새로운 값을 생성하는 확장이 들어오면 이 설명을 그대로 적용할 수는 없습니다.
실제 엔진이 반드시 앞의 설명처럼 한 단계씩 모든 규칙을 훑는다는 뜻도 아닙니다. 새로 생긴 사실만 중심으로 계산하거나, 질문에 필요한 부분만 찾아갈 수 있습니다. 앞에서 나눈 단계는 결과가 어떻게 정당화되는지를 보여주기 위한 것입니다. 이 기본 범위에서 적용할 수 있는 규칙을 빠뜨리지 않고 끝까지 반영하면, 처리 순서가 달라도 같은 최소 고정점에 도달합니다. 실행 방법은 달라질 수 있지만 무엇을 정답으로 볼지는 유지됩니다.
여기까지 오면 온톨로지와의 연결도 보이기 시작합니다. 나비의 예제에서 우리는 단순히 문자열 세 개를 다루지 않았습니다. 고양이, 포유류, 동물이라는 개념을 정했고, 그 사이의 포함 관계를 정했으며, 그 관계가 개별 대상의 분류에 어떤 영향을 주는지도 정했습니다. 지식 표현에서 말하는 온톨로지는 이렇게 어떤 영역을 설명할 개념과 관계, 그리고 그에 관한 공리를 명시하는 일과 관련됩니다. 이름표만 모으는 데서 한 걸음 더 나아가, 그 이름표를 붙였을 때 무엇이 따라오는지도 함께 다룹니다.
“나비는 고양이다”는 개별 대상에 대한 사실입니다. “고양이는 포유류의 한 종류다”는 개념 사이의 관계입니다. 둘을 연결하는 규칙이 있으므로, 개념에 관한 지식이 개별 대상에 관한 새로운 결론으로 이어집니다. 여기에 고정점이 등장하는 이유는 분류 한 번으로 의미의 전파가 끝나지 않기 때문입니다. 포유류라는 분류는 다시 동물이라는 분류를 요구합니다. 이처럼 온톨로지의 일부를 Datalog 규칙으로 표현했을 때, 고정점 계산은 그 규칙의 영향을 더 이상 빠뜨리지 않을 때까지 따라가는 방법이 됩니다.
그래서 동물을 찾는 질문에 나비를 포함할 수 있습니다. 원래 기록에 “나비는 동물이다”라는 문장이 직접 없었어도, 우리가 정한 개념 관계를 존중하면 그 결론이 따라오기 때문입니다. 이때 직접 적어 놓은 사실에 도출 가능한 사실을 모두 보탠 상태를 규칙에 대한 폐쇄(closure)라고 부릅니다. 고정점은 그 폐쇄가 이루어진 상태를 표현합니다. 온톨로지는 어떤 결론이 따라야 하는지를 규정하는 데 기여하고, 고정점은 규칙으로 표현된 그 요구를 어디까지 반영했는지 말해 줍니다.
다만 모든 온톨로지가 Datalog로 표현되는 것은 아닙니다. 온톨로지 언어마다 표현할 수 있는 내용과 추론 방식이 다릅니다. 실제 연결 사례로는 규칙 기반 추론 엔진으로 구현할 수 있도록 설계된 OWL 2 RL이 있습니다. 이는 둘 사이에 실용적인 접점이 있다는 뜻이지, OWL 전체의 의미론을 앞의 규칙 하나나 기본 Datalog로 대체할 수 있다는 뜻은 아닙니다.
또한 폐쇄에 도달했다고 해서 세상의 모든 사실을 알게 된 것도 아닙니다. 나비가 검은색인지, 오늘 어디에 있는지는 이 사실과 규칙만으로 알 수 없습니다. 특히 OWL의 열린 세계 가정에서는 도출하지 못한 문장을 곧바로 거짓이라고 판단하지 않습니다. “나비가 검은색이라고 알 수 없다”와 “나비는 검은색이 아니다”는 다른 말입니다. 이 구분은 OWL 2 Primer에서도 강조합니다. 규칙에 대한 폐쇄와, 기록에 없는 것을 거짓으로 취급하는 닫힌 세계 가정은 서로 다른 문제입니다.
고정점은 지식이 영원히 굳었다는 뜻도 아닙니다. 새 사실이나 새 규칙이 들어오면 다시 결론을 구해야 합니다. 잘못된 전제를 넣었다면 그 잘못도 규칙을 따라 퍼질 수 있습니다. 고정점이 보장하는 것은 현실에 대한 진실성 자체가 아니라, 주어진 전제와 규칙으로 할 수 있는 도출을 끝까지 반영했다는 것입니다.
Datalog를 이야기하다가 고정점을 만나고, 다시 온톨로지를 만나게 되는 것은 결국 같은 질문을 서로 다른 자리에서 다루기 때문입니다. 우리는 무엇을 사실로 받아들이고, 그 사실에서 무엇이 따라온다고 정하며, 그 결과를 어디까지 우리의 지식에 포함할 것인가. 나비를 고양이라고 적는 일은 시작입니다. 고양이라는 말에 담아 둔 관계를 따라 나비가 동물이라는 결론까지 받아들이는 순간, 기록은 추론으로 이어집니다. 최소 고정점은 그 추론이 근거 없이 앞서가지도, 따라야 할 결론을 남겨 두지도 않도록 경계를 정해 줍니다.
Subscribe via RSS
Comments