笔画最多的字900000画,笔画最多字9亿画

首页 > 大全 > 作者:YD1662022-12-11 23:04:36

对于Omniglot中的964类(每类20个样本),这会有185,849,560个可能的配对,这是巨大的!然而,孪生网络需要相同类的和不同类的配对都有。每类E个训练样本,所以每个类别有

笔画最多的字900000画,笔画最多字9亿画(21)

对,这意味着这里有

笔画最多的字900000画,笔画最多字9亿画(22)

个相同类别的配对。

对于Omniglot有183,160对。即使 183,160对已经很大了,但只是所有可能配对的千分之一,因为相同类别的配对数量随着E平方级的增大,但是随着C是线性增加。这个问题非常重要,因为孪生网络训练的时候,同类别和不同类别的比例应该是1:1。或许,它表明逐对训练在那种每个类别有更多样本的数据集上更容易训练。

代码

如果你更喜欢用 jupyter notebook,这里是传送门:https://github.com/sorenbouma/keras-oneshot

下面是模型定义,如果你见过keras,那很容易理解。我只用Sequential来定义一次孪生网络,然后使用两个输入层来调用它,这样两个输入使用相同的参数。然后我们把它们使用绝对距离合并起来,添加一个输出层,使用二分类交叉熵损失来编译这个模型。

from keras.layers import Input, Conv2D, Lambda, merge, Dense, Flatten,MaxPooling2D
from keras.models import Model, Sequential
from keras.regularizers import l2
from keras import backend as K
from keras.optimizers import SGD,Adam
from keras.losses
import binary_crossentropy
import numpy.random as rng
import numpy as np
import os
import dill as pickle
import matplotlib.pyplot as plt
from sklearn.utils import shuffle

def W_init(shape,name=None): """Initialize weights as in paper""" values = rng.normal(loc=0,scale=1e-2,size=shape) return K.variable(values,name=name)#//TODO: figure out how to initialize layer biases in keras.

def b_init(shape,name=None): """Initialize bias as in paper""" values=rng.normal(loc=0.5,scale=1e-2,size=shape) return K.variable(values,name=name)

input_shape = (105, 105, 1)
left_input = Input(input_shape)
right_input = Input(input_shape)
#build convnet to use in each siamese 'leg'
convnet = Sequential
convnet.add(Conv2D(64,(10,10),activation='relu',input_shape=input_shape, kernel_initializer=W_init,kernel_regularizer=l2(2e-4)))
convnet.add(MaxPooling2D)
convnet.add(Conv2D(128,(7,7),activation='relu', kernel_regularizer=l2(2e-4),kernel_initializer=W_init,bias_initializer=b_init))
convnet.add(MaxPooling2D)
convnet.add(Conv2D(128,(4,4),activation='relu',kernel_initializer=W_init,kernel_regularizer=l2(2e-4),bias_initializer=b_init))
convnet.add(MaxPooling2D)
convnet.add(Conv2D(256,(4,4),activation='relu',kernel_initializer=W_init,kernel_regularizer=l2(2e-4),bias_initializer=b_init))
convnet.add(Flatten)
convnet.add(Dense(4096,activation="sigmoid",kernel_regularizer=l2(1e-3),kernel_initializer=W_init,bias_initializer=b_init))
#encode each of the two inputs into a vector with the convnet
encoded_l = convnet(left_input)
encoded_r = convnet(right_input)
#merge two encoded inputs with the l1 distance between them
L1_distance = lambda x: K.abs(x[0]-x[1])
both = merge([encoded_l,encoded_r], mode = L1_distance, output_shape=lambda x: x[0])
prediction = Dense(1,activation='sigmoid',bias_initializer=b_init)(both)
siamese_net = Model(input=[left_input,right_input],output=prediction)
#optimizer = SGD(0.0004,momentum=0.6,nesterov=True,decay=0.0003)

optimizer = Adam(0.00006)
#//TODO: get layerwise learning rates and momentum annealing scheme described in paperworking

siamese_net.compile(loss="binary_crossentropy",optimizer=optimizer)

siamese_net.count_params

原论文中每个层的学习率和冲量都不相同--我跳过了这个步骤,因为使用keras来实现这个太麻烦了,并且超参数不是该论文的重点。Koch等人增加向训练集中增加失真的图像,使用150,000对样本训练模型。因为这个太大了,我的内存放不下,所以我决定使用随机采样的方法。载入图像对或许是这个模型最难实现的部分。因为这里每个类别有 20个样本,我把数据重新调整为 N_classes×20×105×105的数组,这样可以很方便的来索引。

