主要内容

使用注意力的序列到序列转换

这个例子展示了如何使用循环序列到序列的编码器-解码器模型将十进制字符串转换为罗马数字。

循环编码器-解码器模型已被证明在抽象文本摘要和神经机器翻译等任务中是成功的。该模型由一个编码器它通常使用循环层(如LSTM)处理输入数据,以及译码器它将编码的输入映射到所需的输出,通常带有第二层循环层。包含的模型注意机制进入模型允许解码器在生成翻译时专注于编码输入的部分。

对于编码器模型,本例使用了一个简单的网络,由一个嵌入和一个LSTM操作组成。嵌入是一种将类别标记转换为数字向量的方法。

对于解码器模型,本例使用了一个包含LSTM操作和注意机制的网络。注意机制允许解码器参加到编码器输出的特定部分。

负荷训练数据

下载小数-罗马数字对“romanNumerals.csv”

文件名= fullfile(“romanNumerals.csv”);options = detectImportOptions(文件名,...TextType =“字符串”...ReadVariableNames = false);选项。VariableNames = [“源”“目标”];选项。VariableTypes = [“字符串”“字符串”];数据=可读(文件名,选项);

将数据分成训练分区和测试分区,每个分区包含50%的数据。

Idx = randperm(size(data,1),500);dataTrain = data(idx,:);dataTest =数据;dataTest(idx,:) = [];

查看一些小数-罗马数字对。

头(dataTrain)
源目标  ______ ____________ " 437年”“CDXXXVII”“431”“CDXXXI”“102年”“人民共和国”“862年”“DCCCLXII”“738年”“DCCXXXVIII”“527年”“DXXVII”“401年”“CDI”“184年”“CLXXXIV”

数据进行预处理

属性预处理文本数据transformText函数,在示例末尾列出。的transformText函数通过将文本分割为字符并添加开始和停止标记,对输入文本进行预处理和标记,以进行翻译。要通过将文本分解为单词而不是字符来翻译文本,请跳过第一步。

startToken =“<开始>”;stopToken =“<停止>”;strSource = dataTrain.Source;documentsSource = transformText(strSource,startToken,stopToken);

创建一个wordEncoding对象,该对象使用词汇表将标记映射到数值索引,反之亦然。

encSource = worddencoding (documentsSource);

使用word编码,将源文本数据转换为数字序列。

sequencesSource = doc2sequence(encSource,documentsSource,PaddingDirection=“没有”);

使用相同的步骤将目标数据转换为序列。

strTarget = dataTrain.Target;documentsTarget = transformText(strTarget,startToken,stopToken);encTarget = worddencoding (documentsTarget);sequencesTarget = doc2sequence(encTarget,documentsTarget,PaddingDirection=“没有”);

按长度对序列排序。通过增加序列长度排序的序列进行训练,结果是具有大约相同序列长度的序列的批量,并确保在较长的序列批量之前使用较小的序列批量更新模型。

sequenceLengths = cellfun(@(序列)大小(序列,2),sequencesSource);[~,idx] = sort(sequenceLengths);sequencesSource = sequencesSource(idx);sequencesTarget = sequencesTarget(idx);

创建arrayDatastore对象中包含源数据和目标数据,并使用结合函数。

sequencesSourceDs = arrayDatastore(sequencesSource,OutputType=“相同”);sequencesTargetDs = arrayDatastore(sequencesTarget,OutputType=“相同”);sequencesDs = combine(sequencesSourceDs,sequencesTargetDs);

初始化模型参数

初始化模型参数。对于编码器和解码器,指定嵌入维数为128,LSTM层具有100个隐藏单元,dropout层具有随机dropout(概率为0.05)。

embeddingDimension = 128;numHiddenUnits = 100;落差= 0.05;

初始化编码器模型参数

函数初始化编码嵌入的权值initializeGaussian函数,该函数作为支持文件附加到本示例中。金宝app指定平均值为0,标准偏差为0.01。要了解更多信息,请参见高斯函数初始化

inputSize = encSource。NumWords + 1;sz = [embeddingDimension inputSize];Mu = 0;σ = 0.01;parameters.encode .emb. weights = initializ高斯(sz,mu,sigma);

初始化编码器LSTM操作的可学习参数:

  • 类的gloot初始化器初始化输入权重initializeGlorot函数,该函数作为支持文件附加到本示例中。金宝app要了解更多信息,请参见Glorot初始化

  • 类的正交初始化式初始化循环权重initializeOrthogonal函数,该函数作为支持文件附加到本示例中。金宝app要了解更多信息,请参见正交初始化

  • 的单元忘记门初始化器初始化偏置initializeUnitForgetGate函数,该函数作为支持文件附加到本示例中。金宝app要了解更多信息,请参见单元忘记门初始化

