Python 元学习实用指南:1~5(2)

简介: Python 元学习实用指南:1~5(2)

Python 元学习实用指南:1~5(1)https://developer.aliyun.com/article/1426919

最后,我们将x_genuine_pairx_imposite都连接到X以及y_genuiney_imposite都连接到Y

size = 2
total_sample_size = 10000
def get_data(size, total_sample_size):
    #read the image
    image = read_image('data/orl_faces/s' + str(1) + '/' + str(1) + '.pgm', 'rw+')
    #reduce the size
    image = image[::size, ::size]
    #get the new size
    dim1 = image.shape[0]
    dim2 = image.shape[1]
    count = 0
    #initialize the numpy array with the shape of [total_sample, no_of_pairs, dim1, dim2]
    x_geuine_pair = np.zeros([total_sample_size, 2, 1, dim1, dim2]) # 2 is for pairs
    y_genuine = np.zeros([total_sample_size, 1])
    for i in range(40):
        for j in range(int(total_sample_size/40)):
            ind1 = 0
            ind2 = 0
            #read images from same directory (genuine pair)
            while ind1 == ind2:
                ind1 = np.random.randint(10)
                ind2 = np.random.randint(10)
            # read the two images
            img1 = read_image('data/orl_faces/s' + str(i+1) + '/' + str(ind1 + 1) + '.pgm', 'rw+')
            img2 = read_image('data/orl_faces/s' + str(i+1) + '/' + str(ind2 + 1) + '.pgm', 'rw+')
            #reduce the size
            img1 = img1[::size, ::size]
            img2 = img2[::size, ::size]
            #store the images to the initialized numpy array
            x_geuine_pair[count, 0, 0, :, :] = img1
            x_geuine_pair[count, 1, 0, :, :] = img2
            #as we are drawing images from the same directory we assign label as 1\. (genuine pair)
            y_genuine[count] = 1
            count += 1
    count = 0
    x_imposite_pair = np.zeros([total_sample_size, 2, 1, dim1, dim2])
    y_imposite = np.zeros([total_sample_size, 1])
    for i in range(int(total_sample_size/10)):
        for j in range(10):
            #read images from different directory (imposite pair)
            while True:
                ind1 = np.random.randint(40)
                ind2 = np.random.randint(40)
                if ind1 != ind2:
                    break
            img1 = read_image('data/orl_faces/s' + str(ind1+1) + '/' + str(j + 1) + '.pgm', 'rw+')
            img2 = read_image('data/orl_faces/s' + str(ind2+1) + '/' + str(j + 1) + '.pgm', 'rw+')
            img1 = img1[::size, ::size]
            img2 = img2[::size, ::size]
            x_imposite_pair[count, 0, 0, :, :] = img1
            x_imposite_pair[count, 1, 0, :, :] = img2
            #as we are drawing images from the different directory we assign label as 0\. (imposite pair)
            y_imposite[count] = 0
            count += 1
    #now, concatenate, genuine pairs and imposite pair to get the whole data
    X = np.concatenate([x_geuine_pair, x_imposite_pair], axis=0)/255
    Y = np.concatenate([y_genuine, y_imposite], axis=0)
    return X, Y

现在,我们生成数据并检查数据大小。 如您所见,我们有 20,000 个数据点,其中 10,000 个是真实对,而 10,000 个是非对:

X, Y = get_data(size, total_sample_size)
X.shape
(20000, 2, 1, 56, 46)
Y.shape
(20000, 1)

接下来,我们将训练和测试的数据划分为 75% 的训练和 25% 的测试比例:

x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=.25)

现在我们已经成功地生成了数据,我们就建立了连体网络。 首先,我们定义基础网络,该网络基本上是用于特征提取的卷积网络。 我们使用 ReLU 激活和最大池化以及一个平坦层来构建两个卷积层:

def build_base_network(input_shape):
    seq = Sequential()
    nb_filter = [6, 12]
    kernel_size = 3
    #convolutional layer 1
    seq.add(Convolution2D(nb_filter[0], kernel_size, kernel_size, input_shape=input_shape,
                          border_mode='valid', dim_ordering='th'))
    seq.add(Activation('relu'))
    seq.add(MaxPooling2D(pool_size=(2, 2))) 
    seq.add(Dropout(.25))
    #convolutional layer 2
    seq.add(Convolution2D(nb_filter[1], kernel_size, kernel_size, border_mode='valid', dim_ordering='th'))
    seq.add(Activation('relu'))
    seq.add(MaxPooling2D(pool_size=(2, 2), dim_ordering='th')) 
    seq.add(Dropout(.25))
    #flatten 
    seq.add(Flatten())
    seq.add(Dense(128, activation='relu'))
    seq.add(Dropout(0.1))
    seq.add(Dense(50, activation='relu'))
    return seq

接下来,我们将图像对馈送到基础网络,该基础网络将返回嵌入,即特征向量:

input_dim = x_train.shape[2:]
img_a = Input(shape=input_dim)
img_b = Input(shape=input_dim)
base_network = build_base_network(input_dim)
feat_vecs_a = base_network(img_a)
feat_vecs_b = base_network(img_b)

