Vous êtes sur la page 1sur 258

LY_SII __ 1 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Préface
Introduction

chapitre 1-Langage de programmation Java …………………………………………………17


Vue d'ensemble de la langue de Java
Dispositifs de langue de Java
Types de données
Structures de gestion
Objets, classes, et méthodes
Applet et applications
Interface d'indigène de Java
D'autres dispositifs de langue de Java
Paquets
java.lang
java.lang.reflect
java.io
java.util
java.util.zip
java.net
java.awt
java.applet
java.text
java.security
java.beans
java.rmi
java.sql
Environnements de développement de Java
Using Java pour les agents intelligents
Autonomie
Intelligence
Mobilité
Résumé
Exercices

Chapitre 2-Resolution des Problemes utilisant la recherche ……………………36


Définition du problème
L'espace d'état
Stratégies de recherche
Recherche en largeur
Profondeur-Première recherche
La classe de SearchNode
Applet de recherche
Amélioration de la Profondeur-Première recherche
Recherche heuristique
Résumé
Exercices
LY_SII __ 2 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

chapitre 3- Représentation des connaissances…………………………..58


De la connaissance à la représentation de connaissance
Représentation procédurale
Représentation apparentée
Représentation hiérarchique
Logique d'attribut
Résolution
Unification
Vues
Filets sémantiques
Représentation de l'incertitude
Format d'échange de la connaissance
Établir une base de connaissance
Résumé
Exercices

chapitre 4- Systèmes du Raisonnement………...………………………..72


Motifs avec des règles
Enchaînement vers l'avant
Un exemple de chaînage avant
Enchaînement en arrière
L'applet de règle de Java
Règles
Clauses
Variables
Variables de règle
Base de règle
Exécution de chaînage avant
Vers l'arrière-Enchaînement de l'exécution
Exécution d'applet de règle
Systèmes brouillés de règle
Planification
Résumé
Exercices

chapitre 5-Systèmes d'Approntissage ………………………………………………………………108


Vue d'ensemble
Étude des paradigmes
Réseaux neurologiques
Propagation arrière
Cartes de Kohonen
Arbres de décision
Théorie de l'information
Apprendre l'applet
Variables continues
Variables discrètes
La classe d'ensemble de données
Exécution arrière d'appui vertical
Exécution de carte de Kohonen
LY_SII __ 3 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Exécution d'arbre de décision


L'exécution d'applet d'étude
Systèmes de classificateur
Algorithmes génétiques
Résumé
Exercices

chapitre 6-Agents Intelligents ………………………………………………...149


De l'AI à IA
Perception
Action
Systèmes de Multiagent
Tableaux noirs
Communication
KQML
Agents de coopération
Agents de concurrence
Résumé
Exercices

chapitre 7-Cadre d'agent Intelligent ……..………………………………….159


Conditions
Buts de conception
Caractéristiques fonctionnelles
Architecture d'agent intelligent
Le cadre de CIAgent
La classe basse de CIAgent
CIAgentEvent
CIAgentEventListener
Perfectionnements de RuleBase
Résumé
Exercices

chapitre 8-Application PCManager …………………………………………173


Introduction
Un exemple
Alarmes : Le TimerAgent
Montres : Le FileAgent
Application de PCManager
La classe d'AlarmDialog
La classe de WatchDialog
Discussion
Résumé
Exercices

chapitre 9-Application NewsFilter ………………………………………….195


Introduction
Un exemple
Classe de NewsFilter
LY_SII __ 4 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Classe de NewsArticle
Classe de FilterAgent
Discussion
Résumé
Exercices

chapitre 10-Application MarketPlace ………………………………………226


Introduction
Un exemple
FacilitatorAgent
CIAgentMessage
BuyerAgent
SellerAgent
Acheteurs et vendeurs augmentés
Application de marché
Discussion
Résumé
Exercices

chapitre 11-Environnements d'agent Java-Based ……………………………………….251


Ferrets
Technologie d'agent de logiciel de ftp
Voyager
Odyssée
JATLite
InfoSleuth
Jess
ABE
Discussion
Résumé
Annexe A
Annexe B
Bibliographie
Index
LY_SII __ 5 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Introduction
Dans cette section, nous présentons une introduction aux deux matières principales couvertes
dans ce livre : intelligence artificielle et agents intelligents. Nous traçons l'histoire de la
recherche en matière d'intelligence artificielle et discutons les lieux de base des écoles de
traitement de symbole et de réseau neurologique. Nous explorons l'évolution des systèmes
d'intelligence artificielle d'une technologie intéressante mais en grande partie critiquée dans la
base pour des applications d'aujourd'hui d'agent intelligent. L'apparition simultanée du calcul
de réseau et du World Wide Web, et leurs conditions pour le logiciel intelligent sont
également discutées. Nous présentons les attributs principaux des agents intelligents tels que
l'autonomie, la mobilité, et l'intelligence et fournissons une taxonomie pour classifier de
diverses applications d'agent intelligent.

Intelligence artificielle

La science de l'intelligence artificielle est approximativement quarante années, remontant à


une conférence tenue chez Dartmouth en 1958. Pendant les quarante dernières années, la
perception publique de l'AI n'a pas toujours assorti la réalité. En premières années, l'excitation
des deux scientifiques et la presse populaire ont tendu à exagérer la prouesse réelle des
systèmes d'AI. Le succès tôt dans le jeu de jeu, le théorème mathématique s'avérant, le
raisonnement common-sense, et les mathématiques d'université a semblé promettre le progrès
rapide vers l'intelligence informatique pratique. Pendant ce temps les champs de la
reconnaissance de la parole, de l'arrangement de langage naturel, et de l'identification à
caractère optique tous d'image ont commencé comme des spécialités dans des laboratoires de
recherches d'AI.

Cependant, les succès tôt ont été suivis d'une réalisation lente que ce qui était dure pour des
personnes et facile pour des ordinateurs était plus qu'a compensé par les choses il était facile
pour des personnes faire que mais presque impossible pour que les ordinateurs fassent. La
promesse des premières années jamais n'a été entièrement réalisée, et la recherche d'AI et
l'intelligence artificielle de limite sont devenues associées à l'échec et à la technologie
overhyped.

Néanmoins, les chercheurs en intelligence artificielle ont apporté les contributions


significatives à de l'informatique. Plusieurs d'idées traditionnelles d'aujourd'hui au sujet des
ordinateurs ont été par le passé considérées fortement controversées et impraticables une fois
d'abord proposées par la communauté du renseignement artificielle. Si c'est l'interface
utilisateurs de WIMP (fenêtres, icône, souris, indicateur), qui domine l'interaction homme-
LY_SII __ 6 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

ordinateur aujourd'hui, ou les techniques de programmation orientée objectivement, qui sont


développement de logiciel commercial rapide, AI a fait un impact. Aujourd'hui, l'idée des
agents intelligents de logiciel aidant des utilisateurs font des tâches à travers des réseaux des
ordinateurs même ne serait pas discutée sinon pendant les années de la recherche dans l'AI, la
résolution des problèmes, des motifs, l'étude, et la planification distribués. Ainsi avant que
nous plongions dans les outils et les tours du commerce d'AI, jetons un bref coup d'oeil aux
idées fondamentales derrière l'intelligence dans nos agents intelligents.

Concepts de base

Dans toute son histoire, l'intelligence artificielle s'est concentrée sur les problèmes qui se
trouvent juste au delà de la portée de quels ordinateurs du dernier cri pourraient faire à ce
moment-là (riches et chevalier 1991). En tant que systèmes de l'informatique et informatiques
se sont transformés en des niveaux plus élevés de la fonctionnalité, les secteurs qui tombent
dans le domaine de la recherche d'AI ont également changé. Inventé pour calculer des
diagrammes de balistique pour des armes d'II-ère de guerre mondiale, la puissance et la
polyvalence des ordinateurs juste étaient imaginés. Les calculateurs numériques Étaient un
concept relativement nouveau, et les idées de ce qui serait jeu et mathématiques inclus par
fonctions utiles de jeu d'AI.

Après 40 ans de travail, nous pouvons identifier trois phases importantes du développement
dans la recherche d'AI. En premières années, une grande partie du travail a traité les
problèmes formels qui ont été structurés et a eu des frontières bien définies de problème. Ce
travail inclus sur des qualifications math-connexes telles que prouver des théorèmes, la
géométrie, le calcul, et jouer des jeux tels que des contrôleurs et des échecs. Dans cette
première phase, l'emphase était sur créer la « pensée générale usine » qui serait capable de
résoudre de grandes catégories de problèmes. Ces systèmes ont tendu à inclure le
raisonnement sophistiqué et à rechercher des techniques.

Une deuxième phase a commencé par l'identification que les projets d'AI les plus réussis
étaient des domaines très étroits visés de problème et a habituellement codé beaucoup de
connaissance spécifique au sujet du problème à résoudre. Cette approche d'ajouter la
connaissance spécifique de domaine à un système plus général de raisonnement a mené au
premier succès commercial dans des systèmes d'AI-expert. Des systèmes experts basés sur les
règles ont été développés pour faire de diverses tâches comprenant l'analyse chimique, les
systèmes informatiques de configuration, et diagnostiquer des conditions médicales dans les
patients. Ils ont utilisé la recherche dans la représentation de connaissance, ingénierie
cognitive, et ont avancé des techniques de raisonnement, et ont montré que l'intelligence
artificielle pourrait fournir la valeur réelle dans des applications commerciales. À ce même
temps, des postes de travail d'ordinateur ont été développés spécifiquement pour fonctionner
des applications blèsent, de Prolog, et de causerie. Ces postes de travail d'AI ont comporté les
environnements de développement integrated puissants et étaient des années en avant d'autres
environnements de logiciel commerciaux.

Nous sommes maintenant bien dans une troisième phase des applications d'AI. Depuis les fin
des années 1980, une grande partie de la communauté d'AI avait travaillé à résoudre les
problèmes difficiles de la vision par ordinateur et la parole, l'arrangement et la traduction de
langage naturel, le raisonnement commonsense, et la commande de robot. Une branche d'AI
connue sous le nom de connexionisme a regagné la popularité et a augmenté la gamme des
LY_SII __ 7 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

applications commerciales par l'utilisation des réseaux neurologiques pour l'exploitation de


données, la modélisation, et la commande adaptative. Les méthodes biologiques telles que des
algorithmes génétiques et des systèmes de logique alternatifs tels que la logique floue ont
combiné pour reenergize le champ de l'intelligence artificielle. Récemment, la croissance
explosive de l'Internet et l'informatique répartie a mené à l'idée des agents qui se déplacent par
le réseau, agissant l'un sur l'autre les uns avec les autres et effectuant des tâches pour leurs
utilisateurs. Les agents intelligents emploient les dernières techniques IA Pour fournir les
agents autonomes, intelligents, et mobiles de logiciel, prolongeant de ce fait la portée des
utilisateurs à travers des réseaux.

Quand parlons-nous de l'intelligence artificielle ou les agents intelligents, la question surgit


souvent, que nous signifie par intelligence ? Voulons-nous dire que notre agent agit comme
un humain ? pense comme un humain ? qu'il agit ou pense rationnellement ? Tandis qu'il y a
autant de réponses car il y a des chercheurs impliqués dans le travail d'AI, nous te dirons que
ce que nous pensons il signifie. À nous, un agent intelligent agit rationnellement. Il fait les
choses que nous ferions, mais pas nécessairement la même manière nous les ferions. Nos
agents peuvent ne pas passer l'essai de Turing, proposé par Alan Turing dans 1950 comme
mesure pour juger l'intelligence d'ordinateur. Mais nos agents effectueront des tâches utiles
pour nous. Ils nous rendront plus productifs. Ils nous permettront d'effectuer plus de travail
dans moins de temps, et voient l'information plus intéressante et des données moins inutiles.
Nos programmes seront qualitativement meilleurs using des techniques IA Qu'ils seraient
autrement. N'importe comment humble, c'est notre but-à développer de meilleures, plus futées
applications.

Traitement de symbole

Il y a beaucoup de comportements auxquels nous attribuons l'intelligence. Pouvoir identifier


des situations ou des cas est un type d'intelligence. Par exemple, un docteur qui parle avec un
patient et collecte des informations concernant les symptômes du patient et puis peut
diagnostiquer exactement un mal et le cours du traitement approprié exhibe ce type
d'intelligence. Pouvant apprendre de quelques exemples et puis généraliser et s'appliquer que
la connaissance à de nouvelles situations est une autre forme d'intelligence.

Le comportement intelligent peut être produit par la manipulation des symboles. C'est l'un des
principes primaires des techniques d'intelligence artificielle. Les symboles sont des marques
qui représentent les objets ou les idées réels et peuvent être représentées à l'intérieur d'un
ordinateur par des chaînes de caractères ou par des nombres. Dans cette approche, un
problème doit être représenté par une collection de symboles, et alors un algorithme approprié
doit être développé pour traiter ces symboles.

L'hypothèse physique de systèmes de symbole (Newell et Simon 1980) indique que seulement
« un système physique de symbole a les moyens nécessaires et suffisants pour l'action
intelligente générale. » Cette idée, cette intelligence découle de la manipulation active des
symboles, était la pierre angulaire sur laquelle une grande partie de la recherche suivante d'AI
a été établie. Les chercheurs ont construit les systèmes intelligents using des symboles pour la
reconnaissance des structures, raisonnement, apprenant, et prévoyant (Russell et Norvig
1995). L'histoire a prouvé que les symboles peuvent être appropriés pour le raisonnement et la
planification, mais que la reconnaissance des structures et l'étude peuvent mieux être laissées
à d'autres approches.
LY_SII __ 8 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Il y a plusieurs manières typiques de manoeuvrer les symboles qui ont prouvé utile en
résolvant des problèmes. Le plus commun est d'employer les symboles dans les formulations
de si alors les règles qui sont traitées using des techniques de raisonnement appelaient
l'enchaînement vers l'avant et en arrière. L'enchaînement vers l'avant laisse le système déduire
la nouvelle information d'un ensemble donné de données d'entrée. L'enchaînement en arrière
permet au système de tirer des conclusions basées sur un état de but spécifique. Une autre
technique de traitement de symbole est un réseau sémantique, dans lequel les symboles et les
concepts qu'ils représentent sont reliés par des liens dans un réseau de la connaissance qui
peut alors être employé pour déterminer de nouveaux rapports. Un autre formalisme est une
armature, où des attributs relatifs d'un concept sont groupés ensemble dans une structure avec
des fentes et sont traités par un ensemble de procédures relatives appelées des démons ou les
remplisseurs. Le changement de la valeur d'une fente simple pourrait placer outre d'un ordre
complexe des procédures relatives pendant que l'ensemble de la connaissance représenté par
les armatures est rendu conformé. Ces techniques de raisonnement sont décrites en plus détail
en chapitres 3 et 4.

Les techniques de traitement de symbole représentent relativement un à niveau élevé dans le


processus cognitif. D'une perspective de la science cognitive, le traitement de symbole
correspond à la pensée consciente, où la connaissance est explicitement représentée, et la
connaissance elle-même peut être examinée et manoeuvrée. Tandis que le traitement de
symbole et la pensée consciente sont clairement une partie de l'histoire, un autre ensemble de
chercheurs examinent une approche de symbole-moins à l'intelligence modelée après le
cerveau.

Réseaux neurologiques

Une méthode de plus en plus populaire en intelligence artificielle s'appelle les réseaux
neurologiques ou le connexionisme. Les réseaux neurologiques ont moins à faire avec le
traitement de symbole inspiré par la logique mathématique formelle, et plus pour faire avec la
façon dont l'intelligence humaine ou normale se produit. Les humains ont les réseaux
neurologiques dans des leurs têtes, se composant des centaines de milliards de cellules du
cerveau appelées les neurones, reliés par les synapses adaptatives qui agissent en tant que
systèmes de commutation entre les neurones. Des réseaux neurologiques artificiels sont basés
sur cette architecture massivement parallèle trouvée dans le cerveau. Ils l'information de
processus, pas par des symboles de manipulation, mais en traitant des grands nombres de
données brutes d'une façon parallèle. Différentes formulations des réseaux neurologiques sont
employées pour segmenter ou des données de faisceau, pour classifier des données, et pour
faire les modèles prédictifs using des données. Une collection d'unités de traitement qui
imitent les fonctionnements de base de vrais neurones est employée pour remplir ces
fonctions. Pendant que le réseau neurologique apprend ou est formé, un ensemble de poids de
raccordement entre les unités de traitement est modifié a basé sur les rapports perçus dans les
données.

Comparé aux systèmes de traitement de symbole, les réseaux neurologiques remplissent des
fonctions cognitives relativement de bas niveau. Les connaissances qu'ils acquièrent par
l'apprentissage sont stockées dans les poids de raccordement et ne sont pas facilement
disponibles pour l'examen ou la manipulation. Cependant, la capacité des réseaux
neurologiques d'apprendre de et s'adapter à leurs environnements est une fonction cruciale
requise par les systèmes logiciels intelligents. D'une perspective de la science cognitive, les
LY_SII __ 9 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

réseaux neurologiques sont plutôt la reconnaissance des structures fondamentale et sensoriel


le traitement de cela est effectué par les niveaux sans connaissance de l'esprit humain. Nous
discutons les réseaux neurologiques et l'étude en chapitre 5.

Là où la recherche en matière d'intelligence artificielle a été par le passé dominée par des
techniques de traitement de symbole, il y a maintenant une vue plus équilibrée où les forces
des réseaux neurologiques sont employées pour parer les faiblesses du traitement de symbole.
Dans notre vue, tous les deux sont absolument nécessaires afin de créer des applications
intelligentes et des agents autonomes intelligents.

L'Internet et le World Wide Web

L'Internet s'est développé hors du placement de gouvernement pour les chercheurs qui ont dû
collaborer au-dessus de grandes distances. Comme sous-produit de résoudre ces problèmes,
des protocoles qui ont permis à différents ordinateurs de parler entre eux, les données
d'échange, et le travail ensemble requis ont été développés. Ceci a mené au TCP/IP comme
protocole de gestion de réseau de norme de fait pour l'Internet. La croissance de l'Internet est
stupéfiante, avec le nombre d'emplacements s'élevant exponentiellement. Des milliers de
nouveaux emplacements sont reliés à l'Internet chaque mois.

Tandis que le courrier électronique (email) était par le passé le service primaire fourni par
l'Internet, l'édition de l'information et la distribution de logiciel sont maintenant d'importance
égale. Le service d'information des textes de Gopher, qui a gagné la popularité au début des
années 90 a produit de la première vague d'information éditant sur le filet. Le File Transfer
Protocol permet à des utilisateurs de télécharger des travaux de recherche et les articles aussi
bien que recherchent des actualisations de logiciel et accomplissent même des logiciels au-
dessus de l'Internet. Mais c'était le protocole de transfert hypertexte (HTTP) qui a introduit
l'Internet du royaume du milieu universitaire et des technologues informaticiens dans la
conscience publique. Le développement du navigateur de mosaïque à l'Université des Illinois
a transformé l'Internet en support de communications d'usage universel, où les novices et les
experts en matière d'ordinateur, les consommateurs, et les entreprises peuvent agir l'un sur
l'autre des manières entièrement nouvelles.

Le World Wide Web, avec ses possibilités de édition et de radiodiffusion, a développé


l'étendue des applications et les services qui sont à la disposition des utilisateurs de l'Internet.
Le web browser maintenant-omniprésent fournit une interface universelle aux applications
indépendamment desquelles la plate-forme de serveur sert vers le haut l'application. En mode
de lecture rapide ou de « traction », le Web permet à des individus d'explorer de vastes
quantités de l'information dans un environnement relativement sans couture. Savoir que toute
les information est dehors là, mais ne pas savoir exactement la trouver, peut rendre
l'expérience de lecture rapide de Web tout à fait frustrante. Les moteurs de recherche et des
emplacements de l'index populaires de Web tels qu'AltaVista, excitent, Yahoo, et Lycos
fournissent un service important aux utilisateurs du Web, par l'information de groupement par
des matières et des mots-clés. Mais même avec les moteurs de recherche et les emplacements
d'index, le Web passant en revue est toujours une proposition hit-or-miss (avec des coups
manqués plus probables que des coups). Dans cet environnement, les agents intelligents
émergeront en tant qu'aides personnels véritablement utiles par la recherche, la conclusion, et
l'information de filtrage du Web, et de le porter à la connaissance d'un utilisateur. Même
pendant que le Web se transforme en la « poussée » ou le mode d'émission, où les utilisateurs
LY_SII __ 10 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

souscrivent aux emplacements qui envoient les mises à jour constantes à leurs pages Web,
cette condition pour filtrer l'information ne partira pas. À moins que les emplacements
d'émission puissent envoyer les jets très personnalisés d'information, l'utilisateur devra encore
séparer l'information valable du bruit inutile.

Tandis que l'Internet et le World Wide Web ont attiré l'attention de public, les entreprises
adaptent rapidement la manière qu'elles emploient la technologie de l'information par leurs
réseaux internes ou intranets. Intranets d'utilisation de compagnies pour l'email interne et
externe, pour signaler l'information, et pour gérer des tâches administratives courantes. Les
Intranets permettent à une large variété d'ordinateurs de client de se relier aux serveurs
centralisés, sans coût et complexité des applications se développantes de client/serveur. Les
Intranets atteignent le même objectif et ont les mêmes avantages pour les compagnies que
l'Internet a pour des individus. Une application de client normalisée, le web browser, courant
sur les PCs standard ou les ordinateurs de réseau peu coûteux, peuvent fournir un unique de
l'accès à une collection d'applications network-based de corporatewide.

De l'AI aux agents intelligents

De même que souvent le cas quand un champ technique provoque l'intérêt commercial, il y a
eu un grands mouvement et changement de foyer de la communauté de la recherche d'AI pour
s'appliquer les techniques de base d'intelligence artificielle aux systèmes informatiques
répartis, aux intranets d'entreprise, à l'Internet, et au World Wide Web. Au commencement, le
foyer a été limité pour exprimer les tâches de recherches, de recherche documentaire, et de
filtrage. Mais car de plus en plus des transactions commerciales sont effectuées sur des
réseaux, il y a plus d'intérêt en ayant des agents futés qui peuvent effectuer des actions
spécifiques. En rapportant une étape et en regardant ce qu'est devenu l'Internet, beaucoup de
chercheurs qui avaient regardé comment les agents intelligents pourraient coopérer à réaliser
des tâches sur les systèmes informatiques répartis se sont rendus compte qu'il y a finalement
un problème à la recherche d'une technologie (par opposition à l'autre manière autour). Les
agents intelligents peuvent fournir la valeur réelle aux utilisateurs en ce monde nouveau, relié
ensemble, et géré en réseau.

Jusqu'à ce point, nous avons discuté l'intelligence artificielle et son évolution dans des agents
de logiciel à un niveau abstrait. Dans les sections suivantes, nous explorons certaines des
facettes techniques des agents intelligents, comment ils fonctionnent, et comment nous
pouvons les classifier avons basé sur leurs capacités et technologies fondamentales.

Événement-Condition-Actions

Supposer que nous avons un agent intelligent, fonctionnant de façon autonome, amorcé avec
la connaissance au sujet des tâches que nous exigeons de elle et préparons pour sortir sur le
réseau quand l'occasion se présente. Maintenant ce qui ? Comment l'agent sait-il que nous
voulons qu'il fasse quelque chose pour nous, ou qu'elle devrait répondre à quelqu'un qui
essaye de nous contacter ? C'est où nous devons traiter des événements, identifier des
conditions, et agir.

Dans le cadre des agents intelligents, un événement est quelque chose qui s'avère justement
changer l'environnement ou n'importe quoi dont l'agent devrait se rendre compte. Par
exemple, un événement a pu être l'arrivée d'un nouveau morceau de courrier. Ou c'a pu être un
LY_SII __ 11 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

changement à une page Web. Ou ce pourrait être un temporisateur allant au loin au minuit-
temps de commencer à envoyer les fax qui sont alignés vers le haut. Court de avoir notre
agent constamment exploiter et vérifier ou voter tous les systèmes de dispositif et
informatiques nous voulons qu'il surveille, ayant le signal d'événements les occurrences
qu'importantes soit la prochaine meilleure chose. En fait, ce peut être la meilleure chose, parce
que notre agent peut dormir, pensent au sujet de ce qui s'est produite pendant le jour, font des
tâches de ménage, ou toute autre chose utiles tandis qu'il attend le prochain événement pour se
produire.

Quand un événement se produit, l'agent doit identifier et évaluer ce que signifie l'événement
et puis répond à lui. Cette deuxième étape, déterminant ce que l'état ou l'état du monde est,
pourrait être simple ou extrêmement complexe selon la situation. Si le courrier est arrivé, alors
l'événement individu-décrira-un le nouveau morceau de courrier est arrivé. L'agent peut alors
devoir questionner le système de courrier pour découvrir qui a envoyé le courrier, et ce qui est
le sujet ou matière, ou même balayer le texte de courrier pour trouver des mots-clés. Toute la
ceci fait partie du composant de reconnaissance du cycle. L'événement initial peut réveiller
l'agent, mais l'agent alors doit figurer dehors ce qu'est la signification de l'événement dans la
limite de ses fonctions. Dans l'exemple de courrier, supposer que l'agent identifie que le
courrier est de votre patron, et que le message est classifié comme PRESSANT. Ceci nous
amène à prochain et peut-être la plupart utile d'aspect des agent-actions intelligentes.

Si les agents intelligents vont rendre nos vies plus faciles (ou au moins plus intéressantes),
elles doivent pouvoir agir, pour faire des choses pour nous. En ayant des ordinateurs faire les
choses pour nous n'est pas une nouvelle idée. Des ordinateurs ont été développés pour aider
des personnes travaillent. Cependant, ayant l'initié d'ordinateur une action en notre nom est
quelque chose totalement différente de sélectionner une commande sur la ligne de commande
et en pressant entrer pour courir la commande. Tandis que les résultats de notre introduire la
commande et du pressurage entrent ne peut pas toujours être exactement ce que nous avons eu
à l'esprit quand nous l'avons saisi (il semble toujours que nous réalisons que ce que nous
devrions avoir dactylographié après que nous pressions entrer), nous savons que celui qui se
produise, il est notre faire. En ayant un agent (intelligent ou pas, humain ou automatisé)
prendre une mesure pour nous exige un certain saut de la foi ou au moins un certain niveau de
confiance. Nous devons espérer que notre agent intelligent va se comporter rationnellement et
dans notre meilleur intérêt. Comme toutes les situations où nous déléguons la responsabilité à
un tiers, nous devons peser les risques et les récompenses. Le risque est que l'agent salira des
choses vers le haut, et nous devrons effectuer encore plus le travail pour placer des choses
droites. La récompense est que nous sommes libérés de devoir s'inquiéter des détails d'obtenir
ce morceau de travail fait.

Ainsi, les événement-condition-actions définissent le fonctionnement de notre agent.


Quelques chercheurs estiment qu'un agent doit également être proactif. Il doit non seulement
réagir aux événements, mais doit pouvoir prévoir et lancer des actions seule. Nous convenons.
Cependant, dans notre vue, cette action (signalant un certain événement, ou appelle une
certaine interface d'application) est le résultat d'un certain événement plus tôt ce qui a fait
entrer notre agent dans le mode de planification. Nous voulons que nos agents intelligents
puissent lancer des transactions avec d'autres agents en notre nom, using toute les
connaissance d'intelligence et de domaine qu'ils peuvent s'appliquer. Mais c'est juste une
prolongation du paradigme d'événement-condition-action.
LY_SII __ 12 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Taxonomies des agents

Tandis que les agents intelligents sont toujours quelque peu nouveaux dans les
environnements de calcul commerciaux, ils ont été le centre des chercheurs pendant des
années. Dans ce temps, on a proposé beaucoup de différentes manières de classifier ou de
classer des agents par catégorie. L'one-way est de placer l'agent dans le cadre de l'intelligence,
de l'agence, et de la mobilité. Une autre approche est de se concentrer sur la stratégie de
traitement primaire de l'agent. Un tiers est de classer l'agent par la fonction qu'il remplit. Dans
les sections suivantes nous explorons chacune des trois perspectives sur des possibilités
d'agent de visionnement.

Agence, intelligence, et mobilité

Quand nous parlons des agents de logiciel, il y a trois dimensions ou haches que nous
employons pour mesurer les possibilités : agence, intelligence, et mobilité (IBM 1996).
L'agence traite le degré d'autonomie que l'agent de logiciel a en représentant l'utilisateur à
d'autres agents, d'applications, et de systèmes informatiques. Un agent représente l'utilisateur,
aide l'utilisateur, guide l'utilisateur, et dans certains cas, prend des mesures unilatérales au
nom de l'utilisateur. Cette progression d'aide simple au véritable aide nous prend des agents
qui peuvent être hardcoded, à ceux, qui hors de la nécessité simple, doivent contenir des
techniques plus avancées d'intelligence.

L'intelligence se rapporte à la capacité de l'agent de capturer et appliquer la connaissance


domain-specific d'application et le traitement pour résoudre des problèmes. Ainsi nos agents
peuvent être relativement sourds-muets, using la logique codée simple, ou ils peuvent être
relativement sophistiqués, suivre des méthodes AI-basées complexes telles qu'inferencing et
apprendre.

Un agent est mobile s'il peut se déplacer entre les systèmes dans un réseau. La mobilité
présente la complexité additionnelle à un agent intelligent, parce qu'elle soulève des
inquiétudes concernant la sécurité (l'agent et le système de cible) et le coût. Les Intranets sont
un environnement particulièrement mûr pour que les agents intelligents mobiles errent parce
qu'ils exigent moins de sécurité que dans l'Internet grand -ouvert.

Stratégies de traitement

Un des types les plus simples d'agents sont des agents réactifs ou réflexes, qui répondent en
mode d'événement-condition-action. Les agents réflexes n'ont pas les modèles internes du
monde. Ils répondent seulement aux stimulus externes et aux informations disponibles de leur
détection de l'environnement (ruisseaux 1986). Comme les réseaux neurologiques, les agents
réactifs montrent le comportement émergent, qui est le résultat des interactions de ces
différents agents simples. Quand les agents réactifs agissent l'un sur l'autre, ils partagent des
données de bas niveau, la connaissance symbolique non à niveau élevé. Un des principes
fondamentaux des agents réactifs est qu'ils sont fondus dans des données physiques de sonde
et ne fonctionnent pas dans l'espace artificiel de symbole. Des applications de ces agents ont
été limitées aux robots qui utilisent des sondes pour percevoir le monde.
LY_SII __ 13 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Les agents délibératifs ou goal-directed ont la connaissance de domaine et les possibilités de


planification nécessaires pour prendre un ordre des actions dans l'espoir d'atteindre ou
d'atteindre un but spécifique. Les agents délibératifs peuvent proactivement coopérer avec
d'autres agents à réaliser une tâche. Ils en peuvent employer et toutes les techniques
symboliques de raisonnement d'intelligence artificielle qui ont été développés au cours des
quarante dernières années.

Les agents de collaboration fonctionnent ensemble pour résoudre des problèmes. La


communication entre les agents est un élément important, et tandis que chaque agent
individuel est autonome, c'est la synergie résultant de leur coopération qui rend les agents de
collaboration intéressants et utiles. Les agents de collaboration peuvent résoudre les grands
problèmes qui sont au delà de la portée de n'importe quel agent simple et ils permettent une
approche modulaire basée sur la spécialisation des fonctions d'agent ou de la connaissance de
domaine. Par exemple, les agents de collaboration peuvent fonctionner comme aides de
conception sur la grande, complexe technologie projette. Différents agents peuvent être
invités pour vérifier différents aspects de la conception, mais leur expertise commune est
appliquée pour assurer que la conception globale est conformée. Dans un système de
collaboration d'agent, les agents doivent pouvoir échanger des informations sur la croyance,
les désirs, et les intentions, et partagent probablement même leur connaissance.

Comme cité précédemment, un agent mobile est un processus de logiciel (le code et son état
d'un programme courant) qui peut voyager à travers les systèmes informatiques dans un
réseau effectuant le travail pour son propriétaire. Un avantage des agents mobiles est que les
communications entre le système à la maison et les systèmes à distance sont réduites. En
permettant à l'agent d'aller au système à distance et aux données d'accès localement sur ce
système, nous permettons une nouvelle classe entière des applications. Par exemple, les
agents mobiles ont pu fournir une manière simple de faire l'équilibrage de la charge dans les
systèmes distribués. « Oh, le processeur est fortement chargé ici, un meilleur houblon dessus
plus d'au système X pendant un moment. »

Comme Nwana (1996) dit, la « mobilité n'est ni un état nécessaire ni suffisant pour
l'agenthood. » Parfois elle semble raisonnable de faire sortir à votre agent sur le réseau ;
d'autres fois elle ne fait pas. Pour certains, l'idée d'envoyer un agent mobile au travail est
éteinte confortable et normale. Pour d'autres, le manque d'un modèle de calcul familier (il ni
serveur-n'est basé ni client/serveur) rend les agents mobiles durs pour sonder. Par exemple, si
votre agent contient la quelques connaissance ou algorithmes exclusifs de domaine, puis
envoyant que la propriété intellectuelle dehors sur le réseau à résider sur les centres serveurs
étrangers, peut ne pas être une bonne idée. Améliorer pour maintenir cet agent à la maison
dans un système sûr et bloqué, et envoyer les agents de collaboration de messager pour faire
le déplacement. Peut-être le plus grand inhibiteur à l'utilisation répandue des agents mobiles
est sécurité. Nous avons un nom pour le logiciel qui vient unbidden sur nos systèmes et
commence à s'exécuter. Nous les appelons des virus. Comment nous assurons-nous que
seulement les « bons » agents mobiles peuvent fonctionner sur notre système, mais pas les
« mauvais » agents ? Et comment pouvons-nous faire la différence ?

Fonctions de traitement

Peut-être la façon de penser la plus normale au sujet des différents types d'agents est basée sur
la fonction qu'elles remplissent. Ainsi, nous avons des agents d'interface utilisateurs, qui
LY_SII __ 14 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

essayent « font ce que voulez dire vous que » plutôt que ce que vous dites en agissant l'un sur
l'autre avec un morceau d'application ou de logiciel système. Nous avons des agents de
recherche, qui sortent sur l'Internet et trouvent des documents pour nous. Nous avons des
agents de filtre, qui traitent le courrier entrant ou les articles de discussion, ferreting dehors la
substance d'intérêt des déchets plus mondains ou plus inintéressants. Nous avons des aides
domain-specific, qui peuvent réserver un voyage d'affaires, prévoir une réunion, ou vérifier
que notre conception ne viole aucune contrainte. En bref, n'importe quelle combinaison des
attributs d'agent intelligent peut être combinée et appliquée à un domaine spécifique pour
créer un nouveau, fonction-spécifique agent intelligent. Tous que nous devons sont les outils
logiciels et l'infrastructure de calcul pouvoir ajouter les possibilités de la connaissance et de
raisonnement ou d'étude de domaine à nos agents, et le niveau de confort qui est exigé de faire
confiance à l'agent pour faire notre offre.

Les agents d'interface fonctionnent comme les aides personnels pour aider un utilisateur à
accomplir des tâches. Les agents d'interface utilisent habituellement l'étude pour s'adapter aux
habitudes de travail et aux préférences de l'utilisateur. Patti Maes (1994) au MIT identifie
quatre manières que l'étude peut se produire. D'abord, un agent peut apprendre par
l'observation au-dessus de l'épaule de l'utilisateur, observant ce que l'utilisateur fait et imitant
l'utilisateur. En second lieu, l'agent peut offrir le conseil ou agir au nom de l'utilisateur et puis
apprendre en recevant la rétroaction ou le renfort de l'utilisateur. Troisièmement, l'agent peut
obtenir des instructions explicites de l'utilisateur (quand ceci se produit, puis font cela). En
conclusion, en demandant d'autres agents le conseil, et l'agent peut apprendre de leurs
expériences. Noter que les agents d'interface collaborent principalement avec l'utilisateur, pas
avec d'autres agents (demander le conseil est l'une exception). Using de diverses mécanismes
d'apprentissage, connecter l'offre d'agents la promesse d'adapter l'interface utilisateurs d'un
système informatique ou l'ensemble de demandes d'utilisateur particulier et de leur travail
unique dénomment. Si les agents d'interface peuvent collaborer et partager leur connaissance
au sujet de la façon faire une tâche, alors quand une personne dans une équipe de travail
figure dehors comment faire quelque chose, que la compétence pourrait être transmissible à
tous autres utilisateurs dans cette équipe de travail par leurs agents d'interface. Les gains de
productivité ont pu être énormes.

Une autre classe générique des agents est des agents de l'information. Par certains côtés, les
agents de l'information sont le Dr. Jekyll/M. Hyde des logiciels. Quelques agents de
l'information sortent sur l'Internet ou le Web et cherchent l'information d'intérêt à l'utilisateur.
D'autres filtrent des jets d'information venant dans des signalisations de correspondance et de
newsgroup d'email. Mais l'une ou l'autre manière, agents de l'information essayent d'aider
avec le problème de noyau d'obtenir la bonne information au bon moment. La question n'est
pas s'il y a trop d'information ou trop peu, mais veillant que vous voyez la bonne information.
Naturellement, ce qui est « exact » dépend du contexte dans lequel vous travaillez. Ce
problème de surcharge de l'information est l'un des facteurs principaux dans l'apparition des
agents intelligents de logiciel en tant que produits commercial viables. Si conduisant en
particulier les messages électroniques importants, ou activement construisant un journal
personnel (consistant seulement en articles et annonces intéressants), le soulagement de
promesse d'agents de l'information de la quantité primordialement de données nous sont
exposés à chaque jour. Des agents de l'information appelés Spiders déjà sont employés pour
indexer le Web. Généralement les agents de l'information peuvent être statiques, et se
reposent sur un système using des moteurs de recherche et des index de Web pour recueillir
des informations pour l'utilisateur, ou ils peuvent être mobiles et sortir et activement
LY_SII __ 15 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

rechercher l'information. L'une ou l'autre manière, leur fonction demeure même-à fournissent
l'information utile à l'utilisateur.

Dans les sections précédentes, nous avons décrit trois des taxonomies principales pour
classifier des agents, mais il y a d'autres. Certains estiment que la collaboration avec d'autres
agents est une distinction importante. D'autres estiment que, que l'agent agisse l'un sur l'autre
directement avec un utilisateur humain ou pas est important. Encore d'autres jugent que cela la
capacité d'étude devrait servir de base primaire à classifier des agents. Si l'agent fonctionne à
travers un réseau ou seulement localement sur un PC ou un poste de travail est un autre
attribut de distinction. Puisque les agents intelligents existent dans un espace
multidimensionnel, la caractérisation des agents par deux ou de trois de ces dimensions est
quelque peu risquée (Nwana 1996). Cependant, nous estimons que la perte de précision est
plus qu'avons compensé par la clarté des deux ou approche tridimensionnelle. Les gens ont du
mal à penser dans les six ou sept-dimensionnels espaces. À notre avis, l'agence et
l'intelligence sont les possibilités fondamentales fondamentales sur lesquelles des agents
devraient être classifiés. La mobilité ou certaine autre caractéristique a pu être employée
comme troisième axe, au besoin.

Pour être sûr, il y a des agents de logiciel qui sont autonomes, mais de non intelligent. Ces
agents agissent souvent en tant que moniteurs d'exécution de machine simple dans des
applications de gestion de système distribué. Les agents de Simple Network Management
Protocol sont un exemple de cette sorte. D'une part, il y a des programmes ou des applications
qui emploient les techniques d'intelligence artificielle telles que l'étude et des motifs, mais qui
avoir relativement peu d'autonomie. Les systèmes experts classiques sont un exemple. Ils ne
sont pas les agents intelligents dans le sens utilisé dans ce livre. Nous sommes intéressés par
les programmes à l'intersection de deux domaines, intelligences et agences. Toutes autres
caractéristiques sont secondaires et des mai ou mai pour ne pas être présentes pour que nous
emploient l'agent intelligent de limite.

Les agents intelligents sont des logiciels, rien plus et rien moins. Parfois ceci a un impact
négatif quand quelqu'un nouveau à la matière vient à la réalisation qu'il n'y a aucune magie
ici, juste programmant. Cependant, les agents intelligents, du moins car nous les définissons et
nous référons, sont des logiciels avec une attitude. Ils existent pour aider des utilisateurs à
obtenir leur travail effectué. Vous pouvez dire qu'est ce ce que le logiciel d'application est
censé pour faire. C'est vrai. Cependant, beaucoup d'applications assument aujourd'hui un
niveau de connaissance et de sophistication dans les utilisateurs que beaucoup d'utilisateurs
sont incapables ou peu disposés à réaliser. Les gens veulent juste obtenir leur travail réalisé.
Les la plupart ne s'inquiètent pas si la police est TrueType ou Adobe, si le modèle composant
est ActiveX ou JavaBeans, ou si le code est client/serveur ou basé sur le WEB. Vers cette
extrémité, le logiciel d'agent intelligent est logiciel pratique. Il obtient juste le travail réalisé.
Si le logiciel d'agent intelligent présente un autre niveau de complexité lequel l'utilisateur doit
traiter, alors ce sera un échec. Les agents intelligents doivent être permettants et
automatisants, non frustrant ou intrusif.

Résumé

En ce chapitre, nous avons présenté une introduction à l'intelligence artificielle et aux agents
intelligents. Les points principaux incluent :
LY_SII __ 16 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

• Le champ de l'intelligence artificielle est approximativement 40 années. Pendant ce


temps, l'AI a évolué de l'essai de construire les solutionneurs de problèmes généraux
employant la recherche, aux systèmes experts de bâtiment avec profondément mais à
la connaissance de domaine d'étroit, à combiner la connaissance, le raisonnement, et
l'étude pour résoudre des problèmes réels difficiles.
• Un agent de logiciel montre l'intelligence s'il agit rationnellement. Il emploie la
connaissance, l'information, et le raisonnement pour prendre des mesures raisonnables
à la poursuite d'un but ou des buts.
• La plupart des techniques d'intelligence artificielle sont basées sur le traitement de
symbole. Un ensemble de marques ou de symboles est employé pour représenter la
connaissance et l'état du monde, et les algorithmes manoeuvrent ces symboles pour
imiter des fonctions de traitement cognitif à niveau élevé.
• Les réseaux neurologiques sont une autre école d'intelligence artificielle, basée sur
un brainlike, le modèle adaptatif d'informatique parallèle qui n'emploie pas des
symboles explicites. Les réseaux neurologiques apprennent ou s'adaptent une fois
exposés aux données et correspondent aux fonctions cognitives telles que le traitement
et la reconnaissance des structures sensoriels de bas niveau.
• L'Internet, les intranets, et le Web ont causé une explosion dans la quantité des
informations disponibles aux gens. Les navigateurs faciles à utiliser de Web ont
également attiré beaucoup de nouveaux utilisateurs d'ordinateur au monde en ligne.
Ces facteurs ont contribué pour créer une occasion énorme pour les agents intelligents,
le logiciel qui aide des utilisateurs à faire des tâches de calcul complexes.
• Les agents intelligents doivent pouvoir identifier des événements, déterminent la
signification de ces événements, et puis prennent des mesures au nom d'un utilisateur.
• Des agents peuvent être classés par catégorie en les plaçant dans un espace
tridimensionnel, où les haches sont agence, la quantité d'autonomie qu'un agent a,
l'intelligence, la connaissance, le raisonnement, et les possibilités d'étude de l'agent, et
de la mobilité, pouvant se déplacer entre les systèmes dans un réseau.
• Les agents réactifs sont des agents relativement simples qui sentent leur monde, et
répondent réfléchi aux stimulus externes. Les agents délibératifs sont plus complexes,
using la connaissance, raisonnement, et même apprenant à prévoir et atteindre leurs
buts. Les agents de collaboration fonctionnent ensemble en équipe, combinant la leur
connaissance et qualifications spécialisées pour résoudre de grands problèmes.
• Des agents intelligents peuvent également être classifiés par les fonctions qu'ils
remplissent. Les agents d'interface fonctionnent comme les aides personnels pour
aider l'utilisateur à accomplir des tâches. Les agents de l'information fonctionnent pour
empêcher la surcharge de l'information. Ils peuvent activement découvrir ont désiré
l'information sur le Web, ou filtrent dehors des données inintéressantes ou non
désirées pendant qu'il arrive. Les agents Domain-specific peuvent faire des achats en
ligne, prendre des arrangements de voyage, suggérer un bon livre ou CD, ou aider à
prévoir une réunion.
• En fin de compte, le succès ou l'échec des agents intelligents dépendra de combien
de valeur ils fournissent à leurs utilisateurs.
LY_SII __ 17 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Chapitre 1

Le langage de programmation de Java


Le chapitre 1 présente le langage de programmation de Java développé par Sun
Microsystems et les fonctions fournies par le kit 1.1 de développement de Java.
Ce chapitre fournit une vue d'ensemble rapide de la langue, censée pour obtenir
les programmeurs orientés objectivement expérimentés jusqu'à la vitesse
rapidement sur les aspects uniques de la langue de Java. Nous brièvement
comparons et contrastons Java contre C++ et causerie comme langage de
programmation d'usage universel et orientée objectivement et discutons
comment Java répond aux exigences pour les agents intelligents, y compris
l'autonomie, l'intelligence, et la mobilité.
La documentation complète des caractéristiques de langue de Java et des
interfaces de programmation API pour commandes Tempus-link est disponible
sur le Web chez http://www.javasoft.com. Il y a beaucoup d'excellents livres sur
programmer dans Java. Java en un mot (Flanagan 1996) et noyau Java (Cornell
et Horstmann 1996) sont deux que nous avons trouvés particulièrement utiles.

Vue d'ensemble de la langue de Java

Java est un langage de programmation orientée objectivement développé par Sun


Microsystems. Il a été à l'origine conçu pour le logiciel inclus en temps réel de programmation
pour l'électronique grand public, en particulier boîtes de placer-dessus à l'interface entre les
fournisseurs de câbles, radiodiffuseurs, et télévisions ou appareils de televisionlike.
Cependant, l'effort a été réorienté à l'Internet quand le marché pour des boîtes de placer-
dessus ne s'est pas développé assez rapidement, alors que l'Internet éclatait dans la popularité.

À l'origine les réalisateurs de Java ont prévu pour employer C++ pour leur développement de
logiciel. Mais ils ont eu besoin d'une langue qui pourrait s'exécuter sur différents ensembles
de puces pour adapter au marché toujours changeant d'électronique grand public. Ainsi ils ont
décidé de concevoir leur propre langue qui serait indépendant du matériel fondamental.

C'est cet aspect « architecture-neutre » de Java qui lui fait l'idéal pour programmer sur
l'Internet. Il permet à un utilisateur de recevoir le logiciel d'un système à distance et de
l'exécuter sur un système local, indépendamment du matériel fondamental ou du système
d'exploitation. Un interprète et un temps d'exécution s'appellent la machine virtuelle de Java
qui isole le logiciel du matériel fondamental.

À la différence des langues plus traditionnelles, le code source de Java n'obtient pas traduit en
instructions de machine pour une plate-forme particulière d'ordinateur. Au lieu de cela, le
code source de Java (.java) est compilé dans une forme intermédiaire appelée les bytecodes
qui sont stockés dans un dossier de .class. Ces bytecodes peuvent être exécutés sur n'importe
quel système informatique qui met en application une machine virtuelle de Java. Cette
portabilité est peut-être l'un des dispositifs les plus irrésistibles de la langue de Java, d'une
LY_SII __ 18 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

perspective commerciale. Dans l'ère courante du développement d'applications de croix-plate-


forme, n'importe quel outil qui permet à des programmeurs d'écrire le code une fois et de
s'exécuter elle sur beaucoup de plates-formes va obtenir l'attention.

Le portable, nature interprétée de Java effectue son exécution. Tandis que l'exécution du code
interprété de Java est meilleure que des langues scripting et assez rapide pour des applications
interactives, elle est plus lente que les langues traditionnelles dont le code source est compilé
directement dans le code machine pour une machine particulière. Pour améliorer l'exécution,
des compilateurs juste à temps (JITs) ont été développés. Un compilateur de JIT fonctionne en
même temps que la machine virtuelle de Java et détermine quels morceaux de code de Java
s'appellent le plus souvent. Ceux-ci sont compilés en marche dans des instructions de machine
de sorte qu'ils n'aient pas besoin d'être interprété chaque fois eux soient produits dans le cadre
d'un programme. Des compilateurs statiques également sont développés pour compiler le code
source de Java dans le code machine qui peut être exécuté sans interprétation. Il est important
de noter que, à la différence des bytecodes, le code machine produit n'est pas portable et ne
s'exécutera pas sur d'autres plates-formes.

La portabilité de bytecode est ce qui permet à Java d'être transporté à travers un réseau et
d'être exécuté sur n'importe quel système informatique de cible. Les Java applets Sont de
petits programmes de Java conçus pour être inclus dans un document de Web de HTML
(langage de balisage hypertexte). Les étiquettes de HTML spécifient le nom du Java applet Et
de son localisateur de ressources uniformes. L'URL est l'endroit sur l'Internet auquel les
bytecodes d'applet résident. Quand un web browser Java-permis affiche un document de
HTML contenant une étiquette d'applet, les bytecodes de Java sont téléchargés de l'endroit
spécifique et la machine virtuelle de Java interprète ou exécute les bytecodes. Les Java applets
Sont ce qui permettent à des pages Web de contenir les graphiques animated et le contenu
interactif.

Puisque des Java applets Peuvent être téléchargés de n'importe quel système, les mécanismes
de sécurité existent dans la machine virtuelle de Java pour se protéger contre les applet
malveillants ou errants. Le système d'exécution de Java vérifie que les bytecodes pendant
qu'ils sont téléchargés du réseau pour s'assurer ils sont les bytecodes valides et que le code ne
viole pas des restrictions inhérentes l'unes des imposées aux applet. Les Java applets Sont
restreints de la communication avec n'importe quel serveur autre que le centre serveur de
commencement, celui dont ils ont été téléchargés. Ils ne peuvent pas lancer un programme
exécutable local ou accéder aux dossiers locaux. Les restrictions sont in place pour empêcher
un Java applet D'accéder au du système d'exploitation fondamental ou des données sur le
système. Ces restrictions peuvent être soulagées, cependant, par l'utilisation des signatures
digitales et des réalisations alternatives de SecurityManager.

Mais Java peut être employé pour plus que les applet de programmation pour courir chez un
navigateur. Java est un langage de programmation de plein exercice qui peut être employé
pour écrire des applications autonomes. Ces applications ne sont pas placées sous les mêmes
restrictions de sécurité que les applet et peuvent donc accéder à des données et à la fonction
du système d'exploitation fondamentale.

Java est un langage de programmation orientée objectivement, empruntant fortement à la


causerie, au C objectif, et au C++. Il est caractérisé par beaucoup comme meilleur, plus sûr
C++. Java emploie la syntaxe de C++ et est aisément accessible en grande Communauté pour
LY_SII __ 19 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

le Développement existante de C++. Java, cependant, ne traîne pas le long du legs du C. Il ne


permet pas des variables globales, des fonctions, ou des procédures. Excepté quelques types
de données primitifs aimer les nombres entiers ou les nombres à point mobile, tout dans Java
est un objet. Les références d'objet ne sont pas des indicateurs, et la manipulation d'indicateur
n'est pas permise. Ceci contribue à la robustesse générale des programmes de Java puisque les
opérations d'indicateur tendent à être particulièrement méchantes et bogue-enclines. Java
contrôle également la mémoire elle-même, évitant de ce fait des problèmes avec l'attribution
et la désaffectation des objets. Il ne permet pas la transmission multiple comme C++ fait, mais
soutient un autre type de réutilisation par l'utilisation des définitions d'interface formelles.

Java est assez semblable à C et à C++ qu'il se sent déjà bien connu à la majeure partie de la
communauté de programmation existante. Mais il est assez différent des manières importantes
(gestion de la mémoire principale et portabilité de croix-plate-forme) ces il le vaut pour que
les programmeurs commutent à une nouvelle langue.

La prochaine section examine les dispositifs de langue de Java en plus détail.

Dispositifs de langue de Java

Cette section n'inclut pas les pleines spécifications de langue pour Java mais est prévue pour
fournir assez d'informations pour que le C.A. ou le programmeur de C++ obtienne une
sensation pour Java. Pour une description complète de la langue, voir le site Web de JavaSoft
chez http://www.javasoft.com/.

Types de données

Bien que Java soit un langage de type objet, les types de données primitifs ne sont pas des
objets. Des objets équivalents sont fournis en tant qu'élément de la langue, mais les primitifs
permettent une exécution plus rapide en éliminant l'associé aérien aux objets. Le tableau 1.1
décrit les types de données primitifs (de non-Objet) disponibles dans Java.

Les déclarations variables sont semblables à ceux dans C ou C++. Java est un langage opérant
sur des objets de types déterminés fortement, ainsi chaque variable doit avoir un type. Le type
peut être un type de données primitif, ou un type de données de référence. Les données
primitives dactylographient dedans Java sont passées par valeur. Ceci signifie que la valeur
réelle est stockée dans une variable, et la valeur elle-même est passée aux appels de méthode.
Les objets, d'une part, sont des types de données de référence. Ceci signifie que la variable
contient une référence à l'adresse à laquelle l'objet est stocké, et la référence est passée aux
appels de méthodes. Des variables dans Java peuvent être initialisées une fois avouées et
peuvent être déclarées à un point quelconque dans le code. Voici quelques exemples des
déclarations variables dans Java :

boolean flag;
char answer = ‘N’;
int[] arrayOfIntegers = new int[10];
String myName;

Comme vu dans l'exemple ci-dessus, les rangées sont les objets de première classe dans Java,
et, comme d'autres objets, doivent être explicitement créées using le nouvel opérateur.
LY_SII __ 20 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Structures de gestion

Plusieurs de structures de gestion du `s de Java sont identiques à C et à C++. Celles-ci


incluent l'embranchement conditionnel d'if/else, alors que des boucles et font des boucles.
Java est plus restrictif que C parce que l'expression conditionnelle doit être un type de
données booléen de Java. Une valeur booléenne ne peut pas être moulée à d'autres types, ainsi
vous devez employer les opérateurs ou les méthodes de comparaison qui renvoient des valeurs
booléennes. L'extrait de code suivant illustre ce point :

int i = 0;
while ( i < 10) {
if ( i == 0) {
... // first time
} else {
int j = 0 ;
do {
... // all other times
j++ ;
} while ( j < 10) ;
i++ ;
}

Le rapport de commutateur de Java emploie également la syntaxe standard de C, jusqu'à


l'appui pour l'étiquette de défaut. Pour la syntaxe de boucle a été légèrement modifié dans
Java. Comme C++, vous pouvez définir des variables de boucle dans la section d'initialisation,
mais à la différence de C++, elles scoped à l'intérieur de la boucle seulement. Java permet des
rapports multiples dans les sections d'initialisation et d'incrément, mais ils doivent être
délimités par des virgules.

for (int i=0, long sum=0 ; i < 10 ; i++) {


sum += i ;
}

// note i and sum are out of scope

Java soutient la coupure et continue des rapports pour l'éclatement de joindre des boucles et
des rapports de commutateur. Mais Java ajoute une torsion à ces fonctions. Vous pouvez
ajouter une étiquette à une boucle en ajoutant l'étiquette et des deux points avant les
spécifications de boucle. Ceci te permet de spécifier hors de quelle boucle enfermante vous
voulez se casser ou continuer.

Objets, classes, et méthodes

Java est un langage de programmation orientée objectivement. Ainsi, la manière que vous
obtenez des choses faites est en définissant des classes, des objets de instanciation, et appeler
des méthodes sur ces objets. Une classe est une collecte des données (appelés les membres) et
méthodes (des fonctions ou des procédures) qui manoeuvrent ces données. Un objet est un
exemple d'une classe et nous employons le terme l'un pour l'autre. Dans Java, il n'y a aucune
variable globale ou fonction ; tout doit être défini en tant qu'élément d'une classe. Au-dessous
de nous définissons une classe de Java simple appelée Agent :

public class Agent {


LY_SII __ 21 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

String name ;
Agent contact ;
public String getName() { return name ;}
public Agent getContact() { return contact; }
Agent(String aName) { name = aName; ) ;
}

Dans cet exemple, le nom et le contact sont des membres et le getName () et le getContact ()
sont des méthodes. L'agent (aName de corde) est un constructeur qui est habitué pour créer
un exemple d'agent avec le membre nommé initialisé à la valeur de l'aName. Un objet peut
être créé dans Java suivre le nouveau mot-clé (plus terrain communal) ou la méthode de
newInstance () sur la classe. Par exemple :

Agent msmart,jbond;
msmart = new Agent(“Maxwell”);
jbond = Agent newInstance();

Ici l'agent est une classe, et le msmart et le jbond sont des variables qui mettent en référence
ou se rapportent à des exemples d'agent. Se rappeler, Java n'a aucun indicateur qui peut être
manoeuvré (et probablement abusé). Les variables tiennent les types de données ou les
références élémentaires d'objet, qui sont toujours valides. Noter qu'avant que la deuxième
ligne soit exécutée, le msmart variable ne se rapporte à aucun objet et a une valeur spéciale
appelée nulle. Une référence nulle causera une exception (erreur) si vous essayez de
l'employer pour se référer à un objet.

Nous pouvons obtenir et placer des membres de données dans l'objet l'un ou l'autre
directement, si les permissions d'objet laissent, ou indirectement par des méthodes, encore si
les permissions laissent. Le public, les privés, et des mots-clés protégés détermine si d'autres
objets peuvent accéder à des méthodes de membre ou d'appel de données sur un objet. Par
exemple, nous pourrions obtenir le nom du jbond en codant jbond.name pour accéder au nom
directement, ou en employant l'appel jbond.getName de méthode ().

Dans notre classe d'agent, le nom et le contact s'appellent les variables d'exemple. Chaque
exemple d'un agent aura son propre nom et contacte des membres de données. Nous pouvons
créer les variables de classe, qui s'appliquent à tous les exemples d'agent en employant le
mot-clé statique.

Java ne soutient pas la transmission multiple, où une classe peut être dérivée de deux parents
ou plus ou classes basses. Au lieu de cela, il présente la notion d'une interface. Une interface
est un ensemble de méthodes qui définissent un comportement spécifique qui est soutenu par
un exemple de la classe. Par certains côtés, la construction d'interface est plus puissante que la
transmission multiple, parce qu'elle permet à n'importe quelle classe d'imiter le comportement
d'autres classes sans souci de leurs membres de données ou stratégies d'exécution
fondamentaux. La causerie permet seulement la transmission simple, mais n'a rien comme la
construction d'interface de Java. C++ permet la transmission multiple, mais la complexité
rend using elle sujet aux erreurs. L'interface de Java semble comme un compromis
raisonnable entre ces deux approches.
LY_SII __ 22 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Applet et applications

Le code de Java peut être couru comme seules applications de stand ou comme applet, qui
sont courus dans un navigateur en tant qu'élément d'une page Web. Commençons par des
applications parce qu'ils sont les plus familiers. N'importe quelle classe de Java peut être
transformée en application en fournissant () à une méthode principale la signature suivante :

public class AgentApp {

public static void main(String[] args) {


// this is an application
}
...
}

Une application de Java peut être appelée de la ligne de commande en spécifiant le mot-clé
Java suivi du nom de la classe : Java AgentApp. Se rappeler que Java est une langue
interprétée, ainsi nous ont besoin d'un autre programme pour lire et interpréter les bytecodes
dans l'AgentApp.class classent. C'est le temps d'exécution de Java, dans le dossier exécutable
Java. Tous les paramètres ont passé dedans sur la ligne de commande peuvent être accédés
par la rangée de corde d'args, tout comme le paramètre d'argv dans le C.A. ou la force de C++
().

Afin de fonctionner comme applet, une classe doit être une sous-classe de la classe d'applet.
Un applet est conçu pour fonctionner sous la commande d'un autre programme,
habituellement un web browser, et se sert de la fenêtre de navigateur pour se montrer et pour
agir l'un sur l'autre avec l'utilisateur. La méthode d'init () est employée aux paramètres de
processus et fait d'autres initialisations ci-devant pour l'applet. Le début () et des méthodes
d'arrêt () s'appellent toutes les fois que l'utilisateur évoque ou laisse la page Web. Un applet
témoin est montré ci-dessous :

public class AgentApplet extends Applet {

String name = “Double ought seven” ;


public void Paint(Graphics g) {
g.drawString(“Hello, my name is ” + name + “!”, 100,100) ;
}
public void init() {
// for an applet, init is like the main()
// it is the first applet method called
}
public void start() {
// called after init, and when user returns to Web page
}
public void stop() {
// called when user moves off the Web page
}
}

Puisque le web browser appelle réellement l'applet, nous avons besoin d'un dossier de HTML
qui met en référence l'applet et spécifie ses paramètres, comme :
LY_SII __ 23 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

<APPLET code=“AgentApplet.class” width=300 height=200>


<PARAM name=“contact” value=“chief”>

</APPLET>Noter qu'avec le nom du dossier de l'applet .class, vous spécifiez la taille et la


largeur de la zone d'exposition que l'applet exigera sur la page Web courante. Vous pouvez
également spécifier des paramètres additionnels dans le HTML using l'étiquette de
<PARAM>, que l'applet peut rechercher par l'intermédiaire de la méthode de getParameter ().
L'applet peut rechercher cette valeur en appelant :

String param = this.getParameter(“contact”);

Le temps d'exécution de Java fournit une visionneuse d'applet qui peut être employée pour
courir des applet en tant qu'applications autonomes. La visionneuse d'applet joue le rôle du
navigateur dans ce cas-ci. Les applet devraient également appliquer le getAppletInfo () et des
méthodes de getParameterInfo () pour fournir des informations sur l'applet et ses paramètres
aux utilisateurs.

Les applet sont l'une des raisons principales que les gens se réfèrent à Java comme langage de
programmation d'Internet. Puisqu'ils ont été employés la première fois pour épicer vers le haut
les pages Web mates avec le texte et les graphiques animated, les Java applets Sont le
dispositif le plus bien connu de la langue de Java. Des applet maintenant sont employés pour
créer les panneaux dynamiques de saisie de données pour des applications navigateur-basées
d'Internet. Ils ont également émergé comme technologie principale derrière le développement
des ordinateurs de réseau en tant que remplacements possibles pour les terminaux non-
intelligents.

Interface d'indigène de Java

L'interface indigène de Java fournit une technique standard pour que Java exige à d'autres
environnements de langue et pour que la machine virtuelle de Java soit appelée des
programmes écrits en d'autres langues. Un d'usage courant de JNI est de fournir des méthodes
indigènes ou des « emballages » de Java pour des api existants de C ou de C++. Ceci permet à
des programmeurs de Java de traiter ces api en tant que prolongements simples à
l'environnement de Java. Mais ce qui se produit réellement sous les couvertures est que la
commande de transferts de JVM dans l'autre environnement de langue (une bibliothèque
partagée de DLL ou d'UNIX) et des passages un indicateur à l'environnement courant de Java.

Quand un programme de Java appelle une méthode indigène, les paramètres (l'un ou l'autre
types de données primitifs tels que l'international, long, ou le flotteur, ou les références aux
objets de Java) sont passées dans la fonction de C ou de C++ api. La méthode indigène peut
alors employer l'indicateur d'environnement de Java et un ensemble de fonctions de JNI pour
appeler des méthodes sur ces objets de Java. Ainsi, un appel de méthode de Java est
transformé en ensemble de paramètres et la fonction fondamentale de C ou de C++ api
s'appelle. Les résultats d'api sont alors retournés à l'environnement de Java, directement ou
par des méthodes appelées pour placer des membres de données sur des objets de Java. Le JNI
fournit également une interface de sorte que le C.A. ou le programme de C++ puisse
commencer vers le haut le JVM et le faire lancer charger et un programme de Java.
LY_SII __ 24 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

D'autres dispositifs de langue de Java

Un dispositif intéressant de Java est qu'il emploie le jeu de caractères de 16 bits d'Unicode,
plutôt que le codage sur 8 bits d'ASCII. Unicode définit plus de 34.000 caractères codés
couvrant les langues écrites principales de partout dans le monde. Ces possibilités intégrées de
soutien de langue nationale, combinées avec une partie du lieu () ou de traitement sensible
d'endroit présenté dans Java 1.1, signifient que des Java applets Pourraient être écrits pour une
assistance mondiale.

Puisque le temps d'exécution de Java soutient les fils multiples de la commande, les
spécifications de langue incluent un rapport synchronisé qui peut être employé pour marquer
les sections critiques du code pour empêcher la corruption des objets. Le filetage est un
dispositif important pour des applications de calcul de réseau, parce qu'il permet à des
programmes de serveur d'entretenir les clients multiples en tournant de nouveaux fils pour
chaque demande. Cette approche donne une exécution bien meilleure que commençant un
nouveau processus (comme des programmes de CGI-BIN) quand une demande entre.

Paquets

Des ensembles de classes relatives sont groupés ensemble dans des paquets de Java. De la
même manière des cartes d'un nom de classe de Java aux dossiers identiquement appelés de la
source (.java) et de la classe (.class), paquets de Java sont tracées aux annuaires identiquement
appelés. Tous les dossiers de .class qui font partie d'un paquet doivent résider dans le même
annuaire. Un paquet tel que java.awt.image trace à une structure d'annuaire de
Java/d'awt/d'image.

Java soutient grouper des collections de paquets et de classes de Java dans des dossiers de
fermeture éclair dans le format non comprimé. Un nouveau format de empaquetage appelé
JAR (archives de Java) peut également être employé pour grouper des collections de paquets
de Java dans un dossier simple de sorte qu'un ensemble complet de classes de Java puisse être
téléchargé d'un web server dans une transaction simple.

java.lang

Le paquet de java.lang contient les classes de langue de base de Java. Celles-ci incluent des
classes pour des objets et se classent (l'objet, la classe), quelques types de données de base
(booléens, byte, caractère, double, flotteur, nombre entier, long, maths, nombre, short,
corde, StringBuffer, vide), sécurité (SecurityManager), fils (fil, ThreadGroup), et une
autre classe connexe à la compilation et au temps d'exécution de Java. Noter que ces classes
définissent seulement le comportement très essentiel des classes dans la langue de Java. Des
classes plus complexes et plus puissantes basées sur les classes dans java.lang existent en
d'autres paquets. Le paquet de java.lang inclut également l'interface de Cloneable qui est
employée pour indiquer si un objet peut être copié, et l'interface praticable qui indique qu'une
classe devrait être exécutée par un fil.

Ceci le seul paquet qui est automatiquement importé dans chaque programme de Java. La
classe d'objets est la racine de toutes les hiérarchies de classe. Puisque chaque autre objet de
Java est une sous-classe d'objet, tous les objets peuvent appeler le public et les méthodes
protégées de classe d'objets. Ceux-ci incluent des essais pour l'égalité, la synchronisation de
LY_SII __ 25 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

fil sur des objets, la génération de code de gâchis, et l'identification de la classe à laquelle un
objet appartient. Certaines de ces méthodes devraient être dépassées dans les sous-classes
pour fournir les comportements appropriés. Un objet de classe représente une classe de Java
et peut fournir les informations sur la classe elle-même comprenant le nom de classe, les
superclass de la classe, et les interfaces mises en application par la classe.

La majorité du type de données classes sont assez explicite. Les classes de corde et de
StringBuffer sont d'un certain intérêt parce que les objets de corde dans Java sont
immuables, ainsi il signifie que les cordes ont des valeurs constantes qui ne peuvent pas être
changé une fois elles sont définies. Des objets de StringBuffer sont employés pour des
valeurs de corde modifiables.

La classe de SecurityManager est employée principalement par des navigateurs de Web et


des téléspectateurs d'applet en chargeant le code hors sécurité. Un objet de SecurityManager
met en application une politique de sécurité donnée. Avant qu'une opération soit appelée, un
objet de SecurityManager peut vérifier pour voir si on permet au le visiteur d'effectuer
l'opération. La politique de sécurité d'applet de défaut suppose que le visiteur n'a pas la
permission d'effectuer tout sauf les opérations les plus bénignes.

java.lang.reflect

Le paquet de java.lang.reflect inclut des classes pour soutenir l'introspection au sujet des
classes et des objets fonctionnant actuellement dans une machine virtuelle de Java. Ceux-ci
incluent des classes pour créer et accéder à des rangées (rangée) et pour fournir des
informations et pour accéder aux classes, aux constructeurs, aux champs, aux méthodes, et
aux modificateurs (classe, constructeur, champ, méthode, modificateur). Noter que les
méthodes de réflexion de soutiens de classe mais est réellement une partie de java.lang pour
la compatibilité ascendante avec des versions antérieures de Java.

java.io

Le paquet de java.io contient un grand nombre de classes, plus dont sont descendus
d'InputStream ou d'OutputStream. Les classes de jet définissent des méthodes pour des
données de lecture et d'écriture d'une série de jets. En plus de l'entrée-sortie de jet, java.io
inclut des classes pour l'entrée-sortie de dossier et le filtrage de nom de fichier (dossier,
FilenameFilter, RandomAccessFile). Le paquet de java.io inclut également un certain
nombre d'interfaces. Deux des interfaces plus intéressantes sont Serializable et
Externalizable. L'interface de Serializable permet à un objet d'écrire son état à un jet et de le
relire encore. C'est utile pour passer des objets car des paramètres dans une architecture
distribuée (comme le RMI) et pour rendre un état d'objet persistant. L'interface
d'Externalizable est également employée pour couler des objets, mais l'applicateur a plus de
contrôle du format et du contenu du jet.

java.util

Le paquet de java.util contient plusieurs des structures de données utilisées généralement dans
des programmes d'application. Ceux-ci incluent des classes et des interfaces pour la
LY_SII __ 26 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

manipulation de date et d'heure (calendrier, date, GregorianCalendar, SimpleTimeZone,


fuseau horaire), des structures de données communes (BitSet, dictionnaire, énumération,
table de brouillage, aléatoire, pile, StringTokenizer, vecteur), avis (EventListener,
EventObject, chose observable, observateur), et des propriétés, des lieux, et des paquets de
ressource (ListResourceBundle, lieu, propriétés, PropertyResourceBundle,
ResourceBundle). Tandis que les la plupart de ces derniers sont explicites, quelques uns de
ces derniers méritent un œil plus attentif.

Les classes et les interfaces d'avis peuvent être employées pour mettre en application le
modèle de conception d'observateur (gamma et autres, 1995), le plus familier dont est le
paradigme de modèle/vue. Un objet observable est un dont les changements doivent être
observés par d'autres objets dans une application. Objets qui souhaitent être annoncés quand
les changements observables d'objet doivent mettre en application l'interface d'observateur
et doivent s'enregistrer avec l'objet observable.

La classe de propriétés laisse des paires de clef/valeur à lire de et être écrit à un jet. Des
objets de propriétés peuvent être employés pour rechercher des valeurs personnalisables
basées sur une clef, semblable aux variables d'environnement dans beaucoup de logiciels
d'exploitation. Si une application doit être courue dans différents lieux, des propriétés peuvent
être chargées dans un PropertiesResourceBundle, qui est une sous-classe de
ResourceBundle. La classe de ResourceBundle contiennent les objets lieu-spécifiques qui
peuvent être chargés comme appropriés pour le lieu courant. Ceci permet à une application
d'être écrit l'indépendant du lieu de l'utilisateur. Ces classes sont très utiles en écrivant le code
qui peut être téléchargé du Web et être exécuté n'importe où dans le monde. Les classes de
ResourceBundle et de lieu, avec des classes dans le paquet de java.text, fournissent l'appui
de l'internationalisation de Java.

java.util.zip

Le paquet de java.util.zip fournit l'appui pour la compression et la décompression de dossiers


de FERMETURE ÉCLAIR. Ceci inclut des classes pour comprimer et décomprimer les
données (Deflater, DeflaterOutputStream, GZIPInputStream, GZIPOutputStream,
Inflater, InflaterInputStream, ZipEntry, ZipFile, ZipInputStream, ZipOutputStream)
aussi bien que des classes et des interfaces pour faire des sommes sur ces dossiers (Adler32,
CRC32, CheckedInputStream, CheckedOutputStream, somme).

java.net

Le paquet de java.net fournit un ensemble de classes pour des communications à travers des
réseaux. Ceci inclut des classes pour travailler avec des adresses d'Internet et des localisateurs
de ressources uniformes (INetAddress, URL, URLConnection, URLEncoder,
URLStreamHandler, et URLStreamHandlerFactory) et des douilles (douille,
ServerSocket, DatagramSocket, SocketImpl, et SocketImplFactory) aussi bien que des
données (ContentHandler, ContentHandlerFactory, DatagramPacket). Ces classes
permettent à des programmes de Java de fonctionner à n'importe lequel de trois niveaux
logiques au-dessus des réseaux, à l'URL lu un niveau de HTML page, aux douilles crues de
niveau, et au niveau encore plus bas de datagramme. Ces puissance et flexibilité n'est pas le
foyer réseau-central de Java donné étonnant.
LY_SII __ 27 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

java.awt

La trousse à outils abstraite de fenêtrage est un cadre orienté objectivement pour créer les
interfaces utilisateurs graphiques dans Java. Elle fournit des classes pour les composants de
base de fenêtre, pour des graphiques de schéma, et pour automatiquement arranger et remettre
à la côte des composants. À la différence de C et de C++, qui n'ont pas adressé des fonctions
de GUI, Java fournit ce paquet standard de sorte que les Java applets et les applications
Puissent fonctionner sur les logiciels d'exploitation multiples avec l'appui fondamental très
différent de GUI et aller voir raisonnablement commun et se sentir.

Le paquet de java.awt est tout à fait grand avec plus de 100 classes et interfaces dans les
paquets bas d'awt, de java.awt.event, de java.awt.image, et de java.awt.datatransfer. Le
paquet d'awt soutient la plupart des commandes standard de GUI (fenêtre, panneau,
dialogue, vue, toile, ScrollBar, menu, MenuBar, MenuItem, bouton, Checkbox, choix
(listboxes), étiquette, TextField, et TextArea). L'appui de graphiques inclut (police,
FontMetrics, graphiques, image, point, polygone, et rectangle).

Le modèle d'événement de JDK 1.1 soutient a Causerie-comme le paradigme de modèle-vue-


contrôleur, où les données fondamentales sont stockées dans le modèle, la vue est les
commandes graphiques, et le contrôleur est une classe qui détecte à l'oreille des événements et
maintient le modèle et la vue synchronisés. Ce modèle d'événement de délégation est
également employé comme base pour le modèle d'événement de JavaBeans et est
extrêmement important pour construire Java GUIs et composants de logiciel réutilisables.

java.applet

Le paquet de Java applet Contient une classe simple, l'applet, qui est une sous-classe
d'awt.Panneau, et trois interfaces, l'AppletContext, AppletStub, et AudioClip.
L'AppletContext définit l'environnement que l'applet fonctionne dedans, le document de
HTML il a été chargé de, et d'autres applet dans ce document. L'AppletContext fournit une
manière pour qu'un applet obtienne des références à d'autres applet sur la même page Web, et
d'agit l'un sur l'autre avec eux. L'AppletStub est l'interface entre l'applet et le navigateur ou le
téléspectateur d'applet. Des applet ont été discutés dans une première section sur des applet et
des applications.

java.text

Le soutien d'internationalisation des applet et des applications est fourni par le paquet de
java.text. Ce paquet contient des classes et des interfaces pour traiter le texte d'une manière
lieu-spécifique. Des classes sont données pour des dates, des décimales, des messages, et des
nombres de formatage d'une manière dont se conforme aux conventions de langue locales
(ChoiceFormat, DateFormat, DateFormatSymbols, DecimalFormat,
DecimalFormatSymbols, FieldPosition, format, MessageFormat, NumberFormat,
ParsePosition, SimpleDateFormat). Des interfaces et les classes sont également données
pour assembler et réitérer au-dessus des cordes internationalisées des textes (BreakIterator,
CharacterIterator, CollationElementIterator, CollationKey, assembleuse,
RuleBasedCollator, StringCharacterIterator).
LY_SII __ 28 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

java.security

La sécurité est un souci important avec des applications d'Internet et Java fournit l'appui
architectural intégré pour l'applet et la sécurité d'application. Le paquet de java.security
soutient les applet et les dossiers signés de FIOLE (archives de Java), le chiffrage principal, et
les listes de contrôle d'accès. La classe de sécurité contrôle de soi-disant fournisseurs de
sécurité et centralise toutes les fonctions liées à la sécurité. Un fournisseur de sécurité est
une exécution appelée de certains ou de tous les api de sécurité de Java. Le fournisseur de The
Sun est le défaut et met en application l'algorithme de chiffrage d'architecture de signature
digitale.

java.beans

Le paquet de java.beans contient le modèle réutilisable de composant de logiciel de


JavaBeans, une collection d'approximativement 20 classes et des interfaces. JavaBeans peut
s'étendre de petites, légères commandes de GUI aux composants relativement grands tels que
des bilans ou des unités de traitement de texte. Sans compter que fournir des spécifications
composantes pour des applications de Java, JavaBeans pourra interopérer avec des
composants de Microsoft ActiveX. La portabilité de plate-forme était un but important de
conception pour l'architecture de JavaBeans.

« Un haricot de Java est un composant de logiciel réutilisable qui peut être manoeuvré
visuellement dans un outil de constructeur » (JavaSoft 1996). Chaque haricot fournit à des
ensembles de propriétés, de méthodes, et d'événements pour l'interaction d'autres haricots.
Introspection de soutien d'haricots, qui permet aux générateurs d'application visuels d'analyser
comment chaque haricot travaille, et personnalisation, de sorte que le comportement de
l'haricot puisse être prolongé dans un environnement visuel de constructeur. Une fois qu'un
haricot est adapté et combiné aux besoins du client avec d'autres haricots dans une
application, elle soutient la persistance, de sorte qu'elle puisse être sauvée et reconstituée plus
tard.

JavaBeans interactif par un modèle d'événement sophistiqué qui comporte les objets
d'EventSource qui peuvent mettre le feu aux événements et aux objets d'EventListener qui
peuvent recevoir ces événements. Par l'introspection, les haricots peuvent déterminer quels
événements d'autres haricots peuvent se produire ou écouter. Il y a un enregistrement
d'événement qui permet à des haricots de changer dynamiquement leurs rôles comme
générateurs ou auditeurs d'événement. Des objets d'EventAdapter peuvent être insérés entre
une source d'événement et un auditeur d'événement pour filtrer des événements, événements
de file d'attente, ou pour agir en tant que « standard » centralisé pour des communications
d'inter-Haricot.

java.rmi

Le paquet de java.rmi est employé pour créer des applications réparties ou des applet d'objet
dans Java. L'invocation à distance de méthode emploie un substitut ou un moignon local pour
contrôler l'invocation des méthodes sur un objet à distance. N'importe quel objet qui peut être
accédé à distance doit mettre en application l'interface à distance. Un objet de nomination
LY_SII __ 29 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

fournit le mécanisme initial de circuit fermé pour obtenir des références à distance d'objet. En
outre, on fournit un RMISecurityManager qui définit la politique de sécurité pour des
moignons dans des applications réparties.

Il y a un certain nombre de subpackages liés au RMI. Le paquet de java.rmi.dgc fournit des


classes pour la collection d'ordures distribuée d'objets inactifs. Le paquet de java.rmi.registry
fournit des interfaces et des classes pour l'enregistrement qui existe sur chaque noeud dans un
système distribué (enregistrement, RegistryHandler, LocateRegistry). Un enregistrement
est employé aux noms de carte aux références à distance d'objet. Le paquet de java.rmi.server
contient le reste des interfaces et les classes qui mettent en application le RMI. Ceux-ci
incluent des interfaces pour des appels aux objets à distance (RemoteCall) et pour des
références à distance d'objet (RemoteRef, ServerRef). Des classes sont définies pour les
objets, les serveurs, et les moignons à distance (RemoteObject, RemoteServer,
RemoteStub, UnicastRemoteObject) aussi bien que l'identité d'objet (ObjectID, UID), les
descriptions de méthode (opération), et la notation des erreurs (LogStream).

java.sql

Le paquet de java.sql est mieux connu comme JDBC (connectivité de base de données de
Java). Ce paquet permet l'accès aux bases de données de Java et est basé sur le X/Open SQL
CLI. On fournit une classe de DriverManager qui est responsable de charger l'exécution
correcte de conducteur pour une base de données donnée et créera un raccordement à une
base de données particulière. Une exécution de conducteur pour des bases de données
d'ODBC est fournie en tant qu'élément du JDK, et d'autres fournisseurs de base de données
fournissent des conducteurs pour leurs réalisations particulières de base de données. Des
interfaces sont données pour exécuter des commandes SQL Contre une base de données
(CallableStatement, PreparedStatement, rapport), et pour renvoyer les résultats des
questions de base de données (ResultSet). Des classes sont également données pour les types
de données de SQL (date, temps, horodateur, types). En outre, l'appui est fourni pour
questionner la base de données et le conducteur fondamentaux de base de données pour
découvrir quels dispositifs sont soutenus (DatabaseMetaData, DriverPropertyInfo,
ResultSetMetaData). Le résultat global est un paquet qui permet au code de Java d'accéder à
des données dans différentes bases de données de différents fournisseurs employant une
interface de programmation commune.

Environnements de développement de Java

La condition minimum est d'obtenir le kit de développement de Sun Java, qui fournit une
ligne de commande compilateur de Java, un programme de mise au point rudimentaire, et une
exécution basse de la machine virtuelle de Java. Cependant, la plupart des réalisateurs sont
accoutumés à employer les environnements de développement integrated qui comportent des
constructeurs de GUI, l'élimination des imperfections de code source, gestion des projets pour
la commande de code source, et des navigateurs et des rédacteurs de code source. Inclure le
plus également les compilateurs de JIT pour une meilleure exécution. Il y a plusieurs
compagnies qui vendent des environnements de développement integrated pour Java. Ceux-ci
incluent Symantec, avec son ensemble de produits de Café, Sun avec ses outils d'atelier de
Java, IBM avec son VisualAge pour Java, et Microsoft, avec son instrument de
développement de J++. La beauté de Java et de son interface portative de machine virtuelle
est qu'elle n'importe pas vraiment que le fournisseur ou vous usinent choisissent de
LY_SII __ 30 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

développer vos applications et agents de Java. Tant que vous employez les classes qui font
partie de la langue standard et des utilités, il pourra courir sur n'importe quelle autre plate-
forme. Une note d'avertissement est que Microsoft a présenté les éléments de propriété
industrielle dans ses outils et exécution de Java. Faire attention que vous ne comptez pas sur
ces dispositifs, ou vous serez limité aux plates-formes de Microsoft Windows.

Notes de programmeur de C.A. ++

Comme programmeur expérimenté de C++, de tout que j'avais eu connaissance de Java, j'ai
compris que la programmation dans Java ne serait aucune affaire. Après plusieurs jours du
codage, je peux confirmer que Java est un environnement de programmation confortable et
très productif. Pour donner une petite meilleure perspective sur d'où je viens, me laisser
décrivent brièvement mon expérience de C++. J'ai conçu et écrit plusieurs instruments de
développement dans C++ using le GUI de Star Division, Inc. StarView classer les
bibliothèques (pour OS/2, Windows 3.1, et AIX) aussi bien que les bibliothèques de classe
d'IBM OpenClass (également connu sous le nom d'ICLUI). J'ai également employé les
bibliothèques calibre-basées standard de classe de collection qui sont maintenant norme dans
le monde de C++. Ainsi, voici quelques commentaires de novice de Java d'un programmeur
expérimenté de C++ :

Défaut de la reproduction sonore ! L'inscription du nouveau code dans Java me rappelle des
jours plus tôt using les produits de Turbo Pascal. J'emploie l'environnement de développement
de Symantec Café qui est gentil, mais non exceptionnel de quelque façon. Ce qui
m'impressionne plus est la puissance basse des classes de langue et d'utilité de Java. Il en a
pris s'habituant pour moi à ne pas esquisser dehors mes classes dans des dossiers d'en-tête et
créer alors des moignons de méthode dans un cpp classer. Dans Java vous devez définir les
méthodes à l'intérieur de la définition de classe (à moins que vous employez des interfaces,
qui est quelque chose que nous obtiendrons plus tard). Un autre problème que j'ai frappé était
que j'ai voulu faire tous de mon public de classes d'aide, mais Java ne me laissera pas. Elles
toutes doivent être dans les dossiers séparés de .java. Puisque j'étais juste prototypage une
partie du code d'AI, j'ai voulu employer juste un dossier de programme. J'ai fait ceci en
rendant toute les aide des classes non publiques. Une fois que j'écris plus de code, je retirerai
les objets communs dans leurs propres classes.

Que m'impressionne au sujet de Java ? La classe de vecteur semble être une classe
extrêmement utilisable de liste qui peut tenir des objets de n'importe quel type et qui peut être
employée comme file d'attente. J'aime particulièrement l'interface d'énumération qui dirigent
(et d'autres classes) l'appui. Avec ceci vous pouvez faire une boucle de moment () avec
list.hasMoreElements () et list.nextElement (). Je ne peux pas te dire combien d'heures elle a
pris pour obtenir à ma première classe calibre-basée par C++ pour compiler et lier. C'était une
brise.

Une autre chose que j'ai dû faire était d'analyser un certain texte. Dans C j'emploierais la
fonction de strtok (), avec laquelle n'est pas joli pour travailler. À IBM OpenClass
j'emploierais un IString avec une interface de mot (). Dans Java, j'ai employé un
StringTokenizer qui soutient l'interface d'énumération. L'analyse d'une corde dans des
marques était aussi facile que les hasMoreElements d'un moment () () et la boucle de
LY_SII __ 31 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

nextElement (). Frais.

J'ai eu quelques problèmes avec le Java « tout est que paradigme d'une référence » pour des
objets. Quand je code une classe dans C++, je peux spécifier que des objets sont contenus ou
mis en référence. C++ sait assez pour appeler le constructeur de défaut pour les membres de
données qui sont contenus (pas des indicateurs aux exemples). Mais puisque Java n'a aucune
notion des indicateurs, tous les membres de données sont implicitement « par la référence. »
Ceci signifie qu'à moins que vous fassiez un nouveau classXYZ () dans votre constructeur, la
première fois que vous essayiez de mettre en référence tous les objets de membre de données,
vous obtiendrez une exception de pointeur nul.

Un autre « dispositif » non-ainsi-évident de Java est les classes immuables d'emballage pour
les types de données primitifs. Vous pouvez coder des choses comme international, flotter, et
doubler juste comme dans C ou C++. Java fournit avec bonté le nombre entier, le flotteur, et
les doubles classes ainsi vous pouvez transformer ces valeurs en véritables objets de Java. Le
seul problème est qu'ils sont immuables (ils ne peuvent pas être changé une fois eux sont
créés). J'ai essayé de passer un nombre entier dans une méthode et de l'employer comme
variable de rendement. Mais cela n'a pas fonctionné parce que je ne pourrais pas le changer.
Une méthode de setValue () sur la classe d'emballage sure serait gentille.

Un dernier commentaire sur Java contre C++ comme langue de développement : Je ne


manque pas le long édite/compile/cycles de lien du tout. Quel plaisir de pouvoir apporter une
modification rapide et immédiatement compiler et courir le code changé. Pas aussi gentil que
la causerie, mais un énorme progrès au-dessus de la productivité de C++.

Using Java pour les agents intelligents

Dans les sections précédentes de ce chapitre, nous avons décrit les éléments principaux du
langage de programmation de Java. Dans cette section, nous parlons des dispositifs
spécifiques de Java qui soutiennent des applications d'agent intelligent. Ces dispositifs seront
explorés en plus détail dans le reste du livre.

Notes d'un programmeur de causerie

Comme programmeur expérimenté de causerie, je n'attendais pas beaucoup de Java. ,


L'environnement de programmation de causerie m'est le plus productif j'ont jamais éprouvé.
Je sais que Java est censé être chaud, mais je vraiment ne pourrais pas le voir causerie se
surpassante à mon coeur. Mon expérience de programmation de causerie a commencé en
1987, quand je travaillais sur ma maîtrise à l'université de Lehigh à Bethlehem, PA. J'avais
écrit la majeure partie de ma thèse sur de divers types de réseaux neurologiques et j'ai voulu
mettre en application un environnement de développement et d'essai de réseau neurologique.
Mon conseiller de thèse, Ed Kay, enseignait une classe de causerie et était placé par Tektronix
(nous avons eu 3 ou 4 postes de travail de Tek Smalltalk-80). Ainsi, ce septembre j'ai
commencé à apprendre simultanément la causerie et les techniques de programmation d'OO.
C'était tout à fait une expérience. Plus tard, nous avions l'habitude Digitalk Smalltalk/V pour
mettre en application le GUI sur le produit de service de la version 2 de réseau neurologique,
LY_SII __ 32 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

qui a été libéré au mid-1991. C'était l'un des premiers produits d'IBM embarqués qui a été
écrit dans la causerie. Plus récemment, j'ai employé IBM VisualAge pour que la causerie crée
une interface de prototype aux api de NNU V3. Ceci a renforcé ma perception que la causerie
est un environnement de programmation supérieur à C++ et également convaincu me que la
programmation visuelle pourrait être extrêmement productive. Plus tôt, j'ai dit que je
n'attendais pas beaucoup de Java, une fois comparé à la causerie. C'est parce que Java se
fonde sur la syntaxe de C++, qui est encore plus mauvaise que le C. Mais je sais que Java
objet-est basé (ignorer ces international et types de flotteur pour le moment) et qu'il a la
collection d'ordures au lieu de ces méthodes merveilleuses nouvelles/suppression dans C++.
Une fois que vous apprenez l'ordre de causerie de la priorité (unaire, binaire, message) pour
l'évaluation d'expression, c'est la manière la plus normale de penser au code d'OO, ainsi je ne
pourrais pas image ce que Java pourrait offrir.

Bien, après utilisation de Java je peux dire qu'il a quelques choses gentilles. Le premier dont
est que le code est compréhensible pour des programmeurs de C et de C++ (tristement,
causerie en prend qui s'habitue). J'ai également trouvé la hiérarchie de classe pour être moins
complexe et plus franc pour employer que les classes traditionnelles de causerie.

Tandis que Java pourrait regarder plutôt C++, il pense (se sent et agit) plutôt la causerie.
J'aime pouvoir faire un vecteur ou une pile des objets, et être libre pour mettre n'importe quel
type d'objet dans lui. Naturellement vous devez connaître quel type d'objet il est quand vous
lui accédez, mais comme la causerie, Java vous laisse demander l'objet « quel type c'est. »
Cette fonction apparemment simple ajoute un bon nombre de puissance à la langue.

Autonomie

Pour qu'un logiciel soit autonome, ce doit être un processus ou un fil séparé. Les applications
de Java sont des processus séparés et car telles peuvent être longues et autonomes. Une
application de Java peut communiquer avec d'autres programmes using des douilles. Dans une
application, un agent peut être un fil séparé de commande. Java soutient des applications
filetées et fournit l'appui pour l'autonomie using les deux techniques.

Dans l'introduction, nous avons décrit les agents intelligents en tant que des programmes ou
processus autonomes. En soi, ils sont toujours attente, prêtes à répondre à une demande
d'utilisateur ou à un changement de l'environnement. Une question qui vient à l'esprit est
« comment fait l'agent savent quand quelque chose change ? » Dans notre modèle, comme
avec beaucoup d'autres, l'agent est informé en lui envoyant un événement. D'une perspective
de conception orientée objectivement, un événement n'est rien davantage qu'un appel ou un
message de méthode, avec l'information passée le long à l'appel de méthode qui définit ce qui
s'est produit ou les quels action nous voulons que l'agent effectue, comme eus besoin pour
traiter l'événement.

Dans Java il y a un mécanisme de événement-traitement qui est employé dans la trousse à


outils abstraite de fenêtrage pour passer des événements définis pour l'utilisateur tels que des
mouvements de souris et des options de menu aux composants fondamentaux de fenêtre.
Selon le type d'agent que nous construisons, nous peut devoir traiter ces événements de bas
niveau, comme quand une commande de GUI obtient le foyer, ou une fenêtre est remise à la
côte ou déplacée, ou les presses d'utilisateur une clef. Le paquet d'awt.event soutient
également des événements sémantiques de plus haut niveau tels que des actions d'utilisateur.
LY_SII __ 33 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Java soutient également une autre interface de plus haut niveau d'événement appelée le cadre
observable/observateur. Dans cette approche, les objets ou les observateurs intéressés
s'enregistrent avec l'objet observable. Toutes les fois qu'une méthode est invitée la chose
observable ou l'objet de modèle qui cause un changement crucial d'état ou quelque chose
équivalente à un événement sémantique à niveau élevé, puis on annonce tous les objets
enregistrés d'observateur.

Intelligence

L'intelligence dans les agents intelligents peut s'étendre de la logique procédurale ou orientée
objectivement hardcoded aux possibilités sophistiquées de raisonnement et d'étude. Tandis
que le Prolog et blèsent sont les deux langues habituellement liées à l'intelligence artificielle
planifiant, ces dernières années, une grande partie du travail commercial d'AI a été codé dans
C et C++. Comme un langage de programmation d'usage universel et orientée objectivement,
Java fournit toute les fonction basse requise pour soutenir ces comportements.

Il y a deux aspects importants aux applications d'AI, à la représentation de connaissance et


aux algorithmes qui manoeuvrent ces représentations. Toutes les représentations de
connaissance sont basées sur l'utilisation des fentes ou les attributs qui tiennent l'information
concernant une certaine entité, et les liens ou les références à d'autres entités. Des objets de
Java peuvent être employés pour coder ces données et comportement aussi bien que les
rapports entre les objets. Représentation de connaissance standard d'AI telle que des
armatures, filets sémantiques, et si alors des règles peuvent tout mettre en application
facilement et naturellement using Java.

Mobilité

Il y a plusieurs différents aspects à la mobilité dans le cadre des agents intelligents et des
applications intelligentes. Les bytecodes de Java et les dossiers portatifs de FIOLE permettent
à des groupes de classes de Java compilées d'être envoyés au-dessus d'un réseau et d'être puis
exécutés sur la machine cible. Les Java applets Fournissent un mécanisme pour courir le code
de Java à distance par l'intermédiaire d'un web browser. D'autres environnements, tels que les
ferrets d'IBM, permettent à des processus de Java d'être commencés, suspendus, et déplacés.

Une des conditions principales pour des programmes mobiles est la capacité de sauver l'état
du processus courant, l'embarque au loin, et reprend alors où la gauche de processus au loin,
seulement maintenant elle fonctionne sur un système différent. Les chercheurs de
l'informatique ont exploré cette matière dans le grand détail par rapport à l'équilibrage de la
charge sur les systèmes informatiques répartis tels que des réseaux des postes de travail. Avoir
les machines homogènes était une partie cruciale de faire ce travail. De nouveau, la machine
virtuelle de Java vient à la délivrance. En fournissant un environnement de calcul standard
pour qu'un processus de Java coure dedans, le JVM fournit une machine virtuelle homogène
qui permet à des agents de Java de se déplacer entre les systèmes de matériel hétérogènes
(d'un PC à un poste de travail de Sun, à un système d'AS/400) sans perdre un battement.

D'autres aspects de Java permettent également la mobilité. Le modèle d'événement de


délégation décrit plus tôt permet l'enregistrement dynamique d'EventSources et
LY_SII __ 34 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

d'EventListeners. Ainsi un agent mobile de Java pourrait « brancher » à un environnement de


serveur déjà-courant quand il arrive, et alors « débrancher » lui-même, quand il est temps de
passer. Le paquet de java.net fournit les possibilités de communications de réseau qui
permettent à des agents mobiles ou à une infrastructure mobile d'agent de parler à d'autres
serveurs et d'envoyer le code de Java et l'état de processus au-dessus des douilles.

Résumé

En ce chapitre nous avons décrit les dispositifs principaux du langage de programmation de


Java. Nous l'avons également comparé à C++ et à causerie et l'avons évalué comme langue
pour mettre en application des applications d'agent intelligent. Les points principaux incluent
ce qui suit :

• Java est une langue portative et architecture-neutre, parce qu'il est compilé dans les
bytecodes qui sont alors interprétés et courent par la machine virtuelle de Java. N'importe
quel système qui soutient la machine virtuelle de Java peut lancer n'importe quel
programme pur de Java.

• Des compilateurs juste à temps sont employés pour convertir des bytecodes en de langage
machine pour améliorer l'exécution. Les compilateurs statiques sont également disponibles
pour transformer le code source de Java directement en executables platform-specific.

• Java ne fournit pas des indicateurs ou des opérateurs pour la manipulation des indicateurs.
Toute l'attribution de mémoire est commandée par le programmeur employant le nouvel
opérateur. Le stockage d'objet (mémoire) est récupéré par une fonction automatique de
collection d'ordures.

• Les Java applets Sont les petits programmes de Java qui peuvent être téléchargés d'un
serveur à un client et à une course par un web browser en tant qu'élément d'un HTML page.
Pour des raisons de sécurité, les applet sont restreints d'accéder aux ressources locales.
Cependant, Java soutient les applications autonomes qui sont équivalentes aux programmes
standard de C ou de C++, et qui ont accès sans restriction aux ressources du système.

• Java est un langage de programmation orientée objectivement avec la syntaxe de C++. Une
classe de Java contient les membres et les méthodes de données qui définissent l'état et le
comportement des exemples ou des objets de cette classe. En plus des objets, Java soutient
les types de données élémentaires pour l'exécution.

• L'interface indigène de méthode de Java permet des programmes courant le Java machine
virtuelle pour appeler des interfaces de programme d'application écrites dans C et C++. Elle
permet également des programmes écrits en d'autres langues pour commencer vers le haut
la VM de Java et pour lancer des programmes de Java.

• Java emploie le jeu de caractères de 16 bits d'Unicode pour le texte de codage. Le format
d'Unicode soutient la plupart des langues principales dans le monde, y compris le Japonais, le
Coréen, et le Chinois.
LY_SII __ 35 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

• La langue de Java est prolongée par un ensemble de paquets, qui contiennent les classes
de soutien pour une large variété de fonctions, y compris les communications de réseau
(java.net), la sécurité (java.security), les interfaces utilisateurs graphiques (java.awt), les
appels à distance de méthode (java.rmi), et l'accès aux bases de données (java.sql).

• JavaBeans est l'architecture de composant de logiciel de Java. Des haricots peuvent être
employés dans un environnement visuel de constructeur pour construire des applications
hors des composants de logiciel existants, y compris des composants d'ActiveX.

• Java fournit toute les fonctionnalité exigée pour concevoir et mettre en application les
agents intelligents. Ses possibilités de langage de type objet d'usage universel et permettent
à la connaissance d'être représentés et raisonnement et à algorithmes d'étude à mettre en
application facilement. Les bytecodes portatifs de Java permettent à des agents d'être
empaquetés comme applet ou en tant que programmes mobiles de Java.

Exercices

1. Si vous n'avez pas déjà fait ainsi, obtenir commencé à apprendre Java. Le site Web chez
http://java.sun.com a un excellent cours d'instruction en ligne sur Java comprenant des
descriptions des perfectionnements de JDK 1.1.

2. Écrire bonjour une application du monde using n'importe quel instrument de


développement de Java. Que fait à une classe de Java une demande ? Passer votre nom dans
l'application ainsi elle montre « bonjour le name>> de <<your ! »

3. Écrire bonjour un applet du monde. Le courir using la visionneuse d'applet ou votre web
browser préféré. Passer votre nom dans l'applet ainsi il montre « bonjour le name>> de
<<your ! » Pouvez-vous passer dans un type de valeur de paramètre ?
LY_SII __ 36 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Chapitre 2
Problem résolvant Using la recherche
En ce chapitre nous explorons la première poussée de commandant de la
recherche en matière d'intelligence artificielle, résolution des problèmes using la
recherche. Nous discutons comment des problèmes peuvent être représentés
comme états et être puis résolus suivre des techniques simples de recherche de
force brutale ou des méthodes tout à fait sophistiquées de recherche heuristique.
On développe un Java applet Qui met en application quatre algorithmes de
recherche de base.

Définition du problème

Le centre de beaucoup de recherche d'AI a été sur résoudre des problèmes. Tandis que
quelques critiques ont argué du fait que l'AI s'est concentrée sur de manière irréaliste des
problèmes simples ou de jouet dans le passé, ce n'est certainement pas le cas aujourd'hui.
Rétrospectivement, il n'est pas clair que les critiques « de problème de jouet » aient été
justifiées du tout. Une grande partie du point de la recherche d'AI était de comprendre
« comment » résoudre le problème, pour obtenir pas simplement une solution. Les problème-
puzzles tellement apparemment simples, jeux, empilant bloquer-étaient le centre des
programmes d'AI. Et un des premiers domaines, des méthodes de résolution des problèmes
générales, a accentué une barrière importante au logiciel d'intelligence artificielle. Comment
représentez-vous un problème de sorte que l'ordinateur puisse le résoudre ? Même avant cela,
comment définissez-vous le problème avec assez de précision de sorte que vous puissiez
figurer dehors comment la représenter ?

Tandis que « savoir dans quelles affaires vous êtes » est une des maximes élémentaires
d'affaires, parce que d'intelligence artificielle qu'elle « sait quel problème vous essayez de
résoudre. » Pour certains, la solution du problème est avec succès le seul but. Pourquoi utiliser
un ordinateur à moins qu'il puisse l'aide vous résoudre des problèmes commerciaux (et gagner
l'argent) ? Pour d'autres, le défi est de reproduire des techniques de résolution des problèmes
humaines ou, au moins, de gagner un meilleur arrangement de la façon dont les gens résolvent
des problèmes complexes. Aujourd'hui, très peu de personnes réclameraient que les méthodes
rechercher-basées démontrent comment notre cerveau résout des problèmes, mais ces
méthodes ont prouvé extrêmement utile et ont un endroit dans n'importe quel examen des
techniques IA Pratiques.

La première étape dans n'importe quel exercice de résolution des problèmes est définissent à
clairement et succinctement ce que c'est nous essayent de faire. Voulons-nous trouver le
meilleur itinéraire pour un voyage ou juste celui qui nous y arriveront ? Pouvons-nous
attendre plusieurs heures ou jours une réponse, ou avons-nous besoin de la meilleure réponse
que nous pouvons calculer en dix secondes ? Quelqu'un une fois que dit que dans l'AI la
plupart de part importante de résolution des problèmes est « représentation, représentation, la
représentation. » En ce chapitre, nous sommes intéressés par la façon représenter notre
problème de sorte que nous puissions le résoudre using des techniques de recherche, et qui
recherchent des techniques pour employer. Dans la prochaine section, nous explorons une des
représentations primaires de problème, l'approche du l'état-espace.
LY_SII __ 37 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

L'espace d'état

Supposer que le problème que nous voulons résoudre des affaires avec jouer et gagner un jeu.
Ce jeu a pu être simple tel que le tic-tac-orteil ou plus complexe tel que des contrôleurs, des
échecs, ou le jacquet. En tous cas, la clef à traiter ce problème avec un ordinateur est de
développer une cartographie du monde du jeu-espace des morceaux et le modèle géométrique
sur un conseil, à une structure de données qui capture l'essence du jeu-état courant. Pour le tic-
tac-orteil, nous pourrions employer une rangée avec neuf éléments, ou nous pourrions définir
des 3 par la matrice 3 avec 1s et 0s pour dénoter le Xs et l'OS de chaque joueur.

Nous commençons par un premier état, qui dans le cas de tic-tac-orteil pourrait être des 3 par
la matrice 3 remplie d'espaces (ou de marqueurs vides). Pour n'importe quel état de notre
panneau de jeu, nous prenons un ensemble d'opérateurs qui peuvent être employés pour
modifier l'état actuel, créant de ce fait un nouvel état. Dans notre cas, ce serait un joueur
marquant un espace vide avec un X ou un O. La combinaison de l'état initial et l'ensemble
d'opérateurs composent l'espace d'état du problème. L'ordre des états produits par la demande
valide des opérateurs à partir de l'état initial s'appelle le chemin dans l'espace d'état.
Maintenant que nous avons les moyens d'aller de notre état initial aux états valides
additionnels de jeu, nous devons pouvoir détecter quand nous avons atteint notre état de but.
Dans le tic-tac-orteil un état de but est quand n'importe quelle rangée, colonne, ou diagonale
se compose de tout le Xs ou OS. Dans cet exemple simple, nous pouvons vérifier le panneau
de tic-tac-orteil pour voir si l'un ou l'autre joueur a gagné en déterminant explicitement notre
état de but. Dans des problèmes plus compliqués, la définition de l'essai de but peut être un
problème substantiel en soi.

À beaucoup de problèmes de recherche, nous sommes non seulement intéressés à atteindre un


état de but, nous voudrions l'atteindre avec le plus bas possible coût (ou le bénéfice
maximum). Ainsi, nous pouvons calculer un coût pendant que nous appliquons des opérateurs
et la transition à partir de l'état à l'état. Ce chemin a coûté ou la fonction de coût est
habituellement dénotée par le G. Donné un problème qui peut être représenté par un ensemble
d'états et d'opérateurs et être puis résolu using un algorithme de recherche, nous pouvons
comparer la qualité de la solution en mesurant le coût de chemin. Dans le cas du tic-tac-orteil
nous avons un espace de recherche limité. Cependant, parce que beaucoup de problèmes réels
que l'espace de recherche peut se développer très grand, ainsi nous avoir besoin d'algorithmes
pour traiter cela.

Les algorithmes de recherche efficaces doivent faire deux choses : le mouvement de cause ou
le traversal de l'espace d'état, et font ainsi d'une façon commandée ou systématique. La
recherche aléatoire peut fonctionner dans quelques problèmes, mais généralement nous
devons rechercher d'une manière organisée et méthodique. Si nous avons une stratégie de
recherche systématique qui n'emploie pas des informations sur le problème pour aider direct
la recherche, ce s'appelle la recherche de la force brutale, non informée, ou sans visibilité. La
seule différence entre les différentes techniques de recherche de force brutale est l'ordre dans
lequel des noeuds sont augmentés. Mais même les légers changements de l'ordre peuvent
avoir un impact significatif sur le comportement de l'algorithme. Des algorithmes de
recherche qui emploient des informations sur le problème, tel que le coût ou la distance à
l'état de but, s'appellent recherche heuristique, au courant, ou dirigée. L'avantage primaire des
algorithmes de recherche heuristique est que nous pouvons faire de meilleurs choix au sujet
LY_SII __ 38 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

dont noeud à augmenter après. Ceci améliore sensiblement l'efficacité des algorithmes de
recherche.

Il y a plusieurs aspects de l'exécution d'un algorithme de recherche il est important reconnaître


que (Russell et Norvig 1995). Un algorithme est optimal s'il trouvera la meilleure solution de
parmi plusieurs solutions possibles. Une stratégie est complète si elle garantit qu'elle trouvera
une solution si on existe. La complexité de l'algorithme, en termes de complexité de temps
(combien de temps elle prend pour trouver une solution) et complexité de l'espace (combien
de mémoire elle exige), est une considération pratique importante. Avoir un algorithme de
recherche optimale qui fonctionne pour toujours a peu de valeur pratique. Ni fait ayant un
algorithme complet qui est un porc de mémoire, s'il manque de mémoire juste avant qu'il
trouve la solution.

Stratégies de recherche

Dans cette section nous examinons deux techniques de base de recherche employées pour
résoudre des problèmes dans l'AI, la recherche en largeur et la profondeur-première
recherche. Plus tard nous explorerons des perfectionnements à ces algorithmes, mais d'abord
nous devons nous assurer que nous comprenons comment ces approches de base fonctionnent.
Nous travaillerons par plusieurs exemples, using la carte représentée sur le schéma 2.1.

D'abord, nous devons définir le problème que nous essayons de résoudre. Dans ce cas-ci, il est
tout à fait simple. Donné un point de départ à des villes sur la carte, pouvons-nous trouver une
autre ville sur la carte tant que il y a une ville de chemin dès le début à la ville d'extrémité ou
de but ? Actuellement, nous n'essayons pas de trouver le Shortest-Path ou n'importe quoi de
pareil. Nous voulons simplement trouver si la ville de but est sur la carte.

L'ok, maintenant que nous avons défini le problème, la prochaine étape est de décider
comment représenter le problème comme état-espace. Une carte comme celle sur le schéma
2.1 peut être naturellement représentée par une structure de données de graphique, où les
noms de villes sont les noeuds, et les chaussées principales entre les villes sont les liens ou les
bords du graphique. Ainsi, d'une perspective de programmation, notre problème est de
traverser une structure de données de graphique d'une manière systématique jusqu'à nous
trouvent la ville de but ou épuisent toutes les possibilités. Avoir si tout va bien l'état-espace
entier montré sur une carte facilitera comprenant les opérations des algorithmes de recherche.
Dans des problèmes plus complexes, tout que nous avons est l'état simple de début et un
ensemble d'opérateurs qui sont habitués pour produire de plus en plus de nouveaux états. Les
algorithmes de recherche fonctionnent la même manière, mais conceptuellement, nous
sommes s'élevants ou augmentant le graphique, au lieu de l'avoir a spécifié au début.

Le schéma 2.1 carte des villes du Mid-West des États-Unis.

Recherche en largeur

L'algorithme de recherche en largeur recherche un état-espace en construisant une structure


arborescente hiérarchique se composant d'un ensemble de noeuds et de liens. L'algorithme
définit une manière de se déplacer par la structure arborescente, examinant les valeurs aux
noeuds d'une manière commandée et systématique, de sorte que nous puissions trouver un
noeud qui offre une solution au problème que nous avons représenté using l'arbre-structure.
LY_SII __ 39 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

L'algorithme suit :

1. Créer une file d'attente et ajouter le premier noeud à lui.


2. Boucle :
• Si la file d'attente est vide, stopper.
• Enlever le premier noeud de la file d'attente.
• Si le noeud contient l'état de but, alors sortie avec le noeud comme solution.
• Pour chaque enfant du noeud courant : Ajouter le nouvel état au dos de la file
d'attente.

L'algorithme en largeur étend dans un noeud uniforme de façon dès le début. Dès le début, il
regarde chaque bord du noeud un loin. Alors il sort de ces noeuds à tous les noeuds deux
bords à partir du début. Ceci continue jusqu'à ce qu'ou le noeud de but soit trouvé ou l'arbre
entier est recherché. La recherche en largeur est complète ; elle trouvera une solution si on
existe. Mais elle n'est ni optimale dans le cas général (elle ne trouvera pas la meilleure
solution, juste le premier qui assortit l'état de but), ni elle a le bon temps ou complexité de
l'espace (elle se développe exponentiellement dans la consommation de temps et de
mémoire).

Marchons par un exemple pour voir comment la recherche en largeur pourrait trouver une
ville sur notre carte. Notre recherche commence à Rochester, et nous voulons savoir si nous
pouvons obtenir à Wausau de là. Le noeud de Rochester est placé sur la file d'attente dans
l'étape 1. Après nous écrivons notre boucle de recherche à l'étape 2. Nous enlevons Rochester,
le premier noeud de la file d'attente. Rochester ne contient pas notre état de but (Wausau)
ainsi nous l'augmentons en prenant chaque noeud d'enfant à Rochester, et en les ajoutant au
dos de la file d'attente. Ainsi nous ajoutons [les automnes de Sioux, Minneapolis, LaCrosse,
et le Dubuque] à notre file d'attente de recherche. Maintenant nous sommes en arrière au
dessus de notre boucle. Nous enlevons le premier noeud de la file d'attente (automnes de
Sioux) et l'examinons pour voir si c'est notre état de but. Il n'est pas, ainsi nous l'augmentons,
ajoutant Fargo et Rochester à l'extrémité de notre file d'attente, qui contient maintenant
[Minneapolis, LaCrosse, Dubuque, Fargo, et Rochester]. Nous enlevons Minneapolis, les
échouer d'essai de but, et nous augmentons ce noeud, ajoutant St.Cloud, Wausau, Duluth,
LaCrosse, et Rochester à la file d'attente de recherche, se tenant maintenant [LaCrosse,
Dubuque, Fargo, Rochester, St.Cloud, Wausau, Duluth, LaCrosse, et Rochester]. Nous
examinons le LaCrosse et puis l'augmentons, ajoutant Minneapolis, GreenBay, Madison,
Dubuque, et Rochester à la liste, qui a maintenant devenu [Dubuque, Fargo, Rochester,
St.Cloud, Wausau, Duluth, LaCrosse, Rochester, Minneapolis, GreenBay, Madison,
Dubuque, et Rochester]. Nous enlevons Dubuque et ajoutons Rochester, LaCrosse, et
Rockford à la file d'attente de recherche.

En ce moment, nous avons examiné chaque noeud qui est un niveau dans l'arbre à partir du
noeud de début (Rochester). Notre file d'attente de recherche contient les noeuds suivants :
[Fargo, Rochester, St.Cloud, Wausau, Duluth, LaCrosse, Rochester, Minneapolis,
GreenBay, Madison, Dubuque, Rochester, Rochester, LaCrosse, et Rockford]. Nous
enlevons Fargo, qui est deux niveaux à partir de Rochester, et ajoutons les fourchettes
grandes, le nuage de rue, et les automnes de Sioux. Alors nous examinons et augmentons
Rochester (Rochester vers Minneapolis vers Rochester est deux niveaux à partir de notre
LY_SII __ 40 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

début). Est après le nuage de rue ; encore nous augmentons ce noeud. En conclusion, nous
obtenons à Wausau ; notre essai de but réussit et nous déclarons le succès. Notre ordre de
recherche était Rochester, automnes de Sioux, Minneapolis, LaCrosse, Dubuque, Fargo,
Rochester, nuage de rue, et Wausau.

Noter que cette trace pourrait avoir été considérablement simplifiée en maintenant les noeuds
qui avaient été examinés et augmentés. Ceci aurait réduit notre temps et complexité de
l'espace, et avait toujours été un algorithme complet parce que nous aurions recherché chaque
noeud avant que nous nous soyons arrêtés. Cependant, dans des problèmes plus réalistes où
chaque noeud est augmenté using une liste d'opérateurs et les états sont plus complexes que
juste des cordes, il n'est pas aussi facile de déterminer que deux états sont identiques. Cette
explosion des noeuds et des états n'est pas peu commune pour un problème de recherche en
largeur.

Profondeur-Première recherche

la Profondeur-première recherche est une autre manière de traverser systématiquement une


structure arborescente pour trouver un noeud de but ou de solution. Au lieu de rechercher
complètement chaque niveau de l'arbre avant d'aller plus profond, le profondeur-premier
algorithme suit une branche simple de l'arbre en bas d'autant de niveaux comme possible
jusqu'à nous atteindre une solution ou un cul-de-sac. L'algorithme suit :

1. Créer une file d'attente et ajouter le premier SearchNode à lui.


2. Boucle :
• Si la file d'attente est vide, stopper.
• Enlever le premier SearchNode de la file d'attente.
• Si le SearchNode contient l'état de but, alors sortie avec le SearchNode comme
solution.
• Pour chaque enfant du SearchNode courant : Ajouter le nouvel état à l'avant de la
file d'attente.

Noter que cet algorithme est identique à la recherche en largeur excepté l'étape 2d. Le
profondeur-premier algorithme recherche dès le début ou noeud de racine toute la manière
vers le bas à un noeud de feuille. S'il ne trouve pas le noeud de but, il fait marche arrière vers
le haut de l'arbre et recherche en bas du prochain chemin non essayé jusqu'à ce qu'il atteigne
la prochaine feuille. Si vous imaginez un grand arbre, le profondeur-premier algorithme peut
passer un grand nombre de temps recherchant les chemins sur le gauche inférieur quand la
réponse est vraiment dans la droite inférieure. Mais puisque la profondeur-première recherche
est une méthode de force brutale, il suivra aveugle ce modèle de recherche jusqu'à ce qu'il
trouve un noeud contenant l'état de but, ou il recherche l'arbre entier. la Profondeur-première
recherche a des conditions de mémoire inférieure que la recherche en largeur, mais elle n'est
ni complète ni optimale.

Comme nous avons fait en haut, marchons par un exemple simple de la façon dont la
profondeur-première recherche fonctionnerait si nous commencions à Rochester et a voulu
voir si nous pourrions obtenir à Wausau. Commençant par Rochester, nous l'examinons et
augmentons, plaçant les automnes de Sioux, puis Minneapolis, puis le LaCrosse, puis le
Dubuque à l'avant de la file d'attente de recherche [Dubuque, LaCrosse, Minneapolis,
automnes de Sioux]. Nous enlevons Dubuque et l'examinons ; il échoue, ainsi nous
LY_SII __ 41 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

l'augmentons ajoutant Rochester à l'avant, puis LaCrosse, puis Rockford. Notre file
d'attente de recherche ressemble maintenant à [Rockford, LaCrosse, Rochester, LaCrosse,
Minneapolis, automnes de Sioux]. Nous enlevons Rockford, et ajoutons Dubuque,
Madison, et Chicago à l'avant de la file d'attente dans cet ordre, rapportant [Chicago,
Madison, Dubuque, LaCrosse, Rochester, LaCrosse, Minneapolis, automnes de Sioux].
Nous examinons Chicago, et endroit Rockford, et Milwaukee sur la file d'attente. Nous
prenons Milwaukee de l'avant et ajoutons Chicago, Madison, et Green Bay à la file
d'attente de recherche. Elle est maintenant [Green Bay, Madison, Chicago, Rockford,
Chicago, Madison, Dubuque, LaCrosse, Rochester, LaCrosse, Minneapolis, automnes de
Sioux]. Nous enlevons le Green Bay et ajoutons Milwaukee, LaCrosse, et Wausau à la file
d'attente dans cet ordre. En conclusion, Wausau est à l'avant de la file d'attente et notre essai
de but réussit et nos extrémités de recherche. Notre ordre de recherche était Rochester,
Dubuque, Rockford, Chicago, Milwaukee, Green Bay, et Wausau.

Dans cet exemple, nous encore n'avons pas empêché des noeuds examinés d'être ajouté à la
file d'attente de recherche. En conséquence, nous avons eu des noeuds doubles sur la file
d'attente. Dans le profondeur-premier cas, ceci pourrait avoir été désastreux. Nous pourrions
avoir facilement eu un cycle ou faire une boucle où nous avons examiné une ville, puis un
deuxième, puis le premier encore, ad infinitum. Dans la prochaine section, nous montrons
notre exécution de Java de ces algorithmes de recherche, commençant par une classe pour le
noeud et une pour le graphique de recherche. Nous incluons des essais pour éviter cette
duplication des essais et les noeuds sur la recherche s'alignent.

La classe de SearchNode

Ces problèmes sont définis using une structure hiérarchique d'arbre ou de graphique, consistée
en un ensemble de noeuds et de liens. Nos algorithmes de recherche travaillent à une structure
de noeud que nous avons définie comme classe de Java SearchNode. Les constructeurs de
SearchNode et les membres de données sont montrés ci-dessous :

public class SearchNode extends Object {


String label ; // symbolic name
Object state ; // defines the state-space
Object oper; // operator used to generate this node
Vector links; // links to other nodes
int depth ; // depth in a tree from start node
boolean expanded ; // indicates if node has been expanded
boolean tested ; // indicates if node was ever tested
float cost=0 ; // cost to get to this node

static TextArea textArea1 ; // used for trace output only


public static final int FRONT = 0 ;
public static final int BACK = 1 ;
public static final int INSERT = 2;

SearchNode(String Label, Object State) {


label = Label ; state = State ; depth = 0 ;
links = new Vector() ; oper = null ;
expanded = false ; tested = false ;
}

Le premier membre de données dans notre classe de SearchNode est le nom symbolique ou
l'étiquette du noeud. Est après l'état, qui peut être n'importe quel objet. L'opération est la
LY_SII __ 42 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

définition de l'opération qui a créé l'état de l'objet. Ceci serait employé dans les problèmes tels
que des jeux où l'état d'un noeud de parent serait augmenté dans des noeuds et des états
d'enfant multiples basés sur l'application de l'ensemble d'opérateurs.

Le membre de liens est un vecteur contenant des références à tous autres objets de
SearchNode auxquels ce noeud est lié. Des objets de SearchNode peuvent être reliés pour
former n'importe quel graphique comprenant les structures arborescentes.

Le membre de profondeur est un nombre entier qui définit la distance dès le début ou le noeud
de racine dans n'importe quelle recherche. Les deux drapeaux booléens, augmentés et
examinés, sont employés par les algorithmes de recherche pour éviter d'entrer dans les boucles
infinies. Le premier, augmenté, est placé toutes les fois que le noeud est augmenté pendant
une recherche. La seconde, examinée, est employée toutes les fois que l'état du noeud est
examiné pendant une recherche. Selon l'algorithme de recherche ce drapeau mai ou mai ne
pas être employé. Le membre de coût peut être employé pour représenter le coût accumulé
courant ou n'importe quelle autre mesure de coût liée au problème de recherche. Le membre
statique textArea1 est habitué pour montrer l'information de trace dans notre applet d'exemple.

Le constructeur prend deux paramètres, le nom ou l'étiquette, et l'état d'objet initial. Le


constructeur initialise les liens et les membres d'opération et place les drapeaux booléens à
faux.

Une fois que nous avons créé un exemple d'un SearchNode, nous devons spécifier les liens à
d'autres noeuds. L'addLink () et des méthodes d'addLinks () sont fournis à cette fin. Des
méthodes sont données pour ajouter un, deux, trois, ou quatre liens à d'autres objets de
SearchNode aussi bien qu'un vecteur des objets de SearchNode une fois nécessaires pour les
graphiques très grands en connectivité élevée.

public void addLinks(SearchNode Node) {


links.addElement(Node);
}

public void addLinks(SearchNode n1, SearchNode n2) {


links.addElement(n1) ;
links.addElement(n2) ;
}

public void addLinks(SearchNode n1, SearchNode n2,


SearchNode n3) {
links.addElement(n1) ;
links.addElement(n2) ;
links.addElement(n3) ;
}

public void addLinks(SearchNode n1, SearchNode n2,


SearchNode n3, SearchNode n4) {
links.addElement(n1) ; links.addElement(n2) ;
links.addElement(n3) ; links.addElement(n4) ;
}

public void addLinks(Vector Nodes) {


LY_SII __ 43 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

for (int i=0 ; i < Nodes.size() ; i++) {


links.addElement(Nodes.elementAt(i)) ;
}
}

Nous fournissons un ensemble de méthodes pour l'essai si le noeud est un noeud de feuille
dans une structure arborescente (c.-à-d., elle n'a aucun enfant) et des méthodes pour placer la
profondeur, l'opération, augmentée, et les membres d'essais.

public boolean leaf() { return (links.size() == 0) ; }


public void setDepth(int Depth) { depth = Depth; }
public void setOperator(Object Oper) { oper = Oper; }
public void setExpanded() { expanded = true; }
public void setExpanded(boolean state) { expanded = state; }
public void setTested(boolean state) { tested = state ; }

La méthode de remise () est employée pour remettre à zéro la profondeur de noeud et les
drapeaux booléens avant de commencer une recherche.

// initialize the node for another search


public void reset() {
depth = 0 ;
expanded = false ;
tested = false ;
}

La méthode setDisplay() est employée dans notre applet d'exemple pour enregistrer un
composant de TextArea pour montrer l'information de trace pendant une recherche.

static public void setDisplay(TextArea textArea) {


textArea1 = textArea;
}

La méthode de trace () écrit une corde dentelée indiquant la profondeur du noeud dans l'arbre
de recherche avec son étiquette et état. Comme écrit, elle suppose que l'état est une corde.

// write a trace statement -- indent to indicate depth


public void trace() {
String indent = new String() ;
for (int i=0 ; i < depth ; i++) indent += “ ” ;
textArea1.appendText(indent + “Searching ” +
depth + “: ” + label +
“ with state = ” + state + “\n”) ;
}

La méthode la plus compliquée dans notre classe de SearchNode est la méthode d'expansion
(), qui est employée par les divers algorithmes de recherche pour accumuler un arbre de
recherche du graphique de recherche initial. Les paramètres sont une file d'attente (un
exemple de vecteur) et un paramètre spécifiant où les noeuds d'enfant devraient être placés
sur la file d'attente. Les options sont AVANT, ARRIÈRES, ou INSERTION, basée sur le coût
courant dans le SearchNode.

D'abord nous marquons le noeud comme augmenté. Alors nous faisons une boucle au-dessus
de tous les noeuds auxquels le noeud a des liens. Pour chaque noeud, s'il n'a pas été examiné
LY_SII __ 44 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

(réellement, ce des moyens placés sur la file d'attente, parce que les états de noeud sont
examinés seulement quand ils sont enlevés de l'avant de la file d'attente) nous la marquons
comme examiné, plaçons sa profondeur dans l'arbre de recherche, et puis la plaçons sur la file
d'attente en position spécifique. Pour l'AVANT, nous ajoutons à la position 0. Pour le DOS
nous simplement addElement (), qui le place à la fin du vecteur.

Pour la caisse d'INSERTION, nous prenons une boucle où nous comparons le coût du noeud
que nous essayons de nous insérer, nextNode, au coût des noeuds sur la file d'attente. Si le
nextCost est inférieur, nous le plaçons sur la file d'attente. Il y a deux cas où le nextNode ne
sera pas inséré dans cette boucle : quand la file d'attente est vide, et quand le nextCost est plus
grand que le coût de tous les noeuds déjà sur la file d'attente. Nous employons un drapeau
booléen, inséré, pour traiter ces cas.

// expand the node and add to queue at specified position


// position 0=front, 1=back, 2=based on node cost
public void expand(Vector queue, int position) {
setExpanded() ;
for (int j = 0; j < links.size(); j++) {
SearchNode nextNode = (SearchNode)links.elementAt(j) ;
if (!nextNode.tested) {
nextNode.setTested(true) ;
nextNode.setDepth(depth+1) ;
switch (position) {
case FRONT: queue.insertElementAt(nextNode,0);
break ;
case BACK: queue.addElement(nextNode);
break ;
case INSERT:
boolean inserted = false ;
float nextCost = nextNode.cost ;
for (int k=0 ; k < queue.size() ; k++) {
// find where to insert this node
if (nextCost < ((SearchNode)queue.elementAt(k)).cost){
queue.insertElementAt(nextNode, k);
inserted = true ;
break ; // exit the for loop
}
}
// couldn’t find place to insert, just add to end
if (!inserted) queue.addElement(nextNode) ;
break;
}
}
}
}

Maintenant que nous avons défini notre classe de SearchNode, nous pouvons parler de notre
classe de SearchGraph, qui contient l'ensemble d'objets de SearchNode qui définissent nos
états de problème. SearchGraph est une classe relativement simple. Il prolonge le
java.util.La classe de table de brouillage en ajoutant un nom et un ensemble de méthodes
pour des opérations de manipulation sur une collection de SearchNode objecte. Le
constructeur prend un paramètre simple de corde pour le nom de SearchGraph. Le
comportement de table de brouillage de défaut est prolongé par deux méthodes. La méthode
de remise () emploie l'énumération des éléments dans la table de brouillage pour réitérer au-
dessus des objets de SearchNode et pour remettre à zéro chacun alternativement. () La
LY_SII __ 45 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

méthode mise prend un objet de SearchNode comme argument simple, et puis appelle () la
méthode mise par table de brouillage using l'étiquette comme clef et l'objet de SearchNode
comme valeur.

// SearchGraph is a container for a set of SearchNodes


// SearchNodes are stored in a Hashtable so they can be
// retrieved by name

class SearchGraph extends Hashtable {


String name ;

SearchGraph(String Name) {
name = Name ;
}

// reset each SearhNode in the graph


// clear expanded and tested flags, set depth=0
void reset() {
Enumeration enum = this.elements() ;
while (enum.hasMoreElements()) {
SearchNode nextNode = (SearchNode)enum.nextElement();
nextNode.reset() ;
}
}

// add node to Hashtable, using node label as key


void put(SearchNode node) {
put(node.label, node) ;
}

Applet de recherche

Dans cette section, nous développons un Java applet Pour illustrer le comportement de quatre
algorithmes de recherche. L'interface utilisateurs de notre applet est composée d'un dialogue
simple représenté sur le schéma 2.2. Un utilisateur peut choisir parmi une de quatre
techniques de recherche : profondeur-premier, en largeur, réitérer-approfondissement, et
meilleur-premier. Nous avons déjà présenté les algorithmes en largeur et profondeur-premiers
de manière assez détaillée. Nous discuterons chacun des quatre algorithmes et leurs
réalisations dans les sections suivantes. L'utilisateur peut choisir l'état de début et de but dans
notre essai SearchGraph avec le type. Le pressurage du bouton marche appellera
l'algorithme de recherche correspondant avec les états spécifiques de noeud et de but de début
passés comme arguments. Pendant que les algorithmes de recherche progressent, l'information
de trace est montrée dans le TextArea au dessus du dialogue. Appuyer sur le bouton clair
dégagera ce secteur entre les courses, si désiré.

Le schéma 2.2 le dialogue d'applet de recherche.

Un testGraph statique simple de méthode () est défini dans notre SearchApplet qui crée un
échantillon SearchGraph pour nos exemples des différents algorithmes de recherche. Ce
graphique définit l'ensemble de villes du Mid-West des États-Unis suivant les indications du
schéma 2.1. D'abord nous instancions l'objet de SearchGraph et puis un SearchNode pour
chaque ville. Après que chaque objet de SearchNode soit créé, il est ajouté au SearchGraph
en employant () la méthode mise héritée de la table de brouillage. Noter que le nom de
LY_SII __ 46 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

noeud et l'état (aussi le nom de la ville) sont identiques. C'est pour l'illustration seulement. Il
n'y a aucune raison pour laquelle l'étiquette de noeud et l'état doivent être identique. En fait, le
deuxième paramètre sur le constructeur de SearchNode peut être n'importe quel objet de
Java. Ainsi nous pourrions avoir une rangée ou une matrice pour le tic-tac-orteil, ou une autre
représentation arbitrairement complexe du l'état-espace, selon le problème que nous essayons
de résoudre.

Après que les objets de SearchNode soient créés, nous définissons alors la connectivité entre
les noeuds suivre la méthode d'addLinks (). Après, nous avons placé le coût de chaque noeud
comme distance de chaque ville vers Rochester, Minnesota. Nous employons ceci dans le
meilleur-premier exemple de recherche discuté plus tard dans cette section. Cette valeur de
coût est placée à une valeur statique pour cet exemple. Dans la plupart des problèmes de
recherche, le coût serait calculé comme recherche progresse. C'est une autre simplification
employée pour rendre les explications complémentaires plus claires.

// build a test graph and then call the specified search routine
// this graph is a set of cites in the mid-west United States
public static SearchGraph testGraph() {

SearchGraph graph = new SearchGraph(“test”) ;


// first build the example tree
SearchNode roch = new SearchNode(“Rochester”,“Rochester”);
graph.put(roch) ;
SearchNode sfalls = new SearchNode(“Sioux Falls”,“Sioux Falls”);
graph.put(sfalls) ;
SearchNode mpls = new SearchNode(“Minneapolis”,“Minneapolis”) ;
graph.put(mpls) ;
SearchNode lacrosse = new SearchNode(“LaCrosse”,“LaCrosse”) ;
graph.put(lacrosse) ;
SearchNode fargo = new SearchNode(“Fargo”,“Fargo”) ;
graph.put(fargo) ;
SearchNode stcloud = new SearchNode(“St.Cloud”,“St.Cloud”) ;
graph.put(stcloud) ;
SearchNode duluth = new SearchNode(“Duluth”,“Duluth”) ;
graph.put(duluth) ;
SearchNode wausau = new SearchNode(“Wausau”,“Wausau”) ;
graph.put(wausau) ;
SearchNode gforks = new SearchNode(“Grand Forks”,“Grand Forks”);
graph.put(gforks) ;
SearchNode bemidji = new SearchNode(“Bemidji”,“Bemidji”) ;
graph.put(bemidji) ;
SearchNode ifalls = new SearchNode(“International Falls”,“International
Falls”) ;
graph.put(ifalls) ;
SearchNode gbay = new SearchNode(“Green Bay”,“Green Bay”) ;
graph.put(gbay) ;
SearchNode madison = new SearchNode(“Madison”,“Madison”) ;
graph.put(madison) ;
SearchNode dubuque = new SearchNode(“Dubuque”,“Dubuque”) ;
graph.put(dubuque) ;
SearchNode rockford = new SearchNode(“Rockford”,“Rockford”) ;
graph.put(rockford) ;
SearchNode chicago = new SearchNode(“Chicago”,“Chicago”) ;
graph.put(chicago) ;
SearchNode milwaukee = new SearchNode(“Milwaukee”,“Milwaukee”) ;
graph.put(milwaukee) ;
LY_SII __ 47 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

roch.addLinks(mpls, lacrosse, sfalls, dubuque) ;


mpls.addLinks(duluth, stcloud, wausau);
mpls.addLinks(lacrosse, roch ) ;
lacrosse.addLinks(madison, dubuque, roch);
lacrosse.addLinks(mpls,gbay) ;
sfalls.addLinks(fargo, roch) ;
fargo.addLinks(sfalls, gforks, stcloud) ;
gforks.addLinks(bemidji, fargo, ifalls) ;
bemidji.addLinks(gforks, ifalls, stcloud, duluth) ;
ifalls.addLinks(bemidji, duluth, gforks) ;
duluth.addLinks(ifalls,mpls, bemidji) ;
stcloud.addLinks(bemidji, mpls, fargo) ;
dubuque.addLinks(lacrosse, rockford) ;
rockford.addLinks(dubuque, madison, chicago) ;
chicago.addLinks(rockford, milwaukee) ;
milwaukee.addLinks(gbay, chicago) ;
gbay.addLinks(wausau, milwaukee, lacrosse) ;
wausau.addLinks(mpls, gbay) ;

// use as costs for best first search example


// straight line distances from cities to Rochester
roch.cost = 0 ; // goal
sfalls.cost = 232 ;
mpls.cost = 90 ;
lacrosse.cost = 70 ;
dubuque.cost = 140 ;
madison.cost = 170 ;
milwaukee.cost = 230 ;
rockford.cost = 210 ;
chicago.cost = 280 ;
stcloud.cost = 140 ;
duluth.cost = 180 ;
bemidji.cost = 260 ;
wausau.cost = 200 ;
gbay.cost = 220;
fargo.cost = 280;
gforks.cost = 340;

return graph ;
}

Nous construisons notre SearchApplet using l'environnement de développement visuel de


Café de Symantec. Le constructeur visuel nous permet de créer un moignon générique
d'applet et traîner alors - et - laisser tomber les commandes d'awt sur le carreau. Café visuel
produit automatiquement du code de Java pour créer les commandes et les ajouter à l'applet.
Ce code n'est pas présenté ici, parce que n'importe quel instrument de développement de Java
pourrait avoir été utilisé pour construire ce dialogue. Après le code produit de Java, nous
insérons notre code pour instancier notre essai SearchGraph, remplissons deux commandes
bien choisies (de listbox) de noms des villes sur la carte, plaçons le profondeur-premier
bouton par radio dessus, enregistrons la commande de TextArea pour l'information de trace,
Rochester choisi comme notre endroit de début de défaut, et attente l'entrée d'utilisateur.

// set up for search test applet


graph = testGraph(); // build the test graph
Enumeration enum = graph.keys() ; // get city names
while(enum.hasMoreElements()) {
String name = (String)enum.nextElement();
LY_SII __ 48 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

choice1.addItem(name); // fill start cities


choice2.addItem(name); // fill goal cities
} // endwhile
radioButton1.setState(true) ; // default to depth-first
SearchNode.setDisplay(textArea1) ; // used for trace display
choice1.select(“Rochester”) ;
}

Quand le bouton marche est pressé, un awt. L'événement est produit et le code suivant est
appelé. D'abord nous recherchons les cordes de début et de but des commandes bien choisies.
Après nous dégageons tous les objets de SearchNode dans le graphique en appelant la remise
(). Alors nous vérifions l'état des boutons par radio using le getState () et appelons
l'algorithme de recherche choisi. Seulement un radiobutton peut être choisi à tout moment.

void button1_Clicked(Event event) {


int method = 0 ;
SearchNode answer = null ;
SearchNode startNode ;
String start = choice1.getSelectedItem() ;
startNode = (SearchNode)graph.get(start) ;
String goal = choice2.getSelectedItem() ;
graph.reset() ; // reset all nodes for another search
if (radioButton1.getState() == true) {
textArea1.appendText(“\n\nDepth-First Search for ” +
goal + “:\n\n”);
answer = graph.depthFirstSearch(startNode,goal) ;
} // endif
if (radioButton2.getState() == true){
textArea1.appendText(“\n\nBreadth-First Search for ” +
goal + “:\n\n”);
answer = graph.breadthFirstSearch(startNode, goal) ;
} // endif
if (radioButton3.getState() == true) {
textArea1.appendText(“\n\nIterated-Deepening Search for ”
+ goal + “:\n\n”);
answer = graph.iterDeepSearch(startNode, goal) ; //
} // endif
if (radioButton4.getState() == true) {
textArea1.appendText(“\n\nBest-First Search for ” + goal
+ “:\n\n”);
choice2.select(“Rochester”) ; // goal must be Rochester
answer = graph.bestFirstSearch(startNode, “Rochester”) ;
} // endif

if (answer == null) {
textArea1.appendText(“Could not find answer!\n”);
} else {
textArea1.appendText(“Answer found in node ” +
answer.label);
}
}

void button2_Clicked(Event event) { // stop


// for later use --- when we have large search problems
}
LY_SII __ 49 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Quand le bouton clair est appuyé sur, nous dégageons dehors le TextArea.

void button3_Clicked(Event event) { // clear


// Clear the text for TextArea
textArea1.setText(“”);
}

La recherche en largeur l'exécution de Java de l'algorithme de recherche en largeur est


montrée ci-dessous. La méthode de breadthFirstSearch () fait partie de la classe de
SearchGraph. Les paramètres sont le SearchNode initial et l'état de but, qui dans notre
SearchApplet est étiquette d'objet. D'abord nous instancions notre file d'attente des noeuds
pour rechercher, et ajoutons l'initialNode à la file d'attente et le marquons comme examiné.

Dans une boucle de moment (), nous vérifions d'abord pour voir s'il y a plus de noeuds à
augmenter. Si oui, nous enlevons le premier noeud, appelons la trace () pour imprimer un
message, alors examinons le noeud pour voir s'il assortit notre état de but. Bien que dans notre
applet d'exemple nous employions des cordes pour représenter notre état de but, ce code
devrait fonctionner pour n'importe quel objet d'état l'a fourni applique la méthode d'égales ().
Si l'essai de but réussit, nous retournons avec le noeud qui a assorti l'état de but. Si l'essai de
but échoue et nous n'avons pas déjà augmenté ce noeud, nous l'augmentons en plaçant tous les
noeuds qu'il a des liens à sur le dos de la file d'attente. Quand la file d'attente est vide nous
sortons la boucle de moment ().

// do a breadth-first search on graph


public SearchNode breadthFirstSearch(SearchNode initialNode,
Object goalState)
{
Vector queue = new Vector() ;
queue.addElement(initialNode) ;
initialNode.setTested(true) ; // test each node once
while (queue.size()> 0) {
SearchNode testNode = (SearchNode)queue.firstElement() ;
queue.removeElementAt(0) ;
testNode.trace() ;
if (testNode.state.equals(goalState)) return testNode;
if (!testNode.expanded) {
testNode.expand(queue,SearchNode.BACK) ;
}
}
return null ;
}

La recherche en largeur exécute une recherche complète de l'espace d'état. S'il y a les
solutions multiples, il trouvera une solution avec le nombre le plus court d'étapes parce que
chaque noeud de la longueur n est examiné avant que la prochaine couche de noeuds soit
explorée. Sur le schéma 2.3 nous montrons le rendement d'une course de recherche en largeur
contre notre testGraph avec Chicago comme noeud de début et Rochester comme destination.
Noter que l'ordre exact de recherche est affecté par la façon dont nous définissons les liens
dans notre testGraph.

Le schéma 2.3 exemple d'algorithme de recherche en largeur.


LY_SII __ 50 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

La Profondeur-Première recherche le code de Java pour notre profondeur-premier


algorithme de recherche est semblable à notre code en largeur, à moins que les noeuds
d'enfant soient ajoutés à l'avant de la file d'attente au lieu du dos. Ce petit changement fait une
grande différence dans le comportement de recherche de l'algorithme. la Profondeur-première
recherche exige moins de mémoire que la recherche en largeur mais elle peut également
dépenser un bon nombre de chemins les explorant de temps qui mènent aux culs-de-sac.

Les paramètres de depthFirstSearch () sont le SearchNode commençant et l'état de but.


D'abord nous instancions notre file d'attente des noeuds pour rechercher, et ajoutons
l'initialNode à la file d'attente et le marquons comme examiné.

Dans une boucle de moment (), nous vérifions d'abord pour voir s'il y a plus de noeuds à
augmenter. Si oui, nous enlevons le premier noeud, appelons la trace () pour imprimer un
message, alors examinons le noeud pour voir s'il assortit notre état de but. Bien que dans notre
applet d'exemple nous employions des cordes pour représenter notre état de but, ce code
devrait fonctionner pour n'importe quel objet d'état l'a fourni applique la méthode d'égales ().
Si l'essai de but réussit, nous retournons avec le noeud qui a assorti l'état de but. Si l'essai de
but échoue et nous n'avons pas déjà augmenté ce noeud, nous l'augmentons en plaçant tous les
noeuds qu'il a des liens à sur l'avant de la file d'attente. Quand la file d'attente est vide nous
sortons la boucle de moment ().

// do a depth first search on graph


public SearchNode depthFirstSearch(SearchNode initialNode,
Object goalState)
{
Vector queue = new Vector() ;
queue.addElement(initialNode) ;
initialNode.setTested(true) ; // test each node once

while (queue.size()> 0) {
SearchNode testNode = (SearchNode)queue.firstElement() ;
queue.removeElementAt(0) ;
testNode.trace() ; // display trace information
if (testNode.state.equals(goalState)) return testNode;

if (!testNode.expanded) {
testNode.expand(queue,SearchNode.FRONT);
}
}
return null ;
}

Le schéma 2.4 Profondeur-premier exemple d'algorithme de recherche.

Le schéma 2.4 la profondeur-première recherche de montux résulte using notre applet de


recherche avec Chicago comme noeud de début et Rochester comme destination.

Amélioration de la Profondeur-Première recherche

Une manière simple de faire avancer les meilleures caractéristiques du profondeur-premier


algorithme de recherche les avantages de la recherche en largeur est d'employer une technique
LY_SII __ 51 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

appelée la recherche de itératif-approfondissement. Dans cette approche, nous ajoutons


d'abord une légère modification au profondeur-premier algorithme de recherche, en ajoutant
un paramètre appelé le maxDepth, pour limiter notre recherche à une profondeur maximum de
l'arbre. Alors nous ajoutons une boucle d'avertissement où nous approfondissons
continuellement notre profondeur-première recherche jusqu'à nous trouvons la solution.

Cet algorithme, comme la recherche en largeur standard, est une recherche complète et
trouvera une solution optimale, mais il a des conditions de mémoire beaucoup inférieure,
comme le profondeur-premier algorithme. Bien que nous retracions la terre quand nous
augmentons notre profondeur de recherche, cette approche est toujours plus efficace que
profondeur-première la recherche illimitée en largeur ou pure pure des grands espaces de
recherche (Russell et Norvig 1995).

Notre exécution de Java emploie deux méthodes. La méthode d'iterDeepSearch () exécute la


boucle externe, appelle à plusieurs reprises le depthLimitedSearch () avec le maxDepth
augmentant d'un pour chaque appel successif. La méthode de depthLimitedSearch () est
essentiellement identique comme notre profondeur-premier algorithme de recherche standard,
avec l'essai contre le maxDepth pour court-circuiter l'expansion de SearchNode une fois
qu'ils deviennent trop profonds.

// this is a slightly modified depth-first search algorithm


// that stops searching at a pre-defined depth
public SearchNode depthLimitedSearch(SearchNode initialNode,
Object goalState,
int maxDepth)
{
Vector queue = new Vector() ;
queue.addElement(initialNode) ;
initialNode.setTested(true) ; // only test each node once

while (queue.size()> 0) {
SearchNode testNode = (SearchNode)queue.firstElement() ;
queue.removeElementAt(0) ;
testNode.trace() ;
if (testNode.state.equals(goalState)) return testNode ;

// limit the depth of search to maxDepth


if (testNode.depth < maxDepth) {
if (!testNode.expanded) {
testNode.expand(queue,SearchNode.FRONT) ;
}
}
}
return null ;
}

// use depth-first search to find goal


public SearchNode iterDeepSearch(SearchNode startNode,
Object goalState) {
int maxDepth = 10 ; // arbitrary limit
for (int j=0 ; j < maxDepth ; j++) {
reset() ;
SearchNode answer = depthLimitedSearch(startNode,goalState,j);
if (answer != null) return answer;
}
return null ; // failed to find solution in maxDepth
LY_SII __ 52 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Sur le schéma 2.5 nous montrons une trace d'une recherche de Chicago à Rochester using
l'algorithme de réitérer-approfondissement. Notification comment l'algorithme récupère la
terre pendant qu'il va à chaque niveau.

Recherche heuristique

Le problème de représentant de commerce, où un vendeur fait une excursion complète des


villes sur son itinéraire, visitant chaque ville exactement une fois, tout en voyageant la
distance la plus courte possible, est un exemple d'un problème qui a une explosion
combinatoire. En soi, il ne peut pas être résolu using la recherche en largeur ou profondeur-
première des problèmes d'aucune taille réaliste. Le TSP appartient à une classe des problèmes
connus sous le nom de NP-durs ou NP-complets. Malheureusement, il y a beaucoup de
problèmes qui ont cette forme et qui sont essentiellement insurmontables (elles ne peuvent pas
être résolus). Dans ces cas, la conclusion de la meilleure réponse n'est pas informatique
faisable, et ainsi nous devons arranger pour une bonne réponse. Dans cette section nous
discutons plusieurs méthodes de recherche heuristique qui essayent de fournir des moyens
pratiques pour approcher ces genres de problèmes de recherche.

Le schéma 2.5 exemple de Réitérer-approfondissement d'algorithme de recherche.

Des méthodes de recherche heuristique sont caractérisées par ce sens que nous avons un
temps et un espace limités dans lesquels pour trouver une réponse aux problèmes complexes
et ainsi nous sommes disposés à accepter une bonne solution. En soi, nous appliquons
l'heuristique ou les principes de base pendant que nous recherchons l'arbre pour essayer de
déterminer la probabilité que suivre un chemin ou des autres est pour mener à une solution.
Noter ceci est dans le contraste radical aux méthodes de force brutale indépendamment des
lesquelles souffler le long joyeux si une solution est n'importe où en vue.

Fonctions objectives d'utilisation de méthodes de recherche heuristique appelées (surprise !)


fonctions heuristiques à essayer de mesurer la valeur d'un noeud particulier dans l'arbre de
recherche et d'estimer la valeur de suivre en bas des chemins l'uns des du noeud. Dans les
prochaines sections nous décrivons quatre types d'algorithmes de recherche heuristique.

Produire et examiner du produire et l'algorithme d'essai est la fonction la plus fondamentale


de recherche heuristique. Les étapes sont :

1. Produire d'une solution possible, un nouvel état ou un chemin par l'espace de


problème.
2. Examiner pour voir si le nouveau état ou chemin est une solution en la comparant à
un ensemble d'états de but.
3. Si une solution a été trouvée, renvoyer le succès ; retour d'autre à l'étape 1.

C'est un profondeur-premier procédé de recherche qui exécute une recherche approfondie de


l'espace d'état. Si une solution est possible, l'algorithme de produire et d'essai la trouvera.
Cependant, cela peut prendre un moment extrêmement bon. Pour de petits problèmes, se
LY_SII __ 53 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

produire et l'essai peut être un algorithme efficace, mais pour de grands problèmes, la
stratégie de recherche non dirigée mène aux temps d'exécution prolongés et est impraticable.
La faiblesse principale de se produisent et l'essai est que nous n'obtenons aucune rétroaction
sur laquelle direction à rechercher. Nous pouvons considérablement améliorer cet algorithme
en fournissant la rétroaction par l'utilisation des fonctions heuristiques.

S'élever de colline est un algorithme amélioré de produire-et-essai, où la rétroaction des essais


sont employées pour aider direct la génération (et l'évaluation) de nouveaux états de candidat.
Quand un état de noeud est évalué par la fonction d'essai de but, une mesure ou une
évaluation de la distance à l'état de but est également calculée. Un problème avec la recherche
s'élevante de colline est en général que l'algorithme peut se faire attraper dans des minimum
ou des maximum locaux. Puisque nous allons toujours dans la direction de moindre coût, nous
pouvons suivre un chemin jusqu'à une solution localement bonne, tout en manquant la
solution globalement excellente disponible juste quelques noeuds loin. Une fois au dessus de
la solution localement la meilleure, le déplacement à n'importe quel autre noeud mènerait à un
noeud avec la qualité inférieure. Une autre possibilité est qu'un plateau ou une tache plate
existe dans l'espace de problème. Une fois que l'algorithme de recherche obtient jusqu'à ce
secteur que tous les mouvements auraient la même qualité et ainsi le progrès serait arrêté.

Pour on a proposé éviter d'obtenir a emprisonné dans les états suboptimaux, variations sur la
stratégie s'élevante de colline. On est d'injecter le bruit dans la fonction d'évaluation, avec la
haute initiale de niveau de bruit et diminuer lentement avec le temps. Cette technique, appelée
le recuit simulé, permet à l'algorithme de recherche d'aller dans les directions qui ne sont pas
« les meilleures » mais de permettre une exploration plus complète de l'espace de recherche.
Le recuit simulé est analogue au recuit des métaux, par lequel elles soient heated et alors
graduellement refroidies. Ainsi un paramètre de la température est employé dans le recuit
simulé, où une température laisse plus de recherche, mais pendant que la température se
refroidit, l'algorithme de recherche retourne au comportement s'élevant de colline plus
standard (Kirkpatrick, Gelatt, et Vecchi 1983).

Dans la prochaine section nous décrivons la meilleur-première recherche, qui est des
spécifications plus formelles d'un avide, type s'élevant algorithme de colline de recherche.

La Meilleur-première recherche de la Meilleur-Première recherche est une stratégie


systématique de commande, combinant les forces de la recherche en largeur et profondeur-
première dans un algorithme. La différence principale entre la meilleur-première recherche et
les techniques de recherche de force brutale est que nous nous servons d'une évaluation ou
d'une fonction heuristique pour commander les objets de SearchNode sur la file d'attente. De
cette façon, nous choisissons le SearchNode qui semble être le meilleur, avant tous les autres,
indépendamment de leur position dans l'arbre ou le graphique. Notre exécution de la Meilleur-
première recherche emploie la valeur courante du membre de données concernant les coûts
pour commander les objets de SearchNode sur la file d'attente de recherche. Dans notre
méthode de testGraph () ci-dessus, nous avons placé le coût des noeuds pour être égaux à la
distance à ligne directe approximative de chaque ville vers Rochester, Minnesota.

Les paramètres de bestFirstSearch () sont le SearchNode initial et l'état de but. Pour


commencer, nous instancions notre file d'attente des noeuds pour rechercher, et ajoutons
l'initialNode à la file d'attente et le marquons comme examiné.
LY_SII __ 54 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Dans une boucle de moment (), nous vérifions d'abord pour voir s'il y a plus de noeuds à
augmenter. Si oui, nous enlevons le premier noeud, appelons la trace () pour imprimer un
message, alors examinons le noeud pour voir s'il assortit notre état de but. Bien que dans notre
applet d'exemple nous employions des cordes pour représenter notre état de but, ce code
devrait fonctionner pour n'importe quel objet d'état l'a fourni applique la méthode d'égales ().
Si l'essai de but réussit, nous retournons avec le noeud qui a assorti l'état de but. Si l'essai de
but échoue et nous n'avons pas déjà augmenté ce noeud, nous l'augmentons en plaçant tous les
noeuds qu'il a des liens à sur l'avant de la file d'attente. Quand la file d'attente est vide nous
sortons la boucle de moment ().

// use best-first search algorithm to find the goal


// default implementation based on SearchNode cost
public SearchNode bestFirstSearch(SearchNode initialNode,
Object goalState)
{
Vector queue = new Vector() ;
queue.addElement(initialNode) ;
initialNode.setTested(true) ; // only test each node once

while (queue.size()> 0) {
SearchNode testNode = (SearchNode)queue.firstElement() ;
queue.removeElementAt(0) ;
testNode.trace() ;
if (testNode.state.equals(goalState)) return testNode ;

// now, heuristically add nodes to queue


// insert the child nodes according to cost
if (!testNode.expanded) {
testNode.expand(queue,SearchNode.INSERT) ;
}
}
return null ;
}

Le schéma 2.6 montre un exemple du meilleur-premier algorithme de recherche appliqué au


problème de trouver le meilleur itinéraire à partir de Chicago vers Rochester, manganèse.

La recherche avide de recherche avide est une meilleur-première stratégie où nous essayons
de réduire au minimum le coût estimatif pour atteindre le but (certainement une approche
intuitive !). Puisque nous sommes avides, nous augmentons toujours le noeud on estime que
qui est le plus proche de l'état de but. Malheureusement, le coût exact d'atteindre l'état de but
habituellement ne peut pas être calculé, mais nous pouvons l'estimer en employant une
estimation des coûts ou une fonction heuristique h (). Quand nous examinons le noeud n, alors
h (n) nous donne le coût estimatif du chemin le meilleur marché de l'état du n à l'état de but.
Naturellement, le meilleur une évaluation h () donne, le meilleur et plus rapide nous trouvera
une solution à notre problème. La recherche avide a le comportement semblable à la
profondeur-première recherche. Ses avantages sont fournis par l'intermédiaire de l'utilisation
d'une fonction heuristique de qualité de diriger la recherche.

La recherche une d'A* des algorithmes de recherche les plus célèbres utilisés dans l'AI est
l'algorithme de recherche d'A*, qui combine l'algorithme de recherche avide pour l'efficacité
avec la recherche d'uniforme-coût de l'optimalité et de la perfection. Dans A* la fonction
d'évaluation est calculée en ajoutant les deux mesures heuristiques ; le h (n) estimation des
LY_SII __ 55 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

coûts de traversée de n à l'état de but, et g (n) qui est le chemin connu coûter dès le début le
noeud à n dans une fonction appelée le f (n).

Le schéma 2.6 Meilleur-premier exemple d'algorithme de recherche.

Encore, il n'y a rien vraiment nouveau ici d'un point de vue d'algorithme de recherche ; tout
que nous faisons emploie la meilleure information (heuristique) pour évaluer et commander
les noeuds sur notre recherche aligner. Nous savons combien cela a coûté d'obtenir où nous
sommes (le noeud n) et nous pouvons guesstimate combien cela coûtera d'atteindre le but du
N. De ce fait nous apportons toute les information au sujet du problème que nous pouvons
concerner diriger notre recherche. Cette combinaison des stratégies s'avère fournir à A* la
perfection et l'optimalité.

La satisfaction de contrainte une autre approche à la résolution des problèmes using la


recherche s'appelle la satisfaction de contrainte. Tous les problèmes ont quelques contraintes
qui définissent ce que sont les solutions acceptables. Par exemple, si notre problème est de
charger un camion de livraison avec des paquets, une contrainte peut être que le camion tient
seulement 2000 livres. Cette contrainte pourrait nous aider sensiblement à réduire notre
espace de recherche en ignorant les itinéraires de recherche qui contiennent un ensemble
d'articles qui pèsent plus de 2000 livres. La recherche de satisfaction de contrainte emploie un
ensemble de contraintes pour définir l'espace des solutions acceptables. Plutôt qu'un essai
simple de but, la recherche est complète quand nous avons un ensemble d'attaches des valeurs
aux variables, un état où l'ensemble minimum de contraintes se tient.

Le rôle des contraintes est de lier des variables aux valeurs ou de limiter la gamme des valeurs
qu'elles peuvent prendre, de ce fait réduisant le nombre de combinaisons que nous devons les
explorer. D'abord, un premier ensemble de contraintes sont appliqué et propagé par le
problème, sujet aux dépendances qui existent. Par exemple, si nous fixons une limite de 2000
livres pour un camion de livraison particulier, qui peut immédiatement enlever quelques
articles de la considération. En outre, une fois que nous assignons un objet au camion qui pèse
800 livres, nous pouvons impliquer que maintenant la limite pour les articles additionnels est
de 1200 livres. Si une solution n'est pas trouvée en propageant les contraintes, alors la
recherche est exigée. Ceci peut impliquer de faire marche arrière ou défaire un transfert ou
une attache variable.

L'analyse Means-ends d'analyse Means-Ends est un processus pour la résolution des


problèmes qui est basée sur détecter des différences entre les états et puis essayer de réduire
ces différences. D'abord utilisé dans le Général solutionneur de problèmes (Newell et Simon
1963), l'analyse means-ends emploie en avant et raisonnement en arrière et un algorithme
récursif pour réduire au minimum systématiquement les différences entre l'initiale et les états
de but.

Comme n'importe quel algorithme de recherche, l'analyse means-ends a un ensemble d'états


avec un ensemble d'opérateurs qui transforment cet état. Cependant, les opérateurs ou les
règles pour la transformation ne spécifient pas complètement avant et après des états. L'aile
gauche ou l'antécédent de la règle contient seulement le sous-ensemble de conditions, les
conditions préalables, qui doivent être vraies pour que l'opérateur soit appliqué. De même le
LY_SII __ 56 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

côté droit de la règle identifie seulement ces parties de l'état que l'opérateur change. Tellement
chaque opérateur a a avant et après la liste d'états et de changements d'état qui sont seulement
un sous-ensemble de l'espace entier de problème. Une version simplifiée de l'analyse means-
ends est décrite ici (riches et chevalier 1991).

Analyse Means-Ends (Courant-État, But-État)

1. Comparer le courant-état au but-état. Si les états sont puis succès de retour


identique.
2. Choisir la différence la plus importante et la réduire en exécutant les étapes
suivantes jusqu'au succès ou à l'échec :
a. Choisir un opérateur qui s'applique à la différence courante. S'il n'y a aucun
opérateur qui peut être appliqué, alors renvoyer l'échec.
b. Essayer d'appliquer l'opérateur à l'état actuel en produisant de deux états
provisoires, un où les conditions préalables de l'opérateur sont vraies (prestate), et un
qui seraient le résultat si l'opérateur étaient appliqués à l'état actuel (poststate).
c. Diviser le problème en deux parts, une PREMIÈRE partie, du courant-état au pré-
état, et à une DERNIÈRE partie, du poststate à l'état de but. Appeler l'analyse means-
ends pour résoudre les deux morceaux. Si tous les deux sont vrais, alors renvoyer le
succès, avec la solution comprenant la PREMIÈRE partie, l'opérateur choisi, et la
DERNIÈRE cloison.

L'analyse Means-ends est un algorithme de recherche heuristique puissant. Elle a été


appliquée dans beaucoup d'applications d'intelligence artificielle, particulièrement dans des
systèmes de planification.

Résumé

En ce chapitre nous avons présenté les algorithmes de recherche principaux utilisés dans des
applications d'intelligence artificielle. Les points principaux incluent :

• L'approche de l'espace d'état à la représentation de problème exige une cartographie


du problème réel à une structure de données (l'état), un premier état, un ensemble
d'opérateurs pour changer l'état, et définir un essai de but pour déterminer quand nous
avons atteint un état de but. Un coût peut également être associé à un état produit.
• Les algorithmes de recherche efficaces doivent causer le mouvement systématique
par l'espace d'état. Les algorithmes de recherche de force brutale recherchent
aveuglément l'espace d'état, alors que les algorithmes de recherche heuristique
emploient la rétroaction ou les informations sur le problème pour diriger la recherche.
• Un algorithme de recherche est optimal si on le garantit de trouver la meilleure
solution d'un ensemble de solutions possibles. Un algorithme est complet s'il trouvera
toujours une solution si on existe. La complexité de temps définit comment rapidement
un algorithme exécute et des balances. La complexité de l'espace décrit de combien de
mémoire l'algorithme exige pour exécuter la recherche.
• La recherche en largeur est un algorithme complet avec du temps et la complexité
exponentiels de l'espace. Elle examine chaque étape de l'état un à partir de l'état initial,
puis chacun deux étapes loin, et ainsi de suite.
• la Profondeur-première recherche a des conditions de mémoire inférieure que la
recherche en largeur, mais elle n'est ni complète (elle peut devenir stuck dans les
LY_SII __ 57 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

boucles) ni optimale. Dans - profondeur - la première recherche, un chemin simple est


choisi et suivi jusqu'à ce qu'une solution soit trouvée, ou un cul-de-sac (un noeud de
feuille) est atteint. L'algorithme alors soutient et continue en bas d'un autre chemin.
• la recherche de Réitérer-approfondissement emploie une version modifiée de la
profondeur-première recherche pour limiter la profondeur de la recherche. Elle fait
ceci avec une boucle d'avertissement qui augmente la profondeur sur chaque itération.
L'approche combine le meilleur de la recherche en largeur et meilleur-première. Elle
est complète et optimale, et elle a des conditions de mémoire beaucoup inférieure que
la profondeur-première recherche illimitée.
• Informations d'utilisation d'algorithmes de recherche heuristique sur le problème
pour aider direct le chemin par l'espace de recherche. Cette information est employée
pour choisir qui des noeuds à augmenter. la Meilleur-première recherche augmente
toujours le noeud qui regarde pour être le plus proche de la solution. La recherche
d'A* emploie le coût connu combiné avec une évaluation de la distance de l'état au but
pour choisir un noeud pour augmenter. A* est complet et optimal, et a des conditions
de mémoire inférieure semblables à la profondeur-première recherche.
• La recherche de satisfaction de contrainte emploie des informations sur les états
valides pour aider à limiter ou contraindre la gamme de la recherche. L'analyse
Means-ends résout des problèmes en détectant des différences entre les états et puis en
essayant de réduire ces différences.

Exercices

1. Ouvrir votre visionneuse Java-permise de web browser ou d'applet sur le dossier de


SearchApplet.html. Tout en se rapportant au schéma 2.1, choisir deux villes et courir
les profondeur-premiers, en largeur, et réitérer-approfondissants algorithmes de
recherche. Quel algorithme trouve la solution dans le moindre temps ? Pourquoi ? Ce
qui si vous choisissiez deux autres villes ?
2. En ce chapitre nous avons développé une version du meilleur-premier algorithme
de recherche, où le coût estimatif du noeud courant à l'état de but était la distance à
ligne directe au but. L'algorithme de recherche d'A* combine ce coût estimatif avec le
noeud connu courant de coût dès le début au noeud courant. Prolonger la meilleur-
première exécution pour exécuter la recherche d'A*. Comment compare-t-elle au
meilleur-premier ?
3. Que l'inconvénient principal à la résolution des problèmes rechercher-basée est-il
approche ? Comment l'aide d'heuristique surmontent-elles cette faiblesse ? Comment
la recherche de satisfaction de contrainte surmonte-t-elle cette faiblesse ?
LY_SII __ 58 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Chapitre 3
Représentation du Knowledge
En ce chapitre nous explorons certaines des techniques employées pour
représenter la connaissance de domaine dans des programmes d'intelligence
artificielle. Nous commençons par un examen de plusieurs genres de
connaissance et de différentes demandes que place de personnes et d'ordinateurs
sur des représentations de connaissance. Nous décrivons les représentations de
connaissance procédurales et déclaratives et suivons avec une introduction à la
logique propositionnelle et d'attribut. Après nous discutons des armatures et les
filets sémantiques, deux ont rapporté des techniques d'intelligence artificielle
pour représenter des concepts et leurs rapports. Le format d'échange de la
connaissance est présenté comme standard industriel naissant pour la
représentation de connaissance. En conclusion, nous discutons le procédé
d'acquisition de la connaissance, où la connaissance experte de domaine est
transformée en base de connaissance pour la résolution des problèmes.

De la connaissance à la représentation de connaissance

Quelle est la connaissance ? La connaissance est-elle la même chose que des faits ? Le
dictionnaire de Webster (Merriam-Webster 1988) définit la connaissance comme « fait ou état
de savoir quelque chose avec la connaissance gagnée par l'expérience ou l'association. » La
connaissance de gain de personnes à travers qu'expérience-ils voient, entendent, touchent, se
sentent, et goûtent le monde autour de elles. Nous pouvons associer quelque chose que nous
voyons avec quelque chose nous entendons, gagnant de ce fait de nouvelles connaissances au
sujet du monde. Une définition alternative pour la connaissance est « le fait ou l'état de se
rendre compte de quelque chose. » Comment mettons-nous au courant un ordinateur de
quelque chose ? Supposer que nous savons que le soleil est chaud, des boules sommes en
rond, et le ciel est bleu. Ces faits sont la connaissance au sujet du monde. Comment stockons-
nous cette connaissance dans notre cerveau ? Comment pourrions-nous stocker cette
connaissance dans un ordinateur ? Ce problème, appelé la représentation de connaissance, est
un du premier, la plupart des issues fondamentales aux lesquelles les chercheurs en
intelligence artificielle ont dû faire face. Et la réponse qu'ils ont trouvée était des symboles.

Tandis que les psychologues et les neurologistes recherchent toujours la réponse à la façon
dont la connaissance de magasin de personnes dans leurs cerveaux (nous savons elle a
quelque chose faire avec les synapses), dans le domaine de l'intelligence artificielle, des
programmeurs emploient des symboles pour représenter et manoeuvrer la connaissance dans
des ordinateurs. Quel est un symbole ? Un symbole est une chaîne de nombre ou de caractères
qui représente un objet ou une idée. Des cordes et des nombres sont employés parce que les
ordinateurs sont très bons pour les traiter. Ceci s'appelle la représentation interne de la
connaissance. Cependant, les gens sont les plus confortables using un de langage naturel
comme l'anglais pour représenter la connaissance. Ainsi, pour des raisons pratiques, nous
avons besoin des tracés des faits à une représentation d'ordinateur interne et également à une
forme que les gens peuvent comprendre.
LY_SII __ 59 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Cependant de langage naturel est peut-être la représentation de connaissance la plus


facilement compréhensible pour des personnes, il n'est certainement pas le meilleur pour des
ordinateurs, parce que de langage naturel est en soi ambigu. C'est-à-dire, deux personnes
peuvent lire le même rapport et être en désaccord quant à ce que signifie il. Cette ambiguïté
est exactement pourquoi les langues des mathématiques et de la logique formelles ont été
développées pendant les derniers deux milléniums. Il devrait n'être aucune surprise que
l'intelligence artificielle a été appliquée la première fois aux langages formels et au théorème
mathématique s'avérant, et qu'une des premières représentations de connaissance était logique
formelle. Pour ces mathématiciens qui étaient les premiers informaticiens, la logique était une
langue « normale » à employer.

Il y a beaucoup de différents genres de connaissance que nous pouvons vouloir pour


représenter : simples faits ou rapports complexes, formules mathématiques ou règles pour la
syntaxe de langage naturel, associations entre les concepts relatifs, hiérarchies de transmission
entre les classes des objets. Car nous montrerons, chaque type de connaissance place des
conditions spéciales sur la compréhension humaine et la manipulation d'ordinateur. La
représentation de connaissance n'est pas une proposition one-size-fits-all. En conséquence, le
choix d'une représentation de connaissance pour n'importe quelle application particulière
implique des différences entre les besoins des personnes et les ordinateurs. En plus d'être
facile à utiliser, une bonne représentation de connaissance doit également être facilement
modifiée et prolongée, en changeant la connaissance manuellement ou par des techniques
d'étude automatiques de machine. Regardons quelques genres communs de connaissance et
les approches les plus populaires pour stocker cette connaissance dans des ordinateurs.

Représentation procédurale

Peut-être la technique la plus commune pour représenter la connaissance dans des ordinateurs
est la connaissance procédurale. Le code procédural code non seulement des faits (des
constantes ou des variables attachées) mais définit également l'ordre des opérations pour
employer et manoeuvrer ces faits. Ainsi, le code de programme est une manière parfaitement
normale de coder la connaissance procédurale. Si des structures ou les objets de données sont
employés pour modeler le problème, le programme est essentiellement une grande
représentation de connaissance. Les programmes écrits en langues scripting telles que Visual
Basic, Javascript, et LotusScript sont des exemples d'une représentation de connaissance
procédurale. La connaissance de la façon traiter des données est codée dans les structures de
gestion et l'ordre des rapports de programme. Cette logique « hardcoded » n'est pas
typiquement considérée une partie d'AI intrinsèquement, mais peu de vrais programmes d'AI
existent qui ne contiennent pas une certaine quantité de code de commande procédural.

En code procédural, la connaissance et la manipulation de cette connaissance sont


inextricablement liées. Cette faiblesse est surmontée par l'approche la plus populaire de
représentation de connaissance, appelée déclarative. Dans la représentation de connaissance
déclarative, un utilisateur énonce simplement des faits, des règles, et des rapports. Ces faits,
règles, et rapports se tiennent prêt eux-mêmes et représentent la connaissance pure. La plupart
des techniques de représentation de connaissance étudiées en intelligence artificielle et
discutées dans le reste de ce chapitre sont déclaratives. Cependant, la connaissance déclarative
doit être traitée par un certain code procédural, ainsi nous n'obtenons trop jamais loin du
besoin d'instructions séquentielles explicites pour que l'ordinateur suive. Toujours, la
séparation de la connaissance de l'algorithme employé pour manoeuvrer ou raisonner avec
LY_SII __ 60 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

cette connaissance fournit des avantages par rapport au code procédural. Puisque la
connaissance est explicitement représentée, elle peut plus facilement être modifiée. En outre,
la séparation de la logique de commande et des algorithmes de motif de la connaissance nous
permet d'écrire des procédures inferencing optimisées et réutilisables.

Représentation apparentée

Une autre manière de représenter l'information est en forme apparentée, comme cela utilisé
dans des systèmes de base de données relationnelle. Les bases de données relationnelles
fournissent un mécanisme puissant et flexible pour stocker la connaissance, qui est pourquoi
elles presque totalement ont assuré les affaires de stocker l'information dans des systèmes
économiques commerciaux. La connaissance est représentée par des tuples ou des disques
d'informations sur un article, avec chaque tuple contenant un ensemble de champs ou
colonnes définissant des attributs de détail et des valeurs de cet article. En stockant une
collecte d'informations dans une table, nous pouvons employer le calcul apparenté pour
manoeuvrer les données, basées sur les relations définies, et questionnons l'information
stockée dans la table. Le langage d'interrogation structuré est la langue la plus populaire pour
manoeuvrer des données apparentées.

Tandis que les tables de base de données relationnelle sont flexibles, elles ne sont pas bonnes
pour représenter des rapports complexes entre les concepts ou les objets dans le monde réel.
C'est où les systèmes de réseau et de base de données hiérarchique, tels que l'IMS d'IBM, sont
forts. Avoir des liens ou des indicateurs entre les groupes relatifs de données permet les
graphiques hiérarchiques et complexes de réseau à construire. Les techniques IA Des filets
sémantiques et des armatures, discutées en plus détail plus tard en ce chapitre, emploient une
approche semblable pour représenter la connaissance.

Représentation hiérarchique

Un autre type de connaissance est la connaissance dont on peut hériter, qui porte sur des
rapports et des attributs partagés entre les genres ou les classes d'objets. La connaissance
hiérarchique mieux est employée pour représenter des rapports de « AIS », où un type général
ou abstrait (par exemple, boule) est lié à des types plus spécifiques (le caoutchouc, golf, base-
ball, football) qui héritent des propriétés de base du type général. La force de la transmission
d'objet tient compte de la représentation compacte de la connaissance et permet à des
algorithmes de raisonnement de traiter à différents niveaux d'abstraction ou de granularité.
Nous pourrions raisonner au sujet des sports et des attributs communs des boules à un niveau,
ou nous pourrions fouiller dans les détails d'un sport particulier et son type respectif de boule.
L'utilisation des catégories ou des types donne la structure au monde en groupant les objets
semblables ensemble. Using des catégories ou des faisceaux simplifie le raisonnement en
limitant le nombre de choses distinctes que nous devons traiter. Une taxonomie ou une
hiérarchie des objets ou des concepts est une manière utile d'organiser des collections de
catégories, parce qu'elle nous permet de réduire la complexité et de penser à des niveaux plus
élevés d'abstraction si possible.

Using des objets modeler le monde et représenter la connaissance devient de plus en plus
populaire. En plus des bases de données réseau relationnelles et, des bases de données d'objet
LY_SII __ 61 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

qui stockent des données d'objet et les méthodes maintenant sont déployées. Sans compter que
tracer naturellement sur le monde réel, des objets peuvent également être employés pour
modeler des idées abstraites et leurs rapports. Les langages de programmation orientée
objectivement tels que la causerie, le C++, et le Java fournissent un cadre normal pour
représenter la connaissance comme objets, et pour raisonner environ et manoeuvrer ces objets.
Les hiérarchies de transmission et de classe sont des concepts fondamentaux sur lesquels des
programmes objets sont établis, et viennent fondamentalement « pour libre » avec les
représentations de connaissance orientées objectivement.

Capturant la connaissance au sujet des objets dans le monde réel et des mesures nonphysical
telles que le temps sont souvent exigés dans la résolution des problèmes d'AI. Savoir quoi
prévoir a basé sur le temps écoulé d'un événement à l'autre est souvent le cachet du
comportement intelligent. Savoir qu'un ami a juste jeté une boule de neige à votre tête serait la
connaissance utile de sorte que vous ayez pu attacher pour l'impact possible. Connaître quel
ami a jeté la boule de neige pourrait vous aider à déterminer la probabilité de l'obtention
frappée du tout. Mais cette connaissance s'appliquerait seulement pendant une courte période
(2-5 secondes après l'événement de lancement). Les concepts de temps comme avant, après,
et pendant sont cruciaux au raisonnement et à la planification common-sense. Quand nous
essayons de résoudre un problème, nous feignons souvent que le « temps se tient toujours »
tandis que nous faisons notre calcul. Cependant, dans beaucoup de problèmes nous devons
explicitement traiter des changements, dus au dépassement du temps, ou au mouvement des
objets du monde. Des formulaires spéciaux de la logique, appelés la logique temporelle, ont
été développés pour traiter la représentation et des motifs au sujet du temps.

Bien qu'il y ait autant de différentes manières de représenter la connaissance car il y a des
types de connaissance, seulement une poignée de représentations de connaissance sont
employée couramment dans des applications intelligentes artificielles. Dans le reste de ce
chapitre, nous explorons la logique formelle, les armatures, et les filets sémantiques, alors que
dans le prochain chapitre nous explorons les représentations de connaissance basées sur les
règles. Nous commençons notre discussion avec la logique d'attribut.

Logique d'attribut

L'utilisation de la logique formelle comme représentation de connaissance primaire harkens


de nouveau aux commencements de la recherche en matière d'intelligence artificielle. La
déduction mathématique, basée sur la logique, était une méthode bien connue de produire de
nouvelles connaissances de la connaissance existante. Les premiers chercheurs d'AI ont
employé, donc, ce qu'ils ont su. Tandis qu'un examen détaillé de la logique formelle est en
dehors de la portée de ce livre, nous donnerons une brève introduction à cette matière.

La logique formelle est une langue avec sa propre syntaxe, qui définit comment faire des
phrases, et sémantique correspondante, qui décrit la signification des phrases. La forme la
plus fondamentale de représentation de logique s'appelle logique booléenne ou
propositionnelle, où chaque proposition ou fait est représentée par un symbole qui évalue à
vrai ou à faux. Des phrases peuvent être construites using les symboles de proposition (P, Q,
R,…) et opérateurs booléens, tels que la conjonction (et), disjonction (ou), implication (P
implique Q), et équivalence (A est équivalent à B). Using cette syntaxe simple, nous
pouvons écrire des implications ou ordonnons, comme (P et Q) implique R ou, plus
programmatically si P et Q puis R. Dans la règle précédente, P et Q s'appelle les lieux ou
LY_SII __ 62 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

antécédent, et R est la conclusion ou le conséquent. En plus des propositions de jointure avec


des liaisons, une proposition peut être niée (pas) de sorte que si P est vraie, puis pas P est
faux. Des règles communes de l'inférence peuvent être employées pour raisonner et pour
impliquer des faits des phrases de logique propositionnelle. Un du plus familier s'appelle
Modus Ponens, où donné une règle, A implique B, et la connaissance que la phrase
antécédente A est vraie, nous pouvons impliquer que la phrase B est également vraie.

La logique booléenne est employée comme base pour concevoir les calculateurs numériques
et est tout adaptée et puissante pour la conception de circuit. Cependant, elle manque
rapidement de vapeur comme langue de représentation de connaissance. Ainsi, la logique
d'attribut, qui permet à des attributs sur des objets de définir des attributs et des relations
entre les objets, est devenue la logique preferred pour la représentation de connaissance dans
des systèmes d'intelligence artificielle. Using des objets, des attributs, et des relations, nous
pouvons représenter presque n'importe quel type de connaissance. En outre, la logique
d'attribut présente le concept des quantifiers, qui nous permettent de nous référer à des
ensembles d'objets. Les deux quantifiers sont existentiels (là existe un certain objet qui a
l'attribut spécifique) et universel (tous les objets de ce type ont cet attribut). Un rapport tel que
le « Minnesota est froid en hiver. » a pu être représenté dans la logique d'attribut de
plusieurs manières. Nous pourrions employer une conjonction des fonctions (dire que jeûnent
trois fois), où les fonctions sont des relations avec un paramètre simple, en tant qu'en place (le
Minnesota) et la température (froide) et la saison (hiver). Ou nous pourrions employer une
relation simple, telle que le froid (Minnesota, hiver). Ou nous pourrions même dire l'hiver
(Minnesota, froids) pour représenter le même rapport.

De ces exemples, vous pouvez voir que la logique d'attribut ne donne aucun conseil quant à
quels attributs nous devrions employer. Ils également n'indiquent pas explicitement comment
le temps ou les événements devrait être représenté. Ce n'est pas de dire que nous ne pouvons
pas représenter ces types de connaissance dans la logique d'attribut. Il peut être fait. La
question principale est de comprendre que même avec la syntaxe et la sémantique définies,
nous avons toujours beaucoup de décisions à faire dans la façon dont la connaissance est
représentée par des attributs.

Mais avoir une bonne représentation de connaissance résout seulement la moitié du problème,
parce que nous devons également manoeuvrer la connaissance pour produire de nouveaux
faits et pour prouver ou réfuter des affirmations sur la connaissance. Malheureusement, la
logique d'attribut ne fournit pas une manière sure-fire de dériver la nouvelle information.
Cependant, elle peut encore être employée pour traiter la connaissance d'une manière utile.
Deux techniques, appelées résolution et l'unification sont employées pour traiter des rapports
d'attribut pour prouver si un rapport particulier est vrai ou pas, basé sur les autres faits connus.
Ensemble ces algorithmes forment la base pour le Prolog (programmant dans la logique). Le
Prolog et blèsent sont les deux langages de programmation traditionnellement utilisés pour
des applications d'intelligence artificielle (Clocksin et Mellish 1981). Tandis que nous
n'entrerons pas dans des détails ici, un arrangement de base des possibilités et des limitations
de la logique d'attribut comme base pour le raisonnement est nécessaire. Notre discussion se
concentrera sur deux mécanismes, résolutions et unifications de base.

Résolution
LY_SII __ 63 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

La résolution est un algorithme pour prouver des faits vrais ou faux en vertu de la
contradiction (Robinson 1965). Si nous voulons nous avérer qu'un théorème X est vrai, nous
devons prouver que la négation de X n'est pas vraie. Par exemple, supposer que nous savons
les deux faits suivants :

1. pas plumes (Tweety) ou oiseau (Tweety)


2. plumes (Tweety)

La phrase 1 déclare qu'ou Tweety n'a pas des plumes ou bien Tweety est un oiseau. La phrase
2 déclare que Tweety a des plumes. Pour montrer que Tweety est un oiseau, nous ajoutons
d'abord une prétention qui est la négation de cet attribut, donnant la phrase 3 :

1. pas plumes (Tweety) ou oiseau (Tweety)


2. plumes (Tweety)
3. pas oiseau (Tweety)

Dans les phrases annulation de 1 et de 2, pas de plumes (Tweety) et de plumes (Tweety)


dehors. La résolution des phrases 1 et 2 produit le resolvant, la phrase 4, qui est ajoutée à
notre fait réglé :

1. pas plumes (Tweety) ou oiseau (Tweety)


2. plumes (Tweety)
3. pas oiseau (Tweety)
4. oiseau (Tweety)

Il est clair que les phrases 3 et 4 ne peut pas tous les deux être vrai, ou Tweety est un oiseau
ou il n'est pas. Ainsi, nous avons une contradiction. Nous avons juste montré que notre
première prétention, pas oiseau (Tweety), est fausse, et l'alternative, oiseau (Tweety), doit
être vraie (Winston 1993). Si les clauses à résoudre sont choisies des manières systématiques,
alors la résolution est garantie de trouver une contradiction si on existe, bien que cela puisse
prendre un bon moment de trouver.

Unification

L'unification est une technique pour prendre deux phrases dans la logique d'attribut et trouver
une substitution qui les fait regarder la même chose. C'est une condition pour prouver des
théorèmes using la résolution, comme discuté précédemment. Si deux attributs sont
identiques, alors ils s'assortissent, par définition. Si on ou tous les deux contient des variables,
alors des substitutions appropriées doivent être trouvées using l'unification comme suit :

• Une variable peut être remplacée par une constante.


• Une variable peut être remplacée par une autre variable.
• Une variable peut être remplacée par un attribut, tant que l'attribut ne contient pas
cette variable.

Etant donné l'ensemble suivant d'attributs, nous laisser explorent comment ils peuvent être
unifiés :

1. haines (X, Y)
LY_SII __ 64 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

2. haines (George, broccoli)


3. haines (Alex, épinards)

Nous pourrions unifier la phrase 2 avec 1 en liant George à X variable, et le broccoli au Y.


variable. De même, nous pourrions lier Alex à X et épinards au Y. Noter que si les noms
d'attribut étaient différents, nous ne pourrions pas unifier ces attributs. Si nous présentons
quelques plus d'attributs, nous pouvons explorer des unifications plus complexes :

4. haines (X, légume (Y))


5. haines (George, légume (broccoli))
6. haines (Z, broccoli)

Nous pourrions unifier la phrase 6 avec la phrase 1 en remplaçant X variable par Z variable et
Y variable avec le broccoli constant. Les phrases 4 et 5 ont pu être unifiées avec George lié à
X, et le broccoli au Y. variable.

Une version généralisée de l'algorithme d'unification, appelée l'allumette, est employée en


Prolog. Des faits sont représentés en Prolog par les clauses, qui ressemblent aux attributs
standard, et déclarent les choses qui sont sans réserve vraies. Les règles sont des clauses où la
conclusion peut être vraie, à condition que toutes les clauses dans la pièce de condition soient
vraies. Le Prolog fournit un procédé inferencing intégré, basé sur la résolution, parce que
traiter des règles et répondre à des questions posées comme clauses de but (Bratko 1986).

Tandis que des attributs peuvent être employés pour représenter et raison avec des règles, tous
les systèmes de règle n'emploient pas la logique d'attribut en tant que leur langue de
représentation de connaissance. Des systèmes basés sur les règles tôt ont été développés using
le Prolog et blèsent, mais la plupart des réalisations commerciales sont maintenant écrites
dans C et C++. En chapitre 4, nous décrirons un système inferencing basé sur les règles
développé using Java.

Vues

Une armature est une collection d'attributs qui définit l'état d'un objet et de son rapport avec
d'autres armatures (objets). Mais une armature est beaucoup plus qu'une structure juste de
disque ou de données contenant des données. Dans l'AI, des armatures s'appellent les
représentations de données de fente-et-remplisseur. Les fentes sont les valeurs de données, et
les remplisseurs sont des procédures jointes avant lesquelles s'appellent, pendant (pour
calculer la valeur de), ou après la valeur de la fente est changé. Des vues sont souvent
incorporées dans une hiérarchie pour représenter l'avoir-partie et les rapports d'AIS.

Si un ordre des actions est appliqué à une armature, alors certains des attributs changent tandis
que les la plupart restent les mêmes. Quand des ordres des opérations sont exigés, comme
dans un problème de recherche, un problème connu sous le nom de problème d'armature
surgit. Le problème est que si nous copions l'armature complète (état de l'objet) pour chaque
étape dans l'ordre, alors nous pouvons rapidement épuiser le de mémorisation par ordinateur,
car nous reproduisons la même connaissance inchangée à plusieurs reprises. Le problème
d'armature traite le problème qui quand une action se produit, il n'est pas toujours évident que
LY_SII __ 65 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

les attributs dans l'armature devraient changer. La solution est de spécifier seulement les
parties de l'état qui doit s'assortir pour qu'une condition soit vrai, et de changer seulement ces
fentes ou attributs qui changent en raison d'un opérateur.

Le schéma 3.1 montre un exemple d'une représentation d'armature pour un sous-ensemble du


domaine des véhicules. Noter que chaque armature a un ensemble de fentes, et les armatures
sont liés ensemble pour montrer leurs rapports. Par exemple, l'armature d'automobile a trois
fentes. C'est un sous-ensemble de véhicules comme indiqué par le lien d'AIS. Notre définition
indique qu'une automobile doit avoir des portes, un moteur, et quatre roues. Une voiture de
sport est un sous-ensemble ou un type d'automobile. Elle a deux portes et est petite. Noter que
la voiture de sport hérite de plusieurs attributs d'automobile. En conclusion, Corvette est un
exemple d'une voiture de sport, et chaque exemple a un nombre de permis unique.

Le schéma 3.1 exemple d'armature d'A.

À n'importe qui au courant de la programmation orientée objectivement, une armature


ressemble infiniment à d'un objet, dont les membres de données sont les fentes, et dont les
méthodes sont les procédures ou les démons joints. Dans un certain sens, n'importe quel
programme de Java est un mécanisme armature-basé pour la représentation de connaissance.
Les programmes orientés objectivement se servent également de la transmission pour des
rapports d'AIS, et la retenue ou les références pour des rapports d'avoir-partie. En fait, un
exemple d'un objet de Java avec des membres de données pour tenir l'état et les méthodes
pour examiner et changer l'état d'objet fournit une solution au problème d'armature décrit ci-
dessus.

Filets sémantiques

Des filets sémantiques sont employés pour définir la signification d'un concept par ses
rapports avec d'autres concepts. Une structure de données de graphique est employée, avec
des noeuds employés pour tenir des concepts, et des liens avec les étiquettes de langage
naturel employées pour montrer les rapports. Une partie d'une représentation de filet
sémantique du domaine de véhicule est montrée sur le schéma 3.2.

Encore, les rapports standard tels que l'AIS, l'avoir-partie, et l'exemple devraient être au
courant aux lecteurs de l'expérience de conception orientée objectivement. Une grande partie
de l'objet modelant le travail a été prévu par la recherche de filet sémantique faite dans les
années 60 (Quillian 1968). Une fois que le filet sémantique est construit, une technique
appelée l'activation de propagation est employée pour voir comment deux concepts ou
noeuds sont connexes.

Une exécution moderne d'un filet sémantique est l'utilité de la connaissance (KnU)
développée par IBM. KnU est la technologie site Web derrière Aqui'
(http://www.ibm.aqui.com). Plus que juste un filet sémantique, KnU établit un filet attribué,
où chaque utilisateur fait tracer ses propres préférences sur le filet sémantique fondamental.
Ceci permet des vues personnalisées du contenu et des concepts codés dans le filet
sémantique. Par exemple, une personne peut être au noeud de concept de Java et avoir des
liens forts aux fournisseurs gastronomes de café, alors que des autres peuvent avoir des liens
forts aux fournisseurs d'instrument de développement de Java.
LY_SII __ 66 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Tandis que non évidentes au premier regard, des armatures et les filets sémantiques sont très
étroitement liés à la logique d'attribut. Russell et Norvig (1995) fournissent un algorithme
pour transformer les deux représentations en logique de premier ordre. La différence
principale entre ces représentations de connaissance est leur syntaxe. Certains préfèrent la
logique formelle, alors que d'autres peuvent rapporter plus facilement aux représentations
graphiques.

Le schéma 3.2 exemple de filet sémantique d'A.

Représentation de l'incertitude

Dans presque aucune application réelle, un système de raisonnement n'aura pas toutes les
informations importantes qu'il doit résoudre un problème a priori. Nous avons l'incertitude.
Dans la plupart des cas, il y aura quelques informations disponibles, mais le repos devra être
impliqué ou rassemblé pendant qu'inferencing procède. Heureusement, nous avons une
théorie statistique qui fonctionne bien dans des conditions de l'incertitude, appelées théorème
de Bayes le' règle ou Bayes'.

La probabilité de quelque chose peut s'étendre d'une probabilité de 0.0 (aucune chance) à une
probabilité de 1.0 (certitude). Les statisticiens différencient entre deux genres de probabilités.
D'abord, il y a des probabilités sans conditions (ou antérieures) qui représentent la chance que
quelque chose se produira. Par exemple, nous pouvons regarder dans un almanach de temps et
voir que, en moyenne, il a plu 10 jours en mars au Minnesota au cours des cent dernières
années. Ainsi la probabilité qu'il pleuvra n'importe quel jour donné en mars est
approximativement 33 pour cent. C'est la probabilité antérieure ou sans conditions.
Cependant, supposer que nous savons qu'un grand orage souffle dedans du Dakota du Sud, et
il atteindra le Minnesota demain. Étant donné que la connaissance, nous peut indiquer il y a
un risque de 80 pour cent de pluie. La probabilité à long terme a-t-elle changé ? Non ; mais
nous avons une évidence qui demain pourrait être un jour pluvieux, ainsi nous nous servons
de cette évidence pour mettre à jour notre prévision. Les statisticiens appellent ce type
d'évaluation de probabilité une probabilité conditionnelle, exprimé comme P (H | E), celui est
lu comme probabilité de l'hypothèse H étant donné que nous avons observé l'évidence E.

Théorème de Bayes le' indique que nous pouvons calculer la probabilité conditionnelle que
l'événement Y se produira étant donné que l'événement X déjà produit, étant donné que nous
sachions les probabilités antérieures que X et Y pourraient se produire, et la probabilité
conditionnelle que X se produira quand nous savons que Y s'est déjà produit.

P (Y | X) = P (X | Y) P (Y)/P (X)

Un réseau bayésien (également appelé un réseau de croyance, réseau causal, ou le réseau


probabiliste) est une structure de données (un graphique acyclique dirigé) qui est employée
pour représenter la dépendance entre les variables. Chacun variable a un noeud correspondant
avec une table conditionnelle de probabilité définissant les rapports entre ses noeuds de
parent. L'utilisation primaire des réseaux bayésiens est d'employer la théorie des probabilités
pour raisonner avec l'incertitude.
LY_SII __ 67 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Format d'échange de la connaissance

Jusqu'ici, nous avons discuté la logique d'attribut, les armatures, et les filets sémantiques, et
avons montré comment ils peuvent représenter la connaissance. Puisque les partisans des
diverses techniques ne pourraient pas convenir sur un format de représentation de
connaissance pour leurs applications d'AI, un besoin clair a été identifié d'un langage commun
d'établir les liens.

Le format d'échange de la connaissance est une langue qui a été expressément conçue pour
l'échange de la connaissance entre les agents (Gensereth et Fikes 1992). Basé sur la logique
d'attribut, KIF est une langue flexible de représentation de connaissance qui soutient la
définition des objets, des fonctions, des relations, des règles, et de la métaconnaissance (la
connaissance au sujet de la connaissance). En ces dernières années, KIF a émergé comme la
langue preferred dans les efforts d'avoir un format standard de représentation de connaissance
pour l'usage entre une série d'agents intelligents.

La syntaxe de langue de KIF est réminiscente de blèsent, qui n'est pas étonnante donnée sa
base de logique d'attribut. À la différence de blèsent cependant, KIF ne sont pas censés être un
langage de programmation. Ni est il a signifié pour être employé comme représentation de
connaissance interne. KIF a été explicitement conçu pour fournir un format commun pour
échanger l'information. KIF est puissant et assez expressif pour soutenir les conditions d'une
large variété de programmes d'AI. Indépendamment des représentations de connaissance
internes, tant que chaque programme peut lire et écrire KIF, la connaissance est portative et
réutilisable dans beaucoup de différents contextes. KIF est formellement défini et est le
résultat de plusieurs années d'effort par la connaissance de Defense Advanced Research
Projects Agency partageant l'équipe de travail d'environnement. La syntaxe de KIF peut être
coupée en trois groupes importants : variables, opérateurs, et constantes.

KIF soutient deux types de variables, les différentes variables qui commencent par ?
caractère, et variables d'ordre qui commencent par @ un caractère. Les quatre types
d'opérateurs incluent des opérateurs de limite, des opérateurs de règle, des opérateurs de
phrase, et des opérateurs de définition. Si une marque n'est pas une variable ou un opérateur,
alors ce doit être une constante. KIF fournit des distinctions pour plusieurs différents types de
constantes. Tous les nombres, caractères, et cordes sont des constantes de base dans KIF. Les
constantes d'objet dénotent des objets, les constantes de fonction dénotent des fonctions au-
dessus des objets, les constantes de relation dénotent des relations, et les constantes logiques
expriment des conditions booléennes au sujet du monde qui doit être vrai ou faux.

La langue soutient quatre types d'expressions : limites, phrases, règles, et définitions. Les
limites dénotent des objets, les phrases représentent des faits, les règles représentent des
étapes légales d'inferencing, et des définitions sont employées pour définir des constantes. Des
phrases se composent des constantes, des limites, et d'autres phrases. KIF définit en avant (des
lieux suivis de conséquent) et des règles d'inverse (conséquent suivi des lieux).

Une forme dans KIF est une phrase, une règle, ou une définition. Et en conclusion, une base
de connaissance de KIF est un ensemble fini de formes. L'ordre des formes dans une base de
connaissance n'est pas important.

Une règle d'exemple dans KIF est :


LY_SII __ 68 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

(AGENT de => (EventName « :COMMENCER ")


(SetIdentifiedIntervalAlarm « NETSCAPE » 20 « minutes "))

ce qui traduit « si nous recevons un AGENT :EN COMMENÇANT l'événement, commencer


alors une alarme appelée d'intervalle (un temporisateur) identifiée par la corde
« NETSCAPE » pour aller outre de toutes les 20 minutes. » La règle de KIF :

(GetStockPrice ?prix) (IntegerCompare ?prix « > » 150)

les moyens « obtiennent le cours des actions d'actions et le placent dans la variable ?le prix,
examinent alors pour voir si le prix est plus grand que 150. »

Une règle complexe peut être presque illisible (aucune offense prévue pour bléser des
programmeurs) :

(=> (ET (ET (EventName « StockAdapter :StockPriceEvent ")


(GetStockEventCount ?compte)) (IntegerCompare ?compte « = » 2))
(ET (ET (ET (TurnOffIdentifiedIntervalAlarm « IBM ")
(StockAdapterShow « a arrêté l'alarme. » « » « "))
(SetIdentifiedIntervalAlarm « HSY » 20 « minutes "))
(StockAdapterShow « a commencé une alarme pour Hershey. » « « « ")))

Ceci signifie « si c'est un StockPriceEvent et c'est le deuxième événement de StockPriceEvent


envoyé du StockAdapter, alors arrête le temporisateur de montre d'actions d'IBM, affiche un
message, commence un temporisateur pour des nourritures de Hershey, et affiche un autre
message. » Maintenir dans l'esprit que KIF n'est pas un langage de programmation. En
employant KIF comme représentation de connaissance, nous pourrions employer les
rédacteurs multiples de règle avec les environnements graphiques gentils (mais très différents)
pour écrire des règles. Tant que ils tous écrivent les règles dans KIF composent, elles seraient
interchangeables. De même, si un agent pourrait écrire sa connaissance dans le format de KIF
et encore pourrait le lire, elles pourraient partager leur connaissance, quoique leurs
représentations internes puissent être totalement incompatibles. Malheureusement, il y a plus
à l'interaction d'agent que juste ayant un format commun d'échange pour la connaissance.
Nous discutons certaines de ces issues en chapitre 6.

Ceci nous amène à la fin de notre examen des formats de représentation de connaissance.
Nous pouvons employer le code procédural, logique d'attribut, si puis des règles, des filets
sémantiques, ou des armatures pour représenter des faits, des règles, des rapports, et des
transformations complexes. Ces divers genres de représentations de connaissance peuvent
coder la connaissance au sujet de presque n'importe quel domaine de problème. Une fois
combinée avec un moteur de système ou d'inférence de raisonnement, une base de
connaissance devient une partie d'un système à base de connaissances, la dernière matière en
ce chapitre.

Établir une base de connaissance

Maintenant que nous avons décrit beaucoup de types de connaissance que nous voulons
représenter, nous devons la grouper toute ensemble dans un endroit, dans notre base de
connaissance. La base de connaissance est le dépôt central d'information contenant les faits
que nous connaissons des objets et leurs rapports. C'était seulement après plusieurs années de
recherche d'AI qu'il est apparu clairement que la connaissance était la clef aux systèmes
LY_SII __ 69 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

réussis du bâtiment AI. Tout les travail sur des algorithmes de recherche et des méthodes de
résolution des problèmes générales a prouvé que sans connaissance profonde du domaine de
problème, n'importe quel problème réaliste bientôt sorti des limites des techniques standard de
recherche.

Le processus de tracer l'ensemble de la connaissance dans un domaine particulier de problème


et de le convertir en base de connaissance s'appelle l'ingénierie cognitive ou l'acquisition de
connaissance. Ces limites se sont développées hors du développement des systèmes experts
tôt, où plusieurs rôles uniques ont été identifiés. D'abord, il y a l'expert en matière de
domaine, qui, au cours des années d'expérience, a recueilli la connaissance au sujet de la
façon dont les choses fonctionnent et se rapportent à un un autre, et de la façon résoudre des
problèmes dans sa spécialité. Un ingénieur de connaissance est une personne qui peut prendre
cette connaissance de domaine et la représenter sous une forme à l'usage du système de
raisonnement. Comme intermédiaire entre l'expert humain et le système expert, l'ingénieur de
connaissance doit avoir de bonnes qualifications de personnes aussi bien que de bonnes
qualifications techniques. Une combinaison des questionnaires, des entrevues, et les
observations de première main sont employées pour donner à l'ingénieur de connaissance
l'arrangement profond exigé pour transformer la connaissance approfondie en faits et règles
pour la base de connaissance.

Ce processus s'appelle l'acquisition de connaissance et, tandis qu'essentiel, il est bientôt


apparu clairement que c'était un processus difficile et coûteux. Parfois les experts n'étaient pas
aussi vif sur faire capturer leur expertise et être transformée en application informatique qui
pourrait les mettre hors d'un travail. Parfois les experts ne pourraient pas vraiment expliquer
« comment » ils ont proposé ces perspicacités incroyables dans des problèmes. Parfois, le
problème était que les experts étaient, bien, des experts, et ainsi leur temps était valeur. Ils ont
dû résoudre les problèmes, pour ne pas passer des heures parlant à un ingénieur de
connaissance. Un troisième mot a été bientôt associé à l'acquisition de connaissance, et ce mot
était goulot. Plusieurs projets importants de système expert échoués dans les seconde moitié
années 80, et le goulot d'acquisition de connaissance ont été identifiés en tant que coupable
principal.

En même temps, les chercheurs d'intelligence artificielle sont devenus intéressés par des
techniques d'étude de machine. Using des données historiques des bases de données ou des
exemples produits par des experts, des réseaux neurologiques ont pu être formés pour
effectuer des tâches de classification et de prévision sans passer par le procédé d'acquisition
cher de la connaissance. Ces réseaux experts, car ils s'appellent, ont exécuté comme les
systèmes basés sur les règles dans beaucoup de cas soigneusement ouvrés. Ceci a stimulé un
intérêt remplacé pour les systèmes adaptatifs qui continue aujourd'hui.

Les réseaux neurologiques sont plus qu'une solution au problème d'acquisition de


connaissance, ils offrent une alternative au traitement de symbole. Les réseaux neurologiques
ne manoeuvrent des symboles, ou rien qui peuvent être facilement liés aux symboles. Comme
représentation de données ou de connaissance, les réseaux neurologiques sont essentiellement
« une boîte noire noire. » La topologie et les valeurs des poids d'interconnexion définissent le
réseau neurologique et la connaissance qu'il code. Cette représentation ne se prête pas
facilement à l'examen ou à l'arrangement au niveau symbolique. Seulement les champs
LY_SII __ 70 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

d'entrée et les champs de rendement sont identifiables. Les états internes et le traitement sont
le résultat d'un procédé adaptatif de « étude » ou de « formation » où des données sont
présentées au réseau neurologique et les poids de raccordement sont automatiquement ajustés
par l'intermédiaire d'un algorithme d'étude.

Cependant, quoique des réseaux neurologiques ne puissent être facilement convertis en forme
symbolique, ils sont le plus certainement une base de connaissance, parce qu'ils codent la
connaissance implicite dans les données de formation. Nous regardons les réseaux
neurologiques et l'étude en plus détail en chapitre 5.

Résumé

En ce chapitre nous avons parlé des techniques de la connaissance et de représentation de


connaissance utilisées généralement dans des programmes d'intelligence artificielle. Les
questions principales sont :

• Il y a beaucoup de types de connaissance comprenant les faits et les rapports, qui


peuvent être organisés par des catégories, des associations, et des hiérarchies. Le
processus de prendre la connaissance et de la mettre dans un ordinateur ainsi nous peut
l'employer pour résoudre des problèmes s'appelle la représentation de connaissance.
• Une bonne représentation de connaissance doit être facilement compréhensible par
des personnes. Elle devrait être non ambiguë et facilement manoeuvrée par des
ordinateurs et des algorithmes de raisonnement.
• Il y a deux types primaires de représentations de connaissance, procédural et
déclaratif. Un exemple d'une représentation de connaissance procédurale est code de
programme. La plupart des représentations de connaissance utilisées en intelligence
artificielle sont déclaratives.
• La logique propositionnelle et d'attribut peut être employée pour représenter des
objets, des fonctions, et des rapports. La résolution et l'unification sont des techniques
employées pour prouver des théorèmes et pour impliquer de nouveaux faits using la
logique.
• Des vues sont composées des fentes pour des valeurs de données, et des
remplisseurs, procédures pour calculer des valeurs et imposer des contraintes entre les
fentes. Chaque armature représente un concept et peut être liée à d'autres armatures
pour former des rapports hiérarchiques et de transmission.
• Les filets sémantiques sont des graphiques employés pour définir des rapports entre
les concepts. Les étiquettes de langage naturel sur des liens montrent les rapports entre
les noeuds.
• Règle de Bayes la' sert de base aux réseaux bayésiens ou causaux, qui représentent
des rapports probabilistes entre les variables et laissent raisonner dans des conditions
de l'incertitude.
• Le format d'échange de la connaissance est une langue conçue pour permettre aux
agents intelligents d'échanger la connaissance. KIF est basé sur la logique d'attribut, et
soutient des définitions d'objet, des fonctions au-dessus des objets, des rapports entre
les objets, et des règles.
• Une base de connaissance est une collection de lié aux connaissances à un domaine
spécifique de problème. Un ingénieur de connaissance traduit une connaissance
approfondie en représentation de connaissance d'ordinateur.
LY_SII __ 71 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

• L'étude de machine peut être employée pour surmonter le goulot d'acquisition de


connaissance. Les réseaux neurologiques peuvent coder la connaissance dans leurs
poids de raccordement, mais comme représentation de connaissance ils sont « une
boîte noire noire. »

Exercices

1. Écrire un ensemble de règles using la logique propositionnelle qui représentent cette


connaissance : Les agents intelligents ont trois attributs importants : agence,
intelligence, et mobilité. L'agence mesure l'autonomie d'agent. L'intelligence indique
des possibilités de raisonnement ou d'étude d'agent. La mobilité signifie que l'agent
peut se déplacer à travers le réseau.
2. Récrire l'ensemble de règles dans l'exercice 3.1 using la logique d'attribut.
3. Créer un filet sémantique pour le domaine de la crème glacée de pâté en croûte de
pizza ou glacée (votre choix).
LY_SII __ 72 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Chapitre 4
Systèmes &du Reasoning
En ce chapitre, nous nous concentrons sur la façon dont les diverses
représentations de connaissance présentées en chapitre 3 peuvent être employées
pour le raisonnement. Nous mettons en application un Java applet Qui emploie
en avant et vers l'arrière-enchaînant des algorithmes pour traiter si puis des
règles. Le traitement de logique floue et de règle sont également décrits. En
conclusion, nous explorons plusieurs techniques d'intelligence artificielle utilisées
pour la planification, y compris la planification de pile de but, la planification
non linéaire, et la planification hiérarchique.

Motifs avec des règles

Si alors les règles sont devenues la forme la plus populaire de représentation de connaissance
déclarative utilisée dans des applications d'intelligence artificielle. Il y a plusieurs raisons de
ceci. La connaissance a représenté comme si alors les règles est facilement compréhensible.
La plupart des personnes contrastent des règles confortables de lecture, avec la connaissance
représentée dans la logique d'attribut. Chaque règle peut être regardée comme morceau
autonome de la connaissance ou d'unité d'information dans une base de connaissance. De
nouvelles connaissances peuvent être facilement ajoutées, et la connaissance existante peut
être changée simplement en créant ou en modifiant différentes règles.

Des règles sont facilement manoeuvrées par les systèmes de motif. L'enchaînement vers
l'avant peut être employé pour produire de nouveaux faits (par conséquent les règles de
« production » de terme), et l'enchaînement en arrière peut déduire si les rapports sont vrais
ou pas. Les systèmes basés sur les règles étaient l'un des premiers succès commerciaux à
grande échelle de la recherche en matière d'intelligence artificielle. Un système expert ou le
système à base de connaissances est le terme commun employé pour décrire un système de
traitement basé sur les règles. Il se compose de trois éléments importants, une base de
connaissance (l'ensemble de si puis des règles et des faits connus), une mémoire temporaire
de travail ou une base de données des faits et des données dérivés, et un moteur d'inférence,
qui contient la logique de raisonnement employée pour traiter les règles et les données.

Avant que nous entrions dans les détails des motifs avec des règles, regardons une règle
simple :

si num_wheels = 4 et moteur = oui puis vehicleType = automobile

fait joindre deux clauses antécédentes par une conjonction (num_wheels = 4 et moteur = oui)
et a une clause conséquente simple (vehicleType = automobile). Une règle énonce un rapport
entre les clauses (des affirmations ou des faits) et, selon la situation, peut être employée pour
produire de la nouvelle information ou pour prouver la vérité d'une affirmation. Par exemple,
si nous savons qu'un véhicule a quatre roues et un moteur, alors, using la règle ci-dessus, nous
pouvons conclure que le vehicleType est une automobile et ajouter cette base de fait à notre
connaissance. D'une part, si nous essayons de montrer que le vehicleType est une automobile,
nous devons découvrir si le véhicule a quatre roues et un moteur. Dans le premier cas, nous
LY_SII __ 73 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

sommes enchaînement vers l'avant, using des faits et des règles pour dériver de nouveaux
faits. Dans le deuxième cas, nous sommes enchaînement en arrière, essayant de prouver une
affirmation dans la conséquence d'une règle en prouvant que les clauses antécédentes sont
vraies.

Une règle dont les clauses antécédentes sont toutes vraies serait déclenchée ou préparent pour
mettre le feu. Nous mettons le feu à une règle déclenchée en affirmant la clause conséquente
et en l'ajoutant comme fait à notre mémoire temporaire de travail. À tout moment, une base de
règle peut contenir plusieurs règles qui sont prêtes à mettre le feu. Elle est jusqu'à la stratégie
de commande du moteur d'inférence à décider lesquels obtient mis le feu. Nous discuterons ce
point en plus détail plus tard en ce chapitre.

La plupart des systèmes basés sur les règles permettent à des règles d'avoir des noms ou des
étiquettes telles que Rule1 : ou automobile : pour identifier facilement des règles pour éditer
ou pour tracer pendant inferencing. Quelques systèmes permettent des disjonctions (ou) entre
les clauses antécédentes. C'est une sténographie qui réduit la taille d'une base de règle. Par
exemple :

Rule 1: if num_wheels = 2 then vehicleType = cycle


Rule 2: if num_wheels = 3 then vehicleType = cycle
Rule 3: if (num_wheels = 2 or num_wheels = 3) then vehicleType = cycle

La règle 3 a pu remplacer la règle 1 et la règle 2 dans la base de règle. La plupart des systèmes
de règle permettent également les opérateurs booléens de condition comme <, >, et !=, en plus
de l'égalité. Nous pourrions récrire la règle 3 sans employer des disjonctions :

Rule 4: if (num_wheels > 1) and (num_wheels < 4) then vehicleType = cycle

Un autre perfectionnement commun à la syntaxe de règle est l'addition d'un facteur de


confiance ou de certitude. Si nous écrivons seulement les règles qui ont appliqué 100 pour
cent du temps avec 100 pour cent de confiance, nous aurions une base de connaissance très
petite. Généralement nous écrivons les règles qui s'appliquent le plus souvent. Ces
l'heuristique ou les « principes de base » sont souvent suffisants pour produire le
comportement raisonnable à partir d'un système basé sur les règles. Cependant, dans certains
cas, nous devons traiter l'incertitude dans les données, aussi bien que notre incertitude au sujet
de l'applicabilité de la règle. Par exemple, nous pourrions écrire une règle pour la prévision de
temps :

Rule 5: if (weather_forecast = rain) and (weather_probability > 80%) then


(chance_of_rain = high) with CF: 90.

Cela indique si les prévisions météorologiques indiquent que la pluie est probable avec une
probabilité au-dessus de 80 pour cent, alors nous soyons de 90 pour cent de certains qu'il
pleuvra. Si nous avons peu de confiance dans notre service local de prévision de temps, nous
pouvons abaisser le facteur de certitude de la règle à 50 pour cent.

Beaucoup de systèmes basés sur les règles permettent à des fonctions de s'appeler des clauses
antécédentes. Ces fonctions s'appellent les sondes, parce qu'elles sortent du moteur d'inférence
et examinent une certaine condition dans l'environnement. Les sondes renvoient
LY_SII __ 74 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

habituellement des valeurs booléennes, mais elles peuvent également renvoyer des données
ou des faits à la mémoire temporaire de travail. Quand on permet des fonctions dans le
conséquent, elles s'appellent les effecteur, qui augmentent considérablement les possibilités du
système basé sur les règles. Un effecteur transforme une règle d'un mécanisme fait-produisant
en dispositif action-produisant. Ces règles d'action permettent aux agents intelligents de faire
des choses pour nous. Par exemple, un agent intelligent qui traite l'email pourrait contenir la
règle suivante :

Rule 6: if sensor(mailArrived) then effector(processMail)

là où mailArrived et le processMail sont définis car les fonctions qui se connectent par
interface au système d'email, et la sonde () et l'effecteur () sont des méthodes données par le
système inferencing pour appeler ces fonctions.

Dans de petits nombres, les règles peuvent en juste proportion représenter beaucoup de types
de connaissance de domaine. Cependant, pendant que le nombre de règles se développe, les
aspects intuitifs de si alors des règles sont diminuées, et eux perdent leur efficacité d'une
perspective de lisibilité. Les systèmes commerciaux de règle-base laissent souvent grouper ou
diviser des règles de sorte qu'ils puissent être traités en tant que blocs logiques de la
connaissance afin d'essayer de surmonter cette faiblesse.

Un autre problème commun dans les systèmes basés sur les règles est que car l'information
plus complète entre, ou pendant que les choses changent dans le monde extérieur, les règles
qui ont pu avoir été vraies avant devenu fausses. La conséquence est que nous pouvons devoir
« rapporter » certains des « faits » qui ont été produits par les règles. Par exemple, si nous
voyons que l'herbe est humide, nous pouvons mettre le feu à une règle qui la conclut pleut.
Cependant, nous pouvons alors obtenir l'information que le système d'arrosage est allumé.
Ceci peut nous faire rétracter notre affirmation qu'il pleut ou abaisser au moins notre
confiance ou certitude en faisant cette conclusion. Ce problème de traiter des changements et
de rétracter des faits ou des affirmations s'appelle le raisonnement non monotonique.
Maintenir une règle-base conformée en contrôlant des dépendances entre les faits impliqués
exige un système de maintenance de l'intégrité. La plupart des systèmes de raisonnement, tels
que la logique d'attribut, sont monotoniques, c.-à-d., ils ajoutent l'information mais ne
rétractent pas l'information de la base de connaissance.

Dans les deux prochaines sections nous explorons le raisonnement vers l'avant et en arrière
avec des règles. Dans les deux sections, nous emploierons une base de règle simple comme
exemple. Cette base de règle devrait être familière. Nous avons décrit certaines des relations
en chapitre 3 quand nous avons discuté des armatures et des filets sémantiques. Nous
l'appelons la base de règle de véhicules (le schéma 4.1). Il a seulement neuf règles et sept
variables, avec une variable intermédiaire. Sept des règles définissent le genre de véhicule, et
deux sont employés pour déterminer si le véhicule est un cycle ou une automobile. La brièveté
et la clarté de cette petite règle-base nous aideront à se concentrer sur les questions liées à
inferencing basé sur les règles, et à ne pas obtenir si tout va bien distraits par les détails d'un
domaine plus complexe de problème.

Notre domaine de véhicules peut identifier trois types de cycles (un avec un moteur et deux en
dehors) et sept types d'automobiles. Nous différencions des cycles en tant qu'ayant plus moins
de quatre roues, et automobiles en tant qu'ayant exactement quatre roues et un moteur. Les
LY_SII __ 75 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

divers types d'automobiles sont identifiés par leur taille relative et le nombre de portes qu'ils
ont.

Enchaînement vers l'avant

L'enchaînement vers l'avant est un processus data-driven de raisonnement où un ensemble de


règles est employé pour dériver de nouveaux faits d'un premier ensemble de données. Il
n'emploie pas l'algorithme de résolution utilisé dans la logique d'attribut. L'algorithme de
chaînage avant produit de nouvelles données par l'application ou la mise à feu simple et
directe des règles. Comme procédé inferencing, l'enchaînement vers l'avant est très
rapidement. Les applications tôt de système expert de l'enchaînement vers l'avant incluent
R1/XCON, un système expert employé pour établir des configurations pour les systèmes
informatiques du VAX de Digital Equipment Corporation (McDermott 1982), et le
PROSPECTEUR, un système employé pour prévoir l'endroit des dépôts de minérai de
prospecter des données (Duda, et autres 1977). L'enchaînement vers l'avant est également
employé dans la surveillance en temps réel et les systèmes diagnostiques où l'identification et
la réponse rapides aux problèmes sont exigées.

Plusieurs outils commerciaux ont été développés principalement pour faire le raisonnement de
chaînage avant. La langue OPS5, et les OPS83 augmentés postérieurs, ont été développés à
l'université de carnegie-mellon. OPS5 a été employé pour mettre en application le système
R1. IBM a développé KnowledgeTool et TIR (le système Integrated de raisonnement), qui
étaient principalement des outils de chaînage avant. En plus de fournir les possibilités de
raisonnement, ces systèmes commerciaux ont également fourni plusieurs différentes stratégies
de commande et la capacité de se mélanger en code procédural de programme, effectivement
donnant la sonde et les possibilités d'effecteur.

Le schéma 4.1 les véhicules ordonnent la base.

Plus tard, nous allons mettre en application un système de chaînage avant, mais d'abord, nous
avons laissés regarder le processus de raisonnement en plus détail. Comme mentionné avant,
n'importe quel système expert exige trois éléments de base, une base de connaissance des
règles et des faits, une mémoire temporaire de travail pour stocker des données pendant
inferencing, et un moteur d'inférence. Les étapes suivantes font partie du cycle de chaînage
avant :

1. Charger la base de règle dans le moteur d'inférence, et tous les faits de la base de
connaissance dans la mémoire temporaire de travail.

2. Ajouter n'importe quelles données initiales additionnelles dans la mémoire temporaire de


travail.

3. Assortir les règles contre les données dans la mémoire temporaire de travail et déterminer
quelles règles sont déclenchées, signifiant que toutes leurs clauses antécédentes sont vraies.
Cet ensemble de règles déclenchées s'appelle l'ensemble de conflit.
LY_SII __ 76 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

4. Employer le procédé de résolution du conflit pour choisir une règle simple de l'ensemble
de conflit.

5. Mettre le feu à la règle choisie en évaluant les clauses conséquentes ; l'une ou l'autre mise
à jour la mémoire temporaire de travail si c'est une règle fait-produisante, ou appellent le
procédé effecteur, si c'est une règle d'action. Ceci désigné sous le nom de l'étape d'acte.

6. Répéter les étapes 3, 4, et 5 jusqu'à ce que l'ensemble de conflit soit vide.

Pendant la phase d'allumette de l'enchaînement vers l'avant le système d'inférence compare les
faits ou la mémoire temporaire de travail connus contre les clauses antécédentes dans les
règles pour déterminer quelle règle ou règles pourraient mettre le feu. Dans une base de
connaissance avec beaucoup de faits et de règles, la phase d'allumette peut prendre une
énorme quantité de durée de la transformation. Ainsi, nous voudrions examiner seulement ces
règles dont les clauses antécédentes se rapportent aux faits qui ont été mis à jour par la mise à
feu de la règle antérieure. L'algorithme de Rete, développé pour la langue OPS5, établit une
structure de données de réseau pour contrôler les dépendances entre les données, la condition
examine, et des règles, et réduit au minimum le nombre d'essais exigés pour chaque opération
d'allumette (Forgy 1982). Tandis que l'algorithme de Rete est Cadillac des algorithmes
d'allumette, beaucoup de systèmes de chaînage avant emploient les méthodes qui sont moins
efficaces mais plus faciles pour mettre en application.

Une fois que nous avons fini la phase d'allumette et avons produit l'ensemble de conflit, nous
nous déplaçons à l'étape de résolution du conflit. La résolution du conflit est peut-être l'étape
la plus importante en termes de comportement du système inferencing de chaînage avant.
Quand l'ensemble de conflit est vide ou contient seulement une règle simple, le problème est
insignifiant. Cependant, dans beaucoup de cas, il y aura plus d'une règle qui est déclenchée.
Quelle règle choisissons-nous pour mettre le feu ? Plusieurs solutions de rechange sont
disponibles :

• Choisir la première règle dans l'ensemble de conflit. C'est certainement simple, et pour
quelques domaines cela fonctionne.

• Choisir la règle avec la spécificité ou le nombre la plus élevée de clauses antécédentes.


L'idée ici est de choisir la règle qui est la plus spécifique (a les la plupart des conditions
d'essai sur si une partie de la règle) avant que nous mettions le feu à des règles plus
générales qui ont peu de clauses antécédentes.

• Choisir la règle qui se rapporte aux données qui ont changé récemment. Cette méthode
exige que les changements à la mémoire temporaire de travail sont horodatés ou de façon
ou d'autre étiquetés pour montrer quand ils étaient Last modified.

• Si la règle a mis le feu sur le cycle précédent, ne pas l'ajouter à l'ensemble de conflit. Cette
règle est parfois prolongée aux règles de limite ainsi ils peuvent seulement mettre le feu une
fois.

• Dans les cas où il y a une cravate, choisir une règle aléatoirement de ce sous-ensemble de
l'ensemble original de conflit.
LY_SII __ 77 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

En plus de l'allumette et de la résolution du conflit, les systèmes de chaînage avant utilisent


souvent une de plusieurs stratégies de commande pour aider à guider le processus inferencing.
Noter que cette pratique est une déviation de l'approche déclarative pure de si puis des règles.
Cependant, pour établir les systèmes experts fonctionnants pratiques de chaînage avant ces
stratégies de commande sont exigées. C'est un cas du pragmatisme réel contre l'idéalisme
scolaire.

Une stratégie de commande commune dans les systèmes basés sur les règles est d'assigner des
priorités aux règles qui peuvent être employées pour faciliter le processus de sélection. Si
nous avons un processus qui procède en trois phases, nous pouvons assigner la priorité 1 à
l'ensemble de règles qui contribuent à la première phase, la priorité 2 aux règles dans la
deuxième phase, et de même pour le troisième sous-ensemble de règles. L'avantage
d'employer des priorités est qu'il réduit considérablement le nombre de règles qui doivent être
recherchées et examinées dans la phase d'allumette.

Une autre approche qui peut être employée pour réaliser les mêmes résultats, même si le
système inferencing ne soutient pas formellement des priorités, est d'employer des clauses de
garde. Par exemple, nous pourrions ajouter une priorité de clause = 1 aux antécédents de
toutes les règles dans le groupe un, priorité = 2 dans le groupe deux et ainsi de suite. Tandis
que moins efficace que si les priorités inferencing de soutiens de système, il produit le
comportement désiré.

Un exemple de chaînage avant

Dans cette section, nous jetons un coup d'oeil à un exemple simple de l'enchaînement vers
l'avant dans notre domaine de véhicule. Pour commencer, nous chargeons notre base de règle
de véhicule dans le moteur d'inférence et définissons un ensemble de valeurs initiales pour des
variables dans la mémoire temporaire de travail :

num_wheels=4
motor=yes
num_doors = 3
size=medium

Après, nous faisons une phase d'allumette, où nous examinons les clauses antécédentes de
chaque règle pour déterminer lesquels peuvent être déclenchés. Nous n'avons aucune valeur
pour le vehicleType, ainsi les sept premières règles ne sont pas déclenchées. Les deux
dernières règles exigent des valeurs pour des num_wheels et circulent en voiture, ainsi elles
sont des candidats. Les num_wheels < clause 4 dans le cycle : la règle est fausse, de sorte que
ne soit pas déclenché, mais les num_wheels = 4 et moteur = sont oui rectifient et ainsi la règle
d'automobile est déclenchée. Notre ensemble de conflit de notre premier cycle d'allumette
contient une règle simple, l'automobile : règle.

Automobile: IF num_wheels=4
AND motor=yes
THEN vehicleType=automobile

La résolution du conflit est facile : Nous choisissons la règle simple et lui mettons le feu dans
le cycle d'acte. Mise à feu de l'automobile : ordonner les causes nous pour lier la valeur
LY_SII __ 78 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

« automobile » à la variable de vehicleType, et les ajouter à notre mémoire temporaire de


travail :

num_wheels=4
motor=yes
num_doors = 3
size=medium
vehicleType=automobile

Maintenant nous sommes prêts pour notre prochain cycle inferencing. Nous faisons une
allumette contre les règles pour déterminer lesquels pourraient être mis le feu. Maintenant que
le vehicleType a une valeur, les sept premières règles sont des candidats. Cependant, les trois
premières règles exigent ces vehicleType = cycle, qui est faux. Ceci laisse les quatre
prochaines règles : SportsCar, berline, monospace, et SUV. Seulement une règle simple a
toutes ses clauses antécédentes satisfaites, le monospace : ordonner avec des num_doors = 3
et taille = milieu. De nouveau, notre ensemble de conflit a seulement une règle simple dans
lui :

MiniVan: IF vehicleType=automobile
AND size=medium
AND num_doors=3
THEN vehicle=MiniVan

Nous mettons le feu au monospace : ordonner et ajouter la nouvelle information ce véhicule =


monospace à la mémoire temporaire de travail :

num_wheels=4
motor=yes
num_doors = 3
size=medium
vehicleType=automobile
vehicle=MiniVan

Nous faisons encore une autre phase d'allumette et constatons que seulement une règle est
déclenchée encore, le monospace : règle. Cependant, parce qu'elle a déjà mis le feu, nous ne
l'ajoutons pas à l'ensemble de conflit. Notre ensemble de conflit est maintenant vide, ainsi
nous arrêtons notre chaînage avant inferencing. Dans cet exemple, nous avons commencé par
quatre faits et avons calculé deux nouveaux faits, déterminant que le véhicule est un
monospace.

Enchaînement en arrière

L'enchaînement en arrière s'appelle souvent inferencing goal-directed, parce qu'une clause


particulière de conséquence ou de but est évaluée d'abord, et alors nous passons à reculons par
les règles. À la différence de l'enchaînement vers l'avant, qui emploie des règles pour produire
la nouvelle information, l'enchaînement en arrière emploie des règles pour répondre à des
questions au sujet de, qu'une clause de but soit vraie ou pas. L'enchaînement en arrière est
plus focalisé que l'enchaînement vers l'avant, parce qu'il traite seulement les règles qui sont
appropriées à la question. Il est semblable à la façon dont la résolution est employée dans la
LY_SII __ 79 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

logique d'attribut. Cependant, il n'emploie pas la contradiction. Il traverse simplement l'essai


bas de règle de montrer que les clauses sont vraies d'une façon systématique.

L'enchaînement en arrière est employé pour les systèmes experts consultatifs, où les
utilisateurs posent des questions et obtiennent ont posé de principales questions trouver une
réponse. Un système expert tôt célèbre, Mycin, enchaînement en arrière utilisé pour exécuter
des diagnostics des infections bactériennes dans les patients médicaux (Shortliffe 1976).

Un avantage de l'enchaînement en arrière est que, parce qu'inferencing est dirigé,


l'information peut être demandée de l'utilisateur quand elle est nécessaire. Quelques systèmes
de raisonnement fournissent également des possibilités de trace qui permettent à l'utilisateur
de demander au moteur d'inférence pourquoi elles demandent une certaine information, ou
pourquoi elles sont arrivées à une certaine conclusion.

Nous allons mettre en application un système de vers l'arrière-enchaînement plus tard en ce


chapitre. Maintenant, regardons les étapes qui font partie du cycle de vers l'arrière-
enchaînement :

1. Charger la base de règle dans le moteur d'inférence, et tous les faits de la base de
connaissance dans la mémoire temporaire de travail.

2. Ajouter n'importe quelles données initiales additionnelles dans la mémoire temporaire de


travail.

3. Spécifier une variable de but pour le moteur d'inférence pour trouver.

4. Trouver l'ensemble de règles qui se rapportent à la variable de but dans une clause
conséquente. C'est-à-dire, trouver toutes les règles qui placent la valeur de la variable de but
quand elles mettent le feu. Mettre chaque règle sur la pile de but.

5. Si la pile de but est vide, halte.

6. Prendre la règle supérieure outre de la pile de but.

7. Essayer de s'avérer que la règle est vraie en examinant toutes les clauses antécédentes
pour voir si elles sont vraies. Nous examinons chaque clause antécédente à leur tour : (a) Si la
clause est vraie, continuer à la prochaine clause antécédente. (b) Si la clause est fausse, alors
sauter la règle outre de la pile de but ; passer à l'étape 5. (c) si la valeur de vérité est
inconnue parce que la variable antécédente est inconnue, passent à l'étape 4, avec la
variable antécédente comme nouvelle variable de but. (d) Si toutes les clauses antécédentes
sont vraies, mettre le feu à la règle, plaçant la variable conséquente à la valeur conséquente,
bruit la règle outre de la pile de but, et aller à 5.

Regardons plus étroitement la façon dont l'algorithme de vers l'arrière-enchaînement


fonctionne using une règle de notre base de règle de véhicule comme exemple. Supposer que
nous voulons découvrir si le véhicule que nous avons est un monospace. C'est la règle qui doit
être satisfaisante :

MiniVan: IF vehicleType=automobile
LY_SII __ 80 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

AND size=medium
AND num_doors=3
THEN vehicle=MiniVan

Nous commençons par une mémoire temporaire de travail vide. Aucun fait n'est connu sur les
attributs de véhicule. La première chose que nous ferions est de vérifier la mémoire
temporaire de travail pour voir si le véhicule = le monospace est déjà vrai. Sinon, puis toutes
les clauses antécédentes du monospace : la règle doit être vraie pour conclure sans risque que
le véhicule est un monospace. En conséquence, nous devons essayer de prouver chaque clause
antécédente alternativement. La première chose que nous faisons est essai si le vehicleType =
l'automobile est vrai. La variable de vehicleType est n'a aucune valeur, ainsi nous recherchons
une règle qui a le vehicleType = l'automobile dans sa clause conséquente, et trouvons
l'automobile : règle ci-dessous :

Automobile: IF num_wheels=4
AND motor=yes
THEN vehicleType=automobile

C'est un exemple de l'enchaînement en arrière. Nous commençons par le monospace :


ordonner, et, au cours de montrer que vrais, nous avons enchaîné à une autre règle,
l'automobile : règle. Ces règles sont liées par la clause de vehicleType = d'automobile cette
elles les deux part. Concentré maintenant sur l'automobile : ordonner, nous doivent savoir si
des num_wheels = 4. Nous regardons dans la mémoire temporaire de travail et voyons que les
num_wheels n'a aucune valeur. Nous recherchons une règle qui a des num_wheels = 4 comme
conséquent. Il n'y en a aucun. Maintenant, nous pourrions ou abandonner, ou nous pourrions
demander à l'utilisateur d'apporter une réponse. Nous demandons à l'utilisateur, qui dit qu'il y
a quatre roues sur le véhicule. La première clause antécédente est vraie, ainsi nous passons à
la deuxième clause, moteur = oui. Nous vérifions la mémoire temporaire de travail, et le
moteur n'a aucune valeur. Nous encore recherchons la base de règle une règle avec le moteur
= oui dans le conséquent et pouvons n'en trouver aucun. Nous demandons à l'utilisateur de
fournir une valeur. L'utilisateur répond que le véhicule a un moteur, ainsi moteur = est oui
vrai. Toutes les deux clauses antécédentes sont maintenant vraies. Nous avons montré que
l'automobile : la règle est vraie, et nous pouvons placer le vehicleType = l'automobile. Notre
mémoire temporaire de travail contient maintenant les faits suivants :

num_wheels=4
motor=yes
vehicleType=automobile

Retournant à notre règle originale, nous savons maintenant que la première clause antécédente
est vraie. Nous devons après trouver des valeurs pour la taille et les num_doors. Using le
même processus décrit ci-dessus, nous finissons demander vers le haut à l'utilisateur ces
valeurs. L'utilisateur indique ces taille = milieu et num_doors = 3. Toutes les clauses
antécédentes ont été satisfaites, ainsi nous pouvons conclure que le véhicule est un
monospace. Notre mémoire temporaire de travail finale contient :

num_wheels=4
motor=yes
vehicleType=automobile
size=medium
num_doors=3
vehicle=MiniVan
LY_SII __ 81 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Tandis que notre petit exemple établi, dans beaucoup de cas la règle que nous essayons de
prouver n'est pas vrai. Nous pouvons devoir rechercher d'autres chemins par des règles
alternatives afin de répondre à la question de l'utilisateur. L'algorithme de vers l'arrière-
enchaînement exécute quelles quantités à une profondeur-première recherche par la base de
règle tout en essayant de prouver une clause de but.

L'applet de règle de Java

Dans cette section, nous présentons un applet qui met en application les deux types principaux
d'algorithmes de raisonnement utilisés avec les systèmes basés sur les règles : enchaînement
vers l'avant et en arrière. L'applet de règle comporte trois carreaux des textes pour montrer la
base de règle, les variables et leurs valeurs courantes, et une trace du processus inferencing.
Le schéma 4.2 montre l'applet de règle.

Le schéma 4.2 le dialogue d'applet de règle.

Au dessus du dialogue, les utilisateurs peuvent choisir une des trois bases de règle
d'échantillon fournies, la base de règle de véhicule déjà discutée, une base de règle de bogues,
et les usines ordonnent la base. Les règles de la base choisie de règle sont montrées dans le
java.awt. Commande de TextArea au gauche supérieur. Vers la droite sont deux commandes
bien choisies de java.awt (boîtes de liste drop-down) qui permettent à l'utilisateur de
spécifier la variable et une valeur correspondante pour la variable choisie. Noter que la
variable et la valeur doivent être choisies pour que la variable soit changée. Toutes les fois
qu'une variable est changée, sa nouvelle valeur est ouverte une session la commande variable
de TextArea qui est immédiatement au-dessous des commandes bien choisies.

Près du fond du dialogue, deux commandes de bouton par radio permettent à l'utilisateur de
spécifier si l'enchaînement vers l'avant ou en arrière devrait être employé. Si l'enchaînement
en arrière est spécifié, le nom de la variable de but doit être écrit dans le but java.awt
TextField à la droite des boutons par radio.

Il y a trois boutons poussoirs au fond même du dialogue. L'extrême gauche est le but de
trouvaille, qui cause un cycle inferencing d'être exécuté. Ce cycle emploiera les arrangements
courants de variable-valeur. Après qu'un but de trouvaille soit exécuté, vous pouvez appuyer
sur le bouton de remise sur la droite inférieure. Ceci placera toutes les variables de règle-base
aux valeurs nulles. Quand le bouton moyen, démo courue, est cliqué, selon la base de règle et
méthode d'enchaînement choisie, les variables sera placé aux valeurs de préréglage, et un but
automatique de trouvaille est exécuté.

Notre exécution de Java inclut une classe de règle, une classe de RuleVariable, et une classe
de RuleBase, aussi bien que plusieurs classes de soutien telles que la clause. Nous
commençons notre discussion avec la classe de règle.

Règles

La classe de règle est employée pour définir une règle simple et contient également les
méthodes qui soutiennent le processus inferencing. Chaque règle a un membre nommé de
données, une référence à l'objet de possession de RuleBase (décrit plus tard), un choix de
clauses antécédentes, et une clause conséquente simple. La valeur de vérité de la règle est
LY_SII __ 82 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

stockée dans la vérité booléenne. La note, ceci est un objet booléen, pas une variable
booléenne élémentaire. Ceci nous permet d'employer une valeur nulle pour indiquer que la
vérité de la règle ne peut pas être déterminée (parce qu'une des variables référencées dans une
clause est également nulle ou non définie). Le membre booléen mis le feu indique si cette
règle a été mise le feu ou pas.

Il y a plusieurs constructeurs de règle, chacun qui exigent une référence à l'exemple de


RuleBase, le nom de règle, une ou plusieurs clauses antécédentes ou de gauche-main-côté, et
la clause simple conséquente ou de droit-main-côté. Chaque constructeur assigne le nombre
correct d'entrées dans la rangée d'antécédents, et s'enregistre également avec les objets de
clause pendant qu'il les ajoute à ses membres de données. La vérité de règle est initialisée à la
nulle, signification éliminée ou inconnue, et la règle s'enregistre avec le RuleBase de
possession.

public class Rule {


RuleBase rb ;
String name ;
Clause antecedents[] ; // allow up to 4 antecedents for now
Clause consequent ; //only 1 consequent clause allowed
Boolean truth; // states = (null=unknown, true, or false)
boolean fired=false;
Rule(RuleBase Rb, String Name, Clause lhs, Clause rhs) {
rb = Rb ;
name = Name ;
antecedents = new Clause[1] ;
antecedents[0] = lhs ;
lhs.addRuleRef(this) ;
consequent = rhs ;
rhs.addRuleRef(this) ;
rhs.isConsequent() ;
rb.ruleList.addElement(this) ; // add self to rule list
truth = null ;
}

Rule(RuleBase Rb, String Name, Clause lhs1, Clause lhs2,


Clause rhs) {
rb = Rb ;
name = Name ;
antecedents = new Clause[2] ;
antecedents[0] = lhs1 ;
lhs1.addRuleRef(this) ;
antecedents[1] = lhs2 ;
lhs2.addRuleRef(this) ;
consequent = rhs ;
rhs.addRuleRef(this) ;
rhs.isConsequent() ;
rb.ruleList.addElement(this) ; // add self to rule list
truth = null ;
}
Rule(RuleBase Rb, String Name, Clause lhs1, Clause lhs2,
Clause lhs3, Clause rhs) {
rb = Rb ;
name = Name ;
antecedents = new Clause[3] ;
antecedents[0] = lhs1 ;
lhs1.addRuleRef(this) ;
antecedents[1] = lhs2 ;
LY_SII __ 83 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

lhs2.addRuleRef(this) ;
antecedents[2] = lhs3 ;
lhs3.addRuleRef(this) ;
consequent = rhs ;
rhs.addRuleRef(this) ;
rhs.isConsequent() ;
rb.ruleList.addElement(this) ; // add self to rule list
truth = null ;
}
Rule(RuleBase Rb, String Name, Clause lhs1, Clause lhs2,
Clause lhs3, Clause lhs4,
Clause rhs) {
rb = Rb ;
name = Name ;
antecedents = new Clause[4] ;
antecedents[0] = lhs1 ;
lhs1.addRuleRef(this) ;
antecedents[1] = lhs2 ;
lhs2.addRuleRef(this) ;
antecedents[2] = lhs3 ;
lhs3.addRuleRef(this) ;
antecedents[3] = lhs4 ;
lhs4.addRuleRef(this) ;
consequent = rhs ;
rhs.addRuleRef(this) ;
rhs.isConsequent() ;
rb.ruleList.addElement(this) ; // add self to rule list
truth = null ;
}

long numAntecedents() { return antecedents.length; }


. . .
}

Clauses

Des clauses sont employées dans les parties antécédentes et conséquentes d'une règle. Une
clause se compose habituellement d'un RuleVariable du côté à gauche, d'une condition, qui
examine l'égalité, plus grande que, ou moins que, et du côté droit, qui dans notre exécution est
une valeur (symbolique ou numérique) de corde. Par exemple la règle :

Automobile: IF num_wheels=4
AND motor=yes

THEN vehicleType=automobile contient trois clauses. La première clause antécédente se


compose du RuleVariable « num_wheels », la condition « = », et la corde « 4 ». Les autres
clauses se composent pareillement. Une clause contient également un vecteur des règles qui
contiennent cette clause, un booléen conséquent qui indique si la clause apparaît dans
l'antécédent ou le conséquent de la règle, et d'une vérité booléen qui indique si la clause est
vraie, faux, ou inconnu (nulle).

Le constructeur de clause prend un RuleVariable pour le côté à gauche, un objet de


condition, et une corde pour le côté droit. La clause s'enregistre avec le RuleVariable de
LY_SII __ 84 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

sorte que toutes les fois que la valeur de variable est changée, la clause puisse être
automatiquement retestée. Le booléen conséquent est placé à faux, au commencement, parce
que la plupart des clauses sont des clauses antécédentes.

La classe de clause contient quatre méthodes. La méthode d'addRuleRef () est employée par
le constructeur de règle pour enregistrer la règle avec cette clause. La méthode de contrôle ()
réalise un essai de la clause. Si la clause est employée comme clause conséquente, alors l'essai
de sa valeur de vérité ne semble aucun raisonnable ; nous renvoyons une valeur nulle. Si la
variable du côté à gauche est non liée, nous renvoyons également la nulle, parce qu'une valeur
de vérité ne peut pas être déterminée. Si la variable est liée, nous employons le rapport de
commutateur pour examiner l'état logique spécifique et pour renvoyer la valeur de vérité en
résultant. L'isConsequent () renvoie les retours conséquents booléens et de getRule () de
méthode une référence à l'exemple de possession de règle.

public class Clause {


Vector ruleRefs ;
RuleVariable lhs ;
String rhs ;
Condition cond ;
Boolean consequent ; // true or false
Boolean truth ; // states = null(unknown), true or false
Clause(RuleVariable Lhs, Condition Cond, String Rhs)
{
lhs = Lhs ; cond = Cond ; rhs = Rhs ;
lhs.addClauseRef(this) ;
ruleRefs = new Vector() ;
truth = null ;
consequent = new Boolean(false) ;
}

void addRuleRef(Rule ref) { ruleRefs.addElement(ref) ; }

Boolean check() {
if (consequent.booleanValue() == true) return null ;
if (lhs.value == null) {
return truth = null ; // var value is undefined
} else {

switch(cond.index) {
case 1: truth = new Boolean(lhs.value.equals(rhs)) ;
break ;
case 2: truth = new Boolean(lhs.value.compareTo(rhs) > 0) ;
break ;
case 3: truth = new Boolean(lhs.value.compareTo(rhs) < 0) ;
break ;
case 4: truth = new Boolean(lhs.value.compareTo(rhs) != 0) ;
break ;
}

return truth ;
}
}

void isConsequent() { consequent = new Boolean(true); }

Rule getRule() { if (consequent.booleanValue() == true)


return (Rule)ruleRefs.firstElement() ;
LY_SII __ 85 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

else return null ;}


};

La classe de condition est une classe d'aide à la clause. Elle prend une représentation de
corde d'un essai conditionnel et des convertis qui dans un code pour l'usage dans le rapport
de commutateur dans la méthode de Clause.check ().

public class Condition {


int index ;
String symbol ;
Condition(String Symbol) {
symbol = Symbol ;
if (Symbol.equals(“=”)) index = 1 ;
else if (Symbol.equals(“>”)) index = 2 ;
else if (Symbol.equals(“<”)) index = 3 ;
else if (Symbol.equals(“!=”)) index = 4 ;
else index = -1 ;
}
String asString() {
String temp = new String() ;
switch (index) {
case 1: temp = “=” ;
break;
case 2: temp = “>” ;
break ;
case 3: temp = “<” ;
break;
case 4: temp = “!=” ;
break;
}
return temp ;
}
}

Variables

Nous définissons une classe basse pour les variables qui soutiennent la fonction que nous
avons besoin pour le traitement de règle et pour apprendre dans le prochain chapitre. La classe
variable a un membre nommé pour identifier la variable, et un membre de valeur de corde
(qui pourrait être un objet dans une application plus d'usage universel). La colonne est
employée pour spécifier la position de la variable dans un fichier de données. Il y a un
constructeur de défaut, aussi bien qu'un où le nom est spécifié. Deux méthodes d'accédant
sont fournies pour placer la valeur et pour obtenir la valeur de la variable. Le membre
d'étiquettes est habitué pour tenir des symboles discrets pour des variables catégoriques. La
méthode de setLabels () est employée pour définir les valeurs symboliques valides pour des
variables catégoriques. La méthode de getLabel () renvoie la valeur symbolique pour l'index
spécifique et le getIndex inverse de méthode () renvoie l'index donné une valeur symbolique.
Un exemple de la façon dont ces méthodes sont employées est montré dans la section sur
l'exécution de base de règle de véhicule où les variables sont définies.

public abstract class Variable {

String name ;
LY_SII __ 86 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

String value ;
int column ;

public Variable() {} ;
public Variable(String Name) {name = Name; value = null; }
void setValue(String val) { value = val ; }
String getValue() { return value; }

// used by categorical only


Vector labels ;
void setLabels(String Labels) {
labels = new Vector() ;
StringTokenizer tok = new StringTokenizer(Labels,“ ”) ;
while (tok.hasMoreTokens()) {
labels.addElement(new String(tok.nextToken())) ;
}
}
// return the label with the specified index
String getLabel(int index) {
return (String)labels.elementAt(index);
}

// return a string containing all labels


String getLabels() {
String labelList = new String();
Enumeration enum = labels.elements() ;
while(enum.hasMoreElements()) {
labelList += enum.nextElement() + “ ” ;
}
return labelList ;
}

// given a label, return its index


int getIndex(String label) {
int i = 0, index = 0 ;
Enumeration enum = labels.elements() ;
while(enum.hasMoreElements()) {
if (label.equals(enum.nextElement()))
{ index = i ; break ; }
i++;
}
return i;
}

Rule variables

Pour le traitement de règle, nous sous-classe notre classe variable et ajoutons un certain
comportement règle-spécifique. Il fournit l'appui nécessaire pour des variables utilisées dans
inferencing. Le constructeur prend le nom de la variable comme seul paramètre.
RuleVariables héritent du comportement symbolique discret de la classe variable basse. Un
nouveau membre de données est les clauseRefs de vecteur, qui tient des références à toutes
les clauses qui se rapportent à cette variable. Les exemples de la clause s'enregistrent en
appelant la méthode d'addClauseRef (). Il y a plusieurs méthodes qui sont dépassées comme
quelques neufs supplémentaires pour le traitement de règle. La méthode de setValue () place
non seulement la valeur de la variable, elle appelle également la méthode d'updateClauses (),
qui réitère par chaque clause qui se rapporte à ce RuleVariable et reteste sa valeur de vérité
par l'intermédiaire de sa méthode de contrôle ().
LY_SII __ 87 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

promptString stocke le texte qui est montré quand l'utilisateur est incité à fournir une valeur
pour cette variable. Le ruleName tient le nom de la règle qui a placé cette valeur de
RuleVariables ; quand les feux de règle il appelle la méthode de setRuleName () en
conséquence. La méthode d'askUser () appelle la méthode de waitForAnswer () sur le
RuleApplet. Ceci ouvre une zone de dialogue qui incite l'utilisateur à fournir une valeur pour
la variable pendant inferencing. Ceci est seulement employé par l'algorithme de vers l'arrière-
enchaînement.

public class RuleVariable extends Variable {

public RuleVariable(String Name) {


super(Name);
clauseRefs = new Vector();
}

void setValue(String val) { value = val;


updateClauses(); }

// prompt a user to provide a value for a variable during inferencing


String askUser() {
String answer = RuleApplet.waitForAnswer(name, getLabels()) ;
RuleBase.appendText(“\n !!! Looking for ” + name +
“. User entered: ” + answer) ;
setValue(answer) ; // need to set value from textField here
return value ;
}

Vector clauseRefs ; // clauses which refer to this var


void addClauseRef(Clause ref) { clauseRefs.addElement(ref) ; }

void updateClauses() {
Enumeration enum = clauseRefs.elements() ;
while(enum.hasMoreElements()) {
((Clause)enum.nextElement()).check() ; // retest the clause
}
}
String promptString ; // used to prompt user for value
String ruleName ; // if value is inferred, null = user provided
void setRuleName(String rname) { ruleName = rname; }

...

};

Base de règle

La classe de RuleBase définit un ensemble de RuleVariables et de règles, avec les méthodes


à niveau élevé pour l'enchaînement vers l'avant et en arrière. Le RuleBase a un nom, un
variableList qui contient tout les RuleVariables référencé par les règles, et le ruleList, qui
contient toutes les règles. Le forwardChain () et des méthodes de backwardChain () comme
d'autres membres de données de RuleBase qui sont employés par les algorithmes inferencing
sont décrits dans leurs sections respectives plus tard en ce chapitre.
LY_SII __ 88 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

public class RuleBase {

String name ;
Hashtable variableList ; // all variables in the rulebase
Clause clauseVarList[];
Vector ruleList ; // list of all rules
Vector conclusionVarList ; // queue of variables
Rule rulePtr ; // working pointer to current rule
Clause clausePtr ; // working pointer to current clause
Stack goalClauseStack; // for goals (cons clauses) and subgoals

static TextArea textArea1 ;


public void setDisplay(TextArea txtArea) { textArea1 = txtArea; }

RuleBase(String Name) { name = Name; }


public static void appendText(String text) {
textArea1.appendText(text); }

// for trace purposes - display all variables and their value


public void displayVariables(TextArea textArea) {

Enumeration enum = variableList.elements() ;


while(enum.hasMoreElements()) {
RuleVariable temp = (RuleVariable)enum.nextElement() ;
textArea.appendText(“\n” + temp.name + “ value = ”
+ temp.value) ;
}
}

// for trace purposes - display all rules in text format


public void displayRules(TextArea textArea) {
textArea.appendText(“\n” + name + “ Rule Base: ” + “\n”);
Enumeration enum = ruleList.elements() ;
while(enum.hasMoreElements()) {
Rule temp = (Rule)enum.nextElement() ;
temp.display(textArea) ;
}
}

// for trace purposes - display all rules in the conflict set


public void displayConflictSet(Vector ruleSet) {
textArea1.appendText(“\n” + “ -- Rules in conflict set:\n”);
Enumeration enum = ruleSet.elements() ;
while(enum.hasMoreElements()) {
Rule temp = (Rule)enum.nextElement() ;
textArea1.appendText(temp.name + “(” + temp.numAntecedents()+ “), ” )
;
}
}

// reset the rule base for another round of inferencing


// by setting all variable values to null
public void reset() {
textArea1.appendText(“\n --- Setting all ” + name + “ variables to
null”);
Enumeration enum = variableList.elements() ;
while(enum.hasMoreElements()) {
RuleVariable temp = (RuleVariable)enum.nextElement() ;
temp.setValue(null) ;
}
LY_SII __ 89 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

...

Exécution de chaînage avant

Notre exécution de chaînage avant emploie des méthodes dans la classe de RuleBase et la
classe de règle. La méthode de forwardChain () dans la classe de RuleBase contient la
logique de commande principale pour l'enchaînement vers l'avant. La méthode assigne
d'abord le vecteur de conflictRuleSet. La méthode d'allumette () s'appelle avec un paramètre
vrai booléen pour forcer un premier essai de toutes les règles dans la base de règle. Ceci
retourne avec le conflictRuleSet initial, un vecteur des règles qui sont déclenchées et pourrait
être mis le feu. Nous écrivons alors une boucle de moment (), qui fonctionne jusqu'à ce que
nous ayons un conflictRuleSet vide. À l'intérieur de la boucle, nous d'abord appel la méthode
de selectRule (), passant le conflictRuleSet comme paramètre. La méthode de selectRule ()
exécute la stratégie de résolution du conflit et revient avec une règle simple au feu. Nous
appelons la méthode de Rule.fire () pour effectuer la tâche conséquente de clause, et puis
retestons toutes les clauses et règles qui se rapportent à la variable mise à jour. Tandis que
pas une exécution de Rete, cette approche limite la quantité d'essai de clause qui doit être
exécutée. Avec le variableList mis à jour, nous appelons l'allumette () encore, mais cette fois
où nous passons en valeur fausse booléenne de paramètre. Ceci indique l'allumette () regarder
seulement les valeurs de vérité de règle, pour ne pas examiner chaque règle.

public void forwardChain() {


Vector conflictRuleSet = new Vector() ;

// first test all rules, based on initial data


conflictRuleSet = match(true); // see which rules can fire

while(conflictRuleSet.size() > 0) {

Rule selected = selectRule(conflictRuleSet); // select the “best”


rule
selected.fire() ; // fire the rule
// do the consequent action/assignment
// update all clauses and rules

conflictRuleSet = match(false); // see which rules can fire


}
}

Maintenant regardons en plus détail les différentes méthodes. La méthode de RuleBase.match


() prend un paramètre booléen simple. Elle marche par le ruleList. Si le paramètre d'essai est
vrai, il appelle la méthode de Rule.check () pour examiner toutes les clauses antécédentes de
règle et pour placer la valeur de vérité de la règle. Si l'essai est faux, l'allumette () regarde
simplement la valeur de vérité de la règle courante. Si la règle est vraie, et elle n'a pas été déjà
mise le feu, elle l'ajoutera au vecteur de matchList. Sinon, nous continuons juste dessus à la
prochaine règle sur le ruleList. Pour le découverte, nous montrons l'ensemble de conflit.

// used for forward chaining only


// determine which rules can fire, return a Vector
public Vector match(boolean test) {
LY_SII __ 90 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Vector matchList = new Vector() ;


Enumeration enum = ruleList.elements() ;
while (enum.hasMoreElements()) {
Rule testRule = (Rule)enum.nextElement() ;
if (test) testRule.check() ; // test the rule antecedents
if (testRule.truth == null) continue ;
// fire the rule only once for now
if ((testRule.truth.booleanValue() == true) &&
(testRule.fired == false)) matchList.addElement(testRule);
}
displayConflictSet(matchList) ;
return matchList ;
}

La méthode de RuleBase.selectRule () prend un vecteur des règles, l'ensemble de conflit,


comme paramètre d'entrée. Notre exécution est assez simple. Nous employons la spécificité,
c.-à-d., le nombre de clauses antécédentes, en tant que notre méthode primaire pour choisir
une règle mettre le feu. Si deux règles ou plus ont le même nombre de clauses antécédentes,
nous choisissons la première règle que nous rencontrons. Nous commençons par prendre la
première règle outre de la liste et l'indiquons en tant que notre bestRule, prenant également le
nombre d'antécédents comme meilleure ou maximum valeur courante. Dans une boucle de
moment (), nous marchons par le reste de l'ensemble de conflit. Si nous rencontrons une règle
qui a des clauses plus antécédentes que notre maximum précédent, nous plaçons cette règle en
tant que notre bestRule courant et valeur maximum correspondante. Après avoir regardé toutes
les règles dans l'ensemble de conflit, nous renvoyons le bestRule final à mettre le feu.

// used for forward chaining only


// select a rule to fire based on specificity
public Rule selectRule(Vector ruleSet) {
Enumeration enum = ruleSet.elements() ;
long numClauses ;
Rule nextRule ;

Rule bestRule = (Rule)enum.nextElement() ;


long max = bestRule.numAntecedents() ;
while (enum.hasMoreElements()) {
nextRule = (Rule)enum.nextElement() ;
if ((numClauses = nextRule.numAntecedents()) > max) {
max = numClauses ;
bestRule = nextRule ;
}
}
return bestRule ;
}

La méthode de Rule.check () est employée pendant l'enchaînement vers l'avant pour examiner
les clauses antécédentes de la règle. Le cas échéant des clauses a une valeur de vérité non
définie, puis une valeur nulle est retournée par le contrôle (). Le cas échéant des clauses est
faux, puis la valeur de vérité de la règle est placée à faux et une valeur fausse est retournée. Si
toutes les clauses antécédentes sont vraies, alors la valeur de vérité de la règle est placée pour
rectifier, et une valeur vraie est retournée. Noter que seulement des conjonctions (et) sont
soutenus, pas des disjonctions (ou) entre les clauses.

// used by forward chaining only !


Boolean check() { // if antecedent is true and rule has not fired
LY_SII __ 91 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

RuleBase.appendText(“\nTesting rule ” + name ) ;


for (int i=0 ; i < antecedents.length ; i++ ) {
if (antecedents[i].truth == null) return null ;
if (antecedents[i].truth.booleanValue() == true) {
continue ;
} else {
return truth = new Boolean(false) ; //don’t fire this rule
}
} // endfor
return truth = new Boolean(true) ; // could fire this rule
}

La méthode de Rule.fire () est employée pendant l'enchaînement vers l'avant quand une règle
avec une véritable valeur de vérité est choisie être mise le feu. Le drapeau booléen mis le feu
est placé pour prouver que la règle a mis le feu. Le RuleVariable du côté à gauche de la
clause conséquente est placé à la valeur du côté droit. La méthode de checkRules () s'appelle
alors pour retester ces règles qui se rapportent à la variable conséquente.

// used by forward chaining only !


// fire this rule -- perform the consequent clause
// if a variable is changes, update all clauses where
// it is references, and then all rules which contain
// those clauses
void fire() {
RuleBase.appendText(“\nFiring rule ” + name ) ;
truth = new Boolean(true) ;
fired = true ;
// set the variable value and update clauses
consequent.lhs.setValue(consequent.rhs) ;
// now retest any rules whose clauses just changed
checkRules(consequent.lhs.clauseRefs) ;
}

La méthode de Rule.checkRules () reteste chaque clause qui se rapporte au RuleVariable qui


a été changé par la mise à feu de la règle. Puisque le RuleVariable a maintenant une valeur,
toutes les clauses antécédentes qui se sont rapportées à elle et l'avaient éliminé ou valeurs de
vérité nulles peuvent maintenant être placées à vrai ou à faux. Ceci signifie que les règles qui
se sont rapportées à ces clauses peuvent maintenant évaluer comme vrai ou faux.

// used by forward chaining only !


// a variable value was found, so retest all clauses
// that reference that variable, and then all rules which
// references those clauses
public static void checkRules(Vector clauseRefs) {
Enumeration enum = clauseRefs.elements();
while(enum.hasMoreElements()) {
Clause temp = (Clause)enum.nextElement();
Enumeration enum2 = temp.ruleRefs.elements() ;
while(enum2.hasMoreElements()) {
((Rule)enum2.nextElement()).check() ; // retest the rule
}
}
LY_SII __ 92 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

La méthode de Rule.display () écrit l'éliminer dans une mise en forme de texte dans le
TextArea supérieur du RuleApplet. Ceci permet à l'utilisateur d'examiner le RuleBase pour
suivre une chaîne d'inférence.

// display the rule in text format


void display(TextArea textArea) {
textArea.appendText(name +“: IF ”) ;
for(int i=0 ; i < antecedents.length ; i++) {
Clause nextClause = antecedents[i] ;
textArea.appendText(nextClause.lhs.name +
nextClause.cond.asString() +
nextClause.rhs + “ ”) ;
if ((i+1) < antecedents.length)
textArea.appendText(“\n AND ”) ;
}
textArea.appendText(“\n THEN ”) ;
textArea.appendText(consequent.lhs.name +
consequent.cond.asString() +
consequent.rhs + “\n”) ;
}

Vers l'arrière-Enchaînement de l'exécution

La méthode de RuleBase.backwardChain () prend un paramètre simple, une corde qui est le


nom de la variable de but. Ce nom variable est employé pour rechercher l'exemple de
RuleVariable du but. Toutes les clauses qui se rapportent à la variable de but sont énumérées
et une boucle de moment () est employées pour traiter chaque objet de clause. Si ce n'est pas
une clause conséquente, il est ignoré et nous continuons par la boucle à examiner le prochain
goalClause. Si c'est une clause conséquente, nous la poussons sur notre goalClauseStack.
Nous obtenons alors une référence à la règle qui contient cette clause en tant que son
conséquent. Nous appelons Rule.backChain () sur cette règle pour voir s'il est vrai ou pas.
Rule.backChain () fera des appels récursifs à RuleBase.backwardChain () au besoin, pour
suivre une chaîne des inférences par la base de règle afin de découvrir si le goalClause
original est vrai ou faux. Rule.backChain () renverra la valeur de vérité de la règle.

• Si la valeur de vérité de la règle est nulle, nous ne pourrions pas déterminer si le


goalClause courant est vrai ou pas. Ou la base de règle est inachevée, ou l'utilisateur a fourni
une valeur inadmissible une fois incité à fournir un.

• Si la règle était vraie prouvé, nous mettons le feu à la règle en fixant le but courant
variable à la valeur du côté droit du goalClause ; nous ajoutons une référence à la variable
pour lui dire quelle règle a produit sa valeur ; nous sautons la clause outre du
goalClauseStack et affichons un message de succès. Si le goalClauseStack est vide, nous
sommes enchaînement en arrière fait, ainsi nous affichons un message de victoire et
éclatons de la boucle.

• Si la règle était fausse, nous sautons le goalClause du goalClauseStack, affichons un


message d'échec, et continuons par la boucle de moment () à traiter le prochain goalClause.

// for all consequent clauses which refer to this goalVar


// try to find goalVar value via a rule being true
// if rule is true then pop, assign value, re-eval rule
LY_SII __ 93 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

// if rule is false then pop, continue


// if rule is null then we couldnt find a value
//
public void backwardChain(String goalVarName)
{
RuleVariable goalVar = (RuleVariable)variableList.get(goalVarName);
Enumeration goalClauses = goalVar.clauseRefs.elements() ;

while (goalClauses.hasMoreElements()) {
Clause goalClause = (Clause)goalClauses.nextElement() ;
if (goalClause.consequent.booleanValue() == false) continue ;

goalClauseStack.push(goalClause) ;

Rule goalRule = goalClause.getRule();


Boolean ruleTruth = goalRule.backChain(); // find rule truth
if (ruleTruth == null) {
textArea1.appendText(“\nRule ” + goalRule.name +
“ is null, can’t determine truth value.”);
} else if (ruleTruth.booleanValue() == true) {
// rule is OK, assign consequent value to variable
goalVar.setValue(goalClause.rhs) ;
goalVar.setRuleName(goalRule.name) ;
goalClauseStack.pop() ; // clear item from subgoal stack
textArea1.appendText(“\nRule ” + goalRule.name +
“ is true, setting ” + goalVar.name + “: = ”
+ goalVar.value);

if (goalClauseStack.empty() == true) {
textArea1.appendText(“\n +++ Found Solution for goal: ”
+ goalVar.name);
break ; // for now, only find first solution, then stop
}
} else {
goalClauseStack.pop() ; // clear item from subgoal stack
textArea1.appendText(“\nRule ” + goalRule.name +
“ is false, can’t set ” + goalVar.name);
}
}

if (goalVar.value == null) {
textArea1.appendText(“\n +++ Could Not Find Solution for goal: ”
+ goalVar.name);
}
}

La méthode de Rule.backChain () essayera de s'avérer qu'une règle vraie ou fausse en


appelant périodiquement RuleBase.backwardChain () jusqu'à la valeur de vérité peut être
déterminée. La méthode se compose d'a pour () la boucle, où chaque clause antécédente est
évaluée alternativement. Si la variable dans une clause antécédente est non définie, alors
RuleBase.backwardChain () s'appelle pour déterminer sa valeur. Si une valeur ne peut pas
être impliquée, l'utilisateur est incité pour une valeur suivre la méthode de
RuleVariable.askUser (). Une fois que l'utilisateur fournit une valeur, la clause est examinée
suivre la méthode de Clause.check (). Si la clause est vraie, nous continuons par la boucle à
évaluer la prochaine clause. Si elle était fausse, nous sortons, rapportant que la valeur de
vérité de la règle est fausse parce qu'une des clauses antécédentes est fausse. Si nous obtenons
LY_SII __ 94 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

par la boucle entière, alors toutes les clauses antécédentes sont vraies, ainsi nous plaçons et
renvoyons vrai comme valeur de vérité de la règle.

// determine if a rule is true or false


// by recursively trying to prove its antecedent clauses are true
// if any are false, the rule is false
Boolean backChain()
{
RuleBase.appendText(“\nEvaluating rule ” + name) ;
for (int i=0; i < antecedents.length; i++) { // test each clause
if (antecedents[i].truth == null)
rb.backwardChain(antecedents[i].lhs.name);
if (antecedents[i].truth == null) { // couldn’t prove t or f
antecedents[i].lhs.askUser() ; // so ask user for help
truth = antecedents[i].check() ; // redundant?
}
if (antecedents[i].truth.booleanValue() == true) {
continue ; // test the next antecedent (if any)
} else {
return truth = new Boolean(false) ; // exit, one is false
}
}
return truth = new Boolean(true) ; // all antecedents are true
}

Exécution d'applet de règle

Notre applet de règle emploie les classes de RuleBase, de règle, et de RuleVariable et


fournit une interface graphique pour l'expérimentation. Le dialogue principal d'applet a été
conçu using Symantec CafŽ visuel mais ce code pourrait avoir été mis en application
manuellement ou avec un autre outil de constructeur de GUI de Java. La classe de
RuleApplet est une sous-classe de la classe d'applet.

Les six premières méthodes traitent des actions d'utilisateur sur le sélecteur bien choisi de
base de règle de trois commandes-le, le sélecteur variable, et la valeur sélecteur-et les trois
boutons-le trouvent, des boutons de démo de course, et de remise respectivement.

La méthode d'init () contient une section de code produite par le constructeur visuel visuel de
CafŽ pour initialiser les commandes d'awt d'applet. Au fond de la méthode d'init (), nous
instancions une armature pour être le parent du RuleVarDialog, initialisons la commande
bien choisie de base de règle, et puis instancions et initialisons les trois objets de RuleBase
d'exemple. () La méthode handleEvent conduit les actions d'utilisateur aux méthodes de
événement-manipulation appropriées. Noter que c'est modèle de événement-manipulation de
Java 1.0.2. Le modèle d'événement de Java 1.1 est très différent, mais Symantec n'a pas eu le
soutien de Java 1.1 quand nous avons écrit ce code. Après, les commandes d'awt sont
assignées par code produit CafŽ, et nous fournissons des variables statiques de membre pour
tenir notre armature et exemples de RuleBase. Le membre de currentRuleBase indique
toujours la base choisie de règle dans la commande choice1.

/* RuleApplet class

Constructing Intelligent Agents with Java


(C) Joseph P.Bigus and Jennifer Bigus 1997
LY_SII __ 95 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

*/

import java.awt.*;
import java.applet.*;
import java.util.* ;

public class RuleApplet extends Applet {

// user selected a rule base


void choice1_Clicked() {
String rbName = choice1.getSelectedItem() ;
if (rbName.equals(“Vehicles”)) currentRuleBase = vehicles ;
if (rbName.equals(“Bugs”)) currentRuleBase = bugs ;
if (rbName.equals(“Plants”)) currentRuleBase = plants ;

currentRuleBase.reset() ; // reset the rule base


Enumeration vars = currentRuleBase.variableList.elements() ;
while (vars.hasMoreElements()) {
choice2.addItem(((RuleVariable)vars.nextElement()).name) ;
}
currentRuleBase.displayVariables(textArea3) ;

// user selected a variable


void choice2_Clicked(Event event) {
String varName = choice2.getSelectedItem() ;
choice3.removeAll() ;

RuleVariable rvar =
(RuleVariable)currentRuleBase.variableList.get(varName);
Enumeration labels = rvar.labels.elements();
while (labels.hasMoreElements()) {
choice3.addItem(((String)labels.nextElement())) ;
}
}
// user selected a value for a variable
void choice3_Clicked(Event event) {
String varName = choice2.getSelectedItem() ;
String varValue = choice3.getSelectedItem() ;

RuleVariable rvar =
(RuleVariable)currentRuleBase.variableList.get(varName);
rvar.setValue(varValue) ;
textArea3.appendText(“\n”+ rvar.name + “ set to ”+ varValue) ;
}

// user pressed Find button -- do an infernece cycle


void button1_Clicked(Event event) {
String goal = textField1.getText() ;

textArea2.appendText(“\n --- Starting Inferencing Cycle --- \n”);


currentRuleBase.displayVariables(textArea2) ;
if (radioButton1.getState() == true)
currentRuleBase.forwardChain();
if (radioButton2.getState() == true)
currentRuleBase.backwardChain(goal);
currentRuleBase.displayVariables(textArea2) ;
textArea2.appendText(“\n --- Ending Inferencing Cycle --- \n”);
}
LY_SII __ 96 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

// user pressed Demo button -- do inference with pre-set values


void button2_Clicked(Event event) {
String rbName = choice1.getSelectedItem() ;
if (rbName.equals(“Vehicles”)) {
if (radioButton1.getState() == true)
demoVehiclesFC(vehicles);
if (radioButton2.getState() == true)
demoVehiclesBC(vehicles);
} else if (rbName.equals(“Bugs”)) {
if (radioButton1.getState() == true) demoBugsFC(bugs);
if (radioButton2.getState() == true) demoBugsBC(bugs);
} else {
if (radioButton1.getState() == true) demoPlantsFC(plants);
if (radioButton2.getState() == true) demoPlantsBC(plants);
}
}

// User press the Reset button


void button3_Clicked(Event event) {

//{{CONNECTION
// Clear the text for TextArea
textArea1.setText(“”);
textArea2.setText(“”);
textArea3.setText(“”);
//}}

currentRuleBase.reset() ;
currentRuleBase.displayRules(textArea1);
currentRuleBase.displayVariables(textArea3) ;
}

public void init() {


super.init();

// Note, this code is generated by Visual Cafe’


//{{INIT_CONTROLS
setLayout(null);
addNotify();
resize(624,527);
button1 = new java.awt.Button(“Find Goal”);
button1.reshape(36,468,108,30);
add(button1);
button2 = new java.awt.Button(“Run Demo”);
button2.reshape(228,468,108,30);
add(button2);
button3 = new java.awt.Button(“Reset”);
button3.reshape(444,468,108,30);
add(button3);
textArea1 = new java.awt.TextArea();
textArea1.reshape(12,48,312,144);
add(textArea1);
textArea2 = new java.awt.TextArea();
textArea2.reshape(12,216,600,168);
add(textArea2);
label2 = new java.awt.Label(“Trace Log”);
label2.reshape(24,192,168,24);
add(label2);
label1 = new java.awt.Label(“Rule Base”);
LY_SII __ 97 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

label1.reshape(24,12,96,24);
add(label1);
choice1 = new java.awt.Choice();
add(choice1);
choice1.reshape(132,12,192,24);
Group1 = new CheckboxGroup();
radioButton1 = new java.awt.Checkbox(“Forward Chain”,
Group1, false);
radioButton1.reshape(36,396,156,21);
add(radioButton1);
choice3 = new java.awt.Choice();
add(choice3);
choice3.reshape(480,36,135,24);
label5 = new java.awt.Label(“Value”);
label5.reshape(480,12,95,24);
add(label5);
choice2 = new java.awt.Choice();
add(choice2);
choice2.reshape(336,36,137,24);
textArea3 = new java.awt.TextArea();
textArea3.reshape(336,72,276,122);
add(textArea3);
label4 = new java.awt.Label(“Variable”);
label4.reshape(336,12,109,24);
add(label4);
radioButton2 = new java.awt.Checkbox(“Backward Chain”,
Group1, false);
radioButton2.reshape(36,420,156,24);
add(radioButton2);
textField1 = new java.awt.TextField();
textField1.reshape(324,420,142,27);
add(textField1);
label3 = new java.awt.Label(“Goal”);
label3.reshape(324,384,80,30);
add(label3);
//}}

// initialize the rule applet


frame = new Frame(“Ask User”) ;
frame.resize(50,50) ;
frame.setLocation(100,100) ;
choice1.addItem(“Vehicles”) ;
choice1.addItem(“Bugs”) ;
choice1.addItem(“Plants”) ;

vehicles = new RuleBase(“Vehicles Rule Base”) ;


vehicles.setDisplay(textArea2) ;
initVehiclesRuleBase(vehicles) ;
currentRuleBase = vehicles ;

bugs = new RuleBase(“Bugs Rule Base”) ;


bugs.setDisplay(textArea2) ;
initBugsRuleBase(bugs) ;

plants = new RuleBase(“Plants Rule Base”) ;


plants.setDisplay(textArea2) ;
initPlantsRuleBase(plants) ;

// initialize textAreas and list controls


currentRuleBase.displayRules(textArea1) ;
LY_SII __ 98 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

currentRuleBase.displayVariables(textArea3) ;
radioButton1.setState(true) ;
choice1_Clicked() ; // fill variable list
}

// Note: this is Java 1.0.2 event model


public boolean handleEvent(Event event) {
if (event.target == button1 &&
event.id == Event.ACTION_EVENT) {
button1_Clicked(event);
return true;
}if (event.target == button2 &&
event.id == Event.ACTION_EVENT) {
button2_Clicked(event);
return true;
}if (event.target == button3 &&
event.id == Event.ACTION_EVENT) {
button3_Clicked(event);
return true;
}
if (event.target == dlg &&
event.id == Event.ACTION_EVENT) {
return dlg.handleEvent(event);
}
if (event.target == choice1 &&
event.id == Event.ACTION_EVENT) {
choice1_Clicked();
return true;
}
if (event.target == choice2 &&
event.id == Event.ACTION_EVENT) {
choice2_Clicked(event);
return true;
}
if (event.target == choice3 &&
event.id == Event.ACTION_EVENT) {
choice3_Clicked(event);
return true;
}
return super.handleEvent(event);
}

// Note this code is generated by Visual Cafe’


//{{DECLARE_CONTROLS
java.awt.Button button1;
java.awt.Button button2;
java.awt.Button button3;
java.awt.TextArea textArea1;
java.awt.TextArea textArea2;
java.awt.Label label2;
java.awt.Label label1;
java.awt.Choice choice1;
java.awt.Checkbox radioButton1;
CheckboxGroup Group1;
java.awt.Choice choice3;
java.awt.Label label5;
java.awt.Choice choice2;
java.awt.TextArea textArea3;
java.awt.Label label4;
java.awt.Checkbox radioButton2;
LY_SII __ 99 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

java.awt.TextField textField1;
java.awt.Label label3;
//}}

static Frame frame ;


static RuleVarDialog dlg ;
static RuleBase bugs ;
static RuleBase plants ;
static RuleBase vehicles ;
static RuleBase currentRuleBase ;
....
// Rule base definitions

La méthode de RuleApplet.waitForAnswer () s'appelle par la méthode de RuleVariable,


askUser (), pendant l'enchaînement en arrière dans les cas où une valeur pour la variable ne
pourrait pas être déterminée par inferencing. Un exemple du RuleVarDialog est créé comme
dialogue modal (le schéma 4.3). La corde prompte, avec une liste des valeurs valides, et un
TextField simple sont montrés dans un petit dialogue. Quand l'utilisateur écrit une valeur
pour la variable et clique dessus le bouton poussoir d'ensemble, la valeur variable est
retournée et le dialogue est fermé.

// display dialog to get user value for a variable


static public String waitForAnswer(String prompt, String labels) {

// position dialog over parent dialog


Point p = frame.getLocation() ;
dlg = new RuleVarDialog(frame, true) ;
dlg.label1.setText(“ ” + prompt + “\n (” + labels + “)”);
dlg.setLocation(400, 250) ;
dlg.show() ;
String ans = dlg.getText() ;
return ans ;
}

Le schéma 4.3 le dialogue de RuleVar.

L'exécution de base de règle de véhicules la base de règle de véhicules est définie suivre la
méthode de RuleApplet.initVehiclesRuleBase (). L'exemple de RuleBase est passé dedans
comme seul argument. La méthode instancie d'abord les membres de goalClauseStack et de
variableList. Chaque RuleVariable est alors défini en créant un exemple avec le nom
variable comme argument sur le constructeur. Les valeurs valides sont placées suivre la
méthode de setLabels (). Le texte prompt, utilisé dans le RuleVarDialog pendant
l'enchaînement en arrière, est placé suivre la méthode de setPromptText (). Chaque
RuleVariable est également ajouté directement au variableList de RuleBase.

Après que toutes les variables soient définies, quelques exemples de condition ne sont créés
pour des égales, pas égales, et moins que. Le membre de ruleList est instancié, et chaque
règle dans le RuleBase est instanciée. Les constructeurs de règle prennent une référence de
RuleBase, la corde de nom de règle, et deux clauses ou plus. La dernière clause est la clause
conséquente et toutes les clauses antérieures sont des clauses antécédentes.
LY_SII __ 100 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

// initialize the Vehicles rule base


public void initVehiclesRuleBase(RuleBase rb) {
rb.goalClauseStack = new Stack() ; // goals and subgoals

rb.variableList = new Hashtable() ;


RuleVariable vehicle = new RuleVariable(“vehicle”) ;
vehicle.setLabels(“Bicycle Tricycle MotorCycle Sports_Car Sedan MiniVan
Sports_Utility_Vehicle”) ;
vehicle.setPromptText(“What kind of vehicle is it?”);

rb.variableList.put(vehicle.name,vehicle) ;
RuleVariable vehicleType = new RuleVariable(“vehicleType”) ;
vehicleType.setLabels(“cycle automobile”) ;
vehicleType.setPromptText(“What type of vehicle is it?”) ;
rb.variableList.put(vehicleType.name, vehicleType) ;

RuleVariable size = new RuleVariable(“size”) ;


size.setLabels(“small medium large”) ;
size.setPromptText(“What size is the vehicle?”) ;
rb.variableList.put(size.name,size) ;

RuleVariable motor = new RuleVariable(“motor”) ;


motor.setLabels(“yes no”) ;
motor.setPromptText(“Does the vehicle have a motor?”) ;
rb.variableList.put(motor.name,motor) ;

RuleVariable num_wheels = new RuleVariable(“num_wheels”) ;


num_wheels.setLabels(“2 3 4”) ;
num_wheels.setPromptText(“How many wheels does it have?”);
rb.variableList.put(num_wheels.name,num_wheels) ;

RuleVariable num_doors = new RuleVariable(“num_doors”) ;


num_doors.setLabels(“2 3 4”) ;
num_doors.setPromptText(“How many doors does it have?”) ;
rb.variableList.put(num_doors.name,num_doors) ;

// Note: at this point all variables values are NULL

Condition cEquals = new Condition(“=”) ;


Condition cNotEquals = new Condition(“!=”) ;
Condition cLessThan = new Condition(“<”) ;

// define rules
rb.ruleList = new Vector() ;
Rule Bicycle = new Rule(rb, “bicycle”,
new Clause(vehicleType,cEquals, “cycle”) ,
new Clause(num_wheels,cEquals, “2”),
new Clause(motor, cEquals, “no”),
new Clause(vehicle, cEquals, “Bicycle”)) ;

Rule Tricycle = new Rule(rb, “tricycle”,


new Clause(vehicleType,cEquals, “cycle”) ,
new Clause(num_wheels,cEquals, “3”),
new Clause(motor, cEquals, “no”),
new Clause(vehicle, cEquals, “Tricycle”)) ;

Rule Motorcycle = new Rule(rb, “motorcycle”,


new Clause(vehicleType,cEquals, “cycle”) ,
new Clause(num_wheels,cEquals, “2”),
new Clause(motor,cEquals, “yes”),
LY_SII __ 101 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

new Clause(vehicle,cEquals, “Motorcycle”)) ;

Rule SportsCar = new Rule(rb, “sportsCar”,


new Clause(vehicleType,cEquals, “automobile”) ,
new Clause(size,cEquals, “small”),
new Clause(num_doors,cEquals, “2”),
new Clause(vehicle,cEquals, “Sports_Car”)) ;

Rule Sedan = new Rule(rb, “sedan”,


new Clause(vehicleType,cEquals, “automobile”) ,
new Clause(size,cEquals, “medium”),
new Clause(num_doors,cEquals, “4”),
new Clause(vehicle,cEquals, “Sedan”)) ;

Rule MiniVan = new Rule(rb, “miniVan”,


new Clause(vehicleType,cEquals, “automobile”) ,
new Clause(size,cEquals, “medium”),
new Clause(num_doors,cEquals, “3”),
new Clause(vehicle,cEquals, “MiniVan”)) ;

Rule SUV = new Rule(rb, “SUV”,


new Clause(vehicleType,cEquals, “automobile”) ,
new Clause(size,cEquals, “large”),
new Clause(num_doors,cEquals, “4”),
new Clause(vehicle,cEquals, “Sports_Utility_Vehicle”)) ;

Rule Cycle = new Rule(rb, “Cycle”,


new Clause(num_wheels,cLessThan, “4”) ,
new Clause(vehicleType,cEquals, “cycle”)) ;

Rule Automobile = new Rule(rb, “Automobile”,


new Clause(num_wheels,cEquals, “4”) ,
new Clause(motor,cEquals, “yes”),
new Clause(vehicleType,cEquals, “automobile”)) ;

La méthode de RuleApplet.demoVehiclesFC () s'appelle quand les véhicules ordonnent la base


et le bouton par radio d'enchaînement vers l'avant sont choisis et courir le bouton poussoir de
démo est cliqué. Les variables de véhicule et de vehicleType sont placées pour annuler, et les
autres variables sont placées pour indiquer que le véhicule est un monospace. Les
arrangements de valeur variable sont montrés dans le panneau de trace, la méthode de
RuleBase.forwardChain () s'appelle, et les valeurs variables en résultant sont montrées dans le
panneau de trace.

public void demoVehiclesFC(RuleBase rb) {

textArea2.appendText(“\n --- Starting Demo ForwardChain ---\n ”) ;


// should be a Mini-Van
((RuleVariable)rb.variableList.get(“vehicle”)).setValue(null) ;
((RuleVariable)rb.variableList.get(“vehicleType”)).setValue(null) ;
((RuleVariable)rb.variableList.get(“size”)).setValue(“medium”) ;
((RuleVariable)rb.variableList.get(“num_wheels”)).setValue(“4”) ;
((RuleVariable)rb.variableList.get(“num_doors”)).setValue(“3”) ;
((RuleVariable)rb.variableList.get(“motor”)).setValue(“yes”) ;
rb.displayVariables(textArea2) ;
rb.forwardChain() ; // chain until quiescence...
textArea2.appendText(“\n --- Stopping Demo ForwardChain! ---\n”) ;
LY_SII __ 102 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

rb.displayVariables(textArea2);
}

The RuleApplet.demoVehiclesBC() method is called when the vehicles rule base and the
backward chaining radio button are selected and the Run Demo pushbutton is clicked. The
vehicle and vehicleType variables are set to null, and the other variables are set to indicate that
the vehicle is a MiniVan. The variable value settings are displayed in the trace panel, the
RuleBase.backwardChain() method is called, and the resulting variable values are displayed
in the trace panel.

public void demoVehiclesBC(RuleBase rb) {


textArea2.appendText(“\n --- Starting Demo BackwardChain ---\n ”) ;
// should be a minivan
((RuleVariable)rb.variableList.get(“vehicle”)).setValue(null) ;
((RuleVariable)rb.variableList.get(“vehicleType”)).setValue(null) ;
((RuleVariable)rb.variableList.get(“size”)).setValue(“medium”) ;
((RuleVariable)rb.variableList.get(“num_wheels”)).setValue(“4”) ;
((RuleVariable)rb.variableList.get(“num_doors”)).setValue(“3”) ;
((RuleVariable)rb.variableList.get(“motor”)).setValue(“yes”) ;
rb.displayVariables(textArea2) ;
rb.backwardChain(“vehicle”) ; // chain until quiescence...
textArea2.appendText(“\n --- Stopping Demo BackwardChain! ---\n ”) ;
rb.displayVariables(textArea2) ;
}

La méthode de RuleApplet.demoVehiclesBC () s'appelle quand les véhicules ordonnent la base


et le bouton par radio d'enchaînement en arrière sont choisis et courir le bouton poussoir de
démo est cliqué. Les variables de véhicule et de vehicleType sont placées pour annuler, et les
autres variables sont placées pour indiquer que le véhicule est un monospace. Les
arrangements de valeur variable sont montrés dans le panneau de trace, la méthode de
RuleBase.backwardChain () s'appelle, et les valeurs variables en résultant sont montrées dans
le panneau de trace.

public void demoVehiclesBC(RuleBase rb) {


textArea2.appendText(“\n --- Starting Demo BackwardChain ---\n ”) ;
// should be a minivan
((RuleVariable)rb.variableList.get(“vehicle”)).setValue(null) ;
((RuleVariable)rb.variableList.get(“vehicleType”)).setValue(null) ;
((RuleVariable)rb.variableList.get(“size”)).setValue(“medium”) ;
((RuleVariable)rb.variableList.get(“num_wheels”)).setValue(“4”) ;
((RuleVariable)rb.variableList.get(“num_doors”)).setValue(“3”) ;
((RuleVariable)rb.variableList.get(“motor”)).setValue(“yes”) ;
rb.displayVariables(textArea2) ;
rb.backwardChain(“vehicle”) ; // chain until quiescence...
textArea2.appendText(“\n --- Stopping Demo BackwardChain! ---\n ”) ;
rb.displayVariables(textArea2) ;
}

L'exécution des autres bases de deux règles équipées d'applet de règle, de bogues et de bases
de règle d'usines, est énumérée dans l'annexe A.

Cela apporte à une fin notre discussion de l'applet de règle. Tout les code décrit en ce chapitre
est inclus sur le disque compact-ROM. L'applet et les classes fondamentales de RuleBase, de
règle, et de RuleVariable fournissent la fonctionnalité de base. Quelques perfectionnements
LY_SII __ 103 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

évidents viennent à l'esprit. Ceux-ci incluent le soutien des objets du côté à gauche et droit
des clauses. Dans la classe de règle, il ferait beau de fournir l'appui pour les sondes et les
effecteur (qui pourraient être utiles dans la partie de ce livre). Le soutien des exemples
multiples des variables et du temps-estampillage des données prolongerait considérablement
les possibilités de chaînage avant. Ajouter des priorités de règle serait également utile pour de
plus grandes bases de règle. S'ajouter a augmenté la découverte et comment et pourquoi le
soutien de l'algorithme de vers l'arrière-enchaînement serait un contact gentil. Bien qu'il y ait
beaucoup plus nous pourrions faire, il est important d'identifier que même ce niveau de la
fonctionnalité est capable de produire le comportement utile dans les applications, ou car nous
verrons, dans les agents intelligents.

Dans le reste de ce chapitre, nous changeons notre foyer de l'exécution en discussion plus à
niveau élevé des systèmes brouillés et puis de la planification de règle. Des systèmes brouillés
de règle sont employés pour exécuter un genre d'enchaînement vers l'avant, mais le système
de logique fondamental est basé sur la logique floue, logique non booléenne. La planification
est une matière intéressante, parce que pour n'importe quoi autre que des tâches simples, nos
agents intelligents devront établir des plans et les exécuter afin d'effectuer le travail utile pour
nous.

Systèmes brouillés de règle

Dans l'histoire riche du raisonnement basé sur les règles dans l'AI, les moteurs d'inférence
presque sans exception ont été basés sur la logique booléenne ou binaire. Cependant, de la
même manière ce les réseaux neurologiques ont enrichi le paysage d'AI en fournissant une
alternative aux techniques de traitement de symbole, logique floue a fourni une alternative
aux systèmes logique-basés booléens (Bigus 1996).

À la différence de la logique booléenne, qui a seulement deux affaires de logique floue d'états,
vraie ou fausse, avec les valeurs de vérité qui s'étendent sans interruption de 0 à 1. Ainsi
quelque chose pourrait être 0.5 à moitié vrai ou très probable rectifier 0.9 ou pas rectifier
probablement 0.1. L'utilisation de la logique floue dans des impacts de systèmes de
raisonnement non seulement le moteur d'inférence mais la représentation de connaissance lui-
même (Zadeh 1994). Pour, au lieu de faire des distinctions arbitraires entre les variables et les
états, comme est exigé avec des systèmes de logique booléenne, la logique floue permet à on
d'exprimer la connaissance en format de règle qui est proche d'une expression de langage
naturel. Par exemple, nous pourrions dire :

si la température est chaude et l'humidité est collante alors fan_speed est haute

La différence entre cette règle brouillée et les règles de Booléen-logique que nous avons
employées dans notre vers l'avant et le vers l'arrière-enchaînement des exemples est que les
clauses la « température est chaude » et la « humidité est collante » ne sont pas strictement
vraie ou fausse. Les clauses dans des règles brouillées sont des fonctions à valeurs réelles
appelées les fonctions d'adhésion qui tracent l'ensemble brouillé « chaud » sur le domaine de
la « température » variable brouillée et produisent une vérité-valeur qui s'étend de 0.0 à 1.0
(une valeur continue de rendement, tout comme les réseaux neurologiques).

Les motifs avec les systèmes brouillés de règle sont un procédé de chaînage avant. Les
valeurs de données numériques initiales fuzzified, c.-à-d., s'est transformé en valeurs
LY_SII __ 104 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

brouillées using les fonctions d'adhésion. Au lieu d'une phase d'allumette et de résolution du
conflit où nous choisissons une règle déclenchée pour mettre le feu, dans les systèmes
brouillés, à toutes les règles sont évalués, parce que toutes les règles brouillées peuvent être
vraies à un certain degré (s'étendant de 0.0 à 1.0). Les valeurs de vérité antécédentes de clause
sont combinées using des opérateurs de logique floue (une conjonction brouillée ou et
l'opération prend la valeur minimum des deux clauses brouillées). Après, les ensembles
brouillés spécifiques dans les clauses conséquentes de toutes les règles sont combinés, using
les valeurs de vérité de règle comme facteurs de cadrage. Le résultat est un ensemble brouillé
simple, qui defuzzified alors pour renvoyer une valeur croquante de rendement.

Planification

La planification est l'un des problèmes les plus complexes que l'AI a essayé de résoudre.
Cependant, elle est une des plus utile dans cela que la plupart des problèmes non triviaux
exigent un certain ordre commandé des opérations et d'une série de techniques. La
planification implique plusieurs aspects de la résolution des problèmes. Est d'abord la
décomposition d'un grand problème dans de plus petits, plus facilement résolus sous-
problèmes. En second lieu tient compte des contraintes sur l'ordre de la solution des sous-
problèmes de sorte que nous ne défassions pas le travail que nous avons effectué dans l'étape
précédente. Le bout est le défi de maintenir l'état du monde car nous progressons vers une
solution (Russell et Norvig 1995).

Un aspect important à se rappeler est que la planification n'est pas recherche. Nous n'essayons
pas simplement de traverser un graphique à la recherche d'un noeud simple de but ou de
trouver un chemin à un noeud de but. Dans la planification, nous calculons plusieurs étapes
d'une approche de résolution des problèmes sans les exécuter. En outre, il n'est parfois pas
possible de commencer au début et de procéder linéairement à une solution. Il peut y avoir des
discontinuités ou des coupures dans notre plan que nous ne pouvons pas surmonter. Par
exemple, comment obtenons-nous d'A à B quand il n'y a aucun chemin entre ces points ? Afin
de prévoir, nous devons avoir un modèle interne du monde de sorte que nous puissions
proposer des opérations et prévoir ce que seront les résultats, et s'ils nous amèneront plus près
du but ou pas.

Semblable à n'importe quel algorithme de recherche, un système de planification doit pouvoir


choisir la meilleure action ou opération pour exécuter pour une situation donnée. Il doit
pouvoir modeler ou prévoir ce qui la conséquence de la prise que l'action sera. Il doit détecter
quand il a atteint le but, ou se rendre compte quand il a atteint un cul-de-sac et doit essayer un
nouveau plan d'attaque.

Peut-être la plus grande différence entre un problème de planification et un problème de


recherche simple est que l'espace d'état est si grand que vous ne pouvez pas porter l'état
complet avec vous à chaque noeud de décision. La taille fine de l'espace nous fait doit
maintenir seulement les choses qui changent explicitement en raison d'une certaine mesure
que nous prenons. Ce problème s'appelle le problème d'armature en littérature d'intelligence
artificielle.
LY_SII __ 105 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Il y a trois approches importantes à la planification que nous discuterons brièvement. Est


d'abord la planification de pile de but, qui est une technique de résolution des problèmes
linéaire, est après une méthode non linéaire, et finalement nous regarderons un algorithme
hiérarchique de planification.

La planification de pile de but était une tentative tôt à résoudre les problèmes qui pourraient
être divisés vers le haut en plus petits problèmes, et où il y avait des contraintes ou des
interactions entre les étapes dans le plan. Le problème de recherches s'est concentré sur un
monde de blocs, où le but était simplement d'arranger les blocs dans un ordre spécifique.
Tandis qu'apparemment insignifiant (âgé de deux ans peut faire la tâche), ce problème de
jouet illustrait des conditions essentielles pour des stratégies réussies de planification.

Donné une première configuration des blocs empilés sur l'un l'autre, ou se trouvant sur une
table, nous spécifions une configuration désirée de but. Nous avons un ensemble défini
d'opérateurs pour les blocs en mouvement, pour les prendre, pour les déplacer, ou pour les
empiler sur d'autres blocs. Notre plan est de développer l'ordre des opérateurs qui va de notre
état initial à notre état de but. La planification de pile de but, comme nom implique, emploie
une pile pour tenir des subgoals ou des buts intermédiaires que nous résolvons comme sous-
problèmes. Un problème majeur avec la planification de pile de but est que nous pouvons
déployer beaucoup d'effort résolvant des parties du problème tout en rendant d'autres pièces
plus difficiles, sinon impossible, pour résoudre. La faiblesse principale est que la planification
de pile de but essaye de résoudre le problème et les sous-problèmes linéairement, dans l'ordre,
et ne tient pas compte de comment l'ordre des actions peut effectuer le progrès vers l'objectif
ultime.

Plusieurs techniques non linéaires ont été développées afin d'éviter les problèmes avec des
techniques linéaires de planification telles que la planification de pile de but. Les approches
de planification non linéaires peuvent expliquer les interactions entre les opérateurs et les
subgoals. Elles construisent un plan où deux subgoals ou plus peuvent procéder
simultanément. Les systèmes non linéaires célèbres de planification d'AI incluent NOÉ
(Sacerdoti 1974) et MOLGEN (Stefik 1981). Une technique appelée la signalisation de
contrainte permet à des plans non linéaires d'être construits incrémentalement, avec un
ensemble d'opérateurs avec l'ordre incomplètement spécifique et les attaches variables. Plutôt
qu'appliquent un opérateur simple et puis mettent à jour l'espace comme dans la planification
ou la recherche du l'état-espace, la signalisation de contrainte permet les opérateurs multiples
et les attaches variables à spécifier dans un noeud simple.

L'analyse Means-ends est employée pour choisir des opérations le long de la manière. Chaque
opérateur a un ensemble de conditions préalables qui doivent tenir afin de l'opérateur pour
applicables, et un ensemble de postconditions qui spécifient les changements à l'état une fois
l'opérateur est appliqué. Ceci pré/la liste état de poteau vient à bout le problème d'armature
parce qu'il énonce explicitement quel sous-ensemble d'états doit se tenir et quel états seront
changés.

La troisième classe principale des algorithmes de planification, planification hiérarchique,


permet à des plans de procéder à des niveaux plus élevés d'abstraction afin d'esquisser dehors
les solutions faisables sans entrer immédiatement dans les détails. Le système d'ABSTRIPS
(Sacerdoti 1974) a employé des valeurs de criticalité pour ranger l'importance de diverses
conditions préalables sur des opérateurs. L'idée est à d'abord résolvent le problème entier de
LY_SII __ 106 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

planification tenant compte seulement de ceux aux niveaux les plus élevés de la criticalité, et
procéder alors à une granularité plus fine et plus fine en ajoutant les opérateurs additionnels
pour satisfaire les conditions préalables aux niveaux plus bas. Tandis que l'arrangement
approprié des valeurs de criticalité est crucial, les planificateurs hiérarchiques se sont avérés
être l'une des techniques les plus pratiques pour des systèmes de planification.

Résumé

En ce chapitre nous nous sommes concentrés sur des techniques de raisonnement utilisées
avec si puis des règles. Les questions principales incluent :

• Si alors les règles sont les plus réussies former de la représentation de connaissance.
Il est facile pour des personnes créer et les comprendre.
• Une règle a deux parts, si ou partie antécédente, et la cloison puis ou conséquente.
Une condition dans la règle s'appelle une clause. Si toutes les clauses antécédentes
d'une règle sont vraies, elle est déclenchée pour mettre le feu. Quand les feux d'une
règle, sa clause conséquente est rendus vrais et un nouveau fait est ajouté à la base de
connaissance.
• Des facteurs de certitude ou de confiance sont employés comme modificateurs de
règle dans les situations où la validité des données ou l'applicabilité de la règle est
incertaine.
• Les systèmes de raisonnement sont monotoniques, signification qu'ils ajoutent
seulement jamais de nouveaux faits à la mémoire temporaire de travail, ou non
monotoniques, où ils peuvent rétracter des faits quand si avec la nouvelle évidence.
Les systèmes non monotoniques de raisonnement doivent traiter la maintenance de
l'intégrité, et les dépendances de voie entre les faits dans la mémoire temporaire de
travail.
• L'enchaînement vers l'avant est une méthode inferencing data-driven using des
règles. Commençant par un premier ensemble de faits dans la mémoire temporaire de
travail, la phase d'allumette choisit l'ensemble de conflit de règles qui sont prêtes à
mettre le feu. La résolution du conflit choisit une règle simple mettre le feu. La phase
d'acte met le feu à la règle et ajoute un nouveau fait à la mémoire temporaire de
travail. Ce processus est répété jusqu'à ce que l'ensemble de conflit soit vide.
• L'enchaînement en arrière est une méthode inferencing but-conduite using des
règles. Commençant par une variable ou une clause de but, les chaînes d'algorithme
par les règles des clauses conséquentes aux clauses antécédentes, essayant de prouver
la règle vraie.
• Nous avons mis en application un applet de règle qui emploie des classes de
RuleBase, de règle, et de RuleVariable pour effectuer l'enchaînement vers l'avant et en
arrière dans Java.
• Les systèmes brouillés sont des systèmes de règle qui emploient la logique floue
plutôt que la logique booléenne pour prendre des décisions.
• La planification est une condition fondamentale pour les agents intelligents. La
planification est difficile parce qu'il y a parfois des interactions entre les sous-
problèmes ou les morceaux du plan. Trois types importants d'algorithmes de
planification ont été étudiés. Ceux-ci incluent linéaire, non linéaire, et hiérarchique.

Exercices
LY_SII __ 107 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

1. Using la base de règle de véhicules, marcher manuellement pendant un cycle de


vers l'arrière-enchaînement d'inférence avec le but des véhicules = de la berline. À
combien de fois avez-vous dû demander l'utilisateur pour fournir une valeur pour une
variable inconnue tout en inferencing ?
2. Ouvrir votre visionneuse Java-permise de web browser ou d'applet sur le dossier de
RuleApplet.html. Choisir la base de règle de véhicules et placer les variables de sorte
que le véhicule soit identifié comme berline. Faire ceci using l'enchaînement vers
l'avant et l'enchaînement en arrière. Quelles étaient les différences entre les deux ?
Pour l'enchaînement en arrière, les résultats de trace comparent-ils pour exercer 4.1 ?
3. Comment pourriez-vous prolonger la classe de clause aux sondes de soutien et des
effecteur ? Que d'autres classes devraient-elles être modifiées ou être prolongées pour
soutenir ces fonctions ?
LY_SII __ 108 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Chapitre 5
Systèmes du Learning
En chapitre 5, nous nous concentrons sur des techniques d'étude adaptatives de
logiciel et de machine. Nous commençons par une vue d'ensemble des différents
paradigmes pour effectuer l'étude de machine, y compris l'étude dirigée, non
surveillée, et de renfort. Nous fournissons une introduction générale aux réseaux
neurologiques et puis une discussion plus détaillée de la propagation arrière et
des cartes de Kohonen. Après, nous explorons l'utilisation de la théorie de
l'information de construire des arbres de décision des données. Nous concevons et
mettons en application un Java applet Et les classes de Java correspondantes
pour la propagation arrière, les cartes de Kohonen, et les algorithmes d'arbre de
décision. Durer, nous regardent les systèmes de classificateur, qui ajoutent l'étude
aux systèmes basés sur les règles using des algorithmes génétiques.

Vue d'ensemble

Un élément central du comportement intelligent est la capacité de s'adapter ou apprendre de


l'expérience. Pour tous les algorithmes sophistiqués de représentation et de raisonnement de
connaissance que nous développons, il n'y a aucune manière que nous pouvons savoir a priori
toutes les situations que notre agent intelligent rencontrera. Ainsi, pouvoir s'adapter aux
changements de l'environnement ou devenir meilleur aux tâches par l'expérience va bien à un
différentiateur significatif pour n'importe quel système logiciel. N'importe quel agent qui peut
apprendre a un avantage d'un qui ne peut pas. Ajoutant l'étude ou le comportement adaptatif à
un agent intelligent l'élève à un de plus haut niveau de la capacité. Un agent de étude peut
s'adapter à vos goûts et aversions. Il peut apprendre quels agents à faire confiance et coopérer
avec, et lesquels à éviter. Un agent de étude peut identifier des situations qu'il a étées dedans
avant et améliorer son exécution basée sur une expérience antérieure.

Il y a beaucoup de formes d'étude. D'abord, il y a étude par coeur, où un exemple est donné et
les copies d'étudiant (agent intelligent) l'exemple et reproduit exactement le comportement.
Tandis que l'étude par coeur est une forme simple d'étude elle peut encore être puissante. Par
exemple, nous pourrions courir une simulation 24 heures sur 24, 7 jours par semaine, pour
produire de nouvelles situations et du comportement désiré, puis présentons ces exemples à
notre agent. Notre agent pourrait mieux répondre aux situations pendant une semaine après
que nous avons commencé la formation et serait encore un meilleur un mois plus tard
(assumer la connaissance additionnelle n'a pas ralenti ses temps de réponse).

Une autre forme d'étude est ajustement de paramètre ou de poids. Dans ce cas-ci, nous
pouvons savoir a priori quels facteurs sont importants dans une certaine décision, mais nous
ne savons pas peser leur contribution à la réponse. Dans ce cas-ci, nous pouvons ajuster les
facteurs de pondération avec le temps de sorte que nous améliorions la probabilité d'une
décision correcte ou la produisions. Cette technique sert de base à l'étude de réseau
neurologique.

L'induction est un processus d'apprendre par exemple où nous essayons d'extraire les
caractéristiques importantes du problème nous permettant de ce fait de généraliser aux
LY_SII __ 109 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

situations ou aux entrées originales. Les arbres de décision et les réseaux neurologiques tous
les deux effectuent l'induction et peuvent être employés pour des problèmes de classification
ou de régression (prévision). L'aspect clé des méthodes inductives est que les exemples sont
transformés et automatiquement transformés en forme interne (représentation de
connaissance) qui capture l'essence du problème.

Un autre type d'étude s'appelle groupement, chunking, ou abstraction de la connaissance.


Tandis que les gens apprennent des exemples ou des situations très spécifiques, la capacité de
détecter les modèles communs et de généraliser à de nouvelles situations est un type d'étude.
Par chunking dix cas dans un cas plus général, nous réduisons la quantité de stockage que
nous avons besoin et également la recherche ou la durée de la transformation. Par la pensée à
des niveaux plus élevés ou plus abstraits, nous pouvons penser des « grandes pensées » sans
se faire attraper dans la confusion de million de petits détails.

Le groupement est un autre algorithme d'étude, qui est un type de chunking. Les algorithmes
de groupement regardent les données haut-dimensionnelles (données avec beaucoup
d'attributs) et les marquent pour la similitude basée sur un certain critère. Le résultat est que
chaque échantillon est assigné à un faisceau ou à un groupe avec d'autres exemples considérés
pour être « semblable. » Cette similitude a pu être employée comme manière d'assigner la
signification à ce groupe d'échantillons. Par exemple, les données de groupement des clients
professionnels peuvent avoir comme conséquence quatre faisceaux distincts. L'examen des
clients qui sont tombés dans chaque faisceau peut les montrer qu'elles sont groupées basées
sur leurs intérêts (types de produit achètent) ou par leurs modèles acquéreurs (le nombre de
visites, ventes se monte, rentabilité) ou un autre critère ce qui ne peut pas avoir été évidente
en regardant les données originales. Un autre exemple grouperait des documents que nous
avons trouvés particulièrement utile. Ceci pourrait fournir des informations valables pour
améliorer l'exécution d'un Search Engine de document la prochaine fois que nous faisons une
question.

Tous ces techniques d'étude, induction pour la classification et prévision, et groupement sont
employés dans des outils d'exploitation de données. L'exploitation de données est un
processus d'extraire valable, l'information nonobvious de grandes collectes des données
(Bigus 1996). La contribution principale de l'exploitation de données à est des modèles de
trouvaille qui n'ont pas été connus pour exister, c.-à-d., pour découvrir la nouvelle information
ou connaissance (certains se réfèrent à l'exploitation de données comme découverte de la
connaissance). Ainsi apprenant, pour l'exploitation de données, peut être considéré pendant
qu'une manière pour que les agents intelligents découvrent automatiquement la connaissance
plutôt que l'ayant prédéfinissait using la logique d'attribut, les règles, ou une autre
représentation.

Étude des paradigmes

Il y a plusieurs paradigmes importants, ou approches, à l'étude de machine. Ceux-ci incluent


étude dirigée, non surveillée, et de renfort. En outre, beaucoup de chercheurs et de créateurs
d'application combinent deux ou plus de ces approches de étude dans un système. Comment
les données de formation sont traitées est un aspect important de ces paradigmes de étude.

L'étude dirigée est la forme la plus commune d'étude et s'appelle parfois programmation par
exemple. L'agent de étude est formé en lui montrant des exemples de l'état de problème ou des
LY_SII __ 110 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

attributs avec le rendement ou l'action désiré. L'agent de étude fait une prévision basée sur les
entrées et si le rendement diffère du rendement désiré, alors l'agent est ajusté ou adapté pour
produire le rendement correct. Ce processus est répété à plusieurs reprises jusqu'à ce que
l'agent apprenne à faire des classifications ou des prévisions précises. L'étude dirigée est le
type le plus commun d'étude de machine. Les données historiques des bases de données, des
notations de sonde, ou des notations de trace sont employées souvent comme données de
formation ou d'exemple. Nous fournissons deux réalisations des algorithmes d'étude dirigés
plus tard en ce chapitre : le réseau neurologique de propagation arrière, et un arbre de
décision.

L'étude non surveillée est employée quand l'agent de étude doit identifier des similitudes entre
les entrées ou identifier des dispositifs dans les données d'entrée. Les données sont présentées
à l'agent, et elles s'adaptent de sorte qu'elles divisent les données dans des groupes. Le
processus de groupement ou de segmentation continue jusqu'à ce que l'agent place les mêmes
données dans le même groupe sur les passages successifs au-dessus des données. Un
algorithme d'étude non surveillé exécute un type de détection de dispositif où des attributs
communs importants dans les données sont extraits. Nous présentons une exécution de réseau
neurologique d'une technique d'étude non surveillée appelée une carte de Kohonen.

L'étude de renfort est un type de diriger apprenant où l'information d'erreur est moins
spécifique. Elle peut également être employée dans les cas où il y a un ordre des entrées et le
rendement ou la mesure est seulement pris après que l'ordre spécifique se produise. Puisque
nous fournissons moins d'informations spécifiques d'erreur, le renfort apprenant prend
habituellement plus longtemps l'étude que dirigée et est moins efficace. Cependant, dans
beaucoup de situations, avoir l'information préalable exacte au sujet des résultats désirés n'est
pas possible. De plusieurs manières, l'étude de renfort est la forme la plus réaliste d'étude.

Une autre distinction importante en apprenant des agents est si l'étude est en ligne ou en
différé fait. En ligne l'étude signifie que l'agent est envoyé pour effectuer ses tâches et qu'il
peut apprendre ou s'adapter après que chaque transaction soit traitée. En ligne l'étude est
comme la formation sur le tas et les conditions graves d'endroits sur les algorithmes d'étude.
Ce doit être très rapide et très écurie (nous ne voulons pas un agent en mort cérébrale au
milieu d'une grande négociation de ventes). L'étude en différé, d'une part, est plutôt une
conférence d'affaires. Vous prenez vos vendeurs outre du plancher et les placez dans un
environnement où ils peuvent se concentrer sur améliorer leurs qualifications sans
distractions. Après une période de formation appropriée, elles sont envoyées pour appliquer la
leur connaissance récemment découverte et qualifications. Dans un contexte d'agent
intelligent, ceci signifie que nous des données de rassemblement des situations que les agents
ont éprouvées. Nous pourrions alors augmenter ces données avec des informations sur la
réponse désirée d'agent pour établir un ensemble de données de formation. Une fois que nous
avons cette base de données nous pouvons l'employer pour modifier le comportement de nos
agents.

Réseaux neurologiques

Les réseaux neurologiques fournissent une manière simple d'ajouter la capacité d'étude aux
agents. Il y a beaucoup de différents types des réseaux neurologiques, d'une partie qui
LY_SII __ 111 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

s'exercent rapidement et d'une partie qui exigent beaucoup de passages au-dessus des données
avant qu'ils apprennent les tâches assignées. Des réseaux neurologiques peuvent être
employés dans dirigé, non surveillé, et le renfort apprenant des scénarios. Ils peuvent être
employés pour la classification, le groupement, et la prévision. Dans beaucoup d'applications,
les réseaux neurologiques peuvent remplacer les systèmes experts, particulièrement où les
données suffisantes sont disponibles pour la formation.

Une pas simplement autre technique d'algorithme ou de régression d'étude, les réseaux
neurologiques réellement représentent un nouveau et différent modèle de calcul à partir des
ordinateurs périodiques de von Neumann nous que toute connaît et aiment (et employer
journalier). Empruntant fortement à la métaphore du cerveau humain, les réseaux
neurologiques ont des centaines ou des milliers de processeurs simples (appelés les unités de
traitement, traitant des éléments, ou des neurones) reliés par des centaines ou des milliers de
poids adaptatifs comme illustré sur le schéma 5.1. Ce réseau des processeurs et des
raccordements forme un ordinateur parallèle qui peut de manière adaptative se refaire
l'installation électrique quand il est exposé aux données. Les poids adaptatifs sont ajustés et
forment la mémoire de l'ordinateur de réseau neurologique, jouant le rôle que la synapse a
dans le cerveau humain. Une autre différence entre les réseaux neurologiques et les
calculateurs numériques traditionnels est que les processeurs de réseau neurologique sont
analogue, non numérique. Ils ne crachent pas dehors 1s binaire et 0s, ils produisent une
gamme continue des sorties, habituellement de 0.0 à 1.0. Le nombre infini de valeurs réelles
entre 0 et 1 peut être employé à notre avantage.

Le schéma 5.1 unité de traitement neurale d'A.

La nouvelle informatique parallèle malgré le modèle, la plupart des réalisations de réseau


neurologique sont aujourd'hui programme simplement le fonctionnement sur les ordinateurs
périodiques. Nous pouvons simuler le comportement de l'ordinateur de réseau neurologique
sur nos PCs extrêmement flexibles et puissants (et bon marché) et postes de travail. Et tandis
qu'ils pourraient faire des choses merveilleuses si mis en application dans le matériel
parallèle, les réseaux neurologiques ont prouvé utile dans beaucoup d'applications
commerciales même lorsque simulé sur les ordinateurs standard.

Propagation arrière

La propagation arrière est l'architecture la plus populaire de réseau neurologique pour l'étude
dirigée. Elle comporte une topologie de raccordement de réaction, signifiant que les données
traversent le réseau dans une direction simple, et emploient une technique appelée la
propagation en arrière des erreurs pour ajuster les poids de raccordement (Rumelhart,
Hinton, et Williams 1986). En plus d'une couche d'unités d'entrée et de rendement, un réseau
de rétropropagation peut avoir une ou plusieurs couches d'unités cachées, qui reçoivent des
entrées seulement à partir d'autres unités, pas l'environnement externe. Un réseau de
rétropropagation avec une seule couche cachée d'unités de traitement peut apprendre à
modeler n'importe quelle fonction continue une fois donné assez d'unités dans la couche
cachée. Les applications primaires des réseaux de rétropropagation sont pour la prévision et la
classification.

Le schéma 5.2 montre un diagramme d'un réseau neurologique de rétropropagation et montre


les trois étapes principales dans le processus de formation. D'abord, des données d'entrée sont
LY_SII __ 112 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

présentées à la couche d'entrée d'unités du côté gauche, et traversent le réseau jusqu'à ce


qu'elles atteignent les unités de rendement de réseau du côté droit. Ceci s'appelle le passage
vers l'avant. Les activation ou les valeurs des unités de rendement représentent le rendement
réel ou prévu du réseau. La valeur désirée de rendement est également présentée au réseau,
parce que c'est étude dirigée. Après, la différence entre le rendement désiré et réel est
calculée, produisant l'erreur de réseau. Cette limite d'erreur est alors passée vers l'arrière par le
réseau pour ajuster les poids de raccordement. Ce processus est décrit dans le détail sanglant
plus tard dans cette section.

Le schéma 5.2 réseau neurologique de propagation de dos.

Chaque unité d'entrée de réseau prend une valeur numérique simple, XI, qui est
habituellement mesurée ou normalisée à une valeur entre 0.0 et 1.0. Cette valeur devient
l'activation d'unité d'entrée. Après, nous devons propager les données en avant, par le réseau
neurologique. Pour chaque unité dans la couche cachée, nous calculons la somme des produits
des activation d'unité d'entrée et des poids reliant ceux les unités entrées de couche à la
couche cachée. Cette somme est le produit intérieur (également appelé le point ou le produit
scalaire) du vecteur d'entrée et des poids dans l'unité cachée. Une fois que cette somme est
calculée, nous ajoutons une valeur- seuil et puis passons cette somme par une fonction non
linéaire d'activation, f, produisant le yj d'activation d'unité suivant les indications du schéma
5.1. La formule pour calculer l'activation de n'importe quelle unité dans une couche cachée ou
de rendement dans le réseau est :

là où I s'étend au-dessus de toutes les unités menant dans l'unité j, et de la fonction


d'activation est

f(sumj) = 1 / 1 + e-sumj.

Le schéma 5.3 la fonction logistique d'activation.

Comme cité précédemment, nous employons la fonction sigmoïde ou logistique en forme de s


pour le F. suivant les indications du schéma 5.3, parce que une gamme des sommes de - 5 à
+5 nous obtenons une valeur de y s'étendant de 0 à 1. positif extrêmement grand ou les
sommes négatives obtiennent pressées (qu'est à dire une limite technique) dans cette même
gamme. L'effet du seuil q est de décaler la courbe en forme de s laissée ou droite. Par
exemple, si le seuil est +5, puis la somme devrait être -5 afin de produire une valeur de
rendement de 0.5.

La formule pour calculer les changements aux poids est

là où le wij est l'unité se reliante i de poids à l'unité j, le η est le paramètre de taux d'étude, le
δj est le signal d'erreur pour cette unité, et yi est le rendement ou la valeur d'activation de
l'unité i. Pour des unités dans la couche de rendement, le signal d'erreur est la différence entre
le tj de rendement de cible et le yj réel de rendement multipliés par le dérivé de la fonction
logistique d'activation.
LY_SII __ 113 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

δj = (tj - yj) f’j(sumj) = (tj - yj) yj (1 - yj)

Pour chaque unité dans la couche cachée, le signal d'erreur est le dérivé de la fonction
d'activation multipliée par la somme des produits des poids sortants de raccordement et de
leurs signaux correspondants d'erreur. Ainsi pour l'unité cachée j

là où k s'étend au-dessus des index des unités recevant le signal de sortie de l'unité j.

Une modification commune de la règle de mise à jour de poids est l'utilisation d'une limite a
d'élan, de réduire l'oscillation des poids. Ainsi, le changement de poids devient une
combinaison du changement courant de poids, calculée comme avant, plus une certaine
fraction (le α s'étend de 0 à 1) du changement précédent de poids. Ceci complique l'exécution
parce que nous maintenant devons stocker les changements de poids de l'étape antérieure.

La base mathématique pour la propagation en arrière est décrite en détail dans Rumelhart,
Hinton, et Williams (1986). Quand les changements de poids se résument (ou sont traités en
lots) au-dessus d'une présentation entière de l'ensemble de formation, la fonction de
minimisation d'erreur remplie s'appelle la descente de gradient. Dans la pratique, la plupart
des personnes mettent à jour immédiatement les poids de réseau après que chaque vecteur
d'entrée soit présenté. Tandis que des mises à jour de modèle, en tant que ceci s'appelle, peut
parfois produire le comportement indésirable, il a habituellement comme conséquence une
formation plus rapide qu'using les mises à jour en lots.

Cartes de Kohonen

Les cartes à organisation autonome de dispositif se sont développées par Tuevo Kohonen ont
devenu des modèles les plus populaires et les plus pratiques de réseau neurologique (Kohonen
1990). Une carte de Kohonen est un réseau neurologique à une seule couche, consisté en une
couche d'entrée et une couche de rendement. À la différence de la propagation arrière, qui est
un paradigme de étude dirigé, les cartes de dispositif effectuent l'étude non surveillée. Chaque
fois que un vecteur d'entrée est présenté au réseau, sa distance à chaque unité dans la couche
de rendement est calculée. De diverses mesures de distance ont été employées. Plus le terrain
communal et celui utilisés ici est juste la distance euclidienne. L'unité de rendement avec la
plus petite distance au vecteur d'entrée est déclarée le « gagnant. » L'unité de gain et un
ensemble d'unités dans les poids de voisinage sont ajustés en déplaçant les poids vers le
vecteur d'entrée. Au commencement, le voisinage et le taux de étude sont tout à fait grands et
ainsi beaucoup d'unités sont déplacées autour l'espace d'entrée pour assortir l'ensemble de
vecteurs de formation. Pendant que la formation progresse, le voisinage se rétrécit et le taux
de étude est diminué. La carte de Kohonen individu-organise avec le temps, et à la fin d'une
course réussie de formation, une carte topographique est créée. Un attribut d'une telle carte est
que les entrées qui sont près de l'un l'autre dans la carte de l'espace d'entrée sur les unités de
rendement qui sont dans la grande proximité dans la couche de rendement du réseau
neurologique.
LY_SII __ 114 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Sur le schéma 5.4, nous montrons un schéma de principe d'une carte de Kohonen. D'abord les
entrées sont présentées à la couche d'entrée. En second lieu, la distance du modèle d'entrée
aux poids à chaque unité de rendement est calculée using la formule euclidienne de distance
ci-dessous :

yj= || x - wj || 2

Le schéma 5.4 réseau neurologique de carte de Kohonen.

là où x est le vecteur d'entrée, le wj est le vecteur de poids dans l'unité de rendement j, et le yj


est la distance en résultant. L'unité de rendement j avec le yj de valeur minimum est déclarée
le gagnant. Les poids du gagnant et les unités dans son voisinage sont alors ajustés using :

là où le wj est le vecteur de poids dans l'unité j au temps précédent t et au temps courant t + 1,


α (k) est le taux d'étude à l'itération k, Cij (k) est la valeur de la fonction de voisinage pour des
unités i et j à l'itération k, et le yj est la distance euclidienne entre le vecteur X d'entrée et le
wj de vecteur de poids au temps T. Cette fonction Cij de voisinage (k) s'appelle une fonction
gaussienne et est shaped comme un chapeau mexicain ou un sombrero et est défini comme :

là où I et j sont les coordonnées des unités dans la carte bidimensionnelle, et k est le nombre
d'itération. le σ (k) 2 est la largeur de la fonction de voisinage, qui commence aussi au loin que
la carte quand k est petit et diminue à une valeur finale entourant une unité simple quand k est
à sa valeur maximum. Le α (k) le paramètre est le taux d'étude pour l'itération K. Ceci est
calculé comme :

là où la limite de kmax est le nombre maximum des itérations à exécuter. Le α de taux d'étude
(k) exponentiellement les diminutions comme nombre k d'itération devient plus grande. Nous
commençons à l'itération k = 0 avec le αinitial apprenons le taux et finissons à l'itération k =
kmax avec la valeur de αfinal. Les valeurs typiques pour αinitial et le αfinal sont 1.0 et 0.05
respectivement. Ces formules sont employées comme base pour notre exécution de Java,
décrite plus tard en ce chapitre.

Arbres de décision

Les arbres de décision effectuent l'induction sur des ensembles de données d'exemple,
produisant des classificateurs et des modèles de prévision. Un arbre de décision examine
l'ensemble de données, et emploie la théorie de l'information pour déterminer quel attribut
contient la plupart d'information sur laquelle pour baser une décision. Cet attribut est alors
employé dans un noeud de décision pour couper l'ensemble de données en deux groupes,
basés sur la valeur de cet attribut. À chaque noeud suivant de décision, l'ensemble de données
est dédoublé encore. Le résultat est un arbre de décision, une collection de noeuds. Les
noeuds de feuille représentent une classification finale du disque. Les exemples des
algorithmes d'arbre de décision sont ID3 (Quinlan 1986) et les systèmes C4.5. Une limite
LY_SII __ 115 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

générique pour la classe des algorithmes d'arbre de décision est CHARIOT, pour des arbres
de classification et de régression.

Théorie de l'information

Des arbres de décision sont basés sur la théorie de l'information, un concept mathématique
d'abord présenté par Shannon et Weaver (1949). L'unité d'information est un peu, et la
quantité de l'information dans une réponse binaire simple est log2P (v), où P (v) est la
probabilité de l'événement v se produisant. Le contenu de l'information est basé sur les
probabilités antérieures d'obtenir la réponse correcte à une question ou à une classification
(Russell et Norvig 1995). Supposer que nous essayons de classifier les clients qui
remplaceront leurs comptes de service d'Internet ou les décommanderont et commuteront à un
concurrent. Assumons-nous a eu l'année dernière 1000 clients, 800 qui ont remplacé et 200
qui ont décommandé. Nous ferions placer une formation avec 1000 disques, contenant un
certain ensemble d'attributs de ces clients (leur âge, sexe, revenu, temps de connexion
mensuel de moyenne, années comme clients, etc.) aussi bien que l'information dessus s'ils ont
remplacé ou ont décommandé leur service. Ceux qui nous ont remplacés considèrent les
exemples positifs, P. Ceux qui nous ont décommandés considèrent les exemples négatifs, N.
Si nous avions l'information sur un client et devions essayer de prévoir si ce client
remplacerait ou décommanderait, combien d'information serait contenue dans une réponse
correcte ? La formule utilisée est

Noter que le dénominateur en tous les termes est tout le nombre de disques. La première
limite est la probabilité que le cas positif se produit (p/(p + n)) s'est multiplié par la teneur en
information de ce journal d'événement 2 (p/(p + n)). La deuxième limite est une expression
équivalente appliquée aux exemples négatifs. Dans notre exemple de service d'Internet, la
chance que n'importe quel facteur simple (par exemple, âge) diviserait complètement le
groupe en ceux qui remplacerait ou l'annulation est petite. Nous devons mesurer de combien
d'informations nous avons besoin toujours après l'essai. N'importe quel attribut A qui a des
valeurs distinctes de v divise l'ensemble de données en sous-ensembles de v selon les valeurs
de l'A. Chaque sous-ensemble en résultant des données de formation a son propre maquillage
des résultats de p et de n. En moyenne, après qu'examinant l'attribut A, nous avons besoin
toujours

le peu d'information où I va de 1 à v, le nombre de valeurs discrètes qui attribuent A peut


prendre. La différence entre l'information a eu besoin avant l'essai d'attribut et le reste
s'appelle le gain de l'information de l'essai.

Gain(A) = I (p / (p + n), n / (p + n) – Remainder (A)

Comme exemple, supposer que les hommes remplacent 90 pour cent du temps, et des femmes
remplacent 70 pour cent, et que notre ensemble de client est moitié composée des hommes et
moitié des femmes. Combien de gain de l'information obtiendrions-nous simplement par
l'essai si un client est masculin ou femelle ?
LY_SII __ 116 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Gain(Sex) = 1 - [(500/1000) I(450/500, 50/500) + (500/1000) I (350/500,


150/500))]
= 1 - [(.5) I(.9, .1) + (.5) I (.7, .3)]
= 1 - [(.5) 0.468996 + (.5) 0.881291]
= 0.324857

Supposer que nous habitudes d'utilisation avions groupé clients des' dans 3 groupes : au-
dessous de 4 heures par mois, de 4 à 10 heures, et plus de 10. supposent également qu'elles
ont été également dédoublées entre tous les clients. Quand nous regardons leurs taux de
renouvellement, nous voyons que le premier groupe remplace à 50 pour cent, à la seconde à
90 pour cent, et au tiers à 100 pour cent. Quelle information obtiendrions-nous par l'essai sur
cet attribut ?

Gain(Usage) = 1 - [(.333) I(166/333, 166/333) + (.333) I (300/333, 33/333)


+ (.333) I(1,0) )]
= 1 - [(.333) I(.5, .5) + (.333) I (.9, .1) + (.333) I(1,0)]
= 1 - [(.333) 1.0 + (.333) 0.466133 + (.333) 0.0]
= 0.511778

Vous pouvez voir que dans cet exemple, le deuxième cas nous donne plus de gain de
l'information que le premier. Ainsi, si nous construisions un arbre de décision, nous voudrions
à la première fente que les données ont basée sur combien de temps de connexion elles ont
employé, et puis dessus si le client était masculin ou femelle. Ce processus de dédoubler les
données, basé sur les paires d'attribuer-valeur contenant la plupart d'information, est continué
jusqu'à ce que nous puissions classifier les données à notre degré d'exactitude désiré.

Apprendre l'applet

Dans cette section nous décrivons un applet qui démontre trois techniques d'étude. Celles-ci
incluent deux types des réseaux neurologiques et d'un classificateur d'arbre. Avant que nous
entrions dans les détails des réalisations des algorithmes d'étude, nous devons présenter
quelques classes d'aide. Le schéma 5.5 montre la disposition de notre applet de étude. Il
comporte deux zones d'exposition des textes, une pour les données employées pour former les
réseaux neurologiques ou le classificateur d'arbre, et une pour l'information de trace des
modèles elles-mêmes.

Le schéma 5.5 l'applet d'étude.

Avant que nous entrions dans les détails de l'exécution de l'applet d'étude, nous devons
présenter deux sous-classes de la classe variable pour l'usage dans les algorithmes d'étude.
Ces classes traitent spécifiquement des variables discrètes et continues et soutiennent le
comportement exigé pour la normalisation de données exigée par les algorithmes de réseau
neurologique.

Variables continues

La classe de ContinuousVariable est une sous-classe de variable. Elle fournit l'appui


nécessaire pour les variables qui peuvent prendre une valeur réelle continue s'étendant d'un
certain minimum prédéfini à une valeur maximum. Le constructeur prend le nom de la
LY_SII __ 117 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

variable comme seul paramètre. La minute et les membres maximum peuvent être placés
suivre le setMin () et des méthodes de setMax () directement, ou elles peuvent être calculées
automatiquement si le ContinuousVariable est employé en tant qu'élément d'un ensemble de
données en appelant la méthode de computeStatistics (). La méthode de normalisation () est
employée par la classe d'ensemble de données quand elle crée une version tout-numérique
d'un ensemble de données. Cette méthode fait une graduation linéaire simple de la valeur
d'entrée à une valeur dans la gamme de 0.0 à 1.0. La classe de ContinuousVariable hérite de
la méthode de normalizedSize () de la classe variable, parce que la taille normale est toujours
1.

class ContinuousVariable extends Variable {

float min = (float)0.0;


float max = (float)0.0;

ContinuousVariable(String name) { super(name); }


void setMin(float Min) { min = Min; }
void setMax(float Max) { max = Max; }

public void computeStatistics(String inValue){


float val = new Float(inValue).floatValue();
if (val < min) min = val ;
if (val > max) max = val ;
}

// scale the inValue to 0.0 and 1.0


public int normalize(String inStrValue, float[] outArray, int inx) {
float outValue ;
float inValue = Float.valueOf(inStrValue).floatValue();
if (inValue <= min) {
outValue = min ;
} else if (inValue >= max) {
outValue = max ;
} else {
float factor = max - min ;
outValue = inValue / factor ;
}
outArray[ inx] = outValue ;
return inx+1 ;
}

};

Variables discrètes

La classe de DiscreteVariable est une sous-classe de variable. Elle fournit l'appui nécessaire
pour les variables qui peuvent prendre un ensemble prédéfini de valeurs numériques ou
symboliques. Le constructeur prend le nom de la variable comme seul paramètre. Le
minimum et les valeurs maximum peuvent être placés suivre le setMin () et des méthodes de
setMax () directement, ou ils peuvent être calculés automatiquement si le DiscreteVariable
est employé en tant qu'élément d'un ensemble de données en appelant la méthode de
computeStatistics (). On assume que la valeur est symbolique. Si le symbole est déjà dans les
étiquettes puis il est ignoré ; autrement, on l'ajoute à la liste. La méthode de normalisation ()
est employée par la classe d'ensemble de données quand elle crée une version tout-
numérique d'un ensemble de données. Cette méthode convertit chaque symbole en sa valeur et
LY_SII __ 118 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

puis convertis d'index qui dans un-de-n code. La méthode de normalizedSize () renvoie le
nombre de valeurs discrètes uniques que la variable peut prendre, qui est également la taille
d'un-de-n code quand la variable est normale. La méthode de getDecodedValue () est
employée pour transformer le rendement d'un réseau neurologique de nouveau dans une
valeur de corde pour l'affichage.

class DiscreteVariable extends Variable {

Integer min ;
Integer max ;

DiscreteVariable(String name) { super(name);


labels = new Vector();}

void setMin(Integer Min) { min = Min; }

void setMax(Integer Max) { max = Max; }


public void computeStatistics(String inValue){
if (labels.contains(inValue)) return ;
else labels.addElement(inValue) ;
};

// translate inValue index into a one-of-N code


public int normalize(String inValue, float[] outArray, int inx) {
int index = getIndex(inValue) ; // look up symbol index

float code[] = new float[labels.size()] ;


if (index < code.length) code[index] = (float)1.0 ;
// copy one of N code to outArray, increment inx
for (int i=0 ; i < code.length; i++) {
outArray[inx++] = code[i] ;
}
return inx ; // return output index
}

public int normalizedSize() { return labels.size() ; }

// turn an array of floats back into a string value


public String getDecodedValue(float[] act, int start) {
int len = labels.size() ;
String value ;
float max = (float)-1.0 ;
value = String.valueOf(0) ;
for (int i=0 ; i < len ; i++) {
if (act[start+i] > max) {
max = act[start+i] ;
value = getLabel(i) ;
}
}
return value ;
}

};

La classe d'ensemble de données

Nous fournissons aux ensembles de données multiples pour l'usage les trois techniques
d'étude. Celles-ci incluent exclusif-OU l'ensemble de données, un ensemble de données de
LY_SII __ 119 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

véhicules qui reflète la base de règle de véhicules utilisée dans le chapitre 4, un ensemble de
données de restaurant, un ensemble de données linéaire de rampe, et un ensemble de données
de groupement. Chaque ensemble de données est employé pour préciser des aspects
spécifiques de nos algorithmes d'étude. Puisque chacun des trois algorithmes se sert d'un
ensemble de données de formation, nous avons conçu et avons mis en application une classe
de Java de terrain communal DataSet appelé. La définition de classe d'ensemble de données
est montrée ci-dessous. Elle se sert du paquet de java.io pour charger les données à partir des
dossiers plats des textes dans la mémoire, et emploie également les classes de java.awt
(TextArea) pour montrer les données et pour l'information de trace. Chaque exemple
d'ensemble de données a un nom, un membre pour stocker le nom de fichier, et un drapeau
booléen qui indique s'il y a des données symboliques dans le dossier. Deux vecteurs sont
employés pour stocker les données lus du dossier et d'un ensemble de données normal, que
nous discuterons plus tard. La table de brouillage de variableList tient un ensemble de
variables qui définissent les types de données logiques pour chaque champ dans le dossier.
Le membre de fieldList tient des références aux mêmes variables que le variableList, mais les
variables sont ajoutées au vecteur dans l'ordre qu'elles sont ajoutées à l'ensemble de
données, suivant les indications de la méthode addVariable. Ceci nous permet d'avoir une
correspondance linéaire entre les champs dans le dossier et les variables qui définissent les
données. Le membre de fieldsPerRec est équivalent à la taille du fieldList et est donné pour la
convenance. NumRecords est placé au nombre de disques dans le dossier.

Il y a un constructeur simple pour la classe d'ensemble de données. Il prend comme des


paramètres le nom d'objet et le nom de fichier.

// A DataSet is read from a text file


// The variables must be defined and added in the correct order
// The string data is available in the data member
// The normalized numeric data is in the normalizedData member
public class DataSet extends Object {
String name ;
String fileName ;
boolean allNumericData ; // if true use float[] else String[]

Vector data ; // raw data from file


Vector normalizedData ; // scaled and translated data
Hashtable variableList ; // variable definitions
Vector fieldList ; // field definitions where index = column

int fieldsPerRec=0;
int normFieldsPerRec=0;
int numRecords=0 ;

DataSet(String Name, String FileName) {


name = Name; // object name
fileName = FileName ; // text file name
fieldsPerRec = 0 ; // start with no variables defined
allNumericData = true ; // assume all numeric data
data = new Vector() ; // holds string data
variableList = new Hashtable(); // for named lookup
fieldList = new Vector(); // for ordered lookup
}

// for trace purposes - display all variables and their value


public void displayVariables() {
LY_SII __ 120 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Enumeration enum = variableList.elements() ;


while(enum.hasMoreElements()) {
String values ;
Variable temp = (Variable)enum.nextElement() ;
if(temp.labels != null) {
values = temp.getLabels();
} else {
values = “< real>” ;
}
trace(“\n” + temp.name + “( ” + values + “) ”) ;
}
}

public int getClassFieldSize() {


if (variableList.get(“ClassField”) == null) {
trace(“DataSet ” + name + “does not have a ClassField”) ;
return 0 ;
} else
return((Variable)variableList.get(“ClassField”)).normalizedSize();
}

. . .

Nous incluons une charge statique TextArea qui est employée par l'applet pour montrer des
informations sur l'ensemble de données. La méthode de trace écrit simplement une corde au
TextArea.

static TextArea textArea1 ;

static public void trace(String text) { textArea1.appendText(text);}


static public void setDisplay(TextArea tx1) { textArea1 = tx1; }

Une des méthodes principales dans la classe d'ensemble de données est () la méthode
loadDataFile. Cette méthode ne prend aucun paramètre parce que toutes les informations
nécessaires pour lire le dossier dans la mémoire sont fournies sur le constructeur d'ensemble
de données (). Avant que nous chargions le fichier de données, nous avons lu la première fois
la définition de format de fichier suivre la méthode de loadDataFileDefinition (). Nous
employons un FileInputStream pour lire des données à partir du dossier. Nous employons la
méthode de readLine () pour lire une ligne à la fois et un StringTokenizer pour analyser
dehors chaque valeur de champ. Selon, que le dossier contienne toutes les données
numériques ou pas, nous instancions une nouvelle corde ou flottons la rangée pour détenir le
record. Cette rangée est alors ajoutée au vecteur de données. Car nous lisons le dossier, nous
maintenons la taille du dossier et stockons cette information dans le membre de numRecords.

public void loadDataFile() {

String tempRec[]=null ; // used when data is symbolic

loadDataFileDefinition(); // first read the .dfn,create the vars


fieldsPerRec = fieldList.size();

String line=null ;
trace(“\nReading file ” + fileName + “.dat with ” +
fieldsPerRec + “ fields per record\n ”) ;
LY_SII __ 121 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

DataInputStream in=null ;
try {
in = new DataInputStream(new FileInputStream(fileName + “.dat”));
} catch (FileNotFoundException exc) {
trace(“Error: Can't find file ” + fileName + “.dat” ) ;
}

int recInx = 0 ;
int token= 0 ;
StringTokenizer input = null ;
do {
try {
line = in.readLine();
if (line != null) {
input = new StringTokenizer(line) ;
tempRec = new String[fieldsPerRec] ;
data.addElement(tempRec) ; // add record
} else {
break ;
}
} catch (IOException exc){
trace(“Error reading file: ” + fileName + “.dat”) ;
}

trace(“\n Record ” + recInx + “: ”);


for(int i= 0 ; i < fieldsPerRec ; i++) {
tempRec[i] = input.nextToken() ;
((Variable)fieldList.elementAt(i)).computeStatistics(tempRec[i]);
trace(tempRec[i] + “ ”);
}
recInx++ ;
} while (token != StreamTokenizer.TT_EOF) ;
numRecords = recInx ;
trace(“\nLoaded ” + numRecords + “ records into memory.\n”) ;
normalizeData() ; // now convert to numeric form
displayVariables() ;
displayNormalizedData();
}

La définition de fichier de données est un dossier simple des textes qui contient une liste des
types de données de champ et de leurs noms. Un champ simple doit être indiqué comme
« ClassField. » Les types de données peuvent être ou « continus, » « discret, » ou
« catégorique. » Dans l'exécution courante, discret et catégorique sont traités les mêmes.
Pendant que chaque type de champ est lu à partir du dossier de définition de données, une
variable de ce type est instanciée avec le nom spécifique, et la variable est alors ajoutée à
l'ensemble de données suivre () la méthode addVariable. On assume que les champs sont
définis dans le même ordre comme les données correspondantes dans le fichier de données.

public void loadDataFileDefinition() {

String tempRec[]=null ; // used when data is symbolic

String line=null ;
trace(“\nReading file definition ” + fileName + “.dfn with ” +
fieldsPerRec + “ fields per record\n ”) ;
DataInputStream in=null ;
try {
in = new DataInputStream(new FileInputStream(fileName + “.dfn”));
LY_SII __ 122 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

} catch (FileNotFoundException exc) {


trace(“Error: Can't find file ” + fileName + “.dfn” ) ;
}

int recInx = 0 ;
int token= 0 ;
StringTokenizer input = null ;
do {
try {
line = in.readLine();
if (line != null) {
input = new StringTokenizer(line) ;
} else {
break ;
}
} catch (IOException exc){
trace(“Error reading file: ” + fileName + “.dfn”) ;
}

trace(“\n Record ” + recInx + “: ”);


String varType = input.nextToken() ;
String varName = input.nextToken() ;
if (varType.equals(“continuous”)) {
addVariable(new ContinuousVariable(varName)) ;
} else if (varType.equals(“discrete”)) {
addVariable(new DiscreteVariable(varName)) ;
} else if (varType.equals(“categorical”)) {
addVariable(new DiscreteVariable(varName)) ;
}
trace(varType + “ ” + varName);
recInx++ ;
} while (token != StreamTokenizer.TT_EOF) ;
fieldsPerRec = fieldList.size() ;
trace(“\nCreated ” + fieldsPerRec + “ variables.\n”) ;

public void addVariable(Variable var) {


variableList.put(var.name, var) ;
fieldList.addElement(var) ; // add in order of arrival
var.setColumn(fieldsPerRec);
fieldsPerRec++ ;
}

Les membres suivants font partie de la classe variable présentée dans le chapitre précédent, et
sont employés par la classe d'ensemble de données.

public class Variable {


...
// used by the DataSet class
public void setColumn(int col) { column = col ; }
public abstract void computeStatistics(String inValue) ;
public abstract int normalize(String inValue, float[] outArray,
int inx);
public int normalizedSize() { return 1 ; }
...
}
LY_SII __ 123 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Les trois prochaines méthodes traitent des données normales. Les réseaux neurologiques
exigent toutes les données d'entrée numériques et tellement n'importe quels ensembles de
données qui contiennent des symboles doivent être prétraités. En outre, les réseaux
neurologiques exigent habituellement les données d'entrée soient mesurés à une gamme
spécifique, dans notre cas, de 0.0 à 1.0. Cette conversion des données et de la graduation
symboliques des données continues s'appelle la normalisation. Nous employons les
définitions variables pour déterminer comment normaliser les données. Quand nous avons un
DiscreteVariable, nous prenons le symbole ou le nombre et obtenons sa valeur d'index. Cette
valeur d'index est alors convertie en un-de-n vector. Par exemple, si nous avons {oui, non,
peut-être} en tant que trois cordes possibles dans un domaine défini comme
DiscreteVariable, nous convertirions chaque symbole en code de 1s et de 0s dont la longueur
est égale au nombre de valeurs discrètes. Tellement « oui » ne tracerait à 1 0 0, « non » à 0 1
0, et « peut-être » à 0 0 1. Noter également que nous avons une expansion ici. Un disque
d'entrée avec 10 champs de DiscreteVariable, chacun ayant trois valeurs possibles,
augmenterait dans une rangée de trente-élément pour la présentation au réseau neurologique.

Une autre conversion que nous devons faire est de mesurer les données continues qui sont
hors de la gamme 0.0 à 1.0. Nous faisons une graduation linéaire simple ici. Il y a des
techniques plus sophistiquées qui pourraient être employées. L'utilité de réseau neurologique
d'IBM, par exemple, emploie une technique bilinéaire de graduation où le point médian de la
plage des données d'entrée est tracé au point médian (0.5) de la plage des données de
rendement. Ceci tend « à se concentrer » les données sur 0.5 dans les cas où il y a des annexes
ou d'autres anomalies. Noter que notre un-de-n scaling utilisée pour des données discrètes est
déjà dans les 0 à 1 gammes, ainsi aucune graduation additionnelle est nécessaire.

La méthode getNormalizedRecordSize () des calculs l'expansion qui se produit pendant ce


procédé de normalisation. La méthode de normalizeData () effectue la graduation et la
traduction réelles à un-de-n codes. Le rawData est les données lues dedans à partir du dossier.
Le normNumRec est la rangée de flotteurs qui résulte après le prétraitement. Quand tout est
complet, le vecteur de normalizedData tient un ensemble de données normal complet
correspondant à l'ensemble de données original, de manière opérationnelle par les réseaux
neurologiques. La méthode de displayNormalizedData () est une méthode de service pour
montrer le code normal pour l'élimination des imperfections.

public int getNormalizedRecordSize() {

int sum = 0 ;
Enumeration vars = variableList.elements() ;
while (vars.hasMoreElements()) {
Variable thisVar = (Variable)vars.nextElement() ;
sum += thisVar.normalizedSize() ;
}
return sum ;
}

// walk through the file data and scale/translate it


// to all numeric data ranging between 0 and 1
public void normalizeData() {
String tempRec[]= null ;
LY_SII __ 124 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

normalizedData = new Vector() ;


normFieldsPerRec = getNormalizedRecordSize();
Enumeration rawData = data.elements() ;
while(rawData.hasMoreElements()) {
int inx = 0 ;
float normNumRec[] = new float[normFieldsPerRec] ;
Enumeration fields = fieldList.elements() ;
tempRec = (String[])rawData.nextElement() ;

for (int i=0 ; i < fieldsPerRec ; i++) {


Variable thisVar = (Variable)fields.nextElement() ;
inx = thisVar.normalize(tempRec[i], normNumRec, inx) ;

}
normalizedData.addElement(normNumRec) ;
}
}

public void displayNormalizedData() {

float tempNumRec[] ;

Enumeration rawData = normalizedData.elements() ;


while(rawData.hasMoreElements()) {
int recInx = 0 ;
trace(“\n Record ” + recInx + “: ”);
tempNumRec = (float[])rawData.nextElement() ;
int numFields = tempNumRec.length ;
for (int i=0 ; i < numFields ; i++) {
trace(String.valueOf(tempNumRec[i]) + “ ”) ;
}
recInx++ ;
}
}

Les méthodes de getClassFieldValue () sont employées pour montrer les résultats après
qu'une course de formation de réseau neurologique. La première méthode prend l'index record
et renvoie la valeur originale de « ClassField ». La deuxième méthode prend une rangée de
flotteurs et l'index commençant de l'unité de rendement. La méthode de
DiscreteVariable.getDecodedValue () est employée pour choisir l'élément de tableau avec la
valeur maximum, la convertit en index variable discret, et puis recherche la valeur de corde
correspondante. C'est essentiellement l'inverse des étapes exécutées dans le normalizeData ()
quand une valeur discrète est transformée en un-de-n code.

public String getClassFieldValue(int recIndex) {


Variable classField = (Variable)variableList.get(“ClassField”) ;
return ((String[])data.elementAt(recIndex))[classField.column] ;
}

public String getClassFieldValue(float[] activations, int index) {


String value ;
Variable classField = (Variable)variableList.get(“ClassField”) ;
if (classField.categorical()) {
value = classField.getDecodedValue(activations, index) ;
} else {
value = String.valueOf(activations[index]) ;
}
return value ;
LY_SII __ 125 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Dans les sections suivantes, nous présentons nos réalisations de Java des modèles arrières de
réseau neurologique de propagation et de carte de Kohonen. Ils sont orientés objectivement,
mais pas servile ainsi. Nous voudrions que ces algorithmes fonctionnent en nombre de heures
raisonnable sur le matériel de PC standard. Ainsi nous n'avons pas des objets pour chaque
unité ou couche de traitement. Notre expérience prouve que nous pouvons avoir les avantages
de la conception orientée objectivement et l'exécution du code algorithmique optimisé.

Exécution arrière d'appui vertical

Dans cette section, nous décrivons notre exécution de la propagation arrière. La classe de
BackProp a quatre ensembles différents de membres ou de paramètres de données. Est
d'abord un ensemble qui est employé pour contrôler les données. Nous avons une référence à
un objet d'ensemble de données, au ds, à l'index d'article courant, au recInx, à tout le nombre
de disques dans l'ensemble de données, aux numRecs, et à nombre de champs par disque,
fieldsPerRec. Est après un ensemble de paramètres d'architecture de réseau. Ceux-ci incluent
les numInputs, numHid1, et les numOutputs, qui définissent le nombre d'unités dans chacune
des trois couches réseau. Quelques réalisations soutiennent des couches cachées multiples.
Cette conception peut être facilement prolongée pour fournir cet appui. NumUnits est la
somme des trois couches d'unités, et les numWeights est tout le nombre de poids dans le
réseau.

Dans notre exécution, nous fournissons les paramètres de commande suivants : mode,
learnRate, élan, et tolérance. Quand le paramètre de mode est placé à une valeur de 0, le
réseau est en mode de formation et les poids de raccordement sont ajustés. Quand le mode est
placé à 1, les poids de réseau sont verrouillés. Les paramètres de learnRate et d'élan sont
employés pour commander la taille des mises à jour de poids comme décrit plus tôt. Le
paramètre de tolérance est employé pour spécifier combien étroit la valeur prévue de
rendement doit être à la valeur désirée de rendement avant que l'erreur soit considérée 0. Par
exemple, si notre valeur désirée de rendement était 1.0, avec une tolérance de 0.1, n'importe
quelle valeur prévue de rendement plus considérablement que 0.9 aurait comme conséquence
0 erreurs.

Après, nous avons un ensemble de rangées de données et d'erreurs. Les rangées primaires sont
les activation, les poids, et les seuils. Celles-ci spécifient l'état actuel du réseau. La rangée
d'enseignement tient les valeurs désiré ou de cible de rendement. Les autres rangées sont
employées dans le calcul des changements d'erreur et de poids.

// standard backward propagation with momentum


public class BackProp extends Object {
String name ;

// data parameters
static DataSet ds ;
Vector data ; // train/test data from file
int recInx=0 ; // current record index
int numRecs=0 ; // number of records in data
int fieldsPerRec=0;

// error measures
float sumSquaredError ; // total SSE for an epoch
LY_SII __ 126 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

float aveRMSError ; // average root-mean-square error


int numPasses ; // number of passes over the data set

// network architecture parameters


int numInputs ;
int numHid1 ;
int numOutputs ;
int numUnits ;
int numWeights ;

// network control parameters


int mode;
float learnRate ;
float momentum;
float tolerance;

// network data
float activations[] ;
float weights[] ;
float wDerivs[] ;
float thresholds[];
float tDerivs[] ;
float tDeltas[] ;
float teach[]; // target output values
float error[];
float deltas[]; // the error deltas
float wDeltas[] ;

TextArea textArea1 ;

BackProp(String Name) {
name = Name ;
data = new Vector() ;
}

public void show_array(String name, float[] arr) {


textArea1.appendText(“\n” + name + “= ”) ;
for (int i=0 ; i < arr.length ; i++) {
textArea1.appendText(arr[i] + “ ”) ;
}

public void display_network() {


// show_array(“weights”,weights);
// show_array(“thresholds”, thresholds);
show_array(“activations”,activations) ;
// show_array(“teach”,teach) ;
textArea1.appendText(“\n Passes Completed: ” + numPasses +
“ RMS Error = ” + aveRMSError + “ \n”) ;

String desired = ds.getClassFieldValue(recInx-1);


String actual = ds.getClassFieldValue(activations,
numInputs+numHid1);
textArea1.appendText(“\n Desired: ” + desired +
“ Actual: ” + actual ) ;
}
. . .

}
LY_SII __ 127 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Pour définir un réseau neurologique de propagation arrière, nous fournissons la méthode de


createNetwork (). Cette méthode est fournie plutôt qu'using le constructeur de sorte que
l'architecture de réseau puisse être changée sans créer un nouvel objet. La méthode de
createNetwork () prend trois paramètres, le nombre d'entrées, caché, et d'unités de rendement.
Nous calculons quelques membres commodes de données, initialisons les paramètres de
commande, et puis assignons toutes les rangées. Alors nous appelons la méthode de remise ()
pour initialiser les rangées de réseau.

// create a Back Prop network with specified architecture


public void createNetwork(int NumIn, int NumHid1, int NumOut){

// set the network architecture


numInputs = NumIn ;
numHid1 = NumHid1;
numOutputs = NumOut;
numUnits = numInputs + numHid1 + numOutputs ;
numWeights = (numInputs*numHid1) + (numHid1*numOutputs);

// initialize control parameters


learnRate = (float)0.2;
momentum = (float)0.7 ;
tolerance = (float)0.1 ;
mode = 0 ; // 0 = train mode, 1 = run mode
aveRMSError = (float)0.0 ;
numPasses = 0;

// create weight and error arrays


activations = new float[numUnits]; // unit activations
weights = new float[numWeights];
wDerivs = new float[numWeights]; // accumulated wDeltas
wDeltas = new float[numWeights]; // weight changes
thresholds = new float[numUnits];
tDerivs = new float[numUnits]; // accumulated tDeltas
tDeltas = new float[numUnits] ; // threshold changes
teach = new float[numOutputs] ; // desired outputs
deltas = new float[numUnits];
error = new float[numUnits] ;

reset() ; // reset and initialize the weight arrays

return;
}

public void reset() {


int i ;
for (i=0 ; i < weights.length ; i++) {
weights[i] = (float)1.0 - (float)Math.random();
wDeltas[i] = (float)0.0 ;
wDerivs[i] = (float)0.0 ;
}
for (i=0 ; i < numUnits ; i++) {
thresholds[i] = (float)1.0 - (float)Math.random();
tDeltas[i] = (float)0.0 ;
tDerivs[i] = (float)0.0 ;
}
}
LY_SII __ 128 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Pour le passage vers l'avant, trois méthodes sont employées. La méthode de readInputs ()
prend un disque de l'ensemble de données et copie les valeurs d'entrée dans les activation des
unités d'entrée. Elle copie également les valeurs à atteindre dans la rangée d'enseignement. La
méthode de computeOutputs () fait le passage vers l'avant complet par le réseau. Commençant
par la première couche, elle calcule la somme du seuil et de chaque activation d'unité d'entrée
multipliés par le poids correspondant, et puis appelle () la méthode logistique pour calculer et
placer la valeur d'activation.

public float logistic(double sum) {


return (float)(1.0 / (1 + Math.exp(-1.0 * sum)));
}

// move data from train/test set into network input units


public void readInputs() {
recInx = recInx % numRecs ; // keep index from 0 to n-1 records
int inx=0 ;
float[] tempRec = (float[])data.elementAt(recInx); //get record
for (inx=0 ; inx < numInputs ; inx++ ) {
activations[inx] = tempRec[inx] ;
}
for (int i=0 ; i < numOutputs; i++ ) {
teach[i] = tempRec[inx++] ;
}
recInx++ ;
}

// do a single forward pass through the network


public void computeOutputs() {
int i, j ;
int firstHid1 = numInputs ;
int firstOut = numInputs + numHid1 ;

// first layer
int inx = 0 ;
for(i = firstHid1 ; i < firstOut ; i++) {
float sum = thresholds[i];
for (j = 0 ; j < numInputs ; j++) { // compute net inputs
sum += activations[j] * weights[inx++] ;
}
activations[i] = logistic(sum) ; // compute activation
}
// second layer
for(i = firstOut ; i < numUnits ; i++) {
float sum = thresholds[i] ;
for (j = firstHid1 ; j < firstOut ; j++) { // compute sum
sum += activations[j] * weights[inx++] ;
}
activations[i] = logistic(sum) ; // compute activation
}
}

Pour calculer les erreurs, nous commençons à la couche de rendement et travaillons en arrière
vers l'entrée. D'abord, les erreurs de rendement sont calculées en prenant la différence entre
les activation produites par des computeOutputs () et les valeurs d'enseignement. Noter que
nous additionnons également les erreurs carrées ici pour calculer l'erreur moyenne de RMS.
Les deltas sont alors calculés using le dérivé de la fonction d'activation.
LY_SII __ 129 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

// compute the errors using backward error propagation


// weight changes get placed in (or added to) wDerivs
// threshold changes get placed in (or added to) tDerivs
public void computeError() {
int i, j ;
int firstHid1 = numInputs ;
int firstOut = numInputs + numHid1 ;

// clear hidden unit errors


for(i = numInputs ; i < numUnits ; i++) {
error[i] = (float)0.0 ;
}

// compute output layer errors and deltas


for(i = firstOut ; i < numUnits ; i++) {
error[i] = teach[i-firstOut] - activations[i];
sumSquaredError += error[i] * error[i];
if (Math.abs(error[i]) < tolerance) error[i] = (float)0.0;
deltas[i] = error[i] * activations[i] * (1 - activations[i]);
}

// compute hidden layer errors


int winx = numInputs * numHid1 ; // offset into weight array
for(i = firstOut ; i < numUnits ; i++) {
for (j = firstHid1; j < firstOut ; j++) {
wDerivs[winx] += deltas[i] * activations[j] ;
error[j] += weights[winx] * deltas[i] ;
winx++ ;
}
tDerivs[i] += deltas[i] ;
}

// compute hidden layer deltas


for (i = firstHid1 ; i < firstOut ; i++) {
deltas[i] = error[i] * activations[i] * (1 - activations[i]);
}

// compute input layer errors


winx = 0 ; // offset into weight array
for(i = firstHid1 ; i < firstOut ; i++) {
for (j = 0; j < firstHid1 ; j++) {
wDerivs[winx] += deltas[i] * activations[j] ;
error[j] += weights[winx] * deltas[i] ;
winx++ ;
}
tDerivs[i] += deltas[i] ;
}

Pour ajuster les poids, la méthode d'adjustWeights () calcule d'abord les deltas courants de
poids, wDeltas, et ajoute ensuite ces deltas aux poids. Noter que nous employons la rangée de
wDerivs, qui tient l'ensemble accumulé de modifications de poids qui devraient être apportées
aux poids. Dans la mise à jour de modèle, les wDerivs tient seulement les changements du
modèle courant. Cependant, si nous voulions employer le groupe mettant à jour, cette rangée
tiendrait les changements de tous les modèles de l'ensemble de données. Après que nous
LY_SII __ 130 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

ajustions les poids, nous mettons dehors la rangée de wDerivs. Après nous ajustons les poids
de seuil à chaque unité dans cachée et produisons la couche. Les calculs sont semblables à
ceux pour les poids réguliers. Note : Il n'est pas nécessaire d'avoir une rangée séparée de seuil.
Quelques réalisations juste ajoutent les poids additionnels aux rangées de poids et ajoutent
une unité additionnelle simple qui a une valeur constante d'activation de 1 à chaque couche.

// apply the changes to the weights


public void adjustWeights() {
int i ;

// first walk through the weights array


for(i = 0; i < weights.length ; i++) {
wDeltas[i] = (learnRate * wDerivs[i]) + (momentum * wDeltas[i]);
weights[i] += wDeltas[i] ; // modify the weight
wDerivs[i] = (float)0.0 ;
}

// then walk through the threshold array


for(i=numInputs ; i < numUnits ; i++) {
tDeltas[i] = learnRate * tDerivs[i] + (momentum * tDeltas[i]);
thresholds[i] += tDeltas[i] ; // modify the threshold
tDerivs[i] = (float)0.0 ;
}

// if at the end of an epoch, compute average RMS Error


if (recInx == numRecs) {
numPasses++ ; // increment pass counter
aveRMSError = (float)Math.sqrt( sumSquaredError /
(numRecs * numOutputs));
sumSquaredError = (float)0.0 ; // clear the accumulator
}
}

public void process() {

readInputs() ; // set input unit activations


computeOutputs() ; // do forward pass through network
computeError() ; // compute error and deltas

// only adjust if in training mode


if (mode == 0) adjustWeights() ; // apply changes to weights

}Pour voir comment notre réseau arrière de propagation fonctionne, nous pouvons employer
le notre apprenons l'applet. Using un navigateur Java-permis, s'ouvrir sur le dossier de
LearnApplet.html. Le dialogue d'applet d'étude suivant les indications du schéma 5.5 apparaît.
Choisir en arrière l'appui vertical comme méthode de étude et l'ensemble de données de
XOR dans la commande bien choisie au dessus du panneau. Cliquer dessus le bouton de
charge pour lire dedans les données de XOR. Le dossier de xor.dfn et les données seront
chargés dans l'ensemble de données et montrés dans la commande supérieure de TextArea.
Presser le bouton marche pour former le réseau arrière de propagation. Quand la formation
est complète, les valeurs désirées et réelles, le contenu de la rangée d'activation, et l'erreur
finale de la moyenne RMS seront montrés dans le TextArea inférieur.

Vous pouvez choisir d'autres ensembles de données pour travailler avec l'appui vertical
arrière, y compris les véhicules et les animaux pour la classification, et la rampe linéaire pour
LY_SII __ 131 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

voir un exemple d'un problème de prévision. Le contenu de chaque ensemble de données est
énuméré dans l'annexe B. Toutes les fois que vous choisissez un ensemble de données
différent, soit sûr d'appuyer sur le bouton de charge. Le bouton de remise dégagera le
TextArea inférieur et remettra à zéro les poids dans le réseau arrière d'appui vertical.

Exécution de carte de Kohonen

Pour notre exécution du réseau neurologique de carte de Kohonen, nous définissons une
classe appelée KMapNet. La structure de cette classe est semblable à celle de la classe de
BackProp déjà présentée en ce chapitre. La différence principale entre le KMapNet et la
classe de BackProp est la non surveillée contre le paradigme de étude dirigé. La carte de
Kohonen effectue l'étude non surveillée, tellement là n'est aucune notion d'un résultat désiré ;
il y a seulement des entrées.

La classe de KMapNet a quatre ensembles différents de membres ou de paramètres de


données. Est d'abord un ensemble qui est employé pour contrôler les données. Nous avons
une référence à un ensemble de données, au ds, à l'index d'article courant, au recInx, à tout le
nombre de disques dans l'ensemble de données, aux numRecs, et à nombre de champs par
disque, fieldsPerRec. Est après un ensemble de paramètres d'architecture de réseau. Ceux-ci
incluent des numInputs, des numRows, et des numCols, qui définissent le nombre d'unités
dans la couche d'entrée et les dimensions de la couche bidimensionnelle de rendement.
NumUnits est la somme de ces deux couches, et les numWeights est tout le nombre de poids
dans le réseau. Dans notre exécution relativement simple, nos paramètres de commande
comprennent l'initalLearnRate et le finalLearnRate, et le LearnRate courant. Le mode de
réseau est train ou essai. Le sigma est employé dans le calcul de la fonction de voisinage.

Après nous avons un ensemble de rangées de données et d'erreurs. Les rangées primaires sont
les activation et les poids, qui spécifient l'état actuel du réseau. La rangée de distance est une
grille precomputed qui définit les distances entre les unités sur la grille bidimensionnelle de
rendement. Par exemple, dans un réseau avec un résultat 4 by-4, les distances à partir de
l'unité 0.0 (gauche supérieur) seraient :

0 1 16 81

1 2 17 82

16 17 32 97

81 82 97 162

Comme vous pouvez voir, la distance à partir de l'unité à elle-même est 0, à ces unités
horizontalement et verticalement adjacent elle est 1, et les distances se développent pendant
que vous vous éloignez de l'unité. La rangée de distance est employée dans la méthode
d'adjustNeighborhood ().

public class KMapNet extends Object {


String name ;
LY_SII __ 132 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

// data parameters
static DataSet ds;
Vector data ;
int recInx=0 ; // current record index
int numRecs=0 ; // number of records in data
int fieldsPerRec;
int numPasses=0 ;

// network architecture parameters


int numInputs ;
int numRows ;
int numCols ;
int numOutputs ;
int numUnits ;

// network control parameters


int mode;
float learnRate ;
float initLearnRate = (float)1.0 ;
float finalLearnRate = (float)0.05 ;
float sigma ;
int maxNumPasses=20 ; // default

// network data
int winner ; // index of the winning unit
float activations[];
float weights[];
int distance[]; // used in neighborhood computation

static TextArea textArea1 ;

KMapNet(String Name) {
name = Name ;
data = new Vector() ;
}

public void show_array(String name, float[] arr) {


textArea1.appendText(“\n” + name + “= ”) ;
for (int i=0 ; i < arr.length ; i++) {
textArea1.appendText(arr[i] + “ ”) ;
}

}
public void display_network() {
// show_array(“weights”,weights);
show_array(“activations”,activations) ;
textArea1.appendText(“\nWinner = ” + winner + “\n”) ;
}

. .

Pour définir un réseau neurologique de carte de Kohonen, nous fournissons la méthode de


createNetwork (). Cette méthode est fournie plutôt qu'using le constructeur de sorte que nous
puissions changer l'architecture de réseau sans créer un nouvel objet. La méthode de
createNetwork () prend trois paramètres ; le nombre d'entrées, de rangées, et de colonnes.
Nous calculons quelques membres de données de convenance, initialisons les paramètres de
LY_SII __ 133 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

commande, et puis assignons les activation et les rangées de poids. La méthode de


computeDistances () initialise la rangée de distance. Alors nous appelons la méthode
d'adjustNeighborhood () pour placer l'initiale apprenons le taux et puis appelons la remise ()
pour initialiser la rangée de poids.

// create a Kohonen network with the specified architecture


public void createNetwork(int NumIn, int NumRows, int NumCols){

// set the network architecture


numInputs = NumIn;
numRows = NumRows;
numCols = NumCols;
numOutputs = numRows * numCols ;
numUnits = numInputs + numOutputs;

// initialize control parameters


learnRate = (float)0.1;
mode = 0 ; // 0 = train mode, 1 = run mode

// create arrays
activations = new float[numUnits];
weights = new float[numInputs*numOutputs];

// fill in the Distance matrix


computeDistances() ;

adjustNeighborhood() ; // set the initial learnRate


reset() ; // reset and initialize weight arrays

return ; // all data in the network


}

public void reset() {


int i ;
for (i=0 ; i < weights.length ; i++) {
weights[i] = (float)0.6 - ((float)0.2
*(float)Math.random()); // between 0.4 and 0.6
}
}

// initialize a matrix of distances from each unit to all others


public void computeDistances() {
int i, j, xi, xj, yi, yj ;
distance = new int[numOutputs* numOutputs] ;
for(i=0 ; i < numOutputs ; i++) {
xi = i % numCols ;
yi = i / numRows ;
for (j=0 ; j < numOutputs ; j++) {
xj = j % numCols ;
yj = j / numRows ;
distance[i*numOutputs+j] =
(int) Math.pow((double)((xi-xj)*(xi-xj)),2.0)+
(int) Math.pow((double)((yi-yj)*(yi-yj)),2.0);
}
}
}
LY_SII __ 134 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Pour traiter un modèle d'entrée, trois méthodes sont employées. La méthode de ReadInputs ()
prend un disque de l'ensemble de données et copie les valeurs d'entrée dans les activation des
unités de couche d'entrée. Puisque les cartes de Kohonen emploient l'étude non surveillée, il
n'y a aucune valeur à atteindre. La méthode de computeOutputs () calcule la distance
euclidienne entre le vecteur d'entrée et les vecteurs de poids. La méthode de computeWinner
() choisit le rendement avec l'activation minimum (le plus étroitement au vecteur d'entrée) en
tant que gagnant. Note, nous pourrions faire cette fonction dans notre méthode de
computeOutputs () dans ce cas-ci. Cependant, si nous ajoutons des techniques plus
sophistiquées de choix de gagnant, telles que l'étude avec une conscience, nous voudrions ceci
éclaté dans une étape séparée.

// move data from train/test set into network input units


public void readInputs() {
recInx = recInx % numRecs ; // keep index from 0 to n-1 records
int inx=0 ;
float[] tempRec = (float[])data.elementAt(recInx); // get record
for (inx=0 ; inx < numInputs ; inx++ ) {
activations[inx] = tempRec[inx] ;
}
recInx++ ;
if (recInx ==0){
numPasses++ ; // completed another pass
adjustNeighborhood() ;
}
}

// do a single forward pass through the network


// compute the Euclidean distance from input vector
// to all output units
public void computeOutputs() {
int index, i, j ;
int lastOut = numUnits - 1;
int firstOut = numInputs;

//first layer
for(i = firstOut ; i <= lastOut ; i++) {
index = (i - firstOut) * numInputs;
activations[i] = (float)0.0 ;
for (j = 0 ; j < numInputs ; j++) { // compute net inputs
activations[i] += (activations[j] - weights[index+j]) *
(activations[j] - weights[index+j]);
}
}
}

// find the output unit with the smallest activation


// and declare it the “winner”
public void selectWinner() {

winner = 0;
float min = activations[numInputs];

for(int i = 0 ; i < numOutputs ; i++) {


if (activations[i+numInputs] < min) {
min = activations[i+numInputs] ;
winner = i ;
}
}
LY_SII __ 135 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

La méthode d'adjustNeighborhood () calcule deux valeurs cruciales pendant que la formation


progresse, le taux d'étude et la largeur ou le sigma de voisinage. D'abord, on calcule un
rapport qui indique à quelle distance le long de nous être dans la formation. Ce rapport
augmente avec le temps. Puis, le learnRate courant est calculé, diminuant avec le temps. En
conclusion, la valeur de voisinage, sigma, est calculée.

public void adjustNeighborhood() {


double ratio = (double) (numPasses/ maxNumPasses) ;
learnRate = initLearnRate *
(float)Math.pow((double)finalLearnRate /
(double)initLearnRate,ratio);
sigma = (float)numCols * (float)Math.pow((0.20 /
(double)numCols),ratio) ;

Une fois que le gagnant est choisi, les poids sont ajustés. Dans les adjustWeights () méthode,
nous employons le voisinage, la distance à partir de l'unité de gain, et le learnRate pour
ajuster les poids d'unités dans le voisinage du gagnant.

public void adjustWeights() {

// apply the changes to the weights


int i, j, inx, base ;
int numOutputs = numRows * numCols ;
double dist, range, sigma_squared ;

sigma_squared = sigma * sigma ;

for(i = 0 ; i < numOutputs ; i++) {


dist = Math.exp((distance[winner*numOutputs+i] * -1.0) /
(2.0 * sigma_squared));

base = i * numInputs ; // compute the base index


range = learnRate * dist ;
for (j=0 ; j < numInputs ; j++) {
inx = base + j ;
weights[inx] += range * (activations[j]- weights[inx]);
}
}
}

public void cluster() {

readInputs() ; // set input unit activations


computeOutputs() ; // do forward pass through network
selectWinner() ; // find the winning unit

// only adjust if in training mode


if (mode == 0) adjustWeights() ; // apply changes to weights

Maintenant nous regardons un exemple de la formation de carte de Kohonen using le notre


apprenons l'applet. Using un navigateur Java-permis, s'ouvrir sur le dossier de
LearnApplet.html. Le dialogue d'applet d'étude suivant les indications du schéma 5.5 apparaît.
LY_SII __ 136 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Choisir la carte de Kohonen comme la méthode de étude et l'ensemble de données de


faisceau dans la commande bien choisie au dessus du panneau. Cliquer dessus le bouton de
charge pour lire dedans les données de faisceau. Le dossier de kmap1.dfn et les données
seront chargés dans l'ensemble de données et montrés dans la commande supérieure de
TextArea. Presser le bouton marche pour former le réseau de carte de Kohonen. Quand la
formation est complète, l'index d'unité de rendement de gagnant et le contenu de la rangée
d'activation seront montrés pour chaque disque dans le TextArea inférieur.

Exécution d'arbre de décision

Maintenant, tournons-nous vers notre exécution d'un algorithme d'arbre de décision qui
manipule des variables discrètes. Il y a deux classes principales : un noeud qui représente un
noeud d'arbre de décision, et la classe de DecisionTree elle-même. Le noeud a un nom ou
étiquette, un vecteur des liens à d'autres noeuds dans le DecisionTree, une référence au
noeud de parent, et un vecteur contenant des références aux noeuds d'enfants.

// decision tree node


class Node {

String label ; // name of the node


Vector linkLabels; // tests on links from parent to child
Node parent ; // parent node
Vector children ; // any children nodes

public Node() { parent = null ;


children = new Vector();
linkLabels = new Vector(); }

public Node(String Label) {


label = Label ;
children = new Vector() ;
parent = null;
linkLabels = new Vector() ; }

public Node(Node Parent, String Label) {


parent = Parent ;
children = new Vector() ;
label = Label ;
linkLabels = new Vector() ; }
void addChild(Node child, String linkLabel) {
children.addElement(child) ;
linkLabels.addElement(linkLabel) ;}
boolean hasChildren() {
if (children.size() == 0) return false;
else return true; }
void setLabel(String Label) { label = Label ; }

static void displayTree(Node root) {


if (root.children.size() == 0) {
DecisionTree.appendText(“\nLeaf node - ” + root.label) ;
return ;
} else {
Enumeration enum = root.children.elements() ;
Enumeration enum2 = root.linkLabels.elements() ;
DecisionTree.appendText(“\nInterior node - ” + root.label) ;
while (enum.hasMoreElements()) {
LY_SII __ 137 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

DecisionTree.appendText(“\nLink - ” +
(String)enum2.nextElement()) ;
displayTree((Node)enum.nextElement()) ;
}
}
}

};

La classe de DecisionTree a un nom, une référence à un objet d'ensemble de données,


comme dans les classes de réseau neurologique, le but variable, classVar, et une liste de
toutes les définitions variables pour l'ensemble de données. Le vecteur des exemples détient
l'ensemble de records de formation qui est le résultat d'une division de l'ensemble original de
formation, basé sur un essai d'attribut. Le DecisionTree a un constructeur simple permettant
au nom d'être placé.

public class DecisionTree {


String name ;
static DataSet ds ;
Variable classVar ; // the class Variable
Hashtable variableList ;

Vector ruleList ; // list of all rules


Vector conclusionVarList ; // queue of variables
Vector examples ;
int fieldsPerRec;
static TextArea textArea1 ;

static public void appendText(String text) {


textArea1.appendText(text);}
String record[] ; // one record of train/test data in string format

DecisionTree(String Name) {
name = Name;
}
. . .
}

La méthode de buildDecisionTree () est la méthode principale dans cette classe. Elle prend
trois paramètres, un vecteur des exemples (l'ensemble de données), une liste des définitions
de champ, et la valeur par défaut qui devrait être retournée au cas où l'arbre de décision
échouerait. La première chose que nous faisons est instancient un noeud de racine pour
l'arbre de décision. Si l'ensemble de formation est vide, alors nous retournons avec la valeur
par défaut. Autrement, si tous les disques dans l'ensemble de formation ont une valeur
identique pour le champ de classe, nous retournons avec un noeud de feuille avec cette
valeur. Si ni l'un ni l'autre de ces deux conditions ne sont vraies, nous devons construire un
arbre de décision. La première étape est de choisir le meilleur attribut ou variable sur lesquels
pour se dédoubler. () La méthode chooseVariable fait ce choix using la théorie de
l'information. Nous assignons la racine de cet arbre à un noeud pour cette variable.
Maintenant que nous avons décidé quelle variable à dédoubler dessus, nous devons
déterminer quelle valeur discrète de cet attribut nous devrions employer pour la fente. Pour ()
la boucle fait ceci en prenant chaque valeur possible du variable, subsetting les données de
formation (exemples) dans des groupes où tous les disques ont la valeur désirée de cet
attribut, et calculant le gain de l'information pour cette valeur de la variable. Nous appelons
LY_SII __ 138 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

périodiquement le buildDecisionTree () pour compléter des branches jusqu'à ce que nous


ayons construit un arbre de décision complet commençant par notre noeud de racine.

public Node buildDecisionTree(Vector examples,


Hashtable variables,
Node defaultValue)
{

Node tree = new Node() ;

if (examples.size() == 0) return defaultValue ;


else if (identical(examples, classVar))
return
new Node(((String[])examples.firstElement())[classVar.column]) ;
else if (variables.size() == 0)
return new Node(majority(examples)) ;
else {
Variable best = chooseVariable(variables, examples);
tree = new Node(best.name) ; // variable with most Gain
Enumeration enum = best.labels.elements();
int numValues = best.labels.size();
for (int i=0 ; i < numValues; i++) {
Vector examples1 = subset(examples, best, best.getLabel(i)) ;
Hashtable variables1 = (Hashtable)variables.clone();
variables1.remove(best) ;
Node subTree = buildDecisionTree( examples1,variables1,
new Node(majority(examples1)));
tree.addChild(subTree, best.name + “=” + best.getLabel(i) ) ;
}
}
return tree ;
}

() La méthode chooseVariable prend deux paramètres : la liste de variables à considérer, et


un sous-ensemble des données de formation. D'abord nous appelons des getCounts () pour
calculer le nombre d'exemples positifs et négatifs de notre variable de classe, qui sont
retournés comme éléments d'une rangée de nombre entier. Après nous calculons la valeur de
l'information de cet ensemble de données using le p et le n retournés par des getCounts ().
Noter que dans cette exécution, nous avons seulement des variables binaires de classe. Enfin
nous calculons le reste pour chacun variable et choisissons celui ce des résultats dans le plus
grand gain de l'information.

// return the variable with most gain


Variable chooseVariable(Hashtable variables, Vector examples)
{
Enumeration enum = variables.elements() ;
double gain = 0.0, bestGain = 0.0 ;
Variable best = null ;
int counts[] ;
counts = getCounts(examples) ;
int pos = counts[0] ;
int neg = counts[1] ;
double info = computeInfo(pos, neg);
textArea1.appendText(“\nInfo = ” + info ) ;

while(enum.hasMoreElements()) {
Variable tempVar = (Variable)enum.nextElement() ;
LY_SII __ 139 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

gain = info - computeRemainder(tempVar, examples);


textArea1.appendText(“\n” + tempVar.name + “ gain = ” + gain) ;
if (gain > bestGain) {
bestGain = gain ;
best = tempVar;
}
}
textArea1.appendText(“\nChoosing best variable: ” + best.name) ;
return best; //

La méthode de computeInfo () prend deux paramètres de nombre entier, p et n, comme


entrées. Elle calcule la valeur de l'information comme définie dans la formule pour I () ci-
dessus. La seule partie rusée est le calcul du log2 n, qui emploie le fait mathématique que la
notation de n'importe quelle base peut être calculée using la notation normale de n divisée par
la notation normale des 2. bas. La méthode de computeRemainder () prend un ensemble de
données simple variable et comme entrées. Elle calcule le nombre de positif et des formes
négatives pour chaque valeur unique que la variable discrète peut prendre. Elle additionne la
valeur de l'information pour chaque valeur distincte pesée par la probabilité antérieure de
cette valeur et la renvoie comme reste.

// compute information content, given # of pos and neg examples


double computeInfo(int p, int n) {

double total = p + n ;
double pos = p / total ;
double neg = n / total;
double temp;
if ((p ==0) || (n == 0)) {
temp = 0.0 ;
} else {
temp = (-1.0 * (pos * Math.log(pos)/Math.log(2))) -
(neg * Math.log(neg)/Math.log(2)) ;
}
return temp ;
}

double computeRemainder(Variable variable, Vector examples)


{
int positive[] = new int[variable.labels.size()] ;
int negative[] = new int[variable.labels.size()] ;
int index = variable.column ;
int classIndex = classVar.column ;
double sum = 0 ;
double numValues = variable.labels.size() ;
double numRecs = examples.size() ;

for( int i=0 ; i < numValues ; i++) {


String value = variable.getLabel(i); // get discrete value

Enumeration enum = examples.elements() ;


while (enum.hasMoreElements()) {
String record[] = (String[])enum.nextElement();
if (record[index].equals(value)) {
if (record[classIndex].equals(“yes”)) {
positive[i]++ ;
} else {
LY_SII __ 140 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

negative[i]++;
}
}
} /* endwhile */
double weight = (positive[i]+negative[i]) / numRecs;
double myrem = weight * computeInfo(positive[i], negative[i]);
sum = sum + myrem ;
} /* endfor */

return sum ;
}

En conclusion, nous avons plusieurs méthodes de service. La méthode de sous-ensemble ()


prend un ensemble de données, une définition variable, et une valeur pour cette variable et
renvoie le sous-ensemble de données qui contiennent cette valeur dans chaque disque. () La
méthode identique prend l'ensemble de données et une variable simple comme entrée, et
renvoie un booléen indiquant si tous les disques de formation contiennent la même valeur
pour la variable spécifique. La méthode de majorité () examine un ensemble de données et
renvoie l'étiquette qui a les la plupart des exemples pour la variable spécifique. La méthode de
getCounts () calcule le nombre de fois où chaque valeur discrète apparaît pour la variable
spécifique dans l'ensemble de données.

// return a subset of examples with (variableName = value)


Vector subset(Vector examples, Variable variable, String value)
{
int index = variable.column ;
Enumeration enum = examples.elements() ;
Vector matchingExamples = new Vector() ;

while(enum.hasMoreElements()) {
String[] record = (String[])enum.nextElement() ;
if (value.equals(record[index])) {
matchingExamples.addElement(record) ;
}
}
textArea1.appendText(“\n Subset - there are ” +
matchingExamples.size() +
“ records with ” +
variable.name + “ = ” + value) ;

return matchingExamples;
}

public boolean identical(Vector examples, Variable variable)


{
int index = variable.column ;
Enumeration enum = examples.elements() ;
boolean same = true ;
String value = ((String[])examples.firstElement())[index] ;
while(enum.hasMoreElements()) {
if (value.equals(((String[])enum.nextElement())[index]))
continue ;
else { same = false ; break ; }
}
return same;

}
LY_SII __ 141 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

public String majority(Vector examples)


{
int index = classVar.column ;
Enumeration enum = examples.elements() ;
int counts[] = new int[classVar.labels.size()] ;

while(enum.hasMoreElements()) {
String value = ((String[])enum.nextElement())[index];
int inx = ((Variable)classVar).getIndex(value) ;
counts[inx]++;
} /* enbwhile */

int maxVal = 0 ;
int maxIndex = 0 ;
for(int i=0 ; i < classVar.labels.size() ; i++) {
if (counts[i] > maxVal) {
maxVal = counts[i] ;
maxIndex = i ;
} /* endif */
} /* endfor */
return classVar.getLabel(maxIndex);
}

public int[] getCounts(Vector examples)


{
int index = classVar.column ;
Enumeration enum = examples.elements() ;
int counts[] = new int[classVar.labels.size()] ;

while(enum.hasMoreElements()) {
String value = ((String[])enum.nextElement())[index];
int inx = ((Variable)classVar).getIndex(value);
counts[inx]++;
} /* enbwhile */
return counts;
}

Nous employons l'ensemble de données de restaurant et notre apprendre l'applet pour montrer
un exemple d'un arbre de décision. Using un navigateur Java-permis, ouvrir le dossier de
LearnApplet.html. Le dialogue d'applet d'étude suivant les indications du schéma 5.5 apparaît.
Choisir l'arbre de décision comme méthode de étude et l'ensemble de données de restaurant
dans la commande bien choisie au dessus du panneau. Cliquer dessus le bouton de charge
pour lire dedans les données de restaurant. Le dossier de resttree.dfn et les données seront
chargés dans l'ensemble de données et montrés dans la commande supérieure de TextArea.
Presser le bouton marche pour construire l'arbre de décision. Quand la formation est
complète, la structure de l'arbre de décision sera montrée dans le TextArea inférieur.

L'exécution d'applet d'étude

L'applet d'étude est un Java applet Qui a été développé using l'instrument de développement
visuel de Symantec CafŽ. La vue d'applet se compose de deux commandes de TextArea
pour montrer l'ensemble de données et former l'information respectivement, de trois
commandes de radiobutton pour choisir l'algorithme d'étude, d'une commande bien choisie
LY_SII __ 142 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

pour choisir l'ensemble de données, et de l'awt quatre.Boutons suivant les indications du


schéma 5.5. Quand le bouton marche est cliqué, une méthode correspondante d'essai ()
s'appelle, selon l'algorithme choisi.

public class LearnApplet extends Applet {


void button1_Clicked(Event event) {

// start -- first get the selected data set


String dsName = choice1.getSelectedItem() ;
dataSet = (DataSet)dataSets.get(dsName) ;

if (radioButton1.getState() == true)
testBackProp(dataSet, textArea2);
if (radioButton2.getState() == true)
testKMapNet(dataSet,textArea2) ;
if (radioButton3.getState() == true)
testDecisionTree(dataSet, textArea2);
}

// stop
void button2_Clicked(Event event) {
// for future use
}
// reset
void button3_Clicked(Event event) {

//{{CONNECTION
// Clear the text for TextArea
textArea2.setText(“”);
//}}
}

// load the selected data set


public void button4_Clicked(Event event) {
String dsName = choice1.getSelectedItem() ;
dataSet = (DataSet)dataSets.get(dsName) ;
if (dataSet.numRecords > 0) {
textArea1.appendText(“\nDataSet '” + dsName +
“' was already loaded!\n”) ;
} else {
dataSet.loadDataFile() ; // load the data set
}
}

public void init() {


super.init();

//{{INIT_CONTROLS
setLayout(null);
addNotify();
resize(518,495);
label1 = new java.awt.Label(“Data Set”);
label1.reshape(12,12,168,25);
add(label1);
textArea1 = new java.awt.TextArea();
textArea1.reshape(0,48,516,120);
add(textArea1);
textArea2 = new java.awt.TextArea();
textArea2.reshape(0,204,516,168);
LY_SII __ 143 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

add(textArea2);
button1 = new java.awt.Button(“Start”);
button1.reshape(36,444,96,36);
add(button1);
button2 = new java.awt.Button(“Stop”);
button2.reshape(192,444,109,32);
add(button2);
button3 = new java.awt.Button(“Reset”);
button3.reshape(372,444,97,36);
add(button3);
radioButtonGroupPanel1 =
new symantec.itools.awt.RadioButtonGroupPanel();
radioButtonGroupPanel1.setLayout(null);
radioButtonGroupPanel1.reshape(12,384,499,43);
add(radioButtonGroupPanel1);
Group1 = new CheckboxGroup();
radioButton1 = new java.awt.Checkbox(“back prop”,
Group1, false);
radioButton1.reshape(24,12,135,24);
radioButtonGroupPanel1.add(radioButton1);
radioButton2 = new java.awt.Checkbox(“Kohonen map”,
Group1, false);
radioButton2.reshape(180,12,114,22);
radioButtonGroupPanel1.add(radioButton2);
radioButton3 = new java.awt.Checkbox(“Decision Tree”,
Group1, false);
radioButton3.reshape(348,12,132,24);
radioButtonGroupPanel1.add(radioButton3);
choice1 = new java.awt.Choice();
add(choice1);
choice1.reshape(180,12,182,24);
label2 = new java.awt.Label(“Status ”);
label2.reshape(0,168,416,31);
add(label2);
button4 = new java.awt.Button(“Load”);
button4.reshape(396,12,108,28);
add(button4);
//}}
radioButton1.setState(true) ; // select back prop
DataSet.setDisplay(textArea1) ; // top text area
dataSets = new Hashtable() ;
choice1.addItem(“XOR”) ; // select XOR as default
dataSets.put(“XOR”, new DataSet(“XOR”,“xor”)) ;
choice1.addItem(“Vehicles”);
dataSets.put(“Vehicles”,
new DataSet(“Vechicles”, “vehicles”)) ;
choice1.addItem(“Restaurant”);
dataSets.put(“Restaurant”,
new DataSet(“Restaurant”,“resttree”)) ;
choice1.addItem(“Linear Ramp”) ;
dataSets.put(“Linear Ramp”,
new DataSet(“Linear Ramp”,“ramp2”)) ;
choice1.addItem(“Cluster Data”) ;
dataSets.put(“Cluster Data”,
new DataSet(“Cluster Data”,“kmap1”)) ;
choice1.addItem(“Animal Data”) ;
dataSets.put(“Animal Data”,
new DataSet(“Animal Data”,“animal”)) ;
choice1.addItem(“XOR Tree Data”) ;
dataSets.put(“XOR Tree Data”,
LY_SII __ 144 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

new DataSet(“XOR Tree Data”,“xortree”)) ;


}

public boolean handleEvent(Event event) {


if (event.target == button1 && event.id==Event.ACTION_EVENT) {
button1_Clicked(event);
return true;
} if (event.target == button2 && event.id==Event.ACTION_EVENT) {
button2_Clicked(event);
return true;
}if (event.target == button3 && event.id==Event.ACTION_EVENT) {
button3_Clicked(event);
return true;
}if (event.target == button4 && event.id==Event.ACTION_EVENT) {
button4_Clicked(event);
return true;
}
return super.handleEvent(event);
}

//{{DECLARE_CONTROLS
java.awt.Label label1;
java.awt.TextArea textArea1;
java.awt.TextArea textArea2;
java.awt.Button button1;
java.awt.Button button2;
java.awt.Button button3;
symantec.itools.awt.RadioButtonGroupPanel radioButtonGroupPanel1;
java.awt.Checkbox radioButton1;
CheckboxGroup Group1;
java.awt.Checkbox radioButton2;
java.awt.Checkbox radioButton3;
java.awt.Choice choice1;
java.awt.Label label2;
java.awt.Button button4;
//}}

Hashtable dataSets ;
DataSet dataSet = null ;

public static void testBackProp(DataSet dataSet, TextArea bottomText) {


BackProp testNet = new BackProp(“Test Back Prop Network”);
testNet.textArea1 = bottomText ;
testNet.ds = dataSet ;
testNet.numRecs = dataSet.numRecords ;
testNet.fieldsPerRec = dataSet.normFieldsPerRec ;
testNet.data = dataSet.normalizedData ; // get vector of data
int numOutputs = dataSet.getClassFieldSize() ;
int numInputs = testNet.fieldsPerRec - numOutputs;

testNet.createNetwork(numInputs,numInputs,numOutputs) ;
int maxNumPasses = 2500 ; // default -- could be on applet
int numSteps = maxNumPasses * testNet.numRecs ;
for (int i=0 ; i < numSteps; i++) {
testNet.process() ; // train
}

testNet.mode = 1 ; // lock the network


// do a final pass and display the results
for (int i=0 ; i < testNet.numRecs ; i++) {
LY_SII __ 145 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

testNet.process() ; // test
testNet.display_network() ;
}
}

public static void testKMapNet(DataSet dataSet, TextArea bottomText) {


KMapNet testNet = new KMapNet(“Test Kohonen Map Network”);

testNet.textArea1 = bottomText ;
testNet.ds = dataSet ;
testNet.numRecs = dataSet.numRecords ;
testNet.fieldsPerRec = dataSet.fieldsPerRec ;
testNet.data = dataSet.normalizedData ; // get vector of data

// create network, all fields are inputs


testNet.createNetwork(testNet.fieldsPerRec, 4, 4) ;
int maxNumPasses = 20 ; // default -- could be on applet
int numCycles = maxNumPasses * testNet.numRecs ;

// train the network


for (int i=0 ; i < numCycles; i++) {
testNet.cluster() ; // train
}

testNet.mode = 1 ; // lock the network weights


for (int i=0 ; i < testNet.numRecs ; i++) {
testNet.cluster() ; // test
testNet.display_network() ;
}
}

public static void testDecisionTree(DataSet dataSet,


TextArea bottomText)
{
DecisionTree tree = new DecisionTree(“\n Test Decision Tree ”) ;
tree.textArea1 = bottomText ;
tree.ds = dataSet ;

tree.textArea1.appendText(“Starting DecisionTree ”) ;

tree.fieldsPerRec = dataSet.fieldsPerRec;
tree.examples = dataSet.data ; // get vector of data
tree.variableList = dataSet.variableList ;
tree.classVar = (Variable)tree.variableList.get(“ClassField”) ;
tree.variableList.remove(“ClassField”) ;

// recursively build tree


Node root = tree.buildDecisionTree(tree.examples,
tree.variableList,
new Node(“default”)) ;

// now display the results


tree.textArea1.appendText(“\nDecisionTree -- classVar = ”
+ tree.classVar.name);
Node.displayTree(root) ;
tree.textArea1.appendText(“\nStopping DecisionTree - success!”) ;
}

}
LY_SII __ 146 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Systèmes de classificateur

Des systèmes de classificateur ont été développés par John Hollande comme manière de
présenter l'étude aux systèmes basés sur les règles. Son mécanisme a été basé sur une
technique connue sous le nom d'algorithmes génétiques.

Algorithmes génétiques

Les algorithmes génétiques utilisent une métaphore de processus biologique pour dériver des
solutions aux problèmes. D'abord, l'état de problème doit être codé dans une corde qui est
habituellement une corde binaire, mais pourrait être une corde des valeurs réelles. Il doit y a
une évaluation ou une fonction objective qui prend que corde codée comme entrée et produit
« qualité » ou des points biologiques de « forme physique ». Ces points sont alors employés
pour ranger un ensemble de cordes (individus dans la population des cordes) basées sur leur
forme physique. Si vous êtes familiarisé avec un quelconque des théories de Darwin vous
pouvez voir probablement où ceci va.

Les cordes (individus) qui sont les plus adaptées sont aléatoirement choisies survivre et
procréer même dans la prochaine génération des cordes. Deux opérateurs génétiquement
inspirés sont habitués pour modifier la corde choisie, afin d'essayer d'améliorer sa forme
physique et obtenir même plus près du but ou de la solution optima. D'abord, il y a la
mutation, qui, comme la mutation biologique, exécute des mutations aléatoires ou change
dans le chromosome de l'individu. Dans la caisse binaire habituelle de corde, nous renversons
aléatoirement quelque peu. En second lieu, il y a croisement, où deux en particulier individus
adaptés sont choisis et le matériel génétique de eux est combiné, formant un nouvel individu
ou enfant. Le raisonnement intuitif pour cette opération est que parce que les deux parents
étaient les individus convenables, il est probable bons que la progéniture également bien-soit
dotée en termes de fournir une bonne solution au problème actuel.

Des algorithmes génétiques sont en particulier adaptés aux problèmes d'optimisation parce
qu'ils, essentiellement, exécutent une recherche parallèle dans l'espace d'état. L'opérateur de
croisement injecte des grands nombres de bruit dans le processus pour s'assurer que l'espace
de recherche entier est couvert. L'opérateur de mutation permet fine-tuning des individus
d'ajustement en quelque sorte semblables aux techniques de colline-montée de recherche. Des
paramètres de commande sont employés pour déterminer combien grand la population est,
comment des individus sont choisis parmi la population, et combien de fois n'importe quels
mutation et croisement est exécutée.

En représentant la connaissance sous la forme de règle et puis en représentant intérieurement


les règles en tant que cordes binaires, la Hollande a employé les algorithmes génétiques pour
adapter ses règles (Hollande et autres 1986). Les puissances de linéarisation ont été
concentrées sur améliorer l'applicabilité de la base de connaissance elle-même.

Résumé
LY_SII __ 147 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

En ce chapitre, nous avons discuté des techniques d'étude de machine telles que les réseaux
neurologiques et les arbres de décision. Les questions principales incluent :

• Il y a plusieurs différentes formes d'étude. L'étude par coeur est basée sur la mémorisation
des exemples. L'extraction et l'induction de dispositif sont employées pour trouver des
caractéristiques importantes d'un problème et pour établir un modèle qui peut être employé
pour la prévision dans de nouvelles situations. Le groupement est une manière d'organiser
les modèles semblables en groupes.

• L'étude dirigée se fonde sur un professeur qui fournit les données d'entrée aussi bien que
la solution désirée. L'étude non surveillée dépend des données d'entrée seulement et ne fait
aucune demande sur connaître la solution. L'étude de renfort est un genre d'étude dirigée,
où la rétroaction est plus générale.

• En ligne l'étude signifie que l'agent s'adapte tandis que cela fonctionne. L'étude en différé
implique des données d'économie tandis que l'agent est travaillant et employant ces
données plus tard pour former l'agent.

• Les réseaux neurologiques sont des modèles d'informatique parallèle qui s'adaptent une
fois présentés avec des données de formation. Ils fonctionnent en modes dirigée, non
surveillés, et de renfort d'étude. Un réseau neurologique est composé d'un ensemble
d'unités de traitement simples et d'un ensemble de poids adaptatifs et à valeurs réelles de
raccordement. L'étude dans les réseaux neurologiques fait par l'ajustement des poids de
raccordement.

• Des réseaux arrières de propagation sont dirigés, les réseaux neurologiques de réaction qui
emploient la propagation en arrière de l'algorithme d'erreurs pour ajuster les poids de
raccordement. Ils sont employés pour des problèmes de classification et de prévision.

• Les réseaux de carte de Kohonen sont non surveillés, les réseaux neurologiques de réaction
qui individu-organisent et apprennent à tracer les entrées semblables sur les unités de
rendement qui sont dans la grande proximité entre eux. Les entrées sont mesurées using la
distance euclidienne dans un espace haut-dimensionnel d'entrée et sont tracées sur un
espace bidimensionnel de rendement. Les cartes de Kohonen exécutent le groupement des
données d'entrée.

• Les arbres de décision emploient la théorie de l'information pour déterminer où dédoubler


des ensembles de données afin de construire des classificateurs et des arbres de régression.

• Les systèmes de classificateur sont des systèmes de règle qui emploient des algorithmes
génétiques pour modifier la base de règle. Les algorithmes génétiques utilisent une
métaphore biologique pour exécuter la recherche parallèle dans l'espace d'état. L'état de
problème est codé en tant que cordes binaires et est modifié using les opérateurs génétiques
tels que le croisement et la mutation.

Exercices
LY_SII __ 148 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

1. Etant donné les situations suivantes, que l'étude du paradigme (dirigé, non surveillé, ou
renfort) est les la plupart approprié et pourquoi ?

• Articles de rang basés sur la rétroaction d'utilisateur spécifique

• Clients de division dans des groupes avec les préférences semblables

• Jeu du tic-tac-orteil ou des contrôleurs

2. Ajouter un nouvel ensemble de données au LearnApplet pour classifier des pièces de


monnaie par leurs attributs. Ceci signifie qu'un nouveau dossier de définition de données
doit également être créé. Employer l'ensemble de données de XOR dans le LearnApplet
comme exemple. Former un réseau arrière de propagation pour classifier les pièces de
monnaie. De combien d'exemples avez-vous eus besoin pour obtenir des résultats
satisfaisants ?

3. Employer le même ensemble de données des exemples et grouper les données avec une
carte de dispositif de Kohonen.

4. Employer le même ensemble de données de l'exercice 2 et construire un arbre de


décision. Comparer l'exactitude de classification du réseau arrière de propagation. Comment
le temps de formation a-t-il comparé ?
LY_SII __ 149 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Chapitre 6
Agents Intelligent
En ce chapitre nous explorons le rapport entre l'intelligence artificielle et les
agents intelligents en détail. Nous montrons comment des matières d'intelligence
artificielle telles que la représentation de connaissance, le raisonnement, et
l'étude peuvent être combinées pour construire les agents intelligents. Nous
discutons les conditions pour les agents intelligents autonomes comprenant la
perception, le raisonnement, et la capacité d'agir. Nous décrivons une des
architectures multiagent les plus tôt, systèmes de tableau noir, et puis discutons
quelques idées courantes au sujet des communications d'agent, spécifiquement la
langue de question et de manipulation de la connaissance. En conclusion, nous
regardons des systèmes où les agents intelligents multiples communiquent,
coopèrent, et concurrencent.

De l'IA à AI

Dans les chapitres précédents, nous avons exploré les matières principales dans l'intelligence
artificielle, les techniques de recherche, les représentations de connaissance, les algorithmes
de motif, et l'étude. Dans le chapitre préliminaire, nous avons décrit comment des agents
intelligents peuvent être employés pour renforcer le potentiel des applications et pour aider
des utilisateurs à obtenir leur travail effectué. Dans cette section, nous précisons les liens
explicites entre les technologies de base d'AI et les comportements correspondants d'agent
intelligent. Nous précisons également les dispositifs de la langue de Java qui sont applicables
ou importants pour mettre en application ces comportements.

En chapitre 2, nous avons discuté la résolution des problèmes état-basée using la recherche.
En chapitre 3, nous avons exploré plusieurs formes de représentation de connaissance. Pour
nos agents intelligents, la représentation de connaissance est une question cruciale. Comme
dans n'importe quelle application d'AI, ce qu'on s'attend à ce que notre agent fasse, et dans
quel domaine, aura un impact significatif sur le type de représentation de connaissance que
nous devrions employer. Si notre agent a un nombre limité de situations qu'il doit répondre à,
hardcoding peut-être l'intelligence dans le code procédural de programme est la solution. Si
notre agent doit construire ou employer les modèles sophistiqués du domaine de problème et
résoudre des problèmes à différents niveaux d'abstraction, alors les armatures ou les filets
sémantiques sont la réponse. Cependant, si l'agent doit répondre à des questions ou produire
de nouveaux faits des données existantes, alors de la logique d'attribut ou si alors des règles
sont considérées. Dans beaucoup d'applications, nous devrons employer un mélange de ces
représentations de connaissance. Si notre agent va agir l'un sur l'autre avec d'autres agents et
doit partager la connaissance puis il devrait pouvoir probablement lire et écrire des données
de KIF.

La quantité d'intelligence, en termes de connaissance de domaine et puissance des algorithmes


de raisonnement, exigées par notre agent est liée au degré d'autonomie et, à un moindre degré,
de mobilité qu'il a. Si notre agent va devoir traiter un éventail de situations, alors il a besoin
d'une large base de connaissance et d'un moteur d'inférence flexible. S'il est mobile, il peut y a
une prime sur avoir une petite, compacte représentation de connaissance et un système léger
de raisonnement en termes de nombre d'instructions. En même temps, la mobilité peut
LY_SII __ 150 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

présenter les problèmes de sécurité qui peuvent rendre explicite si puis des règles moins
souhaitables qu'une autre représentation de connaissance. Par exemple, les attributs de la
« boîte noire » d'un réseau neurologique, qui sont vus comme inconvénient d'une perspective
symbolique traditionnelle d'AI, deviennent un avantage distinct une fois vus d'une position
avantageuse de connaissance-sécurité.

Si l'étude est une fonction souhaitable dépend du domaine que l'agent intelligent fonctionnera
dedans, aussi bien que l'environnement. Si l'agent est longévital et effectuera les tâches
semblables beaucoup de fois pendant sa vie, alors l'étude peut être employée pour améliorer
son exécution. Mais ajouter l'étude serait surpuissant si l'agent sera employé seulement de
temps en temps. Si nous savons les principes de base principaux, pourquoi est-ce que les
programmer pas simplement dans l'agent et il être capable de se comporter bien dès le début,
au lieu d'ajouter la complexité et les coûts se sont associés aux algorithmes d'étude ? Mais il y
a beaucoup de domaines, tels que les aides personnels, où nous ne savons pas les préférences
d'utilisateur à l'avance. Une option est de demander à l'utilisateur d'énoncer explicitement ce
que sont ses préférences. Mais ceci peut être pénible pour l'utilisateur et, selon le domaine,
peut être impossible. Souvent, les gens savent ce qu'ils l'aiment mais ne peuvent pas aisément
exprimer en mots. C'est où l'avantage d'un agent de étude apprête. En observant ce que
l'utilisateur fait dans certaines situations, l'agent intelligent peut apprendre ce que l'utilisateur
préfère d'une manière beaucoup moins importune qu'en jouant un jeu de « 20 questions. »

S'il y aura un grand nombre d'incertitude dans le domaine de problème, alors using les réseaux
bayésiens ou si alors les facteurs de règles avec certitude peuvent être appropriés. Si l'agent
doit trouver une réponse optimale, alors état-basées des techniques de recherche ou des
algorithmes génétiques biologiquement basés devraient être employés. Des méthodes de
planification, de ce type discutées en chapitre 4, s'appelleraient pour si l'agent doit exécuter
une série complexe d'actions. L'étude de renfort a pu également être employée pour apprendre
des ordres des opérations exigées pour atteindre un but.

Pour récapituler, toutes les techniques d'intelligence artificielle discutées dans ce livre
s'appliquent aux agents intelligents. Mais, elles sont juste des outils qui doivent être employés
par un concepteur habile pour ouvrer une solution qui répond aux besoins d'une application
spécifique. Dans le code de Java d'exemple, nous fournissons les blocs constitutifs simples
pour commencer votre exploration des agents intelligents. Dans la partie, nous combinerons
ces éléments de base dans une architecture pour produire un comportement plus puissant.
Dans les prochaines sections, nous regardons des possibilités, la perception et l'action
spécifiques d'agent, et discutons les conditions qu'ils placent sur des réalisations d'agent
intelligent.

Perception

Pour qu'un agent de logiciel prenne une certaine mesure intelligente, il d'abord doit pouvoir
percevoir ce qui continue autour de lui, pour avoir une certaine idée de l'état du monde. Pour
des animaux, ce problème est résolu par les sens du contact, de l'odeur, du goût, de l'audition,
et de la vue. Le prochain problème est de ne pas obtenir accablé par le jet constant
d'information. En raison du grand nombre d'entrée sensorielle crue nous obtenons, une des
premières choses que les humains apprennent est de filtrer dehors et ignorer les entrées qui
sont prévues ou habituelles. Nous développons un modèle interne du monde, de ce que les
conséquences prévues sont quand nous prenons une mesure. Tant que les choses vont comme
LY_SII __ 151 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

prévu, nous pouvons passer sans prêter trop d'attention (par exemple, quelqu'un conduisant
une voiture tout en parlant à un téléphone mobile). Mais nous également avons appris à nous
concentrer immédiatement sur les changements inattendus de l'environnement, tel qu'une
voiture apparaissant dans notre vision périphérique tout en entrant dans une intersection.

Notre agent intelligent doit avoir une source équivalente d'informations sur le monde en
lequel elle vit. Cette information entre par ses sondes, qui des mai ou mai ne pas être fondu
dans le monde physique. Notre agent intelligent n'a pas besoin d'avoir des sens de la même
manière que nous faisons, mais il doit encore pouvoir recueillir des informations au sujet de
son environnement. Ceci pourrait être fait activement, en envoyant des messages à d'autres
agents ou systèmes, ou il pourrait être fait passivement en recevant un jet des messages
d'événement du système, de l'utilisateur, ou d'autres agents. Juste comme des personnes,
l'agent de logiciel doit pouvoir distinguer les événements normaux (mouvements de souris)
des événements significatifs (double-click sur une icône d'action). Dans un environnement
moderne de GUI tel que Windows ou Macintosh, l'utilisateur produit d'un jet constant des
événements au système fondamental de fenêtrage. Nos agents peuvent surveiller ce jet et
doivent identifier des ordres des actions d'utilisateur de base (le mouvement de souris, font
une pause, cliquent, mouvement de souris, pause, double-click) en tant que signalisation d'un
certain événement ou action d'utilisateur sémantique à plus grande échelle.

Si notre agent fonctionne dans le domaine de moniteur d'email ou de newsgroup, il devra


identifier quand les nouveaux documents arrivent, si l'utilisateur est intéressé par les thèmes
ou pas, et s'interrompre l'utilisateur à une autre tâche de l'informer de l'information
nouvellement disponible. Toute la ceci tombe dans le royaume de la perception. Pouvoir noter
ou identifier l'information cachée dans les données n'est pas facile. Il exige la connaissance
d'intelligence et de domaine. Est ainsi s'appelant « clairvoyant » un compliment
habituellement réservé aux personnes intelligentes. Pour être les aides personnels utiles, les
agents doivent être clairvoyants.

Naturellement, nous pouvons concevoir nos agents et leurs messages de telle manière qu'ils ne
doivent pas être très clairvoyants. Nous pouvons exiger de l'utilisateur de dire explicitement à
notre agent quoi faire et comment le faire. Les événements qui sont produits pourraient
contenir toute l'information que l'agent doit déterminer l'état actuel et l'action appropriée.
Cependant, une des raisons principales que nous voulons et les agents intelligents du besoin
est de libérer l'utilisateur de devoir être si explicites. La définition de l'ordre des actions
nécessaires pour qu'un ordinateur accomplisse une tâche, dans le détail sanglant, n'est pas
amusement (elle programme !). C'est la puissance et le plaisir de la délégation à d'autres.
Vous pouvez dire, « programmer un voyage à Raleigh, » et à votre agent l'obtient fait.

Si la perception est la capacité d'identifier des modèles, et notre agent reçoit ses entrées
comme jets des données, alors nous écrivons le royaume de l'étude de machine et de la
reconnaissance des structures. L'étude est non seulement utile pour adapter le comportement,
il est également tout à fait maniable quand nous voulons identifier des changements de notre
environnement. Un système d'étude peut associer certains états du monde à certaines
situations. Ces situations peuvent être normales et prévues, ou elles peuvent être originales et
inattendues. Simplement la notation de l'occurrence des événements et alors le calcul de la
probabilité ou de la probabilité d'un événement ou d'une situation peuvent ajouter la fonction
utile à un agent. La construction d'une table ou d'une carte entre ces états et l'action associée
est encore meilleure. Des algorithmes d'étude dirigés, tels que les réseaux neurologiques et les
LY_SII __ 152 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

arbres de décision, peuvent être employés pour faire automatiquement ces associations. De
nouveau, si nous savons toutes les situations notre agent peut entrer dans avant que nous
l'envoyions dehors dans le Cyberspace, puis nous pouvons juste construire ceci dans sa base
de connaissance. Mais dans un environnement complexe avec un grand nombre de situations
possibles, l'étude peut être la seule manière d'aller.

Action

Une fois que notre agent a clairvoyant identifié qu'un événement significatif s'est produit, la
prochaine étape est de prendre une certaine mesure. Cette action pourrait être de se rendre
compte qu'il n'y a aucune action à prendre, ou elle pourrait être d'envoyer un message à un
autre agent pour prendre une mesure en notre nom. Comme des personnes, les agents agissent
par des effecteur. Pour des personnes, un effecteur est nos muscles, quand nous prenons une
mesure physique dans le monde. Ou nous pouvons agir par la parole (using différents
muscles) ou par l'envoi de l'email (différents muscles encore). Par communication avec
d'autres personnes, nous pouvons les faire agir et changer l'environnement.

Mais supposer que nous prenons une mesure en donnant une instruction au du système
d'exploitation ou à une application. Supprimer un dossier. Envoyer l'email. Supposons-nous
que l'action a accompli ? Changeons-nous notre modèle courant de l'état du monde ? Que si
nous demandons un autre agent font quelque chose pour nous ? Est-il prudent de penser que
tout fonctionnera correctement (l'agent est-il digne de confiance et exempt d'erreurs, le
serveur de réseau est-il en fonctionnement, la transaction va-t-il à travers le fil) ? Ou,
adoptons-nous la position cynique que la loi de Murphy est toujours en effet ? La réponse est,
« il dépend. » Juste comme nous savons que si nous mettons un livre vers le bas, il sera là la
prochaine fois que nous marchons près, si notre agent prend une mesure directement sous sa
commande, nous pouvons la considérer probablement faite. Mais quand nous traitons des
intermédiaires, si d'autres agents ou systèmes inconnus, puis quelques précautions
supplémentaires et la vérification sont probablement en règle.

Systèmes de Multiagent

Jusqu'ici en ce chapitre, nous avons regardé de quel agent intelligent simple doit être capable
pour aider un utilisateur à obtenir son travail effectué. Mais, juste comme nous avons des
compagnies où les talents uniques de beaucoup de personnes sont combinés pour résoudre des
problèmes, nous pouvons avoir les agents intelligents multiples travailler ensemble vers un
objectif commun.

Toutes les fois que deux autonomes, les êtres modérément intelligents essayent de
communiquer, les problèmes surgissent. D'abord, ils doivent parler la même langue qui
emploie le même ensemble de marques ou de symboles. Puis, ils doivent convenir sur ce que
signifient ces symboles. Ils doivent également développer une manière d'échanger des
communications dans cette langue. Ils ne peuvent pas tous les deux parler en même temps. Ils
ne peuvent pas tous les deux essayer d'écrire sur la même page en même temps. Ce sont tous
des problèmes fondamentaux dans la communication, et les premières applications
intelligentes artificielles qui ont essayé d'employer les agents multiples ont dû traiter et
résolvent chacun de ces problèmes. Dans la prochaine section, nous décrivons des systèmes
de tableau noir, la première architecture multiagent d'application d'AI.
LY_SII __ 153 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Tableaux noirs

Tandis que les tableaux noirs et la craie sont maintenant légèrement hors de la mode,
remplacée par des whiteboards et sèchent des marqueurs d'effacement, ils sont toujours une
métaphore utile pour une architecture importante d'intelligence artificielle. L'architecture de
tableau noir a été présentée la première fois dans le projet de reconnaissance de la parole de la
rumeur II (Erman et autres 1980). Elle a comporté un système avec des sources multiples de
la connaissance ou les agents indépendants, chacun avec un domaine spécifique d'expertise se
sont rapportés à l'analyse de la parole. Le tableau noir est une structure de données qui est
employée comme mécanisme général de communication pour les sources multiples de la
connaissance et est contrôlée et arbitrée par un contrôleur (Jagannathan, et autres 1989).

Pendant que chaque agent travaille à sa partie du problème, il regarde au tableau noir pour
prendre la nouvelle information signalée par d'autres agents, et elles, à leur tour, signalent
leurs résultats au tableau noir. Ainsi, tout comme un tableau noir dans un environnement de
salle de classe, le tableau noir est un dispositif de partage d'informations, avec les auteurs
multiples et les lecteurs multiples. Les agents, comme des étudiants, chaque travail à leur
propre rythme sur les problèmes qui sont de la plupart d'intérêt à eux ou où ils ont la
connaissance à appliquer, et ajoutent l'information au tableau noir quand ils peuvent. D'autres
agents emploient cette information pour promouvoir leur propre travail. Ainsi, l'architecture
de tableau noir permet aux agents multiples de travailler indépendamment et coopérer à
résoudre un problème.

Le modèle de tableau noir est une généralisation de l'architecture utilisée dans la rumeur II.
C'est une technique de résolution des problèmes opportuniste parce qu'à chaque cycle
opératoire n'importe quel genre de méthode de raisonnement peut être employé. Un modèle
d'événement est employé pour signaler quand des modifications sont apportées au tableau noir
et pour informer les sources ou les agents de la connaissance que quelque chose a changés. Un
événement pourrait déclencher l'activation d'un ensemble d'agents ou le contrôleur pourrait
dynamiquement déterminer quel agent à commencer. Le contrôleur limite également l'accès
au tableau noir de sorte que deux agents n'essayent pas d'écrire sur le même espace en même
temps.

Un point final concernant des systèmes de tableau noir est que les sources ou les agents de la
connaissance sont très étroitement accouplés par la structure de données de tableau noir et ses
interfaces. Si vous établissez une grande application simple et voulez modulariser les bases de
connaissances, alors les tableaux noirs sont très bien. Cependant, si vous voulez un
environnement où les agents avec les structures très différentes et sans la connaissance d'un
tableau noir peuvent fonctionner ensemble, nous avons besoin des interfaces plus formelles
comme ceux décrits dans la prochaine section.

Communication

Quand nos agents doivent parler entre eux, ils peuvent faire ceci d'une série de manières. Ils
peuvent parler directement entre eux, s'ils parlent la même langue. Ou ils peuvent parler par
un interprète ou le facilitant, étant donné qu'ils sachent parler à l'interprète, et à l'interprète
peut parler à l'autre agent.
LY_SII __ 154 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Il y a un niveau de syntaxe de langue-le et de format de base des messages, et il y a un niveau


plus profond la signification ou la sémantique. Tandis que la syntaxe est souvent facilement
compréhensible, la sémantique ne sont pas. Par exemple, deux agents d'expression anglaise
peuvent obtenir confus si on parle de la botte et du capot, et l'autre au sujet du capot et du
tronc d'une automobile. Ils doivent avoir un vocabulaire partagé des mots et de leur
signification. Ce vocabulaire partagé s'appelle un ontology.

KQML

La langue de question et de manipulation de la connaissance (KQML) fournit un cadre pour


des programmes et des agents à l'information et à la connaissance d'échange. Comme KIF,
KQML (la sorte de ces acronymes de tombent la langue, ne font pas ils ?) est sorti de la
connaissance de DARPA partageant l'effort. Considérant que KIF traite des représentations de
connaissance, KQML se concentre sur des formats de message et des protocoles de gestion de
messages entre les agents courants. Mais seulement les protocoles de message sont spécifiés.
Le contenu ou la langue employée pour représenter le contenu n'est pas. KQML définit les
opérations que les agents peuvent essayer sur les bases de connaissances de chacun, et fournit
une architecture de base pour des agents à la connaissance et à l'information de part par les
agents spéciaux appelés les facilitants. Les facilitants agissent en tant que des marieurs ou des
secrétaires pour les agents qu'ils entretiennent.

Des messages de KQML s'appellent les performatives. Chaque message est prévu pour
effectuer implicitement une certaine action spécifique. Il y a un grand nombre de
performatives définis dans KQML, et la plupart d'appui de systèmes agent-basé seulement un
petit sous-ensemble. Les performatives, ou les types de message, sont des mots réservés dans
KQML. Using des performatives, les agents peuvent demander d'autres agents l'information,
dire à d'autres agents des faits, souscrire aux services des agents, et offrir leurs propres
services.

KQML emploie des ontologies, des caractéristiques explicites de la signification, des


concepts, et des rapports applicables à un certain domaine spécifique, pour assurer que deux
agents communiquant dans la même langue peuvent correctement interpréter des rapports
dans cette langue. Par exemple, en anglais, quand nous disons « Java, » nous se rapportent au
café, à une île, ou à un langage de programmation ? Pour éliminer cette ambiguïté, chaque
message de KQML énonce explicitement quel ontology est employé.

Les messages de KQML codent l'information à trois niveaux architecturaux différents :


contenu, message, et communication. Un exemple d'un message de KQML d'agent Joe
s'enquérant du prix d'une part des actions du SOLEIL pourrait être codé comme :

(ask-one
:sender joe
:content (real price = sun.price())
:receiver stock-server
:reply-with sun-stock
:language java
:ontology NYSE-TICKS)

Le KQML performative est demande-un ; le récepteur du message est un agent appelé


provision-serveur. :le paramètre content définit complètement le niveau content. :répondre-
LY_SII __ 155 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

avec :expéditeur, et :les paramètres de récepteur spécifient l'information au niveau de


communication. Le nom performative :spécifications de langue, et :le nom d'ontology font
partie du niveau de message. Noter que nous pourrions envoyer à l'exact le même message
avec une langue différente et un morceau content, ou la même langue mais ontology différent,
et ainsi changer subtilement ou nettement la signification du message. Mais les performative,
demandent-un, seraient toujours une question d'agent Joe au provision-serveur d'agent.

Deux agents qui veulent communiquer using KQML ont besoin des services d'un facilitant ou
du marieur de KQML (Kuokka et Harada 1995). Les agents communiquent avec le marieur
employant les messages standard de KQML. Ils peuvent s'enregistrer comme fournisseur des
services ou de l'information using la publicité performative. Les agents peuvent également
demander au marieur de recommander d'autres agents using la recommandation, recruter, et
des performatives de courtier. Le marieur fournit un réunion-endroit centralisé pour des
agents, et établit une communauté où les agents peuvent agir l'un sur l'autre. Noter que le
marieur ne peut pas garantir pour la fidélité des agents qui annoncent leurs services, ni
garantit qu'un agent peut fournir une information spécifique. Néanmoins, le marieur joue un
rôle important dans un système multiagent. L'alternative serait pour que chaque agent
questionne chaque autre agent toutes les fois qu'elle a dû collaborer.

Agents de coopération

Le point de droit pour avoir les agents multiples coopèrent à réaliser un travail est irrésistible.
Pourquoi avoir un agent slave loin quand nous pouvons avoir cinquante fonctionner ensemble
pour accomplir la même tâche ? Quel est plus, pourquoi a une base et application de
connaissance énormes d'intelligence artificielle si nous pouvons résoudre le même problème
en construisant et le maintien d'une collection avec des agents beaucoup plus petits et plus
simples ? Naturellement, l'idée de diviser un problème en plus petits morceaux n'était pas une
invention d'intelligence artificielle. Diviser et conquérir est une stratégie militaire antique.
Dans de l'informatique, c'était l'un des principes de base de la conception structurée de haut en
bas, utilisés dans tout de COBOL à Pascal. Cependant, avec des agents, les avantages des
petites, indépendantes sources de la connaissance doivent être pesés contre les inconvénients
considérables de présenter une barrière linguistique entre eux. Mais dans plusieurs domaines,
les avantages ont loin été supérieurs aux coûts.

Car les modèles de client/serveur et de l'informatique répartie ont évolué pendant les deux
dernières décennies, les problèmes avec la gestion-système sont devenus évidents. Les
directeurs de système, qui ont été employés aux moniteurs d'exécution qui ont fourni des vues
claires de l'état du centre serveur centralisé, ont eu un temps difficile manipuler les
applications qui ont fonctionné à travers des réseaux des postes de travail. La solution était de
déployer des agents de moniteur de système sur chacun de ces systèmes distribués. Un
contrôleur centralisé pourrait communiquer (habituellement par le vote) avec l'agent du
moniteur à distance pour obtenir l'information sur le statut de chaque ordinateur à distance.
L'application de gestion de système distribué a pu intégrer cette information pour fournir une
vue d'ensemble de l'état actuel du système. Tout en commençant en tant qu'agents très
simples, ces agents de moniteur ont élevé un temps fini plus sophistiqué.

Dans le commerce électronique (commerce électronique), les agents intelligents représentent


des consommateurs et des fournisseurs des produits et des services. Sous une forme simple,
un agent peut représenter un voyageur qui veut réserver un vol, et qui questionne de divers
LY_SII __ 156 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

systèmes de réservation de ligne aérienne pour trouver le meilleurs prix et itinéraire. Sous une
forme plus complexe, l'acheteur doit avoir affaire avec un agent de vente, qui a son propre
ordre du jour (pour gagner autant argent comme possible par transaction pour son
propriétaire) et stratégies. Dans ce contexte de marché, les agents coopèrent à servir les
intérêts des deux parties. En même temps, il y a un courant de fond de la concurrence. Le
système de Kasbah développé à MIT Media Lab est un exemple de ce genre de marché
électronique (Chavez et Maes, 1996).

Les systèmes de conception de Multiagent sont un autre secteur où les agents indépendants de
multiple ont prouvé leur valeur (Lander 1997). Dans un système de conception de
collaboration, beaucoup de concepteurs doivent travailler aux morceaux de recouvrement de
la conception en même temps. Un changement d'une part peut effectuer plusieurs autres. Le
système de conception entier inclut les concepteurs humains aussi bien que les agents
intelligents, ainsi les interactions homme-ordinateur sont importantes. Souvent, des agents
d'aide personnel sont employés pour fournir l'interface entre les concepteurs et les agents dans
le système. Un aspect important des systèmes de conception multiagent est que chaque agent
intelligent a sa propre perspective sur ce qu'est une « bonne » ou « optimale » conception,
selon son domaine de spécialisation. Ce n'est pas très différent des équipes de développement
croix-fonctionnelles. Ainsi, chaque agent doit être disposé à compromettre afin de réaliser une
meilleure conception globale ou globale. Ceci signifie également qu'il y a quelque part un
agent qui peut fournir une certaine mesure de la qualité de la conception globale et peut
arbitrer des conflits entre les agents plus spécialisés.

Agents de concurrence

Dès que nous aurons des agents qui font notre offre, d'autres personnes auront des agents pour
faire le leur. Notre agent veut obtenir la meilleure affaire pour nous. D'autres agents veulent
obtenir la meilleure affaire pour leurs propriétaires. À qui agent gagne ? Celui probablement
avec la plupart d'intelligence, connaissance la plus spécialisée au sujet de la tâche qu'elle
essaye d'effectuer, système de raisonnement le plus puissant pour s'appliquer cette
connaissance à la résolution des problèmes dans le domaine, et, dans toute la probabilité, celui
qui peuvent apprendre de l'expérience et devenir meilleurs avec le temps.

Ceci suppose naturellement que nous avons un terrain de jeu de niveau. Il ne serait pas juste si
votre agent était sur un serveur étant en pourparlers avec un autre agent qui pourrait accéder à
une grande base de données locale d'information alors que votre agent ne pourrait pas. Ou, si
votre agent était balayé par le serveur, et des informations sur votre position de négociation
(prix, stratégies, règles, etc.) a été confié à l'autre agent. Enverriez-vous votre agent dehors sur
l'Internet si vous pensiez que vous pourriez perdre ou être tiré profit tellement facilement ?
Probablement pas. À notre avis, il devra y avoir les marchés bloqués d'agent, où les agents
s'enregistrent et sont garantis d'avoir l'égalité d'accès aux services et à l'information de
serveur.

Il y a également concurrence entre les agents à l'moins niveau direct. Par exemple, supposer
que deux travailleurs intellectuels aux compagnies de concurrence sont tous deux qui essayent
de finir une proposition de projet pour un client. Si on a un ensemble d'agents intelligents qui
peuvent l'aider à trouver les informations priées dans la moitié du temps en tant que son
concurrent, cela lui donne un avantage compétitif. C'est juste une prolongation de l'utilisation
courante de la technologie de l'information d'aider une compagnie à gagner dans le marché.
LY_SII __ 157 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Quand chacun a les agents intelligents travailler pour eux, le gagnant sera celui avec les
meilleurs agents.

Résumé

En ce chapitre, nous avons exploré comment des techniques d'intelligence artificielle sont
employées pour construire les agents intelligents. Les questions principales sont :

• Il y a beaucoup de techniques alternatives d'intelligence artificielle pour la


représentation de connaissance, le raisonnement, et l'étude. Les fonctions spécifiques
et les conditions d'un agent intelligent sont la cause déterminante principale dont des
techniques IA Devraient être employées.
• La quantité d'intelligence exigée par un agent, en termes de taille de la base de
connaissance et de la sophistication des algorithmes de raisonnement, est sensiblement
effectuée par le degré d'autonomie et mobilité que l'agent a. Conditions spéciales
d'endroit mobile d'agents sur la sécurité de la base de connaissance comme elle voyage
par le réseau.
• L'étude est la plus utile quand un agent est employé dans les environnements
complexes pour effectuer des tâches réitérées, ou quand l'agent doit s'adapter aux
situations inconnues. Aides personnels que les préférences d'utilisateur modèles et les
agents de collaboration bénéficieront également des possibilités d'étude.
• Les agents doivent pouvoir percevoir le monde physique ou virtuel autour de elles
using des sondes. Une partie fondamentale de perception est la capacité d'identifier et
filtrer dehors les événements prévus et de s'occuper les inattendus.
• Les agents intelligents emploient des effecteur pour agir en envoyant des messages à
d'autres agents ou en appelant des interfaces de programmation API pour commandes
Tempus-link ou des services de système directement.
• L'architecture de tableau noir permet à un groupe d'agents de coopérer à résoudre
des problèmes. Le tableau noir est une structure de données centralisée utilisée pour le
partage de communication et de données entre les agents. Le contrôleur contrôle le
tableau noir aussi bien que la génération et l'expédition des événements aux agents
dans le système.
• La langue de question et de manipulation de la connaissance (KQML) fournit un
cadre pour un ensemble d'agents indépendants pour communiquer et coopérer à un
problème using des messages appelés les performatives. KQML spécifie l'information
au niveau de communication (expéditeur et récepteur), au niveau de message (langue
et ontology), et au niveau content (phrases spécifiques à une langue).
• La coopération des agents permet à une communauté des agents spécialisés de
mettre leurs possibilités pour résoudre de grands problèmes, mais avec le coût
additionnel de frais généraux de communication. La gestion de systèmes distribués, le
commerce électronique, et les systèmes de conception multiagent sont trois domaines
d'application où des agents de coopération ont été appliqués.
• La concurrence entre les agents se produira dès que des agents intelligents seront
déployés par des individus ou des compagnies avec différents ordres du jour, et ces
agents agissent l'un sur l'autre dans l'environnement de commerce électronique. Des
agents intelligents seront employés pour fournir des avantages compétitifs pour des
individus et des entreprises.

Exercices
LY_SII __ 158 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

1. Comment l'intelligence artificielle se rapporte-t-elle aux agents intelligents en


termes de leur perceptiveness et des capacités d'action-prise ? L'AI est-elle essentielle
ou superflue ?
2. Quel rôle le contrôleur joue-t-il dans un système de tableau noir ?
3. Quelles sont les forces de KQML comme langage de communication d'agent ? Les
faiblesses ?
4. Quelle infrastructure serait exigée pour installer une enchère ou un marché à l'usage
des agents intelligents multiples ?
LY_SII __ 159 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

chapitre 7
Cadre d'agent du Intelligent
En ce chapitre, nous développons une architecture d'agent intelligent using des
techniques de conception orientée objectivement. Nous commençons par un
ensemble générique de conditions et les raffinons en jeu de caractéristiques. Nous
énonçons explicitement notre philosophie et buts de conception et considérons de
diverses solutions de rechange de conception et d'exécution sous ces contraintes.
Nous explorons comment des agents intelligents peuvent être employés pour
augmenter les possibilités des applications traditionnelles et comment ils peuvent
servir de contrôleur à un groupe d'applications. Avec des modifications mineures,
nous réutilisons les fonctions d'intelligence artificielle que nous avons
développées dans Java dans la partie de ce livre.

Conditions

La première étape dans n'importe quel projet de développement de logiciel est la collection de
conditions de la communauté d'utilisateur prévue. Dans notre cas, ceci est rendu difficile
parce que nos lecteurs ne peuvent pas fournir ce genre de rétroaction jusqu'à ce que nous
ayons déjà conçu et ayons développé le produit (ce livre). Cependant, nous avons fait
quelques décisions évidentes au sujet de l'assistance et but prévu de ce livre, comme indiqué
par le titre. Nous allons développer les agents intelligents using Java. Nous allons également
fournir la capacité d'ajouter l'intelligence aux applications ou les applet écrits dans Java. Une
condition fondamentale additionnelle est que nous devrions réutiliser le code d'intelligence
artificielle que nous avons développé dans la partie 1.

Une autre condition est que notre cadre d'agent intelligent soit pratique. Non pratique dans le
sens que c'est code de produit-niveau prêt à mettre dans la production, mais parce que les
principes de base et la poussée de notre conception s'applique à résoudre des problèmes réels.
Tandis que la fourniture d'une expérience d'étude stimulante est un but, nous ne sommes pas
intéressés à explorer purement l'universitaire ou, plus exactement, les issues ésotériques. Si
vous comprennent ce que nous faisons et pourquoi nous le font, vous devriez pouvoir
employer ces techniques pour développer vos propres applications d'agent intelligent.

Le foyer sur la matière actuelle est une autre condition. C'est un livre concernant les agents
intelligents. Nous ferions le lecteur un service si nous dépensions des grands nombres de code
de communications se développant de temps, d'une base de données orientée objectivement,
ou d'un mécanisme pour exécuter des appels de procédure à distance. Nous essayerons de
maximiser la quantité de code traitant les agents intelligents, et réduisons au minimum le code
pas directement lié à la matière. En même temps, parce que c'est un livre concernant Java
programmant, nous voulons employer les dispositifs et les possibilités ont trouvé dans Java et
l'environnement de développement de JDK 1.1.

Après avoir dit juste le tout ceci, nous reconnaissons également cela qui fournit une interface
utilisateurs décente est également une condition. Heureusement, avec les environnements de
développement courants de Java, la fourniture d'un GUI utilisable n'est pas un problème
majeur. Nous utiliserons l'outil visuel de Symantec CafŽ pour créer ces interfaces. Puisque la
LY_SII __ 160 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

majeure partie du code de GUI est produite, nous ne dépenserons pas beaucoup d'heure ou
d'énergie discutant cet aspect de nos applications. Il y a certainement d'autres outils visuels de
constructeur qui peuvent produire du code de Java et elles pourraient être employées au lieu
du produit de Symantec.

La prochaine condition vient des auteurs, qui dépensent également les livres de
programmation de beaucoup de lecture de temps. Nous ne créerons pas une conception
complexe et ne la décrirons pas dans le détail minutieux. Tandis que ce serait un exercice
intéressant pour nous, nous doutons qu'il te fournisse la valeur, nos lecteurs. Nous appellerons
ceci la « subsistance il » condition simple et stupide, et espérons qu'explicitement l'énumérant
ici shame nous dans suivre cette maxime quand nous obtenons trop emportés.

La dernière condition est que notre architecture doit être assez flexible pour soutenir les
applications présentées dans les trois prochains chapitres. Comme prévision, nous
construirons les agents intelligents pour manipuler la gestion d'un PC, de l'information filtrant
au-dessus de l'Internet, et des transactions multiagent simples de commerce électronique.

Pour récapituler les conditions, nous voulons une architecture simple pourtant flexible qui est
concentrée sur des questions d'agent intelligent. Elle doit être pratique ainsi elle peut résoudre
des problèmes réalistes et doit avoir une interface utilisateurs décente ainsi ses fonctions et
limitations seront tout à fait évidentes aux utilisateurs. Dans la prochaine section, nous parlons
de nos buts d'une perspective technique.

Buts de conception

Les conditions viennent de nos utilisateurs et nous indiquent quelles fonctions ou propriétés
notre produit doit avoir afin d'être réussi. Avoir un ensemble validé de conditions est utile,
parce qu'il concentre notre énergie sur la substance importante. Il est juste comme important
pour avoir un ensemble clair de buts de conception que nous pouvons employer pour guider
les décisions techniques qui doivent être prises pendant que nous développons la solution qui
répond à ces exigences. Juste comme avec des conditions, nous devons explicitement énoncer
nos buts et prétentions de conception.

Il y a quelques issues fondamentales qui conduiront notre conception. Le premier est que nous
pouvons regarder nos agents intelligents ou en tant qu'ajouter la valeur à une application
autonome simple, ou en tant que communauté libre des agents qui agissent l'un sur l'autre les
uns avec les autres et d'autres applications. Le premier est une vue application-centrale des
agents, où les agents sont des aides à la demande (et donc des utilisateurs de l'application).
Cette approche est moins la complexe parce que nous pouvons regarder l'agent comme
prolongation simple de la fonctionnalité d'application. Par la fourniture notre agent intelligent
fonctionne comme cadre orienté objectivement, nous peut facilement ajouter le comportement
intelligent à n'importe quelle application de Java.

La deuxième approche est agent-centrale, où les agents appellent les projectiles et le moniteur
et conduisent les applications. Ici notre directeur d'agent est une application à son propre chef
et doit se connecter par interface à d'autres applications qui sont conduites par les agents. La
complexité ici est que nous devons définir un mécanisme générique pour des communications
d'application par notre directeur d'agent. Une méthode est d'exiger de chaque application de
modifier son code pour être « agent-avertie. » Un autre est pour que nous fournissent une
LY_SII __ 161 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

manière commune de se connecter par interface aux interfaces de programmation API pour
commandes Tempus-link uniques.

Le cadre de CIAgent doit être facilement compréhensible et franc pour employer. Le but
primaire de développer ce cadre est d'illustrer comment des agents intelligents et les
différentes techniques IA Peuvent être employés pour augmenter des applications. Notre
modèle de codage sera franc. Les membres de données seront publics et des fonctions
d'accédant seront employées seulement quand l'encapsulation stricte est exigée. Ce n'est pas
code de commercial-niveau. Le code de blindage à l'épreuve des balles peut parfois faire
même la logique simple sembler complexe. Nos applications fonctionneront comme conçu,
mais elles ne pourront pas manipuler toutes les données d'entrée ou conditions d'erreur
inattendues.

Nous construirons le cadre d'agent de sorte que la communication interagent puisse être
soutenue aussi bien que la mobilité de nos agents à travers des réseaux. La flexibilité inclut
également la capacité d'ajouter facilement le soutien de nouvelles applications, de techniques
IA, Et d'autres dispositifs.

Caractéristiques fonctionnelles

Dans cette section, nous prenons les conditions et nos buts de conception, et les transformons
en liste de fonctions qui satisfont ces conditions et buts. Ceci définit ce que nous devons
construire. Les caractéristiques fonctionnelles sont un contrat entre l'équipe de développement
et la communauté d'utilisateur. Voici la fonctionnalité que nous pensons que nous avons
besoin :

1. Il doit être facile d'ajouter un agent intelligent à une application de Java existante.
2. Un outil graphique de construction doit être disponible pour composer des agents
hors d'autres composants de Java et d'autres agents.
3. Les agents doivent soutenir des possibilités de événement-traitement relativement
sophistiquées. Notre agent devra manipuler des événements du monde extérieur,
d'autres agents, et d'événements de signal aux applications extérieures.
4. Nous devons pouvoir ajouter la connaissance de domaine à notre agent using si puis
des règles, et soutenons le traitement basé sur les règles vers l'avant et en arrière avec
des sondes et des effecteur.
5. Les agents doivent pouvoir apprendre à faire la classification, le groupement, et la
prévision using des algorithmes d'étude.
6. Des applications de Multiagent doivent être soutenues using a KQML-comme le
protocole de message.
7. L'agent devrait être persistant. C'est-à-dire, une fois qu'un agent est construit, il doit
y a une manière de la sauver dans un dossier et de recharger son état à un temps
postérieur.

Architecture d'agent intelligent

Maintenant que nous avons spécifié les fonctions que notre architecture d'agent intelligent
doit fournir, nous devons prendre nos décisions de conception. Nous accepterons les points de
vue de fonction dans l'ordre et discuterons les diverses issues et différences que nous devons
faire.
LY_SII __ 162 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

1. Il doit être facile d'ajouter un agent intelligent à une application de Java existante.
La manière la plus facile pour que nous ajoutent un agent à une application existante
est de faire puis appeler l'application instancier et configurer l'agent et les méthodes de
l'agent comme routines de service. Que la manière l'application est toujours dans la
commande, et elle peut employer les fonctions intelligentes comme appropriées. C'est
facile, mais est à peine ce ce que nous considérerions un agent intelligent. C'est
intelligence incluse, mais il n'y a aucune autonomie. Une autre possibilité est de faire
puis le commencer l'application instancier et configurer l'agent et vers le haut dans un
fil séparé. Ceci donnerait à l'agent de l'autonomie, bien qu'il fonctionne dans l'espace
du procédé de l'application. L'application pourrait rapporter à l'agent si nécessaire, et
l'agent rapporterait quand il était traitement fait de sorte que l'application ait pu
continuer. Une troisième possibilité est d'avoir la course d'agent dans un fil séparé,
mais emploie des événements pour communiquer entre l'application et l'agent. Dans
Java, ceci peut être fait using l'observateur/cadre observable. L'agent serait un
observateur, et toutes les fois qu'on a annoncé l'agent qu'un événement s'est produit, il
serait exécuté. Un inconvénient de cette approche est que l'application devrait être une
sous-classe de chose observable.
2. Un outil graphique de construction doit être disponible pour composer des agents
hors d'autres composants de Java et d'autres agents. Il y a des instruments de
développement graphiques tels que Symantec VisualAge visuel de CafŽ et d'IBM
pour Java qui te permettent de construire des applications using « construction une
métaphore de pièces ». Cependant, le dégagement de JDK 1.1 de Java fournit des
possibilités de composant de base par son paquet de java.beans. JavaBeans est un
modèle composant de Java qui permet aux fonctions de logiciel d'être traitées comme
« partie » qui peuvent être remontées pour construire une application. Chaque
JavaBean a une interface bien définie qui permet à un outil visuel de constructeur de
manoeuvrer cet objet. Il a également une interface d'exécution définie qui permet des
applications consistées en JavaBeans pour courir. Un autre dispositif gentil des
haricots est qu'elles peuvent être nichées. Ce répond à notre exigence pour que la
capacité compose des agents hors d'autres agents. Ceci nous permet de développer les
agents pour un but particulier qui peuvent être réutilisés dans d'autres agents de plus
haut niveau. Par exemple, nous pouvons avoir des agents de bas niveau qui emploient
les réseaux neurologiques pour l'étude et les agents à niveau élevé qui emploient des
règles pour déterminer quelles actions à prendre. Cette fonction est rudement
équivalente au modèle de conception composée comme spécifique par Gamma et
autres (1995). Le BeanBox fait partie du kit de développement d'haricot et fournit à un
rudimentaire, mais efficace, environnement graphique de défaut pour travailler des
haricots. Nous pouvons employer le BeanBox en tant que notre environnement visuel
de construction.
3. Les agents doivent soutenir des possibilités de événement-traitement relativement
sophistiquées. Notre agent devra manipuler des événements du monde extérieur,
d'autres agents, et d'événements de signal aux applications extérieures. Le dégagement
de JDK 1.1 comporte un nouveau modèle de événement-traitement puissant appelé le
modèle d'événement de délégation. Ce nouveau cadre a été conduit réellement par les
conditions du modèle de composant de JavaBeans. Ce modèle est basé sur des sources
d'événement et des auditeurs d'événement. Il y a beaucoup de différentes classes des
événements avec différents niveaux de granularité. Nous emploierons le modèle
d'événement de JavaBeans dans notre cadre d'agent.
LY_SII __ 163 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

4. Nous devons pouvoir ajouter la connaissance de domaine à notre agent using si puis
des règles, et soutenons le traitement basé sur les règles vers l'avant et en arrière avec
des sondes et des effecteur. Les classes de RuleBase, de règle, et de RuleVariable
que nous avons développées en chapitre 4 peuvent être employées dans nos agents
pour fournir inferencing basé sur les règles vers l'avant et en arrière. Nous devrons
prolonger la fonctionnalité aux sondes et aux effecteur de soutien. La raison principale
de fournir cette fonctionnalité est de fournir une manière non programmeuse pour que
les utilisateurs spécifient des conditions et des actions. Si nous construisons un
rédacteur de propriété de JavaBeans pour notre classe de RuleBase, n'importe quel
utilisateur pourrait facilement construire un ensemble avec de RuleVariables et de
règles pour exécuter la logique derrière un comportement d'agent intelligent. Nous
fournissons cette fonctionnalité, mais ne l'exploitons pas. C'est-à-dire, la tâche de
développer un rédacteur de propriété pour l'usage dans un environnement de
développement visuel d'haricot est laissée comme exercice pour le lecteur.
5. Les agents doivent pouvoir apprendre à faire la classification, le groupement, et la
prévision using des algorithmes d'étude. En chapitre 5, nous avons conçu et avons
développé des classificateurs d'arbre de décision, et des algorithmes neuraux de
groupement et de prévision dans Java. Nous pouvons employer les classes de
DecisionTree, de BackPropNet, et de KMapNet pour fournir ces fonctions à nos
agents.
6. Des applications de Multiagent doivent être soutenues using a KQML-comme le
protocole de message. Afin de fournir cette fonctionnalité, nous devrons retourner au
drawingboard, et fournir un agent qui peut gérer des tâches comme un facilitant ou le
marieur de KQML. Nous voudrions employer nos possibilités existantes de règle pour
aider à fournir cette fonction, si possible. Nous pouvons employer le modèle
d'événement de JavaBean pour fournir le mécanisme de communication entre les
agents et le facilitant et pour définir nos propres objets d'événement pour tenir le
contenu de message.
7. L'agent devrait être persistant. C'est-à-dire, une fois qu'un agent est construit, il doit
y a une manière de la sauver dans un dossier et de recharger son état à un temps
postérieur. La fabrication en série de 1.1 soutien de JDK de Java objecte. En outre, le
cadre de JavaBeans fait l'économie et le chargement des objets très faciles à faire. En
fait, tous membres de données qui ne sont pas explicitement déclarés car la charge
statique ou la coupure sera sauvée dehors à un dossier. Tellement voici un autre
avantage d'employer le modèle composant de JavaBean en tant que notre base.

Le cadre de CIAgent

Tout en essayant de ne pas être trop mignons, nous avons choisi le CIAgent nommé pour
notre cadre d'agent intelligent, où CIAgent représente « construire les agents intelligents. »
Beaucoup d'autres noms se sont suggérés à nous, mais ceci semble comme un choix
raisonnable, donné le titre du livre. S'il vous branche vraiment sur table d'écoute, nous
espérons qu'il n'interfère pas votre arrangement et utilisation de notre conception.

Pour récapituler les décisions que nous avons prises dans la section précédente, nous vont
construire nos agents intelligents ainsi ils peuvent agir l'un sur l'autre avec le modèle de
composant de JavaBeans. Cette décision de conception, combinée avec la réutilisation du
code de la partie de ce livre, nous permet de rencontrer la plupart de nos caractéristiques
fonctionnelles. Nous devons faire quelques perfectionnements à notre traitement de règle,
LY_SII __ 164 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

aussi bien que développons un facilitant pour nos applications multiagent. Cependant, nous
allons bien sur notre chemin à fournir un cadre utilisable d'agent intelligent.

La prochaine étape est d'esquisser dehors notre structure et interfaces de classe. Nous
commençons par une classe basse abstraite qui définit l'interface commune employée par les
éléments de notre architecture.

La classe basse de CIAgent

CIAgent est le nom de la classe basse qui définit une interface et un comportement de
programmation communs pour tous les agents dans notre cadre. En termes de modèles de
conception, CIAgent emploie une conception composée. Ceci signifie que nous pouvons
employer un CIAgent simple ou les composer dans des groupes, et traite toujours le groupe
comme si c'était un objet logique simple de CIAgent. Ce modèle de conception est très
puissant parce qu'il nous permet d'accumuler une hiérarchie de CIAgents using d'autres
classes spécialisées de CIAgent dans le processus.

La classe de CIAgent met en application l'interface praticable, qui exige que nos sous-classes
de CIAgent fournissent une méthode de run () qui peut servir de corps d'un thread. C'est le
mécanisme que nous employons pour donner à nos agents l'autonomie. Pour communiquer
avec l'autre CIAgents et tout autre JavaBeans, nous mettons en application l'interface de
CIAgentEventListener. Cette interface prolonge l'interface standard de Java EventListener
employée par tous les composants et JavaBeans d'AWT. Bien que nous ne prolongions
aucune classe de JavaBeans, la classe de CIAgent est un JavaBean, en vertu de notre interface
d'EventListener et du public, méthode de CIAgent() de constructeur de défaut de zéro-
argument (). Un vecteur des auditeurs tient tous les objets de Java qui mettent en application
l'interface de CIAgentEventListener et qui se sont enregistrés suivre la méthode
d'addCIAgentEventListener (). N'importe quel objet de CIAgent peut être la source
d'événement pour CIAgentEvents, et n'importe quel objet de CIAgent peut être un auditeur
enregistré pour ces événements. La classe de CIAgent fournit l'addCIAgentEventListener () et
les méthodes de removeCIAgentEventListener () de sorte que l'autre CIAgents puisse être
ajouté à la liste d'avis d'événement de multicast. Ces méthodes approuvent pleinement
l'événement api de JavaBeans, ainsi CIAgents peut être câblé vers le haut using le BeanBox
ou n'importe quel outil visuel de constructeur qui soutient JavaBeans.

La méthode de notifyCIAgentEventListeners () est employée pour envoyer des événements


aux auditeurs enregistrés. Noter que, comme l'addCIAgentEventListener () et des méthodes de
removeCIAgentEventListener (), notifyCIAgentEventListener () doivent être synchronisées
pour commander l'accès au vecteur de l'auditeur dans un environnement multifil.

Chaque CIAgent a un membre ou une propriété de corde pour son name, et nous appliquons
les méthodes standard de JavaBean pour l'arrangement et obtenir le name par le BeanBox ou
tout autre outil visuel de constructeur. Nous employons la classe de JavaBean
PropertyChangeSupport pour faire au name une propriété de liage. Quand le name est
changé, on annoncera d'autres auditeurs.

Les autres méthodes que nous définissons incluent initialize () et remettent à reset () des
méthodes pour obtenir l'agent à un état KNOWN, et process () et des méthodes d'stop () pour
commencer le fil de agent-traitement ou l'arrêter. Noter que nous pourrions avoir employé la
LY_SII __ 165 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

méthode d'init() de Java comme défini dans la classe d'applet, mais nous avons choisi
d'éviter toutes les collisions nommées. En fait, nous pourrions facilement avoir prolongé notre
classe de CIAgent d'applet pour faire tous de nos applet de CIAgents. Le CIAgents fourni
dans ce livre a pu être facilement tourné dans des applet ou par subclassing un
awt.Composant ils pourraient être transformés en JavaBeans évident. Comme mis en
application ici, notre CIAgents sont les haricots invisibles, signifiant qu'ils peuvent être
employés dans l'environnement visuel de constructeur, mais ils ne représentent pas les
composants graphiques dans le GUI d'application.

L'exécution de Java de notre classe de CIAgent suit. Elle contient des membres et des
méthodes pour la gestion d'EventListeners et traitement d'événement, constructeurs d'objet,
soutien du traçage utilisé par nos applications d'agent dans les chapitres suivants, et l'appui de
fil d'agent.

public class CIAgent implements CIAgentEventListener, Runnable {

private Vector listeners = new Vector() ; // list of listeners

public synchronized void


addCIAgentEventListener(CIAgentEventListener ciaEventListener) {
listeners.addElement(ciaEventListener) ;
}

public synchronized void


removeCIAgentEventListener(CIAgentEventListener ciaEventListener)
{
listeners.removeElement(ciaEventListener) ;
}

protected void notifyCIAgentEventListeners() {


Vector l ;
CIAgentEvent e = new CIAgentEvent(this) ;

synchronized(this) { l = (Vector)listeners.clone(); }
for (int i=0 ; i < l.size() ; i++) { // deliver the event
((CIAgentEventListener)l.elementAt(i)).ciaEventFired(e);
}
}

// deliver the ciagent event to registered listeners


protected void notifyCIAgentEventListeners(CIAgentEvent e) {
Vector l ;
synchronized(this) { l = (Vector)listeners.clone(); }
for (int i=0 ; i < l.size() ; i++) { // deliver the event
((CIAgentEventListener)l.elementAt(i)).ciaEventFired(e);
}
}

public void ciaEventFired(CIAgentEvent e) {


System.out.println(“CIAgent: CIAgentEvent received by ” +
name + “ from ” + e.getSource() +
“ with args ” + e.getArgObject()) ;
}

String name ; // the agent’s name


public String getName() { return name ; }
LY_SII __ 166 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

public void setName(String Name) {


String oldName = name ;
name = Name ;
changes.firePropertyChange(“name”, oldName, name);
}

private PropertyChangeSupport changes =


new PropertyChangeSupport(this);

public CIAgent() { name = “CIAgent”; }


public CIAgent(String Name) { name = Name ;
}

// used for tracing and display of agent status


Object market ;
int traceLevel=0 ; // 0 = summary, 1 = detailed
TextArea textArea ;
public void setDisplay(Object mkt, int trace) {
market = mkt ; traceLevel = trace ;
}

// used for displaying message in our applications


public synchronized void trace(String msg) {
// comment this line out if using this outside Marketplace app
if (market != null) ((Marketplace)market).trace(msg) ;
if (textArea != null) textArea.appendText(msg) ;
}

public void process() {}; // start the agent processing thread

public void reset() {}; // reset the agent to a known state

public void initialize() {}; // initialize the agent

// required by Runnable interface and Threads


Thread runnit = new Thread(); // start()ed in the process() method
boolean stopped = false; // control flag
public void stop() {} ; // stop the agent thread
public void run() {} ; // body of thread

};

CIAgentEvent

Le CIAgentEvent est dérivé de la classe de Java EventObject, selon les exigences des
spécifications de JavaBeans. Les constructeurs de CIAgentEvent () prennent ou un paramètre
simple, une référence à l'objet envoyant l'événement, ou une paire de paramètres définissant la
source et un argument d'événement objecte. Nous définissons ceci comme objet de sorte que
les sous-classes de CIAgent puissent envoyer n'importe quel objet comme argument dans un
CIAgentEvent. Nous ne pouvons pas savoir à l'avance ce qui sera nécessaire dans une sous-
classe. En chapitre 10, nous emploierons cette flexibilité de définir a KQML-comme l'objet de
message.

public class CIAgentEvent extends java.util.EventObject {


Object argObject ;
LY_SII __ 167 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

public Object getArgObject() { return argObject; }

// for extremely simple events


CIAgentEvent(CIAgent source) {
super(source) ;
} ;

// for more complex events


CIAgentEvent(CIAgent source, Object arg) {
super(source) ;
argObject = arg ;
}
};

CIAgentEventListener

L'interface de CIAgentEventListener prolonge l'interface d'EventListener et exige qu'une


méthode simple soit appliquée par la classe de CIAgent, ciaEventFired (). Cette méthode
prend un objet de CIAgentEvent comme seul paramètre.

public interface CIAgentEventListener extends java.util.EventListener


{
void ciaEventFired(CIAgentEvent e) ;
};

Perfectionnements de RuleBase

Dans cette section, nous décrivons les perfectionnements à nos classes de règle. Nous
incluons le soutien pour des sondes et des effecteur dans les règles, et des faits en tant
qu'élément de la base de règle. Nous définissons deux interfaces de Java, sensor et effector,
pour soutenir cette fonction. Nous prolongeons également la classe de clause avec un
SensorClause et un EffectorClause. Notre appui est comme suit :

if sensor(sensorName, RuleVariable) then effector(effectorName, parameters)

là où la sonsor est un exemple de SensorClause, et l'effector est un exemple


d'EffectorClause. Le SensorClause fait un appel à une méthode de sonde qui est définie par
n'importe quelle classe qui met en application l'interface de Sensor et l'enregistre avec le
RuleBase. Le temps d'exécution le RuleBase regarde vers le haut le sensorName et appelle la
méthode sur l'objet enregistré de sonde. Une technique semblable est employée pour les
effecteur.

En prolongeant le comportement de la classe de Rule pour soutenir les classes de


SensorClause et d'EffectorClause, nous avons découvert une caisse où nous avons violé la
règle de l'encapsulation de la connaissance au sujet de l'exécution. Dans la méthode de
Rule.display (), la clause a été composée pour l'affichage. Ce code a inclus des références à un
lhs, à une condition, et à un rhs dans la clause. Cependant, notre SensorClause et
EffectorClause ne placent pas ces membres de données ainsi la méthode de Rule.display ()
échoués. La solution à ceci est de définir une méthode d'display () sur la classe de clause et
ses sous-classes et pour Rule.display () pour employer cette méthode. Ceci encapsule la
connaissance au sujet de la clause et comment il devrait être composé pour l'affichage.
LY_SII __ 168 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Pour soutenir des faits, nous ajoutons une nouvelle classe appelée Fact dont le constructeur
prend une clause simple comme paramètre. Un fait peut être une attribution d'une valeur à un
RuleVariable, à un appel de sonde, ou à un appel effecteur. Les faits sont définis en tant
qu'élément du RuleBase avec les autres règles. Mais les faits sont également enregistrés dans
le RuleBase. La méthode d'initializeFacts () s'appelle pour placer les faits avant qu'un cycle
inferencing soit exécuté.

public class Fact {


RuleBase rb ;
String name ;
Clause fact ; //only 1 clause allowed
Boolean truth; // states = (null=unknown, true, or false)
boolean fired=false;
Fact(RuleBase Rb, String Name, Clause f) {
rb = Rb ;
name = Name ;
fact = f ;
rb.addFact(this) ; // add self to fact list
truth = null ;
}

// assert the fact


public void assert(RuleBase rb) {
if (fired == true) return ; // only assert once
RuleBase.appendText(“\nAsserting fact ” + name ) ;
truth = new Boolean(true) ;
fired = true ;
if (fact.lhs == null) {
// it’s an effector
((EffectorClause)fact).perform(rb); // call effector method
} else {
// set the variable value and update clauses
fact.lhs.setValue(fact.rhs) ;
// now retest any rules whose clauses just changed
}
}

// display the fact in text format


void display(TextArea textArea) {
textArea.appendText(name +“: ”) ;
textArea.appendText(fact.display() + “\n”) ;
}

};

L'interface effector permet à n'importe quelle class de s'appeler comme effecteur par une
Rule dans un RuleBase. Elle doit mettre en application l'interface en fournissant () une
méthode effectrice simple qui prend un Object, un nom effecteur (pour des cas où la classe de
mise en oeuvre peut soutenir les effecteur multiples), et un paramètre d'argument de corde.
Une exécution alternative qui peut être utile serait de soutenir un objet comme paramètre
d'argument.

public abstract interface Effector {

public long effector(Object obj, String eName, String args) ;


};
LY_SII __ 169 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

La classe d'EffectorClause prolonge la clause en ajoutant des membres pour le nom de


l'effecteur à l'appel, d'un membre de données contenant une référence à l'objet qui applique ()
la méthode effectrice, et d'une String des arguments. Le constructeur d'EffectorClause ()
prend deux paramètres, le nom de l'effecteur à l'appel, et la corde d'arguments. La méthode
perform () est fournie pour appeler la méthode effector() sur l'objet qui s'est enregistré pour
fournir cette fonction effectrice.

public class EffectorClause extends Clause {


Effector object ; // object to call
String effectorName ; // method to call
String arguments ; // parameters to pass

EffectorClause(String eName, String args)


{
ruleRefs = new Vector() ;
truth = new Boolean(true); // always true
consequent = new Boolean(true); // must be consequent
effectorName = eName ;
arguments = args ;
}

public String display() {


return “effector(” + effectorName + “,” + arguments + “) ” ;
}

// call the effector method on the target object


Boolean perform(RuleBase rb) {
object = (Effector)(rb.getEffectorObject(effectorName)) ;
object.effector(this, effectorName, arguments) ;
return truth ; // always true

}
};

L'interface de Sonsor se compose d'une Sonsor() simple de méthode qui prend trois
paramètres : l'objet (SensorClause) qui a appelé la méthode, le nom de la sonde pour
employer, et un RuleVariable dans lequel pour stocker les résultats, le cas échéant. La
méthode de Sonsor () renvoie une valeur booléenne.

public abstract interface Sensor {


public Boolean sensor(Object obj, String sName, RuleVariable lhs) ;
};

La classe de SensorClause prolonge la clause en ajoutant un membre de données d'objet qui


met en application l'interface de sonde et une corde qui spécifie le sensorName. Un
SensorClause () prend deux paramètres : le nom de la sonde à examiner et d'un RuleVariable
pour tenir la valeur de vérité. La méthode de check() s'appelle par une Rule pour évaluer la
valeur de vérité du SensorClause.

public class SensorClause extends Clause {


Sensor object ;
String sensorName ;

SensorClause(String sName, RuleVariable Lhs)


{
lhs = Lhs ;
LY_SII __ 170 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

cond = new Condition(“=”) ;


rhs = “ ”;
lhs.addClauseRef(this) ;
ruleRefs = new Vector() ;
truth = null ;
consequent = new Boolean(false) ;
sensorName = sName ;
}

public String display() {


return “sensor(” + sensorName + “,” + rhs + “) ” ;
}

Boolean check() {
if (consequent.booleanValue() == true) return null ;

if (lhs.value == null) {
RuleBase rb = ((Rule)ruleRefs.firstElement()).rb ;
object = (Sensor)(rb.getSensorObject(sensorName)) ;
truth = object.sensor(this, sensorName, lhs) ;
}
return truth ;
}
};

Le fragment suivant de code donne un exemple de la façon dont des sondes et les effector
pourraient être employés dans les véhicules RuleBase :

Rule EffectorTest = new Rule(rb, “EffectorTest”,


new Clause(num_wheels, cEquals, “4”),
new EffectorClause(“display”, “It has 4 wheels!”)) ;

rb.addEffector( new TestEffector(), “display”) ; // test it

RuleVariable sensor_var = new RuleVariable(“sensor_var”) ;


sensor_var.setLabels(“2 3 4”) ;
sensor_var.setPromptText(“What does the sensor say?”) ;
rb.variableList.put(sensor_var.name,sensor_var) ;

Rule SensorTest = new Rule(rb, “SensorTest”,


new Clause(num_wheels, cEquals, “4”),
new SensorClause(“sensor_var”, sensor_var),
new EffectorClause(“display”, “It’s an automobile!!!!”)) ;

rb.addSensor( new TestSensor(), “sensor_var”) ; // test it

Fact f1 = new Fact(rb, “f1”,


new Clause(num_wheels, cEquals, “4”)) ;
Fact f2 = new Fact(rb, “f2”,
new SensorClause(“sensor_var”, sensor_var)) ;
Fact f3 = new Fact(rb, “f3”,
new EffectorClause(“display”, “Just the facts man !”)) ;

Nous employons les effecteur dans l'application de marché en chapitre 10.

Résumé
LY_SII __ 171 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

En ce chapitre nous avons décrit le cadre d'agent intelligent de CIAgent. Les questions
principales incluent :

• Nous avons décrit nos conditions et buts à niveau élevé de conception et les avons
traduits en ensemble de caractéristiques. Ceux-ci inclus :
1. Il doit être facile d'ajouter un agent intelligent à une application de Java existante.
2. Un outil graphique de construction doit être disponible pour construire des agents.
3. Les agents doivent soutenir des possibilités de événement-traitement relativement
sophistiquées.
4. Nous devons soutenir le traitement basé sur les règles vers l'avant et en arrière avec
des sondes et des effecteur.
5. Les agents doivent pouvoir apprendre à faire la classification, le groupement, et la
prévision.
6. Des applications de Multiagent doivent être soutenues using a KQML-comme le
protocole de message.
7. Les agents doivent être persistants, permettant pour les sauver dans un dossier et
pour reconstituer leur état plus tard.
• Nous avons décrit la classe basse de CIAgent, la classe de CIAgentEvent, et
l'interface de CIAgentEventListener pour fournir l'appui pour les agents intelligents
utilisés dans les applications dans les chapitres suivants.
• Nous avons prolongé le notre si puis fonction de traitement de règle en ajoutant les
classes de faits, de SensorClause, et d'EffectorClause, et les interfaces d'effecteur et de
sondes. Ceci permet à nos règles d'appeler des fonctions aux conditions d'essai et
d'effectuer des actions.

Exercices

1. Quelles sont certaines des décisions de conception qui limitent l'utilité ou


l'applicabilité de l'architecture de CIAgent ?
2. Comment pourriez-vous prolonger l'architecture de CIAgent pour mettre en
application un agent de filtrage de l'information ? un agent d'ordinateur personnel de
directeur ?
3. Dans l'architecture de CIAgent, nous avons choisi d'employer le modèle
d'événement de délégation de JavaBeans. Comment pourriez-vous fournir la fonction
équivalente using l'observateur de Java/cadre observable ? Quel est l'avantage et les
inconvénients de cette approche comparée à employer l'avis d'événement de
JavaBeans ?
4. Comparer votre conception de l'exercice 4.3 pour ajouter des sondes et des effecteur
à l'exécution fournie en ce chapitre. Avez-vous employé des interfaces de Java dans
votre solution ?
‫‪LY_SII‬‬ ‫__‬ ‫‪172‬‬ ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢْ‬
LY_SII __ 173 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Chapitre 8
Application du PCManager
En ce chapitre, nous illustrons comment nous pouvons employer l'architecture
de CIAgent pour construire une application qui emploie les agents intelligents
pour aider un utilisateur avec la gestion de PC et les activités locales
d'application. Deux agents intelligents autonomes simples sont développés, un
sont basés sur un temporisateur et des autres qui observent le système de fichiers
de PC. Quand un événement de déclenchement se produit, les agents peuvent se
signaler, alertent l'utilisateur, ou exécutent une commande de système.

Introduction

La première application d'agent intelligent que nous développons est un aide personnel conçu
pour nous aider en contrôlant notre PC. Notre but est de fournir la fonctionnalité de base tout
en illustrant les notions générales exigées pour ce domaine. Nous développons deux nouveaux
agents intelligents en prolongeant la classe basse de CIAgent présentée en chapitre 7. La
classe d'application de PCManager fournit une interface utilisateurs graphique using le cadre
de Java AWT, et fournit l'interface à TimerAgents et à FileAgents pour nous aider à contrôler
les ressources dans notre PC.

Le PCManager permet à un utilisateur de spécifier deux fonctions, alarmes et montres


importantes. Les alarmes emploient le TimerAgent pour gérer les aspects de time-keeping de
la fonction. Une alarme est un événement temps-basé qui peut se produire à un seul instant
spécifique ou à répéter des intervalles. Par exemple, nous pourrions placer une alarme pour
aller au loin à 6h00 du matin demain matin, ou nous pourrions spécifier une alarme pour aller
outre de toutes les 10 minutes. Les montres emploient les possibilités d'un FileAgent pour
détecter des changements à l'état d'un dossier ou d'un annuaire dans le système de fichiers de
PC. Une montre pourrait être réglée pour signaler quand un dossier est changé ou supprimé,
ou quand il se développe au-dessus d'une taille spécifique.

Le schéma 8.1 l'application de PCManager.

Le schéma 8.1 montre le panneau principal de l'application de CIAgent PCManager.


L'interface d'application se compose de deux parts. L'awt supérieur.La boîte de liste montre
les alarmes et les montres actuellement spécifiques. Le fond awt.TextArea est employé pour
montrer le statut et pour tracer des messages.

Il y a deux panneaux ou dialogues secondaires importants utilisés dans l'application de


PCManager, l'AlarmDialog et le WatchDialog, qui sont employés pour spécifier les
paramètres exigés pour des alarmes et des montres respectivement.

Le schéma 8.2 montre l'AlarmDialog. Chaque alarme a un nom, un type, alarme monocoup
ou intervalle, et une action associée. Une alarme monocoup doit avoir la période des alarmes
spécifiques en heures et minutes. L'alarme d'intervalle exige les spécifications du nombre de
secondes entre les alarmes. L'utilisateur peut choisir parmi une de trois actions quand les feux
d'alarme. Elle peut alerter l'utilisateur par l'intermédiaire d'un AlertDialog. Elle peut exécuter
LY_SII __ 174 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

une commande de système ou lancer un programme d'application au nom de l'utilisateur. Ou,


elle peut envoyer un CIAgentEvent à un autre CIAgent. Quand l'utilisateur clique dessus
CORRECT, un exemple d'un TimerAgent est créé et ses méthodes s'appellent pour placer
l'intervalle ou la date, déclenchent la condition, et l'action. Le nom d'alarme est employé
comme nom de l'exemple de TimerAgent.

Le schéma 8.3 montre le WatchDialog. Comme une alarme, une montre a un nom, un
paramètre pour spécifier le dossier ou l'annuaire à observer, la condition de déclenchement sur
le dossier, et l'action pour prendre quand le déclenchement se produit. Si l'utilisateur ne
connaît pas le chemin de nom de fichier ou d'annuaire, elle peut cliquer dessus le bouton
poussoir de lecture rapide et ouvrir un awt.FileDialog standard sur le système de fichiers de
PC. Les conditions de déclenchement incluent si le dossier est supprimé ou modifié, ou si sa
taille dépasse un seuil spécifique. Les montres soutiennent les mêmes trois actions que des
alarmes, alertes, exécutant des commandes, et signalant des événements. Quand l'utilisateur
clique l'OK sur le WatchDialog, un FileAgent est instancié et les paramètres d'utilisateur
sont passés à l'exemple. Le nom du FileAgent est placé au nom de montre.

Le schéma 8.2 le panneau d'AlarmDialog.

Deux dialogues additionnels liés aux actions d'alarme et de montre sont employés dans cette
application. L'AlertDialog affiche un message alerte à l'utilisateur. L'ExecuteDialog donne
les résultats d'une action d'exécution. Les deux dialogues d'action permettent à l'utilisateur de
reconnaître eux en cliquant l'OK, ou de décommander l'alarme ou la montre avec le bouton
d'annulation. Ces dialogues sont montrés dans l'exemple dans la prochaine section.

Le schéma 8.3 le panneau de WatchDialog.

Un exemple

Les fonctions de base ont fourni par Alarms et des montres peuvent être combinées pour
produire le comportement intéressant. Dans cette section, deux alarmes et une montre sont
employées pour illustrer ceci par exemple. Les alarmes seront employées pour copier un de
deux dossiers dans un dossier de cible. Ce dossier de cible sera le centre d'une montre. Quand
chacun des trois agents est en activité dans l'application de PCManager, le scénario est
comme suit :

1. Alarm1 copie le dossier test1.dat à test.dat.


2. Watch1 détecte que le dossier test.dat a été modifié et signale une alerte à
l'utilisateur.
3. Alarm2 copie le dossier test2.dat à test.dat.
4. La montre détecte ce changement au dossier, et signale une autre alerte à
l'utilisateur.
5. Cet ordre répète indéfiniment.

Les figures suivantes montrent les dialogues de paramètre et l'alerte et exécutent des
dialogues produits par cet exemple. Les schémas 8.4 et 8.5 montrent les paramètres employés
pour installer le TimerAgents pour Alarm1 et Alarm2 respectivement. Le schéma 8.6 montre
les arrangements Watch1.
LY_SII __ 175 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Le schéma 8.4 paramètres d'Alarm1 TimerAgent.

Le schéma 8.5 paramètres d'Alarm2 TimerAgent.

Quand les trois agents sont créés ils commencent immédiatement à fonctionner. Le schéma
8.7 montre au PCManager le panneau principal quand tous les agents sont configurés et
fonctionnement. Pendant qu'Alarm1 va au loin, le TimeAgent exécute la corde de paramètre
spécifique qui copie le dossier test1.dat au-dessus de test.dat (le schéma 8.8). L'agent Watch1
réveille toutes les 15 secondes pour vérifier le statut du dossier de test.dat. Quand il voit que
le dossier a été modifié (il emploie la méthode de Java File.getLastChanged ()) qu'elle signale
une alerte. Cet AlertDialog est montré sur le schéma 8.9.

Le schéma 8.6 paramètres de Watch1 FileAgent.

Le schéma 8.7 exemple d'application de PCManager.

Maintenant que vous avez vu un exemple du PCManager dans l'action, nous prenons un œil
plus attentif à l'exécution des deux agents, et le cours d'application de PCManager.

Alarmes : Le TimerAgent

Notre premier agent, le TimerAgent, n'est pas très intelligent mais est certainement autonome
et fournit une fonction extrêmement utile. Un TimerAgent peut être employé pour placer les
alarmes monocoup pendant n'importe quelle heure spécifique, ou il peut être employé pour
mettre le feu aux alarmes récurrentes à intervalles spécifiques. Quand des conditions d'alarme
se produisent, le TimerAgent peut prendre une de trois mesures. Il peut montrer une alerte,
où un dialogue saute vers le haut sur l'écran pour informer l'utilisateur qu'une alarme de
temporisateur est allée au loin. Il peut exécuter une commande de système arbitraire ou
appeler un programme d'application sur le système, passant des paramètres au besoin. Le
TimerAgent peut également simplement mettre le feu à un CIAgentEvent pour signaler un
autre CIAgent ou tout autre objet de Java.

Le schéma 8.8 Alarm1-generated ExecuteDialog.

Le schéma 8.9 Watch1-generated AlertDialog.

Le TimerAgent a plusieurs membres de données. L'intervalle tient une valeur, en


millisecondes, pour des alarmes d'intervalle. Le membre de date tient la date et l'heure pour
les alarmes monocoup. Le membre d'action définit que des trois actions l'agent devrait
prendre quand l'alarme va au loin. Le runnit de membre de fil et les booléens arrêtés sont
hérités de la classe basse de CIAgent. () La méthode de processus est employée pour tourner
un nouveau fil quand le TimerAgent est commencé, et la méthode d'arrêt () est employée
pour interrompre le fil de runnit quand elle dort (suivre la méthode de Thread.sleep () dans la
méthode de TmerAgent.run ()).

Nous fournissons un ensemble de méthodes d'accédant pour chacune de ces propriétés pour
l'usage dans le BeanBox ou tout autre environnement de JavaBean.

public class TimerAgent extends CIAgent {


LY_SII __ 176 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

public final int ALERT = 0; // display a dialog


public final int EXECUTE = 1; // start a process
public final int EVENT = 2; // send an event to another agent

int action = EVENT ; // default action is to send an event


String parms ;
int interval ; // for interval timers
Date time ; // for one shot timers
Dialog actionDialog ;

public TimerAgent() { name = “Timer”; }


public TimerAgent(String Name) { super(Name); } ;

// return a string for display in a list box


public String getDisplayString() {
String out = new String() ;
out = name + “ int=” + (interval/1000) +
“ date=” + time + “ action=” + action + “parms=” + parms ;
return out ;
}

public void setInterval(int secs) {


interval = secs * 1000 ;
}

public int getInterval() { return interval ; }


public void setTime(Date t) { time = t ; }

public Date getTime() { return time ; }


public void setAction(int act) { action = act; }

public int getAction() { return action ; }

public void setParms(String params) { parms = params; }


public String getParms() { return parms ; }

public void setDialog(Dialog dlg) { actionDialog= dlg ; }

public void process() {


stopped = false ;
// start a thread running
trace(“Starting ” + name + “\n”) ;
runnit.start() ;
}
public void stop() {
stopped = true;
trace(name + “ stopped \n”) ;
runnit.stop() ;
}

};

La méthode de run () contient le corps du fil de TimerAgent. Elle se compose de deux parties
alternatives, une pour le rythmeur et une pour le temporisateur monocoup. Si une valeur
d'intervalle est placée, elle écrit une boucle de while (), où elle va Sleep() pour le nombre
spécifique de millisecondes, effectue l'action désirée, et puis des répétitions jusqu'à ce que la
méthode Stop () s'appelle. Si le membre d'intervalle égale 0, alors une alarme monocoup est
LY_SII __ 177 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

placée. Un objet de date est employé pour obtenir la date du jour et pour la chronométrer qui
est alors employée pour calculer le nombre de heures jusqu'à ce que le temporisateur
monocoup aille au loin. Le TimerAgent va Sleep () une fois et se réveille au temps désigné,
effectue l'action désirée et sort le fil.

La méthode ciaEventFired() montre simplement tous les événements qu'elle reçoit de l'autre
CIAgents. Nous pourrions facilement augmenter cette méthode pour permettre à d'autres
agents de commencer et arrêter des temporisateurs employant CIAgentEvents au lieu des
appels de méthode. Cependant, cette fonction n'est pas exigée dans l'application de
PCManager.

La méthode de performAction () contient un rapport de commutateur pour manipuler les trois


actions possibles. Pour une ALERTE, le texte de dialogue est spécifié using la parms String ,
et est montré suivre la méthode d'show (). L'AlertDialog permet l'utilisateur à reconnaissent
l'alerte et ferment la fenêtre en appuyant sur le bouton OK, ou pour décommander l'alarme,
arrêtant de ce fait le fil de TimerAgent, en cliquant sur le bouton d'alarme ou de montre
d'annulation. Le cas d'EXÉCUTION appelle la méthode d'execCmd () qui commence vers le
haut un nouveau processus et passe la String parms au processeur de commandes de système
pour l'exécution. N'importe quelle commande de système ou nom d'application valide avec
des paramètres peut être spécifiée dans cette corde. Un ExecuteDialog est montré et le
rendement, le cas échéant, de la commande est montré dans un TextArea dans la zone de
dialogue. L'utilisateur a encore l'option de reconnaître l'alarme ou de décommander le
TimerAgent, si c'était une alarme d'intervalle. Le troisième cas, ÉVÉNEMENT, fait envoyer
le TimerAgent un CIAgentEvent à tous les auditeurs enregistrés.

// method of Runnable (Thread) interface


public void run() {
if (interval > 0) {
while(stopped == false){ // interval timer
try {
Thread.sleep((long)interval) ; // in milliseconds
trace(name + “: Interval sleep over ”) ;
}
catch (InterruptedException e)
{
// interrupted
}
performAction() ;
}
} else { // one-shot alarm
try {
Date date = new Date() ; // get current date/time
int curDay = date.getDay() ;
int curHour = date.getHours() ;
int alarmHour = time.getHours() ;
int deltaHour = ((alarmHour - curHour) % 24) * 60; // mins
int curMin = date.getMinutes() ;
int alarmMin = time.getMinutes() ;
long deltaMin = (alarmMin - curMin) % 60 ;
long delta = (deltaHour + deltaMin) * 60 * 1000 ;
trace(“going to sleep for ” + delta + “ msecs”) ;
Thread.sleep(delta) ; // in milliseconds
date = new Date() ; // get current date/time
trace(name + “:Alarm sleep over at ” + date) ;
notifyCIAgentEventListeners(new
LY_SII __ 178 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

CIAgentEvent(this, “alarm”)); // signal listeners


}
catch (InterruptedException e)
{
// interrupted
}
performAction() ;
}

}
public void ciaEventFired(CIAgentEvent e) {
trace(name + “: CIAgentEvent received by ” + name + “ from ” +
e.getSource() + “ with arg ” + e.getArgObject()) ;
}

// perform the action specified by the user


void performAction() {
switch (action) {
case 0:
trace(name + “: Alert fired \n”) ;
((AlertDialog)actionDialog).label1.setText(parms) ;
actionDialog.show() ;
if (((AlertDialog)actionDialog).cancel == true)stop();
break ;
case 1:
trace(name + “: Executing command \n”) ;
executeCmd(parms) ;
break ;
case 2:
notifyCIAgentEventListeners(
new CIAgentEvent(this,“interval”)); // signal
break ;
}
}

public int executeCmd(String cmd) {


Process process ;
String line ;
textArea = ((ExecuteDialog)actionDialog).textArea1 ;
actionDialog.show() ;
try {
// this prefix works for Win 95/NT only ???
process = Runtime.getRuntime().exec(“command.com /c ” +
cmd + “\n”);

DataInputStream data =
new DataInputStream(process.getInputStream());
while ((line = data.readLine()) != null)
{
trace(line);
}
data.close();

} catch (IOException err) {


trace(“Error: EXEC failed, ” + err.toString()) ;
err.printStackTrace();
return -1 ;
}
if (((ExecuteDialog)actionDialog).cancel == true) stop() ;
return process.exitValue() ;
LY_SII __ 179 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Noter que le TimerAgent fournit un ensemble flexible de possibilités. Une alarme peut être
placée pour commencer des supports de système à minuit ou une alarme d'intervalle peut être
placée pour informer l'utilisateur chaque heure l'heure. Des alarmes peuvent également être
employées pour donner un coup de pied au loin l'autre CIAgents pour faire leur chose.
Encore, il n'y a pas beaucoup d'intelligence ici, mais c'est logiciel utile néanmoins.

Montres : Le FileAgent

Le prochain CIAgent est appelé FileAgent parce que son but dans la vie est d'observer un
système de fichiers et de faire l'application savoir quand un certain événement spécifique se
produit. L'agent peut alerter l'utilisateur avec l'AlertDialog toutes les fois qu'un dossier est
modifié, ou si la taille d'un dossier devient trop grande (surveillant la taille d'un fichier
SWAP, par exemple). Elle peut également alerter une application ou un agent différent toutes
les fois que le dossier de cible est supprimé. Comme le TimerAgent, le FileAgent peut
effectuer une de trois actions quand il détecte l'état de montre. Il peut montrer une alerte,
exécuter une commande ou commencer une application, ou informer un autre CIAgent de la
condition.

Une grande partie de la logique du FileAgent est semblable au TimerAgent, ainsi nous
discuterons seulement les membres uniques de données et () la méthode de processus. Trois
états de montre sont soutenus : si le dossier EST MODIFIÉ, si le dossier EST SUPPRIMÉ (il
n'existe pas dans le système de fichiers), et si le dossier dépasse une taille de seuil en bytes.
Le nom du dossier ou de l'annuaire est stocké dans le membre de corde de nom de fichier, et
des informations sur quand elles ont été changées sont stockées dans lastChanged.

La méthode de run () va dormir toutes les 15 secondes et examine l'état de dossier spécifique
chaque fois que elle se réveille. Si la condition de montre est remplie, alors l'action spécifique
est effectuée comme elle était dans le TimerAgent.

public class FileAgent extends CIAgent {

// conditions to check for


public final int MODIFIED = 0 ;
public final int DELETED = 1 ;
public final int THRESHOLD = 2 ;

// actions to take when conditions are true


public final int ALERT = 0; // display a dialog
public final int EXECUTE = 1; // start a process
public final int EVENT = 2; // send an event to another agent

int action = EVENT ; // default action is to send an event

public FileAgent() { name = “Watch” ; };


public FileAgent (String Name) { super(Name) ;}

String fileName ;
File file ;
long lastChanged ;
int condition ;
int threshold ;
LY_SII __ 180 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Dialog actionDialog ;
String parms ;

// return a string for display in a list box


public String getDisplayString() {
String out = new String() ;
out = name + “ fileName=” + fileName + “cond=” + condition ;
return out ;
}

public void setFileName(String fName) { fileName = fName;


file = new File(fName) ;
lastChanged =
file.lastModified() ; }

public String getFileName() { return fileName; }


public boolean exists() { return file.exists() ; }
public boolean changed() { long changeTime = lastChanged ;
lastChanged = file.lastModified() ;
return !(lastChanged == changeTime) ;
}
public long length() { return file.length(); } // file size
public boolean isDirectory() { return file.isDirectory(); }
public long lastModified() { return file.lastModified(); }

public void setCondition(int cond) { condition = cond; }


public int getCondition() { return condition ; }

public void setThreshold(int thresh) { threshold = thresh; }


public int getThreshold() { return threshold; }

public void setAction(int act) { action = act ; }


public int getAction() { return action ; }

public void setParms(String params) { parms = params; }


public String getParms() { return parms; }

public void setDialog(Dialog dlg) { actionDialog= dlg ; }

public void stop() {


stopped = true;
trace(name + “ stopped \n”) ;
runnit.stop() ;
}

public void process() {


stopped = false ;
// start a thread running
System.out.println(“Starting Watch \n”) ;
runnit.start() ;
}

// start an interval timer and watch the specified file/dir


// for the condition every 15 seconds. If the condition occurs
// do the specified action.
public void run() {

int interval = 15 * 1000 ; // 15 seconds


while(stopped == false){ // interval timer
try {
LY_SII __ 181 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Thread.sleep((long)interval) ; // in milliseconds
trace(name + “: checking ” + fileName + “ \n”) ;
}
catch (InterruptedException e)
{
// interrupted
}
boolean cond = checkCondition() ;
if (cond == true) {
performAction() ;
}
}
return ;
}

boolean checkCondition() {
boolean truth = false ;
switch (condition) {
case MODIFIED:
truth = changed() ;
break ;
case DELETED: // was file deleted?
truth = !exists() ; // see if file exists
break ;
case THRESHOLD:
truth = threhold > length() ;
break ;
}
return truth ;
}
void performAction() {
switch (action) {
case 0:
trace(name + “: Alert fired \n”) ;
((AlertDialog)actionDialog).label1.setText(parms) ;
actionDialog.show() ;
if (((AlertDialog)actionDialog).cancel == true) stop() ;
break ;
case 1:
trace(name + “: Executing command \n”) ;
executeCmd(parms) ;
break ;
case 2:
notifyCIAgentEventListeners(
new CIAgentEvent(this,“interval”)); // signal
break ;
}
}

public int executeCmd(String cmd) {


Process process ;
String line ;
textArea = ((ExecuteDialog)actionDialog).textArea1 ;
actionDialog.show() ;
try {
// this prefix works for Win 95/NT only ???
process = Runtime.getRuntime().exec(“command.com /c ” +
cmd + “\n”);
DataInputStream data =
new DataInputStream(process.getInputStream());
LY_SII __ 182 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

while ((line = data.readLine()) != null)


{
trace(line);
}
data.close();

} catch (IOException err) {


trace(“Error: EXEC failed, ” + err.toString()) ;
err.printStackTrace();
return -1 ;
}
if (((ExecuteDialog)actionDialog).cancel == true) stop() ;
return process.exitValue() ;
}

public void ciaEventFired(EventObject e) {


System.out.println(“FileAgent: CIAgentEvent received by ” +
name + “ from ” + e) ;
}

Application de PCManager

La classe de PCManager contient () l'application principale qui constitue notre application


de PCManager. Nous avions l'habitude Symantec Café visuel pour produire du code de GUI
et avions l'habitude notre TimerAgent et FileAgent sous les couvertures pour fournir l'alarme
et pour observer des fonctions respectivement. La majeure partie du code est produite et n'est
pas particulièrement intéressante. Nous précisons plusieurs points d'intérêt. Le PCManager
contient deux tables de brouillage, une pour des alarmes et une pour des montres. Quand
l'utilisateur choisit la création une alarme ou crée une montre du MenuBar, un dialogue
secondaire est montré et l'utilisateur spécifie les paramètres s'est associé à l'alarme ou à la
montre. Les spécifications d'une alarme ou d'une montre ont comme conséquence la création
d'un nouvel agent pour fournir cette fonction. Ainsi nous pourrions avoir des dizaines d'agents
fonctionnant en même temps, chacune using ses propres fil et venir vivant pour vérifier l'état
spécifique ou pour informer l'utilisateur ou d'autres agents d'une certaine occurrence.

() La méthode principale montre simplement la vue d'application. Tous les événements


d'utilisateur viennent par les actions de MenuBar et sont traités par la méthode d'action () et
conduits à l'événement spécifique manipulant des méthodes.

public class PCManager extends Frame {


void Alarm_Action(Event event) {

//{{CONNECTION
// Create with title, show as modal...
AlarmDialog dlg = new AlarmDialog(this, “Create Alarm”, true);
//}}
dlg.show() ;
TimerAgent agent = dlg.getAgent() ;
if (agent != null) {
alarms.put(agent.name, agent) ;
list1.addItem(agent.getDisplayString()) ;
agent.textArea = textArea1 ;
agent.process() ;
LY_SII __ 183 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

}
}

void Watch_Action(Event event) {

//{{CONNECTION
// Create with title, show as modal...
WatchDialog dlg = new WatchDialog(this, “Create Watch”, true);
//}}
dlg.show() ;
FileAgent agent = dlg.getAgent() ;
if (agent != null) {
watches.put(agent.name, agent) ;
list1.addItem(agent.getDisplayString()) ;
agent.textArea = textArea1 ;
agent.process() ;
}
}

void Open_Action(Event event) {


//{{CONNECTION
// Action from Open... Show the OpenFileDialog
openFileDialog1.show();
//}}
}

void Cut_Action(Event event) {


if (event.target == list1) {
int index = list1.getSelectedIndex() ;
String item = list1.getSelectedItem() ;
list1.delItem(index) ;

StringTokenizer tok = new


StringTokenizer(item,“:”) ;
String itemName = tok.nextToken() ;
// need to actually remove it from the
// hashtable or vector here !!!!!
watches.remove(itemName) ;
alarms.remove(itemName) ;

}
}

void About_Action(Event event) {


//{{CONNECTION
// Action from About Create and show as modal
(new AboutPCManagerDialog(this, true)).show();
//}}
}

void Exit_Action(Event event) {


//{{CONNECTION
// Action from Exit Create and show as modal
(new QuitDialog(this, true)).show();
//}}
}

public PCManager() {

//{{INIT_CONTROLS
LY_SII __ 184 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

setLayout(null);
addNotify();
resize(insets().left + insets().right + 405,insets().top +
insets().bottom + 305);
openFileDialog1 = new java.awt.FileDialog(this,
“Open”,FileDialog.LOAD);
//$$ openFileDialog1.move(36,276);
list1 = new java.awt.List(0,false);
add(list1);
list1.reshape(insets().left + 12,insets().top + 24,372,70);
textArea1 = new java.awt.TextArea();
textArea1.reshape(insets().left + 12,insets().top + 144,374,135);
add(textArea1);
label1 = new java.awt.Label(“Activity Log”);
label1.reshape(insets().left + 12,insets().top + 120,196,20);
add(label1);
label2 = new java.awt.Label(“Watches/Alarms”);
label2.reshape(insets().left + 12,insets().top + 0,180,20);
add(label2);
setTitle(“CIAgent PCManager Application”);
//}}

//{{INIT_MENUS
mainMenuBar = new java.awt.MenuBar();

menu1 = new java.awt.Menu(“File”);


menu1.add(“New”);
menu1.add(“Open...”);
menu1.add(“Save”);
menu1.add(“Save As...”);
menu1.addSeparator();
menu1.add(“Exit”);
mainMenuBar.add(menu1);

menu2 = new java.awt.Menu(“Edit”);


menu2.add(“Cut”);
mainMenuBar.add(menu2);

menu4 = new java.awt.Menu(“Create”);


menu4.add(“Watch...”);
menu4.add(“Alarm...”);
mainMenuBar.add(menu4);

menu3 = new java.awt.Menu(“Help”);


mainMenuBar.setHelpMenu(menu3);
menu3.add(“About”);
mainMenuBar.add(menu3);
setMenuBar(mainMenuBar);
//$$ mainMenuBar.move(4,277);
//}}
}

public PCManager(String title) {


this();
setTitle(title);
}

public synchronized void show() {


move(50, 50);
super.show();
LY_SII __ 185 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

public boolean handleEvent(Event event) {


if (event.id == Event.WINDOW_DESTROY) {
hide(); // hide the Frame
dispose(); // free the system resources
System.exit(0); // close the application
return true;
}
return super.handleEvent(event);
}

public boolean action(Event event, Object arg) {


if (event.target instanceof MenuItem) {
String label = (String) arg;
if (label.equalsIgnoreCase(“Alarm...”)) {
Alarm_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Watch...”)) {
Watch_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Open...”)) {
Open_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“About”)) {
About_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Exit”)) {
Exit_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Cut”)) {
Cut_Action(event);
return true;
}
}
return super.action(event, arg);
}

static public void main(String args[]) {


(new PCManager()).show();
}

//{{DECLARE_CONTROLS
java.awt.FileDialog openFileDialog1;
java.awt.List list1;
java.awt.TextArea textArea1;
java.awt.Label label1;
java.awt.Label label2;
//}}

//{{DECLARE_MENUS
java.awt.MenuBar mainMenuBar;
java.awt.Menu menu1;
java.awt.Menu menu2;
java.awt.Menu menu4;
LY_SII __ 186 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

java.awt.Menu menu3;
//}}

Hashtable watches = new Hashtable() ;


Hashtable alarms = new Hashtable() ;
}

La classe d'AlarmDialog

La classe d'AlarmDialog est créée et appelée par la méthode de PCManager.Alarm_Action ()


en réponse au choix de l'utilisateur de la création une option de menu d'alarme. La majeure
partie de ce code a été produite par Symantec Visual Café. Le seul membre de données est le
TimerAgent qui est instancié quand le bouton CORRECT est cliqué (dans la méthode de
button1_Clicked ()). Les données d'utilisateur sont extraites à partir du TextField et des
commandes et les méthodes bien choisies de TimerAgent s'appellent pour placer les valeurs
sur l'agent elle-même. Quand cette information est transmise à l'exemple de TimerAgent
l'AlarmDialog est fermé suivre la méthode de peau (). Le PCManager recherche alors le
TimerAgent de création récente et configuré du dialogue en appelant la méthode
d'AlarmDialog.getAgent ().

public class AlarmDialog extends Dialog {

// Cancel
void button2_Clicked(Event event) {

//{{CONNECTION
// Hide the Dialog
hide();
//}}
}

// OK
void button1_Clicked(Event event) {

String name = textField4.getText() ;


agent = new TimerAgent(name) ;
if (radioButton1.getState() == true) {
// one time
String time = textField2.getText() ;
StringTokenizer tok = new StringTokenizer(time,“:”) ;
int hour = new Integer(tok.nextToken()).intValue() ;
int min = new Integer(tok.nextToken()).intValue() ;
Date date = new Date() ;
date.setHours(hour) ;
date.setMinutes(min) ;
agent.setTime(date) ;
} else {
// interval
String interval = textField3.getText() ;
agent.setInterval(new Integer(interval).intValue()) ;
}
int action = choice2.getSelectedIndex() ;
agent.setAction(action) ;
if (action == 0) agent.setDialog(new
AlertDialog((Frame)this.getPar-
ent(),name + “: Alert”, false)) ;
LY_SII __ 187 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

if (action == 1) agent.setDialog(new
ExecuteDialog((Frame)this.getPar-
ent(),name + “Execute”, false)) ;

String parms = textField1.getText() ;


agent.setParms(parms) ;

//{{CONNECTION
// Hide the Dialog
hide();
//}}
}

public AlarmDialog(Frame parent, boolean modal) {

super(parent, modal);

//{{INIT_CONTROLS
setLayout(null);
addNotify();
resize(insets().left + insets().right + 433,insets().top +
insets().bottom + 351);
setBackground(new Color(12632256));
label3 = new java.awt.Label(“Action”);
label3.reshape(insets().left + 48,insets().top + 228,84,36);
add(label3);
label4 = new java.awt.Label(“Parameters”);
label4.reshape(insets().left + 36,insets().top + 264,108,24);
add(label4);
textField1 = new java.awt.TextField();
textField1.reshape(insets().left + 156,insets().top +
264,187,29);
add(textField1);
label5 = new java.awt.Label(“Interval”);
label5.reshape(insets().left + 84,insets().top + 168,96,24);
add(label5);
choice2 = new java.awt.Choice();
choice2.addItem(“Alert”);
choice2.addItem(“Execute”);
choice2.addItem(“Fire CIAgent Event”);
add(choice2);
choice2.reshape(insets().left + 156,insets().top + 228,134,24);
label2 = new java.awt.Label(“Time”);
label2.reshape(insets().left + 84,insets().top + 108,89,24);
label2.setBackground(new Color(16777215));
add(label2);
Group1 = new CheckboxGroup();
radioButton1 = new java.awt.Checkbox(“One Time”, Group1, false);
radioButton1.reshape(insets().left + 24,insets().top +
48,147,17);
add(radioButton1);
radioButton2 = new java.awt.Checkbox(“Repeating”, Group1, false);
radioButton2.reshape(insets().left + 24,insets().top +
144,126,20);
add(radioButton2);
textField2 = new java.awt.TextField();
textField2.reshape(insets().left + 216,insets().top +
108,127,26);
add(textField2);
textField3 = new java.awt.TextField();
LY_SII __ 188 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

textField3.reshape(insets().left + 216,insets().top +
168,124,27);
add(textField3);
label6 = new java.awt.Label(“seconds”);
label6.reshape(insets().left + 348,insets().top + 168,84,26);
label6.setBackground(new Color(16777215));
add(label6);
button2 = new java.awt.Button(“Cancel”);
button2.reshape(insets().left + 252,insets().top + 312,84,24);
add(button2);
label7 = new java.awt.Label(“Name”);
label7.reshape(insets().left + 24,insets().top + 12,152,28);
add(label7);
textField4 = new java.awt.TextField();
textField4.reshape(insets().left + 180,insets().top + 12,134,26);
add(textField4);
button1 = new java.awt.Button(“OK”);
button1.reshape(insets().left + 108,insets().top + 312,84,24);
add(button1);
label8 = new java.awt.Label(“hh:mm”);
label8.reshape(insets().left + 348,insets().top + 108,78,24);
add(label8);
setTitle(“”);
setResizable(false);
//}}

textField4.setText(“Alarm”) ;
}

public AlarmDialog(Frame parent, String title, boolean modal) {


this(parent, modal);
setTitle(title);
}

public synchronized void show() {


Rectangle bounds = getParent().bounds();
Rectangle abounds = bounds();
move(bounds.x + (bounds.width - abounds.width)/ 2,
bounds.y + (bounds.height - abounds.height)/2);
super.show();
}

public boolean handleEvent(Event event) {


if(event.id == Event.WINDOW_DESTROY) {
hide();
return true;
}
if (event.target == button1 && event.id == Event.ACTION_EVENT) {
button1_Clicked(event);
return true;
}
if (event.target == button2 && event.id == Event.ACTION_EVENT) {
button2_Clicked(event);
return true;
}
return super.handleEvent(event);
}

//{{DECLARE_CONTROLS
java.awt.Label label1;
LY_SII __ 189 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

java.awt.Choice choice1;
java.awt.Label label3;
java.awt.Label label4;
java.awt.TextField textField1;
java.awt.Label label5;
java.awt.Choice choice2;
java.awt.Label label2;
java.awt.Checkbox radioButton1;
CheckboxGroup Group1;
java.awt.Checkbox radioButton2;
java.awt.TextField textField2;
java.awt.TextField textField3;
java.awt.Label label6;
java.awt.Button button2;
java.awt.Label label7;
java.awt.TextField textField4;
java.awt.Button button1;
java.awt.Label label8;
//}}

protected TimerAgent agent ;

TimerAgent getAgent() { return agent; }


}

La classe de WatchDialog

La classe de WatchDialog est créée et appelée par la méthode de PCManager.Watch_Action


() en réponse au choix de l'utilisateur de la création une option de menu de montre. Comme
la classe d'AlarmDialog, la majeure partie de ce code a été également produite par Symantec
Visual Café. Le seul membre de données est le FileAgent qui est instancié quand le bouton
CORRECT est cliqué (dans la méthode de button2_Clicked ()). Les données d'utilisateur sont
extraites à partir du TextField et des commandes et les méthodes bien choisies de FileAgent
s'appellent pour placer les valeurs sur l'agent elle-même. Quand cette information est
transmise à l'exemple de FileAgent, le WatchDialog est fermé suivre la méthode de peau ().
Le PCManager recherche alors le FileAgent de création récente et configuré du dialogue en
appelant () la méthode getAgent.

public class WatchDialog extends Dialog {


// Browse -- open File dialog
void button1_Clicked(Event event) {

//{{CONNECTION
// Create with title, show as modal...
FileDialog dlg = new FileDialog((Frame)this.getParent(), “Select
File
or Directory”, FileDialog.LOAD);
//}}
dlg.show() ;
String dir = dlg.getDirectory() ;
String fName = dlg.getFile() ;
textField1.setText(dir+fName) ;
}

// cancel
void button3_Clicked(Event event) {
LY_SII __ 190 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

//{{CONNECTION
// Hide the Dialog
hide();
//}}
}

// OK
void button2_Clicked(Event event) {

String name = textField4.getText() ;


agent = new FileAgent(name) ;

String fileOrDirName = textField1.getText() ;


agent.setFileName(fileOrDirName) ;

int cond = choice2.getSelectedIndex() ;


agent.setCondition(cond) ;
String threshold = textField2.getText() ;
if (threshold.length() == 0) threshold = “0” ;
agent.setThreshold(new Integer(threshold).intValue());

int action = choice1.getSelectedIndex() ;


agent.setAction(action) ;
if (action == 0) agent.setDialog(new
AlertDialog((Frame)this.getPar-
ent(),name + “: Alert”, false)) ;
if (action == 1) agent.setDialog(new
ExecuteDialog((Frame)this.getPar-
ent(),name + “: Execute”, false)) ;
String parms = textField3.getText() ;
agent.setParms(parms) ;
//{{CONNECTION
// Hide the Dialog
hide();
//}}
}

public WatchDialog(Frame parent, boolean modal) {

super(parent, modal);

//{{INIT_CONTROLS
setLayout(null);
addNotify();
resize(insets().left + insets().right + 430,insets().top +
insets().bottom + 309);
setBackground(new Color(12632256));
label1 = new java.awt.Label(“File or Directory”);
label1.reshape(insets().left + 24,insets().top + 48,135,24);
add(label1);
label2 = new java.awt.Label(“Action”);
label2.reshape(insets().left + 24,insets().top + 180,144,33);
add(label2);
choice1 = new java.awt.Choice();
choice1.addItem(“Alert”);
choice1.addItem(“Execute”);
choice1.addItem(“Fire CIAgent Event”);
add(choice1);
choice1.reshape(insets().left + 36,insets().top + 216,146,28);
textField1 = new java.awt.TextField();
LY_SII __ 191 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

textField1.reshape(insets().left + 24,insets().top + 72,204,24);


add(textField1);
label3 = new java.awt.Label(“Condition”);
label3.reshape(insets().left + 264,insets().top + 48,139,25);
add(label3);
choice2 = new java.awt.Choice();
choice2.addItem(“Modified”);
choice2.addItem(“Deleted”);
choice2.addItem(“Over threshold”);
add(choice2);
choice2.reshape(insets().left + 264,insets().top + 72,138,24);
label4 = new java.awt.Label(“Threshold”);
label4.reshape(insets().left + 264,insets().top + 108,116,24);
add(label4);
textField2 = new java.awt.TextField();
textField2.reshape(insets().left + 264,insets().top +
132,129,24);
add(textField2);
button1 = new java.awt.Button(“Browse...”);
button1.reshape(insets().left + 24,insets().top + 120,117,29);
add(button1);
label5 = new java.awt.Label(“Parameters”);
label5.reshape(insets().left + 252,insets().top + 192,124,19);
add(label5);
textField3 = new java.awt.TextField();
textField3.reshape(insets().left + 264,insets().top +
216,144,24);
add(textField3);
button2 = new java.awt.Button(“OK”);
button2.reshape(insets().left + 108,insets().top + 264,72,26);
add(button2);
button3 = new java.awt.Button(“Cancel”);
button3.reshape(insets().left + 252,insets().top + 264,72,24);
add(button3);
label6 = new java.awt.Label(“Name”);
label6.reshape(insets().left + 24,insets().top + 12,142,25);
add(label6);
textField4 = new java.awt.TextField();
textField4.reshape(insets().left + 180,insets().top + 12,136,24);
add(textField4);
setTitle(“”);
setResizable(false);
//}}
textField4.setText(“Watch”) ; // default name
}

public WatchDialog(Frame parent, String title, boolean modal) {


this(parent, modal);
setTitle(title);
}

public synchronized void show() {


Rectangle bounds = getParent().bounds();
Rectangle abounds = bounds();

move(bounds.x + (bounds.width - abounds.width)/ 2,


bounds.y + (bounds.height - abounds.height)/2);

super.show();
}
LY_SII __ 192 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

public boolean handleEvent(Event event) {


if(event.id == Event.WINDOW_DESTROY) {
hide();
return true;
}
if (event.target == button1 && event.id == Event.ACTION_EVENT) {
button1_Clicked(event);
return true;
}
if (event.target == button2 && event.id == Event.ACTION_EVENT) {
button2_Clicked(event);
return true;
}
if (event.target == button3 && event.id == Event.ACTION_EVENT) {
button3_Clicked(event);
return true;
}
return super.handleEvent(event);
}

//{{DECLARE_CONTROLS
java.awt.Label label1;
java.awt.Label label2;
java.awt.Choice choice1;
java.awt.TextField textField1;
java.awt.Label label3;
java.awt.Choice choice2;
java.awt.Label label4;
java.awt.TextField textField2;
java.awt.Button button1;
java.awt.Label label5;
java.awt.TextField textField3;
java.awt.Button button2;
java.awt.Button button3;
java.awt.Label label6;
java.awt.TextField textField4;
//}}

protected FileAgent agent ;

FileAgent getAgent() { return agent ; }


}

Discussion

Il y a d'autres approches que nous pourrions avoir employées pour fournir la fonction fournie
par l'application de PCManager. En ce chapitre, nous avons consciemment décidé d'adopter
l'approche la plus franche dans l'intérêt de la clarté de la présentation. Dans cette section nous
discutons certaines des solutions de rechange possibles.

Une méthode populaire pour créer ces types d'applications simples d'agent est d'employer si
puis des règles comme moteur. De cette perspective, le TimerAgent serait employé
seulement comme agent événement-produisant, et le FileAgent serait employé comme sondes
dans l'antécédent d'une règle. Toutes les fois que le TimerAgent a envoyé un CIAgentEvent
à l'application de PCManager, il appellerait un cycle d'inférence de chaînage avant, avec des
règles spécifiant les divers conditions d'alarme ou de montre. Les trois actions devraient être
LY_SII __ 193 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

définies en tant que méthodes effectrices dans le RuleBase. Quand les feux d'une règle, la
méthode effectrice s'appelleraient et l'AlertDialog, l'ExecuteDialog, ou un CIAgentEvent
serait exécuté.

Cette conception aurait certainement effectué notre interface utilisateurs. Au lieu de compléter
met en place dans l'AlarmDialog ou les panneaux de WatchDialog, l'utilisateur auraient
complété probablement si puis calibre de règle. Le PCManager serait alors un grand
RuleBase qui reçoit CIAgentEvents et effectue inferencing (et actions) en réponse à ces
événements. Nous nous servons de la fonction semblable dans l'application de marché
discutée en chapitre 10.

Une autre alternative serait de déplacer l'AlertDialog et l'ExecuteDialog communs


transformant hors du TimerAgent et du FileAgent en quelques autres classes de Java
séparées. Peut-être nous pourrions avoir développé un AlertAgent dont le rôle dans la vie
était d'attendre un CIAgentEvent et puis d'afficher un message à un utilisateur. Cet
AlertAgent a pu même signaler le message d'annulation de nouveau à l'agent de demandeur.
Un CommandAgent ou un ExecuteAgent a pu être commencé de même et alors attendre un
CIAgentEvent demandant une chaîne d'ordres pour être couru. Quand elle a accompli, elle
pourrait envoyer le rendement de nouveau à l'agent de demandeur.

En concevant des applications d'agent intelligent, ou des applications orientées objectivement


généralement elle n'est pas toujours définie comment la fonction devrait être divisée. Dans
beaucoup de cas, elle descend à un appel de jugement quant à combien de fois la fonction
particulière sera réutilisée par d'autres classes. Si elle a l'applicabilité large, alors la séparation
de elle est dehors probablement la chose à faire.

Résumé

En ce chapitre que nous avons développé une application simple de PCManager using deux
CIAgent-a basé les agents intelligents. Les questions principales incluent :

• L'application de PCManager permet à un utilisateur de remplir deux fonctions


importantes : régler les alarmes pour s'attaquer au loin aux heures ou aux intervalles
spécifiques et les montres d'ensemble sur des dossiers ou des annuaires.
• Chaque alarme représente un exemple d'un TimerAgent autonome. Le TimerAgent
tourne son propre fil et puis va dormir une fois pour une alarme monocoup, ou à
plusieurs reprises pour une alarme d'intervalle.
• Chaque montre représente un exemple d'un FileAgent autonome. Le FileAgent
tourne son propre fil et puis va dormir pour les 15 seconde intervalles. Quand il se
réveille, il vérifie l'état du dossier ou de l'annuaire de cible.
• Cette application représente un exemple de l'utilité d'autonome, mais pas
nécessairement d'intelligent, agents.

Exercices

1. Que d'autres fonctions ont-elles pu le TimerAgent exécuter pour une application ?


Mettre en application un.
2. Comment prolongeriez-vous le FileAgent pour manipuler des conditions plus
complexes ou des montres sur les dossiers multiples ?
LY_SII __ 194 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

3. Esquisser dehors une conception pour l'application de PCManager using l'approche


de RuleBase décrite dans la section de discussion. L'application serait-elle plus grande
ou plus petite ? Les agents seraient-ils plus grands ou plus petits ?
4. Les alarmes de PCManager partent quand l'application finit. Comment pourriez-
vous rendre le TimerAgents et les alarmes persistants à travers des invocations de
l'application ?
LY_SII __ 195 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Chapitre 9
Application du NewsFilter
L'Internet est le centre de l'application d'agent intelligent en ce chapitre. Une
application de base de lecteur Usenet d'Internet est conçue, mise en application,
et augmentée avec un agent intelligent qui aide un utilisateur en filtrant
l'information. L'utilisateur peut spécifier un profil des mots-clés et fournir la
rétroaction positive et négative sur chaque article de nouvelles. Trois autres
méthodes sont données pour filtrer les articles, y compris l'allumette de mot-clé,
le groupement, et la modélisation prédictive basée sur la rétroaction d'utilisateur.

Introduction

L'application de CIAgent NewsFilter s'est développée en ce chapitre fournit la fonctionnalité


de base d'un lecteur des informations. Le NewsFilter peut se relier à un serveur de nouvelles
spécifique, demander les articles d'un groupe spécifique de nouvelles (tel que
comp.lang.java.api), et les télécharger au PC. La ligne objet de chaque article de nouvelles est
montrée dans un awt.Énumérer la commande, et l'utilisateur peut passer en revue différents
articles en les choisissant à partir de la liste. Jusqu'ici, nous avons décrit un lecteur Usenet
standard.

Le but de cette application est d'aider l'utilisateur à traiter tout les bruit électronique produit
dans les groupes de nouvelles. Ne serait-il pas grand si quand vous avez téléchargé un groupe
de nouvelles, tous que vous avez vus étaient les articles qui vous ont véritablement intéressé ?
Tous les poteaux de cette secousse sur la côte ouest disparaîtraient. Toutes les signalisations
de Spam offrant le grand service de téléphone mobile ou le plus grand logiciel puisque le pain
coupé en tranches perdrait jamais encore votre temps. D'une part, vous ne manqueriez jamais
un article ou un poteau de nouvelles qui ont discuté les matières qui vous intéressent. C'est la
motivation derrière le CIAgent NewsFilter. Dans le reste de ce chapitre nous nous montrons
comment appliquer les agents intelligents pour marquer et filtrer dehors les articles de
nouvelles non désirés. Nous explorerons également les mécanismes de base pour des groupes
de nouvelles d'Internet de lecture using le protocole de transport de nouvelles nettes.

Figure9.1 application de CIAgent NewsFilter.

Le panneau principal de l'application de NewsFilter, représenté sur Figure9.1, se compose de


trois commandes principales de GUI. Le gauche supérieur contient une liste de groupes de
nouvelles. La droite supérieure contient une liste de lignes objet d'article du groupe choisi de
nouvelles. Le fond TextArea est employé pour montrer l'article de nouvelles choisi et pour
montrer l'information de trace pendant que les articles sont marqués et les divers types de
filtres sont créés.

Sous le menu déroulant de dossier décrit sur Figure9.2, l'utilisateur peut s'ouvrir et les
raccordements étroits avec le centre serveur choisi de nouvelles (un serveur courant le logiciel
de serveur de NNTP) et également ajouter le nom d'un groupe de nouvelles à la liste de
groupes de nouvelles. Pour charger réellement le groupe de nouvelles, l'utilisateur doit
double-click sur l'article dans l'awt de newsgroup.Liste. Différents articles peuvent être
LY_SII __ 196 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

sauvés et chargés de l'application de NewsFilter. Cette fonction est fournie principalement


en tant qu'utile à la mise au point, de sorte que des articles choisis puissent être marqués et
ajoutés au fichier de données de profil d'utilisateur. Tous les deux sauf des options d'article et
d'article de charge apportent vers le haut un FileDialog ainsi l'utilisateur peut spécifier le
chemin et le dossier d'annuaire pour écrire ou lire.

Le menu de profil représenté sur Figure9.3 contient une liste d'actions liées à l'entretien du
profil de NewsFilter de l'utilisateur. L'option de mots-clés… montre un dialogue secondaire
qui permet à l'utilisateur d'écrire une liste de mots ou les limites qui est d'intérêt. Le dialogue
de mot-clé permet à l'utilisateur d'ajouter ou enlever des mots-clés de la liste. La spécification
des mots-clés est la première chose qu'un utilisateur devrait faire en commençant à établir un
profil d'utilisateur adapté aux besoins du client. Un ensemble de défaut de limites est fourni
dans l'application de NewsFilter. Les dix mots-clés de défaut sont : {« Java, » « agents, »
« brouillé, » « intelligent, » « neural, » « réseau, » « génétique, » « Symantec, » « café, »
« haricots »}.

Figure9.2 menu fichier de NewsFilter.

Figure9.3 menu de profil de NewsFilter.

Chaque article est recherché et le nombre de fois où chaque mot-clé apparaît est compté et
additionné. Ce nombre total de coups de mot-clé est employé comme points crus d'allumette
pour le filtrage de mot-clé.

L'option de profil de création détruira le profil courant et créera un neuf, using les mots-clés
décrits dans le dialogue de mot-clé. Le profil de filtre se compose de deux dossiers des textes.
Le premier, newsfilter.prf, contient la définition d'ensemble de données (comme décrit dans
chapitre 5), qui est une liste de types de données, continus ou discrets, et les noms de champ
correspondants. Ce dossier décrit la disposition des données dans le dossier de newsfilter.dat.
L'utilisateur peut ajouter des disques au dossier de newsfilter.dat en choisissant l'article
d'ajouter ou ajouter tout l'article de menu d'articles. Ajouter l'article apposera un disque
simple contenant les comptes d'allumette, la valeur courante de rétroaction, et les points
courants de filtre à newsfilter.dat. Ajouter tous les articles apposera les disques de profil
pour tous les articles dans le groupe actuellement chargé de nouvelles au dossier de
newsfilter.dat. Ce dossier est employé comme ensemble de données d'entrée pour construire le
filtre neural de faisceau et le filtre neural de rétroaction.

L'article de points et marquent tous les articles appliquera le filtre actuellement choisi
(mot-clé, faisceau, ou rétroaction) à l'article spécifique ou articles et mettra à jour leurs
points d'allumette. Pour le filtre de mot-clé, les points sont simplement la somme de toutes les
allumettes de mot-clé comme décrit plus tôt. Pour le filtre de faisceau, ces points sont la
moyenne des points de mot-clé pour tous les articles qui tombent dans ce faisceau (voir le
chapitre 5 pour l'examen complet du groupement neural). Les points pour le filtre de
rétroaction sont l'activation arrière d'unité de rendement de réseau neurologique de
propagation, une valeur s'étendant de 0.0 à 1.0. Nous discuterons ceci en plus détail dans la
prochaine section.

Le menu de filtre contient les options liées à si le filtrage est appliqué aux articles dans les
groupes de nouvelles, que le type de filtre est employé, et bâtiment que les modèles ont exigé
LY_SII __ 197 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

pour les diverses méthodes de filtre. Filtrer les articles, suivant les indications du schéma
9.4, est un article de menu de checkbox qui se transfère sur au loin. Quand vérifiés, les articles
seront filtrés using la technique indiquée dans le prochain groupement de menu. Le défaut
emploie des mots-clés, mais l'utilisateur peut choisir using des faisceaux ou rétroaction
d'utilisation comme méthode de filtre à appliquer.

Le prochain ensemble d'options de menu est employé pour établir les modèles internes exigés.
Construire le filtre de mot-clé force simplement un compte de toutes les allumettes de mot-
clé dans les articles chargés. Le filtre de faisceau de construction instancie un réseau
neurologique de carte de Kohonen et indique les données dans le dossier de newsfilter.dat
pour former le réseau. Construire le filtre de rétroaction crée et forme un réseau
neurologique de propagation arrière qui emploie les données de rétroaction dans le dossier de
newsfilter.dat comme cible d'un modèle de prévision.

Le menu de rétroaction montré sur Figure9.5 permet à l'utilisateur d'assigner une valeur
correspondant à l'utilité de l'article. Chaque article a une valeur de rétroaction qui est
automatiquement placée quand le groupe de nouvelles est lu. Cette valeur s'étend de 0 pour
des articles où il n'y a aucune allumette à un 1 pour les articles qui ont plus de 5 allumettes.
Cette tâche automatique est fournie comme convenance à l'utilisateur de sorte qu'une valeur
ne doive pas être placée pour chaque article.

Figure9.4 menu de filtre de NewsFilter.

Figure9.5 menu de rétroaction de NewsFilter.

On permet à l'l'utilisateur de dépasser la valeur automatique par le menu de rétroaction. La


valeur de rétroaction est employée en formant le modèle arrière de propagation dans le filtre
de rétroaction. Les cartes inutiles d'étiquette à une valeur de rétroaction de 0, pas très utile à
0.25, neutre à 0.5, modérément intéressant à 0.75, et intéressant à 1.0.

Un exemple

Dans cette section, nous décrivons le processus employé pour créer et utiliser les trois filtres
fournis par l'application de NewsFilter. Pour commencer l'application, courir la commande
suivante :

Java NewsFilter

Quand le panneau principal de NewsFilter est montré, aller d'abord classer et choisir les
nouvelles ouvertes accueillir… l'option. Le dialogue de NewsHost est montré montrant un
serveur de nouvelles de défaut. Les utilisateurs devraient écrire le nom de leur serveur de
nouvelles de Service Provider d'Internet ici. Si CORRECT est cliqué, un message sera
évident dans le TextArea déclarant que le NewsFilter essaye de se relier au serveur. La
réponse du serveur sera faite écho au TextArea.

Après, aller au menu de profil, et choisir les mots-clés. Une liste des 10 mots-clés de défaut
hardcoded dans l'application de NewsFilter est montrée. Les utilisateurs peuvent choisir
n'importe quel mot-clé qu'ils souhaitent (des mots simples seulement, aucunes expressions).
Après, choisi créer le profil du même menu. Ceci initialise les deux fichiers des profils,
LY_SII __ 198 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

newsfilter.dat et newsfilter.dfn. Vous êtes prêt à commencer à vous construire profil personnel
de filtre.

La liste de newsgroup sur le gauche supérieur s'amorce avec cinq groupes de nouvelles
d'Internet de défaut. Ils sont : comp.ai.fuzzy, comp.ai.neural-nets, comp.lang.java.api,
comp.lang.java.misc, et comp.lang.java.tech. Ces groupes de nouvelles ont été choisis parce
qu'ils sont les endroits raisonnables pour trouver des poteaux ou des articles de nouvelles
contenant notre liste de mot-clé de défaut. Pour charger le premier groupe de nouvelles,
comp.lang.ai, double-click sur cet article dans la liste. Le NewsFilter commence alors à
demander tous les articles que le centre serveur de nouvelles a dans le groupe de nouvelles.
Pendant que chaque article est téléchargé, la ligne objet est analysée de l'en-tête et est montrée
dans les articles de nouvelles énumèrent sur la droite supérieure, et le corps ou le texte du
message est lu et montré dans le TextArea. La ligne objet et le corps sont stockés dans l'objet
de NewsArticle.

Maintenant qu'un ensemble d'articles de nouvelles est chargé, nous pouvons voir qu'à quel
point ils assortissent nos mots-clés spécifiques. Les points d'allumette pour l'ensemble de
totalité peuvent être calculés using le profil/points tous les articles ou, pour un article
individuel, using l'article de profil/points. L'allumette de mot-clé est montrée au fond du
TextArea. Filtrer les articles en allant au menu de filtre et en vérifiant l'article de menu
d'articles de filtre au dessus de ce menu. Les articles sont assortis par leurs points d'allumette
et affiché à nouveau dans l'ordre décroissant dans les articles de nouvelles énumérer. L'article
de marquage supérieur est choisi, et le corps de cet article est montré dans le TextArea.
Quand l'article de menu d'articles de filtre est ne pas sélectionner, la liste d'article retourne à
l'ordre original.

Afin d'utiliser le filtre de faisceau ou le filtre de rétroaction, des données de profil doivent
d'abord être sauvées sur un ensemble d'articles raisonnablement classé. Par défaut, le
NewsFilter lit seulement 20 articles de chaque groupe de nouvelles (c'est une constante qui
peut être facilement changée dans le code). Si les cinq groupes de nouvelles de défaut sont
lus, cela donne 100 disques de profil d'article. Pour stocker les profils d'article au dossier des
textes de newsfilter.dat, choisir l'ajouter toute l'option de menu d'articles dans le menu de
profil. Supposant que le serveur de nouvelles a eu 20 articles dans le groupe de nouvelles de
comp.lang.ai, 20 disques seraient écrits.

Continuer en téléchargeant chacun des quatre prochains groupes de nouvelles alternativement,


en choisissant des points tous les articles, et puis ajouter tous les articles du menu de
profil. En ce moment il devrait y avoir approximativement 100 disques dans le dossier de
newsfilter.dat.

Maintenant qu'il y a des données de profil, nous pouvons utiliser le filtre de faisceau. D'abord,
choisir l'option de filtre de faisceau de construction sur le menu de filtre. On crée un réseau
neurologique qui lit le dossier de newsfilter.dat et groupe les disques de profil d'article dans 4
segments. (C'était une décision arbitraire, il pourrait facilement avoir été 9, 16, ou 25). Les
points crus d'allumette pour chaque article dans chaque faisceau sont additionnés et une
moyenne est calculée. Les points pour chaque article sont placés à la valeur moyenne du
faisceau qu'elle est tombé dans. Cette étape produit le modèle de segmentation employé par le
filtre de faisceau. Cependant, afin d'utiliser le filtre de faisceau, elle doit également être
choisie en tant qu'à filtre sur le menu de filtre. Si des articles de filtre est vérifiés, la liste
LY_SII __ 199 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

d'articles de nouvelles sera régénérée avec les articles du faisceau de dessus-marquage


d'abord, le faisceau de deuxième choix en second lieu, et ainsi de suite.

Le dernier filtre fourni dans l'application de NewsFilter est le filtre de rétroaction. Ceci
emploie aussi un modèle de réseau neurologique pour déterminer les points d'article. Un
modèle de régression arrière de propagation est créé using les disques de profil d'article
comme entrée et les points de rétroaction comme valeur de rendement de cible. Si aucun des
arrangements de rétroaction de défaut n'était dépassé avant que les articles aient été ajoutés au
profil, le modèle prévoira des points s'étendant de 0 à 1, où tous les articles ayant plus de 5
allumettes de mot-clé sont un 1. Si différentes valeurs étaient données pour les articles choisis
par rétroaction d'utilisateur explicite using les cinq niveaux définis dans le menu de
rétroaction, les résultats pourraient être légèrement différents. Par exemple, si des articles qui
ont contenu le mot « neural » étaient évalués comme plus intéressants que ceux qui a contenu
« Java, » ils auraient de plus hauts points du filtre de rétroaction, quoique chacun ait eu des
points crus identiques d'allumette.

Après que le modèle de filtre de rétroaction soit établi, il doit être choisi comme technique
désirée de filtre en vérifiant l'option de utilisation de rétroaction sur le menu de filtre. Si des
articles de filtre est allumés, les articles dans la boîte de liste de droite supérieure seront
commandés à nouveau selon les points qu'ils ont obtenus du modèle neural de prévision.

Classe de NewsFilter

La classe de NewsFilter est la classe d'application principale dans cette application. Elle
prolonge la vue using deux commandes de liste et un grand TextArea comme éléments
principaux d'interface, et un MenuBar des options pour permettre à l'utilisateur d'effectuer les
actions désirées. Une grande partie du code de GUI est produit par le produit visuel de
Symantec Café, et ainsi nous parlerons seulement de lui à un à niveau élevé.

Il y a beaucoup de méthodes d'action employées dans cette application en raison de le grand


nombre d'options de menu fournies. La méthode d'action () examine l'étiquette de l'objet
d'événement et la méthode appropriée s'appelle quand un événement sémantique à niveau
élevé est produit par une action d'utilisateur. En regardant les cordes examinées dans la
méthode d'action (), vous pouvez facilement déterminer la méthode correspondante
d'événement-traiteur.

Des actions qui traitent des données de communication et de lecture du serveur de nouvelles
sont manipulées par la classe de NewsFilter. Les actions se sont rapportées à établir le profil
d'utilisateur, articles de marquage, ou construisant les filtres de réseau neurologique sont
manipulés par le FilterAgent. La plupart de ces appels de méthodes sont traitées
synchroniquement. C'est-à-dire, l'application de NewsFilter appelle le FilterAgent dans son
fil de traitement principal et la commande ne retourne pas jusqu'à ce que la méthode de
FilterAgent accomplisse. Les exceptions à ce modèle sont les méthodes de
BuildClusterFilter_Action () et de BuildFeedbackFilter_Action (). Ils appellent les méthodes
dans le FilterAgent qui allument simplement les commutateurs ou les drapeaux booléens
pour que l'agent voie quand il se réveille périodiquement recherchant le travail. Cette
approche est employée parce que la formation des réseaux neurologiques peut être une action
LY_SII __ 200 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

longue et en gérant ces tâches asynchrone, l'application de NewsFilter n'est pas attachée vers
le haut d'attendre ces derniers pour accomplir. Le FilterAgent signale quand il est fait en
appelant le clusterNetBuilt () et des méthodes de scoreNetBuilt (). Ceux-ci permettent les
options correspondantes de menu de filtre, de sorte que le filtrage de faisceau et de
rétroaction puisse être choisi.

public class NewsFilter extends Frame {


void AddNewsGroup_Action(Event event) {

//{{CONNECTION
// Create with title, show as modal...
NewsGroupDialog dlg = new NewsGroupDialog(this,
“Enter name of New Group to add to list”, true);
dlg.show();
//}}
String newsGroup = dlg.textField1.getText() ;
if (newsGroup != null) {
list1.addItem(newsGroup) ;
}
}

void OpenNewsHost_Action(Event event) {

//{{CONNECTION
// Create with title, show as modal...
NewsHostDialog dlg = new NewsHostDialog(this,
“Enter address or name of News Server”, true) ;
//}}
dlg.setNewsHost(newsHost) ; // set to current value
dlg.show() ;
String temp = dlg.newsHost ;
if (temp != null) {
newsHost = temp ;
}
if (newsHost != null) {
textArea1.appendText(“\nConnecting to ” + newsHost + “ \n” );
connectToNewsHost() ;
}
}

void Keywords_Action(Event event) {

//{{CONNECTION
// Create with title, show as modal...
KeywordDialog dlg =
new KeywordDialog(this,“Specify Filter Keywords”,true);
//}}
dlg.setKeywords(keywords) ; // initialize the list
dlg.show() ;
keywords = dlg.getKeywords();
if (keywords != null) {
textArea1.appendText(keywords[0]) ;
}
}
// user selected a news group -- download it
void NewsGroup_Action(Event event) {
list2.clear() ;
textArea1.setText(“”) ;
articles = new Vector() ;
LY_SII __ 201 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

String newsGroup = list1.getSelectedItem() ;


readNewsGroup(newsGroup) ;
}

// user selected a news item -- put body in textArea


void NewsItem_Action(Event event) {
int index = list2.getSelectedIndex();
if (index != -1) {
currentArt = (NewsArticle)articles.elementAt(index) ;
textArea1.setText(currentArt.body); // clear the display
filterAgent.score(currentArt, keywords, filterType);
} else {
currentArt = null ;
}
}

void Feedback_Action(double feedback) {


if (currentArt != null) {
currentArt.feedback = feedback ;
}
}

void FilterArticles_Action(Event event) {


filterArticles() ;
}

void FilterType_Action(int fType) {


switch (fType) {
case 0: // keyword
keywordFilter.setState(true) ;
clusterFilter.setState(false) ;
feedbackFilter.setState(false) ;

break ;
case 1: // cluster
keywordFilter.setState(false) ;
clusterFilter.setState(true) ;
feedbackFilter.setState(false) ;
break ;
case 2: // feedback
keywordFilter.setState(false) ;
clusterFilter.setState(false) ;
feedbackFilter.setState(true) ;
break ;
}
filterType = fType ;
if (filterArt.getState() == true) filterArticles() ;
}

void AddArticle_Action(Event event) {


// open the profile file and append the
// score data for this article
filterAgent.addArticleToProfile(currentArt) ;
}

void AddAllArticles_Action(Event event) {


// open the profilefile and append the
// score data for all articles
filterAgent.addAllArticlesToProfile(articles) ;
}
LY_SII __ 202 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

void ScoreArticle_Action(Event event) {


filterAgent.score(currentArt, keywords, filterType) ;
}

void ScoreAllArticles_Action(Event event) {


// do it unconditionally
filterAgent.score(articles, keywords, filterType) ;
scored = true ;
}

void CreateProfile_Action(Event event) {


// empty the newsfilter.prf file
File prf = new File(“newsfilter.prf”) ;
prf.delete() ;
File dat = new File(“newsfilter.dat”) ;
dat.delete() ;
// create a newsfilter.prf file using current keywords
filterAgent.writeProfileDataDefinition(keywords) ;
}

void BuildKeywordFilter_Action(Event event) {


filterAgent.score(articles, keywords, filterType);
}

void BuildClusterFilter_Action(Event event) {


clusterFilter.disable(); // wait for completion
filterAgent.buildClusterNet(); // do asynchronously
}

void BuildFeedbackFilter_Action(Event event) {


feedbackFilter.disable(); // wait for completion
filterAgent.buildScoreNet(); // do asynchronously
}

void clusterNetBuilt() {
clusterFilter.enable() ; // FilterAgent has completed
}

void scoreNetBuilt() {
feedbackFilter.enable() ; // FilterAgent has completed
}

void Open_Action(Event event) {


//{{CONNECTION
// Action from Open... Show the OpenFileDialog
openFileDialog1.show();
//}}
}
void LoadArticle_Action(Event event) {
//{{CONNECTION
// Action from Open... Show the OpenFileDialog
openFileDialog1.show();
//}}
String fileName = openFileDialog1.getFile();
if (fileName != null) {
NewsArticle art = new NewsArticle(fileName) ;
art.readArticle(fileName) ;
list2.addItem(fileName) ;
textArea1.setText(art.body) ;
LY_SII __ 203 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

articles.addElement(art) ;
}
}
void SaveArticle_Action(Event event) {
//{{CONNECTION
// Action from Save... Show the SaveFileDialog
saveFileDialog1.show();
//}}
String fileName = saveFileDialog1.getFile();
if (fileName != null) {
int index = list2.getSelectedIndex();
if (index != -1) {
NewsArticle art = (NewsArticle)articles.elementAt(index) ;
art.writeArticle(fileName) ;
}
}
}

void About_Action(Event event) {


//{{CONNECTION
// Action from About Create and show as modal
(new AboutNewsFilterDialog(this, true)).show();
//}}
}

void Exit_Action(Event event) {


closeNewsHost() ;
//{{CONNECTION
// Action from Exit Create and show as modal
(new QuitDialog(this, true)).show();
//}}
}

public NewsFilter() {

//{{INIT_CONTROLS
setLayout(null);
addNotify();
resize(insets().left + insets().right + 587,insets().top +
insets().bottom + 404);
setBackground(new Color(16777215));
openFileDialog1 =
new java.awt.FileDialog(this, “Open”,FileDialog.LOAD);
saveFileDialog1 =
new java.awt.FileDialog(this, “Save”,FileDialog.SAVE);

//$$ openFileDialog1.move(36,336);
list1 = new java.awt.List(0,false);
add(list1);
list1.reshape(insets().left + 12,insets().top + 36,276,88);
list1.setBackground(new Color(16777215));
list2 = new java.awt.List(0,false);
add(list2);
list2.reshape(insets().left + 300,insets().top + 36,276,88);
list2.setBackground(new Color(16777215));
textArea1 = new java.awt.TextArea();
textArea1.reshape(insets().left + 12,insets().top + 144,564,208);
add(textArea1);
label1 = new java.awt.Label(“Newsgroup”);
label1.reshape(insets().left + 12,insets().top + 12,93,24);
LY_SII __ 204 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

add(label1);
label2 = new java.awt.Label(“News Item”);
label2.reshape(insets().left + 300,insets().top + 12,86,24);
add(label2);
setTitle(“CIAgent News Filter Application”);
setResizable(false);
//}}

//{{INIT_MENUS
mainMenuBar = new java.awt.MenuBar();

menu1 = new java.awt.Menu(“File”);


menu1.add(“Reset”);
menu1.add(“Open News Host...”);
menu1.add(“Close News Host”);
menu1.add(“Add News Group...”);
menu1.addSeparator();
menu1.add(“Save Article...”);
menu1.add(“Load Article...”);
menu1.addSeparator();
menu1.add(“Exit”);
mainMenuBar.add(menu1);

menu6 = new java.awt.Menu(“Profile”);


menu6.add(“Keywords...”);
menu6.addSeparator();
menu6.add(“Add article”);
menu6.add(“Add all articles”);
menu6.addSeparator();
menu6.add(“Score article”);
menu6.add(“Score all articles”);
menu6.addSeparator();
menu6.add(“Create profile”);
mainMenuBar.add(menu6);

menu2 = new java.awt.Menu(“Edit”);


menu2.add(“Cut”);
menu2.add(“Paste”);
mainMenuBar.add(menu2);

menu4 = new java.awt.Menu(“Filter”);


filterArt =
new java.awt.CheckboxMenuItem(“Filter articles”);
filterArt.setState(false);
menu4.add(filterArt);
menu4.addSeparator();
keywordFilter =
new java.awt.CheckboxMenuItem(“using Keywords”);
keywordFilter.setState(true);
menu4.add(keywordFilter);
clusterFilter =
new java.awt.CheckboxMenuItem(“using Clusters”);
clusterFilter.setState(false);
clusterFilter.disable(); // disable until model is built
menu4.add(clusterFilter);
feedbackFilter =
new java.awt.CheckboxMenuItem(“using Feedback”);
feedbackFilter.setState(false);
feedbackFilter.disable(); // disable until model is built
menu4.add(feedbackFilter);
LY_SII __ 205 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

menu4.addSeparator();
menu4.add(“Build Keyword filter”);
menu4.add(“Build Cluster filter”);
menu4.add(“Build Feedback filter”);
mainMenuBar.add(menu4);

menu5 = new java.awt.Menu(“Feedback”);


menu5.add(“Useless”); // 0
menu5.add(“Not very useful”) ; // 0.25
menu5.add(“Neutral”) ; // 0.5
menu5.add(“Mildly interesting”) ; // 0.75
menu5.add(“Interesting”) ; // 1
mainMenuBar.add(menu5);

menu3 = new java.awt.Menu(“Help”);


mainMenuBar.setHelpMenu(menu3);
menu3.add(“About”);
mainMenuBar.add(menu3);
setMenuBar(mainMenuBar);
//$$ mainMenuBar.move(0,336);
//}}

// set default values here


newsHost = “news-s01.ny.us.ibm.net” ;
filterAgent.newsFilter = this ;
filterAgent.textArea = textArea1 ;
list1.addItem(“comp.ai.fuzzy”) ;
list1.addItem(“comp.ai.neural-nets”) ;
list1.addItem(“comp.lang.java.api”) ;
list1.addItem(“comp.lang.java.misc”) ;
list1.addItem(“comp.lang.java.tech”) ;
keywords = new String[10] ; // default list
keywords[0] = “java” ;
keywords[1] = “agents” ;
keywords[2] = “fuzzy” ;
keywords[3] = “intelligent” ;
keywords[4] = “neural” ;
keywords[5] = “network” ;
keywords[6] = “genetic” ;
keywords[7] = “Symantec” ;
keywords[8] = “Cafe” ;
keywords[9] = “Beans” ;
filterAgent.process() ; // start the Filter agent thread
}

public NewsFilter(String title) {


this();
setTitle(title);
}

public synchronized void show() {


move(50, 50);
super.show();
}
public boolean handleEvent(Event event) {
if (event.id == Event.WINDOW_DESTROY) {
hide(); // hide the Frame
dispose(); // free the system resources
System.exit(0); // close the application
return true;
LY_SII __ 206 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

}
return super.handleEvent(event);
}

public boolean action(Event event, Object arg) {

if (event.target instanceof MenuItem) {


String label = ((MenuItem)event.target).getLabel();

if (label.equalsIgnoreCase(“Add News Group...”)) {


AddNewsGroup_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Open News Host...”)) {
OpenNewsHost_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Close News Host”)) {
closeNewsHost();
return true;
} else
if (label.equalsIgnoreCase(“Open...”)) {
Open_Action(event);
return true;
} else

if (label.equalsIgnoreCase(“Save article...”)) {
SaveArticle_Action(event);
return true;
} else

if (label.equalsIgnoreCase(“Load article...”)) {
LoadArticle_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Add article”)) {
AddArticle_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Add all articles”)) {
AddAllArticles_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Score article”)) {
ScoreArticle_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Score all articles”)) {
ScoreAllArticles_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“About”)) {
About_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Exit”)) {
Exit_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Keywords...”)) {
LY_SII __ 207 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Keywords_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Create profile”)) {
CreateProfile_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Useless”)) {
Feedback_Action(0);
return true;
} else
if (label.equalsIgnoreCase(“Not very useful”)) {
Feedback_Action(0.25);
return true;
} else
if (label.equalsIgnoreCase(“Neutral”)) {
Feedback_Action(0.5);
return true;
} else
if (label.equalsIgnoreCase(“Mildly interesting”)) {
Feedback_Action(0.75);
return true;
} else

if (label.equalsIgnoreCase(“Interesting”)) {
Feedback_Action(1.0);
return true;
} else

if (label.equalsIgnoreCase(“Filter articles”)) {
FilterArticles_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“using Keywords”)) {
FilterType_Action(0);
return true;
} else if (label.equalsIgnoreCase(“using Clusters”)) {
FilterType_Action(1);
return true;
} else if (label.equalsIgnoreCase(“using Feedback”)) {
FilterType_Action(2);
return true;
} else
if (label.equalsIgnoreCase(“Build Keyword filter”)) {
BuildKeywordFilter_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Build Cluster filter”)) {
BuildClusterFilter_Action(event);
return true;
} else
if (label.equalsIgnoreCase(“Build Feedback filter”)) {
BuildFeedbackFilter_Action(event);
return true;
}

}
if (event.target instanceof List) {
if (event.target == list1) {
// load selected news group
LY_SII __ 208 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

NewsGroup_Action(event) ;
return true;
}
if (event.target == list2) {
// load selected article
NewsItem_Action(event);
return true ;
}
}
return super.action(event, arg);
}

static public void main(String args[]) {


(new NewsFilter()).show();
}

//{{DECLARE_CONTROLS
java.awt.FileDialog openFileDialog1;
java.awt.FileDialog saveFileDialog1;
java.awt.List list1;
java.awt.List list2;
java.awt.TextArea textArea1;
java.awt.Label label1;
java.awt.Label label2;
//}}

//{{DECLARE_MENUS
java.awt.MenuBar mainMenuBar;
java.awt.Menu menu1;
java.awt.Menu menu6;
java.awt.Menu menu2;
java.awt.Menu menu4;
java.awt.CheckboxMenuItem filterArt;
java.awt.CheckboxMenuItem keywordFilter;
java.awt.CheckboxMenuItem clusterFilter;
java.awt.CheckboxMenuItem feedbackFilter;
java.awt.Menu menu5;
java.awt.Menu menu3;
//}}

Les membres des données de la classe de NewsFilter incluent le newsHost, qui se transfère
sur le serveur global des informations d'IBM, la douille de nouvelles, qui est utilisée pour se
relier et parler au serveur de nouvelles, et les jets de newsIn et de newsOut, qui sont employés
pour envoyer des données au-dessus du raccordement de douille. Deux vecteurs, articles et
originalArticleList, tiennent la liste non triée assortie et originale d'articles actuellement
chargés dans l'application de NewsFilter. Le membre de currentArt est une référence à
l'exemple de NewsArticle choisi dans la boîte de liste de droite supérieure. La rangée de
corde de mots-clés tient les mots que l'utilisateur a définis pour l'usage en évaluant la
pertinence des articles.

Le membre filterAgent tient une référence au FilterAgent, un agent intelligent CIAgent-basé


qui manipule l'entretien de profil d'utilisateur et le marquage et la construction des modèles
employés par les filtres de faisceau et de rétroaction. Le drapeau marqué indique si l'ensemble
entier d'articles de groupe de nouvelles a été marqué ou pas. Le membre à filtre indique quelle
technique de filtre l'utilisateur veut employer, le cas échéant, pour déterminer l'ordre que des
articles sont montrés. La valeur 0 correspond au filtre de mot-clé, 1 signifie le filtre de
LY_SII __ 209 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

faisceau, et le filtre de rétroaction de 2 moyens. Ces valeurs sont placées dans la méthode de
FilterType_Action () basée sur les arrangements des trois CheckboxMenuItems
correspondant dans le menu de filtre.

String newsHost ; // name of the news server


Socket news ; // socket to news server
PrintStream newsOut ; // stream to write to server
DataInputStream newsIn ; // stream to read from server
Vector articles = new Vector() ; // articles in news group
Vector originalArticleList ; //unfiltered list of articles
String[] keywords = new String[0] ; // user specified keywords
FilterAgent filterAgent = new FilterAgent() ;
NewsArticle currentArt ; // currently selected article
boolean scored = false ; // true if articles were scored
int filterType = 0 ; // keyword filter as default

La méthode de connectToNewsHost () établit le rapport initial au serveur de nouvelles de


NNTP. La corde de newsHost contient l'adresse du serveur de nouvelles fourni par
l'utilisateur (le défaut est news-s01.ny.us.ibm.net). Les spécifications de Network News
Transfer Protocol par Kantor et Lapsley (RFC #977 d'Internet) indiquent que les serveurs de
NNTP devraient écouter sur le numéro d'accès bien connu 119, ainsi un exemple d'une
douille au newsHost sont créées using ce port. Les trains de données de données de newsIn et
de newsOut sont instanciés. Un DataInputStream est employé pour la lecture, et un
PrintStream pour l'écriture. Après avoir établi le rapport initial de douille, le serveur de
nouvelles répond avec son statut, qui est lu dans la corde de réponse et montré dans le
TextArea principal. Si quelque chose va mal, comme un host address incorrect de nouvelles
ou une erreur de communication de réseau, toutes les exceptions produites par les opérations
d'E/S sont attrapées et un message d'erreur est affiché.

public void connectToNewsHost() {

try {
news = new Socket(newsHost, 119) ;
newsIn = new DataInputStream(news.getInputStream()) ;
newsOut = new PrintStream(news.getOutputStream()) ;
String reply = newsIn.readLine();
textArea1.appendText( reply + “\n”) ;
}
catch (Exception e) {
textArea1.appendText(“Exception:” + e) ;
}
}

La méthode de closeNewsHost () a arrêté le raccordement de douille au serveur de nouvelles.


Le protocole de NNTP exige du client d'envoyer la corde « STOPPÉE » comme message
fermant. Que de la corde est envoyée au serveur et est faite écho à l'affichage. Le serveur de
nouvelles n'envoie aucun message de réponse à l'« QUIT. »

public void closeNewsHost() {

try {
String cmd = “QUIT \n” ;
newsOut.println(cmd) ;
textArea1.appendText( cmd + “ \n” ) ;
newsIn.close() ;
LY_SII __ 210 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

}
catch (Exception e) {
textArea1.appendText(“Exception:” + e) ;
}

La méthode de readNewsGroup () prend un paramètre simple, le nom du groupe de nouvelles


à lire. Le NNTP exige l'ordre de « GROUPE », suivi du nom du groupe de nouvelles using la
représentation de corde standard (telle que comp.ai.neural-nets). Le serveur de nouvelles
répond avec un code retour, le nombre d'articles dans le groupe de nouvelles que le serveur
tient, la première et dernière identification d'article, et une corde de reconnaissance déclarant
que le groupe de nouvelles a été choisi. Un StringTokenizer est employé pour analyser ces
paramètres. Une fois qu'un groupe de nouvelles est choisi sur le serveur, un article individuel
doit être choisi. La première identification d'article retournée par ordre de « GROUPE » est
employée avec la commande de « stat » de placer un curseur interne sur le serveur de
nouvelles, de sorte que des articles suivants puissent être recherchés using le
« PROCHAINE » commande, plutôt qu'exigeant que chaque identification d'article soit
connue à l'avance.

La majorité de la méthode est a font une boucle, où la « PROCHAINE » commande est


utilisée comme moyen de marcher par le groupe de nouvelles, un article à la fois. D'abord la
commande « PRINCIPALE » est utilisée comme moyen de télécharger l'en-tête pour chaque
article. La méthode de parseHeader () prend l'identification d'article comme paramètre à
entrée unique. Elle instancie un exemple de NewsArticle, lit chaque ligne de l'en-tête et
l'analyse, recherchant le « objet : » ligne. Elle stocke le sujet dans le membre soumis de l'objet
de NewsArticle et continue de lire les lignes d'en-tête jusqu'à ce que le serveur de nouvelles le
signale qu'est fait en envoyant une ligne contenant seulement un caractère de période. En
arrière dans la méthode de readNewsGroupHeaders (), le sujet est montré dans la liste de
nouvelle, et le nouvel objet de NewsArticle est ajouté au vecteur d'articles.

D'une façon semblable, la commande de « CORPS » est envoyée au serveur de nouvelles et


les lignes des articles de nouvelles sont téléchargées et montrées dans le TextArea. Quand le
caractère de sentinelle de période est reçu, le membre de corps d'article est placé en faisant un
getText () sur la commande de TextArea.

La « PROCHAINE » commande fait un pas au prochain article dans le groupe de nouvelles.


Après que chaque chaîne d'ordres de NNTP soit envoyée, les codes retour sont vérifiés.
Pendant l'essai que nous avons rencontré des cas où le serveur a renvoyé l'en-tête pour un
article en réponse la commande « PRINCIPALE », mais avons d'autre part contrarié quand à
la commande de « CORPS » a été envoyé pour la même identification d'article

Faire une boucle les sorties quand la limite de maxArticles est atteinte, ou si un code retour
d'erreur est reçu du serveur. Le cas échéant des articles sont lus, le premier est choisi dans la
liste de nouvelle, et son corps est montré dans le TextArea. Avant que le filtrage soit effectué
sur le nouveau groupe, l'ensemble original d'articles dans l'ordre original est copié au membre
d'originalArticles. Si le filtrage est tourné sur les articles sont immédiatement filtrés using l'à
filtre spécifique.
LY_SII __ 211 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Noter svp, la classe de NewsFilter n'est pas un lecteur Usenet commercial. Il y a d'une
logique beaucoup plus de contrôle d'erreurs qui devrait être ajoutée pour un lecteur Usenet
d'usage universel. Cependant, ceci fournit le mécanisme de base pour passer en revue un
groupe de nouvelles d'Internet et nous permet d'obtenir des articles de nouvelles pour établir
le profil d'utilisateur.

public void readNewsGroup(String newsGroup) {

int maxArticles = 20 ;
boolean exit = false ;

scored = false ; // articles were not scored yet

try {
String cmd = “GROUP ” + newsGroup + “ \n” ;
newsOut.println(cmd) ;
String reply = newsIn.readLine();
textArea1.appendText( cmd + “ \n” + reply + “\n”) ;

StringTokenizer st = new StringTokenizer(reply) ;


String s1 = st.nextToken() ; // response code
String s2 = st.nextToken() ; // number of appends
String s3 = st.nextToken() ; // first id
String s4 = st.nextToken() ; // last id
String s5 = st.nextToken() ; // newsgroup

cmd = “STAT ” + s3 + “\n” ;


newsOut.println(cmd) ;
reply = newsIn.readLine();
textArea1.appendText( cmd + “ \n” + reply + “\n”) ;

String retCode ;
do {
textArea1.setText(“”);
cmd = “HEAD \n” ;
newsOut.println(cmd) ;
reply = newsIn.readLine();
textArea1.appendText( cmd + “ \n” + reply + “\n”) ;
StringTokenizer tok = new StringTokenizer(reply, “ ”) ;
retCode = tok.nextToken() ;
String id = tok.nextToken() ;
String msgId = tok.nextToken() ;

if (!retCode.equals(“221”)) continue ;

// now read all header records for this article and parse
NewsArticle art = parseHeader(id) ;
list2.addItem( art.subject ) ; // display subject only
articles.addElement(art) ; // add to Vector

cmd = “BODY \n” ;


newsOut.println(cmd) ;
reply = newsIn.readLine();
StringTokenizer stok = new StringTokenizer(reply) ;
retCode = stok.nextToken() ; // response code
if (!retCode.equals(“222”)) { // error?
articles.removeElement(art) ; // bad article
continue ;
}
LY_SII __ 212 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

textArea1.appendText( cmd + “ \n” + reply + “\n”) ;


do {
reply = newsIn.readLine();
textArea1.appendText( reply + “\n”) ;

} while(!reply.equals(“.”)) ;
art.body = textArea1.getText() ;
cmd = “\n NEXT \n” ;
newsOut.println(cmd) ;
reply = newsIn.readLine();
textArea1.appendText( cmd + “ \n” + reply + “\n”) ;
StringTokenizer st2 = new StringTokenizer(reply) ;
retCode = st2.nextToken() ; // response code

} while (retCode.equals(“223”) &&


(articles.size() < maxArticles)) ;
list2.select(0) ;
currentArt = (NewsArticle)articles.elementAt(0) ;
textArea1.setText(currentArt.body) ;
}
catch (Exception e) {
textArea1.appendText(“Exception:” + e) ;
}
originalArticleList = (Vector)articles.clone() ; // save copy
if (filterArt.getState() == true) filterArticles();

}La méthode de filterArticles () vérifie d'abord pour voir si les articles CheckboxMenuItem
de filtre dans le menu de filtre est choisis. Si tous les articles dans le groupe de nouvelles
n'ont pas été marqués encore, ils sont marqués. Noter que les articles, l'ensemble courant de
mots-clés, et tous le type courant de filtre sont passés au FilterAgent pour le marquage. Le
résultat de cet appel est que le membre de points de chaque NewsArticle est placé à la valeur
appropriée basée sur le courant à filtre. La méthode d'insertionSort () s'appelle pour assortir
les articles par ordre décroissant par des points. La liste d'article est alors dégagée et remplie
avec les articles dans le nouvel ordre.

// the user wants us to filter articles


// clear the news Item list and redisplay
// using the article scores to determine the order
public void filterArticles() {
if (filterArt.getState() == true) {
if (!scored) filterAgent.score(articles, keywords,
filterType) ;
Vector sortedByScore = insertionSort(articles) ;
articles = sortedByScore;
} else {
articles = originalArticleList ;
}

list2.clear() ; // remove all articles from list


Enumeration enum = articles.elements() ;
while (enum.hasMoreElements()) {
NewsArticle art = (NewsArticle)enum.nextElement();
list2.addItem(art.subject) ;
}
list2.select(0) ;
currentArt = (NewsArticle)articles.elementAt(0) ;
LY_SII __ 213 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Cette méthode d'insertionSort () est une exécution standard de l'algorithme de sorte d'insertion
(Sedgewick 1984) adapté pour Java. D'abord, les éléments dans le vecteur sont copiés dans
une rangée pour la convenance. Après, commençant au deuxième élément dans la rangée,
nous marchons par la rangée, assortissant pendant que nous allons. Une fois que l'algorithme
de sorte est complet, les éléments de tableau sont copiés de nouveau dans un vecteur de
retour.

// sort articles by decreasing order of score


Vector insertionSort(Vector articles) {

int i, j ;
int size = articles.size() ;

NewsArticle sortedList[] = new NewsArticle[articles.size()] ;


articles.copyInto( sortedList ) ;
NewsArticle temp ;

for (i=1 ; i < size ; i++) {


temp = sortedList[i] ;
textArea1.appendText(temp.score + “ ”) ;
j = i ;
while ((j > 0) && (sortedList[j-1].score < temp.score)) {
sortedList[j] = sortedList[j-1];
j = j - 1 ;
}
sortedList[j] = temp;
}

Vector outList = new Vector() ;


textArea1.appendText(“\n Sorted list = ”) ;
for (i=0 ; i < size ; i++) {
temp = sortedList[i] ;
textArea1.appendText(temp.score + “ ”) ;
outList.addElement(temp) ;
}
return outList ;
}

Classe de NewsArticle

La classe de NewsArticle définit toutes les informations sur un article de nouvelles simple.
L'information primaire est l'identification de NNTP, qui est retournée par le serveur de
nouvelles en réponse à la « PROCHAINE » commande comme tous les articles dans le
groupe de nouvelles est lue. Le membre soumis contient la ligne objet analysée hors de
l'information d'en-tête. Le membre de corps est le texte entier retourné par le serveur en
réponse à la commande de « CORPS ». Le reste des membres sont employés par les fonctions
de NewsFilter. La rangée de comptes tient les points crus d'allumette de mot-clé. La somme
est la somme de comptes. La rétroaction est employée pour décrire l'utilité de l'article à
l'utilisateur (0.0 est inutile, 1.0 est intéressant). Les points sont le rang de l'article basé sur le
LY_SII __ 214 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

type de filtre étant appliqué. Le clusterId tient le segment dans lequel cet article tombe dans le
filtre de faisceau.

Le readArticle () et des méthodes de writeArticle () sont fournis pour permettre à des


signalisations d'essai d'être ajoutés au profil d'utilisateur. Par exemple, l'utilisateur pourrait
charger quelques articles d'intérêt particulier afin de les marquer, leur donne des valeurs
élevées using l'option de rétroaction, et puis ajoute le disque de profil au dossier de
newsfilter.dat. Le sujet et le corps du texte sont enchaînés et écrits et lisent comme bloc
simple de bytes.

() La méthode getProfileString est employée pour composer le contenu de la rangée de


comptes [], avec la rétroaction et les notes. Cette corde est une représentation espace-
délimitée des textes qui est retournée à addArticleToProfile () et apposée au dossier de
newsfilter.dat.

public class NewsArticle {


String id ; // numeric article ID -- valid only for this server
String subject ; // subject line from header
String body ; // body or text of news posting
int counts[] ; // raw counts of keys in body
int sum ; // sum of raw counts
double feedback=0.5 ; // 0=useless 0.5=neutral, 1.0=interesting
double score ; // match score or ranking
int clusterId; // cluster this article falls into
NewsArticle(String id) { this.id = id ; }

void readArticle(String fileName) {


File f = new File(fileName) ;
int size = (int) f.length();
int bytesRead = 0 ;
try {
FileInputStream in = new FileInputStream(f) ;

byte[] data = new byte[size] ;


in.read(data, 0, size);
subject = “Subject: ” + fileName ;
body = new String(data) ;
id = fileName ;
in.close() ;
}
catch (IOException e) {
System.out.println(“Error: couldn’t read news article from ”
+ fileName + “\n”) ;
}
}

void writeArticle(String fileName) {


File f = new File(fileName) ;
String dataOut = subject + “ ” + body ;
int size = (int) dataOut.length();
int bytesOut = 0 ;
byte data[] = new byte[size] ;
body.getBytes(0, body.length(), data, 0) ;
try {
FileOutputStream out = new FileOutputStream(f) ;
out.write(data, 0, size);
out.flush() ;
LY_SII __ 215 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

out.close() ;
}
catch (IOException e) {
System.out.println(“Error: couldn’t write news article to ”+
fileName + “\n”);
}
}

// return the profile data as a string for writing out


String getProfileString() {

StringBuffer outString = new StringBuffer(“”) ;


for (int i = 0 ; i < counts.length ; i++) {
outString.append(counts[i]) ;
outString.append(“ ”) ;
}
outString.append(feedback) ;
outString.append(“ ”) ;
outString.append(score) ;

return outString.toString() ;
}
}

Classe de FilterAgent

La classe de FilterAgent est une sous-classe de CIAgent. Elle fournit toutes les fonctions
liées à la gestion des données de profil d'utilisateur, du mot-clé marquant, et de la construction
des modèles de réseau neurologique utilisés pour les filtres de faisceau et de rétroaction. La
classe de FilterAgent est étroitement accouplée à l'application de NewsFilter. En soi, elle
pourrait être employée simplement comme classe intelligente d'aide, avec toutes ses méthodes
simplement appelées directement par le NewsFilter. Cependant, pour illustrer comment un
agent intelligent peut être étroitement accouplé mais court toujours de façon autonome, nous
ajoutons un protocole de signalisation simple pour application de NewsFilter aux fonctions
longues initiées de FilterAgent.

Le FilterAgent définit cinq membres additionnels de données au-dessus de classe de


CIAgent, une référence à l'exemple d'application de possession de NewsFilter, le clusterNet
qui tient l'exemple de KMapNet pour le groupement, et le membre de scoreNet qui tient
l'exemple de BackProp pour le marquage prédictif. Les deux drapeaux booléens sont placés
par l'application de NewsFilter quand l'utilisateur choisit les options de menu de filtre de
faisceau de construction et de filtre de rétroaction de construction. Quand l'un ou l'autre
de ces drapeaux sont placés, le FilterAgent notera ce fait quand il se réveille de son sommeil
périodique () dans la méthode de course (). Cette approche permet l'agent forment à de façon
autonome et asynchrone les réseaux neurologiques. Quand la formation est complète, le
FilterAgent signale l'accomplissement suivre le clusterNetBuilt de NewsFilter () et des
méthodes de scoreNetBuilt ().

Se rappeler que la classe de CIAgent met en application l'interface de


CIAgentEventListener qui exige () la méthode ciaEventFired. Cette méthode a été permise
de permettre à l'autre CIAgents de lancer la formation des réseaux neurologiques par le
mécanisme de CIAgentEvent.
LY_SII __ 216 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

public class FilterAgent extends CIAgent {

NewsFilter newsFilter ;
protected KMapNet clusterNet ;
protected BackProp scoreNet ;
protected boolean buildClusterNet = false;
protected boolean buildScoreNet = false ;

public FilterAgent() { name = “Filter”; }


public FilterAgent(String Name) { super(Name); } ;

public void process() {


stopped = false ;
// start a thread running
trace(“Starting ” + name + “\n”) ;
runnit = new Thread(this) ;
runnit.start() ;
}

public void stop() {


stopped = true;
trace(name + “ stopped \n”) ;
runnit.stop() ;
}

// method of Runnable (Thread) interface


public void run() {

while(stopped == false){ // interval timer


try {
runnit.sleep((long) 5 * 1000) ; // in milliseconds
if (buildClusterNet) {
trace(name + “: Starting to build Cluster network\n”);
trainClusterNet() ; // train the network
buildClusterNet = false ;
newsFilter.clusterNetBuilt() ; // signal application
trace(\n name + “: Completed build of Cluster network\n”);
} else if (buildScoreNet) {
trace(name + “: Starting to build Score network \n”);
trainScoreNet() ; // train the network
buildScoreNet = false ;
newsFilter.scoreNetBuilt();
trace(\n name + “: Completed build of Score network\n”);
}
}
catch (InterruptedException e)
{
// interrupted
}

public void ciaEventFired(CIAgentEvent e) {


trace(name + “: CIAgentEvent received by ” + name +
“ from ” + e.getSource() + “ with arg ” +
e.getArgObject()) ;
// set flag for autonomous thread to see
String arg = (String)e.getArgObject() ;
LY_SII __ 217 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

if (arg.equals(“buildClusterNet”)) {
buildClusterNet = true ;
} else if (arg.equals(“buildScoreNet”))
buildScoreNet = true ;
}

// used to trigger autonomous build of Kohonen Map


public void buildClusterNet() {
buildClusterNet = true ;
}

// used to trigger autonomous build of BackProp network


public void buildScoreNet() {
buildScoreNet = true ;
}

La méthode de scoreArticle () prend un NewsArticle simple, la liste courante de mots-clés, et


l'à filtre actuellement choisi comme entrées. La première tâche est de compter l'occurrence de
chaque mot-clé dans l'article, et puis calcule la somme. Le traitement continue, basé sur l'à
filtre. Pour le filtre de mot-clé, les points sont simplement la somme d'allumettes de mot-clé.
Si le filtre de faisceau a été créé, les données de profil d'article sont passées par le réseau
neurologique de carte de Kohonen pour déterminer la marque de faisceau, qui est stockée
dans l'objet d'article. Les points réels de filtre de faisceau ne peuvent pas être calculés dans
cette méthode, parce qu'ils se fondent sur le calcul d'une vingtaine moyenne de tous les
articles avec le même clusterId. Ces points sont placés seulement dans la deuxième méthode
de points () qui prend le vecteur entier d'articles comme paramètre.

Une approche semblable est adoptée quand le filtrage de rétroaction est permis et le modèle
arrière de propagation a été établi. Mais dans le cas de rétroaction, le rendement de réseau
neurologique est les points de rétroaction. Ainsi il est immédiatement assigné à l'article.score.

Une dernière fonction remplie dans cette méthode est l'attribution automatique des valeurs de
rétroaction. Une échelle simple de seuil détermine cette valeur basée sur les points crus
d'allumette. Si des valeurs user-specific étaient placées avant que des points () se soient
appelés, cette information serait perdue.

// score a single article


void score(NewsArticle article, String[] keywords, int filterType) {

article.counts = countWordMultiKeys(keywords, article.body);


int size = article.counts.length ;
int sum = 0 ;
for (int i = 0 ; i < size ; i++) sum += article.counts[i];
article.sum = sum ; // for convenience

// based on the type of filter the user wants


// fill in the score slot of each article
switch(filterType) {
case 0: // keyword
article.score = (double)sum ;
break;
case 1: // cluster
if (clusterNet != null) {
// pass through network --- get clusterID
float[] inputRec = new float[size+2] ;
LY_SII __ 218 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

for (int i=0 ; i < size ; i++) inputRec[i] =


(float)article.counts[i];
inputRec[size] = (float)article.sum ;
inputRec[size+1] = (float)article.feedback ;
article.score = clusterNet.getCluster(inputRec) ;
}
break ;
case 2: // feedback
if (scoreNet != null) {
// pass through network --- get score
float[] inputRec = new float[size+2] ;
for (int i=0 ; i < size ; i++) inputRec[i] =
(float)article.counts[i];
inputRec[size] = (float)article.sum ;
inputRec[size+1] = (float)article.feedback ;
article.score = scoreNet.getPrediction(inputRec) ;
}
break ;
}
// OK, now do an automatic feedback pass
// so user doesn’t have to do it for each article
// User can override via Feedback menu option
if (sum == 0) {
article.feedback = 0.0 ; // negative feedback
} else if (sum < 2) {
article.feedback = 0.25 ;
} else if (sum < 4) {
article.feedback = 0.50 ;
} else if (sum < 6) {
article.feedback = 0.75 ;
} else article.feedback = 1.0 ;
}

La deuxième méthode de points () prend un vecteur des objets de NewsArticle, la liste


courante de mots-clés, et l'à filtre actuellement choisi comme entrées. Elle appelle la première
méthode de points () pour chaque NewsArticle dans le vecteur. Puisque la méthode simple de
points d'article () prend soin des aspects uniques de chaque à filtre, aucun traitement filtre-
spécifique n'est exigé dans cette boucle. Cependant, le filtre de faisceau exige le traitement
spécial après que les clusterIds aient été placés. C'est pourquoi il y a un appel aux
computeClusterAverages () à l'extrémité des points ().

Comme indiqué par son nom, les computeClusterAverages () marche par le NewsArticles et
calcule la somme crue de points d'allumette et le nombre d'articles dans chaque faisceau. Les
points moyens d'allumette pour chaque faisceau sont calculés en divisant la somme par le
nombre d'articles dans le faisceau. Une fois que ces points moyens sont calculés, un autre
passage au-dessus du NewsArticles est exigé pour placer les points d'article à la valeur
moyenne correspondante.

// score all loaded articles using the designated filter


void score(Vector articles, String[] keywords, int filterType) {

try {
String id ;

Enumeration enum = articles.elements() ;


while (enum.hasMoreElements()) {
LY_SII __ 219 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

trace(“”) ;
NewsArticle article = (NewsArticle)enum.nextElement() ;
score(article, keywords, filterType) ;
}
}
catch (Exception e) {
trace(“Exception:” + e) ;
}
if (filterType == 1) computeClusterAverages(articles) ;
}

// compute the average score for each cluster


// and set the score of each article in each cluster
// to that average value
void computeClusterAverages(Vector articles) {

int numClusters = 4 ; // we are using 4 for now


int sum[] = new int[numClusters] ;
int numArticles[] = new int[numClusters] ;
double avgs[] = new double[numClusters] ;

Enumeration enum = articles.elements() ;


while (enum.hasMoreElements()) {
NewsArticle article = (NewsArticle)enum.nextElement() ;
int cluster = article.clusterId ;
sum[cluster] += article.sum; // sum of counts
numArticles[cluster]++ ; // bump counter
}

// now compute the average score for each cluster


for (int i=0 ; i < numClusters ; i++) {
if (numArticles[i] > 0) {
avgs[i] = (double)sum[i] / (double)numArticles[i];
} else {
avgs[i] = 0.0 ;
}
trace(“ cluster ” + i + “ avg = ” + avgs[i] + “\n”) ;
}

enum = articles.elements() ;
while (enum.hasMoreElements()) {
NewsArticle article = (NewsArticle)enum.nextElement() ;
article.score = avgs[article.clusterId] ;
}

}La méthode de countMultiWordKeys () est cheval de labour dans la classe de FilterAgent.


Elle prend une rangée de cordes de mot-clé et le texte d'article comme paramètres. Elle
exécute une recherche brute mais efficace des allumettes complètes de mot-clé. D'abord, un
choix de vecteurs est peuplé avec chaque clef, avec chaque mot-clé de la longueur N stocké
au nième index de la rangée. Ainsi, par exemple, le vecteur à la table [5] contiendrait chaque
clef de la longueur 5. Pendant le même passage au-dessus des mots-clés, la table de
brouillage de keyHash est peuplée avec le mot-clé comme clef de table de brouillage, et la
longueur du mot-clé (et donc de son index dans la table []) comme valeur de table de
brouillage. Ceci fournit une cartographie du mot-clé à son index dans la rangée de clefs.
Chaque élément dans la rangée de comptes [] est également initialisé à 0.
LY_SII __ 220 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Le texte est recherché un mot à la fois, using un StringTokenizer pour faire l'analyse. La
longueur de chaque mot est employée pour limiter la recherche aux mots-clés de la même
longueur. Une recherche linéaire est exécutée en ce moment. Quand une allumette ne
distinguant pas majuscules et minuscules est trouvée, le keyHash est employé pour trouver
l'index du mot-clé. Cet index est employé pour incrémenter l'élément correspondant dans la
rangée de comptes []. Le résultat des countMultWordKeys est un choix de nombres entiers, où
chaque élément représente tout le nombre de coups pour chaque mot-clé dans le texte.

// count the number of occurrences of the specified keys


// in the text
int[] countWordMultiKeys(String[] keys, String text) {
StringTokenizer tok = new StringTokenizer(text) ;
int keyLen = 1 ; // for now
int counts[] = new int[keys.length] ;
Vector table[] = new Vector[50] ; // up to length 50
Hashtable keyHash = new Hashtable() ;

trace(“Searching for keywords ... \n”) ;

for (int i=0 ; i < keys.length ; i++) {


int len = keys[i].length() ;
if (table[len] == null) table[len] = new Vector() ;
table[len].addElement(keys[i]) ;
keyHash.put(keys[i], new Integer(i)) ;
counts[i] = 0 ;
}

while (tok.hasMoreTokens()) {
String token = tok.nextToken() ;
int len = token.length();
if ((len < 50) && (table[len] != null)) {
Vector searchList = table[len] ;
Enumeration enum = searchList.elements() ;
while (enum.hasMoreElements()) {
String key = (String)enum.nextElement() ;
if (token.equalsIgnoreCase(key)) {
Integer index = (Integer)keyHash.get(key);
counts[index.intValue()]++ ; // found another one
continue ;
}
}
}
}

for (int i=0 ; i < keys.length ; i++) {


trace(“key = ” + keys[i] +
“ count = ” + counts[i] + “\n”) ;
}

return counts ;
}

La méthode de writeProfileDataDefinition () produit du dossier des textes de newsfilter.dfn,


qui définit la disposition du fichier de données de profil d'utilisateur. Un dossier de .dfn est lu
par la classe d'ensemble de données quand il charge un dossier pour les réseaux
LY_SII __ 221 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

neurologiques de formation ou les arbres de décision. Le dossier contient des paires de types
de données et de noms de champ. Chaque mot-clé a une entrée pour le nombre de fois où il
apparaît dans le texte d'article. La valeur de rétroaction d'article et les points courants sont
également écrits. Noter que cette opération dépend de quel filtre est en vigueur, parce que les
points d'article varieront, basé sur le choix de filtre.

// used by neural networks to read newsfilter.dat


void writeProfileDataDefinition(String[] keywords) {

try {
FileWriter writer = new FileWriter(“newsfilter.dfn”) ;
BufferedWriter out = new BufferedWriter(writer) ;
for (int i=0 ; i < keywords.length ; i++) {
out.write(“continuous ”) ;
out.write(keywords[i]) ;
out.newLine() ;
}
out.write(“continuous ClassField”) ; // feedback
out.newLine() ;
out.write(“continuous score”) ;
out.newLine() ;
out.flush() ;
out.close() ;
}
catch (IOException e) {
System.out.println(“Error: couldn’t create ‘newsfilter.dfn’ \n”);
}
}

() La méthode addArticleToProfile prend un NewsArticle simple comme paramètre et


appose son disque de profil de filtre au dossier de newsfilter.dat. Cette information est
composée par la méthode de NewsArticle.getProfileString (). () La méthode
addAllArticlesToProfile appelle () la méthode addArticleToProfile pour chaque NewsArticle
dans le vecteur d'articles.

void addArticleToProfile(NewsArticle currentArt) {

try {
FileWriter writer = new FileWriter(“newsfilter.dat”, true) ;
BufferedWriter out = new BufferedWriter(writer) ;

out.write(currentArt.getProfileString()) ;
out.newLine() ;
out.flush() ; out.close() ;
}
catch (IOException e) {
System.out.println(“Error: couldn’t append article to profile \n”);
}

}
void addAllArticlesToProfile(Vector articles) {
try {
FileWriter writer = new FileWriter(“newsfilter.dat”, true) ;
BufferedWriter out = new BufferedWriter(writer) ;

Enumeration enum = articles.elements() ;


while (enum.hasMoreElements()) {
LY_SII __ 222 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

NewsArticle art = (NewsArticle)enum.nextElement() ;


out.write(art.getProfileString()) ;
out.newLine() ;

}
out.flush() ;
out.close() ;
}
catch (IOException e) {
System.out.println(“Error: couldn’t append article to profile \n”);
}

La méthode de trainClusterNet () construit la carte de dispositif de Kohonen employée par le


filtre de faisceau. D'abord, un objet d'ensemble de données est instancié au-dessus du dossier
de newsfilter.dat. Un objet de KMapNet est créé et alors configuré pour avoir autant d'entrées
comme sont définis dans le dossier de newsfilter.dfn et pour avoir 4 sorties ou faisceaux.
L'objet de KMapNet est formé pour 20 passages au-dessus des données, les poids de réseau
sont fermés à clef en plaçant le mode à 1, et un passage simple est employé pour vérifier les
résultats. Noter qu'après que chaque passage de formation, le fil de FilterAgent rapporte ().
Ceci permet à l'article de NewsFilter de continuer son traitement.

void trainClusterNet() {

DataSet dataSet = new DataSet(“ProfileData”, “newsfilter”) ;


dataSet.setDisplay(textArea) ;
dataSet.loadDataFile() ; // load the data set

// create a KMap neural network


// specify the newsfilter.dat as the training data
// cluster it into 4 clusters

clusterNet = new KMapNet(“NewsFilter Cluster Profile”);


clusterNet.textArea1 = textArea ;
clusterNet.ds = dataSet ;
clusterNet.numRecs = dataSet.numRecords ;
clusterNet.fieldsPerRec = dataSet.fieldsPerRec ;
clusterNet.data = dataSet.normalizedData; // get vector of data

// create network, all fields are inputs


clusterNet.createNetwork(clusterNet.fieldsPerRec, 2, 2) ;
int maxNumPasses = 20 ;
int numRecs = clusterNet.numRecs ;

// train the network


for (int i= 0 ; i < maxNumPasses ; i++) {
for (int j=0 ; j < numRecs; j++) {
clusterNet.cluster() ; // train
}
runnit.yield() ; // after each pass
}
clusterNet.mode = 1 ; // lock the network weights
for (int i=0 ; i < clusterNet.numRecs ; i++) {

clusterNet.cluster() ; // test
// clusterNet.display_network() ;
}
LY_SII __ 223 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

La méthode de trainScoreNet () est employée pour établir le modèle de régression arrière de


propagation employé par le filtre de rétroaction. Le dossier de newsfilter.dat est employé
comme données de formation. Un réseau neurologique est créé et formé pour 2500 passages
au-dessus des données (cette valeur peut être excessive pour de grands ensembles de
formation). Après que chaque passage de formation que le fil rapporte () au NewsFilter.
Quand la formation accomplit, le réseau est verrouillé et un passage simple d'essai est assuré
les données. Le réseau qualifié emploie le profil d'article (les allumettes de mot-clé et les
points) pour prévoir s'est attendu à l'utilité comme représentée par la valeur de rétroaction
assignée à l'article. La valeur de rendement s'étend de 0.0 à 1.0, ainsi des articles sont assortis
dans l'ordre décroissant avec les points les plus proches de 1.0 au dessus de la liste et les
points les plus proches de 0.0 au fond.

void trainScoreNet() {
DataSet dataSet = new DataSet(“ProfileData”, “newsfilter”) ;
dataSet.setDisplay(textArea) ;
dataSet.loadDataFile() ; // load the data set

scoreNet = new BackProp(“NewsFilter Score Model”);


scoreNet.textArea1 = textArea ;
scoreNet.ds = dataSet ;
scoreNet.numRecs = dataSet.numRecords ;
scoreNet.fieldsPerRec = dataSet.normFieldsPerRec ;
scoreNet.data = dataSet.normalizedData ; // get vector of data
int numOutputs = dataSet.getClassFieldSize() ;
int numInputs = scoreNet.fieldsPerRec - numOutputs;
scoreNet.createNetwork(numInputs,2 * numInputs,numOutputs) ;
int maxNumPasses = 2500 ; // default -- could be on applet
int numRecs = scoreNet.numRecs ;
for (int i = 0 ; i < maxNumPasses ; i++) {
for (int j=0 ; j < numRecs; j++) {
scoreNet.process() ; // train
}
runnit.yield() ; // after each pass
}

scoreNet.textArea1.appendText(“\n Passes Completed: ” +


maxNumPasses + “ RMS Error = ” +
scoreNet.aveRMSError + “ \n”) ;

scoreNet.mode = 1 ; // lock the network


// do a final pass and display the results
for (int i=0 ; i < scoreNet.numRecs ; i++) {
scoreNet.process() ; // test
// scoreNet.display_network() ;
}
}

Discussion

Peut-être le thème principal dans cette application est le rapport entre le code d'application et
l'agent intelligent. On pourrait facilement imaginer une exécution où le FilterAgent n'a pas
existé, et ses fonctions ont été remplies par la classe de NewsFilter elle-même. Cependant, en
maintenant la nature distincte des fonctions de FilterAgent, nous sommes en mesure pour la
LY_SII __ 224 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

réutiliser dans un agent composé ou un système multiagent. Tandis que le FilterAgent n'est
pas aussi autonome que le TimerAgents ou le FileAgents, il forme asynchrone les modèles
de réseau neurologique. Avec juste le travail additionnel, le FilterAgent CIAgent-basé a pu
être modifié pour établir des modèles de réseau neurologique contre n'importe quel ensemble
de données, plutôt que les données hardcoded de newsfilter.

Dans cette application, nous avons développé des possibilités modestes d'exploitation de
données (Bigus 1996). En fournissant l'autre CIAgents qui peut accéder à des bases de
données ou d'autres points d'émission (la classe de NewsFilter fournit des sources de
nouvelles d'Internet), un système autonome d'exploitation de données pourrait être établi. Le
TimerAgent et le FileAgent développés en chapitre 8 pourraient être employés à
automatiquement extraient des données aux temps d'ensemble ou seulement quand des
dossiers spécifiques sont modifiés.

Une autre issue de conception pour cette application est la manière que l'assortiment de mot-
clé est fait. Dans la classe de NewsFilter, seulement des allumettes complètes de mot-clé ont
été comptées. Il y a des algorithmes pour faire les allumettes partielles de sorte que le
singulier et le pluriel soient comptés pour chaque limite. L'approche simple d'entier-mot a
employé manque ici également des cas où les signes de ponctuation sont à côté du mot dans le
texte. Évidemment, plus l'information d'allumette est meilleure, plus nous pouvons filtrer les
articles pour l'utilisateur plus exactement.

La dernière issue est la compilation des données de profil d'utilisateur. Le filtre de faisceau et
le filtre de rétroaction exécuteront seulement comme les données de profil employées pour
former le réseau neurologique fondamental modèle. Avec plusieurs des nouveaux groupes
nous avons examiné, seulement quelques uns des 20 articles ont eu les allumettes crues plus
considérablement que 0 de mot-clé. Une solution à ce problème est de produire d'uns les
disques en boîte de profil, qui sont main construite avec un nombre représentatif d'allumettes
de mot-clé et de valeurs de rétroaction. Cet ensemble de formation pourrait être employé
comme données basses, et des profils choisis d'article pourraient être ajoutés.

Résumé

En ce chapitre, nous avons développé un filtre de l'information pour des groupes de nouvelles
d'Internet using un agent intelligent CIAgent-basé étroitement accouplé. Les questions
principales incluent :

• Le protocole fondamental pour accéder à un serveur de nouvelles d'Internet est le


protocole de transport des informations. Les serveurs de nouvelles écoutent au port
119. Les chaînes d'ordres de base incluent le GROUPE pour choisir un groupe de
nouvelles, stat placer le curseur sur un article spécifique, à côté de la promenade par
les articles de groupe de nouvelles, et CHEF et CORPS pour rechercher des données
au sujet des différents articles.
• La classe d'application de NewsFilter fournit trois types de filtrage des articles de
groupe de nouvelles : Filtrage de mot-clé basé sur le nombre d'allumettes de mot-clé,
filtrage de faisceau basé sur grouper les articles semblables using le groupement
neural, et rétroaction filtrant using un modèle neural de prévision pour marquer des
articles basés sur la pertinence.
LY_SII __ 225 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

• Le FilterAgent prolonge la classe de CIAgent en fournissant des méthodes pour le


mot-clé marquant et pour former de façon autonome la carte de Kohonen et les
réseaux neurologiques de propagation arrière. Le FilterAgent maintient également les
dossiers des textes de profil d'utilisateur, newsfilter.dat et newsfilter.dfn, utilisés pour
former les réseaux neurologiques.

Exercices

1. Comment modifieriez-vous l'application de NewsFilter pour être plus flexible ?


Ajouter les possibilités pour que l'utilisateur place le nombre d'articles de nouvelles
pour lire par le GUI.
2. Tout en passant en revue un ensemble de groupes de nouvelles, établir votre propre
profil d'utilisateur, choisissant les articles représentatifs à travers l'inutile au spectre
intéressant. Être sûr de placer les niveaux de rétroaction convenablement. Construire le
filtre de rétroaction, et voir comment il exécute. Ajouter 20 disques main-construits de
profil au dossier de newsfilter.dat, et reconstruire le filtre de rétroaction. Le filtrage
s'est-il amélioré ?
3. Augmenter ou remplacer la méthode de countWordMultiKeys () pour améliorer
l'exactitude d'allumette de mot-clé du NewsFilter. La rangée de comptes
d'international [] fonctionnera-t-elle toujours pour votre nouvelle approche ? Ce qui si
vous ajoutez des possibilités d'allumette de mot partiel ?
LY_SII __ 226 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Chapitre 10
Application du MarketPlace
Ce chapitre se concentre sur les questions impliquées quand les agents
autonomes multiples agissent l'un sur l'autre dans les systèmes multiagent.
L'application est un marché d'agent intelligent, où les agents d'acheteur et de
vendeur coopèrent et concurrencent à traiter des transactions de ventes pour
leurs propriétaires. Un agent de facilitant est développé pour agir en tant que
directeur pour le marché. Les agents d'acheteur et de vendeur s'étendent
d'employer la logique hardcoded à inferencing basé sur les règles dans leurs
stratégies de négociation.

Introduction

Les systèmes de Multiagent fournissent un environnement complexe et intéressant pour


évaluer le comportement d'agent intelligent. Tandis que de tels systèmes ont été employés
dans des systèmes de conception assistée par ordinateur, la connaissance de domaine exigée
de comprendre ces applications est tout spécialisée. En ce chapitre, nous établirons un
système multiagent pour un marché électronique parce que chacun est familiarisé avec acheter
et vendre des choses. Nous développons un FacilitatorAgent qui contrôle le marché, et
incluons plusieurs genres de BuyerAgents et de SellerAgents pour agir l'un sur l'autre là.
Tous ces agents intelligents sont dérivés de la classe basse de CIAgent présentée en chapitre
7. Les agents d'acheteur et de vendeur sont différenciés principalement par la sophistication de
leurs stratégies de négociation, s'étendant de la logique simple et hardcoded à la règle de
chaînage avant inferencing.

Le FacilitatorAgent est l'intermédiaire ou le marieur entre les acheteurs et les vendeurs. Tous
les agents doivent s'inscrire au facilitant avant qu'ils puissent avoir toutes les interactions avec
d'autres agents dans le marché. Les vendeurs annoncent leur désir de vendre des produits ou
des services avec le FacilitatorAgent, alors que les acheteurs demandent au facilitant de
recommander un vendeur éventuel. Une fois que les agents d'acheteur et de vendeur ont été
présentés par le facilitant, ils communiquent toujours indirectement par le FacilitatorAgent.

Le schéma application 10.1 de marché de CIAgent.

Le schéma 10.1 montre le panneau principal de l'application de marché de CIAgent. Les deux
commandes de TextArea sont employées pour afficher des messages du facilitant et le
BuyerAgents et le SellerAgents dans le marché. Les options de MenuBar incluent le début
et l'arrêt, un choix contenu de détail ou de résumé de message, et le choix de trois types des
acheteurs et de vendeurs pour placer dans le marché. Ces agents peuvent être choisis dans
n'importe quelle combinaison des acheteurs et des vendeurs de base, intermédiaires, et
avancés. L'arrangement de défaut est pour qu'un BuyerAgent de base simple et un
SellerAgent de base soit dans le marché. Jusqu'à six indépendants et agents autonomes
peuvent être placés dans le marché en même temps.

Quand l'utilisateur choisit deux à six agents et choisit l'option de début à partir du menu
d'actions, un FacilitatorAgent simple est créé avec les agents choisis d'acheteur et de
LY_SII __ 227 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

vendeur. Le SellerAgents annoncent les articles qu'ils doivent se vendre en envoyant des
messages au facilitant et initialiser leur inventaire interne des articles. Le BuyerAgents
initialisent leurs propres listes d'achats. En ce moment, le marché est ouvert pour des affaires.

Tous les agents courent leurs propres fils qui se réveillent à intervalles spécifiques. La
première négociation de ventes a lieu quand une du BuyerAgents réveille et prend un article
de son wishList. Elle demande alors au facilitant de recommander un SellerAgent qui a
annoncé sa capacité de vendre cet article. Si plus d'un vendeur a annoncé un article, le
facilitant choisit aléatoirement un et renvoie le nom de ce SellerAgent à l'acheteur. Ceci
commence la communication entre l'acheteur et le vendeur pendant qu'ils essayent de
convenir sur un prix et clôturent l'affaire. Le facilitant agit en tant qu'intermédiaire entre
toutes les communications d'acheteur et de vendeur. Ceci nous permet d'observer l'échange
des messages entre les agents d'acheteur et de vendeur et de montrer une trace de ces
interactions dans le TextArea supérieur.

Toutes les communications entre les acheteurs, les vendeurs, et l'utilisation de facilitant
l'interface de CIAgentEvent et de CIAgentEventListener ont décrit en chapitre 7. L'objet
d'argument qui est passé avec le CIAgentEvents est un nouvel objet appelé un
CIAgentMessage, qui est modelé après un paquet standard de message de KQML. Bien que
nous n'analysions pas des messages de KQML dans cette application, l'utilisation de la classe
de CIAgentMessage devrait te donner une bonne sensation pour quelle application d'agent de
KQML regarderait comme (voir le chapitre 6 pour une description de KQML). Se rappeler
que KQML emploie des performatives pour indiquer l'action qu'il veut qu'un autre agent
prenne en son nom. Tandis que KQML spécifie le format et une partie du contenu de ces
interactions, les détails de nitty-gritty de la négociation d'Acheteur-Vendeur est jusqu'à
l'application à définir. La convention de négociation de ventes utilisée dans l'application de
marché est décrite dans la section suivante.

Après que tous les agents soient inscrits au facilitant et les vendeurs annoncent leurs articles,
le CIAgentMessages suivant sont échangés :

1. L'acheteur demande au facilitant de recommander-un le vendeur pour un article,


P.
2. Le facilitant indique à l'acheteur le nom du vendeur.
3. L'acheteur demande au vendeur (par le facilitant) si le vendeur a un article P à
vendre.
4. Le vendeur faire-offrent à l'acheteur, passant l'article P, une identification d'article
unique, et un premier prix demandé, ou nieront qu'il a l'article P à vendre.
5. L'acheteur alors peut ou accepter l'offre en faisant écho l'offre de nouveau au
vendeur, ou faire une contre- proposition (avec un prix différent) au vendeur.
6. Le vendeur peut accepter l'offre, faire une contre- proposition, ou rejeter l'offre.
7. Si le vendeur accepte il envoie un message de dire à l'acheteur, et la transaction de
ventes est complète.
8. Si le vendeur rejette l'offre, la négociation est terminée.

Noter que l'acheteur et le vendeur ne communiquent directement jamais, mais employer


toujours le facilitant en tant qu'intermédiaire dans une négociation de ventes.

Le schéma exemple 10.2 de marché 1.


LY_SII __ 228 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Un exemple

L'exemple suivant illustre les interactions entre un BuyerAgent de base simple et un


SellerAgent de base simple. L'application est commencée en sélectionnant la commande
suivante :

marché de Java

Quand la fenêtre principale de marché monte, l'application est commencée en choisissant


l'option de début dans le menu d'actions. Le schéma 10.2 expositions la droite de fenêtre
principale après le début est choisi, mais avant que toutes les transactions de ventes
commencent.

Le BuyerAgent va dormir pendant approximativement 5 secondes avant lui réveille et


sélectionne le premier article de sa liste d'achats. Cette pause entre les négociations est
employée pour le faciliter pour suivre les messages échangés entre les agents. L'acheteur a
une liste courte d'articles à acheter, une guitare pour $100, un ensemble de tambours pour
$200, et une guitare différente pour $100 (doit commencer un trio de puissance ?). Il s'avère
que le SellerAgent s'avère justement juste avoir deux guitares et un ensemble de tambours
dans son inventaire. Comme décrit plus tôt, le BuyerAgent demande au facilitant de
recommander le nom d'un SellerAgent qui a une guitare à vendre. Puisqu'il y a seulement un
SellerAgent, le facilitant renvoie le nom du SellerAgent au BuyerAgent et les choses
progressent de là. Quand le premier article est vendu, le BuyerAgent retourne pour dormir
pendant encore 5 secondes. Quand le BuyerAgent se réveille, il retire le prochain article la
liste d'achats et un autre ensemble de communications se produit. Cet ordre continue jusqu'à
ce que le BuyerAgent ait épuisé sa liste d'achats. Quand le marché s'est calmé, et aucune
activité autre que l'acheteur fièrement la proclamation de ses achats reste, la course
d'application de marché peut être arrêtée en choisissant l'option de menu d'arrêt sous le menu
d'actions.

Quand un SellerAgent vend un article, il envoie un message d'éviter la publicité au facilitant,


et ainsi le SellerAgent est enlevé de la liste. Note, si un vendeur a les articles multiples du
même type, il annonce des périodes multiples, ainsi en chaque vente et chaque
correspondance éviter la publicité du message, le facilitant a toujours une liste à jour de
vendeurs et d'articles disponibles.

Le schéma 10.3 contient les messages de trace du facilitant qui apparaissent dans le TextArea
supérieur. Vous pouvez voir que le FacilitatorAgent est commencé quand () la méthode de
processus s'appelle et est arrêtée quand la méthode d'arrêt () s'appelle. La première
transaction entre l'acheteur et le vendeur est reflétée dans les lignes 6 à 12. Le schéma 10.4
contient les messages de trace des agents dans le marché. Les deux agents sont commencés
par () la méthode de processus. Quand le BuyerAgent se réveille, il annonce qu'il veut
acheter une guitare. L'échange des offres suit, et la transaction accomplit. Noter que c'est le
niveau récapitulatif de défaut des messages de trace. Si l'option de détails était choisie, des
informations supplémentaires seraient montrées, y compris le contenu du CIAgentMessages.

Le schéma notation 10.3 de trace de facilitant de l'exemple 1.

Le schéma notation 10.4 de trace de marché de l'exemple 1.


LY_SII __ 229 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Comme vous pouvez voir, le BuyerAgent de base et le SellerAgent de base sont venus à un
prix de $175 pour les guitares, et à $325 pour les tambours. Ces prix sont beaucoup plus
élevés que les prix du but de l'acheteur. Des courses semblables peuvent être faites pour
l'acheteur intermédiaire et le vendeur intermédiaire, ou le meilleur acheteur peut être assorti
vers le haut contre le vendeur de base. Dans ce cas-ci, l'acheteur fait bien mieux ; suivant les
indications du schéma 10.5, il achète les trois instruments musicaux pour $500.

Le schéma notation 10.5 de trace de marché de l'exemple 2.

Le dernier exemple présente les négociations entre le BestBuyerAgent et le BestSellerAgent.


Le schéma 10.6 montre cet échange. Il y a beaucoup plus de continuer de marchandage car les
stratégies mises en application dans les règle-bases de ces agents sont plus complexes. Le
Meilleur-BuyerAgent obtient le meilleur du BestSellerAgent, accomplissant ses trois achats
pour seulement $470.

Le schéma notation 10.6 de trace de marché de l'exemple 3.

Ces exemples montrent l'opération de base et les interactions entre les agents dans le marché.
Dans les sections suivantes, la conception et les détails de l'exécution du FacilitatorAgent,
BuyerAgent, SellerAgent, les agents augmentés d'acheteur et de vendeur, et l'application
principale de marché elle-même sont décrits.

FacilitatorAgent

Le FacilitatorAgent emploie le modèle de conception de singleton (gamma et autres 1995),


qui assure que seulement un exemple simple de FacilitatorAgent existe en même temps. Ceci
empêche un imposteur que le facilitant de réorienter des ventes trafiquent à ses propres agents
préférés, et s'assure que tous les acheteurs et vendeurs peuvent trouver le marché par
l'exemple global de FacilitatorAgent. Tandis que nous favorisons une conception de modèle-
vue-contrôleur, où les agents dans le marché représentent le modèle et le java.awt. La vue et
le GUI représente la vue et contrôleur, nous mélangeons ces éléments dans cette demande de
simplicité.

Le facilitant contient deux tables de brouillage, une qui sont un enregistrement pour tous les
agents dans le marché, et une qui contiennent les communautés de l'intérêt pour le marché.
Par exemple, il peut y avoir un ensemble de SellerAgents qui vend les instruments musicaux,
et un ensemble différent qui vend des billets d'avion. Ceux-ci sont groupés dans les
communautés ou des domaines. SellerAgents sont ajoutés à ces communautés en annonçant
leur volonté ou capacité de vendre un produit dans ce domaine. Juste parce qu'un agent
annonce quelque chose, cependant, ne signifie pas qu'elle peut vraiment fournir ce produit ou
service. Sans compter que le cas évident où le deviousness est impliqué, un vendeur sincère
peut annoncer un article one-of-a-kind. Un acheteur peut alors demander au facilitant de
recommander un vendeur pour cet article. Les négociations ont pu procéder entre le vendeur
et l'acheteur, ayant pour résultat une vente. En attendant, un autre acheteur pourrait demander
au facilitant de recommander un vendeur et quand il contacte le vendeur demandant un prix,
le vendeur niera qu'il a un tel article. Ce scénario pourrait être empêché si le vendeur rétracte
toujours ses annonces. Cette application fournit ces possibilités. Cependant, si les transactions
multiples continuent en même temps, un acheteur devra être déçu (ou le vendeur pourrait
LY_SII __ 230 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

vendre le même article à deux acheteurs, qui est non conformiste mais très profitable à court
terme).

Comme sous-classe de CIAgent, le FacilitatorAgent est praticable. () La méthode de


processus est employée pour commencer son fil. Elle dort pendant 10 secondes et puis se
réveille pour voir si quelque chose qui excite se produit. Noter que ce n'est pas strictement
nécessaire pour cette application. Le FacilitatorAgent fonctionne dans le fil d'application
principale en manipulant synchroniquement CIAgentEvents. Une conception alternative pour
des transactions longues serait de tourner un nouveau fil toutes les fois qu'un événement a dû
être traité. Mais ceci obtient peu un impliqué et obtient très loin de l'exploration des principes
fondamentaux des systèmes multiagent.

la classe publique FacilitatorAgent prolonge CIAgent {

exemple = nulle statiques privés de FacilitatorAgent ; Singleton de //

// dans le modèle de conception de singleton, employé pour obtenir


l'exemple simple
exemple public statique de FacilitatorAgent () {
si (nulle de == d'exemple) {
exemple = nouveau FacilitatorAgent (« facilitant ") ;
}
exemple de retour ;
}

Note de // : ne peut pas être employé comme haricot de Java sans


constructeur public

FacilitatorAgent protégé () {superbe () ; nom = « facilitant » ; }


FacilitatorAgent protégé (nom de corde) {superbe (nom) ; } ;

textArea public de java.awt.TextArea ;


trace vide synchronisée par public (msg de corde) {
textArea.appendText (msg) ;
}

Aléatoire aléatoire = nouvel aléatoire () ; // choisissait des agents

allAgents de table de brouillage = nouvelle table de brouillage () ;


Les communautés de table de brouillage = nouvelle table de brouillage () ;
Msg de CIAgentMessage ; message courant de // étant traité

remise vide de public () {


allAgents = nouvelle table de brouillage () ; espace libre de // tous les
agents
les communautés = nouvelle table de brouillage () ; espace libre de //
toutes les communautés
}

processus vide de public () {


arrêté = faux ;
le début de // un fil et envoient à un message de mise à jour sec de chaque
intervalle
trace (« facilitant : processus () \ n ") ;
runnit = nouveau fil (ceci) ;
LY_SII __ 231 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

runnit.start () ;
}

arrêt de vide de public () {


arrêté = rectifier ;
tracer (« facilitant : arrêté \ n ") ;
runnit.stop () ;
}

méthode de // d'interface praticable (de fil)


vide de public couru () {
tandis que (== arrêté faux) {
essai {
Thread.sleep ((long) 10*1000) ; // viennent vivant toutes les 10 sec
si (traceLevel > 0) trace (« facilitant : active \ n ") ;
}
crochet (InterruptedException e)
{
// s'est interrompu
}
// font n'importe quel ménage ici ! ! !
}
}

() La méthode ciaEventFired est exigée par l'interface de CIAgentEventListener. L'objet de


CIAgentEvent contient l'objet de source qui a mis le feu à l'événement (recherché suivre la
méthode de getSource () sur l'objet de CIAgentEvent), et l'objet d'argument qui est recherché
suivre la méthode de getArgObject (). Dans tous les cas dans l'application de marché, cet objet
d'argument est un objet de CIAgentMessage, rudement équivalent à un paquet de message de
KQML. Une fois que le CIAgentMessage est extrait à partir du CIAgentEvent, la méthode
d'itinéraire () s'appelle pour traiter le message.

vide de public ciaEventFired (CIAgentEvent e) {


si (traceLevel > 0) {
trace (« facilitant : CIAgentEvent a reçu par » + nom +
« de » + e.getSource () + « avec des args » +
e.getArgObject () + « \ n ") ;
}
Arg d'objet = e.getArgObject () ;
msg = arg (de CIAgentMessage) ;
si (traceLevel > 0) msg.display () ;
itinéraire (msg) ;
}

registre vide synchronisé par charge statique publique (CIAgentEvent e) {


si exemple (de nulle de == d'exemple) = nouveau FacilitatorAgent () ;
instance.allAgents.put (((CIAgent) e.getSource ()).getName (),
e.getSource ()) ;
((CIAgent) e.getSource ()).addCIAgentEventListener (exemple) ;
instance.addCIAgentEventListener ((CIAgent) e.getSource ()) ;
}

La méthode d'itinéraire () prend un CIAgentMessage comme paramètre. La méthode obtient


une référence au CIAgent de envoi de la table de brouillage des agents enregistrés. Le
facilitant manipule trois types de performatives dans cette méthode, annoncent, évitent la
publicité, recommandent-un, en plus de sa fonction de message-cheminement. La publicité
et évitent la publicité des performatives ajoutent ou enlèvent l'agent de envoi d'une
LY_SII __ 232 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

communauté. La logique spéciale est nécessaire pour traiter les cas où l'agent est le premier à
ajouter, ou dernier pour être enlevée d'une communauté.

Le premier performative de recommandation-un vérifie pour voir s'il y a des agents dans la
communauté d'intérêt. Si oui, le facilitant détermine combien d'agents ont annoncé et puis
roulent un à nombre aléatoire pour choisir on pour recommander au BuyerAgent. Le cas où
un BuyerAgent demande un SellerAgent, et aucun n'a annoncé, a comme conséquence un
état non défini dans l'application d'échantillon. Le BuyerAgent s'attend à une réponse de dire
du facilitant ce qui ne vient jamais. Une solution raisonnable ici serait d'envoyer un message
de nier de nouveau au BuyerAgent. Cependant, la logique d'état de BuyerAgent devrait être
mise à jour pour traiter ceci correctement.

itinéraire vide synchronisé par public (msg de CIAgentMessage) {


Expéditeur de CIAgent = (CIAgent) allAgents.get (msg.sender) ;
l'agent de // veut dire qu'ils peuvent manipuler des questions au sujet de
contenu
si (msg.performative.equals (« annoncer ")) {
tracer (« facilitant : s'ajouter » + msg.sender +
« à » + msg.content + la « communauté \ n ") ;
si (communities.containsKey (msg.content)) {
Agents de vecteur = (vecteur) communities.get (msg.content) ;
agents.addElement (expéditeur) ;
} autrement {
Agents de vecteur = nouveau vecteur () ;
communities.put (msg.content, agents) ;
agents.addElement (expéditeur) ;
}
retour ;
}

l'agent de // veut s'enlever de la communauté


si (msg.performative.equals (« éviter la publicité ")) {
tracer (« facilitant : élimination » + msg.sender +
« de » + msg.content + la « communauté \ n ") ;

si (communities.containsKey (msg.content)) {
Agents de vecteur = (vecteur) communities.get (msg.content) ;
agents.removeElement (expéditeur) ;
si == 0 (d'agents.size ()) communities.remove (msg.content) ;
}
retour ;
}

l'agent de // veut que le facilitant recommande un agent


// sélectionnent aléatoirement un de la liste d'annoncer des agents de
vendeur
si (msg.performative.equals (« recommander-un ")) {
Ficeler l'article = le msg.content ;
si (communities.containsKey (msg.content)) {
Diriger les agents = (vecteur) communities.get (msg.content) ;
international numérique = agents.size () ;
index d'international ;
si (> 1) numérique {
double couche-point = random.nextDouble () ;
index = (international) (couche-point * numérique) ;
} autrement {
index = 0 ;
LY_SII __ 233 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

}
Agent de CIAgent = (CIAgent) agents.elementAt (index) ;
msg.performative = « indiquent » ;
msg.content = agent.name ;
msg.receiver = msg.sender ;
msg.sender = nom ; le facilitant d'exposition de // est expéditeur des msg
la construction de // un message d'anwser et l'envoient à l'agent
d'expéditeur
trace (« facilitant : Recommandé » + agent.name +
« à » + msg.receiver +
« pour » + article + « \ n ") ;
sender.ciaEventFired (nouveau CIAgentEvent (ceci, msg)) ;

} autrement {
trace (« facilitant : il n'y a aucun agent annonçant » +
msg.content + « \ n ") ;
}
retour ;
}
trace (« facilitant : acheminement » + msg.performative +
« message de » + msg.sender + « à » +
msg.receiver + « \ n ") ;
itinéraire de // le message à l'agent de récepteur
Récepteur de CIAgent = (CIAgent) allAgents.get (msg.receiver) ;
si (récepteur != nulle) {
receiver.ciaEventFired (nouveau CIAgentEvent (ceci, msg)) ;
} autrement {
// devrait indiquer à expéditeur que le récepteur ne pourrait pas être
trouvé ?
trace (« facilitant : récepteur » +
msg.receiver + « est inconnu ! \ n ") ;
}}

CIAgentMessage

La classe de CIAgentMessage est employée comme objet d'argument dans un


CIAgentEvent. C'est fondamentalement une collecte des données qui correspond aux fentes
principales de message de KQML. La classe a deux constructeurs, avec la seule différence
être celui-là permet les spécifications de la langue et de l'ontology, et l'autre ne fait pas.
Chaque CIAgentMessage doit spécifier un performative, une corde contente, le nom d'agent
d'expéditeur, et le nom d'agent de récepteur. Dans une transaction d'acheteur/vendeur ce
seraient les noms des agents d'acheteur et de vendeur, quoique les messages soient conduits
par le FacilitatorAgent. Le paramètre de replyWith indique l'objet de récepteur employer
cette corde dans la fente d'inReplyTo dans le message de réponse. Tous les paramètres sont
publiquement évidents, bien qu'ils pourraient avoir été protégés en les rendant privés ou avoir
été protégés et en fournissant des méthodes d'accédant. EventObjects sont habituellement
immuable, ainsi ce serait l'approche désirée dans une vraie application.

classe publique CIAgentMessage {

Corde performative ;
Contenu de corde ;
inReplyTo de corde ;
Langue de corde ;
Ontology de corde ;
Récepteur de corde ;
LY_SII __ 234 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

replyWith de corde ;
Expéditeur de corde ;

CIAgentMessage (corde Performative, contenu de corde,


Corde InReplyTo, langue de corde,
Ontology de corde, récepteur de corde,
Corde ReplyWith, expéditeur de corde) {
performative = Performative ;
contenu = contenu ;
inReplyTo = InReplyTo ;
langue = langue ;
ontology = Ontology ;
récepteur = récepteur ;
replyWith = ReplyWith ;
expéditeur = expéditeur ;
}

CIAgentMessage (corde Performative, contenu de corde,


Corde InReplyTo, récepteur de corde,
Corde ReplyWith, expéditeur de corde) {
performative = Performative ;
contenu = contenu ;
inReplyTo = InReplyTo ;
récepteur = récepteur ;
replyWith = ReplyWith ;
expéditeur = expéditeur ;
}

affichage de vide de public () {


System.out.println (« performative : » + performative + « \ n » +
« contenu : » + contenu + « \ n » +
« inReplyTo : » + inReplyTo + « \ n » +
« langue : » + langue + « \ n » +
« ontology : » + ontology + « \ n » +
« récepteur : » + récepteur + « \ n » +
« replyWith : » + replyWith + « \ n » +
« expéditeur : » + expéditeur + « \ n ") ;
}
}

BuyerAgent

La classe de BuyerAgent contient la fonctionnalité basse pour tous les acheteurs. Les
membres principaux de données incluent le wishList, un vecteur des articles que l'agent veut
acheter et des prix d'achat désirés ; l'inventaire, qui contient tous les articles le BuyerAgent a
acheté ; et les négociations, une table de brouillage des négociations en marche. () La
méthode de processus est employée pour enregistrer le BuyerAgent avec le facilitant global,
pour initialiser le wishList avec des exemples des objets de BasicNegotiation, et pour
commencer le fonctionnement du fil de l'agent. La méthode de course () fournit le corps du fil
de l'agent. Elle va dormir pendant 5 secondes, et puis s'il a toujours des articles sur son
wishList et aucune négociation n'est en marche, elle prend le premier article outre du wishList
et donne un coup de pied au loin une négociation en demandant au facilitant de recommander
un vendeur pour l'article. Elle fait ceci d'abord en instanciant un objet de CIAgentMessage et
en plaçant les fentes convenablement, et ensuite using elle comme argument pour un nouvel
objet de CIAgentEvent. Quand le BuyerAgent s'est inscrit au facilitant, le facilitant s'est
ajouté à la liste de CIAgentEventListener du BuyerAgent. Ainsi, appeler la méthode de
LY_SII __ 235 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

notifyCIAgentEventListener () a comme conséquence le facilitant recevant une copie du


CIAgentMessage.

Le facilitant répondra au BuyerAgent en envoyant un CIAgentMessage. Tous tels messages


sont reçus par () la méthode ciaEventFired.

la classe publique BuyerAgent prolonge CIAgent {

Msg de CIAgentMessage ; message courant de // étant traité


Courant de BasicNegotiation ;

wishList de vecteur = nouveau vecteur () ;

BasicNegotiation en suspens = nulle ; vendeur de attente d'article de //

Inventaire de table de brouillage = nouvelle table de brouillage () ;


articles de // que nous avons achetés
long = 0 totalSpent ; argent total de // dépensé

Négociations de table de brouillage = nouvelle table de brouillage () ;


histoire de transaction de //
la clef de // est l'identification d'article

BuyerAgent public () {nom = « acheteur » ; }

BuyerAgent public (nom de corde) {superbe (nom) ; } ;

le vide de public initialisent () {} ;

processus vide de public () {


initialiser () ; appel de // tout code d'initialisation spécifique
arrêté = faux ;
le début de // un fonctionnement de fil et envoient à message sec de chaque
intervalle
trace (nom + « : processus () \ n ") ;
msg = nouveau CIAgentMessage (« registre : », nom, nulle, nulle,
nulle, nulle) ;
CIAgentEvent e = nouveau CIAgentEvent (ceci, msg) ;
FacilitatorAgent.register (e) ; // ajoutent cet agent au facilitant

// ajoutent des articles pour acheter sur la liste d'objectifs


wishList.addElement (nouveau BasicNegotiation (« guitare », 100)) ;
wishList.addElement (nouveau BasicNegotiation (« tambours », 200)) ;
wishList.addElement (nouveau BasicNegotiation (« guitare », 100)) ;
runnit = nouveau fil (ceci) ;
runnit.start () ;
}
arrêt de vide de public () {
trace (nom + « : arrêté \ n ") ;
arrêté = rectifier ;
runnit.stop () ;
}

méthode de // d'interface praticable (de fil)


vide de public couru () {
tandis que (== arrêté faux) {
essai {
Thread.sleep ((longs) 5 * 1000) ; // viennent vivant toutes les 5 sec
si (traceLevel > 0) trace (nom + « : active \ n ") ;
LY_SII __ 236 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

}
crochet (InterruptedException e)
{
// s'est interrompu
}
si ((wishList.size () > 0) && (en attendant la nulle de ==)) {
courant = (BasicNegotiation) wishList.firstElement () ;
en attendant = courant ; // nous avons une négociation en attente
wishList.removeElementAt (0) ;
trace (le nom + « regarde pour acheter » + current.offer.item +
« \ n ") ;
msg = nouveau CIAgentMessage (« recommander-un », current.offer.item,
annuler, le « vendeur », current.offer.item, nom) ;
CIAgentEvent e = nouveau CIAgentEvent (ceci, msg) ;
notifyCIAgentEventListeners (e) ; auditeurs de signal de //
}
trace (le nom + « a acheté » + inventory.size () +
« articles pour » + totalSpent + « \ n ") ;
}

}

} ;

La méthode ciaEventFired est la méthode du BuyerAgent pour recevoir des messages


d'autres agents dans le marché. Deux niveaux de l'information de découverte sont soutenus
dans l'application de marché, un niveau récapitulatif de 0, et des détails de niveau de 1. Si
des détails sont désirés, un message de trace est affiché. Après le CIAgentMessage est extrait
à partir du CIAgentEvent et la méthode de processMessage () s'appelle, passant le message
comme paramètre.

vide de public ciaEventFired (CIAgentEvent e) {


si (traceLevel > 0) {
trace (nom + « : CIAgentEvent a reçu par » + nom +
« de » + e.getSource () + « avec des args » +
e.getArgObject ()) ;
}
Arg d'objet = e.getArgObject () ;
msg = arg (de CIAgentMessage) ;
si (traceLevel > 0) msg.display () ; contenu de message d'exposition de //
processMessage (msg) ;
}

La méthode de processMessage () est cheval de labour du BuyerAgent. Il y a plusieurs


conditions qu'il doit détecter et manipuler. Le premier est quand le facilitant répond à la
demande de recommandation. Ce message est un dire performative et le contenu est le nom
d'un SellerAgent qui a annoncé l'article désiré. Dans ce cas-ci, le vendeur est demandé (par le
facilitant) combien il veut pour l'article.

Autrement, le message doit contenir une offre ou un contre-offre d'un vendeur. D'abord un
exemple d'une offre est créé. Une offre contient un triplet du nom d'article, de la marque
d'article, et du prix d'offre, avec le nom du vendeur faisant la proposition. Le constructeur
LY_SII __ 237 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

d'offre prend le CIAgentMessage comme paramètre et extrait l'information à partir des fentes
d'expéditeur et de contenu de message. Maintenant qu'une offre existe, nous devons
déterminer si c'est la première offre d'une nouvelle négociation ou si c'est un contre-offre dans
une négociation continue. Si un objet de BasicNegotiation existe dans la table de brouillage
de négociation avec la même marque d'article contenue dans l'offre, l'objet de
BasicNegotiation est mis à jour en passant la dernière offre suivre la méthode de newOffer ().
Si l'identification d'article n'est pas dans la table de brouillage, ce doit être la première offre
du SellerAgent. Le BuyerAgent prend la négociation en attente, lui fait le courant, place en
attendant pour annuler, et place la négociation sur la liste active.

L'offre peut contenir un de trois performatives : faire-offrir, accepter-offrir, ou rejeter-offrir.


Si le message est accepter-offrir, la vente est reconnue en envoyant un message de dire de
nouveau au vendeur, l'article est mis dans l'inventaire des achats, la négociation est enlevée de
la liste active, l'argent dépensé est ajouté à l'étiquette, et le message est envoyé. La
négociation en attente est également placée pour annuler, de sorte que la prochaine fois que le
BuyerAgent se réveille, elle puisse commencer une nouvelle négociation s'il restent des
articles sur son wishList. Ce comportement est une décision de conception arbitraire. Nous
pourrions juste comme facilement avoir décidé de retirer immédiatement une autre
négociation le wishList, mais nous avons choisi cette conception pour permettre aux
transactions d'être plus étendues à temps.

Une autre alternative est que le vendeur rejette la dernière offre. Dans notre conception, ceci
finit les négociations. De nouveau, c'est une décision arbitraire, mais nous avons voulu rendre
explicite quand le vendeur décide de finir une négociation et de ne pas avoir l'essai de
BuyerAgent pour faire des contre-offres pour prolonger la négociation. Le BuyerAgent
nettoie la négociation de la liste et des endroits actifs il en arrière sur le wishList ainsi il peut
essayer encore en demandant au facilitant de recommander un autre vendeur pour l'article.

La troisième alternative est que le vendeur fait une proposition et le BuyerAgent doit
négocier using (quoi encore ?) négocier () la méthode. La sophistication de la logique de
traitement dans cette méthode est le point primaire de différentiation entre notre BuyerAgent
de base, le BetterBuyerAgent, et les classes de BestBuyerAgent.

processMessage vide de public (msg de CIAgentMessage) {

le facilitant de // a trouvé un vendeur pour l'article


si (msg.sender.equals (« facilitant ")) {
si (msg.performative.equals (« dire ")) {
l'OK de //, demandent à vendeur ce qu'il veut pour l'article
Réponse de CIAgentMessage =
nouveau CIAgentMessage (« demander », pending.offer.item, msg.replyWith,
msg.content, pending.offer.item, nom) ;
CIAgentEvent e = nouveau CIAgentEvent (ceci, réponse) ;
notifyCIAgentEventListeners (e) ; // répondent par le facilitant
retour ;
}
}

le vendeur de // est niant lui a tous les articles à vendre


// ceci est une réponse à l'« demandent »
si (msg.performative.equals (« nier ")) {
tracer (nom + « : Le vendeur a nié notre `demandent' sur » +
msg.content + « \ n ") ;
LY_SII __ 238 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

wishList.addElement (en attendant) ; // a remis sur la liste d'objectifs


en attendant = nulle ; essai de // encore
retour ;
}

Offre d'offre = nouvelle offre (msg) ; //


trace (nom + « : Offre de » + offer.sender + « : le contenu est » +
offer.item + « » + offer.id + « » + offer.price + « \ n ") ;

si (negotiations.containsKey (offer.id)) {
courant = (BasicNegotiation) negotiations.get (offer.id) ;

} autrement {
courant = en suspens ; // obtiennent la négociation en suspens
en attendant = nulle ; // aucuns articles en suspens
negotiations.put (offer.id, courants) ; endroit de // sur la liste active
}
current.newOffer (offre) ; mise à jour de // l'objet de négociation

le vendeur de // a convenu et la transaction de ventes est complète


l'article de transfert de // à l'acheteur, vendeur accepte notre dernière
offre
si (msg.performative.equals (« accepter-offrir ")) {
tracer (nom + « : OK -- vente de l'article » + offer.item +
« avec l'identification » + offer.id +
« au prix de » + offer.price + « est complet \ n ") ;
Réponse de CIAgentMessage =
nouveau CIAgentMessage (« dire », msg.content, msg.replyWith,
msg.sender, offer.item, nom) ;
trace (nom + « : » + offer.item + « acheté ! \ n ") ;
inventory.put (offer.id, courants) ;
negotiations.remove (offer.id) ;
+= totalSpent offer.price ;
CIAgentEvent e = nouveau CIAgentEvent (ceci, réponse) ;
notifyCIAgentEventListeners (e) ; // répondent par le facilitant
}

le vendeur de // fait une contre- proposition


// nous pouvons, faire une contre- offre, les accepter, ou le rejet
si (msg.performative.equals (« faire-offrir ")) {
négocier (offre, msg) ;
}

le vendeur de // rejette notre dernière offre - aucune contre- offre


si (msg.performative.equals (« rejeter-offrir ")) {
tracer (nom + « : » + msg.sender + « a rejeté notre dernière offre ") ;
negotiations.remove (offer.id) ; // enlèvent de la liste active
wishList.addElement (courant) ; // a remis sur la liste d'objectifs
}
}

Le BuyerAgent de base vérifie si le prix d'offre du vendeur est inférieur au strikePrice, qui
est le prix maxima désiré. S'il est, il accepte l'offre en faisant écho le message de nouveau au
vendeur, supposant que le SellerAgent acceptera la proposition qu'il a juste faite. Si le prix
d'offre est plus élevé que l'acheteur est disposé à payer, le BuyerAgent doit faire une contre-
proposition (se rappeler, dans cette conception, seulement le vendeur peut rejeter une offre).
L'agent de base taille simplement $25 outre du prix du vendeur et fait une proposition dans les
LY_SII __ 239 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

espoirs que le vendeur acceptera. Plus tard en ce chapitre, nous explorerons les stratégies plus
sophistiquées employées par nos agents de BetterBuyer et de BestBuyer.

// cette méthode doit être dépassé pour fournir le remplacement


stratégies de négociation de //
le vide négocient (offre d'offre, msg de CIAgentMessage) {

si (offer.price < current.strikePrice) {


// acceptent l'offre -- en la répétant au vendeur
Réponse de CIAgentMessage =
nouveau CIAgentMessage (« faire-offrir », msg.content,
msg.replyWith, msg.sender, offer.item, nom) ;
CIAgentEvent e = nouveau CIAgentEvent (ceci, réponse) ;
notifyCIAgentEventListeners (e) ; // répondent par le facilitant
} autrement {
// font une contre- proposition
current.lastOffer = offer.price - 25 ; //
Réponse de CIAgentMessage =
nouveau CIAgentMessage (« faire-offrir », offer.item + « » +
offer.id + « » + current.lastOffer, offer.item,
msg.sender, offer.item, nom) ;
CIAgentEvent e = nouveau CIAgentEvent (ceci, réponse) ;
notifyCIAgentEventListeners (e) ; // répondent par le facilitant
}
}

SellerAgent

La classe de SellerAgent contient la fonctionnalité basse pour tous les vendeurs. Les
membres principaux de données incluent l'inventaire, une table de brouillage des articles que
l'agent veut se vendre et des prix de ventes désirés, et les négociations, une table de
brouillage des négociations en marche. () La méthode de processus est employée pour
enregistrer le SellerAgent avec le facilitant global, initialise l'inventaire avec des exemples
des objets de BasicNegotiation, annonce les articles au facilitant, et pour commencer le
fonctionnement du fil de l'agent. La méthode de course () fournit le corps du fil de l'agent.
Elle va dormir pendant 15 secondes et puis rapporte le statut de son inventaire.

Après que le SellerAgent annonce ses articles au facilitant, il doit attendre un BuyerAgent
éventuel pour entrer en contact avec le facilitant recherchant une recommandation d'un
vendeur probable. Quand le facilitant passe le nom du SellerAgent au BuyerAgent,
l'acheteur envoie un message de demand pour commencer les négociations. Comme le
BuyerAgent décrit plus tôt, le SellerAgent reçoit n'importe quel message comme argument
d'un CIAgentEvent par () la méthode ciaEventFired. La méthode de processMessage ()
interprète le message et détermine la réponse appropriée.

la classe publique SellerAgent prolonge CIAgent {

longue graine privée = 0 ;

Msg de CIAgentMessage ; message courant de // étant traité


Courant de BasicNegotiation ;
long revenu = 0 ; argent total de // gagné
Inventaire de vecteur = nouveau vecteur () ; articles de // que nous
prenons pour la vente
LY_SII __ 240 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Négociations de table de brouillage = nouvelle table de brouillage () ;


histoire de transaction de //
la clef de // est l'identification d'article

SellerAgent public () {nom = « vendeur » ; }

SellerAgent public (nom de corde) {superbe (nom) ; } ;

processus vide de public () {


initialiser () ; appel de // tout code d'initialisation spécifique
arrêté = faux ;
le début de // un fonctionnement de fil et envoient à un message sec de
chaque intervalle
trace (nom + « : processus () \ n ") ;
msg = nouveau CIAgentMessage (« registre : », nom, nulle,
nul, nul, nulle) ;
CIAgentEvent e = nouveau CIAgentEvent (ceci, msg) ;
FacilitatorAgent.register (e) ; // ajoutent l'agent au facilitant

inventory.addElement (nouveau BasicNegotiation (« guitare », 100));

inventory.addElement (nouveau BasicNegotiation (« tambours », 225));

inventory.addElement (nouveau BasicNegotiation (« guitare », 100));

// annoncent tous les articles à vendre


Enum d'énumération = inventory.elements () ;
tandis que (enum.hasMoreElements ()) {
courant = (BasicNegotiation) enum.nextElement () ;
msg = nouveau CIAgentMessage (« annoncer », current.offer.item,
nul, nul, current.offer.item, nom) ;
e = nouveau CIAgentEvent (ceci, msg) ;
notifyCIAgentEventListeners (e) ; observateur intéressé de signal de //
}

runnit = nouveau fil (ceci) ;


runnit.start () ;
}

arrêt de vide de public () {


arrêté = rectifier ;
trace (nom + « arrêté \ n ") ;
runnit.stop () ;
}

méthode de // d'interface praticable (de fil)


vide de public couru () {
tandis que (== arrêté faux) {
essai {
Thread.sleep ((longs) 15 * 1000) ; // viennent vivant toutes les 15 sec
si (traceLevel > 0) trace (nom + « : active \ n ") ;
}
crochet (InterruptedException e)
{
// s'est interrompu
}
trace (le nom + « a » + inventory.size () +
« articles dans l'inventaire. \ n ") ;
trace (le nom + « a gagné » + revenu + « .\ n ") ;
}
LY_SII __ 241 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

vide de public ciaEventFired (CIAgentEvent e) {


si (traceLevel > 0) {
trace (nom + « : CIAgentEvent a reçu par » + nom +
« de » + e.getSource () + « avec des args » +
e.getArgObject () + « \ n ") ;
}
Arg d'objet = e.getArgObject () ;
msg = arg (de CIAgentMessage) ;
si (traceLevel > 0) msg.display () ;
processMessage (msg) ;
}

// produisent d'une identification unique pour un article dans l'inventaire


genId de corde () {
seed++ ;
name+seed de retour ;
}

} ;

La méthode de processMessage () doit prendre soin de deux cas de base. Est d'abord répondre
à une initiale demandent d'un BuyerAgent, et l'autre est de répondre à une offre. Dans le
premier cas, il y a beaucoup de ménage à faire quand un BuyerAgent pose des questions sur
un article. D'abord, il doit vérifier pour voir s'il a toujours les articles désirés l'uns des dans
l'inventaire (comme décrit plus tôt, il pourrait avoir juste vendu dernier). L'assumer a l'article,
il produit alors d'une marque unique d'article en appelant la méthode de genID (). La marque
d'article est le nom du SellerAgent enchaîné avec un nombre entier nonrepeating (par
exemple, Seller1, ou BetterSeller3). Cette marque d'article est alors employée comme clef de
table de brouillage des objets pour d'acheteur et de vendeur négociation. L'objet de
BasicNegotiation de l'article est enlevé de la liste d'inventaire, le membre d'identification
d'article est placé, et le champ de lastOffer est initialisé au prix de vente minimum plus $100.
Le SellerAgent place alors le BasicNegotiation sur les négociations actives énumèrent, et
envoient faire-offrent le message de nouveau au BuyerAgent par le facilitant qui est un
CIAgentEventListener enregistré.

Dans le deuxième cas, il y a une négociation en marche. Le SellerAgent instancie une offre
du CIAgentMessage et recherche la négociation des négociations actives énumèrent, using
l'identification d'article comme clef de table de brouillage. S'il n'y a aucun objet de
BasicNegotiation avec cette identification d'article, elle doit avoir été juste vendue, ainsi le
SellerAgent envoie rejeter-offrent le message au BuyerAgent. Autrement, il met à jour le
BasicNegotioation avec l'offre courante. Après, il détermine si l'acheteur fait une contre-
proposition ou en reconnaissant simplement un précédent accepter-offrir le message. Si c'est
un dire, ce doit être le dernier cas, un article a été juste vendu. Le BasicNegotiation est
enlevé des négociations actives énumèrent, et le prix d'achat est ajouté au total de ventes.
LY_SII __ 242 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Si le performative est faire-offrir, la méthode de négociation () s'appelle pour proposer une


réponse appropriée. Comme dans notre BuyerAgent, cette méthode est dépassée par des
sous-classes pour présenter les niveaux de la sophistication croissants.

processMessage vide de public (msg de CIAgentMessage) {

si (msg.performative.equals (« demander ")) {


Ficeler l'article = le msg.content ;
// voient si nous faisons laisser n'importe quels articles
si (itemInInventory (article)) {
début de // une nouvelle négociation
Corde identification = genId () ;
courant = removeItemFromInventory (article) ;
current.offer = nouvelle offre (msg.sender, article, identification, 0) ;
current.lastOffer = current.strikePrice + 100 ;
negotiations.put (identification, courantes) ;
Réponse de CIAgentMessage =
nouveau CIAgentMessage (« faire-offrir », article + « » + identification +
« » +
current.lastOffer, msg.replyWith, msg.sender, article,
nom) ;
CIAgentEvent e = nouveau CIAgentEvent (ceci, réponse) ;
notifyCIAgentEventListeners (e) ; // répondent par Facil.
} autrement {
// nous en nient ont à vendre
Réponse de CIAgentMessage =
nouveau CIAgentMessage (« nier », l'article, msg.replyWith,
msg.sender, article, nom) ;
CIAgentEvent e = nouveau CIAgentEvent (ceci, réponse) ;
trace (nom + « : nous en nier ont » + article +
« pour se vendre » + msg.sender + « \ n ") ;
notifyCIAgentEventListeners (e) ; // répondent par Facil
}
retour ;
}

Offre d'offre = nouvelle offre (msg) ; //


trace (nom + « : Offre de » + offer.sender + « : le contenu est » +
offer.item + « » + offer.id + « » + offer.price + « \ n ") ;

courant = (BasicNegotiation) negotiations.get (offer.id) ;


si (nulle courante de ==) {
// nous devons avoir vendu cet article --- offre de rejet
Réponse de CIAgentMessage =
nouveau CIAgentMessage (« rejeter-offrir », msg.content,
msg.replyWith, msg.sender, offer.item, nom) ;
CIAgentEvent e = nouveau CIAgentEvent (ceci, réponse) ;
notifyCIAgentEventListeners (e) ; // répondent par Facil
retour ;
}
current.newOffer (offre) ;

l'acheteur de // a fait une proposition, nous peut accepter,


// font une contre- offre, ou le rejet
si (msg.performative.equals (« faire-offrir ")) {
négocier (offre, msg) ;
}

l'acheteur de // a reçu et les nos reconnus accepter-offrent


LY_SII __ 243 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

// enlèvent l'article de la liste active, ajoutent $ au revenu


// évitent la publicité de cet article avec le facilitant
si (msg.performative.equals (« dire ")) {
negotiations.remove (offer.id) ;
revenu += offer.price ;
CIAgentMessage msg2 =
nouveau CIAgentMessage (« éviter la publicité », current.offer.item,
nul, nul, current.offer.item, nom) ;
CIAgentEvent e = nouveau CIAgentEvent (ceci, msg2) ;
notifyCIAgentEventListeners (e) ; auditeurs de signal de //
}

Dans le SellerAgent de base, si le prix d'offre de l'acheteur est au-dessus du prix de vente
minimum désiré, l'offre est immédiatement acceptée et un message est renvoyé au
BuyerAgent. Le BuyerAgent doit reconnaître l'offre avec un message de dire avant que
l'article puisse être enlevé de l'inventaire. Si le BuyerAgent offre moins que le SellerAgent
est disposé à prendre pour l'article, l'offre est rejetée et les négociations sont clôturées en
plaçant l'article en arrière dans l'inventaire (se rappeler, ceci est l'agent de base).

le vide négocient (offre d'offre, msg de CIAgentMessage) {


si (offer.price > current.strikePrice) {
// acceptent
Réponse de CIAgentMessage =
nouveau CIAgentMessage (« accepter-offrir », msg.content,
msg.replyWith, msg.sender, offer.item, nom) ;
CIAgentEvent e = nouveau CIAgentEvent (ceci, réponse) ;
notifyCIAgentEventListeners (e) ;
retour ;
}
si (offer.price < current.strikePrice) {
rejet de //
rejectOffer (offre) ;
retour ;
}
Réponse de CIAgentMessage =
nouveau CIAgentMessage (« faire-offrir », offer.item + « » +
offer.id + « » + current.lastOffer,
msg.replyWith, msg.sender,
offer.item, nom) ;
CIAgentEvent e = nouveau CIAgentEvent (ceci, réponse) ;
notifyCIAgentEventListeners (e) ;
}

// interrompent une négociation (pour quelque raison)


l'article de retour de // à l'inventaire, enlèvent des négociations de la
liste active
rejectOffer vide (offre d'offre) {
Réponse de CIAgentMessage =
nouveau CIAgentMessage (« rejeter-offrir », msg.content,
msg.replyWith, msg.sender, offer.item, nom) ;
CIAgentEvent e = nouveau CIAgentEvent (ceci, réponse) ;
notifyCIAgentEventListeners (e) ;
negotiations.remove (offer.id) ; // aucune autre négociation
current.offer.id = nulle ; l'identification d'offre de // a expiré
inventory.addElement (courant) ; dos d'article d'endroit de // dans
l'inventaire
LY_SII __ 244 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

() La méthode itemInInventory s'appelle par processMessage () en réponse à l'« demandent »


d'un acheteur de voir que le cas échéant les articles de ce type sont disponibles pour se vendre.
Une énumération est employée pour marcher par le vecteur pour examiner chaque article
dans l'inventaire. Un vrai booléen est retourné quand une allumette est trouvée. () La méthode
removeItemFromInventory a une structure semblable à itemInInventory (). Dans ce cas-ci,
bien que, l'article trouvé soit enlevé du vecteur d'inventaire, et l'objet de BasicNegotiation
est retourné au visiteur. Le BasicNegotiation est employé comme support commode pour des
articles d'inventaire.

// renvoie vrai si l'article est en stock


itemInInventory booléen (article de corde) {
Enum d'énumération = inventory.elements () ;
haveItem booléen = faux ;
tandis que (enum.hasMoreElements ()) {
stockItem de BasicNegotiation =
(BasicNegotiation) enum.nextElement () ;
si (stockItem.getItem () .equals (article)) {
le haveItem = rectifient ;
coupure ;
} autrement {
continuer ;
}
}
renvoyer le haveItem ;
}

// renvoie l'article spécifique de l'inventaire


// si nous n'avons aucun article, renvoient la nulle
BasicNegotiation removeItemFromInventory (article de corde) {
Enum d'énumération = inventory.elements () ;
stockItem = nulle de BasicNegotiation ;
tandis que (enum.hasMoreElements ()) {
stockItem = (BasicNegotiation) enum.nextElement () ;
si (stockItem.getItem () .equals (article)) {
inventory.removeElement (stockItem) ;
coupure ;
} autrement {
stockItem = nulle ; // ceci n'est pas lui
continuer ;
}
}
renvoyer le stockItem ;
}

Acheteurs et vendeurs augmentés

Dans cette section, nous décrivons des améliorations à notre acheteur et SellerAgents de base.
Ces agents augmentés prolongent les classes de BuyerAgent et de SellerAgent,
principalement en dépassant les méthodes de négociation (), et en utilisant des informations
supplémentaires au sujet du processus de négociation. Le BetterBuyerAgent et le
BetterSellerAgent emploient la logique hardcoded avec une stratégie légèrement plus
agressive de négociation et ne justifient aucune autre discussion. Le BestBuyerAgent et le
LY_SII __ 245 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

BestSellerAgent se servent de la classe de RuleBase et si puis des règles pour déterminer


leurs prix et actions pendant les négociations.

Nous nous concentrons d'abord sur la classe de BestBuyerAgent. Comme sous-classe de


BuyerAgent, elle hérite de tout les comportement de base de transaction de ventes.
BestBuyerAgents ont les mêmes articles et prix de wishList que le BuyerAgents. Cependant,
ils se servent des informations supplémentaires dans l'objet de BasicNegotiation et emploient
également un ensemble de règles pour déterminer quelle action pour rentrer le processus de
négociation.

Plusieurs membres additionnels de données sont ajoutés à la classe de BestBuyerAgent.


Ceux-ci incluent le Rb, l'exemple de RuleBase qui est initialisé quand dépassés initialisent ()
la méthode sont appelés de la méthode de démarrage de processus de BuyerAgent (), et
plusieurs RuleVariables utilisé dans le RuleBase. La méthode d'initializeBestBuyerRuleBase
() définit l'offerDelta, une variable de rendement intermédiaire, et la diffusion et le firstOffer,
qui sont des variables antécédentes. La variable de diffusion est la différence entre le prix
offert courant et demander ou le strikePrice. La variable de firstOffer est une booléenne
employée pour signaler quand une négociation commence juste.

En plus des règles qui calculent la valeur de l'offerDelta, il y a deux ou trois règles d'action.
Ces règles ont EffectorClauses en tant que leur conséquent. Quand ils mettent le feu, ils
appellent () la méthode effectrice définie dans BestBuyerAgent. Comme décrit plus tôt, les
agents d'acheteur peuvent seulement faire des propositions au SellerAgents. Si le chaînage
avant inferencing décide de faire une contre- proposition ou de l'accepter, () la méthode
effectrice s'appelle par la règle d'action, et le message est envoyé directement de la mise à feu
de règle. Une conception alternative, si le RuleBase ne soutenait pas des règles d'effecteur ou
d'action, serait de calculer l'offerDelta et de faire déterminer la logique hardcoded examiner la
valeur et quelle action pour exécuter.

La stratégie de négociation est plus complexe que dans le BuyerAgent et le


BetterBuyerAgent, mais ce n'est pas un chef d'oeuvre du marchandage par aucun bout droit
de l'imagination. Si l'offre du vendeur est à moins de $25 du strikePrice, l'offre est acceptée.
Autrement, selon la taille de la diffusion, des montants croissants sont déduits de l'offre du
vendeur et une contre- proposition est faite. L'intention était de montrer comment la
connaissance de domaine sous forme de règles pourrait être integrated dans un marché d'agent
intelligent. Dans une application commerciale, une base plus sophistiquée de règle serait
employée.

-
LY_SII __ 246 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Après, nous nous concentrons sur la classe de BestSellerAgent. Beaucoup de sa structure


reflète les changements à BestBuyerAgent. Comme sous-classe de SellerAgent, elle hérite
de tout les comportement de base de transaction de ventes. BestSellerAgents ont les mêmes
articles et prix d'inventaire que le SellerAgents.

Des membres additionnels de données sont ajoutés à la classe de BestSellerAgent. Ceux-ci


incluent le Rb, un exemple de RuleBase, et plusieurs RuleVariables utilisé dans le
RuleBase. La méthode d'initializeBestSellerRuleBase () définit l'offerDelta, une variable de
rendement intermédiaire, et la diffusion et le firstOffer, qui sont des variables antécédentes. La
diffusion est la différence entre le prix offert courant et demander ou le strikePrice. FirstOffer
est un booléen employé pour signaler quand une négociation commence juste.

Comme le BestBuyerAgent, les règles d'action d'utilisations de BestSellerAgent pour


envoyer directement des réponses. Comme décrit plus tôt, la commande de SellerAgents les
négociations, et peut décider d'accepter une offre, de faire une contre- proposition, ou de
rejeter l'offre et d'interrompre des négociations avec le BuyerAgents. Le RuleBase dans le
BestSellerAgent est plus complexe que le BestBuyerAgent pour manipuler ces cas
additionnels et actions associées.

La méthode première de négociation () remet à zéro la base et puis les calculs de règle et
place la valeur de la diffusion. Un essai spécial est ajouté pour voir si l'offre précédente et
l'offre courante sont identiques. Si oui, ce signifie que l'acheteur a accepté l'offre, et la valeur
de la diffusion soit placée à >50 pour forcer accepter-offrent l'action. Évidemment, accepter
pourrait s'être appelé directement, mais dans ce cas-ci, nous voulons démontrer le mécanisme
d'action de RuleBase.
LY_SII __ 247 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Dans l'essai avec le BestBuyerAgent et le BestSellerAgent basés sur les règles, nous avons
constaté que les agents pourraient entrer dans les boucles infinies négociant dans les deux
sens. Le membre d'itération a été ajouté à la classe de BasicNegotiation pour maintenir ceci
et pour court-circuiter ces boucles quand elles se produisent. Cependant, dans de vrais
systèmes multiagent il y a toujours la possibilité de produire des orages ou des impasses de
message entre les agents. Des mécanismes de sécurité pour détecter et éviter ces conditions
doivent être inclus dans des réalisations commerciales.

Un cycle d'inférence de chaînage avant est exécuté. La quantité de la contre- offre (le cas
échéant) est calculée, et l'action ou l'effecteur () que des méthodes s'appellent pendant le
processus inferencing. () La méthode effectrice contient la logique pour interpréter la valeur
de l'offerDelta RuleVariable, selon lequel l'effecteur a été appelé.

--
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

Application de marché

Le GUI d'application de marché de CIAgent a été développé using l'instrument de


développement visuel de Symantec Café, la version 1.0d. Ce code produit du code de Java
1.0.2-level et emploie le modèle d'événement 1.0.2. Cependant, ce code a été examiné et
course sous la machine virtuelle de Java 1.1.1. Comme mentionné dans les applications plus
tôt, ce code n'est pas central à l'application d'agent intelligent développée en ce chapitre.

Nous précisons une partie de la logique unique d'application priée pour mettre en application
le marché de CIAgent. Les sept CIAgents, le FacilitatorAgent, et les trois types de
l'acheteur et du SellerAgents sont des membres de données de la classe d'application de
marché.

-
LY_SII __ 248 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Discussion

Dans cette section, nous décrivons le raisonnement derrière certaines des décisions de
conception prises tout en développant l'application de marché.

Peut-être la première décision que nous avons prise était le choix de l'utilisation KQML-
comme CIAgentMessage au lieu vrais » messages de KQML d'analyse des « . Notre
sentiment était que nous pourrions explorer plusieurs des questions des communications
d'agent using la classe de CIAgentMessage sans obtenir embourbés dans les complexités
d'analyser des rapports de KQML. Nous avons traité les issues liées à l'utilisation des
performatives de KQML et à l'entretien des états de conversation entre le facilitant et les
agents d'acheteur et de vendeur.

Notre prochaine décision était de concevoir notre propre marieur ou facilitant. Il y en a


plusieurs facilitants de KQML dans la littérature et dans des applications mises en place et des
produits. Notre facilitant relativement simple a fourni toute les fonction que nous avons eue
besoin pour cette application sans devenir trop complexes. Avoir le séjour de facilitant « dans
la boucle » entre l'acheteur et le vendeur était une autre décision de conception qui pourrait
facilement être allée l'autre manière. Nous nous sommes sentis que les notations de trace
fournies par un facilitant centralisé seraient utiles. Cependant, dans un système avec beaucoup
d'agents, le facilitant comme intermédiaire serait un goulot d'exécution. Car un marieur qui
présente les agents et puis sort de la manière, le facilitant fournirait la majeure partie de la
fonction tandis que n'étant pas une responsabilité d'exécution.

Un autre choix fondamental de conception employait les fils de BuyerAgent et de


SellerAgent pour donner un coup de pied au loin des transactions, mais pour ne pas effectuer
le traitement d'événement. Dans une application multiagent commerciale, il vaudrait mieux de
faire se produire la signalisation de message synchroniquement mais le traitement de
messages se produisent asynchrone. C'est une conception plus compliquée parce que vous
devez s'inquiéter des événements de extrémité-de-événement-traitement de signalisation. Mais
elle permettrait une sortie plus élevée avec un grand nombre d'agents et également
améliorerait la robustesse du système global, parce qu'un agent simple de pokey ne collerait
pas vers le haut des travaux.

Également, comme peut avoir déjà été évident, nous n'avons pas dépensé des stratégies
optimales se développantes beaucoup de temps de négociation pour le BuyerAgent et le
SellerAgents. Comme nous avons énoncé, notre but était de montrer le progrès des agents de
hardcoded simple à basé sur les règles plus complexe, et de montrer les avantages de la
sophistication accrue dans l'exécution de ligne de fond des algorithmes. On pourrait
facilement imaginer hardcoding une meilleure stratégie que nous avons employé dans le
BestBuyerAgent et le BestSellerAgent. Mais le point était d'illustrer la puissance et la
flexibilité d'utiliser un moteur basé sur les règles pour commander les négociations et pour
prendre directement les mesures suivre () les méthodes effectrices.
LY_SII __ 249 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Une simplification utilisée dans la stratégie de négociation était que le SellerAgent a enlevé
l'article de l'inventaire dès qu'un acheteur s'est enquis de. Ceci a empêché deux négociations
continuant en même temps pour le même article. Tandis que dans un vrai marché cette sorte
de chose entre toute les heure, la complexité supplémentaire de cette approche semble être
supérieure aux avantages.

Résumé

En ce chapitre, nous avons développé une application électronique de marché using sept
CIAgent-avons basé les agents intelligents. Les questions principales incluent :

• L'application de marché s'est composée d'un FacilitatorAgent simple, un ou


plusieurs BuyerAgents, et un ou plusieurs SellerAgents. Il y avait trois types d'acheteur
et de SellerAgents using des stratégies de négociation de plus en plus meilleures.
• Toutes les communications entre les acheteurs et les vendeurs sont passées par le
facilitant employant KQML-comme des objets appelés CIAgentMessages. Ces le
message incluent KQML-performatives tel que le registre, annoncent, évitent la
publicité, le recommandent-un, demandent, et indiquent. Les performatives
spécifiques à l'application ont inclus faire-offrent, accepter-offrent, et rejeter-offrent.
• Le protocole de négociation de ventes a été défini, donnant au SellerAgent plus de
contrôle du processus. La classe de BasicNegotiation a été présentée pour encapsuler
les détails de chaque transaction. Une offre comprend le nom d'agent, l'article, une
identification d'article unique produite par le SellerAgent au début d'une négociation,
et le prix d'offre.
• Le BestBuyerAgent et le BestSellerAgent ont employé les classes de RuleBase, de
règle, et de clause du chapitre 4, avec des perfectionnements tels qu'EffectorClauses
pour représenter la stratégie de négociation sous la forme de règle, et envoient
directement les messages de réponse using des règles d'action.

Exercices

1. Écrire un analyseur simple de KQML pour le sous-ensemble de la langue soutenue


par la classe de CIAgentMessage.
2. Modifier ou prolonger le comportement de CIAgent FacilitatorAgent de sorte que
les acheteurs et les vendeurs communiquent directement les uns avec les autres après
qu'une allumette soit faite. Quels sont les avantages et les inconvénients de ceci
s'approchent-ils ?
3. Quelle connaissance de domaine serait nécessaire pour avoir un BuyerAgent que
vous feriez confiance pour du votre argent ? Quelles qualifications de raisonnement
seraient exigées ? Combien difficile serait-il de construire ce BuyerAgent ?
‫‪LY_SII‬‬ ‫__‬ ‫‪250‬‬ ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢْ‬
LY_SII __ 251 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Chapitre 11
Environnements d'agent du Java-Based
Nous concluons avec un examen d'autre les environnements Java-basés d'agent.
Tandis que ce n'est pas une liste approfondie d'agents Java-basés, il fournit une
vue d'ensemble de ce qui se produit dans l'industrie, d'où le foyer est, et ce qui
plus doit être fait pour apporter à Java les agents intelligents à leur pleine
capacité.

Ferrets

Les ferrets sont les agents autonomes Java-basés développés par IBM. Ils fournissent les
possibilités de base exigées pour la mobilité. Chaque ferret a le nom unique d'a globalement -.
Un itinéraire de voyage est employé pour spécifier les destinations auxquelles l'agent doit
voyager et quelles mesures il doit prendre à chaque endroit. Pour qu'un ferret coure sur un
système particulier, ce système doit courir une application de centre serveur de ferret. Ceci
fournit un environnement plate--neutre d'exécution pour le ferret. L'établi de ferret inclut un
directeur de sécurité configurable de Java, qui peut limiter l'activité d'un ferret sur le système
de la même manière que le directeur de sécurité de défaut est habitué pour limiter les activités
d'un applet.

Les ferrets peuvent communiquer using un whiteboard qui permet à des agents de collaborer
et de partager l'information asynchrone. Le dépassement synchrone et asynchrone de message
est également soutenu pour la communication de ferret. Des ferrets sont coulés using la
fabrication en série ou l'externalisation standard de Java. On fournit un chargeur de classe
d'agent de réseau qui permet le jet et l'état du bytecode d'un ferret au voyage à travers un
réseau.

Tandis que les ferrets ne sont pas en soi intelligents, ils sont écrits dans Java et peuvent être
prolongés avec le code intelligent l'un des de Java que nous avons fourni dans les chapitres
premiers de ce livre. Pour plus d'information sur des ferrets, voir le
http://www.trl.ibm.co.jp/aglets/.

Technologie d'agent de logiciel de ftp

La technologie d'agent de logiciel de ftp est logiciel Java-basé conçu pour contrôler les
réseaux hétérogènes à travers l'Internet using la technologie d'agent. Les agents sont
autonomes et mobiles, et peuvent se déplacer à n'importe quel système dans le réseau qui a un
répondeur d'agent installé. Pendant que l'agent se déplace de système/système, ses tâches
peuvent changer, selon l'environnement du système qu'il visite.

Les agents peuvent agir l'un sur l'autre avec d'autres agents ou avec l'utilisateur, comme
nécessaire. Mais les agents de ftp n'exigent aucun utilisateur interaction-basé sur la
technologie de « poussée », ils peuvent se déplacer de système/système, répondre aux
événements, et effectuer des tâches selon des critères prédéfinis par l'utilisateur. Un directeur
d'agent est responsable de lancer l'agent.
LY_SII __ 252 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

En raison du centre commercial et réseau-central des agents de logiciel de ftp, il y a une


emphase forte sur la sécurité. En plus du modèle de sécurité standard de Java, le logiciel
fournit l'authentification par mot de passe d'utilisateur, sécurité de RC2 (algorithmes
exportables de la RSA), et de DES (norme de chiffrage de Digitals).

Deux applications sont disponibles employant cette technologie d'agent : Auditeur d'IP et
distributeur d'IP. L'application d'auditeur d'IP emploie des agents pour collecter le matériel, le
logiciel, et les informations de configuration pour des systèmes dans un réseau. L'application
de distributeur d'IP distribuera le logiciel et exécutera sur option des fonctions sur des
systèmes de cible dans un réseau.

La littérature sur la technologie d'agent de logiciel de ftp décrit les agents comme
« intelligents, » mais l'intelligence a été défini dans ce contexte comme ayant la capacité de
transporter des données et des instructions, d'analyser l'environnement de cible, et de répondre
aux conditions a trouvé au système de cible. Il ne semble pas se rapporter à inferencing basé
sur les règles ou à machine apprenant comme décrit plus tôt dans ce livre. Des détails sur des
agents de logiciel de ftp peuvent être trouvés chez http:/www.ftp.com/.

Voyager

Voyager, d'ObjectSpace, Inc., est un courtier de demande d'objet agent-augmenté écrit


entièrement dans Java. Un CORPS ROND fournit les possibilités pour créer des objets sur un
système à distance et pour appeler des méthodes sur ces objets. Voyager augmente le CORPS
ROND traditionnel avec des possibilités d'agent.

Les agents de Voyager ont la mobilité et l'autonomie qui est fournie dans la classe basse,
agent. Un agent peut se déplacer d'un endroit à l'autre et peut laisser une adresse d'expédition
avec un « secrétaire » de sorte que de futurs messages puissent être expédiés à son nouvel
endroit. Des agents spécialisés, appelés Messengers, sont employés pour fournir des
messages. Les messages peuvent être synchrones, à sens unique (semblable à asynchrone), ou
un futur, qui sont asynchrones mais renvoient un texte d'attente qui peut être employé pour
rechercher une valeur de retour à un temps postérieur.

Comme des ferrets, l'itinéraire d'un agent instruit l'agent quant à quelles opérations il doit
effectuer à chaque endroit. En outre, comme des ferrets, Voyager emploie la fabrication en
série pour couler l'état de l'agent pendant que l'agent se déplace de l'endroit à l'endroit.
Voyager inclut également un directeur de sécurité qui peut être employé pour limiter les
opérations qu'un agent peut effectuer. À l'avenir, Voyager sera augmenté avec un modèle de
sécurité basé sur les règles qui complète le mécanisme de sécurité standard de Java.

Les agents de Voyager eux-mêmes ne sont pas en soi intelligents. Ils ne contiennent un
moteur d'inférence, des réseaux neurologiques, ou aucune autre technologie d'intelligence
artificielle. Mais, comme des ferrets, ils peuvent être augmentés avec des techniques l'unes
des d'intelligence artificielle décrites dans ce livre. Des informations supplémentaires sur
Voyager peuvent être trouvées chez http://www.objectspace.com/.

Odyssée
LY_SII __ 253 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

Le système d'agent de l'odyssée de la magie générale est un ensemble de classes de Java qui
soutiennent des agents d'odyssée, des systèmes d'agent, et des endroits. Des agents sont créés
par subclassing la classe d'agent ou la classe d'ouvrier. Chaque agent s'exécute en son propre
fil de Java, et peut voyager d'un endroit à l'autre. À chaque nouvel endroit, cependant, le fil
doit être remis en marche. Un exemple de la classe d'endroit définit l'environnement
d'exécution pour un agent sur chaque système hôte. Un ouvrier est une spécialisation de la
classe d'agent. Un ouvrier contient un itinéraire qui est un ensemble de tâches et de
destinations. L'ouvrier exécute une tâche à chaque centre serveur de destination. Le système
d'agent d'odyssée fournit également des classes de soutien telles que le billet, qui définit
comment et où un agent peut voyager, et la pétition, qui identifie d'autres agents avec qui
l'agent souhaite communiquer.

L'odyssée est écrite entièrement dans Java et exige JDK 1.1. Elle emploie le RMI en tant que
son transport, mais peut également être configurée pour employer le modèle composant
distribué de Microsoft (DCOM) ou le protocole d'Inter-CORPS ROND d'Internet. Un CORPS
ROND n'est pas inclus en tant qu'élément du système d'odyssée, mais se fonde à la place sur
l'installation de VisiBroker de Visigenic pour Java. Même si DCOM ou IIOP est employé,
l'enregistrement de RMI est encore nécessaire par le trouveur, qui détermine où un endroit
particulier est localisé. À la différence des ferrets ou de Voyager, l'odyssée n'inclut pas son
propre SecurityManager, et se fonde seulement sur les mécanismes de sécurité standard de
Java.

Comme les autres agents décrits en ce chapitre, des agents d'odyssée ne sont pas dotés
d'intelligence, mais, parce qu'ils sont écrits dans Java, peuvent être augmentés par les
techniques d'intelligence artificielle démontrées par le CIAgents dans ce livre. Pour plus
d'information sur des agents d'odyssée, voir le http://www.genmagic.com

JATLite

Le calibre Lite (JATLite) d'agent de Java est un ensemble de paquets légers de Java étant
développés à l'Université de Stanford qui peut être employée pour établir les systèmes
multiagent. C'est une architecture en couches qui fournit un protocole de transmission
différent à chaque couche. La couche la plus bottom-most fournit seulement les classes
abstraites qui peuvent être mises en application pour soutenir n'importe quel protocole de
transmission. La couche basse emploie le TCP/IP comme mécanisme de communication et
permet aux applicateurs d'agent de définir le protocole de message. La couche de KQML
fournit l'appui pour les agents qui emploient des messages de KQML. La couche de routeur
ajoute le cheminement de message qui permet à des messages d'être alignés pour les agents
qui ne sont pas en activité quand le message est reçu par le routeur.

Le cadre de JATLite est prévu pour le dactylographier-message se développant, les agents


autonomes qui communiquent using un protocole peer-to-peer. Le dépassement synchrone et
asynchrone de message sont soutenus. Des messages peuvent être fournis par la queue de vote
ou de message. Le cadre fournit la sécurité additionnelle qui examine le nom et le mot de
passe d'agent pour assurer un raccordement plus bloqué.

Le centre de JATLite est évidemment sur la communication. Comme plusieurs des autres
environnements d'agent discutés en ce chapitre, n'importe quelle intelligence devrait être
fournie par les applicateurs d'agent using des techniques décrites plus tôt dans ce livre. Pour
LY_SII __ 254 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

plus d'information sur JATLite (et son prédécesseur, JAT), voir le


http://java.stanford.edu/java_agent/html.

InfoSleuth

InfoSleuth est un projet de recherche au MCC qui emploie des agents pour trouver,
rechercher, et intégrer des informations des points d'émission hétérogènes. Son architecture
est basée sur les agents autonomes de coopération qui annoncent leurs services et traitent des
demandes de ces services. Elle est basée sur KQML, KIF, HTTP, et technologies de Java.

KQML est employé comme format et protocole de message standard entre les agents. Le
projet d'InfoSleuth inclut une exécution de Java de la langue et de protocole de KQML. KIF
est employé pour échanger la connaissance entre les agents. Les agents convertissent
l'information de leurs formats internes en KIF et emballage il dans un KQML performative
avant d'envoyer le message à un autre agent.

À la couche la plus bottom-most dans l'architecture d'InfoSleuth sont les agents basés sur les
règles écrits en une série de langues comprenant blèsent (un langage de programmation liste-
basé d'AI), LDL++ (une langue déclarative de base de données qui inclut inferencing basé sur
les règles), AGRAFES (un système expert basé sur les règles), et Java. Puisque KQML et KIF
sont employés pour l'échange d'information, il n'est pas que tous les agents soient mis en
application dans un unilingue. Sur la coopérative les agents est une couche d'ontologies qui
inclut les agents qui emploient un vocabulaire commun et un modèle sémantique pour chaque
domaine particulier de problème. Des applications sont écrites sur la couche d'ontologies de
l'architecture.

Un certain nombre d'agents sont employés dans les différentes couches de l'architecture. Les
agents d'utilisateur, écrits dans Java, des demandes de poignée des utilisateurs, conduisent les
demandes aux agents de serveur, et renvoient les résultats à l'utilisateur. Ils sont persistants et
autonomes, qui leur permet de courir des tâches pour des utilisateurs même lorsque
l'utilisateur n'agit l'un sur l'autre plus avec l'agent. Les agents d'utilisateur contiennent
également l'intelligence de modeler le comportement de l'utilisateur. Pour faire ceci, ils
agissent l'un sur l'autre avec d'autres agents de Java appelés les moniteurs. Un moniteur
dépiste l'accès et les rapports du Web d'un utilisateur il à l'agent d'utilisateur pour l'usage dans
la détection inferencing et de modèle.

L'agent de courtier est un autre agent de Java qui est semblable au facilitant dans notre
application de marché. Elle assortit des demandes des agents avec les agents de serveur
d'ontology qui annoncent les ontologies pour lesquels ils ont la responsabilité. Les agents
d'ontology agissent l'un sur l'autre avec les agents d'exécution qui sont responsables d'exécuter
les questions ontology-basées. Les agents d'exécution fonctionnent avec les agents de
ressource qui sont les embouts avant intelligents au datastore fondamental. Les données
renvoyées d'une question peuvent également être passées aux agents d'analyse de données qui
effectuent l'analyse, l'exploitation de la connaissance, et les tâches intelligentes de
reconnaissance des structures sur les données.

Tandis que Java n'était pas la seule langue employée pour construire InfoSleuth, il a été
employé dans toute l'exécution. Des applet et la sécurité d'applet ont été employés sans risque
pour distribuer et regarder les données. La nature plate--indépendante de Java a également
LY_SII __ 255 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

permis pour déployer les agents dans l'environnement hétérogène pour lequel InfoSleuth a été
développé. Tandis que des AGRAFES et d'autres langues basées sur les règles étaient utilisées
pour fournir une grande partie de l'intelligence, certains des agents ont compté sur
l'intelligence mise en application seulement dans Java. Plus d'information sur InfoSleuth peut
être trouvée chez http://www.mcc.com/projects/infosleuth.

Jess

Jess n'est pas un environnement d'agent, intrinsèquement, mais est une exécution subsetted du
système expert générique d'AGRAFES écrit dans Java. Il peut être employé pour donner des
applications de Java, des applet, et des agents la capacité de raisonner using une base de règle
d'AGRAFES.

Des AGRAFES (système de production Integrated de langage C) ont été développées par le
centre spatial de Johnson de la NASA et ont été autorisées près plus de 4.000 publics et
établissements privés. C'est un système basé sur les règles de chaînage avant, basé sur
l'algorithme de Rete. Le code écrit dans la langue d'AGRAFES peut être couru par Jess.

Jess est une application de Java qui peut être courue de la ligne de commande comme
n'importe quelle autre application de Java. Mais le paquet de Jess contient également les
classes qui permettent à des applications ou à des applet de Java d'appeler le moteur de
système expert (la classe de Rete) ou l'analyseur de Jess (la classe de Jesp). Ceci permet aux
agents de Java d'être développés qui peuvent traiter les bases de connaissances de
préexistence d'AGRAFES. Pour l'information sur Jess, voir le
http://herzberg.ca.sandia.gov/jess/.

ABE

L'environnement de constructeur d'agent d'IBM est bibliothèque et architecture de classe de


C.A. ++ pour enfoncer les agents intelligents dans des applications. Nous mentionnons ABE
en ce chapitre sur les environnements Java-basés parce qu'il vient avec des adapteurs de Java,
permettant à des agents d'ABE de communiquer avec des applications de Java. Un des auteurs
(Joe) a contribué à la conception et aux spécifications de l'architecture d'ABE. Par certains
côtés, le cadre de CIAgent présenté dans ce livre est une version de Java d'abélite.

Un des différentiateurs principaux entre ABE et la plupart des autres environnements Java-
basés d'agent est qu'il est concentré sur la partie « intelligente » d'agents intelligents. Tandis
qu'IBM a des ferrets pour les agents mobiles de Java, ABE fournit un moteur basé sur les
règles puissant de raisonnement pour commander le comportement de l'agent. Ce moteur basé
sur les règles, appelé l'AUGMENTER, a été développé au centre de recherches d'IBM T.J.
Watson et est le coeur d'ABE. Il soutient une approche souple pour les sondes de
configuration et les effecteur à l'usage de l'agent en tant que lui raisonne au sujet du monde.

L'ABE est plus qu'un agent, il est une architecture et une trousse à outils pour les agents
révélateurs. ABE se compose de plusieurs composants importants, y compris des moteurs, la
connaissance, la bibliothèque, des vues, et des adapteurs. Le moteur dans ABE est le
moteur basé sur les règles d'AUGMENTER, mais l'architecture soutient le concept des
moteurs que l'on peut brancher. La connaissance est représentée dans KIF, et un rédacteur
Java-basé de GUI est donné pour écrire de règle. ABE fournit également un composant
LY_SII __ 256 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

flexible de bibliothèque pour stocker les ensembles et les méta-données appelés de règle de
KIF. Le composant de vue définit comment des rédacteurs et les dialogues peuvent être
attachés par des adapteurs d'ABE pour fournir à la séparation propre de modèle-vue entre le
traitement intelligent de l'agent et les interactions d'agent des utilisateurs.

Des adapteurs sont utilisés pour connecter les agents d'ABE et le moteur d'inférence
d'AUGMENTER aux applications. Par exemple, ABE vient avec des adapteurs les serveurs de
nouvelles à du World Wide Web (HTTP), de l'Internet (NNTP), un adapteur de temps qui
déclenche des événements basés sur le temps, et un adapteur de dossier ce des événements de
déclenchements quand des dossiers sont changés. Ces adapteurs fournissent un ensemble de
fonctions beaucoup plus riche que ceux fournis dans l'exécution CIAgent-basée développée
dans ce livre. Des adapteurs témoin à se connecter par interface aux systèmes d'email et pour
observer des cours des actions d'actions à un site Web sont également inclus. Ces adapteurs
peuvent être écrits dans Java et ils peuvent entièrement interopérer avec d'autres adapteurs de
Java ou de C++ par l'ABE. Pour l'information sur l'environnement de constructeur de l'agent
d'IBM, voir le http://www.networking.ibm.com/iag/iaghome.html.

Discussion

La majeure partie de la recherche pour ce chapitre a été faite sur le World Wide Web. Pendant
notre recherche de l'information sur les outils et les produits Java-basés, il était clair qu'il y ait
beaucoup d'activité dans ce secteur. En même temps, il est également évident que plusieurs
des outils d'agent aient été concentrés plus sur la mobilité d'agent que sur l'intelligence
artificielle. Sans exception, on a assumé que des agents sont autonomes, ainsi il semble que au
moins cet attribut est convenu.

Nous avons essayé de choisir l'ensemble de ce qui a semblé être les environnements d'agent
de Java les plus complets. Nous n'avons pas téléchargé et n'avons pas essayé tous les outils
mentionnés en ce chapitre. Mais des systèmes que nous avons examinés, quelques modèles
clairs pourrait être identifié. KQML a été cité dans plusieurs comme protocole de transmission
interagent. KIF a été cité dans plusieurs autres comme représentation de connaissance. Dans
les quelques systèmes où l'intelligence artificielle était le traitement inclus et basé sur les
règles était un dispositif assez standard. L'étude, cependant, n'a pas été incluse dans la plupart
des systèmes.

Comme avec d'autres secteurs de nouvelle technologie, il semble que les agents intelligents ne
seront pas un « marché » seuls. Au lieu de cela, ils seront enfoncés dans des applications
économiques où ils peuvent fournir la valeur la plus immédiate, et se transforment lentement
en la fourniture devoir-ont des dispositifs dans le logiciel commercial. Comme cité
précédemment, l'autonomie est maintenant donné pour les agents intelligents. La mobilité a
clairement l'appel, mais il n'est pas clair si ceci fasse appel aux lotisseurs ou aux utilisateurs
potentiels. La sécurité et l'infrastructure exigées pour que les agents mobiles se déplacent
facilement autour de l'Internet peuvent simplement être trop d'un obstacle.

L'intelligence sera employée dans les agents où le domaine d'application l'exige. Pour
beaucoup d'applications aujourd'hui, il semble que la logique hardcoded suffira. Là où plus de
flexibilité est exigée, inferencing basé sur les règles est inclus. Typiquement ce sont les
moteurs d'inférence assez légers, en ce qui concerne la fonction traditionnelle d'AI. Dans
LY_SII __ 257 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

beaucoup d'applications, telles que l'automation d'email, le traitement de règle simple est tout
ce qui est exigé.

L'étude suivra probablement ce même modèle. Les frais généraux informatiques des
algorithmes d'étude communs font à leur utilisation un luxe pour la plupart des applications.
Cependant, parce que les agents intelligents basés sur des serveurs d'Internet, où leur capacité
d'interpréter des données et des modèles dans les transactions ajouterait la valeur significative,
l'étude peut rapidement devenir une nécessité. Les outils du filtrage de collaboration du réseau
de luciole utilisent l'étude pour fournir la personnalisation intelligente et les communautés en
ligne adaptatives pour des compagnies fournissant des sites Web du haut-trafic. La
technologie d'étude de luciole peut être employée pour élaborer une liste personnalisée du
principal 10 pour des utilisateurs sans exiger des mots-clés ou écrire de règle
(http://www.firefly.com).

Jusque Java et agents intelligents vont, elle semble comme une allumette faite dans le ciel. La
fonction intégrée de Java filetant l'appui permet à l'autonomie d'être facilement ajoutée à
n'importe quelle classe de Java. Son appui de fabrication en série et possibilités à distance
d'invocation de méthode rendent la mobilité loin moins intimidante qu'avec d'autres langages
de programmation. En conclusion, son appui de programmation orientée objectivement de
vers le bas-à-le-noyau permet à des algorithmes d'intelligence artificielle d'être facilement mis
en application, comme nous espérons que nous avons démontré dans ce livre.

Notre dernier commentaire se rapporte à JavaBeans et comment le modèle de composant de


logiciel d'haricots affectera les agents intelligents. Comme nous avons mentionné en chapitre
7, le modèle d'événement sophistiqué, la fabrication en série, et les outils visuels de
construction font à JavaBeans une manière d'idée de développer les agents intelligents dans
Java. Selon à quelle rapiditè les bons instruments de développement visuels pour des haricots
obtiennent de lancer sur le marché, aussi bien qu'à quelle rapiditè des haricots sont acceptés
dans le marché, ils peuvent devenir « » l'architecture pour des agents. Un haricot de Java est
un composant de logiciel encapsulé standard que chaque ordinateur avec une machine
virtuelle de Java peut courir. Ajouter la mobilité aux haricots, et vous avez un environnement
imbattable pour le développement et le déploiement d'agent intelligent.

Résumé

En ce chapitre, nous avons examiné plusieurs environnements et outils d'agent intelligent de


Java. Ceux-ci incluent :

• Les ferrets sont le cadre autonome et mobile d'IBM de Java d'agent. Les ferrets sont
libres pour errer entre les serveurs courant le logiciel de centre serveur de ferret et
peuvent suivre un itinéraire prédéfini de voyage. Un processus de ferret peut être
suspendu, transporté à un autre système hôte de ferret, et à un résumé comme si il n'a
été jamais arrêté.
• Les agents de logiciel de ftp sont les agents Java-basés de gestion-système de réseau
et de distribution de logiciel.
• Le système de Voyager, d'ObjectSpace, Inc., est un courtier de demande d'objet
agent-augmenté écrit dans Java. Les agents de Voyager sont autonomes et mobiles,
mais non intelligents.
LY_SII __ 258 ْ‫ﺑــــــــــــــــﺴْﻢِ اﷲ اﻟﺮَﺣْﻤَـــــــــــــــﺎن اﻟﺮَﺣِﯿــــــــــــــــــــــﻢ‬

• L'odyssée est le système d'agent de Java de la magie générale. Écrit entièrement dans
Java, les agents d'odyssée emploient le RMI et sont autonomes et mobiles, mais non
intelligents.
• Le calibre Lite (JATLite) d'agent de Java est un ensemble de paquets légers de Java
développés à l'Université de Stanford. JATLite fournit une architecture en couches
pour les systèmes multiagent de bâtiment.
• InfoSleuth est un projet de recherche au MCC qui emploie des agents pour trouver,
rechercher, et intégrer des informations des points d'émission hétérogènes. Il emploie
KQML et KIF pour la communication et la représentation de connaissance et fournit
les agents basés sur les règles dans Java.
• Système expert générique Java est une exécution de Java de l'environnement
standard de base de règle d'AGRAFES développé au laboratoire national de Sandia.
Tandis que pas un environnement d'agent, Jess fournit l'appui inferencing basé sur les
règles dans Java.
• L'environnement de constructeur d'agent développé à IBM est une trousse à outils
d'architecture et de développement d'agent intelligent pour ajouter les agents
incorporés aux applications. Le moteur d'inférence d'AUGMENTER est utilisé comme
contrôleur d'agent, avec KIF ordonne et soutien de bibliothèque de stockage de la
connaissance et de méta-données. L'architecture d'ABE inclut les adapteurs qui se
connectent par interface directement aux applications et communiquent avec le moteur
d'AUGMENTER par des sondes et des effecteur.
• La situation actuelle des agents intelligents de Java est qu'ils sont autonomes et
mobiles, mais pas très intelligent. Là où l'intelligence artificielle est utilisée, le plus
souvent elle est par inferencing basé sur les règles, et rarement avec l'étude. Le futur
pour Java et agents intelligents semble lumineux.

Vous aimerez peut-être aussi