忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

かんがんL-system その2 描画

前回かんたんなL-systemのプログラムを書きました。
しかしその実行結果はわけがわからないものでした。

ABAABABAABAABABAABABA
というふうに、文字だけで、おもしろくありません。
L-systemはやはり描画してこそでしょう。

ソースコード

Algae.cs

using System;
using System.Collections.Generic;

namespace LSystem.Algae
{
    public class Algae
    {
        public AlgaeCell Root = new AlgaeCell { IsBig = false };
        public List<AlgaeCell> Cells = new List<AlgaeCell>();

        public Algae()
        {
            Cells.Add(Root);
        }

        public void Develop()
        {
            foreach (var cell in Cells)
            {
                cell.Update();
            }

            var newCells = new List<AlgaeCell>();

            foreach (var cell in Cells)
            {
                newCells.Add(cell);

                if (cell.CreatedChildren)
                {
                    newCells.Add(cell.Child1);
                    newCells.Add(cell.Child2);
                }
            }

            this.Cells = newCells;
        }

        public void ForEachTerminal(Action<AlgaeCell> executedInTerminal)
        {
            ForEachTerminal(executedInTerminal, Root);
        }

        public void ForEachTerminal(Action<AlgaeCell> executedInTerminal, AlgaeCell cell)
        {
            if (cell.HasChildren)
            {
                ForEachTerminal(executedInTerminal, cell.Child1);
                ForEachTerminal(executedInTerminal, cell.Child2);
            }
            else
            {
                executedInTerminal(cell);
            }
        }
    }

    public class AlgaeCell
    {
        public bool IsBig;
        public AlgaeCell Child1;
        public AlgaeCell Child2;

        public bool HasChildren
        {
            get { return Child1 != null; }
        }

        public bool CreatedChildren { get; private set; }

        public void Update()
        {
            CreatedChildren = false;

            if (HasChildren)
            {
                return;
            }

            if (IsBig)
            {
                this.Child1 = new AlgaeCell
                {
                    IsBig = true
                };
                this.Child2 = new AlgaeCell
                {
                    IsBig = false
                };
                CreatedChildren = true;
            }
            else
            {
                IsBig = true;
            }
        }
    }
}


MainWindow.xaml.cs

using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;

namespace LSystem.Algae
{
    public partial class MainWindow : Window
    {
        Algae algae = new Algae();

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            algae.Develop();

            Title = getText(algae.Root);
            canvas.Children.Clear();
            draw(algae.Root, new Vector(300, 100), 0);
        }

        private string getText(AlgaeCell cell)
        {
            var text = "";
            algae.ForEachTerminal((c) => text += c.IsBig ? "A" : "B");
            return text;
        }

        private void draw(AlgaeCell cell, Vector position, double angle)
        {
            var transform = new TransformGroup();
            transform.Children.Add(
                new ScaleTransform(cell.IsBig ? 1 : 0.5, cell.IsBig ? 1 : 0.5)
                );
            transform.Children.Add(new RotateTransform(angle));
            var endPosition = position 
                + transform.Value.Transform(new Vector(0, 100));

            var line = new Line
            {
                X1 = position.X,
                Y1 = position.Y,
                X2 = endPosition.X,
                Y2 = endPosition.Y,
                Stroke = cell.IsBig ? Brushes.Green : Brushes.LightGreen,
                StrokeThickness = 5
            };
            canvas.Children.Add(line);

            if (cell.HasChildren)
            {
                draw(
                    cell.Child1,
                    endPosition,
                    angle + 8
                    );
                draw(
                    cell.Child2,
                    endPosition,
                    angle - 8
                    );
            }
        }
    }
}

MainWindow.xaml

<Window x:Class="LSystem.Algae.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>

        <Canvas x:Name="canvas">

        </Canvas>
        <Button Height="24" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="58" Margin="0,0,10,10" Click="Button_Click">Next</Button>
    </Grid>
</Window>

実行結果

このプログラムを実行するとこうなります:

おお!!

なんとなく、藻類のように見えなくもないような気がしますね。
ただ、再帰で適当に描いたのとたいして変わりないじゃないかと言われると困りますが。

本来L-systemは文字とその変換だけでプログラムはすむはずなのですが、
ここではイメージというかフレイバーというかそういったものにこだわったソースコードにしてみました。
それにしては成長しきった細胞がいきなり現れたりという変な感じはしますが……

拍手[1回]

PR

かんたんL-system その1 文字だけ

L-systemというアルゴリズムがあります。
これを使うと植物の形が描けます。
やってみましょう。

Program.cs

