如何用keras 时间序列预测训练之后的weights预测

内容字号:
段落设置:
字体设置:
keras示例程序解析(3):验证网络siamese
你们是懂我的,一般来说,在写完一篇文章后,我会悠然悠然的过个一个月,才会有内心的恐慌感【啊再不更新的话他们一定认为我根本没有学习】来督促我继续更新。
再说了,看着以往文章的点赞量和【赞赏】金额(主要是这个!),我老婆的可乐钱和薯片钱都出不来,凭什么要使劲写!
但是,今天,我要打破这个传统了,首先,上一篇文字@爱可可-爱生活 老师翻了牌子,我十分感激,并且诚惶诚恐,这让我深知不好好写文章对不起爱可可老师的厚爱。
当然,这并不是主要的,主要的是上一篇文章有这样一条暖心的评论:
啊~就像冬日里的一缕阳光,温暖了我在寒风中踽踽独行的心,还有什么能比被读者所【承认】更令人心满意足呢?比起这来,我对【赞赏】金额的耿耿于怀,显得那么的市侩和俗气(但是并不是说我就不要了啊!!赞赏我才是核心动力!)。
于是我准备加班一篇,以报答这位【没有关注我】也【没有关注我的专栏】的读者,以及更多【点赞完就算】的读者们。
本来这篇准备写一下神经网络可视化的,但是keras的神经网络可视化过于简单,体现不出我的诚意,所以今天学习一个非常实用和比较有代码技巧的一个网络siamese有同学希望我上一些nlp的文章,不要老在cv上打转。说实话nlp我懂得不多,真要写的话对着代码分析也不是不可以,但是终究有点心怯,我还是先把这一亩三分地耕好。
阅读本文你将学到:
Siamese网络用于验证类问题的原理
如何使用泛型模型Model
如何pair-wise的训练一个网络
如何自定义损失函数
Siamese网络与验证类问题
首先,这个英文单词怎么读呢~来跟我读:赛~密~死,中文意思是暹罗,所以这个网络叫暹罗网络。
胡说的,siamese确实是暹罗,但是wiki还给出另一个意思:
Siamese, an informal term for conjoined or fused, including with a two-cylinder motorcycle's exhaust pipes
也就是很像的容易混淆的, .Siamese网络用于判断两个目标是不是&很像&,也就是常说的验证类问题,比如人脸验证, 指纹验证等。
今天这个例子我们来判断两个数字是否是同一个,相关文献是杨乐坤(LeCun)于06年发表的文章&Dimensionality Reduction by Learning an Invariant Mapping。
Simese网络长啥样呢?确切的说Simese是一种处理验证类问题的方法,而不是一个具体的网络,它的思路非常直接:
(1)用一个网络提取特征,这个特征要具有足够的鲁棒性和判别性,也就是对一张图片而言,旋转、扭曲、加噪声等变换后,图像出来的特征要相似。对不同图片而言,他们的特征要不相似。这一点跟图片分类的要求是一致的。
(2)通过某个标准来衡量两张图片的相似性,进行是/不是的判决
这里我们用一个简单的MLP作为特征提取的网络,所以整个网络的图是:
显而易见,既然只是对特征进行相似度判断,那么只要用一个网络就可以了。网络的作用就是提取特征。
但是如果用一个网络的话,就要分别调用两次前向运算,然后计算损失,然后再回传回去。整个过程需要人工控制,写起来非常麻烦。所以还是应该用两个网络做成一个多输入的模型。
但是,确实没有必要训练两个网络啊,本来就只是一个网络的问题。
最终的方案是,&形式上是两个网络,但两个网络共享同一套权重,所以本质上是一个网络&。
这是结构,再说训练。
网络的作用是判断两张输入图片是否相似,怎么训练呢?很容易想到,训练样本的正例是一对相同标签的图片,训练样本的负例是一对标签不同的图片。这种用成对成对的样本训练的方式一般称为pair-wise的训练,这与常规的网络训练方式是很不同的(更凶狠的还有3个一组的训练,四个一组的训练&&),如何组织训练样本,我们稍后代码上说。
最后一个问题是损失函数,这个其实是Siamese比较关键的地方。感性的想,损失函数应该满足下面两个性质:
对两张相同标签的图片,损失函数的值应该尽量小
对两张不同标签的图片,损失函数的值应该尽量大
不妨设两个图片的输出特征分别为&&和&&,它们两个的相似度我们不妨用欧式距离来表示,就记作&&吧,那么,我们的损失函数要做到,当&&比较小(特征相似),并且即两个图片确实标签相同时,值比较小。&&比较小,但两个图片标签却不一样时输出比较大,&&大的时候也是同样的道理。
当然,深度学习中的损失函数嘛,一定要可导,这个是必然的。
Siamese网络的损失函数定义如下:
看着麻烦,其实不麻烦。Y是标签,只有两种情况,=1表示两个图片标签不同,=0表示相同。对于Y=0的情况,第二项为0,第一项直接变成两个特征的距离平方,显然是距离越近值越小,距离越远值越大。
对Y=1的情况,第一项为0,第二项是一个hinge loss,多了个平方而已,学过SVM的都见过。当距离小于m的时候,就会获得一个的惩罚,但是当距离大于m的时候,就没有惩罚了。距离越大惩罚越小,刚好对应两张图片类别不同时我们希望的情况。
我们下面要进行Keras代码实现,盘点一下,目前的难点主要有三个:
如何进行权值共享
如何进行pair-wise的训练
如何定义损失函数
第一步还是准备数据。
本例所用的数据集是MNIST手写数字数据集,我想不知道这个数据集的应该很少,所以我就不多嘴介绍了。通过keras自带的dataset模块可以轻松导入:
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(60000, 784) #将图片向量化
X_test = X_test.reshape(10000, 784)
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255 # 归一化
X_test /= 255
我们这里的网络是一个简单全连接网络,所以需要讲原来的2D图片向量化为一条向量,一共是6W的训练集和1W的测试集。
然后我们搭建网络,如何在Keras中搭建一个权值共享的网络呢?有一个概念我每次写文章都要强调,那就是Keras模型/层是张量到张量的映射,只要保证两个输入经由同样的计算图得到输出,那么显然就可以达到权值共享的作用&&实际上它们就是一个两输入一输出的网络。
首先搭建一个简单的全连接网络:
def create_base_network(input_dim):
'''Base network to be shared (eq. to feature extraction).
'''
seq = Sequential()
seq.add(Dense(128, input_shape=(input_dim,), activation='relu'))
seq.add(Dropout(0.1))
seq.add(Dense(128, activation='relu'))
seq.add(Dropout(0.1))
seq.add(Dense(128, activation='relu'))
return seq
注意我们将它写成了函数,下面调用这个函数得到一个全连接的模型,我们用这个模型进行特征提取:
base_network = create_base_network(input_dim)
多输入的模型需要用功能更强大的Model进行建模,首先声明两个输入张量,然后将它们连接在上面的模型之前,最后搞一个输出出来:
input_a = Input(shape=(input_dim,))
input_b = Input(shape=(input_dim,))
processed_a = base_network(input_a)
processed_b = base_network(input_b)
distance = Lambda(euclidean_distance, output_shape=eucl_dist_output_shape)([processed_a, processed_b])
model = Model(input=[input_a, input_b], output=distance)
等&&等等,base_network不是一个模型吗?那base_network(input_a)又是什么鬼?
问这个问题的人,肯定没有看过本专栏的&
Keras的Layer也好,model也好,规定的都是输入张量到输出张量的映射,它们都是像函数一样callable的。意思就是,输入张量input_a经过一个网络base_network的作用和,得到了输出张量processed_a。
上面的代码还使用了一个Lambda层来计算两个特征的欧式距离,Lambda层通常用来完成功能比较简单,不含有可训练参数的计算需求。如果Lambda层的输出张量的shape改变了,需要设置输出变量的shape,或传入一个用于计算输出张量的shape的函数。
上面Lambda层所用到的计算欧式距离和计算输出shape的函数定义如下,因为它们是计算图的一部分,显然需要用纯tensor语言编写:
def euclidean_distance(vects):
x, y = vects
return K.sqrt(K.sum(K.square(x - y), axis=1, keepdims=True))
def eucl_dist_output_shape(shapes):
shape1, shape2 = shapes
return (shape1[0], 1)
最后,调用Model将计算图概括起来,包装为一个真正的Keras model。至此,我们的模型就搭建完毕了。
模型搭建完毕,还需要编写刚才的loss,编写自己的loss是一门技术活,主要是符号式语言编写起来太蛋疼,写完还不怎么拿得准。但这真没办法,loss函数作为计算图的一部分,是必须用这种语言写好的。
def contrastive_loss(y_true, y_pred):
'''Contrastive loss from Hadsell-et-al.'06
/exdb/publis/pdf/hadsell-chopra-lecun-06.pdf
'''
margin = 1
return K.mean(y_true * K.square(y_pred) + (1 - y_true) * K.square(K.maximum(margin - y_pred, 0)))
仔细看看,return的那个东西就是我们刚才定义的loss function,hinge loss的参数m设置为1.
然后我们需要组织数据,生成一对对的正样本和一对对的负样本以供训练。正样本对从来自同一标签的样本集中抽取,负样本对从不同标签的样本集中抽取。
首先获得各个类别的样本下标,即按照类别来对样本集分组:
digit_indices = [np.where(y_train == i)[0] for i in range(10)]
digits_indicse的计算结果应为list of numpy,依次是每个类别的样本的下标。我们根据这些下标来选出正样本和负样本:
def create_pairs(x, digit_indices):
'''Positive and negative pair creation.
Alternates between positive and negative pairs.
'''
pairs = []
labels = []
n = min([len(digit_indices[d]) for d in range(10)]) - 1
for d in range(10):
for i in range(n):
z1, z2 = digit_indices[d][i], digit_indices[d][i+1]
pairs += [[x[z1], x[z2]]]
inc = random.randrange(1, 10)
dn = (d + inc) % 10
z1, z2 = digit_indices[d][i], digit_indices[dn][i]
pairs += [[x[z1], x[z2]]]
labels += [1, 0]
return np.array(pairs), np.array(labels)
这部分是纯Python代码,计算过程见注释,比较诡异的是这行代码:
n = min([len(digit_indices[d]) for d in range(10)]) - 1
这里n是所有类别的样本数目的最小值再减1,注意到n在循环体中作为循环范围:
for d in range(10):
for i in range(n):
z1, z2 = digit_indices[d][i], digit_indices[d][i+1]
将n设置为所有类别样本数目之最小值,可以保证对所有类别而言,生成的正样本数目和负样本数目都是一样的,从而保证整个训练集的类别均衡。-1是因为在循环中需要访问[i+1],这是为了保证不超出范围。
组织样本的写法有多种多样,生成的样本数目也有所不同。你完全可以编写一种能生成出更多样本的方式,例如正样本不仅仅取相邻的[i]和[i+1],而是遍历所有的组合可能性,这些完全可以按照实际需求编写。
训练集和测试集的政府样本对均照此生成:
digit_indices = [np.where(y_train == i)[0] for i in range(10)]
tr_pairs, tr_y = create_pairs(X_train, digit_indices)
digit_indices = [np.where(y_test == i)[0] for i in range(10)]
te_pairs, te_y = create_pairs(X_test, digit_indices)
代码的最后是对整个网络的训练,相比较而言,这部分已经不是什么值得谈的东西了,简单放在下面就好:
rms = RMSprop()
pile(loss=contrastive_loss, optimizer=rms)
model.fit([tr_pairs[:, 0], tr_pairs[:, 1]], tr_y,
validation_data=([te_pairs[:, 0], te_pairs[:, 1]], te_y),
batch_size=128,
nb_epoch=nb_epoch)
pred = model.predict([tr_pairs[:, 0], tr_pairs[:, 1]])
tr_acc = compute_accuracy(pred, tr_y)
pred = model.predict([te_pairs[:, 0], te_pairs[:, 1]])
te_acc = compute_accuracy(pred, te_y)
print('* Accuracy on training set: %0.2f%%' % (100 * tr_acc))
print('* Accuracy on test set: %0.2f%%' % (100 * te_acc))
哦,这里的准确率是自己算的,因为这种网络的准确率keras好像没有定义过,需要一个阈值来判断最终预测结果是正例还是负例,非常简单,贴一下:
def compute_accuracy(predictions, labels):
'''Compute classification accuracy with a fixed threshold on distances.
'''
return labels[predictions.ravel() & 0.5].mean()
全部代码请参考keras examples里的&,噫,看后缀有个_graph我怀疑这个例子在上古时代(Keras还有Graph这个类型的时候)就有了,也算缅怀一下那逝去的旧时光吧~
分享给小伙伴们:
本类最热新闻
48小时最热
010203040506070891011
CopyRight © 2015- , All Rights Reserved.}

我要回帖

更多关于 时间序列预测案例 的文章

更多推荐

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

点击添加站长微信