Так как в заголовке был отмечен «для любопытных программистов», хочу сказать, что и моё любопытство привело к тому, что я, будучи разработчиком мобилных игр, написал такой пост. Я совершенно уверен, что найдутся программисты, которые когда-то думали об искусственных интелектах и это очень хороший шанс для них.
Прочитав множество статьей по нейронным сетьям, я хотел бы отметить некоторые из них, которые мне реально помогли освоить тему:
пример на 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 выбирает индекс элемента который содержит наибольшую вероятность и возвращает эго как ответ.
После изучении многочисленной литературы я узнал, что активатр можно реализовать с помощю Сигмоидальной передаточной функцией, каторая усиливает слабые сигналы и придерживает сильные, но немогу понять каким образом это будет отражаться на улучшение распознавания символов.
Хотелось бы увидеть пример распознавание символов с помощю Радиално Базисной функции, так так почти везде говорится, что с помощью этого метода улучшается распознавания.
Заключение
В заключении хотелось сказать, что для лучшего понимания кодов нейронных сетей советую немного почитать литературы и попытаться самостоятельно решить задачи такого типа, начиная с примитивного однослойного пердептрона.
Хотелось увидеть различные отзывы на тему и на пост.
Автор: hakandr