C++元编程——深度双向RNN实验

使用C++的标准库实现了双向RNN的功能。最近对DRNN做了一些改进,同时进行了实验,首先DRNN的代码如下:

#ifndef _RNN_HPP_
#define _RNN_HPP_
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include "mat.hpp"
#include "bp.hpp"
#include "activate_function.hpp"
/*
* 									     X<k> 
* 										  ^ 
* 										  | 
* 									     afV 
*										  ^
*							              |
*                                        +bv  
* 										  ^ 
* 										  | 
* 							 bw 		  V 
* 							 |  		  ^
*  							 v  		  | 
*	S<t-1> ------> W ------> + --------> afW ------> S<t>
*							 ^
*							 |
*							 U
*							 ^
*							 |
*							 X<k-1>      	
* */
template<typename val_t, template<typename> class update_method_templ, template<typename> class activate_func>
struct rnn_unite 
{using target_t = typename std::template vector<val_t>;val_t W;				//* 用于对层内加权val_t U;				//* 用于对上层加权val_t V;				//* 用于对激活值加权val_t bv;				//* 激活值的偏移量val_t bw;				//* 层内加权偏移量val_t S;update_method_templ<val_t> umW, umU, umV, umBv, umBw, umS;activate_func<val_t> afW;activate_func<val_t> afV;	//* 对下层的计划函数target_t	pre_input;target_t	pre_S;			//* 上一层进来的Svoid print() const{printf("W:%.4lf U:%.4lf V:%.4lf bv:%.4lf bw:%.4lf S:%.4lf\r\n", W, U, V, bv, bw, S);}rnn_unite():S(0.) {static std::default_random_engine e;std::uniform_real_distribution<double> ud(0, 1);W = ud(e);						//* 随机初始化U = ud(e);V = ud(e);bv = ud(e);bw = ud(e);}inline val_t do_forward(const int& i, val_t& cur_S){cur_S = afW.forward(U * pre_input[i] + W * cur_S + bw);pre_S[i] = cur_S;return afV.forward(V * cur_S + bv);}inline val_t do_backward(const int& i, const target_t& mt_delta, val_t& S_delta, const val_t& Wbak, const val_t& Ubak, const val_t& Vbak, val_t& Vdelta, val_t& bVdelta, val_t& Wdelta, val_t& bWdelta, val_t& Udelta){auto delta_before_afV = mt_delta[i] * afV.backward();Vdelta = Vdelta + pre_S[i] * delta_before_afV;bVdelta = bVdelta + delta_before_afV;//V = umV.update(V, pre_S[i] * delta_before_afV);//bv = umBv.update(bv, delta_before_afV);auto layer_back = (delta_before_afV * Vbak + S_delta) * afW.backward();Wdelta = Wdelta + pre_S[i - 1] * layer_back;bWdelta = bWdelta + layer_back;Udelta = Udelta + pre_input[i] * layer_back;//W = umW.update(W, pre_S[i - 1] * layer_back);//bw = umBw.update(bw, layer_back);//U = umU.update(U, pre_input[i]);S_delta = layer_back * Wbak;return layer_back * Ubak;}//* 升序遍历数组,正向通过网络,返回值为层间正向输出target_t asc_forward(const target_t& mt_in){pre_S.resize(mt_in.size(), 0.);pre_input = mt_in;target_t vec_ret(mt_in.size(), 0.);val_t cur_S = S;						//* 层内计算的值for (int i = 0; i < pre_input.size(); ++i){vec_ret[i] = do_forward(i, cur_S);}return vec_ret;}//* 反向遍历数组反馈误差,返回值为层间反向传播的误差target_t desc_backward(const target_t& mt_delta){val_t Wbak = W, Ubak = U, Vbak = V;target_t ret(mt_delta.size(), 0.);val_t S_delta = 0., Vdelta = 0., bVdelta = 0., Wdelta = 0., bWdelta = 0., Udelta = 0.;for (int i = mt_delta.size()-1; i >= 0; --i){ret[i] = do_backward(i, mt_delta, S_delta, Wbak, Ubak, Vbak, Vdelta, bVdelta, Wdelta, bWdelta, Udelta);}V = umV.update(V, Vdelta);bv = umBv.update(bv, bVdelta);W = umW.update(W, Wdelta);bw = umBw.update(bw, bWdelta);U = umU.update(U, Udelta);S = umS.update(S, S_delta);return ret;}//* 反向遍历数组,正向通过网络target_t desc_forward(const target_t& mt_in){pre_S.resize(mt_in.size(), 0.);pre_input = mt_in;target_t vec_ret(mt_in.size(), 0.);val_t cur_S = S;						//* 层内计算的值for (int i = mt_in.size()-1; i >= 0; --i){vec_ret[i] = do_forward(i, cur_S);}return vec_ret;}//* 正向遍历数组,反向通过网络target_t asc_backward(const target_t& mt_delta){val_t Wbak = W, Ubak = U, Vbak = V, bvbak = bv, bwbak = bw, Sbak = S;target_t ret(mt_delta.size(), 0.);val_t S_delta = 0., Vdelta = 0., bVdelta = 0., Wdelta = 0., bWdelta = 0., Udelta = 0.;for (int i = 0; i < mt_delta.size(); ++i){ret[i] = do_backward(i, mt_delta, S_delta, Wbak, Ubak, Vbak, Vdelta, bVdelta, Wdelta, bWdelta, Udelta);}V = umV.update(V, Vdelta);bv = umBv.update(bv, bVdelta);W = umW.update(W, Wdelta);bw = umBw.update(bw, bWdelta);U = umU.update(U, Udelta);S = umS.update(S, S_delta);return ret;}};template<typename val_t, template<typename> class update_method_templ, template<typename> class activate_func>
struct rnn_dup
{using target_t = typename std::template vector<val_t>;rnn_unite<val_t, update_method_templ, activate_func> rnn_forward, rnn_backward;using weight_type = bp<val_t, 1, gd, no_activate, XavierGaussian, 2, 1>;weight_type w;rnn_dup():rnn_forward(), rnn_backward(){}target_t forward(const target_t& mt_in){target_t mt_forward = rnn_forward.asc_forward(mt_in);target_t mt_backward = rnn_backward.desc_forward(mt_in);target_t ret(mt_in.size(), 0.);for (int i = 0; i < mt_in.size(); ++i){mat<2, 1, val_t> mt;mt[0] = mt_forward[i];mt[1] = mt_backward[i];ret[i] = w.forward(mt)[0];}return ret;}target_t backward(const target_t& mt_delta) {target_t mt_forward(mt_delta.size(), 0.);target_t mt_backward(mt_delta.size(), 0.);for (int i = 0; i < mt_delta.size(); ++i){mat<1, 1, val_t> mt(mt_delta[i]);auto mt_out = w.backward(mt);mt_forward[i] = mt_out[0];mt_backward[i] = mt_out[1];}target_t mt_forward_out = rnn_forward.desc_backward(mt_forward);target_t mt_backward_out = rnn_backward.asc_backward(mt_backward);target_t mt_ret(mt_delta.size(), 0.);for (int i = 0; i < mt_delta.size(); ++i){mt_ret[i] = mt_forward_out[i] + mt_backward_out[i];}return mt_ret;}void print() const {printf("bp weight:\r\n");w.print();printf("forward:\r\n");rnn_forward.print();printf("backward:\r\n");rnn_backward.print();}
};template<typename val_t, template<typename> class update_method_templ, template<typename> class activate_func>
struct rnn_flow 
{using target_t = typename std::template vector<val_t>;using rnn_type = rnn_dup<val_t, update_method_templ, activate_func>;rnn_type* p_layers;int layer_num;rnn_flow(const int& i_layer_num) :layer_num(i_layer_num) {p_layers = new rnn_type[layer_num];}virtual ~rnn_flow() {delete[] p_layers;}target_t forward(const target_t& mt_in) {auto mt = mt_in;for (int i = 0; i < layer_num; ++i) {mt = p_layers[i].forward(mt);}return mt;}target_t backward(const target_t& mt_in) {auto ret = mt_in;for (int i = layer_num-1; i >= 0; --i) {ret = p_layers[i].backward(ret);}return ret;}void print() const{for (int i = 0; i < layer_num; ++i){p_layers[i].print();}}
};#endif

接着使用nadam进行DRNN权值更新,然后输入了两个数组1,2,3和2,3,4进行训练,最后使用各种数组进行训练结果测试,代码如下:

#include "rnn.hpp"
#include "activate_function.hpp"
#include <math.h>const double ln2 = log(2);template<int row_num, int col_num>
mat<row_num, col_num, double> cross_entropy_grad(const mat<row_num, col_num, double>& expect, const mat<row_num, col_num, double>& real)
{mat<row_num, col_num, double> one(1.);return (expect / real - (one - expect)/(one - real))/ln2;
}int main(int argc, char** argv)
{using tgt_mat = std::vector<double>;rnn_flow<double, nadam, Tanh> rnn(2);//* 生成随机矩阵,根据矩阵计算出int train_num = 600000;for (int i = 0; i < train_num; ++i){{tgt_mat in = {.1, .2, .3};auto ret = rnn.forward(in);tgt_mat expect = {.3, .3, .3};mat<1, 3, double> mt_ret(ret.begin(), ret.end());mat<1, 3, double> mt_exp(expect.begin(), expect.end());mat<1, 3, double> mt_delta = cross_entropy_grad(mt_ret, mt_exp);std::vector<double> vec_delta = mt_delta.to_vector();mat<1, 3, double> backdelta = rnn.backward(vec_delta);if (i %(train_num/10) == 0){printf("output:");mt_ret.print();}}{tgt_mat in = {.2, .3, .4};auto ret = rnn.forward(in);tgt_mat expect = {.45, .45, .45};mat<1, 3, double> mt_ret(ret.begin(), ret.end());mat<1, 3, double> mt_exp(expect.begin(), expect.end());mat<1, 3, double> mt_delta = cross_entropy_grad(mt_ret, mt_exp);std::vector<double> vec_delta = mt_delta.to_vector();mat<1, 3, double> backdelta = rnn.backward(vec_delta);if (i %(train_num/10) == 0){printf("output:");mt_ret.print();}}if (i %(train_num/10) == 0){printf("--------------------------\r\n");}}printf("out\r\n");mat<1, 3, double>(rnn.forward({.1, .2, .0})).print();mat<1, 3, double>(rnn.forward({.2, .3, .0})).print();mat<1, 3, double>(rnn.forward({.3, .4, .0})).print();return 0;
}

训练结果如下:

 

第一个按顺序输入.1,.2,.0结果显示输入第2个的结果就是0.3228和我们训练使用的0.3有一点差距,不过差距比较小。第二个按照顺序输入.2,.3,.0,我们训练用的是.2,.3,.4;结果显示是0.456,和我们训练的输出0.45几乎一样。第三个是一个程序没有见过的输入.3,.4,.0;输出没有什么参考意义。总之,可见这个DRNN具备一定的学习能力的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/88014.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】树与二叉树

文章目录 &#x1f340;树型结构&#x1f431;‍&#x1f464;什么是树型结构&#x1f431;‍&#x1f453;树型结构的概念&#x1f431;‍&#x1f3cd;树的表示形式&#x1f431;‍&#x1f409;树的应用 &#x1f333;二叉树&#x1f431;‍&#x1f464;二叉树的概念&#…

Java-内部类:成员内部类、局部内部类、匿名内部类、静态内部类

文章目录 Java中的内部类一、成员内部类1. 成员内部类是什么&#xff1f;2. 为什么要使用成员内部类&#xff1f;3. 在哪里使用&#xff08;场景&#xff09;4. 优缺点5. 成员内部类示例代码 二、局部内部类1. 是什么&#xff1a;2. 为什么使用3. 在哪里使用4. 优缺点5. 局部内…

部署 ssm 项目到云服务器上(购买云服务器 + 操作远程云服务器 + 服务器中的环境搭建 + 部署项目到服务器)

部署 Web 项目 1、获取 Linux 环境1.1、如何去买一个云服务器1.2、远程操作云服务器1.3、在 Linux 系统中搭建 Java Web 的运行环境。1&#xff09;安装 JDK&#xff08;使用包管理器 yum 来安装&#xff09;2&#xff09; 安装Tomcat3&#xff09;安装 MySQL。 1.4、在云服务器…

成功项目风险预防可控的5个重点

成功的项目往往重视项目风险的预防和管控&#xff0c;这样有利于可能风险的及时控制和解决&#xff0c;将其不利影响降到最小。如果不重视对风险的预防和管控&#xff0c;不及时发现和处理项目风险&#xff0c;那么项目风险往往会为我们带来意想不到的不利后果&#xff0c;往往…

Stm32的时钟系统以及使用SysTick滴答定时器实现延时

前言 STM32的时钟系统由多个时钟源和时钟树组成时钟源包括主时钟源&#xff08;HSE&#xff09;、内部高速时钟源&#xff08;HSI&#xff09;、内部低速时钟源&#xff08;LSI&#xff09;和外部低速时钟源&#xff08;LSE&#xff09;。时钟树由多个时钟分频器和时钟门控器组…

08 通过从 库1 复制 *.ibd 到 库2 导致 mysql 启动报错

前言 呵呵 最近同事有这样的一个需求 需要将 库1 的一张表 复制到 库2 然后 我想到了 之前一直使用的通过复制这个库的 data 文件来进行数据迁移的思路, 是需要复制这个 库对应的 data 目录下的数据文件, 以及 ibdata1 文件 然后 我又在想 这里的场景能否也使用这里的额方式…

机器连接和边缘计算

以一种高效、可扩展的方式进行连接和边缘计算的结合&#xff0c;解决了在工业物联网应用中的机器数据集成问题。 一 边缘计算 边缘计算描述了由中央平台管理的数据分散式处理&#xff0c;它对于工业物联网而言非常重要。在许多应用程序中&#xff0c;由于数据量非常大&#xf…

一文速学-让神经网络不再神秘,一天速学神经网络基础-前向传播(三)

前言 思索了很久到底要不要出深度学习内容&#xff0c;毕竟在数学建模专栏里边的机器学习内容还有一大半算法没有更新&#xff0c;很多坑都没有填满&#xff0c;而且现在深度学习的文章和学习课程都十分的多&#xff0c;我考虑了很久决定还是得出神经网络系列文章&#xff0c;…

Could not autowire. No beans of ‘DiscoveryClient‘ type found.

一、导错了包 DiscoveryClient对应有两个包&#xff1a; org.springframework.cloud.client.discovery.DiscoveryClient; com.netflix.discovery.DiscoveryClient; 目前导入的包是&#xff1a; 改成第一个包&#xff0c;发现不再报红了。

构建安全可信、稳定可靠的RISC-V安全体系

安全之安全(security)博客目录导读 2023 RISC-V中国峰会 安全相关议题汇总 说明&#xff1a;本文参考RISC-V 2023中国峰会如下议题&#xff0c;版权归原作者所有。

GaussDB技术解读系列:高级压缩之OLTP表压缩

8月16日&#xff0c;第14届中国数据库技术大会&#xff08;DTCC2023&#xff09;在北京国际会议中心顺利举行。在GaussDB“五高两易”核心技术&#xff0c;给世界一个更优选择的专场&#xff0c;华为云数据库GaussDB首席架构师冯柯对华为云GaussDB数据库的高级压缩技术进行了详…

【QT】ComboBox的使用(14)

ComboBox这个控件我常用于多文本的储存、调用&#xff0c;正如他的中文意思为&#xff1a;下拉列表框。 下拉列表框&#xff1a;字面意思就是一个多文本的列表框&#xff0c;今天来看下如何使用ComboBox这个控件。 一.环境配置 1.python 3.7.8 可直接进入官网下载安装&…