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.mzph.cn/news/57751.shtml

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

相关文章

爬虫逆向实战(二十五)--某矿采购公告

一、数据接口分析 主页地址&#xff1a;某矿 1、抓包 通过抓包可以发现数据接口是cgxj/by-lx-page 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现有一个param的加密参数 请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 无c…

React 18 选择 State 结构

参考文章 选择 State 结构 构建良好的 state 可以让组件变得易于修改和调试&#xff0c;而不会经常出错。以下是在构建 state 时应该考虑的一些建议。 构建 state 的原则 当编写一个存有 state 的组件时&#xff0c;需要选择使用多少个 state 变量以及它们都是怎样的数据格…

网关认证的技术方案

我们认证授权使用springsecurity 和oauth2技术尽心实现具体实现流程见第五章文档&#xff0c;这里就是记录一下我们的技术方案 这是最开始的技术方案&#xff0c;我们通过认证为服务获取令牌然后使用令牌访问微服务&#xff0c;微服务解析令牌即可。但是缺点就是每个微服务都要…

TIA博途_更新或修改程序时,如何避免数据块中的参数丢失?

TIA博途_更新或修改程序时,如何避免数据块中的参数丢失? DB 快照功能 可以通过捕获 DB 块变量实际值快照用于恢复值操作,捕获的实际快照值可以复制到 CPU 中的实际值中,也可以用于替换变量的起始值。 通过快照能解决以下场景的问题: • 在 HMI 中设置了很多工艺参数,担心…

1.1 数据库系统简介

思维导图&#xff1a; 1.1.数据库系统简介 前言&#xff1a; 数据库系统是一个软件系统&#xff0c;用于管理和操作数据库。它提供了一个组织良好、高效并能够方便存取的数据存储机制&#xff0c;并且能够支持各种数据操作、事务管理、并发控制和恢复功能。以下是数据库系统的…

9个至少提升50%效率的VSCODE插件

在开始编码之前&#xff0c;您首先需要让您的工作流程适合您。让它更高效、更漂亮、更可定制。它会节省您大量的时间和精力&#xff0c;因此您将有更多的能力进行编码。 无论您是前端还是后端开发人员还是高级 Java 程序员&#xff0c;都没关系。这篇文章对你们大多数人来说仍然…

el-form的表单验证,有值却报红!

正确的写法是 el-form中的form用 :model绑定&#xff0c;各个输入项用 v-model绑定值 原因 显然&#xff0c;区别就是 v-model 和 :model的区别 V-mode v-model是一个语法糖&#xff0c;用于 “表单元素上” 实现双向数据绑定。包括数据绑定和事件监听。 <input v-model&q…

高职教育应对ChatGPT应用的策略

一、完善顶层设计&#xff0c;提升技术水平 在推广ChatGPT平台的过程中&#xff0c;高职院校需要关注技术本身的问题。这就需要在国家和地方政府的引导下&#xff0c;引入更完善的技术顶层设计&#xff0c;提高人工智能在高职教育中的运用水平。具体来说&#xff0c;一方面需要…

Maven之hibernate-validator 高版本问题

hibernate-validator 高版本问题 hibernate-validator 的高版本&#xff08;邮箱注解&#xff09;依赖于高版本的 el-api&#xff0c;tomcat 8 的 el-api 是 3.0&#xff0c;满足需要。但是 tomcat 7 的 el-api 只有 2.2&#xff0c;不满足其要求。 解决办法有 2 种&#xff…

copilot切换下一条提示的快捷键

注意是右ALT 右ALT] 触发提示 右ALT/ 参考 https://www.cnblogs.com/strengthen/p/17210356.html

Ceph入门到精通-大流量10GB/s支持OSPF(ECMP)-LVS 集群

Keepalived-LVS 能够提高集群的高可用性并增加后端检测功能、简化配置&#xff0c;满足常规需求。但Keepalived-LVS集群中&#xff0c;同一个VIP只能由一台设备进行宣告&#xff0c;为一主多备的架构&#xff0c;不能横向拓展集群的性能&#xff0c;为此我们引入OSPF来解决该问…

引入本地 jar 包教程

将本地 jar 包&#xff0c;放到 resource 目录下&#xff0c;在 pom.xml 文件中加入如下依赖&#xff1a; <dependency><groupId>com.hk</groupId><artifactId>examples</artifactId><version>1.0</version><scope>system<…

lenovo联想笔记本小新Air-15 2021款Intel版ITL版(82GM)原装出厂Win10系统

自带所有驱动、出厂主题壁纸LOGO、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;16G或以上的U盘 文件格式&#xff1a;ISO 文件大小&#xff1a;11.2GB 链接&#xff1a;https://pan.baidu.com/s/12NTMOt5eUjOIsbayXPyiww?pwdrs4v 提取码&#xf…

证明arcsinx+arccosx=π/2,并且为什么arcsinx-arccosx=π/2不成立

下面我们先直接用代数式来证明一下&#xff1a; 设 y 1 arcsin ⁡ x &#xff0c; y 2 arccos ⁡ x &#xff0c;求 y 1 y 2 由于 x sin ⁡ y 1 cos ⁡ y 2 &#xff0c;而 cos ⁡ y 2 sin ⁡ ( y 2 π 2 ) 那么就得到 y 1 y 2 π 2 &#xff0c;即 y 1 − y 2 π 2 …

Lua基础知识

文章目录 1. Lua简介1.1 设计目的&#xff1a;1.2 特性1.3 应用场景 2. Lua脚本学习2.1 安装2.2 lua操作2.3 lua案例 学习lua主要是为了后续做高性能缓存架构所准备的基础技术。可以先了解下基础&#xff0c;在实际使用时&#xff0c;再查缺补漏。 1. Lua简介 Lua 是一种轻量小…

【UI 设计】触摸界面设计

触摸界面设计是一种以触摸操作为主的用户界面设计。以下是一些触摸界面设计的要点&#xff1a; 界面布局&#xff1a;设计简洁、直观的界面布局&#xff0c;使用户可以快速找到所需的功能和信息。避免过于拥挤的布局&#xff0c;保持按钮和菜单的大小适中&#xff0c;以便用户能…

Spearman Footrule距离

Spearman Footrule距离是一种用于衡量两个排列之间差异的指标。它衡量了将一个排列变换为另一个排列所需的操作步骤&#xff0c;其中每个操作步骤都是交换相邻元素。具体而言&#xff0c;Spearman Footrule距离是每个元素在两个排列中的排名差的绝对值之和。 这个指标的名字中…

[Unity]VSCode无代码提示

查看vscode的output窗口&#xff0c;提示加载 csproj 文件失败 It need be converted to new SDK style to work in C# Dev Kit. 把unity的PackageManager里的 Visual Studio Editor 版本升级到2.0.20或以上&#xff0c;重新生成csproj文件就好了。 参考vscode官方&#xff…

前端面试的计算机网络部分(4)每天10个小知识点

目录 系列文章目录前端面试的计算机网络部分&#xff08;1&#xff09;每天10个小知识点前端面试的计算机网络部分&#xff08;2&#xff09;每天10个小知识点前端面试的计算机网络部分&#xff08;3&#xff09;每天10个小知识点 知识点31. **IPv4 和 IPv6的区别**32. 在前端开…

删除命名空间一直处于Terminating

删除命名空间一直处于Terminating 通常删除命名空间或者其他资源一直处于Terminating状态&#xff0c;是由于资源调度到的节点处于NotReady状态&#xff0c;需要将节点重新加入到集群使其状态变为Ready状态才能解决问题&#xff0c;当node重新加入处于Ready状态后&#xff0c;…