主要内容

使用自动编码器生成文本

这个例子展示了如何使用自动编码器生成文本数据。

自动编码器是一种深度学习网络,经过训练可以复制其输入。自动编码器由两个较小的网络组成:编码器和解码器。编码器将输入数据映射到某个潜在空间中的特征向量。解码器利用这个潜在空间中的向量重建数据。

培训过程没有监督。换句话说,该模型不需要标记数据。要生成文本,可以使用解码器从任意输入重新构造文本。

这个例子训练一个自动编码器来生成文本。编码器使用单词嵌入和LSTM操作将输入文本映射为潜在向量。解码器使用LSTM操作和相同的嵌入从潜在向量重建文本。

加载数据

该文件sonnets.txt在一个文本文件中包含了莎士比亚所有的十四行诗。

阅读文件中的莎士比亚十四行诗数据“sonnets.txt”

文件名=“sonnets.txt”;textData = fileread(文件名);

十四行诗由两个空格缩进。删除缩进使用取代控件将文本拆分为单独的行分裂函数。删除头九个元素和短十四行诗标题。

textData = replace(textData,”“"");textData = split(textData,换行符);textData(1:9) = [];textData(strlength(textData)<5) = [];

准备数据

创建一个对文本数据进行标记和预处理的函数。这个函数preprocessText,在示例末尾列出,执行以下步骤:

  1. 分别使用指定的开始和停止令牌对每个输入字符串进行前置和追加。

  2. 使用标记化文本tokenizedDocument

预处理文本数据并指定开始和停止令牌“<开始>”而且“<停止>”,分别。

startToken =“<开始>”;stopToken =“<停止>”;documents = preprocessText(textData,startToken,stopToken);

从标记化的文档创建单词编码对象。

enc = worddencoding(文档);

在训练深度学习模型时,输入数据必须是包含固定长度序列的数字数组。因为文档有不同的长度,所以必须用填充值填充较短的序列。

重新创建单词编码以包括填充标记并确定该标记的索引。

paddingToken =“<垫>”;newVocabulary = [c. vocabulary paddingToken];enc = worddencoding (newVocabulary);paddingIdx = word2ind(enc,paddingToken)
paddingIdx = 3595

初始化模型参数

初始化以下模型的参数。

在这里, T 是序列长度, x 1 x T 输入序列是单词索引,和 y 1 y T 是重构序列。

编码器通过将输入转换为使用嵌入的词向量序列,将词向量序列输入到LSTM操作中,并对LSTM输出的最后一个时间步应用全连接操作,从而将词索引序列映射到潜在向量。解码器使用对编码器输出初始化的LSTM重构输入。对于每个时间步,解码器预测下一个时间步,并使用输出进行下一个时间步预测。编码器和解码器都使用相同的嵌入。

指定参数的尺寸。

embeddingDimension = 100;numHiddenUnits = 150;latentDimension = 75;vocabularySize = c. numwords;

为参数创建一个结构。

参数= struct;

函数初始化嵌入的权重,使用高斯函数initializeGaussian函数,该函数作为支持文件附加到本示例中。万博1manbetx指定平均值为0,标准偏差为0.01。要了解更多信息,请参见高斯函数初始化(深度学习工具箱)

Mu = 0;σ = 0.01;parameters.emb.Weights = initializ高斯([embeddingDimension vocabularySize],mu,sigma);

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

  • 类的gloot初始化器初始化输入权重initializeGlorot函数,该函数作为支持文件附加到本示例中。万博1manbetx要了解更多信息,请参见Glorot初始化(深度学习工具箱)

  • 类的正交初始化式初始化循环权重initializeOrthogonal函数,该函数作为支持文件附加到本示例中。万博1manbetx要了解更多信息,请参见正交初始化(深度学习工具箱)

  • 的单元忘记门初始化器初始化偏置initializeUnitForgetGate函数,该函数作为支持文件附加到本示例中。万博1manbetx要了解更多信息,请参见单元忘记门初始化(深度学习工具箱)

sz = [4*numHiddenUnits embeddingDimension];numOut = 4*numHiddenUnits;numIn = embeddingDimension;parameters. lstmencode . inputwights = initializeGlorot(sz,numOut,numIn);parameters. lstmencode . recurrentwights = initializeOrthogonal([4*numHiddenUnits numHiddenUnits]);parameters. lstmencode . bias = initializeunit健忘门(numHiddenUnits);

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

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

  • 属性将偏差初始化为零initializeZeros函数,该函数作为支持文件附加到本示例中。万博1manbetx要了解更多信息,请参见零初始化(深度学习工具箱)

sz = [latentDimension numHiddenUnits];numOut = latentDimension;numIn = numHiddenUnits;parameters. fcencode . weights = initializeGlorot(sz,numOut,numIn);parameters. fcencode . bias = initializeZeros([latentDimension 1]);

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

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

  • 用正交初始化式初始化循环权值。

  • 使用单元忘记门初始化器初始化偏置。

sz = [4*latentDimension embeddingDimension];numOut = 4*latentDimension;numIn = embeddingDimension;parameters. lstmdecoder . inputwights = initializeGlorot(sz,numOut,numIn);parameters. lstmdecode . recurrentwights = initializeOrthogonal([4*latentDimension latentDimension]);parameters.lstmDecoder.Bias = initializeZeros([4*latentDimension 1]);

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

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

  • 用0初始化偏差。

sz = [vocabularySize latentDimension];numOut = vocabularySize;numIn = latentDimension;parameters.fcDecoder.Weights = initializeGlorot(sz,numOut,numIn);parameters. fcdecode . bias = initializeZeros([vocabularySize 1]);

要了解关于权重初始化的更多信息,请参见初始化模型函数的可学习参数(深度学习工具箱)

定义模型编码器函数

创建函数modelEncoder,列于编码器模型函数部分,该部分计算编码器模型的输出。的modelEncoder函数,将单词索引、模型参数和序列长度作为输入序列,返回相应的潜在特征向量。要了解有关定义模型编码器函数的详细信息,请参见定义文本编码器模型函数(深度学习工具箱)

定义模型解码器函数

创建函数modelDecoder,列于解码器模型函数部分的示例,其中计算解码器模型的输出。的modelDecoder函数,将单词索引、模型参数和序列长度作为输入序列,返回相应的潜在特征向量。要了解有关定义模型解码器函数的详细信息,请参见定义文本解码器模型函数(深度学习工具箱)

定义模型梯度函数

modelGradients函数中列出的模型梯度函数实例部分,以输入模型的可学习参数,输入数据dlX,以及用于掩蔽的序列长度向量,并返回损失相对于可学习参数和相应损失的梯度。要了解关于定义模型梯度函数的更多信息,请参见定义自定义训练循环的模型梯度函数(深度学习工具箱)

指定培训项目

指定培训选项。

训练100个epoch,迷你批量大小为128。

miniBatchSize = 128;numEpochs = 100;

以0.01的学习率进行训练。

learnRate = 0.01;

以图表形式展示训练进度。

情节=“训练进步”

如果有GPU,可以在GPU上进行训练。使用GPU需要并行计算工具箱™和受支持的GPU设备。万博1manbetx有关受支持设备的信息,请参见万博1manbetxGPU支万博1manbetx持版本(并行计算工具箱)

executionEnvironment =“汽车”

列车网络的

使用自定义训练循环训练网络。

初始化Adam优化器的参数。

trailingAvg = [];trailingAvgSq = [];

初始化培训进度图。创建一条动画线,在相应的迭代中绘制损失。

如果情节= =“训练进步”图lineLossTrain = animatedline(“颜色”,[0.85 0.325 0.098]);包含(“迭代”) ylabel (“损失”) ylim([0 inf])网格结束

