Skip to content

Instantly share code, notes, and snippets.

@fogside
Created August 23, 2017 10:20
Show Gist options
  • Save fogside/0ff06c704d16cf0b769252a213d69578 to your computer and use it in GitHub Desktop.
Save fogside/0ff06c704d16cf0b769252a213d69578 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"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
}
Display the source blob
Display the rendered blob
Raw
{
"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