初始化编码器LSTM操作的可学习参数。

sz = [4*numHiddenUnits embeddingDimension];numOut = 4*numHiddenUnits;numIn = embeddingDimension;parameters.encode .lstm. inputwights = initializeGlorot(sz,numOut,numIn);parameters.encode .lstm. recurrentwights = initializeOrthogonal([4*numHiddenUnits numHiddenUnits]);parameters.encode .lstm. bias = initializeUnitForgetGate(numHiddenUnits);

初始化解码器模型参数

函数初始化编码嵌入的权值initializeGaussian函数。指定平均值为0,标准偏差为0.01。

outputSize = encTarget。NumWords + 1;sz = [embeddingDimension outputSize];Mu = 0;σ = 0.01;parameters.decoder.emb.Weights = initializ高斯(sz,mu,sigma);

属性,使用Glorot初始化器初始化注意力机制的权重initializeGlorot函数。

sz = [numHiddenUnits numHiddenUnits];numOut = numHiddenUnits;numIn = numHiddenUnits;parameters.decoder.attention.Weights = initializeGlorot(sz,numOut,numIn);

初始化解码器LSTM操作的可学习参数:

  • 类的gloot初始化器初始化输入权重initializeGlorot函数。

  • 类的正交初始化式初始化循环权重initializeOrthogonal函数。

  • 的单元忘记门初始化器初始化偏置initializeUnitForgetGate函数。

初始化解码器LSTM操作的可学习参数。

sz = [4*numHiddenUnits embeddingDimension+numHiddenUnits];numOut = 4*numHiddenUnits;numIn = embeddingDimension + numHiddenUnits;parameters.decoder.lstm. inputwights = initializeGlorot(sz,numOut,numIn);parameters.decode .lstm. recurrentwights = initializeOrthogonal([4*numHiddenUnits numHiddenUnits]);parameters.decode .lstm. bias = initializeUnitForgetGate(numHiddenUnits);

初始化解码器全连接操作的可学习参数:

  • 使用gloriot初始化器初始化权重。

  • 属性将偏差初始化为零initializeZeros函数,该函数作为支持文件附加到本示例中。金宝app要了解更多信息,请参见零初始化

sz = [outputSize 2*numHiddenUnits];numOut = outputSize;numIn = 2*numHiddenUnits;parameters.decoder.fc.Weights = initializeGlorot(sz,numOut,numIn);parameters.decoder.fc.Bias = initializeZeros([outputSize 1]);

定义模型函数

创建函数modelEncoder而且modelDecoder,列在示例末尾,分别计算编码器和解码器模型的输出。

modelEncoder函数中列出的编码器模型函数部分,获取输入数据、模型参数、用于确定训练正确输出的可选掩码,并返回模型输出和LSTM隐藏状态。

modelDecoder函数中列出的解码器模型函数节,取输入数据、模型参数、上下文向量、LSTM初始隐藏状态、编码器输出、退出概率,输出解码器输出、更新后的上下文向量、更新后的LSTM状态、注意得分。

定义模型损失函数

创建函数modelLoss,列于模型损失函数部分的示例,该示例获取编码器和解码器模型参数,一小批输入数据和对应于输入数据的填充掩码,以及退出概率,并返回相对于模型中可学习参数的损失和损失的梯度。

指定培训项目

在100个epoch中使用小批大小为32的训练,学习率为0.001。

miniBatchSize = 32;numEpochs = 100;learnRate = 0.001;

初始化Adam的选项。

gradientDecayFactor = 0.9;squaredGradientDecayFactor = 0.999;

火车模型

使用自定义训练循环训练模型。使用minibatchqueue在训练过程中处理和管理小批量图像。对于每个小批量:

  • 使用自定义小批量预处理功能preprocessMiniBatch(在本例结束时定义)来查找迷你批处理中所有序列的长度,并分别为源序列和目标序列将这些序列填充为与最长序列相同的长度。

  • 排列填充序列的第二和第三维度。

  • 返回未格式化的小批处理变量dlarray具有基础数据类型的对象.所有其他输出都是数据类型的数组

  • 如果有GPU,可以在GPU上进行训练。返回GPU上所有的迷你批处理变量(如果有的话)。使用GPU需要并行计算工具箱™和受支持的GPU设备。金宝app有关支持的设备信息,请参见按版本支持的金宝appGPU。

minibatchqueue对象为每个迷你批处理返回四个输出参数:源序列、目标序列、迷你批处理中所有源序列的长度以及目标序列的序列掩码。

numMiniBatchOutputs = 4;mbq = minibatchqueue(sequencesDs,numMiniBatchOutputs,...MiniBatchSize = MiniBatchSize,...MiniBatchFcn = @ (x, t) preprocessMiniBatch (x, t, inputSize outputSize));

属性的值初始化adamupdate函数。

trailingAvg = [];trailingAvgSq = [];

计算训练进度监控器的总迭代次数

numObservationsTrain = numel(sequencesSource);numIterationsPerEpoch = ceil(numObservationsTrain / miniBatchSize);numIterations = nummepochs * numIterationsPerEpoch;

初始化培训进度监视器。因为计时器在创建监视器对象时开始,所以请确保创建的对象接近训练循环。

monitor = trainingProgressMonitor(...指标=“损失”...信息=“时代”...包含=“迭代”);

训练模型。对于每个小批量:

  • 阅读一小批填充序列。

  • 计算损失和梯度。

  • 类更新编码器和解码器模型参数adamupdate函数。

  • 更新培训进度监控。

  • 停止训练时,停止训练进度监控器的属性为真正的.的停止属性更改为1当你点击停止按钮时。

Epoch = 0;迭代= 0;%遍历epoch。epoch < numEpochs && ~monitor。停止epoch = epoch + 1;重置(兆贝可);在小批上循环。Hasdata (mbq) && ~monitor。停止iteration = iteration + 1; [X,T,sequenceLengthsSource,maskSequenceTarget] = next(mbq);计算损失和梯度。[loss,gradients] = dlfeval(@modelLoss,parameters,X,T,sequenceLengthsSource,...maskSequenceTarget,辍学);使用adamupdate更新参数。[parameters,trailingAvg,trailingAvgSq] = adamupdate(parameters,gradients,trailingAvg,trailingAvgSq,...迭代,learnRate、gradientDecayFactor squaredGradientDecayFactor);按序列长度归一化损失。loss = loss ./ size(T,3);更新培训进度监视器。recordMetrics(监控、迭代损失=损失);updateInfo(监视、时代=时代+“的”+ numEpochs);班长。进度= 100*iteration/numIterations;结束结束

生成翻译

要使用训练过的模型为新数据生成翻译,请使用与训练并将序列输入编码器-解码器模型时相同的步骤将文本数据转换为数值序列,并使用令牌索引将结果序列转换回文本。

使用与训练时相同的步骤预处理文本数据。使用transformText函数将文本分割为字符并添加开始和停止令牌,该函数列在示例末尾。

strSource = dataTest.Source;strTarget = dataTest.Target;

方法翻译文本modelPredictions函数。

maxSequenceLength = 10;分隔符="";strTranslated = translateText(参数,strSource,maxSequenceLength,miniBatchSize,...encSource、encTarget startToken stopToken,分隔符);

创建一个包含测试源文本、目标文本和翻译的表。

TBL =表;资源描述。来源= strSource;资源描述。目标= strTarget;资源描述。翻译= strTranslated;

查看随机选择的翻译。

idx = randperm(size(dataTest,1),miniBatchSize);台(idx:)
ans =32×3表源目标翻译  ______ ___________ ____________ " 996”“CMXCVI”“CMMXCVI”“576年”“DLXXVI”“DCLXXVI”“86年”“LXXXVI”“DCCCLXV”“23”“二十三”“CCCCXIII”“99年”“XCIX”“CMMXIX”“478年”“CDLXXVIII”“DCCCLXXVII”“313年”“CCCXIII”“CCCXIII”“60”“LX”“DLX”“864年”“DCCCLXIV”“DCCCLIV”“280年”“CCLXXX”“CCCCLX”“792年”“DCCXCII”“DCCCIII”“959年”“CMLIX”“CMLXI”“283年”“CCLXXXIII”“CCCCLXXIII”“356年”“CCCLVI”“CCCCVI”“534年”“DXXXIV”“DCCXXIV”“721年”“DCCXXI DCCCII”⋮

文本转换函数

transformText函数通过将文本分割为字符并添加开始和停止标记,对输入文本进行预处理和标记,以进行翻译。要通过将文本分解为单词而不是字符来翻译文本,请跳过第一步。

函数documents = transformText(str,startToken,stopToken) str = strip(replace(str, stopToken)""”“));str = startToken + str + stopToken;documents = tokenizedDocument(str,CustomTokens=[startToken stopToken]);结束

