| //+------------------------------------------------------------------+
|
| //| VAE.mqh |
|
| //| Copyright DNG® |
|
| //| https://www.mql5.com |
|
| //+------------------------------------------------------------------+
|
| #property copyright "Copyright DNG®"
|
| #property link "https://www.mql5.com/ru/users/dng"
|
| #property version "1.00"
|
| //---
|
| #ifndef lr
|
| #include "..\..\NeuroNet_DNG\NeuroNet.mqh"
|
| #endif
|
| #define class_vae
|
| //---
|
| enum ENUM_VAE_SAMPLE_MODE
|
| {
|
| VAE_SAMPLE_RANDOM = 0,
|
| VAE_SAMPLE_MEAN
|
| };
|
| //---
|
| class CVAE : public CNeuronBaseOCL
|
| {
|
| protected:
|
| float m_fKLD_Mult;
|
| CBufferFloat* m_cRandom;
|
| ENUM_VAE_SAMPLE_MODE m_eSampleMode;
|
| ///\ingroup neuron_base_ff
|
| virtual bool feedForward(CNeuronBaseOCL *NeuronOCL); ///< \brief Feed Forward method of calling kernel ::VAE().@param NeuronOCL Pointer to previos layer.
|
| bool ValidateCheckedScratch(CNeuronBaseOCL *NeuronOCL,
|
| CBufferFloat *numeric_flags,
|
| CBufferFloat *numeric_ones,
|
| CBufferFloat *numeric_scalar);
|
| virtual bool VAEFeedForward(CNeuronBaseOCL *NeuronOCL,
|
| CBufferFloat *numeric_flags);
|
|
|
| ///\ingroup neuron_base_opt
|
| virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) { return true; } ///< Method for updating weights.\details Calling one of kernels ::UpdateWeightsMomentum() or ::UpdateWeightsAdam() in depends of optimization type (#ENUM_OPTIMIZATION).@param NeuronOCL Pointer to previos layer.
|
|
|
|
|
| public:
|
| CVAE();
|
| ~CVAE();
|
| virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint numNeurons, ENUM_OPTIMIZATION optimization_type, uint batch);
|
| //---
|
| virtual void SetKLDMult(float value) { m_fKLD_Mult = value; }
|
| virtual void SetSampleMode(ENUM_VAE_SAMPLE_MODE mode)
|
| {
|
| m_eSampleMode = mode;
|
| }
|
| virtual ENUM_VAE_SAMPLE_MODE SampleMode(void) const
|
| {
|
| return(m_eSampleMode);
|
| }
|
| virtual bool FeedForwardChecked(CNeuronBaseOCL *NeuronOCL,
|
| CBufferFloat *numeric_flags,
|
| CBufferFloat *numeric_ones,
|
| CBufferFloat *numeric_scalar,
|
| bool &rejected);
|
| ///< Method of initialization class.@param[in] numOutputs Number of connections to next layer.@param[in] myIndex Index of neuron in layer.@param[in] open_cl Pointer to #COpenCLMy object. #param[in] numNeurons Number of neurons in layer @param optimization_type Optimization type (#ENUM_OPTIMIZATION)@return Boolen result of operations.
|
| ///\ingroup neuron_base_gr
|
| ///@{
|
| virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL); ///< Method to transfer gradient to previous layer by calling kernel ::CalcHiddenGradient(). @param NeuronOCL Pointer to next layer.
|
| ///@}
|
| //---
|
| virtual bool Save(int const file_handle);///< Save method @param[in] file_handle handle of file @return logical result of operation
|
| virtual bool Load(int const file_handle);///< Load method @param[in] file_handle handle of file @return logical result of operation
|
| //---
|
| virtual int Type(void) const { return defNeuronVAEOCL; }///< Identificator of class.@return Type of class
|
| virtual void SetOpenCL(COpenCLMy *obj);
|
| virtual bool SupportsOpenCLChecked(void) const override;
|
| virtual bool SetOpenCLChecked(COpenCLMy *obj) override;
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Construct the VAE sampler with random sampling as the default. |
|
| //+------------------------------------------------------------------+
|
| CVAE::CVAE()
|
| : m_fKLD_Mult(0.01f),
|
| m_eSampleMode(VAE_SAMPLE_RANDOM)
|
| {
|
| m_cRandom = new CBufferFloat();
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Release the VAE random sampling buffer. |
|
| //+------------------------------------------------------------------+
|
| CVAE::~CVAE()
|
| {
|
| if(!!m_cRandom)
|
| delete m_cRandom;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Initialize the VAE layer and its sampling buffer. |
|
| //+------------------------------------------------------------------+
|
| bool CVAE::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint numNeurons, ENUM_OPTIMIZATION optimization_type, uint batch)
|
| {
|
| //--- Initialize the inherited layer before allocating the random source.
|
| if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, numNeurons, optimization_type, batch))
|
| return(false);
|
| //--- Allocate the deterministic/random sampling buffer.
|
| if(!m_cRandom)
|
| {
|
| m_cRandom = new CBufferFloat();
|
| if(!m_cRandom)
|
| return(false);
|
| }
|
| if(!m_cRandom.BufferInit(numNeurons, 0.0))
|
| return(false);
|
| if(!m_cRandom.BufferCreate(OpenCL))
|
| return(false);
|
| //--- Initialization completed successfully.
|
| return(true);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Execute one deterministic or random VAE forward sample. |
|
| //+------------------------------------------------------------------+
|
| bool CVAE::feedForward(CNeuronBaseOCL *NeuronOCL)
|
| {
|
| //--- Validate the posterior input layout.
|
| if(!OpenCL || !NeuronOCL)
|
| return(false);
|
| if(NeuronOCL.Neurons() % 2 != 0 ||
|
| NeuronOCL.Neurons() / 2 != Neurons())
|
| return(false);
|
| //--- Select the deterministic mean sampler or generate random samples.
|
| if(m_eSampleMode == VAE_SAMPLE_MEAN)
|
| {
|
| if(!m_cRandom.Fill(0.0f))
|
| return(false);
|
| }
|
| else
|
| {
|
| double random[];
|
| if(!MathRandomNormal(0, 1, m_cRandom.Total(), random))
|
| return(false);
|
| if(!m_cRandom.AssignArray(random))
|
| return(false);
|
| if(!m_cRandom.BufferWrite())
|
| return(false);
|
| }
|
| //--- Execute the selected sample through the VAE kernel.
|
| return(VAEFeedForward(NeuronOCL, NULL));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Validates caller scratch against every VAE kernel operand. |
|
| //+------------------------------------------------------------------+
|
| bool CVAE::ValidateCheckedScratch(CNeuronBaseOCL *NeuronOCL,
|
| CBufferFloat *numeric_flags,
|
| CBufferFloat *numeric_ones,
|
| CBufferFloat *numeric_scalar)
|
| {
|
| //--- Resolve the exact posterior, local-draw, and output operands.
|
| if(!OpenCL || !NeuronOCL || !m_cRandom || !Output || !NeuronOCL.getOutput() || Neurons() < 1 ||
|
| NeuronOCL.Neurons() % 2 != 0 || NeuronOCL.Neurons() / 2 != Neurons())
|
| return(false);
|
| CBufferFloat *posterior = NeuronOCL.getOutput();
|
| const ulong posterior_total = 2 * ulong(Neurons());
|
| //--- Reject invalid scratch ownership, spans, and aliases before sampling mutates a draw.
|
| if(!numeric_flags || !numeric_ones || !numeric_scalar || numeric_flags == numeric_ones ||
|
| numeric_flags == numeric_scalar || numeric_ones == numeric_scalar ||
|
| posterior.GetOpenCL() != OpenCL || m_cRandom.GetOpenCL() != OpenCL || Output.GetOpenCL() != OpenCL ||
|
| numeric_flags.GetOpenCL() != OpenCL || numeric_ones.GetOpenCL() != OpenCL ||
|
| numeric_scalar.GetOpenCL() != OpenCL || posterior.GetIndex() < 0 || m_cRandom.GetIndex() < 0 ||
|
| Output.GetIndex() < 0 || numeric_flags.GetIndex() < 0 || numeric_ones.GetIndex() < 0 ||
|
| numeric_scalar.GetIndex() < 0 || ulong(posterior.Total()) < posterior_total || m_cRandom.Total() < Neurons() ||
|
| Output.Total() < Neurons() || numeric_flags.Total() < Neurons() || numeric_ones.Total() < Neurons() ||
|
| numeric_scalar.Total() != 1 || numeric_flags.GetIndex() == numeric_ones.GetIndex() ||
|
| numeric_flags.GetIndex() == numeric_scalar.GetIndex() || numeric_ones.GetIndex() == numeric_scalar.GetIndex() ||
|
| numeric_flags.GetIndex() == posterior.GetIndex() || numeric_flags.GetIndex() == m_cRandom.GetIndex() ||
|
| numeric_flags.GetIndex() == Output.GetIndex() || numeric_ones.GetIndex() == posterior.GetIndex() ||
|
| numeric_ones.GetIndex() == m_cRandom.GetIndex() || numeric_ones.GetIndex() == Output.GetIndex() ||
|
| numeric_scalar.GetIndex() == posterior.GetIndex() || numeric_scalar.GetIndex() == m_cRandom.GetIndex() ||
|
| numeric_scalar.GetIndex() == Output.GetIndex())
|
| return(false);
|
| //--- Finalize the pre-dispatch scratch validation.
|
| return(true);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Dispatch the selected VAE sample with an optional numeric flag. |
|
| //+------------------------------------------------------------------+
|
| bool CVAE::VAEFeedForward(CNeuronBaseOCL *NeuronOCL,
|
| CBufferFloat *numeric_flags)
|
| {
|
| //--- Validate the VAE buffers required by the checked kernel binding.
|
| if(!OpenCL || !NeuronOCL || !m_cRandom)
|
| return(false);
|
| const ulong posterior_total = 2 * ulong(Neurons());
|
| if(numeric_flags && (!NeuronOCL.getOutput() || !Output || Neurons() < 1 ||
|
| NeuronOCL.getOutput().GetOpenCL() != OpenCL ||
|
| m_cRandom.GetOpenCL() != OpenCL || Output.GetOpenCL() != OpenCL ||
|
| numeric_flags.GetOpenCL() != OpenCL || NeuronOCL.getOutput().GetIndex() < 0 ||
|
| m_cRandom.GetIndex() < 0 || Output.GetIndex() < 0 || numeric_flags.GetIndex() < 0 ||
|
| ulong(NeuronOCL.getOutput().Total()) < posterior_total || m_cRandom.Total() < Neurons() ||
|
| Output.Total() < Neurons() || numeric_flags.Total() < Neurons() ||
|
| numeric_flags.GetIndex() == NeuronOCL.getOutput().GetIndex() ||
|
| numeric_flags.GetIndex() == m_cRandom.GetIndex() ||
|
| numeric_flags.GetIndex() == Output.GetIndex()))
|
| return(false);
|
| //--- Bind the posterior, selected local draw, output, and optional status.
|
| if(!OpenCL.SetArgumentBuffer(def_k_VAEFeedForward, def_k_vaeff_inputs, NeuronOCL.getOutput().GetIndex()))
|
| return(false);
|
| if(!OpenCL.SetArgumentBuffer(def_k_VAEFeedForward, def_k_vaeff_random, m_cRandom.GetIndex()))
|
| return(false);
|
| if(!OpenCL.SetArgumentBuffer(def_k_VAEFeedForward, def_k_vaeff_outputd, Output.GetIndex()))
|
| return(false);
|
| if(!OpenCL.SetArgumentBuffer(def_k_VAEFeedForward, def_k_vaeff_numeric_flags,
|
| (numeric_flags ? numeric_flags.GetIndex() : Output.GetIndex())))
|
| return(false);
|
| if(!OpenCL.SetArgument(def_k_VAEFeedForward, def_k_vaeff_flags_enabled, (numeric_flags ? 1 : 0)))
|
| return(false);
|
| uint off_set[] = {0};
|
| uint NDrange[] = {Neurons()};
|
| if(!OpenCL.Execute(def_k_VAEFeedForward, 1, off_set, NDrange))
|
| return(false);
|
| //--- Finalize the VAE kernel dispatch.
|
| return(true);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Runs one checked VAE sample and reduces its output status. |
|
| //+------------------------------------------------------------------+
|
| bool CVAE::FeedForwardChecked(CNeuronBaseOCL *NeuronOCL,
|
| CBufferFloat *numeric_flags,
|
| CBufferFloat *numeric_ones,
|
| CBufferFloat *numeric_scalar,
|
| bool &rejected)
|
| {
|
| rejected = false;
|
| //--- Reject malformed posterior layouts and scratch aliases before sampling.
|
| if(!ValidateCheckedScratch(NeuronOCL, numeric_flags, numeric_ones, numeric_scalar))
|
| return(false);
|
| //--- Preserve the selected mean/random sampling mode before dispatch.
|
| if(m_eSampleMode == VAE_SAMPLE_MEAN)
|
| {
|
| if(!m_cRandom || !m_cRandom.Fill(0.0f))
|
| return(false);
|
| }
|
| else
|
| {
|
| double random[];
|
| if(!m_cRandom || !MathRandomNormal(0, 1, m_cRandom.Total(), random) ||
|
| !m_cRandom.AssignArray(random) || !m_cRandom.BufferWrite())
|
| return(false);
|
| }
|
| //--- Dispatch and immediately reduce the VAE-written output prefix.
|
| if(!VAEFeedForward(NeuronOCL, numeric_flags))
|
| return(false);
|
| //--- Finalize the checked VAE dispatch.
|
| return(ReduceNumericFlags(numeric_flags, numeric_ones, numeric_scalar,
|
| (uint)Neurons(), rejected));
|
| }
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| bool CVAE::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
|
| {
|
| if(!OpenCL || !NeuronOCL)
|
| return false;
|
| //---
|
| if(!OpenCL.SetArgumentBuffer(def_k_VAECalcHiddenGradient, def_k_vaehg_input, NeuronOCL.getOutput().GetIndex()))
|
| return false;
|
| if(!OpenCL.SetArgumentBuffer(def_k_VAECalcHiddenGradient, def_k_vaehg_inp_grad, NeuronOCL.getGradient().GetIndex()))
|
| return false;
|
| if(!OpenCL.SetArgumentBuffer(def_k_VAECalcHiddenGradient, def_k_vaehg_random, m_cRandom.GetIndex()))
|
| return false;
|
| if(!OpenCL.SetArgumentBuffer(def_k_VAECalcHiddenGradient, def_k_vaehg_gradient, Gradient.GetIndex()))
|
| return false;
|
| if(!OpenCL.SetArgument(def_k_VAECalcHiddenGradient, def_k_vaehg_kld_mult, m_fKLD_Mult))
|
| return false;
|
| uint off_set[] = {0};
|
| uint NDrange[] = {Neurons()};
|
| if(!OpenCL.Execute(def_k_VAECalcHiddenGradient, 1, off_set, NDrange))
|
| return false;
|
| //---
|
| return true;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| bool CVAE::Save(const int file_handle)
|
| {
|
| //---
|
| if(!CNeuronBaseOCL::Save(file_handle))
|
| return false;
|
| if(FileWriteFloat(file_handle, m_fKLD_Mult) < sizeof(m_fKLD_Mult))
|
| return false;
|
| //---
|
| return true;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| bool CVAE::Load(const int file_handle)
|
| {
|
| if(!CNeuronBaseOCL::Load(file_handle))
|
| return false;
|
| m_fKLD_Mult = FileReadFloat(file_handle);
|
| //---
|
| if(!m_cRandom)
|
| {
|
| m_cRandom = new CBufferFloat();
|
| if(!m_cRandom)
|
| return false;
|
| }
|
| m_cRandom.BufferFree();
|
| if(!m_cRandom.BufferInit(Neurons(), 0.0))
|
| return false;
|
| if(!m_cRandom.BufferCreate(OpenCL))
|
| return false;
|
| //---
|
| return true;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| |
|
| //+------------------------------------------------------------------+
|
| void CVAE::SetOpenCL(COpenCLMy *obj)
|
| {
|
| CNeuronBaseOCL::SetOpenCL(obj);
|
| if(!!m_cRandom)
|
| m_cRandom.BufferCreate(obj);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Checks checked-transfer support of VAE base and random holder. |
|
| //+------------------------------------------------------------------+
|
| bool CVAE::SupportsOpenCLChecked(void) const
|
| {
|
| return(Type() == defNeuronVAEOCL && CheckPointer(m_cRandom) != POINTER_INVALID);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Transfers VAE random data before inherited context publication. |
|
| //+------------------------------------------------------------------+
|
| bool CVAE::SetOpenCLChecked(COpenCLMy *obj)
|
| {
|
| if(CheckPointer(obj) == POINTER_INVALID || !SupportsOpenCLChecked())
|
| return(false);
|
| //--- Synchronize the random holder only when it still belongs to another context.
|
| if(m_cRandom.Total() > 0 && m_cRandom.GetIndex() >= 0 &&
|
| m_cRandom.GetOpenCL() != obj && !m_cRandom.BufferRead())
|
| return(false);
|
| //--- Recreate a changed or host-only random holder before base publication.
|
| if(m_cRandom.Total() > 0 &&
|
| (m_cRandom.GetOpenCL() != obj || m_cRandom.GetIndex() < 0) &&
|
| !m_cRandom.BufferCreate(obj))
|
| return(false);
|
| //--- Finalize by transferring inherited base owners last.
|
| return(CNeuronBaseOCL::SetOpenCLChecked(obj));
|
| }
|
| //+------------------------------------------------------------------+
|
|
|
|
|
| //+------------------------------------------------------------------+
|