В этом примере показано, как сгенерировать текстовые данные с помощью автоэнкодеров.
Автоэнкодер является типом нейронной сети для глубокого обучения, которая обучена тиражировать свой вход. Автоэнкодер состоит из двух небольших сетей: и энкодера и декодера. Энкодер преобразует входные данные в вектор функции в некотором скрытом пространстве. Декодер восстанавливает данные с помощью векторов в этом скрытом пространстве.
Процесс обучения не обеспечен. Другими словами, модель не требует маркированных данных. Чтобы сгенерировать текст, можно использовать декодер, чтобы восстановить текст с произвольного входа.
Этот пример обучает автоэнкодер генерировать текст. Энкодер использует встраивание слова и операцию LSTM, чтобы сопоставить входной текст с латентными векторами. Декодер использует операцию LSTM и то же встраивание, чтобы восстановить текст из латентных векторов.
Файл sonnets.txt
содержит все сонеты Шекспира в одном текстовом файле.
Считайте данные Sonnets Шекспира из файла "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 Initialization (Deep Learning Toolbox).
Инициализируйте повторяющиеся веса с помощью ортогонального инициализатора с помощью initializeOrthogonal
функция, которая присоединена к этому примеру как вспомогательный файл. Дополнительные сведения см. в разделе Ортогональная инициализация (Deep Learning Toolbox).
Инициализируйте смещение с помощью модуля forget gate initializer с помощью initializeUnitForgetGate
функция, которая присоединена к этому примеру как вспомогательный файл. Для получения дополнительной информации смотрите Unit Forget Gate Initialization (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]);
Чтобы узнать больше об инициализации веса, смотрите Initialize Learnable Parameters for Model Function (Deep Learning Toolbox).
Создайте функцию modelEncoder
, перечисленный в разделе Encoder Model Function примера, который вычисляет выход модели энкодера. The modelEncoder
function, принимает в качестве входных последовательностей индексы слов, параметры модели и длины последовательности и возвращает соответствующий латентный вектор функции. Чтобы узнать больше об определении функции энкодера модели, смотрите Define Text Encoder Model Function (Deep Learning Toolbox).
Создайте функцию modelDecoder
, перечисленный в разделе Decoder Model Function примера, который вычисляет выход модели декодера. The modelDecoder
function, принимает в качестве входных последовательностей индексы слов, параметры модели и длины последовательности и возвращает соответствующий латентный вектор функции. Чтобы узнать больше об определении функции декодера модели, смотрите Define Text Decoder Model Function (Deep Learning Toolbox).
The modelGradients
функция, перечисленная в разделе Model Gradients Function примера, принимает в качестве входов обучаемые модели параметры, входные данные dlX
, и вектор длин последовательностей для маскировки, и возвращает градиенты потерь относительно настраиваемых параметров и соответствующих потерь. Чтобы узнать больше об определении функции градиентов модели, смотрите Задать функцию градиентов модели для Пользовательского цикла обучения (Deep Learning Toolbox).
Укажите опции обучения.
Train на 100 эпох с мини-партией размером 128.
miniBatchSize = 128; numEpochs = 100;
Обучите со скоростью обучения 0,01.
learnRate = 0.01;
Отображение процесса обучения на графике.
plots = "training-progress";
Обучите на графическом процессоре, если он доступен. Для использования графический процессор требуется 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
функции, перечисленной в конце примера. The 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,"
The modelEncoder
function, принимает за вход параметры модели, последовательности словесных индексов и длины последовательности и возвращает соответствующий латентный вектор функции.
Поскольку входные данные содержат заполненные последовательности различной длины, заполнение может иметь неблагоприятные эффекты для вычислений потерь. Для операции 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
The 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
The modelGradients
функция, которая принимает в качестве входов настраиваемые параметры модели, входные данные dlX
, и вектор длин последовательностей для маскировки, и возвращает градиенты потерь относительно настраиваемых параметров и соответствующих потерь.
Чтобы вычислить маскированные потери, функция градиентов модели использует maskedCrossEntropy
функции потерь, перечисленной в конце примера. Чтобы обучить декодер предсказывать следующий временной шаг последовательности, задайте цели, которые будут входными последовательностями, сдвинутыми на один временной шаг.
Чтобы узнать больше об определении функции градиентов модели, смотрите Задать функцию градиентов модели для Пользовательского цикла обучения (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
The 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
The 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
The 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
The 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