训练模型。对于第一个纪元,对数据进行洗牌并在小批量数据上进行循环。

对于每个小批量:

  • 将文本数据转换为单词索引序列。

  • 将数据转换为dlarray

  • 对于GPU训练,将数据转换为gpuArray对象。

  • 计算损失和梯度。

  • 方法更新可学习参数adamupdate函数。

  • 更新培训进度图。

跑步训练需要一些时间。

numObservations = numel(文档);numIterationsPerEpoch = floor(numObservations / miniBatchSize);迭代= 0;开始= tic;epoch = 1:numEpochs%洗牌。idx = randperm(numObservations);Documents = Documents (idx);i = 1:numIterationsPerEpoch迭代=迭代+ 1;读取小批。idx = (i-1)*miniBatchSize+1:i*miniBatchSize;documentsBatch = documents(idx);转换为序列。X = doc2sequence(enc,documentsBatch,...“PaddingDirection”“对”...“PaddingValue”, paddingIdx);X = cat(1,X{:});%转换为darray。dlX = dlarray(X,“BTC”);如果在GPU上训练,则将数据转换为gpuArray。如果(executionEnvironment = =“汽车”&& canUseGPU) || executionEnvironment ==“图形”dlX = gpuArray(dlX);结束计算序列长度。sequenceLengths = doclength(documentsBatch);评估模型梯度。[gradients,loss] = dlfeval(@modelGradients, parameters, dlX, sequenceLengths);更新可学习参数。[parameters,trailingAvg,trailingAvgSq] = adamupdate(参数,梯度,...trailingAvg trailingAvgSq,迭代,learnRate);%显示培训进度。如果情节= =“训练进步”D = duration(0,0,toc(start),“格式”“hh: mm: ss”);addpoints (lineLossTrain、迭代、双(收集(extractdata(损失))))标题(”时代:“+ epoch +,消失:"+字符串(D))现在绘制结束结束结束

生成文本

通过使用不同的随机状态初始化解码器,使用闭环生成生成文本。闭环生成是指模型每次生成一个时间步数据,并使用前一个预测作为下一个预测的输入。

指定生成3个长度为16的序列。

numGenerations = 3;sequenceLength = 16;

创建一个随机值数组来初始化解码器状态。

dlZ = dlarray(randn(latentDimension,numGenerations),“CB”);

如果在GPU上进行预测,则将数据转换为gpuArray

如果(executionEnvironment = =“汽车”&& canUseGPU) || executionEnvironment ==“图形”dlZ = gpuArray(dlZ);结束

使用modelPredictions函数,在示例末尾列出。的modelPredictions函数返回给定模型参数、解码器初始状态、最大序列长度、字编码、开始令牌和迷你批处理大小的解码器的输出分数。

dlY = modelDecoderPredictions(parameters,dlZ,sequenceLength,enc,startToken,miniBatchSize);

找出得分最高的单词索引。

[~,idx] = max(dlY,[],1);Idx =挤压(Idx);

将数值索引转换为单词并使用加入函数。

strGenerated = join(c. vocabulary (idx));

方法提取第一个停止标记之前的文本extractBefore函数。为了防止函数在没有停止令牌时返回缺失,可以在每个序列的末尾附加一个停止令牌。

strGenerated = extractBefore(strGenerated+stopToken,stopToken);

删除填充标记。

strGenerated = erase(strGenerated,paddingToken);

生成过程在每个预测之间引入空白字符,这意味着一些标点字符在前后出现不必要的空格。通过删除适当的标点符号字符前后的空格,重新构造生成的文本。

删除出现在指定标点符号之前的空格。

标点符号= [“。””、““”“)””:“”;““?”“啊!”];strGenerated =替换(strGenerated,”“+ punctuationCharacters punctuationCharacters);

删除出现在指定标点符号后的空格。

