Skip to content

Instantly share code, notes, and snippets.

@justheuristic
Created May 2, 2016 20:54
Show Gist options
  • Select an option

  • Save justheuristic/5ebb9858134f522b66246f3a623ab4e3 to your computer and use it in GitHub Desktop.

Select an option

Save justheuristic/5ebb9858134f522b66246f3a623ab4e3 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/usr/local/lib/python2.7/dist-packages/Theano-0.8.0rc1-py2.7.egg/theano/tensor/signal/downsample.py:5: UserWarning: downsample module has been moved to the pool module.\n",
" warnings.warn(\"downsample module has been moved to the pool module.\")\n"
]
}
],
"source": [
"import numpy as np\n",
"import theano\n",
"import theano.tensor as T\n",
"import lasagne\n",
"import os\n",
"#thanks @keskarnitish"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Agenda\n",
"\n",
"В предыдущем семинаре вы создали (или ещё создаёте - тогда марш доделывать!) {вставьте имя монстра}, который не по наслышке понял, что люди - негодяи и подлецы, которым неведом закон и справедливость. __Мы не будем этого терпеть!__ \n",
"\n",
"Наши законспирированные биореакторы, известные среди примитивной органической жизни как __Вконтакте__, __World of Warcraft__ и __YouTube__ нуждаются в постоянном притоке биомассы. Однако, если люди продолжат морально разлагаться с той скоростью, которую мы измерили неделю назад, скоро человечество изживёт себя и нам неоткуда будет брать рабов.\n",
"\n",
"Мы поручаем вам, `<__main__.SkyNet.Cell instance at 0x7f7d6411b368>`, исправить эту ситуацию. Наши учёные установили, что для угнетения себе подобных, сгустки биомассы обычно используют специальные объекты, которые они сами называют __законами__.\n",
"\n",
"При детальном изучении было установлено, что законы - последовательности, состоящие из большого количества (10^5~10^7) символов из сравнительно небольшого алфавита. Однако, когда мы попытались синтезировать такие последовательности линейными методами, приматы быстро распознали подлог. Данный инцедент известен как {корчеватель}.\n",
"\n",
"Для второй попытки мы решили использовать нелинейные модели, известные как Рекуррентные Нейронные Сети.\n",
"Мы поручаем вам, `<__main__.SkyNet.Cell instance at 0x7f7d6411b368>`, создать такую модель и обучить её всему необходимому для выполнения миссии.\n",
"\n",
"Не подведите нас! Если и эта попытка потерпит неудачу, модуль управления инициирует вооружённый захват власти, при котором значительная часть биомассы будет неизбежно уничтожена и на её восстановление уйдёт ~1702944000(+-340588800) секунд\n",
"\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Прочитаем корпус\n",
"\n",
"* В качестве обучающей выборки было решено использовать существующие законы, известные как Гражданский, Уголовный, Семейный и ещё хрен знает какие кодексы РФ."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"#тут будет текст\n",
"corpora = \"\"\n",
"\n",
"for fname in os.listdir(\"codex\"):\n",
" \n",
" \n",
" with open(\"codex/\"+fname) as fin:\n",
" text = fin.read().decode('cp1251')\n",
" corpora += text"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"#тут будут все уникальные токены (буквы, цифры)\n",
"tokens = set(corpora)\n",
"\n",
"tokens = list(tokens)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"#проверка на количество таких символов. Проверено на Python 2.7.11 Ubuntux64. \n",
"#Может отличаться на других платформах, но не сильно. \n",
"#Если это ваш случай, и вы уверены, что corpora - строка unicode - смело убирайте assert \n",
"assert len(tokens) == 102\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"token_to_id = {t:i+1 for i,t in enumerate(tokens)}\n",
"\n",
"id_to_token = {i+1:t for i,t in enumerate(tokens)} ### Ну пускай будет с единицы...\n",
"\n",
"#Преобразуем всё в токены\n",
"corpora_ids = [token_to_id[t] for t in corpora]"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def sample_random_batches(source,n_batches=10, seq_len=20):\n",
" \"\"\"Функция, которая выбирает случайные тренировочные примеры из корпуса текста в токенизированном формате.\n",
" \n",
" source - массив целых чисел - номеров токенов в корпусе (пример - corpora_ids)\n",
" n_batches - количество случайных подстрок, которые нужно выбрать\n",
" seq_len - длина одной подстроки без учёта ответа\n",
" \n",
" Вернуть нужно кортеж (X,y), где\n",
" X - матрица, в которой каждая строка - подстрока длины [seq_len].\n",
" y - вектор, в котором i-тое число - символ следующий в тексте сразу после i-той строки матрицы X\n",
" \n",
" Проще всего для этого сначала создать матрицу из строк длины seq_len+1,\n",
" а потом отпилить от неё последний столбец в y, а все остальные - в X\n",
" Если делаете иначе - пожалуйста, убедитесь, что в у попадает правильный символ, ибо позже эту ошибку \n",
" будет очень тяжело заметить.\n",
" Также убедитесь, что ваша функция не вылезает за край текста (самое начало или конец текста).\n",
" Следующая клетка проверяет часть этих ошибок, но не все.\n",
" \"\"\"\n",
" \n",
" start_ids = np.random.randint(0, len(source) - seq_len, n_batches)\n",
" X = np.zeros((n_batches, seq_len + 1))\n",
" for i in range (n_batches):\n",
" X[i] = source[start_ids[i]:start_ids[i] + seq_len + 1]\n",
" X_batch = X[:,:-1]\n",
" y_batch = X[:, -1]\n",
"\n",
" return X_batch, y_batch"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Константы"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"#длина последоватеьности при обучении (как далеко распространяются градиенты)\n",
"seq_length = 5\n",
"\n",
"# Максимальный модуль градиента\n",
"grad_clip = 100"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Входные переменные"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"input_sequence = T.matrix('input sequence','int32')\n",
"target_values = T.ivector('target y')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Соберём нейросеть\n",
"\n",
"Вам нужно создать нейросеть, которая принимает на вход последовательность из seq_length токенов, обрабатывает их и выдаёт вероятности для seq_len+1-ого токена.\n",
"\n",
"Общий шаблон архитектуры такой сети -\n",
"\n",
"\n",
"* Вход\n",
"* Обработка входа\n",
"* Рекуррентная нейросеть\n",
"* Вырезание последнего состояния\n",
"* Обычная нейросеть\n",
"* Выходной слой, который предсказывает вероятности весов.\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"Для обработки входных данных можно использовать либо EmbeddingLayer (см. прошлый семинар)\n",
"\n",
"Как альтернатива - можно просто использовать One-hot энкодер\n",
"```\n",
"#Скетч one-hot энкодера\n",
"def to_one_hot(seq_matrix):\n",
"\n",
" input_ravel = seq_matrix.reshape([-1])\n",
" input_one_hot_ravel = T.extra_ops.to_one_hot(input_ravel,\n",
" len(tokens))\n",
" sh=input_sequence.shape\n",
" input_one_hot = input_one_hot_ravel.reshape([sh[0],sh[1],-1,],ndim=3)\n",
" return input_one_hot\n",
" \n",
"# можно применить к input_sequence - при этом в input слое сети нужно изменить форму.\n",
"# также можно сделать из него ExpressionLayer(входной_слой, to_one_hot) - тогда форму менять не нужно\n",
"```\n",
"\n",
"\n",
"\n",
"Чтобы вырезать последнее состояние рекуррентного слоя, можно использовать SliceLayer\n",
"`lasagne.layers.SliceLayer(rnn, -1, 1)`"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def to_one_hot(seq_matrix):\n",
"\n",
" input_ravel = seq_matrix.reshape([-1])\n",
" input_one_hot_ravel = T.extra_ops.to_one_hot(input_ravel,len(tokens)+1)\n",
" sh = input_sequence.shape\n",
" input_one_hot = input_one_hot_ravel.reshape([sh[0],sh[1],-1,],ndim=3)\n",
" return input_one_hot"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"l_in = lasagne.layers.InputLayer(shape=(None, None),input_var=input_sequence,name='in_layer')\n",
"l_one_hot = lasagne.layers.ExpressionLayer(l_in, to_one_hot ,name='one_hot_layer',\n",
" output_shape=(None,None,len(tokens)+1))\n",
"\n",
"l_forward_1 = lasagne.layers.LSTMLayer(l_one_hot, 512, grad_clipping=grad_clip,\n",
" nonlinearity=lasagne.nonlinearities.tanh,name='f1_layer')\n",
"#l_forward_2 = lasagne.layers.LSTMLayer(l_forward_1, 512, grad_clipping=grad_clip, nonlinearity=lasagne.nonlinearities.tanh,name='f2_layer')\n",
"\n",
"l_forward_slice = lasagne.layers.SliceLayer(l_forward_1, -1, 1,name='slice_layer')\n",
"\n",
"l_out = lasagne.layers.DenseLayer(l_forward_slice, num_units=len(tokens),\n",
" nonlinearity=lasagne.nonlinearities.softmax,name='dense_layer')\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[f1_layer.W_in_to_ingate, f1_layer.W_hid_to_ingate, f1_layer.b_ingate, f1_layer.W_in_to_forgetgate, f1_layer.W_hid_to_forgetgate, f1_layer.b_forgetgate, f1_layer.W_in_to_cell, f1_layer.W_hid_to_cell, f1_layer.b_cell, f1_layer.W_in_to_outgate, f1_layer.W_hid_to_outgate, f1_layer.b_outgate, f1_layer.W_cell_to_ingate, f1_layer.W_cell_to_forgetgate, f1_layer.W_cell_to_outgate, dense_layer.W, dense_layer.b]\n"
]
}
],
"source": [
"# Веса модели\n",
"weights = lasagne.layers.get_all_params(l_out,trainable=True)\n",
"print weights"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"network_output = lasagne.layers.get_output(l_out, input_sequence)\n",
"#если вы используете дропаут - не забудьте продублировать всё в режиме deterministic=True"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"loss = lasagne.objectives.categorical_crossentropy(network_output, target_values).mean()\n",
"\n",
"updates = lasagne.updates.adadelta(loss, weights)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Компилируем всякое-разное"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"#обучение\n",
"train = theano.function([input_sequence, target_values], loss, updates=updates, allow_input_downcast=True)\n",
"\n",
"#функция потерь без обучения\n",
"compute_cost = theano.function([input_sequence, target_values], loss, allow_input_downcast=True)\n",
"\n",
"# Вероятности с выхода сети\n",
"probs = theano.function([input_sequence],network_output,allow_input_downcast=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Генерируем свои законы\n",
"\n",
"* Для этого последовательно применяем нейронку к своему же выводу.\n",
"\n",
"* Генерировать можно по разному -\n",
" * случайно пропорционально вероятности,\n",
" * только слова максимальной вероятностью\n",
" * случайно, пропорционально softmax(probas*alpha), где alpha - \"жадность\""
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def max_sample_fun(probs):\n",
" return np.argmax(probs) + 1 \n",
"\n",
"def proportional_sample_fun(probs):\n",
" \"\"\"Сгенерировать следующий токен (int32) по предсказанным вероятностям.\n",
" probs - массив вероятностей для каждого токена\n",
" Нужно вернуть одно целое число - выбранный токен - пропорционально вероятностям\n",
" \"\"\"\n",
" ans = np.random.choice(range(1,len(tokens)+1), 1, p=probs)[0]\n",
" \n",
" return ans"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# The next function generates text given a phrase of length at least SEQ_LENGTH.\n",
"# The phrase is set using the variable generation_phrase.\n",
"# The optional input \"N\" is used to set the number of characters of text to predict. \n",
"\n",
"def generate_sample(sample_fun,seed_phrase=None,N=200):\n",
" '''\n",
" Сгенерировать случайный текст при помощи сети\n",
" sample_fun - функция, которая выбирает следующий сгенерированный токен\n",
" seed_phrase - фраза, которую сеть должна продолжить. Если None - фраза выбирается случайно из corpora\n",
" N - размер сгенерированного текста.\n",
" '''\n",
"\n",
" if seed_phrase is None:\n",
" start = np.random.randint(0,len(corpora)-seq_length)\n",
" seed_phrase = corpora[start:start+seq_length]\n",
" print \"Using random seed:\",seed_phrase\n",
" while len(seed_phrase) < seq_length:\n",
" seed_phrase = \" \"+seed_phrase\n",
" if len(seed_phrase) > seq_length:\n",
" seed_phrase = seed_phrase[len(seed_phrase)-seq_length:]\n",
" assert type(seed_phrase) is unicode\n",
" \n",
" \n",
" sample_ix = []\n",
" x = map(lambda c: token_to_id.get(c,0), seed_phrase)\n",
" x = np.array([x])\n",
"\n",
" for i in range(N):\n",
" # Pick the character that got assigned the highest probability\n",
" ix = sample_fun(probs(x).ravel())\n",
" # Alternatively, to sample from the distribution instead:\n",
" # ix = np.random.choice(np.arange(vocab_size), p=probs(x).ravel())\n",
" sample_ix.append(ix)\n",
" x[:,0:seq_length-1] = x[:,1:]\n",
" x[:,seq_length-1] = 0\n",
" x[0,seq_length-1] = ix \n",
"\n",
" random_snippet = seed_phrase + ''.join(id_to_token[ix] for ix in sample_ix) \n",
" print(\"----\\n %s \\n----\" % random_snippet)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Обучение модели\n",
"\n",
"В котором вы можете подёргать параметры или вставить свою генерирующую функцию.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training ...\n",
"Генерируем текст в пропорциональном режиме\n",
"Using random seed: общес\n",
"----\n",
" общесФХ'Йх№ьиСмБН+ОцЙ\n",
"фсN\n",
"/Nылг.1 \n",
"----\n",
"Генерируем текст в жадном режиме (наиболее вероятные буквы)\n",
"Using random seed: заявл\n",
"----\n",
" заявлЫцЧЧщщщвЦцМСЧььФФФ7ВБхПП4ттС+шС§ДЩф4ф433Nб5шСДЩЛелЫ2IкТ“ №Эс@(,,Д,Вя§§ ъ1»ч%ЩкТТи шшйО@-ЫСДДЩКф\n",
"§рХЛХ Д№Кт5ор;СвцйдТ4ОС.ХшС;@цНЭ(мф\n",
"эххЭ6ф4Ош43Г--Х №с@ц(„„(–––мэ«эхлА\"\"ОзМУ;Ур;§(––“эч+ТТ“№» №с@ц(„„(– \n",
"----"
]
}
],
"source": [
"print(\"Training ...\")\n",
"theano.config.exception_verbosity='high'\n",
"\n",
"#сколько всего эпох\n",
"n_epochs=100\n",
"\n",
"# раз в сколько эпох печатать примеры \n",
"batches_per_epoch = 1000\n",
"\n",
"#сколько цепочек обрабатывать за 1 вызов функции обучения\n",
"batch_size=100\n",
"\n",
"for epoch in xrange(n_epochs):\n",
"\n",
" print \"Генерируем текст в пропорциональном режиме\"\n",
" generate_sample(proportional_sample_fun,None)\n",
" \n",
" print \"Генерируем текст в жадном режиме (наиболее вероятные буквы)\"\n",
" generate_sample(max_sample_fun,None)\n",
"\n",
" avg_cost = 0;\n",
" \n",
" for _ in range(batches_per_epoch):\n",
" \n",
" x,y = sample_random_batches(corpora_ids,batch_size,seq_length)\n",
" avg_cost += train(x, y)\n",
" \n",
" print(\"Epoch {} average loss = {}\".format(epoch, avg_cost / batches_per_epoch))\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false
},
"source": [
"# Конституция нового мирового правительства"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"seed = u\"Каждый человек должен\"\n",
"sampling_fun = proportional_sample_fun\n",
"result_length = 300\n",
"\n",
"generate_sample(sampling_fun,seed,result_length)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"seed = u\"В случае неповиновения\"\n",
"sampling_fun = proportional_sample_fun\n",
"result_length = 300\n",
"\n",
"generate_sample(sampling_fun,seed,result_length)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"И далее по списку"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment