2016年7月27日水曜日

UWP:アプリケーションは、別のスレッドにマーシャリングされたインターフェイスを呼び出しました。 (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

ViewModelにINotifyPropertyChangedインターフェイスを使っていて、用意したTask処理の中でViewModeを更新するとこういったエラーが起きる。

原因は、UIスレッド以外でUIを更新しようとしたからである。
これを対処するにはタスク処理からUIスレッドに処理を実行してもらうことが必要になる。
(データクラスはGet;set;で1行で終わらせられるのを展開して実装しないといけないし、UIスレッドの面倒まで見ないといけないなんてめんどくさいこの上ない。)

この原因を紐解くにはまずTaskの仕組みについて知る必要がある。

~(Task Async Awaitとは)~
 スレッドA(UIスレッド)、スレッドB(別スレッド)の2つあったとしよう。
スレッドAでTaskクラスを実行すると、処理はスレッドBで実行される。
逆にスレッドBでTaskクラスを実行すると、スレッドAになる。
これがTaskクラスで、重たい処理は他スレッドにやらせることが基本動作になる。
Taskクラスの処理が終わりを知るのにawaitを使う。

まとめると、
Task ・・・ 処理の単位
Async ・・・ 別スレッドで動作する処理がある
Await ・・・ 別スレッドからの帰りを待つ

・Taskを作っただけでは別スレッドにはならない。
・タスクをRunさせることでスケジューラに登録されて処理が開始される。


ざっくり書いたがこんな感じに覚えておけば良い。


例でボタンクリックイベントメソッドを使った時の挙動を見てみよう。

OK:そもそも別スレッドでViewModelを更新してないので問題なし

        private void button_Click(object sender, RoutedEventArgs e)
        {
            vm.Update();
        }

NG:ボタンクリックメソッド内のタスク実行したので、別スレッドでUI更新している。

        private async void button_Click(object sender, RoutedEventArgs e)
        {
            await Task.Run(async () =>
            {
                await Task.Delay(1000);
              vm.Update();
            });
        }


OK:ボタンクリックメソッド内でUI更新したので問題なし。
        private async void button_Click(object sender, RoutedEventArgs e)
        {
            await Task.Run(async () =>
            {
                await Task.Delay(1000);
            });
            vm.Update();
        }

OK:2つ上のと違ってUIスレッドで動いているボタンクリックメソッド上でawaitしているので問題なし。

        private async void button_Click(object sender, RoutedEventArgs e)
        {
            await Update();
        }

        async Task Update()
        {
            await Task.Delay(1000);
            vm.Update();
        }


-----

つまり、

・View上のボタンクリックメソッドはタスク処理でないので、UIスレッドで動作しているというのが分かる。
・asyncメソッド内部で複数のTaskが存在していても、それぞれがスケジューラに登録されて実行刷るわけではないので、すべて同スレッドで実行される。
・タスクをRunしたらその時点で別スレッド開始する。

なんとなく分かっただろうか。
少しだけスケジューラについて触れておくと、デバッグ時にタスクウィンドウで現在どのようなタスクが動いているかが見れるようになっていて便利。


最後に別スレッドからUIを更新するには、ViewクラスにCoreDispatcherクラスで定義されたDispatcherプロパティがあるので、これをタスク内にあるUI更新部分を括ると回避する方法を下記にのせておく。


asyncメソッド内
・・・
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
    Update();
);
・・・




2016年7月22日金曜日

IOTしよう

IOTっていったいなに?

これから考える人、携わる人の考えの推移をまとめてみました。

 


IOTのソフト開発スタート
      ↓
とりあえず何でもかんでもハードウェアにある情報はすべてクラウド上に送信しよう
      ↓
サーバサイドでは溜まった世の中のセンサー情報が集まってデータマイニング!
      ↓
インターネット上で新たなサービスが う、生まれる
      ↓
ブラウザさえ開ければどこでもデジタル家電全部の操作ができる。
      ↓
ロボット型家電があれば知識データ(ノウハウ)あればなんでもこなせるよね
      ↓
データって重要だなあ。制御するのも大変だけど


IOTのハード寄りファームウェア開発スタート
      ↓
 IOTってなんだ?マイコンにBluetoothやWifiがつながってデータが飛ばせるのか。
じゃあUARTをちょっといじってデータを外に飛ばそう。
受信したデータで動作を変えるのは従来通りのやり方で通用しそうだ。
      ↓
オンラインアップデートに対応?じゃあブートローダを用意しよう。
      ↓
