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)

2016年6月24日金曜日

[RaspberryPi]RpiをArduinoライターにする


色々試行錯誤してようやくできたでまとめて置く。


Step1.ソフトのインストール
$ sudo apt-get install bison automake autoconf flex git gcc
$ sudo apt-get install gcc-avr binutils-avr avr-libc
$ wget http://download.savannah.gnu.org/releases/avrdude/avrdude-6.3.tar.gz
$ ./configure --prefix=/opt/avrdude --enable-linuxgpio

Step2.ソースの修正
 なせかprintf("%ud",gpio) という部分で 数字d となって 文字列が正しく出ない問題があるので
linuxgpioのソース上にある  %ud を全て %d に書き換える。

Step3. avrdude.confの修正
 下図のようなピン配置にした場合、
  まずソース内に入っているavrdude.confを開き、「linuxgpio」で検索して、以下の様に編集する。

------------------------------------------------------------
programmer
  id    = "linuxgpio";
  desc  = "Use the Linux sysfs interface to bitbang GPIO lines";
  type  = "linuxgpio";
  reset = 12;
  sck   = 16;
  mosi  = 20;
  miso  = 21;
;
------------------------------------------------------------



avrマイコンを書き込む用のピンアサイン

電源は3.3VとGNDを使う事。
(5Vでも良いが、RPI自身が3.3V/5Vトレラントでないので注意しておくと良い)


もしDigisparkを使う場合は最後の行に以下の内容も追記しておく。

#------------------------------------------------------------
# ATtiny87
#------------------------------------------------------------
# Changes against ATtiny167 (beside IDs)
#    memory "flash"
#        size            = 8192;
#        num_pages       = 64;
part
     id            = "t87";
     desc          = "ATtiny87";
     has_debugwire = yes;
     flash_instr   = 0xB6, 0x01, 0x11;
     eeprom_instr  = 0xBD, 0xF2, 0xBD, 0xE1, 0xBB, 0xCF, 0xB4,
               0x00, 0xBE, 0x01, 0xB6, 0x01, 0xBC, 0x00, 0xBB,
               0xBF, 0x99, 0xF9, 0xBB, 0xAF;
## no STK500 devcode in XML file, use the ATtiny45 one
     stk500_devcode   = 0x14;
##  avr910_devcode   = ?;
##  Try the AT90S2313 devcode:
     avr910_devcode   = 0x20;
     signature        = 0x1e 0x93 0x87;
     reset            = io;
     chip_erase_delay = 1000;
     pgm_enable       = "1 0 1 0  1 1 0 0    0 1 0 1  0 0 1 1",
                        "x x x x  x x x x    x x x x  x x x x";
     chip_erase       = "1 0 1 0  1 1 0 0    1 0 0 x  x x x x",
                        "x x x x  x x x x    x x x x  x x x x";
    timeout     = 200;
    stabdelay       = 100;
    cmdexedelay     = 25;
    synchloops      = 32;
    bytedelay       = 0;
    pollindex       = 3;
    pollvalue       = 0x53;
    predelay        = 1;
    postdelay       = 1;
    pollmethod      = 0;
    pp_controlstack     =
        0x0E, 0x1E, 0x0E, 0x1E, 0x2E, 0x3E, 0x2E, 0x3E,
        0x4E, 0x5E, 0x4E, 0x5E, 0x6E, 0x7E, 0x6E, 0x7E,
        0x06, 0x16, 0x46, 0x56, 0x0A, 0x1A, 0x4A, 0x5A,
        0x1E, 0x7C, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00;
    hventerstabdelay    = 100;
    progmodedelay       = 0;
    latchcycles         = 5;
    togglevtg           = 1;
    poweroffdelay       = 20;
    resetdelayms        = 1;
    resetdelayus        = 0;
    hvleavestabdelay    = 15;
    chiperasepulsewidth = 0;
    chiperasepolltimeout = 10;
    programfusepulsewidth = 0;
    programfusepolltimeout = 5;
    programlockpulsewidth = 0;
    programlockpolltimeout = 5;
    idr                 = 0x00;
    spmcr               = 0x57;
    allowfullpagebitstream = no;
     memory "eeprom"
         size            = 512;
        paged           = no;
        page_size       = 4;
         min_write_delay = 4000;
         max_write_delay = 4500;
         readback_p1     = 0xff;
         readback_p2     = 0xff;
         read            = "1  0  1  0   0  0  0  0    0 0 x x  x x x a8",
                           "a7 a6 a5 a4  a3 a2 a1 a0   o o o o  o o o o";
         write           = "1  1  0  0   0  0  0  0    0 0 x x  x x x a8",
                           "a8 a6 a5 a4  a3 a2 a1 a0   i i i i  i i i i";
  loadpage_lo   = "  1   1   0   0      0   0   0   1",
        "  0   0   0   0      0   0   0   0",
        "  0   0   0   0      0   0  a1  a0",
        "  i   i   i   i      i   i   i   i";
  writepage = "  1   1   0   0      0   0   1   0",
        "  0   0   x   x      x   x   x   x",
        "  0   0  a5  a4     a3  a2   0   0",
        "  x   x   x   x      x   x   x   x";
  mode      = 0x41;
  delay     = 10;
  blocksize = 4;
  readsize  = 256;
       ;
     memory "flash"
         paged           = yes;
         size            = 8192;
         page_size       = 128;
         num_pages       = 64;
         min_write_delay = 4500;
         max_write_delay = 4500;
         readback_p1     = 0xff;
         readback_p2     = 0xff;
         read_lo         = "  0   0   1   0    0   0   0   0",
                           "  0   0   0  a12  a11 a10  a9  a8",
                           " a7  a6  a5  a4   a3  a2   a1  a0",
                           "  o   o   o   o    o   o   o   o";
         read_hi         = "  0   0   1   0    1   0   0   0",
                           "  0   0   0  a12  a11 a10  a9  a8",
                           " a7  a6  a5  a4   a3  a2   a1  a0",
                           "  o   o   o   o    o   o   o   o";
         loadpage_lo     = "  0   1   0   0    0   0   0   0",
                           "  0   0   0   x    x   x   x   x",
                           "  x   x  a5  a4   a3  a2  a1  a0",
                           "  i   i   i   i    i   i   i   i";
         loadpage_hi     = "  0   1   0   0    1   0   0   0",
                           "  0   0   0   x    x   x   x   x",
                           "  x   x  a5  a4   a3  a2  a1  a0",
                           "  i   i   i   i    i   i   i   i";
         writepage       = "  0  1  0  0   1   1   0  0",
                           "  0  0  0  0  a11 a10 a9 a8",
                           " a7 a6 a5  x   x  x  x  x",
                           "  x  x  x  x   x  x  x  x";
  mode      = 0x41;
  delay     = 10;
  blocksize = 64;
  readsize  = 256;
       ;
#   ATtiny87 has Signature Bytes: 0x1E 0x93 0x87.
     memory "signature"
         size            = 3;
         read            = "0  0  1  1   0  0  0  0   0  0  0  x   x  x  x  x",
                           "x  x  x  x   x  x a1 a0   o  o  o  o   o  o  o  o";
       ;
     memory "lock"
         size            = 1;
         write           = "1 0 1 0  1 1 0 0  1 1 1 x  x x x x",
                           "x x x x  x x x x  x x x x  x x i i";
         read            = "0 1 0 1  1 0 0 0  0 0 0 0  0 0 0 0",
                           "0 0 0 0  0 0 0 0  o o o o  o o o o";
        min_write_delay = 9000;
        max_write_delay = 9000;
       ;
     memory "lfuse"
         size            = 1;
         write           = "1 0 1 0  1 1 0 0  1 0 1 0  0 0 0 0",
                           "x x x x  x x x x  i i i i  i i i i";
         read            = "0 1 0 1  0 0 0 0  0 0 0 0  0 0 0 0",
                           "x x x x  x x x x  o o o o  o o o o";
        min_write_delay = 9000;
        max_write_delay = 9000;
       ;
     memory "hfuse"
         size            = 1;
         write           = "1 0 1 0  1 1 0 0  1 0 1 0  1 0 0 0",
                           "x x x x  x x x x  i i i i  i i i i";
         read            = "0 1 0 1  1 0 0 0  0 0 0 0  1 0 0 0",
                           "x x x x  x x x x  o o o o  o o o o";
        min_write_delay = 9000;
        max_write_delay = 9000;
       ;
     memory "efuse"
         size            = 1;
         write           = "1 0 1 0  1 1 0 0  1 0 1 0  0 1 0 0",
                           "x x x x  x x x x  x x x x  x x x i";
         read            = "0 1 0 1  0 0 0 0  0 0 0 0  1 0 0 0",
                           "x x x x  x x x x  o o o o  o o o o";
        min_write_delay = 9000;
        max_write_delay = 9000;
     ;
     memory "calibration"
         size            = 1;
         read            = "0  0  1  1   1  0  0  0    0 0 0 x  x x x x",
                           "0  0  0  0   0  0  0  a0   o o o o  o o o o";
     ;
  ;
