exponenta event banner

Создание текста с помощью автокодеров

В этом примере показано, как создавать текстовые данные с помощью автокодеров.

Автокодер - это тип сети глубокого обучения, который обучен воспроизводить свои входные данные. Автокодер состоит из двух меньших сетей: и кодера и декодера. Кодер отображает входные данные в вектор признаков в некотором латентном пространстве. Декодер восстанавливает данные, используя векторы в этом латентном пространстве.

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

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

Загрузить данные

Файл sonnets.txt содержит все сонеты Шекспира в одном текстовом файле.

Прочитать данные «Сонетов» Шекспира из файла "sonnets.txt".

filename = "sonnets.txt";
textData = fileread(filename);

Сонеты разделяются двумя символами пробела. Удалить отступы с помощью replace и разбить текст на отдельные строки с помощью split функция. Удалите заголовок из первых девяти элементов и заголовков коротких сонетов.

textData = replace(textData,"  ","");
textData = split(textData,newline);
textData(1:9) = [];
textData(strlength(textData)<5) = [];

Подготовка данных

Создайте функцию, которая маркирует и предварительно обрабатывает текстовые данные. Функция preprocessText, перечисленное в конце примера, выполняет следующие шаги:

  1. Добавляет и добавляет каждую входную строку с указанными маркерами запуска и остановки соответственно.

  2. Маркировка текста с помощью tokenizedDocument.

Предварительная обработка текстовых данных и указание маркеров запуска и остановки "<start>" и "<stop>"соответственно.

startToken = "<start>";
stopToken = "<stop>";
documents = preprocessText(textData,startToken,stopToken);

Создание объекта кодирования слов из маркированных документов.

enc = wordEncoding(documents);

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

Повторно создайте кодировку слова, чтобы также включить маркер заполнения и определить индекс этого маркера.

paddingToken = "<pad>";
newVocabulary = [enc.Vocabulary paddingToken];
enc = wordEncoding(newVocabulary);
paddingIdx = word2ind(enc,paddingToken)
paddingIdx = 3595

Инициализация параметров модели

Инициализируйте параметры для следующей модели.

Здесь T - длина последовательности, x1,⋯,xT - входная последовательность индексов слов, а y1,⋯,yT - восстановленная последовательность.

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

Укажите размеры параметров.

embeddingDimension = 100;
numHiddenUnits = 150;
latentDimension = 75;
vocabularySize = enc.NumWords;

Создайте структуру для параметров.

parameters = struct;

Инициализируйте веса встраивания с помощью гауссова initializeGaussian функция, которая присоединена к этому примеру в качестве вспомогательного файла. Укажите среднее значение 0 и стандартное отклонение 0,01. Дополнительные сведения см. в разделе Гауссовская инициализация.

mu = 0;
sigma = 0.01;
parameters.emb.Weights = initializeGaussian([embeddingDimension vocabularySize],mu,sigma);

Инициализируйте обучаемые параметры для операции LSTM кодера:

  • Инициализируйте входные веса с помощью инициализатора Glorot с помощью initializeGlorot функция, которая присоединена к этому примеру в качестве вспомогательного файла. Дополнительные сведения см. в разделе Инициализация Glorot.

  • Инициализируйте повторяющиеся веса ортогональным инициализатором с помощью initializeOrthogonal функция, которая присоединена к этому примеру в качестве вспомогательного файла. Дополнительные сведения см. в разделе Ортогональная инициализация.

  • Инициализируйте смещение с помощью инициализатора литника unit forget с помощью initializeUnitForgetGate функция, которая присоединена к этому примеру в качестве вспомогательного файла. Дополнительные сведения см. в разделе Инициализация Gate Unit Forget.

sz = [4*numHiddenUnits embeddingDimension];
numOut = 4*numHiddenUnits;
numIn = embeddingDimension;

parameters.lstmEncoder.InputWeights = initializeGlorot(sz,numOut,numIn);
parameters.lstmEncoder.RecurrentWeights = initializeOrthogonal([4*numHiddenUnits numHiddenUnits]);
parameters.lstmEncoder.Bias = initializeUnitForgetGate(numHiddenUnits);

