AI-For-Beginners/translations/da/lessons/2-Symbolic/Animals.ipynb

477 lines
18 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Implementering af et dyreekspertsystem\n",
"\n",
"Et eksempel fra [AI for Beginners Curriculum](http://github.com/microsoft/ai-for-beginners).\n",
"\n",
"I dette eksempel vil vi implementere et simpelt vidensbaseret system til at bestemme et dyr baseret på nogle fysiske karakteristika. Systemet kan repræsenteres ved følgende AND-OR træ (dette er en del af det komplette træ, vi kan nemt tilføje flere regler):\n",
"\n",
"![](../../../../../../translated_images/da/AND-OR-Tree.5592d2c70187f283.webp)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Vores egen ekspertsystem-shell med baglæns inferens\n",
"\n",
"Lad os prøve at definere et enkelt sprog til vidensrepræsentation baseret på produktionsregler. Vi vil bruge Python-klasser som nøgleord til at definere regler. Der vil i det væsentlige være 3 typer klasser:\n",
"* `Ask` repræsenterer et spørgsmål, der skal stilles til brugeren. Det indeholder sættet af mulige svar.\n",
"* `If` repræsenterer en regel, og det er bare en syntaktisk sukker til at gemme indholdet af reglen\n",
"* `AND`/`OR` er klasser til at repræsentere AND/OR-grene af træet. De gemmer blot listen af argumenter indeni. For at forenkle koden er al funktionalitet defineret i forældrekassen `Content`\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"class Ask():\n",
" def __init__(self,choices=['y','n']):\n",
" self.choices = choices\n",
" def ask(self):\n",
" if max([len(x) for x in self.choices])>1:\n",
" for i,x in enumerate(self.choices):\n",
" print(\"{0}. {1}\".format(i,x),flush=True)\n",
" x = int(input())\n",
" return self.choices[x]\n",
" else:\n",
" print(\"/\".join(self.choices),flush=True)\n",
" return input()\n",
"\n",
"class Content():\n",
" def __init__(self,x):\n",
" self.x=x\n",
" \n",
"class If(Content):\n",
" pass\n",
"\n",
"class AND(Content):\n",
" pass\n",
"\n",
"class OR(Content):\n",
" pass"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"I vores system ville arbejdshukommelsen indeholde listen over **fakta** som **attribut-værdi-par**. Vidensbasen kan defineres som et stort opslagsværk, der forbinder handlinger (nye fakta, der skal indsættes i arbejdshukommelsen) med betingelser, udtrykt som OG-ELLER-udtryk. Desuden kan nogle fakta `Ask`es.\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"rules = {\n",
" 'default': Ask(['y','n']),\n",
" 'color' : Ask(['red-brown','black and white','other']),\n",
" 'pattern' : Ask(['dark stripes','dark spots']),\n",
" 'mammal': If(OR(['hair','gives milk'])),\n",
" 'carnivor': If(OR([AND(['sharp teeth','claws','forward-looking eyes']),'eats meat'])),\n",
" 'ungulate': If(['mammal',OR(['has hooves','chews cud'])]),\n",
" 'bird': If(OR(['feathers',AND(['flies','lies eggs'])])),\n",
" 'animal:monkey' : If(['mammal','carnivor','color:red-brown','pattern:dark spots']),\n",
" 'animal:tiger' : If(['mammal','carnivor','color:red-brown','pattern:dark stripes']),\n",
" 'animal:giraffe' : If(['ungulate','long neck','long legs','pattern:dark spots']),\n",
" 'animal:zebra' : If(['ungulate','pattern:dark stripes']),\n",
" 'animal:ostrich' : If(['bird','long nech','color:black and white','cannot fly']),\n",
" 'animal:pinguin' : If(['bird','swims','color:black and white','cannot fly']),\n",
" 'animal:albatross' : If(['bird','flies well'])\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For at udføre baglæns inferens, vil vi definere klassen `Knowledgebase`. Den vil indeholde:\n",
"* Arbejdende `memory` - en ordbog, der forbinder attributter med værdier\n",
"* Knowledgebase `rules` i det format, der er defineret ovenfor\n",
"\n",
"To hovedmetoder er:\n",
"* `get` for at opnå værdien af en attribut, og udføre inferens om nødvendigt. For eksempel, `get('color')` ville hente værdien af et farveslot (den vil spørge om nødvendigt og gemme værdien til senere brug i arbejdshukommelsen). Hvis vi spørger `get('color:blue')`, vil den spørge efter en farve og derefter returnere `y`/`n` værdi alt efter farven.\n",
"* `eval` udfører den faktiske inferens, dvs. gennemgår AND/OR-træet, evaluerer delmål osv.\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"class KnowledgeBase():\n",
" def __init__(self,rules):\n",
" self.rules = rules\n",
" self.memory = {}\n",
" \n",
" def get(self,name):\n",
" if ':' in name:\n",
" k,v = name.split(':')\n",
" vv = self.get(k)\n",
" return 'y' if v==vv else 'n'\n",
" if name in self.memory.keys():\n",
" return self.memory[name]\n",
" for fld in self.rules.keys():\n",
" if fld==name or fld.startswith(name+\":\"):\n",
" # print(\" + proving {}\".format(fld))\n",
" value = 'y' if fld==name else fld.split(':')[1]\n",
" res = self.eval(self.rules[fld],field=name)\n",
" if res!='y' and res!='n' and value=='y':\n",
" self.memory[name] = res\n",
" return res\n",
" if res=='y':\n",
" self.memory[name] = value\n",
" return value\n",
" # field is not found, using default\n",
" res = self.eval(self.rules['default'],field=name)\n",
" self.memory[name]=res\n",
" return res\n",
" \n",
" def eval(self,expr,field=None):\n",
" # print(\" + eval {}\".format(expr))\n",
" if isinstance(expr,Ask):\n",
" print(field)\n",
" return expr.ask()\n",
" elif isinstance(expr,If):\n",
" return self.eval(expr.x)\n",
" elif isinstance(expr,AND) or isinstance(expr,list):\n",
" expr = expr.x if isinstance(expr,AND) else expr\n",
" for x in expr:\n",
" if self.eval(x)=='n':\n",
" return 'n'\n",
" return 'y'\n",
" elif isinstance(expr,OR):\n",
" for x in expr.x:\n",
" if self.eval(x)=='y':\n",
" return 'y'\n",
" return 'n'\n",
" elif isinstance(expr,str):\n",
" return self.get(expr)\n",
" else:\n",
" print(\"Unknown expr: {}\".format(expr))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lad os nu definere vores dyrevidensbase og udføre konsultationen. Bemærk, at dette opkald vil stille dig spørgsmål. Du kan svare ved at skrive `y`/`n` for ja-nej spørgsmål, eller ved at angive tal (0..N) for spørgsmål med længere multiple-choice svar.\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"hair\n",
"y/n\n",
"sharp teeth\n",
"y/n\n",
"claws\n",
"y/n\n",
"forward-looking eyes\n",
"y/n\n",
"color\n",
"0. red-brown\n",
"1. black and white\n",
"2. other\n",
"has hooves\n",
"y/n\n",
"long neck\n",
"y/n\n",
"long legs\n",
"y/n\n",
"pattern\n",
"0. dark stripes\n",
"1. dark spots\n"
]
},
{
"data": {
"text/plain": [
"'giraffe'"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"kb = KnowledgeBase(rules)\n",
"kb.get('animal')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Brug af Experta til Fremadrettet Slutning\n",
"\n",
"I det næste eksempel vil vi forsøge at implementere fremadrettet slutning ved hjælp af et af bibliotekerne til videnrepræsentation, [Experta](https://github.com/nilp0inter/experta). **Experta** er et bibliotek til at skabe fremadrettede slutningssystemer i Python, som er designet til at ligne det klassiske gamle system [CLIPS](http://www.clipsrules.net/index.html). \n",
"\n",
"Vi kunne også have implementeret fremadrettet kædning selv uden mange problemer, men naive implementeringer er normalt ikke særlig effektive. For mere effektiv regelmatchning bruges en speciel algoritme [Rete](https://en.wikipedia.org/wiki/Rete_algorithm).\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Collecting git+https://github.com/nilp0inter/experta\n",
" Cloning https://github.com/nilp0inter/experta to /tmp/pip-req-build-7qurtwk3\n",
" Running command git clone --filter=blob:none --quiet https://github.com/nilp0inter/experta /tmp/pip-req-build-7qurtwk3\n",
" Resolved https://github.com/nilp0inter/experta to commit c6d5834b123861f5ae09e7d07027dc98bec58741\n",
" Installing build dependencies ... \u001b[?25ldone\n",
"\u001b[?25h Getting requirements to build wheel ... \u001b[?25ldone\n",
"\u001b[?25h Preparing metadata (pyproject.toml) ... \u001b[?25ldone\n",
"\u001b[?25hRequirement already satisfied: frozendict~=2.4.6 in /opt/conda/envs/ai4beg/lib/python3.12/site-packages (from experta==1.9.5.dev1) (2.4.7)\n",
"Collecting schema~=0.6.7 (from experta==1.9.5.dev1)\n",
" Downloading schema-0.6.8-py2.py3-none-any.whl.metadata (14 kB)\n",
"Downloading schema-0.6.8-py2.py3-none-any.whl (14 kB)\n",
"Building wheels for collected packages: experta\n",
" Building wheel for experta (pyproject.toml) ... \u001b[?25ldone\n",
"\u001b[?25h Created wheel for experta: filename=experta-1.9.5.dev1-py3-none-any.whl size=34804 sha256=888c459512a5e713f4b674caa9a0f96cfdf07ec0d6eb56cc318ce0653d218014\n",
" Stored in directory: /tmp/pip-ephem-wheel-cache-1eeii9zy/wheels/3d/e8/bb/22d7956359603fa8dd679aa09f5b8efb3f29991c3986fdc787\n",
"Successfully built experta\n",
"Installing collected packages: schema, experta\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2/2\u001b[0m [experta]\n",
"\u001b[1A\u001b[2KSuccessfully installed experta-1.9.5.dev1 schema-0.6.8\n"
]
}
],
"source": [
"import sys\n",
"!{sys.executable} -m pip install git+https://github.com/nilp0inter/experta"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"from experta import *\n",
"#import experta"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Vi vil definere vores system som en klasse, der underklasser `KnowledgeEngine`. Hver regel defineres af en separat funktion med `@Rule` annotation, som angiver, hvornår reglen skal aktiveres. Inde i reglen kan vi tilføje nye fakta ved hjælp af `declare` funktionen, og tilføjelsen af disse fakta vil resultere i, at nogle flere regler bliver kaldt af fremadrettet inferensmotor.\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"class Animals(KnowledgeEngine):\n",
" @Rule(OR(\n",
" AND(Fact('sharp teeth'),Fact('claws'),Fact('forward looking eyes')),\n",
" Fact('eats meat')))\n",
" def cornivor(self):\n",
" self.declare(Fact('carnivor'))\n",
" \n",
" @Rule(OR(Fact('hair'),Fact('gives milk')))\n",
" def mammal(self):\n",
" self.declare(Fact('mammal'))\n",
"\n",
" @Rule(Fact('mammal'),\n",
" OR(Fact('has hooves'),Fact('chews cud')))\n",
" def hooves(self):\n",
" self.declare('ungulate')\n",
" \n",
" @Rule(OR(Fact('feathers'),AND(Fact('flies'),Fact('lays eggs'))))\n",
" def bird(self):\n",
" self.declare('bird')\n",
" \n",
" @Rule(Fact('mammal'),Fact('carnivor'),\n",
" Fact(color='red-brown'),\n",
" Fact(pattern='dark spots'))\n",
" def monkey(self):\n",
" self.declare(Fact(animal='monkey'))\n",
"\n",
" @Rule(Fact('mammal'),Fact('carnivor'),\n",
" Fact(color='red-brown'),\n",
" Fact(pattern='dark stripes'))\n",
" def tiger(self):\n",
" self.declare(Fact(animal='tiger'))\n",
"\n",
" @Rule(Fact('ungulate'),\n",
" Fact('long neck'),\n",
" Fact('long legs'),\n",
" Fact(pattern='dark spots'))\n",
" def giraffe(self):\n",
" self.declare(Fact(animal='giraffe'))\n",
"\n",
" @Rule(Fact('ungulate'),\n",
" Fact(pattern='dark stripes'))\n",
" def zebra(self):\n",
" self.declare(Fact(animal='zebra'))\n",
"\n",
" @Rule(Fact('bird'),\n",
" Fact('long neck'),\n",
" Fact('cannot fly'),\n",
" Fact(color='black and white'))\n",
" def straus(self):\n",
" self.declare(Fact(animal='ostrich'))\n",
"\n",
" @Rule(Fact('bird'),\n",
" Fact('swims'),\n",
" Fact('cannot fly'),\n",
" Fact(color='black and white'))\n",
" def pinguin(self):\n",
" self.declare(Fact(animal='pinguin'))\n",
"\n",
" @Rule(Fact('bird'),\n",
" Fact('flies well'))\n",
" def albatros(self):\n",
" self.declare(Fact(animal='albatross'))\n",
" \n",
" @Rule(Fact(animal=MATCH.a))\n",
" def print_result(self,a):\n",
" print('Animal is {}'.format(a))\n",
" \n",
" def factz(self,l):\n",
" for x in l:\n",
" self.declare(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Når vi har defineret en videnbase, fylder vi vores arbejdshukommelse med nogle indledende fakta og kalder derefter `run()` metoden for at udføre inferensen. Du kan se som resultat, at nye udledte fakta bliver tilføjet til arbejdshukommelsen, inklusive det endelige faktum om dyret (hvis vi har sat alle de indledende fakta korrekt op).\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Animal is tiger\n"
]
},
{
"data": {
"text/plain": [
"FactList([(0, InitialFact()),\n",
" (1, Fact(color='red-brown')),\n",
" (2, Fact(pattern='dark stripes')),\n",
" (3, Fact('sharp teeth')),\n",
" (4, Fact('claws')),\n",
" (5, Fact('forward looking eyes')),\n",
" (6, Fact('gives milk')),\n",
" (7, Fact('mammal')),\n",
" (8, Fact('carnivor')),\n",
" (9, Fact(animal='tiger'))])"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ex1 = Animals()\n",
"ex1.reset()\n",
"ex1.factz([\n",
" Fact(color='red-brown'),\n",
" Fact(pattern='dark stripes'),\n",
" Fact('sharp teeth'),\n",
" Fact('claws'),\n",
" Fact('forward looking eyes'),\n",
" Fact('gives milk')])\n",
"ex1.run()\n",
"ex1.facts"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n\n<!-- CO-OP TRANSLATOR DISCLAIMER START -->\n**Ansvarsfraskrivelse**:\nDette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, bedes du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det oprindelige dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os intet ansvar for misforståelser eller fejltolkninger, der måtte opstå som følge af brugen af denne oversættelse.\n<!-- CO-OP TRANSLATOR DISCLAIMER END -->\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.7.4 64-bit (conda)",
"metadata": {
"interpreter": {
"hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5"
}
},
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
},
"coopTranslator": {
"original_hash": "8ef43db4b9182239fd150a76bd494fdb",
"translation_date": "2026-01-16T02:15:58+00:00",
"source_file": "lessons/2-Symbolic/Animals.ipynb",
"language_code": "da"
}
},
"nbformat": 4,
"nbformat_minor": 2
}