“one shot learning aims to learn information about object categories from one, or only a few, training samples/images” --wiki
所以说 one-shot learning 和 few shot learning 中的one 和few指的是在学习分类过程中,training samples 的数量。比如我们上一节讲的电信号分为九类,one-shot,那么就是有9个样本,每个类别一个,few-shot 就是每个类别多于一个样本,到极端情况下,我们会碰都某些类别中有0个训练样本,那么这种极端情况叫做zero-shot learning。 像这种小样本学习,他们的判别方法是通过判断进来的类型是否为同一类来作为分类的方法,在测试的时候,通过将待测数据通过习得的模型,来进行分类。 所以在训练的时候,我们也需要同时输入进去不同的样本个体,而对应的y值,分别用0和1来表示,如果输入模型中的样本为同一类那么y值为0如果不是同一类那么y值为1。通过这样的方式,我们可以减少样本训练时所需要的样本量。 而siamese neural network (孪生神经网络)使用了两个之前讲过的卷积神经网络,来作为两个样本的输入,从而搭建出小样本学习框架。“few-shot learning is refers to the practice of feeding a learning model with a very small amount of training data, contrary to the normal practice of using a large amount of data.” --Dr.Michael J.Garbade
可以简单理解为下图: 所以原理其实很简单,那么下面我们还是使用上一篇文章使用的数据集,来进行siamese network编码。“A siamese neural network (sometimes called a twin neural network) is an artificial neural network that uses the same weights while working in tandem on two different input vectors to compute comparable output vectors." --wiki
一、导入包和数据
import osimport csvimport pandas as pdimport kerasimport numpy as npfrom keras.models import Sequential,Modelfrom keras.layers import Dense, Activation, Flatten, Convolution1D, Dropout, MaxPooling1D, Inputimport timefrom datetime import datetimefrom keras import backend as Kfrom keras.layers.core import Lambda, Flatten, Dense
path=r'E:\ilm\train_data'files=os.listdir(path)
二、处理数据使之变为两个vectors(向量),并设好y值。(这步有点技术)
column_names=[]train=pd.DataFrame()for item in files: data_frame=pd.read_csv('E:/ilm/train_data/'+item) data_frame['mins']=range(len(data_frame)) data_frame=data_frame.drop(['Unnamed: 0'],axis=1) data_frame=pd.pivot_table(data_frame,columns=['mins']) train=train.append(data_frame)
train=train[:140]train.reset_index()train1=train.sort_index()train2=train1.groupby(train1.index).head(9)train2=train2.groupby(train2.index).head(9)
我们得到的初始数据如下:
dataframe里面一共有9类用电器,我这边每一类使用head(9)取的是前9条的数据,那么一共有81行数据。每行120分钟就形成了上面81 rows x 120 columns 的表格。 然后将dataframe变成array:
x=np.array(train2)
然后将上面的数据变成两个vectors,我们将这两个vector分别命名为x_right,x_left。
x_right=xfor i in range(len(x_right)-1): x_right=np.concatenate((x_right,x))print(x_right.shape)
x_left=[]for i in range(len(x)): for j in range(len(x)): x_left.append(x[i])x_left=np.array(x_left)print(x_left.shape)
x_right,x_left 分别由一个 81x120 的 x array变成了一个(6561,120)的array,其过程如下图:
注: x00: 表示第1类的第1行数据;xij: 即第i+1类的第j+1行数据。 然后下面接下来的是,通过一种关于x_left和x_right的运算得到可以和y值产生一定的规律,这里我们不能说通过这种运算就可以得到y值,这个过程就类似于线性拟合,大部分点都是线条之外地,何况我们这里讨论的是分类问题,正如上文讲到的,设置两个通道的模型是用于判断两个模型中的输入是否为同类,那么这里就是一个二分类问题,所以这个y矩阵里面的值是一列非0即1的值。 下面来创建y矩阵,因为在得到我们的矩阵之前,我们已经按照相同类型做好了排序,每9行均为相同类型,所以y值也会呈现出一定的规律,先假设x_right和x_left所对应的全部不相同,他们之间相减的abs(absolute)值很大,那么先创建一个全为1的y矩阵:
matrix_l=81y=[]count=0for i in range(matrix_l): for j in range(matrix_l): y.append(1)
得到的y vector shape为6561x1,然后对y矩阵里面相应的位置赋值为0,相同类赋值为0,即x_left和x_right中右下角i值相同,y对应的值就为0:
大概是下面这样的矩阵:
所以可以得到y矩阵如上图,它只有一列数,不过貌似没有这种写法吧,我把他们这样子框起来,方便理解,右下角的数字代表的是框起来的行数。其对应的代码如下:
group=9for i in range(matrix_l): if i%group==0: for j in range(i*matrix_l+i-0,i*matrix_l+i+9): y[j]=0 if i%group==1: for j in range(i*matrix_l+i-1,i*matrix_l+i+8): y[j]=0 if i%group==2: for j in range(i*matrix_l+i-2,i*matrix_l+i+7): y[j]=0 if i%group==3: for j in range(i*matrix_l+i-3,i*matrix_l+i+6): y[j]=0 if i%group==4: for j in range(i*matrix_l+i-4,i*matrix_l+i+5): y[j]=0 if i%group==5: for j in range(i*matrix_l+i-5,i*matrix_l+i+4): y[j]=0 if i%group==6: for j in range(i*matrix_l+i-6,i*matrix_l+i+3): y[j]=0 if i%group==7: for j in range(i*matrix_l+i-7,i*matrix_l+i+2): y[j]=0 if i%group==8: for j in range(i*matrix_l+i-8,i*matrix_l+i+1): y[j]=0
因为是一维的卷积神经网络,所以这里要调整 x_left 和 x_right 的维度,方法大致为:先创建所需维度的零矩阵,然后将值赋值进去。
x_left_r=np.zeros((len(x_left),120,1))x_left_r[:,:,0]=x_left[:,:120]
x_right_r=np.zeros((len(x_right),120,1))x_right_r[:,:,0]=x_right[:,:120]
三、建立两个卷积神经网络模型 所需要的 vectors 都已经弄好了,下面我们开始建立两个一样 weight 的 CNN 模型。
# Keras model with one Convolution1D layerinput_shape=(120,1)left_input = Input(input_shape)right_input = Input(input_shape)nb_features=120nb_class=9model = Sequential()model.add(Convolution1D(nb_filter=52, filter_length=3, input_shape=(nb_features, 1))) model.add(MaxPooling1D(pool_size=2,strides=None))model.add(Convolution1D(nb_filter=52, filter_length=3,input_shape=(60,1)))model.add(MaxPooling1D())model.add(Activation('relu'))model.add(Flatten()) #建立平坦层model.add(Dropout(0.5)) #丢掉一些神经元和神经网络model.add(Dense(162, activation='relu')) #全连接层定义model.add(Dense(81, activation='relu')) #全连接层定义model.add(Dense(nb_class))model.add(Activation('softmax'))encoded_l=model(left_input)encoded_r=model(right_input)
从上面的代码可以看到,我们使用了两个input,模型是一样的,这两个Input进入同样的模型,然后得到对应的两个output,分别为encoded_l,和encoded_r。然后开始计算他们之间的距离。因为这边使用的是keras,所以直接用keras.backend.abs()就可以了,看到keras里面的源代码,发现使用的是math_ops.abs()。
关于math_ops.abs()的详情可以参考下列官方链接:https://www.tensorflow.org/api_docs/cc/class/tensorflow/ops/abs
到这里我们还没有传值进去,只是搭了大致的模型,接下来在model.fit()时,值才会传进去,再进行运算。 下面计算 x_left 与 x_right 的差值:L1_layer=Lambda(lambda x:K.abs(x[0]-x[1]))L1_distance=L1_layer([encoded_l,encoded_r])prediction = Dense(1,activation='softmax')(L1_distance)siamese_net = Model(inputs=[left_input,right_input],outputs=prediction)
上面可以看到我们使用了K.abs(x[0]-x[1]),这个就是下面L1_layer([ ])里面的值中前一个减后一个的差值取绝对值的运算,x:[encoded_l,encoded_r], x[0]:encoded_l,x[1]:encoded_r。然后我们对于上面这个运算使用的是softmax作为激活函数,去激活上面这个步骤,softmax可以把我们的值限定在0和1之内,当两个差值很大时,趋于1,当很小时,接近0。 The standard(unit) softmax function :
where: z for input vector 使用图片表示这个过程如下图:
四、训练模型
在Compile模型时设置一些参数,我们这个问题涉及的是判断两个通道是否为同类,所以是二分类问题,上一期写的是多分类,那么这里要使用的是binary_crossentropy,而不是 categorical_crossentropy 。 然后就直接训练模型了,compile和fit的代码,如下:siamese_net.compile(loss='binary_crossentropy',optimizer=keras.optimizers.Adam(lr=0.001,beta_1=0.9,beta_2=0.999,amsgrad=False),metrics = ['accuracy'])siamese_net.fit([x_left_r,x_right_r],y,verbose=1,epochs=10)
做预测的话和上一篇文章讲的大同小异,只是在predict时这里需要传入两个值。 上文纯属个人理解下的总结,如有差错,还望指正。