/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ #include "Rbm.hpp" void mylog(const char* format, ...); #define printf mylog #define EPSILON_SIGMA 0.001 Rbm::Rbm(Weights &weights, const MatrixXd &batch) : m_w(weights) , m_batch(batch) , m_progress(0) { Noise_Init(&m_noise, 0x32727155); updateHiddenBatch(); } Rbm::~Rbm() { Noise_Free(&m_noise); } void Rbm::noiseGaussian(MatrixXd &dst) { for (size_t i=0; i < dst.rows(); i++) { for (size_t j=0; j < dst.cols(); j++) { dst(i, j) = Noise_Gaussian(&m_noise); } } } void Rbm::noiseUniform(MatrixXd &dst) { for (size_t i=0; i < dst.rows(); i++) { for (size_t j=0; j < dst.cols(); j++) { dst(i, j) = Noise_Uniform(&m_noise); } } } void Rbm::sampleGaussian(MatrixXd &dst, MatrixXd const &src) { MatrixXd n(src.rows(), src.cols()); noiseGaussian(n); dst = n.array() + src.array(); } void Rbm::sampleGaussian(MatrixXd &srcDst) { sampleGaussian(srcDst, srcDst); } void Rbm::sample(MatrixXd &srcDst) { sample(srcDst, srcDst); } void Rbm::sample(MatrixXd &dst, MatrixXd const &src) { MatrixXd n(src.rows(), src.cols()); noiseUniform(n); dst = (src.array() > n.array()).cast(); } void Rbm::probsLogistic(MatrixXd &srcDst) { srcDst = (1 + (-srcDst.array()).exp()).array().cwiseInverse(); } void Rbm::probsLogistic(RowVectorXd &srcDst) { srcDst = (1 + (-srcDst.array()).exp()).array().cwiseInverse(); } void Rbm::normalizeData(MatrixXd &dst, MatrixXd const &src) { MatrixXd mean = src.rowwise().mean(); // cout << "mean" << ": " << endl << mean << endl; dst = src - mean.replicate(1, src.cols()); // cout << "dst - mean" << ": " << endl << dst << endl; MatrixXd x = dst.array().square(); MatrixXd var = x.rowwise().mean(); // cout << "var" << ": " << endl << var << endl; MatrixXd stddev_norm = var.array().sqrt().cwiseInverse(); dst.array() *= stddev_norm.replicate(1, src.cols()).array(); // cout << "dst" << ": " << endl << dst << endl; } void Rbm::train(size_t numEpochs, size_t miniBatchSize, double sigmaMin) { size_t i; size_t epoch; size_t gibbs; size_t trainingSize = m_batch.rows(); size_t trainingSizeRemain = trainingSize; size_t batchRowIndex = 0; double dProgress = 1.0/(numEpochs*(double)trainingSize/std::min(miniBatchSize, trainingSize)); MatrixXd grad_bias_v(MatrixXd::Zero(1, m_w.getNumVisible())); MatrixXd grad_bias_h(MatrixXd::Zero(1, m_w.getNumHidden())); MatrixXd grad_weight(MatrixXd::Zero(m_w.getNumVisible(), m_w.getNumHidden())); MatrixXd __batch = m_batch_normalized; MatrixXd momentum_weights = MatrixXd::Zero(m_w.getNumVisible(), m_w.getNumHidden()); MatrixXd momentum_bias_v(MatrixXd::Zero(1, m_w.getNumVisible())); MatrixXd momentum_bias_h(MatrixXd::Zero(1, m_w.getNumHidden())); MatrixXd penalty_weights = MatrixXd::Zero(m_w.getNumVisible(), m_w.getNumHidden()); double L1 = 0; double L2 = 0; if (m_params.m_doNormalizeData && !m_params.m_useVisibleGaussian) { probsLogistic(__batch); } m_progress = 0; while (trainingSizeRemain) { cout << "trainingSizeRemain: " << trainingSizeRemain << endl; size_t toSlice = std::min(miniBatchSize, trainingSizeRemain); MatrixXd batch = __batch.block(batchRowIndex, 0, toSlice, m_w.getNumVisible()); trainingSizeRemain -= toSlice; batchRowIndex += toSlice; size_t batchSize = batch.rows(); double learning_rate = m_params.m_muWeights/std::min(miniBatchSize, trainingSize); MatrixXd batch_sampled(batchSize, m_w.getNumVisible()); MatrixXd v_sampled(batchSize, m_w.getNumVisible()); MatrixXd vis(batchSize, m_w.getNumVisible()); MatrixXd hid(batchSize, m_w.getNumHidden()); for (epoch=0; epoch < numEpochs; epoch++) { onProgressChanged(); if (m_params.m_doSampleBatch) { // When the hidden units are being driven by data, always use stochastic binary states sample(batch_sampled, batch); // Create hidden layer base on sampled training data hid = batch_sampled * m_w.weights(); hid += m_w.hiddenBias().replicate(batchSize, 1); probsLogistic(hid); } else { // Create hidden layer base on training data hid = batch * m_w.weights(); hid += m_w.hiddenBias().replicate(batchSize, 1); probsLogistic(hid); } // Sample hidden if (!m_params.m_doRaoBlackwell) { sample(hid); } // Update weights (positive phase) grad_bias_v = batch.colwise().sum(); grad_bias_h = hid.colwise().sum(); grad_weight = batch.transpose() * hid; for (gibbs=0; gibbs < m_params.m_numGibbs; gibbs++) { if (m_params.m_useHiddenGaussian) { sampleGaussian(hid); } else { sample(hid); } if (m_params.m_useVisibleGaussian) { // Create visible reconstruction (a fantasy...) given hid vis = hid * m_w.weights().transpose(); vis += m_w.visibleBias().replicate(batchSize, 1); sampleGaussian(v_sampled, vis); hid = v_sampled * m_w.weights(); hid += m_w.hiddenBias().replicate(batchSize, 1); probsLogistic(hid); } else { // Create visible reconstruction (a fantasy...) given hid vis = hid * m_w.weights().transpose(); vis += m_w.visibleBias().replicate(batchSize, 1); probsLogistic(vis); if (m_params.m_doSampleVisible) { sample(v_sampled, vis); // Create hidden representation given sampled v hid = v_sampled * m_w.weights(); hid += m_w.hiddenBias().replicate(batchSize, 1); probsLogistic(hid); } else { // Create hidden representation given v hid = vis * m_w.weights(); hid += m_w.hiddenBias().replicate(batchSize, 1); probsLogistic(hid); } } } // Update weights (negative phase) grad_bias_v -= vis.colwise().sum(); grad_bias_h -= hid.colwise().sum(); grad_weight -= vis.transpose() * hid; for (int i=0; i < m_w.weights().rows(); i++) { for (int j=0; j < m_w.weights().cols(); j++) { if (m_w.weights()(i,j) >= 0) { penalty_weights(i,j) = m_params.m_weightDecay; } else { penalty_weights(i,j) = -m_params.m_weightDecay; } } } L1 = m_w.weights().array().abs().sum(); L2 = m_w.weights().array().square().sum(); momentum_bias_v = m_params.m_momentum*momentum_bias_v + grad_bias_v; momentum_bias_h = m_params.m_momentum*momentum_bias_h + grad_bias_h; momentum_weights = m_params.m_momentum*momentum_weights + grad_weight - L2*penalty_weights; m_w.visibleBias() += learning_rate*momentum_bias_v; if (m_params.m_doSparse) { MatrixXd h1 = hid-MatrixXd::Ones(hid.rows(), hid.cols())*m_params.m_sparsity; RowVectorXd hm = h1.colwise().mean(); m_w.hiddenBias() -= m_params.m_muSparsity * hm; } else { m_w.hiddenBias() += learning_rate*momentum_bias_h; } m_w.weights() += learning_rate*momentum_weights; if (m_params.m_sigmaDecay > 0) { if (m_variableSigma[0] > sigmaMin) { } } m_progress += dProgress; } // Number of epochs MatrixXd diffErr = batch - vis; diffErr.array() *= diffErr.array(); double err = diffErr.colwise().sum().sum(); cout << "error (per mini batch) = " << err << endl; cout << "L1 = " << L1 << endl; cout << "L2 = " << L2 << endl; } // number of mini batches updateHiddenBatch(); MatrixXd vis = m_h * m_w.weights().transpose(); vis += m_w.visibleBias().replicate(__batch.rows(), 1); probsLogistic(vis); MatrixXd diffErr = __batch - vis; diffErr.array() *= diffErr.array(); double err = diffErr.colwise().sum().sum(); cout << "error (total) = " << err << endl; onProgressChanged(); } double Rbm::getProgress() const { return m_progress; } void Rbm::toHidden(RowVectorXd &h, RowVectorXd const &v) { h = v * m_w.weights(); h += m_w.hiddenBias(); probsLogistic(h); } void Rbm::toVisible(RowVectorXd &v, RowVectorXd const &h) { v = h * m_w.weights().transpose(); v += m_w.visibleBias(); if (!m_params.m_useVisibleGaussian) { probsLogistic(v); } } void Rbm::setConstantSigma(double value) { m_params.m_constantSigma = value; onParamsChanged(); } RowVectorXd& Rbm::getVariableSigma() { return m_variableSigma; } void Rbm::setSigmaDecay(double value) { m_params.m_sigmaDecay = value; onParamsChanged(); } void Rbm::setWeightDecay(double value) { m_params.m_weightDecay = value; onParamsChanged(); } void Rbm::setLambda(double value) { m_params.m_lambda = value; onParamsChanged(); } void Rbm::setSparsity(double value) { m_params.m_sparsity = value; onParamsChanged(); } void Rbm::setUseVisibleGaussian(bool flag) { m_params.m_useVisibleGaussian = flag; onParamsChanged(); } void Rbm::setUseHiddenGaussian(bool flag) { m_params.m_useHiddenGaussian = flag; onParamsChanged(); } void Rbm::setDoRaoBlackwell(bool flag) { m_params.m_doRaoBlackwell = flag; onParamsChanged(); } void Rbm::setDoSampleVisible(bool flag) { m_params.m_doSampleVisible = flag; onParamsChanged(); } void Rbm::setDoSampleBatch(bool flag) { m_params.m_doSampleBatch = flag; onParamsChanged(); } void Rbm::setDoSparse(bool flag) { m_params.m_doSparse = flag; onParamsChanged(); } void Rbm::setNormalizeData(bool flag) { m_params.m_doNormalizeData = flag; onParamsChanged(); } void Rbm::setDoLearnVariance(bool flag) { m_params.m_doLearnVariance = flag; onParamsChanged(); } void Rbm::setNumGibbs(size_t value) { m_params.m_numGibbs = value; onParamsChanged(); } void Rbm::setMuWeights(double value) { m_params.m_muWeights = value; onParamsChanged(); } void Rbm::setMuSparsity(double value) { m_params.m_muSparsity = value; onParamsChanged(); } void Rbm::setMomentum(double value) { m_params.m_momentum = value; onParamsChanged(); } MatrixXd const& Rbm::getHiddenBatch() { return m_h; } MatrixXd const& Rbm::getBatch() { return m_batch_normalized; } void Rbm::updateHiddenBatch() { if (m_batch.rows() == 0) { return; } m_batch_normalized.resize(m_batch.rows(), m_w.getNumHidden()); if (m_params.m_doNormalizeData) { normalizeData(m_batch_normalized, m_batch); } else { m_batch_normalized = m_batch; } m_h.resize(m_batch.rows(), m_w.getNumHidden()); m_h = m_batch_normalized * m_w.weights(); m_h += m_w.hiddenBias().replicate(m_batch.rows(), 1); probsLogistic(m_h); } Rbm::Params const& Rbm::params() { return m_params; }