В этом примере показано, как преобразовать десятичные строки в Римские цифры с помощью модели декодера энкодера повторяющейся последовательности к последовательности с вниманием.
Текущие модели декодера энкодера оказались успешными в задачах как абстрактное текстовое резюмирование и нейронный машинный перевод. Модели, сопоставимые из энкодера, который обычно входные данные процессов с текущим слоем, такие как LSTM и декодер, который сопоставляет закодированный вход в желаемый выход, обычно со вторым текущим слоем. Модели, которые включают механизмы внимания в модели, позволяют декодеру фокусироваться на частях закодированного входа при генерации перевода.

Для модели энкодера этот пример использует простую сеть, состоящую из встраивания, сопровождаемого двумя операциями LSTM. Встраивание является методом преобразования категориальных лексем в числовые векторы.

Для модели декодера этот пример использует сеть, очень похожую на энкодер, который содержит два LSTMs. Однако важное различие - то, что декодер содержит механизм внимания. Механизм внимания позволяет декодеру проявлять внимание к определенным частям энкодера выход.

Загрузите пары десятичной Римской цифры с "romanNumerals.csv"
filename = fullfile("romanNumerals.csv"); options = detectImportOptions(filename, ... 'TextType','string', ... 'ReadVariableNames',false); options.VariableNames = ["Source" "Target"]; options.VariableTypes = ["string" "string"]; data = readtable(filename,options);
Разделите данные в обучение и протестируйте разделы, содержащие 50% данных каждый.
idx = randperm(size(data,1),500); dataTrain = data(idx,:); dataTest = data; dataTest(idx,:) = [];
Просмотрите некоторые пары десятичной римской цифры.
head(dataTrain)
ans=8×2 table
Source Target
______ _________
"168" "CLXVIII"
"154" "CLIV"
"765" "DCCLXV"
"714" "DCCXIV"
"649" "DCXLIX"
"346" "CCCXLVI"
"77" "LXXVII"
"83" "LXXXIII"
Предварительно обработайте обучающие данные с помощью preprocessSourceTargetPairs функция, перечисленная в конце примера. preprocessSourceTargetPairs функция преобразует входные текстовые данные в числовые последовательности. Элементами последовательностей являются положительные целые числа, которые индексируют в соответствующий wordEncoding объект. wordEncoding лексемы карт в числовой индекс и наоборот использование словаря. Чтобы подсветить начало и концы последовательностей, кодирование также инкапсулирует специальные лексемы "<start>" и "<stop>".
startToken = "<start>"; stopToken = "<stop>"; [sequencesSource, sequencesTarget, encSource, encTarget] = preprocessSourceTargetPairs(dataTrain,startToken,stopToken);
Например, десятичная строка "441" закодирован можно следующим образом:
strSource = "441";Вставьте пробелы между символами.
strSource = strip(replace(strSource,""," "));
Добавьте специальный запуск и лексемы остановки.
strSource = startToken + strSource + stopToken
strSource = "<start>4 4 1<stop>"
Маркируйте текст с помощью tokenizedDocument функция и набор 'CustomTokens' опция к специальным лексемам.
documentSource = tokenizedDocument(strSource,'CustomTokens',[startToken stopToken])documentSource = tokenizedDocument: 5 tokens: <start> 4 4 1 <stop>
Преобразуйте документ последовательности маркерных индексов с помощью word2ind функция с соответствующим wordEncoding объект.
tokens = string(documentSource); sequenceSource = word2ind(encSource,tokens)
sequenceSource = 1×5
1 7 7 2 5
Данные о последовательности, такие как текст естественно имеют различные длины последовательности. Чтобы обучить модель с помощью последовательностей переменной длины, заполните мини-пакеты входных данных, чтобы иметь ту же длину. Чтобы гарантировать, что дополнительные значения не влияют на вычисления потерь, создайте маску, которая записывает, какие элементы последовательности действительны, и которые только дополняют.
Например, полагайте, что мини-пакет, содержащий десятичное число, представляет в виде строки "437", "431", и "102" с соответствующей Римской цифрой представляет в виде строки "CDXXXVII", "CDXXXI" и "CII". Для познаковых последовательностей входные последовательности имеют ту же длину и не должны быть дополнены. Соответствующая маска является массивом из единиц.