#------------------------------------------------------------
# ATtiny167
#------------------------------------------------------------
part
     id            = "t167";
     desc          = "ATtiny167";
     has_debugwire = yes;
     flash_instr   = 0xB6, 0x01, 0x11;
     eeprom_instr  = 0xBD, 0xF2, 0xBD, 0xE1, 0xBB, 0xCF, 0xB4,
               0x00, 0xBE, 0x01, 0xB6, 0x01, 0xBC, 0x00, 0xBB,
               0xBF, 0x99, 0xF9, 0xBB, 0xAF;
## no STK500 devcode in XML file, use the ATtiny45 one
     stk500_devcode   = 0x14;
##  avr910_devcode   = ?;
##  Try the AT90S2313 devcode:
     avr910_devcode   = 0x20;
     signature        = 0x1e 0x94 0x87;
     reset            = io;
     chip_erase_delay = 1000;
     pgm_enable       = "1 0 1 0  1 1 0 0    0 1 0 1  0 0 1 1",
                        "x x x x  x x x x    x x x x  x x x x";
     chip_erase       = "1 0 1 0  1 1 0 0    1 0 0 x  x x x x",
                        "x x x x  x x x x    x x x x  x x x x";
    timeout     = 200;
    stabdelay       = 100;
    cmdexedelay     = 25;
    synchloops      = 32;
    bytedelay       = 0;
    pollindex       = 3;
    pollvalue       = 0x53;
    predelay        = 1;
    postdelay       = 1;
    pollmethod      = 0;
    pp_controlstack     =
        0x0E, 0x1E, 0x0E, 0x1E, 0x2E, 0x3E, 0x2E, 0x3E,
        0x4E, 0x5E, 0x4E, 0x5E, 0x6E, 0x7E, 0x6E, 0x7E,
        0x06, 0x16, 0x46, 0x56, 0x0A, 0x1A, 0x4A, 0x5A,
        0x1E, 0x7C, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00;
    hventerstabdelay    = 100;
    progmodedelay       = 0;
    latchcycles         = 5;
    togglevtg           = 1;
    poweroffdelay       = 20;
    resetdelayms        = 1;
    resetdelayus        = 0;
    hvleavestabdelay    = 15;
    chiperasepulsewidth = 0;
    chiperasepolltimeout = 10;
    programfusepulsewidth = 0;
    programfusepolltimeout = 5;
    programlockpulsewidth = 0;
    programlockpolltimeout = 5;
    idr                 = 0x00;
    spmcr               = 0x57;
    allowfullpagebitstream = no;
     memory "eeprom"
         size            = 512;
        paged           = no;
        page_size       = 4;
         min_write_delay = 4000;
         max_write_delay = 4500;
         readback_p1     = 0xff;
         readback_p2     = 0xff;
         read            = "1  0  1  0   0  0  0  0    0 0 x x  x x x a8",
                           "a7 a6 a5 a4  a3 a2 a1 a0   o o o o  o o o o";
         write           = "1  1  0  0   0  0  0  0    0 0 x x  x x x a8",
                           "a8 a6 a5 a4  a3 a2 a1 a0   i i i i  i i i i";
  loadpage_lo   = "  1   1   0   0      0   0   0   1",
        "  0   0   0   0      0   0   0   0",
        "  0   0   0   0      0   0  a1  a0",
        "  i   i   i   i      i   i   i   i";
  writepage = "  1   1   0   0      0   0   1   0",
        "  0   0   x   x      x   x   x   x",
        "  0   0  a5  a4     a3  a2   0   0",
        "  x   x   x   x      x   x   x   x";
  mode      = 0x41;
  delay     = 10;
  blocksize = 4;
  readsize  = 256;
       ;
     memory "flash"
         paged           = yes;
         size            = 16384;
         page_size       = 128;
         num_pages       = 128;
         min_write_delay = 4500;
         max_write_delay = 4500;
         readback_p1     = 0xff;
         readback_p2     = 0xff;
         read_lo         = "  0   0   1   0    0   0   0   0",
                           "  0   0   0  a12  a11 a10  a9  a8",
                           " a7  a6  a5  a4   a3  a2   a1  a0",
                           "  o   o   o   o    o   o   o   o";
         read_hi         = "  0   0   1   0    1   0   0   0",
                           "  0   0   0  a12  a11 a10  a9  a8",
                           " a7  a6  a5  a4   a3  a2   a1  a0",
                           "  o   o   o   o    o   o   o   o";
         loadpage_lo     = "  0   1   0   0    0   0   0   0",
                           "  0   0   0   x    x   x   x   x",
                           "  x   x  a5  a4   a3  a2  a1  a0",
                           "  i   i   i   i    i   i   i   i";
         loadpage_hi     = "  0   1   0   0    1   0   0   0",
                           "  0   0   0   x    x   x   x   x",
                           "  x   x  a5  a4   a3  a2  a1  a0",
                           "  i   i   i   i    i   i   i   i";
         writepage       = "  0  1  0  0   1   1   0  0",
                           "  0  0  0  0  a11 a10 a9 a8",
                           " a7 a6 a5  x   x  x  x  x",
                           "  x  x  x  x   x  x  x  x";
  mode      = 0x41;
  delay     = 10;
  blocksize = 64;
  readsize  = 256;
       ;