feat_vecs_afeat_vecs_b是我们图像对的特征向量。 接下来,我们将这些特征向量馈入能量函数以计算它们之间的距离,然后使用欧几里得距离作为能量函数:

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)
distance = Lambda(euclidean_distance, output_shape=eucl_dist_output_shape)([feat_vecs_a, feat_vecs_b])

现在,我们将周期长度设置为13,并使用 RMS 属性进行优化并定义我们的模型:

epochs = 13
rms = RMSprop()
model = Model(input=[input_a, input_b], output=distance)

接下来,我们将损失函数定义为contrastive_loss函数并编译模型:

def contrastive_loss(y_true, y_pred):
    margin = 1
    return K.mean(y_true * K.square(y_pred) + (1 - y_true) * K.square(K.maximum(margin - y_pred, 0)))
model.compile(loss=contrastive_loss, optimizer=rms)

现在,我们训练模型:

img_1 = x_train[:, 0]
img_2 = x_train[:, 1] 
model.fit([img_1, img_2], y_train, validation_split=.25, batch_size=128, verbose=2, nb_epoch=epochs)

您会看到损失随着时间的推移而减少:

Train on 11250 samples, validate on 3750 samples
Epoch 1/13
 - 60s - loss: 0.2179 - val_loss: 0.2156
Epoch 2/13
 - 53s - loss: 0.1520 - val_loss: 0.2102
Epoch 3/13
 - 53s - loss: 0.1190 - val_loss: 0.1545
Epoch 4/13
 - 55s - loss: 0.0959 - val_loss: 0.1705
Epoch 5/13
 - 52s - loss: 0.0801 - val_loss: 0.1181
Epoch 6/13
 - 52s - loss: 0.0684 - val_loss: 0.0821
Epoch 7/13
 - 52s - loss: 0.0591 - val_loss: 0.0762
Epoch 8/13
 - 52s - loss: 0.0526 - val_loss: 0.0655
Epoch 9/13
 - 52s - loss: 0.0475 - val_loss: 0.0662
Epoch 10/13
 - 52s - loss: 0.0444 - val_loss: 0.0469
Epoch 11/13
 - 52s - loss: 0.0408 - val_loss: 0.0478
Epoch 12/13
 - 52s - loss: 0.0381 - val_loss: 0.0498
Epoch 13/13
 - 54s - loss: 0.0356 - val_loss: 0.0363

现在,我们使用测试数据进行预测:

pred = model.predict([x_test[:, 0], x_test[:, 1]])

接下来,我们定义一个用于计算精度的函数:

def compute_accuracy(predictions, labels):
    return labels[predictions.ravel() < 0.5].mean()

现在,我们对模型的准确率:

compute_accuracy(pred, y_test)
0.9779092702169625

使用连体网络构建音频识别模型

在上一教程中,我们了解了如何使用连体网络识别人脸。 现在,我们将看到如何使用连体网络来识别音频。 我们将训练我们的网络,以区分狗的声音和猫的声音。 可以从此处下载猫和狗音频的数据集

下载数据后,我们将数据分成三个文件夹:DogsSub_dogsCats。 在DogsSub_dogs中,放置狗的吠叫音频,在Cats文件夹中,放置猫的音频。 我们网络的目标是识别音频是狗的吠叫还是其他声音。 众所周知,对于连体网络,我们需要成对输入输入。 我们从DogsSub_dogs文件夹中选择一个音频并将其标记为真正对,并从DogsCats文件夹中选择一个音频并将它们标记为非对。 即,(Dogs, Sub_dogs)是真正的对,(Dogs, Cats)是非配对的。

现在,我们将逐步展示如何训练连体网络以识别音频是狗的吠叫声还是其他声音。

为了更好地理解,您可以检查完整的代码,该代码可以在 Jupyter 笔记本中找到,并在此处进行解释

首先,我们将加载所有必需的库:

#basic imports
import glob
import IPython
from random import randint
#data processing
import librosa
import numpy as np
#modelling
from sklearn.model_selection import train_test_split
from keras import backend as K
from keras.layers import Activation
from keras.layers import Input, Lambda, Dense, Dropout, Flatten
from keras.models import Model
from keras.optimizers import RMSprop

在继续之前,我们加载并收听音频片段:

IPython.display.Audio("data/audio/Dogs/dog_barking_0.wav")
IPython.display.Audio("data/audio/Cats/cat_13.wav")

那么,如何将这些原始音频馈送到我们的网络? 我们如何从原始音频中提取有意义的特征? 众所周知,神经网络仅接受向量化输入,因此我们需要将音频转换为特征向量。 我们该怎么做? 嗯,我们可以通过几种机制生成音频的嵌入。 这样的流行机制之一是梅尔频率倒谱系数MFCC)。 MFCC 使用对数功率谱在频率的非线性梅尔尺度上的线性余弦变换来转换音频的短期功率谱。 要了解有关 MFCC 的更多信息,请查看此不错的教程

我们将使用librosa库中的 MFCC 函数来生成音频嵌入。 因此,我们定义了一个名为audio2vector的函数,该函数在给定音频文件的情况下返回音频嵌入:

def audio2vector(file_path, max_pad_len=400):
    #read the audio file
    audio, sr = librosa.load(file_path, mono=True)
    #reduce the shape
    audio = audio[::3]
    #extract the audio embeddings using MFCC
    mfcc = librosa.feature.mfcc(audio, sr=sr) 
    #as the audio embeddings length varies for different audio, we keep the maximum length as 400
    #pad them with zeros
    pad_width = max_pad_len - mfcc.shape[1]
    mfcc = np.pad(mfcc, pad_width=((0, 0), (0, pad_width)), mode='constant')
    return mfcc

