<jupyter_start><jupyter_text>RNN ile Metin Üretimi Bu dosya karakter temelli tahmin işlemi gerçekleştiren RNN modeli ile metin üretimine dair bir rehber niteliğindedir. Not: Dosyayı daha hızlı çalıştırabilmek için GPU Hızlandırıcısını açabilirsiniz. *Çalışma zamanı > Çalışma zamanı türünü değiştir > Donanım hızlandırıcı > GPU*. Veri setlerinde yer alan cümlelerin bazıları kurallı ve tamamlanmış iken bazıları yarım veya devrik olabilmektedir. Model kelimelerin anlamını öğrenmemektedir ancak şunları göz önünde bulundurmak faydalı olacaktır:* Model karakter temelli çalışmakta. Eğitim başladığında model, kelimeleri nasıl heceleyeceğini veya bu kelimelerin bir metnin parçası olup olmadığını bile bilmemekte.* Model, küçük (her biri 100 karakter) metin grupları (batch) ile eğitilmiş olsa da tutarlı bir yapıya sahip uzun metinler oluşturabilmekte.  Kurulum  Tensorflow'un ve Diğer Kütüphanelerin Import Edilmesi<jupyter_code>import tensorflow as tf
from tensorflow.keras.layers.experimental import preprocessing

import nltk
nltk.download('stopwords')
from nltk.corpus import stopwords

import urllib

import numpy as np
import os
import time<jupyter_output>[nltk_data] Downloading package stopwords to /root/nltk_data...
[nltk_data]   Package stopwords is already up-to-date!<jupyter_text>Veri Setinin İndirilmesi<jupyter_code>url = "https://raw.githubusercontent.com/ardauzunoglu/sanatkar.ai/main/data-sets/tirad_veri_setleri/kadin_tirad_veri_setleri/kadin_tirad_veri_seti.txt"

data_file = urllib.request.urlopen(url)
data = ""

for line in data_file:
  line = line.decode("utf-8")
  data += line
  data += "\n"

text = data
print(text)<jupyter_output><empty_output><jupyter_text>Verinin OkunmasıMetne göz atalım:<jupyter_code>#Metindeki ilk 250 karaktere bakalım.
print(text[:250])
#Metindeki eşsiz karakter sayısı.
vocab = sorted(set(text))
print(f'{len(vocab)} eşsiz karakter')<jupyter_output>90 unique characters<jupyter_text>Metnin İşlenmesi  Metnin Vektörize EdilmesiEğitime başlamadan önce karakter tabanlı değerleri (string) sayısal bir gösterime dönüştürmeliyiz. `preprocessing.StringLookup` katmanı her karakteri sayısal bir kimliğe dönüştürebilir, ancak bunun için önce metnin belirteçlere bölünmesi gerekiyor.<jupyter_code>example_texts = ['abcdefg', 'xyz']

chars = tf.strings.unicode_split(example_texts, input_encoding='UTF-8')
chars<jupyter_output><empty_output><jupyter_text>`preprocessing.StringLookup` katmanını oluşturalım:<jupyter_code>ids_from_chars = preprocessing.StringLookup(
    vocabulary=list(vocab), mask_token=None)<jupyter_output><empty_output><jupyter_text>Bu katman her bir tokeni karakter kimliğine dönüştürmektedir:<jupyter_code>ids = ids_from_chars(chars)
ids<jupyter_output><empty_output><jupyter_text>Not: Bu aşamada `sorted(set(text))` ile üretilen kelime listesini kullanmak yerine `preprocessing.StringLookup` katmanının `get_vocabulary()` fonksiyonunu kullanıyoruz ki `[UNK]`* tokenleri de aynı şekilde ayarlanabilsin.*`[UNK]`, tokenize etme işlemi sırasında oluşmuş bilinmeyen kelimelerin yerini tutan bir değerdir.<jupyter_code>chars_from_ids = tf.keras.layers.experimental.preprocessing.StringLookup(
    vocabulary=ids_from_chars.get_vocabulary(), invert=True, mask_token=None)<jupyter_output><empty_output><jupyter_text>Bu katman, karakterleri kimlik vektörlerinden kurtarır ve onları `tf.RaggedTensor` karakterleri olarak döndürür:<jupyter_code>chars = chars_from_ids(ids)
