#ifdef HAVE_CONFIG_H
# include "config_auto.h"
#endif
#include "lstm.h"
#ifdef _OPENMP
# include <omp.h>
#endif
#include <cstdio>
#include <cstdlib>
#include <sstream>
#if defined(_MSC_VER) && !defined(__clang__)
# include <intrin.h>
#endif
#include "fullyconnected.h"
#include "functions.h"
#include "networkscratch.h"
#include "tprintf.h"
#ifdef _OPENMP
# define PARALLEL_IF_OPENMP(__num_threads) \
PRAGMA(omp parallel if (__num_threads > 1) num_threads(__num_threads)) { \
PRAGMA(omp sections nowait) { \
PRAGMA(omp section) {
# define SECTION_IF_OPENMP \
} \
PRAGMA(omp section) {
# define END_PARALLEL_IF_OPENMP \
} \
} \
}
# ifdef _MSC_VER
# define PRAGMA(x) __pragma(x)
# else
# define PRAGMA(x) _Pragma(# x)
# endif
#else
# define PARALLEL_IF_OPENMP(__num_threads)
# define SECTION_IF_OPENMP
# define END_PARALLEL_IF_OPENMP
#endif
namespace tesseract {
const TFloat kStateClip = 100.0;
const TFloat kErrClip = 1.0f;
static inline uint32_t ceil_log2(uint32_t n) {
#if defined(__GNUC__)
uint32_t l2 = 31 - __builtin_clz(n);
#elif defined(_MSC_VER)
unsigned long l2 = 0;
_BitScanReverse(&l2, n);
#else
if (n == 0)
return UINT_MAX;
if (n == 1)
return 0;
uint32_t val = n;
uint32_t l2 = 0;
while (val > 1) {
val >>= 1;
l2++;
}
#endif
return (n == (1u << l2)) ? l2 : l2 + 1;
}
LSTM::LSTM(const std::string &name, int ni, int ns, int no, bool two_dimensional, NetworkType type)
: Network(type, name, ni, no)
, na_(ni + ns)
, ns_(ns)
, nf_(0)
, is_2d_(two_dimensional)
, softmax_(nullptr)
, input_width_(0) {
if (two_dimensional) {
na_ += ns_;
}
if (type_ == NT_LSTM || type_ == NT_LSTM_SUMMARY) {
nf_ = 0;
ASSERT_HOST(no == ns);
} else if (type_ == NT_LSTM_SOFTMAX || type_ == NT_LSTM_SOFTMAX_ENCODED) {
nf_ = type_ == NT_LSTM_SOFTMAX ? no_ : ceil_log2(no_);
softmax_ = new FullyConnected("LSTM Softmax", ns_, no_, NT_SOFTMAX);
} else {
tprintf("%d is invalid type of LSTM!\n", type);
ASSERT_HOST(false);
}
na_ += nf_;
}
LSTM::~LSTM() {
delete softmax_;
}
StaticShape LSTM::OutputShape(const StaticShape &input_shape) const {
StaticShape result = input_shape;
result.set_depth(no_);
if (type_ == NT_LSTM_SUMMARY) {
result.set_width(1);
}
if (softmax_ != nullptr) {
return softmax_->OutputShape(result);
}
return result;
}
void LSTM::SetEnableTraining(TrainingState state) {
if (state == TS_RE_ENABLE) {
if (training_ == TS_TEMP_DISABLE) {
training_ = TS_ENABLED;
}
} else if (state == TS_TEMP_DISABLE) {
if (training_ == TS_ENABLED) {
training_ = state;
}
} else {
if (state == TS_ENABLED && training_ != TS_ENABLED) {
for (int w = 0; w < WT_COUNT; ++w) {
if (w == GFS && !Is2D()) {
continue;
}
gate_weights_[w].InitBackward();
}
}
training_ = state;
}
if (softmax_ != nullptr) {
softmax_->SetEnableTraining(state);
}
}
int LSTM::InitWeights(float range, TRand *randomizer) {
Network::SetRandomizer(randomizer);
num_weights_ = 0;
for (int w = 0; w < WT_COUNT; ++w) {
if (w == GFS && !Is2D()) {
continue;
}
num_weights_ +=
gate_weights_[w].InitWeightsFloat(ns_, na_ + 1, TestFlag(NF_ADAM), range, randomizer);
}
if (softmax_ != nullptr) {
num_weights_ += softmax_->InitWeights(range, randomizer);
}
return num_weights_;
}
int LSTM::RemapOutputs(int old_no, const std::vector<int> &code_map) {
if (softmax_ != nullptr) {
num_weights_ -= softmax_->num_weights();
num_weights_ += softmax_->RemapOutputs(old_no, code_map);
}
return num_weights_;
}
void LSTM::ConvertToInt() {
for (int w = 0; w < WT_COUNT; ++w) {
if (w == GFS && !Is2D()) {
continue;
}
gate_weights_[w].ConvertToInt();
}
if (softmax_ != nullptr) {
softmax_->ConvertToInt();
}
}
void LSTM::DebugWeights() {
for (int w = 0; w < WT_COUNT; ++w) {
if (w == GFS && !Is2D()) {
continue;
}
std::ostringstream msg;
msg << name_ << " Gate weights " << w;
gate_weights_[w].Debug2D(msg.str().c_str());
}
if (softmax_ != nullptr) {
softmax_->DebugWeights();
}
}
bool LSTM::Serialize(TFile *fp) const {
if (!Network::Serialize(fp)) {
return false;
}
if (!fp->Serialize(&na_)) {
return false;
}
for (int w = 0; w < WT_COUNT; ++w) {
if (w == GFS && !Is2D()) {
continue;
}
if (!gate_weights_[w].Serialize(IsTraining(), fp)) {
return false;
}
}
if (softmax_ != nullptr && !softmax_->Serialize(fp)) {
return false;
}
return true;
}
bool LSTM::DeSerialize(TFile *fp) {
if (!fp->DeSerialize(&na_)) {
return false;
}
if (type_ == NT_LSTM_SOFTMAX) {
nf_ = no_;
} else if (type_ == NT_LSTM_SOFTMAX_ENCODED) {
nf_ = ceil_log2(no_);
} else {
nf_ = 0;
}
is_2d_ = false;
for (int w = 0; w < WT_COUNT; ++w) {
if (w == GFS && !Is2D()) {
continue;
}
if (!gate_weights_[w].DeSerialize(IsTraining(), fp)) {
return false;
}
if (w == CI) {
ns_ = gate_weights_[CI].NumOutputs();
is_2d_ = na_ - nf_ == ni_ + 2 * ns_;
}
}
delete softmax_;
if (type_ == NT_LSTM_SOFTMAX || type_ == NT_LSTM_SOFTMAX_ENCODED) {
softmax_ = static_cast<FullyConnected *>(Network::CreateFromFile(fp));
if (softmax_ == nullptr) {
return false;
}
} else {
softmax_ = nullptr;
}
return true;
}
void LSTM::Forward(bool debug, const NetworkIO &input, const TransposedArray *input_transpose,
NetworkScratch *scratch, NetworkIO *output) {
input_map_ = input.stride_map();
input_width_ = input.Width();
if (softmax_ != nullptr) {
output->ResizeFloat(input, no_);
} else if (type_ == NT_LSTM_SUMMARY) {
output->ResizeXTo1(input, no_);
} else {
output->Resize(input, no_);
}
ResizeForward(input);
NetworkScratch::FloatVec temp_lines[WT_COUNT];
int ro = ns_;
if (source_.int_mode() && IntSimdMatrix::intSimdMatrix) {
ro = IntSimdMatrix::intSimdMatrix->RoundOutputs(ro);
}
for (auto &temp_line : temp_lines) {
temp_line.Init(ns_, ro, scratch);
}
NetworkScratch::FloatVec curr_state, curr_output;
curr_state.Init(ns_, scratch);
ZeroVector<TFloat>(ns_, curr_state);
curr_output.Init(ns_, scratch);
ZeroVector<TFloat>(ns_, curr_output);
int buf_width = Is2D() ? input_map_.Size(FD_WIDTH) : 1;
std::vector<NetworkScratch::FloatVec> states, outputs;
if (Is2D()) {
states.resize(buf_width);
outputs.resize(buf_width);
for (int i = 0; i < buf_width; ++i) {
states[i].Init(ns_, scratch);
ZeroVector<TFloat>(ns_, states[i]);
outputs[i].Init(ns_, scratch);
ZeroVector<TFloat>(ns_, outputs[i]);
}
}
NetworkScratch::FloatVec softmax_output;
NetworkScratch::IO int_output;
if (softmax_ != nullptr) {
softmax_output.Init(no_, scratch);
ZeroVector<TFloat>(no_, softmax_output);
int rounded_softmax_inputs = gate_weights_[CI].RoundInputs(ns_);
if (input.int_mode()) {
int_output.Resize2d(true, 1, rounded_softmax_inputs, scratch);
}
softmax_->SetupForward(input, nullptr);
}
NetworkScratch::FloatVec curr_input;
curr_input.Init(na_, scratch);
StrideMap::Index src_index(input_map_);
StrideMap::Index dest_index(output->stride_map());
do {
int t = src_index.t();
bool valid_2d = Is2D();
if (valid_2d) {
StrideMap::Index dim_index(src_index);
if (!dim_index.AddOffset(-1, FD_HEIGHT)) {
valid_2d = false;
}
}
int mod_t = Modulo(t, buf_width);
source_.CopyTimeStepGeneral(t, 0, ni_, input, t, 0);
if (softmax_ != nullptr) {
source_.WriteTimeStepPart(t, ni_, nf_, softmax_output);
}
source_.WriteTimeStepPart(t, ni_ + nf_, ns_, curr_output);
if (Is2D()) {
source_.WriteTimeStepPart(t, ni_ + nf_ + ns_, ns_, outputs[mod_t]);
}
if (!source_.int_mode()) {
source_.ReadTimeStep(t, curr_input);
}
PARALLEL_IF_OPENMP(GFS)
if (source_.int_mode()) {
gate_weights_[CI].MatrixDotVector(source_.i(t), temp_lines[CI]);
} else {
gate_weights_[CI].MatrixDotVector(curr_input, temp_lines[CI]);
}
FuncInplace<GFunc>(ns_, temp_lines[CI]);
SECTION_IF_OPENMP
if (source_.int_mode()) {
gate_weights_[GI].MatrixDotVector(source_.i(t), temp_lines[GI]);
} else {
gate_weights_[GI].MatrixDotVector(curr_input, temp_lines[GI]);
}
FuncInplace<FFunc>(ns_, temp_lines[GI]);
SECTION_IF_OPENMP
if (source_.int_mode()) {
gate_weights_[GF1].MatrixDotVector(source_.i(t), temp_lines[GF1]);
} else {
gate_weights_[GF1].MatrixDotVector(curr_input, temp_lines[GF1]);
}
FuncInplace<FFunc>(ns_, temp_lines[GF1]);
if (Is2D()) {
if (source_.int_mode()) {
gate_weights_[GFS].MatrixDotVector(source_.i(t), temp_lines[GFS]);
} else {
gate_weights_[GFS].MatrixDotVector(curr_input, temp_lines[GFS]);
}
FuncInplace<FFunc>(ns_, temp_lines[GFS]);
}
SECTION_IF_OPENMP
if (source_.int_mode()) {
gate_weights_[GO].MatrixDotVector(source_.i(t), temp_lines[GO]);
} else {
gate_weights_[GO].MatrixDotVector(curr_input, temp_lines[GO]);
}
FuncInplace<FFunc>(ns_, temp_lines[GO]);
END_PARALLEL_IF_OPENMP
MultiplyVectorsInPlace(ns_, temp_lines[GF1], curr_state);
if (Is2D()) {
int8_t *which_fg_col = which_fg_[t];
memset(which_fg_col, 1, ns_ * sizeof(which_fg_col[0]));
if (valid_2d) {
const TFloat *stepped_state = states[mod_t];
for (int i = 0; i < ns_; ++i) {
if (temp_lines[GF1][i] < temp_lines[GFS][i]) {
curr_state[i] = temp_lines[GFS][i] * stepped_state[i];
which_fg_col[i] = 2;
}
}
}
}
MultiplyAccumulate(ns_, temp_lines[CI], temp_lines[GI], curr_state);
ClipVector<TFloat>(ns_, -kStateClip, kStateClip, curr_state);
if (IsTraining()) {
node_values_[CI].WriteTimeStep(t, temp_lines[CI]);
node_values_[GI].WriteTimeStep(t, temp_lines[GI]);
node_values_[GF1].WriteTimeStep(t, temp_lines[GF1]);
node_values_[GO].WriteTimeStep(t, temp_lines[GO]);
if (Is2D()) {
node_values_[GFS].WriteTimeStep(t, temp_lines[GFS]);
}
}
FuncMultiply<HFunc>(curr_state, temp_lines[GO], ns_, curr_output);
if (IsTraining()) {
state_.WriteTimeStep(t, curr_state);
}
if (softmax_ != nullptr) {
if (input.int_mode()) {
int_output->WriteTimeStepPart(0, 0, ns_, curr_output);
softmax_->ForwardTimeStep(int_output->i(0), t, softmax_output);
} else {
softmax_->ForwardTimeStep(curr_output, t, softmax_output);
}
output->WriteTimeStep(t, softmax_output);
if (type_ == NT_LSTM_SOFTMAX_ENCODED) {
CodeInBinary(no_, nf_, softmax_output);
}
} else if (type_ == NT_LSTM_SUMMARY) {
if (src_index.IsLast(FD_WIDTH)) {
output->WriteTimeStep(dest_index.t(), curr_output);
dest_index.Increment();
}
} else {
output->WriteTimeStep(t, curr_output);
}
if (Is2D()) {
CopyVector(ns_, curr_state, states[mod_t]);
CopyVector(ns_, curr_output, outputs[mod_t]);
}
if (src_index.IsLast(FD_WIDTH)) {
ZeroVector<TFloat>(ns_, curr_state);
ZeroVector<TFloat>(ns_, curr_output);
}
} while (src_index.Increment());
#if DEBUG_DETAIL > 0
tprintf("Source:%s\n", name_.c_str());
source_.Print(10);
tprintf("State:%s\n", name_.c_str());
state_.Print(10);
tprintf("Output:%s\n", name_.c_str());
output->Print(10);
#endif
#ifndef GRAPHICS_DISABLED
if (debug) {
DisplayForward(*output);
}
#endif
}
bool LSTM::Backward(bool debug, const NetworkIO &fwd_deltas, NetworkScratch *scratch,
NetworkIO *back_deltas) {
#ifndef GRAPHICS_DISABLED
if (debug) {
DisplayBackward(fwd_deltas);
}
#endif
back_deltas->ResizeToMap(fwd_deltas.int_mode(), input_map_, ni_);
NetworkScratch::FloatVec outputerr;
outputerr.Init(ns_, scratch);
NetworkScratch::FloatVec curr_stateerr, curr_sourceerr;
curr_stateerr.Init(ns_, scratch);
curr_sourceerr.Init(na_, scratch);
ZeroVector<TFloat>(ns_, curr_stateerr);
ZeroVector<TFloat>(na_, curr_sourceerr);
NetworkScratch::FloatVec gate_errors[WT_COUNT];
for (auto &gate_error : gate_errors) {
gate_error.Init(ns_, scratch);
}
int buf_width = Is2D() ? input_map_.Size(FD_WIDTH) : 1;
std::vector<NetworkScratch::FloatVec> stateerr, sourceerr;
if (Is2D()) {
stateerr.resize(buf_width);
sourceerr.resize(buf_width);
for (int t = 0; t < buf_width; ++t) {
stateerr[t].Init(ns_, scratch);
sourceerr[t].Init(na_, scratch);
ZeroVector<TFloat>(ns_, stateerr[t]);
ZeroVector<TFloat>(na_, sourceerr[t]);
}
}
NetworkScratch::FloatVec sourceerr_temps[WT_COUNT];
for (auto &sourceerr_temp : sourceerr_temps) {
sourceerr_temp.Init(na_, scratch);
}
int width = input_width_;
NetworkScratch::GradientStore gate_errors_t[WT_COUNT];
for (auto &w : gate_errors_t) {
w.Init(ns_, width, scratch);
}
NetworkScratch::FloatVec softmax_errors;
NetworkScratch::GradientStore softmax_errors_t;
if (softmax_ != nullptr) {
softmax_errors.Init(no_, scratch);
softmax_errors_t.Init(no_, width, scratch);
}
TFloat state_clip = Is2D() ? 9.0 : 4.0;
#if DEBUG_DETAIL > 1
tprintf("fwd_deltas:%s\n", name_.c_str());
fwd_deltas.Print(10);
#endif
StrideMap::Index dest_index(input_map_);
dest_index.InitToLast();
StrideMap::Index src_index(fwd_deltas.stride_map());
src_index.InitToLast();
do {
int t = dest_index.t();
bool at_last_x = dest_index.IsLast(FD_WIDTH);
int up_pos = -1;
int down_pos = -1;
if (Is2D()) {
if (dest_index.index(FD_HEIGHT) > 0) {
StrideMap::Index up_index(dest_index);
if (up_index.AddOffset(-1, FD_HEIGHT)) {
up_pos = up_index.t();
}
}
if (!dest_index.IsLast(FD_HEIGHT)) {
StrideMap::Index down_index(dest_index);
if (down_index.AddOffset(1, FD_HEIGHT)) {
down_pos = down_index.t();
}
}
}
int mod_t = Modulo(t, buf_width);
if (at_last_x) {
ZeroVector<TFloat>(na_, curr_sourceerr);
ZeroVector<TFloat>(ns_, curr_stateerr);
}
if (type_ == NT_LSTM_SUMMARY) {
if (dest_index.IsLast(FD_WIDTH)) {
fwd_deltas.ReadTimeStep(src_index.t(), outputerr);
src_index.Decrement();
} else {
ZeroVector<TFloat>(ns_, outputerr);
}
} else if (softmax_ == nullptr) {
fwd_deltas.ReadTimeStep(t, outputerr);
} else {
softmax_->BackwardTimeStep(fwd_deltas, t, softmax_errors, softmax_errors_t.get(), outputerr);
}
if (!at_last_x) {
AccumulateVector(ns_, curr_sourceerr + ni_ + nf_, outputerr);
}
if (down_pos >= 0) {
AccumulateVector(ns_, sourceerr[mod_t] + ni_ + nf_ + ns_, outputerr);
}
if (!at_last_x) {
const float *next_node_gf1 = node_values_[GF1].f(t + 1);
for (int i = 0; i < ns_; ++i) {
curr_stateerr[i] *= next_node_gf1[i];
}
}
if (Is2D() && t + 1 < width) {
for (int i = 0; i < ns_; ++i) {
if (which_fg_[t + 1][i] != 1) {
curr_stateerr[i] = 0.0;
}
}
if (down_pos >= 0) {
const float *right_node_gfs = node_values_[GFS].f(down_pos);
const TFloat *right_stateerr = stateerr[mod_t];
for (int i = 0; i < ns_; ++i) {
if (which_fg_[down_pos][i] == 2) {
curr_stateerr[i] += right_stateerr[i] * right_node_gfs[i];
}
}
}
}
state_.FuncMultiply3Add<HPrime>(node_values_[GO], t, outputerr, curr_stateerr);
ClipVector<TFloat>(ns_, -state_clip, state_clip, curr_stateerr);
#if DEBUG_DETAIL > 1
if (t + 10 > width) {
tprintf("t=%d, stateerr=", t);
for (int i = 0; i < ns_; ++i)
tprintf(" %g,%g,%g", curr_stateerr[i], outputerr[i], curr_sourceerr[ni_ + nf_ + i]);
tprintf("\n");
}
#endif
PARALLEL_IF_OPENMP(GFS)
node_values_[CI].FuncMultiply3<GPrime>(t, node_values_[GI], t, curr_stateerr, gate_errors[CI]);
ClipVector(ns_, -kErrClip, kErrClip, gate_errors[CI].get());
gate_weights_[CI].VectorDotMatrix(gate_errors[CI], sourceerr_temps[CI]);
gate_errors_t[CI].get()->WriteStrided(t, gate_errors[CI]);
SECTION_IF_OPENMP
node_values_[GI].FuncMultiply3<FPrime>(t, node_values_[CI], t, curr_stateerr, gate_errors[GI]);
ClipVector(ns_, -kErrClip, kErrClip, gate_errors[GI].get());
gate_weights_[GI].VectorDotMatrix(gate_errors[GI], sourceerr_temps[GI]);
gate_errors_t[GI].get()->WriteStrided(t, gate_errors[GI]);
SECTION_IF_OPENMP
if (t > 0) {
node_values_[GF1].FuncMultiply3<FPrime>(t, state_, t - 1, curr_stateerr, gate_errors[GF1]);
ClipVector(ns_, -kErrClip, kErrClip, gate_errors[GF1].get());
gate_weights_[GF1].VectorDotMatrix(gate_errors[GF1], sourceerr_temps[GF1]);
} else {
memset(gate_errors[GF1], 0, ns_ * sizeof(gate_errors[GF1][0]));
memset(sourceerr_temps[GF1], 0, na_ * sizeof(*sourceerr_temps[GF1]));
}
gate_errors_t[GF1].get()->WriteStrided(t, gate_errors[GF1]);
if (up_pos >= 0) {
node_values_[GFS].FuncMultiply3<FPrime>(t, state_, up_pos, curr_stateerr, gate_errors[GFS]);
ClipVector(ns_, -kErrClip, kErrClip, gate_errors[GFS].get());
gate_weights_[GFS].VectorDotMatrix(gate_errors[GFS], sourceerr_temps[GFS]);
} else {
memset(gate_errors[GFS], 0, ns_ * sizeof(gate_errors[GFS][0]));
memset(sourceerr_temps[GFS], 0, na_ * sizeof(*sourceerr_temps[GFS]));
}
if (Is2D()) {
gate_errors_t[GFS].get()->WriteStrided(t, gate_errors[GFS]);
}
SECTION_IF_OPENMP
state_.Func2Multiply3<HFunc, FPrime>(node_values_[GO], t, outputerr, gate_errors[GO]);
ClipVector(ns_, -kErrClip, kErrClip, gate_errors[GO].get());
gate_weights_[GO].VectorDotMatrix(gate_errors[GO], sourceerr_temps[GO]);
gate_errors_t[GO].get()->WriteStrided(t, gate_errors[GO]);
END_PARALLEL_IF_OPENMP
SumVectors(na_, sourceerr_temps[CI], sourceerr_temps[GI], sourceerr_temps[GF1],
sourceerr_temps[GO], sourceerr_temps[GFS], curr_sourceerr);
back_deltas->WriteTimeStep(t, curr_sourceerr);
if (Is2D()) {
CopyVector(ns_, curr_stateerr, stateerr[mod_t]);
CopyVector(na_, curr_sourceerr, sourceerr[mod_t]);
}
} while (dest_index.Decrement());
#if DEBUG_DETAIL > 2
for (int w = 0; w < WT_COUNT; ++w) {
tprintf("%s gate errors[%d]\n", name_.c_str(), w);
gate_errors_t[w].get()->PrintUnTransposed(10);
}
#endif
NetworkScratch::GradientStore source_t, state_t;
source_t.Init(na_, width, scratch);
source_.Transpose(source_t.get());
state_t.Init(ns_, width, scratch);
state_.Transpose(state_t.get());
#ifdef _OPENMP
# pragma omp parallel for num_threads(GFS) if (!Is2D())
#endif
for (int w = 0; w < WT_COUNT; ++w) {
if (w == GFS && !Is2D()) {
continue;
}
gate_weights_[w].SumOuterTransposed(*gate_errors_t[w], *source_t, false);
}
if (softmax_ != nullptr) {
softmax_->FinishBackward(*softmax_errors_t);
}
return needs_to_backprop_;
}
void LSTM::Update(float learning_rate, float momentum, float adam_beta, int num_samples) {
#if DEBUG_DETAIL > 3
PrintW();
#endif
for (int w = 0; w < WT_COUNT; ++w) {
if (w == GFS && !Is2D()) {
continue;
}
gate_weights_[w].Update(learning_rate, momentum, adam_beta, num_samples);
}
if (softmax_ != nullptr) {
softmax_->Update(learning_rate, momentum, adam_beta, num_samples);
}
#if DEBUG_DETAIL > 3
PrintDW();
#endif
}
void LSTM::CountAlternators(const Network &other, TFloat *same, TFloat *changed) const {
ASSERT_HOST(other.type() == type_);
const LSTM *lstm = static_cast<const LSTM *>(&other);
for (int w = 0; w < WT_COUNT; ++w) {
if (w == GFS && !Is2D()) {
continue;
}
gate_weights_[w].CountAlternators(lstm->gate_weights_[w], same, changed);
}
if (softmax_ != nullptr) {
softmax_->CountAlternators(*lstm->softmax_, same, changed);
}
}
#if DEBUG_DETAIL > 3
void LSTM::PrintW() {
tprintf("Weight state:%s\n", name_.c_str());
for (int w = 0; w < WT_COUNT; ++w) {
if (w == GFS && !Is2D()) {
continue;
}
tprintf("Gate %d, inputs\n", w);
for (int i = 0; i < ni_; ++i) {
tprintf("Row %d:", i);
for (int s = 0; s < ns_; ++s) {
tprintf(" %g", gate_weights_[w].GetWeights(s)[i]);
}
tprintf("\n");
}
tprintf("Gate %d, outputs\n", w);
for (int i = ni_; i < ni_ + ns_; ++i) {
tprintf("Row %d:", i - ni_);
for (int s = 0; s < ns_; ++s) {
tprintf(" %g", gate_weights_[w].GetWeights(s)[i]);
}
tprintf("\n");
}
tprintf("Gate %d, bias\n", w);
for (int s = 0; s < ns_; ++s) {
tprintf(" %g", gate_weights_[w].GetWeights(s)[na_]);
}
tprintf("\n");
}
}
void LSTM::PrintDW() {
tprintf("Delta state:%s\n", name_.c_str());
for (int w = 0; w < WT_COUNT; ++w) {
if (w == GFS && !Is2D()) {
continue;
}
tprintf("Gate %d, inputs\n", w);
for (int i = 0; i < ni_; ++i) {
tprintf("Row %d:", i);
for (int s = 0; s < ns_; ++s) {
tprintf(" %g", gate_weights_[w].GetDW(s, i));
}
tprintf("\n");
}
tprintf("Gate %d, outputs\n", w);
for (int i = ni_; i < ni_ + ns_; ++i) {
tprintf("Row %d:", i - ni_);
for (int s = 0; s < ns_; ++s) {
tprintf(" %g", gate_weights_[w].GetDW(s, i));
}
tprintf("\n");
}
tprintf("Gate %d, bias\n", w);
for (int s = 0; s < ns_; ++s) {
tprintf(" %g", gate_weights_[w].GetDW(s, na_));
}
tprintf("\n");
}
}
#endif
void LSTM::ResizeForward(const NetworkIO &input) {
int rounded_inputs = gate_weights_[CI].RoundInputs(na_);
source_.Resize(input, rounded_inputs);
which_fg_.ResizeNoInit(input.Width(), ns_);
if (IsTraining()) {
state_.ResizeFloat(input, ns_);
for (int w = 0; w < WT_COUNT; ++w) {
if (w == GFS && !Is2D()) {
continue;
}
node_values_[w].ResizeFloat(input, ns_);
}
}
}
}