Gerador delirante: crie textos em qualquer idioma usando uma rede neural

Oi Habr.

Este artigo estará em um formato um pouco "sexta-feira", hoje trataremos da PNL. Não é a PNL sobre quais livros são vendidos em passagens inferiores, mas a que o Natural Language Processing está processando idiomas naturais. Como exemplo desse processamento, será usada a geração de texto usando uma rede neural. Podemos criar textos em qualquer idioma, do russo ou do inglês ao C ++. Os resultados são muito interessantes, você provavelmente pode adivinhar a partir da imagem.



Para aqueles que estão interessados ​​no que acontece, os resultados e o código fonte estão ocultos.

Preparação de dados


Para o processamento, usaremos uma classe especial de redes neurais - as chamadas redes neurais recorrentes (RNN). Essa rede difere da usual, pois além das células usuais, possui células de memória. Isso nos permite analisar dados de uma estrutura mais complexa e, de fato, mais próxima da memória humana, porque também não iniciamos todos os pensamentos "do zero". Para escrever código, usaremos redes LSTM (Long Short-Term Memory), uma vez que elas já são suportadas pelo Keras.



O próximo problema que precisa ser resolvido é, de fato, trabalhar com texto. E aqui há duas abordagens - para enviar símbolos ou as palavras inteiras para a entrada. O princípio da primeira abordagem é simples: o texto é dividido em blocos curtos, onde as "entradas" são um fragmento do texto e a "saída" é o próximo caractere. Por exemplo, para a última frase, 'inputs é um pedaço de texto':

input: output: ""
input: : output: ""
input: : output:""
input: : output: ""
input: : output: "".

E assim por diante Assim, a rede neural recebe fragmentos de texto na entrada e na saída os caracteres que deve formar.

A segunda abordagem é basicamente a mesma, apenas palavras inteiras são usadas em vez de palavras. Primeiro, um dicionário de palavras é compilado e os números são inseridos em vez de palavras na entrada da rede.

Esta, é claro, é uma descrição bastante simplificada. Keras já tem exemplos de geração de texto, mas, em primeiro lugar, eles não são descritos em detalhes e, em segundo lugar, todos os tutoriais em inglês usam textos bastante abstratos, como Shakespeare, que são difíceis de entender pelo nativo. Bem, estamos testando uma rede neural na nossa grande e poderosa, que, é claro, será mais clara e compreensível.

Treinamento em rede


Como texto de entrada, usei ... os comentários de Habr, o tamanho do arquivo de origem é de 1 MB (na verdade, existem mais comentários, é claro, mas eu tive que usar apenas uma parte; caso contrário, a rede neural teria sido treinada por uma semana e os leitores não teriam visto esse texto até sexta-feira). Deixe-me lembrá-lo de que apenas as letras são alimentadas na entrada de uma rede neural; a rede “não sabe” nada sobre o idioma ou sua estrutura. Vamos começar o treinamento em rede.

5 minutos de treinamento:

Até agora, nada está claro, mas você já pode ver algumas combinações reconhecíveis de letras:

. . . «

15 minutos de treinamento:

O resultado já é visivelmente melhor:



1 hora de treinamento:

« » — « » » —

Por alguma razão, todos os textos ficaram sem pontos e sem letras maiúsculas, talvez o processamento utf-8 não tenha sido feito corretamente. Mas no geral, isso é impressionante. Analisando e lembrando apenas códigos de símbolos, o programa realmente aprendeu “independentemente” as palavras russas e pode gerar um texto com aparência bastante crível.

Não menos interessante é o fato de o programa memorizar muito bem o estilo do texto. No exemplo a seguir, o texto de alguma lei foi usado como ensino. Tempo de treinamento em rede 5 minutos.

"" , , , , , , , ,

E aqui, as anotações médicas para medicamentos foram usadas como um conjunto de entradas. Tempo de treinamento em rede 5 minutos.



, ,

Aqui vemos frases quase inteiras. Isso se deve ao fato de o texto original ser curto e a rede neural realmente "memorizar" algumas frases como um todo. Esse efeito é chamado de "reciclagem" e deve ser evitado. Idealmente, você precisa testar uma rede neural em grandes conjuntos de dados, mas o treinamento nesse caso pode levar muitas horas e, infelizmente, não tenho um supercomputador extra.