chars<jupyter_output><empty_output><jupyter_text>Karakterleri tekrar karakter tabanlı değerlere çevirmek için `tf.strings.reduce_join` kullanılabilir.<jupyter_code>tf.strings.reduce_join(chars, axis=-1).numpy()
def text_from_ids(ids):
  return tf.strings.reduce_join(chars_from_ids(ids), axis=-1)<jupyter_output><empty_output><jupyter_text><NAME> Bir karakter veya bir karakter dizisi verildiğinde, onları takip edecek en olası karakter nedir?  Bu soru, modeli gerçekleştirmesi için eğittimiz görevdir. Modele verilecek girdi bir karakter dizisi olacaktır ve model her time step'te bir sonraki karakteri tahmin edecek şekilde çıktıyı oluşturacaktır.  Eğitim Örneklerinin ve Hedeflerin OluşturulmasıMetni her bir giriş dizisi `seq_length` içerecek şekilde örnek dizilere bölüyoruz.Her giriş dizisi için, bir karakter sağa kaydırılanlar dışında, karşılık gelen hedefler aynı uzunlukta metin içerir.Bu yüzden metni 'seq_length+1' parçalarına ayırıyoruz. Örneğin, "seq_length" 4 karakter uzunluğunda ve metnimiz "Naber" diyelim. Giriş dizisi "Nabe" ve hedef dizi "aber" olacaktır.Bunu yapabilmek adına metin vektörünü bir karakter indeksleri akışına dönüştürmek için `tf.data.Dataset.from_tensor_slices` fonksiyonunu kullanıyoruz.<jupyter_code>all_ids = ids_from_chars(tf.strings.unicode_split(text, 'UTF-8'))
all_ids
ids_dataset = tf.data.Dataset.from_tensor_slices(all_ids)
for ids in ids_dataset.take(10):
    print(chars_from_ids(ids).numpy().decode('utf-8'))
seq_length = 100
examples_per_epoch = len(text)//(seq_length+1)<jupyter_output><empty_output><jupyter_text>`batch`, bu tekil karakterleri istediğimiz boyuttaki dizilere kolayca dönüştürmemize olanak tanımaktadır.<jupyter_code>sequences = ids_dataset.batch(seq_length+1, drop_remainder=True)

for seq in sequences.take(1):
  print(chars_from_ids(seq))<jupyter_output>tf.Tensor(
[b'A' b'm' b'a' b'n' b' ' b'y' b'a' b'r' b'a' b'b' b'b' b'i' b'm' b'.'
 b'.' b'.' b' ' b'K' b'a' b'r' b'a' b'r' b' ' b'v' b'e' b'r' b'm' b'e'
 b'k' b' ' b'n' b'e' b' ' b'g' b'\xc3\xbc' b'\xc3\xa7' b' ' b'\xc5\x9f'
 b'e' b'y' b'm' b'i' b'\xc5\x9f' b'.' b'.' b'.' b' ' b'B' b'i' b'r' b' '
 b'k' b'i' b'\xc5\x9f' b'i' b',' b' ' b'i' b'k' b'i' b' ' b'k' b'i'
 b'\xc5\x9f' b'i' b' ' b'o' b'l' b's' b'a' b' ' b'n' b'e' b' ' b'i' b's'
 b'e' b'.' b'.' b'.' b' ' b'A' b'm' b'a' b' ' b'd' b'\xc3\xb6' b'r' b't'
 b' ' b'k' b'i' b'\xc5\x9f' b'i' b'.' b'.' b'.' b' ' b'G' b'e' b'l'], shape=(101,), dtype=string)<jupyter_text>Bu işlemi daha rahat anlamak için tokenleri tekrar karakter tabanlı değerlere dönüştürebiliriz:<jupyter_code>for seq in sequences.take(5):
  print(text_from_ids(seq).numpy())<jupyter_output>b'<NAME>... Karar vermek ne g\xc3\xbc\xc3\xa7 \xc5\x9feymi\xc5\x9f... Bir ki\xc5\x9fi, iki ki\xc5\x9fi olsa ne ise... Ama d\xc3\xb6rt ki\xc5\x9fi... Gel'
b' de birini se\xc3\xa7. Nikanor \xc4\xb0vanovi\xc3\xa7 biraz zay\xc4\xb1f ama hi\xc3\xa7 de fena de\xc4\x9fil. \xc4\xb0van Kuzmi\xc3\xa7 de fena de\xc4\x9fil. A\xc3\xa7\xc4\xb1k k'
b'onu\xc5\x9fmak gerekirse, \xc4\xb0van Pavlovi\xc3\xa7 de biraz \xc5\x9fi\xc5\x9fman ama, pekala g\xc3\xb6steri\xc5\x9fli bir erkek. S\xc3\xb6yleyin bana ne y'
b'apay\xc4\xb1m? Baltazar Baltazarovi\xc3\xa7 de de\xc4\x9ferli bir adam. Ah ne zor \xc5\x9fey bu karar vermek... Anlatamam, anlata'
b"mam. Nikonor \xc4\xb0vanovi\xc3\xa7'in dudaklar\xc4\xb1n\xc4\xb1, \xc4\xb0van Kuzmi\xc3\xa7'in burnunu alsak... Baltazar Baltazarovi\xc3\xa7'in de hal"<jupyter_text>Eğitim için birer dizi olan girdi ve etiket değerlerinden oluşan `(girdi, etiket)` çiftlerini içeren bir veri setine ihtiyacımız var.Girdi olarak bir dizi alan ve her time step için girdiyi ve etiketi hizalayan bir fonksiyon tanımlayabiliriz:<jupyter_code>def split_input_target(sequence):
    input_text = sequence[:-1]
    target_text = sequence[1:]
    return input_text, target_text
split_input_target(list("Tensorflow"))
dataset = sequences.map(split_input_target)
for input_example, target_example in dataset.take(1):
    print("Input :", text_from_ids(input_example).numpy())
    print("Target:", text_from_ids(target_example).numpy())<jupyter_output>Input : b'<NAME>... Karar vermek ne g\xc3\xbc\xc3\xa7 \xc5\x9feymi\xc5\x9f... Bir ki\xc5\x9fi, iki ki\xc5\x9fi olsa ne ise... Ama d\xc3\xb6rt ki\xc5\x9fi... Ge'
Target: b'<NAME>... Karar vermek ne g\xc3\xbc\xc3\xa7 \xc5\x9feymi\xc5\x9f... Bir ki\xc5\x9fi, iki ki\xc5\x9fi olsa ne ise... Ama d\xc3\xb6rt ki\xc5\x9fi... Gel'<jupyter_text>Eğitim için Batch OluşturmakMetni daha kolay kullanılabilir dizilere bölmek için `tf.data` kullandık. Ancak bu verileri modele beslemeden önce verileri karıştırmamız ve yığınlar halinde paketlememiz gerekir.<jupyter_code>BATCH_SIZE = 64

#Veri setini karmak için kullanılan buffer boyutu
BUFFER_SIZE = 10000

dataset = (
    dataset
    .shuffle(BUFFER_SIZE)
    .batch(BATCH_SIZE, drop_remainder=True)
    .prefetch(tf.data.experimental.AUTOTUNE))

dataset<jupyter_output><empty_output><jupyter_text>Modeli Geliştirmek Bu kısım modeli bir `keras.Model` alt sınıfı olarak tanımlamaktadır.Bu model üç katmana sahiptir:* `tf.keras.layers.Embedding`: Girdi katmanı. Her karakter kimliğini "embedding_dim" boyutlarına sahip bir vektörle eşleyecek, eğitilebilir bir arama tablosu.* `tf.keras.layers.GRU`: `units=rnn_units` boyutuna sahip bir RNN türü (Burada alternatif olarak LSTM katmanı da kullanabilir.)* `tf.keras.layers.Dense`: Çıktı katmanı. Sözlükteki her karakter için bir logit çıktısı verir. Bu çıktılar, modele göre her karakterin log-olasılığıdır.<jupyter_code>#Kelime listesi uzunluğu
vocab_size = len(vocab)

#Embedding boyutu
embedding_dim = 256