我们将加载一个音频文件并查看嵌入内容:

audio_file = 'data/audio/Dogs/dog_barking_0.wav'
audio2vector(audio_file)
array([[-297.54905127, -288.37618855, -314.92037769, ...,    0\.        ,
           0\.        ,    0\.        ],
       [  23.05969394,    9.55913148,   37.2173831 , ...,    0\.        ,
           0\.        ,    0\.        ],
       [-122.06299523, -115.02627567, -108.18703056, ...,    0\.        ,
           0\.        ,    0\.        ],
       ...,
       [  -6.40930836,   -2.8602708 ,   -2.12551478, ...,    0\.        ,
           0\.        ,    0\.        ],
       [   0.70572914,    4.21777791,    4.62429301, ...,    0\.        ,
           0\.        ,    0\.        ],
       [  -6.08997702,  -11.40687886,  -18.2415214 , ...,    0\.        ,
           0\.        ,    0\.        ]])

现在我们已经了解了如何生成音频嵌入,我们需要为我们的连体网络创建数据。 众所周知,连体网络可以成对接受数据,因此我们定义了获取数据的函数。 我们将创建一个真正的对(DogsSub_dogs),并将标签指定为1,将非正当对创建为(DogsCats),并将标签指定为0

def get_data():
    pairs = []
    labels = []
    Dogs = glob.glob('data/audio/Dogs/*.wav')
    Sub_dogs = glob.glob('data/audio/Sub_dogs/*.wav')
    Cats = glob.glob('data/audio/Cats/*.wav')
    np.random.shuffle(Sub_dogs)
    np.random.shuffle(Cats)
    for i in range(min(len(Cats),len(Sub_dogs))):
        #imposite pair
        if (i % 2) == 0:
            pairs.append([audio2vector(Dogs[randint(0,3)]),audio2vector(Cats[i])])
            labels.append(0)
        #genuine pair
        else:
            pairs.append([audio2vector(Dogs[randint(0,3)]),audio2vector(Sub_dogs[i])])
            labels.append(1)
    return np.array(pairs), np.array(labels)
X, Y = get_data("/home/sudarshan/sudarshan/Experiments/oneshot-audio/data/")

接下来,我们将训练和测试的数据划分为 75% 的训练和 25% 的测试比例:

X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.2)

现在我们已经成功地生成了数据,我们就建立了连体网络。 我们定义了用于特征提取的基本网络,我们使用了三个密集层,中间有一个丢弃层:

def build_base_network(input_shape):
    input = Input(shape=input_shape)
    x = Flatten()(input)
    x = Dense(128, activation='relu')(x)
    x = Dropout(0.1)(x)
    x = Dense(128, activation='relu')(x)
    x = Dropout(0.1)(x)
    x = Dense(128, activation='relu')(x)
    return Model(input, x)

接下来,我们将音频对馈送到基础网络,基础网络将返回特征:

input_dim = X_train.shape[2:]
audio_a = Input(shape=input_dim)
audio_b = Input(shape=input_dim)
base_network = build_base_network(input_dim)
feat_vecs_a = base_network(audio_a)
feat_vecs_b = base_network(audio_b)

feat_vecs_afeat_vecs_b是我们音频对的特征向量。 接下来,我们将这些特征向量馈入能量函数以计算它们之间的距离,然后使用欧几里得距离作为能量函数:

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)
distance = Lambda(euclidean_distance, output_shape=eucl_dist_output_shape)([feat_vecs_a, feat_vecs_b])

接下来,我们将周期长度设置为13,并使用 RMS 属性进行优化:

epochs = 13
rms = RMSprop()
model = Model(input=[audio_a, audio_b], output=distance)

最后,我们将损失函数定义为contrastive_loss并编译模型:

def contrastive_loss(y_true, y_pred):
    margin = 1
    return K.mean(y_true * K.square(y_pred) + (1 - y_true) * K.square(K.maximum(margin - y_pred, 0)))
model.compile(loss=contrastive_loss, optimizer=rms)

现在,我们训练模型:

audio1 = X_train[:, 0]
audio2 = X_train[:, 1]
model.fit([audio_1, audio_2], y_train, validation_split=.25,
          batch_size=128, verbose=2, nb_epoch=epochs)

您可以了解历代的损失:

Train on 8 samples, validate on 3 samples
Epoch 1/13
 - 0s - loss: 23594.8965 - val_loss: 1598.8439
Epoch 2/13
 - 0s - loss: 62360.9570 - val_loss: 816.7302
Epoch 3/13
 - 0s - loss: 17967.6230 - val_loss: 970.0378
Epoch 4/13
 - 0s - loss: 20030.3711 - val_loss: 358.9078
Epoch 5/13
 - 0s - loss: 11196.0547 - val_loss: 339.9991
Epoch 6/13
 - 0s - loss: 3837.2898 - val_loss: 381.9774
Epoch 7/13
 - 0s - loss: 2037.2965 - val_loss: 303.6652
Epoch 8/13
 - 0s - loss: 1434.4321 - val_loss: 229.1388
