Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions demos/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,6 @@ create_demo(Demo_Stereo "Demo Stereo" "DGM")

# ================================================ DEMO ICR =================================================
create_demo(Demo_ICR "Demo ICR" "DGM;DNN;VIS")

# ================================================ DEMO RBM =================================================
create_demo(Demo_RBM "Demo RBM" "DGM;DNN;VIS")
124 changes: 124 additions & 0 deletions demos/Demo RBM.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include "DNN.h"
#include "DGM.h"
#include "VIS.h"
#include "DGM/timer.h"
#include <fstream>

namespace dgm = DirectGraphicalModels;

/**
* Reads the digits numerical value in a decimal notation
*
* @param file to read, and the number of digits to read
* @return an array of digit labels
*/
std::vector<byte> readGroundTruth(const std::string& fileName)
{
std::vector<byte> res;
std::ifstream inFile;
inFile.open(fileName.c_str());

if (inFile.is_open()) {
int val;
while (!inFile.eof()) {
inFile >> val;
res.push_back(static_cast<byte>(val));
}
inFile.close();
}
return res;
}


float sigmoidFunction(float x)
{
return 1.0f / (1.0f + expf(-x));
}

float sigmoidFunction_derivative(float x)
{
float s = sigmoidFunction(x);
return s * (1 - s);
}

int main()
{
const float learningRate = 0.05f;
const size_t numEpochs = 20;
const size_t numTestSamples = 2000;
const size_t numTrainSamples = 4000;

//const byte nStates = 10;
const word nFeatures = 28 * 28;
const size_t numNeuronsHiddenLayer = 10;

#ifdef WIN32
const std::string dataPath = "../../data/digits/";
#else
const std::string dataPath = "../../../data/digits/";
#endif

auto pLayerVisible = std::make_shared<dgm::dnn::CNeuronLayer>(nFeatures, 1, [](float x) { return x; }, [](float x) { return 1.0f; });
auto pLayerHidden = std::make_shared<dgm::dnn::CNeuronLayer>(numNeuronsHiddenLayer, nFeatures, &sigmoidFunction, &sigmoidFunction_derivative);

pLayerVisible->generateRandomWeights();
pLayerHidden->generateRandomWeights();

dgm::dnn::CRBM rbm({ pLayerVisible, pLayerHidden });

//rbm.debug();
Mat fv;

// ==================== TRAINING DIGITS ====================
dgm::Timer::start("Training...\n");
auto trainGT = readGroundTruth(dataPath + "train_gt.txt");
for (size_t e = 0; e < numEpochs; e++)
for (int s = 0; s < numTrainSamples; s++) {
std::stringstream ss;
ss << dataPath << "train/digit_" << std::setfill('0') << std::setw(4) << s << ".png";
std::string fileName = samples::findFile(ss.str());
Mat img = imread(fileName, 0);
img = img.reshape(1, img.cols * img.rows);
img.convertTo(fv, CV_32FC1, 1.0 / 255);
fv = Scalar(1.0f) - fv;

rbm.contrastiveDivergence(fv, 0.5f);
} // samples
dgm::Timer::stop();

// ==================== TESTING DIGITS ====================
//dgm::CCMat confMat(nStates);
dgm::Timer::start("Testing...");
auto testGT = readGroundTruth(dataPath + "test_gt.txt");
for (size_t s = 0; s < numTestSamples; s++) {
std::stringstream ss;
ss << dataPath << "test/digit_" << std::setfill('0') << std::setw(4) << s << ".png";
std::string fileName = samples::findFile(ss.str());
Mat img = imread(fileName, 0);
img = img.reshape(1, img.cols * img.rows);
img.convertTo(fv, CV_32FC1, 1.0 / 255);
fv = Scalar(1.0f) - fv;

Mat outputValues = rbm.reconstruct(fv);

//Point maxclass;
//minMaxLoc(outputValues, NULL, NULL, NULL, &maxclass);
//int number = maxclass.y;

//confMat.estimate(number, testGT[s]);
//printf("prediction [%d] for digit %d with %.3f%s at position %zu \n", number, testDataDigit[z], maxAccuracy, "%", z);
} // samples
dgm::Timer::stop();
//printf("Accuracy = %.2f%%\n", confMat.getAccuracy());

// Confusion matrix
//dgm::vis::CMarker marker;
//Mat cMat = confMat.getConfusionMatrix();
//Mat cMatImg = marker.drawConfusionMatrix(cMat, dgm::vis::MARK_BW);
//imshow("Confusion Matrix", cMatImg);
rbm.debug();

waitKey();

return 0;
}
1 change: 1 addition & 0 deletions include/DNN.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "DNN/Neuron.h"
#include "DNN/NeuronLayer.h"
#include "DNN/Perceptron.h"
#include "DNN/RBM.h"
// #include "DNN/Functions.hpp"