ネットにつながることがIOTだったのか。



IOTのハード開発スタート
      ↓
専用ハードウェアに外部通信機能をくっつけるだけでいいのね。
ファーム開発者さん用意するから頼んだ。
      ↓
アクチュエーターはモジュール化を進めるので個々に固有IDと通信機能をくっつけてと、
      ↓
ハードウェアはIOTになっても汎用・専用ハードの共存当たり前だね


ざっと考えてみると、IOTとはハードに触らないエンジニアが
今後もハードを触らずにハードの制御ソフトを書く案件のようです。
ハード知らずに制御ソフトを書くなんて、数えきれないくらいのバグが出てきそうなので
組み込みのノウハウは今後もファームウェアの中にしまっておいていた方が無難。

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

人工知能に関する記事で今回が5回目になり最後にしたいと思います。

過去4回に渡りニューロンとは、シナプスとは、学習方法などニューラルネットワークに欠かせないことについて触れてきました。
今回はその集大成でニューラルネットワークに学習させて結果を得るというところまでやりたいと思います。

今回5回目である未知のパラメータが与えて教えた結果の推定値を求めることができるようになるので入門は卒業ですね。
この次によくやっているのはゲームで人工知能を使うことです。
これは評価関数を作ってスコアの高い手を指していくという手法が用いられています。
自動化を目指すならやはりイベントドリブンな構成ですね。
どれも発展途上で確率された方法がない状態ですので、
実務で人工知能投入を考えている方は、やはり時間的な制約があるので
確立された手法でないと実践投入することは難しいです。
というような背景をお持ちの方はトライ&エラーで仕上げるより確実な方法で自分でデータを解析していったほうが良くて、今回の人工知能入門が済んだら別のを勉強することをおススメしたいです。

ではさっそく本題。
前回のソースコードで良かったのですが隠れ層を多段にできるように変更を加えました。
あと簡単な数字認識するデータも用意してあります。
実際に動かしてみるとわかると思いますが、それなりに学習させるには多大な計算量が必要であることが分かります。
Googleの囲碁AIも最先端のパソコンを1000台投入しているのでまだまだ一般的ではないことが分かりますね。
しかし一度学習を済ませたニューラルネットワークはロジック回路と同様の扱いになるのでマイコンみたいな小さなCPU上でも扱えるレベルの計算量であることは補足しておきます。

最後に、人工知能とはパターン認識の1つの手法である。
・パラメータの最適化の事を機械学習と言っていて、まるで自ら勉強してるかのようにとらえられがち。言葉の持つ意味と現実はとてもかけ離れている。
・ディープラーニングという言葉が有名になったけど、あれはGoogleカーに用いる技術で使われている1実装だと思ってよい。
・人工知能で最善手を打つというのは、あくまでパターン認識の延長線上であって自ら考えているのではない。AとくればBというようなKey-Value型で表せるのでニューラルネットワークを使う必要はない。
・もし人工知能が人間のようなふるまいをさせるなら、脳の機能をプログラムして自ら改良して知識空間を拡張させ連結させていく辞書型を構築したほうが現実的である。
・パターン認識が分かったところで裏の意味を理解できない機械はミスリーディングする。

例えば選挙ポスターやTV画面に映っている人の顔を人がいると認識してしまうなど、ホントまだまだな領域であるので、実践投入したいが時間がない方は触れない方が悩みが少なくなってハッピーになれる。

