范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

神经网络变得轻松(第五部分)OpenCL中的多线程计算

  内容概述 1. MQL5 中如何组织多线程计算 2. 神经网络中的多线程计算 3. 利用 OpenCL 实现多线程计算 3.1. 前馈内核 3.2. 反向传播内核 3.3. 更新权重 3.4. 创建主程序的类 3.5. 创建基础神经元类来操控 OpenCL 3.6. CNet 类中的附加 4. 测试 结束语 链接 本文中用到的程序
  概述
  在之前的文章中,我们讨论过某些类型的神经网络实现。 如您所见,神经网络由大量相同类型的神经元组成,并在其中执行相同的操作。 然而,网络拥有的神经元越多,它消耗的计算资源也就越多。 结果就是,训练神经网络所需的时间呈指数增长,这是因为在隐藏层添加一个神经元,需要了解上一层和下一层中所有神经元的连接。 有一种减少神经网络训练时间的方法。 现代计算机的多线程功能可以同时计算多个神经元。 由于线程数量的增加,时间将可预见地大大减少。
  1. MQL5 中如何组织多线程计算
  MetaTrader 5 终端具有多线程体系架构。 终端中的线程分布受到严格控制。 根据文档,脚本和智能交易系统是在单独的线程中启动。 至于指示器,每个品种会提供单独的线程。 即时报价处理和历史记录同步于指标所在线程中执行。 这意味着终端只为每个智能交易系统分配一个线程。 某些计算可以在指标中执行,其可提供一个额外的线程。 然而,指标中过多的计算会减慢与即时报价数据处理相关的终端操作,这可能会导致针对市场状况的失控。 这种状况能对 EA 性能产生负面影响。
  不过,有一个解决方案。 MetaTrader 5 开发人员为其提供了利用第三方 DLL 的能力。 在多线程体系结构上创建动态库会自动为函数库中实现的操作提供多线程支持。 在此,EA 操作以及与函数库之间的数据交换依然保留在智能交易系统的主线程之中。
  第二个选项是利用 OpenCL 技术。 在这种情况下,我们可以用标准方法在支持该技术的处理器和视频卡上规划多线程计算。 对于此选项,程序代码不依赖所使用的设备。 该站点上有许多与 OpenCL 技术有关的出版物。 特别是,该主题在 [第五篇] 和 [第六篇] 文章里已有很好介绍。
  因此,我决定使用 OpenCL。 首先,运用该技术时,用户不需要额外配置终端,并为第三方 DLL 设置权限。 其次,这样的智能交易系统可通过一个 EX5 文件在终端之间传送。 这允许将计算部分转移到视频卡,因视频卡通常在终端操作期间处于空闲状态。
  2. 神经网络中的多线程计算
  我们已选择了该技术。 现在,我们需要决定将计算部分拆分为线程的过程。 您还记得完全连接感知器算法吗? 信号顺序从输入层转至隐藏层,然后转至输出层。 没必要为每个层分配线程,因为计算必须按顺序执行。 直到收到来自上一层的结果之后,该层才能开始计算。 一层中独立神经元的计算不依赖该层中其他神经元的计算结果。 这意味着我们可为每个神经元分配单独的线程,并发送一整层的所有神经元进行并行计算。
  深入到一个神经元的运算,我们可以研究把计算输入值与权重系数的乘积并行化的可能性。 不过,结果值的进一步求和,以及计算激活函数的数值被合并到一个线程当中。 我决定利用 vector 函数在单个 OpenCL 内核中实现这些操作。
  类似的方法也用来拆分反馈线程。 其实现如下所示。 3. 利用 OpenCL 实现多线程计算
  选择了基本方法后,我们就能够继续实现了。 我们从创建内核(可执行的OpenCL函数)开始。 根据以上逻辑,我们将创建 4 个内核。 3.1. 前馈内核。
  与之前文章中讨论的方法类似,我们创建一个前馈推算内核  FeedForward  。
  不要忘记内核是在每个线程中运行的函数。 调用内核时需设置此类线程的数量。 在内核内部的操作是特定循环内的嵌套操作;循环的迭代次数等于被调用线程的次数。如此,在前馈内核中,我们可以指定计算独立神经元状态的操作,并可从主程序调用内核时以指定神经元数量。
  内核从参数中接收权重矩阵,输入数据数组和输出数据数组的引用,以及输入数组的元素数量,和激活函数类型。 请注意,OpenCL 中的所有数组都是一维的。 因此,如果在 MQL5 中将二维数组用做权重系数,则此处我们需要计算初始位置的位移,以便读取第二个、及后续神经元的数据。 __kernel void FeedForward(__global double *matrix_w,                               __global double *matrix_i,                               __global double *matrix_o,                               int inputs, int activation)
  在内核的开头,我们获得线程的序列号,其可判定所计算神经元的序列号。 声明私密(内部)变量,包括向量变量  inp  和  weight 。 还要定义我们的神经元权重的位移。   {    int i=get_global_id(0);    double sum=0.0;    double4 inp, weight;    int shift=(inputs+1)*i;
  接下来,组织一个循环来获取输入值与其权重的乘积的合计。 如上所述,我们用到 4 个元素  inp  和  weight  的向量来计算乘积合计。 然而,内核接收的所有数组并非都是 4 的倍数,因此缺少的元素应替换为零值。 注意输入数据向量中的一个 "1" - 它对应于贝叶斯偏差的权重。    for(int k=0; k<=inputs; k=k+4)      {       switch(inputs-k)         {          case 0:            inp=(double4)(1,0,0,0);            weight=(double4)(matrix_w[shift+k],0,0,0);            break;          case 1:            inp=(double4)(matrix_i[k],1,0,0);            weight=(double4)(matrix_w[shift+k],matrix_w[shift+k+1],0,0);            break;          case 2:            inp=(double4)(matrix_i[k],matrix_i[k+1],1,0);            weight=(double4)(matrix_w[shift+k],matrix_w[shift+k+1],matrix_w[shift+k+2],0);            break;          case 3:            inp=(double4)(matrix_i[k],matrix_i[k+1],matrix_i[k+2],1);            weight=(double4)(matrix_w[shift+k],matrix_w[shift+k+1],matrix_w[shift+k+2],matrix_w[shift+k+3]);            break;          default:            inp=(double4)(matrix_i[k],matrix_i[k+1],matrix_i[k+2],matrix_i[k+3]);            weight=(double4)(matrix_w[shift+k],matrix_w[shift+k+1],matrix_w[shift+k+2],matrix_w[shift+k+3]);            break;         }       sum+=dot(inp,weight);      }
  获得乘积之和后,计算激活函数,并将结果写入输出数据数组。    switch(activation)      {       case 0:         sum=tanh(sum);         break;       case 1:         sum=pow((1+exp(-sum)),-1);         break;      }    matrix_o[i]=sum;   } 3.2. 反向传播内核。
  为反向传播误差梯度创建两个内核。 在第一个   CaclOutputGradient  中计算输出层误差。 它的逻辑很简单。 所获参考值在激活函数的数值范围进行常规化。 然后,将参考值和实际值之间的差乘以激活函数的导数。 将结果值写入梯度数组的相应单元格中。 __kernel void CaclOutputGradient(__global double *matrix_t,                                  __global double *matrix_o,                                  __global double *matrix_ig,                                  int activation)   {    int i=get_global_id(0);    double temp=0;    double out=matrix_o[i];    switch(activation)      {       case 0:         temp=clamp(matrix_t[i],-1.0,1.0)-out;         temp=temp*(1+out)*(1-(out==1 ? 0.99 : out));         break;       case 1:         temp=clamp(matrix_t[i],0.0,1.0)-out;         temp=temp*(out==0 ? 0.01 : out)*(1-(out==1 ? 0.99 : out));         break;      }    matrix_ig[i]=temp;   }
  在第二个内核中,在  CaclHiddenGradient  里计算隐藏层神经元的误差梯度。 内核构建类似于上述的前馈内核。 它还用到了向量运算。 区别在于前馈推算中以下一层的梯度向量替代前一层的输出值,并采用不同的权重矩阵。 而且,代替计算激活函数,结果合计是与激活函数导数的乘积。 内核代码给出如下。 __kernel void CaclHiddenGradient(__global double *matrix_w,                               __global double *matrix_g,                               __global double *matrix_o,                               __global double *matrix_ig,                               int outputs, int activation)   {    int i=get_global_id(0);    double sum=0;    double out=matrix_o[i];    double4 grad, weight;    int shift=(outputs+1)*i;    for(int k=0;k0)      {       if(CheckPointer(Weights)==POINTER_INVALID)         {          Weights=new CBufferDouble();          if(CheckPointer(Weights)==POINTER_INVALID)             return false;         }       int count=(int)((numNeurons+1)*numOutputs);       if(!Weights.Reserve(count))          return false;       for(int i=0;i1 ? 1 : target<-1 ? -1 : target)-result[n];       error+=delta*delta;      }    error/= total;    error = sqrt(error);    recentAverageError+=(error-recentAverageError)/recentAverageSmoothingFactor;     if(!neuron.calcOutputGradients(targetVals))       return;; //--- Calc Hidden Gradients    CObject *temp=NULL;    total=layers.Total();    for(int layerNum=total-2; layerNum>0; layerNum--)      {       CLayer *nextLayer=currentLayer;       currentLayer=layers.At(layerNum);       neuron=currentLayer.At(0);       neuron.calcHiddenGradients(nextLayer.At(0));      } //---    CLayer *prevLayer=layers.At(total-1);    for(int layerNum=total-1; layerNum>0; layerNum--)      {       currentLayer=prevLayer;       prevLayer=layers.At(layerNum-1);       neuron=currentLayer.At(0);       neuron.updateInputWeights(prevLayer.At(0));      }   }
  针对 getResult 方法略微进行了一些修改。    if(CheckPointer(opencl)!=POINTER_INVALID && output.At(0).Type()==defNeuronBaseOCL)      {       CNeuronBaseOCL *temp=output.At(0);       temp.getOutputVal(resultVals);       return;      }
  附件中提供了所有方法和函数的完整代码。 4. 测试
  采用与之前测试相同的条件,测试所创建类的操作。 已创建 Fractal_OCL EA 用于测试,它与先前创建的 Fractal_2 完全相同。 在 H1 时间帧,EURUSD 货币对上测试了神经网络的训练。 将 20 根烛条的数据输入到神经网络。 训练时采用最近两年的数据。 实验在支持 OpenCL 的 "Intel(R) Core(TM)2 Duo CPU T5750 @ 2.00GHz" 设备上运行。
  在 5 小时 27 分钟的测试中,利用 OpenCL 技术的 EA 共执行了 75 个训练时期。 对于 12405 根烛条的区间,这平均需要 4 分 22 秒。 未利用 OpenCL 技术的同一智能交易系统,在同一台笔记本电脑上的相同神经网络体系结构下,每个时期平均要花费 40 分钟 48 秒。 如此,利用 OpenCL 可以令学习过程快 9.35 倍。
  结束语
  本文演示了利用 OpenCL 技术在神经网络中规划多线程计算的可能性。 测试表明,在同一 CPU 上,性能几乎提高了 10 倍。 期望利用 GPU 进一步提高算法性能 - 在这种情况下,将计算转移到兼容的 GPU 不需要修改智能交易系统代码。
  总体而言,结果证明该方向的进一步发展具有良好的前景。
  链接神经网络变得轻松 神经网络变得轻松(第二部分):网络训练和测试 神经网络变得轻松(第三部分):卷积网络 神经网络变得轻松(第四部分):循环网络 OpenCL: 通往并行世界的桥梁 OpenCL: 从初学到精通编程
  本文中用到的程序
  #
  名称
  类型
  说明
  1
  Fractal_OCL.mq5   智能交易系统   利用 OpenCL 技术的含有分类神经网络(输出层中有 3 个神经元)的智能交易系统   2
  NeuroNet.mqh   类库   用于创建神经网络的类库   3
  NeuroNet.cl   代码库   OpenCL 程序代码库

持续擦亮中国盐帮菜之乡品牌自贡盐帮菜品牌宣传推广活动在成都举行自贡网记者欧亚非3月21日至26日,自贡盐帮菜品牌宣传推广活动在成都举行。现场,20余种盐帮特色菜品开展品鉴30余种盐帮特色美食展示展销,自贡冷吃兔富顺豆花荣州麻辣鸡等自贡美味受到探秘禁区克格勃埃及考古绝密档案(中)寻找外星知识密室导读上篇提到冷战时期,执行伊西斯计划的苏联克格勃不仅在埃及吉萨高原发现了外星宇航员木乃伊的坟墓,而且通过探地雷达扫描,还发现坟墓下方有一个巨大的球形房间。克格勃认为他们发现了传说中莫斯科金环小镇弗拉基米尔与古罗斯是什么关系?弗拉基米尔古镇弗拉基米尔小镇位于莫斯科东南190公里,是一个比莫斯科还古老的城市。它是弗拉基米尔大公国的发源地,这个大公国曾经是古俄罗斯最大的政治经济和文化中心,而当时的莫斯科才只西方嘲笑中国厕所脏,我们却领先3000年,带你揭秘中国厕所进化史西方人总是嘲笑我们没有马桶,厕所臭得要命,进去不到1分钟,就恶心得干呕冲了出来!以至于很长时间我们国家的厕所,沦为了西方各国不怀好意的笑柄。但,事实真的如此吗?根据历史文献记载,我张学良与宋美龄的关系你要是杀了他,我也马上离开退守台湾之前,蒋介石特意派出特务将杨虎城暗杀,对于同样发动了西安事变的张学良,蒋介石虽然恨之入骨,但却没法对他下毒手,只因为宋美龄说了一句话。一听说宋美龄马陵之战孙膑佯装败退,庞涓命丧马陵战国时期,魏国会同赵国联合攻打韩国,韩国经过几番顽强拼死抵抗后,仍然抵挡不住魏国强势的进攻。此时的韩国只有向齐国告急求救。齐国在经过激烈的争辩分析利弊之后,最终决定出兵救助韩国。于沽名钓誉,欺世盗名,渣男这个词竟然限制住了他的发挥(下)戊戌变法因损害到慈禧太后为首的守旧派利益,所以遭到强烈的抵制与反对。而慈禧太后等人发动戊戌政变后,光绪帝被囚禁至中南海瀛台,戊戌变法宣告失败。变法失败后,康有为自知将大祸临头。这时全票通过!美涉华法案要将中国藏南划给印度,莫迪有胆要吗?文占鳌华夏必将崛起,终将独占鳌头!君可愿,青灯笑语,又何忆,昨日当年!公元1858年,大英帝国于印度之地创建殖民地,开始执掌殖民权柄长达一百九十载!时值公元1914年,英方使臣亨利招远第1个共产党员第1任县长,曾任徐州市长,1950年被暗杀招远第一个共产党员叫王德安。王德安是招远留仙庄村人,出生于1908年。招远红色革命早期,有一所著名的学校叫道头4小。道头这个地方,在抗日战争和解放战争中都很有名。抗战时期,八路军在史上著名的九大馊主意,一个比一个馊,至今依旧遗臭万年头条创作挑战赛历史上最著名的九个坏主意是什么,一个比一个糟糕。纵观历史,每一个成功的人,无论是皇帝,还是家族,几乎都离不开智库的帮助。齐桓公让管仲在春秋当权,刘备三顾茅庐,诸葛亮帮蔡英文窜访前遭打击,第九个断交国来了,一段私怨浮出了水面当蔡英文正欣欣然地准备窜访之际,早已预订的晴天霹雳当空炸响。当地时间3月25日,洪都拉斯外交部发表声明表示,世界上只有一个中国,中华人民共和国是代表中国的唯一合法政府,台湾是中国领
医生胃炎,其实并不简单出现以下症状需要警惕了上午门诊来了一位我的老患者,40岁的廖先生,在一个月前总出现无明显诱因胸骨后疼痛还有灼痛感上腹部胀痛,饭后饱胀还伴有反酸嗳气口干口苦等症状,而且纳差。当时找我来检查,我给廖先生安排塞方坚决不反俄,乌议员放话报复塞民众,武契奇祝你们一切顺利自俄乌冲突爆发以来,作为始作俑者,以美国为首的西方国家不断煽风点火,从始至终拉偏架,霸道蛮横地逼迫他国在俄乌之间选边站队。不过,面对西方的再三逼迫,塞尔维亚始终不屈服,坚持自己的立美拉中印限制油价,普京布局出其不意,国际石油市场或重新洗牌在美国找中国帮忙,妄图对俄油限价之后,俄罗斯另辟蹊径。据环球时报报道,美媒援引一份俄方文件称,俄罗斯政府计划明年建立国家石油基准,与西方争夺石油定价权。据悉,今年10月,俄政府部门历史上根本就不存在的6位猛将,均是虚构人物,别再被电视剧骗了正所谓将军角弓不得控,都护铁衣冷犹著,在古代任何一个国家的安定,都离开将士们的付出。他们抛头颅洒热血,为保护国家与百姓献上了自己的生命,我国历史上更是有非常多赫赫有名的大将军。例如支付宝新功能上线,网友喊话微信跟进最近有些朋友没少被钱的事情困扰,特别是村镇银行这事。当然,今天黑马不是来说这事的,银保监会也已经有了相应的措施,这事也算是告了一段落。今天谈谈支付宝,也不是什么大事,就更新了一个功女网红王澄澄炫富引争议,高调晒公职父亲照片,被质疑后注销账号其实是因为她爸爸,她才有了这一切。随着沈阳方面的及时通报,有关王澄澄利用直升机展示视频的一些问题已经得到澄清。然而,一些深层次的问题并没有向公众解释。比如15年前你是什么样的家庭背幸福到万家秀玉被顶替上大学,4人反应大不同,让我看透人性幸福到万家自播出之后,剧里面的矛盾层出不穷,每一个矛盾都让人十分恼火。在最新的剧情中,万传美顶替王秀玉上大学一事被揭穿,本来被偷走人生已经足够难过了。王家人还在秀玉最难受的时候,火高考成绩下发才懂,常补课和不补课的差距,不是一星半点文萌妈教育日记补课不是万能的,但是不补课是万万不能的。这是很多家长的下意识,在很多家长的眼中,高中的知识比较广泛,难度也比较大。高中老师的精力有限,也不可能照顾到班级里面所有的同学大众新探岳实车现身,外观变时尚,比现款更香了?相对于途观L,探岳晚来了许多年,不过得益于大众品牌的先天优势,探岳也算是站在巨人的肩膀上,推出之后销量也是节节高升,最高的时候与途观L一样可以突破到两万以上。算起来,现款探岳上市已天热喝啤酒,不管有钱没钱,这4种啤酒要少喝,没酒味还上头如果你也喜欢美食,点击关注,每天不断更新精彩内容!导语天热喝啤酒,不管有钱没钱,这4种啤酒要少喝,没酒味还上头!一说到啤酒,相信大家在日常生活中还是非常常见的,尤其是现在到了夏季的晚年搭伙过日子,女人到底该不该出钱?3位退休女人各有想法导语单身的老人,越是到了晚年,就越是害怕孤独,所以大家可以看到,退休后的老人搭伙过日子的最多,因为这个时候子女也都组建家庭,自己也光荣退休,终于有时间可以考虑自己的幸福了,但同时,