Epoch 9/13
 - 0s - loss: 2553.0562 - val_loss: 215.1207
Epoch 10/13
 - 0s - loss: 1046.6870 - val_loss: 197.1127
Epoch 11/13
 - 0s - loss: 569.4632 - val_loss: 183.8586
Epoch 12/13
 - 0s - loss: 759.0131 - val_loss: 162.3362
Epoch 13/13
 - 0s - loss: 819.8594 - val_loss: 120.3017

总结

在本章中,我们学习了什么是连体网络,以及如何使用连体网络构建人脸和音频识别模型。 我们探索了连体网络的架构,该网络基本上由两个相同的神经网络组成,它们具有相同的权重和架构,并且将这些网络的输出插入到一些能量函数中以了解相似性。

在下一章中,我们将学习原型网络及其变种,例如高斯原型网络和半原型网络。 我们还将看到如何使用原型网络进行全方位字符集分类。

问题

  1. 什么是连体网络?
  2. 什么是对比损失函数?
  3. 能量函数是什么?
  4. 连体网络所需的数据格式是什么?
  5. 连体网络有哪些应用?

进一步阅读

三、原型网络及其变体

在上一章中,我们了解了什么是连体网络以及如何将它们用于执行少量学习任务。 我们还探讨了如何使用连体网络进行人脸和音频识别。 在本章中,我们将介绍另一种有趣的少样本学习算法,称为原型网络,该算法能够将其推广到训练集中没有的类。 我们将从了解什么是原型网络开始,然后我们将了解如何使用原型网络在 omniglot 数据集中执行分类任务。 然后,我们将看到原型网络的不同变体,例如高斯原型网络和半原型网络。

在本章中,您将了解以下内容:

  • 原型网络
  • 原型网络算法
  • 将原型网络用于分类
  • 高斯原型网络
  • 高斯原型网络算法
  • 半原型网络

原型网络

原型网络是另一种简单,高效,很少的镜头学习算法。 像连体网络一样,原型网络尝试学习度量空间以执行分类。 原型网络的基本思想是创建每个类的原型表示形式,并根据类原型与查询点之间的距离对查询点(即新点)进行分类。

假设我们有一个包含狮子,大象和狗的图像的支持集,如下图所示:

因此,我们分为三类: {Lion, Eleph, Dog}。 现在,我们需要为这三个类中的每一个创建一个原型表示。 我们如何构建这三个类的原型? 首先,我们将使用嵌入函数来学习每个数据点的嵌入。 嵌入函数f[φ]()可以是可用于提取特征的任何函数。 由于我们的输入是图像,因此我们可以使用卷积网络作为嵌入函数,该函数将从输入图像中提取特征:

一旦了解了每个数据点的嵌入,就可以将每个类中数据点的均值嵌入并形成类原型,如下图所示。 因此,类原型基本上就是在类中数据点的平均嵌入:

同样,当有新的数据点(即我们要为其预测标签的查询点)进入时,我们将使用与创建类原型相同的嵌入函数为该新数据点生成嵌入。 是,我们使用卷积网络为查询点生成嵌入:

对查询点进行嵌入后,我们将比较类原型和查询点嵌入之间的距离,以查找查询点所属的类。 我们可以使用欧几里得距离作为查找类原型与查询点嵌入之间距离的度量,如下所示:

在找到类原型与查询点嵌入之间的距离后,我们将 softmax 应用于该距离并获得概率。 由于我们有狮子,大象和狗这三个类,因此我们将获得三个概率。 因此,概率最高的类别将是我们查询点的类别。

由于我们希望网络从几个数据点中学习,也就是说,我们希望执行几次快照学习,因此我们以相同的方式训练网络。 因此,我们使用了间歇式训练-对于每个剧集,我们从数据集中的每个类随机采样一些数据点,我们称其为支持集,仅使用支持集而不是整个数据集来训练网络。 同样,我们从数据集中随机抽取一个点作为查询点,并尝试预测其类别。 因此,通过这种方式,我们的网络受到了如何从较小的数据点集中学习的训练。

下图显示了我们原型网络的整体流程。 如您所见,首先,我们将为支持集中的所有数据点生成嵌入,并通过在类中获取数据点的平均嵌入来构建类原型。 我们还为查询点生成嵌入。 然后,我们计算类原型与查询点嵌入之间的距离。 我们使用欧几里得距离作为距离度量。 然后,我们将 softmax 应用于此距离并获得概率。 如下图所示,由于我们的查询点是狮子,因此狮子的概率很高,为 0.9:

原型网络不仅用于单样本/少样本学习,而且还用于零样本学习。 考虑以下情况:每个类没有数据点,但是您具有包含每个类的高级描述的元信息。 因此,在这些情况下,我们从每个类的元信息中学习嵌入,以形成类原型,然后使用该类原型进行分类。

算法

