LSTM预测大写数字的c++ 代码

2023-11-18

自己写的LSTM预测大写数字的c++ 代码,有较详细的注释,有不懂的可以交流。
平台:vs2015
头文件: LstmCppH.h
#pragma once
#include “iostream”
#include “math.h”
#include “stdlib.h”
#include “time.h”
#include “vector”
#include “assert.h”
#include"DataPreProc.h"
#include"string"
using namespace std;
class LstmCppH
{
public:
LstmCppH();
~LstmCppH();
#define innode 1 //输入结点数,存储特征向量
#define hidenode 128 //隐藏结点数,
#define outnode 10 //输出结点数,每个时间点用一个向量代表结果
#define alpha 0.0005 //学习速率
#define timesteps 3 //time steps 时间点数量
#define epochs 25000
#define trainErrThreshold 2.5

#define random(x) rand()%(x)
#define randval(high) ( (double)rand() / RAND_MAX * high )
#define uniform_plus_minus_one ( (double)( 2.0 * rand() ) / ((double)RAND_MAX + 1.0) - 1.0 )  //均匀随机分布

#define CHINESE

double W_I[innode][hidenode];     //连接输入与隐含层单元中输入门的权值矩阵
double U_I[hidenode][hidenode];   //连接上一隐层输出与本隐含层单元中输入门的权值矩阵
double B_I[hidenode][1];   //连接输入与隐含层单元中输入门的偏置
double W_F[innode][hidenode];     //连接输入与隐含层单元中遗忘门的权值矩阵
double U_F[hidenode][hidenode];   //连接上一隐含层与本隐含层单元中遗忘门的权值矩阵
double B_F[hidenode][1];    //连接输入与隐含层单元中遗忘门的偏置
double W_O[innode][hidenode];     //连接输入与现在时刻的隐含层输出门的权值矩阵
double U_O[hidenode][hidenode];   //连接上一隐含层与现在时刻的隐含层的权值矩阵
double B_O[hidenode][1];    //连接输入与隐含层单元中输出门的偏置
double W_G[innode][hidenode];     //用于产生新记忆的权值矩阵
double U_G[hidenode][hidenode];   //用于产生新记忆的权值矩阵
double B_G[hidenode][1]; //用于产生新记忆的偏置
double W_out[hidenode][outnode];  //连接隐层与输出层的权值矩阵
double B_out[outnode][1];  //连接隐层与输出层的权值矩阵


double dsigmoid(double y);
double sigmoid(double x);
//tanh的导数,y为tanh值
double dtanh(double y);
void winit(double w[], int n); //权值初始化

void train();
vector<string> encode2Truth(map<string, int> &dsMap, vector<double *> &predictedM);
vector<string> encode2Sample(map<string, int> &dsMap, vector<int> &sample);
vector<int> sample2Encode(map<string, int> &dsMap, vector<string> sample);
void predict();

};
源代码文件:LstmCppH.cpp:

#include “stdafx.h”
#include “LstmCppH.h”

LstmCppH::LstmCppH()
{

winit((double*)W_I, hidenode * innode);
winit((double*)U_I, hidenode * hidenode);
winit((double*)B_I, hidenode * 1);
winit((double*)W_F, hidenode * innode);
winit((double*)U_F, hidenode * hidenode);
winit((double*)B_F, hidenode * 1);
winit((double*)W_O, hidenode * innode);
winit((double*)U_O, hidenode * hidenode);
winit((double*)B_O, hidenode * 1);
winit((double*)W_G, hidenode * innode);
winit((double*)U_G, hidenode * hidenode);
winit((double*)B_G, hidenode * 1);
winit((double*)W_out, hidenode * outnode);
winit((double*)B_out, outnode * 1);

}

LstmCppH::~LstmCppH()
{

}

double LstmCppH::dsigmoid(double y)
{
return y * (1.0 - y);
}

double LstmCppH::sigmoid(double x)
{
return 1.0 / (1.0 + exp(-x));
}

double LstmCppH::dtanh(double y)
{
y = tanh(y);
return 1.0 - y * y;
}

void LstmCppH::winit(double w[], int n)
{
for (int i = 0; i<n; i++)
{
w[i] = uniform_plus_minus_one; //均匀随机分布
/* cout << “w”<<i<<"=" << w[i] << endl;*/
}

}

