В этом примере показано, как сгенерировать текстовые данные с помощью автоэнкодеров.
Автоэнкодер является типом нейронной сети для глубокого обучения, которая обучена реплицировать ее вход. Автоэнкодер состоит из двух меньших сетей: и энкодер и декодер. Энкодер сопоставляет входные данные с характеристическим вектором на некотором скрытом пробеле. Декодер восстанавливает данные с помощью векторов на этом скрытом пробеле.
Учебный процесс является безнадзорным. Другими словами, модель не требует маркированных данных. Чтобы сгенерировать текст, можно использовать декодер, чтобы восстановить текст от произвольного входа.
Этот пример обучает автоэнкодер генерировать текст. Энкодер использует встраивание слова и операцию 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
, перечисленный в конце примера, выполняет эти шаги:
Предварительно ожидает и добавляет каждую входную строку с заданным запуском и лексемами остановки, соответственно.
Маркируйте текст с помощью 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
Инициализируйте параметры для следующей модели.
Здесь, длина последовательности, входная последовательность словарей, и восстановленная последовательность.
Энкодер сопоставляет последовательности словарей к собственному вектору путем преобразования входа в последовательности векторов слова с помощью встраивания, введения последовательностей вектора слова в операцию LSTM и применение полностью связанной операции к последнему временному шагу LSTM выход. Декодер восстанавливает вход с помощью LSTM, инициализировал энкодер выход. Для каждого временного шага декодер предсказывает следующий временной шаг и использует выход для следующих предсказаний такта. И энкодер и декодер используют то же встраивание.
Задайте размерности параметров.
embeddingDimension = 100; numHiddenUnits = 150; latentDimension = 75; vocabularySize = enc.NumWords;
Создайте struct для параметров.
parameters = struct;
Инициализируйте веса встраивания с помощью Гауссова использования initializeGaussian
функция, которая присоединена к этому примеру как к вспомогательному файлу. Задайте среднее значение 0 и стандартное отклонение 0,01. Чтобы узнать больше, смотрите Гауссову Инициализацию (Deep Learning Toolbox).
mu = 0; sigma = 0.01; parameters.emb.Weights = initializeGaussian([embeddingDimension vocabularySize],mu,sigma);
Инициализируйте настраиваемые параметры для операции LSTM энкодера:
Инициализируйте входные веса инициализатором Glorot с помощью initializeGlorot
функция, которая присоединена к этому примеру как к вспомогательному файлу. Чтобы узнать больше, см. Инициализацию Glorot (Deep Learning Toolbox).
Инициализируйте текущие веса ортогональным инициализатором с помощью initializeOrthogonal
функция, которая присоединена к этому примеру как к вспомогательному файлу. Чтобы узнать больше, смотрите Ортогональную Инициализацию (Deep Learning Toolbox).
Инициализируйте смещение модулем, забывают инициализатор логического элемента с помощью initializeUnitForgetGate
функция, которая присоединена к этому примеру как к вспомогательному файлу. Чтобы узнать больше, смотрите, что Модуль Забывает Инициализацию Логического элемента (Deep Learning Toolbox).
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
функция, которая присоединена к этому примеру как к вспомогательному файлу. Чтобы узнать больше, смотрите Нулевую Инициализацию (Deep Learning Toolbox).
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]);
Чтобы узнать больше об инициализации веса, смотрите, Инициализируют Настраиваемые параметры для Функций Модели (Deep Learning Toolbox).
Создайте функциональный modelEncoder
, перечисленный в разделе Encoder Model Function примера, который вычисляет выход модели энкодера. modelEncoder
функция, берет в качестве входных последовательностей словарей, параметров модели и длин последовательности, и возвращает соответствующий скрытый характеристический вектор. Чтобы узнать больше об определении функции энкодера модели, смотрите, Задают текстовую Функцию Модели Энкодера (Deep Learning Toolbox).
Создайте функциональный modelDecoder
, перечисленный в разделе Decoder Model Function примера, который вычисляет выход модели декодера. modelDecoder
функция, берет в качестве входных последовательностей словарей, параметров модели и длин последовательности, и возвращает соответствующий скрытый характеристический вектор. Чтобы узнать больше об определении функции декодера модели, смотрите, Задают текстовую Функцию Модели Декодера (Deep Learning Toolbox).
modelGradients
функция, перечисленная в разделе Model Gradients Function примера, берет в качестве входа настраиваемые параметры модели, входные данные dlX
, и вектор из длин последовательности для маскирования, и возвращает градиенты потери относительно настраиваемых параметров и соответствующей потери. Чтобы узнать больше об определении функции градиентов модели, смотрите Функцию Градиентов Модели Define для Пользовательского Учебного Цикла (Deep Learning Toolbox).
Задайте опции для обучения.
Обучайтесь в течение 100 эпох с мини-пакетным размером 128.
miniBatchSize = 128; numEpochs = 100;
Обучайтесь со скоростью обучения 0,01.
learnRate = 0.01;
Отобразите прогресс обучения в графике.
plots = "training-progress";
Обучайтесь на графическом процессоре, если вы доступны. Используя графический процессор требует Parallel Computing Toolbox™, и CUDA® включил NVIDIA®, графический процессор с вычисляет возможность 3.0 или выше.
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 для Пользовательского Учебного Цикла (Deep Learning Toolbox).
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
выполняет эти шаги:
Предварительно ожидает и добавляет каждую входную строку с заданным запуском и лексемами остановки, соответственно.
Маркируйте текст с помощью 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
doc2sequence
| tokenizedDocument
| word2ind
| wordEncoding