标点符号= [”(““”];strGenerated = replace(strGenerated,标点符号+”“, punctuationCharacters);

控件移除前导和尾随空白函数并查看生成的文本。

strGenerated = strip(strGenerated)
strGenerated =3×1的字符串"爱的你休息得很轻,最好的错误显示出被我看得够远" "就像他弯着镰刀的罗盘来找。"“夏天的了?真理一旦被引导,就会被带走。”

编码器模型函数

modelEncoder函数,将模型参数、单词索引序列和序列长度作为输入,返回相应的潜在特征向量。

由于输入数据包含不同长度的填充序列,因此填充会对损失计算产生不利影响。对于LSTM操作,不是返回序列的最后一个时间步骤的输出(这可能对应于处理大量填充值后的LSTM状态),而是确定函数给出的实际最后一个时间步骤sequenceLengths输入。

函数dlZ = modelEncoder(参数,dlX,sequenceLengths)%嵌入。weights = parameters.emb.Weights;dlZ =嵌入(dlX,权重);% LSTM。inputwights = parameters. lstmencode . inputwights;recurrentwights = parameters. lstmencode . recurrentwights;bias = parameters. lstmencode . bias;numHiddenUnits = size(recurrentwights,2);hiddenState = 0 (numHiddenUnits,1,“喜欢”dlX);cellState = 0 (numHiddenUnits,1,“喜欢”dlX);dlZ1 = lstm(dlZ,hiddenState,cellState, inputwights, recurrentwights,bias,“DataFormat”“认知行为治疗”);输出模式为'last',带屏蔽。miniBatchSize = size(dlZ1,2);dlZ = 0 (numHiddenUnits,miniBatchSize,“喜欢”dlZ1);n = 1:miniBatchSize t = sequenceLengths(n);dlZ(,n) = dlZ1(:,n,t);结束%完全连接。weights = parameters. fcencode . weights;bias = parameters. fcencode . bias;dlZ = fulllyconnect (dlZ,权重,偏差,“DataFormat”“CB”);结束

解码器模型函数

modelDecoder函数,将模型参数、词索引序列和网络状态作为输入,并返回解码后的序列。

因为lstm函数是有状态(当给定一个时间序列作为输入时,该函数在每个时间步骤之间传播和更新状态)嵌入而且fullyconnect函数在默认情况下是时间分布的(当给定一个时间序列作为输入时,函数独立地在每个时间步上操作)modelDecoder函数支持序列和单时万博1manbetx间步输入。

函数[dlY,state] = modelDecoder(parameters,dlX,state)%嵌入。weights = parameters.emb.Weights;dlX =嵌入(dlX,权重);% LSTM。inputwights = parameters. lstmdecoder . inputwights;recurrentwights = parameters. lstmdecoder . recurrentwights;bias = parameters.lstmDecoder.Bias;hiddenState = state.HiddenState;cellState = state.CellState;[dlY,hiddenState,cellState] = lstm(dlX,hiddenState,cellState,...inputWeights recurrentWeights,偏见,“DataFormat”“认知行为治疗”);状态。HiddenState = HiddenState;状态。CellState = CellState;%完全连接。weights = parameters.fcDecoder.Weights;bias = parameters.fcDecoder.Bias;ly =完全连接(ly,权重,偏差,“DataFormat”“认知行为治疗”);% Softmax。d = softmax(d,“DataFormat”“认知行为治疗”);结束

模型梯度函数

modelGradients将模型可学习参数作为输入的函数,即输入数据dlX,以及用于掩蔽的序列长度向量,并返回损失相对于可学习参数和相应损失的梯度。

为了计算屏蔽损失,模型梯度函数使用maskedCrossEntropy损失函数,列在示例的末尾。为了训练解码器预测序列的下一个时间步长,将目标指定为平移一个时间步长的输入序列。

要了解关于定义模型梯度函数的更多信息,请参见定义自定义训练循环的模型梯度函数(深度学习工具箱)

函数[gradients, loss] = modelGradients(parameters,dlX,sequenceLengths)型号编码器。dlZ = modelEncoder(参数,dlX,sequenceLengths);初始化LSTM状态。State = struct;状态。HiddenState = dlZ;状态。CellState = 0 (size(dlZ),“喜欢”, dlZ);%老师强迫。dlY = modelDecoder(参数,dlX,状态);%的损失。dlYPred = dlY(:,:,1:end-1);dlT = dlX(:,:,2:end);loss = mean(maskedCrossEntropy(dlYPred,dlT, sequencelths));%梯度。Gradients = dlgradient(损失,参数);%用于绘图的归一化损失。sequenceLength = size(dlX,3);loss = loss / sequenclength;结束

模型预测函数

modelPredictions函数返回给定模型参数、解码器初始状态、最大序列长度、字编码、开始令牌和迷你批处理大小的解码器的输出分数。

函数dlY = modelDecoderPredictions(parameters,dlZ,maxLength,enc,startToken,miniBatchSize) numObservations = size(dlZ,2);numIterations = ceil(numObservations / miniBatchSize);startTokenIdx = word2ind(enc,startToken);vocabularySize = c. numwords;dlY = 0 (vocabularySize,numObservations,maxLength,“喜欢”, dlZ);在小批上循环。i = 1:numIterations idxMiniBatch = (i-1)*miniBatchSize+1:min(i*miniBatchSize,numObservations);miniBatchSize = numel(idxMiniBatch);初始化状态。State = struct;状态。HiddenState = dlZ(:,idxMiniBatch);状态。CellState = 0 (size(dlZ(:,idxMiniBatch)),“喜欢”, dlZ);初始化解码器输入。decoderInput = dlarray(repmat(startTokenIdx,[1 miniBatchSize]),“认知行为治疗”);%遍历时间步骤。t = 1:maxLength预测下一个时间步骤。[dlY(:,idxMiniBatch,t), state] = modelDecoder(parameters,decoderInput,state);%闭环生成。[~,idx] = max(dlY(:,idxMiniBatch,t));decoderInput = idx;结束结束结束

屏蔽交叉熵损失函数

maskedCrossEntropy函数计算指定输入序列和目标序列之间的损失,忽略使用指定序列长度向量包含填充的任何时间步长。

函数maskedLoss = maskedCrossEntropy(dlY,T, sequencelths) numClasses = size(dlY,1);miniBatchSize = size(dlY,2);sequenclength = size(dlY,3);maskedLoss = 0 (sequenclength,miniBatchSize,“喜欢”、海底);t = 1:sequenceLength T1 = single(oneHot(t(:,:,t),numClasses));mask = (t<=sequenceLengths)';maskedLoss (t):) =面具。* crossentropy(海底(:,:,t), T1,“DataFormat”“认知行为治疗”);结束maskedLoss = sum(maskedLoss,1);结束

文本预处理功能

这个函数preprocessText执行以下步骤:

  1. 分别使用指定的开始和停止令牌对每个输入字符串进行前置和追加。

  2. 使用标记化文本tokenizedDocument

函数documents = preprocessText(textData,startToken,stopToken)添加开始和停止令牌。textData = startToken + textData + stopToken;标记文本。文档= tokenizedDocument(文本数据,“CustomTokens”, (startToken stopToken]);结束

嵌入函数

嵌入函数使用给定的权重将索引序列映射到向量。

函数Z =嵌入(X,权重)将输入重塑为向量。[N, T] = size(X, 2:3);X =重塑(X, N*T, 1);嵌入矩阵的索引。Z =权重(:,X);通过分离批次和序列尺寸来重塑输出。Z =重塑(Z, [], N, T);结束

单热编码函数

oneHot函数将数值索引数组转换为单热编码向量。

函数oh = oneHot(idx, outputSize) miniBatchSize = nummel (idx);oh = 0 (outputSize,miniBatchSize);:miniBatchSize c = idx(n);Oh (c,n) = 1;结束结束

另请参阅

|||

相关的话题