void LstmCppH::train()
{
//char s[] = “十 四 五 规 划 和 二 零 三 五 年 远 景 目 标 纲 要 明 确 实 施 积 极 应 对 人 口 老 龄 化 国 家 战 略 制 定 人 口 长 期 发 展 战 略 优 化 生 育 政 策”;
//string ss = “十四五规划和二零三五年远景目标纲要明确实施积极应对人口老龄化国家战略制定人口长期发展战略优化生育政策”;
//char s[] = “锄 禾 日 当 午 汗 滴 和 下 土 谁 知 盘 中 餐 粒 粒 皆 辛 苦”;
//string ss = “锄禾日当午汗滴和下土谁知盘中餐粒粒皆辛苦”;

char s[] = "壹 贰 叁 肆 伍 陆 柒 捌 玖 拾";
string ss = "壹贰叁肆伍陆柒捌玖拾";
int epoch = 0, i, j, k, m, p;
//中间变量值
double *x = new double[innode];
vector<double*> I_vector;      //输入门
vector<double*> F_vector;      //遗忘门
vector<double*> O_vector;      //输出门
vector<double*> G_vector;      //新记忆
vector<double*> S_vector;      //状态值
vector<double*> h_vector;      //输出值
vector<double*> y_delta_vector;        //保存误差关于输出层的偏导
vector<double *>predictV;
DataPreProc dpp;
map<string, int> dsMap = dpp.loadDataset(s);
vector<int> curSample;//每个样本应该清零,存储当前样本中每个时间点的输入向量
vector<int> curTimestep_truth_10;//每个样本中每个timestep对应的真值,十进制;每训练一个样本应该清零
vector<double*>truth_steps;//每个样本所有timesteps的真值,应该清零

int offset = 0;

#ifdef CHINESE
int end_offset = (timesteps + 1) * 2;
#else
int end_offset = (timesteps + 1) ;
#endif // CHINESE
int randomMax = ss.size() - end_offset;//随机产生样本索引时,随机值的范围最大值
cout << “randomMax” << randomMax << endl;

double e = 10000000.0;  //误差

/*for (epoch = 0; epoch < epochs; epoch++) */ //训练次数
while(e > trainErrThreshold)
{
	//1、清零和初始化

	if ((offset + end_offset) > ss.size())
	{
		offset = 0;
	}
	e = 0.0;
	//在0时刻是没有之前的隐含层的,所以初始化一个全为0的
	double *S = new double[hidenode] {0};     //状态值
	double *h = new double[hidenode] {0};     //输出值
	for (size_t i = 0; i < hidenode; i++)
	{
		S[i] = 0.0;
		h[i] = 0.0;
	}
	S_vector.push_back(S);
	h_vector.push_back(h);

	//存储每个时间点计算出来的权重delta,并累加;一个样本训练完之后进行更新权重,这个先不考虑
	//获取样本
	curSample.clear();
	curTimestep_truth_10.clear();

#ifdef CHINESE
for (size_t i = 0; i < timesteps * 2; i = i + 2)//获取本样本的timesteps个时间点数据,因为是汉字占2个位置,因此2
{
curSample.push_back(dsMap[ss.substr(offset + i, 2)]);
curTimestep_truth_10.push_back(dsMap[ss.substr(offset + i + 2, 2)]);
//cout << curSample.back() << endl;
//cout << curTimestep_truth_10.back() << endl;
}
#else
for (size_t i = 0; i < timesteps ; i = i + 1)//获取本样本的timesteps个时间点数据,因为是汉字占2个位置,因此
2
{
curSample.push_back(dsMap[ss.substr(offset + i, 1)]);
curTimestep_truth_10.push_back(dsMap[ss.substr(offset + i + 1, 1)]);
//cout << curSample.back() << endl;
//cout << curTimestep_truth_10.back() << endl;
}
#endif // CHINESE
//对真值进行one_hot编码
truth_steps.clear();
for (size_t i = 0; i < timesteps; i++)
{
double LableD = new double[outnode] {0};//打印测试???
for (size_t j = 0; j < dsMap.size(); j++)
{
if ((j + 1) == curTimestep_truth_10[i])
{
LableD[j] = 1.0f;
/cout << “LableD:” << LableD[j] << endl;/
}
else
{
LableD[j] = 0.0f;
/
cout << “LableD:” << LableD[j] << endl;*/
}
}
/cout << “当前时间点真值:” << endl << curTimestep_truth_10[i] << endl;/
truth_steps.push_back(LableD);//矩阵必须要复制,否则每次都引用了相同的数据
/cout << “truth_steps=”<<i<<endl << truth_steps.back() << endl;/
}
//正向传播:一直计算完所有的time step
for (p = 0; p < timesteps; p++)//循环遍历二进制数组,从最低位开始,p相当于time steps
{
x[0] = (float)curSample[p] ;
/cout << “x[0]” << p << “=” << x[0] << endl;/
double *in_gate = new double[hidenode] {0}; //输入门取sigmoid函数后的值
double *out_gate = new double[hidenode] {0}; //输出门取sigmoid函数后的值
double *forget_gate = new double[hidenode] {0}; //遗忘门取sigmoid函数后的值
double *g_gate = new double[hidenode] {0}; //新记忆取tanh函数后的值
double *state = new double[hidenode] {0}; //状态值
double *h = new double[hidenode]{0}; //隐层输出值
double *output = new double[outnode] {0};//当前时间点的预测数组
double *y_pre = new double[outnode] {0};//当前时间点的预测数组
double * truthLabel = new double[outnode] {0};//当前时间点的真值数组
double * y_delta = new double[outnode] {0};//当前时间点的真值数组
for (size_t j = 0; j < hidenode; j++)
{
double inGateValue = 0;
double forgetGateValue = 0;
double outGateValue = 0;
double gGateValue = 0;
//当前时间点输入
for (size_t k = 0; k < innode; k++)
{
forgetGateValue += x[k] * W_F[k][j];

				inGateValue += x[k] * W_I[k][j];
				gGateValue += x[k]  * W_G[k][j];
				outGateValue += x[k] * W_O[k][j];
			}
			//前一个状态
			double * h_pre = h_vector.back();
			double * state_pre = S_vector.back();
			for (size_t i = 0; i < hidenode; i++)
			{
				forgetGateValue += h_pre[i] * U_F[i][j];
				inGateValue += h_pre[i] * U_I[i][j];
				gGateValue += h_pre[i] * U_G[i][j];
				outGateValue += h_pre[i] * U_O[i][j];
			}
			
			//偏置
			forgetGateValue += B_F[j][0] * 1.0;
			inGateValue += B_I[j][0] * 1.0;
			gGateValue += B_G[j][0] * 1.0;
			outGateValue += B_O[j][0] * 1.0;

			in_gate[j] = sigmoid(inGateValue);
			out_gate[j] = sigmoid(outGateValue);
			forget_gate[j] = sigmoid(forgetGateValue);
			g_gate[j] = tanh(gGateValue);//这里应该是tanh

			double s_pre = state_pre[j];
			state[j] = forget_gate[j] * s_pre + g_gate[j] * in_gate[j];//状态
			h[j] = out_gate[j] * tanh(state[j]);//输出
			/*cout << "h[j]=" << h[j] << endl;*/
		}
		truthLabel = truth_steps[p];
		/*for (size_t m = 0; m < outnode; m++)
		{
			cout << "truthLabel" << m << "=" << truthLabel[m] << endl;
		}*/
		for (k = 0; k < outnode; k++)//输出节点
		{
			//隐藏层传播到输出层,输出层的权重和sigmoid函数是自己根据实际需要添加的,一般情况下,LSTM就有个h输出
			for (j = 0; j < hidenode; j++)
			{
				double tmp =h[j] * W_out[j][k];
				/*cout << "tmp" <<  "=" << tmp << endl;
				cout << "output" << j << "=" << output[k] << endl;*/
				output[k] += tmp;
				//cout << "h" << j << "=" << h[j] << endl;
				//cout << "W_out" << j << k << "=" << W_out[j][k] << endl;
				//cout << "output" << j << "=" << output[k] << endl;
			}
			output[k] += B_out[k][0] * 1.0;
			y_pre[k] = sigmoid(output[k]);               //输出层各单元输出
		/*	cout << "y_pre" << k << "=" << y_pre[k] << endl;*/
		}
		predictV.push_back(y_pre);
		I_vector.push_back(in_gate);
		F_vector.push_back(forget_gate);
		O_vector.push_back(out_gate);
		S_vector.push_back(state);
		G_vector.push_back(g_gate);
		h_vector.push_back(h);

		//保存标准误差关于输出层的偏导

		for (size_t k  = 0; k  < outnode; k ++)
		{
			y_delta[k] = (truthLabel[k] - output[k]) * dsigmoid(y_pre[k]);
			e += fabs(truthLabel[k] - output[k]);          //误差
			/*cout << "output" << k << "=" << output[k] << ";truthLabel" << k << "=" << truthLabel[k] << endl;*/
		}
		y_delta_vector.push_back(y_delta);
	}
	//误差反向传播
	//隐含层偏差,通过和当前时间点输出层的误差来计算
	double h_delta[hidenode]{ 0 };//当前时间点输出(不含输出层)的delta,包含两部分:后一个时间点的隐含层h(t+1)误差,输出层y的误差y_delta,这两部分相加
	double *y_deltaB = new double[outnode]{ 0 };
	double *O_delta = new double[hidenode] {0};
	double *I_delta = new double[hidenode] {0};
	double *F_delta = new double[hidenode] {0};
	double *G_delta = new double[hidenode] {0};
	double *state_delta = new double[hidenode] {0};
	//后一个时间点的隐藏层误差
	double *O_future_delta = new double[hidenode] {0};
	double *I_future_delta = new double[hidenode] {0};
	double *F_future_delta = new double[hidenode] {0};
	double *G_future_delta = new double[hidenode] {0};
	double *state_future_delta = new double[hidenode] {0};
	double *forget_gate_future = new double[hidenode] {0};//存储前一个时间点的遗忘门节点值,不是delta
	for (j = 0; j<hidenode; j++)//从最后一个时间点开始计算,此时门节点的delta都是0
	{
		O_future_delta[j] = 0;
		I_future_delta[j] = 0;
		F_future_delta[j] = 0;
		G_future_delta[j] = 0;
		state_future_delta[j] = 0;
		forget_gate_future[j] = 0;
	}
	//从最后一个时间点反向计算
	for (p = timesteps - 1; p >= 0; p--)
	{
		x[0] = (float)curSample[p];
		//当前隐藏层,取出每个门的节点值,
		double *in_gate = I_vector[p];     //输入门,当前时间点中,门的所有神经元节点值
		double *out_gate = O_vector[p];    //输出门
		double *forget_gate = F_vector[p]; //遗忘门
		double *g_gate = G_vector[p];      //新记忆
		double *state = S_vector[p + 1];     //状态值,算0元素,共有8+1=9个元素
		double *h = h_vector[p + 1];         //隐层输出值,算0元素,共有8+1=9个元素
											 //前一个隐藏层
		double *h_pre = h_vector[p];
		double *state_pre = S_vector[p];
		y_deltaB = y_delta_vector[p];
		//对于网络中每个隐藏单元,计算误差项,并更新权值
		for (j = 0; j < hidenode; j++)
		{
			h_delta[j] = 0.0;
			for (k = 0; k<outnode; k++)
			{
				h_delta[j] += y_deltaB[k] * W_out[j][k];
				/*cout << "h_delta" << j << h_delta[j] << endl;*/
			}
			for (k = 0; k<hidenode; k++)//当前的h也输入到下一个时间点的cell中了,因此也需要累加下一个时间点的相关delta
			{
				h_delta[j] += I_future_delta[k] * U_I[j][k];
				h_delta[j] += F_future_delta[k] * U_F[j][k];
				h_delta[j] += O_future_delta[k] * U_O[j][k];
				h_delta[j] += G_future_delta[k] * U_G[j][k];
			}
			//隐含层中每个门的每个神经元的误差
			O_delta[j] = h_delta[j] * tanh(state[j]) * dsigmoid(out_gate[j]);
			state_delta[j] = h_delta[j] * out_gate[j] * dtanh(state[j]) +
				state_future_delta[j] * forget_gate_future[j];
			F_delta[j] = state_delta[j] * state_pre[j] * dsigmoid(forget_gate[j]);
			I_delta[j] = state_delta[j] * g_gate[j] * dsigmoid(in_gate[j]);
			G_delta[j] = state_delta[j] * in_gate[j] * dtanh(g_gate[j]);//此处用dtanh???
																		//更新前一个时间点隐含层和当前时间点隐含层之间的权值
			for (k = 0; k<hidenode; k++)
			{
				U_I[k][j] += alpha * I_delta[j] * h_pre[k];
				U_F[k][j] += alpha * F_delta[j] * h_pre[k];
				U_O[k][j] += alpha * O_delta[j] * h_pre[k];
				U_G[k][j] += alpha * G_delta[j] * h_pre[k];
			}
			//更新当前时间点输入层和当前时间点隐含层之间的连接权
			for (k = 0; k<innode; k++)
			{
				W_I[k][j] += alpha * I_delta[j] * x[k];
				W_F[k][j] += alpha * F_delta[j] * x[k];
				W_O[k][j] += alpha * O_delta[j] * x[k];
				W_G[k][j] += alpha * G_delta[j] * x[k];
			}
			//更新偏置
			B_I[j][0] += alpha * I_delta[j];
			B_O[j][0] += alpha * O_delta[j];
			B_F[j][0] += alpha * F_delta[j];
			B_G[j][0] += alpha * G_delta[j];
		}
		for (k = 0; k<outnode; k++)  //对于网络中每个输出单元,更新权值,这个在一般的LSTM并没有,一般的LSTM只有h层
		{
			//更新隐含层和输出层之间的连接权
			for (j = 0; j < hidenode; j++)
			{
				W_out[j][k] += alpha * y_deltaB[k] * h[j];
			}
			B_out[k][0] += alpha * y_deltaB[k];
		}
		O_future_delta = O_delta;
		F_future_delta = F_delta;
		I_future_delta = I_delta;
		G_future_delta = G_delta;
		state_future_delta = state_delta;
		forget_gate_future = forget_gate;
	}
	if (epoch % 1000 == 0)
	{

		cout << "第  " << epoch <<"  epoch:"<< endl;
		cout << "样本数据:"  << endl;
		vector<string> vsamp = encode2Sample(dsMap, curSample);
		for (k = 0; k < timesteps; k++)
			cout <<" "<< vsamp[k];
		cout << endl;
		cout << "error: " << e << endl;
		cout << "pred: ";
		vector<string> vpre = encode2Truth(dsMap, predictV);
		for (k = 0; k < timesteps; k++)
			cout<< " " <<  vpre[k];
		cout << endl;
		vector<string> vtru = encode2Truth(dsMap, truth_steps);
		cout << "true: ";
		for (k = 0; k < timesteps; k++)
			cout << " " << vtru[k];
		cout << endl;
		cout << endl;
	}

	//for (i = 0; i<I_vector.size(); i++)
	//	delete I_vector[i];
	//for (i = 0; i<F_vector.size(); i++)
	//	delete F_vector[i];
	//for (i = 0; i<O_vector.size(); i++)
	//	delete O_vector[i];
	//for (i = 0; i<G_vector.size(); i++)
	//	delete G_vector[i];
	//for (i = 0; i<S_vector.size(); i++)
	//	delete S_vector[i];
	//for (i = 0; i<h_vector.size(); i++)
	//	delete h_vector[i];
	predictV.clear();
	truth_steps.clear();
	I_vector.clear();
	F_vector.clear();
	O_vector.clear();
	G_vector.clear();
	S_vector.clear();
	h_vector.clear();
	y_delta_vector.clear();


	delete S;
	S = NULL;
	delete h;
	h = NULL;
	//随机获取一个新样本
	offset = random(randomMax);

#ifdef CHINESE
while (offset % 2 != 0)//必须是2的倍数,否则获取汉字时进入死循环
{
offset = random(randomMax);
}
#endif // CHINESE

	/*cout << "当前偏移:" << offset << endl;*/
	epoch++;
}

}
void BubbleSort(double *p, int length, int * ind_diff)
{
for (int m = 0; m < length; m++)
{
ind_diff[m] = m;
}

for (int i = 0; i < length; i++)
{
	for (int j = 0; j < length - i - 1; j++)
	{
		if (p[j] > p[j + 1])
		{
			double temp = p[j];
			p[j] = p[j + 1];
			p[j + 1] = temp;

			int ind_temp = ind_diff[j];
			ind_diff[j] = ind_diff[j + 1];
			ind_diff[j + 1] = ind_temp;
		}
	}
}

}
vector LstmCppH::encode2Truth(map<string, int>& dsMap, vector<double*>& predictedM)
{
vector sRes;
sRes.clear();
int maxInd = 0;
for (size_t i = 0; i < predictedM.size(); i++)
{
double * pre = predictedM[i];
for (size_t j = 0; j < outnode; j++)
{
pre[j] = abs(pre[j]);
/cout << “当前节点 " << j << " 预测的值:” << pre[j] << endl;/
}
int ind[outnode] = { 0 };
BubbleSort(pre,outnode,ind);
maxInd = ind[outnode - 1];
//cout <<“当前时间点 “<<i<<” 最大值索引:”<< maxInd << endl;
for (map<string, int>::iterator it = dsMap.begin(); it != dsMap.end(); it++)
{
if ((*it).second == (maxInd + 1))
{
sRes.push_back((*it).first);
}
}
}
return sRes;
}