Выходные последовательности имеют различные длины, таким образом, они требуют дополнения. Соответствующая дополнительная маска содержит нули, где соответствующие временные шаги дополняют значения.

Инициализируйте параметры модели. и для энкодера и для декодера, задайте размерность встраивания 256, два слоя LSTM с 200 скрытыми модулями и слои уволенного со случайным уволенным с вероятностью 0.05.
embeddingDimension = 256; numHiddenUnits = 200; dropout = 0.05;
Инициализируйте параметры модели энкодера:
Задайте размерность встраивания 256 и размер словаря исходного словаря плюс 1, где дополнительное значение соответствует дополнительной лексеме.
Задайте две операции LSTM с 200 скрытыми модулями.
Инициализируйте веса встраивания путем выборки от случайного нормального распределения.
Инициализируйте веса LSTM и смещения путем выборки от равномерного распределения с помощью uniformNoise функция, перечисленная в конце примера.
inputSize = encSource.NumWords + 1; parametersEncoder.emb.Weights = dlarray(randn([embeddingDimension inputSize])); parametersEncoder.lstm1.InputWeights = dlarray(uniformNoise([4*numHiddenUnits embeddingDimension],1/numHiddenUnits)); parametersEncoder.lstm1.RecurrentWeights = dlarray(uniformNoise([4*numHiddenUnits numHiddenUnits],1/numHiddenUnits)); parametersEncoder.lstm1.Bias = dlarray(uniformNoise([4*numHiddenUnits 1],1/numHiddenUnits)); parametersEncoder.lstm2.InputWeights = dlarray(uniformNoise([4*numHiddenUnits numHiddenUnits],1/numHiddenUnits)); parametersEncoder.lstm2.RecurrentWeights = dlarray(uniformNoise([4*numHiddenUnits numHiddenUnits],1/numHiddenUnits)); parametersEncoder.lstm2.Bias = dlarray(uniformNoise([4*numHiddenUnits 1],1/numHiddenUnits));
Инициализируйте параметры модели декодера.
Задайте размерность встраивания 256 и размер словаря целевого словаря плюс 1, где дополнительное значение соответствует дополнительной лексеме.
Инициализируйте веса механизма внимания с помощью uniformNoise функция.
Инициализируйте веса встраивания путем выборки от случайного нормального распределения.
Инициализируйте веса LSTM и смещения путем выборки от равномерного распределения с помощью uniformNoise функция.
outputSize = encTarget.NumWords + 1; parametersDecoder.emb.Weights = dlarray(randn([embeddingDimension outputSize])); parametersDecoder.attn.Weights = dlarray(uniformNoise([numHiddenUnits numHiddenUnits],1/numHiddenUnits)); parametersDecoder.lstm1.InputWeights = dlarray(uniformNoise([4*numHiddenUnits embeddingDimension+numHiddenUnits],1/numHiddenUnits)); parametersDecoder.lstm1.RecurrentWeights = dlarray(uniformNoise([4*numHiddenUnits numHiddenUnits],1/numHiddenUnits)); parametersDecoder.lstm1.Bias = dlarray( uniformNoise([4*numHiddenUnits 1],1/numHiddenUnits)); parametersDecoder.lstm2.InputWeights = dlarray(uniformNoise([4*numHiddenUnits numHiddenUnits],1/numHiddenUnits)); parametersDecoder.lstm2.RecurrentWeights = dlarray(uniformNoise([4*numHiddenUnits numHiddenUnits],1/numHiddenUnits)); parametersDecoder.lstm2.Bias = dlarray(uniformNoise([4*numHiddenUnits 1], 1/numHiddenUnits)); parametersDecoder.fc.Weights = dlarray(uniformNoise([outputSize 2*numHiddenUnits],1/(2*numHiddenUnits))); parametersDecoder.fc.Bias = dlarray(uniformNoise([outputSize 1], 1/(2*numHiddenUnits)));
Создайте функции modelEncoder и modelDecoder, перечисленный в конце примера, которые вычисляют выходные параметры моделей энкодера и декодера, соответственно.
modelEncoder функция, перечисленная в разделе Encoder Model Function примера, берет входные данные, параметры модели, дополнительная маска, которая используется, чтобы определить правильные выходные параметры для обучения и возвращает выходные параметры модели и LSTM скрытое состояние.
modelDecoder функция, перечисленная в разделе Decoder Model Function примера, берет входные данные, параметры модели, вектор контекста, начальная буква LSTM скрытое состояние, выходные параметры энкодера и вероятность уволенного и выводит декодер выход, обновленный вектор контекста, обновленное состояние LSTM и баллы внимания.
Создайте функциональный modelGradients, перечисленный в разделе Model Gradients Function примера, который берет параметры модели энкодера и декодера, мини-пакет входных данных и дополнительных масок, соответствующих входным данным и вероятности уволенного, и возвращает градиенты потери относительно настраиваемых параметров в моделях и соответствующей потери.
Обучайтесь с мини-пакетным размером 32 в течение 40 эпох. Задайте скорость обучения 0,002 и отсеките градиенты с порогом 5.
miniBatchSize = 32; numEpochs = 40; learnRate = 0.002; gradientThreshold = 5;
Инициализируйте опции от Адама.
gradientDecayFactor = 0.9; squaredGradientDecayFactor = 0.999;
Задайте, чтобы построить процесс обучения. Чтобы отключить график процесса обучения, установите plots значение к "none".
plots = "training-progress";Обучите модель с помощью пользовательского учебного цикла.
В течение первой эпохи обучайтесь с последовательностями, отсортированными путем увеличения длины последовательности. Это приводит к пакетам с последовательностями приблизительно той же длины последовательности и гарантирует, что меньшие пакеты последовательности используются, чтобы обновить модель перед более длинными пакетами последовательности. В течение последующих эпох переставьте данные.
Для каждого мини-пакета:
Преобразуйте данные в dlarray.
Вычислите потерю и градиенты.
Отсеките градиенты.
Обновите параметры модели энкодера и декодера с помощью adamupdate функция.
Обновите график процесса обучения.
Сортировка последовательностей в течение первой эпохи.
sequenceLengthsEncoder = cellfun(@(sequence) size(sequence,2), sequencesSource); [~,idx] = sort(sequenceLengthsEncoder); sequencesSource = sequencesSource(idx); sequencesTarget = sequencesTarget(idx);
Инициализируйте график процесса обучения.
if plots == "training-progress" figure lineLossTrain = animatedline('Color',[0.85 0.325 0.098]); ylim([0 inf]) xlabel("Iteration") ylabel("Loss") grid on end
Инициализируйте значения для adamupdate функция.
trailingAvgEncoder = []; trailingAvgSqEncoder = []; trailingAvgDecoder = []; trailingAvgSqDecoder = [];
Обучите модель.
numObservations = numel(sequencesSource); numIterationsPerEpoch = floor(numObservations/miniBatchSize); iteration = 0; start = tic; % Loop over epochs. for epoch = 1:numEpochs % Loop over mini-batches. for i = 1:numIterationsPerEpoch iteration = iteration + 1; % Read mini-batch of data idx = (i-1)*miniBatchSize+1:i*miniBatchSize; [XSource, XTarget, maskSource, maskTarget] = createBatch(sequencesSource(idx), ... sequencesTarget(idx), inputSize, outputSize); % Convert mini-batch of data to dlarray. dlXSource = dlarray(XSource); dlXTarget = dlarray(XTarget); % Compute loss and gradients. [gradientsEncoder, gradientsDecoder, loss] = dlfeval(@modelGradients, parametersEncoder, ... parametersDecoder, dlXSource, dlXTarget, maskSource, maskTarget, dropout); % Gradient clipping. gradientsEncoder = dlupdate(@(w) clipGradient(w,gradientThreshold), gradientsEncoder); gradientsDecoder = dlupdate(@(w) clipGradient(w,gradientThreshold), gradientsDecoder); % Update encoder using adamupdate. [parametersEncoder, trailingAvgEncoder, trailingAvgSqEncoder] = adamupdate(parametersEncoder, ... gradientsEncoder, trailingAvgEncoder, trailingAvgSqEncoder, iteration, learnRate, ... gradientDecayFactor, squaredGradientDecayFactor); % Update decoder using adamupdate. [parametersDecoder, trailingAvgDecoder, trailingAvgSqDecoder] = adamupdate(parametersDecoder, ... gradientsDecoder, trailingAvgDecoder, trailingAvgSqDecoder, iteration, learnRate, ... gradientDecayFactor, squaredGradientDecayFactor); % Display the training progress. if plots == "training-progress" D = duration(0,0,toc(start),'Format','hh:mm:ss'); addpoints(lineLossTrain,iteration,double(gather(loss))) title("Epoch: " + epoch + ", Elapsed: " + string(D)) drawnow end end % Shuffle data. idx = randperm(numObservations); sequencesSource = sequencesSource(idx); sequencesTarget = sequencesTarget(idx); end