以上、一か月経て到達した答えです。






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

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

            //xor
            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 }),
            };

            //Odd
            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 }),
            };

            //digit
            var data3 = new List()
            {
                new TeachingData(
                    new float[] {
                        1.0f, 1.0f, 1.0f,
                        1.0f, 0.0f, 1.0f,
                        1.0f, 0.0f, 1.0f,
                        1.0f, 0.0f, 1.0f,
                        1.0f, 1.0f, 1.0f,
                    }, new float[] { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }),
                new TeachingData(
                    new float[] {
                        0.0f, 1.0f, 0.0f,
                        0.0f, 1.0f, 0.0f,
                        0.0f, 1.0f, 0.0f,
                        0.0f, 1.0f, 0.0f,
                        0.0f, 1.0f, 0.0f,
                    }, new float[] { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }),
                new TeachingData(
                    new float[] {
                        1.0f, 0.0f, 0.0f,
                        1.0f, 0.0f, 0.0f,
                        1.0f, 0.0f, 0.0f,
                        1.0f, 0.0f, 0.0f,
                        1.0f, 0.0f, 0.0f,
                    }, new float[] { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }),
                new TeachingData(
                    new float[] {
                        0.0f, 0.0f, 1.0f,
                        0.0f, 0.0f, 1.0f,
                        0.0f, 0.0f, 1.0f,
                        0.0f, 0.0f, 1.0f,
                        0.0f, 0.0f, 1.0f,
                    }, new float[] { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }),
                new TeachingData(
                    new float[] {
                        1.0f, 1.0f, 1.0f,
                        0.0f, 0.0f, 1.0f,
                        1.0f, 1.0f, 1.0f,
                        1.0f, 0.0f, 0.0f,
                        1.0f, 1.0f, 1.0f,
                    }, new float[] { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }),

                new TeachingData(
                    new float[] {
                        1.0f, 1.0f, 1.0f,
                        0.0f, 0.0f, 1.0f,
                        1.0f, 1.0f, 1.0f,
                        0.0f, 0.0f, 1.0f,
                        1.0f, 1.0f, 1.0f,
                    }, new float[] { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }),
                new TeachingData(
                    new float[] {
                        0.0f, 1.0f, 0.0f,
                        0.0f, 1.0f, 0.0f,
                        1.0f, 1.0f, 0.0f,
                        1.0f, 1.0f, 1.0f,
                        0.0f, 1.0f, 0.0f,
                    }, new float[] { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }),
                new TeachingData(
                    new float[] {
                        1.0f, 1.0f, 1.0f,
                        1.0f, 0.0f, 0.0f,
                        1.0f, 1.0f, 1.0f,
                        0.0f, 0.0f, 1.0f,
                        1.0f, 1.0f, 1.0f,
                    }, new float[] { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f }),

                new TeachingData(
                    new float[] {
                        1.0f, 1.0f, 1.0f,
                        1.0f, 0.0f, 0.0f,
                        1.0f, 1.0f, 1.0f,
                        1.0f, 0.0f, 1.0f,
                        1.0f, 1.0f, 1.0f,
                    }, new float[] { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f }),
                new TeachingData(
                    new float[] {
                        1.0f, 1.0f, 1.0f,
                        0.0f, 0.0f, 1.0f,
                        0.0f, 1.0f, 0.0f,
                        0.0f, 1.0f, 0.0f,
                        0.0f, 1.0f, 0.0f,
                    }, new float[] { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }),
                new TeachingData(
                    new float[] {
                        1.0f, 1.0f, 1.0f,
                        1.0f, 0.0f, 1.0f,
                        1.0f, 1.0f, 1.0f,
                        1.0f, 0.0f, 1.0f,
                        1.0f, 1.0f, 1.0f,
                    }, new float[] { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f }),
                new TeachingData(
                    new float[] {
                        1.0f, 1.0f, 1.0f,
                        1.0f, 0.0f, 1.0f,
                        1.0f, 1.0f, 1.0f,
                        0.0f, 0.0f, 1.0f,
                        1.0f, 1.0f, 1.0f,
                    }, new float[] { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }),
            };

            if (File.Exists("neuron.json"))
            {
                nn = Json.Load("neuron.json");
                nn.Calculate(new float[]
                {
                        0.0f, 0.0f, 1.0f,
                        0.0f, 0.0f, 1.0f,
                        0.0f, 0.0f, 1.0f,
                        0.0f, 0.0f, 1.0f,
                        0.0f, 0.0f, 1.0f,
                });
                Console.WriteLine(nn.Dump());
                Show(nn, data3);
            }
            else
            {
                nn = NeuralNetwork.CreateInstance(2, 3, 1);  //ニューロン作成
                nn.Teach(data1);                                //学習
                Show(nn, data1);                                //学習結果
                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 public bool GetBool() => 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;
            var count = 0;

            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 = 0.8f;
                }
                else if(0.2f > Gain)
                {
                    Gain = 0.2f;
                }
                count = (count + 1) % 1000;
                if(0 == count) Console.WriteLine(sum);
            }
            while (0.25f < sum);
        }

        void Normalize()
        {
            for (var i = 1; i < neurons.Count; i++)
            {
                neurons[i].Normalize();
            }
        }

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

        public string Dump()
        {
            var result = "";
            neurons.ForEach(_ => result += $" => {_.ToString()}\r\n");
            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} ]";
        }

        public void Normalize()
        {
            var min1 = Data.Select(_ => _.Weights.Min()).Min();
            var max1 = Data.Select(_ => _.Weights.Max()).Max();
            var min2 = Data.Select(_ => _.Bias).Min();
            var max2 = Data.Select(_ => _.Bias).Max();
            var min = Math.Min(min1, min2);
            var max = Math.Max(max1, max2);
            var dif = max - min;
            var gain = 2.0f / dif;
            if (-1.0f > min || max > +1.0f)
            {
                foreach (var dat in Data)
                {
                    dat.Bias = (dat.Bias - min) * gain - 1.0f;
                    for (var i = 0; i < dat.Weights.Count; i++)
                    {
                        dat.Weights[i] = (dat.Weights[i] - min) * gain - 1.0f;
                    }
                }
            }
        }
    }

    [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)
                {
                    if (NeuralNetwork.GetBool())
                    {
                        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
}

2016年7月20日水曜日

[C#]Debug.WriteLineが出力されない

UWP環境においてConsole.WriteLineが使えないのでDebug.WriteLineを使うのだが、
出力ウィンドウに表示されない。。
一体なんだろうと思ったら
イミディエイトウィンドウに出力されるようになっているらしい。
出力ウィンドウはコンソール用、UWPにはコンソール出力は無い
これで把握した。
ちなみにイミディエイトウィンドウは CTRL+ALT+Iで開きます。



参考URL

http://manbou404.hatenablog.com/entry/2015/11/05/000000

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
}

2016年7月15日金曜日

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


の続き。

2回に渡ってニューラルネットワークの説明と実装について書いたので今回はニューラルネットワークの考え方のまとめを行いたいと思います。

人工知能の学習やニューロンの仕組みを学んでいくと、パターン認識であるという事がわかってくると思います。
この機械学習を進めていくと、様々なパターンを組み合わせて認識するディープラーニングというものになっていきます。ディープラーニングは自ら共通項目を見つけ出してラベリングしていくのでもう少し進化してますが現時点はここが最先端になります。

昔からあって今になって話題になったけど、何が変わったかというと

・ニューロンのデザイン
 学習方法(誤差伝播、損失関数)
 →問題と解答を教えることでニューロンを学習する方式から
  自ら入力パターンを見つけ出してパターンの組み合わせから解答する方式へ
 
・ニューロン同士の接続
 →全結合かつ3層構成から
   繰り返し・分岐・状態保持、多層化などが考案されて高度化へ


このような変化がありました。

プログラム的に言い表すと、

・入力データのパターン化、enum型(イベント、状態)
 これは文字認識や音声認識はこれができれば良いのです。

・時系列のパターン認識、Dictionary型
 外部入力からenum型に変換したあと、次に待っているのが時間軸の変化のパターン認識です。
AとくればBというような反応であることからdictionary型だといえます。
これは将棋やオセロ、チェスなどの1手先の読み、Q&A形式のトラブルシューティングなどに当てはまります。
あえて意味とは書かない理由は、機械的に計算された一致率で解答を算出していることが意味を理解しているのか?と思えるからです。

・パターンの組み合わせ、タスク(シーケンス処理
 AとくればB、BときたらCといったようにパターンの掛け合わせでより先にある良い結果の道筋を算出します。これを戦略的判断とは言いづらいですが、数学的に効率的な判断をしていきます。
 これには問題があって、統計情報からは人間みたいに一手間二手間増やしてさらに良い結果を導き出そうという考えに行き着かない当たりがロボットみたいだと思われてくるんだろうと予想されます。

・パターンの並列処理、ディスパッチ処理
 将棋や囲碁でも盤面上で戦っているのは一箇所ではなく複数箇所あって互いのタスク協調動作をしていく部分になります。この領域になると抱えるタスクが増えるごとに人間が負けていきそうです。


特定された状況下で1つの入力に対して作業させることは可能になります。
ゲームやオートメーション化はここまでで良いです。しかし車などは多種多様な入力を受け付けます。そういった時は状態遷移(オートマトン)が必要になってきます。


・様々な入力を同時に受け取った時のパターン化、enum型(イベント、状態)
 例えばポーカーを例にとると、相手の表情やカードの引き具合、様々な事が情報になります。
笑顔で遅くカード引いた場合、 真剣な顔で目を閉じて早くカードを引いた場合、部屋にある火災警報が鳴ったなど、状況は様々あります。

・適材適所の判断をグループ分け、enum型(モード)
 神経衰弱、マインスイーパー、将棋、オセロ、など異なるルールがあるのでそれに合ったルール別に回路を変える。人間でもハサミを使うとき、包丁を使う時で考えが全く異なっていて、知らずに思考を切り替えています。


ここで1つ例で台所調理ロボットがあって、レシピ選択するだけ調理してくれる物があった場合
包丁も洗い物{コップやお皿}をうまく扱え、更に調理も火加減など上手くこなして、盛り付けも綺麗にできる、そのようなものをどうやったら出来るのかについて考えてみよう。

調理モードでレシピ(シーケンス)があって、包丁を持ってる、まな板に野菜が乗っているという状態があって、シーケンスには千切りにすると書かれていて、それを実行する。
シーケンス処理には条件が揃うのを待つといった事も可能でないといけないのをどうやってニューラルでどうやって表現するのかが問題になる事が見えてくる。これの解決策で分岐や繰り返し、状態の保持などが考案されている段階です。


最後にコンピュータがより人間らしさを手に入れるには、5感というものがないのでメリット/デメリットを自己採点刷る必要がでてきます。

・損得勘定、自己採点
 言われたことをやるだけではなく、それが自身にメリットがあるか判断する。
 これができればロボットが反発したり、抑止に繋がったりする



ひとまずここまで。
今回はニューロンを理解した先に何が待っているのかの道標の回でした。

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));
    }




