// clayer.cpp // David R. Morrison #include "clayer.hpp" // Constructor; input_num is the number of non-recurrent inputs to the layer, // block_num is the number of cell blocks, and block_size is the number of // cells in each block. CLayer::CLayer(int input_num, int block_num, int block_size) { inputNum = input_num; blockNum = block_num; blockSize = block_size; outputVectorSize = blockSize * blockNum; // input_num + all recurrent connections in the layer inputVectorSize = input_num + blockNum * (blockSize + BLOCK_GATE_NUM); blocks.resize(blockNum); layerOutput.resize(outputVectorSize); // Create a new block with blockSize cells each with fully recurrent connections for (int i = 0; i < blockNum; ++i) blocks[i] = new Block(blockSize, inputVectorSize); } // Delete the blocks CLayer::~CLayer() { for (int i = 0; i < blockNum; ++i) delete blocks[i]; } // Return a vector containing the output from each cell in the layer vector CLayer::calcLayerOutput(vector inputs) { vector inputVector = getInputVector(inputs); // Calculating the output is a two-step process; first all the gates need to // be updated, then the internal state of each cell needs to be updated. for (int i = 0; i < blockNum; ++i) { blocks[i]->calcGates(inputVector); vector blockOutput = blocks[i]->calcOutput(inputVector); // then we store the calculated values in the layerOutput vector for (int j = 0; j < blockSize; ++j) layerOutput[i * blockNum + j] = blockOutput[j]; } return layerOutput; } // Calculate the weighted error signals based on what we were expecting (outputSignals). // Note that all signals are stored on the block level. vector CLayer::calcSignals(vector outputSignals, vector< vector > outputWeights) { vector signals; for (int i = 0; i < blockNum; ++i) { vector blockSignals = blocks[i]->calcSignals(outputSignals, outputWeights, i); for (int j = 0; j < blockSize; ++j) signals.push_back(blockSignals[j]); } return signals; } // Figure out the necessary weight updates based on learning rate eta // and calculated signals. void CLayer::updateWeights(double eta) { for (int i = 0; i < blockNum; ++i) blocks[i]->updateWeights(eta, layerInput); } void CLayer::clear() { for (int i = 0; i < blockNum; ++i) blocks[i]->clear(); for (int i = 0; i < outputVectorSize; ++i) layerOutput[i] = 0.0; } // Figure out the total input to the layer, including all recurrent connections. // The vector format is // // [ ] // vector CLayer::getInputVector(vector inputs) { layerInput.clear(); for (int i = 0; i < inputs.size(); ++i) layerInput.push_back(inputs[i]); for (int i = 0; i < outputVectorSize; ++i) layerInput.push_back(layerOutput[i]); for (int i = 0; i < blockNum; ++i) { layerInput.push_back(f(blocks[i]->getInputGateValue())); layerInput.push_back(f(blocks[i]->getForgetGateValue())); layerInput.push_back(f(blocks[i]->getOutputGateValue())); } return layerInput; } vector< vector > CLayer::getInputWeights() { vector< vector > inputWeights(inputNum); for (int i = 1; i < inputNum + 1; ++i) { inputWeights[i - 1].resize(blockNum * blockSize); for (int j = 0; j < blockNum; ++j) for (int k = 0; k < blockSize; ++k) inputWeights[i - 1][j * blockNum + k] = blocks[j]->getWeight(k, i); } return inputWeights; } int CLayer::getOutputSize() { return outputVectorSize; } ostream& CLayer::print(ostream& out) { out << "Layer has " << blockNum << " block(s). Each block has " << blockSize << " cell(s)." << endl; for (int i = 0; i < blockNum; ++i) out << *blocks[i] << endl; return out; } ostream& operator<<(ostream& out, CLayer& l) { return l.print(out); }