vector LstmCppH::encode2Sample(map<string, int>& dsMap, vector& sample)
{
vector sRes;
sRes.clear();
for (size_t i = 0; i < sample.size(); i++)
{
for (map<string, int>::iterator it = dsMap.begin(); it != dsMap.end(); it++)
{
if (it->second == sample[i])
{
sRes.push_back(it->first);
}
}
}
return sRes;
}

vector LstmCppH::sample2Encode(map<string, int>& dsMap, vector sample)
{
vector res;
for (size_t i = 0; i < sample.size(); i++)
{
for (map<string, int>::iterator it=dsMap.begin();it != dsMap.end(); it++)
{
if (it->first == sample[i])
{
res.push_back(it->second);
}
}
}
return res;
}

void LstmCppH::predict()
{
DataPreProc ddp;

char dataset[] = "壹 贰 叁 肆 伍 陆 柒 捌 玖 拾";
map<std::string,int> ds= ddp.loadDataset(dataset);
//输入样本数据
char s[200] = { NULL };
cout << "输入样本数据,不超过3个汉字且在训练数据集范围内:" << endl;
cin.getline(s, 200);
string ss(s);
vector<string> re = ddp.split(ss, " ");
vector<int> input = sample2Encode(ds,re);
vector<double*> predictV;      //预测值
vector<double*> I_vector;      //输入门
vector<double*> F_vector;      //遗忘门
vector<double*> O_vector;      //输出门
vector<double*> G_vector;      //新记忆
vector<double*> S_vector;      //状态值
vector<double*> h_vector;      //输出值
double *x = new double[innode];
//在0时刻是没有之前的隐含层的,所以初始化一个全为0的
double *S = new double[hidenode] {0};     //状态值
double *h = new double[hidenode] {0};     //输出值

vector<std::string> result(re.begin(),re.begin()+timesteps); 
int cnt = 0;
while (cnt < 5)
{
	/*正向传播:一直计算完所有的time step*/
	for (int p = 0; p < timesteps; p++)//循环遍历二进制数组,从最低位开始,p相当于time steps
	{
		if (p == 0)
		{
			for (size_t i = 0; i < hidenode; i++)
			{
				S[i] = 0.0;
				h[i] = 0.0;
			}
			S_vector.push_back(S);
			h_vector.push_back(h);
		}
		x[0] = (float)input[p];
		//cout << "x[0]" << p << "=" << x[0] << endl;
		double *in_gate = new double[hidenode] {0};     //输入门取sigmoid函数后的值
		double *out_gate = new double[hidenode] {0};    //输出门取sigmoid函数后的值
		double *forget_gate = new double[hidenode] {0}; //遗忘门取sigmoid函数后的值
		double *g_gate = new double[hidenode] {0};      //新记忆取tanh函数后的值
		double *state = new double[hidenode] {0};       //状态值
		double *h = new double[hidenode] {0}; //隐层输出值
		double *output = new double[outnode] {0};//当前时间点的预测数组
		double *y_pre = new double[outnode] {0};//当前时间点的预测数组

		//前一个状态
		double * h_pre = h_vector.back();
		double * state_pre = S_vector.back();
		for (size_t j = 0; j < hidenode; j++)
		{
			double inGateValue = 0;
			double forgetGateValue = 0;
			double outGateValue = 0;
			double gGateValue = 0;
			//当前时间点输入
			for (size_t k = 0; k < innode; k++)
			{
				forgetGateValue += x[k] * W_F[k][j];
				inGateValue += x[k] * W_I[k][j];
				gGateValue += x[k] * W_G[k][j];
				outGateValue += x[k] * W_O[k][j];
			}
			for (size_t i = 0; i < hidenode; i++)
			{
				forgetGateValue += h_pre[i] * U_F[i][j];
				inGateValue += h_pre[i] * U_I[i][j];
				gGateValue += h_pre[i] * U_G[i][j];
				outGateValue += h_pre[i] * U_O[i][j];
			}

			//偏置
			forgetGateValue += B_F[j][0] * 1.0;
			inGateValue += B_I[j][0] * 1.0;
			gGateValue += B_G[j][0] * 1.0;
			outGateValue += B_O[j][0] * 1.0;

			in_gate[j] = sigmoid(inGateValue);
			out_gate[j] = sigmoid(outGateValue);
			forget_gate[j] = sigmoid(forgetGateValue);
			g_gate[j] = tanh(gGateValue);//这里应该是tanh

			double s_pre = state_pre[j];
			state[j] = forget_gate[j] * s_pre + g_gate[j] * in_gate[j];//状态
			h[j] = out_gate[j] * tanh(state[j]);//输出
												/*cout << "h[j]=" << h[j] << endl;*/
		}

		/*for (size_t m = 0; m < outnode; m++)
		{
		cout << "truthLabel" << m << "=" << truthLabel[m] << endl;
		}*/
		for (int k = 0; k < outnode; k++)//输出节点
		{
			//隐藏层传播到输出层,输出层的权重和sigmoid函数是自己根据实际需要添加的,一般情况下,LSTM就有个h输出
			for (int j = 0; j < hidenode; j++)
			{
				double tmp = h[j] * W_out[j][k];
				/*cout << "tmp" <<  "=" << tmp << endl;
				cout << "output" << j << "=" << output[k] << endl;*/
				output[k] += tmp;
				//cout << "h" << j << "=" << h[j] << endl;
				//cout << "W_out" << j << k << "=" << W_out[j][k] << endl;
				//cout << "output" << j << "=" << output[k] << endl;
			}
			output[k] += B_out[k][0] * 1.0;
			y_pre[k] = sigmoid(output[k]);               //输出层各单元输出
			/*	cout << "y_pre" << k << "=" << y_pre[k] << endl;*/
		}
		predictV.push_back(y_pre);
		S_vector.push_back(state);
		h_vector.push_back(h);
	}
	vector<string> vpre = encode2Truth(ds, predictV);

	predictV.clear();
	S_vector.clear();
	h_vector.clear();

	//cout << "预测值:" << vpre[timesteps - 1] << endl;
	result.push_back(vpre[timesteps-1]);
	cout << "预测" << cnt << ":";
	for (size_t i = 0; i < result.size(); i++)
	{
		cout << result[i];
	}
	cout << endl;
	//修改输入
	re.erase(re.begin());
	re.push_back(vpre[timesteps - 1]);
	input.clear();
	input = sample2Encode(ds, re);
	//for (size_t i = 0; i < input.size(); i++)
	//{
	//	cout << "input value:" << input[i] << endl;
	//}
	//cout << "re" <<re.back()<< endl;
	//继续预测下一个
	cnt++;
}
result.clear();

}
调用方法:
int main()
{

srand(time(NULL));

LstmCppH lstmcpph;
lstmcpph.train();
cout << "训练结束" << endl;
lstmcpph.predict();
system("pause");
return 0;

}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LSTM预测大写数字的c++ 代码 的相关文章

  • 数据结构的优化存储以实现快速查找和持久化

    Scenario 我有以下方法 public void AddItemSecurity int itemId int userIds public int GetValidItemIds int userId 最初我正在考虑表单上的存储 i
  • 将列表数组中的值绑定到列表框

    任何机构都可以给出一个简短的示例 用于将列表数组中的值绑定到 c net 中的列表框 这取决于您的列表数组的情况 让我们从一个简单的示例开始 List
  • 沿着长数据序列在固定大小的移动窗口中查找中值

    给定一个数据序列 可能有重复项 一个固定大小的移动 窗口 从数据开始处每次迭代时移动窗口 序列 使得 1 从窗口中删除最旧的数据元素并添加新数据 元素被推入窗口 2 求每次移动时窗口内数据的中位数 以下帖子没有帮助 有效地找到随机序列的中值
  • C/C++ 程序是在 CPU 上运行还是在内核上运行?

    我已经编程很多年了 但有一件事我一直不明白 有两种类型的编程语言 编译型语言和解释型语言 编译型语言首先需要编译成解释型语言 然后才能执行 例如 C C 需要先编译为机器语言 然后才能执行 我的问题来了 谁真正运行已编译的 C C Wind
  • MVC 中的 Blazor:组件被渲染,但 @onclick 不起作用。连接问题

    我正在尝试在 net core 3 MVC 项目中使用 Blazor 我使用了一些教程来做到这一点 例如https fizzylogic nl 2019 08 18 integrating blazor in an existing asp
  • 为什么不能使用initializer_list来初始化unique_ptr的向量? [复制]

    这个问题在这里已经有答案了 我想知道为什么initializer list 不能与unique ptr 一起使用 std vector
  • 函数的不明确的引用/值版本

    考虑以下函数原型 void Remove SomeContainer Vec const std size t Index SomeContainer Remove SomeContainer Vec const std size t In
  • 我可以将特定警告视为错误吗?

    以下是我有时在学生代码中看到的模式的简化版本 bool foobar int a int b if a lt b return true 当然 真正的代码要复杂得多 Visual Studio 报告警告 C4715 并非所有控制路径都会返回
  • 如何实现可变虚拟成员函数

    所以我有这个功能 virtual void CallRemoteFunction const char pServerGameObjectId const char pFunctionName OVariant arg1 OVariant
  • Docker 不遵循构建目录中的符号链接

    我正在对一个应用程序进行 Docker 化 其中涉及通过 Clang 将二进制文件与其他 C 文件链接 我们维护二进制文件的符号链接版本 因为它们在整个代码库中使用 我的 Docker 构建目录包含整个代码库 包括源文件以及这些源文件的符号
  • Ajax 函数在重定向后不保存滚动位置

    正如标题所述 我编写了一个 ajax 函数 该函数应该滚动到用户在重定向之前所在的位置 我写了一个alert对于测试场景 它确实触发了 但滚动不断回到顶部 我在这里做错了什么 JavaScript ajax type GET url Adm
  • 为什么 C++20 范围不只提供管道语法?

    我知道这个问题听起来很奇怪 所以这里有一些背景信息 最近 我很失望地了解到 C 20 范围内的映射缩减并不像人们所期望的那样工作 即 const double val data transform accumulate 不起作用 你必须这样
  • 如何在 C 预处理器中可靠地检测 Mac OS X、iOS、Linux、Windows? [复制]

    这个问题在这里已经有答案了 如果有一些跨平台 C C 代码需要在 Mac OS X iOS Linux Windows 上编译 我如何在预处理器过程中可靠地检测到它们 大多数编译器都使用预定义的宏 您可以找到列表here http sour
  • PARITY_NONE 是 C++ Windows 中的关键字吗?

    我正在使用 boost 编写一个串行库 并且我有一个枚举 enum parity t PARITY NONE PARITY ODD PARITY EVEN 我收到如下错误 错误 1 错误 C2059 语法错误 我无法弄清楚问题是什么 然后我
  • 使用 Node.js 访问用 C++ 编写的 SDK

    我有一个用 C 语言编写的 SDK 可以与我的扫描仪设备进行通信 我需要开发一个可以访问扫描仪设备的电子应用程序 我知道有很多库可用于扫描仪 但我想使用这个 SDK 因为它允许我访问设备的完整功能 而且它是由设备制造商提供的 那么 有没有什
  • 在标准 C 中将 int 转换为 string

    我是 C 新手 我正在寻找一个可以调用函数进行转换的示例int串起来 我发现itoa但这不是标准 C 的一部分 我还发现sprintf str d aInt 但问题是我不知道所需的 str 的大小 因此 我如何传递输出字符串的正确大小 有多
  • 如何正确地将十六进制转义添加到字符串文字中?

    当你有C语言的字符串时 你可以在里面直接添加十六进制代码 char str abcde a b c d e 0x00 char str2 abc x12 x34 a b c 0x12 0x34 0x00 这两个示例在内存中都有 6 个字节
  • 使texture2D在运行时/脚本Unity3D中可读[重复]

    这个问题在这里已经有答案了 我有一个插件 可以让我访问 Android 手机图库中的图片 这给了我一个Texture2D类型的纹理 然后我想使用 GetPixels 函数对其进行编辑 但默认情况下它未设置为可读 如何使纹理可读 以便我可以在
  • 频繁插入已排序的集合

    我已经对集合 列表 进行了排序 并且我需要始终保持其排序 我目前在我的集合上使用 List BinarySearch 然后在正确的位置插入元素 我也尝试过在每次插入后对列表进行排序 但性能不可接受 有没有一种解决方案可以提供更好的性能 也许
  • File.Move 的原子性

    我想将目录中的文件重命名为原子事务 该文件不会更改目录 该路径作为 NTFS 文件系统的 UNC 路径提供 可能位于服务器 03 或 08 上 File Move 对于这些目的来说是原子的吗 例如 它要么成功完成 要么失败 以使原始文件仍然

