477 lines
18 KiB
Plaintext
477 lines
18 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {
|
||
"collapsed": true
|
||
},
|
||
"source": [
|
||
"# Gyvūnų Eksperto Sistemos Įgyvendinimas\n",
|
||
"\n",
|
||
"Pavyzdys iš [AI pradedantiesiems mokymo plano](http://github.com/microsoft/ai-for-beginners).\n",
|
||
"\n",
|
||
"Šiame pavyzdyje įgyvendinsime paprastą žinių pagrindu veikiančią sistemą, skirtą gyvūnui nustatyti pagal kai kurias fizines savybes. Sistema gali būti pavaizduota tokiu AND-OR medžiu (tai yra dalis viso medžio, galime lengvai pridėti dar keletą taisyklių):\n",
|
||
"\n",
|
||
"\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Mūsų paties ekspertinių sistemų aplinka su atgaline išvada\n",
|
||
"\n",
|
||
"Pabandykime apibrėžti paprastą žinių reprezentavimo kalbą, pagrįstą gamybos taisyklėmis. Mes naudosime Python klases kaip raktinius žodžius taisyklėms apibrėžti. Iš esmės bus 3 klasių tipai:\n",
|
||
"* `Ask` reiškia klausimą, kurį reikia užduoti vartotojui. Jame yra galimų atsakymų rinkinys.\n",
|
||
"* `If` reiškia taisyklę ir yra tik sintaksinė cukraus forma taisyklės turiniui saugoti.\n",
|
||
"* `AND`/`OR` yra klasės, reprezentuojančios AND/OR medžio šakas. Jos tiesiog saugo argumentų sąrašą viduje. Kad supaprastintume kodą, visa funkcionalumas apibrėžtas tėvinėje klasėje `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": [
|
||
"Mūsų sistemoje darbo atmintyje būtų laikomas **faktų** sąrašas kaip **atributų-reikšmių poros**. Žinių bazę galima apibrėžti kaip vieną didelį žodyną, kuris susieja veiksmus (naujus faktus, kurie turėtų būti įterpti į darbo atmintį) su sąlygomis, išreikštomis AND-OR išraiškomis. Taip pat kai kurių faktų galima `Paklausti`.\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": [
|
||
"Norėdami atlikti atvirkštinę išvadą, apibrėšime klasę `Knowledgebase`. Ji turės:\n",
|
||
"* Darbinę `memory` – žodyną, susiejantį atributus su reikšmėmis\n",
|
||
"* Žinių bazės `rules` pagal aukščiau apibrėžtą formatą\n",
|
||
"\n",
|
||
"Du pagrindiniai metodai yra:\n",
|
||
"* `get`, skirtas gauti atributo reikšmę, jei reikia, atliekant išvadą. Pavyzdžiui, `get('color')` gautų spalvos reikšmę (jei reikia, paklaus ir išsaugos reikšmę vėlesniam naudojimui darbo atmintyje). Jei paklausime `get('color:blue')`, paklaus spalvos, o tada grąžins reikšmę `y`/`n` priklausomai nuo spalvos.\n",
|
||
"* `eval` atlieka pačią išvadą, t. y. keliauja AND/OR medžiu, įvertina po tikslus ir pan.\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": [
|
||
"Dabar apibrėžkime mūsų gyvūnų žinių bazę ir atlikime konsultaciją. Atkreipkite dėmesį, kad šis kvietimas užduos jums klausimus. Galite atsakyti rašydami `y`/`n` už klausimus su atsakymais taip/ne, arba nurodydami numerį (0..N) už klausimus su ilgesniais daugybinio pasirinkimo atsakymais.\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": [
|
||
"## Naudojimasis Experta pirmyn inferencijai\n",
|
||
"\n",
|
||
"Kitame pavyzdyje bandysime įgyvendinti pirmyn inferenciją naudodami vieną iš žinių reprezentacijos bibliotekų, [Experta](https://github.com/nilp0inter/experta). **Experta** yra biblioteka pirmyn inferencijos sistemoms kurti Python kalba, sukurta taip, kad būtų panaši į klasikines senas sistemas, tokias kaip [CLIPS](http://www.clipsrules.net/index.html).\n",
|
||
"\n",
|
||
"Mes taip pat galėjome įgyvendinti pirmyn grandininimą patys be didelių problemų, tačiau naivios įgyvendinimo metodikos dažniausiai nėra labai efektyvios. Dėl efektyvesnio taisyklių suderinimo naudojamas specialus algoritmas [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": [
|
||
"Apibrėšime mūsų sistemą kaip klasę, paveldinčią iš `KnowledgeEngine`. Kiekviena taisyklė apibrėžiama atskira funkcija su `@Rule` anotacija, kuri nurodo, kada taisyklė turėtų būti aktyvuojama. Taisyklės viduje galime pridėti naujų faktų naudodami `declare` funkciją, o pridėjus tuos faktus, į priekį veikiantis spėliojimo variklis iškvies daugiau taisyklių.\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": [
|
||
"Kai apibrėžiame žinių bazę, užpildome darbo atmintį pradiniais faktais, o tada iškviečiame `run()` metodą, kad atliktume išvedimą. Kaip rezultatą galite matyti, kad nauji išvesti faktai pridedami prie darbo atminties, įskaitant galutinį faktą apie gyvūną (jei teisingai nustatėme visus pradinius faktus).\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**Atsakomybės apribojimas**:\nŠis dokumentas buvo išverstas naudojant dirbtinio intelekto vertimo paslaugą [Co-op Translator](https://github.com/Azure/co-op-translator). Nors siekiame tikslumo, prašome atkreipti dėmesį, kad automatiniai vertimai gali turėti klaidų ar netikslumų. Originalus dokumentas jo gimtąja kalba turėtų būti laikomas autoritetingu šaltiniu. Kritinei informacijai rekomenduojama naudoti profesionalų žmogaus vertimą. Mes neatsakome už jokias nesusipratimus ar neteisingus interpretavimus, kilusius iš šio vertimo naudojimo.\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-16T06:41:58+00:00",
|
||
"source_file": "lessons/2-Symbolic/Animals.ipynb",
|
||
"language_code": "lt"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 2
|
||
} |