忍者ブログ

Memeplexes

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

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



GPUでニューラルネットワーク更新(マトリクス×ベクトル) その2 (OpenCL)

パフォーマンス改善

この間、GPUでニューラルネットワークを更新しましたが、その実装は適当でした。
遅いのです。
その実装を流用して制限付きボルツマンマシン(Restricted Boltzmann Machine : RBM)を実装した所、CPUより遅いというありさまでした。
今回はパフォーマンスを改善したやり方でニューラルネットワークの更新をしてみようと思います。
と言っても中身はただのマトリクスとベクトルの乗算です。
他にも流用できそうですね。


拍手[0回]


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

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

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


拍手[1回]


GPUで疑似乱数 (OpenCL, Cloo)(Xorshift)

GPUで擬似乱数

DirectX11を使ってGPUで擬似乱数を生成したので、今度はOpenCLで同じことをやってみます。

Program.cs

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

myKernelProgram.cl

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が同じなので当たり前ですが。
上手く行っているようです。

拍手[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回]