2016年7月12日火曜日

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



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

前回は人工知能とは何かというテーマから何ができるのか
それから極小ニューラルネットワークの骨組みの実装まで行いました。

今回は前回のソースコードに「シグモイド関数」と、
学習させるのに使用する「誤差逆伝播法」の説明と実装を行います。

---------------------------------------

Q.シグモイド関数って何?
 ニューロンの反応特性です。
 アークタンジェントを90度ずらしたような曲線になっていて
 1と0の中間も表せる様にした曲線だと思ってOK

Q.誤差逆伝播法って何?
 ニューロンで求めた結果と実際の結果の誤差をニューロン全体に教えて
 内部パラメータをそれに合うように補正させる方法です。
 これとシグモイド関数が合わさる事で学習(=内部パラメータの最適化)が行われます。


---------------------------------------

では実装。
前回は適当な出力特性と学習方法を用いたので上手くいってなかったですが
今回はちゃんとした計算式を使います。
一体内部で何がおきているのか?を
前回と今回の学習過程を見てチェックしてみると理解が進みます。

    class Program
    {
        static void Main(string[] args)
        {
            var core = new NeuralNetwork(2, 8, 1);

            // 学習
            for (int i = 0; i < 10000; i++)
            {
                core.SetSignal(0.0f, 0.0f);
                core.Teaching(0.0f);

                core.SetSignal(0.0f, 1.0f);
                core.Teaching(1.0f);

                core.SetSignal(1.0f, 0.0f);
                core.Teaching(1.0f);

                core.SetSignal(1.0f, 1.0f);
                core.Teaching(0.0f);
            }

            // 出力
            core.SetSignal(0.0f, 0.0f);
            Console.WriteLine($"0 0 => {core.Outputs[0].Output}");

            core.SetSignal(0.0f, 1.0f);
            Console.WriteLine($"0 1 => {core.Outputs[0].Output}");

            core.SetSignal(1.0f, 0.0f);
            Console.WriteLine($"1 0 => {core.Outputs[0].Output}");

            core.SetSignal(1.0f, 1.0f);
            Console.WriteLine($"1 1 => {core.Outputs[0].Output}");

            Console.WriteLine("===");

            Console.ReadKey();
        }
    }

    public class NeuralNetwork
    {
        public List Inputs { get; } = new List();
        public List Hiddens { get; } = new List();
        public List Outputs { get; } = new List();

        public NeuralNetwork(int in_counts, int hidden_counts, int out_counts)
        {
            for (var idx = 0; idx < in_counts; idx++) Inputs.Add(new InputNeural());
            for (var idx = 0; idx < hidden_counts; idx++) Hiddens.Add(new Neural(Inputs.ToArray()));
            for (var idx = 0; idx < out_counts; idx++) Outputs.Add(new Neural(Hiddens.ToArray()));
        }

        public void SetSignal(params float[] values)
        {
            for (var idx = 0; idx < values.Length; idx++) ((InputNeural)Inputs[idx]).Input = values[idx];
        }

        public void Teaching(params float[] results)
        {
            for (var out_idx = 0; out_idx < Outputs.Count; out_idx++) Outputs[out_idx].Feedback(results[out_idx] - Outputs[out_idx].Output);
        }
    }

    public interface INeural
    {
        float Input { get; }
        float Output { get; }
        void Feedback(float value);
    }

    public class InputNeural : INeural
    {
        public float Input { get; set; }
        public float Output => Input;
        public void Feedback(float value) { }
    }

    public class Neural : INeural
    {
        static Random rnd = new Random(DateTime.Now.Millisecond);
        List Inputs { get; } = new List();
        float Weight { get; set; }
        float Bias { get; set; }
        public float Input => Inputs.Sum(_ => _.Output);
        public float Output => (Input + Bias).Sigmoid();

        public Neural(params INeural[] neurons)
        {
            Inputs.AddRange(neurons.Select(_ => new Synapse(_, rnd.NextInitialValue())));
            Bias = rnd.NextInitialValue();
        }
        public void Feedback(float value)
        {
            var output = Output;
            var _signal = value * output * (1.0f - output);
            Inputs.ForEach(_ => _.Feedback(_signal));
            Bias += _signal / 2.0f;
        }
    }

    public class Synapse : INeural
    {
        INeural sender { get; }
        float Weight { get; set; }
        public float Input => sender.Output;
        public float Output => Input * Weight;

        public Synapse(INeural neural, float weight)
        {
            sender = neural;
            Weight = weight;
        }

        public void Feedback(float value)
        {
            var _value = value * Input / 2.0f;
            Weight += _value;
            sender.Feedback(_value);
        }
    }

    public static class RandomExtentions
    {
        public static float NextFloat(this Random self) => (float)self.NextDouble();
        public static float NextInitialValue(this Random self) => Math.Sign(self.NextFloat() - 0.5f) * self.NextFloat();
    }

    public static class floatExtentions
    {
        public static float Sigmoid(this float value) => 1.0f / (1.0f + (float)Math.Exp(-value));
    }




コメントを投稿

Androider