class Program
{
    static void Main()
    {
        var text = "A";
        System.Console.WriteLine(text);

        for (int i = 0; i < 10; i++)
        {
            var newText = "";

            for (int textIndex = 0; textIndex < text.Length; textIndex++)
            {
                switch(text[textIndex])
                {
                    case 'A':
                        newText += "AB";
                        break;
                    case 'B':
                        newText += "A";
                        break;
                }

            }

            text = newText;
            System.Console.WriteLine(text);
        }
    }
}
結構簡単なプログラムですね。
ディスプレイが小さくても一画面に収まりそうです。

結果

A
AB
ABA
ABAAB
ABAABABA
ABAABABAABAAB
ABAABABAABAABABAABABA
ABAABABAABAABABAABABAABAABABAABAAB
ABAABABAABAABABAABABAABAABABAABAABABAABABAABAABABAABABA
ABAABABAABAABABAABABAABAABABAABAABABAABABAABAABABAABABAABAABABAABAABABAABABAABAABABAABAAB
ABAABABAABAABABAABABAABAABABAABAABABAABABAABAABABAABABAABAABABAABAABABAABABAABAABABAABAABABAABABAABAABABAABABAABAABABAABAABABAABABAABAABABAABABA

さて……文字列が表示されました。
何でしょう?この結果は。
私たちは植物を描こうとしていたはずでしたが…?

実はこのプログラムはL-systemのエッセンスを実装しただけであって、見た目はダメです。
植物の形にするにはきちんと体裁を整えなければなりません。
では続きは次回。

拍手[0回]


