- класс нейросети и средств ее обработки;
- класс перевода текста в двоичный вид;
- класс хеш-таблицы и методов работы с ней;
- класс разбиения текста на лексемы и распознавания.
Созданные классы будут оформлены в виде библиотечного компонента.
Для тестирования созданного класса будет создано Windows-приложение с использованием визуальных компонентов. Тестирующая программа должна осуществлять ввод текстовой информации из файла и с клавиатуры, а также ввод параметров нейросети: количество эпох обучения, количество нейронов скрытого слоя, момент и коэффициент обучения.
2. Проектирование библиотеки классов для реализации нейросети и тестовой программы
2.1 Программная реализация нейросети обратного распространения ошибки
Алгоритм был реализован в виде класса NeuroNetwork. UML диаграмма класса представленна на рисунке 6.
Класс NeuroNetwork |
Закрытые поля |
string filename; |
double[,] INP_PATTERNS; |
double[,] OUT_PATTERNS; |
int MAX_INP; |
int MAX_HID; |
int MAX_OUT; |
int MAX_PAT; |
double[] test_pat; |
double[] desired; |
neuron_type[] ipl; |
neuron_type[] hl; |
neuron_type[] ol; |
double BETA; |
double M; |
int num_cycles; |
Закрытые методы |
private double sigmoid(double x) |
private void run_input_layer() |
private void run_hidden_layer() |
private void run_output_layer() |
private void run_the_network() |
private void display_the_results(out string[] outp) |
private void AddWeightsToFile() |
private void blank_changes() |
private void calculate_output_layer_errors() |
private void calculate_hidden_layer_errors() |
private void calculate_input_layer_errors() |
private void weight_change() |
private void back_propagate() |
private void ExtractWeights() |
Открытые методы |
public void random_weights() |
public void get_test_pattern(double[] tests) |
public void train_the_network() |
public string[] test_the_network(double[] test) |
Public NeuroNetwork(double[,] INP_PATTERNS1, double[,] OUT_PATTERNS1, int Max_inp, int N_HID, int Max_pat, double beta, double m,int Epoch,string name,bool indicate) |
Рисунок 6 – UML-диаграмма класса NeuroNetwork
Параметры конструктора:
- обучающая выборка - INP_PATTERNS;
- желаемый выход - OUT_PATTERNS;
- момент - M;
- коэффициент обучения - BETA;
- количество нейронов скрытого слоя - N_HID;
- количество нейронов входного слоя - Max_inp;
- количество образцов, содержащихся в обучающей выборке - Max_par;
- количество эпох обучения - Epoch;
- имя файла с обучающей выборкой - name;
- логическая пременная indicate, сигнализирующая о необходимости обучения сети.
Ввод тестируемой выборки производится посредствам метода get_test_pattern(double[] tests).
Для описания нейрона была создана структура neuron_type, содержащая следующие поля:
- список весовых коэффициентов для связей между данным нейроном и всеми нейронами предыдущего слоя (или входными данными, если нейрон находится во входном слое). Каждый весовой коэффициент - действительное число (по 1 весовому коэффициенту на нейрон предыдущего слоя);
- пороговый уровень;
- значение ошибки, используется только на стадии обучения;
- изменение ошибки, также используется только во время обучения.
Описание нейрона привидено ниже:
struct neuron_type
{
public double[] w; //весовые коэффициенты
public double[] change;//модификация весовых коэффициентов используется в процессе обучения
public double threshold, a; //а-сигнал на выходе нейрона,threshold-значение порога
public double t_change; //модификация порога используется в процессе обучения
public double E; //значениеошибки
}
Выходной сигнал нейрона хранится в поле a (так называемая активность нейрона). Нейрон должен отреагировать на входной сигнал, поступающий по взвешенным связям, вычислив при этом выходной сигнал. Для трансформации входных сигналов в выходные необходима функция.
Ниже приведена декларация узлов с помощью закрытых переменных. Постоянные обозначают количество нейронов во входном, скрытом и выходном слое:
int MAX_INP; //Количество нейронов в входном слое
int MAX_HID; //Количество нейронов в скрытом слое
int MAX_OUT; //Количество нейронов в выходном слое
neuron_type[] ipl; //Входной слой
neuron_type[] hl; //Скрытый слой
neuron_type[] ol; //Выходной слой Функция активации (2) была реализована в открытом методе sigmoid. В данном метода используется формула (3). Код на С# для этого метода:private double sigmoid(double x)
{ return 1 / (1 + Math.Exp(-x)); }
Однако функция Exp приводит к ошибке в программе, если входное значение выходит из промежутка -39..38. К счастью, эти значения настолько далеко отстоят от начала координат, что мы можем считать: при аргументе < -39 значение функции равно 0, при аргументе > 38, - значение функции равно 1. Для предотвращения ошибки добавим несколько строчек:
private double sigmoid(double x)
{ if (Math.Abs(x) < 38) //проверка условия нахождения функции в интервале -39..38
{ return 1 / (1 + Math.Exp(-x)); }
else
{ if (x >= 38)
{ return 1; }
else { return 0; } } }
Биологические нейроны не срабатывают (не выдают выходной сигнал) до тех пор, пока уровень входного сигнала не достигнет некоторого порогового значения, т.е. на вход нейрона поступает сумма взвешенных сигналов минус некоторая величина. Полученное значение проходит через активационную функцию.
Каждая активность нейрона предыдущего слоя умножается на соответствующий весовой коэффициент, результаты умножения суммируются (1), вычитается пороговое значение, вычисляется значение сигмоидной функции (5). Вот пример на С#:
Вычисление суммы взвешенных сигналов для входного набора данных
private void run_input_layer()
{ double sum = 0;
for (int i = 0; i < MAX_INP; i++)
{ sum = 0;
for (int j = 0; j < MAX_INP; j++)
{ sum = sum + ipl[i].w[j] * test_pat[j];//(1) }
ipl[i].a = sigmoid(sum - ipl[i].threshold);//(5) } }
Вычисление суммы взвешенных сигналов для скрытого и выходного слоев (методы run_hidden_layer и run_output_layer) производится аналогично.
Каждый слой нейронов базируется на выходе предыдущего слоя (за исключением входного слоя, базирующегося непосредственно на предъявляемых сети входных данных (в коде - массив test_pat). Это значит, что значения входного слоя должны быть полностью рассчитаны до вычисления значений скрытого слоя, которые в свою очередь, должны быть рассчитаны до вычисления значений выходного слоя.
Выходы нейронной сети - значения активностей (поле a) нейронов выходного слоя. Программа, симулирующая работу нейронной сети, в процессе обучения будет сравнивать их со значениями, которые должны быть на выходе сети.
Полный алгоритм обучения НС с помощью процедуры обратного распространения строится так:
1. Инициализировать пороговые значения и весовые коэффициенты небольшими случайными величинами (не более 0.4). Инициализация весовых коэффициентов случайными вещественными значениями с помощью класса Random производится в функции random_weights.2. Подать на входы сети один из возможных образов и в режиме обычного функционирования НС, когда сигналы распространяются от входов к выходам, рассчитать значения последних. Метод run_the_networ.
3. Вычислить ошибки для выходного слоя (calculate_output_layer_errors). При этом используем формулу (7) для каждого i-ого значения выходного слоя. Ниже представлена соответствующая функция:
private void calculate_output_layer_errors()
{ for (int j = 0; j < MAX_OUT; j++)
{ ol[j].E = (desired[j] - ol[j].a) * ol[j].a * (1 - ol[j].a); } }
Вычислениеошибкидляскрытогоивходногослоевпроизводитсяметодами calculate_input_layer_errors и calculate_hidden_layer_errors поформуле (8). Кодсоответствеющейфункциидлявходногослояпредтсавленниже:
private void calculate_input_layer_errors()
{
double sum;
for (int i = 0; i < MAX_INP; i++)
{
sum = 0; // {Сумма ошибок скрытого слоя}
for (int j = 1; j < MAX_HID; j++)
{ sum = sum + hl[j].E * hl[j].w[i]; }
ipl[i].E = ipl[i].a * (1 - ipl[i].a) * sum;
}
}
Используя формулы (9), (10), (11) получим функцию, обучающую весовые коэффициенты и пороговые уровни:
private void weight_change()
{ //i обозначает нейрон скрытого слоя, j - выходного
for (int j = 0; j < MAX_OUT; j++) //выходнойслой
{ for (int i = 0; i < MAX_HID; i++) //Подстройка}
{ ol[j].change[i] = BETA * ol[j].E * hl[i].a + M * ol[j].change[i];
ol[j].w[i] = ol[j].w[i] + ol[j].change[i]; }
//Подстройказначенияпорога
ol[j].t_change = BETA * ol[j].E * 1 + M * ol[j].t_change;
ol[j].threshold = ol[j].threshold + ol[j].t_change; // }
//Модифицируем веса между входным слоем и скрытым слоем
…
//Модифицируем веса между входами и входным слоем
… } }
Далее объединим вышеуказанные функции в одном методе back_propogate().
В общем виде функция обучения сети будет выглядеть следующим образом:
public void train_the_network()
{ blank_changes();//Очисткапредыдущихзначений changes}
for (int loop = 0; loop < num_cycles; loop++)
{ for (int pat = 0; pat < MAX_PAT; pat++)
{ for (int i = 0; i < MAX_INP; i++) //Копированиевходногообраза}
{ test_pat[i] = INP_PATTERNS[pat, i]; } /вмассив 'test_pat'
for (int i = 0; i < MAX_OUT; i++) //Копированиевыходногообраза
{ desired[i] = OUT_PATTERNS[pat, i]; } //вмассив'desired'
run_the_network();//Определение выходов сети
back_propagate(); } }
AddWeightsToFile(); }
Для очистки предыдущих значений используется функция blank_changes.
Для упрощения временной сложности работы сети полученные весовые коэффициенты будем записывать в отдельные файлы (метод AddWeightsToFile()), имена которым даются программой автоматически Для считывания сохраненных параметров будет применяться метод ExtractWeights().
2.2 Класс перевода текста в двоичный вид
Данный класс предназначен для бинаризации исходных данных (слов), т.е. перевода слов с естественного языка в набор единиц и нулей. Данная процедура является необходимой, т.к. нейронная сеть обратного распространения ошибки работает только с двоичными данными. UML диаграмма класса Binarization представлена на рисунке 7.
Класс Binarization |
Закрытые поля |
string[] sLetter; |
int iLengthPattern; |
Закрытые методы |
private string DecToBin(string value) |
Открытые методы |
public double[] GetBinarizeWord(string sWord) |
public double[,] GetBinarizeText(out double[,] OUT_PATTERNS, out int max_pat, out int iLengthPattern1) |
public Binarization(string[] sLetter1) |
Рисунок 7 –UML-диаграмма класса Binarization
Параметром конструктора является массив строк для перевода в двоичный вид.
Алгоритм работы методов GetBinarizeWord и GetBinarizeText данного класса в общем состоит из следующих этапов:
- кодировка слова: суммирование произведений ASCII-кодов букв на i+4, где i – номер буквы в слове;