请问一亩三分地移民长是18米宽应该是多少米

菜鸡的啄米日常菜鸡的啄米日常别拿菜鸡不当鸡!(啥?)关注专栏更多最新文章{&debug&:false,&apiRoot&:&&,&paySDK&:&https:\u002F\u002Fpay.zhihu.com\u002Fapi\u002Fjs&,&wechatConfigAPI&:&\u002Fapi\u002Fwechat\u002Fjssdkconfig&,&name&:&production&,&instance&:&column&,&tokens&:{&X-XSRF-TOKEN&:null,&X-UDID&:null,&Authorization&:&oauth c3cef7c66aa9e6a1e3160e20&}}{&database&:{&Post&:{&&:{&title&:&【消息】:keras中文文档发布&,&author&:&bigmoyan&,&content&:&\u003Cp\u003E
作为基于python的最流行的深度学习框架,keras以其快速上手,支持theano\u002Ftensorflow无缝切换,文档丰富等若干优点广受好评……额好吧总体来说它还是比较小众就是了。虽然也有很多缺点,但做快速原型真是不错。\u003C\u002Fp\u003E
经过一周左右艰苦的【并没有】的工作,本鸡完成了目前keras中文文档翻译,目前的一个初稿放在这里:\u003Ca href=\&http:\u002F\u002Fkeras-cn.readthedocs.io\u002F\& data-title=\&keras中文文档\& class=\&\& data-editable=\&true\&\u003Ekeras中文文档\u003C\u002Fa\u003E\u003Cp\u003E
本鸡毕竟还是一只菜鸡,水平有限的很,许多部分,尤其是递归神经网络和涉及到自然语言处理的部分,可能有一些错漏之处。但好歹架子是搭起来了,如果各位觉得哪里做的不好,或者有哪里可以补充,欢迎知乎私信我。所有的contribution都会被记录在网站的“致谢”一栏里。文档中我也挖了一些“此处应有XXX”的坑,如果你乐意填坑,我也是欢迎之至。\u003C\u002Fp\u003E\u003Cp\u003E
同时,也很真诚的欢迎大家提供更多的keras使用实例、代码解释以及使用的tips。目前的文档在我看来还比较粗糙,安装本鸡的计划,这个文档的下一步工作要添加更友好的使用建议,澄清相关的深度学习概念。这件事只靠本菜鸡是完不成的,还需要各位鼎立相助~让我们建设一个更NB的keras文档吧!\u003C\u002Fp\u003E\u003Cp\u003E***\u003C\u002Fp\u003E\u003Cp\u003E
好了,闲事说完了,说正事。\u003C\u002Fp\u003E\u003Cp\u003E
一时兴起申了个专栏……其实也没有完全想好要写什么进去\u003C\u002Fp\u003E\u003Cp\u003E
唉好烦啊,实在没有什么可写的话,写成技术博客好了。本鸡虽然水平有限,但讲东西还是有一套的。同学有云,“我们是学了10分的东西,能讲出7分就不错了。你是学了5分的东西,却能讲出10分”。\u003C\u002Fp\u003E\u003Cp\u003E
(●?▽`●)\u003C\u002Fp\u003E\u003Cp\u003E
同学你憋走,大家一样的鸡为毛你学的是10分老子就只学到5分啊\u003C\u002Fp\u003E\u003Cp\u003E
唉,总而言之呢,这个技术博客的内容不会很深,很多大概也是老生常谈的东西吧,毕竟太高深的东西我也不会讲,我讲了你也不懂,你懂……别人也不懂。反正放上来的东西我保证你都能看明白就是了。\u003C\u002Fp\u003E\u003Cp\u003E
看不明白也憋说出来,悄悄私信我,我再改。\u003C\u002Fp\u003E\u003Cp\u003E
就酱,玩的愉快。\u003C\u002Fp\u003E\u003Cp\u003E
污码撸~~\u003C\u002Fp\u003E&,&updated&:new Date(&T06:28:09.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:20,&likeCount&:67,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T14:28:09+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic1.zhimg.com\u002Fef04debc471_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:20,&likesCount&:67},&&:{&title&:&【啄米日常】1:使用keras搭建残差网络&,&author&:&bigmoyan&,&content&:&\u003Cp\u003E本文是之前文章的精修版,由于CSDN博客被黑掉了,好不容易才把原来的文章找回来,所以决定以后就更知乎了,于是把完整版的残差网络转换和搭建过程放在这里。 \u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E——————————\u003C\u002Fp\u003E\u003Cp\u003E目录:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E残差网络简介 \u003C\u002Fli\u003E\u003Cli\u003EKeras搭建残差网络(ResNet50)\u003C\u002Fli\u003E\u003Cli\u003E载入ResNet50权重\u003C\u002Fli\u003E\u003Cli\u003E测试网络\u003C\u002Fli\u003E\u003Cli\u003E如果你不想看这么长的文章直接跳到这里\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E ——————————\u003C\u002Fp\u003E\u003Ch2\u003E残差网络简介\u003C\u002Fh2\u003E\u003Cp\u003EMSRA的Kaiming He的残差网络绝对要算的上是2015年\u003Ca href=\&http:\u002F\u002Flib.csdn.net\u002Fbase\u002Fdeeplearning\& class=\&\& data-editable=\&true\& data-title=\&深度学习\&\u003E深度学习\u003C\u002Fa\u003E的大新闻了,深度残差网络将深度网络的层深拓展到一个不可思议的程度(152层),并在ImageNet竞赛中以绝对优势取得多个项目的冠军。后来,Kaiming\n He大神后来又把这一深度加到1000层的深度,能训练这么深的网络,残差网络的优势可见一斑。Kaiming He也因为这项工作获得了2016 \nCVPR的best paper award,这也是他获得的第二个CVPR best paper,顺便,据知乎爆料,Kaiming \nHe还是广东省某年的高考状元哟! 果然神的成长路径我们只能仰望……\u003C\u002Fp\u003E\u003Cp\u003E这篇文章的主要目的是提供一个Keras的深度残差网络的实现,理论上的分析大家尽可以去搜索别的文章,我们这里快速的过一下。\u003C\u002Fp\u003E\u003Cp\u003E训练深度网络是一件很头疼的事情,尽管已经有了ReLU等一系列手段加深网络,但梯度爆炸\u002F弥散的问题并没有真正的解决。对深度学习而言,深度是比较值钱的,深度这个特性带来的性能提升也是最可观的。由于训练不到位,单纯的把layer叠起来深层网络的效果反而不如合适层数的较浅的网络效果好,这种情况称为网络退化。\u003C\u002Fp\u003E\u003Cp\u003EResNet(深度残差网络)的出现为训练更深的网络提供了方法,深度残差网络其实是Highway网络的一种特殊情况,其主要特色是跨层连接。作者的一个假设是,相比于直接拟某个函数合y=f(x),试图去拟合g(x)=y-f(x),也就是残差,可能会更加容易,虽然为什么这样我也说不清楚,但效果看起来是不错。基本上,拟合残差也就是把网络按照下图的跨层连接搭起来:\u003C\u002Fp\u003E\u003Cimg src=\&v2-358f29d5d8fab3ed6bea88ca.png\& data-rawwidth=\&453\& data-rawheight=\&269\&\u003E\u003Cbr\u003E\u003Cp\u003E网络试图将x映射为F(x)+x,那么网络的映射F(x)自然就趋向于F(x)=0。\u003C\u002Fp\u003E\u003Cp\u003Ekaiming he的github是\u003Ca href=\&https:\u002F\u002Fgithub.com\u002FKaimingHe\& class=\&\& data-editable=\&true\& data-title=\&KaimingHe (Kaiming He)\&\u003EKaimingHe (Kaiming He)\u003C\u002Fa\u003E\u003Cbr\u003E\n里面有残差网络的caffe实现和相应权重,使用caffe的同学可以直接下载。\u003C\u002Fp\u003E\u003Cp\u003E使用Keras的同学,就接着往下看了。\u003C\u002Fp\u003E\u003Ch2\u003EKeras搭建残差网络(ResNet50)\u003C\u002Fh2\u003E\u003Cp\u003EKeras便于搭建网络的特点使得搭建网络大部分情况是一种“照猫画虎”的便捷工作,很开心kaiming \nhe的github上提供了残差网络的可视化结构,如果你有双屏,完全可以一屏看图一屏搭结构,爽的不要不要的。我们选择kaiming \nhe提供的最浅的50层残差网络进行搭建,它的图在这里:\u003Ca href=\&http:\u002F\u002Fethereon.github.io\u002Fnetscope\u002F#\u002Fgist\u002Fdb945b393d40bfa26006\& data-editable=\&true\& data-title=\&Res50\&\u003ERes50\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E\n\n观察一下网络图,首先跨层连接,肯定要使用泛型模型Model,整个网络有两种重复单元,一种是主path和shortcut都有卷积层,这种单元长这样\u003Cimg src=\&v2-ab8ddd6f4efd3c72de6d.png\& data-rawwidth=\&321\& data-rawheight=\&588\&\u003E另一种主path有卷积,但是shortcut没有卷积,是直接连过来的。主路上的卷积核大小选3 × 3,再加一圈padding,就保证输入输出的大小是一样了。这个模块长这样:\u003C\u002Fp\u003E\u003Cimg src=\&v2-7b95380d6bec5e74afd724.png\& data-rawwidth=\&268\& data-rawheight=\&621\&\u003E\u003Cp\u003E基本上,残差网络就是这两种模块反复交替。\u003C\u002Fp\u003E\u003Cp\u003E为什么会有这样呢,因为残差网络的关键步骤,跨层的合并需要保证x和F(x)的shape是完全一样的,否则它们加不起来。\u003C\u002Fp\u003E\u003Cp\u003E理解了这一点,我们开始用keras做实现,我们把输入输出大小相同的模块称为identity_block,而把输出比输入小的模块称为conv_block,首先,导入所需的模块:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Efrom keras.layers import merge\nfrom keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D, AveragePooling2D\nfrom keras.layers.core import Dense, Activation,Flatten\nfrom keras.layers.normalization import BatchNormalization\nfrom keras.models import Model\nfrom keras.layers import Input\n\u003C\u002Fcode\u003E\u003Cp\u003E这些层的位置还是比较好记的,注意BatchNormalization是在normalization这个模块里,这个模块目前就这一个层。另外注意\u003Cb\u003E我们导入的是函数merge而不是层Merge\u003C\u002Fb\u003E,这两个是不一样的,在构造泛型模型时要使用函数merge,因为泛型模型的整个构造过程就是对张量的操作。最后注意Input不是一个层,而是一个函数,函数的返回值是一个张量。\u003C\u002Fp\u003E\u003Cp\u003E主路的第一个和第三个卷积模块的核大小都是1,这点应该是借鉴的Inception网络,所以也有人说残差网络是Highway跟Inception的结合。\u003C\u002Fp\u003E\u003Cp\u003E然后我们先来编写identity_block,这是一个函数,接受一个张量为输入,并返回一个张量。为了使得输入的shape与经过主路的shape一样,我们可以在卷积前使用ZeroPadding2D进行padding,与其他深度学习框架不同,\u003Cb\u003EKeras的卷积层并不包含padding的选项,所以需要手动添加。\u003C\u002Fb\u003E另一种等价的方式是在卷积层中设置border_mode=’same’,则卷积层会自动保证输入输出具有相同的shape,代码如下:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef identity_block(x,nb_filter,kernel_size=3):\n
k1,k2,k3 = nb_filter\n
out = Convolution2D(k1,1,1)(x)\n
out = BatchNormalization()(out)\n
out = Activation('relu')(out)\n\n
out = Convolution2D(k2,kernel_size,kernel_size,border_mode='same')(out)\n
out = BatchNormalization()(out)\n
out = Activation('relu')(out)\n\n
out = Convolution2D(k3,1,1)(out)\n
out = BatchNormalization()(out)\n\n\n
out = merge([out,x],mode='sum')\n
out = Activation('relu')(out)\n
return out\u003C\u002Fcode\u003E\u003Cp\u003Ek1,k2,k3分别是三个卷积层的核大小,注意caffe中的BN层分两步完成,所以结构图里除了BN还有一个scale,Keras里BN就是BN。小心主路的第三个卷积模块是没有relu的, relu在merge以后。\u003Cbr\u003E\u003Cbr\u003E类似的我们写conv_block,注意为了使得两个张量可以相加,shortcut这条路的滤波器数目一定和主路最后一个卷积模块的滤波器数目是一样的。但是仔细注意的话会发现,从第2个stage开始主路的第一个卷积层和shortcut的卷积都有个(2,2)的subsample,这个问题我们后面修,先看个大概。\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef conv_block(x,nb_filter,kernel_size=3):\n
k1,k2,k3 = nb_filter\n\n
out = Convolution2D(k1,1,1)(x)\n
out = BatchNormalization()(out)\n
out = Activation('relu')(out)\n\n
out = out = Convolution2D(k2,kernel_size,kernel_size)(out)\n
out = BatchNormalization()(out)\n
out = Activation('relu')(out)\n\n
out = Convolution2D(k3,1,1)(out)\n
out = BatchNormalization()(out)\n\n
x = Convolution2D(k3,1,1)(x)\n
x = BatchNormalization()(x)\n\n
out = merge([out,x],mode='sum')\n
out = Activation('relu')(out)\n
return out\n\u003C\u002Fcode\u003E\u003Cp\u003E 剩下的事情就很简单了,数好identity_block和conv_block是如何交错的,照着网络搭就好了:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Einp = Input(shape=(3,224,224))\nout = ZeroPadding2D((3,3))(inp)\nout = Convolution2D(64,7,7,subsample=(2,2))(out)\nout = BatchNormalization()(out)\nout = Activation('relu')(out)\nout = MaxPooling2D((3,3),strides=(2,2))(out)\n\nout = conv_block(out,[64,64,256])\nout = identity_block(out,[64,64,256])\nout = identity_block(out,[64,64,256])\n\nout = conv_block(out,[128,128,512])\nout = identity_block(out,[128,128,512])\nout = identity_block(out,[128,128,512])\nout = identity_block(out,[128,128,512])\n\nout = conv_block(out,[256,256,1024])\nout = identity_block(out,[256,256,1024])\nout = identity_block(out,[256,256,1024])\nout = identity_block(out,[256,256,1024])\nout = identity_block(out,[256,256,1024])\nout = identity_block(out,[256,256,1024])\n\nout = conv_block(out,[512,512,2048])\nout = identity_block(out,[512,512,2048])\nout = identity_block(out,[512,512,2048])\n\nout = AveragePooling2D((7,7))(out)\nout = Dense(1000,activation='softmax')(out)\n\nmodel = Model(inp,out)\u003C\u002Fcode\u003E\u003Cp\u003E这里需要注意的几个点是,MaxPooling2D按照网络图有一个长为2的stride,用strides参数指定,同样的功能在Convlution2D中则称为subsample,这个命名冲突非常的不友好,请多加注意。然后\u003Cb\u003E网络的最后一层前的Pool5是一个AveragePooling而不是MaxPooling\u003C\u002Fb\u003E。\u003C\u002Fp\u003E\u003Cp\u003E如果你是为了搭建一个残差网络然后自己训练,那差不多就可以了——哦记得把“从第3个stage开始主路和辅路的第一个卷积层都有(2,2)的subsample”修正一下(忘了其实也没关系),在函数里写个条件判断即可。编译之后,这个模型就可以训练了。\u003C\u002Fp\u003E\u003Cp\u003E如果在网络搭建完毕后,你希望能够使用kaiming he的预训练权重进行初始化,以便在其上进行transfer \nlearning或prediction,你就要继续往下看了。 kaiming \nhe提供的权重是caffe版本的,下面我们来讲如何把caffe的权重文件变为Keras广泛使用的,更加友好的h5文件。\u003C\u002Fp\u003E\u003Ch2\u003E载入ResNet50的权重\u003C\u002Fh2\u003E\u003Cp\u003E\u003Cb\u003E深呼吸,准备好,我们的故事从这里才刚刚开始\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E深呼吸,准备好,我们的故事从这里才刚刚开始\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E深呼吸,准备好,我们的故事从这里才刚刚开始\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E准备好了吗?好的让我们开始\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E第一个问题,权重格式转换\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E我们遇到的第一个问题是是如何将caffe的权重文件转换为.h5文件。这里有两个思路,第一个是利用mxnet提供的工具caffe_converter,安装mxnet后可以利用这个脚本将caffe的模型和权重转换为mxnet的.params文件,.params文件相对于.caffemodel文件就要友好的多,其本质就是一个字典,利用mxnet读入后保存为h5即可。\u003C\u002Fp\u003E\u003Cp\u003E第二个思路是利用caffe读取,这个方法应该说是更简单的——虽然配置caffe要麻烦一些。首先读入模型,然后依次将网络的params参数取出来存入h5文件中,脚本如下:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Eimport caffe\nimport numpy as np\nimport h5py\n\nmodel_file = 'ResNet-50-deploy.prototxt'\npretrained_model = 'ResNet-50-model.caffemodel'\nnet = caffe.Net(model_file,pretrained_model,caffe.TEST)\nf = h5py.File('resnet50.h5','w')\nfor name in net.params.keys():\n
if len(net.params[name])==1:\n
print \&%s has only one params\&%name\n
grp = f.create_group(name)\n
grp.create_dataset('weights',data=net.params[name][0].data)\n
grp.create_dataset('bias',data=np.zeros(shape=(net.params[name][0].data.shape[0],)))\n
grp = f.create_group(name)\n
grp.create_dataset('weights',data=net.params[name][0].data)\n
grp.create_dataset('bias',data=net.params[name][1].data)\nf.close()\u003C\u002Fcode\u003E\u003Cp\u003E运行完毕后,我们得到了Res50Net.h5这个权重文件。这里注意的是,ResNet50的所有res开头的卷积都是没有bias的!这点实在很坑,在最后载入权重的时候才发现。这里的策略是,对于没有bias的网络生成一个全0的bias,虽然稍微浪费一点空间,但对keras载入权重可是大大的方便。\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E第二个问题,名字配准\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E显然,我们做出来的h5文件不可能用model.load_weights()这种语句一条就载入,因为层的名字无法对应起来,打印一下h5文件的keys可以发现,得到caffe里各个层的名字与网络框图中画的是一样的(当然了),类似于res2a_branch2a,\n bn2b_branch2b这种,我们有两个选择,要么把h5文件的名字变的符合我们的网络,要么我们把网络的层起一个和模型一样的名字。\u003C\u002Fp\u003E\u003Cp\u003E显然,第二种方法可行性更高一点,因为原网络的命名也是有规律的,除去开头的几层和结尾的几层,剩下的层命名规则都很一致,形如:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003E[type][stage][block]_branch[branch][layer]\u003C\u002Fcode\u003E\u003Cp\u003E首先是层类型,type,一共三种:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003Eres:卷积层\u003C\u002Fli\u003E\u003Cli\u003Ebn:BN层\u003C\u002Fli\u003E\u003Cli\u003Escale:BN层\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E注意caffe的BN是分两层实现的,这点给权重转换带来了很大麻烦,我们稍后说。\u003C\u002Fp\u003E\u003Cp\u003Estage是大的阶段,每个阶段由一个conv_layer领起,在上面搭建网络的时候我们用空格将各个阶段代码分开,很容易分辨。\u003C\u002Fp\u003E\u003Cp\u003Eblock是在一个stage中当前层处于的块的位置,block就是我们实现的时候identity_block或conv_block之一,用abcd表示。\u003C\u002Fp\u003E\u003Cp\u003E然后一个下划线,branch后接branch的分支号,左分支(shortcut)是1,右分支(main path)是2,对于identity_block而言就只有2分支没有1分支。\u003C\u002Fp\u003E\u003Cp\u003E最后是该层在该分支的具体位置layer,用abcd表示。\u003C\u002Fp\u003E\u003Cp\u003E例如,res2a_branch2a表明这是一个卷积层,位于第二个stage的a(第一个)block中,是该block的右分支的第a层。\u003C\u002Fp\u003E\u003Cp\u003E此外,\u003Cb\u003E注意左分支只有一组网络,因此最后面没有abc的标注,直接就是res2a_branch1\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003E观察到这种规律后,我们可以在搭建网络时自动生成对应的名字,这样,我们的代码就要改一改了,需要接收一个标明stage和block号的参数。当然,没有参数的层,比如relu层,我们就不用取名字了。我们小心的为我们的block添加stage和block的关键字参数,为了篇幅不那么长,我们这里只示范一个:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef conv_block(input_tensor, nb_filter, stage, block, kernel_size=3):\n
conv_block indicate the block that has a conv layer at shortcut\n
input_tensor: input tensor\n
nb_filter: list of integers, the nb_filters of 3 conv layer at main path\n
stage: integet, current stage number\n
block: str like 'a','b'.., curretn block\n
kernel_size: defualt 3, the kernel size of middle conv layer at main path\n\n
Note that from stage 3, the first conv layer at main path is with subsample=(2,2)\n
And the shortcut should have subsample=(2,2) as well\n
nb_filter1, nb_filter2, nb_filter3 = nb_filter\n
if stage == 2:\n
out = Convolution2D(nb_filter1, 1, 1, name='res'+str(stage)+block+'_branch2a')(input_tensor)\n
out = Convolution2D(nb_filter1, 1, 1, subsample=(2, 2), name='res'+str(stage)+block+'_branch2a')(input_tensor)\n\n
out = BatchNormalization(axis=1, name='bn'+str(stage)+block+'_branch2a')(out)\n
out = Activation('relu')(out)\n\n
out = out = Convolution2D(nb_filter2, kernel_size, kernel_size, border_mode='same',\n
name='res'+str(stage)+block+'_branch2b')(out)\n
out = BatchNormalization(axis=1, name='bn'+str(stage)+block+'_branch2b')(out)\n
out = Activation('relu')(out)\n\n
out = Convolution2D(nb_filter3, 1, 1, name='res'+str(stage)+block+'_branch2c')(out)\n
out = BatchNormalization(axis=1, name='bn'+str(stage)+block+'_branch2c')(out)\n\n
if stage == 2:\n
shortcut = Convolution2D(nb_filter3, 1, 1, name='res'+str(stage)+block+'_branch1')(input_tensor)\n
shortcut = Convolution2D(nb_filter3, 1, 1, subsample=(2, 2), name='res'+str(stage)+block+'_branch1')(input_tensor)\n
shortcut = BatchNormalization(axis=1, name='bn'+str(stage)+block+'_branch1')(shortcut)\n\n
out = merge([out, shortcut], mode='sum')\n
out = Activation('relu')(out)\n
return out\u003C\u002Fcode\u003E\u003Cp\u003E建立网络的过程中,我们要在每个stage的block搭建中传入当前stage是第几个stage,以及当前的block是该stage的哪个block,这样在block内部,就可以自动生成对应的名字了。\u003Cb\u003E注意这里已经考虑了stage大于2的时候要在主路和从路的conv上增加一个(2,2)的subsample\u003C\u002Fb\u003E,下面给出全部的搭建网络代码, 请注意观察conv_block和identity_block是如何一组一组的搭建的。\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef get_resnet50():\n
this function return the resnet50 model\n
you should load pretrained weights if you want to use this model directly\n
Note that since the pretrained weights were converted from caffemodel\n
so the order of channels for input image should be 'BGR' (the channel order of caffe)\n
inp = Input(shape=(3, 224, 224))\n
out = ZeroPadding2D((3, 3))(inp)\n
out = Convolution2D(64, 7, 7, subsample=(2, 2), name='conv1')(out)\n
out = BatchNormalization(axis=1, name='bn_conv1')(out)\n
out = Activation('relu')(out)\n
out = MaxPooling2D((3, 3), strides=(2, 2))(out)\n\n
out = conv_block(out, [64, 64, 256], 2, 'a')\n
out = identity_block(out, [64, 64, 256], 2, 'b')\n
out = identity_block(out, [64, 64, 256], 2, 'c')\n\n
out = conv_block(out, [128, 128, 512], 3, 'a')\n
out = identity_block(out, [128, 128, 512], 3, 'b')\n
out = identity_block(out, [128, 128, 512], 3, 'c')\n
out = identity_block(out, [128, 128, 512], 3, 'd')\n\n
out = conv_block(out, [256, 256, 1024], 4, 'a')\n
out = identity_block(out, [256, 256, 1024], 4, 'b')\n
out = identity_block(out, [256, 256, 1024], 4, 'c')\n
out = identity_block(out, [256, 256, 1024], 4, 'd')\n
out = identity_block(out, [256, 256, 1024], 4, 'e')\n
out = identity_block(out, [256, 256, 1024], 4, 'f')\n\n
out = conv_block(out, [512, 512, 2048], 5, 'a')\n
out = identity_block(out, [512, 512, 2048], 5, 'b')\n
out = identity_block(out, [512, 512, 2048], 5, 'c')\n\n
out = AveragePooling2D((7, 7))(out)\n
out = Flatten()(out)\n
out = Dense(1000, activation='softmax', name='fc1000')(out)\n\n
model = Model(inp, out)\n\n
return model\u003C\u002Fcode\u003E\u003Cp\u003E这样,我们的网络就有和.h5一样的名字了,这为载入权重铺平道路。\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E将h5的权重载入到网络中\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E此时,运行model.load_weights()即可。\u003C\u002Fp\u003E\u003Cp\u003E开玩笑的,真这么简单我不好意思在本文末尾收钱了(是的本文末尾有个二维码,本文我自认为还有一定价值,可以收一波打赏,给多少看您心意)\u003C\u002Fp\u003E\u003Cp\u003E载入权重是这样的,首先打开h5文件,然后对于model中的层,如果层名以”res”开头,那么它就是个卷积层,由于我们在制作h5文件时已经很有先见之明(并没有,都是发现出错才改的)的把没有bias的情况设置了全0,这一步就调用set_weights载入h5文件的同名参数即可。Keras的set_weights方法接受一个列表作为权重,对卷积层和全连接层而言,这个列表长为2,第一项时权重W,第二项时偏执b。\u003C\u002Fp\u003E\u003Cp\u003E麻烦的是BN层。\u003C\u002Fp\u003E\u003Cp\u003E先复习一下BatchNormalization,这个技术可以说是深度学习的里程碑式的技术,BN基本上已经和Dropout一样,成为各种神经网络的标配了,甚至已经在很多场合取代了Dropout,比如本文的残差网络,就没有Dropout。\u003C\u002Fp\u003E\u003Cp\u003E这个技术的核心思想是在网络运算过程中,对每个batch,将层的计算结果重新规范化,然后再加以放缩以保持层学习到的特征性质。整个BN的技术分为两步,第一步是去均值,除标准差进行规范化,第二部是乘γ,加β,完成放缩。技术细节这里就不多讲了。\u003C\u002Fp\u003E\u003Cp\u003E坑爹的是,Caffe里这个过程是由两个层完成的,BN层只完成规范化,还有一个scale层完成放缩。而在keras中,BN就是BN,一层内完成两步。\u003C\u002Fp\u003E\u003Cp\u003E查看Keras的BatchNormalization的权重shape,可以看到BN层的权重shape是4,也就是说该权重是一个含有4个np\n array的list,而caffe的BN和scale权重的shape都是2,这是理所应当的,两个组合在一起, 就可以载入了。\u003C\u002Fp\u003E\u003Cp\u003E那么,拿一个list把caffe的BN和scale两层的权重装起来就可以了吗?不是的,如果这么简单,我就不好意思在末尾收钱了。\u003C\u002Fp\u003E\u003Cp\u003E这里有一明一暗两个陷阱\u003C\u002Fp\u003E\u003Cp\u003E先说暗的,BN层的四个参数分别是均值,标准差,γ,和β,拿一个list把他们装起来,谁先谁后呢?先规范化再scale,怎么看都该是按上面的顺序排列的,然而在keras的BatchNormalization源代码中,这个过程是这样的:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef __init__(self, epsilon=1e-6, mode=0, axis=-1, momentum=0.9,\n
weights=None, beta_init='zero', gamma_init='one', **kwargs):\n
self.beta_init = initializations.get(beta_init)\n
self.gamma_init = initializations.get(gamma_init)\n
self.epsilon = epsilon\n
self.mode = mode\n
self.axis = axis\n
self.momentum = momentum\n
self.initial_weights = weights #这里将传入的参数保存起来\n
if self.mode == 0:\n
self.uses_learning_phase = True\n
super(BatchNormalization, self).__init__(**kwargs)\n\ndef build(self, input_shape):\n
self.input_spec = [InputSpec(shape=input_shape)]\n
shape = (input_shape[self.axis],)\n\n
self.gamma = self.gamma_init(shape, name='{}_gamma'.format(self.name))\n
self.beta = self.beta_init(shape, name='{}_beta'.format(self.name))\n
self.trainable_weights = [self.gamma, self.beta]\n\n
self.running_mean = K.zeros(shape,\n
name='{}_running_mean'.format(self.name))\n
self.running_std = K.ones(shape,\n
name='{}_running_std'.format(self.name))\n
self.non_trainable_weights = [self.running_mean, self.running_std]\n\n
if self.initial_weights is not None:\n
self.set_weights(self.initial_weights)#如果给了外加参数,则调用set_weights设置参数\n
del self.initial_weights\u003C\u002Fcode\u003E\u003Cp\u003E 再跳入set_weights,看看它是如何设置参数的:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Eparams = self.trainable_weights + self.non_trainable_weights\u003C\u002Fcode\u003E\u003Cp\u003E注意了,set_weights是将训练的权重和非训练权重合并在一起的,可训练的在前,不可训练的在后,BN的可训练权重是γ和β,不可训练的权重是均值和标准差,因为这两个值实际上是根据样本计算出来的。\u003C\u002Fp\u003E\u003Cp\u003E所以添加的顺序应该是γ和β在前,均值和标准差在后。如果弄反了不会报任何错误,因为这四个参数的shape完全一致!所以这是一个暗陷阱……MB我都不知道当时怎么发现的,反正发现以后一身冷汗,就凭这个你们都该给我打赏!\u003C\u002Fp\u003E\u003Cp\u003E我已经向keras提交了专门指明该暗陷阱的注释,并得到接收。 下次使用Keras载入BN层的权重时注意按照文档的提示正确组织。\u003C\u002Fp\u003E\u003Cp\u003E载入权重的代码如下,注意不要漏了第一个stage的卷积层和最后的全连接层。\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef load_weights(model, weights_path):\n
This function load the pretrained weights to the model\n
f = h5py.File(weights_path, 'r')\n
for layer in model.layers:\n
if layer.name[:3] == 'res':\n
layer.set_weights([f[layer.name]['weights'][:], f[layer.name]['bias'][:]])\n
elif layer.name[:2] == 'bn':\n
scale_name = 'scale'+layer.name[2:]\n
weights = []\n
#注意这里的权重组织顺序\n
weights.append(f[scale_name]['weights'][:])\n
weights.append(f[scale_name]['bias'][:])\n
weights.append(f[layer.name]['weights'][:])\n
weights.append(f[layer.name]['bias'][:])\n\n
layer.set_weights(weights)\n
model.get_layer('conv1').set_weights([f['conv1']['weights'][:], f['conv1']['bias'][:]])\n
model.get_layer('fc1000').set_weights([f['fc1000']['weights'][:].T, f['fc1000']['bias'][:]])\n
return model\u003C\u002Fcode\u003E\u003Cp\u003E载入的逻辑和我们刚才说的一样,res开头的就直接载入,bn开头的就找到对应的bn层和scale层,组合为一个list后载入。\u003C\u002Fp\u003E\u003Cp\u003E如果你按照我上面的代码搭建了网络,那么你的程序会在bn层报维度不匹配错误。\u003C\u002Fp\u003E\u003Cp\u003E以第一个bn层’bn_conv’为例,打印出它的shape,再打印出我们网络的这一层需要的shape\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Eprint f['bn_conv1']['weights'][:].shape\nprint model.get_layer('bn_conv1').get_weights()[0].shape\u003C\u002Fcode\u003E\u003Cp\u003E 结果是:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003E(64,)\n(112,)\u003C\u002Fcode\u003E\u003Cp\u003E这个问题的bug非常伤脑筋,主要错误在于BN层有一个参数叫axis,这个参数决定了我们要沿着哪个轴对数据进行批规范化,BN参数的shape就是由数据在axis轴的shape决定的。说实话,我不清楚kaiming\n \nhe的这个resnet50是从哪个轴规范化的,但在这里,因为caffe给出的shape是64,所以我们只能选axis=1,输入数据(None,64,112,112)只有在这个轴上规范化的shape才为64。其他的BN层也是一样,所以估计原网络就是这么干的。axis=1意味着按channel轴进行规范化。\u003C\u002Fp\u003E\u003Cp\u003E有同学指出kaiming he的文章里有说明,确实是按axis=1规范化的,汗……读书不认真啊\u003C\u002Fp\u003E\u003Cp\u003E所以只需要在是上面代码中所有出现BN的地方传入axis=1,即可正确载入权重。\u003Cb\u003E注意fc层的权重需要一个转置。\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Ch2\u003E测试网络\u003C\u002Fh2\u003E\u003Cp\u003E现在我们来\u003Ca href=\&http:\u002F\u002Flib.csdn.net\u002Fbase\u002Fsoftwaretest\& class=\&\& data-editable=\&true\& data-title=\&测试\&\u003E测试\u003C\u002Fa\u003E一下网络,首先写一个预处理输入图片的函数,主要是resize,减均值和更换轴顺序和更换RGB顺序,因为caffe的默认彩色通道是BGR,所以需要将输入的RGB图像转换为BGR(不转换好像问题也不大)\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef read_img(img_path):\n
this function returns preprocessed image\n
mean = (103.939, 116.779, 123.68)\n
img = io.imread(img_path)\n\n
# we only deal with color image for now\n
assert img.shape[2] == 3\n\n
# resize so that the shorter side=256\n
width, height, _ = img.shape\n
resize_ratio = 256.\u002Fmin(width, height)\n
if width & height:\n
height = int(height * resize_ratio)\n
resized_shape = (256, height)\n
width = int(width * resize_ratio)\n
resized_shape = (width, 256)\n
# resize accroding to shorter side\n
img = resize(img, resized_shape)\n
img = img*255\n
# crop the center 224*224 part\n
img = img[16:240, 16:240, :]\n\n
io.imshow(img.astype('uint8'))\n
io.show()\n
#decenterize\n
img[:, :, 0] -= mean[0]\n
img[:, :, 1] -= mean[1]\n
img[:, :, 2] -= mean[2]\n\n
# 'RGB'-&'BGR'\n
img = img[:, :, ::-1]\n\n
#'tf'-&'th'\n
img = np.transpose(img, (2, 0, 1))\n
# expand dim for test\n
img = np.expand_dims(img, axis=0)\n
return img\u003C\u002Fcode\u003E\u003Cp\u003E 然后进行测试,用一个叫sysnset_words的文件来完成输出数字标签到文字类别的对应,这个文件也可以在我的github上下载:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Eif __name__ == '__main__':\n
resnet_model = get_resnet50()\n
resnet_model = load_weights(resnet_model, 'resnet50.h5')\n
test_img1 = read_img('cat.jpg')\n
test_img2 = read_img('airplane.jpg')\n
# you may download synset_words from address given at the begining of this file\n
class_table = open('synset_words', 'r')\n
lines = class_table.readlines()\n
print \&result for test 1 is\&\n
print lines[np.argmax(resnet_model.predict(test_img1)[0])]\n
print \&result for test 2 is\&\n
print lines[np.argmax(resnet_model.predict(test_img2)[0])]\n
class_table.close()\u003C\u002Fcode\u003E\u003Cp\u003E 结果截个图如下,得到了正确结果: \u003C\u002Fp\u003E\u003Ch2\u003E如果你不想看这么长的文章直接到这里就好\u003C\u002Fh2\u003E\u003Cp\u003E实际的代码实现可能与正文展示略有不同,例如实际上的代码其实将载入的权重重新保存了一次,这样就不需要复杂的load_weights函数了,只需要使用模型的load_weights方法即可。另外,实际的代码在维度顺序上做了很多自适应调整,代码的风格和变量名也有极大改观,详细的代码可以查看我的github或Keras的example。\u003C\u002Fp\u003E\u003Cp\u003E然后,下面是本文的一些资源\u003C\u002Fp\u003E\u003Cp\u003ECaffe模型和预训练的caffe模型,网络的图也在这能找到:\u003Ca href=\&https:\u002F\u002Fgithub.com\u002FKaimingHe\& data-editable=\&true\& data-title=\&kaiming he的github\& class=\&\&\u003Ekaiming he的github\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E本文的源代码和模型的json文件在我的github,不star一下不是好孩子哦:\u003Ca href=\&https:\u002F\u002Fgithub.com\u002FMoyanZitto\u002Fkeras-scripts\& data-editable=\&true\& data-title=\&我的github\&\u003E我的github\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E预训练的权重在github的相关文件注释中,在百度云和Google \nDrive均可下载。本程序已经提交给Keras并得到merge,从下个版本开始,你将能够在keras的官方examples里看到全部代码了。另外,keras的作者fchollet基于本代码重新做了调整,添加了对theano后端的支持,fchollet开了一个新的github仓库,看样子是要做一版Keras的“keras\n zoo”。本代码很荣幸的成为该仓库最先收录的三个模型之一。\u003C\u002Fp\u003E\u003Cp\u003E欢迎大家围观f神的github:\u003Ca href=\&https:\u002F\u002Fgithub.com\u002Ffchollet\u002Fdeep-learning-models\& data-editable=\&true\& data-title=\&fchollet\u002Fdeep-learning-models\&\u003Efchollet\u002Fdeep-learning-models\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E感谢阅读,鸣谢\u003Ca class=\&member_mention\& href=\&http:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002F42b4b091fb2e7afd7ec62\& data-hash=\&42b4b091fb2e7afd7ec62\& data-hovercard=\&p$b$42b4b091fb2e7afd7ec62\& data-editable=\&true\& data-title=\&@徐拉达\&\u003E@徐拉达\u003C\u002Fa\u003E在另一篇文章给的建议,由于专栏整理,另一篇文章被删除了,在此特别鸣谢。\u003C\u002Fp\u003E&,&updated&:new Date(&T17:21:37.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:22,&likeCount&:144,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T01:21:37+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic2.zhimg.com\u002F0acfc6d78ecbfe888ddd0ed5440dfce1_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:22,&likesCount&:144},&&:{&title&:&【消息】:快来围观Keras的“keras zoo”&,&author&:&bigmoyan&,&content&:&\u003Cp\u003ECaffe 之所以这么流行,我看一大半的功劳要归功于“caffe zoo”。自己编写或训练一个网络总是困难的,而在别人网络的基础上finetune就爽太多了。\u003C\u002Fp\u003E\u003Cp\u003ECaffe最先出来-&很多研究成果用Caffe实现-&壮大了Caffe Zoo-&follower从caffe zoo下载模型做新的研究-&进一步壮大caffe zoo\u003C\u002Fp\u003E\u003Cp\u003E良性循环~\u003C\u002Fp\u003E\u003Cp\u003E\u003Cbr\u003EKeras终于觉醒啦!\u003C\u002Fp\u003E\u003Cp\u003E就在\u003Cb\u003E十几个小时\u003C\u002Fb\u003E之前,fchollet新开了一个github仓库,“deep learning models”,据介绍,这个仓库将收录很多流行网络的\u003Cb\u003EKeras实现\u003C\u002Fb\u003E和\u003Cb\u003E预训练权重\u003C\u002Fb\u003E,说白了就是个Keras Zoo了\u003C\u002Fp\u003E\u003Cp\u003E目前收录的网络有:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E\u003Cb\u003EVGG-16\u003C\u002Fb\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Cb\u003EVGG-19\u003C\u002Fb\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Cb\u003EResnet50 \u003C\u002Fb\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E咦,等等……好像混进去什么了不得的东西!\u003C\u002Fp\u003E\u003Cp\u003E没错!本菜鸡贡献的50层残差网络在十几个小时之前被merge到Keras中了,而且自恋的想一想,可能正是这个50层残差网络使fchollet神起了建立一个“keras zoo”的念头,然后创建了这个仓库。\u003C\u002Fp\u003E\u003Cp\u003E啊……好激动,比PR被merge还激动,菜鸡原来也有能改写历史(并没有)的一天吗?\u003C\u002Fp\u003E\u003Cp\u003E今晚吃顿好的奖励一下自己~\u003C\u002Fp\u003E\u003Cp\u003E对了,fchollet神的这个版本基于我的代码进行了补充,添加了各种说明,运用了各种花式代码技巧——这些都不是最重要的,最重要的是fchollet神解决了困扰我好久的问题,就是对theano后端的支持。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E现在,resnet支持tensorflow和theano两种后端\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cp\u003EKeras Zoo的下一步工作是添加Inception V3网络,欢迎大家持续围观,下载使用!\u003C\u002Fp\u003E\u003Cp\u003E对了,github地址在这里:\u003Ca class=\&\& href=\&https:\u002F\u002Fgithub.com\u002Ffchollet\u002Fdeep-learning-models\&\u003Ehttps:\u002F\u002Fgithub.com\u002Ffchollet\u002Fdeep-learning-models\u003C\u002Fa\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E鞠躬!\u003C\u002Fp\u003E\u003Cp\u003E(你们说要不要把那个152层的大家伙也弄进去呢……还有一个1000层的咧~)\u003C\u002Fp\u003E&,&updated&:new Date(&T08:21:58.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:22,&likeCount&:93,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T16:21:58+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic4.zhimg.com\u002F5b3ccc91dbf5fabdd805e_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:22,&likesCount&:93},&&:{&title&:&【啄米日常】3:一个不负责任的Keras介绍(中)&,&author&:&bigmoyan&,&content&:&上回书我们简单的把Keras的框架理了一下,下面我们深入(也不怎么深)具体的模块理一下Keras,主要聊一聊每个模块的具体功能和核心函数\u003Ch2\u003Ebackend:百货商店\u003C\u002Fh2\u003E\u003Cp\u003Ebackend这个模块的主要作用,是对tensorflow和theano的底层张量运算进行了包装。用户不用关心具体执行张量运算的是theano还是tensorflow,就可以编写出能在两个框架下可以无缝对接的程序。backend中的函数要比文档里给出的多得多,完全就是一家百货商店。但一般情况下,文档里给出的那些就已经足够你完成大部分工作了,事实上就连文档里给出的函数大部分情况也不会用,这里提几个比较有用的函数——当然是对我来说比较有用,毕竟这是一份不怎么负责任的介绍,如果你想找对你有用的函数,就去backend淘一淘吧~:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E function:毫无疑问这估计是最有用的一个函数了,function用于将一个计算图(计算关系)编译为具体的函数。典型的使用场景是输出网络的中间层结果。\u003C\u002Fli\u003E\u003Cli\u003Eimage_ordering和set_image_ordering:这组函数用于返回\u002F设置图片的维度顺序,由于Theano和Tensorflow的图片维度顺序不一样,所以有时候需要获取\u002F指定。典型应用是当希望网络自适应的根据使用的后端调整图片维度顺序时。\u003C\u002Fli\u003E\u003Cli\u003Elearning_phase:这个函数的主要作用是返回网络的运行状态,0代表测试,1代表训练。当你需要便携一个在训练和测试是行为不同的层(如Dropout)时,它会很有用。\u003C\u002Fli\u003E\u003Cli\u003Eint_shape:这是我最常用的一个函数,用于以整数tuple的形式返回张量的shape。要知道从前网络输出张量的shape是看都看不到的,int_shape可以在debug时起到很大作用。\u003C\u002Fli\u003E\u003Cli\u003Egradients: 求损失函数关于变量的导数,也就是网络的反向计算过程。这个函数在不训练网络而只想用梯度做一点奇怪的事情的时候会很有用,如图像风格转移。\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003Ebackend的其他大部分函数的函数名是望而知义的,什么max,min,equal,eval,zeros,ones,conv2d等等。函数的命名方式跟numpy差不多,下次想用时不妨先‘.’一下,说不定就有。 \u003Cbr\u003E\u003C\u002Fp\u003E\u003Ch2\u003Emodels\u002Flayers:Keras的核心主题\u003C\u002Fh2\u003E\u003Cp\u003E使用Keras最常见的目的,当然还是训练一个网络。之前说了网络就是张量到张量的映射,所以Keras的网络,其实是一个由多个子计算图构成的大计算图。当这些子计算图是顺序连接时,称为Sequential,否则就是一般的model,我们称为泛型模型。\u003C\u002Fp\u003E\u003Cp\u003E模型不但是张量的计算方式,还是层对象的容器,模型用来将它所含有的层整合起来,大家手拉手一起走\u003C\u002Fp\u003E\u003Cp\u003E模型有两套训练和测试的函数,一套是fit,evaluate等,另一套是fit_generator,evaluate_generator,前者适用于普通情况,后者适用于数据是以迭代器动态生成的情况。迭代器可以在内存\u002F显存不足,实时动态数据提升进行网络训练,所以使用Keras的话,Python的迭代器这一部分是一定要掌握的内容。对模型而言,最核心的函数有两个:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003Ecompile():编译,模型在训练前必须编译,这个函数用于完成添加正则项啊,确定目标函数啊,确定优化器啊等等一系列模型配置功能。这个函数必须指定的参数是优化器和目标函数,经常还需要指定一个metrics来评价模型。\u003Cbr\u003E\u003C\u002Fli\u003E\u003Cbr\u003E\u003Cli\u003Efit()\u002Ffit_generator():用来训练模型,参数较多,是需要重点掌握的函数,对于keras使用者而言,这个函数的每一个参数都需要掌握。\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E其他的函数请自己学习。 \u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E另外,模型还有几个常用的属性和函数:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003Elayers:该属性是模型全部层对象的列表,是的就是一个普通的python list\u003C\u002Fli\u003E\u003Cli\u003Eget_layer():这个函数通过名字来返回模型中某个层对象\u003C\u002Fli\u003E\u003Cli\u003Epop():这个函数文档里没有,但是可以用。作用是弹出模型的最后一层,从前进行finetune时没有pop,大家一般用model.layers.pop()来完成同样的功能。 \u003Cbr\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E当然,因为Model是Layer的子类,Layer的所有属性和方法也自动被Model所有,这些有用的属性稍后介绍。\u003C\u002Fp\u003E\u003Cp\u003EKeras的层对象是构筑模型的基石,除了卷积层,递归神经网络层,全连接层,激活层这种烂大街的Layer对象外,Keras还有一些不是那么烂大街的东西:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003EAdvanced Activation:高级激活层,主要收录了包括leakyReLU,pReLU,ELU,SReLU等一系列高级激活函数,这些激活函数不是简单的element-wise计算,所以单独拿出来实现一下\u003C\u002Fli\u003E\u003Cli\u003EMerge层:这个层用于将多个层对象的输出组合起来,支持级联、乘法、余弦等多种计算方式,它还有个小兄弟叫merge,这个函数完成与Merge相同的作用,但输入的对象是张量而不是层对象。\u003C\u002Fli\u003E\u003Cli\u003ELambda层:这是一个神奇的层,看名字就知道它用来把一个函数作用在输入张量上。这个层可以大大减少你的工作量,当你需要定义的新层的计算不是那么复杂的时候,可以通过lambda层来实现,而不用自己完全重写。\u003C\u002Fli\u003E\u003Cli\u003EHighway\u002FMaxout\u002FAtrousConvolution2D层:这个就不多说了,懂的人自然懂,keras还是在一直跟着潮流走的\u003C\u002Fli\u003E\u003Cli\u003EWrapper层:Wrapper层用于将一个普通的层对象进行包装升级,赋予其更多功能。目前,Wrapper层里有一个TimeDistributed层,用于将普通的层包装为对时间序列输入处理的层,而Bidirectional可以将输入的递归神经网络层包装为双向的(如把LSTM做成BLSTM) \u003C\u002Fli\u003E\u003Cli\u003EInput:补一个特殊的层,Input,这个东西实际上是一个Keras tensor的占位符,主要用于在搭建Model模型时作为输入tensor使用,这个Input可以通过keras.layers来import。\u003C\u002Fli\u003E\u003Cli\u003Estateful与unroll:Keras的递归神经网络层,如SimpleRNN,LSTM等,支持两种特殊的操作。一种是stateful,设置stateful为True意味着训练时每个batch的状态都会被重用于初始化下一个batch的初始状态。另一种是unroll,unroll可以将递归神经网络展开,以空间换取运行时间。\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003EKeras的层对象还有一些有用的属性和方法,比较有用的是:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003Ename:别小看这个,从茫茫层海中搜索一个特定的层,如果你对数数没什么信心,最好是name配合get_layer()来用。\u003C\u002Fli\u003E\u003Cli\u003Etrainable:这个参数确定了层是可训练的还是不可训练的,在迁移学习中我们经常需要把某些层冻结起来而finetune别的层,冻结这个动作就是通过设置trainable来实现的。\u003C\u002Fli\u003E\u003Cli\u003Einput\u002Foutput:这两个属性是层的输入和输出张量,是Keras tensor的对象,这两个属性在你需要获取中间层输入输出时非常有用\u003C\u002Fli\u003E\u003Cli\u003Eget_weights\u002Fset_weights:这是两个方法用于手动取出和载入层的参数,set_weights传入的权重必须与get_weights返回的权重具有同样的shape,一般可以用get_weights来看权重shape,用set_weights来载入权重\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E既然是核心主题,我们就多唠两句,在Keras中经常有的一个需求是需要自己编写一个新的层,如果你的计算比较简单,那可以尝试通过Lambda层来解决,如果你不得不编写一个自己的层,那也不是什么大不了的事儿。前两天群里有朋友想编写一个卷积核大小不一样的卷积层(虽然不知道为啥他这么想不开……活着不好吗?),这个显然就要自己编写层了。 \u003C\u002Fp\u003E\u003Cp\u003E要在Keras中编写一个自己的层,需要开一个从Layer(或其他层)继承的类,除了__init__以为你需要覆盖三个函数:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003Ebuild,这个函数用来确立这个层都有哪些参数,哪些参数是可训练的哪些参数是不可训练的。\u003C\u002Fli\u003E\u003Cli\u003Ecall,这个函数在调用层对象时自动使用,里面就是该层的计算逻辑,或计算图了。显然,这个层的核心应该是一段符号式的输入张量到输出张量的计算过程。\u003C\u002Fli\u003E\u003Cli\u003Eget_output_shape_for:如果你的层计算后,输入张量和输出张量的shape不一致,那么你需要把这个函数也重新写一下,返回输出张量的shape,以保证Keras可以进行shape的自动推断\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E其实也不难~是吧,不要忘记Keras是基于Python的框架,你可以随时随地查看那些已经写好的层的代码,模仿着看看你自己的层要怎么写~\u003C\u002Fp\u003E\u003Ch2\u003E优化器,目标函数,初始化策略,等等... \u003Cbr\u003E\u003C\u002Fh2\u003E\u003Cp\u003E和model,layers这种核心功能相比,这些模块的重要性就没有那么大,我们简单介绍一下,里面的具体技术,(下)篇可能会说,也可能不会……我还没想好,但是基本上不说也没什么影响\u003C\u002Fp\u003E\u003Cp\u003Eobjectives是优化目标, 它本质上是一个从张量到数值的函数,当然,是用符号式编程表达的。具体的优化目标有mse,mae,交叉熵等等等等,根据具体任务取用即可,当然,也支持自己编写。需要特别说明的一点是,如果选用categorical_crossentropy作为目标函数,需要将标签转换为one-hot编码的形式,这个动作通过utils.np_utils.to_categorical来完成(记得上篇我就提过了)\u003C\u002Fp\u003E\u003Cp\u003Eoptimizers是优化器,没什么可说了,如何选用合适的优化器不在本文讨论范畴。注意模型是可以传入优化器对象的,你可以自己配置一个SGD,然后将它传入模型中。 另外,最新版本的Keras为所有优化器额外设置了两个参数clipnorm和clipvalue,用来对梯度进行裁剪。\u003C\u002Fp\u003E\u003Cp\u003Eactivation是激活函数,这部分的内容一般不直接使用,而是通过激活层Activation来调用,此处的激活函数是普通的element-wise激活函数,如果想使用高级激活函数,请翻到高级激活函数层。\u003C\u002Fp\u003E\u003Cp\u003Ecallback是回调函数,这其实是一个比较重要的模块,回调函数不是一个函数而是一个类,用于在训练过程中收集信息或进行某种动作。比如我们经常想画一下每个epoch的训练误差和测试误差,那这些信息就需要在回调函数中收集。预定义的回调函数中CheckModelpoint,History和EarlyStopping都是比较重要和常用的。其中CheckPoint用于保存模型,History记录了训练和测试的信息,EarlyStopping用于在已经收敛时提前结束训练。回调函数LearningRateScheduler支持按照用户的策略调整学习率,做模型精调或研究优化器的同学可能对这个感兴趣。\u003C\u002Fp\u003E\u003Cp\u003E值得注意的是,\u003Cb\u003EHistory是模型训练函数fit的返回值\u003C\u002Fb\u003E,也就是说即使你没有使用任何回调函数,找一个变量接住model.fit(),还是能得到不少训练过程中的有用信息。\u003C\u002Fp\u003E\u003Cp\u003E另外,回调函数还支持将信息发送到远程服务器,以及与Tensorflow的tensorboard联动,在网页上动态展示出训练和测试的情况(需要使用tensorflow为后端)\u003C\u002Fp\u003E\u003Cp\u003E回调函数支持用户自定义,定义方法也非常简单,请参考文档说明编写即可\u003C\u002Fp\u003E\u003Cp\u003E初始化方法,正则项,约束项,可视化没有什么特别值得注意的,就略过了。Keras中所有的模块都是可以用户自己定义的,这就是开源和Python的魅力,讲真你让我拿C++写这么个东西……我光把结构摸清楚就要吐血了!\u003C\u002Fp\u003E\u003Cp\u003E另一个文档中没有但实际上有的东西是metrices,这里面定义了一系列用于评价模型的指标,例如“accuracy”。在训练模型时,可以选择一个或多个指标来衡量模型性能。\u003C\u002Fp\u003E\u003Ch2\u003E数据预处理和utils\u003C\u002Fh2\u003E\u003Cp\u003E数据预处理是Keras提供的用于预处理图像、文本和序列数据的一套工具,这个地方就属于各回各家各找各妈了,你处理什么问题就从这里面找什么工具。\u003C\u002Fp\u003E\u003Cp\u003E特别指出的是,数据预处理的图像预处理部分,提供了一套用于实时图像数据提升的工具,这个东西支持用各种各样的方法对输入图像进行数据提升,然后以生成器的形式返回。另外,该工具还支持从文件夹中自动生成数据和标签,简直方便的不要不要的。\u003C\u002Fp\u003E\u003Cp\u003Eutils文档中没有,里面包含的函数也不必怎么了解,除了两个。一个是说了很多遍的to_catgoraical,另一个是convert_kernel。后者的主要作用是把卷积滤波器的卷积核在th和tf之间互相转换。theano和tensorflow相爱想杀,到处搞对抗。其中之一就是卷积核,卷积这个东西,按照信号与系统(哼,才不会告诉你们我是信号系统助教咧)的定义,是翻转-&平移-&相乘-&相加。但反正卷积网络的卷积核都是训练出来的,翻转不翻转有什么关系?\u003C\u002Fp\u003E\u003Cp\u003E所以有些人没翻转,有些人翻转了。是的,说的就是你俩,theano和tensorflow。于是如果一个网络预训练权重是由其中一种后端训练出来的,又要在另一种后端上跑,那么你就需要用kernel_convert这个函数搞一搞了。\u003C\u002Fp\u003E\u003Cp\u003E估计这事儿太不地道,作者也看不下去了。现在utils出了一个新的layer_utils,里面有一个convert_all_kernels_in_model函数,用来把一个模型的所有卷积核全部进行转换,以后就用这个吧~\u003C\u002Fp\u003E\u003Ch2\u003E与scikit-learn联动 \u003Cbr\u003E\u003C\u002Fh2\u003E\u003Cp\u003E上一篇有人留言说希望多讲点这块的内容,很抱歉……我……我也不怎么会,原谅我毕竟是一只菜鸡\u003C\u002Fp\u003E\u003Cp\u003E虽然不怎么会,但是不妨碍我知道这应该是一个非常重要和有潜力的功能。Keras与scikit-learn的协作通过keras.wrapper实现,在这个脚本里定义了两个类,分别是KerasClassifier和KerasRegressor,搭建好Sequential模型(只能是Sequential)将被它们包装为sklearn的分类器和迭代器加入的sklearn的工作流中。\u003C\u002Fp\u003E\u003Cp\u003E这里有一个使用sklearn对Keras进行超参数调整的例子,大家可以参考这篇文章学习Keras和sklearn的联动:\u003Ca class=\&\& data-title=\&Keras\u002FPython深度学习中的网格搜索超参数调优(附源码)\& data-editable=\&true\& href=\&http:\u002F\u002Fgeek.csdn.net\u002Fnews\u002Fdetail\u002F95494\&\u003EKeras\u002FPython深度学习中的网格搜索超参数调优(附源码)\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E中篇就到这里,下篇来介绍Keras的实现原理\u002F原则,常见问题与解答,以及Keras中比较隐蔽和诡异的坑。可能会过一段时间才发哟~最近还是略忙\u003C\u002Fp\u003E\u003Cp\u003E另外你们觉得我这种菜鸡能找到啥工作啊有没有内推什么的求往脸上砸!\u003C\u002Fp\u003E&,&updated&:new Date(&T06:07:32.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:8,&likeCount&:36,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T14:07:32+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic3.zhimg.com\u002Fb0f672d59ea6c07a2285375fffcd4bfa_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:8,&likesCount&:36},&&:{&title&:&【啄米日常】2:一个不负责任的Keras介绍(上)&,&author&:&bigmoyan&,&content&:&\u003Cp\u003E如果很长时间不更专栏的话,会不会给大家一种本菜鸡平常不怎么学习的错(zhen)觉(xiang)?\u003C\u002Fp\u003E\u003Cp\u003E还是稍微更一更吧,听说知乎最近引进了打赏功能……是吧\u003C\u002Fp\u003E\u003Cp\u003E准备分上下两篇,也可能是上中下三篇,来做一个(不)负责任的Keras介绍~上篇主要从宏观的角度来讲,属于面向新手的。中\u002F下篇细节一点,偏重原理\u002F特色。\u003C\u002Fp\u003E\u003Cp\u003E另外,最近准备做一个Keras使用过程中常见坑的踩法,如果大家在Keras使用过程中遇到过什么坑,不妨私信告我哦。好的,开始了\u003C\u002Fp\u003E\u003Ch2\u003EKeras:宏观特性 \u003C\u002Fh2\u003E\u003Cp\u003EKeras是最近蒸蒸日上的深度学习框架, 非常的蒸蒸日上,5月的时候有这么个图:\u003C\u002Fp\u003E\u003Cp\u003E\u003Cimg data-rawheight=\&462\& data-rawwidth=\&690\& src=\&cdbd82c3069.jpg\&\u003ECaffe是老牌选手,Tensorflow有个神爹,不跟这俩比Keras的表现还是十分亮眼的,我想现在如果有排名也会一样出色(注意mxnet是万年老5虽然我一直觉得mxnet其实非常出色……)\u003C\u002Fp\u003E\u003Cp\u003E那么,Keras有啥特点呢,我想下面这些可能是属于Keras的关键词:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E符号主义\u003C\u002Fli\u003E\u003Cli\u003EPython \u003C\u002Fli\u003E\u003Cli\u003E快速原型\u003C\u002Fli\u003E\u003Cli\u003E轻量级,高度模块化 \u003C\u002Fli\u003E\u003Cli\u003E易扩展\u003Cbr\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003EKeras事实上是一个基于Theano和Tensorflow上的一个包装,所谓站在巨人的肩膀上也不外如是了。 因为Theano和Tensorflow都是符号主义的东西(下面讲再说这是啥),因此Keras自然也是符号主义的。\u003C\u002Fp\u003E\u003Cp\u003EKeras由纯Python编写,这意味着它的源代码简单易懂,你可以随时进去看看它都做了什么,怎么做的。并且,当你需要修改源代码的时候,大胆修改就可以了,它会立刻生效。尽管Python的运行效率要低于C++,但Keras只是Tensorflow和Theano的包装而已,这层包装的运行代价是很小的。\u003C\u002Fp\u003E\u003Cp\u003EKeras是一款高度模块化的框架,使用它搭建网络和训练网络将会非常容易,如果你需要深入模型中控制细节,通常使用Keras提供的一些函数就可以了,很少需要深入到Tensorflow或Theano那一层。因此,Keras适合于快速原型生成,如果你经常需要很快的实现一下自己的idea,Keras会是一个不错的选择。\u003C\u002Fp\u003E\u003Cp\u003E另外,Keras的预训练模型库也在逐步建设,目前有VGG-16,VGG-19,resnet50,Inceptionv3四种预训练好的模型供大家使用。 \u003Cbr\u003E\u003C\u002Fp\u003E\u003Ch2\u003E计算图,符号主义和张量\u003C\u002Fh2\u003E\u003Cp\u003E符号主义,Google一下会发现是一个机器学习的名词,但我们这说的符号主义不是那个东西,这里说的符号主义,指的是使用\u003Cb\u003E符号式编程\u003C\u002Fb\u003E的一种方法。 另一种相对的方法是\u003Cb\u003E命令式编程\u003C\u002Fb\u003E。或者是\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E要说Theano\u002FTensorflow\u002FKeras,就不能不提它的符号主义特性\u003C\u002Fp\u003E\u003Cp\u003E事实上,Theano也好,Tensorflow也好,其实是一款符号主义的计算框架,未必是专为深度学习设计的。假如你有一个与深度学习完全无关的计算任务想运行在GPU上,你完全可以通过Theano\u002FTensorflow编写和运行。\u003C\u002Fp\u003E\u003Cp\u003E假如我们要求两个数a和b的和,通常只要把值赋值给a和b,然后计算a+b就可以了,正常人类都是这么写的:\u003C\u002Fp\u003E\u003Cp\u003Ea=3\u003Cbr\u003Eb=5\u003Cbr\u003Ez = a + b \u003C\u002Fp\u003E\u003Cp\u003E运行到第一行,a真的是3.运行到第2行,b真的是5,然后运行第三行,电脑真的把a和b的值加起来赋给z了。\u003C\u002Fp\u003E\u003Cp\u003E一点儿都不神奇。 \u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E但总有不正常的,不正常的会这么想问题:a+b这个计算任务,可以分为三步。(1)声明两个变量a,b。建立输出变量z(2)确立a,b和z的计算关系,z=a+b(3)将两个数值a和b赋值到变量中,计算结果z\u003C\u002Fp\u003E\u003Cp\u003E后面那种“先确定符号以及符号之间的计算关系,然后才放数据进去计算”的办法,就是符号式编程。当你声明a和b时,它们里面是空的。当你确立z=a+b的计算关系时,a,b和z仍然是空的,只有当你真的把数据放入a和b了,程序才开始做计算。\u003C\u002Fp\u003E\u003Cp\u003E符号之间的运算关系,就称为运算图。 \u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E这样做当然不是闲的无聊,符号式计算的一大优点是,当确立了输入和输出的计算关系后,在进行运算前我们可以对这种运算关系进行自动化简,从而减少计算量,提高计算速度。另一个优势是,运算图一旦确定,整个计算过程就都清楚了,可以用内存复用的方式减少程序占用的内存。\u003C\u002Fp\u003E\u003Cp\u003E在Keras,theano和Tensorflow中,参与符号运算的那些变量统一称作张量。张量是矩阵的进一步推广。\u003C\u002Fp\u003E\u003Cblockquote\u003E\u003Cp\u003E规模最小的张量是0阶张量,即标量,也就是一个数。\u003C\u002Fp\u003E\u003Cp\u003E当我们把一些数有序的排列起来,就形成了1阶张量,也就是一个向量\u003C\u002Fp\u003E\u003Cp\u003E如果我们继续把一组向量有序的排列起来,就形成了2阶张量,也就是一个矩阵\u003C\u002Fp\u003E\u003Cp\u003E把矩阵摞起来,就是3阶张量,我们可以称为一个立方体,具有3个颜色通道的彩色图片就是一个这样的立方体\u003C\u002Fp\u003E\u003Cp\u003E把矩阵摞起来,好吧这次我们真的没有给它起别名了,就叫4阶张量了,不要去试图想像4阶张量是什么样子,它就是个数学上的概念。 \u003Cbr\u003E\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Cp\u003E一言以蔽之,Keras的计算过程,就是建立一个从\u003Cb\u003E张量到张量的映射函数\u003C\u002Fb\u003E,然后再放入真实数据进行计算。对深度学习而言,这个“映射函数”就是一个神经网络,而神经网络中的每个层自然也都是从张量到张量的映射。\u003Cbr\u003E\u003C\u002Fp\u003E\u003Ch2\u003EKeras框架结构\u003C\u002Fh2\u003E\u003Cp\u003E我想画一个图,可是想了半天画不明白……我就罗列就好了,Keras的结构大致是这样的:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003Ebackend:后端,对Tensorflow和Theano进行封装,完成低层的张量运算、计算图编译等\u003C\u002Fli\u003E\u003Cli\u003Emodels:模型,模型是层的有序组合,也是层的“容器”,是“神经网络”的整体表示\u003C\u002Fli\u003E\u003Cli\u003Elayers:层,神经网络的层本质上规定了一种从输入张量到输出张量的计算规则,显然,整个神经网络的模型也是这样一种张量到张量的计算规则,因此keras的model是layer的子类\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E上面的三个模块是Keras最为要紧和核心的三块内容,搭建一个神经网络,就只用上面的内容即可。注意的是,backend虽然很重要,但其内容多而杂,大部分内容都是被其他keras模块调用,而不是被用户直接使用。所以它不是新手马上就应该学的,初学Keras不妨先将backend放一旁,从model和layers学起。\u003C\u002Fp\u003E\u003Cp\u003E为了训练神经网络,必须定义一个神经网络优化的目标和一套参数更新的方式,这部分就是目标函数和优化器:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003Eobjectives:目标函数,规定了神经网络的优化方向\u003C\u002Fli\u003E\u003Cli\u003Eoptimizers:优化器,规定了神经网络的参数如何更新\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E上面的两个模块的内容,是在训练一个网络时必须提供的。此外,Keras提供了一组模块用来对神经网络进行配置:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003Einitialization:初始化策略,规定了网络参数的初始化方法\u003C\u002Fli\u003E\u003Cli\u003Eregularizers:正则项,提供了一些用于参数正则的方法,以对抗过拟合\u003C\u002Fli\u003E\u003Cli\u003Econstraints:约束项,提供了对网络参数进行约束的方法\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E为了方便调试、分析和使用网络,处理数据,Keras提供了下面的模块:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003Ecallbacks:回调函数,在网络训练的过程中返回一些预定义\u002F自定义的信息\u003C\u002Fli\u003E\u003Cli\u003Evisualization:可视化,用于将网络结构绘制出来,以直观观察\u003C\u002Fli\u003E\u003Cli\u003Epreprocessing:提供了一组用于对文本、图像、序列信号进行预处理的函数 \u003C\u002Fli\u003E\u003Cli\u003Eutils:常用函数库,比较重要的是utils.np_utils中的to_categorical,用于将1D标签转为one-hot的2D标签和convert_kernel函数,用于将卷积核在theano模式和Tensorflow模式之间转换。最新的代码显示utils的utils.layer_utils里提供了将模型中全部卷积核进行模式转换的函数。大部分其他utils的函数你或许很难用到,但有空不妨一读,或有进益。\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E最后,为了能让用户一上手就能跑一些模型,Keras提供了一个常用数据库的模块,用来载入常用的数据库:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003Edatasets:提供了一些常用数据库的接口,用户将通过这些接口下载和载入数据集\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E额外的一点是,如果用户希望将Keras与scikit-learn联动,Keras也提供了这种联动机制,这个模块是:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E wrappers.scikit-learn \u003Cbr\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cbr\u003E\u003Cbr\u003E\u003Cp\u003E嗯,这个篇幅可以了,这篇先这样。中篇我们把各个模块的东西详细点儿聊一聊,下篇……写着再看,但肯定有下篇的\u003C\u002Fp\u003E\u003Cp\u003E谢谢大家~\u003C\u002Fp\u003E&,&updated&:new Date(&T14:49:57.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:13,&likeCount&:78,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T22:49:57+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic2.zhimg.com\u002Fb9e25cfcbf5483_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:13,&likesCount&:78},&&:{&title&:&【啄米日常】3:一个不负责任的Keras介绍(下)&,&author&:&bigmoyan&,&content&:&\u003Cp\u003E本来上篇文末说,(下)可能会推迟一会儿……\u003C\u002Fp\u003E\u003Cp\u003E但是我控制不住我即己啊!一开始写就想一下子全部写完啊!!\u003C\u002Fp\u003E\u003Cp\u003E所以我又开始笔耕不缀了,这篇专栏写完,我基本是棵废菇了……\u003C\u002Fp\u003E\u003Cp\u003E(下)主要说一下Keras的有用特性,以及一些常见问题,如果还有精力的话,补一些使用Keras的陷阱,没精力这部分就留到番外篇了。理解这些特性对深入了解Keras有比较重要的帮助。\u003Cbr\u003E\u003C\u002Fp\u003E\u003Ch2\u003Ecallable,全部Layer都要callable!\u003C\u002Fh2\u003E\u003Cp\u003EKeras的一大性质是\u003Cb\u003E所有的layer对象都是callable的\u003C\u002Fb\u003E。所谓callable,就是能当作函数一样来使用,层的这个性质不需要依赖任何模型就能成立。比方说你想算算一个向量x的sigmoid值是多少,如果用keras的话,你可以这样写:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Eimport keras.backend as K\nfrom keras.layers import Activation\nimport numpy as np\n\nx = K.placeholder(shape=(3,))\ny = Activation('sigmoid')(x)\nf = K.function([x],[y])\nout = f([np.array([1,2,3])])\n\u003C\u002Fcode\u003E\u003Cp\u003E很显然,我绝对不会写这种代码来求sigmoid,这个例子只是说明,可以这么干,而可以这么干的话很多工作就会很方便。比方说有一次我想给目标函数加一项全变差的正则项,而全变差可以用特定的卷积来实现, 那么我的目标函数的全变差正则项完全就可以用一个Convolution2D层来实现。把层和模型当作张量的函数来使用,是需要认真贯彻落实的一个东西。\u003C\u002Fp\u003E\u003Cp\u003E顺便我们也复习一下上一篇文章说的符号式计算方法。正文第1行先定义了一个“占位符”,它的shape是一个长为3的向量。所谓占位符就是“先占个位置\n“的符号,翻译成中文就是”此处应有一个长为3的向量“。注意第2行,这行我们使用了一个激活层,激活层的激活函数形式是sigmoid,在激活层的后面\n又有一个括号,括号内是我们的输入张量x,可以看到,层对象‘Activation('sigmoid')’是被当做一个函数来使用的。上篇文章说层就是\n张量到张量的运算,那么其输出y自然也是一个张量。\u003C\u002Fp\u003E\u003Cp\u003E第3行通过调用function函数对计算图进行编译,这个计算图很简单,就是输入张量经过sigmoid作用变成输出向量,计算图的各种优化通过这一步得以完成,现在,f就是一个真正的函数了,就可以按照一般的方法使用了。\u003C\u002Fp\u003E\u003Cp\u003E之前说了,\u003Cb\u003E模型也是张量到张量的映射,所以Layer是Model的父类\u003C\u002Fb\u003E,因此,一个模型本身也可以像上面一样使用。总而言之,在Keras中,层对象是callable的。\u003C\u002Fp\u003E\u003Ch2\u003ENode:Keras的网络层复用 \u003C\u002Fh2\u003E\u003Cp\u003EKeras的网络层复用是一个很常用的需求,例如当某一层与多个层相连时,实际上这层是将同一种计算方式复用多次。再比如你用一个网络来抽取两条微博的特征,然后在后面用网络来判断二者是否是同一个主题,那么抽取两次微博的特征这一工作就可以复用同一个网络。\u003C\u002Fp\u003E\u003Cp\u003EKeras的网络复用由一个叫“Node”,或称“计算节点”的东西来实现。笼统地说,每当在某个输入上调用层时,就会为网络层添加一个节点。这个节点将输入张量映射为输出的张量,当你多次调用该层,就会产生多个结点,结点的下标是0,1,2,3...\u003C\u002Fp\u003E\u003Cp\u003E如果仅仅是这样,这部分的东西你依然不需要了解,问题在于,当一个层有多个计算节点时,它的input,output,input_shape,output_shape等属性可能是ill-defined的,因为不清楚你想要的output或input是哪一个。\u003C\u002Fp\u003E\u003Cp\u003E此时,需要使用get_output_at(),get_input_at(),get_output_shape_at()等以at为后缀结尾的函数,at的对象就是层的节点编号。例如get_output_shape_at(2)就会返回第3个输出张量的shape。 \u003Cbr\u003E\u003C\u002Fp\u003E\u003Ch2\u003EShape与Shape自动推断\u003C\u002Fh2\u003E\u003Cp\u003E使用过Keras的都知道,Keras的所有的层有一个“input_shape”的参数,用来指定输入张量的shape。然而这个input_shape,或者有时候是input_dim,只需要在模型的首层加以指定。一旦模型的首层的input_shape指定了,后面的各层就不用再指定,而会根据计算图自动推断。这个功能称为shape的自动推断。\u003C\u002Fp\u003E\u003Cp\u003EKeras的自动推断依赖于Layer中的get_output_shape_for函数来实现,如果大家还记得上一篇文章的话,在提到如何编写自己的Keras层时,我们提到如果你的网络层改变了输入张量的shape,就应该复写get_output_shape_for这个函数,以使后面的层能知道本层输出的shape。\u003C\u002Fp\u003E\u003Cp\u003E在所有的Keras中都有这样一个函数,因此后面的层可以通过查看这个函数的返回值获取前层的输入shape,并通过自己的get_output_shape_for将这个信息传递下去。\u003C\u002Fp\u003E\u003Cp\u003E然而,有时候,这个自动推断会出错。这种情况发生在一个RNN层后面接Flatten然后又接Dense的时候,这个时候Dense的output_shape无法自动推断出。这时需要指定RNN的输入序列长度input_length,或者在网络的第一层通过input_shape就指定。这种情况极少见,大致有个印象即可,遇到的话知道大概是哪里出了问题就好。\u003C\u002Fp\u003E\u003Cp\u003E一般而言,神经网络的数据是以batch为单位的,但在指明input_shape时不需要说明一个batch的样本数。假如你的输入是一个224*224*3的彩色图片,在内部运行时数据的shape是(None,224,224,3),这点在你自己编写层是需要注意。\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cbr\u003E\u003Ch2\u003ETH与TF的相爱相杀\u003C\u002Fh2\u003E\u003Cp\u003E相爱没有,全是相杀\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003EKeras提供了两套后端,Theano和Tensorflow,这是一件幸福的事,手中拿着馒头,想蘸红糖蘸红糖,想蘸白糖蘸白糖\u003Cbr\u003E\u003Cbr\u003Eth和tf的大部分功能都被backend统一包装起来了,但二者还是存在不小的冲突,有时候你需要特别注意Keras是运行在哪种后端之上,它们的主要冲突有:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003Edim_ordering,也就是维度顺序。比方说一张224*224的彩色图片,theano的维度顺序是(3,224,224),即通道维在前。而tf的维度顺序是(224,224,3),即通道维在后。dim_ordering\u003Cb\u003E不是\u003C\u002Fb\u003E一个必须和后端搭配的指标,它只规定了输入图片的维度顺序,只要输入图片按此维度顺序设置即可正确运行。然而,如果dim_ordering与后端搭配的话——我指的是所有层的dim_ordering都与后端搭配,会提高程序的运行效率。否则,数据的shape会在计算过程中不断转来转去,效率会低一些。\u003C\u002Fli\u003E\u003Cli\u003E卷积层权重的shape:从无到有训练一个网络,不会有任何问题。但是如果你想把一个th训练出来的卷积层权重载入风格为tf的卷积层……说多了都是泪。我一直觉得这个是个bug,数据的dim_ordering有问题就罢了,为啥卷积层权重的shape还需要变换咧?我迟早要提个PR把这个bug修掉!\u003C\u002Fli\u003E\u003Cli\u003E然后是卷积层kernel的翻转不翻转问题,这个我们说过很多次了,就不再多提。\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E总而言之,相爱没有,全部都是相杀。尽管Keras已经在统一theano和tensorflow上走了很多很多步,但还需要走更多的一些步。\u003C\u002Fp\u003E\u003Ch2\u003EFAQ与学习资料\u003Cbr\u003E\u003C\u002Fh2\u003E\u003Cp\u003EFAQ模块请参考这里:\u003Ca data-title=\&FAQ - Keras中文文档\& data-editable=\&true\& href=\&http:\u002F\u002Fkeras-cn.readthedocs.io\u002Fen\u002Flatest\u002Fgetting_started\u002FFAQ\u002F\&\u003EFAQ - Keras中文文档\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E另外,在这里有一些使用示范,包括与TensorFlow的联动,分布式训练等:\u003C\u002Fp\u003E\u003Cp\u003E\u003Ca data-title=\&CNN眼中的世界\& data-editable=\&true\& href=\&http:\u002F\u002Fkeras-cn.readthedocs.io\u002Fen\u002Flatest\u002Fblog\u002Fcnn_see_world\u002F\&\u003ECNN眼中的世界\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Ca data-title=\&花式自动编码器\& data-editable=\&true\& class=\&\& href=\&http:\u002F\u002Fkeras-cn.readthedocs.io\u002Fen\u002Flatest\u002Fblog\u002Fautoencoder\u002F\&\u003E花式自动编码器\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Ca data-title=\&面向小数据集构建图像分类模型\& data-editable=\&true\& class=\&\& href=\&http:\u002F\u002Fkeras-cn.readthedocs.io\u002Fen\u002Flatest\u002Fblog\u002Fimage_classification_using_very_little_data\u002F\&\u003E面向小数据集构建图像分类模型\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Ca data-title=\&在Keras模型中使用预训练的词向量\& data-editable=\&true\& href=\&http:\u002F\u002Fkeras-cn.readthedocs.io\u002Fen\u002Flatest\u002Fblog\u002Fword_embedding\u002F\&\u003E在Keras模型中使用预训练的词向量\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Ca data-title=\&将Keras作为tensorflow的精简}

我要回帖

更多关于 一亩三分地移民 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信