Так как в заголовке был отмечен «для любопытных программистов», хочу сказать, что и моё любопытство привело к тому, что я, будучи разработчиком мобильных игр, написал такой пост. Я совершенно уверен, что найдутся программисты, которые когда-то думали об искусственных интеллектах и это очень хороший шанс для них.
Прочитав множество статьей про нейронных сетях, я хотел бы отметить некоторые из них, которые мне реально помогли освоить тему:
пример на java и полезные ссылки
наглядная реализация с использованием ООП
Поскольку теории очень много по этой теме хотелось бы приступить к реализации.
Класс нейронов который содержит weight — двоичный массив весов, minimum — порог функции. Функция transferHard возвращает ответ на входной вектор. Поскольку ответ функции жесткий, я использую его для обучения. На мой взгляд это более эффективно обучает нейроны. Я буду очень благодарен если будут отзывы по этому поводу. Функция transfer возвращает ответ на входной вектор но с вероятностью, сумма может быть ближе к нулю или отрицательной если нейрон обучен для другого символа.
Класс NeuralNetwork содержит массив нейронов, каждый из них предназначен для конкретного символа. В конструкторе создается массив из десяти элементов, потому что пример сделан для распознавания цифр(0-9). Если вы хотите использовать сеть для распознавания букв то поменяйте размер массива соответствующим образом. Функция handleHard вызывается для обучение нейронов, возвращает массив из нулей и единиц. Функция getAnswer ответ сети для входного вектора, использует функцию handle для получения массива ответов, каждый элемент массива содержит ответ нейрона с вероятностью. Функция getAnswer выбирает индекс элемента который содержит наибольшую вероятность и возвращает эго как ответ.
После изучения многочисленной литературы я узнал, что активатор можно реализовать с помощью Сигмоидальной передаточной функции, которая усиливает слабые сигналы и придерживает сильные, но не могу понять каким образом это будет отражаться на улучшение распознавания символов.
Хотелось бы увидеть пример распознавания символов с помощью Радиально Базисной функции, так так почти везде говорится, что с помощью этого метода улучшается распознавание.
В заключении хотелось сказать, что для лучшего понимания кодов нейронных сетей советую немного почитать литературы и попытаться самостоятельно решить задачи такого типа, начиная с примитивного однослойного перцептрона.
Хотелось увидеть различные отзывы на тему и на пост.
Прочитав множество статьей про нейронных сетях, я хотел бы отметить некоторые из них, которые мне реально помогли освоить тему:
пример на 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 выбирает индекс элемента который содержит наибольшую вероятность и возвращает эго как ответ.
После изучения многочисленной литературы я узнал, что активатор можно реализовать с помощью Сигмоидальной передаточной функции, которая усиливает слабые сигналы и придерживает сильные, но не могу понять каким образом это будет отражаться на улучшение распознавания символов.
Хотелось бы увидеть пример распознавания символов с помощью Радиально Базисной функции, так так почти везде говорится, что с помощью этого метода улучшается распознавание.
Заключение
В заключении хотелось сказать, что для лучшего понимания кодов нейронных сетей советую немного почитать литературы и попытаться самостоятельно решить задачи такого типа, начиная с примитивного однослойного перцептрона.
Хотелось увидеть различные отзывы на тему и на пост.
Поделиться с друзьями
Комментарии (14)
Stawros
24.08.2016 19:32+2После изучении многочисленной литературы я узнал, что активатр можно реализовать с помощю Сигмоидальной передаточной функцией, каторая усиливает слабые сигналы и придерживает сильные, но немогу понять каким образом это будет отражаться на улучшение распознавания символов.
Суть в том, что при очередном этапе в процессе обучения сигнал не должен значительно сдвигать веса нейронов, иначе любой шум будет настолько разбалтывать значение весов, что сеть не сможет нормально обучиться. И наоборот, в случае, когда обучение почти завершено, правильные сигналы будут давать слишком маленькую дельту и сетка не выйдет из некоторого локального минимума, а остановится скажем в 93% распознавания, хотя при той же структуре можно было бы добиться, скажем, 97%. Сигмоидальная функция поможет сгладить экстремумы, позволяя сети (в процессе обучения) найти экстремумы.
Sinatr
25.08.2016 16:44Перед тем, как показывать код кому-то, следовало бы немножко подготовиться (в частности прочитать вот это). Если вы пишете о чем-то специфическом, то может быть не следует отвлекать читателя, к примеру, деталями реализации чего-то отвлеченного (в частности XML сериализации). Стиль комментариев тоже «необычный», к тому же присутствует полное отсутствие комментариев в коде.
Извините за придирки, но вы сами попросили отзывов, а меня лично такой стиль исходников немного раздражает (возможно потому, что в моей команде из 4 программистов два пришли из С и продолжают забивать болт на C# стиль и коментарии, уже накипело).
Idot
25.08.2016 17:40Хотелось бы увидеть пример распознавание символов с помощью Радиально Базисной функции
Мне бы хотелось пример простенького AI для простенькой игры на нейросетях. Например, хотя бы игру в крестики-нолики.
ZOXEXIVO
Вы определитесь, все-таки: Pascal или Camel case вы используете
Ununtrium
В C# для методов используется только PascalCase — https://msdn.microsoft.com/en-us/library/ms229043(v=vs.110).aspx.
hakandr
Исправил кое какие переменные. Просто до этого писал на Java поэтому использовал Camel case.