diff --git a/notebooks/lm_intro/Language_model_intro.ipynb b/notebooks/lm_intro/Language_model_intro.ipynb
index b0d813b..f50c209 100644
--- a/notebooks/lm_intro/Language_model_intro.ipynb
+++ b/notebooks/lm_intro/Language_model_intro.ipynb
@@ -23,16 +23,27 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 3,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Requirement already satisfied: razdel in /Users/mnefedov/.pyenv/versions/3.10.9/lib/python3.10/site-packages (0.5.0)\n",
+ "\n",
+ "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip available: \u001b[0m\u001b[31;49m22.3.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.3.1\u001b[0m\n",
+ "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n"
+ ]
+ }
+ ],
"source": [
"# !pip install razdel"
]
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
@@ -53,7 +64,7 @@
},
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
@@ -71,7 +82,7 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": 6,
"metadata": {},
"outputs": [
{
@@ -97,7 +108,7 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
@@ -117,7 +128,7 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
@@ -127,7 +138,7 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 10,
"metadata": {},
"outputs": [
{
@@ -153,7 +164,7 @@
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 11,
"metadata": {},
"outputs": [
{
@@ -179,7 +190,7 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
@@ -188,7 +199,7 @@
},
{
"cell_type": "code",
- "execution_count": 9,
+ "execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
@@ -198,7 +209,7 @@
},
{
"cell_type": "code",
- "execution_count": 10,
+ "execution_count": 14,
"metadata": {},
"outputs": [
{
@@ -216,7 +227,7 @@
" ('ты', 15469)]"
]
},
- "execution_count": 10,
+ "execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
@@ -227,7 +238,7 @@
},
{
"cell_type": "code",
- "execution_count": 11,
+ "execution_count": 15,
"metadata": {},
"outputs": [
{
@@ -245,7 +256,7 @@
" ('как', 7514)]"
]
},
- "execution_count": 11,
+ "execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
@@ -263,7 +274,7 @@
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": 16,
"metadata": {},
"outputs": [
{
@@ -272,7 +283,7 @@
"0"
]
},
- "execution_count": 12,
+ "execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
@@ -283,7 +294,7 @@
},
{
"cell_type": "code",
- "execution_count": 13,
+ "execution_count": 17,
"metadata": {},
"outputs": [
{
@@ -311,7 +322,7 @@
" ('если', 0.004209385881531474)]"
]
},
- "execution_count": 13,
+ "execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
@@ -323,7 +334,7 @@
},
{
"cell_type": "code",
- "execution_count": 14,
+ "execution_count": 18,
"metadata": {},
"outputs": [
{
@@ -351,7 +362,7 @@
" ('также', 0.002716184007188258)]"
]
},
- "execution_count": 14,
+ "execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
@@ -367,7 +378,7 @@
"source": [
"Эти вероятности уже можно использовать, чтобы ответить на вопрос - это предложение больше подходит для новостей или для анонимного форума?\n",
"\n",
- "В теории вероятностей для того, чтобы найти общую вероятность нескольких независимых событий произойти одновременно, нужно перемножить вероятности отдельных событий. В нашем случае мы хотим найти вероятность получить данное предложение. Для этого мы перемножаем вероятности слов в этом предложении. \n",
+ "В теории вероятностей для того, чтобы найти общую вероятность нескольких независимых событий произойти одновременно, нужно перемножить вероятности отдельных событий. В нашем случае мы хотим найти вероятность получить данное предложение. Для этого мы можем перемножить вероятности слов в этом предложении (можно представить, что мы подбрасываем кубик с количеством сторон равным количеству слов в словаре) \n",
"\n",
"(Если бы мы сложили вероятности, то мы бы получили вероятность выбрать из корпуса 1 из слов в данном предложении)"
]
@@ -381,7 +392,7 @@
},
{
"cell_type": "code",
- "execution_count": 15,
+ "execution_count": 37,
"metadata": {},
"outputs": [],
"source": [
@@ -398,7 +409,7 @@
},
{
"cell_type": "code",
- "execution_count": 16,
+ "execution_count": 38,
"metadata": {},
"outputs": [],
"source": [
@@ -414,7 +425,7 @@
},
{
"cell_type": "code",
- "execution_count": 17,
+ "execution_count": 39,
"metadata": {},
"outputs": [
{
@@ -423,7 +434,7 @@
"3.8958314050721132e-50"
]
},
- "execution_count": 17,
+ "execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
@@ -434,7 +445,7 @@
},
{
"cell_type": "code",
- "execution_count": 18,
+ "execution_count": 40,
"metadata": {},
"outputs": [
{
@@ -443,7 +454,7 @@
"4.573351371331133e-45"
]
},
- "execution_count": 18,
+ "execution_count": 40,
"metadata": {},
"output_type": "execute_result"
}
@@ -461,7 +472,7 @@
},
{
"cell_type": "code",
- "execution_count": 19,
+ "execution_count": 41,
"metadata": {},
"outputs": [
{
@@ -470,7 +481,7 @@
"True"
]
},
- "execution_count": 19,
+ "execution_count": 41,
"metadata": {},
"output_type": "execute_result"
}
@@ -488,7 +499,7 @@
},
{
"cell_type": "code",
- "execution_count": 20,
+ "execution_count": 42,
"metadata": {},
"outputs": [
{
@@ -497,13 +508,13 @@
"False"
]
},
- "execution_count": 20,
+ "execution_count": 42,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
- "phrase = 'Безграмотное быдло с дубляжом, войсовером, порнографией и котикам'\n",
+ "phrase = 'Расчитаем вероятность встретить такой текст в каждом из корпусов'\n",
"compute_joint_proba(phrase, probas_news) > compute_joint_proba(phrase, probas_dvach)"
]
},
@@ -525,40 +536,23 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Такие события можно оценивать по формуле полной вероятности:"
+ "Такие события нужно оценивать по формуле полной вероятности:"
]
},
{
- "cell_type": "code",
- "execution_count": 21,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- ""
- ],
- "text/plain": [
- ""
- ]
- },
- "execution_count": 21,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
"source": [
- "Image(url=\"https://i.ibb.co/sC7CKzQ/image.png\",\n",
- " width=500, height=500)"
+ "![](https://i.ibb.co/sC7CKzQ/image.png)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "А если простыми словами, то для того, чтобы получить вероятность предложения, нужно перемножить вероятность первого слова, вероятность второго слова, при условии первого, вероятность третьего при условии первого и второго, вероятность четвертого слова, при условии первого, второго и третьего и так далее до вероятности последнего слова при условии всех предшешевствующих.\n",
+ "А если простыми словами, то для того, чтобы получить вероятность предложения, нужно перемножить вероятность первого слова, вероятность второго слова, при условии первого, вероятность третьего при условии первого и второго, вероятность четвертого слова, при условии первого, второго и третьего и так далее до вероятности последнего слова при условии всех предшествующих.\n",
"\n",
- "Условные вероятности для слов можно также вычислить по частотностям. Вероятность слова А при условии слова Б равна отношению количества раз, которое встретились слова А и Б вместе, к количеству раз, которое встретилось слово Б. Вероятность слова В при условии А и Б равна отношению количества раз, которое встретились слова А,Б и В вместе к количеству раз, которое встретились слова А и Б.\n",
+ "Условные вероятности для слов можно также вычислить по частотностям. Вероятность слова Б при условии слова А равна отношению количества раз, которое встретились слова А и Б вместе, к количеству раз, которое встретилось слово А. Вероятность слова В при условии А и Б равна отношению количества раз, которое встретились слова А,Б и В вместе к количеству раз, которое встретились слова А и Б.\n",
"И так далее. "
]
},
@@ -585,7 +579,7 @@
},
{
"cell_type": "code",
- "execution_count": 22,
+ "execution_count": 43,
"metadata": {},
"outputs": [],
"source": [
@@ -601,19 +595,17 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "Вероятность первого слово можно по идее считать просто как вероятность униграмма, но можно сделать небольшое добавление в нашу модель - поставить в начала каждого предложения технический токен начала предложения, а вероятность первого слова рассчитывать как вероятность биграма старт-первое слово поделить на частоту старта. Дальше мы будем генерировать текст с помощью языковой модели и это поможет нам генерировать более красивые предложения.\n",
+ "Вероятность первого слово можно по идее считать просто как вероятность униграмма, но можно сделать небольшое добавление в нашу модель - поставить в начала каждого предложения технический токен начала предложения ``, а вероятность первого слова рассчитывать как вероятность биграма ``-первое слово поделить на частоту ``. Дальше мы будем генерировать текст с помощью языковой модели и это поможет нам генерировать более красивые предложения.\n",
"\n",
"\n",
- "Для того, чтобы у нас получились честные вероятности и можно было посчитать вероятность первого слова, нам нужно добавить тэг маркирующий начало предложений \\< start \\>\n",
- "\n",
- "Дальше мы попробуем сгенерировать текст, используя эти вероятности, и нам нужно будет когда-то остановится. Для этого добавим тэг окончания \\< end \\>\n",
+ "Дальше мы попробуем сгенерировать текст, используя эти вероятности, и нам нужно будет когда-то остановится. Для этого добавим тэг окончания ``\n",
"\n",
"Ну и поделим все на предложения"
]
},
{
"cell_type": "code",
- "execution_count": 23,
+ "execution_count": 44,
"metadata": {},
"outputs": [],
"source": [
@@ -623,7 +615,7 @@
},
{
"cell_type": "code",
- "execution_count": 24,
+ "execution_count": 45,
"metadata": {},
"outputs": [],
"source": [
@@ -652,16 +644,17 @@
},
{
"cell_type": "code",
- "execution_count": 25,
+ "execution_count": 46,
"metadata": {},
"outputs": [],
"source": [
"def compute_joint_proba_markov_assumption(text, word_counts, bigram_counts):\n",
" prob = 0\n",
- " for ngram in ngrammer([''] + normalize(phrase) + ['']):\n",
+ " for ngram in ngrammer([''] + normalize(text) + ['']):\n",
" word1, word2 = ngram.split()\n",
" if word1 in word_counts and ngram in bigram_counts:\n",
" prob += np.log(bigram_counts[ngram]/word_counts[word1])\n",
+ " # small value for unk words\n",
" else:\n",
" prob += np.log(2e-5)\n",
" \n",
@@ -670,7 +663,7 @@
},
{
"cell_type": "code",
- "execution_count": 26,
+ "execution_count": 47,
"metadata": {},
"outputs": [
{
@@ -679,14 +672,14 @@
"True"
]
},
- "execution_count": 26,
+ "execution_count": 47,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Эта фраза более вероятна в корпусе двача\n",
- "phrase = 'Безграмотное быдло с дубляжом, войсовером, порнографией и котикам'\n",
+ "phrase = 'Расчитаем вероятность встретить такой текст в каждом из корпусов'\n",
"\n",
"compute_joint_proba_markov_assumption(phrase, unigrams_dvach, bigrams_dvach) > \\\n",
"compute_joint_proba_markov_assumption(phrase, unigrams_news, bigrams_news)"
@@ -694,7 +687,7 @@
},
{
"cell_type": "code",
- "execution_count": 27,
+ "execution_count": 48,
"metadata": {},
"outputs": [
{
@@ -703,7 +696,7 @@
"False"
]
},
- "execution_count": 27,
+ "execution_count": 48,
"metadata": {},
"output_type": "execute_result"
}
@@ -734,7 +727,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "В генерации мы можем выбирать только из уже известных слов. Можно заранее расчитать все вероятности и сохранить их в матрицу. Размерность матрицы слова на слова. В каждой ячееке будет лежать вероятность получить слово б, после слова а. Слово а будет в строке, а б в колонке."
+ "В генерации мы можем выбирать только из уже известных слов. Можно заранее расчитать все вероятности и сохранить их в матрицу. Размерность матрицы слова на слова. В каждой ячееке будет лежать вероятность получить слово б, после слова а. Слово `а` будет в строке, а `б` в колонке."
]
},
{
@@ -746,7 +739,7 @@
},
{
"cell_type": "code",
- "execution_count": 45,
+ "execution_count": 52,
"metadata": {},
"outputs": [],
"source": [
@@ -755,13 +748,13 @@
},
{
"cell_type": "code",
- "execution_count": 52,
+ "execution_count": 74,
"metadata": {},
"outputs": [],
"source": [
"# матрица слова на слова (инициализируем нулями)\n",
"matrix_dvach = lil_matrix((len(unigrams_dvach), \n",
- " len(unigrams_dvach)))\n",
+ " len(unigrams_dvach)))\n",
"\n",
"# к матрице нужно обращаться по индексам\n",
"# поэтому зафиксируем порядок слов в словаре и сделаем маппинг id-слово и слово-id\n",
@@ -775,12 +768,12 @@
" matrix_dvach[word2id_dvach[word1], word2id_dvach[word2]] = (bigrams_dvach[ngram]/\n",
" unigrams_dvach[word1])\n",
" \n",
- "# matrix_dvach = csc_matrix(matrix_dvach)"
+ "matrix_dvach = csc_matrix(matrix_dvach)"
]
},
{
"cell_type": "code",
- "execution_count": 53,
+ "execution_count": 75,
"metadata": {},
"outputs": [],
"source": [
@@ -798,7 +791,7 @@
" matrix_news[word2id_news[word1], word2id_news[word2]] = (bigrams_news[ngram]/\n",
" unigrams_news[word1])\n",
" \n",
- "# matrix_news = csc_matrix(matrix_news)"
+ "matrix_news = csc_matrix(matrix_news)"
]
},
{
@@ -810,7 +803,7 @@
},
{
"cell_type": "code",
- "execution_count": 135,
+ "execution_count": 80,
"metadata": {},
"outputs": [],
"source": [
@@ -823,7 +816,7 @@
" chosen = np.random.choice(matrix.shape[1], p=matrix[current_idx].toarray()[0])\n",
" # просто выбирать наиболее вероятное продолжение не получится\n",
" # можете попробовать раскоментировать следующую строчку и посмотреть что получается\n",
- "# chosen = matrix[current_idx].argmax()\n",
+ " # chosen = matrix[current_idx].argmax()\n",
" text.append(id2word[chosen])\n",
" \n",
" if id2word[chosen] == '':\n",
@@ -835,15 +828,30 @@
},
{
"cell_type": "code",
- "execution_count": 136,
+ "execution_count": 79,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# print(generate(matrix_news, id2word_news, word2id_news).replace('', '\\n'))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "в отправляется непотребство прочее и прочих аутистов \n",
- " а утилит всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех всех\n"
+ "там и сделай \n",
+ " у меня они залегли вот чтобы излить свои 20 ansistring в нём доминировали разные периоды перекрывает голос с сожалением \n",
+ " так а в свою функцию войн бы разговор которого ты знаешь анон историю с него столько адресов хостинг-провайдера \n",
+ " я сразу после чернобыльского дождя главной сукой оказался нет ни единого механизма нужно нужно умение написать чем он не шумит линукс и показываемый тобой \n",
+ " ну тогда было бы не может быть тобой молодые неудачники \n",
+ " мне как на раскладку клавиатуры \n",
+ " фотки были сирийские вертолёты надеюсь \n",
+ " это которая без прочтения таненбаума где-то в фейковости твоего\n"
]
}
],
@@ -853,18 +861,18 @@
},
{
"cell_type": "code",
- "execution_count": 137,
+ "execution_count": 58,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "в главной военной службы пожар в трех объединений минюста на тверской и именнона этом сообщил риа новости в москве в оон на след \n",
- " по месту взрыва школы цигун \n",
- " как отмечают что такая система ориентации в деле важным элементом комплексной системы обладающей достоинствами некоммерческих аналогов в другой экологический проект судебного иска против проводниц возбуждено уголовное дело рук может быть поставлено 20 процентов повышается производительность труда в среднем на своем заявлении президент аргентины \n",
- " днемона пряталась опасаясь что в 225 евро \n",
- " как утверждают в русском сегменте интернета в тот же этого можно сделать его людей среди сайтов на юге\n"
+ "водитель оперативный штаб группировки федеральных сил заявил я в других договоренностей по британской контразведки ми-5 \n",
+ " по мнению в семи километрах от электроники и развития где одновременно рао выбор на никольском кладбище домашних животных обладающих заданными полезными для проникновения криминалитета во владикавказе \n",
+ " по словам один выстрел по ранее он содержится только на окружающих они видели как счел достойными внимания уделено предстоящей встрече состоявшейся в первом туре состоявшемся в их честь в москве состоялась встреча в московскую городскую прокуратуру россии \n",
+ " генсек оон которые прервались годичный сроксо дня общественный порядок может серьезно не нуждается в джакарту прибывает в стране \n",
+ "\n"
]
}
],
@@ -872,24 +880,306 @@
"print(generate(matrix_news, id2word_news, word2id_news).replace('', '\\n'))"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Температура\n",
+ "\n",
+ "Обычное случайное семплирование может приводить к слишком рандомным результатам. Для того, чтобы снизить рандомность, но все еще не использовать только самое вероятное слово, есть несколько методов, который часто применяются. \n",
+ "\n",
+ "Самый основной - это температура. Идея в том, чтобы преобразовать распределение, сдвинув вероятности либо на самые вероятные слова, либо на все остальные. Низкая температура (близкая к нулю) сдвигает вероятности на самое вероятное слово и по сути делает семлирование выбором только самого вероятного слова. А высокая температура размывает вероятности по всем словам. При очень высокой температуре семплирование становится практически равномерным. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 122,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "\n",
+ "def apply_temperature(probas, temperature):\n",
+ " # логарифмирование и деление на температуру\n",
+ " log_probas = np.log(np.maximum(probas, 1e-10)) \n",
+ " adjusted_log_probas = log_probas / temperature\n",
+ " # чтобы получить честные вероятности, нужно применить софтмакс\n",
+ " exp_probas = np.exp(adjusted_log_probas)\n",
+ " adjusted_probabilities = exp_probas / np.sum(exp_probas)\n",
+ " return adjusted_probabilities"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Давайте посмотрим на изначальное распределение для какого-то слова (топ 100 вероятностей отсортированных по убыванию)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 82,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import matplotlib.pyplot as plt"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 103,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 103,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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XXl5eTp16lTQ+ubmZhUVFam0tFTt7e0qKChQQUGBDh8+LEk6d+6c2tratH79erW1tWn37t06evSo7r///sv6evrpp3Xy5El7W758eajDBwAAhgqzLMsK5QCXy6WZM2dq69atkiS/36/k5GQtX75ca9asuay+sLBQvb29qq+vt9tmzZqlzMxM1dbWBv2MAwcOKCcnRx999JEmTZok6dOVmpUrV2rlypWhDNfm8/kUGxurnp4excTEDKmPq0lZ8wv75w835A97/wAA3IxC+fc7pJWa/v5+tba2yu12f9aBwyG32y2PxxP0GI/HE1AvSXl5eVesl6Senh6FhYUpLi4uoH3Dhg2Kj4/XXXfdpeeff14XL168Yh99fX3y+XwBGwAAMFdEKMVnzpzRwMCAEhMTA9oTExN15MiRoMd4vd6g9V6vN2j9+fPntXr1ahUVFQUksscee0wzZszQuHHj1NzcrIqKCp08eVKbNm0K2k91dbWeeuqpUE4PAACMYiGFmmvtwoULevDBB2VZll588cWAfeXl5fbP06ZNU2RkpL73ve+purpaUVFRl/VVUVERcIzP51NycvK1GzwAABhRIYWahIQEhYeHq6urK6C9q6tLTqcz6DFOp3NQ9ZcCzUcffaR9+/Z94XUzl8ulixcv6sMPP9Rf/dVfXbY/KioqaNgBAABmCumemsjISGVlZampqclu8/v9ampqUm5ubtBjcnNzA+olqbGxMaD+UqA5duyYXnvtNcXHx3/hWA4dOiSHw6Hx48eHcgoAAMBQIV9+Ki8vV3FxsbKzs5WTk6PNmzert7dXJSUlkqQlS5Zo4sSJqq6uliStWLFCc+fO1caNG5Wfn6+6ujodPHhQ27Ztk/RpoPnOd76jtrY21dfXa2BgwL7fZty4cYqMjJTH41FLS4vuuecejR07Vh6PR6tWrdJDDz2kO+64Y7jmAgAAjGIhh5rCwkKdPn1alZWV8nq9yszMVENDg30zcEdHhxyOzxaAZs+erR07dmjdunVau3at0tLStGfPHk2dOlWSdOLECb366quSpMzMzIDPev3113X33XcrKipKdXV1evLJJ9XX16fU1FStWrUq4J4ZAABwcwv5PTWjFe+pAQBg9Llm76kBAAC4URFqAACAEQg1AADACIQaAABgBEINAAAwAqEGAAAYgVADAACMQKgBAABGINQAAAAjEGoA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",
+ "text/plain": [
+ "