477 lines
18 KiB
Plaintext
477 lines
18 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"collapsed": true
|
|
},
|
|
"source": [
|
|
"# Eläinasiantuntijajärjestelmän toteuttaminen\n",
|
|
"\n",
|
|
"Esimerkki [AI for Beginners Curriculum](http://github.com/microsoft/ai-for-beginners) -materiaalista.\n",
|
|
"\n",
|
|
"Tässä esimerkissä toteutamme yksinkertaisen tietopohjaisen järjestelmän eläimen tunnistamiseksi fyysisten ominaisuuksien perusteella. Järjestelmä voidaan esittää seuraavana JA-TAI-puun osana (tämä on osa koko puuta, ja siihen voidaan helposti lisätä lisää sääntöjä):\n",
|
|
"\n",
|
|
"\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Oma asiantuntijajärjestelmämme kuori takaapäin päättelyllä\n",
|
|
"\n",
|
|
"Yritetään määritellä yksinkertainen kieli tietämyksen esittämiseen tuotantosääntöjen pohjalta. Käytämme Python-luokkia avainsanoina sääntöjen määrittämiseen. Periaatteessa luokkia on kolmenlaisia:\n",
|
|
"* `Ask` edustaa kysymystä, joka täytyy kysyä käyttäjältä. Se sisältää joukon mahdollisia vastauksia.\n",
|
|
"* `If` edustaa sääntöä, ja se on vain syntaktinen sokeri säännön sisällön tallentamiseen\n",
|
|
"* `AND`/`OR` ovat luokkia, jotka edustavat JA/TAI-haaroja puussa. Ne vain tallentavat argumenttilistan sisälleen. Koodin yksinkertaistamiseksi kaikki toiminnallisuus on määritelty yläluokassa `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": [
|
|
"Järjestelmässämme työmuisti sisältäisi luettelon **tiedoista** **ominaisuus-arvopareina**. Tietopohja voidaan määritellä yhtenä suurena sanakirjana, joka yhdistää toiminnot (uudet tiedot, jotka tulisi lisätä työmuistiin) ehtoihin, ilmaistuna JA-TAI lausekkeina. Lisäksi joitakin tietoja voidaan `Kysyä`.\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": [
|
|
"Taaksepäin päättelyn suorittamiseksi määrittelemme `Knowledgebase`-luokan. Se sisältää:\n",
|
|
"* Työmuistin `memory` - sanakirja, joka yhdistää attribuutit arvoihin\n",
|
|
"* Tietokannan `rules` yllä määritellyssä formaatissa\n",
|
|
"\n",
|
|
"Kaksi päämenetelmää ovat:\n",
|
|
"* `get`, jolla saadaan attribuutin arvo, suorittaen päättely tarvittaessa. Esimerkiksi `get('color')` saa väri-kohdan arvon (se kysyy tarvittaessa ja tallentaa arvon myöhempää käyttöä varten työmuistiin). Jos kysymme `get('color:blue')`, se kysyy väriä ja palauttaa sitten arvon `y`/`n` riippuen väristä.\n",
|
|
"* `eval` suorittaa varsinaisen päättelyn, eli käy läpi JA/TAI-puun, arvioi alakohtia jne.\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": [
|
|
"Määritellään nyt eläintietokantamme ja suoritetaan konsultaatio. Huomaa, että tämä kutsu esittää sinulle kysymyksiä. Vastata voit kirjoittamalla `y`/`n` kyllä/ei -kysymyksiin tai valitsemalla numeron (0..N) pidempiin monivalintakysymyksiin.\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": [
|
|
"## Ekspertan käyttäminen eteenpäin päin päättelyyn\n",
|
|
"\n",
|
|
"Seuraavassa esimerkissä yritämme toteuttaa eteenpäin päin päättelyn käyttäen yhtä tiedon esityksen kirjastoista, [Experta](https://github.com/nilp0inter/experta). **Experta** on kirjasto eteenpäin päin päättelyjärjestelmien luomiseen Pythonilla, joka on suunniteltu muistuttamaan klassista vanhaa järjestelmää [CLIPS](http://www.clipsrules.net/index.html).\n",
|
|
"\n",
|
|
"Voisimme myös toteuttaa eteenpäin sarjoittamisen itse ilman suurempia ongelmia, mutta naiivit toteutukset eivät yleensä ole kovin tehokkaita. Tehokkaampaan sääntöjen sovitukseen käytetään erityistä algoritmia [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": [
|
|
"Määrittelemme järjestelmämme luokkana, joka perii `KnowledgeEngine`-luokan. Jokainen sääntö määritellään erillisenä funktiona, jossa on `@Rule`-annotaatio, joka määrittää, milloin sääntö aktivoituu. Säännön sisällä voimme lisätä uusia tietoja käyttämällä `declare`-funktiota, ja näiden tietojen lisääminen johtaa siihen, että eteenpäin päättelymoottori kutsuu lisää sääntöjä.\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": [
|
|
"Kun olemme määrittäneet tietokannan, täytämme työmuistimme joillakin alkuperäisillä tosiasioilla ja kutsumme sitten `run()`-metodia suorittaaksemme päättelyn. Näet tuloksena, että uusia pääteltyjä tosiasioita lisätään työmuistiin, mukaan lukien lopullinen tosiasia eläimestä (jos olemme asettaneet kaikki alkuperäiset tosiasiat oikein).\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**Vastuuvapauslauseke**: \nTämä asiakirja on käännetty tekoälypohjaisella käännöspalvelulla [Co-op Translator](https://github.com/Azure/co-op-translator). Pyrimme tarkkuuteen, mutta otathan huomioon, että automaattikäännöksissä saattaa esiintyä virheitä tai epätarkkuuksia. Alkuperäinen asiakirja sen alkuperäiskielellä on virallinen ja sitova lähde. Tärkeissä asioissa suositellaan ammattilaisen tekemää ihmiskäännöstä. Emme ole vastuussa tämän käännöksen käytöstä mahdollisesti aiheutuvista väärinkäsityksistä tai tulkinnoista.\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:51:56+00:00",
|
|
"source_file": "lessons/2-Symbolic/Animals.ipynb",
|
|
"language_code": "fi"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
} |