#RNN ünitelerinin sayısı
rnn_units = 1024
class MyModel(tf.keras.Model):
  def __init__(self, vocab_size, embedding_dim, rnn_units):
    super().__init__(self)
    self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
    self.gru = tf.keras.layers.GRU(rnn_units,
                                   return_sequences=True,
                                   return_state=True)
    self.dense = tf.keras.layers.Dense(vocab_size)

  def call(self, inputs, states=None, return_state=False, training=False):
    x = inputs
    x = self.embedding(x, training=training)
    if states is None:
      states = self.gru.get_initial_state(x)
    x, states = self.gru(x, initial_state=states, training=training)
    x = self.dense(x, training=training)

    if return_state:
      return x, states
    else:
      return x
model = MyModel(
    vocab_size=len(ids_from_chars.get_vocabulary()),
    embedding_dim=embedding_dim,
    rnn_units=rnn_units)<jupyter_output><empty_output><jupyter_text>Model, her bir karakter için embedding'i arar, girdi olarak embedding'i kullanarak bir kez GRU'yu çalıştırır ve bir sonraki karakterin log-olasılığını tahmin eden logitler oluşturmak için dense katmanı uygular:![](https://github.com/tensorflow/text/blob/master/docs/tutorials/images/text_generation_training.png?raw=1)  Modeli DenemekŞimdi beklediğimiz gibi davranıp davranmadığını görmek için modeli çalıştıralım.İlk önce çıktının şeklini kontrol edelim:<jupyter_code>for input_example_batch, target_example_batch in dataset.take(1):
    example_batch_predictions = model(input_example_batch)
    print(example_batch_predictions.shape, "# (batch_size, sequence_length, vocab_size)")<jupyter_output>(64, 100, 91) # (batch_size, sequence_length, vocab_size)<jupyter_text>Yukarıdaki örnekte, girdinin dizi uzunluğu `100`'dür, ancak model herhangi bir uzunluktaki girdiler üzerinde çalıştırılabilir:<jupyter_code>model.summary()<jupyter_output>Model: "my_model_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_2 (Embedding)      multiple                  23296     
_________________________________________________________________
gru_2 (GRU)                  multiple                  3938304   
_________________________________________________________________
dense_2 (Dense)              multiple                  93275     
=================================================================
Total params: 4,054,875
Trainable params: 4,054,875
Non-trainable params: 0
_________________________________________________________________<jupyter_text>Modelden gerçek tahminler almak için, gerçek karakter indekslerini elde etmek için çıktı dağılımından örneklememiz gerekir. Bu dağılım, karakter sözlüğü üzerindeki logitlerle tanımlanır.Not: Bu dağıtımdan _örnek_ almak önemlidir, çünkü dağıtımın _argmax_ değerini almak modeli bir döngüde kolayca sıkıştırabilir.Batch'deki ilk örnek için deneyelim:<jupyter_code>sampled_indices = tf.random.categorical(example_batch_predictions[0], num_samples=1)
sampled_indices = tf.squeeze(sampled_indices, axis=-1).numpy()<jupyter_output><empty_output><jupyter_text>Bu bize her time stepte bir sonraki karakter indeksinin bir tahminini verir. Eğitimsiz model tarafından tahmin edilen metni görmek için bunları decode edelim:<jupyter_code>print("Input:\n", text_from_ids(input_example_batch[0]).numpy())
print()
print("Next Char Predictions:\n", text_from_ids(sampled_indices).numpy())<jupyter_output>Input:
 b'\xc4\xb1n yadigar\xc4\xb1. Etimi yese de kemiklerimi saklar... \xc4\xb0nsan ya\xc5\x9fad\xc4\xb1\xc4\x9f\xc4\xb1 yere benzer... \xc5\x9eu gen\xc3\xa7 ya\xc5\x9f\xc4\xb1nda y\xc3\xbcz\xc3\xbcn'

