МИНОБРНАУКИ России
ФГБОУ ВПО «Череповецкий государственный университет»
Институт информационных технологий
Кафедра: М ПО ЭВМ
Дисциплина: Системы искусственного интеллекта
Лабораторная работа №4
Обучение без учителя.
Сигнальный метод Хебба
Выполнила:
Группа: 1ПО-41
Проверил:
Отметка о зачете:
Череповец, 2012
Задание:
Реализовать нейронную сеть на основе сигнального метода Хебба, распознающую буквы «Ф» и «И».
Структура сети:
Нейронная сеть имеет 2 слоя:
- Скрытый(внутренний)
- Выходной
На каждый вход внутреннего слоя поступает матрица из 100 элементов, в скрытом слое 40 нейронов, выходной слой имеет 3 выхода.
Для первого слоя НС использует пороговую функцию, для второго – сигмоидальную.
Сигнальный метод Хебба:
Главная черта, делающая обучение без учителя привлекательным, – это его "самостоятельность". Процесс обучения, как и в случае обучения с учителем, заключается в подстраивании весов синапсов. Некоторые алгоритмы, правда, изменяют и структуру сети, то есть количество нейронов и их взаимосвязи, но такие преобразования правильнее назвать более широким термином – самоорганизацией. Очевидно, что подстройка синапсов может проводиться только на основании информации, доступной в нейроне, то есть его состояния и уже имеющихся весовых коэффициентов. Исходя из этого соображения и, что более важно, по аналогии с известными принципами самоорганизации нервных клеток, построены алгоритмы обучения Хебба.
Сигнальный метод обучения Хебба заключается в изменении весов по следующему правилу:
(1)
где yi(n-1) – выходное значение нейрона i слоя (n-1), yj(n) – выходное значение нейрона j слоя n; wij(t) и wij(t-1) – весовой коэффициент синапса, соединяющего эти нейроны, на итерациях t и t-1 соответственно; α – коэффициент скорости обучения. Здесь и далее, для общности, под n подразумевается произвольный слой сети. При обучении по данному методу усиливаются связи между возбужденными нейронами.
Полный алгоритм обучения с применением вышеприведенных формул будет выглядеть так:
1. На стадии инициализации всем весовым коэффициентам присваиваются небольшие случайные значения.
2. На входы сети подается входной образ, и сигналы возбуждения распространяются по всем слоям согласно принципам классических прямопоточных (feedforward) сетей[1], то есть для каждого нейрона рассчитывается взвешенная сумма его входов, к которой затем применяется активационная (передаточная) функция нейрона, в результате чего получается его выходное значение yi(n), i=0...Mi-1, где Mi – число нейронов в слое i; n=0...N-1, а N – число слоев в сети.
3. На основании полученных выходных значений нейронов по формуле (1) производится изменение весовых коэффициентов.
4. Цикл с шага 2, пока выходные значения сети не застабилизируются с заданной точностью. Применение этого нового способа определения завершения обучения, отличного от использовавшегося для сети обратного распространения, обусловлено тем, что подстраиваемые значения синапсов фактически не ограничены.
На втором шаге цикла попеременно предъявляются все образы из входного набора.
Следует отметить, что вид откликов на каждый класс входных образов не известен заранее и будет представлять собой произвольное сочетание состояний нейронов выходного слоя, обусловленное случайным распределением весов на стадии инициализации. Вместе с тем, сеть способна обобщать схожие образы, относя их к одному классу. Тестирование обученной сети позволяет определить топологию классов в выходном слое. Для приведения откликов обученной сети к удобному представлению можно дополнить сеть одним слоем, который, например, по алгоритму обучения однослойного перцептрона необходимо заставить отображать выходные реакции сети в требуемые образы.
Код программы:
#include <vcl. h>
#include <iostream. h>
#include <fstream. h>
#include <math. h>
#pragma hdrstop
#include "Unit1.h"
#include "Unit2.cpp"
//
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//
int InputMas[10][10];
float WeightL1[100][40]; //первый слой
float WeightL2[40][2]; //второй слой
String Dir=GetCurrentDir();
int Step=0;
//--
float a1=0.01; //коэффициент обучения
float a2=0.2;
void Load_File(char fname[255]);
void LoadInputMas(); //чтение образа
//_____________________NeuronA(40)
class NeuronA
{protected:
float Sum;
float F;
float Porog;
public:
float Out;
int IMas[100];
//создание нейрона
NeuronA() //конструктор
{ NeuronCreate(); }
void NeuronCreate()
{ F=Sum=0;
Porog=4.5;
for (int i=0;i<100;i++)
{ IMas[i]=0; } }
float ActivFun(float x)
{ float Temp;
Temp=1/(1 + exp(-0.1*x));//сигмоид
return Temp; }
//выход нейрона(пороговая активационная функция)
/*для каждого нейрона рассчитывается взвешенная сумма его входов,
к которой затем применяется активационная (передаточная)
функция нейрона */
float FunctionY(int NumNeuron)
{ float Temp=0;
F=0;
for (int i=0;i<100;i++)
{ if (WeightL1[i][NumNeuron]>1)
WeightL1[i][NumNeuron]=StrToFloat(Form1->Edit1->Text);
F+=WeightL1[i][NumNeuron]*IMas[i]; }
Sum=F;
Temp=F/10;
return Temp; }
void LoadIMas()
{ for (int i=0;i<10;i++)
for (int j=0;j<10;j++)
{IMas[i*10+j]=InputMas[i][j];} }
void Train(int NumNeuron)
{ Out=FunctionY(NumNeuron); }
};//___________________________end class NeuronA
class NeuronB //_____________________NeuronB
{ protected:
float F;
float Sum;
public:
float Out;
float IMas[40];
float Error;
//создание нейрона
NeuronB()
{ NeuronCreate(); }
void NeuronCreate()
{ F=Sum=0;
for (int i=0;i<40;i++)
{ IMas[i]=0; } }
void FunctionY(int NumNeuron)
{ F=0;
for (int i=0;i<40;i++)
{ F+=WeightL2[i][NumNeuron]*IMas[i]; }
Sum=F; }
float ActivFun()
{ float result=0;
//result=1/(1+exp(-0.05*sum));
result=Sum;
return result; }
}; //end class NeuronB
NeuronA MasNeuronA[40];//выходы первого слоя
NeuronB MasNeuronB[2]; //выходы второго слоя
//чтение образа
void LoadInputMas()
{ String s;
char *m;
for (int i=0;i<10;i++)
for (int j=0;j<10;j++)
InputMas[j][i]=0;
for (int i=0;i<Form1->Memo1->Lines->Count;i++)
{s=Form1->Memo1->Lines->Strings[i];
m=s. c_str();
for (int j=0;j<s. Length();j++)
{ if (m[j]=='1')
{ InputMas[j][i]=1; } } }}
void SelectCell(int ACol, int ARow) //формирование входного образ
{ if (InputMas[ACol][ARow]==0)
{InputMas[ACol][ARow]=1;}
else {InputMas[ACol][ARow]=0;}}
void Draw()
{ or (int i=0;i<10;i++)
for (int j=0;j<10;j++)
{ if (InputMas[i][j]==1)
{ Form1->StringGrid1->Canvas->Brush->Color = clBlack;
Form1->StringGrid1->Canvas->Rectangle(i*25+i, j*25+j, i*25+25+i, j*25+25+j); }
else
{ Form1->StringGrid1->Canvas->Brush->Color = clWhite;
Form1->StringGrid1->Canvas->Rectangle(i*25+i, j*25+j, i*25+25+i, j*25+25+j); } }}
//рассчет данных скрытого слоя, сохранение при обучении
void LayerA(String _Dir)
{ String fn=Dir+_Dir;
Load_File(fn. c_str());
LoadInputMas();
for (int i=0;i<40;i++)
{ MasNeuronA[i].LoadIMas(); //вход
MasNeuronA[i].Train(i); //выход }}
void DetectLayerA()
{ for (int i=0;i<40;i++)
{ MasNeuronA[i].LoadIMas();
MasNeuronA[i].Train(i); }}
//формирование выходного слоя
void LayerB()
{
for (int i=0;i<40;i++)
{MasNeuronB[0].IMas[i]=MasNeuronB[1].IMas[i]=MasNeuronA[i].Out;}
for (int i=0;i<2;i++)
{ MasNeuronB[i].FunctionY(i);
MasNeuronB[i].Out=MasNeuronB[i].ActivFun(); }}
bool HebbA(float eps)
//WeightL1[i][j] (t)=WeightL1[i][j] (t-1) + a1*y1[i]*y2[j]
//eps - заданная точность
{ float Temp;
bool b=true;
float _eps;
for (int j=0;j<40;j++)
for (int i=0;i<10;i++)
for (int k=0;k<10;k++)
{
//предыдущее значение WeightL1[i][j] (t-1)
Temp=WeightL1[i*10+k][j];
// прибавляем + a1*y1[i]*y2[j]
WeightL1[i*10+k][j]+=a1*InputMas[i][j]*MasNeuronA[i].Out;
if (WeightL1[i*10+k][j]>1)
{WeightL1[i*10+k][j]=StrToFloat(Form1->Edit2->Text);}//0.1
_eps=WeightL1[i*10+k][j]-Temp; //Temp - предыд.значение
if (_eps>eps) //пока выходные значения сети не застабилизируются с заданной точностью
b=false; }
return b;}
void Load_File(char fname[255])
{ String t;
Form1->Memo1->Clear();
try
{ fstream f; //открываем поток данного файла
char Temp[11];
char buff[11]; //переменная куда считываются данные из файла построчно
f. open(fname, ios::in); //открываем файл
while (!f. eof()) //пока файл не закончился считываем из него данные
{ f. getline(buff, sizeof(buff));
t=buff;
if (t=="") // если строка пустая то ничего не заносим в список
{continue;}; //проверяем заново на конец файла
Form1->Memo1->Lines->Add(buff); }
}catch(EConvertError*){MessageDlg("Ошибка считывания данных из файла",mtWarning, TMsgDlgButtons() << mbYes, NULL);} //если произошла какаянть ошибка при работе с файлом то выдаем ошибку об этом
}
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TForm1::FormCreate(TObject *Sender)
{ for (int i=0;i<40;i++)
for (int j=0;j<2;j++)
{ /*На стадии инициализации всем весовым
коэффициентам присваиваются небольшие случайные значения*/
WeightL2[i][j]=(exp(sin(M_PI/(random(10)+1)))+random(2))/10;
if (WeightL2[i][j]>1)
{ShowMessage(">1");} }
for (int i=0;i<100;i++)
for (int j=0;j<40;j++)
{ WeightL1[i][j]=(exp(sin(M_PI/(random(10)+1)))+random(2))/10;
if (WeightL1[i][j]>1)
{ShowMessage(">1");} }
Label6->Caption="0"; }
//
void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State)
{ Draw(); }
//
void __fastcall TForm1::StringGrid1SelectCell(TObject *Sender, int ACol,
int ARow, bool &CanSelect)
{ SelectCell(ACol, ARow); }
//
//очистка
void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{ for (int i=0;i<10;i++)
for (int j=0;j<10;j++)
{ InputMas[i][j]=0;
Form1->StringGrid1->Canvas->Brush->Color = clWhite;
Form1->StringGrid1->Canvas->Rectangle(i*25+i, j*25+j, i*25+25+i, j*25+25+j); }}
//
//сохранение
void __fastcall TForm1::BitBtn2Click(TObject *Sender)
{ char *fn;
String fname;
String s=".txt";
if (SaveDialog1->Execute())
{ fname=SaveDialog1->FileName+s;
fn=fname. c_str();
Save_To_File(fn, InputMas); }}
//
//открытие
void __fastcall TForm1::BitBtn3Click(TObject *Sender)
{ for (int i=0;i<10;i++)
for (int j=0;j<10;j++)
{ Form1->StringGrid1->Canvas->Brush->Color = clWhite;
Form1->StringGrid1->Canvas->Rectangle(i*25+i, j*25+j, i*25+25+i, j*25+25+j); }
for (int i=0;i<10;i++)
for (int j=0;j<10;j++)
{InputMas[i][j]=0;}
Form1->Memo1->Clear();
if ( OpenDialog1->Execute()) //открываем окно загрузки, выбираем файл загрузки
{ if ( !FileExists(OpenDialog1->FileName. c_str())) //если файл не выбран то выдаем ошибку
{ MessageDlg("Ошибка открытия файла для загрузки данных",mtWarning, TMsgDlgButtons() << mbYes, NULL);return; }
else
{ Load_File(OpenDialog1->FileName. c_str());
LoadInputMas();
Draw(); }; }; }
//
//обучение
void __fastcall TForm1::BitBtn4Click(TObject *Sender)
{ Form1->Caption = "Training...";
String CurDir;
bool Temp=false;
int NumFile=1;
Label6->Caption=IntToStr(StrToInt(Label6->Caption)+1);
for (int i=NumFile;i<7;i++)
{ Temp=false;
for (int j=0;j<10;j++)
{ CurDir="\\true\\tf"+IntToStr(i)+".txt";
LayerA(CurDir);
Temp=HebbA(0.02); } }
float ress=0;
for (int i=0;i<40;i++)
{ ress+=MasNeuronA[i].Out; }
Label4->Caption="Ф "+FloatToStr(ress/40);
for (int i=0;i<40;i++)
for (int j=0;j<100;j++)
{Memo2->Lines->Add(IntToStr(i)+","+IntToStr(j)+" : "+FloatToStr(WeightL1[j][i]));}
Memo2->Lines->Add("____________");
NumFile=1;
for (int i=NumFile;i<7;i++)
{ Temp=false;
for (int j=0;j<10;j++)
{ CurDir="\\true\\tn"+IntToStr(i)+".txt";
LayerA(CurDir);
Temp=HebbA(0.02); } }
ress=0;
for (int i=0;i<40;i++)
{ ress+=MasNeuronA[i].Out; }
Label5->Caption="И "+FloatToStr(ress/40);
for (int i=0;i<40;i++)
for (int j=0;j<100;j++)
{Memo2->Lines->Add(IntToStr(i)+","+IntToStr(j)+" : "+FloatToStr(WeightL1[j][i]));}
Step++;
if (Step<=StrToInt(Edit3->Text))
{ BitBtn4->Click();
Form1->Caption = "Hebb";
Form1->Refresh(); } }
//
void __fastcall TForm1::BitBtn6Click(TObject *Sender)
{ DetectLayerA();
float ress=0;
for (int i=0;i<40;i++)
{ ress+=MasNeuronA[i].Out; }
LayerB();
Label2->Caption=FloatToStr(MasNeuronB[0].Out);
Label3->Caption=FloatToStr(MasNeuronB[1].Out);
if ((ress/80)<0.3)
ShowMessage("Ошибка "+FloatToStr((ress/80)));
else
{if (((MasNeuronB[0].Out+MasNeuronB[1].Out)/2)<=9)
ShowMessage("Это И "+FloatToStr((ress/80)));
else
ShowMessage("Это Ф "+FloatToStr((ress/80)));};
}
Результат работы:


Контрольные вопросы
1. Какие задачи могут быть решены с помощью нейронных сетей?
Решаемые задачи:
· Классификация образов.
· Кластеризация/категоризация.
· Аппроксимация функций.
· Предсказание/прогноз.
· Оптимизация.
· Память, адресуемая по содержанию (ассоциативная память).
· Управление.
2. Чем рекуррентные сети отличаются от прямоточных?
В прямоточных сетях нейроны не связаны обратными связями, т. е. в таких сетях не образуется петель и информация передаётся в одном направлении.
3. Что такое адаптация ИНС?
Это настройка ИНС для решения той или иной задачи. Производится настройки весов (если необходимо), обучения, эпох. Сеть создается с учетом входных данных.
4. Какие НС называют самоорганизующимися?
К таким сетям можно отнести карту Кахонена. Самоорганизующаяся карта Кохонена (англ. Self-organizing map — SOM) — соревновательная нейронная сеть с обучением без учителя, выполняющая задачу визуализации и кластеризации. Является методом проецирования многомерного пространства в пространство с более низкой размерностью (чаще всего, двумерное), применяется также для решения задач моделирования, прогнозирования и др.
5. Какие сети называют полносвязными?
В полносвязной сети каждый нейрон передает свой выходной сигнал остальным нейронам, включая самого себя. Выходными сигналами сети могут быть все или некоторые выходные сигналы нейронов после нескольких тактов функционирования сети.
Все входные сигналы подаются всем нейронам. Элементы слоистых и полносвязных сетей могут выбираться по-разному.
Для полносвязной сети входной сумматор нейрона фактически распадается на два: первый вычисляет линейную функцию от входных сигналов сети, второй линейную функцию от выходных сигналов других нейронов, полученных на предыдущем шаге.
Функция активации нейронов (характеристическая функция) это нелинейный преобразователь выходного сигнала сумматора. Если функция одна для всех нейронов сети, то сеть называют однородной (гомогенной).


