[PR]
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
プログラミング、3DCGとその他いろいろについて
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
前回は制限(制約)付きボルツマンマシン(Restricted Boltzmann Machine : RBM)の可視ニューロンを(GPUで)更新しました。
今回は隠れニューロンを更新します。
もちろん使うのはGPUです。
この間、GPUでニューラルネットワークを更新しましたが、その実装は適当でした。
遅いのです。
その実装を流用して制限付きボルツマンマシン(Restricted Boltzmann Machine : RBM)を実装した所、CPUより遅いというありさまでした。
今回はパフォーマンスを改善したやり方でニューラルネットワークの更新をしてみようと思います。
と言っても中身はただのマトリクスとベクトルの乗算です。
他にも流用できそうですね。
Deep Learningに使うことがある、制限(制約)付きボルツマンマシン(Restricted Boltzmann Machine : RBM)をまた実装してみました。
ただし今度はGPUで動きます。
といってもあまり考えずにプログラムしたのでGPGPUにしては遅い気がします。
パフォーマンスチューニングはこれからして行きましょう。
DirectX11を使ってGPUで擬似乱数を生成したので、今度はOpenCLで同じことをやってみます。
using System.Linq; using Cloo; struct Xorshift128RandomGpu { public int w, x, y, z; public Xorshift128RandomGpu(int seed) { if (seed == 0) { seed += 11; } w = seed; x = seed << 16 + seed >> 16; y = w + x; z = x ^ y; } } class Program { static void Main() { var platform = ComputePlatform.Platforms .Where(p => p.Devices.Count(d => d.Type == ComputeDeviceTypes.Gpu) > 0) .First(); var devices = platform.Devices .Where(d => d.Type == ComputeDeviceTypes.Gpu) .ToArray(); var context = new ComputeContext( devices, new ComputeContextPropertyList(platform), null, System.IntPtr.Zero ); var commandQueue = new ComputeCommandQueue( context, devices[0], ComputeCommandQueueFlags.None ); const int elementCount = 20; var resultBuffer = new ComputeBuffer<float>( context, ComputeMemoryFlags.ReadWrite, elementCount ); var random = new System.Random(0); var randomGeneratorsBuffer = new ComputeBuffer<Xorshift128RandomGpu>( context, ComputeMemoryFlags.ReadWrite | ComputeMemoryFlags.CopyHostPointer, Enumerable.Range(0, elementCount).Select(i=>new Xorshift128RandomGpu(random.Next())).ToArray() ); var program = new ComputeProgram( context, System.IO.File.ReadAllText("myKernelProgram.cl") ); try { program.Build(devices, null, null, System.IntPtr.Zero); } catch { System.Console.WriteLine(program.GetBuildLog(devices[0])); } var kernel = program.CreateKernel("updateRandom"); kernel.SetMemoryArgument(0, resultBuffer); kernel.SetMemoryArgument(1, randomGeneratorsBuffer); commandQueue.Execute( kernel, null, new long[] { elementCount }, new long[] { 1 }, null ); float[] dataFromGpu = new float[elementCount]; commandQueue.ReadFromBuffer<float>(resultBuffer, ref dataFromGpu, true, null); foreach (var number in dataFromGpu) { System.Console.WriteLine(number); } kernel.Dispose(); program.Dispose(); randomGeneratorsBuffer.Dispose(); resultBuffer.Dispose(); commandQueue.Dispose(); context.Dispose(); } }
typedef struct { int w; int x; int y; int z; }Xorshift128Random; int next(Xorshift128Random* random) { int t = (random->x ^ (random->x << 11)); random->x = random->y; random->y = random->z; random->z = random->w; random->w = (random->w = (random->w ^ (random->w >> 19)) ^ (t ^ (t >> 8))); return random->w; } float nextFloat(Xorshift128Random* random) { return ((float)next(random) / INT_MAX); } __kernel void updateRandom( __global float *resultBuffer, __global Xorshift128Random *randomGeneratorBuffer) { Xorshift128Random random = randomGeneratorBuffer[get_global_id(0)]; resultBuffer[get_global_id(0)] = nextFloat(&random); randomGeneratorBuffer[get_global_id(0)] = random; }
結果は次のようになります:
0.7322494 0.8232476 0.7558671 0.5355725 0.2103059 0.5624865 0.8987527 0.4662975 0.998933 0.2610296 0.2951466 0.4376118 0.6250286 0.4811476 0.9961796 0.01847547 0.8486539 0.9736332 0.671631 0.3234635
この前と結果は同じですね。
seedが同じなので当たり前ですが。
上手く行っているようです。
以前、このような記事を書いたのですが、こっちのほうが正しいような気がしてきました。
しかし性能はどちらがいいかは…。
思い出しっぷりはこちらのほうが悪いですが、可視層もランダムにしようと思ったらこうなるでしょうね。
ちなみにこいつで画像を扱うと、結果がざらつくようです。
あんまり使いたくないですね。
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
まあいいんじゃないでしょうか?