/**
Expand Down
1 change: 1 addition & 0 deletions modules/DNN/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ source_group("" FILES ${DNN_SOURCES} ${DNN_HEADERS})
source_group("Source Files\\Neuron" FILES "Neuron.h" "Neuron.cpp")
source_group("Source Files\\Neuron Layer" FILES "NeuronLayer.h" "NeuronLayer.cpp")
source_group("Source Files\\Perceptron" FILES "Perceptron.h" "Perceptron.cpp")
source_group("Source Files\\RBM" FILES "RBM.h" "RBM.cpp")

# Properties -> C/C++ -> General -> Additional Include Directories
include_directories(${PROJECT_SOURCE_DIR}/include
Expand Down
84 changes: 45 additions & 39 deletions modules/DNN/NeuronLayer.cpp
Original file line number Diff line number Diff line change
@@ -1,39 +1,45 @@
#include "NeuronLayer.h"
#include "DGM/random.h"
#include "DGM/parallel.h"
#include "macroses.h"

namespace DirectGraphicalModels { namespace dnn
{
void CNeuronLayer::generateRandomWeights(void)
{
m_weights = random::U(m_weights.size(), m_weights.type(), -0.5f, 0.5f);
m_biases = random::U(m_biases.size(), m_biases.type(), -0.5f, 0.5f);
}

void CNeuronLayer::dotProd(const Mat& values)
{
// this->m_netValues = this->m_weights * values + m_biases;
gemm(m_weights.t(), values, 1, m_biases, 1, m_netValues);
}

void CNeuronLayer::setNetValues(const Mat& values)
{
// Assertions
DGM_ASSERT(values.type() == m_netValues.type());
DGM_ASSERT(values.size() == m_netValues.size());
values.copyTo(m_netValues);
}

Mat CNeuronLayer::getValues(void) const
{
Mat res(m_netValues.clone());
for (int y = 0; y < res.rows; y++) {
float* pRes = res.ptr<float>(y);
for (int x = 0; x < res.cols; x++)
pRes[x] = m_activationFunction(pRes[x]);
}
return res;
}

}}
#include "NeuronLayer.h"
#include "DGM/random.h"
#include "DGM/parallel.h"
#include "macroses.h"

namespace DirectGraphicalModels {
namespace dnn
{
void CNeuronLayer::generateRandomWeights(void)
{
m_weights = random::U(m_weights.size(), m_weights.type(), -0.5f, 0.5f);
m_biases = random::U(m_biases.size(), m_biases.type(), -0.5f, 0.5f);
}

void CNeuronLayer::dotProd(const Mat& values)
{
// this->m_netValues = this->m_weights * values + m_biases;
gemm(m_weights.t(), values, 1, m_biases, 1, m_netValues);
}
void CNeuronLayer::dotProdVis(const Mat& values, const Mat& weights)
{
// this->m_netValues = weights * values + m_biases;
gemm(weights, values, 1, m_biases, 1, m_netValues);
}
void CNeuronLayer::setNetValues(const Mat& values)
{
// Assertions
DGM_ASSERT(values.type() == m_netValues.type());
DGM_ASSERT(values.size() == m_netValues.size());
values.copyTo(m_netValues);
}

Mat CNeuronLayer::getValues(void) const
{
Mat res(m_netValues.clone());
for (int y = 0; y < res.rows; y++) {
float* pRes = res.ptr<float>(y);
for (int x = 0; x < res.cols; x++)
pRes[x] = m_activationFunction(pRes[x]);
}
return res;
}

}
}
8 changes: 7 additions & 1 deletion modules/DNN/NeuronLayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ namespace DirectGraphicalModels {
*/
DllExport void dotProd(const Mat& values);
/**
* In the dotProd method, we cant multiply hidden neuron values by hidden neuron weights, so dotProdVis is created
* which we can input by which weights neuron are multiplied by.
* in dotProd -> this->m_netValues = this->m_weights * values + this->m_biases;
* in dotProdVis -> this->m_netValues = m_weights * values + this->m_biases;
*/
DllExport void dotProdVis(const Mat& values, const Mat& weights);
/**
* @brief Returns the values of the neurons of the layer
* @note This method returns the result of per-element application of the activation function to the neurons' net values, i.e. activationFunction(netValues)
* @returns The values of the neurons of the layer (size: 1 x numNeurons; type: CV_32FC1)
Expand All @@ -64,4 +71,3 @@ namespace DirectGraphicalModels {
using ptr_nl_t = std::shared_ptr<CNeuronLayer>;
}
}

121 changes: 121 additions & 0 deletions modules/DNN/RBM.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#include "RBM.h"
#include "DGM/random.h"
#include "macroses.h"

namespace DirectGraphicalModels {
namespace dnn {
CRBM::CRBM(const std::vector<ptr_nl_t>& vpLayers){
for (auto& nl : vpLayers)
m_vpNeuronLayers.push_back(nl);
}

Mat CRBM::getBinomial(const Mat& mean) {
Mat res(mean.clone());
for (int y = 0; y < res.rows; y++) {
float* pRes = res.ptr<float>(y);
for (int x = 0; x < res.cols; x++) {
if (pRes[x] < 0 || pRes[x]>1) {
pRes[x] = 0;
}
double r = random::U<double>(); // uniformly distributed random number betwee 0 and 1
if (r < pRes[x])
{
pRes[x] = 1;
}
else
{
pRes[x] = 0;
}
}
}
return res;
}

void CRBM::debug() {
std::cout << "Weight - rows: " << m_vpNeuronLayers[1]->getWeights().rows << " cols: " << m_vpNeuronLayers[1]->getWeights().cols << std::endl;

std::cout << "Positive H mean - rows: " << m_positiveHMean.rows << " cols: " << m_positiveHMean.cols << std::endl;
std::cout << "Positive H sample - rows: " << m_positiveHSample.rows << " cols: " << m_positiveHSample.cols << std::endl;
std::cout << "Negative H mean - rows: " << m_negativeHMean.rows << " cols: " << m_negativeHMean.cols << std::endl;
std::cout << "Negative H sample - rows: " << m_negativeHSample.rows << " cols: " << m_negativeHSample.cols << std::endl;
std::cout << "Negative V mean - rows: " << m_negativeVMean.rows << " cols: " << m_negativeVMean.cols << std::endl;
std::cout << "Negative V sample - rows: " << m_negativeVSample.rows << " cols: " << m_negativeVSample.cols << std::endl;
}

void CRBM::sampleVisible(const Mat& values) {
m_negativeVMean = propagateDown(values);
m_negativeVSample = getBinomial(m_negativeVMean);
}

void CRBM::sampleHiddenPositive(const Mat& values) {
m_positiveHMean = propagateUp(values);
m_positiveHSample = getBinomial(m_positiveHMean);

/*for (int y = 0; y < sample.rows; y++) {
float* pRess = sample.ptr<float>(y);
for (int x = 0; x < sample.cols; x++)
std::cout << pRess[x] << std::endl;
}*/
}

