[PR]
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
プログラミング、3DCGとその他いろいろについて
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
前回かんたんなL-systemのプログラムを書きました。
しかしその実行結果はわけがわからないものでした。
ABAABABAABAABABAABABA
というふうに、文字だけで、おもしろくありません。
L-systemはやはり描画してこそでしょう。
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; } } } }
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 ); } } } }
<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は文字とその変換だけでプログラムはすむはずなのですが、
ここではイメージというかフレイバーというかそういったものにこだわったソースコードにしてみました。
それにしては成長しきった細胞がいきなり現れたりという変な感じはしますが……
L-systemというアルゴリズムがあります。
これを使うと植物の形が描けます。
やってみましょう。
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のエッセンスを実装しただけであって、見た目はダメです。
植物の形にするにはきちんと体裁を整えなければなりません。
では続きは次回。
ここで制限(制約)付きボルツマンマシン(Restricted Boltzmann Machine : RBM)を実装しましたが、いくつか難点があります。
これが私の実装のバグのせいなのか制限付きボルツマンマシンそのものの特性のせいなのかはわかりませんが、データに0に近い値が多い場合、0と1を入れ替えると、つまりデータに1に近い値が多くなると、上手く学習しないのです。
Deep Learningに使うことがある、制限(制約)付きボルツマンマシン(Restricted Boltzmann Machine : RBM)をまた実装してみました。
ただし今度はGPUで動きます。
といってもあまり考えずにプログラムしたのでGPGPUにしては遅い気がします。
パフォーマンスチューニングはこれからして行きましょう。
以前、このような記事を書いたのですが、こっちのほうが正しいような気がしてきました。
しかし性能はどちらがいいかは…。
思い出しっぷりはこちらのほうが悪いですが、可視層もランダムにしようと思ったらこうなるでしょうね。
ちなみにこいつで画像を扱うと、結果がざらつくようです。
あんまり使いたくないですね。
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(); } } }
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
まあいいんじゃないでしょうか?