Сгенерируйте текст Используя автоэнкодеры

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

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

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

Этот пример обучает автоэнкодер генерировать текст. Энкодер использует встраивание слова и операцию 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;

Создайте struct для параметров.

parameters = struct;

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

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

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

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

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

  • Инициализируйте смещение модулем, забывают инициализатор логического элемента с помощью initializeUnitForgetGate функция, которая присоединена к этому примеру как к вспомогательному файлу. Чтобы узнать больше, смотрите, что Модуль Забывает Инициализацию Логического элемента.

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.

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

  • Инициализируйте смещение модулем, забывают инициализатор логического элемента.

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]);

Чтобы узнать больше об инициализации веса, смотрите, Инициализируют Настраиваемые параметры для Функции Модели.

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

Создайте функциональный modelEncoder, перечисленный в разделе Encoder Model Function примера, который вычисляет выход модели энкодера. modelEncoder функция, берет в качестве входных последовательностей словарей, параметров модели и длин последовательности, и возвращает соответствующий скрытый характеристический вектор. Чтобы узнать больше об определении функции энкодера модели, смотрите, Задают текстовую Функцию Модели Энкодера.

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

Создайте функциональный modelDecoder, перечисленный в разделе Decoder Model Function примера, который вычисляет выход модели декодера. modelDecoder функция, берет в качестве входных последовательностей словарей, параметров модели и длин последовательности, и возвращает соответствующий скрытый характеристический вектор. Чтобы узнать больше об определении функции декодера модели, смотрите, Задают текстовую Функцию Модели Декодера.

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

modelGradients функция, перечисленная в разделе Model Gradients Function примера, берет в качестве входа настраиваемые параметры модели, входные данные dlX, и вектор из длин последовательности для маскирования, и возвращает градиенты потери относительно настраиваемых параметров и соответствующей потери. Чтобы узнать больше об определении функции градиентов модели, смотрите Функцию Градиентов Модели Define для Пользовательского Учебного Цикла.

Задайте опции обучения

Задайте опции для обучения.

Обучайтесь в течение 100 эпох с мини-пакетным размером 128.

miniBatchSize = 128;
numEpochs = 100;

Обучайтесь со скоростью обучения 0,01.

learnRate = 0.01;

Отобразите прогресс обучения в графике.

plots = "training-progress";

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

executionEnvironment = "auto";

Обучение сети

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

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

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.

  • Для обучения графического процессора преобразуйте данные в 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');

При предсказании на графическом процессоре затем преобразуйте данные в 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 функция потерь, перечисленная в конце примера. Чтобы обучить декодер предсказывать следующий такт последовательности, задайте цели, чтобы быть входными последовательностями, переключенными к одному такту.

Чтобы узнать больше об определении функции градиентов модели, смотрите Функцию Градиентов Модели Define для Пользовательского Учебного Цикла.

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

Смотрите также

| |

Похожие темы