2016年7月18日月曜日

はじめての人工知能入門の前段階のはなしと実装例(4)

過去の回を通してニューラルネットワークの仕組みと使い方の方向性について書いてきた。

今回は学習させたニューラルネットワークの保存と復元、
3階層だったのをN階層まで可変できるようにしたソースコードを置いておきます。

次回はこれを使ってニューラルネットワークの様々な使い方について記載予定です。


using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Text;
using System.Collections;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            NeuralNetwork nn;

            var data1 = new List()
            {
                new TeachingData(new float[] { 0.0f, 0.0f }, new float[] { 0.0f }),
                new TeachingData(new float[] { 0.0f, 1.0f }, new float[] { 1.0f }),
                new TeachingData(new float[] { 1.0f, 0.0f }, new float[] { 1.0f }),
                new TeachingData(new float[] { 1.0f, 1.0f }, new float[] { 0.0f }),
            };

            var data2 = new List()
            {
                new TeachingData(new float[] { 0.0f, 0.0f, 0.0f }, new float[] { 0.0f }),
                new TeachingData(new float[] { 0.0f, 0.0f, 1.0f }, new float[] { 1.0f }),
                new TeachingData(new float[] { 0.0f, 1.0f, 0.0f }, new float[] { 1.0f }),
                new TeachingData(new float[] { 0.0f, 1.0f, 1.0f }, new float[] { 0.0f }),
                new TeachingData(new float[] { 1.0f, 0.0f, 0.0f }, new float[] { 1.0f }),
                new TeachingData(new float[] { 1.0f, 0.0f, 1.0f }, new float[] { 0.0f }),
                new TeachingData(new float[] { 1.0f, 1.0f, 0.0f }, new float[] { 0.0f }),
                new TeachingData(new float[] { 1.0f, 1.0f, 1.0f }, new float[] { 1.0f }),
            };

            if (File.Exists("neuron.json"))
            {
                nn = Json.Load("neuron.json");
                Show(nn, data2);
            }
            else
            {
                nn = NeuralNetwork.CreateInstance(3, 3, 1);     //ニューロン作成
                nn.Teach(data2);                                //学習
                Show(nn, data2);                                //学習結果
                Json.Save(nn, "neuron.json");                   //保存
            }
        }

        static void Show(NeuralNetwork nn, List data)
        {
            foreach (var dat in data)
            {
                Console.Write("[");
                foreach (var output in dat.Outputs)
                {
                    Console.Write(" " + output.ToString("F2"));
                }
                Console.Write(" ]");
                nn.Calculate(dat.Inputs);
                Console.WriteLine(nn.Dump());
            }

            Console.ReadKey();
        }
    }

