Created
August 23, 2017 10:20
-
-
Save fogside/0ff06c704d16cf0b769252a213d69578 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Going deeper with Tensorflow\n", | |
"\n", | |
"В этом семинаре мы начнем изучать [Tensorflow](https://www.tensorflow.org/) для построения _deep learning_ моделей.\n", | |
"\n", | |
"Для установки tf на свою машину:\n", | |
"* `pip install tensorflow` **cpu-only** TF для Linux & Mac OS\n", | |
"* для установки tf с автомагической поддержкой GPU смотри [TF install page](https://www.tensorflow.org/install/)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 10, | |
"metadata": { | |
"collapsed": true, | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"outputs": [], | |
"source": [ | |
"import tensorflow as tf\n", | |
"gpu_options = tf.GPUOptions(allow_growth=True, per_process_gpu_memory_fraction=0.1)\n", | |
"s = tf.InteractiveSession(config=tf.ConfigProto(gpu_options=gpu_options))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"source": [ | |
"# Разминка\n", | |
"\n", | |
"Для начала, давайте имплементируем простую функцию на numpy просто для сравнения. Напишите подсчет суммы квадратов чисел от 0 до N-1.\n", | |
"\n", | |
"**Подсказка:**\n", | |
"* Массив чисел от 0 до N-1 включительно - numpy.arange(N)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": { | |
"collapsed": true, | |
"slideshow": { | |
"slide_type": "fragment" | |
} | |
}, | |
"outputs": [], | |
"source": [ | |
"import numpy as np\n", | |
"def sum_squares(N):\n", | |
" return <what?>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": { | |
"slideshow": { | |
"slide_type": "skip" | |
} | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"CPU times: user 855 ms, sys: 3.23 s, total: 4.08 s\n", | |
"Wall time: 5.34 s\n" | |
] | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"662921401752298880" | |
] | |
}, | |
"execution_count": 4, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"%%time\n", | |
"sum_squares(10**8)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Tensoflow teaser\n", | |
"\n", | |
"Сделаем тоже самое на tf" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 11, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"#Это будет параметром функции\n", | |
"N = tf.placeholder('int64', name=\"input_to_your_function\")\n", | |
"\n", | |
"#Рецепт получения суммы квадратов\n", | |
"result = tf.reduce_sum((tf.range(N)**2))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 12, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"662921401752298880\n", | |
"CPU times: user 1.35 s, sys: 4.57 s, total: 5.92 s\n", | |
"Wall time: 5.87 s\n" | |
] | |
} | |
], | |
"source": [ | |
"%%time\n", | |
"#А так вычисляется результат как и в примере на numpy\n", | |
"print(result.eval({N:10**8}))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Как это работает?\n", | |
"\n", | |
"1. Сначала объявляем placeholder'ы -- это ячейки в графе вычислений, куда будут подставляться непосредственные значения.\n", | |
"2. Затем мы пишем \"рецепт\" получения выходов по входам. Или преобразования.\n", | |
"3. Запуск вычислений с непосредственными значениями входных параметров для каждого placeholder'а.\n", | |
" * output.eval({placeholder:value}) \n", | |
" * s.run(output, {placeholder:value})\n", | |
"\n", | |
"\n", | |
"* Итак, мы на самом деле имеем две главных сущности -- это \"placeholder\" и преобразование.\n", | |
"* Обе могут представлять собой матрицы, тензоры, просто числа и т.д.\n", | |
"* Обе могут представляться различными типами данных: int32/64, floats, booleans и др.\n", | |
"\n", | |
"\n", | |
" * В tf есть аналоги почти для всех функций из numpy. Примеры:\n", | |
" * `a+b, a/b, a**b, ...` ведут себя также как в numpy\n", | |
" * np.mean -> tf.reduce_mean\n", | |
" * np.arange -> tf.range\n", | |
" * np.cumsum -> tf.cumsum\n", | |
" * Если не можете найти какую-то операцию из numpy, смотри доки: [docs](https://www.tensorflow.org/api_docs/python).\n", | |
" \n", | |
" \n", | |
"Ничего не понятно? Сейчас все исправим.:)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 13, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# Если не указывать параметр shape, то входной тензор может иметь любую произвольную форму\n", | |
"arbitrary_input = tf.placeholder('float32')\n", | |
"\n", | |
"# А так мы задаем вектор произовольной длины\n", | |
"input_vector = tf.placeholder('float32',shape=(None,))\n", | |
"\n", | |
"# Вектор фиксированной длины типа int и длины 10\n", | |
"fixed_vector = tf.placeholder('int32',shape=(10,))\n", | |
"\n", | |
"# Матрица с произвольным количеством строк и 15 столбцами (например, так можно указать формы для минибатчей)\n", | |
"input_matrix = tf.placeholder('float32',shape=(None,15))\n", | |
"\n", | |
"# Итак, используем None везде, где не хотим фиксировать размеры\n", | |
"input1 = tf.placeholder('float64',shape=(None,100,None))\n", | |
"input2 = tf.placeholder('int32',shape=(None,None,3,224,224))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 14, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# Это поэлементное умножение\n", | |
"double_the_vector = input_vector*2\n", | |
"\n", | |
"# Поэлементный косинус\n", | |
"elementwise_cosine = tf.cos(input_vector)\n", | |
"\n", | |
"# Разность между квадратами элементов вектора и самого этого вектора\n", | |
"vector_squares = input_vector**2 - input_vector\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Время немного попрактиковаться.\n", | |
"\n", | |
"Создайте два вектора типа **float32**" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"my_vector = <student.init_float32_vector()>\n", | |
"my_vector2 = <student.init_one_more_such_vector()>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# Напишите такую трансформацию с ними:\n", | |
"#(vec1)*(vec2) / (sin(vec1) +1)\n", | |
"my_transformation = <student.implementwhatwaswrittenabove()>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"print(my_transformation)\n", | |
"# Это нормально, это символьный граф" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"#\n", | |
"dummy = np.arange(5).astype('float32')\n", | |
"\n", | |
"my_transformation.eval({my_vector:dummy,my_vector2:dummy[::-1]})" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Визуализация графов\n", | |
"\n", | |
"А вот и приятная фишка Tensorflow -- **Tensorboard**.\n", | |
"\n", | |
"Визуализацию графа удобно использовать, когда дело касается дебаггинга или оптимизации. Да и вообще, интерактивные визуализации это приятно.\n", | |
"\n", | |
"Для того чтобы запустить _Tensorboard_ выполните команду в терминале:\n", | |
"\n", | |
"* ```tensorboard --logdir=/tmp/tboard --port=7007```\n", | |
" * тут --logdir и --port можете подставить удобный вам;\n", | |
"\n", | |
"Если вы совершенно не рады общению с терминалом, запустить _Tensorboard_ можно и из тетрадки вот так:\n", | |
"\n", | |
"* ```os.system(\"tensorboard --logdir=/tmp/tboard --port=7007 &\"```\n", | |
"\n", | |
"_(Но, пожалуйста, никому не сообщайте, что мы вас этому научили)_\n", | |
"\n", | |
"**Teaser:**" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"<img src=\"https://www.tensorflow.org/images/graph_vis_animation.gif\" width=780>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Port: 6501\n" | |
] | |
} | |
], | |
"source": [ | |
"# Некрасивый способ запуска Tensorboard:\n", | |
"import os\n", | |
"port = 6000 + os.getuid()\n", | |
"print(\"Port: %d\" % port)\n", | |
"#!killall tensorboard\n", | |
"os.system(\"tensorboard --logdir=./tboard --port=%d &\" % port)\n", | |
"\n", | |
"# show graph to tensorboard\n", | |
"writer = tf.summary.FileWriter(\"./tboard\", graph=tf.get_default_graph())\n", | |
"writer.close()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Одна из базовых возможностей Tensorboard это визуализация графа вычислений. Как только вы запустили ячейку сверху, можете проследовать по адресу `localhost:port` на соседней вкладке вашего броузера. Там перейдите во вкладку _graphs_. \n", | |
"\n", | |
"Вы должны наблюдать примерно следующее:\n", | |
"\n", | |
"<img src=\"https://s12.postimg.org/a374bmffx/tensorboard.png\" width=480>\n", | |
"\n", | |
"Tensorboard также позволяет наблюдать кривую обучения, сохранять картинки и аудио...\n", | |
"\n", | |
"Это бывает полезно еще на этапе обучения увидеть, что что-то идет не так.\n", | |
"\n", | |
"Один исследователь сказал:\n", | |
"\n", | |
"```\n", | |
"Если вы провели 4 часа своего рабочего времени наблюдая как алгоритм печатает циферки и рисует фигуры, \n", | |
"\n", | |
"это значит что скорее всего вы неправильно занимаетесь глубоким обучением.\n", | |
"\n", | |
"```\n", | |
"\n", | |
"Original:\n", | |
"\n", | |
"```\n", | |
"If you spent last four hours of your worktime watching as your algorithm prints numbers and draws figures, you're probably doing deep learning wrong.\n", | |
"```" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Более подробная информация по использованию **Tensorboard** может быть найдена [тут](https://www.tensorflow.org/get_started/graph_viz)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Теперь сам\n", | |
"\n", | |
"__[2 points max]__" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# Quest #1 - \n", | |
"# Нужно написать функцию, которая берет 2 вектора и считает среднюю квадратичную ошибку (mse).\n", | |
"# Ваша функция должна брать 2 вектора и возвращать одно единственное число\n", | |
"\n", | |
"<student.define_inputs_and_transformations()>\n", | |
"\n", | |
"mse =<student.define_transformation()>\n", | |
"\n", | |
"compute_mse = lambda vector1, vector2: <how to run you graph?>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# Tests\n", | |
"from sklearn.metrics import mean_squared_error\n", | |
"\n", | |
"for n in [1,5,10,10**3]:\n", | |
" \n", | |
" elems = [np.arange(n),np.arange(n,0,-1), np.zeros(n),\n", | |
" np.ones(n),np.random.random(n),np.random.randint(100,size=n)]\n", | |
" \n", | |
" for el in elems:\n", | |
" for el_2 in elems:\n", | |
" true_mse = np.array(mean_squared_error(el,el_2))\n", | |
" my_mse = compute_mse(el,el_2)\n", | |
" if not np.allclose(true_mse,my_mse):\n", | |
" print('Wrong result:')\n", | |
" print('mse(%s,%s)' % (el,el_2))\n", | |
" print(\"should be: %f, but your function returned %f\" % (true_mse,my_mse))\n", | |
" raise ValueError,\"Что-то не так\"\n", | |
"\n", | |
"print(\"All tests passed\") " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# tf.Variable\n", | |
"\n", | |
"Входы и трансформации не имеют никакого значения за пределами вызова функции.\n", | |
"\n", | |
"Но если вы хотите, чтобы ваша модель имела какие-то параметры (например, веса скрытых слоев нейронной сетки), которые могут изменяться в течение времени, то для их представления удобно использовать `tf.Variable`.\n", | |
"\n", | |
"Объекты типа `tf.Variable` (переменные) обладают **следующими свойствами**:\n", | |
"\n", | |
"* Вы можете присваивать значение переменной в любой момент в вашем графе вычислений;\n", | |
"* В отличии от `placeholder`'ов нет строгой необходимости инициализировать переменные всякий раз как вы запускаете `s.run(...)`;\n", | |
" * _Да, да. Если не передать значения во все плейсхолдеры в графе при запуске сессии, то сессия и не запустится;_\n", | |
" \n", | |
" \n", | |
" \n", | |
"* Переменные и трансформации могут быть использованы похожим образом; " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# создадим переменную\n", | |
"shared_vector_1 = tf.Variable(initial_value=np.ones(5))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# проинициализируем все переменные в графе;\n", | |
"s.run(tf.global_variables_initializer())\n", | |
"\n", | |
"# вычисляем значение переменной \n", | |
"print(\"initial value\", s.run(shared_vector_1))\n", | |
"\n", | |
"# внутри символьного графа переменные используются также как любые другие входы и трансформации,\n", | |
"# нет необходимости делать внутри графа \"get value\"" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# как присвоить переменной другое значение\n", | |
"s.run(shared_vector_1.assign(np.arange(5)))\n", | |
"\n", | |
"# и получить его\n", | |
"print(\"new value\", s.run(shared_vector_1))\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# tf.gradients - почему граф это удобно\n", | |
"\n", | |
"* Tensorflow может автомагически вычислять градиенты, пользуясь графом вычислений\n", | |
"* Градиент вычисляется как произведение элементарных производных \n", | |
" * по правилу взятия производной сложной функции \n", | |
" * или в англоязычной терминалогии -- `chain rule`:\n", | |
"\n", | |
"\n", | |
"$$ {\\partial f(g(x)) \\over \\partial x} = {\\partial f(g(x)) \\over \\partial g(x)}\\cdot {\\partial g(x) \\over \\partial x} $$\n", | |
"\n", | |
"Таким образом можно взять производные по параметрам очень сложной модели до тех пор, пока элементарные функции/операции/трансформации, используемые в модели, являются дифференцируемыми." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 15, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"my_scalar = tf.placeholder('float32')\n", | |
"\n", | |
"scalar_squared = my_scalar**2\n", | |
"\n", | |
"# найдем производную функции scalar_squared по переменной my_scalar\n", | |
"derivative = tf.gradients(scalar_squared, my_scalar)[0]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"import matplotlib.pyplot as plt\n", | |
"%matplotlib inline\n", | |
"\n", | |
"x = np.linspace(-3,3)\n", | |
"x_squared, x_squared_der = s.run([scalar_squared,derivative],\n", | |
" {my_scalar:x})\n", | |
"\n", | |
"plt.plot(x, x_squared,label=\"x^2\")\n", | |
"plt.plot(x, x_squared_der, label=\"derivative\")\n", | |
"plt.legend();" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Почему это круто" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"my_vector = tf.placeholder('float32',[None])\n", | |
"\n", | |
"# Давайте найдем градиент для weird_psychotic_function по параметрам my_scalar и my_vector\n", | |
"# Осторожно! Попытки понять смысл этой функции могут привести к необратимым повреждениям вашего мозга\n", | |
"\n", | |
"weird_psychotic_function = tf.reduce_mean((my_vector+my_scalar)**(1+tf.nn.moments(my_vector,[0])[1]) + 1./ tf.atan(my_scalar))/(my_scalar**2 + 1) + 0.01*tf.sin(2*my_scalar**1.5)*(tf.reduce_sum(my_vector)* my_scalar**2)*tf.exp((my_scalar-4)**2)/(1+tf.exp((my_scalar-4)**2))*(1.-(tf.exp(-(my_scalar-4)**2))/(1+tf.exp(-(my_scalar-4)**2)))**2\n", | |
"\n", | |
"der_by_scalar = <student.compute_grad_over_scalar()>\n", | |
"der_by_vector = <student.compute_grad_over_vector()>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# Строим графики\n", | |
"scalar_space = np.linspace(1, 7, 100)\n", | |
"\n", | |
"y = [s.run(weird_psychotic_function, {my_scalar:x, my_vector:[1, 2, 3]})\n", | |
" for x in scalar_space]\n", | |
"\n", | |
"plt.plot(scalar_space, y, label='function')\n", | |
"\n", | |
"y_der_by_scalar = [s.run(der_by_scalar, {my_scalar:x, my_vector:[1, 2, 3]})\n", | |
" for x in scalar_space]\n", | |
"\n", | |
"plt.plot(scalar_space, y_der_by_scalar, label='derivative')\n", | |
"plt.grid()\n", | |
"plt.legend();" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Напоследок - optimizers\n", | |
"\n", | |
"Теперь, когда мы умеем считать градиенты автомагически с помощью встроенных функций tf, мы могли бы реализовать градиентный спуск и оптимизировать с ним параметры моделей. \n", | |
"\n", | |
"К счастью, в tf также есть готовые оптимайзеры, которые мы можем использовать прямо из коробки.\n", | |
"Помните что-то про `momentum` и `rmsprop`?\n", | |
"\n", | |
"Подробнее про различные виды оптимайзеров можно почитать [тут](http://ruder.io/optimizing-gradient-descent/)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"y_guess = tf.Variable(np.zeros(2,dtype='float32'))\n", | |
"y_true = tf.range(1,3,dtype='float32')\n", | |
"\n", | |
"loss = tf.reduce_mean((y_guess - y_true + tf.random_normal([2]))**2) \n", | |
"\n", | |
"optimizer = tf.train.MomentumOptimizer(0.01,0.9).minimize(loss,var_list=y_guess)\n", | |
"\n", | |
"#same, but more detailed:\n", | |
"#updates = [[tf.gradients(loss,y_guess)[0], y_guess]]\n", | |
"#optimizer = tf.train.MomentumOptimizer(0.01,0.9).apply_gradients(updates)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"from IPython.display import clear_output\n", | |
"\n", | |
"s.run(tf.global_variables_initializer())\n", | |
"\n", | |
"guesses = [s.run(y_guess)]\n", | |
"\n", | |
"for _ in range(100):\n", | |
" s.run(optimizer)\n", | |
" guesses.append(s.run(y_guess))\n", | |
" \n", | |
" clear_output(True)\n", | |
" plt.plot(*zip(*guesses),marker='.')\n", | |
" plt.scatter(*s.run(y_true),c='red')\n", | |
" plt.show()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Логистическа регрессия своими руками\n", | |
"\n", | |
"**[4 балла]**\n", | |
"\n", | |
"Имплементируйте свою простенькую логрегрессию с блэкджеком и кроссэнтропией\n", | |
"\n", | |
"**Что потребуется**\n", | |
"* Используйте tf.Variable для весов\n", | |
"* X и y это входы\n", | |
"* Нужно реализовать 2 функции:\n", | |
" * `train_function(X,y)` - возвращает ошибку и изменяет веса на 1 шаг по граиденту (через updates)\n", | |
" * `predict_fun(X)` - возвращает предсказанные ответы (\"y\") по данным\n", | |
" \n", | |
" \n", | |
"Мы будем работать с двухклассовой версией датасета MNIST;\n", | |
"\n", | |
"Обратите внимание, что `y` принимает значения из `{0,1}` в исходной версии датасеты, а не `{-1,1}` (которые скорее всего вам нужны)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"from sklearn.datasets import load_digits\n", | |
"mnist = load_digits(2)\n", | |
"\n", | |
"X,y = mnist.data, mnist.target\n", | |
"\n", | |
"print(\"y [shape - %s]:\" % (str(y.shape)), y[:10])\n", | |
"print(\"X [shape - %s]:\" % (str(X.shape)))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"print('X:\\n',X[:3,:10])\n", | |
"print('y:\\n',y[:10])\n", | |
"plt.imshow(X[0].reshape([8,8]))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# inputs and shareds\n", | |
"weights = <student.code_variable()>\n", | |
"input_X = <student.code_placeholder()>\n", | |
"input_y = <student.code_placeholder()>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"predicted_y = <predicted probabilities for input_X>\n", | |
"loss = <logistic loss (scalar, mean over sample)>\n", | |
"\n", | |
"optimizer = <optimizer that minimizes loss>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"train_function = <compile function that takes X and y, returns log loss and updates weights>\n", | |
"predict_function = <compile function that takes X and computes probabilities of y>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"from sklearn.model_selection import train_test_split\n", | |
"X_train, X_test, y_train, y_test = train_test_split(X, y)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"from sklearn.metrics import roc_auc_score\n", | |
"\n", | |
"for i in range(5):\n", | |
" <run optimizer operation>\n", | |
" loss_i = <compute loss at iteration i>\n", | |
" \n", | |
" print(\"loss at iter %i:%.4f\" % (i, loss_i))\n", | |
" \n", | |
" print(\"train auc:\",roc_auc_score(y_train, predict_function(X_train)))\n", | |
" print(\"test auc:\",roc_auc_score(y_test, predict_function(X_test)))\n", | |
"\n", | |
" \n", | |
"print (\"resulting weights:\")\n", | |
"plt.imshow(shared_weights.get_value().reshape(8, -1))\n", | |
"plt.colorbar();" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Bonus: my1stNN\n", | |
"\n", | |
"### Нейроночка\n", | |
"\n", | |
"**[базовая часть - 4 балла]**\n", | |
"\n", | |
"Ваше финальное задание - сделать свою первую нейронку [почти] из спичек и желудей.\n", | |
"В этот раз распознавание цифр зашло чуть дальше:\n", | |
"\n", | |
"* картинки 28x28\n", | |
"* 10 классов\n", | |
"* 50k+ картинок только в обучающей выборке\n", | |
"\n", | |
"Вам не нужно (но можно) создавать монстров на 152 слоя, не нужно ничего сворачивать - достаточно сделать простую нейросеть с 1 скрытым и 1 выходным слоем, которая будет работать лучше логистической регрессии.\n", | |
"В конце есть шаблон отчёта - его желательно вести по ходу работ (ну или хотя бы не забыть).\n", | |
"\n", | |
"\n", | |
"**[bonus score]** Далее, если ваша сетка уже побила результаты линейной модели, а запал остался - можно попробовать улучшить результат ещё дальше. Челлендж - превзойти рубежи 95%/97.5%/98.5% точности на тесте без использования свёрток.\n", | |
"\n", | |
"\n", | |
"__СПОЙЛЕР!__\n", | |
"В конце тетрадки есть несколько советов по реализации. Если вы чувствуете в себе силы выстрелить в ногу самостоятельно - ваша воля, но наткнувшись на неразрешимые проблемы будьте добры прочитать \"хвост\" тетрадки, прежде, чем спрашивать семинариста." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"from mnist import load_dataset\n", | |
"\n", | |
"#качаем (по необходимости) и читаем данные.\n", | |
"#Важно, что для обучения можно использовать только train, \n", | |
"#а val - для оценки прогресса, сравнения можелей и early stopping.\n", | |
"#Тест вообще лучше положить под камень до самого конца, но ктоЖ вас поймает.\n", | |
"\n", | |
"X_train,y_train,X_val,y_val,X_test,y_test = load_dataset()\n", | |
"\n", | |
"print (X_train.shape,y_train.shape)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"plt.imshow(X_train[0,0])" | |
] | |
}, | |
{ | |
"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": [ | |
"<а тут - запилить цикл обучения и нужные метрики>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"<и наконец тут - предсказание на тесте - только честно!>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Отчёт\n", | |
"Я делал такое и такое.\n", | |
"\n", | |
"Потом попробовал вот-такое и алгоритм выдал вот-такое.\n", | |
"\n", | |
"А потом я его стукнул и он стал фиолетовый в крапинку.\n", | |
"\n", | |
"А ещё мне очень помогла вон-та статья и вот-этот сорт травы (if any)." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"# SPOILERS\n", | |
"Рекомендуемый порядок:\n", | |
"\n", | |
"* Адаптировать логистическую регрессию на клаccификацию 1 цифры против всех (например, нулей против ненулей)\n", | |
"* Обобщить логистическую регрессию до многоклассовой.\n", | |
" - для этого придётся вспомнить первую лекцию или загуглить.\n", | |
" - вместо одного вектора весов у вас будет матрица (признак, класс)\n", | |
" - softmax (экспонента на сумму экспонент) можно сделать самому, а можно - tf.nn.softmax\n", | |
" - Лучше использовать стохастический градиентный спуск (минибатчевый)\n", | |
" - в котором случае выборку желательно перемешать (ну или брать случайный набор примеров на каждой итерации обучения)\n", | |
"* Добавить скрытый слой. Теперь ваша логистическая регрессия опирается на нейроны, а не на входы.\n", | |
" - Принцип работы первого слоя - такой же, как у выходного, но вместо softmax у него другая нелинейность.\n", | |
" - нужно обучать оба слоя, а не только выходной :)\n", | |
" - важно не инициализировать веса нулями из-за эффекта симметрии. Для начала - случайный нормальный шум с маленькой \"сигмой\".\n", | |
" - Начать рекоммендую с 50 нейронов и сигмоиды, ибо так труднее прострелить себе ногу.\n", | |
" - В идеале у вас будет 2 .dot-а, 1 sigmoid и 1 softmax.\n", | |
" - **Убедитесь, что такая нейронка выучивается лучше, чем логистическая регрессия**\n", | |
"* Теперь время подумать над тем, как улучшить результат. Слои, нейроны, нелинейности, методы оптимизации, инициализация - всё, что хотите, разве что я бы попросил в качестве челленджа обойтись пока без свёрток." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.6.2" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 1 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Going deeper with Tensorflow\n", | |
"\n", | |
"В этом семинаре мы начнем изучать [Tensorflow](https://www.tensorflow.org/) для построения _deep learning_ моделей.\n", | |
"\n", | |
"Для установки tf на свою машину:\n", | |
"* `pip install tensorflow` **cpu-only** TF для Linux & Mac OS\n", | |
"* для установки tf с автомагической поддержкой GPU смотри [TF install page](https://www.tensorflow.org/install/)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 10, | |
"metadata": { | |
"collapsed": true, | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"outputs": [], | |
"source": [ | |
"import tensorflow as tf\n", | |
"gpu_options = tf.GPUOptions(allow_growth=True, per_process_gpu_memory_fraction=0.1)\n", | |
"s = tf.InteractiveSession(config=tf.ConfigProto(gpu_options=gpu_options))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "subslide" | |
} | |
}, | |
"source": [ | |
"# Разминка\n", | |
"\n", | |
"Для начала, давайте имплементируем простую функцию на numpy просто для сравнения. Напишите подсчет суммы квадратов чисел от 0 до N-1.\n", | |
"\n", | |
"**Подсказка:**\n", | |
"* Массив чисел от 0 до N-1 включительно - numpy.arange(N)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": { | |
"collapsed": true, | |
"slideshow": { | |
"slide_type": "fragment" | |
} | |
}, | |
"outputs": [], | |
"source": [ | |
"import numpy as np\n", | |
"def sum_squares(N):\n", | |
" return <what?>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": { | |
"slideshow": { | |
"slide_type": "skip" | |
} | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"CPU times: user 855 ms, sys: 3.23 s, total: 4.08 s\n", | |
"Wall time: 5.34 s\n" | |
] | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"662921401752298880" | |
] | |
}, | |
"execution_count": 4, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"%%time\n", | |
"sum_squares(10**8)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
} | |
}, | |
"source": [ | |
"# Tensoflow teaser\n", | |
"\n", | |
"Сделаем тоже самое на tf" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 11, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"#Это будет параметром функции\n", | |
"N = tf.placeholder('int64', name=\"input_to_your_function\")\n", | |
"\n", | |
"#Рецепт получения суммы квадратов\n", | |
"result = tf.reduce_sum((tf.range(N)**2))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 12, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"662921401752298880\n", | |
"CPU times: user 1.35 s, sys: 4.57 s, total: 5.92 s\n", | |
"Wall time: 5.87 s\n" | |
] | |
} | |
], | |
"source": [ | |
"%%time\n", | |
"#А так вычисляется результат как и в примере на numpy\n", | |
"print(result.eval({N:10**8}))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Как это работает?\n", | |
"\n", | |
"1. Сначала объявляем placeholder'ы -- это ячейки в графе вычислений, куда будут подставляться непосредственные значения.\n", | |
"2. Затем мы пишем \"рецепт\" получения выходов по входам. Или преобразования.\n", | |
"3. Запуск вычислений с непосредственными значениями входных параметров для каждого placeholder'а.\n", | |
" * output.eval({placeholder:value}) \n", | |
" * s.run(output, {placeholder:value})\n", | |
"\n", | |
"\n", | |
"* Итак, мы на самом деле имеем две главных сущности -- это \"placeholder\" и преобразование.\n", | |
"* Обе могут представлять собой матрицы, тензоры, просто числа и т.д.\n", | |
"* Обе могут представляться различными типами данных: int32/64, floats, booleans и др.\n", | |
"\n", | |
"\n", | |
" * В tf есть аналоги почти для всех функций из numpy. Примеры:\n", | |
" * `a+b, a/b, a**b, ...` ведут себя также как в numpy\n", | |
" * np.mean -> tf.reduce_mean\n", | |
" * np.arange -> tf.range\n", | |
" * np.cumsum -> tf.cumsum\n", | |
" * Если не можете найти какую-то операцию из numpy, смотри доки: [docs](https://www.tensorflow.org/api_docs/python).\n", | |
" \n", | |
" \n", | |
"Ничего не понятно? Сейчас все исправим.:)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 13, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# Если не указывать параметр shape, то входной тензор может иметь любую произвольную форму\n", | |
"arbitrary_input = tf.placeholder('float32')\n", | |
"\n", | |
"# А так мы задаем вектор произовольной длины\n", | |
"input_vector = tf.placeholder('float32',shape=(None,))\n", | |
"\n", | |
"# Вектор фиксированной длины типа int и длины 10\n", | |
"fixed_vector = tf.placeholder('int32',shape=(10,))\n", | |
"\n", | |
"# Матрица с произвольным количеством строк и 15 столбцами (например, так можно указать формы для минибатчей)\n", | |
"input_matrix = tf.placeholder('float32',shape=(None,15))\n", | |
"\n", | |
"# Итак, используем None везде, где не хотим фиксировать размеры\n", | |
"input1 = tf.placeholder('float64',shape=(None,100,None))\n", | |
"input2 = tf.placeholder('int32',shape=(None,None,3,224,224))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 14, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# Это поэлементное умножение\n", | |
"double_the_vector = input_vector*2\n", | |
"\n", | |
"# Поэлементный косинус\n", | |
"elementwise_cosine = tf.cos(input_vector)\n", | |
"\n", | |
"# Разность между квадратами элементов вектора и самого этого вектора\n", | |
"vector_squares = input_vector**2 - input_vector\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Время немного попрактиковаться.\n", | |
"\n", | |
"Создайте два вектора типа **float32**" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"my_vector = <student.init_float32_vector()>\n", | |
"my_vector2 = <student.init_one_more_such_vector()>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# Напишите такую трансформацию с ними:\n", | |
"#(vec1)*(vec2) / (sin(vec1) +1)\n", | |
"my_transformation = <student.implementwhatwaswrittenabove()>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"print(my_transformation)\n", | |
"# Это нормально, это символьный граф" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"#\n", | |
"dummy = np.arange(5).astype('float32')\n", | |
"\n", | |
"my_transformation.eval({my_vector:dummy,my_vector2:dummy[::-1]})" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Визуализация графов\n", | |
"\n", | |
"А вот и приятная фишка Tensorflow -- **Tensorboard**.\n", | |
"\n", | |
"Визуализацию графа удобно использовать, когда дело касается дебаггинга или оптимизации. Да и вообще, интерактивные визуализации это приятно.\n", | |
"\n", | |
"Для того чтобы запустить _Tensorboard_ выполните команду в терминале:\n", | |
"\n", | |
"* ```tensorboard --logdir=/tmp/tboard --port=7007```\n", | |
" * тут --logdir и --port можете подставить удобный вам;\n", | |
"\n", | |
"Если вы совершенно не рады общению с терминалом, запустить _Tensorboard_ можно и из тетрадки вот так:\n", | |
"\n", | |
"* ```os.system(\"tensorboard --logdir=/tmp/tboard --port=7007 &\"```\n", | |
"\n", | |
"_(Но, пожалуйста, никому не сообщайте, что мы вас этому научили)_\n", | |
"\n", | |
"**Teaser:**" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"<img src=\"https://www.tensorflow.org/images/graph_vis_animation.gif\" width=780>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Port: 6501\n" | |
] | |
} | |
], | |
"source": [ | |
"# Некрасивый способ запуска Tensorboard:\n", | |
"import os\n", | |
"port = 6000 + os.getuid()\n", | |
"print(\"Port: %d\" % port)\n", | |
"#!killall tensorboard\n", | |
"os.system(\"tensorboard --logdir=./tboard --port=%d &\" % port)\n", | |
"\n", | |
"# show graph to tensorboard\n", | |
"writer = tf.summary.FileWriter(\"./tboard\", graph=tf.get_default_graph())\n", | |
"writer.close()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Одна из базовых возможностей Tensorboard это визуализация графа вычислений. Как только вы запустили ячейку сверху, можете проследовать по адресу `localhost:port` на соседней вкладке вашего броузера. Там перейдите во вкладку _graphs_. \n", | |
"\n", | |
"Вы должны наблюдать примерно следующее:\n", | |
"\n", | |
"<img src=\"https://s12.postimg.org/a374bmffx/tensorboard.png\" width=480>\n", | |
"\n", | |
"Tensorboard также позволяет наблюдать кривую обучения, сохранять картинки и аудио...\n", | |
"\n", | |
"Это бывает полезно еще на этапе обучения увидеть, что что-то идет не так.\n", | |
"\n", | |
"Один исследователь сказал:\n", | |
"\n", | |
"```\n", | |
"Если вы провели 4 часа своего рабочего времени наблюдая как алгоритм печатает циферки и рисует фигуры, \n", | |
"\n", | |
"это значит что скорее всего вы неправильно занимаетесь глубоким обучением.\n", | |
"\n", | |
"```\n", | |
"\n", | |
"Original:\n", | |
"\n", | |
"```\n", | |
"If you spent last four hours of your worktime watching as your algorithm prints numbers and draws figures, you're probably doing deep learning wrong.\n", | |
"```" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Более подробная информация по использованию **Tensorboard** может быть найдена [тут](https://www.tensorflow.org/get_started/graph_viz)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Теперь сам\n", | |
"\n", | |
"__[2 points max]__" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# Quest #1 - \n", | |
"# Нужно написать функцию, которая берет 2 вектора и считает среднюю квадратичную ошибку (mse).\n", | |
"# Ваша функция должна брать 2 вектора и возвращать одно единственное число\n", | |
"\n", | |
"<student.define_inputs_and_transformations()>\n", | |
"\n", | |
"mse =<student.define_transformation()>\n", | |
"\n", | |
"compute_mse = lambda vector1, vector2: <how to run you graph?>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# Tests\n", | |
"from sklearn.metrics import mean_squared_error\n", | |
"\n", | |
"for n in [1,5,10,10**3]:\n", | |
" \n", | |
" elems = [np.arange(n),np.arange(n,0,-1), np.zeros(n),\n", | |
" np.ones(n),np.random.random(n),np.random.randint(100,size=n)]\n", | |
" \n", | |
" for el in elems:\n", | |
" for el_2 in elems:\n", | |
" true_mse = np.array(mean_squared_error(el,el_2))\n", | |
" my_mse = compute_mse(el,el_2)\n", | |
" if not np.allclose(true_mse,my_mse):\n", | |
" print('Wrong result:')\n", | |
" print('mse(%s,%s)' % (el,el_2))\n", | |
" print(\"should be: %f, but your function returned %f\" % (true_mse,my_mse))\n", | |
" raise ValueError,\"Что-то не так\"\n", | |
"\n", | |
"print(\"All tests passed\") " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# tf.Variable\n", | |
"\n", | |
"Входы и трансформации не имеют никакого значения за пределами вызова функции.\n", | |
"\n", | |
"Но если вы хотите, чтобы ваша модель имела какие-то параметры (например, веса скрытых слоев нейронной сетки), которые могут изменяться в течение времени, то для их представления удобно использовать `tf.Variable`.\n", | |
"\n", | |
"Объекты типа `tf.Variable` (переменные) обладают **следующими свойствами**:\n", | |
"\n", | |
"* Вы можете присваивать значение переменной в любой момент в вашем графе вычислений;\n", | |
"* В отличии от `placeholder`'ов нет строгой необходимости инициализировать переменные всякий раз как вы запускаете `s.run(...)`;\n", | |
" * _Да, да. Если не передать значения во все плейсхолдеры в графе при запуске сессии, то сессия и не запустится;_\n", | |
" \n", | |
" \n", | |
" \n", | |
"* Переменные и трансформации могут быть использованы похожим образом; " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# создадим переменную\n", | |
"shared_vector_1 = tf.Variable(initial_value=np.ones(5))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# проинициализируем все переменные в графе;\n", | |
"s.run(tf.global_variables_initializer())\n", | |
"\n", | |
"# вычисляем значение переменной \n", | |
"print(\"initial value\", s.run(shared_vector_1))\n", | |
"\n", | |
"# внутри символьного графа переменные используются также как любые другие входы и трансформации,\n", | |
"# нет необходимости делать внутри графа \"get value\"" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# как присвоить переменной другое значение\n", | |
"s.run(shared_vector_1.assign(np.arange(5)))\n", | |
"\n", | |
"# и получить его\n", | |
"print(\"new value\", s.run(shared_vector_1))\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# tf.gradients - почему граф это удобно\n", | |
"\n", | |
"* Tensorflow может автомагически вычислять градиенты, пользуясь графом вычислений\n", | |
"* Градиент вычисляется как произведение элементарных производных \n", | |
" * по правилу взятия производной сложной функции \n", | |
" * или в англоязычной терминалогии -- `chain rule`:\n", | |
"\n", | |
"\n", | |
"$$ {\\partial f(g(x)) \\over \\partial x} = {\\partial f(g(x)) \\over \\partial g(x)}\\cdot {\\partial g(x) \\over \\partial x} $$\n", | |
"\n", | |
"Таким образом можно взять производные по параметрам очень сложной модели до тех пор, пока элементарные функции/операции/трансформации, используемые в модели, являются дифференцируемыми." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 15, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"my_scalar = tf.placeholder('float32')\n", | |
"\n", | |
"scalar_squared = my_scalar**2\n", | |
"\n", | |
"# найдем производную функции scalar_squared по переменной my_scalar\n", | |
"derivative = tf.gradients(scalar_squared, my_scalar)[0]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"import matplotlib.pyplot as plt\n", | |
"%matplotlib inline\n", | |
"\n", | |
"x = np.linspace(-3,3)\n", | |
"x_squared, x_squared_der = s.run([scalar_squared,derivative],\n", | |
" {my_scalar:x})\n", | |
"\n", | |
"plt.plot(x, x_squared,label=\"x^2\")\n", | |
"plt.plot(x, x_squared_der, label=\"derivative\")\n", | |
"plt.legend();" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Почему это круто" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"my_vector = tf.placeholder('float32',[None])\n", | |
"\n", | |
"# Давайте найдем градиент для weird_psychotic_function по параметрам my_scalar и my_vector\n", | |
"# Осторожно! Попытки понять смысл этой функции могут привести к необратимым повреждениям вашего мозга\n", | |
"\n", | |
"weird_psychotic_function = tf.reduce_mean((my_vector+my_scalar)**(1+tf.nn.moments(my_vector,[0])[1]) + 1./ tf.atan(my_scalar))/(my_scalar**2 + 1) + 0.01*tf.sin(2*my_scalar**1.5)*(tf.reduce_sum(my_vector)* my_scalar**2)*tf.exp((my_scalar-4)**2)/(1+tf.exp((my_scalar-4)**2))*(1.-(tf.exp(-(my_scalar-4)**2))/(1+tf.exp(-(my_scalar-4)**2)))**2\n", | |
"\n", | |
"der_by_scalar = <student.compute_grad_over_scalar()>\n", | |
"der_by_vector = <student.compute_grad_over_vector()>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# Строим графики\n", | |
"scalar_space = np.linspace(1, 7, 100)\n", | |
"\n", | |
"y = [s.run(weird_psychotic_function, {my_scalar:x, my_vector:[1, 2, 3]})\n", | |
" for x in scalar_space]\n", | |
"\n", | |
"plt.plot(scalar_space, y, label='function')\n", | |
"\n", | |
"y_der_by_scalar = [s.run(der_by_scalar, {my_scalar:x, my_vector:[1, 2, 3]})\n", | |
" for x in scalar_space]\n", | |
"\n", | |
"plt.plot(scalar_space, y_der_by_scalar, label='derivative')\n", | |
"plt.grid()\n", | |
"plt.legend();" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Напоследок - optimizers\n", | |
"\n", | |
"Теперь, когда мы умеем считать градиенты автомагически с помощью встроенных функций tf, мы могли бы реализовать градиентный спуск и оптимизировать с ним параметры моделей. \n", | |
"\n", | |
"К счастью, в tf также есть готовые оптимайзеры, которые мы можем использовать прямо из коробки.\n", | |
"Помните что-то про `momentum` и `rmsprop`?\n", | |
"\n", | |
"Подробнее про различные виды оптимайзеров можно почитать [тут](http://ruder.io/optimizing-gradient-descent/)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"y_guess = tf.Variable(np.zeros(2,dtype='float32'))\n", | |
"y_true = tf.range(1,3,dtype='float32')\n", | |
"\n", | |
"loss = tf.reduce_mean((y_guess - y_true + tf.random_normal([2]))**2) \n", | |
"\n", | |
"optimizer = tf.train.MomentumOptimizer(0.01,0.9).minimize(loss,var_list=y_guess)\n", | |
"\n", | |
"#same, but more detailed:\n", | |
"#updates = [[tf.gradients(loss,y_guess)[0], y_guess]]\n", | |
"#optimizer = tf.train.MomentumOptimizer(0.01,0.9).apply_gradients(updates)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"from IPython.display import clear_output\n", | |
"\n", | |
"s.run(tf.global_variables_initializer())\n", | |
"\n", | |
"guesses = [s.run(y_guess)]\n", | |
"\n", | |
"for _ in range(100):\n", | |
" s.run(optimizer)\n", | |
" guesses.append(s.run(y_guess))\n", | |
" \n", | |
" clear_output(True)\n", | |
" plt.plot(*zip(*guesses),marker='.')\n", | |
" plt.scatter(*s.run(y_true),c='red')\n", | |
" plt.show()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Логистическа регрессия своими руками\n", | |
"\n", | |
"**[4 балла]**\n", | |
"\n", | |
"Имплементируйте свою простенькую логрегрессию с блэкджеком и кроссэнтропией\n", | |
"\n", | |
"**Что потребуется**\n", | |
"* Используйте tf.Variable для весов\n", | |
"* X и y это входы\n", | |
"* Нужно реализовать 2 функции:\n", | |
" * `train_function(X,y)` - возвращает ошибку и изменяет веса на 1 шаг по граиденту (через updates)\n", | |
" * `predict_fun(X)` - возвращает предсказанные ответы (\"y\") по данным\n", | |
" \n", | |
" \n", | |
"Мы будем работать с двухклассовой версией датасета MNIST;\n", | |
"\n", | |
"Обратите внимание, что `y` принимает значения из `{0,1}` в исходной версии датасеты, а не `{-1,1}` (которые скорее всего вам нужны)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"from sklearn.datasets import load_digits\n", | |
"mnist = load_digits(2)\n", | |
"\n", | |
"X,y = mnist.data, mnist.target\n", | |
"\n", | |
"print(\"y [shape - %s]:\" % (str(y.shape)), y[:10])\n", | |
"print(\"X [shape - %s]:\" % (str(X.shape)))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"print('X:\\n',X[:3,:10])\n", | |
"print('y:\\n',y[:10])\n", | |
"plt.imshow(X[0].reshape([8,8]))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# inputs and shareds\n", | |
"weights = <student.code_variable()>\n", | |
"input_X = <student.code_placeholder()>\n", | |
"input_y = <student.code_placeholder()>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"predicted_y = <predicted probabilities for input_X>\n", | |
"loss = <logistic loss (scalar, mean over sample)>\n", | |
"\n", | |
"optimizer = <optimizer that minimizes loss>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"train_function = <compile function that takes X and y, returns log loss and updates weights>\n", | |
"predict_function = <compile function that takes X and computes probabilities of y>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"from sklearn.model_selection import train_test_split\n", | |
"X_train, X_test, y_train, y_test = train_test_split(X, y)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"from sklearn.metrics import roc_auc_score\n", | |
"\n", | |
"for i in range(5):\n", | |
" <run optimizer operation>\n", | |
" loss_i = <compute loss at iteration i>\n", | |
" \n", | |
" print(\"loss at iter %i:%.4f\" % (i, loss_i))\n", | |
" \n", | |
" print(\"train auc:\",roc_auc_score(y_train, predict_function(X_train)))\n", | |
" print(\"test auc:\",roc_auc_score(y_test, predict_function(X_test)))\n", | |
"\n", | |
" \n", | |
"print (\"resulting weights:\")\n", | |
"plt.imshow(shared_weights.get_value().reshape(8, -1))\n", | |
"plt.colorbar();" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Bonus: my1stNN\n", | |
"\n", | |
"### Нейроночка\n", | |
"\n", | |
"**[базовая часть - 4 балла]**\n", | |
"\n", | |
"Ваше финальное задание - сделать свою первую нейронку [почти] из спичек и желудей.\n", | |
"В этот раз распознавание цифр зашло чуть дальше:\n", | |
"\n", | |
"* картинки 28x28\n", | |
"* 10 классов\n", | |
"* 50k+ картинок только в обучающей выборке\n", | |
"\n", | |
"Вам не нужно (но можно) создавать монстров на 152 слоя, не нужно ничего сворачивать - достаточно сделать простую нейросеть с 1 скрытым и 1 выходным слоем, которая будет работать лучше логистической регрессии.\n", | |
"В конце есть шаблон отчёта - его желательно вести по ходу работ (ну или хотя бы не забыть).\n", | |
"\n", | |
"\n", | |
"**[bonus score]** Далее, если ваша сетка уже побила результаты линейной модели, а запал остался - можно попробовать улучшить результат ещё дальше. Челлендж - превзойти рубежи 95%/97.5%/98.5% точности на тесте без использования свёрток.\n", | |
"\n", | |
"\n", | |
"__СПОЙЛЕР!__\n", | |
"В конце тетрадки есть несколько советов по реализации. Если вы чувствуете в себе силы выстрелить в ногу самостоятельно - ваша воля, но наткнувшись на неразрешимые проблемы будьте добры прочитать \"хвост\" тетрадки, прежде, чем спрашивать семинариста." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"from mnist import load_dataset\n", | |
"\n", | |
"#качаем (по необходимости) и читаем данные.\n", | |
"#Важно, что для обучения можно использовать только train, \n", | |
"#а val - для оценки прогресса, сравнения можелей и early stopping.\n", | |
"#Тест вообще лучше положить под камень до самого конца, но ктоЖ вас поймает.\n", | |
"\n", | |
"X_train,y_train,X_val,y_val,X_test,y_test = load_dataset()\n", | |
"\n", | |
"print (X_train.shape,y_train.shape)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"plt.imshow(X_train[0,0])" | |
] | |
}, | |
{ | |
"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": [ | |
"<а тут - запилить цикл обучения и нужные метрики>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"<и наконец тут - предсказание на тесте - только честно!>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Отчёт\n", | |
"Я делал такое и такое.\n", | |
"\n", | |
"Потом попробовал вот-такое и алгоритм выдал вот-такое.\n", | |
"\n", | |
"А потом я его стукнул и он стал фиолетовый в крапинку.\n", | |
"\n", | |
"А ещё мне очень помогла вон-та статья и вот-этот сорт травы (if any)." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"```\n", | |
"\n", | |
"# SPOILERS\n", | |
"Рекомендуемый порядок:\n", | |
"\n", | |
"* Адаптировать логистическую регрессию на клаccификацию 1 цифры против всех (например, нулей против ненулей)\n", | |
"* Обобщить логистическую регрессию до многоклассовой.\n", | |
" - для этого придётся вспомнить первую лекцию или загуглить.\n", | |
" - вместо одного вектора весов у вас будет матрица (признак, класс)\n", | |
" - softmax (экспонента на сумму экспонент) можно сделать самому, а можно - tf.nn.softmax\n", | |
" - Лучше использовать стохастический градиентный спуск (минибатчевый)\n", | |
" - в котором случае выборку желательно перемешать (ну или брать случайный набор примеров на каждой итерации обучения)\n", | |
"* Добавить скрытый слой. Теперь ваша логистическая регрессия опирается на нейроны, а не на входы.\n", | |
" - Принцип работы первого слоя - такой же, как у выходного, но вместо softmax у него другая нелинейность.\n", | |
" - нужно обучать оба слоя, а не только выходной :)\n", | |
" - важно не инициализировать веса нулями из-за эффекта симметрии. Для начала - случайный нормальный шум с маленькой \"сигмой\".\n", | |
" - Начать рекоммендую с 50 нейронов и сигмоиды, ибо так труднее прострелить себе ногу.\n", | |
" - В идеале у вас будет 2 .dot-а, 1 sigmoid и 1 softmax.\n", | |
" - **Убедитесь, что такая нейронка выучивается лучше, чем логистическая регрессия**\n", | |
"* Теперь время подумать над тем, как улучшить результат. Слои, нейроны, нелинейности, методы оптимизации, инициализация - всё, что хотите, разве что я бы попросил в качестве челленджа обойтись пока без свёрток." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.6.2" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 1 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment