Так как в заголовке был отмечен «для любопытных программистов», хочу сказать, что и моё любопытство привело к тому, что я, будучи разработчиком мобильных игр, написал такой пост. Я совершенно уверен, что найдутся программисты, которые когда-то думали об искусственных интеллектах и это очень хороший шанс для них.
Прочитав множество статьей про нейронных сетях, я хотел бы отметить некоторые из них, которые мне реально помогли освоить тему:
пример на 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 += " "; } } public void onDeserialize() { weight = new int[row,column]; string[] rows = data.Split(new char[]{' '}); 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 выбирает индекс элемента который содержит наибольшую вероятность и возвращает эго как ответ.
После изучении многочисленной литературы я узнал, что активатор можно реализовать с помощью Сигмоидальной передаточной функцией, которая усиливает слабые сигналы и придерживает сильные, но не могу понять каким образом это будет отражаться на улучшение распознавания символов.
Хотелось бы увидеть пример распознавание символов с помощью Радиально Базисной функции, так так почти везде говорится, что с помощью этого метода улучшается распознавания.
Заключение
В заключении хотелось сказать, что для лучшего понимания кодов нейронных сетей советую немного почитать литературы и попытаться самостоятельно решить задачи такого типа, начиная с примитивного однослойного перцептрона.
Хотелось увидеть различные отзывы на тему и на пост.
Источник: habrahabr.ru
Комментарии: