10차시 「사람의 지식을 적어 넣기」에서 IF-THEN 한 걸음을 손으로 돌려 보았습니다.
오늘은 그 한 걸음을 끝까지 이어 붙입니다.
두 마디만 알려 주었는데 기계가 '펭귄'이라는 말을 꺼내는 자리와,
기계가 스스로 멈추는 자리를 봅니다.
성취기준 12인기01-05
전건 긍정전향 추론연쇄고정점충돌 해소지식과 엔진의 분리
🎯 학습 목표
전건 긍정 한 걸음을 규칙 · 사실 · 새 사실 세 줄로 적을 수 있다.
전향 추론의 로그에서 연쇄가 일어난 자리와 멈춘 자리(고정점)를 짚을 수 있다.
엔진을 한 줄도 고치지 않고 규칙만 늘려 새 판정을 만들 수 있다.
🤔
여는 장면 — 두 마디를 넣었더니 네 마디가 나왔다
지난 10차시에 우리는 규칙 여섯 개를 적어 두고 apply_once()를 세 번 불렀습니다.
첫 번째 호출에서 새다가, 두 번째 호출에서 펭귄이다가 나왔고,
세 번째 호출에서는 "발동할 규칙이 없다"가 찍혔지요.
그 화면을 다시 봅시다. 우리가 기계에 알려 준 것은 딱 두 마디입니다.
깃털이 있다 · 헤엄친다 ← 우리가 넣은 것 2개
그런데 돌려받은 것은 네 마디입니다.
깃털이 있다 · 헤엄친다 · 새다 · 펭귄이다← 기계가 돌려준 것 4개
늘어난 두 마디 가운데 펭귄이다는 규칙표에 적혀 있으니 그렇다 칩시다.
그런데 그 규칙은 이렇게 생겼습니다.
IF 새다 AND 헤엄친다 → 펭귄이다
조건이 둘인데 우리는 '헤엄친다'만 알려 주었습니다.
'새다'는 한 번도 알려 준 적이 없어요. 그런데도 이 규칙이 발동했습니다.
💭 오늘의 물음
① '새다'는 어디서 왔는가?
② 기계는 언제 "이제 더 나올 것이 없다"고 판단하고 스스로 멈추는가?
첫 번째 물음의 답은 짧습니다 — 다른 규칙이 만들어 낸 사실입니다.
규칙 하나가 만든 결론이 다음 규칙의 조건 칸으로 들어간 것이지요.
이것을 연쇄(chaining)라고 부릅니다.
오늘은 이 연쇄를 화면에서 한 걸음씩 보고, 코드로 직접 돌려 봅니다.
킹펭귄 — 깃털이 있으니 새이고, 헤엄을 치니 펭귄이다.
기계는 '펭귄'이라는 낱말을 알아서 꺼낸 것이 아니라, 두 규칙을 이어 붙였을 뿐이다.
사진 속 펭귄을 한 번도 보지 못한 기계라도, 관찰 두 마디만 받으면 같은 이름에 닿는다.
출처: Godot13, Wikimedia Commons (CC BY-SA 4.0)
1
한 걸음의 최소 단위 — 전건 긍정
연쇄를 보기 전에 한 걸음을 정확히 적어 두겠습니다.
추론의 한 걸음은 언제나 세 줄로 적힙니다.
규칙IF 깃털이 있다 → 새다
사실깃털이 있다
새 사실새다
"A이면 B이다"라는 규칙이 있고 "A이다"라는 사실이 있으면 "B이다"를 새 사실로 얻습니다.
이 한 걸음에는 이천 년쯤 된 이름이 붙어 있어요 —
전건 긍정(modus ponens)입니다.
'전건'은 규칙의 앞쪽 조건, '긍정'은 그것이 참이라고 확인했다는 뜻입니다.
왜 이 한 걸음이 필요한가
규칙만 잔뜩 적어 두면 아무 일도 일어나지 않습니다.
규칙은 "조건이 갖춰지면 결론을 적어도 좋다"는 허가증일 뿐이거든요.
허가증이 실제 권한이 되려면 조건이 지금 사실 목록에 들어 있어야 합니다.
그래서 지식 베이스는 늘 두 종류로 나뉩니다.
이름
무엇인가
이 차시에서
바뀌는가
사실
지금 참이라고 아는 말
깃털이 있다 · 헤엄친다
추론하는 동안 늘어난다
규칙
참이 되기 위한 조건과 그 결론
IF 깃털이 있다 → 새다
추론하는 동안 그대로
10차시의 fires()가 하던 두 가지 검사를 떠올려 보세요.
① 조건이 전부 아는 사실인가, ② 결론이 아직 모르는 것인가.
①이 바로 전건 긍정의 '긍정'을 코드로 옮긴 것입니다.
②는 조금 뒤 고정점을 이야기할 때 다시 나옵니다.
반례 ① — 화살표를 거꾸로 읽으면 안 된다
규칙에는 방향이 있습니다. 이 규칙을 봅시다.
IF 새다 AND 빠르게 난다 → 매다
'매다'를 알게 되었다고 해서 '빠르게 난다'를 새 사실로 적으면 안 됩니다.
그렇게 적는 것을 후건 긍정의 오류(affirming the consequent)라고 해요.
사람은 이 실수를 자주 합니다 — "감기에 걸리면 열이 난다. 열이 나네? 그러면 감기다."
열이 나는 까닭은 감기 말고도 많은데 말이지요.
재미있는 것은, 전향 추론 엔진은 구조상 이 실수를 할 수 없다는 점입니다.
코드에서 조건이 담기는 자리(conds)와 결론이 담기는 자리(concl)가
아예 다른 변수이고, 엔진은 조건 자리만 검사하고 결론 자리에만 적기 때문입니다.
화살표를 거꾸로 따라가는 방법은 따로 있는데, 그것이 다음 12차시에 다룰
후향 추론입니다. 거꾸로 읽는 것이 아니라 거꾸로 묻는 것이지요.
반례 ② — AND 는 '전부'라는 뜻이다
우리 지식 베이스의 규칙 여섯 개 가운데 조건이 하나인 것은 둘(깃털 → 새다, 젖 → 포유류다)이고,
나머지 넷은 조건이 둘입니다. 조건이 둘이면 둘 다 사실이어야 발동합니다.
아는 사실
새다?
헤엄친다?
IF 새다 AND 헤엄친다 → 펭귄이다
깃털이 있다
O
X
발동 안 함
헤엄친다
X
O
발동 안 함
빠르게 난다
X
X
발동 안 함
깃털이 있다 · 헤엄친다
O
O
발동
세 번째 줄까지는 전부 "발동 안 함"입니다. 조건 하나가 모자란 규칙은 있으나 마나예요.
그런데 첫 줄을 보세요 — '깃털이 있다'만 아는 상태인데 '새다?' 칸이 이미 O입니다.
우리가 '새다'를 알려 준 적이 없는데도요. 바로 그 자리가 오늘의 핵심입니다.
2
결론이 또 조건이 된다 — 전향 추론
전향 추론(forward chaining)은
이렇게 굴러갑니다.
지금 아는 사실을 놓고 규칙 목록을 위에서 아래로 훑는다.
조건이 전부 갖춰졌고 결론이 아직 모르는 규칙을 하나 찾으면, 그 결론을 사실 목록에 적는다.
다시 1번으로 돌아간다. 사실이 하나 늘었으니 아까 안 되던 규칙이 될 수도 있다.
한 바퀴를 다 돌았는데 발동할 규칙이 하나도 없으면 멈춘다.
3번이 오늘의 전부입니다. 여는 장면의 물음에 대한 답이 여기 있어요.
연쇄가 일어나는 자리. 분홍 화살표 하나가 오늘의 전부다 —
규칙 ①이 만든 '새다'가 규칙 ④의 조건 칸으로 들어가는 순간,
아까까지 조건이 하나 모자라던 규칙 ④가 발동할 수 있게 된다.
이 그림을 코드가 찍어 주는 로그로 옮기면 이렇게 됩니다. 오늘 실습에서 여러분 화면에 나올 두 줄이에요.
[1] IF 깃털이 있다 -> 새다
[2] IF 새다 AND 헤엄친다 -> 펭귄이다
더 나올 사실이 없다. 여기서 멈춘다.
[1]의 오른쪽 끝과 [2]의 왼쪽 끝에 같은 말이 있습니다.
이것이 로그에서 연쇄를 찾는 방법이에요. 결론으로 나온 말이 다음 줄의 조건에 다시 등장하면,
그 자리에서 규칙이 규칙을 부른 것입니다.
관찰을 바꾸면 결론이 바뀐다
지식 베이스는 그대로 두고 관찰만 바꿔 세 번 더 돌린 결과입니다.
세 경우 모두 2단 연쇄가 일어납니다.
관찰한 사실
1단계
2단계
발동 횟수
깃털이 있다 · 빠르게 난다
새다
매다
2
젖을 먹인다 · 헤엄친다
포유류다
돌고래다
2
젖을 먹인다 · 초음파를 쓴다
포유류다
박쥐다
2
매 — 깃털이 있고(→ 새다) 빠르게 난다(→ 매다). 두 걸음 연쇄다.
출처: Kytabu, Wikimedia Commons (CC BY-SA 4.0)돌고래 — 젖을 먹이고(→ 포유류다) 헤엄친다(→ 돌고래다). 펭귄과 같은 '헤엄친다'가 다른 결론으로 이어진다.
출처: Charles J. Sharp, Wikimedia Commons (CC BY-SA 4.0)
네 동물 모두 같은 규칙 여섯 개로 판정됩니다.
바뀐 것은 우리가 넣은 관찰 두 마디뿐이에요.
1차시의 ELIZA는 규칙 네 개가 곧 프로그램의 전부여서 새 대답을 만들려면 규칙을 늘려야 했지만,
여기서는 같은 규칙 더미에서 다른 답이 나옵니다.
물윗수염박쥐 — 밤에 나무껍질에 매달려 쉬는 작은박쥐다. 날 때는 초음파를 내고 되돌아오는 메아리로 물 위의 벌레를 찾는다.
난다고 새가 되는 것이 아니다. '젖을 먹인다'가 '포유류다'를 부르고,
거기에 '초음파를 쓴다'가 더해져야 '박쥐다'가 나온다.
규칙 기반 판정은 겉모습이 아니라 적어 둔 조건만 본다.
출처: Rauno Kalda, Wikimedia Commons (CC BY-SA 3.0)
반례 — 아무 문도 열리지 않는 관찰
관찰을 {빠르게 난다, 헤엄친다}로 주면 어떻게 될까요?
먼저 예측해 보세요. 실행 결과는 이렇습니다.
더 나올 사실이 없다. 여기서 멈춘다.
새로 알아낸 것: (없음)
규칙 여섯 개 가운데 조건이 하나뿐인 규칙은 둘이고, 그 조건은
'깃털이 있다'와 '젖을 먹인다'입니다. 나머지 네 규칙은 전부 '새다'나 '포유류다'를 조건으로 요구하는데,
그 둘은 앞의 두 규칙이 만들어 주어야만 생기지요.
그러니 첫 단추 두 개 중 하나도 없으면 문이 하나도 열리지 않습니다.
관찰 다섯 가지를 켜고 끄는 32가지 조합을 전부 돌려 보면,
아무 규칙도 발동하지 않는 조합이 8가지입니다
(아무것도 켜지 않은 경우를 포함해서요). 여덟 가지 모두 '깃털이 있다'도 '젖을 먹인다'도 없는 조합이에요.
곧 시뮬레이터에서 여러분이 직접 만들어 볼 상황입니다.
⚠️ 오해하기 쉬운 자리
이때 나온 "(없음)"은 "그런 동물은 없다"가 아닙니다.
빠르게 날고 헤엄치는 동물은 실제로 많아요(가마우지·바다오리…).
정확한 뜻은 "이 지식 베이스로는 말할 수 있는 것이 없다"입니다.
규칙 기반 시스템은 세상을 아는 것이 아니라 적어 준 것만 압니다.
12차시에서 이 한계를 정면으로 다룹니다.
3
어디서 멈추는가 — 고정점
기계가 스스로 멈추는 자리에도 이름이 있습니다.
한 바퀴를 다 훑었는데 발동할 규칙이 하나도 없는 순간,
그때의 사실 집합을 고정점(fixed point)이라고 부릅니다.
"한 번 더 규칙을 적용해도 결과가 그대로인 지점"이라는 뜻이에요.
while True:
fired = Nonefor conds, concl in rules:
if all(c in facts for c in conds) and concl not in facts:
fired = (conds, concl)
breakif fired is None: # ← 여기가 고정점이다return facts
while True는 언뜻 무서운 코드입니다. 끝을 적어 두지 않았으니까요.
그런데 이 반복문은 반드시 끝납니다. 까닭이 둘입니다.
까닭 ① — 사실은 늘기만 하고 줄지 않는다
우리 엔진은 사실을 적기만 합니다. 지우는 코드가 한 줄도 없어요.
게다가 10차시의 검사 ②(concl not in facts)가
이미 아는 결론은 다시 적지 않게 막습니다.
그래서 한 걸음마다 사실이 정확히 하나씩 늡니다. 되돌아가는 일이 없습니다.
검사 ②를 빼면 어떻게 될까요? 10차시 [B]에서 이미 보았지요 —
같은 규칙이 여섯 번 다시 발동하고 사실은 하나도 늘지 않았습니다.
그 실습에 반복 상한 range(6)이 걸려 있었기 때문에 여섯 번에서 멈춘 것이고,
상한을 걸지 않고 돌리면 200만 번(0.9초)을 돌려도 끝나지 않습니다. 실제로 재 본 값입니다.
까닭 ② — 나올 수 있는 말이 유한하다
사실이 늘기만 해도, 늘 수 있는 한도가 없으면 끝나지 않겠지요.
그런데 우리 지식 베이스가 적을 수 있는 말은 세어 볼 수 있습니다.
무엇
가짓수
목록
관찰로 넣을 수 있는 말
5
깃털이 있다 · 젖을 먹인다 · 헤엄친다 · 빠르게 난다 · 초음파를 쓴다
규칙의 결론에 적힌 말
6
새다 · 포유류다 · 매다 · 펭귄이다 · 돌고래다 · 박쥐다
겹치는 말
0
—
모두 합쳐
11
이 지식 베이스가 만들 수 있는 말은 여기서 끝이다
늘기만 하는데 천장이 11이면 반드시 멈춥니다.
더 정확히 말하면, 규칙이 새로 적을 수 있는 말은 결론 여섯 가지뿐이므로
발동 횟수는 아무리 많아도 6을 넘을 수 없습니다.
관찰 32가지를 전부 돌려서 확인한 값이에요.
발동 횟수
0
1
2
3
4
5
6
그런 관찰 조합의 수
8
4
9
6
2
2
1
맨 오른쪽 칸의 6걸음짜리 조합은 딱 하나입니다 — 관찰 다섯 개를 전부 켠 경우이고,
그때 여섯 결론이 모두 나옵니다.
관찰 다섯 개를 전부 켠 경우. 걸음마다 사실이 하나씩 늘고,
6걸음째에 천장 11에 닿아 평평해진다. 올라가기만 하는 계단은 천장이 있으면 반드시 멈춘다 —
이것이 고정점을 보장하는 두 줄짜리 증명이다.
그러면 도는 규칙을 넣으면 어떻게 되나
일부러 서로를 부르는 규칙 둘을 넣어 봅시다.
새다 → 날개가 있다와 날개가 있다 → 새다입니다.
끝없이 돌 것 같지요? 실제 실행 결과는 이렇습니다.
[1] IF 깃털이 있다 -> 새다
[2] IF 새다 -> 날개가 있다
더 나올 사실이 없다. 여기서 멈춘다.
추론 뒤 아는 사실: ['깃털이 있다', '날개가 있다', '새다']
두 걸음에서 멈춥니다. 세 번째로 날개가 있다 → 새다가 발동할 차례인데,
'새다'가 이미 사실이기 때문에 검사 ②에 걸려 발동 자격을 잃습니다.
돌아갈 자리가 없어진 것이지요.
📌 4차시에서 본 그 장치다
4차시 「큐냐 스택이냐」에서 방문 집합을 지웠더니
65칸짜리 미로인데도 프론티어가 8,955,970칸까지 자랐던 것을 기억하나요?
검사 ②는 그것과 똑같은 장치입니다.
탐색에서는 '이미 둘러본 칸', 추론에서는 '이미 아는 사실'을 거를 뿐이에요.
"같은 것을 두 번 하지 않는다"는 한 줄이 두 분야에서 똑같이 종료를 보장합니다.
⚠️ 실행하기 전에 꼭 확인할 것
오늘 실습에서 빈칸 ①에 조건을 채울 때
concl not in facts를 빠뜨리면 진짜 무한 루프가 됩니다.
파이썬 실행기는 브라우저가 화면을 그리는 일과 같은 자리에서 돌기 때문에,
무한 루프에 빠지면 탭 전체가 멈추고 정지 버튼도 안 눌립니다.
실행 버튼을 누르기 전에 두 검사가 모두 and로 이어져 있는지 눈으로 한 번 확인하세요.
4
여럿이 동시에 발동할 때 — 충돌 해소
관찰을 {깃털이 있다, 젖을 먹인다, 헤엄친다, 빠르게 난다}로 넉넉히 주면
한 바퀴에 발동할 수 있는 규칙이 여럿이 됩니다.
지금 발동할 자격이 있는 규칙들의 모임을 충돌 집합(conflict set),
그중 무엇을 먼저 쓸지 정하는 일을
충돌 해소(conflict resolution)이라고 합니다.
우리 엔진의 충돌 해소는 아주 단순합니다 — for문이 목록을 위에서 아래로 훑다가
처음 만난 것에서 break 합니다. 곧 먼저 적힌 규칙이 이깁니다.
그러면 규칙을 적는 순서가 결과를 바꿀까요? 규칙 목록을 다섯 번 섞어 돌려 보았습니다.
회
발동 순서
1
포유류다 → 돌고래다 → 새다 → 펭귄이다 → 매다
2
새다 → 포유류다 → 매다 → 펭귄이다 → 돌고래다
3
새다 → 매다 → 펭귄이다 → 포유류다 → 돌고래다
4
포유류다 → 돌고래다 → 새다 → 매다 → 펭귄이다
5
새다 → 펭귄이다 → 포유류다 → 돌고래다 → 매다
—
결론 집합은 다섯 번 모두 같다
— 돌고래다 · 매다 · 새다 · 펭귄이다 · 포유류다 (5개)
섞는 눈이 실행할 때마다 달라지면 표를 대조할 수 없으므로 코드에 random.seed(2026)을 박아 두었습니다.
이 씨앗에서 다섯 번이 모두 다른 순서로 나옵니다. 씨앗을 바꾸면 같은 순서가 겹쳐 나올 수 있습니다.
다섯 번으로는 우연일 수 있으니 가능한 규칙 나열 순서 720가지(여섯 개를 줄 세우는 모든 방법)를
전부 돌려 보았습니다. 이번에는 관찰을 다섯 개 전부 켰습니다 —
여섯 결론이 모두 나오는, 가장 붐비는 상황입니다.
720가지를 전부 돌린 결과 (관찰 5개 전부 · 6걸음)
가짓수
서로 다른 발동 순서
80
서로 다른 결론 집합
1
관찰을 앞의 '다섯 번 섞기' 표와 같은 네 개(5걸음)로 줄이면 발동 순서는 20가지로 줄어듭니다.
결론 집합은 그때도 1가지입니다. 걸음이 늘수록 설명의 갈래만 불어납니다.
왜 결론은 하나로 모이는가
지금 발동 자격이 있는 규칙은, 다른 규칙이 먼저 발동한다고 해서 자격을 잃지 않습니다.
사실은 줄지 않으니 이미 갖춰진 조건이 무너질 일이 없기 때문이에요.
순서를 미루면 미뤄질 뿐 사라지지 않습니다.
다른 규칙이 같은 결론을 먼저 적어 버리는 경우는 어떨까요?
그때 이 규칙은 검사 ②에 걸려 발동하지 못합니다. 하지만 그 순간 적으려던 말은
이미 사실 목록에 들어 있습니다. 어느 쪽이든 최종 사실 집합은 같습니다.
그러면 무엇이 달라지는가
순서를 바꾸면
달라지는가
왜
최종 사실 집합
그대로
미뤄질 뿐 사라지지 않는다
발동 순서(로그)
달라진다
80가지나 된다. 이 로그가 곧 설명이 된다
걸리는 시간
달라진다
규칙이 수천 개면 헛물켜는 검사의 수가 달라진다
가운데 줄이 중요합니다. 사람에게 "왜 그렇게 판단했느냐"를 답해 줄 때 우리가 내미는 것이
바로 이 로그거든요. 결론이 같아도 설명의 모양은 달라집니다.
그래서 실제 시스템은 충돌 해소를 아무렇게나 두지 않고 전략을 고릅니다.
🕐
최신 사실 우선
가장 최근에 알게 된 사실을 쓰는 규칙을 먼저 발동한다.
사람의 주의가 옮겨 가는 모습과 닮아 설명이 자연스럽다.
🔍
구체적인 규칙 우선
조건이 더 많은(더 까다로운) 규칙을 먼저 쓴다.
"새는 난다"보다 "펭귄은 못 난다"를 먼저 적용하는 식이다.
🔢
사람이 매긴 우선순위
규칙마다 숫자를 붙여 둔다. 의료·금융처럼 안전 규칙을 반드시 먼저
검사해야 하는 곳에서 쓴다.
반례 — 순서가 결론까지 바꾸는 시스템
"순서를 바꿔도 결론은 같다"는 공짜로 얻은 성질이 아닙니다.
우리 엔진이 사실을 지우지 않기 때문에 성립하는 것이에요.
규칙이 사실을 지울 수 있는 시스템을 상상해 봅시다.
IF 새다 → '난다'를 적는다
IF 펭귄이다 → '난다'를 지운다
이러면 어느 규칙을 먼저 쓰느냐에 따라 마지막에 '난다'가 남기도 하고 사라지기도 합니다.
사실이 줄어들 수 있으면 사실 수가 올라가기만 하던 계단이 무너지고,
고정점 보장도 함께 사라집니다. 두 규칙이 서로 적고 지우기를 되풀이할 수 있으니까요.
💡 정리하면
우리 엔진이 순서에 대해 안전한 까닭은 사실이 늘기만 한다는
한 가지 성질 때문입니다. 이 성질 하나에서
① 반드시 멈춘다(고정점)와 ② 순서를 바꿔도 결론이 같다가 둘 다 나옵니다.
규칙 기반 시스템을 설계할 때 사실을 지우는 기능을 넣을지 말지가 큰 갈림길인 까닭이지요.
🧭
연쇄가 번져 가는 것을 한 걸음씩 본다
아래 그래프는 10차시의 지식 베이스를 그대로 그린 것입니다.
둥근 상자는 사실, 작은 사각형은 규칙,
화살표는 "이 사실이 이 규칙의 조건이다" 또는 "이 규칙이 이 사실을 만든다"는 뜻이에요.
처음에는 전부 꺼져 있습니다. 여러분이 관찰을 켜야 불이 들어오기 시작합니다.
규칙 상자에 붙은 R1 ~ R6은 규칙 목록의 순서를 그대로 붙인 번호입니다 —
앞 그림의 규칙 ①이 R1(깃털이 있다 → 새다),
규칙 ④가 R4(새다 AND 헤엄친다 → 펭귄이다)입니다.
아래 확인 문제도 이 번호로 묻습니다.
🔗 전향 추론 연쇄 그래프INTERACTIVE
① 관찰을 켜고 → ② 어느 동물이 나올지 예측한 다음 →
③ 한 걸음을 눌러 규칙이 하나씩 발동하는 것을 봅니다.
노란 테두리는 지금 발동할 수 있는 규칙(충돌 집합)이고, 흰 번쩍임은 방금 발동한 규칙입니다.
더 발동할 규칙이 없으면 고정점에 닿아 스스로 멈춥니다.
① 관찰한 사실 — 기계에 알려 주는 것은 이것뿐이다
② 쓸 규칙 — 끄면 그 문은 영영 열리지 않는다
③ 예측 — 돌리기 전에 먼저 고른다
발동한 걸음0
아는 사실0
새로 알아낸 것0
발동 대기 규칙0
본 발동 순서0
본 결론 집합0
예측 적중0/0
관찰을 켠 다음 [▶ 한 걸음]을 눌러 보세요.
[안내]관찰 두 개를 켜고 예측을 고른 뒤 시작합니다.
✍️ 이 시뮬레이터로 할 일 — 공책에 적으면서 하세요
㉮ 표 채우기. 아래 네 줄을 공책에 옮겨 적고, 관찰을 바꿔 가며 직접 채웁니다.
돌리기 전에 예측 칸을 먼저 적으세요.
켠 관찰
예측한 판정
발동한 걸음
새로 알아낸 것
맞혔나
깃털이 있다 · 헤엄친다
젖을 먹인다 · 초음파를 쓴다
깃털이 있다 · 젖을 먹인다 · 헤엄친다
다섯 개 전부
㉯ 반례 만들기 두 가지.
0걸음짜리 관찰을 만드세요 — 관찰을 두 개 이상 켰는데도 아무 규칙이 발동하지 않는 상태입니다.
그런 조합은 두 개 이상 켠 것만 세면 네 가지입니다. 네 가지를 모두 찾아 적고,
넷에 공통으로 빠져 있는 관찰 두 개가 무엇인지 쓰세요.
(아무것도 안 켠 것과 한 개만 켠 것까지 세면 개념 2에서 본 8가지가 됩니다.)
한 마리인데 판정이 둘 나오는 관찰을 만드세요.
"이 동물은 펭귄이면서 동시에 돌고래다"라는 말을 기계가 하게 만드는 것입니다.
그런 조합은 32가지 중 9가지입니다. 그중 하나만 찾으면 됩니다.
㉰ 문턱 찾기.발동 대기 규칙 칸을 보세요.
이 숫자가 4가 되는 순간을 만들 수 있나요?
어떤 관찰을 켜야 하고, 몇 걸음째에 그렇게 되는지 적으세요.
(힌트 — 32가지 관찰 가운데 4를 만드는 조합은 딱 하나입니다.) 규칙 순서는 처음 그대로 R1…R6 으로 두고 하세요.
섞은 뒤라면 [🔄 초기화]로 되돌립니다 — 순서를 섞으면 4까지 오르지 않는 경우가 더 많습니다
(720가지 순서 가운데 4에 닿는 것은 120가지뿐이고, 나머지 600가지는 3에서 멈춥니다).
㉱ 순서 실험. 관찰을 그대로 둔 채
[🔀 규칙 순서 섞기] → [⏩ 끝까지]를 다섯 번 되풀이하고,
본 발동 순서와 본 결론 집합 두 숫자를 적으세요.
두 숫자가 왜 그렇게 벌어지는지가 개념 4의 내용입니다.
💻
손으로 — 추론 엔진의 빈 두 줄을 채운다
시뮬레이터는 누군가 이미 만들어 둔 것입니다. 이제 그 안에서 돌던 엔진을 직접 완성합니다.
빈칸은 두 곳이고, 둘 다 오늘 개념에서 이미 말로 다룬 것이에요.
빈칸
무엇을 적는가
어디서 배웠나
①
이 규칙이 지금 발동하는가 — 두 검사를 and로 잇는다
개념 1(조건이 전부 갖춰졌는가) + 개념 3(결론이 아직 모르는 것인가)
②
언제 멈추는가 — 이번 바퀴에 발동한 규칙이 없을 때
개념 3 고정점
빈칸 두 곳을 채우고 실행합니다.[A]에 [1] 새다, [2] 펭귄이다 두 줄이 찍히고
"새로 알아낸 것: ['새다', '펭귄이다']"가 나오면 성공입니다.
실행 전에 개념 3의 경고 상자를 한 번 더 읽으세요.
연쇄 자리를 손으로 짚습니다.[1]의 결론과 [2]의 조건에서
같은 낱말을 찾아 동그라미 치세요. 그 낱말이 오늘의 전부입니다.
[B]와 [C]를 읽습니다.
매·돌고래·박쥐가 각각 어떤 관찰에서 나왔는지, 그리고 [C]에서 왜 아무것도 안 나왔는지
한 줄로 적으세요.
[D]의 도는 규칙이 왜 멈추는지 답합니다.
출력의 마지막 두 줄이 답을 말해 줍니다. 그 문장을 자기 말로 바꿔 적으세요.
[E]를 봅니다. 다섯 줄의 발동 순서를 비교하고,
그 아래 "결론 집합이 다섯 번 모두 같은가?"의 답을 확인합니다.
시뮬레이터 과제 ㉱에서 적은 두 숫자와 견주어 보세요.
본 과제 — 빈칸 ③. 엔진 forward_chain()은
한 줄도 고치지 말고규칙 3개와 관찰 사실 1개만 더해
올빼미다가 나오게 만듭니다.
그리고 그것이 되는 까닭을 한 줄로 적으세요.
💡 빈칸 ③이 막힐 때
새 규칙의 조건에 이미 있는 말을 하나 넣는 것이 요령입니다.
'깃털이 있다'는 새다를 부르니, 새 규칙의 조건 자리에 "새다"를 쓰면
기존 사슬에 매달 수 있어요. 관찰 사실은 하나만 더할 수 있으니,
그 하나로 여러 단계를 끌어내려면 중간 결론을 한 번 더 거쳐야 합니다.
(예: 새다 + 새 관찰 → 중간 결론 → … → 올빼미다)
빈칸 ① — all(c in facts for c in conds) and concl not in facts
10차시 fires()의 두 검사를 and로 이은 것과 완전히 같습니다.
앞쪽이 전건 긍정(조건이 전부 사실인가), 뒤쪽이 고정점을 보장하는 장치(결론이 아직 모르는 것인가)입니다.
뒤쪽을 빠뜨리면 첫 규칙이 영원히 다시 발동합니다.
빈칸 ② — fired is None
이번 바퀴에 아무 규칙도 잡히지 않았다는 뜻이고, 그것이 곧 고정점입니다.
if not fired:로 써도 같게 동작합니다.
정답은 하나가 아닙니다. 채점 기준은 딱 둘이에요 —
엔진 forward_chain()을 한 줄도 고치지 않았는가,
올빼미다가 나오는가. 아래는 그중 한 가지 답입니다.
[1] IF 깃털이 있다 -> 새다
[2] IF 새다 AND 밤에 활동한다 -> 야행성 새다
[3] IF 야행성 새다 -> 소리로 먹이를 찾는다
[4] IF 야행성 새다 AND 소리로 먹이를 찾는다 -> 올빼미다
새로 알아낸 것: ['새다', '소리로 먹이를 찾는다', '야행성 새다', '올빼미다']
그리고 마지막 줄을 꼭 보세요 —
규칙을 늘린 뒤에도 옛 판정은 그대로입니다(새다 -> 펭귄이다).
새 지식을 얹었는데 이미 있던 판정이 깨지지 않았어요.
이것이 3단원에서 배울 if문 프로그램과 가장 크게 다른 점입니다.
if문으로 짠 판정기에 조건을 하나 더 끼워 넣으려면 기존 분기를 다시 손봐야 하지만,
여기서는 목록에 세 줄을 더했을 뿐입니다.
수리부엉이 — 올빼미 무리(올빼미목)에 드는 새로, 밤에 활동하고 소리로 먹이를 찾는다.
엔진은 이 새를 몰랐지만, 규칙 세 줄을 얹자 이름을 부를 수 있게 되었다.
출처: Alvesgaspar, Wikimedia Commons (CC BY-SA 4.0)
📌 지식과 엔진의 분리
오늘 코드에서 RULES는 지식이고
forward_chain()은 엔진입니다.
엔진은 규칙의 내용을 전혀 모릅니다 — '펭귄'이 무엇인지, '깃털'이 무엇인지 모른 채
조건 자리와 결론 자리를 기계적으로 맞춰 볼 뿐이지요.
그래서 동물 판정기를 병 진단기로 바꾸는 데 엔진 코드는 한 글자도 안 바뀝니다.
1980년대 전문가 시스템이 회사에서 팔리던 방식이 정확히 이것이었어요 —
엔진(셸)은 사서 쓰고, 규칙은 그 분야 전문가가 적었습니다.
📖
정리 — 늘기만 하는 목록 하나가 전부였다
오늘 다룬 것은 결국 사실 목록 하나였습니다.
규칙은 그 목록에 무엇을 더 적어도 되는지를 알려 주는 안내문일 뿐이었고요.
그 목록이 늘기만 하고 줄지 않는다는 한 가지 성질에서 오늘의 결론이 전부 따라 나왔습니다.
물음
오늘의 답
근거
'새다'는 어디서 왔나
규칙 ①이 만들었고, 그것이 규칙 ④의 조건이 되었다
로그 [1]의 결론이 [2]의 조건에 다시 나온다
언제 멈추나
발동할 규칙이 하나도 없을 때 — 고정점
사실은 늘기만 하고, 나올 수 있는 말은 11가지뿐이다
도는 규칙을 넣으면
그래도 멈춘다 (2걸음)
이미 아는 결론은 다시 적지 않는다(검사 ②)
규칙 순서를 바꾸면
설명은 달라지고 결론은 그대로
720가지 순서 전수(관찰 5개) — 발동 순서 80가지 / 결론 집합 1가지
새 동물을 알아맞히려면
엔진은 그대로 두고 규칙만 늘린다
올빼미 4단 연쇄 — 옛 판정 새다 → 펭귄이다는 그대로
마지막 줄이 규칙 기반 인공지능의 값어치이자 동시에 약점입니다.
지식을 늘리는 일이 프로그램을 고치는 일과 분리되어 있다는 것은 큰 장점이에요.
하지만 규칙을 누군가 손으로 적어야 한다는 사실은 그대로 남습니다.
1차시 ELIZA에서 규칙 두 개를 직접 적어 보며 느꼈던 그 막막함이,
규칙이 수천 개가 되면 어떤 모습이 될지 상상해 보세요.
다음 12차시 「왜 그렇게 판단했는지 말할 수 있는가」에서는 방향을 뒤집습니다.
오늘은 아는 사실에서 앞으로 밀고 나갔지만, 다음 시간에는 목표를 놓고
거꾸로 캐물어 들어갑니다(후향 추론). 그러면 "왜 그렇게 판단했는가"에 답할 수 있어요.
그리고 마지막에 오늘의 추론과 7차시의 astar()를 한 프로그램 안에서 잇습니다 —
추론이 내린 결론이 탐색의 비용이 되는 자리입니다.
🔁 되돌아보기
오늘 규칙이 규칙을 부르는 연쇄를 한 걸음씩 보고, 엔진의 빈 두 줄을 직접 채워
고정점에서 스스로 멈추는 추론기를 완성했다.
그리고 엔진을 한 줄도 고치지 않고 규칙만 늘려 올빼미를 알아맞혔다.
다음 시간에는 같은 지식을 거꾸로 따라가며 "왜?"에 답하는 법을 배우고,
그 답을 탐색과 잇는다.
✅
확인 문제
✍️ 문제마다 답을 쓰고 제출하기를 누르세요. 제출하면 모범 답안이 열리고, 제출한 답은 선생님께 전달됩니다.
1. 우리 지식 베이스의 규칙 하나를 골라
전건 긍정 한 걸음을 규칙 · 사실 · 새 사실 세 줄로 적으시오.
그리고 이 세 줄을 거꾸로 읽으면 왜 안 되는지 한 문장으로 쓰시오.
📖 모범 답안
예를 들면 이렇게 적는다.
규칙 IF 젖을 먹인다 → 포유류다 /
사실 젖을 먹인다 / 새 사실 포유류다
거꾸로 읽으면 안 되는 까닭 — 규칙은 "조건이 참이면 결론이 참"만 보증할 뿐,
"결론이 참이면 조건이 참"은 보증하지 않기 때문이다.
'포유류다'를 알았다고 '젖을 먹인다'를 새 사실로 적으면 후건 긍정의 오류가 된다.
전향 추론 엔진은 조건이 담긴 변수(conds)만 검사하고 결론이 담긴 변수(concl)에만
적기 때문에 이 실수를 구조적으로 할 수 없다.
2. 오늘 실습 [A]가 찍은 두 줄이다.
[1] IF 깃털이 있다 -> 새다 [2] IF 새다 AND 헤엄친다 -> 펭귄이다
① 연쇄가 일어난 자리를 낱말 하나로 짚고, ② 그 낱말이 어느 줄에서는 결론이고
어느 줄에서는 조건인지 밝히시오. ③ 그리고 이때 새로 알아낸 것이 몇 개였는지 쓰시오.
📖 모범 답안
① 연쇄가 일어난 낱말은 '새다'다.
② [1]에서는 결론(화살표 오른쪽)이고, [2]에서는 조건(IF 다음)이다.
[1]이 이 말을 사실 목록에 적어 준 덕분에 [2]의 조건 두 개가 비로소 다 갖춰졌다.
③ 새로 알아낸 것은 2개 — '새다'와 '펭귄이다'.
우리가 넣은 것은 2개였고 끝난 뒤 아는 사실은 4개다.
덧붙이면, [2]가 처음부터 발동하지 못했던 까닭은 조건이 둘인데
우리가 '헤엄친다' 하나만 알려 주었기 때문이다. AND는 '전부'라는 뜻이다.
3. 시뮬레이터에서 규칙 순서를 처음 그대로(R1…R6) 둔 채
발동 대기 규칙 칸(= 충돌 집합의 크기)을
4로 만들려면 어떤 관찰을 켜야 하는가? ① 켠 관찰을 적고,
② 몇 걸음째에 4가 되는지, ③ 그 순간 대기 중인 규칙 넷이 무엇인지 쓰시오.
📖 모범 답안
① 관찰 다섯 개를 전부 켜야 한다. 32가지 조합 가운데 대기 규칙이 4가 되는 것은
이 조합 하나뿐이다.
② 2걸음을 지난 뒤다. 걸음마다 대기 규칙 수는
2 → 3 → 4 → 3 → 2 → 1 → 0으로 변한다(마지막 0이 고정점).
③ 두 걸음 동안 '새다'와 '포유류다'가 사실이 되고 나면
R3(→매다) · R4(→펭귄이다) · R5(→돌고래다) · R6(→박쥐다) 넷이 한꺼번에 발동 자격을 얻는다.
이 상태가 충돌 집합이다. 넷 중 무엇을 먼저 쓸지 정하는 일이 충돌 해소이고,
우리 엔진은 '목록에서 먼저 적힌 것'을 고른다. 그리고 이 관찰의 최종 걸음 수는 6걸음으로,
이 지식 베이스에서 나올 수 있는 최댓값이다.
덧붙임 — 4는 규칙 순서에 기댄 값이다. 같은 관찰이라도 순서를 섞으면
720가지 가운데 120가지에서만 4에 닿고, 나머지 600가지는 3에서 멈춘다.
먼저 발동한 규칙이 다음 문을 여는 자리라서, 어느 문을 먼저 여느냐가 한꺼번에 열리는 문의 수를 바꾸는 것이다.
그래도 어떤 순서로도 4를 넘지는 못한다.
4. 전향 추론이 멈추는 조건을 고정점이라는 말로 쓰고,
왜 반드시 멈추는지 근거 두 가지를 대시오.
그리고 새다 → 날개가 있다, 날개가 있다 → 새다처럼
서로를 부르는 규칙을 넣어도 멈추는 까닭을 설명하시오.
📖 모범 답안
고정점 — 규칙 목록을 한 바퀴 다 훑었는데 발동할 규칙이 하나도 없는 상태.
한 번 더 적용해도 사실 집합이 달라지지 않는 지점이다.
근거 ① 사실은 늘기만 하고 줄지 않는다. 지우는 코드가 없고,
검사 ②(concl not in facts)가 이미 아는 결론을 다시 적지 못하게 막으므로
한 걸음마다 사실이 정확히 하나씩 는다. 근거 ② 나올 수 있는 말이 유한하다. 이 지식 베이스가 적을 수 있는 말은
관찰 5 + 결론 6 = 11가지뿐이다. 올라가기만 하는 계단에 천장이 있으면 반드시 멈춘다.
실제로 발동 횟수는 어떤 관찰에서도 6을 넘지 않았다.
도는 규칙 — 새다 → 날개가 있다가 발동해 '날개가 있다'가 사실이 되면,
다음에 날개가 있다 → 새다가 발동할 차례지만 '새다'가 이미 사실이라 검사 ②에 걸린다.
그래서 2걸음에서 멈춘다. 4차시의 방문 집합, 곧 "이미 둘러본 칸은 다시 넣지 않는다"와 같은 장치다.
5. 규칙을 적는 순서를 바꾸면 무엇이 달라지고
무엇이 그대로인가? 그리고 순서가 결론까지 바꾸어 버리는 시스템은 어떤 조건에서 생기는지
한 가지 예를 들어 설명하시오.
📖 모범 답안
그대로인 것 — 최종 사실 집합. 지금 발동 자격이 있는 규칙은 다른 규칙이 먼저 발동해도
자격을 잃지 않는다(사실이 줄지 않으니 조건이 무너지지 않는다). 미뤄질 뿐 사라지지 않는다.
다른 규칙이 같은 결론을 먼저 적어 버린 경우라면, 그때는 적으려던 말이 이미 적혀 있다.
관찰 다섯 개를 전부 켜고 규칙 나열 순서 720가지를 전부 돌려도 결론 집합은 1가지였다.
달라지는 것 — 발동 순서, 곧 설명의 모양이다(같은 실험에서 80가지.
관찰을 네 개로 줄이면 20가지로 줄지만, 결론 집합은 그때도 1가지다).
규칙이 아주 많을 때는 걸리는 시간도 달라진다. 사람에게 "왜 그렇게 판단했는가"를 답할 때
내미는 것이 이 로그이므로, 결론이 같아도 설명은 달라진다.
순서가 결론까지 바꾸는 경우 — 규칙이 사실을 지울 수 있을 때다.
예를 들어 IF 새다 → '난다'를 적는다와 IF 펭귄이다 → '난다'를 지운다가 함께 있으면,
어느 쪽을 먼저 쓰느냐에 따라 마지막에 '난다'가 남기도 하고 사라지기도 한다.
사실이 줄어들 수 있으면 '늘기만 한다'는 성질이 깨지고, 고정점 보장도 함께 사라진다.
6. 오늘 본 과제에서 forward_chain()을
한 줄도 고치지 않고 올빼미를 알아맞혔다.
① 그것이 가능한 까닭을 '지식과 엔진의 분리'로 설명하고,
② 규칙을 늘린 뒤에 옛 관찰(깃털·헤엄)을 다시 돌렸을 때 무엇이 나왔는지 쓰시오.
③ 이 성질이 3단원에서 배울 if문 프로그램과 어떻게 다른지 한 줄로 견주시오.
📖 모범 답안
① 엔진은 규칙의 내용을 전혀 모른다. '올빼미'가 무엇인지, '깃털'이 무엇인지 모른 채
조건 자리에 적힌 말이 사실 목록에 있는지만 맞춰 볼 뿐이다.
그래서 지식(RULES)을 갈아 끼워도 엔진(forward_chain())은 그대로 돈다.
동물 판정기를 병 진단기로 바꾸는 데에도 엔진 코드는 한 글자도 바뀌지 않는다.
② 새다 → 펭귄이다가 그대로 나왔다.
규칙 세 줄을 새로 얹었는데도 이미 있던 판정이 깨지지 않았다.
(덧붙여 올빼미 쪽은 4단 연쇄가 일어나 새로 알아낸 것이 4개였다.)
③ if문으로 짠 판정기는 조건을 하나 더 넣으려면 기존 분기 구조를 다시 손봐야 하고,
그 과정에서 이미 맞던 판정이 조용히 깨질 수 있다.
규칙 기반에서는 목록에 줄을 더하기만 하면 되고, 엔진은 손대지 않는다.
1980년대에 회사들이 규칙 엔진은 사서 쓰고 규칙만 전문가에게 적게 한 까닭이 이것이다.
🔎
더 알아보기
오늘 만든 연쇄가 교실 밖에서는 어디서 쓰였고, 어디서 벽에 부딪혔나
역사
규칙만으로 굴러가던 시절 — 전문가 시스템
1970~80년대에는 전문가 시스템(expert system)이 인공지능의 얼굴이었습니다.
짜임은 오늘 우리가 만든 것과 같아요 — 규칙 더미와 그것을 돌리는 엔진, 그리고 설명 기능.
스탠퍼드의 MYCIN은 규칙 수백 개로 혈액 감염균을 짚고 항생제를 권했습니다.
평가에서는 전문의와 견줄 만한 판단을 내놓았지만 실제 진료에는 쓰이지 않았어요 —
기계가 내린 처방의 책임을 누가 지느냐가 정리되지 않았기 때문입니다.
사진의 VAX-11/780을 만든 회사 DEC는 XCON이라는 규칙 시스템에게
주문서의 부품 구성이 실제로 조립되는지 검사하게 했고, 이것이 규칙 기반 인공지능이 회사에서 돈을 번 첫 사례로 꼽힙니다.
그러다 겨울이 왔습니다. 규칙이 수천 개로 불어나자 서로 부딪히는 규칙을 손으로 찾아내는 일이 감당할 수 없게 되었고,
새 분야로 옮길 때마다 전문가를 붙들고 규칙을 처음부터 다시 캐물어야 했거든요.
이것을 지식 획득 병목이라 부릅니다. Ⅱ단원의 기계학습은 이 병목을 데이터로 우회하려는 시도입니다.
사진: DEC VAX-11/780(Living Computer Museum) · 출처: Joe Mabel, Wikimedia Commons (CC BY-SA 3.0)
원리 더 깊이
규칙이 수천 개가 되면 — 매번 전부 훑을 수는 없다
오늘 엔진은 한 걸음마다 규칙 여섯 개를 처음부터 끝까지 훑습니다. 여섯 개라서 문제가 없었지요.
그런데 규칙이 수천 개라면, 사실이 하나 늘 때마다 수천 개를 다시 훑는 것은 큰 낭비입니다 —
대부분의 규칙은 방금 늘어난 사실과 아무 상관이 없거든요.
1970년대에 찰스 포지(Charles Forgy)가 고안한 RETE 알고리즘은 규칙들을 미리 그물처럼 엮어 둡니다.
조건 하나하나가 거름망이 되고, 조건이 둘인 규칙은 두 거름망이 만나는 자리에 매달립니다.
그림처럼 새 사실이 들어오면 그 사실과 이어진 가지만 흔들리고, 나머지는 지난번 결과를 그대로 씁니다.
"바뀐 것만 다시 계산한다"는 이 발상은 화면에서 바뀐 부분만 다시 그리는 방식, 바뀐 파일만 다시 만드는 빌드 도구와 뿌리가 같습니다.
지금도 대출 심사·보험 청구 검사처럼 판단 근거를 규정집처럼 보여 줘야 하는 곳에서는 이런 규칙 엔진이 돌아갑니다.
생각할 거리
새는 난다 — 그런데 타조는? 예외가 생기면 '늘기만 하는 목록'이 흔들린다
규칙에 IF 새다 → 난다를 더하면 매에게는 맞습니다. 하지만 사진의 타조도, 오늘의 펭귄도 새인데 날지 못해요.
그래서 사람은 "보통은 난다, 예외를 알게 되면 거둔다"고 생각합니다.
이런 추론을 기본값 추론이라 하고, 새 사실이 옛 결론을 지울 수 있다는 뜻에서 비단조 추론이라고도 부릅니다.
문제는 오늘 엔진이 멈추는 근거가 바로 "사실은 늘기만 한다"였다는 점입니다(확인 문제 4·5).
'타조다'가 들어오는 순간 '난다'를 지워야 한다면 목록이 줄어들 수 있고,
규칙 순서에 따라 마지막에 남는 것이 달라지며, 멈춘다는 보장도 사라집니다.
그래서 규칙 시스템은 규칙에 층을 두곤 합니다. '날지 못하는 새인가'를 가려내는 규칙을 먼저 끝까지 돌리고,
그다음 층에서만 IF 새다 AND 날지 못하는 새가 아니다 → 난다를 따지는 식이지요.
예외를 판정할 재료가 다 나온 뒤에 '아니다'를 묻기 때문에 한 층 안에서는 목록이 여전히 늘기만 합니다.
데이터베이스 질의 언어 Datalog의 계층화(stratification)가 바로 이 방법입니다.
사진: 달리는 타조 · 출처: Eric Kilby, Wikimedia Commons (CC BY-SA 2.0)