#   ATtiny167 has Signature Bytes: 0x1E 0x94 0x87.
     memory "signature"
         size            = 3;
         read            = "0  0  1  1   0  0  0  0   0  0  0  x   x  x  x  x",
                           "x  x  x  x   x  x a1 a0   o  o  o  o   o  o  o  o";
       ;
     memory "lock"
         size            = 1;
         write           = "1 0 1 0  1 1 0 0  1 1 1 x  x x x x",
                           "x x x x  x x x x  x x x x  x x i i";
         read            = "0 1 0 1  1 0 0 0  0 0 0 0  0 0 0 0",
                           "0 0 0 0  0 0 0 0  o o o o  o o o o";
        min_write_delay = 9000;
        max_write_delay = 9000;
       ;
     memory "lfuse"
         size            = 1;
         write           = "1 0 1 0  1 1 0 0  1 0 1 0  0 0 0 0",
                           "x x x x  x x x x  i i i i  i i i i";
         read            = "0 1 0 1  0 0 0 0  0 0 0 0  0 0 0 0",
                           "x x x x  x x x x  o o o o  o o o o";
        min_write_delay = 9000;
        max_write_delay = 9000;
       ;
     memory "hfuse"
         size            = 1;
         write           = "1 0 1 0  1 1 0 0  1 0 1 0  1 0 0 0",
                           "x x x x  x x x x  i i i i  i i i i";
         read            = "0 1 0 1  1 0 0 0  0 0 0 0  1 0 0 0",
                           "x x x x  x x x x  o o o o  o o o o";
        min_write_delay = 9000;
        max_write_delay = 9000;
       ;
     memory "efuse"
         size            = 1;
         write           = "1 0 1 0  1 1 0 0  1 0 1 0  0 1 0 0",
                           "x x x x  x x x x  x x x x  x x x i";
         read            = "0 1 0 1  0 0 0 0  0 0 0 0  1 0 0 0",
                           "x x x x  x x x x  o o o o  o o o o";
        min_write_delay = 9000;
        max_write_delay = 9000;
     ;
     memory "calibration"
         size            = 1;
         read            = "0  0  1  1   1  0  0  0    0 0 0 x  x x x x",
                           "0  0  0  0   0  0  0  a0   o o o o  o o o o";
     ;
  ;


Step4.ビルド&インストール
$ make
$ sudo make install



Step5.配線


Digispark ATTiny85の場合は、

D0―MOSI
D1―MISO
D2―SCK
D5―RESET


Digispark pro ATTiny167の場合は、

PA2―MISO
PA4―MOSI
PA5―SCK
PB7―RESET


接続する時の注意ですが、MISO/MOSIはクロス接続にすること。
ストレート接続すると、TX-TX、RX-TXみたいな具合になって通信できません。


Step6.動作確認結果

$ sudo avrdude -c linuxgpio -pt167
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e9487 (probably t167)
avrdude: safemode: Fuses OK (E:FE, H:DD, L:DF)
avrdude done.  Thank you.


以上まとめでした。

2016年6月15日水曜日

基板を作る時に便利なサービスサイト「Fusion PCBA」

サービスに部品実装まである。
特殊パーツがDegikeyなどで購入可能であれば調達までしてくれるという便利さで、かなりお得意様になりそう。

http://qiita.com/mayfair/items/0206bd437c4302be5500

[RaspberryPi]SDカードが壊れたときの修復方法


$ sudo e2fsck /dev/sda2 -c -y

このコマンドを1日くらいかけて実行すれば治る

2016年6月4日土曜日

[RaspberryPi]TS動画をインタレ保持リサイズしてDVDサイズで保存する方法

2016.06.01時点で最新のffmpegはRPiのh264ハードウェアエンコに対応してます。

ってことでlibfdk_aacを追加してffmpegをコンパイル。


タイトルにある通り、インターレースを保持しつつリサイズしてMpeg2からMpeg4にトランスコードしたい。(Chromebookで動画を観たいため)

ということでコマンドを色々調べて見つけた決定版はコレ↓

ffmpeg -c:a libfdk_aac -i 動画名.ts -vf yadif=1,tinterlace=4 -c:v h264_omx -s 720x540 -c:a libfdk_
aac -b:v 2500k -aspect 16:9 -q:a 128 -ar 44100 -vsync 1 -threads 0 -flags +ilme+ildct -y 保存名.mp4



エンコ時間の目安ですが、RPi1だと0.7fps、RPi2だと1fps、RPi3だと5fpsくらいでした。
24時間稼働させるから気にならないよね。。

2016年5月4日水曜日

ソフトウェア設計の最適化方法

ソフトウェア設計の最適化方法


ソースコードとは
 まず、ソースコードで求められているものはなんだろう。というテーマから入って説明したいと思います。
 世の中には
 ・ウォーターフロー開発モデル
 ・アジャイル開発モデル
 ・アスペクト開発モデル
 ・プロトタイプ開発モデル

などいろんな手法が考案されて、様々な現場で利用されているのではないでしょうか。

運用する時は、組織体系に合わせて世の中一般の開発モデルをカスタマイズされているので
A社、B社が同じ開発モデルを利用していて、あるプロジェクトで情報を共有して共同開発しようなんて場面に出くわした時は
必ずといって情報の一元化を行いましょう、という合言葉とともに本来不要なプロセスが発生する。
(同じ開発モデルだから相性はいいが、完全一致させることは不可能と思われる)


情報とは
 熱力学のエントロピーという言葉を耳にしたことがあると思う。

これは、
 集約されている状態は秩序が保たれていてエントロピーは小さく、
 散っていて無秩序な状態のことをエントロピーが大きいと表現している。

この考え方は情報分野にも当てはめることができる。
圧縮アルゴリズムを勉強した人はハフマン符号化を学ぶときに情報エントロピーという言葉が出てくる。
情報エントロピーとは、これ以上圧縮できないデータ量の事で、冗長性が非常に小さい状態のことを意味する。

前の話で組織間で出てきた「情報の一元化」とは、
お互い同じ情報を扱っているのに散ってしまう情報を打破するために行われる手法だという事を意味している事が分かる。
もう少しわかりやすい表現に変化させてみよう。
「情報の一元化」を別の言い方で表すと、「枠組みの中で情報の整理整頓を行う」となる。
ここで出てきた枠組みの中でというのが社間を意味し、そこで得られた情報を整理整頓を行うことである。
つまり社間では情報の中身ではなく、情報の出入りの話であって、これは仕様書などのドキュメントを台帳管理しようという事を示している。


