Skip to content

Commit

Permalink
Merge pull request #20 from kartikdutt18/DarknetModel
Browse files Browse the repository at this point in the history
Add Darknet model.
  • Loading branch information
KimSangYeon-DGU authored Aug 12, 2020
2 parents 84deb29 + acc4eb0 commit 5869a40
Show file tree
Hide file tree
Showing 15 changed files with 845 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ data/*
*.jpg
*.png
*.txt
*.bin
.travis/configs.hpp
Testing/*
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,9 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/)
# Recurse into each model.
set(DIRS
utils/
ensmallen_utils/
dataloader/
models/
tests/
)

Expand Down
1 change: 1 addition & 0 deletions dataloader/dataloader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <boost/foreach.hpp>
#include <mlpack/core.hpp>
#include <utils/utils.hpp>
#include <set>


#ifndef MODELS_DATALOADER_HPP
Expand Down
18 changes: 10 additions & 8 deletions dataloader/dataloader_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ template<

// We use to endls here as one of them will be replaced by print
// command below.
Log::Info << "Found " << imagesDirectory.size() << " belonging to " <<
mlpack::Log::Info << "Found " << imagesDirectory.size() << " belonging to " <<
label << " class." << std::endl << std::endl;

size_t loadedImages = 0;
Expand All @@ -395,7 +395,6 @@ template<
{
continue;
}

mlpack::data::ImageInfo imageInfo(imageWidth, imageHeight, imageDepth);

// Load the image.
Expand All @@ -406,12 +405,14 @@ template<
mlpack::data::Load(imageName.string(), image, imageInfo);

// Add object to training set.
dataset.insert_cols(0, image);
labels.insert_cols(0, arma::vec(1).fill(label));

loadedImages++;
mlpack::Log::Info << "Loaded " << loadedImages << " out of " <<
imagesDirectory.size() << "\r" << std::endl;
if (image.n_rows == dataset.n_rows || dataset.n_elem == 0)
{
labels.insert_cols(0, arma::vec(1).fill(label));
dataset.insert_cols(0, image);
loadedImages++;
mlpack::Log::Info << "Loaded " << loadedImages << " out of " <<
imagesDirectory.size() << "\r" << std::endl;
}
}
}

Expand Down Expand Up @@ -485,6 +486,7 @@ template<
validationData.n_rows - 1);

augmentations.Transform(trainFeatures, imageWidth, imageHeight, imageDepth);
augmentations.Transform(validFeatures, imageWidth, imageHeight, imageDepth);

mlpack::Log::Info << "Found " << totalClasses << " classes." << std::endl;

Expand Down
38 changes: 38 additions & 0 deletions dataloader/preprocessor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,44 @@ class PreProcessor
{
// Nothing to do here. Added to match the rest of the codebase.
}

/**
* Converts image to channel first format used in PyTorch. Performs the same function
* as torch.transforms.ToTensor().
*
* @param trainFeatures Input features that will be converted into channel first format.
* @param imageWidth Width of the image in dataset.
* @param imageHeight Height of the image in dataset.
* @param imageDepth Depth / Number of channels of the image in dataset.
*/
static void ChannelFirstImages(DatasetX& trainFeatures,
const size_t imageWidth,
const size_t imageHeight,
const size_t imageDepth,
bool normalize = true)
{
for (size_t idx = 0; idx < trainFeatures.n_cols; idx++)
{
// Create a copy of the current image so that the image isn't affected.
arma::cube inputTemp(trainFeatures.col(idx).memptr(), 3, 224, 224);

size_t currentOffset = 0;
for (size_t i = 0; i < inputTemp.n_slices; i++)
{
trainFeatures.col(idx)(arma::span(currentOffset, currentOffset +
inputTemp.slice(i).n_elem - 1), arma::span()) =
arma::vectorise(inputTemp.slice(i).t());
currentOffset += inputTemp.slice(i).n_elem;
}
}

if (normalize)
{
// Convert each element to uint8 first and then divide by 255.
for (size_t i = 0; i < trainFeatures.n_elem; i++)
trainFeatures(i) = ((uint8_t)(trainFeatures(i)) / 255.0);
}
}
};

#endif
16 changes: 16 additions & 0 deletions ensmallen_utils/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
project(ensmallen_utils)

set(DIR_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/)

set(SOURCES
print_metric.hpp
periodic_save.hpp)