Next Char Predictions:
 b"tseNzZdm'\xc3\xbcr1L\xe2\x80\xa6z;nGV\xc3\xa2f9I\xc5\x9e))\xc4\x9fFuPnCu\xc3\x9c'r7\xc3\xa7\xe2\x80\x9c\xc4\x9f-f6z0vd1J(\xc3\xa2b\xc4\x9e\xc4\xb0\xe2\x80\x9d\xc3\xa2m2e\xe2\x80\x9c7m0tI\nikVPwv'tzTZp\xc4\xb0hwUHRZ\xc3\x9c/WY/L\xc4\xb1nfY k\xc4\xb0ZH"<jupyter_text>Modeli Eğitmek Bu noktada problem standart bir sınıflandırma problemi olarak ele alınabilir. Önceki RNN durumu ve bu adımdaki girdi göz önüne alındığında, bir sonraki karakterin sınıfı tahmin edilmektedir.  Optimizer ve Loss Fonksiyonu Eklemek Standart `tf.keras.losses.sparse_categorical_crossentropy` loss fonksiyonu, bu örnekte kullanmak için uygundur.Modelimiz logit döndürdüğünden, "from_logits" parametresini ayarlamamız gerekir.<jupyter_code>loss = tf.losses.SparseCategoricalCrossentropy(from_logits=True)
example_batch_loss = loss(target_example_batch, example_batch_predictions)
mean_loss = example_batch_loss.numpy().mean()
print("Prediction shape: ", example_batch_predictions.shape, " # (batch_size, sequence_length, vocab_size)")
print("Mean loss:        ", mean_loss)<jupyter_output>Prediction shape:  (64, 100, 91)  # (batch_size, sequence_length, vocab_size)
Mean loss:         4.511179<jupyter_text>Yeni başlatılan bir model kendinden çok da emin olmamalıdır, çıktı logitlerinin tümü benzer büyüklüklere sahip olmalıdır. Bunu doğrulamak için ortalama kaybın üstel değerinin yaklaşık olarak kelime boyutuna eşit olduğunu kontrol edebiliriz. Çok daha yüksek bir kayıp, modelin yanlış cevaplarından emin olduğu ve kötü bir şekilde başlatıldığı anlamına gelir:<jupyter_code>tf.exp(mean_loss).numpy()<jupyter_output><empty_output><jupyter_text>`tf.keras.Model.compile` fonksiyonunu kullanarak eğitim prosedürünü yapılandıralım. Varsayılan argümanlarla ve loss fonksiyonuyla `tf.keras.optimizers.Adam` kullanabiliriz.<jupyter_code>model.compile(optimizer='adam', loss=loss, metrics=['accuracy'])<jupyter_output><empty_output><jupyter_text>Kontrol Noktalarını Yapılandırmak Eğitim sırasında kontrol noktalarının kaydedildiğinden emin olmak için bir "tf.keras.callbacks.ModelCheckpoint" kullanabiliriz:<jupyter_code>#Kontrol noktalarının kaydedileceği konum
checkpoint_dir = './training_checkpoints'

#Kontrol noktalarının adlandırılması
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt_{epoch}")

checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
    filepath=checkpoint_prefix,
    save_weights_only=True)<jupyter_output><empty_output><jupyter_text>Eğitimi Gerçekleştirmek<jupyter_code>EPOCHS = 50