小批量预处理功能

preprocessMiniBatch函数,在示例的训练模型部分描述,预处理数据进行训练。该函数使用以下步骤对数据进行预处理:

  1. 确定小批中所有源和目标序列的长度

  2. 属性将序列填充到与迷你批处理中最长序列相同的长度padsequences函数。

  3. 排列序列的最后两个维度

函数[X,T,sequenceLengthsSource,maskTarget] = preprocessMiniBatch(sequencesSource,sequencesTarget,inputSize,outputSize) sequenceLengthsSource = cellfun(@(X) size(X, 2),sequencesSource);X = padsequences(sequencesSource,2,PaddingValue=inputSize);X = permute(X,[1 3 2]);[T,maskTarget] = padsequences(sequencesTarget,2,PaddingValue=outputSize);T = permute(T,[1 3 2]);maskTarget = permute(maskTarget,[1 3 2]);结束

模型损失函数

modelLoss函数获取编码器和解码器模型参数、一小批输入数据和与输入数据对应的填充掩码,以及退出概率,并返回损失和损失相对于模型中可学习参数的梯度。

函数[loss,gradients] = modelLoss(参数,X,T,...sequenceLengthsSource maskTarget,辍学)%通过编码器转发。[Z,hiddenState] = modelEncoder(parameters.encoder,X,sequenceLengthsSource);%解码器输出。doTeacherForcing = rand < 0.5;sequenceLength = size(T,3);Y = decoderPredictions(parameters.decoder,Z,T,hiddenState,dropout,...doTeacherForcing sequenceLength);%掩码损失。Y = Y(:,:,1:end-1);T = extractdata(gather(T(:,:,2:end)));T = onehotencode(T,1,ClassNames=1:size(Y,1));maskTarget = maskTarget(:,:,2:end);maskTarget = repmat(maskTarget,[size(Y,1),1,1]);loss = crossentropy(Y,T,Mask=maskTarget,Dataformat=“认知行为治疗”);%更新渐变。Gradients = dlgradient(损失,参数);结束

编码器模型函数

这个函数modelEncoder获取输入数据,模型参数,用于确定训练的正确输出的可选掩码,并返回模型输出和LSTM隐藏状态。

如果sequenceLengths为空,则函数不掩码输出。指定的空值sequenceLengths当使用modelEncoder预测功能。

函数[Z,hiddenState] = modelEncoder(parameters,X,sequenceLengths)%嵌入。weights = parameters.emb.Weights;Z = embed(X,weights,DataFormat=“认知行为治疗”);% LSTM。inputwights = parameters.lstm. inputwights;recurrentwights = parameters.lstm. recurrentwights;bias = parameters.lstm.Bias;numHiddenUnits = size(recurrentwights, 2);initialHiddenState = dlarray(0 ([numHiddenUnits 1]));initialCellState = dlarray(0 ([numHiddenUnits 1]));[Z,hiddenState] = lstm(Z,initialHiddenState,initialCellState, inputwights,...recurrentWeights,偏见,DataFormat =“认知行为治疗”);%掩蔽训练。如果~isempty(sequencelths) miniBatchSize = size(Z,2);n = 1:miniBatchSize hiddenState(:,n) = Z(:,n,sequenceLengths(n));结束结束结束

解码器模型函数

这个函数modelDecoder取输入数据、模型参数、上下文向量、LSTM初始隐藏状态、编码器输出、退出概率,输出解码器输出、更新后的上下文向量、更新后的LSTM状态、注意得分。

函数[Y,context,hiddenState,attentionScores] = modelDecoder(参数,X,context,...hiddenState, Z,辍学)%嵌入。weights = parameters.emb.Weights;X = embed(X,weights,DataFormat=“认知行为治疗”);RNN输入。sequenceLength = size(X,3);Y = cat(1, X, repmat(context,[1 1 sequenclength]));% LSTM。inputwights = parameters.lstm. inputwights;recurrentwights = parameters.lstm. recurrentwights;bias = parameters.lstm.Bias;initialCellState = dlarray(零(大小(hiddenState)));[Y,hiddenState] = lstm(Y,hiddenState,initialCellState,...inputWeights recurrentWeights,偏见,DataFormat =“认知行为治疗”);%的辍学生。mask = rand(size(Y),“喜欢”,Y) >辍学;Y = Y *掩模;%的注意。weights = parameters.attention.Weights;[context,attentionScores] = luongAttention(hiddenState,Z,weights);%连接。Y = cat(1, Y, repmat(context,[1 1 sequenclength]));%完全连接。weights = parameters.fc.Weights;偏差=参数。fc.偏差;Y = fulllyconnect (Y,权重,偏差,DataFormat=“认知行为治疗”);% Softmax。Y = softmax(Y,DataFormat=“认知行为治疗”);结束