2016年7月3日日曜日

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

 教科書のような回答をするならWikipediaを見たほうがいいと最初に言っておきます。
ここではもっと分かりやすく人工知能のしくみとシンプルな実装例を提示していきたいと思います。


必要な予備知識をQ&A形式で書いてみました。



1.人口知能とは
 人間の思考パターンをプログラムで表現したものです。


2.音声認識とか画像認識とかに使われてるけど万能なの?
 ニューロンと呼ばれるモデル自体が汎用性が高いのでもの特定分野に特化することで能力が発揮します。


3.どういう計算してるの?
 ニューロンと呼ばれるものはいわば1つの計算式であって、
 簡単に表現すると

  Y=AX+B

 こんな数式があって、
 Xが入力(実際は複数の入力がある)
 Yが出力
 AとBは{不定係数}

 といった具合になってます。
 人工知能が学習するというのはAとBを確率統計などで最適解を求めていき、
 精度が上がるにつれて係数の変化は減っていく傾向があります。


4.学習ってどうやるの?
 よく行う方法は教師画像という正解を教えてあげ正解率を上げていく方法です。


5.人工知能は人類にとって脅威になる?
 現在の方式だと脅威になりません。
 どうしてかというと、教えて覚えさせる方式だと新しい発想することはなく教えられたルールの中でしか答えを出せないからです。
 あと悪意あるユーザーが使えば危険というのは、包丁なども同じことが言えるのでこれは脅威とは呼びません。
 ターミネーターみたいに指令を与えて自ら目的達成するまでのルートを考え行動できるようになるのは
 あと2,3世代進んだ人工知能モデルだと思われます。(2080年辺り?)