Um exemplo divertido de usar essa rede é a geração de nomes. Depois de enviar uma lista de nomes masculinos e femininos para o arquivo, obtive novas opções bastante interessantes que seriam bastante adequadas para um romance de ficção científica: Rlar, Laaa, Aria, Arera, Aelia, Ninran, Air. Algo neles parece o estilo de Efremov e da Nebulosa de Andrômeda ...

C ++


O interessante é que, em geral, uma rede neural é como lembrar. O próximo passo foi verificar como o programa lida com o código fonte. Como teste, peguei diferentes fontes C ++ e as combinei em um arquivo de texto.

Honestamente, o resultado surpreendeu ainda mais do que no caso da língua russa.

5 minutos de treinamento

Porra, é quase real C ++.

 if ( snd_pcm_state_channels = 0 ) { errortext_ = "rtapialsa::probedeviceopen: esror stream_.buffer stream!"; errortext_ = errorstream_.str(); goto unlock; } if ( stream_.mode == input && stream_.mode == output || false; if ( stream_.state == stream_stopped ) { for ( unsigned int i=0; i<stream_.nuserbuffer[i] ) { for (j=0; j<info.channels; } } } 

30 minutos de treinamento

 void maxirecorder::stopstream() { for (int i = 0; i < ainchannels; i++ ) { int input=(stream, null; conternallock( pthread_cond_wate); // this should not ported to the device is a structure are is and the stream the buffer of the interleaved as and the probe the arauliored format for the the stream os and the software and the supported of the is so aa is set the directsound of allocates the reset and real the read the setup the buffer // this is the setup to the the devices. // asio will open the supported by the supported buffer are asioned structure. // this should not have to setup the counting and the proper in the position we mast it alw the aftware configuration. // this is the the stream and probe the supported be audio for sched/ond changed to this the sample rate it in the counting the fres // mecorend. windevoppart( null, 0, &phase ); possint=>buffersize( 1, bufferlist); } } } ///********************************************* void maxirecorder::abortdevice( device ) { if (cout << aindeff( thread_cont_start); for (int i = 0; i < sample_rates[0]) { if (count == int) { std::cout << "checksing of convernion allocation handler (" << device << ")."; errortext_ = errorstream_.str(); return failure; } } } if ( stream_.userbuffer[i] ) { errortext_ = "rtapicore::probedeviceopen: system error (" << geterrorcode( result ) << ") getting stream configuration info state."; error( rterror::warning ); return; } if ( stream_.mode == output || stream_.mode == duplex ) { callbackinfo *info = (const char *) callback( stream_.userbuffer[1], stream_.convertinfo[0] ); return info; } // setup buffer callback for conversion on do any the supported be audio bet that al alinums be of support of the supported buffer // the buffer size a stream and buffers. info.name = dsdevices[ device ]; return 0; } 

Como você pode ver, o programa "aprendeu" a escrever funções inteiras. Ao mesmo tempo, separou completamente "humanamente" as funções com um comentário com asteriscos, colocou comentários no código e tudo mais. Gostaria de aprender uma nova linguagem de programação com tanta velocidade ... É claro que há erros no código e, é claro, não será compilado. E, a propósito, eu não formatei o código, o programa também aprendeu a colocar colchetes e recuo "eu mesmo".

Certamente, esses programas não têm o principal significado - e, portanto, parecem surreais, como se tivessem sido escritos em um sonho, ou não tivessem sido escritos por uma pessoa completamente saudável. No entanto, os resultados são impressionantes. E talvez um estudo mais profundo da geração de textos diferentes ajude a entender melhor algumas das doenças mentais de pacientes reais. A propósito, como sugerido nos comentários, existe uma doença mental na qual uma pessoa fala em um texto gramaticalmente relacionado, mas completamente sem sentido ( esquizofasia ).

Conclusão


As redes neurais recreativas são consideradas muito promissoras, e esse é realmente um grande avanço em comparação com redes "comuns" como a MLP, que não têm memória. De fato, as capacidades das redes neurais para armazenar e processar estruturas bastante complexas são impressionantes. Foi após esses testes que pensei pela primeira vez que Ilon Mask provavelmente estava certo quando escrevi que a IA no futuro poderia ser "o maior risco para a humanidade" - mesmo que uma simples rede neural possa se lembrar e se reproduzir facilmente padrões bastante complexos, o que uma rede de bilhões de componentes pode fazer? Mas, por outro lado, não esqueça que nossa rede neural não pode pensar , ela se lembra apenas mecanicamente de seqüências de caracteres, sem entender seu significado. Esse é um ponto importante - mesmo se você treinar uma rede neural em um supercomputador e um enorme conjunto de dados, na melhor das hipóteses, ele aprenderá a gerar frases gramaticalmente 100% corretas, mas completamente sem sentido.

Mas não será removido na filosofia, o artigo é ainda mais para os profissionais. Para aqueles que querem experimentar por conta própria, o código-fonte do Python 3.7 está sob o spoiler. Esse código é uma compilação de vários projetos do github e não é uma amostra do melhor código, mas parece executar sua tarefa.

O uso do programa não requer habilidades de programação, basta saber como instalar o Python. Exemplos de partida na linha de comando:
- Criação e treinamento de modelos e geração de texto:
python. \ keras_textgen.py --text = text_habr.txt --epochs = 10 --out_len = 4000
- Somente geração de texto sem treinamento de modelo:
python. \ keras_textgen.py --text = text_habr.txt --epochs = 10 --out_len = 4000 --generate

keras_textgen.py
 import os # Force CPU os.environ["CUDA_VISIBLE_DEVICES"] = "-1" os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # 0 = all messages are logged, 3 - INFO, WARNING, and ERROR messages are not printed from keras.callbacks import LambdaCallback from keras.models import Sequential from keras.layers import Dense, Dropout, Embedding, LSTM, TimeDistributed from keras.optimizers import RMSprop from keras.utils.data_utils import get_file import keras from collections import Counter import pickle import numpy as np import random import sys import time import io import re import argparse # Transforms text to vectors of integer numbers representing in text tokens and back. Handles word and character level tokenization. class Vectorizer: def __init__(self, text, word_tokens, pristine_input, pristine_output): self.word_tokens = word_tokens self._pristine_input = pristine_input self._pristine_output = pristine_output tokens = self._tokenize(text) # print('corpus length:', len(tokens)) token_counts = Counter(tokens) # Sort so most common tokens come first in our vocabulary tokens = [x[0] for x in token_counts.most_common()] self._token_indices = {x: i for i, x in enumerate(tokens)} self._indices_token = {i: x for i, x in enumerate(tokens)} self.vocab_size = len(tokens) print('Vocab size:', self.vocab_size) def _tokenize(self, text): if not self._pristine_input: text = text.lower() if self.word_tokens: if self._pristine_input: return text.split() return Vectorizer.word_tokenize(text) return text def _detokenize(self, tokens): if self.word_tokens: if self._pristine_output: return ' '.join(tokens) return Vectorizer.word_detokenize(tokens) return ''.join(tokens) def vectorize(self, text): """Transforms text to a vector of integers""" tokens = self._tokenize(text) indices = [] for token in tokens: if token in self._token_indices: indices.append(self._token_indices[token]) else: print('Ignoring unrecognized token:', token) return np.array(indices, dtype=np.int32) def unvectorize(self, vector): """Transforms a vector of integers back to text""" tokens = [self._indices_token[index] for index in vector] return self._detokenize(tokens) @staticmethod def word_detokenize(tokens): # A heuristic attempt to undo the Penn Treebank tokenization above. Pass the # --pristine-output flag if no attempt at detokenizing is desired. regexes = [ # Newlines (re.compile(r'[ ]?\\n[ ]?'), r'\n'), # Contractions (re.compile(r"\b(can)\s(not)\b"), r'\1\2'), (re.compile(r"\b(d)\s('ye)\b"), r'\1\2'), (re.compile(r"\b(gim)\s(me)\b"), r'\1\2'), (re.compile(r"\b(gon)\s(na)\b"), r'\1\2'), (re.compile(r"\b(got)\s(ta)\b"), r'\1\2'), (re.compile(r"\b(lem)\s(me)\b"), r'\1\2'), (re.compile(r"\b(mor)\s('n)\b"), r'\1\2'), (re.compile(r"\b(wan)\s(na)\b"), r'\1\2'), # Ending quotes (re.compile(r"([^' ]) ('ll|'re|'ve|n't)\b"), r"\1\2"), (re.compile(r"([^' ]) ('s|'m|'d)\b"), r"\1\2"), (re.compile(r'[ ]?”'), r'"'), # Double dashes (re.compile(r'[ ]?--[ ]?'), r'--'), # Parens and brackets (re.compile(r'([\[\(\{\<]) '), r'\1'), (re.compile(r' ([\]\)\}\>])'), r'\1'), (re.compile(r'([\]\)\}\>]) ([:;,.])'), r'\1\2'), # Punctuation (re.compile(r"([^']) ' "), r"\1' "), (re.compile(r' ([?!\.])'), r'\1'), (re.compile(r'([^\.])\s(\.)([\]\)}>"\']*)\s*$'), r'\1\2\3'), (re.compile(r'([#$]) '), r'\1'), (re.compile(r' ([;%:,])'), r'\1'), # Starting quotes (re.compile(r'(“)[ ]?'), r'"') ] text = ' '.join(tokens) for regexp, substitution in regexes: text = regexp.sub(substitution, text) return text.strip() @staticmethod def word_tokenize(text): # Basic word tokenizer based on the Penn Treebank tokenization script, but # setup to handle multiple sentences. Newline aware, ie newlines are # replaced with a specific token. You may want to consider using a more robust # tokenizer as a preprocessing step, and using the --pristine-input flag. regexes = [ # Starting quotes (re.compile(r'(\s)"'), r'\1 “ '), (re.compile(r'([ (\[{<])"'), r'\1 “ '), # Punctuation (re.compile(r'([:,])([^\d])'), r' \1 \2'), (re.compile(r'([:,])$'), r' \1 '), (re.compile(r'\.\.\.'), r' ... '), (re.compile(r'([;@#$%&])'), r' \1 '), (re.compile(r'([?!\.])'), r' \1 '), (re.compile(r"([^'])' "), r"\1 ' "), # Parens and brackets (re.compile(r'([\]\[\(\)\{\}\<\>])'), r' \1 '), # Double dashes (re.compile(r'--'), r' -- '), # Ending quotes (re.compile(r'"'), r' ” '), (re.compile(r"([^' ])('s|'m|'d) "), r"\1 \2 "), (re.compile(r"([^' ])('ll|'re|'ve|n't) "), r"\1 \2 "), # Contractions (re.compile(r"\b(can)(not)\b"), r' \1 \2 '), (re.compile(r"\b(d)('ye)\b"), r' \1 \2 '), (re.compile(r"\b(gim)(me)\b"), r' \1 \2 '), (re.compile(r"\b(gon)(na)\b"), r' \1 \2 '), (re.compile(r"\b(got)(ta)\b"), r' \1 \2 '), (re.compile(r"\b(lem)(me)\b"), r' \1 \2 '), (re.compile(r"\b(mor)('n)\b"), r' \1 \2 '), (re.compile(r"\b(wan)(na)\b"), r' \1 \2 '), # Newlines (re.compile(r'\n'), r' \\n ') ] text = " " + text + " " for regexp, substitution in regexes: text = regexp.sub(substitution, text) return text.split() def _create_sequences(vector, seq_length, seq_step): # Take strips of our vector at seq_step intervals up to our seq_length # and cut those strips into seq_length sequences passes = [] for offset in range(0, seq_length, seq_step): pass_samples = vector[offset:] num_pass_samples = pass_samples.size // seq_length pass_samples = np.resize(pass_samples, (num_pass_samples, seq_length)) passes.append(pass_samples) # Stack our sequences together. This will technically leave a few "breaks" # in our sequence chain where we've looped over are entire dataset and # return to the start, but with large datasets this should be neglegable return np.concatenate(passes) def shape_for_stateful_rnn(data, batch_size, seq_length, seq_step): """ Reformat our data vector into input and target sequences to feed into our RNN. Tricky with stateful RNNs. """ # Our target sequences are simply one timestep ahead of our input sequences. # eg with an input vector "wherefore"... # targets: herefore # predicts ^ ^ ^ ^ ^ ^ ^ ^ # inputs: wherefor inputs = data[:-1] targets = data[1:] # We split our long vectors into semi-redundant seq_length sequences inputs = _create_sequences(inputs, seq_length, seq_step) targets = _create_sequences(targets, seq_length, seq_step) # Make sure our sequences line up across batches for stateful RNNs inputs = _batch_sort_for_stateful_rnn(inputs, batch_size) targets = _batch_sort_for_stateful_rnn(targets, batch_size) # Our target data needs an extra axis to work with the sparse categorical # crossentropy loss function targets = targets[:, :, np.newaxis] return inputs, targets def _batch_sort_for_stateful_rnn(sequences, batch_size): # Now the tricky part, we need to reformat our data so the first # sequence in the nth batch picks up exactly where the first sequence # in the (n - 1)th batch left off, as the RNN cell state will not be # reset between batches in the stateful model. num_batches = sequences.shape[0] // batch_size num_samples = num_batches * batch_size reshuffled = np.zeros((num_samples, sequences.shape[1]), dtype=np.int32) for batch_index in range(batch_size): # Take a slice of num_batches consecutive samples slice_start = batch_index * num_batches slice_end = slice_start + num_batches index_slice = sequences[slice_start:slice_end, :] # Spread it across each of our batches in the same index position reshuffled[batch_index::batch_size, :] = index_slice return reshuffled def load_data(data_file, word_tokens, pristine_input, pristine_output, batch_size, seq_length=50, seq_step=25): global vectorizer try: with open(data_file, encoding='utf-8') as input_file: text = input_file.read() except FileNotFoundError: print("No input.txt in data_dir") sys.exit(1) skip_validate = True # try: # with open(os.path.join(data_dir, 'validate.txt'), encoding='utf-8') as validate_file: # text_val = validate_file.read() # skip_validate = False # except FileNotFoundError: # pass # Validation text optional # Find some good default seed string in our source text. # self.seeds = find_random_seeds(text) # Include our validation texts with our vectorizer all_text = text if skip_validate else '\n'.join([text, text_val]) vectorizer = Vectorizer(all_text, word_tokens, pristine_input, pristine_output) data = vectorizer.vectorize(text) x, y = shape_for_stateful_rnn(data, batch_size, seq_length, seq_step) print("Word_tokens:", word_tokens) print('x.shape:', x.shape) print('y.shape:', y.shape) if skip_validate: return x, y, None, None, vectorizer data_val = vectorizer.vectorize(text_val) x_val, y_val = shape_for_stateful_rnn(data_val, batch_size, seq_length, seq_step) print('x_val.shape:', x_val.shape) print('y_val.shape:', y_val.shape) return x, y, x_val, y_val, vectorizer def make_model(batch_size, vocab_size, embedding_size=64, rnn_size=128, num_layers=2): # Conversely if your data is large (more than about 2MB), feel confident to increase rnn_size and train a bigger model (see details of training below). # It will work significantly better. For example with 6MB you can easily go up to rnn_size 300 or even more. model = Sequential() model.add(Embedding(vocab_size, embedding_size, batch_input_shape=(batch_size, None))) for layer in range(num_layers): model.add(LSTM(rnn_size, stateful=True, return_sequences=True)) model.add(Dropout(0.2)) model.add(TimeDistributed(Dense(vocab_size, activation='softmax'))) model.compile(loss='sparse_categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy']) return model def train(model, x, y, x_val, y_val, batch_size, num_epochs): print('Training...') # print("Shape:", x.shape, y.shape) # print(num_epochs, batch_size, x[0], y[0]) train_start = time.time() validation_data = (x_val, y_val) if (x_val is not None) else None callbacks = None model.fit(x, y, validation_data=validation_data, batch_size=batch_size, shuffle=False, epochs=num_epochs, verbose=1, callbacks=callbacks) # self.update_sample_model_weights() train_end = time.time() print('Training time', train_end - train_start) def sample_preds(preds, temperature=1.0): """ Samples an unnormalized array of probabilities. Use temperature to flatten/amplify the probabilities. """ preds = np.asarray(preds).astype(np.float64) # Add a tiny positive number to avoid invalid log(0) preds += np.finfo(np.float64).tiny preds = np.log(preds) / temperature exp_preds = np.exp(preds) preds = exp_preds / np.sum(exp_preds) probas = np.random.multinomial(1, preds, 1) return np.argmax(probas) def generate(model, vectorizer, seed, length=100, diversity=0.5): seed_vector = vectorizer.vectorize(seed) # Feed in seed string print("Seed:", seed, end=' ' if vectorizer.word_tokens else '') model.reset_states() preds = None for char_index in np.nditer(seed_vector): preds = model.predict(np.array([[char_index]]), verbose=0) sampled_indices = [] # np.array([], dtype=np.int32) # Sample the model one token at a time for i in range(length): char_index = 0 if preds is not None: char_index = sample_preds(preds[0][0], diversity) sampled_indices.append(char_index) # = np.append(sampled_indices, char_index) preds = model.predict(np.array([[char_index]]), verbose=0) sample = vectorizer.unvectorize(sampled_indices) return sample if __name__ == "__main__": batch_size = 32 # Batch size for each train num_epochs = 10 # Number of epochs of training out_len = 200 # Length of the output phrase seq_length = 50 # 50 # Determines, how long phrases will be used for training use_words = False # Use words instead of characters (slower speed, bigger vocabulary) data_file = "text_habr.txt" # Source text file seed = "A" # Initial symbol of the text parser = argparse.ArgumentParser() parser.add_argument("-t", "--text", action="store", required=False, dest="text", help="Input text file") parser.add_argument("-e", "--epochs", action="store", required=False, dest="epochs", help="Number of training epochs") parser.add_argument("-p", "--phrase_len", action="store", required=False, dest="phrase_len", help="Phrase analyse length") parser.add_argument("-o", "--out_len", action="store", required=False, dest="out_len", help="Output text length") parser.add_argument("-g", "--generate", action="store_true", required=False, dest='generate', help="Generate output only without training") args = parser.parse_args() if args.text is not None: data_file = args.text if args.epochs is not None: num_epochs = int(args.epochs) if args.phrase_len is not None: seq_length = int(args.phrase_len) if args.out_len is not None: out_len = int(args.out_len) # Load text data pristine_input, pristine_output = False, False x, y, x_val, y_val, vectorizer = load_data(data_file, use_words, pristine_input, pristine_output, batch_size, seq_length) model_file = data_file.lower().replace('.txt', '.h5') if args.generate is False: # Make model model = make_model(batch_size, vectorizer.vocab_size) # Train model train(model, x, y, x_val, y_val, batch_size, num_epochs) # Save model to file model.save(filepath=model_file) model = keras.models.load_model(model_file) predict_model = make_model(1, vectorizer.vocab_size) predict_model.set_weights(model.get_weights()) # Generate phrases res = generate(predict_model, vectorizer, seed=seed, length=out_len) print(res) 


Acho que acabou sendo um gerador de texto funcional, muito útil para escrever artigos sobre o Habr . Particularmente interessante é o teste de textos grandes e um grande número de iterações de treinamento; se alguém tiver acesso a computadores rápidos, seria interessante ver os resultados.

Se alguém quiser estudar o tópico com mais detalhes, uma boa descrição do uso da RNN com exemplos detalhados pode ser encontrada em http://karpathy.imtqy.com/2015/05/05/21/rnn-effectiveness/ .

PS: E, finalmente, alguns versículos;) É interessante notar que tanto a formatação do texto quanto a adição de estrelas não foram feitas por mim, "é ela mesma". O próximo passo é interessante para verificar a possibilidade de pintar e compor músicas. Eu acho que as redes neurais são bastante promissoras aqui.

xxx

para alguns, ser pego em biscoitos - tudo de boa sorte em uma quadra de pão.
e à noite de tamaki
sob uma vela, pegue uma montanha.

xxx

logo filhos mons em petachas no bonde
a luz invisível cheira a alegria
é por isso que eu bato juntos cresce
você não ficará doente por causa de um desconhecido.

coração para arrancar em ogora escalonada
não é tão velho que o cereal está comendo,
Eu guardo a ponte para a bola para roubar.

da mesma maneira que Darina em Doba,
Eu ouço no meu coração de neve na minha mão.
nosso cantar branco quantas dumina suave
Afastei o volume de fera de minério.

xxx

veterinários crucificadores com um feitiço
e derramado sob o esquecido.
e você coloca, como nos galhos de Cuba
brilhe nele.
o divertido em zakoto
com o vôo do leite.

oh você é uma rosa, luz
nuvem de luz na mão:
e rolou no amanhecer
como vai meu cavaleiro!

ele está servindo à noite, não ao osso,
à noite em Tanya a luz azul
como uma espécie de tristeza.

E os últimos versos em aprender pelo modo palavra. Aqui a rima desapareceu, mas algum significado apareceu (?).

e você, da chama
as estrelas.
falou com indivíduos distantes.

preocupa você rus ,, você ,, amanhã.
"Pomba chuva,
e lar dos assassinos,
para a princesa menina
o rosto dele.

xxx

oh pastor, acene as câmaras
em um bosque na primavera.

Estou atravessando o coração da casa para a lagoa
e ratos alegres
Sinos de Nizhny Novgorod.

mas não temas, o vento da manhã,
com um caminho, com um taco de ferro,
e pensei com a ostra
abrigado em uma lagoa
em rakit empobrecido.

Source: https://habr.com/ru/post/pt470035/


All Articles