Инициализируйте обучаемые параметры для полностью подключенной операции кодера:

  • Инициализируйте веса с помощью инициализатора Glorot.

  • Инициализируйте смещение нулями с помощью initializeZeros функция, которая присоединена к этому примеру в качестве вспомогательного файла. Дополнительные сведения см. в разделе Инициализация нулей.

sz = [latentDimension numHiddenUnits];
numOut = latentDimension;
numIn = numHiddenUnits;

parameters.fcEncoder.Weights = initializeGlorot(sz,numOut,numIn);
parameters.fcEncoder.Bias = initializeZeros([latentDimension 1]);

Инициализируйте обучаемые параметры для операции декодера LSTM:

  • Инициализируйте входные веса с помощью инициализатора Glorot.

  • Инициализируйте повторяющиеся веса с помощью ортогонального инициализатора.

  • Инициализируйте смещение с помощью инициализатора литника unit forget.

sz = [4*latentDimension embeddingDimension];
numOut = 4*latentDimension;
numIn = embeddingDimension;

parameters.lstmDecoder.InputWeights = initializeGlorot(sz,numOut,numIn);
parameters.lstmDecoder.RecurrentWeights = initializeOrthogonal([4*latentDimension latentDimension]);
parameters.lstmDecoder.Bias = initializeZeros([4*latentDimension 1]);

Инициализируйте обучаемые параметры для полностью подключенной операции декодера:

  • Инициализируйте веса с помощью инициализатора Glorot.

  • Инициализируйте смещение нулями.

sz = [vocabularySize latentDimension];
numOut = vocabularySize;
numIn = latentDimension;

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

Дополнительные сведения об инициализации веса см. в разделе Инициализация обучаемых параметров для функции модели.

Определение функции кодировщика модели

Создание функции modelEncoder, перечисленных в разделе «Функция модели кодера» примера, в котором вычисляются выходные данные модели кодера. modelEncoder функция принимает в качестве входных последовательностей индексов слов, параметров модели и длин последовательностей и возвращает соответствующий скрытый вектор признаков. Дополнительные сведения об определении функции кодировщика модели см. в разделе Определение функции модели кодировщика текста.

Определение функции декодера модели

Создание функции modelDecoder, перечисленных в разделе «Функция модели декодера» примера, который вычисляет выходные данные модели декодера. modelDecoder функция принимает в качестве входных последовательностей индексов слов, параметров модели и длин последовательностей и возвращает соответствующий скрытый вектор признаков. Дополнительные сведения об определении функции декодера модели см. в разделе Определение функции модели декодера текста.

Определение функции градиентов модели

modelGradients функция, перечисленная в разделе «Функция градиентов модели» примера, принимает в качестве входных данных обучаемые параметры модели, входные данные dlXи вектор длин последовательностей для маскирования и возвращает градиенты потерь относительно обучаемых параметров и соответствующих потерь. Дополнительные сведения об определении функции градиентов модели см. в разделе Определение функции градиентов модели для пользовательского учебного цикла.

Укажите параметры обучения

Укажите параметры обучения.

Поезд на 100 эпох с размером мини-партии 128.

miniBatchSize = 128;
numEpochs = 100;

Тренируйтесь с показателем обучения 0,01.

learnRate = 0.01;

Отображение хода обучения на графике.

plots = "training-progress";

Обучение на GPU, если он доступен. Для использования графического процессора требуется Toolbox™ параллельных вычислений и поддерживаемое устройство графического процессора. Сведения о поддерживаемых устройствах см. в разделе Поддержка графического процессора по выпуску (Parallel Computing Toolbox).

executionEnvironment = "auto";

Железнодорожная сеть

Обучение сети с использованием пользовательского цикла обучения.

Инициализируйте параметры оптимизатора Adam.

trailingAvg = [];
trailingAvgSq = [];

Инициализируйте график хода обучения. Создайте анимированную линию, отображающую потери в соответствии с соответствующей итерацией.

if plots == "training-progress"
    figure
    lineLossTrain = animatedline('Color',[0.85 0.325 0.098]);
    xlabel("Iteration")
    ylabel("Loss")
    ylim([0 inf])
    grid on