6.つまり一言でまとめると
 人工知能とは計算式の集合体です。



いよいよ実装のお話

言葉だけでは分からないので
難しい部分をすべて削って極小な人工知能(ニューロン)を実装してみます。

実装デザインは、
①多入力1出力の構造で演算する部分(ニューロン)

②計算部分
 上で書いたY=AX+Bでもよいが、今回は内部パラメータをすべて角度で統一します。
 入力層の値からASin変換で角度値にしておくことで、内部の計算は足し算だけになり
 出力層でSin変換で値を求めれば良いことになるのでシンプルさを求めるとこうなります。

 数式で表すと

  Y=Σ(X+A)

 こうなります。

③学習方法
 上記式を見て分かる通り内部パラメータの最適化が学習のポイントとなります。
 例えば1+1=2 の場合、入力が1,1 出力が2、このような結果にするに期待される内部パラメータAは???という具合です。
 このとき入力から出力を求める、出力から入力を求められるような内部計算式である必要がある事もあって、今回は内部パラメータを角度にしています。

  Y=X+A (入力→出力を求める場合)
  A=Y-X (出力→入力を求める場合)

学習するときは前回使っていた内部パラメータもあるので、学習する際はそのパラメータも含めて
本来は学習曲線というものを使いますが今回は単純に平均値

 次の内部パラメータ=(前回値+学習結果値)/2

とします。


これでようやく必要な情報で出尽くしました。
次は実装で必要になるクラスを考えていきましょう。

まずニューロンのインターフェイスを考えると
public interface INeuron
{
float Output { get; }
void Learn(float value);
}

