477 lines
19 KiB
Plaintext
477 lines
19 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"collapsed": true
|
|
},
|
|
"source": [
|
|
"# تنفيذ نظام خبير للحيوانات\n",
|
|
"\n",
|
|
"مثال من [منهج الذكاء الاصطناعي للمبتدئين](http://github.com/microsoft/ai-for-beginners).\n",
|
|
"\n",
|
|
"في هذا المثال، سنقوم بتنفيذ نظام بسيط قائم على المعرفة لتحديد حيوان بناءً على بعض الخصائص الفيزيائية. يمكن تمثيل النظام بالشجرة AND-OR التالية (هذا جزء من الشجرة الكاملة، يمكننا بسهولة إضافة المزيد من القواعد):\n",
|
|
"\n",
|
|
"\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## قشرة أنظمة الخبراء الخاصة بنا مع الاستدلال العكسي\n",
|
|
"\n",
|
|
"لنحاول تعريف لغة بسيطة لتمثيل المعرفة تعتمد على قواعد الإنتاج. سنستخدم فئات بايثون كالكلمات المفتاحية لتعريف القواعد. سيكون هناك 3 أنواع أساسية من الفئات:\n",
|
|
"* `Ask` تمثل سؤالًا يحتاج إلى طرحه على المستخدم. تحتوي على مجموعة الإجابات الممكنة.\n",
|
|
"* `If` تمثل قاعدة، وهي مجرد سكر نحوي لتخزين محتوى القاعدة\n",
|
|
"* `AND`/`OR` هما فئتان لتمثيل فروع AND/OR للشجرة. هما فقط تخزنان قائمة الوسائط بداخلها. لتبسيط الكود، تم تعريف كل الوظائف في الفئة الأصل `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": [
|
|
"في نظامنا، تحتوي الذاكرة العاملة على قائمة من **الحقائق** كـ **أزواج سمات-قيمة**. يمكن تعريف قاعدة المعرفة على أنها قاموس واحد كبير يربط الإجراءات (حقائق جديدة يجب إدراجها في الذاكرة العاملة) بالشروط، المعبر عنها بتعابير AND-OR. أيضًا، يمكن `Ask` عن بعض الحقائق.\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": [
|
|
"لأداء الاستدلال العكسي، سنقوم بتعريف فئة `Knowledgebase`. ستحتوي على:\n",
|
|
"* `memory` العمل - قاموس يربط السمات بالقيم\n",
|
|
"* قواعد Knowledgebase بصيغة المعرفة أعلاه\n",
|
|
"\n",
|
|
"الطريقتان الرئيسيتان هما:\n",
|
|
"* `get` للحصول على قيمة سمة، مع إجراء الاستدلال إذا لزم الأمر. على سبيل المثال، `get('color')` ستحصل على قيمة خانة اللون (ستطلب إذا لزم الأمر، وتخزن القيمة للاستخدام لاحقًا في الذاكرة العاملة). إذا طلبنا `get('color:blue')`، فسوف تطلب لونًا، ثم تعيد قيمة `y`/`n` اعتمادًا على اللون.\n",
|
|
"* `eval` تُجري الاستدلال الفعلي، أي تتنقل في شجرة AND/OR، تقييم الأهداف الفرعية، إلخ.\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": [
|
|
"الآن دعونا نُعرّف قاعدة معرفتنا عن الحيوانات ونُجري الاستشارة. لاحظ أن هذه المكالمة ستطرح عليك أسئلة. يمكنك الإجابة بكتابة `y`/`n` لأسئلة نعم-لا، أو بتحديد رقم (0..N) لأسئلة ذات إجابات متعددة الأطول.\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": [
|
|
"## استخدام Experta للاستدلال الأمامي\n",
|
|
"\n",
|
|
"في المثال التالي، سنحاول تنفيذ الاستدلال الأمامي باستخدام إحدى مكتبات تمثيل المعرفة، [Experta](https://github.com/nilp0inter/experta). **Experta** هي مكتبة لإنشاء أنظمة الاستدلال الأمامي في بايثون، وهي مصممة لتكون مشابهة للنظام الكلاسيكي القديم [CLIPS](http://www.clipsrules.net/index.html).\n",
|
|
"\n",
|
|
"كان بإمكاننا أيضًا تنفيذ السلسلة الأمامية بأنفسنا بدون مشاكل كثيرة، لكن التنفيذات البدائية عادةً ليست فعالة جدًا. من أجل مطابقة القواعد بشكل أكثر فعالية، يُستخدم خوارزمية خاصة تسمى [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": [
|
|
"سنقوم بتعريف نظامنا كصف فرعي من `KnowledgeEngine`. يتم تعريف كل قاعدة بواسطة دالة منفصلة مع تعليمة `@Rule`، التي تحدد متى يجب أن يتم تطبيق القاعدة. داخل القاعدة، يمكننا إضافة حقائق جديدة باستخدام دالة `declare`، وإضافة هذه الحقائق سيؤدي إلى استدعاء بعض القواعد الأخرى بواسطة محرك الاستدلال الأمامي.\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": [
|
|
"بمجرد أن نحدد قاعدة المعرفة، نقوم بملء ذاكرتنا العاملة ببعض الحقائق الأولية، ثم نستدعي طريقة `run()` لأداء الاستدلال. يمكنك أن ترى كنتيجة أنه تمت إضافة حقائق مستنتجة جديدة إلى الذاكرة العاملة، بما في ذلك الحقيقة النهائية حول الحيوان (إذا قمنا بإعداد جميع الحقائق الأولية بشكل صحيح).\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**تنويه**:\nتمت ترجمة هذا المستند باستخدام خدمة الترجمة الآلية [Co-op Translator](https://github.com/Azure/co-op-translator). بينما نسعى لتحقيق الدقة، يرجى العلم أن الترجمات الآلية قد تحتوي على أخطاء أو معلومات غير دقيقة. يجب اعتبار المستند الأصلي بلغته الأصلية المصدر الرسمي والمعتمد. بالنسبة للمعلومات الحساسة أو الهامة، يُنصح بالاستعانة بالترجمة المهنية البشرية. نحن غير مسؤولين عن أي سوء فهم أو تفسير ناتج عن استخدام هذه الترجمة.\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-15T10:39:30+00:00",
|
|
"source_file": "lessons/2-Symbolic/Animals.ipynb",
|
|
"language_code": "ar"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
} |