end

Тренируйте модель. Для первой эпохи перетасуйте данные и закольцовывайте мини-пакеты данных.

Для каждой мини-партии:

  • Преобразование текстовых данных в последовательности индексов слов.

  • Преобразовать данные в dlarray.

  • Для обучения GPU преобразуйте данные в gpuArray объекты.

  • Вычислить потери и градиенты.

  • Обновление обучаемых параметров с помощью adamupdate функция.

  • Обновите график хода обучения.

Обучение может занять некоторое время.

numObservations = numel(documents);
numIterationsPerEpoch = floor(numObservations / miniBatchSize);

iteration = 0;
start = tic;

for epoch = 1:numEpochs
    
    % Shuffle.
    idx = randperm(numObservations);
    documents = documents(idx);
    
    for i = 1:numIterationsPerEpoch
        iteration = iteration + 1;
        
        % Read mini-batch.
        idx = (i-1)*miniBatchSize+1:i*miniBatchSize;
        documentsBatch = documents(idx);
        
        % Convert to sequences.
        X = doc2sequence(enc,documentsBatch, ...
            'PaddingDirection','right', ...
            'PaddingValue',paddingIdx);
        X = cat(1,X{:});
        
        % Convert to dlarray.
        dlX = dlarray(X,'BTC');
        
        % If training on a GPU, then convert data to gpuArray.
        if (executionEnvironment == "auto" && canUseGPU) || executionEnvironment == "gpu"
            dlX = gpuArray(dlX);
        end
        
        % Calculate sequence lengths.
        sequenceLengths = doclength(documentsBatch);
        
        % Evaluate model gradients.
        [gradients,loss] = dlfeval(@modelGradients, parameters, dlX, sequenceLengths);
        
        % Update learnable parameters.
        [parameters,trailingAvg,trailingAvgSq] = adamupdate(parameters,gradients, ...
            trailingAvg,trailingAvgSq,iteration,learnRate);
        
        % Display the training progress.
        if plots == "training-progress"
            D = duration(0,0,toc(start),'Format','hh:mm:ss');
            addpoints(lineLossTrain,iteration,double(gather(extractdata(loss))))
            title("Epoch: " + epoch + ", Elapsed: " + string(D))
            
            drawnow
        end
    end
end

Создать текст

Создание текста с использованием генерации с замкнутым контуром путем инициализации декодера с различными случайными состояниями. Формирование замкнутого цикла - это когда модель генерирует данные на один шаг за раз и использует предыдущее предсказание в качестве входных данных для следующего предсказания.

Укажите, чтобы создать 3 последовательности длиной 16.

numGenerations = 3;
sequenceLength = 16;

Создайте массив случайных значений для инициализации состояния декодера.

dlZ = dlarray(randn(latentDimension,numGenerations),'CB');

При прогнозировании на GPU преобразуйте данные в gpuArray.

if (executionEnvironment == "auto" && canUseGPU) || executionEnvironment == "gpu"
    dlZ = gpuArray(dlZ);
end

Делать прогнозы с помощью modelPredictions функция, перечисленная в конце примера. modelPredictions функция возвращает выходные оценки декодера с учетом параметров модели, начального состояния декодера, максимальной длины последовательности, кодирования слов, начального маркера и размера мини-пакета.

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

Найдите индексы слов с самыми высокими баллами.

[~,idx] = max(dlY,[],1);
idx = squeeze(idx);

Преобразование числовых индексов в слова и объединение их с помощью join функция.

strGenerated = join(enc.Vocabulary(idx));

Извлеките текст перед первым стоп-маркером с помощью extractBefore функция. Чтобы функция не возвращалась отсутствующей при отсутствии маркеров остановки, добавьте маркер остановки в конец каждой последовательности.

strGenerated = extractBefore(strGenerated+stopToken,stopToken);

Удалить маркеры заполнения.

strGenerated = erase(strGenerated,paddingToken);

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

Удалите пробелы перед указанными символами препинания.

punctuationCharacters = ["." "," "’" ")" ":" ";" "?" "!"];
strGenerated = replace(strGenerated," " + punctuationCharacters,punctuationCharacters);