原型网络的算法如下所示:

  1. 假设我们有数据集D,其中包含{(x1, y1), (x2, y2), ..., (xn, yn)}其中x是特征,y是类别标签。
  2. 由于我们进行了间歇式训练,因此我们从数据集中D中随机抽取每个类别的n个数据点数,并准备了支持集S
  3. 同样,我们选择n个数据点,并准备我们的查询集Q
  4. 我们使用嵌入函数f[∅]来学习数据点在支持集中的嵌入。 嵌入函数可以是任何特征提取器,例如,用于图像的卷积网络和用于文本的 LSTM 网络。
  5. 一旦获得每个数据点的嵌入,就可以通过获取每个类下数据点的平均嵌入来计算每个类的原型:

  1. 同样,我们学习查询集嵌入。
  2. 我们计算查询集嵌入和类原型之间的欧几里德距离d
  3. 我们通过在距离d上应用 softmax 来预测查询集类别的概率p [∅](y = k | x)

  1. 我们将损失函数J(∅)计算为负对数概率J(∅) = -logp[∅](y = k | x),我们尝试使用随机梯度下降法将损失降到最低。

使用原型网络执行分类

现在,我们将看到如何使用原型网络执行分类任务。 我们使用 omniglot 数据集进行分类。 该数据集包含来自 50 个不同字母的 1,623 个手写字符,每个字符都有 20 个不同的示例,这些示例是由不同的人编写的。 由于我们希望我们的网络从数据中学习,因此我们以相同的方式对其进行训练。 我们从每个类中采样五个示例,并将其用作我们的支持集。 我们使用四个卷积块作为编码器来学习支持集的嵌入,并构建类原型。 同样,我们从每个类中为我们的查询集采样五个示例,学习查询集嵌入,并通过比较查询集嵌入和类原型之间的欧式距离来预测查询集类。 让我们逐步了解它会更好地理解这一点。

您还可以在此处查看 Jupyter 笔记本中可用的代码并进行解释

首先,我们导入所有必需的库:

import os
import glob
from PIL import Image
import numpy as np
import tensorflow as tf

现在,我们将探索并查看我们从数据中得到的结果。 众所周知,我们有不同字母的不同字符,每个字符有二十种不同的字母,由不同的人书写。 让我们绘制并检查其中的一些。

让我们从日语字母中绘制一个字符:

Image.open('dahttps://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-meta-learn-py/img/Japanese_(katakana)/character13/0608_01.png')

相同字母的不同变化:

Image.open('dahttps://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-meta-learn-py/img/Japanese_(katakana)/character13/0608_13.png')

让我们看一下梵文字母中的一个字符:

Image.open('dahttps://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-meta-learn-py/img/Sanskrit/character13/0863_09.png')

Image.open('dahttps://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-meta-learn-py/img/Sanskrit/character13/0863_13.png')

我们如何将图像转换为数组? 我们可以使用np.array将这些图像转换为数组并将其重塑为 28 x 28:

image_name = 'dahttps://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/handson-meta-learn-py/img/Sanskrit/character13/0863_13.png'
alphabet, character, rotation = 'Sanskrit/character13/rot000'.split('/')
rotation = float(rotation[3:])

您可以看到如下输出:

array([[1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 0., 1., 1., 1., 0., 1.], [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]],dtype=float32)

现在我们已经了解了数据集中的内容,我们将加载数据集:

root_dir = 'data/'

我们在/data/omniglot/splits/train.txt文件中有拆分的详细信息,该文件中的语言名称,字符号,旋转信息和/data/omniglot/data/中的图像是:

train_split_path = os.path.join(root_dir, 'splits', 'train.txt')
with open(train_split_path, 'r') as train_split:
    train_classes = [line.rstrip() for line in train_split.readlines()]

我们发现类的数量如下:

#number of classes
no_of_classes = len(train_classes)

现在,我们将示例数量设置为 20,因为我们的数据集中每个类有 20 个示例,并将图像的宽度和高度设置为28 x 28

#number of examples
num_examples = 20
#image width
img_width = 28
#image height
img_height = 28
channels = 1

接下来,我们将训练数据集的形状初始化为多个类,示例数以及图像的高度和宽度:

train_dataset = np.zeros([no_of_classes, num_examples, img_height, img_width], dtype=np.float32)

现在,我们读取所有图像,将它们转换为 NumPy 数组,并将它们的标签和值(即train_dataset = [label, values])存储在train_dataset数组中:

for label, name in enumerate(train_classes):
    alphabet, character, rotation = name.split('/')
    rotation = float(rotation[3:])
    img_dir = os.path.join(root_dir, 'data', alphabet, character)
    img_files = sorted(glob.glob(os.path.join(img_dir, '*.png')))
    for index, img_file in enumerate(img_files):
        values = 1\. - np.array(Image.open(img_file).rotate(rotation).resize((img_width, img_height)), np.float32, copy=False)
        train_dataset[label, index] = values

训练数据的形状如下:

train_dataset.shape
(4112, 20, 28, 28)

现在我们已经加载了训练数据,我们需要为它们创建嵌入。 我们使用卷积运算生成嵌入,因为我们的输入值是图像。 因此,我们定义了一个具有 64 个过滤器的卷积块,其中批量标准化和 ReLU 作为激活函数。 接下来,我们执行最大池化操作:

def convolution_block(inputs, out_channels, name='conv'):
    conv = tf.layers.conv2d(inputs, out_channels, kernel_size=3, padding='SAME')
    conv = tf.contrib.layers.batch_norm(conv, updates_collections=None, decay=0.99, scale=True, center=True)
    conv = tf.nn.relu(conv)
    conv = tf.contrib.layers.max_pool2d(conv, 2)
    return conv

现在,我们定义嵌入函数,该函数为我们提供了包含四个卷积块的嵌入:

def get_embeddings(support_set, h_dim, z_dim, reuse=False):
        net = convolution_block(support_set, h_dim)
        net = convolution_block(net, h_dim)
        net = convolution_block(net, h_dim) 
        net = convolution_block(net, z_dim) 
        net = tf.contrib.layers.flatten(net)
        return net

请记住,我们不会使用整个数据集进行训练; 由于我们使用的是单样本学习,因此我们从每个类中抽取一些数据点作为支持集,并以情景方式使用支持集训练网络。

现在,我们定义一些重要的变量-我们考虑 50 路五样本学习场景:

#number of classes
num_way = 50 
#number of examples per class in a support set
num_shot = 5 
#number of query points for query set
num_query = 5 
#number of examples
num_examples = 20
h_dim = 64
z_dim = 64

接下来,我们为支持和查询集初始化占位符:

support_set = tf.placeholder(tf.float32, [None, None, img_height, img_width, channels])
query_set = tf.placeholder(tf.float32, [None, None, img_height, img_width, channels])

并且我们分别在support_set_shapequery_set_shape中存储支持和查询集的形状:

support_set_shape = tf.shape(support_set)
query_set_shape = tf.shape(query_set)

我们获得了用于初始化我们的支持和查询集的类数,支持集中的数据点数以及查询集中的数据点数:

num_classes, num_support_points = support_set_shape[0], support_set_shape[1]
num_query_points = query_set_shape[1]

接下来,我们为标签定义占位符:

y = tf.placeholder(tf.int64, [None, None])
#convert the label to one hot
y_one_hot = tf.one_hot(y, depth=num_classes)

现在,我们使用嵌入函数为支持集生成嵌入:

support_set_embeddings = get_embeddings(tf.reshape(support_set, [num_classes * num_support_points, img_height, img_width, channels]), h_dim, z_dim)

我们计算每个类的原型,这是该类支持集嵌入的均值向量:

embedding_dimension = tf.shape(support_set_embeddings)[-1]
class_prototype = tf.reduce_mean(tf.reshape(support_set_embeddings, [num_classes, num_support_points, embedding_dimension]), axis=1)

接下来,我们使用相同的嵌入函数来获取查询集的嵌入:

query_set_embeddings = get_embeddings(tf.reshape(query_set, [num_classes * num_query_points, img_height, img_width, channels]), h_dim, z_dim, reuse=True)

现在我们有了类原型和查询集嵌入,我们定义了一个距离函数,该距离函数为我们提供了类原型和查询集嵌入之间的距离:

def euclidean_distance(a, b):
    N, D = tf.shape(a)[0], tf.shape(a)[1]
    M = tf.shape(b)[0]
    a = tf.tile(tf.expand_dims(a, axis=1), (1, M, 1))
    b = tf.tile(tf.expand_dims(b, axis=0), (N, 1, 1))
    return tf.reduce_mean(tf.square(a - b), axis=2)

我们计算类原型与查询集嵌入之间的距离:

distance = euclidean_distance(class_prototype,query_set_embeddings)

接下来,我们将每个类别的概率作为距离的 softmax:

predicted_probability = tf.reshape(tf.nn.log_softmax(-distance), [num_classes, num_query_points, -1])

然后,我们计算损失:

loss = -tf.reduce_mean(tf.reshape(tf.reduce_sum(tf.multiply(y_one_hot, predicted_probability), axis=-1), [-1]))

我们计算精度如下:

accuracy = tf.reduce_mean(tf.to_float(tf.equal(tf.argmax(predicted_probability, axis=-1), y)))

然后,我们使用 Adam 优化器将损失降到最低:

train = tf.train.AdamOptimizer().minimize(loss)

现在,我们开始 TensorFlow 会话并训练模型:

sess = tf.InteractiveSession()
init = tf.global_variables_initializer()
sess.run(init)

我们定义周期数和剧集数:

num_epochs = 20
num_episodes = 100

接下来,我们开始进行情景式训练-也就是说,对于每个剧集,我们都对数据点进行采样,构建支持和查询集,并训练模型:

for epoch in range(num_epochs):
    for episode in range(num_episodes):
        # select 60 classes
        episodic_classes = np.random.permutation(no_of_classes)[:num_way]
        support = np.zeros([num_way, num_shot, img_height, img_width], dtype=np.float32)
        query = np.zeros([num_way, num_query, img_height, img_width], dtype=np.float32)
        for index, class_ in enumerate(episodic_classes):
            selected = np.random.permutation(num_examples)[:num_shot + num_query]
            support[index] = train_dataset[class_, selected[:num_shot]]
            # 5 querypoints per classs
            query[index] = train_dataset[class_, selected[num_shot:]]
        support = np.expand_dims(support, axis=-1)
        query = np.expand_dims(query, axis=-1)
        labels = np.tile(np.arange(num_way)[:, np.newaxis], (1, num_query)).astype(np.uint8)
        _, loss_, accuracy_ = sess.run([train, loss, accuracy], feed_dict={support_set: support, query_set: query, y:labels})
        if (episode+1) % 20 == 0:
            print('Epoch {} : Episode {} : Loss: {}, Accuracy: {}'.format(epoch+1, episode+1, loss_, accuracy_))

高斯原型网络