ソフトウェア仕様とエントロピー
 少しずつ現場レベルに考え方をシフトしていく。
ハフマン符号化に出てくる情報エントロピーの考え方でいくと、
「冗長性をなくす」事でエントロピーが小さく、秩序が保たれているとこである事を意味している。
この考えは同じものが無ければまとまっているように思えるが、ソフトウェアの場合は時間軸があり仕様変更があるので、情報エントロピーという考えで情報をまとめることは難しい。
そこで原点に戻って、熱力学のエントロピーには「エントロピーの増大の法則」というものがあり、時間が経過していくとエントロピーは増加するというものである。

この考えを当てはめると、ソフトウェアは時間と共に仕様変更が行われ、エントロピーが増大していく。

と言える。

つまりソフトウェアの性質上、仕様変更が発生して、追加・変更・削除が設計・コードに反映される。

ソフトウェアの変化を管理する事でエントロピーの増大を記録することができる。(減少はしない)

裏返しにするとエントロピーが減るような仕様書は無い、仕様変更は工数が発生して費用が発生するという事が言える。


ソフトウェア設計とエントロピー
 仕様変化することでエントロピーが増大するという事を説明した。
次に仕様と設計について述べていこうと思う。

熱力学でいうと 熱エネルギーは 高いものから低いものへ移動していくというものがある。

これを仕様=熱いもの  設計=低いもの に例えると、
仕様書から設計書へ情報を移すと散らばる(千差万別)と表現してみると分かりやすいのではないだろうか。

設計書を書くとエントロピーが増大していくのは自ら苦しめていくようなものなので、
よくある事例を出すと書き方の統一である。

最近ではUMLで図の書き方を統一しようという動きはあるが、
設計書に適用する場合は運用ルールを決めて、書き方のテンプレートとガイドラインを用意しておくと良い。
また仕様変更で変化した場合は新たにドキュメントを作成しバージョン管理して
変化内容と以前の仕様、最終仕様を明確化しておく事とトレーサビリティが保たれる。


ソースコードとエントロピー

現場でよくあることは、
・Aさんのコードはまとまっていて見やすいコードを書く
・Bさんのコードは乱雑なコードを書く

というように人によって書き方が変わって秩序が乱れエントロピー増大に繋がっていることがある。

これも設計と同じように、書き方のテンプレートやガイドライン、フレームワークを利用する事で
書き方が統一できるようになる。
もう少し細かく書くと、
仕様とは、構造と機能の2つに分類され、
構造はフレームワーク
機能はユースケース、ステートマシン、外部通信仕様などに分類して表せることができて、それぞれに追加・変更・削除
がある。

機能の追加や変更、削除はオブザーバブルなものであって、
ある機能を 
 使う(Main)
 動かす(Sub)
 評価(Test)

に分けることで分担作業が可能になって機能を分割しやすくなる。

より具体的に説明すると、
 Mainクラス(上位:使う側)
 Subクラス(下位:実働側)
 Test(Mainクラスで使い方サンプル集みたいなもの)

の3つに分けて実装し、
機能の管理はMainで行われて、機能の実装はSubクラスで行う
TestクラスでSubクラスの品質評価を行い、
Mainクラス側でビジネスフローを実装する。

こんな具合になる。
これでエントロピーが減らせるのか?と思うだろうが、
仕様変更が起こると 仕様変更(Main)、機能変更(Sub)、変更に伴う評価(Test)
それぞれに作用が行われ、どの規模の変更が起きたのかが現場レベルで実感しやすくなり、
対費用効果の算出などに有用であることが分かる。
例えば、
 ・仕様追加ならMain・Sub・Testに影響
 ・仕様変更ならMainに影響(ビジネスフローは人力で評価を想定)
 ・機能変更ならSub・Testに影響


ざっくりまとめると、
 ・仕様書のバージョン管理
 ・WBSによる機能対応表
 ・ソースコードの実装を分担化

となる。

より精度の高い最適化を行うのであれば、エントロピーの増大について学んで
エントロピーを減らすことはできない事を理解して、いかにして増大するを抑止するかを考えてみると良いと思います。

Androider