これくらいシンプルにしておきます。


次にニューロンクラス{入力・出力}とシナプス(N:1)の3つを実装する。

class InputNeuron : INeuron
class OutputNeuron : INeuron
class Synapse : INeuron


順番に実装説明していくと
・入力クラス
 シグナルは画像データや音声データなどになり、学習は行わないので、インターフェイス実装だけになります。
public class InputNeuron : INeuron
{
    public float Output { get; set; }
    public void Learn(float result) { }
}

・シナプスクラス
 入力値を計算してシグナルを算出して、学習は行うので、先ほどの計算式をここで実装します。
public class Synapse : INeuron
{
    INeuron InputNeuron { get; set; }
    float Theta { get; set; }
    public Synapse(INeuron model) { InputNeuron = model; }
    public float Output => InputNeuron.Output + Theta;
    public void Learn(float result) { Theta = (result + Theta) / 2.0f; }
}

・出力クラス
 複数のシナプスを参照して出力結果を出して、学習はしないが学習させるトリガーはここで行う
public class OutputNeuron : INeuron
{
    List Synapses { get; set; } = new List();
    public float Output => Synapses.Sum(_ => _.Output);
    public void Subscribe(params INeuron[] inputs) => Observers.AddRange(inputs.Select(_ => new Synapse(_)));
    public void Learn(float value) => foreach(var synapse in Synapses) { synapse.Learn(value); }
}

これが人工知能の原型です。

あとは計算式を変えたり、多段にしたり再帰したりして実装することになります。


ちなみに今回の計算式は適当なので正しく結果はでませんが、学習の様子は分かります。
まずは人工知能ってどういう構造なの?って所から入っていくと1つ1つの理論は難しくてもすんなり入っていくのではと思います。



・サンプルコード
 そのままビルドすると動きます

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var s1 = new InputNeuron();
            var s2 = new InputNeuron();
            var o1 = new OutputNeuron(s1, s2);
            var o2 = new OutputNeuron(s1, s2);

            //学習
            for (var i = 0; i < 10; i++)
            {
                s1.Signal = 1.0f;
                s2.Signal = 1.0f;
                o1.Learn(1);
                o2.Learn(0);

                s1.Signal = 0.0f;
                s2.Signal = 0.0f;
                o1.Learn(0);
                o2.Learn(1);

                s1.Signal = 1.0f;
                s2.Signal = 0.0f;
                o1.Learn(0);
                o2.Learn(1);

                s1.Signal = 0.0f;
                s2.Signal = 1.0f;
                o1.Learn(0);
                o2.Learn(1);
            }

            Console.WriteLine("======");


            s1.Signal = 1.0f;
            s2.Signal = 1.0f;
            Console.WriteLine($"1 == {Math.Sin(o1.Output)}, 0 == {Math.Sin(o2.Output)}");

            s1.Signal = 0.0f;
            s2.Signal = 0.0f;
            Console.WriteLine($"0 == {Math.Sin(o1.Output)}, 1 == {Math.Sin(o2.Output)}");

            s1.Signal = 1.0f;
            s2.Signal = 0.0f;
            Console.WriteLine($"0 == {Math.Sin(o1.Output)}, 1 == {Math.Sin(o2.Output)}");

            s1.Signal = 0.0f;
            s2.Signal = 1.0f;
            Console.WriteLine($"0 == {Math.Sin(o1.Output)}, 1 == {Math.Sin(o2.Output)}");

            Console.ReadKey();
        }

        public class InputNeuron : INeuron
        {
            public float Signal { get; set; }
            public float Output => (float)Math.Asin(Signal);
            public void Learn(float result) { }
        }
        public class Synapse : INeuron
        {
            INeuron neuron { get; set; }
            public float Theta { get; set; }
            public float Output => neuron.Output + Theta;
            public Synapse(INeuron model) { neuron = model; }
            public void Learn(float result) { Theta = (result - neuron.Output + Theta) / 2.0f; }
        }
        public class OutputNeuron : INeuron
        {
            List Synapses { get; set; } = new List();
            public float Output => Synapses.Sum(_ => _.Output);
            public OutputNeuron(params INeuron[] models) { Synapses.AddRange(models.Select(_ => new Synapse(_))); }
            public void Learn(float result) => Synapses.ForEach(_ => _.Learn(result));
        }
        public interface INeuron
        {
            float Output { get; }
            void Learn(float result);
        }
    }
}


ーーーーーーーーーーーーーーーー
続きます

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

Androider