#region "neuron"
    public class TeachingData
    {
        public float[] Inputs { get; set; }
        public float[] Outputs { get; set; }

        public TeachingData(float[] inputs, float[] outputs)
        {
            Inputs = inputs;
            Outputs = outputs;
        }
    }

    [DataContract]
    public class NeuralNetwork
    {
        static Random random = new Random();
        static public float GetRandom() => (float)(Math.Sign(random.NextDouble() - 0.5) * random.NextDouble());
        static ILossFunc LossFunc = new Sigmoid();
        public static float Activate(float input) => LossFunc.Activate(input);
        public static float DeActivate(float result) => LossFunc.DeActivate(result);
        public float Gain { get; set; }

        [DataMember] public List neurons { get; set; } = new List();

        public static NeuralNetwork CreateInstance(params int[] neuronArrays)
        {
            var result = new NeuralNetwork();
            var prevCount = 0;
            foreach (var neurons in neuronArrays)
            {
                result.neurons.Add(Neurons.Create(neurons, prevCount));
                prevCount = neurons;
            }
            return result;
        }

        public void Calculate(params float[] signals)
        {
            for (var i = 0; i < signals.Length; i++) neurons[0][i].Output = signals[i];
            for (var i = 1; i < neurons.Count; i++) foreach (var hidden in neurons[i]) (hidden).Calc(neurons[i - 1]);
        }

        public void Teach(List data)
        {
            var sum = 0.0f;
            var idx = neurons.Count - 1;
            Gain = 1.0f;

            do
            {
                sum = 0.0f;
                foreach (var dat in data)
                {
                    Calculate(dat.Inputs);
                    for (var i = 0; i < dat.Outputs.Length; i++)
                    {
                        var diff = dat.Outputs[i] - neurons[idx][i].Output;
                        if (0.0 < Math.Abs(diff))
                        {
                            neurons[idx][i].Teach(this, idx, diff);
                        }
                        sum += Math.Abs(diff);
                    }
                }
                sum /= data.Count;
                Gain = sum;
                if (1.0f < Gain)
                {
                    Gain = 1.0f;
                }
                else if(0.2f > Gain)
                {
                    Gain = 0.2f;
                }
            }
            while (0.04f < sum);
        }

        public override string ToString() => Json.ToJson(this);

        public string Dump()
        {
            var result = "";
            neurons.ForEach(_ => result += $" => {_.ToString()}");
            return result;
        }
    }

    [DataContract]
    public class Neurons : IEnumerable
    {
        public IEnumerator GetEnumerator() => Data.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

        [DataMember]
        public List Data { get; set; } = new List();
        public static Neurons Create(int neurons, int weights)
        {
            var result = new Neurons();
            for (var i = 0; i < neurons; i++)
            {
                var hidden = new Neuron();
                for (var j = 0; j < weights; j++) hidden.Weights.Add(NeuralNetwork.GetRandom());
                result.Data.Add(hidden);
            }
            return result;
        }


        public Neuron this[int index] => Data[index];

        public override string ToString()
        {
            var result = "";
            Data.ForEach(_ => result += (0 > _.Output) ? $" {_.Output.ToString("F2")}" : $" +{_.Output.ToString("F2")}");
            return $"[{result} ]";
        }
    }

    [DataContract]
    public class Neuron
    {
        [DataMember] public float Bias { get; set; } = NeuralNetwork.GetRandom();
        [DataMember] public List Weights { get; set; } = new List();
        public float Output { get; set; }
        public void Teach(NeuralNetwork nn, int layer_no, float value)
        {
            var _value = value * NeuralNetwork.DeActivate(Output) * nn.Gain;
            for (var i = 0; i < Weights.Count; i++)
            {
                Weights[i] += _value * nn.neurons[layer_no - 1][i].Output;
                if (1 < layer_no) nn.neurons[layer_no - 1][i].Teach(nn, layer_no - 1, _value);
            }
            Bias += _value;
        }

        public float Calc(Neurons prev_neurons)
        {
            Output = Bias;
            for (var i = 0; i < Weights.Count; i++) Output += Weights[i] * prev_neurons[i].Output;
            Output = NeuralNetwork.Activate(Output);
            return Output;
        }

        public override string ToString() => Json.ToJson(this);
    }
#endregion
#region "lossFunc"
    public interface ILossFunc
    {
        float Activate(float input);
        float DeActivate(float result);
    }

    public class Sigmoid : ILossFunc
    {
        public float Activate(float input) => 1.0f / (1.0f + (float)Math.Exp(-input));
        public float DeActivate(float result) => result * (1.0f - result);
    }

    public class Tanh : ILossFunc
    {
        public float Activate(float input) => (float)Math.Tanh(input);
        public float DeActivate(float result) => (1.0f - result * result);
    }

    public class ReLU : ILossFunc
    {
        public float Activate(float input) => Math.Max(0.0f, input);
        public float DeActivate(float result) => (0.0f < result) ? 1.0f : 0.0f;
    }

    public class Softsign : ILossFunc
    {
        public float Activate(float input) => input / (1.0f + Math.Abs(input));
        public float DeActivate(float result) => (float)Math.Pow(1.0f - Math.Abs(result), 2);
    }
#endregion
#region "json"
    public static class Json
    {
        public static T Load(string filename)
        {
            if (File.Exists(filename))
            {
                return ToObject(File.ReadAllText(filename));
            }
            else
            {
                return (T)Activator.CreateInstance(typeof(T));
            }
        }

        public static void Save(T model, string filename)
        {
            File.WriteAllText(filename, ToJson(model));
        }

        static T ToObject(string jsonString)
        {
            var serializer = new DataContractJsonSerializer(typeof(T));
            var jsonBytes = Encoding.UTF8.GetBytes(jsonString);
            using (var sr = new MemoryStream(jsonBytes))
            {
                return (T)serializer.ReadObject(sr);
            }
        }

        public static string ToJson(T model)
        {
            if (null == model)
            {
                return "{}";
            }
            using (var ms = new MemoryStream())
            {
                new DataContractJsonSerializer(typeof(T)).WriteObject(ms, model);
                var bytes = ms.ToArray();
                return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
            }
        }
    }
#endregion
}

0 件のコメント:

Androider