|
||||||||||
| [English] | Главная | Продукты | Публикации | Ресурсы | Новости |
|
|
| Главная Партнеры Продукты Статьи Форум Фотоальбом |
Изучаем Cerebrum 1.0
Занятие «Objects-01»
Желательно знакомство с занятием «Streams-01»
В этом упражнении мы сделаем реализацию синхронизированного линейного дерева для хранения словоформ. Это дерево представляет собой автоматную грамматику (http://www.shuklin.com/ai/ht/ru/ai00002f.aspx, http://www.shuklin.com/ai/ht/ru/ai00008f.aspx) Цель данного упражнения - рассмотреть работу с объектами в СООБЗ Cerebrum а не реализовать грамматику. Поэтому пример будет несколько упрощен. Для синтеза сети будет применена рекурсия что наглядно продемонстрирует возможности СООБЗ однако в реальном сценарии может быть неоптимальным решением.
СУООБЗ Cerebrum это многоуровневая система состоящая из различных модулей. Чтобы применять ООБЗ в своей разработке - необходимо определиться, какой именно уровень будет наиболее удобен в разрабатываемом приложении.
Создадим новое решение sln в среде Microsoft Visual Studio 2003. Cerebrum.Samples.Objects-01.sln
Уже существующую форму переименовываем в PrimaryWindow. Затем добавляем класс Application.
В класс Application переносим статическую функцию Main из PrimaryWindow. Переопределяем функцию CreatePrimaryWindow и CreateContextService. Заменяем майн на
static
void Main() Итого имеем using System;
namespace Cerebrum.Samples.Objects01 { /// <summary> /// Summary description for Application. /// </summary> public class Application : Cerebrum.Windows.Forms.Application { public Application() { // // TODO: Add constructor logic here // }
protected override System.Windows.Forms.Form CreatePrimaryWindow() { return new PrimaryWindow(); }
protected override Cerebrum.Windows.Forms.IContextService CreateContextService() { string primaryDirectory = m_MasterContext!=null?(m_MasterContext.SystemDirectory):System.IO.Path.GetDirectoryName(this.GetType().Assembly.Location); string databaseFileName = System.IO.Path.Combine(primaryDirectory, "Cerebrum.Database.Master.bin"); string activityFileName = System.IO.Path.Combine(primaryDirectory, "Cerebrum.Database.Master.log"); string mergeFileName = System.IO.Path.Combine(primaryDirectory, "Cerebrum.Database.Master.xdb");
return new Cerebrum.Integrator.SimpleContextService(this, databaseFileName, activityFileName, 4, mergeFileName, false); }
/// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application app = new Application(); Cerebrum.Windows.Forms.Application.Instance = app; app.Initialize(); app.Show(); System.Windows.Forms.Application.Run(app.PrimaryWindow); app.Shutdown(); app.Dispose(); } } } Это минимальная реализация Application.
Теперь чтобы убедится, что все работает, добавим на форму DataGrid, а в событие Form_Load и Form_Closed следующий код:
Cerebrum.Data.TableView view;
private void PrimaryWindow_Load(object sender, System.EventArgs e) { using(Cerebrum.Runtime.IConnector connector = this.DomainContext.GetTable("Types")) { view = (connector.Component as Cerebrum.Reflection.TableDescriptor).DefaultView; dataGrid1.DataSource = view; } }
private void PrimaryWindow_Closed(object sender, System.EventArgs e) { view.Dispose(); }
В Close вызывать деструктор не обязательно, но крайне желательно - это позволит сработать встроенной системе самодиагностики мемори ликов - если будешь пользоваться Debug версией ядра.
Для нейрона мы создадим базовый класс Cerebrum.Samples.Objects01.NeuronBase у этого класса будет два атрибута - список зависимых нейронов (DependentObjectsList) и список нейронов от которых зависит текущий нейрон (PrecedentObjectsList). Предусмотрим интерфейс INeuronLinks для манипулирования этими списками.
public interface INeuronLinks { void AddPrecedent(Cerebrum.Runtime.NativeHandle h); void AddDependent(Cerebrum.Runtime.NativeHandle h);
void DelPrecedent(Cerebrum.Runtime.NativeHandle h); void DelDependent(Cerebrum.Runtime.NativeHandle h); }
Реализуем этот интерфейс в классе NeuronBase
От базового класса мы унаследуем нейрон, распознающий символ входной символьной последовательности SymbolNeuron : NeuronBase. Этот нейрон будет реализовывать интерфейс ILineralTreeNode В этом интерфейсе нам понадобится два метода - метод для обучения зависимых нейронов GenerateDependents и метод для получения строк символов, соответствующих нейрону RestoreStrings а так же свойство, возвращающее символ, распознаваемый нейроном Symbol.
public interface ILineralTreeNode { Cerebrum.Runtime.NativeHandle[] GenerateDependents(string text); string [] RestoreStrings(); char Symbol {get;set;} }
Обяжем нейрон SymbolNeuron реализовать интерфейс ILineralTreeNodeEx содержащий дополнительный метод FindAllLeafs позволяющий обнаружить все листовые узлы синхронизированного линейного дерева. public interface ILineralTreeNodeEx : INeuronLinks, ILineralTreeNode { void FindAllLeafs(System.Collections.ArrayList leafs); }
Таким образом, объект SymbolNeuron будет обладать 3мя атрибутами - DependentObjectsList, PrecedentObjectsList и Symbol
Создадим в проекте папку Specialized и в ней класс Concepts Этот класс не будет иметь экземпляров - только статические поля. Поэтому сделаем для него статический конструктор, а конструктор экземпляра объявим как private.
Создадив в этом классе несколько полей соответсвующие аттрбутам нейрона Cerebrum.Runtime.NativeHandle DependentObjectsListAttribute; Cerebrum.Runtime.NativeHandle PrecedentObjectsListAttribute; Cerebrum.Runtime.NativeHandle SymbolAttribute; Cerebrum.Runtime.NativeHandle SymbolNeuronType;
В статический конструктор добавим код для их инициализации static Concepts() { using (Cerebrum.Runtime.IConnector connector = Cerebrum.Windows.Forms.Application.Instance.MasterContext.GetTable("Attributes")) { using (Cerebrum.Data.TableView view = (connector.Component as Cerebrum.Reflection.TableDescriptor).DefaultView) { System.ComponentModel.PropertyDescriptor descriptor = view.GetItemProperties(null)["Name"]; DependentObjectsListAttribute = FindHandle(view, descriptor, "DependentObjectsList"); PrecedentObjectsListAttribute = FindHandle(view, descriptor, "PrecedentObjectsList"); SymbolAttribute = FindHandle(view, descriptor, "Symbol"); } } using (Cerebrum.Runtime.IConnector connector = Cerebrum.Windows.Forms.Application.Instance.MasterContext.GetTable("Types")) { using (Cerebrum.Data.TableView view = (connector.Component as Cerebrum.Reflection.TableDescriptor).DefaultView) { System.ComponentModel.PropertyDescriptor descriptor = view.GetItemProperties(null)["Name"]; SymbolNeuronType = FindHandle(view, descriptor, "SymbolNeuron"); } } } для облегчения поиска атрибутов создадим вспомогательную функцию
private static Cerebrum.Runtime.NativeHandle FindHandle(Cerebrum.Data.TableView view, System.ComponentModel.PropertyDescriptor prp, string value) { int index = view.Find(prp, value); if (index >= 0) { return view[index].ObjectHandle; } return Cerebrum.Runtime.NativeHandle.Null; }
Итак в результате
using System;
namespace Cerebrum.Samples.Objects01.Specialized { /// <summary> /// Summary description for Concepts. /// </summary> public class Concepts { static Concepts() { using (Cerebrum.Runtime.IConnector connector = Cerebrum.Windows.Forms.Application.Instance.MasterContext.GetTable("Attributes")) { using (Cerebrum.Data.TableView view = (connector.Component as Cerebrum.Reflection.TableDescriptor).DefaultView) { System.ComponentModel.PropertyDescriptor descriptor = view.GetItemProperties(null)["Name"]; DependentObjectsListAttribute = FindHandle(view, descriptor, "DependentObjectsList"); PrecedentObjectsListAttribute = FindHandle(view, descriptor, "PrecedentObjectsList"); SymbolAttribute = FindHandle(view, descriptor, "Symbol"); } } using (Cerebrum.Runtime.IConnector connector = Cerebrum.Windows.Forms.Application.Instance.MasterContext.GetTable("Types")) { using (Cerebrum.Data.TableView view = (connector.Component as Cerebrum.Reflection.TableDescriptor).DefaultView) { System.ComponentModel.PropertyDescriptor descriptor = view.GetItemProperties(null)["Name"]; SymbolNeuronType = FindHandle(view, descriptor, "SymbolNeuron"); } } }
private Concepts() { // // TODO: Add constructor logic here // }
private static Cerebrum.Runtime.NativeHandle FindHandle(Cerebrum.Data.TableView view, System.ComponentModel.PropertyDescriptor prp, string value) { int index = view.Find(prp, value); if (index >= 0) { return view[index].ObjectHandle; } return Cerebrum.Runtime.NativeHandle.Null; }
public static readonly Cerebrum.Runtime.NativeHandle DependentObjectsListAttribute; public static readonly Cerebrum.Runtime.NativeHandle PrecedentObjectsListAttribute; public static readonly Cerebrum.Runtime.NativeHandle SymbolAttribute; public static readonly Cerebrum.Runtime.NativeHandle SymbolNeuronType;
} }
Теперь создаем класс NeuronBase, унаследованный от Cerebrum.Integrator.GenericComponent
В него добавляем две функции, соответсвующие его аттрибутам и переопределяем функцию SetConnector Переопределение функции SetConnector необходимо для принудительного создания дочерних атрибутов содержащих списки идентификаторов связанных нейронов. Функции являются вспомогательными, облегчающими получения этих списков во время работы.
Итого using System;
namespace Cerebrum.Samples.Objects01 { /// <summary> /// Summary description for NeuronBase. /// </summary> public class NeuronBase : Cerebrum.Integrator.GenericComponent { public NeuronBase() { // // TODO: Add constructor logic here // }
public Cerebrum.Runtime.NativeVector GetPrecedentObjectsListVector() { return this.GetAttributeContainer(Cerebrum.Samples.Objects01.Specialized.Concepts.PrecedentObjectsListAttribute) as Cerebrum.Runtime.NativeVector; }
public Cerebrum.Runtime.NativeVector GetDependentObjectsListVector() { return this.GetAttributeContainer(Cerebrum.Samples.Objects01.Specialized.Concepts.DependentObjectsListAttribute) as Cerebrum.Runtime.NativeVector; }
public void AddPrecedent(Cerebrum.Runtime.NativeHandle h) { using(Cerebrum.Runtime.NativeVector v = GetPrecedentObjectsListVector()) { v.SetMap(h, new Cerebrum.Runtime.NativeHandle(1)); } }
public void AddDependent(Cerebrum.Runtime.NativeHandle h) { using(Cerebrum.Runtime.NativeVector v = GetDependentObjectsListVector()) { v.SetMap(h, new Cerebrum.Runtime.NativeHandle(1)); } }
public void DelPrecedent(Cerebrum.Runtime.NativeHandle h) { using(Cerebrum.Runtime.NativeVector v = GetPrecedentObjectsListVector()) { v.SetMap(h, Cerebrum.Runtime.NativeHandle.Null); } }
public void DelDependent(Cerebrum.Runtime.NativeHandle h) { using(Cerebrum.Runtime.NativeVector v = GetDependentObjectsListVector()) { v.SetMap(h, Cerebrum.Runtime.NativeHandle.Null); } }
protected override void SetConnector(Cerebrum.Runtime.SerializeDirection direction, Cerebrum.Runtime.IConnector connector) { base.SetConnector (direction, connector); switch(direction) { case Cerebrum.Runtime.SerializeDirection.Init: { using(Cerebrum.Runtime.NativeWarden warden = (m_Connector.GetContainer() as Cerebrum.Runtime.NativeWarden)) { warden.Newobj(Cerebrum.Samples.Objects01.Specialized.Concepts.PrecedentObjectsListAttribute, Cerebrum.Runtime.KernelObjectClass.Vector); warden.Newobj(Cerebrum.Samples.Objects01.Specialized.Concepts.DependentObjectsListAttribute, Cerebrum.Runtime.KernelObjectClass.Vector); } break; } } } } }
Теперь создаем класс SymbolNeuron, наследуем его от NeuronBase и ILineralTreeNodeEx В этом классе реализуем свойство Symbol и перекрываем метод SetConnector В отличие от предыдущего класса, в SetConnector создается скалярный аттрибут для хранения сивола, распознаваемого нейроном.
Реализуем методы интерфейса ILineralTreeNode
#region ILineralTreeNode Members
public Cerebrum.Runtime.NativeHandle[] GenerateDependents(string text) { //проверяем входные параметры if(text==null || text.Length<1) return null;
//вырезаем первый символ из строки char symbol = text[0];
//если первый символ совпадает с тем что разпознает этот нейрон - это наша строка if(this.Symbol!=symbol) return null;
//получаем остаток строки string tail = text.Substring(1);
if(tail.Length>0) { //пытаемся найти нейроны, распознающие остаток строки using(Cerebrum.Runtime.NativeVector vector = this.GetDependentObjectsListVector()) { foreach(System.Collections.DictionaryEntry de in vector) { Cerebrum.Runtime.NativeHandle h = (Cerebrum.Runtime.NativeHandle)de.Key; using(Cerebrum.Runtime.IConnector connector = this.DomainContext.AttachConnector(h)) { ILineralTreeNode node = connector.Component as ILineralTreeNode; if(node!=null) { //рекурсивный вызов этой же функции но уже у другого нейрона Cerebrum.Runtime.NativeHandle[] hs = node.GenerateDependents(tail); if(hs!=null) { Cerebrum.Runtime.NativeHandle[] hs2 = new Cerebrum.Runtime.NativeHandle[hs.Length + 1]; hs.CopyTo(hs2, 1); hs2[0] = this.PlasmaHandle; return hs2; } } } } } //если мы здесь, то найти ничего не удалось - создаем то чего нехватает (обучаем сеть). using(Cerebrum.Runtime.NativeSector sector = this.DomainContext.GetSector()) { // создаем ID для нового нейрона Cerebrum.Runtime.NativeHandle h0 = sector.NextSequence(); // создаем новый экземпляр нейрона using(Cerebrum.Runtime.IConnector connector = sector.CreateConnector(h0, Cerebrum.Samples.Objects01.Specialized.Concepts.SymbolNeuronType)) { // а здесь мы приводим напрямую к себе, так как мы создавали именно себя и можем быть уверены что приведение будет успешным // это надо чтоб добраться до функции AddPrecedent которая не описана в интерфейсе ILineralTreeNode ILineralTreeNodeEx node = connector.Component as ILineralTreeNodeEx; if(node!=null) { //задаем нейрону его символ для распознавания node.Symbol = tail[0]; node.AddPrecedent(this.PlasmaHandle); this.AddDependent(connector.PlasmaHandle);
//рекурсивный вызов этой же функции но уже у нового нейрона Cerebrum.Runtime.NativeHandle[] hs = node.GenerateDependents(tail); if(hs!=null) { Cerebrum.Runtime.NativeHandle[] hs2 = new Cerebrum.Runtime.NativeHandle[hs.Length + 1]; hs.CopyTo(hs2, 1); hs2[0] = this.PlasmaHandle; return hs2; } } } } } // мы были последними в списке - дальше распознавать нечего, возвращаем себя return new Cerebrum.Runtime.NativeHandle[] {this.PlasmaHandle}; }
public string[] RestoreStrings() { //опрашиваем всех предшественников и получаем строки, которые они распознают System.Collections.ArrayList result = new System.Collections.ArrayList(); using(Cerebrum.Runtime.NativeVector vector = this.GetPrecedentObjectsListVector()) { foreach(System.Collections.DictionaryEntry de in vector) { Cerebrum.Runtime.NativeHandle h = (Cerebrum.Runtime.NativeHandle)de.Key; using(Cerebrum.Runtime.IConnector connector = this.DomainContext.AttachConnector(h)) { ILineralTreeNode node = connector.Component as ILineralTreeNode; if(node!=null) { //рекурсивный вызов этой же функции но уже у другого нейрона string [] heads = node.RestoreStrings(); //обычно в heads может быть только одна строка - так как обычно предшественник в дереве только один //на случай поддержки многозначности и нескольких путей распространения волны возбуждения предусматриваем //более сложный вариант со многими предшественниками foreach(string head in heads) { result.Add(head + this.Symbol.ToString()); } } } } } // это корневой узел без предшественников - добавляем себя в начало строки if(result.Count<1) { result.Add(this.Symbol.ToString()); } return (string[]) result.ToArray(typeof(string)); }
#endregion
Итого
using System;
namespace Cerebrum.Samples.Objects01 { /// <summary> /// Summary description for SymbolNeuron. /// </summary> public class SymbolNeuron : NeuronBase, ILineralTreeNodeEx { public SymbolNeuron() { // // TODO: Add constructor logic here // }
public char Symbol { get { return Convert.ToChar(GetAttributeComponent(Cerebrum.Samples.Objects01.Specialized.Concepts.SymbolAttribute)); } set { SetAttributeComponent(Cerebrum.Samples.Objects01.Specialized.Concepts.SymbolAttribute, Convert.ToInt32(value)); } }
protected override void SetConnector(Cerebrum.Runtime.SerializeDirection direction, Cerebrum.Runtime.IConnector connector) { base.SetConnector (direction, connector); switch(direction) { case Cerebrum.Runtime.SerializeDirection.Init: { using(Cerebrum.Runtime.NativeWarden warden = (m_Connector.GetContainer() as Cerebrum.Runtime.NativeWarden)) { warden.Newobj(Cerebrum.Samples.Objects01.Specialized.Concepts.SymbolAttribute, Cerebrum.Runtime.KernelObjectClass.Scalar); } break; } } }
#region ILineralTreeNode Members
public Cerebrum.Runtime.NativeHandle[] GenerateDependents(string text) { //проверяем входные параметры if(text==null || text.Length<1) return null;
//вырезаем первый символ из строки char symbol = text[0];
//если первый символ совпадает с тем что разпознает этот нейрон - это наша строка if(this.Symbol!=symbol) return null;
//получаем остаток строки string tail = text.Substring(1);
if(tail.Length>0) { //пытаемся найти нейроны, распознающие остаток строки using(Cerebrum.Runtime.NativeVector vector = this.GetDependentObjectsListVector()) { foreach(System.Collections.DictionaryEntry de in vector) { Cerebrum.Runtime.NativeHandle h = (Cerebrum.Runtime.NativeHandle)de.Key; using(Cerebrum.Runtime.IConnector connector = this.DomainContext.AttachConnector(h)) { ILineralTreeNode node = connector.Component as ILineralTreeNode; if(node!=null) { //рекурсивный вызов этой же функции но уже у другого нейрона Cerebrum.Runtime.NativeHandle[] hs = node.GenerateDependents(tail); if(hs!=null) { Cerebrum.Runtime.NativeHandle[] hs2 = new Cerebrum.Runtime.NativeHandle[hs.Length + 1]; hs.CopyTo(hs2, 1); hs2[0] = this.PlasmaHandle; return hs2; } } } } } //если мы здесь, то найти ничего не удалось - создаем то чего нехватает (обучаем сеть). using(Cerebrum.Runtime.NativeSector sector = this.DomainContext.GetSector()) { // создаем ID для нового нейрона Cerebrum.Runtime.NativeHandle h0 = sector.NextSequence(); // создаем новый экземпляр нейрона using(Cerebrum.Runtime.IConnector connector = sector.CreateConnector(h0, Cerebrum.Samples.Objects01.Specialized.Concepts.SymbolNeuronType)) { // а здесь мы приводим напрямую к себе, так как мы создавали именно себя и можем быть уверены что приведение будет успешным // это надо чтоб добраться до функции AddPrecedent которая не описана в интерфейсе ILineralTreeNode ILineralTreeNodeEx node = connector.Component as ILineralTreeNodeEx; if(node!=null) { //задаем нейрону его символ для распознавания node.Symbol = tail[0]; node.AddPrecedent(this.PlasmaHandle); this.AddDependent(connector.PlasmaHandle);
//рекурсивный вызов этой же функции но уже у нового нейрона Cerebrum.Runtime.NativeHandle[] hs = node.GenerateDependents(tail); if(hs!=null) { Cerebrum.Runtime.NativeHandle[] hs2 = new Cerebrum.Runtime.NativeHandle[hs.Length + 1]; hs.CopyTo(hs2, 1); hs2[0] = this.PlasmaHandle; return hs2; } } } } } // мы были последними в списке - дальше распознавать нечего, возвращаем себя return new Cerebrum.Runtime.NativeHandle[] {this.PlasmaHandle}; }
public string[] RestoreStrings() { //опрашиваем всех предшественников и получаем строки, которые они распознают System.Collections.ArrayList result = new System.Collections.ArrayList(); using(Cerebrum.Runtime.NativeVector vector = this.GetPrecedentObjectsListVector()) { foreach(System.Collections.DictionaryEntry de in vector) { Cerebrum.Runtime.NativeHandle h = (Cerebrum.Runtime.NativeHandle)de.Key; using(Cerebrum.Runtime.IConnector connector = this.DomainContext.AttachConnector(h)) { ILineralTreeNode node = connector.Component as ILineralTreeNode; if(node!=null) { //рекурсивный вызов этой же функции но уже у другого нейрона string [] heads = node.RestoreStrings(); //обычно в heads может быть только одна строка - так как обычно предшественник в дереве только один //на случай поддержки многозначности и нескольких путей распространения волны возбуждения предусматриваем //более сложный вариант со многими предшественниками foreach(string head in heads) { result.Add(head + this.Symbol.ToString()); } } } } } // это корневой узел без предшественников - добавляем себя в начало строки if(result.Count<1) { result.Add(this.Symbol.ToString()); } |