...

среда, 24 августа 2016 г.

[Из песочницы] Нейронные сети для любопытных программистов (с примером на 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)

    Let's block ads! (Why?)

    Комментариев нет:

    Отправить комментарий