foreach(file ${SOURCES})
set(DIR_SRCS ${DIR_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/${file})
endforeach()

# Append sources (with directory name) to list of all models sources (used at
# the parent scope).
set(DIRS ${DIRS} ${DIR_SRCS} PARENT_SCOPE)
102 changes: 102 additions & 0 deletions ensmallen_utils/periodic_save.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* @file utils.hpp
* @author Kartik Dutt
*
* Definition of Periodic Save utility functions.
*
* mlpack is free software; you may redistribute it and/or modify it under the
* terms of the 3-clause BSD license. You should have received a copy of the
* 3-clause BSD license along with mlpack. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/

#ifndef ENSMALLEN_CALLBACKS_PERIODIC_SAVE_HPP
#define ENSMALLEN_CALLBACKS_PERIODIC_SAVE_HPP

#include <ensmallen.hpp>
#include <mlpack/core.hpp>

namespace ens {

/**
* Saves model being trained periodically.
*
* @tparam ANNType Type of model which will be used for evaluating metric.
*/
template<typename AnnType>
class PeriodicSave
{
public:
/**
* Constructor for PeriodicSave class.
*
* @param network Network type which will be saved periodically.
* @param filePath Base path / folder where weights will be saved.
* @param modelPrefix Weights will be stored as
* modelPrefix_epoch_loss.bin.
* @param period Period after which the model will be saved.
* @param silent Boolean to determine whether or not to print saving
* of model.
* @param output Outputstream where output will be directed.
*/
PeriodicSave(AnnType& network,
const std::string filePath = "./",
const std::string modelPrefix = "model",
const size_t period = 1,
const bool silent = false,
std::ostream& output = arma::get_cout_stream()) :
network(network),
filePath(filePath),
modelPrefix(modelPrefix),
period(period),
silent(silent),
output(output)
{
// Nothing to do here.
}

template<typename OptimizerType, typename FunctionType, typename MatType>
bool EndEpoch(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t epoch,
const double objective)
{
if (epoch % period == 0)
{
std::string objectiveString = std::to_string(objective);
std::replace(objectiveString.begin(), objectiveString.end(), '.', '_');
std::string modelName = modelPrefix + "_" + std::to_string(epoch) + "_" +
objectiveString;
mlpack::data::Save(filePath + modelName + ".bin", modelPrefix, network);
if (!silent)
output << "Model saved as " << modelName << std::endl;
}

return false;
}

private:
// Reference to the model which will be used for evaluated using the metric.
AnnType& network;

// Locally held string that depicts path for saving the model.
std::string filePath;

// Locally held string that depicts the prefix name of model being trained.
std::string modelPrefix;

// Period to save the model.
size_t period;

// Locally held boolean to determine whether to print success / failure output
// when model is saved.
bool silent;

// The output stream that all data is to be sent to; example: std::cout.
std::ostream& output;
};

} // namespace ens

#endif
107 changes: 107 additions & 0 deletions ensmallen_utils/print_metric.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* @file utils.hpp
* @author Kartik Dutt
*
* Definition of PrintMetric class.
*
* mlpack is free software; you may redistribute it and/or modify it under the
* terms of the 3-clause BSD license. You should have received a copy of the
* 3-clause BSD license along with mlpack. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/

#ifndef ENSMALLEN_CALLBACKS_PRINT_METRIC_HPP
#define ENSMALLEN_CALLBACKS_PRINT_METRIC_HPP

#include <ensmallen.hpp>
#include <functional>

namespace ens {

/**
* Prints metric on training / validation set.
*
* @tparam ANNType Type of model which will be used for evaluating metric.
* @tparam MetricType Metric class which must have static `Evaluate` function
* that will be called at the end of the epoch.
* @tparam InputType Arma type of dataset features.
* @tparam OutputType Arma type of dataset labels.
*/
template<typename AnnType,
class MetricType,
typename InputType = arma::mat,
typename OutputType = arma::mat
>
class PrintMetric
{
public:
/**
* Constructor for PrintMetric class.
* @param network Network type which will be saved periodically.
* @param features Input features on which model will be evaluated.
* @param responses Ground truth label for the mdoel.
* @param metricName Metric name which will be printed after each epoch.
* @param trainData Boolean to determine whether dataset corresponds to
* training data or validation data.
* @param output Outputstream where output will be directed.
*/
PrintMetric(AnnType &network,
const InputType &features,
const OutputType &responses,
const std::string metricName = "metric",
const bool trainData = false,
std::ostream &output = arma::get_cout_stream()) :
network(network),
features(features),
responses(responses),
metricName(metricName),
trainData(trainData),
output(output)
{
// Nothing to do here.
}

template<typename OptimizerType, typename FunctionType, typename MatType>
bool EndEpoch(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* epoch */,
const double /* objective */)
{
OutputType predictions;
network.Predict(features, predictions);
const double localObjective = MetricType::Evaluate(predictions, responses);
if (!std::isnan(localObjective))
{
std::string outputString = (trainData == true) ? "Train " : "Validation ";
outputString = outputString + metricName + " : " +
std::to_string(localObjective);
output << outputString << std::endl;
}
return false;
}

private:
// Reference to the model which will be used for evaluated using the metric.
AnnType& network;

// Dataset which will be used for evaluating the metric.
InputType features;

// Dataset labels / predictions that will be used for evaluating the dataset.
OutputType responses;

// Locally held string that depicts the name of the metric.
std::string metricName;

// Locally held boolean to determin whether evaluation is done on train data
// or validation data.
bool trainData;

// The output stream that all data is to be sent to; example: std::cout.
std::ostream& output;
};

} // namespace ens

#endif
16 changes: 16 additions & 0 deletions models/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
project(models)

add_subdirectory(darknet)

# Add directory name to sources.
set(DIR_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/)

foreach(file ${SOURCES})
set(DIR_SRCS ${DIR_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/${file})
endforeach()

set(DIR_SRCS ${DIR_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/layer.hpp)
# Append sources (with directory name) to list of all models sources (used at
# the parent scope).
set(DIRS ${DIRS} ${DIR_SRCS} PARENT_SCOPE)
16 changes: 16 additions & 0 deletions models/darknet/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
project(darknet)

set(DIR_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/)
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../")

set(SOURCES
darknet.hpp
darknet_impl.hpp
)

foreach(file ${SOURCES})
set(DIR_SRCS ${DIR_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/${file})
endforeach()

set(DIRS ${DIRS} ${DIR_SRCS} PARENT_SCOPE)
Loading

0 comments on commit 5869a40

Please sign in to comment.