В этом примере показано, как обучить сеть глубокого обучения с длительной краткосрочной памятью (LSTM) для генерации текста.
Чтобы обучить нейронную сеть для глубокого обучения для генерации текста, обучите сеть LSTM с последовательностью в последовательности для предсказания следующего символа в последовательности символов. Чтобы обучить сеть предсказывать следующий символ, задайте входные последовательности, сдвинутые на один временной шаг, в качестве ответов.
Чтобы ввести последовательность символов в сеть LSTM, преобразуйте каждое обучающее наблюдение в последовательность символов, представленных векторами , где D - количество уникальных символов в словаре. Для каждого вектора, если x соответствует символу с индексом i в заданном словаре, и для .
Извлеките текстовые данные из текстового файла sonnets.txt
.
filename = "sonnets.txt";
textData = fileread(filename);
Сонеты изрезаны двумя пробелами и разделены двумя символами новой строки. Удалите углубления с помощью replace
и разделите текст на отдельные сонеты, используя split
. Удалите основной заголовок из первых трех элементов и заголовки сонетов, которые появляются перед каждым сонетом.
textData = replace(textData," ",""); textData = split(textData,[newline newline]); textData = textData(5:2:end);
Посмотрите первые несколько наблюдений.
textData(1:10)
ans = 10×1 cell array
{'From fairest creatures we desire increase,↵That thereby beauty's rose might never die,↵But as the riper should by time decease,↵His tender heir might bear his memory:↵But thou, contracted to thine own bright eyes,↵Feed'st thy light's flame with self-substantial fuel,↵Making a famine where abundance lies,↵Thy self thy foe, to thy sweet self too cruel:↵Thou that art now the world's fresh ornament,↵And only herald to the gaudy spring,↵Within thine own bud buriest thy content,↵And tender churl mak'st waste in niggarding:↵Pity the world, or else this glutton be,↵To eat the world's due, by the grave and thee.' }
{'When forty winters shall besiege thy brow,↵And dig deep trenches in thy beauty's field,↵Thy youth's proud livery so gazed on now,↵Will be a tatter'd weed of small worth held:↵Then being asked, where all thy beauty lies,↵Where all the treasure of thy lusty days;↵To say, within thine own deep sunken eyes,↵Were an all-eating shame, and thriftless praise.↵How much more praise deserv'd thy beauty's use,↵If thou couldst answer 'This fair child of mine↵Shall sum my count, and make my old excuse,'↵Proving his beauty by succession thine!↵This were to be new made when thou art old,↵And see thy blood warm when thou feel'st it cold.' }
{'Look in thy glass and tell the face thou viewest↵Now is the time that face should form another;↵Whose fresh repair if now thou not renewest,↵Thou dost beguile the world, unbless some mother.↵For where is she so fair whose unear'd womb↵Disdains the tillage of thy husbandry?↵Or who is he so fond will be the tomb,↵Of his self-love to stop posterity?↵Thou art thy mother's glass and she in thee↵Calls back the lovely April of her prime;↵So thou through windows of thine age shalt see,↵Despite of wrinkles this thy golden time.↵But if thou live, remember'd not to be,↵Die single and thine image dies with thee.' }
{'Unthrifty loveliness, why dost thou spend↵Upon thy self thy beauty's legacy?↵Nature's bequest gives nothing, but doth lend,↵And being frank she lends to those are free:↵Then, beauteous niggard, why dost thou abuse↵The bounteous largess given thee to give?↵Profitless usurer, why dost thou use↵So great a sum of sums, yet canst not live?↵For having traffic with thy self alone,↵Thou of thy self thy sweet self dost deceive:↵Then how when nature calls thee to be gone,↵What acceptable audit canst thou leave?↵Thy unused beauty must be tombed with thee,↵Which, used, lives th' executor to be.' }
{'Those hours, that with gentle work did frame↵The lovely gaze where every eye doth dwell,↵Will play the tyrants to the very same↵And that unfair which fairly doth excel;↵For never-resting time leads summer on↵To hideous winter, and confounds him there;↵Sap checked with frost, and lusty leaves quite gone,↵Beauty o'er-snowed and bareness every where:↵Then were not summer's distillation left,↵A liquid prisoner pent in walls of glass,↵Beauty's effect with beauty were bereft,↵Nor it, nor no remembrance what it was:↵But flowers distill'd, though they with winter meet,↵Leese but their show; their substance still lives sweet.' }
{'Then let not winter's ragged hand deface,↵In thee thy summer, ere thou be distill'd:↵Make sweet some vial; treasure thou some place↵With beauty's treasure ere it be self-kill'd.↵That use is not forbidden usury,↵Which happies those that pay the willing loan;↵That's for thy self to breed another thee,↵Or ten times happier, be it ten for one;↵Ten times thy self were happier than thou art,↵If ten of thine ten times refigur'd thee:↵Then what could death do if thou shouldst depart,↵Leaving thee living in posterity?↵Be not self-will'd, for thou art much too fair↵To be death's conquest and make worms thine heir.' }
{'Lo! in the orient when the gracious light↵Lifts up his burning head, each under eye↵Doth homage to his new-appearing sight,↵Serving with looks his sacred majesty;↵And having climb'd the steep-up heavenly hill,↵Resembling strong youth in his middle age,↵Yet mortal looks adore his beauty still,↵Attending on his golden pilgrimage:↵But when from highmost pitch, with weary car,↵Like feeble age, he reeleth from the day,↵The eyes, 'fore duteous, now converted are↵From his low tract, and look another way:↵So thou, thyself outgoing in thy noon:↵Unlook'd, on diest unless thou get a son.' }
{'Music to hear, why hear'st thou music sadly?↵Sweets with sweets war not, joy delights in joy:↵Why lov'st thou that which thou receiv'st not gladly,↵Or else receiv'st with pleasure thine annoy?↵If the true concord of well-tuned sounds,↵By unions married, do offend thine ear,↵They do but sweetly chide thee, who confounds↵In singleness the parts that thou shouldst bear.↵Mark how one string, sweet husband to another,↵Strikes each in each by mutual ordering;↵Resembling sire and child and happy mother,↵Who, all in one, one pleasing note do sing:↵Whose speechless song being many, seeming one,↵Sings this to thee: 'Thou single wilt prove none.''}
{'Is it for fear to wet a widow's eye,↵That thou consum'st thy self in single life?↵Ah! if thou issueless shalt hap to die,↵The world will wail thee like a makeless wife;↵The world will be thy widow and still weep↵That thou no form of thee hast left behind,↵When every private widow well may keep↵By children's eyes, her husband's shape in mind:↵Look! what an unthrift in the world doth spend↵Shifts but his place, for still the world enjoys it;↵But beauty's waste hath in the world an end,↵And kept unused the user so destroys it.↵No love toward others in that bosom sits↵That on himself such murd'rous shame commits.' }
{'For shame! deny that thou bear'st love to any,↵Who for thy self art so unprovident.↵Grant, if thou wilt, thou art belov'd of many,↵But that thou none lov'st is most evident:↵For thou art so possess'd with murderous hate,↵That 'gainst thy self thou stick'st not to conspire,↵Seeking that beauteous roof to ruinate↵Which to repair should be thy chief desire.↵O! change thy thought, that I may change my mind:↵Shall hate be fairer lodg'd than gentle love?↵Be, as thy presence is, gracious and kind,↵Or to thyself at least kind-hearted prove:↵Make thee another self for love of me,↵That beauty still may live in thine or thee.' }
Преобразуйте текстовые данные в последовательности векторов для предикторов и категориальные последовательности для ответов.
Создайте специальные символы для обозначения «start of text», «whitespace», «end of text» и «newline». Используйте специальные символы "\x0002"
(начало текста), "\x00B7"
(«·», средняя точка), "\x2403"
(«␃», конец текста) и "\x00B6"
("¶
, "pilcrow) соответственно. Чтобы предотвратить неоднозначность, необходимо выбрать специальные символы, которые не появляются в тексте. Поскольку эти символы не появляются в обучающих данных, они могут использоваться для этой цели.
startOfTextCharacter = compose("\x0002"); whitespaceCharacter = compose("\x00B7"); endOfTextCharacter = compose("\x2403"); newlineCharacter = compose("\x00B6");
Для каждого наблюдения вставьте начало текстового символа в начале и замените пробел и новые строки соответствующими символами.
textData = startOfTextCharacter + textData;
textData = replace(textData,[" " newline],[whitespaceCharacter newlineCharacter]);
Создайте словарь уникальных символов в тексте.
uniqueCharacters = unique([textData{:}]); numUniqueCharacters = numel(uniqueCharacters);
Наведите циклы на текстовые данные и создайте последовательность векторов, представляющих символы каждого наблюдения и категориальную последовательность символов для ответов. Чтобы обозначить конец каждого наблюдения, включите конец текстового символа.
numDocuments = numel(textData); XTrain = cell(1,numDocuments); YTrain = cell(1,numDocuments); for i = 1:numel(textData) characters = textData{i}; sequenceLength = numel(characters); % Get indices of characters. [~,idx] = ismember(characters,uniqueCharacters); % Convert characters to vectors. X = zeros(numUniqueCharacters,sequenceLength); for j = 1:sequenceLength X(idx(j),j) = 1; end % Create vector of categorical responses with end of text character. charactersShifted = [cellstr(characters(2:end)')' endOfTextCharacter]; Y = categorical(charactersShifted); XTrain{i} = X; YTrain{i} = Y; end
Просмотрите первое наблюдение и размер соответствующей последовательности. Последовательность является D-на-S матрицей, где D - количество функций (количество уникальных символов), а S - длина последовательности (количество символов в тексте).
textData{1}
ans = 'From·fairest·creatures·we·desire·increase,¶That·thereby·beauty's·rose·might·never·die,¶But·as·the·riper·should·by·time·decease,¶His·tender·heir·might·bear·his·memory:¶But·thou,·contracted·to·thine·own·bright·eyes,¶Feed'st·thy·light's·flame·with·self-substantial·fuel,¶Making·a·famine·where·abundance·lies,¶Thy·self·thy·foe,·to·thy·sweet·self·too·cruel:¶Thou·that·art·now·the·world's·fresh·ornament,¶And·only·herald·to·the·gaudy·spring,¶Within·thine·own·bud·buriest·thy·content,¶And·tender·churl·mak'st·waste·in·niggarding:¶Pity·the·world,·or·else·this·glutton·be,¶To·eat·the·world's·due,·by·the·grave·and·thee.'
size(XTrain{1})
ans = 1×2
62 611
Просмотрите соответствующую последовательность ответов. Последовательность является 1-by-S категориальным вектором ответов.
YTrain{1}
ans = 1×611 categorical array
F r o m · f a i r e s t · c r e a t u r e s · w e · d e s i r e · i n c r e a s e , ¶ T h a t · t h e r e b y · b e a u t y ' s · r o s e · m i g h t · n e v e r · d i e , ¶ B u t · a s · t h e · r i p e r · s h o u l d · b y · t i m e · d e c e a s e , ¶ H i s · t e n d e r · h e i r · m i g h t · b e a r · h i s · m e m o r y : ¶ B u t · t h o u , · c o n t r a c t e d · t o · t h i n e · o w n · b r i g h t · e y e s , ¶ F e e d ' s t · t h y · l i g h t ' s · f l a m e · w i t h · s e l f - s u b s t a n t i a l · f u e l , ¶ M a k i n g · a · f a m i n e · w h e r e · a b u n d a n c e · l i e s , ¶ T h y · s e l f · t h y · f o e , · t o · t h y · s w e e t · s e l f · t o o · c r u e l : ¶ T h o u · t h a t · a r t · n o w · t h e · w o r l d ' s · f r e s h · o r n a m e n t , ¶ A n d · o n l y · h e r a l d · t o · t h e · g a u d y · s p r i n g , ¶ W i t h i n · t h i n e · o w n · b u d · b u r i e s t · t h y · c o n t e n t , ¶ A n d · t e n d e r · c h u r l · m a k ' s t · w a s t e · i n · n i g g a r d i n g : ¶ P i t y · t h e · w o r l d , · o r · e l s e · t h i s · g l u t t o n · b e , ¶ T o · e a t · t h e · w o r l d ' s · d u e , · b y · t h e · g r a v e · a n d · t h e e . ␃
Определите архитектуру LSTM. Задайте сеть классификации LSTM в последовательности с 200 скрытыми модулями. Установите размерность признаков обучающих данных (количество уникальных символов) в качестве размера входного сигнала и количество категорий в ответах в качестве выходного размера полносвязного слоя.
inputSize = size(XTrain{1},1); numHiddenUnits = 200; numClasses = numel(categories([YTrain{:}])); layers = [ sequenceInputLayer(inputSize) lstmLayer(numHiddenUnits,'OutputMode','sequence') fullyConnectedLayer(numClasses) softmaxLayer classificationLayer];
Задайте опции обучения, используя trainingOptions
функция. Укажите количество циклов обучения 500 и начальный темп обучения 0,01. Чтобы предотвратить взрывание градиентов, установите порог градиента равным 2. Задайте, чтобы перетасовывать данные каждую эпоху путем установки 'Shuffle'
опция для 'every-epoch'
. Чтобы контролировать процесс обучения, установите 'Plots'
опция для 'training-progress'
. Чтобы подавить подробный выход, установите 'Verbose'
на false
.
Опция mini-batch size задает количество наблюдений для обработки за одну итерацию. Задайте размер мини-пакета, который равномерно разделяет данные, чтобы убедиться, что функция использует все наблюдения для обучения. В противном случае функция игнорирует наблюдения, которые не завершают мини-пакет. Установите размер мини-пакета равным 77.
options = trainingOptions('adam', ... 'MaxEpochs',500, ... 'InitialLearnRate',0.01, ... 'GradientThreshold',2, ... 'MiniBatchSize',77,... 'Shuffle','every-epoch', ... 'Plots','training-progress', ... 'Verbose',false);
Обучите сеть.
net = trainNetwork(XTrain,YTrain,layers,options);
Используйте generateText
функция, перечисленная в конце примера, для генерации текста с помощью обученной сети.
The generateText
функция генерирует текстовые символы по символам, начиная с начала текстовых символов и восстанавливает текст с помощью специальных символов. Функция дискретизирует каждый символ, используя выходные счета предсказания. Функция прекращает предсказывать, когда сеть предсказывает символ конца текста или когда сгенерированный текст имеет длину 500 символов.
Сгенерируйте текст с помощью обученной сети.
generatedText = generateText(net,uniqueCharacters,startOfTextCharacter,newlineCharacter,whitespaceCharacter,endOfTextCharacter)
generatedText = "Look, that your lepperites of such soous toor men, Where than proud on your sweetest but lever ill lie. One of Death a deal doth teal hearts come, And that which gives did mistress one learn Made mens of tongue that hands hear, And all they with me, do I fortune to brief; And every peinted could with this right ampontion sorend By genilir'd lime thau hours, and wonder sposing, And night by day you waster'd then new; For ailling thuse borrowest vein fulse were of here spent, Since my heart morey "
The generateText
функция генерирует текстовые символы по символам, начиная с начала текстовых символов и восстанавливает текст с помощью специальных символов. Функция дискретизирует каждый символ, используя выходные счета предсказания. Функция прекращает предсказывать, когда сеть предсказывает символ конца текста или когда сгенерированный текст имеет длину 500 символов.
function generatedText = generateText(net,uniqueCharacters,startOfTextCharacter,newlineCharacter,whitespaceCharacter,endOfTextCharacter)
Создать вектор начала текстового символа путем нахождения его индекса.
numUniqueCharacters = numel(uniqueCharacters); X = zeros(numUniqueCharacters,1); idx = strfind(uniqueCharacters,startOfTextCharacter); X(idx) = 1;
Сгенерируйте текстовые символы по символам с помощью обученной сети LSTM с помощью predictAndUpdateState
и datasample
. Остановите предсказание, когда сеть предсказывает символ конца текста или когда сгенерированный текст имеет длину 500 символов. The datasample
функция требует Statistics and Machine Learning Toolbox™.
Для больших наборов данных, длинных последовательностей или больших сетей предсказания на графическом процессоре обычно вычисляются быстрее, чем предсказания на центральном процессоре. В противном случае предсказания на центральном процессоре обычно вычисляются быстрее. Для одного временного шага предсказаний используйте центральный процессор. Чтобы использовать центральный процессор для предсказания, установите 'ExecutionEnvironment'
опция predictAndUpdateState
на 'cpu'
.
generatedText = ""; vocabulary = string(net.Layers(end).Classes); maxLength = 500; while strlength(generatedText) < maxLength % Predict the next character scores. [net,characterScores] = predictAndUpdateState(net,X,'ExecutionEnvironment','cpu'); % Sample the next character. newCharacter = datasample(vocabulary,1,'Weights',characterScores); % Stop predicting at the end of text. if newCharacter == endOfTextCharacter break end % Add the character to the generated text. generatedText = generatedText + newCharacter; % Create a new vector for the next input. X(:) = 0; idx = strfind(uniqueCharacters,newCharacter); X(idx) = 1; end
Восстановите сгенерированный текст путем замены специальных символов соответствующими пробелами и символами новой строки.
generatedText = replace(generatedText,[newlineCharacter whitespaceCharacter],[newline " "]); end
lstmLayer
| sequenceInputLayer
| trainingOptions
| trainNetwork