auto lnode = std::static_pointer_cast<nntrainer::LayerNode>(l);
EXPECT_THROW(lnode->setProperty({"invalid_values"}), std::invalid_argument);
- EXPECT_EQ(lnode->getOutputDimensions().size(), size_t(0));
- EXPECT_EQ(lnode->getInputDimensions().size(), size_t(0));
+ EXPECT_THROW(lnode->getOutputDimensions(), std::runtime_error);
+ EXPECT_THROW(lnode->getInputDimensions(), std::runtime_error);
}
TEST_P(LayerPluginCommonTest, DefaultEnvironmentPathLayerNotExist_n) {
/** Dimension of input layers must be known */
for (auto iter = cbegin(); iter != cend(); iter++) {
auto lnode = (*iter);
- if (lnode->getType() == InputLayer::type) {
- if (lnode->getInputDimensions().size() == 0) {
- ml_loge("InputDimension of first layer is not set");
+ if (lnode->getNumInputConnections() == 0) {
+ if (!lnode->hasInputShapeProperty()) {
+ ml_loge("Layer with no inbound connection need input_shape property");
return ML_ERROR_INVALID_PARAMETER;
}
}
/** If a layer does not has input nodes, then it must have input dimension
*/
if (lnode->getNumInputConnections() == 0) {
- for (unsigned int i = 0; i < lnode->getInputDimensions().size(); ++i) {
- if (lnode->getInputDimensions()[i].getDataLen() == 0) {
- ml_loge("Input Dimension must be set");
- status = ML_ERROR_INVALID_PARAMETER;
- NN_RETURN_STATUS();
- }
+ if (!lnode->hasInputShapeProperty()) {
+ ml_loge("Input Dimension must be set");
+ status = ML_ERROR_INVALID_PARAMETER;
+ NN_RETURN_STATUS();
}
}
/**
* invariant: the new realized nodes are added to the end,
- * otherwise this iteration becomes invalid. So, every iteration must be fresh
- * iterator as vector resize invalidates all the iterators.
+ * otherwise this iteration becomes invalid. So, every iteration must be
+ * fresh iterator as vector resize invalidates all the iterators.
*/
for (unsigned int i = 0; i < graph.size(); ++i) {
auto const &lnode = LNODE(*(cbegin() + i));
/**
* The input_layers for ex_graph[0] here is provided to the backbone by the
- * ini file and is overwritten here by the model loader for connection making.
+ * ini file and is overwritten here by the model loader for connection
+ * making.
*
* This loop intends to connect a new backbone to be added with an old
* backbone.
}
std::vector<Var_Grad *>
-NetworkGraph::updateRunContext(std::shared_ptr<Manager> &tensor_manager,
- const std::shared_ptr<LayerNode> &lnode,
- const std::vector<Var_Grad *> &prev_inputs) {
- /**
- * using copy assignment allows setting run_context without adding more
- * interfaces
- */
+NetworkGraph::finalizeContext(const std::shared_ptr<LayerNode> &lnode,
+ const std::vector<Var_Grad *> &prev_inputs) {
const GraphNode &gnode = *lnode.get();
- const InitLayerContext &init_context = lnode->getInitContext();
+ std::vector<TensorDim> input_dims;
+ input_dims.reserve(prev_inputs.size());
+ std::transform(prev_inputs.begin(), prev_inputs.end(),
+ std::back_inserter(input_dims),
+ [](const Var_Grad *vg) { return vg->getDim(); });
+
+ auto init_context = lnode->finalize(input_dims);
+
std::vector<Var_Grad *> inputs = prev_inputs;
if (inputs.empty())
inputs =
const std::vector<Var_Grad *> &outputs =
tensor_manager->requestOutputs(gnode, init_context.getOutputDimensions());
- /**
- * @note must use existing properties like name/trainable of run_context to
- * create the new run_context
- */
- const RunLayerContext &run_context = lnode->getRunContext();
- lnode->updateRunContext(RunLayerContext(
- run_context.getName(), run_context.getLoss(),
+ lnode->configureRunContext(
// TODO: update weights spec for trainable based on layer trainable prop
tensor_manager->requestWeights(gnode, init_context.getWeightsSpec()),
inputs, outputs,
- tensor_manager->requestTensors(gnode, init_context.getTensorsSpec())));
+ tensor_manager->requestTensors(gnode, init_context.getTensorsSpec()));
return outputs;
}
return node->getInputConnections().empty();
};
+ std::vector<Var_Grad *> inputs;
for (unsigned int idx = 0; idx < graph.size(); ++idx) {
auto const &lnode = getSortedLayerNode(idx);
- std::string cur_type = lnode->getType();
ml_logd("layer name : %s", lnode->getName().c_str());
/**
* For input layer, as input dimension is known, set input tensor.
*/
if (!is_input_node(lnode)) {
- auto &input_layers = lnode->getInputLayers();
- lnode->resizeInputDimensions(input_layers.size());
- for (unsigned int i = 0; i < input_layers.size(); ++i) {
- auto in_layer_node = getLayerNode(input_layers[i]);
-
- auto const &in_layer_out_connect = in_layer_node->getOutputLayers();
- unsigned int location =
- std::find(in_layer_out_connect.begin(), in_layer_out_connect.end(),
- lnode->getName()) -
- in_layer_out_connect.begin();
-
-#ifdef DEBUG
- if (location == in_layer_out_connect.size())
- throw std::runtime_error("Invalid connection between nodes.");
-#endif
-
- lnode->setInputDimension(in_layer_node->getOutputDimensions()[location],
- i);
- }
+ if (input_map.find(lnode->getName()) == input_map.end())
+ throw std::runtime_error("Cannot find input buffers for the node");
+ inputs = input_map.at(lnode->getName());
}
/**
* Initialize all the layers, allocate output tensors for each layer
* init2and add optimizer related weights for the layer
*/
- lnode->finalize();
-
- std::vector<Var_Grad *> inputs = {};
- if (!is_input_node(lnode)) {
- if (input_map.find(lnode->getName()) == input_map.end())
- throw std::runtime_error("Cannot find input buffers for the node");
- inputs = input_map.at(lnode->getName());
- }
- const std::vector<Var_Grad *> &outputs =
- updateRunContext(tensor_manager, lnode, inputs);
+ const std::vector<Var_Grad *> &outputs = finalizeContext(lnode, inputs);
/** no need to update input_map for the last layer */
if (idx == graph.size() - 1)
/**
* @brief Create run layer context from the given init layer context
*
- * @param init_context Init layer context to create run context
- * @param run_context Run layer context to be created
+ * @param lnode layer node to finalize and set run context
+ * @param prev_inputs previous input information
*/
- static std::vector<Var_Grad *>
- updateRunContext(std::shared_ptr<Manager> &manager,
- const std::shared_ptr<LayerNode> &lnode,
- const std::vector<Var_Grad *> &inputs);
+ std::vector<Var_Grad *>
+ finalizeContext(const std::shared_ptr<LayerNode> &lnode,
+ const std::vector<Var_Grad *> &prev_inputs);
/** Interface for manager */
#include <weight.h>
namespace nntrainer {
+RunLayerContext::RunLayerContext(const std::string &name, float l,
+ const std::vector<Weight *> &w,
+ const std::vector<Var_Grad *> &in,
+ const std::vector<Var_Grad *> &out,
+ const std::vector<Var_Grad *> &t) :
+ loss(l),
+ weights(w),
+ inputs(in),
+ outputs(out),
+ tensors(t) {
+ std::get<props::Name>(props).set(name);
+ NNTR_THROW_IF(!readyToUse(), std::invalid_argument)
+ << "run context is not ready to use upon creation";
+}
/**
* @brief Get the Weight tensor object
*/
class InitLayerContext {
public:
- /**
- * @brief Construct a new Init Layer Context object
- *
- */
- InitLayerContext() : InitLayerContext({}, 1) {}
-
/**
* @brief Construct a new Init Layer Context object
*
* @param dim Input dimensions for the layer
*/
InitLayerContext(const std::vector<TensorDim> &dim, unsigned int num_out,
- const std::string &n = "") :
+ const std::string &n) :
input_dim(dim),
num_outputs(num_out),
- name(n) {}
+ name(n) {
+ NNTR_THROW_IF(!validate(), std::invalid_argument)
+ << "Invalid init context name: " << name
+ << " num inputs: " << getNumInputs();
+ }
/**
* @brief get name by the layer
}
}
- if (name.empty())
+ if (name.empty()) {
return false;
+ }
return true;
}
*/
class RunLayerContext {
public:
- /**
- * @brief Construct a new Run Layer Context object
- *
- */
- RunLayerContext() : loss(0.0) {}
-
- /**
- * @brief Construct a new Run Layer Context object
- *
- */
- RunLayerContext(const std::string &name) : RunLayerContext() {
- std::get<props::Name>(props).set(name);
- }
-
/**
* @brief Construct a new Run Layer Context object
* @todo Include properties like name/trainable later
const std::vector<Weight *> &w,
const std::vector<Var_Grad *> &in,
const std::vector<Var_Grad *> &out,
- const std::vector<Var_Grad *> &t) :
- loss(l),
- weights(w),
- inputs(in),
- outputs(out),
- tensors(t) {
- std::get<props::Name>(props).set(name);
- }
+ const std::vector<Var_Grad *> &t);
/**
* @brief Get the Weight tensor object
LayerNode::LayerNode(std::unique_ptr<nntrainer::Layer> &&l) :
layer(std::move(l)),
- finalized(false),
activation_type(ActivationType::ACT_NONE),
+ run_context(nullptr),
+ input_shapes(),
layer_node_props(new PropsType(props::Name(), props::Flatten(),
props::Distribute(), props::Trainable(), {})),
loss(new props::Loss()),
PropertyType type = static_cast<PropertyType>(parseLayerProperty(key));
switch (type) {
case PropertyType::input_shape: {
- std::vector<TensorDim> input_dim = init_context.getInputDimensions();
- if (getNumInputs() > 1) {
+ std::vector<TensorDim> input_dim = input_shapes;
+ if (input_shapes.size() > 1) {
throw std::invalid_argument("input_shape keyword is only for one input");
}
- if (getNumInputs() == 0)
+ if (input_shapes.empty())
input_dim.resize(1);
TensorDim &in_dim = input_dim[0];
/** set back to cache value of dimension */
in_dim.batch(cache_batch_size);
throw_status(status);
-
- init_context = InitLayerContext(input_dim, init_context.getNumOutputs());
+ input_shapes = std::move(input_dim);
}
} break;
case PropertyType::activation: {
input_layers = std::vector<props::InputLayer>(layers.begin(), layers.end());
}
+bool LayerNode::hasInputShapeProperty() const { return !input_shapes.empty(); }
+
+const std::vector<TensorDim> LayerNode::getInputDimensions() const {
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ auto sz = run_context->getNumInputs();
+ std::vector<TensorDim> dims;
+ dims.reserve(sz);
+
+ for (auto i = 0u; i < sz; ++i) {
+ dims.push_back(run_context->getInput(i).getDim());
+ }
+
+ return dims;
+}
+
+const std::vector<TensorDim> LayerNode::getOutputDimensions() const {
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ auto sz = run_context->getNumOutputs();
+ std::vector<TensorDim> dims;
+ dims.reserve(sz);
+
+ for (auto i = 0u; i < sz; ++i) {
+ dims.push_back(run_context->getOutput(i).getDim());
+ }
+
+ return dims;
+}
+
void LayerNode::exportTo(Exporter &exporter,
const ExportMethods &method) const {
exporter.saveResult(*layer_node_props, method, this);
}
void LayerNode::read(std::ifstream &file) {
- for (unsigned int i = 0; i < run_context.getNumWeights(); ++i) {
- run_context.getWeight(i).read(file);
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ for (unsigned int i = 0; i < run_context->getNumWeights(); ++i) {
+ run_context->getWeight(i).read(file);
}
}
void LayerNode::save(std::ofstream &file) const {
- for (unsigned int i = 0; i < run_context.getNumWeights(); ++i) {
- run_context.getWeight(i).save(file);
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ for (unsigned int i = 0; i < run_context->getNumWeights(); ++i) {
+ run_context->getWeight(i).save(file);
}
}
/**
* @brief Finalize creating the layer node
*/
-void LayerNode::finalize() {
+InitLayerContext LayerNode::finalize(const std::vector<TensorDim> &input_dims) {
+ std::vector<TensorDim> actual_input_dims;
+ auto &prop_dims = input_shapes;
+ if (!input_dims.empty()) {
+ actual_input_dims = input_dims;
+ if (!prop_dims.empty()) {
+ /// if prop_dims exist, check if it's same with given input_dims
+ NNTR_THROW_IF(input_dims != prop_dims, std::invalid_argument)
+ << "calculated input dimension is different from given input_shape "
+ "property";
+ }
+ } else {
+ actual_input_dims = prop_dims;
+ }
+
+ NNTR_THROW_IF(input_dims.size() < getNumInputConnections(),
+ std::invalid_argument)
+ << "number of input dimensions must be equal or larger "
+ << "than number of input connections, node name: " << getName()
+ << " num input dims: " << input_dims.size()
+ << " num connections: " << getNumInputConnections();
+
/** Create init context right before finalize */
- if (finalized)
+ if (run_context)
throw std::runtime_error("Finalizing a layer which is already finalized");
- init_context = InitLayerContext(init_context.getInputDimensions(),
- init_context.getNumOutputs(), getName());
- NNTR_THROW_IF(!init_context.validate(), std::invalid_argument)
- << "Invalid init context, name: " << getName()
- << " initContext num inputs: " << init_context.getNumInputs();
+ auto num_outputs = output_layers.size();
+ if (output_layers.empty()) {
+ num_outputs = 1;
+ }
+
+ auto init_context =
+ InitLayerContext(actual_input_dims, num_outputs, getName());
- if (layer)
- layer->finalize(init_context);
- finalized = true;
- run_context = RunLayerContext(getName());
+ layer->finalize(init_context);
+ return init_context;
}
/**
* @brief Forward Propagation of a layer
*/
void LayerNode::forwarding(bool training) {
- loss->set(run_context.getRegularizationLoss());
- layer->forwarding(run_context, training);
+ loss->set(run_context->getRegularizationLoss());
+ layer->forwarding(*run_context, training);
}
/**
* @brief calc the derivative to be passed to the previous layer
*/
-void LayerNode::calcDerivative() { layer->calcDerivative(run_context); }
+void LayerNode::calcDerivative() { layer->calcDerivative(*run_context); }
/**
* @brief Calculate the derivative of a layer
*/
-void LayerNode::calcGradient() { layer->calcGradient(run_context); }
+void LayerNode::calcGradient() { layer->calcGradient(*run_context); }
/**
* @brief Set the batch for the layer
*/
void LayerNode::setBatch(unsigned int batch) {
- run_context.setBatch(batch);
- init_context.setBatch(batch);
-
- if (finalized) {
- if (run_context.readyToUse()) {
- getLayer()->setBatch(run_context, batch);
- } else {
- /** run_context has not been created yet */
- getLayer()->setBatch(init_context, batch);
- }
+ /** @todo we won't going to need Layer::setBatch(InitLayerContext), remove it
+ */
+ for (auto &input_shape : input_shapes) {
+ input_shape.batch(batch);
+ }
+
+ if (run_context) {
+ run_context->setBatch(batch);
+ getLayer()->setBatch(*run_context, batch);
}
}
float LayerNode::getLoss() const {
/** add loss only for loss layers */
if (requireLabel())
- loss->set(*loss + run_context.getLoss());
+ loss->set(*loss + run_context->getLoss());
return *loss;
}
-void LayerNode::setInputDimension(const TensorDim &dim, unsigned int idx) {
- NNTR_THROW_IF(idx >= getNumInputs(), std::out_of_range)
- << "Setting dimensions out of bounds, idx: " << idx
- << " size: " << getNumInputs() << " name: " << getName();
-
- std::vector<TensorDim> input_dim = init_context.getInputDimensions();
- if (input_dim[idx] != dim) {
- input_dim[idx] = dim;
- init_context = InitLayerContext(input_dim, init_context.getNumOutputs());
- }
+void LayerNode::configureRunContext(const std::vector<Weight *> &weights,
+ const std::vector<Var_Grad *> &inputs,
+ const std::vector<Var_Grad *> &outputs,
+ const std::vector<Var_Grad *> &tensors) {
+ run_context = std::make_unique<RunLayerContext>(getName(), 0.0f, weights,
+ inputs, outputs, tensors);
}
/**
print(out, flags);
}
-void LayerNode::resizeInputDimensions(unsigned int size) {
- auto cur_input_dim = init_context.getInputDimensions();
- if (cur_input_dim.size() != size) {
- cur_input_dim.resize(size);
- init_context =
- InitLayerContext(cur_input_dim, init_context.getNumOutputs());
- }
-}
-
void LayerNode::printShapeInfo(std::ostream &out) {
- for (unsigned int idx = 0; idx < init_context.getNumInputs(); ++idx) {
- out << "input " << init_context.getInputDimensions()[idx];
+ for (unsigned int idx = 0; idx < getNumInputs(); ++idx) {
+ out << "input " << run_context->getInput(idx).getDim();
}
- for (unsigned int i = 0; i < init_context.getNumWeights(); i++) {
- out << "weight" << std::get<0>(init_context.getWeightsSpec()[i]);
+ for (unsigned int idx = 0; idx < getNumWeights(); idx++) {
+ out << "weight " << run_context->getWeight(idx).getDim();
}
- for (unsigned int idx = 0; idx < init_context.getNumOutputs(); ++idx) {
- out << "output " << init_context.getOutputDimensions()[idx];
+ for (unsigned int idx = 0; idx < getNumOutputs(); ++idx) {
+ out << "output " << run_context->getOutput(idx).getDim();
}
}
}
if (flags & PRINT_SHAPE_INFO) {
- if (init_context.validate()) {
+ if (run_context) {
out << "======shape information: " << std::endl;
printShapeInfo(out);
}
}
if (flags & PRINT_WEIGHTS) {
- if (init_context.validate()) {
+ if (run_context) {
out << "======weights: " << std::endl;
- for (unsigned int idx = 0; idx < init_context.getNumWeights(); idx++) {
- out << '[' << std::get<5>(init_context.getWeightsSpec()[idx]) << ']'
- << std::endl;
- if (run_context.readyToUse())
- out << run_context.getWeight(idx);
+ for (unsigned int idx = 0; idx < getNumWeights(); idx++) {
+ out << run_context->getWeight(idx);
}
}
}
*/
class LayerNode final : public ml::train::Layer, public GraphNode {
public:
- /**
- * @brief Default constructor
- */
- LayerNode() : LayerNode(nullptr) {}
-
/**
* @brief Constructor of LayerNode class for v2
* @param l layer to wrap with, the ownership is transferred to layer node
/**
* @brief Finalize creating the layer node
*
- * @details Input dimensions will be provided set in the context. This
- * function must set output dimensions in the given context. Further, context
- * can be used to request weights for the layer, and any extra tensor required
- * for the operation of the layer.
+ * @param input_dims input dimension provided to be used to set output
+ * dimensions. if empty function This function must set output dimensions in
+ * the given context. Further, context can be used to request weights for the
+ * layer, and any extra tensor required for the operation of the layer.
* @note After calling this it is not allowed to
* change properties.
* @note No memory allocation must be performed in the initialization
* step. Any tensor memory required must be requested to the context which
* will be made available during execution of the layer with the context.
*/
- void finalize();
+ InitLayerContext finalize(const std::vector<TensorDim> &input_dims = {});
/**
* @brief Forward Propagation of a layer
* @brief Get number of inputs
* @retval number of inputs
*/
- unsigned int getNumInputs() const { return init_context.getNumInputs(); }
+ unsigned int getNumInputs() const {
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ return run_context->getNumInputs();
+ }
/**
* @brief Get number of outputs
* @retval number of outputs
*/
- unsigned int getNumOutputs() const { return init_context.getNumOutputs(); }
+ unsigned int getNumOutputs() const {
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ return run_context->getNumOutputs();
+ }
/**
* @brief Get the number of weights
*
* @return unsigned int number of weights
*/
- unsigned int getNumWeights() const { return init_context.getNumWeights(); }
+ unsigned int getNumWeights() const {
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ return run_context->getNumWeights();
+ }
/**
* @brief Get the Input Layers object
*/
void addOutputLayers(const std::string &out_layer) {
output_layers.push_back(out_layer);
- init_context =
- InitLayerContext(init_context.getInputDimensions(), output_layers.size());
}
/**
*/
void setOutputLayers(const std::vector<std::string> &layers) {
output_layers = layers;
- init_context =
- InitLayerContext(init_context.getInputDimensions(),
- std::max((unsigned int)output_layers.size(), 1u));
}
+ /**
+ * @brief check if input shape property is set
+ *
+ * @return bool true if input shape property has set
+ */
+ bool hasInputShapeProperty() const;
+
/**
* @brief Get the input dimension
* @return TensorDim dimension of the input
*/
- const std::vector<TensorDim> getInputDimensions() const {
- return init_context.getInputDimensions();
- }
+ const std::vector<TensorDim> getInputDimensions() const;
/**
* @brief Get the output dimension
* @return TensorDim dimension of the output
*/
- const std::vector<TensorDim> getOutputDimensions() const {
- return init_context.getOutputDimensions();
- }
-
+ const std::vector<TensorDim> getOutputDimensions() const;
/**
* @brief Get the Weight object
*
* @return Weight& Reference to the weight
*/
Weight getWeightWrapper(unsigned int idx) {
- if (run_context.weightHasGradient(idx)) {
- return Weight(run_context.getWeight(idx), run_context.getWeightGrad(idx),
- run_context.getWeightName(idx));
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ if (run_context->weightHasGradient(idx)) {
+ return Weight(run_context->getWeight(idx),
+ run_context->getWeightGrad(idx),
+ run_context->getWeightName(idx));
} else {
- return Weight(run_context.getWeight(idx), Tensor(),
- run_context.getWeightName(idx));
+ return Weight(run_context->getWeight(idx), Tensor(),
+ run_context->getWeightName(idx));
}
}
* @return Tensor& Reference to the weight tensor
*/
Weight &getWeightObject(unsigned int idx) {
- return run_context.getWeightObject(idx);
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ return run_context->getWeightObject(idx);
}
/**
* @param idx Identifier of the weight
* @return Tensor& Reference to the weight tensor
*/
- Tensor &getWeight(unsigned int idx) { return run_context.getWeight(idx); }
+ Tensor &getWeight(unsigned int idx) {
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ return run_context->getWeight(idx);
+ }
/**
* @brief Get the Weight Gradient tensor object
* @return Tensor& Reference to the weight grad tensor
*/
Tensor &getWeightGrad(unsigned int idx) {
- return run_context.getWeightGrad(idx);
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ return run_context->getWeightGrad(idx);
}
/**
* @return const std::string &Name of the weight
*/
const std::string &getWeightName(unsigned int idx) {
- return run_context.getWeightName(idx);
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ return run_context->getWeightName(idx);
}
/**
* @param idx Identifier of the input
* @return Tensor& Reference to the input grad tensor
*/
- Tensor &getInput(unsigned int idx) { return run_context.getInput(idx); }
+ Tensor &getInput(unsigned int idx) {
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ return run_context->getInput(idx);
+ }
/**
* @brief Get the Input Grad tensor object
* @return Tensor& Reference to the input grad tensor
*/
Tensor &getInputGrad(unsigned int idx) {
- return run_context.getInputGrad(idx);
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ return run_context->getInputGrad(idx);
}
/**
* @param idx Identifier of the output
* @return Tensor& Reference to the output tensor
*/
- Tensor &getOutput(unsigned int idx) { return run_context.getOutput(idx); }
+ Tensor &getOutput(unsigned int idx) {
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ return run_context->getOutput(idx);
+ }
/**
* @brief Get the Output Grad tensor object
* @return Tensor& Reference to the output grad tensor
*/
Tensor &getOutputGrad(unsigned int idx) {
- return run_context.getOutputGrad(idx);
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ return run_context->getOutputGrad(idx);
}
/**
* @return Tensor& Reference to the output grad tensor
*/
Tensor &getOutputGradUnsafe(unsigned int idx) {
- return run_context.getOutputGradUnsafe(idx);
+ return run_context->getOutputGradUnsafe(idx);
}
/**
*/
friend std::ostream &operator<<(std::ostream &out, const LayerNode &l);
- /**
- * @brief Get init layer context
- *
- * @retval init layer context
- */
- const InitLayerContext &getInitContext() const { return init_context; }
-
/**
* @brief Get run layer context
*
* @retval run layer context
*/
- const RunLayerContext &getRunContext() const { return run_context; }
-
- /**
- * @brief Set run layer context
- *
- * @param context Updated run layer context
- */
- void updateRunContext(RunLayerContext &&context) {
- // TODO: ensure props/trainable must match
- run_context = std::move(context);
+ const RunLayerContext &getRunContext() const {
+ NNTR_THROW_IF(!run_context, std::runtime_error)
+ << __func__ << " layer needs to be finalized first!";
+ return *run_context;
}
/**
- * @brief Set input dimension for the layer
+ * @brief Set the Run Context object with given tensor packs
*
- * @param dim Input tensor dim
- * @param idx Index of the dim
+ * @param weights weights
+ * @param inputs inputs
+ * @param outputs outputs
+ * @param tensors tensors
*/
- void setInputDimension(const TensorDim &dim, unsigned int idx);
+ void configureRunContext(const std::vector<Weight *> &weights,
+ const std::vector<Var_Grad *> &inputs,
+ const std::vector<Var_Grad *> &outputs,
+ const std::vector<Var_Grad *> &tensors);
/**
* @brief Preset modes for printing summary for the layer
void printPreset(std::ostream &out,
PrintPreset preset = PrintPreset::PRINT_SUMMARY);
- /**
- * @brief Resize the input dimensions
- *
- * @param size Number of input dimensions
- */
- void resizeInputDimensions(unsigned int size);
-
private:
std::unique_ptr<nntrainer::Layer>
layer; /**< The actual object in the graph node */
- bool finalized; /**< if the layer node has been finalized */
-
std::vector<std::string> output_layers; /**< output layer names */
ActivationType
activation_type; /**< activation applied to the output of this node */
- InitLayerContext init_context; /**< context to be built for/while
- initialization of the layer. This will also
- contain the properties of the layer. */
-
- RunLayerContext run_context; /**< context required for running/execution of
- the layer. This will also contain the properties of the
- layer. The properties will be copied upon final creation.
- Editing properties of the layer after init will not the
- properties in the context/graph unless intended. */
+ std::unique_ptr<RunLayerContext>
+ run_context; /**< context required for running/execution of the layer. This
+will also contain the properties of the layer. The properties will be copied
+upon final creation. Editing properties of the layer after init will not the
+properties in the context/graph unless intended. */
using PropsType =
std::tuple<props::Name, props::Flatten, props::Distribute, props::Trainable,
std::vector<props::InputLayer>>;
+
+ std::vector<TensorDim>
+ input_shapes; /**< input shapes, @see LayerNode::finalize() to know how this
+ is interpreted */
/**
* These properties are set for the layer by the user but are intercepted
* and used in the node which forms the basic element of the graph.
*/
TensorDim dist_dim = input_dim;
dist_dim.height(1);
- InitLayerContext dist_context({dist_dim}, context.getNumOutputs());
+ InitLayerContext dist_context({dist_dim}, context.getNumOutputs(), getType());
// During forwarding and backwarding, it set the input and output buffer of
// dist_layer properly
void TimeDistLayer::setBatch(InitLayerContext &context, unsigned int batch) {
TensorDim input_dim = context.getInputDimensions()[SINGLE_INOUT_IDX];
input_dim.height(1);
- InitLayerContext dist_context({input_dim}, context.getNumOutputs());
+ InitLayerContext dist_context({input_dim}, context.getNumOutputs(),
+ getType());
TensorDim output_dim = context.getOutputDimensions()[0];
// input_dim.height is number of time iteration
EXPECT_NO_THROW(lnode->setProperty(valid_properties));
if (!must_fail) {
- EXPECT_NO_THROW(lnode->finalize());
-
- auto &init_context = lnode->getInitContext();
+ nntrainer::InitLayerContext init_context = lnode->finalize();
EXPECT_EQ(init_context.getOutputDimensions().size(),
init_context.getNumOutputs());
if (!must_fail) {
EXPECT_NO_THROW(lnode->finalize());
- auto &init_context = lnode->getInitContext();
- EXPECT_NO_THROW(
- lnode->setBatch(init_context.getInputDimensions()[0].batch() + 10));
} else {
EXPECT_THROW(lnode->finalize(), nntrainer::exception::not_supported);
}