class Siamese_Loader: """For loading batches and testing tasks to a siamese net""" def __init__(self,Xtrain,Xval): self.Xval = Xval self.Xtrain = Xtrain self.n_classes,self.n_examples,self.w,self.h = Xtrain.shape self.n_val,self.n_ex_val,_,_ = Xval.shape def get_batch(self,n): """Create batch of n pairs, half same class, half different class""" categories = rng.choice(self.n_classes,size=(n,),replace=False) pairs=[np.zeros((n, self.h, self.w,1)) for i in range(2)] targets=np.zeros((n,)) targets[n//2:] = 1 for i in range(n): category = categories[i] idx_1 = rng.randint(0,self.n_examples) pairs[0][i,:,:,:] = self.Xtrain[category,idx_1].reshape(self.w,self.h,1) idx_2 = rng.randint(0,self.n_examples) #pick images of same class for 1st half, different for 2nd category_2 = category if i >= n//2 else (category rng.randint(1,self.n_classes)) % self.n_classes pairs[1][i,:,:,:] = self.Xtrain[category_2,idx_2].reshape(self.w,self.h,1) return pairs, targets def make_oneshot_task(self,N): """Create pairs of test image, support set for testing N way one-shot learning. """ categories = rng.choice(self.n_val,size=(N,),replace=False) indices = rng.randint(0,self.n_ex_val,size=(N,)) true_category = categories[0] ex1, ex2 = rng.choice(self.n_examples,replace=False,size=(2,)) test_image = np.asarray([self.Xval[true_category,ex1,:,:]]*N).reshape(N,self.w,self.h,1) support_set = self.Xval[categories,indices,:,:] support_set[0,:,:] = self.Xval[true_category,ex2] support_set = support_set.reshape(N,self.w,self.h,1) pairs = [test_image,support_set] targets = np.zeros((N,)) targets[0] = 1 return pairs, targets def test_oneshot(self,model,N,k,verbose=0): """Test average N way oneshot learning accuracy of a siamese neural net over k one-shot tasks""" pass n_correct = 0 if verbose: print("Evaluating model on {} unique {} way one-shot learning tasks ...".format(k,N)) for i in range(k): inputs, targets = self.make_oneshot_task(N) probs = model.predict(inputs) if np.argmax(probs) == 0: n_correct =1 percent_correct = (100.0*n_correct / k) if verbose: print("Got an average of {}% {} way one-shot learning accuracy".format(percent_correct,N)) return percent_correct

下面是训练过程了。没什么特别的,除了我监测的是验证机精度来测试性能,而不是验证集上的损失。

evaluate_every = 7000
loss_every=300
batch_size = 32
N_way = 20
n_val = 550

siamese_net.load_weights("PATH")best = 76.0

for i in range(900000): (inputs,targets)=loader.get_batch(batch_size) loss=siamese_net.train_on_batch(inputs,targets) if i % evaluate_every == 0: val_acc = loader.test_oneshot(siamese_net,N_way,n_val,verbose=True) if val_acc >= best: print("saving") siamese_net.save('PATH') best=val_acc if i % loss_every == 0: print("iteration {}, training loss: {:.2f},".format(i,loss))

结果

一旦学习曲线变平整了,我使用在 20 类验证集合上表现最好的模型来测试。我的网络在验证集上得到了大约 83%的精度,原论文精度是93%。或许这个差别是因为我没有实现原论文中的很多增强性能的技巧,像逐层的学习率/冲量,使用数据失真的数据增强方法,贝叶斯超参数优化,并且我迭代的次数也不够。我并不担心这个,因为这个教程侧重于简要介绍单样本学习,而不是在那百分之几的分类性能上钻牛角夹。这里不缺乏这方面的资源。

我很好奇,模型的精度是怎么随样本的类别数目N变化的,所以我把它画了出来,与1近邻,随机猜测以及模型在训练集上的精度的比较。

笔画最多的字900000画,笔画最多字9亿画(23)

如图所示,验证集上的精度要比训练集上差一些,尤其是当N的数量很多的时候,这里面肯定有过拟合的问题。我们也想测试一下传统的正则化方法(像dropout)在验证集与训练集完全不同的时候的表现。对于较大的N,它比我期待中的要好,在50-60种类别上,仍旧有65%的平均精度。

讨论

现在我们只是训练了一个来做鉴别相同还是不同的二分类网络。更重要的是,我们展现了模型能够在没有见过的字母表上的20类单样本学习的性能。当然,这不是使用深度学习来做单样本学习的唯一方式。

正如我前面提到的,我认为这个孪生网络的最大缺陷是它要拿测试图像与训练集中图像逐个比较。当这个网络将测试图像与任何图像x1相比,不管训练集是什么,都是相同的。这很愚蠢,假如你在做单样本学习任务,你看到一张图片与测试图像非常类似。然而,当你看到训练集中另外一张图片也与测试集非常相似,你就会对它的类别没那么自信了。训练目标与测试目标是不同的,如果有一个模型可以很好的比较测试图片与训练集,并且使用仅仅有一个训练图片与之拥有相同类别的限制,那模型会表现的更好。

Matching Networks for One Shot learning 这篇论文就是做这个的。它们使用深度模型来端到端的学习一个完整的近邻分类器,而不是学习相似度函数,直接在单样本任务上训练,而不是在一个图像对上。Andrej Karpathy’s notes 很好的解释了这个问题。因为你正在学习机器分类,所以你可以把他视为元学习(meta learning)。

One-shot Learning with Memory-Augmented Neural Networks 这篇论文解释了单样本学习与元学习的关系,它在Omniglot数据集上训练了一个记忆增强网络,然而,我承认我看不懂这篇论文。

下一步是什么?

Omniglot 数据集是2015年的了,现在已经有了可拓展机器学习算法,在特定的单样本学习上达到了人类的水平。希望未来有一天,Omniglo t数据集会像 MNIST 之于监督学习那样,成为单样本学习的标准检测数据集。

图像分类挺酷的,但我并不认为它是机器学习界最有趣的问题。现在我们知道了深度单样本学习有了不错的效果,我想如果尝试把单样本学习应用到更有挑战性的任务上,那样才是真酷。

单样本学习的想法可以被用到样本效率更高的增强学习上面,尤其是像OpenAI's Universe这样的问题,这些问题有很多马尔科夫决策过程或者环境,它们拥有类似的视觉和动态信息。如果有一个增强学习机制能以类似马尔科夫决策过程学习后可以有效的探索新环境,那样简直酷毙了。

OpenAI 的比特世界

笔画最多的字900000画,笔画最多字9亿画(24)

上一页23456下一页

栏目热文

文档排行

本站推荐

Copyright © 2018 - 2021 www.yd166.com., All Rights Reserved.