训练暹罗网络来比较图像
这个例子展示了如何训练暹罗网络来识别手写字符的相似图像。
Siamese网络是一种深度学习网络,它使用两个或多个具有相同架构并共享相同参数和权重的相同子网络。暹罗网络通常用于寻找两个可比较事物之间关系的任务。Siamese网络的一些常见应用包括面部识别、签名验证[1]或释义识别[2]。Siamese网络在这些任务中表现良好,因为它们共享的权重意味着在训练过程中需要学习的参数更少,并且它们可以用相对少量的训练数据产生良好的结果。
Siamese网络在有大量类的情况下特别有用,每个类的观察数量很少。在这种情况下,没有足够的数据来训练深度卷积神经网络来将图像分类为这些类别。相反,Siamese网络可以确定两张图像是否属于同一类。
本例使用Omniglot数据集[3]训练暹罗网络来比较手写字符[4]的图像。Omniglot数据集包含50个字母的字符集,分为30个用于训练,20个用于测试。每个字母表都包含一些字符,从Ojibwe(加拿大土著Sullabics)的14个字符到Tifinagh的55个字符。最后,每个角色都有20个手写的观察。这个例子训练了一个网络来识别两个手写的观察是否是同一字符的不同实例。
还可以使用Siamese网络通过降维来识别相似的图像。有关示例,请参见训练一个用于降维的暹罗网络.
加载和预处理训练数据
下载并提取Omniglot训练数据集。
url =“https://github.com/brendenlake/omniglot/raw/master/python/images_background.zip”;downloadFolder = tempdir;文件名= fullfile(下载文件夹,“images_background.zip”);dataFolderTrain = fullfile(下载文件夹,“images_background”);如果~存在(dataFolderTrain“dir”) disp (“下载Omniglot训练数据(4.5 MB)…”) websave(文件名,url);解压缩(文件名,downloadFolder);结束disp (“下载训练数据。”)
培训数据下载。
方法将训练数据加载为图像数据存储imageDatastore
函数。手动指定标签,方法是从文件名中提取标签并设置标签
财产。
imdsTrain = imageDatastore(dataFolderTrain,...IncludeSubfolders = true,...LabelSource =“没有”);files = imdsTrain.Files;Parts = split(files,filesep);标签= join(parts(:,(end-2):(end-1)),“-”);imdsTrain。标签=categorical(labels);
Omniglot训练数据集由来自30个字母的黑白手写字符组成,每个字符有20个观察结果。图像的大小为105 × 105 × 1,每个像素的值在之间0
而且1
.
显示随机选择的图像。
idx = randperm(numel(imdsTrain.Files),8);为i = 1: nummel (idx) subplot(4,2,i) imshow(readimage(imdsTrain,idx(i))) title(imdsTrain. labels (idx(i)),Interpreter=“没有”);结束
创建相似和不相似的图像对
为了训练网络,必须将数据分组成相似或不相似的图像对。在这里,相似的图像是相同字符的不同手写实例,它们具有相同的标签,而不同字符的不相似图像具有不同的标签。这个函数getSiameseBatch
(定义于金宝app支持功能这个例子的部分)创建随机的相似或不相似的图像对,pairImage1
而且pairImage2
.该函数还返回标签pairLabel
,它可以识别这对图像彼此是否相似。类似的图片已经出现了pairLabel = 1
,而不同的配对则有pairLabel = 0
.
例如,创建一个由五对图像组成的小型代表性集合
batchSize = 10;[pairImage1,pairImage2,pairLabel] = getSiameseBatch(imdsTrain,batchSize);
显示生成的图像对。
为i = 1:batchSize如果pairLabel(i) == 1 s =“相似”;其他的s =“不同”;结束次要情节(2、5、我)imshow ([pairImage1 (::,:, i) pairImage2(::,:,我)]);标题(年代)结束
在本例中,为训练循环的每次迭代创建了一批新的180对图像。这确保了网络是在大量随机图像对上训练的,相似和不同图像对的比例大致相等。
定义网络架构
Siamese网络架构如下图所示。
为了比较两个图像,每个图像将通过两个相同的共享权重的子网络中的一个。子网络将每张105 × 105 × 1的图像转换为4096维的特征向量。同一类的图像具有类似的4096维表示。每个子网络的输出特征向量通过减法组合,结果通过a传递fullyconnect
具有单个输出的操作。一个乙状结肠
操作将此值转换为之间的概率0
而且1
,表示网络对图像相似与否的预测。在训练过程中,使用网络预测和真实标签之间的二进制交叉熵损失来更新网络。
在本例中,两个相同的子网被定义为adlnetwork
对象。最后一个fullyconnect
而且乙状结肠
操作作为子网络输出的功能操作来执行。
创建子网为一系列层,接受105 × 105 × 1的图像,并输出大小为4096的特征向量。
为convolution2dLayer
对象,使用窄正态分布初始化权重和偏差。
为maxPooling2dLayer
对象,设置步幅为2
.
对于期末考试fullyConnectedLayer
对象,指定输出大小为4096,并使用窄正态分布初始化权重和偏置。
layers = [imageInputLayer([105 105 1],归一化=“没有”) convolution2dLayer (64, WeightsInitializer =“narrow-normal”BiasInitializer =“narrow-normal”) reluLayer maxPooling2dLayer(2,Stride=2) convolution2dLayer(7,128,WeightsInitializer=“narrow-normal”BiasInitializer =“narrow-normal”) reluLayer maxPooling2dLayer(2,Stride=2) convolution2dLayer(4,128,WeightsInitializer=“narrow-normal”BiasInitializer =“narrow-normal”) reluLayer maxPooling2dLayer(2,Stride=2) convolution2dLayer(5256,WeightsInitializer=“narrow-normal”BiasInitializer =“narrow-normal”) reluLayer fullyConnectedLayer(4096,WeightsInitializer=“narrow-normal”BiasInitializer =“narrow-normal”));lgraph = layerGraph(图层);
要使用自定义训练循环训练网络并启用自动区分,请将层图转换为adlnetwork
对象。
Net = dlnetwork(lgraph);
创建期末考试的权重fullyconnect
操作。通过从标准偏差为0.01的窄正态分布中随机抽样来初始化权重。
fcWeights = dlarray(0.01*randn(1,4096));fcBias = dlarray(0.01*randn(1,1));fcParams = struct(...“FcWeights”fcWeights,...“FcBias”, fcBias);
如果需要使用网络,请创建该函数forwardSiamese
(定义于金宝app支持功能这个例子的一部分)定义了两个子网络和减法,fullyconnect,
而且乙状结肠
操作被合并。这个函数forwardSiamese
接受网络,包含参数的结构为fullyconnect
操作,和两个训练图像。的forwardSiamese
函数输出关于两个图像相似度的预测。
定义模型损失函数
创建函数modelLoss
(定义于金宝app支持功能部分)。的modelLoss
函数取暹罗子网络网
的参数结构fullyconnect
操作,并输入小批量数据X1
而且X2
用他们的标签pairLabels
.该函数返回相对于网络的可学习参数的损失值和损失的梯度。
暹罗网络的目标是区分两个输入X1
而且X2
.网络的输出是一个概率之间0
而且1
,其中值更接近0
表示图像不相似的预测,以及更接近的值1
图像是相似的。损失由预测分数与真实标签值之间的二进制交叉熵给出:
真正的标签 可以是0或1和 是预测标签。
指定培训项目
指定在培训期间使用的选项。训练10000次迭代。
numIterations = 10000;miniBatchSize = 180;
指定ADAM优化的选项:
设置学习率为
0.00006
.设置梯度衰减因子为
0.9
平方梯度衰减因子0.99
.
learningRate = 6e-5;gradDecay = 0.9;gradDecaySq = 0.99;
在GPU上训练(如果有的话)。使用GPU需要并行计算工具箱™和受支持的GPU设备。金宝app有关受支持设备的信息,请参见金宝appGPU支金宝app持版本(并行计算工具箱).若要自动检测是否有可用的GPU,并将相关数据放置在GPU上,请设置为executionEnvironment
来“汽车”
.如果您没有GPU,或者不想使用GPU进行训练,请设置为executionEnvironment
来“cpu”
.为确保使用GPU进行训练,可设置executionEnvironment
来“图形”
.
executionEnvironment =“汽车”;
火车模型
初始化培训进度图。
图C = colororder;lineLossTrain = animatedline(Color=C(2,:));Ylim ([0 inf]) xlabel(“迭代”) ylabel (“损失”网格)在
初始化ADAM求解器的参数。
trailingAvgSubnet = [];trailingAvgSqSubnet = [];trailingAvgParams = [];trailingAvgSqParams = [];
使用自定义训练循环训练模型。遍历训练数据并在每次迭代中更新网络参数。
对于每个迭代:
方法提取一批图像对和标签
getSiameseBatch
节中定义的函数批量创建镜像对.将数据转换为
dlarray
对象指定维度标签“SSCB”
(空间,空间,通道,批)为图像数据和“CB”
(通道,批次)标签。对于GPU训练,将数据转换为
gpuArray
对象。评估模型损失和梯度使用
dlfeval
和modelLoss
函数。方法更新网络参数
adamupdate
函数。
开始= tic;在小批上循环。为iteration = 1:numIterations提取小批量图像对和图像对标签[X1,X2,pairLabels] = getSiameseBatch(imdsTrain,miniBatchSize);将小批量数据转换为大数组。指定尺寸标签%“SSCB”(空间、空间、通道、批处理)用于图像数据X1 = dlarray(X1,“SSCB”);X2 = dlarray(X2,“SSCB”);如果在GPU上训练,则将数据转换为gpuArray。如果(executionEnvironment = =“汽车”&& canUseGPU) || executionEnvironment ==“图形”X1 = gpuArray(X1);X2 = gpuArray(X2);结束使用dlfeval和modelLoss评估模型损失和梯度在示例末尾列出的%函数。[loss,gradientsSubnet,gradientsParams] = dlfeval(@modelLoss,net,fcParams,X1,X2,pairLabels);更新Siamese子网参数。[net,trailingAvgSubnet,trailingAvgSqSubnet] = adamupdate(net,gradientsSubnet,...trailingAvgSubnet trailingAvgSqSubnet,迭代,learningRate、gradDecay gradDecaySq);更新全连接参数。[fcParams,trailingAvgParams,trailingAvgSqParams] = adamupdate(fcParams,gradientsParams,...trailingAvgParams trailingAvgSqParams,迭代,learningRate、gradDecay gradDecaySq);更新培训损失进度图。D = duration(0,0,toc(start),Format=“hh: mm: ss”);lossValue = double(损失);addpoints (lineLossTrain迭代,lossValue);标题(”经过:“+字符串(D))现在绘制结束
评估网络的准确性
下载并提取Omniglot测试数据集。
url =“https://github.com/brendenlake/omniglot/raw/master/python/images_evaluation.zip”;downloadFolder = tempdir;文件名= fullfile(下载文件夹,“images_evaluation.zip”);dataFolderTest = fullfile(下载文件夹,“images_evaluation”);如果~存在(dataFolderTest“dir”) disp (“下载Omniglot测试数据(3.2 MB)…”) websave(文件名,url);解压缩(文件名,downloadFolder);结束disp (“测试数据已下载。”)
下载测试数据。
方法将测试数据加载为映像数据存储imageDatastore
函数。手动指定标签,方法是从文件名中提取标签并设置标签
财产。
imdsTest = imageDatastore(dataFolderTest,...IncludeSubfolders = true,...LabelSource =“没有”);files = imdsTest.Files;Parts = split(files,filesep);标签= join(parts(:,(end-2):(end-1)),“_”);imdsTest。标签=categorical(labels);
测试数据集包含20个字母,这些字母与网络训练使用的字母不同。测试数据集中总共有659个不同的类。
numClasses = nummel(唯一的(imdsTest.Labels))
numClasses = 659
为了计算网络的准确性,创建一组5个随机的小批量测试对。使用predictSiamese
函数(定义在金宝app支持功能部分)来评估网络预测并计算小批量的平均精度。
精度= 0 (1,5);精度= 150;为I = 1:5提取小批量图像对和图像对标签[X1,X2,pairLabelsAcc] = getSiameseBatch(imdsTest,accuracyBatchSize);将小批量数据转换为大数组。指定尺寸标签%“SSCB”(空间、空间、通道、批处理)用于图像数据。X1 = dlarray(X1,“SSCB”);X2 = dlarray(X2,“SSCB”);如果使用GPU,则将数据转换为gpuArray。如果(executionEnvironment = =“汽车”&& canUseGPU) || executionEnvironment ==“图形”X1 = gpuArray(X1);X2 = gpuArray(X2);结束使用训练过的网络评估预测Y = predictSiamese(net,fcParams,X1,X2);将预测转换为二进制0或1Y = gather(extractdata(Y));Y =圆(Y);计算小批的平均精度precision (i) = sum(Y == pairLabelsAcc)/accuracyBatchSize;结束
计算所有小批的精度
averageAccuracy =平均值(准确度)*100
averageAccuracy = 89.0667
显示带有预测的图像测试集
为了直观地检查网络是否正确地识别了相似和不同的图像对,可以创建一小批图像对进行测试。使用predictSiamese函数获得每个测试对的预测。显示带有预测的一对图像、概率分数和指示预测是否正确的标签。
testBatchSize = 10;[XTest1,XTest2,pairLabelsTest] = getSiameseBatch(imdsTest,testBatchSize);
将测试批数据转换为dlarray
.指定尺寸标签“SSCB”
(空间,空间,通道,批)用于图像数据。
XTest1 = dlarray(XTest1,“SSCB”);XTest2 = dlarray(XTest2,“SSCB”);
如果使用GPU,则将数据转换为gpuArray
.
如果(executionEnvironment = =“汽车”&& canUseGPU) || executionEnvironment ==“图形”XTest1 = gpuArray(XTest1);XTest2 = gpuArray(XTest2);结束
计算预测概率。
YScore = predictSiamese(net,fcParams,XTest1,XTest2);YScore = gather(extractdata(YScore));
将预测转换为二进制0或1。
YPred = round(YScore);
提取用于绘图的数据。
XTest1 = extractdata(XTest1);XTest2 = extractdata(XTest2);
带有预测标签和预测分数的Plot图像。
F =数字;tiledlayout(2、5);f.Position(3) = 2*f.Position(3);predLabels = categorical(YPred,[0 1],[“不同”“相似”]);targetLabels = categorical(pairLabelsTest,[0 1],[“不同”,“相似”]);为i = 1:元素个数(pairLabelsTest) nexttile imshow ([XTest1 (::,:, i) XTest2(::,:,我)]);标题(...目标:“+ string(targetLabels(i)) +换行符+...预测:“+字符串(predLabels(i)) +换行符+...“分数:+ YScore (i))结束
该网络能够比较测试图像以确定它们的相似性,即使这些图像都不在训练数据集中。
金宝app支持功能
训练与预测的模型函数
这个函数forwardSiamese
用于网络训练。函数定义了子网络和fullyconnect
而且乙状结肠
这些操作结合起来形成了完整的暹罗网络。forwardSiamese接受网络结构和两张训练图像,并输出两张图像相似度的预测。在本例中,函数forwardSiamese
在本节中介绍了什么定义网络架构.
函数Y = forwardSiamese(net,fcParams,X1,X2)% forwardSiamese接受网络和对训练图像,和%返回对相似(更接近)的概率的预测%到1)或不相似(接近0)。训练时使用正向暹罗语。通过双子网传递第一个映像Y1 =正向(净,X1);y = sigmoid(Y1);通过双子网传递第二个映像Y2 =正向(净,X2);Y2 = sigmoid(Y2);减去特征向量Y = abs(Y1 - Y2);通过完全连接操作传递结果Y = fulllyconnect (Y,fcParams.FcWeights,fcParams.FcBias);转换为0到1之间的概率。Y = sigmoid(Y);结束
这个函数predictSiamese
使用经过训练的网络对两幅图像的相似性进行预测。函数和函数是相似的forwardSiamese
,前面定义的。然而,predictSiamese
使用预测
函数与网络,而不是向前
函数,因为一些深度学习层在训练和预测过程中表现不同。在本例中,函数predictSiamese
在本节中介绍了什么评估网络的准确性.
函数Y = predictSiamese(net,fcParams,X1,X2)% predictSiamese接受网络和图像对,并返回一个%预测的概率对是相似的(接近1)或%不相似(接近0)。在预测时使用预测词。通过双子网传递第一个映像。Y1 =预测(净,X1);y = sigmoid(Y1);通过双子网传递第二个映像。Y2 =预测(净,X2);Y2 = sigmoid(Y2);减去特征向量。Y = abs(Y1 - Y2);通过完全连接操作传递结果。Y = fulllyconnect (Y,fcParams.FcWeights,fcParams.FcBias);转换为0到1之间的概率。Y = sigmoid(Y);结束
模型损失函数
这个函数modelLoss
比如暹罗人dlnetwork
对象网
,一对小批量输入数据X1
而且X2
,以及标明它们是否相似的标签。该函数返回预测和地面真相之间的二进制交叉熵损失,以及损失相对于网络中可学习参数的梯度。在本例中,函数modelLoss
在本节中介绍了什么定义模型损失函数.
函数[loss,gradientsSubnet,gradientsParams] = modelLoss(net,fcParams,X1,X2,pairLabels)%通过网络传递映像对。Y = forwardSiamese(net,fcParams,X1,X2);计算二元交叉熵损失。loss = binarycrossentropy(Y,pairLabels);计算相对于网络可学习的损失的梯度%的参数。[gradientsSubnet,gradientsParams] = dlgradient(loss,net.Learnables,fcParams);结束
二元交叉熵损失函数
的binarycrossentropy
函数接受网络预测和对标签,并返回二进制交叉熵损失值。
函数loss = binarycrossentropy(Y,pairLabels)获得预测精度,防止因浮点数导致的误差%的精度。precision = underlyingType(Y);将小于浮点精度的值转换为eps。Y(Y < eps(精度))= eps(精度);将1-eps和1之间的值转换为1-eps。Y(Y > 1 - eps(精度))= 1 - eps(精度);计算每对二元交叉熵损失loss = -pairLabels.*log(Y) - (1 -pairLabels)。*log(1 - Y);对小批中的所有对求和并归一化。loss = sum(loss)/numel(pairLabels);结束
批量创建镜像对
以下函数根据它们的标签创建相似或不相似的随机图像对。在本例中,函数getSiameseBatch
在本节中介绍了什么创建相似和不相似的图像对。
获取暹罗批处理函数
的getSiameseBatch
返回随机选择的批处理或配对图像。平均而言,这个函数产生一个由相似和不相似对组成的平衡集。
函数[X1,X2,pairLabels] = getSiameseBatch(imds,miniBatchSize) pairLabels = 0 (1,miniBatchSize);imgSize = size(readimage(imds,1));X1 = 0 ([imgSize 1 miniBatchSize],“单身”);X2 = 0 ([imgSize 1 miniBatchSize],“单身”);为i = 1:miniBatchSize select = rand(1);如果select < 0.5 [pairIdx1,pairIdx2,pairLabels(i)] = getSimilarPair(imds.Labels);其他的[pairIdx1,pairIdx2,pairLabels(i)] = getDissimilarPair(imds.Labels);结束X1(:,:,:,i) = imds.readimage(pairIdx1);X2(:,:,:,i) = imds.readimage(pairIdx2);结束结束
获取相似对函数
的getSimilarSiamesePair
函数返回属于同一类且相似的对标签等于1的图像的随机索引对。
函数[pairIdx1,pairIdx2,pairLabel] = getSimilarPair(classLabel)找到所有唯一的类。classes =唯一的(classLabel);%随机选择一个类,将用于获得类似的一对。classChoice = randi(编号(类));找到所选类中所有观测值的指数。。idxs = find(classLabel==classes(classChoice));从所选类别中随机选择两张不同的图片。pairIdxChoice = randperm(数字(idxs),2);pairIdx1 = idxs(pairIdxChoice(1));pairIdx2 = idxs(pairIdxChoice(2));pairLabel = 1;结束
获取异象对函数
的getDissimilarSiamesePair
函数返回属于不同类的图像的随机索引对,不同的对标签等于0。
函数[pairIdx1,pairIdx2,label] = getDissimilarPair(classLabel)找到所有唯一的类。classes =唯一的(classLabel);随机选择两个不同的班级来获得a%不相似对。classesChoice = randperm(编号(类),2);找到第一个和第二个观测值的指数%的类。idxs1 = find(classLabel==classes(classesChoice(1)));idxs2 = find(classLabel==classes(classesChoice(2)));从每个类别中随机选择一张图片。pairIdx1Choice = randi(编号(idxs1));pairIdx2Choice = randi(编号(idxs2));pairIdx1 = idxs1(pairIdx1Choice);pairIdx2 = idxs2(pairIdx2Choice);Label = 0;结束
参考文献
[1]布罗姆利,J., I.盖恩,Y.勒丘恩,E. Säckinger和R.沙阿。”使用“暹罗式”时滞神经网络进行签名验证。“第六届神经信息处理系统国际会议论文集(NIPS 1993), 1994,第737-744页。可以在使用“暹罗式”时滞神经网络进行签名验证在NIPS论文集网站上。
[2] Wenpeg, Y.和H Schütze。”释义识别的卷积神经网络。《ACL北美分会2015年会议论文集》,2015,pp901-911。可以在释义识别的卷积神经网络访问ACL选集网站
[3] Lake, b.m., Salakhutdinov, R.和Tenenbaum, j.b. "通过概率程序归纳的人类水平概念学习。《科学》,350(6266),(2015)pp1332-1338。
[4] Koch, G., Zemel, R., and Salakhutdinov, R.(2015)。”用于一次性图像识别的暹罗神经网络”。第32届国际机器学习会议论文集,37(2015)。可以在一键图像识别的暹罗神经网络在ICML'15网站上。
另请参阅
dlarray
|dlgradient
|dlfeval
|dlnetwork
|adamupdate