history = model.fit(dataset, epochs=EPOCHS, callbacks=[checkpoint_callback])<jupyter_output><empty_output><jupyter_text>Metin Üretmek Bu modelle metin oluşturmanın en basit yolu, onu bir döngüde çalıştırmak ve çalıştırırken modelin dahili durumunu takip etmektir.![](https://github.com/tensorflow/text/blob/master/docs/tutorials/images/text_generation_sampling.png?raw=1) Aşağıdaki sınıf tek adımlı tahmin yapmamıza olanak sağlamaktadır:<jupyter_code>class OneStep(tf.keras.Model):
  def __init__(self, model, chars_from_ids, ids_from_chars, temperature=1):
    super().__init__()
    self.temperature = temperature
    self.model = model
    self.chars_from_ids = chars_from_ids
    self.ids_from_chars = ids_from_chars

    #"[UNK]"'lerin oluşmasınının engellenmesi
    skip_ids = self.ids_from_chars(['[UNK]'])[:, None]
    sparse_mask = tf.SparseTensor(
        
        values=[-float('inf')]*len(skip_ids),
        indices=skip_ids,
        
        dense_shape=[len(ids_from_chars.get_vocabulary())])
    self.prediction_mask = tf.sparse.to_dense(sparse_mask)

  @tf.function
  def generate_one_step(self, inputs, states=None):
    #Stringlerin token ID'lere dönüşmesi
    input_chars = tf.strings.unicode_split(inputs, 'UTF-8')
    input_ids = self.ids_from_chars(input_chars).to_tensor()

    #Modelin çalıştırılması
    predicted_logits, states = self.model(inputs=input_ids, states=states,
                                          return_state=True)
    #Yalnızca son tahmini kullanıyoruz
    predicted_logits = predicted_logits[:, -1, :]
    predicted_logits = predicted_logits/self.temperature
    predicted_logits = predicted_logits + self.prediction_mask

    #Token ID'si oluşturmak için çıktı logitlerini örnekliyoruz
    predicted_ids = tf.random.categorical(predicted_logits, num_samples=1)
    predicted_ids = tf.squeeze(predicted_ids, axis=-1)

    #Token ID'lerin karaktere dönüştürülmesi
    predicted_chars = self.chars_from_ids(predicted_ids)

    #Karakterin ve model durumunun döndürülmesi
    return predicted_chars, states
one_step_model = OneStep(model, chars_from_ids, ids_from_chars)<jupyter_output><empty_output><jupyter_text>Metin oluşturmak için bir döngüde çalıştıralım. Oluşturulan metne baktığınızda, modelin ne zaman büyük harf kullanacağını, paragraflar oluşturacağını, noktalama işareti kullanacağını bildiğini görebiliriz. Az sayıda epoch ile henüz tutarlı cümleler kurmayı öğrenememiş olabilir.<jupyter_code>start = time.time()
states = None

seed = input("Başlangıç dizesi: ")
length = int(input("Karakter sayısı: "))

next_char = tf.constant([seed])
result = [next_char]

for n in range(length):
  next_char, states = one_step_model.generate_one_step(next_char, states=states)
  result.append(next_char)

result = tf.strings.join(result)
end = time.time()
print(result[0].numpy().decode('utf-8'), '\n\n' + '_'*80)
print('\nRun time:', end - start)<jupyter_output>Başlangıç dizesi: Hapishanede yaşarken hayat çok daha ilgi çekiciydi.
Karakter sayısı: 200
Hapishanede yaşarken hayat çok daha ilgi çekiciydi. Kocam çok için başına bana varik bana iteliyor! İşe kesece bu kadarımı bağışlayam da olur. Sancaklar, borazanlar, askerler yanı başımdan geçip gittir soynuma beni sevdikleri gibi severlerdi! Sonra bi 

________________________________________________________________________________

Run time: 2.8362057209014893<jupyter_text>Sonuçları iyileştirmek için yapabileceğimiz en kolay şey, onu daha uzun süre eğitmektir (`EPOCHS = 10` deneyebiliriz).Ayrıca farklı bir başlangıç dizisi deneyebilir, modelin doğruluğunu artırmak için başka bir RNN katmanı ekleyebilir veya daha fazla veya daha az rastgele tahminler oluşturmak için temperature parametresini ayarlayabiliriz.  Modeli KaydetmekBu model kolayca kaydedilebilir ve geri yüklenebilir, bu da onu `tf.saved_model` kabul edilen her yerde kullanmamıza olanak tanır.<jupyter_code>tf.saved_model.save(one_step_model, 'kadin_tirad_generator')
one_step_reloaded = tf.saved_model.load('kadin_tirad_generator')<jupyter_output>WARNING:tensorflow:Skipping full serialization of Keras layer <__main__.OneStep object at 0x7fe573325350>, because it is not built.
{'fraction_non_alphanumeric': 0.10373145819432325, 'fraction_numerical': 0.018323666786429195, 'mean_word_length': 5.829302488608482, 'pattern_counts': {'":': 0, '<': 103, '<?xml version=': 0, '>': 106, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 11, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '27', 'dir': 'jupyter-structured-clean-dedup', 'id': '397826', 'license': 'MIT', 'n_tokens_mistral': 8500, 'n_tokens_neox': 8047, 'n_words': 1561, 'path': 'creating_text_generators.ipynb', 'repo_name': 'ardauzunoglu/sanatkar.ai', 'size': '19510'}