如何使用keras实现keras 数据增强强

对比学习用 Keras 搭建 CNN RNN 等常用神经网络 - 简书
对比学习用 Keras 搭建 CNN RNN 等常用神经网络
参考:这个网站上有很多机器学习相关的教学视频,推荐上去学习学习。
Keras 是一个兼容 Theano 和 Tensorflow 的神经网络高级包, 用他来组件一个神经网络更加快速, 几条语句就搞定了. 而且广泛的兼容性能使 Keras 在 Windows 和 MacOS 或者 Linux 上运行无阻碍.
今天来对比学习一下用 Keras 搭建下面几个常用神经网络:
自编码分类
它们的步骤差不多是一样的:
[导入模块并创建数据]
[建立模型]
[定义优化器]
[激活模型]
[训练模型]
[检验模型]
[可视化结果]
为了对比学习,用到的数据也差不多是一样的,所以本文只把注意力放在 2. [建立模型] 上面,其它步骤大同小异,可以去参考里提到的教学网站观看或者直接看源代码。
目的是对一组数据进行拟合。
1. 用 Sequential 建立 model2. 再用 model.add 添加神经层,添加的是 Dense 全连接神经层。
参数有两个,一个是输入数据和输出数据的维度,本代码的例子中 x 和 y 是一维的。
如果需要添加下一个神经层的时候,不用再定义输入的纬度,因为它默认就把前一层的输出作为当前层的输入。在这个例子里,只需要一层就够了。
# build a neural network from the 1st layer to the last layer
model = Sequential()
model.add(Dense(output_dim=1, input_dim=1))
2. RNN回归
我们要用 sin 函数预测 cos 数据,会用到 LSTM 这个网络。
RNN vs LSTM
1. 搭建模型,仍然用 Sequential。2. 然后加入 LSTM 神经层。
batch_input_shape
就是在后面处理批量的训练数据时它的大小是多少,有多少个时间点,每个时间点有多少个数据。
output_dim 意思是
LSTM 里面有二十个 unit。
return_sequences 意思是在每个时间点,要不要输出output,默认的是 false,现在我们把它定义为 true。如果等于 false,就是只在最后一个时间点输出一个值。
stateful,默认的也是 false,意义是批和批之间是否有联系。直观的理解就是我们在读完二十步,第21步开始是接着前面二十步的。也就是第一个 batch中的最后一步与第二个 batch 中的第一步之间是有联系的。
3. 有个不同点是 TimeDistributed。
在上一个回归问题中,我们是直接加 Dense 层,因为只在最后一个输出层把它变成一个全连接层。今天这个问题是每个时间点都有一个 output,那需要 dense 对每一个 output 都进行一次全连接的计算。
model = Sequential()
# build a LSTM RNN
model.add(LSTM(
batch_input_shape=(BATCH_SIZE, TIME_STEPS, INPUT_SIZE),
# Or: input_dim=INPUT_SIZE, input_length=TIME_STEPS,
output_dim=CELL_SIZE,
return_sequences=True,
# True: output at all steps. False: output as last step.
stateful=True,
# True: the final state of batch1 is feed into the initial state of batch2
# add output layer
model.add(TimeDistributed(Dense(OUTPUT_SIZE)))
adam = Adam(LR)
pile(optimizer=adam,
loss='mse',)
数据用的是 Keras 自带 MNIST 这个数据包,再分成训练集和测试集。x 是一张张图片,y 是每张图片对应的标签,即它是哪个数字。
简单介绍一下相关模块:
models.Sequential,用来一层一层一层的去建立神经层;
layers.Dense 意思是这个神经层是全连接层。
layers.Activation 激活函数。
optimizers.RMSprop 优化器采用 RMSprop,加速神经网络训练方法。
import numpy as np
np.random.seed(1337)
# for reproducibility
from keras.datasets import mnist
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.optimizers import RMSprop
在回归网络中用到的是 model.add 一层一层添加神经层,今天的方法是直接在模型的里面加多个神经层。好比一个水管,一段一段的,数据是从上面一段掉到下面一段,再掉到下面一段。
第一段就是加入 Dense 神经层。32 是输出的维度,784 是输入的维度。第一层传出的数据有 32 个feature,传给激活单元.
激活函数用到的是 relu 函数。经过激活函数之后,就变成了非线性的数据。
然后再把这个数据传给下一个神经层,这个 Dense 我们定义它有 10 个输出的 feature。同样的,此处不需要再定义输入的维度,因为它接收的是上一层的输出。
接下来再输入给下面的 softmax 函数,用来分类。
# Another way to build your neural net
model = Sequential([
Dense(32, input_dim=784),
Activation('relu'),
Dense(10),
Activation('softmax'),
4. CNN分类
数据仍然是用 mnist。
1. 建立网络第一层,建立一个 Convolution2D,参数有 filter 的数量。
filter 就是滤波器,用32个滤波器扫描同一张图片,每个滤波器会总结出一个 feature。每个滤波器会生成一整张图片,有32个滤波器就会生成32张代表不同特征的图片,
nb_row nb_col 代表这个滤波器有多少行多少列。
border_mode 代表这个滤波器在过滤时候用什么方式,这里我们用 same。因为是第一层,所以需要定义输入数据的维度,1, 28, 28 就是图片图片的维度。滤波器完成之后,会生成32层的数据,但是图片的长和宽是不变的,仍然是28×28。
之后再加一个 relu 激活函数。
# Another way to build your CNN
model = Sequential()
# Conv layer 1 output shape (32, 28, 28)
model.add(Convolution2D(
nb_filter=32,
border_mode='same',
# Padding method
dim_ordering='th',
# if use tensorflow, to set the input dimension order to theano ("th") style, but you can change it.
input_shape=(1,
# channels
# height & width
model.add(Activation('relu'))
2. Pooling 是一个向下取样的过程.它可以缩小生成出来的长和宽,高度不需要被压缩。
pool_size 是向下取样的时候,考虑多长多宽的图片。
strides 步长,是取完一个样之后要跳几步再取样,再跳几步再取样。
# Pooling layer 1 (max pooling) output shape (32, 14, 14)
model.add(MaxPooling2D(
pool_size=(2, 2),
strides=(2, 2),
border_mode='same',
# Padding method
3. 接下来建立第二个神经层
有 64 个 filter,5, 5 的长宽,再跟着一个激活函数。
再跟着一个 MaxPooling2D 取样。
# Conv layer 2 output shape (64, 14, 14)
model.add(Convolution2D(64, 5, 5, border_mode='same'))
model.add(Activation('relu'))
# Pooling layer 2 (max pooling) output shape (64, 7, 7)
model.add(MaxPooling2D(pool_size=(2, 2), border_mode='same'))
4. 接下来进入全联接层
用 Flatten 把卷出来的三维的层,抹平成二维的。
接下来就加一个 Dense 全联接层,抹平就是为了可以把这一个一个点全连接成一个层.
接着再加一个激活函数。
# Fully connected layer 1 input shape (64 * 7 * 7) = (3136), output shape (1024)
model.add(Flatten())
model.add(Dense(1024))
model.add(Activation('relu'))
5. 在第二个全连接层,输出 10 个 unit, 用 softmax 作为分类。
# Fully connected layer 2 to shape (10) for 10 classes
model.add(Dense(10))
model.add(Activation('softmax'))
5. RNN分类
RNN 是一个序列化的神经网,我们处理图片数据的时候,也要以序列化的方式去考虑。图片是由一行一行的像素组成,我们就一行一行地去序列化地读取数据。最后再进行一个总结,来决定它到底是被分辨成哪一类。
用到的参数含义:
TIME_STEPS 是要读取多少个时间点的数据,如果一次读一行需要读28次。
INPUT_SIZE 每次每一行读取多少个像素。
BATCH_SIZE 每一批训练多少张。
BATCH_INDEX 用来生成数据。
OUTPUT_SIZE 分类结果的长度,0到9,所以长度为 10。
CELL_SIZE 网络中隐藏层要放多少个 unit。LR 是学习率。
1. 用 Sequential 建立模型,就是一层一层地加上神经层。
# build RNN model
model = Sequential()
2. 加上 SimpleRNN。 batch_input_shape 就是在后面处理批量的训练数据时它的大小是多少,有多少个时间点,每个时间点有多少个像素。
# RNN cell
model.add(SimpleRNN(
# for batch_input_shape, if using tensorflow as the backend, we have to put None for the batch_size.
# Otherwise, model.evaluate() will get error.
batch_input_shape=(None, TIME_STEPS, INPUT_SIZE),
# Or: input_dim=INPUT_SIZE, input_length=TIME_STEPS,
output_dim=CELL_SIZE,
unroll=True,
3. 加 Dense 输出层。输出 output 长度为 10,接着用 softmax 激活函数用于分类。
# output layer
model.add(Dense(OUTPUT_SIZE))
model.add(Activation('softmax'))
4. 在训练的时候有一个小技巧,就是怎么去处理批量。输出结果时每 500 步输出一下测试集的准确率和损失。
需要用到 BATCH_INDEX,一批批地截取数据,下一批的时候,这个 BATCH_INDEX 就需要累加,后面的时间点和步长没有变化都是28。y 的批量和 x 的处理是一样的,只不过 y 只有二维,所以它只有两个参数。
后面有一个判断语句,如果这个 index 大于训练数据的总数,index 就变为 0,再从头开始一批批处理。
# training
for step in range(4001):
# data shape = (batch_num, steps, inputs/outputs)
X_batch = X_train[BATCH_INDEX: BATCH_INDEX+BATCH_SIZE, :, :]
Y_batch = y_train[BATCH_INDEX: BATCH_INDEX+BATCH_SIZE, :]
cost = model.train_on_batch(X_batch, Y_batch)
BATCH_INDEX += BATCH_SIZE
BATCH_INDEX = 0 if BATCH_INDEX &= X_train.shape[0] else BATCH_INDEX
if step % 500 == 0:
cost, accuracy = model.evaluate(X_test, y_test, batch_size=y_test.shape[0], verbose=False)
print('test cost: ', cost, 'test accuracy: ', accuracy)
6. 自编码分类
自编码,简单来说就是把输入数据进行一个压缩和解压缩的过程。原来有很多 Feature,压缩成几个来代表原来的数据,解压之后恢复成原来的维度,再和原数据进行比较。
做的事情是把 datasets.mnist 数据的 28×28=784 维的数据,压缩成 2 维的数据,然后在一个二维空间中可视化出分类的效果。
模型结构:
encoding_dim,要压缩成的维度。
# in order to plot in a 2D figure
encoding_dim = 2
# this is our input placeholder
input_img = Input(shape=(784,))
建立 encoded 层和 decoded 层,再用 autoencoder 把二者组建在一起。训练时用 autoencoder 层。
1. encoded 用4层 Dense 全联接层激活函数用 relu,输入的维度就是前一步定义的 input_img。接下来定义下一层,它的输出维度是64,输入是上一层的输出结果。在最后一层,我们定义它的输出维度就是想要的 encoding_dim=2。
2. 解压的环节,它的过程和压缩的过程是正好相反的。相对应层的激活函数也是一样的,不过在解压的最后一层用到的激活函数是 tanh。因为输入值是由 -0.5 到 0.5 这个范围,在最后一层用这个激活函数的时候,它的输出是 -1 到 1,可以是作为一个很好的对应。
# encoder layers
encoded = Dense(128, activation='relu')(input_img)
encoded = Dense(64, activation='relu')(encoded)
encoded = Dense(10, activation='relu')(encoded)
encoder_output = Dense(encoding_dim)(encoded)
# decoder layers
decoded = Dense(10, activation='relu')(encoder_output)
decoded = Dense(64, activation='relu')(decoded)
decoded = Dense(128, activation='relu')(decoded)
decoded = Dense(784, activation='tanh')(decoded)
# construct the autoencoder model
autoencoder = Model(input=input_img, output=decoded)
接下来直接用 Model 这个模块来组建模型输入就是图片,输出是解压的最后的结果。
# construct the encoder model for plotting
encoder = Model(input=input_img, output=encoder_output)
当我们想要看由 784 压缩到 2维后,这个结果是什么样的时候,也可以只单独组建压缩的板块,此时它的输入是图片,输出是压缩环节的最后结果。
最后分类的可视化结果:
我是 不会停的蜗牛 Alice85后全职主妇喜欢人工智能,行动派创造力,思考力,学习力提升修炼进行中欢迎您的喜欢,关注和评论!
我是 Alice
喜欢人工智能,行动派
创造力,思考力,学习力提升修炼进行中
欢迎志同道合的小...您还可以使用以下方式登录
当前位置:&>&&>&&>& > 使用Keras搭建深度残差网络
keras 深度学习 使用Keras搭建深度残差网络
残差网络简介MSRA的Kaiming He的残差网络绝对要算的上是2015年深度学习的大新闻了,深度残差网络将深度网络的层深拓展到一个不可思议的程度(152层),并在ImageNet竞赛中以绝对优势取得多个项目的冠军。后来,Kaiming He大神后来又把这一深度加到1000层的深度,能训练这么深的网络,残差网络的优势可见一斑。Kaiming He也因为这项工作获得了2016 CVPR的best paper award,这也是他获得的第二个CVPR best paper,顺便,据知乎爆料,Kaiming He还是广东省某年的高考状元哟! 果然神的成长路径我们只能仰望&&这篇文章的主要目的是提供一个Keras的深度残差网络的实现,理论上的分析大家尽可以去搜索别的文章,我们这里快速的过一下。训练深度网络是一件很头疼的事情,尽管已经有了ReLU等一系列手段加深网络,但梯度爆炸/弥散的问题并没有真正的解决。对深度学习而言,深度是比较值钱的,深度这个特性带来的性能提升也是最可观的。由于训练不到位,单纯的把layer叠起来深层网络的效果反而不如合适层数的较浅的网络效果好,这种情况称为网络退化。ResNet(深度残差网络)的出现为训练更深的网络提供了方法,深度残差网络其实是Highway网络的一种特殊情况,其主要特色是跨层连接。作者的一个假设是,相比于直接拟某个函数合y=f(x),试图去拟合g(x)=y?f(x),也就是残差,可能会更加容易,虽然为什么这样我也说不清楚,但效果看起来是不错。基本上,拟合残差也就是把网络按照下图的跨层连接搭起来:网络试图将x映射为F(x)+x,那么网络的映射F(x)自然就趋向于F(x)=0。kaiming he的github是/KaimingHe里面有残差网络的caffe实现和相应权重,使用caffe的同学可以直接下载。使用Keras的同学,就接着往下看了。Keras搭建残差网络(Res50)如果你看到这里,keras是什么想必不用我再介绍。开始之前,先打广告,欢迎访问keras中文文档学习keras,如果能动动小手在主页最下方打赏我一下,那就更加感谢了。Keras便于搭建网络的特点使得搭建网络大部分情况是一种&照猫画虎&的便捷工作,很开心kaiming he的github上提供了残差网络的可视化结构,如果你有双屏,完全可以一屏看图一屏搭结构,爽的不要不要的。我们选择kaiming he提供的最浅的50层残差网络进行搭建,它的图在这里:Res50观察一下网络图,首先跨层连接,肯定要使用泛型模型Model,整个网络有两种重复单元,一种是主path和shortcut都有卷积层,这种单元长这样:另一种主path有卷积,但是shortcut没有卷积,是直接连过来的。主路上的卷积核大小选3 & 3,再加一圈padding,就保证输入输出的大小是一样了。这个模块长这样:基本上,残差网络就是这两种模块反复交替。为什么会有这样呢,因为残差网络的关键步骤,跨层的合并需要保证x和F(x)的shape是完全一样的,否则它们加不起来。理解了这一点,我们开始用keras做实现,我们把输入输出大小相同的模块称为identity_block,而把输出比输入小的模块称为conv_block,首先,导入所需的模块:from keras.layers import mergefrom keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D, AveragePooling2Dfrom keras.layers.core import Dense, Activation,Flattenfrom keras.layers.normalization import BatchNormalizationfrom keras.models import Modelfrom keras.layers import Input这些层的位置还是比较好记的,注意BatchNormalization是在normalization这个模块里,这个模块目前就这一个层。另外注意我们导入的是函数merge而不是层Merge,这两个是不一样的,在构造泛型模型时要使用函数merge,因为泛型模型的整个构造过程就是对张量的操作。最后注意Input不是一个层,而是一个函数,函数的返回值是一个张量。主路的第一个和第三个卷积模块的核大小都是1,这点应该是借鉴的Inception网络,所以也有人说残差网络是Highway跟Inception的结合。然后我们先来编写identity_block,这是一个函数,接受一个张量为输入,并返回一个张量。为了使得输入的shape与经过主路的shape一样,我们可以在卷积前使用ZeroPadding2D进行padding,与其他深度学习框架不同,Keras的卷积层并不包含padding的选项,所以需要手动添加。另一种等价的方式是在卷积层中设置border_mode=&same&,则卷积层会自动保证输入输出具有相同的shape,代码如下:def identity_block(x,nb_filter,kernel_size=3):
k1,k2,k3 = nb_filter
out = Convolution2D(k1,1,1)(x)
out = BatchNormalization()(out)
out = Activation('relu')(out)
out = Convolution2D(k2,kernel_size,kernel_size,border_mode='same')(out)
out = BatchNormalization()(out)
out = Activation('relu')(out)
out = Convolution2D(k3,1,1)(out)
out = BatchNormalization()(out)
out = merge([out,x],mode='sum')
out = Activation('relu')(out)
return outk1,k2,k3分别是三个卷积层的核大小,注意caffe中的BN层分两步完成,所以结构图里除了BN还有一个scale,Keras里BN就是BN。小心主路的第三个卷积模块是没有relu的,relu在merge以后。类似的我们写conv_block,注意为了使得两个张量可以相加,shortcut这条路的滤波器数目一定和主路最后一个卷积模块的滤波器数目是一样的。def conv_block(x,nb_filter,kernel_size=3):
k1,k2,k3 = nb_filter
out = Convolution2D(k1,1,1)(x)
out = BatchNormalization()(out)
out = Activation('relu')(out)
out = out = Convolution2D(k2,kernel_size,kernel_size)(out)
out = BatchNormalization()(out)
out = Activation('relu')(out)
out = Convolution2D(k3,1,1)(out)
out = BatchNormalization()(out)
x = Convolution2D(k3,1,1)(x)
x = BatchNormalization()(x)
out = merge([out,x],mode='sum')
out = Activation('relu')(out)
return out剩下的事情就很简单了,数好identity_block和conv_block是如何交错的,照着网络搭就好了:inp = Input(shape=(3,224,224))out = ZeroPadding2D((3,3))(inp)out = Convolution2D(64,7,7,subsample=(2,2))(out)out = BatchNormalization()(out)out = Activation('relu')(out)out = MaxPooling2D((3,3),strides=(2,2))(out)out = conv_block(out,[64,64,256])out = identity_block(out,[64,64,256])out = identity_block(out,[64,64,256])out = conv_block(out,[128,128,512])out = identity_block(out,[128,128,512])out = identity_block(out,[128,128,512])out = identity_block(out,[128,128,512])out = conv_block(out,[256,256,1024])out = identity_block(out,[256,256,1024])out = identity_block(out,[256,256,1024])out = identity_block(out,[256,256,1024])out = identity_block(out,[256,256,1024])out = identity_block(out,[256,256,1024])out = conv_block(out,[512,512,2048])out = identity_block(out,[512,512,2048])out = identity_block(out,[512,512,2048])out = AveragePooling2D((7,7))(out)out = Flatten()(out)out = Dense(1000,activation='softmax')(out)model = Model(inp,out)这里需要注意的几个点是,MaxPooling2D按照网络图有一个长为2的stride,用strides参数指定,同样的功能在Convlution2D中则称为subsample,这个命名冲突非常的不友好,请多加注意。然后网络的最后一层前的Pool5是一个AveragePooling而不是MaxPooling。此外,在从卷积层到Dense层过度时需要加一个Flatten将featuremap压扁。网络搭建完毕后,很多同学希望能够使用kaiming he的预训练权重进行初始化,这样在其上进行transfer learning等任务会非常方便,可惜的是kaiming he提供的权重是caffe版本的,下一节我们来讲如何把caffe的权重文件变为Keras广泛使用的,更加友好的h5文件。载入Res50的权重深呼吸,准备好,我们的故事从这里才刚刚开始深呼吸,准备好,我们的故事从这里才刚刚开始深呼吸,准备好,我们的故事从这里才刚刚开始准备好了吗?好的让我们开始第一个问题,权重格式转换我们遇到的第一个问题是是如何将caffe的权重文件转换为.h5文件。这里有两个思路,第一个是利用mxnet提供的工具caffe_converter,安装mxnet后可以利用这个脚本将caffe的模型和权重转换为mxnet的.params文件,.params文件相对于.caffemodel文件就要友好的多,其本质就是一个字典,利用mxnet读入后保存为h5即可。第二个思路是利用caffe读取,这个方法应该说是更简单的&&虽然配置caffe要麻烦一些。首先读入模型,然后依次将网络的params参数取出来存入h5文件中,脚本如下:import caffeimport numpy as npimport h5pymodel_file = 'ResNet-50-deploy.prototxt'pretrained_model = 'ResNet-50-model.caffemodel'net = caffe.Net(model_file,pretrained_model,caffe.TEST)f = h5py.File('resnet50.h5','w')for name in net.params.keys():
if len(net.params[name])==1:
print &%s has only one params&%name
grp = f.create_group(name)
grp.create_dataset('weights',data=net.params[name][0].data)
grp.create_dataset('bias',data=np.zeros(shape=(net.params[name][0].data.shape[0],)))
grp = f.create_group(name)
grp.create_dataset('weights',data=net.params[name][0].data)
grp.create_dataset('bias',data=net.params[name][1].data)f.close()运行完毕后,我们得到了Res50Net.h5这个权重文件。这里注意的是,ResNet50的所有res开头的卷积都是没有bias的!这点实在很坑,在最后载入权重的时候才发现。这里的策略是,对于没有bias的网络生成一个全0的bias,虽然稍微浪费一点空间,但对keras载入权重可是大大的方便。第二个问题,名字配准显然,我们做出来的h5文件不可能用model.load_weights()这种语句一条就载入,因为层的名字无法对应起来,打印一下h5文件的keys可以发现,得到caffe里各个层的名字与网络框图中画的是一样的(自然而然),类似于res2a_branch2a, bn2b_branch2b这种,我们有两个选择,要么把h5文件的名字变的符合我们的网络,要么我们把网络的层起一个和模型一样的名字。显然,第二种方法可行性更高一点,因为原网络的命名也是有规律的,除去开头的几层和结尾的几层,剩下的层命名规则都很一致,形如:[type][stage][block]_branch[branch][layer]首先是层类型,type,一共三种:res:卷积层 bn:BN层 scale:BN层注意caffe的BN是分两层实现的,这点给权重转换带来了很大麻烦,我们稍后说。stage是大的阶段,每个阶段由一个conv_layer领起,在上面搭建网络的时候我们用空格将各个阶段代码分开,很容易分辨。block是在一个stage中当前层处于的块的位置,block就是我们实现的时候identity_block或conv_block之一,用abcd表示。然后一个下划线,branch后接branch的分支号,左分支(shortcut)是1,右分支(main path)是2,对于identity_block而言就只有2分支没有1分支。最后是该层在该分支的具体位置layer,用abcd表示。例如,res2a_branch2a表明这是一个卷积层,位于第二个stage的a(第一个)block中,是该block的右分支的第a层。此外,注意左分支只有一组网络,因此最后面没有abc的标注,直接就是res2a_branch1观察到这种规律后,我们可以在搭建网络时自动生成对应的名字,这样,我们的代码就要改一改了,需要接收一个标明stage和block号的参数。当然,没有参数的层,比如relu层,我们就不用取名字了。我们小心的为我们的block添加stage和block的关键字参数,为了篇幅不那么长,我们这里只示范一个:def conv_block(x,nb_filter,stage,block,kernel_size=3):
k1,k2,k3 = nb_filter
out = Convolution2D(k1,1,1,name='res'+str(stage)+block+'_branch2a')(x)
out = BatchNormalization(name='bn'+str(stage)+block+'_branch2a')(out)
out = Activation('relu')(out)
out = out = Convolution2D(k2,kernel_size,kernel_size,border_mode='same',name='res'+str(stage)+block+'_branch2b')(out)
out = BatchNormalization(name='bn'+str(stage)+block+'_branch2b')(out)
out = Activation('relu')(out)
out = Convolution2D(k3,1,1,name='res'+str(stage)+block+'_branch2c')(out)
out = BatchNormalization(name='bn'+str(stage)+block+'_branch2c')(out)
x = Convolution2D(k3,1,1,name='res'+str(stage)+block+'_branch1')(x)
x = BatchNormalization(name='bn'+str(stage)+block+'_branch1')(x)
out = merge([out,x],mode='sum')
out = Activation('relu')(out)
return out建立网络的过程中,我们要在每个stage的block搭建中传入当前stage是第几个stage,以及当前的block是该stage的哪个block,这样在block内部,就可以自动生成对应的名字了。同样为了篇幅,我们只示范第一个stage怎么搭。inp = Input(shape=(3,224,224))out = ZeroPadding2D((3,3))(inp)out = Convolution2D(64,7,7,subsample=(2,2),name='conv1')(out)out = BatchNormalization(name='bn_conv1')(out)out = Activation('relu')(out)out = MaxPooling2D((3,3),strides=(2,2))(out)out = conv_block(out,[64,64,256],2,'a')out = identity_block(out,[64,64,256],2,'b')out = identity_block(out,[64,64,256],2,'c')这样,我们的网络就有和.h5一样的名字了,这为载入权重铺平道路。将h5的权重载入到网络中此时,运行model.load_weights()即可。开玩笑的,真这么简单我不好意思在本文末尾收钱了(是的本文末尾有个二维码,本文我自认为还有一定价值,可以收一波打赏,给多少看您心意)载入权重是这样的,首先打开h5文件,然后对于model中的层,如果层名以&res&开头,那么它就是个卷积层,由于我们在制作h5文件时已经很有先见之明(并没有,都是发现出错才改的)的把没有bias的情况设置了全0bias,这一步就调用set_weights载入h5文件的同名参数即可。麻烦的是BN层。先复习一下BatchNormalization,这个技术可以说是深度学习的里程碑式的技术,BN基本上已经和Dropout一样,成为各种神经网络的标配了,甚至已经在很多场合取代了Dropout,比如本文的残差网络,就没有Dropout。这个技术的核心思想是在网络运算过程中,对每个batch,将层的计算结果重新规范化,然后再加以放缩以保持层学习到的特征性质。整个BN的技术分为两步,第一步是去均值,除标准差进行规范化,第二部是乘&,加&,完成放缩。技术细节这里就不多讲了。坑爹的是,Caffe里这个过程是由两个层完成的,BN层只完成规范化,还有一个scale层完成放缩。而在keras中,BN就是BN,一层内完成两步。查看Keras的BatchNormalization的权重shape,可以看到BN层的权重shape是4,也就是说该权重是一个含有4个np array的list,而caffe的BN和scale权重的shape都是2,这是理所应当的。那么,拿一个list把caffe的BN和scale两层的权重装起来就可以了吗?不是的,如果这么简单,我就不好意思在末尾收钱了。这里有一明一暗两个陷阱先说暗的,BN层的四个参数分别是均值,标准差,&,和&,拿一个list把他们装起来,谁先谁后呢?先规范化再scale,怎么看都该是按上面的顺序排列的,然而在keras的BatchNormalization源代码中,这个过程是这样的:def __init__(self, epsilon=1e-6, mode=0, axis=-1, momentum=0.9,
weights=None, beta_init='zero', gamma_init='one', **kwargs):
self.beta_init = initializations.get(beta_init)
self.gamma_init = initializations.get(gamma_init)
self.epsilon = epsilon
self.mode = mode
self.axis = axis
self.momentum = momentum
self.initial_weights = weights #这里将传入的参数保存起来
if self.mode == 0:
self.uses_learning_phase = True
super(BatchNormalization, self).__init__(**kwargs)def build(self, input_shape):
self.input_spec = [InputSpec(shape=input_shape)]
shape = (input_shape[self.axis],)
self.gamma = self.gamma_init(shape, name='{}_gamma'.format(self.name))
self.beta = self.beta_init(shape, name='{}_beta'.format(self.name))
self.trainable_weights = [self.gamma, self.beta]
self.running_mean = K.zeros(shape,
name='{}_running_mean'.format(self.name))
self.running_std = K.ones(shape,
name='{}_running_std'.format(self.name))
self.non_trainable_weights = [self.running_mean, self.running_std]
if self.initial_weights is not None:
self.set_weights(self.initial_weights)#如果给了外加参数,则调用set_weights设置参数
del self.initial_weights再跳入set_weights,看看它是如何设置参数的:params = self.trainable_weights + self.non_trainable_weights注意了,set_weights是将训练的权重和非训练权重合并在一起的,可训练的在前,不可训练的在后,BN的可训练权重是&和&,不可训练的权重是均值和标准差,因为这两个值实际上是根据样本计算出来的。所以添加的顺序应该是&和&在前,均值和标准差在后。如果弄反了不会报任何错误,因为这四个参数的shape完全一致!所以这是一个暗陷阱&&MB我都不知道当时怎么发现的,反正发现以后一身冷汗,就凭这个你们都该给我打赏!先把载入权重的代码贴出来,再说明陷阱:f = h5py.File('resnet50.h5','r')for layer in model.layers:
if layer.name[:3]=='res':
layer.set_weights([f[layer.name]['weights'][:],f[layer.name]['bias'][:]])
elif layer.name[:2]=='bn':
scale_name = 'scale'+layer.name[2:]
weights = []
weights.append(f[scale_name]['weights'][:])
weights.append(f[scale_name]['bias'][:])
weights.append(f[layer.name]['weights'][:])
weights.append(f[layer.name]['bias'][:])
layer.set_weights(weights)
except Exception:
print layer.name载入的逻辑和我们刚才说的一样,res开头的就直接载入,bn开头的就找到对应的bn层和scale层,组合为一个list后载入。如果载入中出了什么问题,就把出错的层打印出来。如果你按照我上面的代码搭建了网络,那么你的程序会打印出所有bn层,这就是明陷阱。错误的原因是数据的shape不匹配,以第一个bn层&bn_conv&为例,打印出它的shape,再打印出我们网络的这一层需要的shapeprint f['bn_conv1']['weights'][:].shapeprint model.get_layer('bn_conv1').get_weights()[0].shape结果是:(64,)(112,)这个问题的bug非常伤脑筋,主要错误在于BN层有一个参数叫axis,这个参数决定了我们要沿着哪个轴对数据进行批规范化,BN参数的shape就是由数据在axis轴的shape决定的。说实话,我不清楚kaiming he的这个resnet50是从哪个轴规范化的,但在这里,因为caffe给出的shape是64,所以我们只能选axis=1,输入数据(None,64,112,112)只有在这个轴上规范化的shape才为64。其他的BN层也是一样,所以估计原网络就是这么干的。axis=1意味着按channel轴进行规范化。只需要在是上面代码中所有出现BN的地方传入axis=1,网络即可正确载入,至此本文的工作完毕。就爱阅读网友整理上传,为您提供最全的知识大全,期待您的分享,转载请注明出处。
欢迎转载:
推荐:    

我要回帖

更多关于 keras 读取数据 的文章

 

随机推荐