의미론과 디지털 스레드: 온톨로지와 지식그래프
📍 현재 위치: 3부 · 저장과 연결 — 19장. 히스토리안(historian, 공장의 모든 센서 측정값을 기록하는 시계열 데이터베이스)은 수치를 담고 관계형 모델은 배치(batch)를 담습니다. 이 장에서는 배치, 장비, 원료, 레시피, 결과 등 모든 것을 하나의 탐색 가능한 그래프로 엮어, 단 하나의 질문으로 제품 수명주기 전체를 따라 걸을 수 있게 합니다.
관계형 데이터베이스는 서류 캐비닛입니다. 어느 서랍을 열어야 할지 이미 알고 있다면 더없이 훌륭합니다. 하지만 조사관의 질문은 "7번 서랍 안에 뭐가 있지?"가 아닙니다. "이 의약품(drug product) 바이알이 출하 시험을 통과하지 못했다. 여기까지 오는 동안 거쳐 간 모든 것을 보여 달라"입니다. 이는 조회(lookup)가 아니라 걷기(walk)입니다. 의약품 → 원료의약품(drug substance) → 캡처 풀(capture pool) → 바이오리액터 배치 → 종균 배양(seed train) → 모든 것이 시작된 셀뱅크(cell bank)로 이어집니다(이 제조 단계들은 아래 "그래프로 바꿀 데이터"에서 순서대로 다시 이름이 불리며, 1권의 생산 바이오리액터 장에서 물리적 작업으로서 하나씩 걸어 봅니다). 지식그래프(knowledge graph)는 이러한 관계를 일급(first-class) 사실로 저장합니다 — DS-001 derivedFrom PApool-001, PApool-001 derivedFrom BATCH-2026-001 — 그래서 단 한 번의 쿼리로, 몇 단계가 됐든 데이터베이스가 직접 그 사슬을 따라가게 할 수 있습니다. 처음부터 끝까지 추적한 그 사슬, 그것이 바로 업계에서 말하는 디지털 스레드(digital thread)입니다.
이 장에서 다루는 내용
17장에서는 히스토리안을 PostgreSQL의 배치와 결합했고, 4장에서는 ISA-88/95(배치 제어와 제조 운영 표준)를 관계형 테이블로 모델링했습니다. SQL 조인(join)은 질문의 형태를 미리 알고 있을 때 완벽합니다. 그러나 질문이 재귀적(recursive, 자기 자신의 결과에 스스로를 다시 적용합니다 — 링크를 따라간 다음, 그 자리에서 다음 링크를 따라가고, 그렇게 계속합니다)이고 시스템을 가로지르는 순간 어색해집니다. "이 로트(lot)는 무엇으로부터 파생되었는가, 몇 단계를 거치든 끝까지 거슬러 올라가서?"가 그런 질문입니다. 이 장은 정확히 그러한 부류의 질문에 답합니다.
우리는 다음을 할 것입니다.
- 공장을 작은 온톨로지(ontology) — 배치, 캡처 풀, 종균 배양이 무엇인지에 대한 공유되고 기계가 읽을 수 있는 어휘 — 로 모델링하고, 모두가 수렴해 가는 개방형 표준(RDF, 산업 온톨로지 파운드리, Allotrope, QUDT)에 어떻게 정렬되는지 설명합니다.
- 관계형 사실(배치, 로트 계보, 출하 결과)을 실제로 동작하고 검증된 파이썬(Python)으로 RDF 지식그래프에 적재합니다.
- 한 배치의 전체 계보를 단 하나의 구문으로 추적하는 SPARQL 디지털 스레드 쿼리를 실행하고 실제 출력을 살펴봅니다.
- 단일 LinkML 모델 하나로 SHACL, JSON 스키마, SQL, OWL을 생성하여, 그렇지 않았다면 따로따로 어긋나는 네 벌의 사본으로 손수 유지해야 할 스키마들을 대신하는 방법을 보입니다.
- 그리고 오픈소스 트리플스토어(triplestore)가 진정으로 운영 수준에 도달한 지점과 GxP 래퍼(wrapper)(GxP는 규제 당국이 검사할 수 있는 모든 데이터를 관장하는 "Good x Practice" 품질 규제의 우산입니다 — 제조에는 GMP, 실험실에는 GLP, 임상에는 GCP. 그 래퍼는 이들을 충족하기 위해 엔진 주위에 구축하는 검증되고 변경 통제되는 계층입니다)가 여전히 직접 구축해야 할 영역을 솔직하게 짚습니다.
이 장의 실행 가능한 코드는 단 하나의 파일 — examples/chapters/16-semantics-knowledge-graph/build_graph.py — 이며, RDFLib로 인프로세스(in-process)에서 그래프를 구축하므로 서비스가 전혀 없이도 노트북에서 실행됩니다. 온톨로지 Turtle, SHACL 형상(shape), Apache Jena Fuseki 배포는 등장하는 곳에서 예시용 구성으로 제시됩니다. 이들은 이 그래프를 공장 규모로 제공하는 방식이며, 그렇게 명시되어 있습니다.
왜 그래프인가, 이미 SQL이 있는데
이 책에서 지금까지 다룬 모든 것은 테이블 안에 살았고, 테이블은 우리에게 충분히 잘 봉사해 왔습니다. 그렇다면 굳이 왜 그래프를 더하나요?
의미(meaning)는 시스템 사이를 스스로 건너가지 않기 때문입니다. 바이오리액터 DCS(distributed control system, 분산 제어 시스템 — 장비 제어 소프트웨어)는 측정값을 BR101.Temp.PV라고 부르고, LIMS(laboratory information management system, 실험실 정보 관리 시스템)는 같은 로트를 DS-2026-001이라고 부르며, ERP(enterprise resource planning, 전사적 자원 관리 — 사업/재고 시스템)는 그것을 원료 1000457이라고 부르고, CofA(Certificate of Analysis, 분석 성적서) PDF는 "Lot 26-001"이라고 부릅니다. 각 시스템은 내부적으로는 일관되지만 서로 알아듣지 못하며, 그래프는 이들을 화해시키는 공유 모델이 거주할 수 있는 장소입니다.
그래프의 데이터 모델은 RDF(Resource Description Framework, 자원 기술 프레임워크)이며, 모든 사실을 트리플(triple) — 주어, 술어, 목적어 — 로 표현합니다 [1]. DS-001 derivedFrom PApool-001이 하나의 트리플입니다. BATCH-2026-001 monomerPct 98.611이 또 다른 트리플입니다. 새로운 관계를 추가할 때 마이그레이션해야 할 고정된 스키마(schema)란 없습니다. 그냥 트리플을 더 추가하면 됩니다. 그리고 관계 자체가 외래 키(foreign key)에 의해 암시되는 것이 아니라 데이터로 저장되기 때문에, RDF를 위한 W3C 쿼리 언어인 SPARQL로 그래프에게 그 관계들을 재귀적으로 순회하도록 요청할 수 있습니다 [2] — SQL은 어색한 재귀 CTE(common table expression, 공통 테이블 표현식 — 이름이 붙은 자기 조인 서브쿼리로, 이 장 뒷부분에서 보입니다)로만, 그것도 미리 작성해 두었을 때만 할 수 있는 일입니다. 누구나 추가할 수 있는 그래프에는 관문도 필요하기에, RDF에는 형상에 대해 트리플을 검증하는 제약 언어인 SHACL이 함께 따라옵니다 [3]. SHACL은 이 장 뒷부분에서 본격적으로 활용합니다.
그래프의 해부: 트리플, 그리고 노드
무언가를 구축하기 전에, 나머지 모든 것이 만들어지는 두 가지 구조를 해부해 둘 만합니다 — 그래프 전체가 말 그대로 이 두 가지의 반복일 뿐이기 때문입니다. 트리플은 원자(atom)이고, 노드(node)는 주어를 공유하는 트리플들의 분자(molecule)입니다. 둘 다 곧 읽게 될 로더가 기록하므로, 아래의 모든 필드는 교과서적 추상이 아니라 코드가 실제로 방출하는 사실입니다.
트리플의 해부: 주어, 술어, 목적어
트리플은 가능한 가장 작은 사실입니다. 세 칸, 그 이상은 없습니다. 로더의 마지막 루프는 g.add((BP[r.batch_id], BP.monomerPct, Literal(float(r.value), datatype=XSD.float)))를 기록하며, 그 한 줄이 바로 아래에서 해부하는 트리플입니다. 그 주어(subject)는 한 데이터베이스 밖에서는 아무 의미도 없는 로컬 기본 키(primary key)가 아니라 전역적으로 고유한 IRI(Internationalized Resource Identifier, 국제화 자원 식별자 — 웹 방식의 전역 이름, 여기서는 bp:BATCH-2026-001)입니다. 그 술어(predicate)는 관계를 이름 짓는, 온톨로지에서 가져온 역시 IRI(bp:monomerPct)입니다. 그리고 그 목적어(object)가 바로 RDF가 그래프라는 단어를 얻어 내는 지점입니다. 목적어는 타입이 부여된 리터럴(literal) — 어떤 종류의 값인지 고정하는 xsd(XML Schema) 또는 QUDT 데이터타입 태그가 붙은 어휘 형태(98.611) — 이거나, 아니면 또 다른 IRI이며, 후자라면 그 트리플은 걸어갈 수 있는 엣지(edge)입니다(bp:DS-001 bp:derivedFrom bp:PApool-001). 그 단 하나의 갈림 — 리터럴이냐 자원이냐 — 이 컬럼 값과 외래 키(foreign key, 값이 다른 테이블의 행을 가리키는 데이터베이스 컬럼)의 차이인데, 다만 RDF는 둘 모두를 일급(first-class)의, 주소 지정 가능한(addressable) 데이터로 저장한다는 점이 다릅니다.
하나의 트리플, 필드별로: 주어와 술어는 IRI이고, 목적어는 타입이 부여된 리터럴(값)이거나 또 다른 IRI(걸어갈 엣지)다 — 바로 이 갈림이 RDF를 테이블이 아니라 그래프로 만든다.
저자가 AI의 도움을 받아 직접 제작한 그림입니다.
Batch 노드의 해부: 하나의 노드는 트리플의 묶음이다
RDF에는 별도의 "노드" 객체가 없습니다. 노드란 그저 주어를 공유하는 모든 트리플을 함께 본 것입니다. BATCH-2026-001을 로더가 기록하는 그대로 가져오면, 그 하나의 IRI를 주어로 공유하는 여섯 개의 트리플이 있습니다. 그것은 두 번 타입이 부여됩니다 — 배치 루프에서 온 rdf:type bp:Batch와 계보 루프에서 온 rdf:type bp:Bioreactor — 이는 디지털 스레드 출력이 드러내는 솔직한 두 클래스 사실입니다. 이는 중복이나 버그가 아닙니다. RDF는 한 사물이 동시에 여러 클래스에 속하도록 허용하며, 이 사물은 진정으로 생산 배치(제조 운전)이면서 동시에 바이오리액터(그것을 운전한 용기)이고, 각각이 서로 다른 소스 루프에 의해 단언됩니다 — 그래서 그래프는 하나의 라벨을 강요하는 대신 둘 다 충실히 간직합니다. 그것은 rdfs:label, "PASS"라는 bp:releaseStatus, 98.611^^xsd:float인 bp:monomerPct CQA(Critical Quality Attribute, 핵심품질특성)(^^는 데이터타입을 붙이는데, 여기서는 32비트 float입니다), 그리고 부모 종균 SEED-001로 밖으로 가리키는 하나의 아웃바운드 bp:derivedFrom 엣지를 지닙니다. 계보는 반대 방향으로도 흐릅니다. 자식 풀 PApool-001이 인바운드 bp:derivedFrom(과 편의용 bp:fromBatch)을 지니고 이 노드를 향해 가리키는데, 그 엣지들의 주어는 이 배치가 아니라 자식입니다. 노드를 펼쳐 보면 그래프의 비결 전체가 하나의 주소에 보입니다 — 혈통과 품질, 들어오고 나가는 것, 모두 같은 주어들 위의 트리플로서. 이것이 바로 연결성(connectivity) 장의 "하나의 노드, 완전히 펼친" 측정값 카드의 그래프 대응물입니다.
같은 노드를 완전히 펼친 것: Batch 노드는 그저
bp:BATCH-2026-001을 주어로 공유하는 트리플의 묶음이다 — 두 개의 타입 트리플, 라벨, 출하 상태, 모노머 CQA, 그리고 아웃바운드 derivedFrom 엣지이며, 자식 풀에서 오는 인바운드 엣지들이 그것을 스레드 안의 허브로 만든다.
저자가 AI의 도움을 받아 직접 제작한 그림입니다.
이 노드는 앞선 두 권의 책이 도달하는 오픈소스 종착점입니다. 이것이 대표하는 물리적 사물 — 세포를 키우고 수확물을 생산한 교반 탱크 운전 — 은 1권의 생산 바이오리액터 장의 주제이며, 그 용기가 바로 이 노드의 bp:Bioreactor 타입입니다. monomerPct 98.611 같은 사실이 스프레드시트의 벌거벗은 셀이 아니라 스스로를 기술하는 FAIR 레코드로서 시스템 사이를 건너가야 한다는 발상은 2권의 온톨로지와 FAIR 데이터 및 의미론적 상호운용성 장에서 제기한 열린 과제이며, 혈통 걷기 자체는 거기서 디지털 스레드와 트윈으로 그려집니다. 그 두 권이 하나의 단계와 하나의 데이터 포인트로 서술한 것을, 이 페이지의 트리플들이 마침내 구현하는 실행 가능한 코드입니다.
온톨로지: 사물이 무엇인지에 합의하기
사실들을 연결하기 전에, Batch가 무엇인지, CapturePool이 무엇인지, 그리고 derivedFrom이 자식을 부모에 연결한다는 것을 말해 주는 어휘가 필요합니다. 그 어휘가 온톨로지(ontology)입니다. 아주 작은 로컬 온톨로지를 직접 쓸 수도 있고 — 우리도 그렇게 할 것입니다 — 하지만 진짜 가치는 업계의 나머지가 이미 공유하는 온톨로지에 닻을 내릴 때 나옵니다.
여기서 중요한 세 가지 개방형 표준이 있고, 이들은 층층이 쌓입니다.
- Allotrope(AFO)는 실험실 분석(laboratory-analytics) 온톨로지입니다. 결과, 기기, 시료, 방법에 표준화된 의미를 부여하므로, HPLC 결과는 그것이 여러분의 LIMS에서 나왔든 위탁 실험실에서 나왔든 동일한 것을 의미합니다 [4].
- QUDT는 모든 양(quantity)과 단위(unit)에 타입을 부여하므로,
98.611 %는 숫자에 덧붙은 문자열이 아니라 기계가 읽을 수 있는 사실로서 그 차원(dimension)과 단위를 지닙니다 [5]. 단위 없는 값은 일어나기를 기다리는 미래의 일탈(deviation)입니다. - 산업 온톨로지 파운드리(Industrial Ontologies Foundry, IOF)는 기초 형식 온톨로지(Basic Formal Ontology, BFO, ISO/IEC 21838-2 최상위 온톨로지)에 기반한 제조용 Core 온톨로지를 발행합니다 — BFO는 객체(object), 공정(process), 품질(quality) 같은 도메인 중립적인 뿌리 범주의 아주 작은 집합으로, 더 구체적인 모든 클래스가 거기에 매달리며, 바로 이것이 독립적으로 구축된 온톨로지들조차 각 용어가 어떤 종류의 사물인지에 합의하게 합니다. 이는 장비, 원료, 공정, 그리고 그들 사이의 관계에 대한 원칙에 입각한 중간 수준의 어휘이며, 바로 우리의 mAb(monoclonal antibody, 단클론 항체) 공정 같은 생산 라인을 겨냥한 바이오의약품 릴리스(BMIC, IOF의 Biopharma Manufacturing Industrial Content)를 포함합니다 [6]. 그 설계 근거(공유된 상위 기반이 왜 "모두가 각자의 클래스를 발명하는" 혼란을 막는지)는 IOF Core 논문에 정리되어 있습니다 [7]. 한 가지 솔직한 범위 단서: BMIC와 이 상위 온톨로지들은 의도와 구조 — 레시피, 규격(specification), 장비, 그리고 그들 사이의 CPP(Critical Process Parameter, 핵심공정변수 — 그 설정이 품질 결과에 영향을 주는 온도·pH·공급 속도 같은 공정 입력)/CQA 관계 — 를 모델링하며, 측정된 원시 배열 자체는 담지 않습니다. 스펙트럼이나 크로마토그램은 트리플로 펼쳐지는 것이 아니라, 그래프가 IRI로 참조하는 Allotrope 또는 AnIML 파일에 삽니다.
운영 환경에서는 그러한 온톨로지를 임포트(import)하고 여러분의 공장을 거기에 매핑(map)합니다. 노트북에서 실행 가능한 이 장에서는 작은 로컬 네임스페이스(namespace)를 유지하고 개념적으로 정렬합니다 — 이것이 리포지토리가 전반에 걸쳐 따르는 솔직한 패턴입니다. Turtle(RDF에서 가장 사람이 읽기 좋은 텍스트 형식)로 작성한 어휘는 다음과 같습니다. 읽기 위한 Turtle 두 가지: 벌거벗은 a는 rdf:type("is a")의 약어이고, 각 프리픽스(prefix)는 용어가 어느 어휘에서 왔는지를 이름 짓습니다 — rdf:/rdfs:는 RDF 자체의 핵심 스키마 어휘를, owl:은 RDF 위에 얹힌 더 풍부한 웹 온톨로지 언어(Web Ontology Language, OWL) 계층을 가리키며, OWL은 owl:TransitiveProperty 같은 표현력 있는 구문을 더합니다.
# Illustrative — platform/ontology/bioproc.ttl (the shape you would import & align to IOF/AFO).
@prefix bp: <https://example.org/bioproc#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
bp:Batch a owl:Class ; rdfs:label "Bioreactor batch" .
bp:CapturePool a owl:Class ; rdfs:label "Protein A capture pool" .
bp:SeedTrain a owl:Class ; rdfs:label "Seed train" .
bp:DrugSubstance a owl:Class ; rdfs:label "Drug substance lot" .
bp:DrugProduct a owl:Class ; rdfs:label "Drug product lot" .
bp:derivedFrom a owl:ObjectProperty , owl:TransitiveProperty ;
rdfs:comment "Child material/lot derived from a parent." .
bp:monomerPct a owl:DatatypeProperty ;
rdfs:comment "SEC %monomer release CQA, typed against QUDT." .
derivedFrom이 owl:TransitiveProperty로 선언된 것에 주목하세요. 그 단어 하나가 관계의 의미를 기록합니다. 그것은 의약품이 원료의약품으로부터 파생되고 그 원료의약품이 캡처 풀로부터 파생된다면, 의약품 또한 캡처 풀로부터 파생된다는 것을 — 추이적으로(transitively), 사슬의 맨 아래까지 — 말해 줍니다. 추론기(reasoner) — OWL 공리(axiom)를 읽고 그것들이 논리적으로 함의하는 새 트리플을 기록해 내는 별도의 소프트웨어 — 는 그 선언을 사용해 전체 폐쇄(closure)를 저장된 사실로 구체화(materialize)할 수 있습니다. 다만 노트북 환경인 이 장에서는, 추론기를 전혀 실행하지 않고 더 가벼운 방식으로 같은 끝에서 끝까지의 사슬을 얻습니다. 아래에서 만날 SPARQL (bp:derivedFrom)+ 프로퍼티 경로가 단언된(asserted) 엣지를 쿼리 시점에 직접 걷습니다(바로 그래서 로더는 단언된 91개의 트리플을 방출하고 추론기를 결코 호출하지 않습니다). 추이성은 관계의 선언된 의미이고, + 경로는 여기서 우리가 실제로 그것을 순회하는 방법입니다 — 동일한 사슬에 이르는 두 가지 상호 보완적인 경로이지, 같은 메커니즘이 아닙니다.
테이블에서 스레드로: 관계형 사실이 RDF 트리플로 적재되고, 계보는 derivedFrom 엣지의 사슬이 되며, 하나의 SPARQL 쿼리가 그 사슬을 걸어 로트의 전체 혈통을 재구성한다.
저자가 AI의 도움을 받아 직접 제작한 그림입니다.
그래프로 바꿀 데이터
그래프는 세 개의 커밋된 골든(golden) 데이터셋 — 이 책의 공정 시뮬레이터가 고정된 난수 시드(SIM_SEED=2026)로 생성한, 정본의 버전 관리된 참조 데이터 — 으로 구축되므로, 아래의 모든 숫자는 재현 가능합니다. 먼저, 배치와 그 출하 판정 — examples/datasets/batches.csv에서 가져옵니다.
batch_id,role,release
BATCH-2026-001,golden,PASS
BATCH-2026-002,sibling,PASS
BATCH-2026-003,sibling,PASS
BATCH-2026-004,sibling,OOS
BATCH-2026-005,sibling,PASS
BATCH-2026-006,sibling,PASS
release 컬럼은 각 배치의 판정입니다 — PASS, 또는 시드된 단 하나의 실패 BATCH-2026-004에 대한 OOS(Out Of Specification, 즉 허용 한계를 벗어난 결과)이며, 그 배치의 의약품 DP-004는 하류에서 숙주세포 단백질로 출하에 실패하는 로트입니다(그 스레드는 아래 "왜 중요한가"에서 따라갑니다).
둘째, 로트 계보 — 스레드 자체인 부모/자식 엣지 — 는 examples/datasets/lot_genealogy.csv에서 가져옵니다.
batch_id,child,child_type,parent,parent_type
BATCH-2026-001,SEED-001,seed_train,WCB-CHO-001,wcb
BATCH-2026-001,BATCH-2026-001,bioreactor,SEED-001,seed_train
BATCH-2026-001,PApool-001,capture_pool,BATCH-2026-001,bioreactor
BATCH-2026-001,DS-001,drug_substance,PApool-001,capture_pool
BATCH-2026-001,DP-001,drug_product,DS-001,drug_substance
이 다섯 행을 작은 공급망(supply chain)으로 읽어 보세요. 하나의 작업 세포은행(working cell bank)(WCB-CHO-001)이 종균 배양에 접종하고, 종균 배양은 바이오리액터 배치에 접종하며, 그 배치의 정제된 수확물(clarified harvest)은 Protein A 캡처 풀이 되고, 이것이 정제되어 원료의약품이 되며, 원료의약품이 충전되어 의약품이 됩니다. 모든 배치가 같은 WCB-CHO-001로 거슬러 올라간다는 점에 주목하세요 — 그 공유된 뿌리가 바로 캠페인(campaign) 전체에 걸쳐 셀뱅크 수준의 조사에 답할 수 있게 만듭니다. 위 목록은 BATCH-2026-001의 다섯 행만 보여 주지만, 파일은 여섯 배치 모두의 전체 계보를 담고 있으며, 바로 그래서 로더가 실행되면 구축된 그래프가 (열네 개가 아니라) 91개의 트리플로 나옵니다.
짚어 둘 정직한 바이오공정 단순화가 하나 있습니다. 이 다섯 행 사슬은 하류 전체 공정을 단일 capture_pool → drug_substance 홉으로 압축합니다. 충실한 계보라면 1권이 따라가는 모든 단위공정(unit operation)을 엮습니다 — 캡처 풀은 폴리싱(polishing, 잔존 응집체와 숙주세포 DNA를 제거하는 두 번째 직교 크로마토그래피 단계로, 1권의 폴리싱 크로마토그래피 장에 있습니다)을 거치고, 이어 두 개의 독립적인 바이러스 안전 단계 — 저pH 또는 세제 방식의 바이러스 불활화와 20나노미터 바이러스 여과 — 를 통과한 뒤, UF/DF가 이를 농축·완충액 교환하여 원료의약품으로 만듭니다. 각각은 고유한 derivedFrom 엣지를 가진 고유한 물질 노드이며, 이 속성이 이행적(transitive)이므로 혈통 걷기는 사슬이 다섯 홉이든 열두 홉이든 똑같은 방식으로 세포은행에 도달합니다 — 계보는 더 길어질 뿐, 구조적으로 달라지지 않습니다. 그래프는 또한 각 단계가 내놓는 증거 — 바이러스 여과 단계의 로그 감소값(log-reduction value), UF/DF 사이클의 최종 농도와 다이아볼륨(diavolume) 수 — 를 아래의 SEC 모노머 CQA가 배치에 매달리는 것과 똑같이 출하 관련 속성으로 그 노드에 매달아 얻습니다.
셋째, 출하 분석(assay) — examples/datasets/hplc_results.csv에서 가져옵니다.
batch_id,test,value,unit,spec_low,spec_high,result
BATCH-2026-001,SEC_monomer_pct,98.611,%,95.0,100.0,PASS
BATCH-2026-001,SEC_HMW_pct,1.287,%,0.0,3.0,PASS
BATCH-2026-001,CEX_main_pct,70.686,%,60.0,80.0,PASS
우리는 각 배치 노드에 SEC %monomer 결과를 핵심품질특성(Critical Quality Attribute, CQA)으로 매달아, 혈통 걷기와 품질 결과가 같은 그래프 안에 살게 할 것입니다. %monomer는 크기 배제(size-exclusion, SEC) 순도 결과 — 손상되지 않은 비응집 모노머인 항체의 분율 — 이며, 높은 값(98.6%)은 고분자량 응집체(high-molecular-weight aggregate, HMW, 여기서는 1.287%, 저분자량은 0.439%)가 적다는 뜻으로, 응집체가 면역원성(환자에게 원치 않는 면역 반응을 유발할 수 있음)을 띨 수 있기에 핵심 안전 CQA입니다. 솔직한 한 가지 단서: %monomer는 출하/원료의약품 단계의 SEC 순도 결과지만, 이 장의 단순함을 위해 로더는 그것을 상류의 바이오리액터 배치 노드(build_graph.py는 BP[r.batch_id]에 bp:monomerPct를 추가합니다)에 붙입니다. 충실한 모델이라면 원료의약품 로트에 붙였을 것입니다. 여기서는 바이오리액터 배치 자체가 모노머에 대해 분석되었음을 암시하기보다는 리포지토리의 단순화를 그대로 반영합니다.
RDFLib로 그래프 구축하기
완전한 동반 스택(companion stack)에서는 이 트리플들이 Apache Jena Fuseki — 영속적인 트리플스토어(RDF 트리플을 저장하고 쿼리하기 위해 특별히 만들어진 데이터베이스)에 의해 뒷받침되는 성숙한 오픈소스 SPARQL 1.1 서버 [8] — 에 의해 제공되며, 이는 아래에서 배포합니다. 그러나 이 장과 그 테스트를 서비스 없이 실행 가능하게 유지하기 위해, 우리는 RDF를 구성, 직렬화, 쿼리하는 파이썬 라이브러리인 RDFLib [9]로 동일한 그래프를 인프로세스에서 구축합니다. 여기 이 장의 핵심 — 로더 — 가 examples/chapters/16-semantics-knowledge-graph/build_graph.py에서 등장합니다. 세 개의 CSV를 읽고 트리플을 방출합니다.
from rdflib import Graph, Literal, Namespace, RDF
from rdflib.namespace import RDFS, XSD
DATA = Path(__file__).resolve().parents[2] / "datasets"
BP = Namespace("https://example.org/bioproc#")
def build_graph() -> Graph:
g = Graph()
g.bind("bp", BP)
batches = pd.read_csv(DATA / "batches.csv")
gen = pd.read_csv(DATA / "lot_genealogy.csv")
rel = pd.read_csv(DATA / "hplc_results.csv")
for _, b in batches.iterrows():
s = BP[b.batch_id]
g.add((s, RDF.type, BP.Batch))
g.add((s, RDFS.label, Literal(b.batch_id)))
g.add((s, BP.releaseStatus, Literal(b.release)))
각 g.add((subject, predicate, object))는 말 그대로 그래프에 기록되는 하나의 트리플입니다. (여기서 RDF.type과 RDFS.label은 저자가 직접 만든 술어가 아니라 RDFLib가 제공하는 표준 내장 RDF/RDFS 용어로, 위 Turtle의 그 rdf:type, rdfs:label과 동일합니다.) 첫 번째 루프는 모든 배치를, 출하 상태를 지닌 타입이 부여되고 라벨이 붙은 노드로 바꿉니다.
계보 루프가 바로 스레드가 짜이는 곳입니다. 각 행에 대해 자식과 부모 모두에 타입을 부여한 다음, 둘을 연결하는 derivedFrom 엣지를 추가합니다.
# genealogy edges: child bp:derivedFrom parent
for _, e in gen.iterrows():
child, parent = BP[e.child], BP[e.parent]
g.add((child, RDF.type, BP[e.child_type.title().replace("_", "")]))
g.add((parent, RDF.type, BP[e.parent_type.title().replace("_", "")]))
g.add((child, BP.derivedFrom, parent))
if e.parent_type == "bioreactor":
g.add((child, BP.fromBatch, BP[e.parent]))
.title().replace("_", "")는 CSV의 seed_train을 클래스 SeedTrain으로, capture_pool을 CapturePool로 바꿉니다 — 관계형 어휘에서 온톨로지 클래스로의 작고 결정론적인(deterministic) 매핑입니다. 마지막으로, 출하 결과는 XSD.float로 타입이 부여된(QUDT 정렬은 온톨로지에 삽니다) CQA를 붙여, 값의 데이터 타입이 추측되는 것이 아니라 명시적이 되게 합니다.
# release results: the SEC %monomer CQA attaches to the batch
monomer = rel[rel.test == "SEC_monomer_pct"] # %monomer purity, not titer (yield)
for _, r in monomer.iterrows():
g.add((BP[r.batch_id], BP.monomerPct, Literal(float(r.value), datatype=XSD.float)))
return g
그래프가 담지 않는 것
로더가 무엇을 기록하지 않는지도 똑같이 정확히 짚어 둘 만합니다. 그 경계는 누락이 아니라 설계 선택이기 때문입니다. BMIC와 그것이 기반하는 상위 온톨로지들은 규범적(prescriptive)입니다. 의도와 구조 — 레시피, 규격, 장비, 그리고 그들 사이의 CPP/CQA 관계 — 를 모델링하며, 측정된 원시 배열을 위한 클래스는 그 안에 없습니다. 스펙트럼은 대략 천 개의 강도-대-파수(wavenumber) 점이고, 크로마토그램은 조밀한 시계열이며, 설계 공간(design space)은 다차원 곡선입니다. 그 가운데 하나라도 주어-술어-목적어 트리플로 펼치면 그래프는 의미 없는 수백만 행으로 부풀고, 그러면서도 배열의 형상은 잃습니다.
그래서 무거운 수치 페이로드(payload)는 마땅히 있어야 할 곳 — 벤더 중립 분석 컨테이너 — 에 살고, 그래프는 그것을 삼키는 대신 IRI로 연결합니다. 형상마다 어울리는 세 가지 보완재가 있습니다. Allotrope ADF는 n차원 데이터 큐브(Data Cube)를 중심으로 구축된 HDF5 바이너리로, 스펙트럼, 크로마토그램, 곡선을 담습니다. AnIML은 ASTM 개방형 XML 형식으로, 동일한 배열을 그 SeriesSet에 담습니다. 그리고 Allotrope ASM은 JSON 형식으로, 스칼라(scalar)이며 기계 작동 가능한 결과를 담습니다. 그래프는 Batch-001 hasGlycanProfile <file://…/run.adf> 같은 트리플 — 배열이 아니라 항체의 글리칸 프로파일(glycan profile, 부착된 당 구조의 배열로, 그 자체가 하나의 품질 특성)을 가리키는 포인터 — 을 담으며, 동일한 패턴이 라이브 시계열을 위해 히스토리안 태그(hasTrace <opc.tcp://…>)를 가리킵니다. 이 형식들의 전체 목록과, LADS 서버나 LIMS가 그것들을 어떻게 방출하는지는 분석 실험실: 기기, LIMS, ELN 장의 주제입니다. 크로마토그래피 쪽 — 피크(peak)를 벤더 블롭(blob)이 아니라 ASTM ANDI/NetCDF로 내보내는 일 — 은 다운스트림 수집: 크로마토그래피와 여과 스키드 장에서 다룹니다.
바로 이것이 방금 읽은 로더가 동작을 나누는 이유입니다. 스칼라 출하 결과 — monomerPct 98.611 — 는 트리플로 깔끔하게 매핑됩니다. 타입이 부여된 단일 숫자는 그래프가 추론하고 SHACL로 제약할 수 있는 사실 자체이기 때문입니다. 그러나 스펙트럼이나 크로마토그램은 참조된 문서로 남습니다. 그래프는 배치가 그것을 가진다는 사실, 그것이 어디에 사는지, 그리고 그것이 무엇인지를 기록하고, 배열 자체는 ADF나 AnIML 파일에 남겨 둡니다. 그래프는 디지털 스레드의 색인이지, 그 위의 모든 숫자를 담는 창고가 아닙니다 — 그리고 그 역할 분담이야말로 그래프를 작고, 쿼리 가능하며, 솔직하게 유지하는 것입니다.
디지털 스레드 쿼리
이제 보상입니다. 그래프가 구축되었으니, RDF를 위한 W3C 표준 쿼리 언어인 SPARQL [2]로 하나의 재귀적 질문을 던집니다. 같은 파일에서 가져온 이 쿼리는 원료의약품 로트로부터 derivedFrom 사슬을 그 조상(ancestor)을 향해 걷습니다.
PREFIX bp: <https://example.org/bioproc#>
SELECT ?step ?type WHERE {
bp:DS-001 (bp:derivedFrom)+ ?step .
?step a ?type .
} ORDER BY ?type
(?step a ?type의 벌거벗은 a는 위 Turtle의 그 rdf:type 약어와 동일합니다 — SPARQL로도 그대로 이어지므로, 그 줄은 "그리고 각 단계가 어떤 클래스로 타입이 부여되었든 그것을 ?type에 바인딩하라"로 읽힙니다.) 하중을 지탱하는 토큰은 (bp:derivedFrom)+입니다. 그것은 SPARQL 프로퍼티 경로(property path)입니다. +는 "하나 이상의 derivedFrom 홉(hop)을 따라가라"를 뜻합니다. 그래서 이 한 줄은 "DS-001이 파생되어 나온 모든 단계를, 몇 단계 떨어져 있든 찾아라"라고 말합니다 — 평범한 SQL에서는 고통스러운 바로 그 재귀적 혈통 걷기입니다.
파일을 처음부터 끝까지 실행하면 — python chapters/16-semantics-knowledge-graph/build_graph.py — 이 실제 출력이 나옵니다.
graph: 91 triples
digital thread — what DS-001 derives from:
BATCH-2026-001 (Batch)
BATCH-2026-001 (Bioreactor)
PApool-001 (CapturePool)
SEED-001 (SeedTrain)
WCB-CHO-001 (Wcb)
그것이 그래프가 하나의 쿼리로 재구성한, 한 원료의약품 로트의 완전한 혈통입니다. 그것이 정제되어 나온 캡처 풀, 그것을 생산한 바이오리액터 배치, 그것에 접종한 종균 배양, 그리고 맨 뿌리의 작업 세포은행입니다. (Wcb는 CSV의 wcb — 작업 세포은행 — 에 .title() 변환을 적용한 것일 뿐이어서, CapturePool 같은 깔끔한 두 단어 클래스 옆에 놓이면 조금 어색하게 읽힙니다. 결함이 아니라 같은 결정론적 매핑입니다.) 배치가 두 번 나타나는데 — 한 번은 Batch로 타입이 부여되고(배치 루프에서), 한 번은 Bioreactor로(계보 루프에서) — 이는 그 자체로 다중 소스 모델링(multi-source modeling)의 솔직하고 충실한 모습입니다. 동일한 물리적 사물이 하나 이상의 클래스를 지니며, 그래프는 둘 다 기꺼이 담습니다. 이것이 바로 벤더(vendor)들이 "제조 지식그래프(manufacturing knowledge graph)"로 파는 것의 오픈소스 구현입니다.
출하 CQA가 같은 그래프 안에 살기 때문에, 걷기를 확장하여 조사관이 실제로 던지는 질문 — 그리고 그것의 품질 결과는 무엇이었는가? — 에 답할 수 있습니다. 동반 파일은 두 번째로 검증된 쿼리 THREAD_WITH_CQA_QUERY를 담고 있는데, 이는 같은 조상 방향으로 걸어 CQA를 지닌 상류 배치까지 가서 같은 그래프에서 그 %monomer를 읽습니다.
PREFIX bp: <https://example.org/bioproc#>
SELECT ?batch ?monomer WHERE {
bp:DS-001 (bp:derivedFrom)+ ?batch .
?batch bp:monomerPct ?monomer .
}
방향은 첫 번째 쿼리와 동일합니다. 모노머를 지닌 배치는 DS-001의 조상(ancestor)이므로, 첫 쿼리와 똑같은 (bp:derivedFrom)+ 경로로 조상을 향해 걷습니다 — 다른 것은 추가된 ?batch bp:monomerPct ?monomer 줄 하나뿐이며, 이것이 걷기를 CQA를 지닌 단 하나의 조상으로 좁혀 거릅니다. 처음부터 끝까지 실행하면 기원이 된 배치와 그 결과를 반환합니다.
lineage + quality — originating batch and its release %monomer:
BATCH-2026-001 monomerPct=98.611
혈통과 품질, 하나의 그래프, 하나의 쿼리. 이와 같은 쿼리 — 어휘가 반드시 답할 수 있어야 하는 질문 — 를 온톨로지 엔지니어는 역량 질문(competency question, CQ)이라고 부르며, 그들은 그 목록을 온톨로지의 인수 시험(acceptance test)으로 사용합니다. 각 CQ는 쿼리를 기대 결과와 짝지으므로 "모델이 좋은가?"가 의견이 아니라 기계적인 PASS/FAIL이 됩니다. 위의 두 쿼리는 교과서적인 CQ입니다 — 이 로트는 무엇으로부터 유래하는가? 와 그 기원 배치의 모노머는 무엇인가? — 이며, 4권에서는 거의 그대로 CQ-01과 CQ-03으로 등장하는데, 혈통 걷기의 기대 답이 특정 조상 개수로 고정되어 있어 올바른 집합을 더 이상 반환하지 못하는 모델은 빌드마다 눈에 띄게 실패합니다. 4권은 그러한 질문 23개를 실행 가능한 PASS/FAIL 점검으로 바꾸는데, 그 쿼리로서의 역량 질문 장에서 다루며, 아래의 derivedFrom 이행적 관계와 SHACL 출하 관문 자체가 검증되는 것도 같은 규율 — 요구사항을 실행 가능한 시험 그 자체로 표현하기 — 입니다.
사슬 걷기: 프로퍼티 경로가 실제로 어떻게 순회하는가
(bp:derivedFrom)+가 무엇을 하는지 정확히 짚어 둘 만합니다. +가 보이는 것보다 더 많은 일을 하고 있기 때문입니다. 벌거벗은 bp:derivedFrom은 정확히 한 홉(hop)만 매칭합니다 — DS-001의 바로 위 부모인 PApool-001까지이고, 그 이상은 아닙니다. + 한정사(quantifier)는 그것을 "임의 길이의, 하나 이상의 홉"으로 바꿉니다. 엔진은 DS-001에서 PApool-001로 derivedFrom을 따라가고, 그다음 PApool-001에서 BATCH-2026-001으로 다시, 그다음 SEED-001로, 그다음 WCB-CHO-001로 따라가며, 가는 길에 닿는 모든 노드에 ?step을 바인딩(binding)하고, 더 이상 나가는 derivedFrom 엣지가 없을 때만 멈춥니다. 여기서 혈통은 다섯 홉이지만, 쿼리는 결코 "다섯"이라고 말하지 않습니다 — "얼마나 깊든"이라고 말하며, 바로 그래서 같은 한 줄이 두 단계 사슬에도 스무 단계 사슬에도 수정 없이 동작합니다.
그 임의의 깊이가 SQL과의 하중을 지탱하는 차이입니다. PostgreSQL에서 같은 사슬을 걸으려면 재귀 공통 테이블 표현식(recursive common table expression) — 계보 테이블을 자기 자신에 다시 조인하고, DS-001으로 재귀를 시드하며, 조인이 더 이상 행을 반환하지 않을 때까지 각 새 세대를 결과에 합치는 WITH RECURSIVE — 을 작성합니다. 그것은 동작하지만, 재귀를 손으로 저작하고, 조인 컬럼의 이름을 짓고, 순환(cycle)을 직접 방어해야 합니다. SPARQL에서는 재귀가 곧 연산자입니다. 프로퍼티 경로는 또한 조합됩니다. (bp:derivedFrom)+은 하나의 경로 표현식이지만, 고정된 두 단계 홉을 위해 bp:derivedFrom/bp:fromBatch를 쓰거나 |로 대안(alternation)을 쓸 수 있어서, 쿼리 언어 자체가 절차적 조인에 인코딩하는 대신 걷기의 형태를 표현합니다. 그래프가 엣지를 데이터로 저장하므로, 순회는 프로그램이 아니라 쿼리입니다.
그래프로 그리면, 쿼리가 걷는 사슬은 그저 한 줄의 derivedFrom 엣지이고 출하 CQA가 배치에 매달려 있는 모습입니다 — 위의 semantics-knowledge-graph.svg 히어로가 그 혈통을 처음부터 끝까지 보여 줍니다.
계보는 모델이 분할해야 하는 그룹 키이기도 하다
이 동일한 derivedFrom 스레드는 이 데이터로 모델을 만드는 누구에게나 조용하지만 필수적인 일을 합니다. 바로 그래서 지식 그래프는 조사 도구 이상입니다 — 그것은 머신러닝이 스스로를 정직하게 검증하는 데 필요한 계보 기반(substrate)입니다. 캠페인 데이터로 소프트 센서나 출하 예측 모델을 학습시킬 때, 행 단위 무작위 학습/검증 분할은 가장 큰 죄(cardinal sin)입니다. 자매 배치는 독립적인 예시가 아니기 때문입니다. 이 캠페인의 모든 배치는 같은 WCB-CHO-001에서 유래하며, 종종 같은 미디어 로트와 같은 캡처 스키드를 공유하므로, 한 세포은행에서 나온 두 배치는 거의 쌍둥이입니다. 이들을 무작위로 분할하면 거의 쌍둥이가 학습/검증 경계 양쪽에 모두 들어가고 — 모델은 사실상 답을 본 셈이라 — 보고된 점수는 환상입니다. 해법은 그룹 분할(grouped split, 한 배치의 모든 레코드가 학습이나 검증 중 한쪽에 통째로 들어감)이며, 계보가 바로 그 그룹 키가 사는 곳입니다. 공유 세포은행으로 거슬러 가는 (bp:derivedFrom)+ 걷기가 곧 5권의 데이터 장이 기본값으로 삼고 모델과 검증 장이 GroupKFold와 중첩 교차검증(nested cross-validation)으로 바꾸는 leave-one-batch-out(또는 leave-one-cell-bank-out) 그룹화입니다. 일탈을 추적하는 그래프와 정직한 검증 폴드(fold)를 정의하는 그래프는 같은 그래프입니다.
두 가지 ML 규율이 같은 트리플에 더 매달립니다. 결과를 예측하는 모델에는 적용 영역(applicability domain, 새 배치가 모델이 학습한 배치들과 닮기라도 했는지 묻는 관문으로, 능력 밖일 때 추측을 거부하게 함)이 필요한데, 계보는 어떤 배치가 학습된 범위 밖(새 세포은행, 본 적 없는 스케일)에 있는지를 어떤 예측이 신뢰되기 전에 알려 줍니다. 그리고 모델이 배포되면, 그래프는 그 모델의 계보가 일급 사실로서 속하는 곳입니다 — 어떤 데이터셋 해시, 어떤 모델 버전, 어떤 CQA를 채점했는지가 다른 어떤 것과도 같은 트리플이므로, 나중의 감사가 출하된 로트에서 그것을 건드린 정확한 동결 모델까지 걸어갈 수 있습니다 — 5권의 MLOps 장이 구축하는 모델 계보와 표류(drift) 거버넌스입니다. 그 장은 또한 이 그래프가 곧게 유지하도록 돕는 구분을 그립니다. 공정 표류(살아 있는 세포가 배치마다 진짜로 떠도는 것)는 디지털 스레드가 보존해야 할 실제 제조 신호인 반면, 모델 표류(예측기가 그 움직이는 공정에 대해 낡아가는 것)는 탐지해야 할 결함이며 — 둘을 혼동하는 것이 모니터링 시스템이 헛경보를 울리거나 진짜 변화를 놓치는 경로입니다.
SHACL로 그래프 검증하기
누구나 트리플을 추가할 수 있는 그래프에는 관문이 필요합니다, 그렇지 않으면 썩습니다. 형상 제약 언어(Shapes Constraint Language, SHACL)는 형상 제약에 대해 RDF 그래프를 검증하는 W3C 표준입니다 — 예를 들어, 모든 Batch가 출하되었다고 주장하기 전에 반드시 출하 상태와 정확히 하나의 모노머 결과를 가져야 한다는 것을 강제하는 방식입니다 [3]. 이는 Allotrope의 데이터 모델이 자신의 온톨로지가 어떻게 적용될 수 있는지를 제약하기 위해 사용하는 것과 동일한 메커니즘입니다. 최소한의 형상은 다음과 같습니다.
# Illustrative (simplified from platform/ontology/shapes.ttl, which attaches
# monomerPct to bp:ReleaseShape on the drug-substance/product lot — see the L131 caveat).
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix bp: <https://example.org/bioproc#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
bp:BatchShape a sh:NodeShape ;
sh:targetClass bp:Batch ;
sh:property [ sh:path bp:releaseStatus ; sh:minCount 1 ;
sh:in ( "PASS" "OOS" "PENDING" ) ] ;
sh:property [ sh:path bp:monomerPct ;
sh:datatype xsd:float ; sh:maxCount 1 ] .
(대괄호 [ … ]는 익명의, 이름 없는 노드를 인라인으로 쓰는 Turtle의 방식 — 빈 노드(blank node) — 으로, 여기서는 각 프로퍼티 제약을 자체 IRI를 주지 않고 묶는 데 쓰입니다.) SHACL이 바로 지식그래프를 편리한 조회에서 데이터 품질 통제로 바꾸는 것입니다. 형상을 위반하는 트리플은 조사 도중 모순으로 떠오르는 대신 적재 시점에 잡힙니다.
검증 보고서: 트리플이 실패할 때 SHACL이 돌려주는 것
SHACL은 그저 예/아니오만 말하지 않습니다 — 그것은 검증 보고서(validation report)를 반환하는데, 이 보고서 자체가 RDF 그래프이므로 실패도 다른 어떤 사실과 마찬가지로 쿼리 가능합니다. 검증기를 실행하면(rdflib 생태계는 rdflib 위에 구축된 별도 패키지인 pyshacl을 제공하고, Fuseki는 같은 SHACL 엔진을 HTTP로 노출합니다) 깨끗한 그래프는 sh:conforms true만 보고하고 그 외에는 아무것도 보고하지 않습니다. 규칙 하나를 어기면 — 가령 어떤 Batch가 어쩌다 두 개의 bp:monomerPct 트리플을 지녀 위의 sh:maxCount 1 제약을 위반하면 — 보고서는 대신 sh:conforms false와 함께, 위반한 노드를 sh:focusNode(bp:BATCH-2026-001)에, 프로퍼티를 sh:resultPath(bp:monomerPct)에, 발화된 규칙을 sh:sourceConstraintComponent(sh:MaxCountConstraintComponent)에, sh:Violation인 sh:resultSeverity를, 그리고 사람이 읽을 수 있는 sh:resultMessage를 담은 sh:ValidationResult를 반환합니다. 그 하나하나가 모두 트리플이기 때문에, 보고서는 곧장 같은 그래프 기계장치로 되돌아옵니다. 스택 트레이스(stack trace)를 읽는 대신, 적재 전체에 걸쳐 "어떤 포커스 노드가 어떤 제약을 실패했는가"를 SPARQL로 쿼리할 수 있습니다. 그것이 제약 언어와 단언(assertion)의 차이입니다 — 실패가 예외를 단지 멈추게 하는 것이 아니라 라우팅(routing)할 만큼의 구조화된 맥락을 지니고 있습니다. sh:Violation은 적재를 차단하고, 더 부드러운 sh:Warning이나 sh:Info 심각도(severity)는 경계선상의 트리플을 플래그를 단 채 통과시키는데, 이것이 첫날부터 과거 데이터를 거부하지 않으면서 새 규칙을 단계적으로 도입하는 방식입니다.
하나의 모델, 여러 스키마: LinkML로 저작하기
한 걸음 물러서서, 이 책이 똑같은 몇 안 되는 개념을 위해 손으로 써 온 스키마를 세어 보세요. 출하 상태와 모노머 결과를 지닌 Batch는 위 온톨로지에서는 OWL 클래스이고, 방금 작성한 SHACL에서는 sh:NodeShape이며, 4장의 관계형 모델에서는 CREATE TABLE이고, CQA를 공급하는 실험실 결과의 경우 14장에서는 자체 스키마를 지닌 JSON 문서입니다. 네 개의 스키마, 네 개의 파일, 그리고 누군가 필드를 추가할 때마다 서로 어긋날 네 번의 기회. 이들을 보조 맞춰 주는 것은 아무것도 없습니다.
LinkML(Linked data Modeling Language)은 그 표류에 대한 오픈소스의 답입니다. 동료 심사를 거친 YAML 기반 데이터 모델링 프레임워크로, 로런스 버클리 국립연구소(Lawrence Berkeley National Laboratory)에서 개발되고 Apache-2.0으로 라이선스되며, 데이터를 FAIR하게 만들면서 "일회용 데이터 모델의 난립"을 억제하는 것을 명시적 목표로 삼습니다 [14]. 모델을 YAML로 한 번 저작하고 나머지를 생성합니다. RDFLib 로더와 달리, 이 LinkML 모델은 채택할 저작 패턴으로 제시되며 — 아직 동반 스택에 체크인되어 있지는 않습니다 — 그러니 아래의 파일 경로는 실행 가능한 것이 아니라 예시용으로 보십시오. bioproc.yaml로 모델링한 동일한 Batch:
# Illustrative — platform/model/bioproc.yaml (the one source every schema below is generated from).
id: https://example.org/bioproc
default_range: string
prefixes:
bp: https://example.org/bioproc#
qudt: http://qudt.org/schema/qudt/
classes:
Batch:
class_uri: bp:Batch
slots: [batch_id, release_status, monomer_pct]
slots:
batch_id:
identifier: true
release_status:
slot_uri: bp:releaseStatus
range: ReleaseStatus # enum -> SHACL sh:in + JSON Schema enum
monomer_pct:
slot_uri: bp:monomerPct # the SEC %monomer CQA
range: float
unit:
ucum_code: "%" # UCUM = Unified Code for Units of Measure; a unit can also carry a qudt: quantity-kind URI
enums:
ReleaseStatus:
permissible_values:
PASS:
OOS:
PENDING:
그 단일 파일로부터 LinkML 생성기(generator)들이 여러분이 손으로 써 온 산출물을 방출합니다. gen-shacl은 sh:NodeShape를 만들어 냅니다 — release_status의 enum이 바로 위에서 본 sh:in ( "PASS" "OOS" "PENDING" ) 제약이 됩니다. gen-json-schema는 JSON 인스턴스를 검증할 JSON 스키마를, gen-sqltables는 CREATE TABLE DDL(클래스 → 테이블, 슬롯 → 컬럼)을, gen-owl은 OWL 온톨로지를, gen-pydantic은 로더를 위한 타입이 부여된 파이썬 클래스를 방출합니다. 그리고 linkml-validate는 데이터 파일을 모델에 대해 직접 검증합니다. 검토된 하나의 소스, 그로부터 재생성되는 모든 하류 스키마.
이 장에 결정적으로, 슬롯은 자신의 의미를 지닐 수 있습니다. 위에서 본 slot_uri(그리고 슬롯을 다른 어휘의 동등한 용어에 가리키게 하는 동반 exact_mappings 목록)는 monomer_pct가 어떤 개념인지를 선언하게 하고, unit 메타슬롯은 QUDT 양종류(quantity-kind) URI를 참조할 수 있습니다 — 그래서 그래프가 의존하는 의미와 단위가 나중에 덧붙는 것이 아니라 모델 안에 고정됩니다 [14]. 한 가지 솔직한 경계: LinkML은 표준을 대체하는 것이 아니라 저작 계층입니다. 공식적인 LinkML-Allotrope 바인딩은 없습니다. 필드를 AFO 용어에 정렬하는 것은 임포트하는 기능이 아니라 여러분이 저작하는 매핑입니다. 여러분이 얻는 것은, SHACL 관문, 관계형 테이블, JSON 스키마, OWL 온톨로지가 서로 어긋나기를 멈춘다는 점입니다 — 네 가지 모두 변경 통제 하의 하나의 소스로부터 생성되기 때문이며, 이는 이 장이 거듭 경고하는 소리 없는 표류의 정반대입니다. 연구용 장난감도 아닙니다. 국립 마이크로바이옴 데이터 협업체(National Microbiome Data Collaborative)는 자신의 전체 메타데이터 표준을 LinkML로 모델링하며 [15], 이 프레임워크는 NCATS의 Translator가 수백 개의 생의학 데이터 소스를 조화시키는 데 쓰는 Biolink 모델에서 자라났습니다 [14].
하나의 스키마, 다섯 개의 생성 산출물:
bioproc.yaml이 단일 진실 공급원이며, gen-shacl·gen-json-schema·gen-sqltables·gen-pydantic·gen-owl이 그래프 관문, JSON 검증, 관계형 테이블, 타입이 부여된 파이썬 로더, OWL 온톨로지를 거기서 파생하므로 — 다섯 산출물은 결코 서로 어긋날 수 없습니다.
저자가 AI의 도움을 받아 직접 제작한 그림입니다.
규모에 맞게 제공하기: Fuseki와 Oxigraph
인프로세스 RDFLib는 한 장과 하나의 단위 테스트에는 완벽하지만, 공장은 많은 시스템이 쿼리할 수 있는 영속적인 SPARQL 엔드포인트(endpoint)가 필요합니다. 두 개의 오픈소스 스토어가 적합합니다.
- Apache Jena Fuseki는 성숙한 선택지입니다 — TDB 영속 트리플스토어에 의해 뒷받침되고 서비스로 실행되는 SPARQL 1.1 서버입니다 [8]. 동반 스택에서
semantics프로파일에 해당합니다. 막 띄운 Fuseki에는 데이터셋이 없으므로(compose 블록은ADMIN_PASSWORD만 설정합니다),digitalthread데이터셋을 한 번 — 관리 UI 또는POST /$/datasets로 — 만든 다음, 동일한 트리플을 적재하고/digitalthread/sparql을 HTTP로 노출하면 됩니다.
# Illustrative — load the graph into Fuseki (semantics profile);
# create the digitalthread dataset first (admin UI or POST /$/datasets).
curl -X POST --data-binary @bioproc.ttl \
-H "Content-Type: text/turtle" \
http://localhost:3030/digitalthread/data
- Oxigraph는 가볍고 임베드 가능한 대안입니다 — RocksDB에 의해 뒷받침되는 SPARQL 1.1 쿼리/업데이트 데이터베이스로, JVM(Java Virtual Machine, 자바 가상 머신 — Fuseki와 Jena는 그것이 실행되고 있어야 하는 자바 서비스이고, Oxigraph는 그렇지 않은 단일 네이티브 바이너리입니다) 없이 그래프 스토어를 원할 때 이상적입니다 [10].
이 둘은 모두 RDF 트리플스토어이며, 그 선택 자체가 짚어 둘 만합니다. 그래프 데이터베이스의 다른 큰 계열은 라벨드 프로퍼티 그래프(LPG) 로, 가장 널리 배포된 것은 Cypher로 질의하는 Neo4j이며, 시작이 쉽다는 이유로 많은 팀이 먼저 손을 댑니다. 우리는 RDF/SPARQL을 의도적으로 골랐습니다. 전역적으로 고유한 IRI, 공유 온톨로지(IOF / Allotrope / QUDT), 그리고 W3C 표준 질의·제약 언어야말로 그래프를 한 애플리케이션을 위한 빠른 스토어가 아니라 시스템과 사이트를 가로질러 상호운용 가능하게 만드는 것이기 때문입니다. (같은 표준 뒤에 공급업체 지원을 원한다면 상용 RDF 엔진도 있습니다 — Ontotext GraphDB와 AWS Neptune 등.) LPG 세계는 훌륭한 엔지니어링입니다. 다만 이 장이 다루는 교차 시스템 디지털 스레드와는 다른 것을 최적화할 뿐입니다.
리포지토리는 한 가지 실용적인 주의 사항을 기록합니다. 스택은 공식 Apache 이미지 apache/jena-fuseki:5.2.0을 사용하며, examples/platform/compose/compose.yaml에서 태그로 고정되고, 그에 해당하는 매니페스트 다이제스트(manifest digest)가 examples/platform/versions.lock에 함께 기록되어 있습니다(25장의 공급업체 등록부가 의존하는 패턴입니다). Fuseki에 한해서는 그 다이제스트 — 정확한 이미지 내용물의 암호학적 sha256:… 지문 — 가 VERIFY-BEFORE-USE 자리표시자(placeholder)로 남겨져 있습니다. 락 파일과 참조 아키텍처 라이선스 표 모두가 언급하듯, 커뮤니티 Fuseki 이미지가 리포지토리를 옮겼으므로, 다이제스트로 배포하기 전에 선택한 레지스트리 미러(registry mirror)에 대한 실제 다이제스트를 직접 해석하고 기록해야 합니다. 움직일 수 있는 :5.2.0 태그만 신뢰하는 대신 다이제스트로 고정하는 것이야말로, 모든 사이트가 바이트 단위로 동일한 이미지를 받도록 보장합니다.
어느 스토어를 선택하든, 더 깊은 보상은 이 3부작 전체를 관통해 온 것입니다. 전역적으로 고유한 식별자(globally unique identifier)를 갖춘 공유 어휘 위의 SPARQL 엔드포인트는, 진정으로 FAIR한 — 찾을 수 있고(Findable), 접근 가능하며(Accessible), 상호운용 가능하고(Interoperable), 재사용 가능하며(Reusable), 기계 작동성(machine-actionability)을 명시적 설계 목표로 삼는 — 데이터로 가는 가장 깔끔한 경로입니다 [11]. 다른 시스템이 맞춤형 통합 없이 쿼리할 수 있는 그래프, 그것이 상호운용성이며, 구체적으로 실현된 것입니다.
왜 중요한가
디지털 스레드는 전문 용어 자랑이 아닙니다. 그것은 현대적 조사의 문자 그대로의 메커니즘입니다. 스마트 제조를 위한 디지털 스레드에 관한 체계적 문헌 검토는, 의미론적 링크 — 지식그래프와 온톨로지 — 를 제품 수명주기 전반에 걸친 추적성(traceability)을 제공하는 연결 조직(connective tissue)으로 식별합니다 [12]. DP-004 — DS-004로부터 충전되었고, PApool-004를 거쳐 우리가 시드한 OOS 배치 BATCH-2026-004로 거슬러 올라가는 의약품 — 가 숙주세포 단백질(host-cell protein, HCP — 생산 세포가 흘려보내는 잔류 단백질로, Protein A 캡처 단계가 제거하도록 되어 있는 불순물. 전체 출하 파일은 그 배치에 대해 100.0 ng/mg 상한에 대비되는 128.0 ng/mg의 HCP 결과를 담고 있지만, 앞서 보인 세 행짜리 발췌는 SEC와 CEX 분석만 나열했습니다)에서 출하에 실패하면, 질문은 즉시 "혈통을 공유하는 다른 것은 무엇인가?"가 됩니다 — 그리고 캠페인의 모든 배치가 WCB-CHO-001로 추적되기 때문에, 그래프는 "같은 셀뱅크에서 나온 다른 로트는 무엇인가?"를 단 한 번의 순회로 답할 수 있습니다 — 그리고 공유 크로마토그래피 스키드(skid)까지 노드로 모델링하고 나면, 같은 순회가 "같은 캡처 스키드에서 운전된 로트는 무엇인가?"로 확장됩니다. 그것이 영향 분석(impact analysis)이며, 범위가 한정된 일탈과 맹목적인 캠페인 전체 격리(quarantine) 사이의 차이입니다.
이는 규제 당국에게도 중요한데, 혈통과 영향 분석이야말로 데이터 무결성(data integrity)과 조사 기대치가 요구하는 바로 그것이기 때문입니다. 즉, 임의의 기록에서 그것이 의존하는 모든 기록까지, 시스템을 가로질러, 재현 가능하게 걸어갈 수 있는 능력입니다 — **ALCOA+**와 21 CFR Part 11, EU Annex 11 같은 전자 기록 규칙이 요구하는 추적성과 귀속 가능한 기록(attributable-record)의 요건이며, 이 책은 구성에 의한 ALCOA+와 오픈소스로 구현하는 Part 11 / Annex 11에서 다룹니다. 지식그래프는 그 걷기를, 스프레드시트를 일주일간 상호 참조하는 일 대신 하나의 쿼리로 만듭니다.
실제 현장에서는
상용 벤더들은 이것을 "제조 지식그래프", "맥락화된 데이터 패브릭(contextualized data fabric)", 또는 "셀프서비스 데이터 레이어(self-service data layer)" 같은 이름으로, 대개 히스토리안의 자산 모델 위에 계층화하여 팝니다. 우리가 하나의 파이썬 파일과 하나의 Turtle 어휘로 구축한 것은, 모든 시스템이 말할 수 있는 개방형 표준으로 표현된 동일한 아이디어입니다 — 그리고 표준 수렴은 실재합니다. Allotrope, IOF/BMIC, QUDT는 정확히 한 사이트의 그래프가 다음 사이트에서도 이해 가능하도록 하기 위한 업계 노력입니다. 이것은 탁상공론도 아닙니다. 2026년의 동료 심사(peer-reviewed) 연구는 이질적인 공정 데이터를 통합하고 엔지니어가 매개변수-결과(CPP-to-CQA) 관계를 직접 쿼리할 수 있게 하는 바이오의약품 지식그래프를 구축했습니다 — 제조 지식그래프 아이디어가 실제로 작동한 것입니다 [13].
이제 솔직한 오픈소스 대 상용의 결산입니다. 그래프 기술은 오픈소스에서 진정으로 운영 수준입니다. RDF, SPARQL, SHACL, Fuseki, Oxigraph는 성숙하고, 표준을 준수하며, 라이선스 함정이 없습니다(Jena/Fuseki는 Apache-2, RDFLib는 관대한 BSD, Oxigraph는 MIT). 라이선스 비용 없이 진짜 디지털 스레드를 구축할 수 있습니다. 순수 오픈소스가 건네주지 않는 것은 GxP 래퍼입니다. 즉, 소스 시스템에서 그래프로의 검증된(validated) 변경 통제 매핑, GAMP-5(Good Automated Manufacturing Practice, 가이드 5 — 전산화 시스템을 검증하기 위한 업계 표준) 하의 트리플스토어에 대한 공급업체 책임, 그리고 적격성 평가(qualification — 시스템이 설치되어 의도대로 작동한다는 문서화된 증명을 가리키는 규제 산업 용어) 하에서 적재 과정이 완전하고 정확하다는 보증입니다. 여기서 적격성 평가는 크로마토그래피 스키드가 통과하는 것과 같은 IQ/OQ/PQ 사다리입니다 — 설치 적격성 평가(IQ)는 트리플스토어와 그 로더가 명시된 버전으로 배포되었음을 증명하고(위의 태그+다이제스트 고정이 바로 그것에 대한 기계 검증 가능한 증거를 줍니다), 운영 적격성 평가(OQ)는 적재 작업과 SPARQL 엔드포인트가 그 범위 전반에서 규격대로 동작함을 증명하며, 성능 적격성 평가(PQ)는 실제 데이터에서 알려진 혈통을 처음부터 끝까지 재현함을 증명합니다 — 그리고 이후 매핑, 온톨로지, 이미지 다이제스트에 대한 어떤 변경도 즉석 패치가 아니라 변경 통제 하에 그 사다리에 다시 들어갑니다. 현대 컴퓨터 소프트웨어 보증(Computer Software Assurance, CSA) 사고 하에서 그 시험의 깊이는 위험에 따라 조정됩니다 — 출하 결정에 정보를 주는 쿼리는 읽기 전용 편의 뷰보다 더 많은 스크립트 증거를 얻습니다 — 하지만 결코 생략되지 않습니다. 그래프는 규제 당국이 검사할 수 있는 기록을 담기 때문입니다.
그 검사 가능성이 바로 전자 기록 규정의 요구입니다. 21 CFR Part 11(전자 기록과 서명에 관한 미국 FDA 규정)과 EU Annex 11(그 유럽판 상대)은 GxP 기록을 담는 시스템이 귀속 가능하고(attributable), 감사 추적되며, 접근 통제되고, 정확한 데이터를 강제할 것을 요구합니다 — 그래서 적재 작업의 정합성 점검 로그, SHACL 관문의 검증 보고서, 트리플스토어 자체의 접근 통제는 있으면 좋은 것이 아니라 문자 그대로의 Part 11 / Annex 11 증거이며, 오픈소스로 Part 11 / Annex 11에서 깊이 다룹니다. 그리고 한 사이트의 그래프가 다음 사이트에서도 같은 의미여야 하므로, 같은 래퍼가 모델을 기술이전(tech transfer)을 통해 운반합니다. 검증된 매핑이 개발 사이트에서 상업 공장으로 옮겨질 때 — 또는 10리터 개발 바이오리액터에서 2000리터 생산 트레인으로 스케일업될 때 — IRI, 온톨로지 정렬, SHACL 형상은 변하지 않은 채 따라가므로, 수령 사이트는 어휘를 다시 발명하는 대신 자신의 시스템에 대해 적재만 재적격성 평가하면 됩니다. 그래프는 또한 파생된(derived) 뷰입니다 — 그 트리플은 진실의 관계형 기록에서 복사된 것이므로, 적재가 검증되고 변경 통제 하에 다시 실행되지 않는 한, 그래프는 자신이 반영한다고 주장하는 시스템으로부터 소리 없이 표류(drift)할 수 있으며, 이는 데이터 무결성의 정반대입니다. 이 책의 다른 곳에서와 마찬가지로, 오픈소스는 깔끔하고 검사 가능한 엔진을 건네줍니다. 그 주위의 검증된 시스템은 여러분이 직접 구축하거나 사야 합니다.
그래프가 거짓말할 때: 표류, FAIR 비준수, 그리고 온톨로지 난립
지식그래프가 개방형 표준을 쓴다는 이유로 자동으로 신뢰할 수 있다고 여기고 싶어집니다. 그렇지 않으며, 그것을 대비해 구축할 수 있도록 실패 양상을 짚어 둘 만합니다. 첫째는 표류(drift)입니다. 그래프는 파생된 사본이기 때문에, 적재 매퍼가 다루지 않는 컬럼이나 마지막 적재 이후 가해진 소스 수정은, 관계형 기록이 더는 말하지 않는 무언가를 그래프가 단언하게 남깁니다 — 조사관이 중요해질 때까지 알아채지 못할 수도 있는 모순입니다. 해법은 따분한 것입니다. 적재는 검증되고 변경 통제되는 작업으로, 알려진 트리거에서 다시 실행되며, 트리플 수를 소스와 대조해 맞춥니다(우리 로더가 graph: 91 triples를 출력하는 것은 바로 표류 점검이 대조할 숫자를 갖도록 하기 위함입니다).
둘째 실패는 더 조용하며, FAIR 운동이 존재하는 이유인 바로 그것입니다. 통신선 위에서의 표준 준수가 데이터가 실제로 찾을 수 있고, 상호운용 가능하며, 재사용 가능함을 보장하지는 않습니다 — 그리고 누군가 그것을 측정하면 그 간극은 큽니다. 2024년의 한 메타 연구(meta-research)는 COVID-19 연구 데이터셋을 FAIR 원칙에 대해 평가했고, 사실상 모두가 찾을 수 있었던 반면 상호운용성에서는 겨우 46.7%만이 중간 수준 이상의 준수에 도달했고 접근성에서는 21.5%였음을 발견했습니다 — 공개적으로 발행된 데이터셋의 대다수가, 공유 온톨로지가 제공해야 할 바로 그 "I"(Interoperability)를 통과하지 못한다는 뜻입니다 [16]. 원인은 트리플스토어인 경우가 드뭅니다. 통제 어휘(controlled vocabulary) 없이 저작된 메타데이터, 벌거벗은 문자열로 남겨진 단위, 전역적이 아니라 로컬인 식별자입니다. 그래프는 그 결함들을 입력으로부터 그대로 물려받습니다. 타입이 부여되지 않은 값을 적재하면, FAIR하게 보이지만 FAIR하지 않은 그래프를 얻습니다.
셋째는 온톨로지 난립(ontology sprawl) — IOF Core 논문이 막기 위해 쓰인 "모두가 각자의 클래스를 발명하는" 혼란입니다 [7]. 로컬 bp: 네임스페이스는 노트북 한 장 분량에는 괜찮지만, 모든 사이트가 미묘하게 다른 의미의 자기만의 derivedFrom을 만들어 낸다면, RDF를 라벨드 프로퍼티 그래프 대신 정당화했던 그 교차 사이트 상호운용성은 증발합니다. 이 세 가지 실패를 모두 막아 주는 규율은 이 장이 거듭 돌아오는 바로 그것입니다. 공유 상위 온톨로지(BFO, IOF, Allotrope, QUDT)에 정렬하고, 모델을 변경 통제 하에 한 번 저작하며(LinkML), 모든 적재를 SHACL로 차단하여 거짓말하는 트리플이 들어오기 전에 잡는 것입니다.
핵심 용어
- 온톨로지(Ontology) — 어떤 개체가 존재하는지(Batch, CapturePool)와 그것들이 어떻게 관계 맺는지(
derivedFrom)를 정의하는, 공유되고 기계가 읽을 수 있는 어휘. 서로 다른 시스템이 같은 것을 의미하게 하는 합의. - RDF(트리플)(RDF, triple) — 자원 기술 프레임워크(Resource Description Framework). 모든 사실이 주어-술어-목적어 트리플이며 고정된 스키마가 없어, 관계가 일급 데이터로 저장된다.
- 지식그래프(Knowledge graph) — 배치, 장비, 원료, 레시피, 결과를 하나의 탐색 가능한 전체로 연결하는 RDF 트리플의 그래프.
- 디지털 스레드(Digital thread) — 제품을 그 수명주기 전반에 걸쳐 추적하는 연결된 기록의 끝에서 끝까지 이어진 사슬. 여기서는 의약품에서 셀뱅크까지 거슬러 가는
derivedFrom혈통. - SPARQL 프로퍼티 경로(SPARQL property path) — 관계를 재귀적으로 따라가는 쿼리 연산자(예:
(derivedFrom)+)로, 임의 깊이의 혈통 걷기를 하나의 구문으로 가능하게 한다. - SHACL — 형상 제약 언어(Shapes Constraint Language). 형상 제약에 대해 RDF 그래프를 검증하여, 잘못된 트리플이 그래프에 들어오기 전에 데이터 품질을 강제한다.
- LinkML — 개방형 YAML 기반 데이터 모델링 언어. 모델을 한 번 저작하고 그로부터 SHACL, JSON 스키마, SQL DDL, OWL, Pydantic 클래스를 생성하여, 그 스키마들이 서로 어긋나기를 멈추게 한다.
- 기초 형식 온톨로지(Basic Formal Ontology, BFO) — IOF와 BMIC가 기반하는 ISO/IEC 21838-2 최상위 온톨로지로, 객체·공정·품질 같은 도메인 중립적 뿌리 범주를 정의하여 독립적으로 구축된 온톨로지들이 각 용어가 어떤 종류의 사물인지에 합의하게 함.
- IOF / Allotrope (AFO) / QUDT — 그래프가 정렬되는 개방형 표준들. 각각 제조 온톨로지, 실험실 분석 온톨로지, 단위-양 온톨로지다.
- 추이적 프로퍼티(Transitive property) — A→B와 B→C가 A→C를 함의하는 OWL 관계(
derivedFrom같은)로, 추론기(별도의 추론 소프트웨어)가 그 전체 사슬을 저장된 트리플로 구체화할 수 있고, 또는 — 이 장의 실행 가능한 코드에서처럼 — SPARQL(derivedFrom)+프로퍼티 경로가 같은 폐쇄를 쿼리 시점에 계산할 수 있다. - 트리플스토어(Triplestore) — SPARQL을 통해 RDF 트리플을 저장하고 쿼리하는 데이터베이스. 여기서는 Apache Jena Fuseki 또는 임베드 가능한 Oxigraph.
- IRI(국제화 자원 식별자, Internationalized Resource Identifier) — RDF가 모든 주어, 술어, 자원 목적어에 부여하는 전역적으로 고유한 이름(
bp:BATCH-2026-001). 로컬 기본 키와 달리 시스템과 사이트를 가로질러 같은 것을 의미하며, 이것이 그래프를 데이터베이스 로컬이 아니라 상호운용 가능하게 만든다. - SHACL 검증 보고서(SHACL validation report) — SHACL 실행이 돌려주는 RDF 그래프.
sh:conforms와 더불어, 각 실패에 대해 포커스 노드, 프로퍼티 경로, 제약, 심각도를 이름 짓는sh:ValidationResult를 담는다 — 예/아니오나 스택 트레이스가 아니라, 쿼리 가능하고 구조화된 예외. - GxP / GAMP-5 — GxP는 규제 당국이 검사할 수 있는 모든 데이터를 관장하는 "Good x Practice" 품질 규제(제조의 GMP, 실험실의 GLP, 임상의 GCP)의 우산이다. GAMP-5(Good Automated Manufacturing Practice, 가이드 5)는 그 데이터를 생산하거나 보관하는 전산화 시스템 — 트리플스토어와 그 적재 작업 같은 — 을 검증하기 위한 업계 표준이다.
- 적격성 평가(IQ/OQ/PQ) — 시스템이 사용에 적합함을 세 단계로 문서화한 증명. 설치 적격성 평가(IQ)(명시된 버전으로 배포됨), 운영 적격성 평가(OQ)(범위 전반에서 규격대로 동작함), 성능 적격성 평가(PQ)(실제 데이터에서 알려진 결과를 처음부터 끝까지 재현함). 그래프의 적재 작업과 SPARQL 엔드포인트는 크로마토그래피 스키드와 같은 사다리를 통과하며, 어떤 변경에서도 변경 통제 하에 다시 들어가고, 시험의 깊이는 CSA 하에서 위험에 따라 조정된다.
- 역량 질문(CQ) — 어휘가 반드시 답할 수 있어야 하는 질문으로, 기대 결과와 짝지어져 온톨로지를 기계적 PASS/FAIL로 채점한다. 이 장의 혈통 쿼리와 혈통+품질 쿼리가 CQ이며, 4권에서 PASS/FAIL 인수 시험으로 목록화·실행된다.
- 그룹(leave-one-batch-out) 분할 — 한 배치의 모든 레코드 — 이상적으로는 한 세포은행에서 나온 모든 배치 — 를 학습/검증 경계의 한쪽에 통째로 두어, 모델이 진정으로 본 적 없는 로트로 채점되게 하는 검증 규율. 계보의
derivedFrom걷기가 곧 그 그룹 키이며, 이를 무시한 행 단위 무작위 분할은 환상의 점수를 보고한다.
다음 이야기
우리는 데이터가 어디서나 같은 것을 의미하게 만들었고, 실제 캠페인 데이터에 대해 한 번의 쿼리로 디지털 스레드를 증명했습니다. 그러나 그래프는, 그 앞의 맥락화 뷰와 마찬가지로, 숫자가 이미 우리의 오픈 히스토리안에 산다고 가정합니다. 대부분의 공장은 수십 년의 공정 데이터를 담은 상용 히스토리안을 가지고 있습니다 — 그리고 그것이 우리가 다음에 건너갈 경계입니다. 20장 — 상용 히스토리안으로 다리 놓기: AVEVA/OSIsoft PI에서는 PI Web API로의 양방향 다리(충실한 목업에 대해, 운영 환경의 기본 URL과 자격 증명 교체를 문서화하여)를 구축하여, 오픈 스택과 진실의 검증된 상용 시스템이 마침내 데이터를 교환할 수 있게 합니다.