Чтобы сгенерировать переводы для новых данных с помощью обученной модели, преобразуйте текстовые данные в числовые последовательности с помощью тех же шагов как тогда, когда обучение и ввело последовательности в модель декодера энкодера и преобразует получившиеся последовательности назад в текст с помощью маркерных индексов.
Выберите мини-пакет тестовых наблюдений.
numObservationsTest = 16; idx = randperm(size(dataTest,1),numObservationsTest); dataTest(idx,:)
ans=16×2 table
Source Target
______ ___________
"857" "DCCCLVII"
"991" "CMXCI"
"143" "CXLIII"
"924" "CMXXIV"
"752" "DCCLII"
"85" "LXXXV"
"131" "CXXXI"
"124" "CXXIV"
"858" "DCCCLVIII"
"103" "CIII"
"497" "CDXCVII"
"76" "LXXVI"
"815" "DCCCXV"
"829" "DCCCXXIX"
"940" "CMXL"
"94" "XCIV"
Предварительно обработайте текстовые данные с помощью тех же шагов как тогда, когда обучение. Используйте transformText функция, перечисленная в конце примера, чтобы разделить текст в символы и добавить запуск и лексемы остановки.
strSource = dataTest{idx,1};
strTarget = dataTest{idx,2};
documentsSource = transformText(strSource,startToken,stopToken);Преобразуйте маркируемый текст в пакет заполненных последовательностей при помощи doc2sequence функция. Чтобы автоматически заполнить последовательности, установите 'PaddingDirection' опция к 'right' и установленный дополнительное значение к входному размеру (маркерный индекс дополнительной лексемы).
sequencesSource = doc2sequence(encSource,documentsSource, ... 'PaddingDirection','right', ... 'PaddingValue',inputSize);
Конкатенация и переставляет данные о последовательности в необходимой форме для функции модели энкодера (1 N S, где N является количеством наблюдений, и S является длиной последовательности).
XSource = cat(3,sequencesSource{:});
XSource = permute(XSource,[1 3 2]);Преобразуйте входные данные в dlarray и вычислите модель энкодера выходные параметры.
dlXSource = dlarray(XSource); [dlZ, hiddenState] = modelEncoder(dlXSource, parametersEncoder);
Сгенерировать переводы для нового ввода данных последовательности в модель декодера энкодера и преобразовать получившиеся последовательности назад в текст с помощью маркерных индексов.
Чтобы инициализировать переводы, создайте вектор, содержащий только индексы, соответствующие лексеме запуска.
decoderInput = repmat(word2ind(encTarget,startToken),[1 numObservationsTest]); decoderInput = dlarray(decoderInput);
Инициализируйте вектор контекста и массивы ячеек, содержащие переведенные последовательности и музыку внимания к каждому наблюдению.
context = dlarray(zeros([size(dlZ, 1) numObservationsTest])); sequencesTranslated = cell(1,numObservationsTest); attentionScores = cell(1,numObservationsTest);
Цикл в зависимости от времени продвигается, и переведите последовательности. Сохраните цикличное выполнение по временным шагам, пока все последовательности не перевели. Для каждого наблюдения, когда перевод закончен (когда декодер предсказывает лексему остановки), устанавливает флаг прекращать переводить ту последовательность.
stopIdx = word2ind(encTarget,stopToken); stopTranslating = false(1, numObservationsTest); maxSequenceLength = 10; while ~all(stopTranslating) % Forward through decoder. [dlY, context, hiddenState, attn] = modelDecoder(decoderInput, parametersDecoder, context, ... hiddenState, dlZ); % Loop over observations. for i = 1:numObservationsTest % Skip already-translated sequences. if stopTranslating(i) continue end % Update attention scores. attentionScores{i} = [attentionScores{i} extractdata(attn(:,i))]; % Predict next time step. prob = softmax(dlY(:,i), 'DataFormat', 'CB'); [~, idx] = max(prob(1:end-1,:), [], 1); % Set stopTranslating flag when translation done. if idx == stopIdx || numel(sequencesTranslated{i}) == maxSequenceLength stopTranslating(i) = true; else sequencesTranslated{i} = [sequencesTranslated{i} extractdata(idx)]; decoderInput(i) = idx; end end end
Просмотрите исходный текст, целевой текст и переводы в таблице.
tbl = table;
tbl.Source = strSource;
tbl.Target = strTarget;
tbl.Translated = cellfun(@(sequence) join(ind2word(encTarget,sequence),""),sequencesTranslated)';
tbltbl=16×3 table
Source Target Translated
______ ___________ ___________
"857" "DCCCLVII" "DCCCLVII"
"991" "CMXCI" "CMXCI"
"143" "CXLIII" "CXLIII"
"924" "CMXXIV" "CMXXIV"
"752" "DCCLII" "DCCLII"
"85" "LXXXV" "DCCCLVI"
"131" "CXXXI" "CXXXI"
"124" "CXXIV" "CXXIV"
"858" "DCCCLVIII" "DCCCLVIII"
"103" "CIII" "CIII"
"497" "CDXCVII" "CDXCVII"
"76" "LXXVI" "DCCLVII"
"815" "DCCCXV" "DCCCXV"
"829" "DCCCXXIX" "DCCCXXIX"
"940" "CMXL" "CMXL"
"94" "XCIV" "CMXLVI"
Постройте множество внимания первой последовательности в карте тепла. Подсветка баллов внимания, какие области источника и переведенный упорядочивают модель, проявляет внимание при обработке перевода.
idx = 1;
figure
xlabs = [ind2word(encTarget,sequencesTranslated{idx}) stopToken];
ylabs = string(documentsSource(idx));
heatmap(attentionScores{idx}, ...
'CellLabelColor','none', ...
'XDisplayLabels',xlabs, ...
'YDisplayLabels',ylabs);
xlabel("Translation")
ylabel("Source")
title("Attention Scores")
preprocessSourceTargetPairs берет таблицу data содержание целевых источником пар в двух столбцах и для каждого столбца возвращает последовательности маркерных индексов и соответствующего wordEncoding возразите, что сопоставляет индексы со словами и наоборот.
function [sequencesSource, sequencesTarget, encSource, encTarget] = preprocessSourceTargetPairs(data,startToken,stopToken) % Extract text data. strSource = data{:,1}; strTarget = data{:,2}; % Create tokenized document arrays. documentsSource = transformText(strSource,startToken,stopToken); documentsTarget = transformText(strTarget,startToken,stopToken); % Create word encodings. encSource = wordEncoding(documentsSource); encTarget = wordEncoding(documentsTarget); % Convert documents to numeric sequences. sequencesSource = doc2sequence(encSource, documentsSource,'PaddingDirection','none'); sequencesTarget = doc2sequence(encTarget, documentsTarget,'PaddingDirection','none'); end
transformText функция предварительно обрабатывает и маркирует входной текст для перевода путем разделения текста в символы, и добавление запускают и останавливают лексемы. Чтобы перевести текст путем разделения текста в слова вместо символов, пропустите первый шаг.
function documents = transformText(str,startToken,stopToken) % Split text into characters. str = strip(replace(str,""," ")); % Add start and stop tokens. str = startToken + str + stopToken; % Create tokenized document array. documents = tokenizedDocument(str,'CustomTokens',[startToken stopToken]); end
createBatch функционируйте берет мини-пакет входных и выходных последовательностей и возвращает дополненные последовательности с соответствующими дополнительными масками.
function [XSource, XTarget, maskSource, maskTarget] = createBatch(sequencesSource, sequencesTarget, ... paddingValueSource, paddingValueTarget) numObservations = size(sequencesSource,1); sequenceLengthSource = max(cellfun(@(x) size(x,2), sequencesSource)); sequenceLengthTarget = max(cellfun(@(x) size(x,2), sequencesTarget)); % Initialize masks. maskSource = false(numObservations, sequenceLengthSource); maskTarget = false(numObservations, sequenceLengthTarget); % Initialize mini-batch. XSource = zeros(1,numObservations,sequenceLengthSource); XTarget = zeros(1,numObservations,sequenceLengthTarget); % Pad sequences and create masks. for i = 1:numObservations % Source L = size(sequencesSource{i},2); paddingSize = sequenceLengthSource - L; padding = repmat(paddingValueSource, [1 paddingSize]); XSource(1,i,:) = [sequencesSource{i} padding]; maskSource(i,1:L) = true; % Target L = size(sequencesTarget{i},2); paddingSize = sequenceLengthTarget - L; padding = repmat(paddingValueTarget, [1 paddingSize]); XTarget(1,i,:) = [sequencesTarget{i} padding]; maskTarget(i,1:L) = true; end end
Функциональный modelEncoder берет входные данные, параметры модели, дополнительная маска, которая используется, чтобы определить правильные выходные параметры для обучения и возвращает выходной параметр модели и LSTM скрытое состояние.
function [dlZ, hiddenState] = modelEncoder(dlX, parametersEncoder, maskSource) % Embedding weights = parametersEncoder.emb.Weights; dlZ = embedding(dlX,weights); % LSTM inputWeights = parametersEncoder.lstm1.InputWeights; recurrentWeights = parametersEncoder.lstm1.RecurrentWeights; bias = parametersEncoder.lstm1.Bias; numHiddenUnits = size(recurrentWeights, 2); initialHiddenState = dlarray(zeros([numHiddenUnits 1])); initialCellState = dlarray(zeros([numHiddenUnits 1])); dlZ = lstm(dlZ, initialHiddenState, initialCellState, inputWeights, ... recurrentWeights, bias, 'DataFormat', 'CBT'); % LSTM inputWeights = parametersEncoder.lstm2.InputWeights; recurrentWeights = parametersEncoder.lstm2.RecurrentWeights; bias = parametersEncoder.lstm2.Bias; [dlZ, hiddenState] = lstm(dlZ,initialHiddenState, initialCellState, ... inputWeights, recurrentWeights, bias, 'DataFormat', 'CBT'); % Mask output for training if nargin > 2 dlZ = dlZ.*permute(maskSource, [3 1 2]); sequenceLengths = sum(maskSource, 2); % Mask final hidden state for ii = 1:size(dlZ, 2) hiddenState(:, ii) = dlZ(:, ii, sequenceLengths(ii)); end end end
Функциональный modelDecoder берет входные данные, параметры модели, вектор контекста, начальная буква LSTM скрытое состояние, выходные параметры энкодера и вероятность уволенного и выводит декодер выход, обновленный вектор контекста, обновленное состояние LSTM и баллы внимания.
function [dlY, context, hiddenState, attentionScores] = modelDecoder(dlX, parameters, context, ... hiddenState, encoderOutputs, dropout) % Embedding weights = parameters.emb.Weights; dlX = embedding(dlX, weights); % RNN input dlY = cat(1, dlX, context); % LSTM 1 initialCellState = dlarray(zeros(size(hiddenState))); inputWeights = parameters.lstm1.InputWeights; recurrentWeights = parameters.lstm1.RecurrentWeights; bias = parameters.lstm1.Bias; dlY = lstm(dlY, hiddenState, initialCellState, inputWeights, ... recurrentWeights, bias, 'DataFormat', 'CBT'); if nargin > 5 % Dropout mask = ( rand(size(dlY), 'like', dlY) > dropout ); dlY = dlY.*mask; end % LSTM 2 inputWeights = parameters.lstm2.InputWeights; recurrentWeights = parameters.lstm2.RecurrentWeights; bias = parameters.lstm2.Bias; [~, hiddenState] = lstm(dlY, hiddenState, initialCellState, ... inputWeights, recurrentWeights, bias, 'DataFormat', 'CBT'); % Attention weights = parameters.attn.Weights; attentionScores = attention(hiddenState, encoderOutputs, weights); % Context encoderOutputs = permute(encoderOutputs, [1 3 2]); attentionScores = permute(attentionScores,[1 3 2]); context = dlmtimes(encoderOutputs,attentionScores); context = squeeze(context); % Fully connect weights = parameters.fc.Weights; bias = parameters.fc.Bias; dlY = weights*cat(1, hiddenState, context) + bias; end
embedding функционируйте сопоставляет числовые индексы с соответствующим вектором, данным входными весами.
function Z = embedding(X, weights) % Reshape inputs into a vector [N, T] = size(X, 2:3); X = reshape(X, N*T, 1); % Index into embedding matrix Z = weights(:, X); % Reshape outputs by separating out batch and sequence dimensions Z = reshape(Z, [], N, T); end
attention функция вычисляет баллы внимания по данным Luong "общий" выигрыш.
function attentionScores = attention(hiddenState, encoderOutputs, weights) [N, S] = size(encoderOutputs, 2:3); attentionEnergies = dlarray(zeros( [S N] )); % The energy at each time step is the dot product of the hidden state % and the learnable attention weights times the encoder output hWX = hiddenState .* dlmtimes(weights,encoderOutputs); for tt = 1:S attentionEnergies(tt, :) = sum(hWX(:, :, tt), 1); end % Compute softmax scores attentionScores = softmax(attentionEnergies, 'DataFormat', 'CB'); end
modelGradients функционируйте берет параметры модели энкодера и декодера, мини-пакет входных данных и дополнительных масок, соответствующих входным данным и вероятности уволенного, и возвращает градиенты потери относительно настраиваемых параметров в моделях и соответствующей потери.
function [gradientsEncoder, gradientsDecoder, maskedLoss] = modelGradients(parametersEncoder, ... parametersDecoder, dlXSource, dlXTarget, maskSource, maskTarget, dropout) % Forward through encoder. [dlZ, hiddenState] = modelEncoder(dlXSource, parametersEncoder, maskSource); % Get parameter sizes. [miniBatchSize, sequenceLength] = size(dlXTarget,2:3); sequenceLength = sequenceLength - 1; numHiddenUnits = size(dlZ,1); % Initialize context vector. context = dlarray(zeros([numHiddenUnits miniBatchSize])); % Initialize loss. loss = dlarray(zeros([miniBatchSize sequenceLength])); % Get first time step for decoder. decoderInput = dlXTarget(:,:,1); % Choose whether to use teacher forcing. doTeacherForcing = rand < 0.5; if doTeacherForcing for t = 1:sequenceLength % Forward through decoder. [dlY, context, hiddenState] = modelDecoder(decoderInput, parametersDecoder, context, ... hiddenState, dlZ, dropout); % Update loss. dlT = dlarray(oneHot(dlXTarget(:,:,t+1), size(dlY,1))); loss(:,t) = crossEntropyAndSoftmax(dlY, dlT); % Get next time step. decoderInput = dlXTarget(:,:,t+1); end else for t = 1:sequenceLength % Forward through decoder. [dlY, context, hiddenState] = modelDecoder(decoderInput, parametersDecoder, context, ... hiddenState, dlZ, dropout); % Update loss. dlT = dlarray(oneHot(dlXTarget(:,:,t+1), size(dlY,1))); loss(:,t) = crossEntropyAndSoftmax(dlY, dlT); % Greedily update next input time step. prob = softmax(dlY,'DataFormat','CB'); [~, decoderInput] = max(prob,[],1); end end % Determine masked loss. maskedLoss = sum(sum(loss.*maskTarget(:,2:end))) / miniBatchSize; % Update gradients. [gradientsEncoder, gradientsDecoder] = dlgradient(maskedLoss, parametersEncoder, parametersDecoder); % For plotting, return loss normalized by sequence length. maskedLoss = extractdata(maskedLoss) ./ sequenceLength; end
crossEntropyAndSoftmax потеря вычисляет перекрестную энтропию и softmax потерю.
function loss = crossEntropyAndSoftmax(dlY, dlT) offset = max(dlY); logSoftmax = dlY - offset - log(sum(exp(dlY-offset))); loss = -sum(dlT.*logSoftmax); end
uniformNoise функциональные демонстрационные веса от равномерного распределения.
function weights = uniformNoise(sz, k) weights = -sqrt(k) + 2*sqrt(k).*rand(sz); end
clipGradient функционируйте отсекает градиенты модели.
function g = clipGradient(g, gradientThreshold) wnorm = norm(extractdata(g)); if wnorm > gradientThreshold g = (gradientThreshold/wnorm).*g; end end
oneHot функция кодирует словари как одногорячие векторы.
function oh = oneHot(idx, numTokens) tokens = (1:numTokens)'; oh = (tokens == idx); end
adamupdate | crossentropy | dlarray | dlfeval | dlgradient | dlupdate | doc2sequence | lstm | softmax | tokenizedDocument | word2ind | wordEncoding