Удалите пробелы, появляющиеся после указанных знаков препинания.

punctuationCharacters = ["(" "‘"];
strGenerated = replace(strGenerated,punctuationCharacters + " ",punctuationCharacters);

Удалите начальные и конечные пробелы с помощью strip и просмотр сгенерированного текста.

strGenerated = strip(strGenerated)
strGenerated = 3×1 string
    "love's thou rest light best ill mistake show seeing farther cross enough by me"
    "as before his bending sickle's compass come look find."
    "summer's lays? truth once lead mayst take,"

Функция модели кодировщика

modelEncoder функция принимает в качестве входных параметров модели, последовательности индексов слов и длины последовательности и возвращает соответствующий скрытый вектор признаков.

Поскольку входные данные содержат дополненные последовательности различной длины, заполнение может оказывать неблагоприятное влияние на вычисления потерь. Для операции LSTM вместо возврата выходного сигнала последнего временного шага последовательности (который, вероятно, соответствует состоянию LSTM после обработки большого количества значений заполнения) определите фактический последний временной шаг, заданный sequenceLengths вход.

function dlZ = modelEncoder(parameters,dlX,sequenceLengths)

% Embedding.
weights = parameters.emb.Weights;
dlZ = embedding(dlX,weights);

% LSTM.
inputWeights = parameters.lstmEncoder.InputWeights;
recurrentWeights = parameters.lstmEncoder.RecurrentWeights;
bias = parameters.lstmEncoder.Bias;

numHiddenUnits = size(recurrentWeights,2);
hiddenState = zeros(numHiddenUnits,1,'like',dlX);
cellState = zeros(numHiddenUnits,1,'like',dlX);

dlZ1 = lstm(dlZ,hiddenState,cellState,inputWeights,recurrentWeights,bias,'DataFormat','CBT');

% Output mode 'last' with masking.
miniBatchSize = size(dlZ1,2);
dlZ = zeros(numHiddenUnits,miniBatchSize,'like',dlZ1);

for n = 1:miniBatchSize
    t = sequenceLengths(n);
    dlZ(:,n) = dlZ1(:,n,t);
end

% Fully connect.
weights = parameters.fcEncoder.Weights;
bias = parameters.fcEncoder.Bias;
dlZ = fullyconnect(dlZ,weights,bias,'DataFormat','CB');

end

Функция модели декодера

modelDecoder функция принимает в качестве входных параметров модели, последовательности индексов слов и состояние сети и возвращает декодированные последовательности.

Потому что lstm функция имеет статус (если в качестве входных данных задан временной ряд, функция распространяет и обновляет состояние между каждым временным шагом) и что embedding и fullyconnect функции распределяются по времени по умолчанию (при вводе временного ряда функции работают на каждом временном шаге независимо), modelDecoder функция поддерживает как последовательные, так и одномоментные входы.

function [dlY,state] = modelDecoder(parameters,dlX,state)

% Embedding.
weights = parameters.emb.Weights;
dlX = embedding(dlX,weights);

% LSTM.
inputWeights = parameters.lstmDecoder.InputWeights;
recurrentWeights = parameters.lstmDecoder.RecurrentWeights;
bias = parameters.lstmDecoder.Bias;

hiddenState = state.HiddenState;
cellState = state.CellState;

[dlY,hiddenState,cellState] = lstm(dlX,hiddenState,cellState, ...
    inputWeights,recurrentWeights,bias,'DataFormat','CBT');

state.HiddenState = hiddenState;
state.CellState = cellState;

% Fully connect.
weights = parameters.fcDecoder.Weights;
bias = parameters.fcDecoder.Bias;
dlY = fullyconnect(dlY,weights,bias,'DataFormat','CBT');

% Softmax.
dlY = softmax(dlY,'DataFormat','CBT');

end

Функция градиентов модели

modelGradients функция, которая принимает в качестве входных данных модели обучаемые параметры, входные данные dlXи вектор длин последовательностей для маскирования и возвращает градиенты потерь относительно обучаемых параметров и соответствующих потерь.