随机推荐

  • 关于图像模式识别的几种分类方法概述

    1 基于概率统计的Bayes分类器 因为在实际分类中由于考虑的侧重点不同或者关心的点不一样导致不能使用同一决策去解决所有的事件的分类 所以需要根据不同的准测函数选择不同的分类决策 基于最小错误率的Bayes决策 基于最小风险的Bayes决策
  • mybatis 分页_Mybatis 分页查询方法

    Mybatis自带的两种分页方法 Mybatis有两种自带分页方法 RowBounds和PageHelper 其中前者是逻辑分页 后者是物理分页 本文后续假设使用myabtis generator生成mapper RowBounds Row
  • 二阶段无法解决的问题

    二阶段无法解决的问题 协调者再发出commit消息之后宕机 而唯一接收到这条消息的参与者同时也宕机了 那么即使协调者通过选举协议产生了新的协调者 这条事务的状态也是不确定的 没人知道事务是否被已经提交 与两阶段提交不同的是 三阶段提交有两个
  • python - 快速搜索指定格式文件的方法

    需求1 快速查找指定文件夹下 所有满足开头为NC 结尾为 nc的文件 root path Users xpji convert kuihua9data 20230621 from pathlib import Path def get fi
  • ES6中Null判断运算符(??)正确打开方式-

    读取对象属性的时候 如果某个属性的值是null或者undefined 有时候需要为它们指定默认值 常见的作法是通过 运算符指定默认值 const headerText response settings headerText Hello w
  • C语言:指针(进阶)

    目录 指针 指针概念 a 什么是地址 b 什么是指针 指针变量 a 指针变量是用来做什么的 b 指针变量的类型怎么理解 和 操作符 a 单目操作符 是什么 b 单目操作符 是什么 二级指针 a 二级指针是什么 指针类型的意义 a 为什么指针
  • iOS 报错:没有查看权限the file "xxx.app" couldn't be opened because you don't have permission to view it.

    iOS the file xxx app couldn t be opened because you don t have permission to view it 问题解决方法汇总 1 最近重构项目 增加target来区分环境进行差异
  • order by排序的用法

    一 order by 1 其实1表示第一个栏位 2表示第二栏位 依此类推 当表中只有2个栏位时 oder by 3就会出错 这个跟order by 列名没有什么区别 不过在特殊情况下还是很有用的 select owner table nam
  • windows vs2010 下使用glib库 mono4.3

    开发工具 vs2010 1 下载glib http ftp gnome org pub gnome binaries win32 glib 中有各个版本的glib列表 选一个感兴趣的 本文使用2 12 glib 2 12 11 zip gl
  • OKR与CFR管理模式(一)-什么是OKR?

    前言 无论任何管理书籍 都是围绕着人性 如何激发员工的人性中的自尊和自我价值观 自我成就感 作为一名领导者 在管理面前 必须要是冷静 安静的对待他人 好主意 再加上 卓越的执行 就一定可以创造奇迹 而这正是OKR 目标与关键结果 Objec
  • 常见面试问题 - 2(计算机网络)

    OSI七层模型 TCP IP四层模型 五层协议 OSI七层模型 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 TCP IP四层模型 应用层 传输层 网络层 网络接口层 五层体系结构 应用层 传输层 段 网络层 包 数据链路层
  • glint360k数据集的解压

    关于训练集的解压早就有人写了blog了 文章地址 https blog csdn net weixin 43408232 article details 109687884 但是对于它剩余的7个bin文件我很困惑 根据他们在官方的微博上声明
  • Verilog学习(3)initial,always,task,function,常见系统任务

    结构说明语句 Verilog中任何过程模块都属于以下四种结构的说明语句 initial说明语句 always 说明语句 task说明语句 function说明语句 一个程序模块可以有多个initial和always 过程块 每个initia
  • 常用的windows命令大全

    当我们熟练掌握windows命令时 可以通过输入命令来快速完成各种系统操作 非常的便捷 那么常用的windows命令有哪些呢 今天 小编就把命令介绍给大家 windows命令 1 gpedit msc 组策略 2 sndrec32 录音机
  • 12-JavaScript的正则表达式 DAY9 (04.20)

    1 正则表达式的定义 正则表达式是由一个字符序列形成的搜索模式 用来匹配 当在文本中搜索数据时 可以使用搜索模式来描述查询内容 其可以是一个简单的字符 或者一个更复杂的模式 2 正则表达式的创建 1 字面量 var reg1 abc g g
  • 论人工智能历史、现状与未来发展战略

    来源 学术前沿 作者 郭毅可 人工智能问世60多年来 承载着人类对自己的智慧的无限自信 在这样的自信下 人工智能发展到了今天 人们在追求机器从事尽可能多的智力劳动的路上走得很快 也很远 今天人工智能的发展 实际上标志着人类第三次认知革命 即
  • 理解cpp的重载,重写,重定义

    函数重载 overload 函数重载是指在一个类中声明多个名称相同但参数列表不同的函数 这些的参数可能个数或顺序 类型不同 但是不能靠返回类型来判断 特征是 1 相同的范围 在同一个作用域中 2 函数名字相同 3 参数不同 4 virtua
  • 死锁产生条件和解决办法

    死锁 死锁产生的四个条件 产生死锁必须同时满足以下四个条件 只要其中任一条件不成立 死锁就不会发生 互斥条件 线程要求对所分配的资源 如打印机 进行排他性控制 即在一段时间内某资源仅为一个线程所占有 此时若有其他线程请求该资源 则请求线程只
  • 城市污水管网监测系统解决方案

    一 方案概述 在经济快速发展和政府政策的推动下 以产业聚焦为核心的城市园区经济发展迅速 由于在城市园区企业 工厂在生产制造过程产生了大量的废水等其他污染物都是由污水管进行排放 一旦发生井下污水管网堵塞 会造成废水中的气体等其他有害物质的传播
  • LSTM预测大写数字的c++ 代码

    自己写的LSTM预测大写数字的c 代码 有较详细的注释 有不懂的可以交流 平台 vs2015 头文件 LstmCppH h pragma once include iostream include math h include stdlib