かんたん!制限付きボルツマンマシン実装 実数(0から1)バージョン (C#)訂正その2、 0と1を反転 [Deep Learningシリーズ]

0と1を反転すると…

ここで制限(制約)付きボルツマンマシン(Restricted Boltzmann Machine : RBM)を実装しましたが、いくつか難点があります。
これが私の実装のバグのせいなのか制限付きボルツマンマシンそのものの特性のせいなのかはわかりませんが、データに0に近い値が多い場合、0と1を入れ替えると、つまりデータに1に近い値が多くなると、上手く学習しないのです。


拍手[0回]


かんたん!制限付きボルツマンマシン GPUで実装 実数(0から1)バージョン (C#) [Deep Learningシリーズ]

制限付きボルツマンマシンをGPUで動かす

Deep Learningに使うことがある、制限(制約)付きボルツマンマシン(Restricted Boltzmann Machine : RBM)をまた実装してみました。
ただし今度はGPUで動きます。
といってもあまり考えずにプログラムしたのでGPGPUにしては遅い気がします。
パフォーマンスチューニングはこれからして行きましょう。


拍手[1回]


かんたん!制限付きボルツマンマシン実装 実数バージョン (C#)訂正 [Deep Learningシリーズ]

可視層の状態をランダムに

以前、このような記事を書いたのですが、こっちのほうが正しいような気がしてきました。
しかし性能はどちらがいいかは…。

思い出しっぷりはこちらのほうが悪いですが、可視層もランダムにしようと思ったらこうなるでしょうね。

ちなみにこいつで画像を扱うと、結果がざらつくようです。
あんまり使いたくないですね。

RestrictedBoltzmannMachine.cs

using System;
using System.Threading.Tasks;

namespace RestrictedBoltzmannMachines.RealValue
{
    public class RestrictedBoltzmannMachine
    {
        public SymmetricConnection[][] Connections;
        public VisibleNeuron[] VisibleNeurons;
        public HiddenNeuron[] HiddenNeurons;

        public RestrictedBoltzmannMachine(int visibleNeuronCount, int hiddenNeuronCount, Random random) :
            this(SymmetricConnection.CreateRandomWeights(random, visibleNeuronCount, hiddenNeuronCount), new double[visibleNeuronCount], new double[hiddenNeuronCount], random)
        {
        }

        public RestrictedBoltzmannMachine(double[][] weights, double[] visibleBiases, double[] hiddenBiases, Random random)
        {
            this.VisibleNeurons = Neuron.CreateNeurons<VisibleNeuron>(visibleBiases);
            this.HiddenNeurons = Neuron.CreateNeurons<HiddenNeuron>(hiddenBiases);
            this.Connections = SymmetricConnection.CreateConnections(weights, VisibleNeurons, HiddenNeurons);
            Neuron.WireConnections(this.Connections);

            foreach (var neuron in this.VisibleNeurons)
            {
                neuron.Random = new Random(random.Next());
            }

            foreach (var neuron in this.HiddenNeurons)
            {
                neuron.Random = new Random(random.Next());
            }
        }

        public void SetVisibleNeuronValues(double[] visibleValues)
        {
            for (int i = 0; i < this.VisibleNeurons.Length; i++)
            {
                this.VisibleNeurons[i].Value = visibleValues[i];
            }
        }

        public void LearnFromData(double learningRate, int freeAssociationStepCount = 1)
        {
            Wake(learningRate);
            Sleep(learningRate, freeAssociationStepCount);
            EndLearning();
        }

        public void Wake(double learningRate)
        {
            UpdateHiddenNeurons();
            learn(learningRate);
        }

        public void UpdateVisibleNeurons()
        {
            updateNeurons(this.VisibleNeurons);
        }

        public void UpdateHiddenNeurons()
        {
            updateNeurons(this.HiddenNeurons);
        }

        private void updateNeurons(Neuron[] neurons)
        {
            Parallel.ForEach(neurons, neuron => neuron.Update());
        }


        private void learn(double learningRate)
        {
            Parallel.ForEach(Connections, connectionRow =>
            {
                foreach (var connection in connectionRow)
                {
                    connection.Learn(learningRate);
                }
            });

            Parallel.ForEach(VisibleNeurons, neuron => neuron.Learn(learningRate));
            Parallel.ForEach(HiddenNeurons, neuron => neuron.Learn(learningRate));
        }

        public void Sleep(double learningRate, int freeAssociationStepCount)
        {
            doFreeAssociation(freeAssociationStepCount);
            learn(-learningRate);
        }

        //Gibbs sampling
        private void doFreeAssociation(int freeAssociationStepCount)
        {
            for (int step = 0; step < freeAssociationStepCount; step++)
            {
                UpdateVisibleNeurons();
                UpdateHiddenNeurons();
            }
        }

        public void EndLearning()
        {
            Parallel.ForEach(Connections, connectionRow =>
            {
                foreach (var connection in connectionRow)
                {
                    connection.EndLearning();
                }
            });

            Parallel.ForEach(VisibleNeurons, neuron => neuron.EndLearning());
            Parallel.ForEach(HiddenNeurons, neuron => neuron.EndLearning());
        }

        public void Associate()
        {
            UpdateHiddenNeurons();
            UpdateVisibleNeurons();
        }
    }
}

Neuron.cs

using System;
using System.Collections.Generic;

namespace RestrictedBoltzmannMachines.RealValue
{
    public abstract class Neuron
    {
        public double Value;
        public double Bias;
        public double DeltaBias;
        public List<Synapse> Synapses = new List<Synapse>();
        public Random Random;

        public abstract void Update();
        public abstract void Learn(double learningRate);

        public void EndLearning()
        {
            this.Bias += this.DeltaBias;
            this.DeltaBias = 0;
        }

        public static T[] CreateNeurons<T>(double[] biases)
            where T : Neuron, new() 
        {
            T[] result = new T[biases.Length];

            for (int i = 0; i < result.Length; i++)
            {
                result[i] = new T { Bias = biases[i] };
            }

            return result;
        }

        public static void WireConnections(SymmetricConnection[][] connections)
        {
            foreach (var connectionRow in connections)
            {
                foreach (var connection in connectionRow)
                {
                    Synapse hiddenConnection = new Synapse();
                    hiddenConnection.Connection = connection;
                    hiddenConnection.SourceNeuron = connection.VisibleNeuron;
                    connection.HiddenNeuron.Synapses.Add(hiddenConnection);

                    Synapse visibleConnection = new Synapse();
                    visibleConnection.Connection = connection;
                    visibleConnection.SourceNeuron = connection.HiddenNeuron;
                    connection.VisibleNeuron.Synapses.Add(visibleConnection);
                }
            }
        }

        protected double GetInputFromSourceNeurons()
        {
            double result = 0;

            for (int i = 0; i < Synapses.Count; i++)
            {
                var s = Synapses[i];
                result += s.Connection.Weight * s.SourceNeuron.Value;
            }

            return result;
        }

        protected static double Sigmoid(double x)
        {
            return 1.0 / (1.0 + Math.Exp(-x));
        }
    }

    public class VisibleNeuron : Neuron
    {
        public override void Update()
        {
            this.Value = Sigmoid(nextGaussian(Random) + GetInputFromSourceNeurons() + Bias);
        }

        private static double nextGaussian(Random random)
        {
            return Math.Sqrt(-2.0 * Math.Log(random.NextDouble())) 
                * Math.Sin(2.0 * Math.PI * random.NextDouble()); 
        }

        public override void Learn(double learningRate)
        {
            this.DeltaBias += learningRate * this.Value;
        }
    }

    public class HiddenNeuron : Neuron
    {
        public double Probability;

        public override void Update()
        {
            this.Probability = Sigmoid(GetInputFromSourceNeurons() + Bias);
            this.Value = nextBool(Random, this.Probability) ? 1 : 0;
        }

        private static bool nextBool(Random random, double rate)
        {
            if (rate < 0 || 1 < rate) return false;
            return random.NextDouble() < rate;
        }

        public override void Learn(double learningRate)
        {
            this.DeltaBias += learningRate * this.Probability;
        }
    }
}

前回のソースコードと合わせると結果はこうなります:

0.99    0.46    0.94    0.04    0.00
0.01    0.10    0.89    0.66    0.93

まあいいんじゃないでしょうか?

拍手[0回]