梁注意函数

luongAttention函数根据Luong“general”评分[1]返回上下文向量和注意力分数。这相当于用查询、键和值分别指定为隐藏状态、加权潜在表示和潜在表示的点积注意力。

函数[context,attentionScores] = luongAttention(hiddenState,Z,weights) numHeads = 1;queries = hiddenState;键= pagemtimes(weights,Z);values = Z;[context,attentionScores] = attention(查询,键,值,numHeads,...规模= 1,...DataFormat =“认知行为治疗”);结束

解码器模型预测函数

decoderModelPredictions函数返回预测序列Y给定输入序列,目标序列,隐藏状态,退出概率,允许教师强制的标志,以及序列长度。

函数Y = decoderprediction(参数,Z,T,hiddenState,dropout,...doTeacherForcing sequenceLength)%转换为darray。T = dlarray(T);初始化上下文。miniBatchSize = size(T,2);numHiddenUnits = size(Z,1);context = 0 ([numHiddenUnits miniBatchSize],“喜欢”, Z);如果doTeacherForcing%通过解码器转发。Y = modelDecoder(参数,T,上下文,hiddenState,Z,dropout);其他的获得解码器的第一次步骤。decoderInput = T(:,:,1);初始化输出。numClasses = numel(parameters.fc.Bias);Y = 0 ([numClasses miniBatchSize sequenceLength],“喜欢”, decoderInput);%遍历时间步骤。t = 1:sequenceLength%通过解码器转发。[Y(:,:,t), context, hiddenState] = modelDecoder(parameters,decoderInput,context,...hiddenState, Z,辍学);更新解码器输入。[~, decoderInput] = max(Y(:,:,t),[],1);结束结束结束

文本翻译功能

translateText函数通过在小批量上迭代转换文本数组。该函数将模型参数、输入字符串数组、最大序列长度、迷你批大小、源和目标字编码对象、开始和停止令牌以及用于组装输出的分隔符作为输入。

函数strTranslated = translateText(参数,strSource,maxSequenceLength,miniBatchSize,...encSource、encTarget startToken stopToken,分隔符)转换文本。documentsSource = transformText(strSource,startToken,stopToken);sequencesSource = doc2sequence(encSource,documentsSource, doc2sequence)...PaddingDirection =“正确”...PaddingValue = encSource。NumWords + 1);%转换为darray。X = cat(3,sequencesSource{:});X = permute(X,[1 3 2]);X = dlarray(X);初始化输出。numObservations = numel(strSource);strTranslated = strings(numObservations,1);在小批上循环。numIterations = ceil(numObservations / miniBatchSize);i = 1:numIterations idxMiniBatch = (i-1)*miniBatchSize+1:min(i*miniBatchSize,numObservations);miniBatchSize = numel(idxMiniBatch);%使用模型编码器编码。sequenceLengths = [];[Z, hiddenState] = modelEncoder(parameters.encoder,X(:,idxMiniBatch,:),sequenceLengths);解码器预测。doTeacherForcing = false;Dropout = 0;decoderInput = repmat(word2ind(encTarget,startToken),[1 miniBatchSize]);decoderInput = darray (decoderInput);Y = decoderprediction (parameters.decoder,Z,decoderInput,hiddenState,dropout,...doTeacherForcing maxSequenceLength);[~, idxPred] = max(extractdata(Y),[],1);继续翻译旗帜。idxStop = word2ind(encTarget,stopToken);keepTranslating = idxPred ~= idxStop;%遍历时间步骤。T = 1;t <= maxSequenceLength && any(keeptranslations (:,:,t))%更新输出。newWords = ind2word(encTarget, idxPred(:,:,t))';idxUpdate = idxMiniBatch(keepTranslating(:,:,t));strTranslated(idxUpdate) = strTranslated(idxUpdate) +分隔符+ newWords(keeptranslations (:,:,t));T = T + 1;结束结束结束

参考书目

[1]梁、明升、范晓和克里斯托弗·曼宁。“基于注意力的神经机器翻译的有效方法。”arXiv预印arXiv:1508.04025(2015)。

另请参阅

(文本分析工具箱)|(文本分析工具箱)|(文本分析工具箱)|||||||||(文本分析工具箱)

相关的话题