这个例子展示了如何训练暹罗网络来识别手写字符的相似图像。
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”,真的,...“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
.
显示随机选择的图像。
idxs = randperm(numel(imdsTrain.Files),8);为i = 1: nummel (idxs) subplot(4,2,i) imshow(readimage(imdsTrain,idxs(i)) title(imdsTrain. labels (idxs(i)),“翻译”,“没有”);结束
为了训练网络,必须将数据分组成相似或不相似的图像对。在这里,相似的图像是相同字符的不同手写实例,它们具有相同的标签,而不同字符的不相似图像具有不同的标签。这个函数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,并使用窄正态分布初始化权重和偏置。
图层= [imageInputLayer([105 105 1],“名字”,“input1”,“归一化”,“没有”64年)convolution2dLayer(10日,“名字”,“conv1”,“WeightsInitializer”,“narrow-normal”,“BiasInitializer”,“narrow-normal”) reluLayer (“名字”,“relu1”) maxPooling2dLayer (2“步”,2,“名字”,“maxpool1”) convolution2dLayer (7128,“名字”,“conv2”,“WeightsInitializer”,“narrow-normal”,“BiasInitializer”,“narrow-normal”) reluLayer (“名字”,“relu2”) maxPooling2dLayer (2“步”,2,“名字”,“maxpool2”) convolution2dLayer (4128,“名字”,“conv3”,“WeightsInitializer”,“narrow-normal”,“BiasInitializer”,“narrow-normal”) reluLayer (“名字”,“relu3”) maxPooling2dLayer (2“步”,2,“名字”,“maxpool3”) convolution2dLayer (5256,“名字”,“conv4”,“WeightsInitializer”,“narrow-normal”,“BiasInitializer”,“narrow-normal”) reluLayer (“名字”,“relu4”) fullyConnectedLayer (4096,“名字”,“fc1”,“WeightsInitializer”,“narrow-normal”,“BiasInitializer”,“narrow-normal”));lgraph = layerGraph(图层);
要使用自定义训练循环训练网络并启用自动区分,请将层图转换为adlnetwork
对象。
Dlnet = 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
函数输出关于两个图像相似度的预测。
创建函数modelGradients
(定义于金宝app支持功能部分)。的modelGradients
函数取暹罗子网络dlnet
的参数结构fullyconnect
操作,并输入小批量数据X1
而且X2
用他们的标签pairLabels
.该函数返回相对于网络的可学习参数的损失值和损失的梯度。
暹罗网络的目标是区分两个输入X1
而且X2
.网络的输出是一个概率之间0
而且1
,其中值更接近0
表示图像不相似的预测,以及更接近的值1
图像是相似的。损失由预测分数与真实标签值之间的二进制交叉熵给出:
真正的标签 可以是0或1和 是预测标签。
指定在培训期间使用的选项。训练10000次迭代。
numIterations = 10000;miniBatchSize = 180;
指定ADAM优化的选项:
设置学习率为0.00006
.
初始化尾随平均梯度和尾随平均梯度平方衰减率[]
对于这两个dlnet
而且fcParams
.
设置梯度衰减因子为0.9
平方梯度衰减因子0.99
.
learningRate = 6e-5;trailingAvgSubnet = [];trailingAvgSqSubnet = [];trailingAvgParams = [];trailingAvgSqParams = [];gradDecay = 0.9;gradDecaySq = 0.99;
在GPU上训练(如果有的话)。使用GPU需要并行计算工具箱™和受支持的GPU设备。金宝app有关受支持设备的信息,请参见金宝appGPU支金宝app持版本(并行计算工具箱).若要自动检测是否有可用的GPU,并将相关数据放置在GPU上,请设置为executionEnvironment
来“汽车”
.如果您没有GPU,或者不想使用GPU进行训练,请设置为executionEnvironment
来“cpu”
.为确保使用GPU进行训练,可设置executionEnvironment
来“图形”
.
executionEnvironment =“汽车”;
为了监控训练进度,您可以在每次迭代后绘制训练损失图。创建变量情节
包含“训练进步”
.如果您不想绘制训练进度,请将此值设置为“没有”
.
情节=“训练进步”;
初始化训练损失进度图的图参数。
plotRatio = 16/9;如果情节= =“训练进步”trainingPlot =数字;trainingPlot.Position(3) = plotRatio*trainingPlot.Position(4);trainingPlot。可见=“上”;trainingPlotAxes = gca;lineLossTrain = animatedline(trainingPlotAxes);包含(trainingPlotAxes“迭代”) ylabel (trainingPlotAxes“损失”)标题(trainingPlotAxes“培训期间的损失”)结束
使用自定义训练循环训练模型。遍历训练数据并在每次迭代中更新网络参数。
对于每个迭代:
方法提取一批图像对和标签getSiameseBatch
节中定义的函数批量创建镜像对.
将数据转换为dlarray
具有基础类型的对象单
并指定尺寸标签“SSCB”
(空间,空间,通道,批)为图像数据和“CB”
(通道,批次)标签。
对于GPU训练,将数据转换为gpuArray
对象。
评估模型梯度使用dlfeval
和modelGradients
函数。
方法更新网络参数adamupdate
函数。
在小批上循环。为iteration = 1:numIterations提取小批量图像对和图像对标签[X1,X2,pairLabels] = getSiameseBatch(imdsTrain,miniBatchSize);将小批量数据转换为大数组。指定尺寸标签% 'SSCB'(空间,空间,通道,批处理)用于图像数据dlX1 = dlarray(single(X1),“SSCB”);dlX2 = dlarray(single(X2),“SSCB”);如果在GPU上训练,则将数据转换为gpuArray。如果(executionEnvironment = =“汽车”&& canUseGPU) || executionEnvironment ==“图形”dlX1 = gpuArray(dlX1);dlX2 = gpuArray(dlX2);结束使用评估模型梯度和发电机状态的末尾列出的modelGradients函数%的例子。[gradientsSubnet, gradientsParams,loss] = dlfeval(@modelGradients,dlnet,fcParams,dlX1,dlX2,pairLabels);lossValue = double(gather(extractdata(loss)));更新Siamese子网参数。[dlnet, trailingAvgSubnet trailingAvgSqSubnet] =...adamupdate (dlnet gradientsSubnet,...trailingAvgSubnet trailingAvgSqSubnet,迭代,learningRate、gradDecay gradDecaySq);更新全连接参数。[fcParams, trailingAvgParams trailingAvgSqParams] =...adamupdate (fcParams gradientsParams,...trailingAvgParams trailingAvgSqParams,迭代,learningRate、gradDecay gradDecaySq);更新培训损失进度图。如果情节= =“训练进步”addpoints (lineLossTrain迭代,lossValue);结束drawnow;结束
下载并提取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”,真的,...“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提取小批量图像对和图像对标签[XAcc1,XAcc2,pairLabelsAcc] = getSiameseBatch(imdsTest,accuracyBatchSize);将小批量数据转换为大数组。指定尺寸标签% 'SSCB'(空间,空间,通道,批处理)用于图像数据。dlXAcc1 = dlarray(single(XAcc1),“SSCB”);dlXAcc2 = dlarray(single(XAcc2),“SSCB”);如果使用GPU,则将数据转换为gpuArray。如果(executionEnvironment = =“汽车”&& canUseGPU) || executionEnvironment ==“图形”dlXAcc1 = gpuArray(dlXAcc1);dlXAcc2 = gpuArray(dlXAcc2);结束使用训练过的网络评估预测dlY = predictSiamese(dlnet,fcParams,dlXAcc1,dlXAcc2);将预测转换为二进制0或1Y = gather(extractdata(dlY));Y =圆(Y);计算小批的平均精度precision (i) = sum(Y == pairLabelsAcc)/accuracyBatchSize;结束%计算所有小批的准确性averageAccuracy =平均值(准确度)*100
averageAccuracy = 88.6667
为了直观地检查网络是否正确地识别了相似和不同的图像对,可以创建一小批图像对进行测试。使用predictSiamese函数获得每个测试对的预测。显示带有预测的一对图像、概率分数和指示预测是否正确的标签。
testBatchSize = 10;[XTest1,XTest2,pairLabelsTest] = getSiameseBatch(imdsTest,testBatchSize);将测试批数据转换为darray。指定尺寸标签% 'SSCB'(空间,空间,通道,批处理)用于图像数据和'CB'%(通道,批次)用于标签dlXTest1 = dlarray(single(XTest1),“SSCB”);dlXTest2 = dlarray(single(XTest2),“SSCB”);如果使用GPU,则将数据转换为gpuArray如果(executionEnvironment = =“汽车”&& canUseGPU) || executionEnvironment ==“图形”dlXTest1 = gpuArray(dlXTest1);dlXTest2 = gpuArray(dlXTest2);结束计算预测的概率dlYScore = predictSiamese(dlnet,fcParams,dlXTest1,dlXTest2);YScore = gather(extractdata(dlYScore));将预测转换为二进制0或1YPred = round(YScore);提取数据绘制XTest1 = extractdata(dlXTest1);XTest2 = extractdata(dlXTest2);有预测标签和预测评分的图像testingPlot =图;testingPlot.Position(3) = plotRatio*testingPlot.Position(4);testingPlot。可见=“上”;为i = 1:数字(pairLabelsTest)如果YPred(i) == 1 predLabel =“相似”;其他的predLabel =“不同”;结束如果pairLabelsTest(i) == YPred(i) testStr =“{暗绿色}\ bf \颜色正确\ rm \换行符”;其他的testStr =“{红}\ bf \颜色错误\ rm \换行符”;结束次要情节(2、5、我)imshow ([XTest1 (::,:, i) XTest2(::,:,我)]);标题(testStr +{黑}\颜色预测:“+ predLabel +“\ newlineScore:“+ YScore(我));结束
该网络能够比较测试图像以确定它们的相似性,即使这些图像都不在训练数据集中。
这个函数forwardSiamese
用于网络训练。函数定义了子网络和fullyconnect
而且乙状结肠
这些操作结合起来形成了完整的暹罗网络。forwardSiamese接受网络结构和两张训练图像,并输出两张图像相似度的预测。在本例中,函数forwardSiamese
在本节中介绍了什么定义网络架构.
函数Y = forwardSiamese(dlnet,fcParams,dlX1,dlX2)siamese接受网络和一对训练图像,并返回一个%预测的概率对是相似的(接近1)或%不相似(接近0)。训练时使用正向连体语。通过双子网传递第一个映像F1 = forward(dlnet,dlX1);F1 = sigmoid(F1);通过双子网传递第二个映像F2 = forward(dlnet,dlX2);F2 = sigmoid(F2);减去特征向量Y = abs(F1 - F2);通过完全连接操作传递结果Y = fulllyconnect (Y,fcParams.FcWeights,fcParams.FcBias);转换为0到1之间的概率。Y = sigmoid(Y);结束
这个函数predictSiamese
使用经过训练的网络对两幅图像的相似性进行预测。函数和函数是相似的forwardSiamese
,前面定义的。然而,predictSiamese
使用预测
函数与网络,而不是向前
函数,因为一些深度学习层在训练和预测过程中表现不同。在本例中,函数predictSiamese
在本节中介绍了什么评估网络的准确性.
函数Y = predictSiamese(dlnet,fcParams,dlX1,dlX2)% predictSiamese接受网络和图像对,并返回一个对相似概率的%预测(更接近1)%或不相同(接近于0)。通过双子网传递第一个映像F1 = predict(dlnet,dlX1);F1 = sigmoid(F1);通过双子网传递第二个映像F2 = predict(dlnet,dlX2);F2 = sigmoid(F2);减去特征向量Y = abs(F1 - F2);通过完全连接操作传递结果Y = fulllyconnect (Y,fcParams.FcWeights,fcParams.FcBias);转换为0到1之间的概率。Y = sigmoid(Y);结束
这个函数modelGradients
比如暹罗人dlnetwork
对象网
,一对小批量输入数据X1
而且X2
,以及标明它们是否相似的标签。该函数返回关于网络中可学习参数的损失的梯度,以及预测和地面真相之间的二进制交叉熵损失。在本例中,函数modelGradients
在本节中介绍了什么定义模型梯度函数.
函数[gradientsSubnet,gradientsParams,loss] = modelGradients(dlnet,fcParams,dlX1,dlX2,pairLabels)模型梯度函数计算二元交叉熵损失%的配对图像,并返回损失和损失的梯度%网络可学习参数%通过网络传递映像对Y = forwardSiamese(dlnet,fcParams,dlX1,dlX2);计算二元交叉熵损失loss = binarycrossentropy(Y,pairLabels);计算相对于网络可学习的损失的梯度%的参数[gradientsSubnet,gradientsParams] = dlgradient(loss,dlnet.Learnables,fcParams);结束函数loss = binarycrossentropy(Y,pairLabels)% binarycrossentropy接受网络的预测Y,即true% label和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
在本节中介绍了什么创建相似和不相似的图像对。
函数[X1,X2,pairLabels] = getSiameseBatch(imds,miniBatchSize)% getSiameseBatch返回随机选择的批处理或成对图像。在%平均,这个函数产生一个平衡的相似和不相似的集合%对。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);结束结束函数[pairIdx1,pairIdx2,pairLabel] = getSimilarPair(classLabel)getSimilarSiamesePair返回图像的随机索引对%属于同一类且相似的pair标签= 1。找到所有唯一的类。classes =唯一的(classLabel);%随机选择一个类,将用于获得类似的一对。classChoice = randi(编号(类));找到所选类中所有观测值的指数。。idxs = find(classLabel==classes(classChoice));从所选类别中随机选择两张不同的图片。pairIdxChoice = randperm(数字(idxs),2);pairIdx1 = idxs(pairIdxChoice(1));pairIdx2 = idxs(pairIdxChoice(2));pairLabel = 1;结束函数[pairIdx1,pairIdx2,label] = getDissimilarPair(classLabel)getDissimilarSiamesePair返回图像的随机索引对%,在不同的类和不同的对标签= 0。找到所有唯一的类。classes =唯一的(classLabel);%随机选择两个不同的班级,将会得到一个不同的配对。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