现在,我们将研究一种原型网络的变体,称为高斯原型网络。 我们刚刚学习了原型网络如何学习数据点的嵌入以及如何通过获取每个类的均值嵌入并使用类原型进行分类来构建类原型的。

在高斯原型网络中,连同为数据点生成嵌入,我们在它们周围添加一个以高斯协方差矩阵为特征的置信区域。 拥有置信度区域有助于表征单个数据点的质量,并且在嘈杂且不太均匀的数据中很有用。

因此,在高斯原型网络中,编码器的输出将是嵌入以及协方差矩阵。 除了使用完整的协方差矩阵之外,我们还包括来自协方差矩阵的半径或对角线分量以及嵌入:

  • 半径分量:如果我们使用协方差矩阵的半径分量,则我们的协方差矩阵的维数将为 1,因为半径只是一个整数。
  • 对角分量:如果我们使用协方差矩阵的对角分量,则我们的协方差矩阵的维数将与嵌入矩阵的维数相同。

此外,我们使用协方差矩阵的逆矩阵来代替直接使用协方差矩阵。 我们可以使用以下任何一种方法将原始协方差矩阵转换为逆协方差矩阵。 令S_ori为协方差矩阵,S为逆协方差矩阵:

  • S = 1 + Softplus(S_ori)
  • S = 1 + Sigmoid(S_ori)
  • S = 1 + 4 * Sigmoid(S_ori)
  • S = Bias + Scale * softplus(S_ori),其中BiasScale是可训练的参数

因此,编码器,以及为输入生成嵌入,还返回协方差矩阵。 我们使用协方差矩阵的对角线或半径分量。 同样,我们使用逆协方差矩阵代替直接使用协方差矩阵。

但是将协方差矩阵与嵌入一起使用有什么用? 如前所述,它在数据点周围添加了置信区域,在嘈杂的数据中非常有用。 看下图。 假设我们有两个类,AB。 黑点表示数据点的嵌入,黑点周围的圆圈表示协方差矩阵。 大的虚线圆表示一个类的整体协方差矩阵。 中间的星星表示类的原型。 如您所见,在嵌入周围有这个协方差矩阵,这给了我们围绕数据点和类原型的置信度区域:

让我们通过查看代码更好地理解这一点。 假设我们有一个图像X,我们想为该图像生成嵌入。 让我们用 sigma 表示协方差矩阵。 首先,我们选择要使用协方差矩阵的哪个分量,即我们要使用对角分量还是半径分量。 如果我们使用半径分量,那么我们的协方差矩阵维将仅为 1。 如果我们选择对角线分量,则协方差矩阵的大小将与嵌入维数相同:

if component =='radius':
    covariance_matrix_dim = 1
else:
    covariance_matrix_dim = embedding_dim

现在,我们定义编码器。 由于我们的输入是图像,因此我们使用卷积块作为编码器。 因此,我们定义了过滤器的大小,过滤器的数量以及池化层的大小:

filters = [3,3,3,3] 
num_filters = [64,64,64,embedding_dim +covariance_matrix_dim] 
pools = [2,2,2,2]

我们将嵌入初始化为我们的图片X

previous_channels = 1 
embeddings = X 
weight = []
bias = []
conv_relu = []
conv = []
conv_pooled = []

然后,我们执行卷积运算并获得嵌入:

for i in range(len(filters)):
    filter_size = filters[i]
    num_filter = num_filters[i]
    pool = pools[i]
    weight.append(tf.get_variable("weights_"+str(i), shape=[filter_size, filter_size, previous_channels, num_filter])
    bias.append(tf.get_variable("bias_"+str(i), shape=[num_filter]))
    conv.append(tf.nn.conv2d(embeddings, weight[i], strides=[1,1,1,1], padding='SAME') + bias[i])
    conv_relu.append(tf.nn.relu(conv[i]))
    conv_pooled.append(tf.nn.max_pool(conv_relu[i], ksize = [1,pool,pool,1], strides=[1,pool,pool,1], padding = "VALID"))
    previous_channels = num_filter
    embeddings = conv_pooled [i]

我们将最后一个卷积层的输出作为我们的嵌入,并对结果进行整形以具有嵌入以及协方差矩阵:

X_encoded = tf.reshape(embeddings,[-1,embedding_dim + covariance_matrix_dim ])

现在,我们将嵌入和原始协方差矩阵拆分,因为我们需要将原始协方差矩阵转换为逆协方差矩阵:

embeddings, raw_covariance_matrix = tf.split(X_encoded, [embedding_dim, covariance_matrix_dim], 1)

接下来,我们使用任何讨论的方法来计算协方差矩阵的逆:

if inverse_transform_type == "softplus":
    offset = 1.0
    scale = 1.0
    inv_covariance_matrix = offset + scale * tf.nn.softplus(raw_covariance_matrix)
elif inverse_transform_type == "sigmoid":
    offset = 1.0
    scale = 1.0
    inv_covariance_matrix = offset + scale * tf.sigmoid(raw_covariance_matrix)
elif inverse_transform_type == "sigmoid_2":
    offset = 1.0
    scale = 4.0
    inv_covariance_matrix = offset + scale * tf.sigmoid(raw_covariance_matrix)
elif inverse_transform_type == "other":
    init = tf.constant(1.0)
    scale = tf.get_variable("scale", initializer=init)
    div = tf.get_variable("div", initializer=init)
    offset = tf.get_variable("offset", initializer=init)
    inv_covariance_matrix = offset + scale * tf.nn.softplus(raw_covariance_matrix/div)

到目前为止,我们已经看到我们可以计算协方差矩阵以及输入的嵌入。 下一步是什么? 我们如何计算类原型? 类原型p[c]可以如下计算:

在该方程式中,s[i]^c是逆协方差矩阵的对角线,x[i]^c表示嵌入,上标c表示类别。

Python 元学习实用指南:1~5(3)https://developer.aliyun.com/article/1426921

相关文章
|
2月前
|
机器学习/深度学习 前端开发 算法
学习Python需要多久?
【7月更文挑战第6天】学习Python需要多久?
51 5
|
22天前
|
程序员 测试技术 开发工具
豆瓣评分7.9!世界级讲师耗时5年整理出的Python学习手册!
Python是一门流行的开源编程语言,广泛用于各个领域的独立程序与脚本化应用中。它不仅免费、可移植、功能强大,同时相对简单,而且使用起来充满乐趣。从软件业界的任意一角到来的程序员,都会发现Python着眼于开发者的生产效率以及软件质量,因此无论你的项目是大还是小,选择Python都将带来战略性的优势。 今天给小伙伴们分享的这份手册讲述了完整的Python语言,力争满足“语言”和“原理”两个方面的需求,并拥有足够的深度以便实用。废话不多说,下面展示给大家。
|
23天前
|
数据采集 数据可视化 Ruby
GitHub星标破万!Python学习教程(超详细),真的太强了!
Python 是一门初学者友好的编程语言,想要完全掌握它,你不必花上太多的时间和精力。 Python 的设计哲学之一就是简单易学,体现在两个方面: 1. 语法简洁明了:相对 Ruby 和 Perl,它的语法特性不多不少,大多数都很简单直接,不玩儿玄学。 2. 切入点很多:Python 可以让你可以做很多事情,科学计算和数据分析、爬虫、Web 网站、游戏、命令行实用工具等等等等,总有一个是你感兴趣并且愿意投入时间的。
|
1月前
|
机器学习/深度学习 开发者 Python
Python 与 R 在机器学习入门中的学习曲线差异
【8月更文第6天】在机器学习领域,Python 和 R 是两种非常流行的编程语言。Python 以其简洁的语法和广泛的社区支持著称,而 R 则以其强大的统计功能和数据分析能力受到青睐。本文将探讨这两种语言在机器学习入门阶段的学习曲线差异,并通过构建一个简单的线性回归模型来比较它们的体验。
47 7
|
1月前
|
JSON API 开发者
Python学习Get方式通过商品 ID请求 获取拼多多商品详情数据接口
拼多多商品详情数据接口服务使开发者或商家能编程获取平台商品详情,涵盖标题、价格、销量等关键信息,助力市场分析与决策。使用前需注册开发者账号并获取API密钥;构造含商品ID等参数的请求URL后发送至API服务器;接口以JSON格式返回数据。应用场景包括商品销售分析、选品、品牌口碑挖掘及竞品分析,为商家提供强大数据支持。
|
1月前
|
算法 数据挖掘 大数据
深入学习Python的性能优化
【8月更文挑战第9天】深入学习Python性能优化涵盖设定明确目标、运用timeit与cProfile等工具诊断瓶颈、优化代码结构与算法、采用并行/并发技术、利用生成器与第三方库等策略。这是一个持续学习的过程,旨在全面提升代码效率与响应速度。
25 1
|
1月前
|
数据采集 人工智能 数据可视化
【2023年电工杯竞赛】B题 人工智能对大学生学习影响的评价 数学建模方案和python代码
本文介绍了2023年电工杯竞赛B题的数学建模方案和Python代码实现,详细阐述了如何分析调查问卷数据,建立评价指标体系,构建数学模型评估人工智能对大学生学习的影响,并提供了数据预处理、特征编码、可视化分析等代码示例。
33 0
【2023年电工杯竞赛】B题 人工智能对大学生学习影响的评价 数学建模方案和python代码
|
17天前
|
存储 JSON 测试技术
Python中最值得学习的第三方JSON库
Python中最值得学习的第三方JSON库
|
2月前
|
机器学习/深度学习 搜索推荐 TensorFlow
使用Python实现深度学习模型:智能教育与个性化学习
【7月更文挑战第29天】 使用Python实现深度学习模型:智能教育与个性化学习
111 9
|
1月前
|
机器学习/深度学习 人工智能 TensorFlow
神经网络不再是黑魔法!Python带你一步步拆解,让AI学习看得见
【8月更文挑战第3天】神经网络,曾被视为难以触及的黑魔法,现已在Python的助力下变得平易近人。以TensorFlow或PyTorch为“魔法杖”,仅需几行Python代码即可构建强大的AI模型。从零开始,我们将教导AI识别手写数字,利用经典的MNIST数据集。通过数据加载、预处理至模型训练与评估,每个步骤都如精心编排的舞蹈般清晰可见。随着训练深入,AI逐渐学会辨认每个数字,其学习过程直观展现。这不仅揭示了神经网络的奥秘,更证明了任何人都能借助Python创造AI奇迹,共同探索未来的无限可能。
31 2