Нейронні мережі для допитливих програмістів (з прикладом на c#)

Так як в заголовку був відзначений «для допитливих програмістів», хочу сказати, що і моя цікавість привела до того, що я, будучи розробником мобілних ігор, написав такий пост. Я абсолютно впевнений, що знайдуться програмісти, які колись думали про штучних интелектах і це дуже хороший шанс для них.

Прочитавши безліч статті по нейронних сетьям, я хотів би відзначити деякі з них, які мені реально допомогли освоїти тему:

приклад на java і корисні посилання
наочна реализацыя з исползованием ООП

Оскільки теорія дуже багато по цій темі хотілося б приступити до реалізації.

Реалізація
using UnityEngine;
using System.Collections;
using System.Xml.Serialization;

public class Neuron {

[XmlAttribute("weight")]
public string data;

[XmlIgnore]
public int[,] weight; // ваги нейронів

[XmlIgnore]
public int minimum = 50; // поріг

[XmlIgnore]
public int row = 64,column = 64;

/**
* Конструктор нейрона, створює весі і встановлює випадкові значення
*/
public Neuron()
{
weight = new int[row,column];
randomizeWeights();
}

/**
* відповіді нейронів, жорстка порогова
* @param input - вхідний вектор
* @return 0 або 1 відповідь
*/
public int transferHard(int[,] input)
{
int Power = 0;
for(int r = 0; r < row;r++)
{
for(int c = 0; c < column;c++)
{
Power += weight[r,c]*input[r,c];
}
}
//Debug.Log("Power: " + Power);
return Power >= minimum ? 1 : 0;
}

/**
* відповіді нейронів з ймовірностями
* @param input - вхідний вектор
* @return n вірогідність
*/
public int transfer(int[,] input)
{
int Power = 0;
for(int r = 0; r < row;r++)
for(int c = 0; c < column;c++)
Power += weight[r,c]*input[r,c];

//Debug.Log("Power: " + Power);
return Power;
}

/**
* встановлює довільні початкові значення ваг 
*/
void randomizeWeights()
{
for(int r = 0; r < row;r++)
for(int c = 0; c < column;c++)
weight[r,c] = Random.Range(0,10);
}

/**
* змінює ваги нейронів
* @param input - вхідний вектор
* @param d - різниця між виходом нейрона і потрібним виходом
*/
public void changeWeights(int[,] input,int d)
{
for(int r = 0; r < row;r++)
for(int c = 0; c < column;c++)
weight[r,c] += d*input[r,c];
}

public void prepareForSerialization()
{
data = "";
for(int r = 0; r < row;r++)
{
for(int c = 0; c < column;c++)
{
data += weight[r,c] + " ";
}
data += "\n";
}
}

public void onDeserialize()
{
weight = new int[row,column];

string[] rows = data.Split(new char[]{'\n'});
for(int r = 0; r < row;r++)
{
string[] columns = rows[r].Split(new char[]{' '});
for(int c = 0; c < column;c++)
{
weight[r,c] = int.Parse(columns[c]);
}
}
}
}



Клас нейронів який містить weight — двійковий масив ваг, minimum — поріг функції. Функція transferHard повертає відповідь на вхідний вектор. Поськольку відповідь функції жорсткий, я использоваю його для навчання. На мій погляд, це більш ефективно навчає нейрони. Я буду дуже блогодарен якщо будуть відгуки з цього приводу. Функція transfer повертає відповідь на вхідний вектор але з ймовірністю, сума може бути ближче до нуля або негативною якщо нейрон навчений для іншої символу.

using UnityEngine;
using System.Collections;
using System.Xml.Serialization;
using System.Xml;
using System.IO;

public class NeuralNetwork {

[XmlArray("Neurons")]
public Neuron[] neurons;

/**
* Конструктор мережі створює нейрони
*/
public NeuralNetwork()
{
neurons = new Neuron[10];

for(int i = 0;i<neurons.Length;i++)
neurons[i] = new Neuron();
}

/**
* функція розпізнавання символу, що використовується для навчання
* @param input - вхідний вектор
* @return масив з нуллей і одиниць, відповіді нейронів
*/
int[] handleHard(int[,] input)
{
int[] output = new int[neurons.Length];
for(int i = 0;i<output.Length;i++)
output[i] = neurons[i].transferHard(input);

return output;
}

/**
* функція розпізнавання символу, що використовується для конечново відповіді
* @param input - вхідний вектор
* @return масив ймовірностей, відповіді нейронів
*/
int[] handle(int[,] input)
{
int[] output = new int[neurons.Length];
for(int i = 0;i<output.Length;i++)
output[i] = neurons[i].transfer(input);

return output;
}

/**
* відповідь мережі
* @param input - вхідний вектор
* @return індекс нейронів призначений для конкретного символу
*/
public int getAnswer(int[,] input)
{
int[] output = handle(input);
int maxIndex = 0;
for(int i = 1; i < output.Length;i++)
if(output[i] > output[maxIndex])
maxIndex = i;

return maxIndex;
}

/**
* функція навчання
* @param input - вхідний вектор
* @param correctAnswer - правильна відповідь
*/
public void study(int[,] input,int correctAnswer)
{
int[] correctOutput = new int[neurons.Length];
correctOutput[correctAnswer] = 1;

int[] output = handleHard(input);
while(!compareArrays(correctOutput,output))
{
for(int i = 0; i < neurons.Length;i++)
{
int dif = correctOutput[i]-output[i];
neurons[i].changeWeights(input,dif);
}
output = handleHard(input);
}
}

/**
* порівняння двох вектор
* @param true - якщо масиви однакові, false - якщо немає
*/
bool compareArrays(int[] a,int[] b)
{
if(a.Length != b.Length)
return false;

for(int i = 0;i<a.Length;i++)
if(a[i] != b[i])
return false;

return true;
}

void prepareForSerialization()
{
foreach(Neuron n in neurons)
n.prepareForSerialization();
}

void onDeserialize()
{
foreach(Neuron n in neurons)
n.onDeserialize();
}

public void saveLocal()
{
prepareForSerialization();

XmlSerializer serializer = new XmlSerializer(this.GetType());
FileStream stream = new FileStream(Application.dataPath + "/NeuralNetwork.txt", FileMode.Create);
XmlWriter writer = new XmlTextWriter(stream, new System.Text.ASCIIEncoding());
using(writer)
{
serializer.Serialize(writer, this);
}
}

public static NeuralNetwork fromXml()
{
string xml = "";
FileStream fStream = new FileStream(Application.dataPath + "/NeuralNetwork.txt",
FileMode.OpenOrCreate);
if(fStream.Length > 0)
{
byte[] tempData = new byte[fStream.Length];
fStream.Read(tempData, 0, tempData.Length);

xml = System.Text.Encoding.ASCII.GetString(tempData);
}
fStream.Close();

if(string.IsNullOrEmpty(xml))
return new NeuralNetwork();


NeuralNetwork data;

XmlSerializer serializer = new XmlSerializer(typeof(NeuralNetwork));
using(TextReader reader = new StringReader(xml))
{
data = serializer.Deserialize(reader) as NeuralNetwork;
}

data.onDeserialize();

return data;
}
}



Клас NeuralNetwork містить масив нейронів, кожний з них призначений для конкретного символу. У конструкторі створюється масив із десяти елементів, тому що приклад зроблений для разпознования цифр(0-9). Якщо ви хочете використовувати мережу для розпізнавання літер то поміняйте розмір масиву відповідним чином. Функція handleHard викликається для навчання нейронів, повертає масив з нуллей і одиниць. Функція getAnswer відповідь мережі для вхідного вектора, використовує функцію handle для отримання масиву відповідей, кожен елемент масиву містить відповідь нейрона з імовірністю. Функція getAnswer вибирає індекс елемента, який містить найбільшу ймовірність і повертає його як відповідь.

Після вивчення численної літератури я дізнався, що активатр можна реалізувати з допомогою Сигмоїдною передатною функцією, каторая посилює слабкі сигнали і притримує сильні, але неможу зрозуміти яким чином це буде відображатися на поліпшення розпізнавання символів.

Хотілося б побачити приклад розпізнавання символів за допомогою Радиално Базисної функції, так так майже скрізь говориться, що за допомогою цього методу поліпшується розпізнавання.

Висновок
У висновку хотілося сказати, що для кращого розуміння кодів нейронних мереж раджу трохи почитати літератури і спробувати самостійно розв'язати завдання такого типу, починаючи з примітивного одношарового пердептрона.

Хотілося побачити різні відгуки на тему і на посаду.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.