void CRBM::sampleHiddenNegative(const Mat& values) {
m_negativeHMean = propagateUp(values);
m_negativeHSample = getBinomial(m_negativeHMean);
}

Mat CRBM::propagateUp(const Mat& values) {
m_vpNeuronLayers[0]->setNetValues(values); //set the visible layer values

m_vpNeuronLayers[1]->dotProd(m_vpNeuronLayers[0]->getValues()); //sigmoid(sum(visible * weights)+bias)

return m_vpNeuronLayers.back()->getValues();
}

Mat CRBM::propagateDown(const Mat& values){
m_vpNeuronLayers[0]->dotProdVis(values, m_vpNeuronLayers[1]->getWeights());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use here just the old dotProd() method?
I.e.m_vpNeuronLayers[1]->dotProd(values); (index 1 instead of 0) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tried my best to explain why I used dotProdVis(values, weights) above. Please check it out.


return m_vpNeuronLayers[0]->getValues();
}

void CRBM::gibbsHVH(const Mat& hiddenSample) {
sampleVisible(hiddenSample);
sampleHiddenNegative(m_negativeVSample);
}
/* This implementation of RBM uses single step contrastive divergence algorithm, called CD-1 */
void CRBM::contrastiveDivergence(const Mat& values, float learningRate) {
//-------POSITIVE PHASE--------------------
/*In the positive phase, the input sample �v� from the visible layer is �clamped� to the input layer,
and then is propagated to the hidden layer. The result of the hidden layer activation is h. */
sampleHiddenPositive(values);

//------NEGATIVE PHASE---------------------
/*In the negative phase, �h� from the hidden layer is propagated back to the visible layer with the
new v, say v�. This is then propagated back to the hidden layer with activation result �h� */
gibbsHVH(m_positiveHMean);

std::vector<double> test = m_negativeHSample;
for (int i = 0; i < m_vpNeuronLayers[1]->getNumNeurons(); i++) {
//std::cout << i << std::endl;
for (int j = 0; j < m_vpNeuronLayers[0]->getNumNeurons(); j++)
{
m_vpNeuronLayers[1]->getWeights().at<float>(j, i) +=
learningRate * (m_positiveHMean.at<float>(i, 0) * values.at<float>(j, 0) - m_negativeHMean.at<float>(i, 0) * m_negativeVSample.at<float>(j, 0))/4000; // divide
}
m_vpNeuronLayers[1]->getBiases().at<float>(i, 0) += learningRate * (m_positiveHSample.at<float>(i, 0) - m_negativeHMean.at<float>(i, 0))/4000; //divide
}
for (int i = 0; i < m_vpNeuronLayers[0]->getNumNeurons(); i++)
{
//std::cout << i << std::endl;
m_vpNeuronLayers[0]->getBiases().at<float>(i, 0) += learningRate * (values.at<float>(i, 0) * m_negativeVSample.at<float>(i, 0))/4000; //divide
}
}

Mat CRBM::reconstruct(const Mat& values) {
Mat h, temp;

h = propagateUp(values);
temp = propagateDown(h);
return temp;
}
}
}
Loading