Для вычисления маскированной потери функция градиентов модели использует maskedCrossEntropy функция потерь, перечисленная в конце примера. Чтобы научить декодер предсказывать следующий временной шаг последовательности, укажите цели, которые должны быть входными последовательностями, сдвинутыми на один временной шаг.

Дополнительные сведения об определении функции градиентов модели см. в разделе Определение функции градиентов модели для пользовательского учебного цикла.

function [gradients, loss] = modelGradients(parameters,dlX,sequenceLengths)

% Model encoder.
dlZ = modelEncoder(parameters,dlX,sequenceLengths);

% Initialize LSTM state.
state = struct;
state.HiddenState = dlZ;
state.CellState = zeros(size(dlZ),'like',dlZ);

% Teacher forcing.
dlY = modelDecoder(parameters,dlX,state);

% Loss.
dlYPred = dlY(:,:,1:end-1);
dlT = dlX(:,:,2:end);
loss = mean(maskedCrossEntropy(dlYPred,dlT,sequenceLengths));

% Gradients.
gradients = dlgradient(loss,parameters);

% Normalize loss for plotting.
sequenceLength = size(dlX,3);
loss = loss / sequenceLength;

end

Функция прогнозирования модели

modelPredictions функция возвращает выходные оценки декодера с учетом параметров модели, начального состояния декодера, максимальной длины последовательности, кодирования слов, начального маркера и размера мини-пакета.

function dlY = modelDecoderPredictions(parameters,dlZ,maxLength,enc,startToken,miniBatchSize)

numObservations = size(dlZ,2);
numIterations = ceil(numObservations / miniBatchSize);

startTokenIdx = word2ind(enc,startToken);
vocabularySize = enc.NumWords;

dlY = zeros(vocabularySize,numObservations,maxLength,'like',dlZ);

% Loop over mini-batches.
for i = 1:numIterations
    idxMiniBatch = (i-1)*miniBatchSize+1:min(i*miniBatchSize,numObservations);
    miniBatchSize = numel(idxMiniBatch);
    
    % Initialize state.
    state = struct;
    state.HiddenState = dlZ(:,idxMiniBatch);
    state.CellState = zeros(size(dlZ(:,idxMiniBatch)),'like',dlZ);
    
    % Initialize decoder input.
    decoderInput = dlarray(repmat(startTokenIdx,[1 miniBatchSize]),'CBT');
    
    % Loop over time steps.
    for t = 1:maxLength
        % Predict next time step.
        [dlY(:,idxMiniBatch,t), state] = modelDecoder(parameters,decoderInput,state);
        
        % Closed loop generation.
        [~,idx] = max(dlY(:,idxMiniBatch,t));
        decoderInput = idx;
    end
end

end

Маскированная функция потери перекрестной энтропии

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

function maskedLoss = maskedCrossEntropy(dlY,T,sequenceLengths)

numClasses = size(dlY,1);
miniBatchSize = size(dlY,2);
sequenceLength = size(dlY,3);

maskedLoss = zeros(sequenceLength,miniBatchSize,'like',dlY);

for t = 1:sequenceLength
    T1 = single(oneHot(T(:,:,t),numClasses));
    
    mask = (t<=sequenceLengths)';
 
    maskedLoss(t,:) = mask .* crossentropy(dlY(:,:,t),T1,'DataFormat','CBT');
end

maskedLoss = sum(maskedLoss,1);

end

Функция предварительной обработки текста

Функция preprocessText выполняет следующие шаги:

  1. Добавляет и добавляет каждую входную строку с указанными маркерами запуска и остановки соответственно.

  2. Маркировка текста с помощью tokenizedDocument.

function documents = preprocessText(textData,startToken,stopToken)

% Add start and stop tokens.
textData = startToken + textData + stopToken;

% Tokenize the text.
documents = tokenizedDocument(textData,'CustomTokens',[startToken stopToken]);

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

Функция одномоментного кодирования

oneHot функция преобразует массив числовых индексов в одноступенчатые кодированные векторы.

function oh = oneHot(idx, outputSize)

miniBatchSize = numel(idx);
oh = zeros(outputSize,miniBatchSize);

for n = 1:miniBatchSize
    c = idx(n);
    oh(c,n) = 1;
end

end

См. также

| |

Связанные темы