#!/usr/bin/env python # coding: utf-8 # # Оптимизация функций. Символьные вычисления # Шестаков А.В. Майнор по анализу данных - 02/02/2016 # Сегодня мы рассмотрим следущие темы: # # 1) Методы оптимизации функций в Python и графика с ней связанная # 2) Символьные вычисления # 3) Решение задачи линейной регрессии тремя способами! # In[ ]: import matplotlib.pyplot as plt from mpl_toolkits import mplot3d import numpy as np import scipy.optimize as opt import sympy plt.style.use('ggplot') get_ipython().run_line_magic('matplotlib', 'inline') # # Методы оптимизации функции в Python # ## Выпуклые функции # # Рассмотрим две функции: # In[ ]: x = np.linspace(-5, 5, 100) y1 = lambda(x): 0.5*x**2 + 10*np.sin(x) - 2 y2 = lambda(x): x**2 + 0.5**x - 4 # In[ ]: fig, ax = plt.subplots(1, 2, figsize=(14,7)) ax[0].plot(x, y1(x)) ax[0].set_ylabel('$y_1$', fontsize=20) ax[0].set_xlabel('$x$', fontsize=20) ax[1].plot(x, y2(x)) ax[1].set_ylabel('$y_2$', fontsize=20) ax[1].set_xlabel('$x$', fontsize=20) t = [0,5] alpha = 0.5 f1 = alpha*y1(t[0]) + (1-alpha)*y1(t[1]) f2 = y1(alpha*t[0] + (1-alpha)*t[1]) ax[0].plot(alpha*t[0] + (1-alpha)*t[1], f1, '*r') ax[0].plot(alpha*t[0] + (1-alpha)*t[1], f2, 'sb') # Функция $f(x)$ называется выпуклой (convex), если для любых двух точек $u$ и $v$ и для любого числа $\alpha \in [0,1]$ выполняется: # $$ f\left(\alpha u + [1-\alpha] v\right) \leq \alpha f(u) + (1-\alpha) f(v) $$ # # Какая из этих функций является выпуклой? И заодно посмотрим, что это за выражение такое. # Чем хороши выпуклые фукнции с точки хрения поиска оптимального значения? # Посмотрим на выпуклую функцию в 3D! # In[ ]: x = np.linspace(-5, 5, 100) y = np.linspace(-5, 5, 100) X, Y = np.meshgrid(x,y) Z = X**2 + 0.25*Y**2 # In[ ]: fig = plt.figure(figsize=(14, 7)) ax = fig.add_subplot(1, 2, 1, projection='3d') ax.view_init(40, 25) ax.plot_surface(X, Y, Z, alpha=0.3,) # ax.plot_(X, Y, Z) ax.set_xlabel('$x$') ax.set_ylabel('$y$') ax = fig.add_subplot(1, 2, 2) contour = ax.contour(X, Y, Z) plt.clabel(contour, inline=1, fontsize=10) ax.set_xlabel('$x$') ax.set_ylabel('$y$') # ## Гладкие функции # Гладкие функции (непрерывно дифференцируемые, smooth functions) удобны тем, что в любой точке можно посчитать её производую (градиент) # In[ ]: x = np.linspace(-5, 5, 100) y1 = lambda(x): 10*x**(2) y2 = lambda(x): abs(x) # In[ ]: fig, ax = plt.subplots(1, 2, figsize=(14,7)) ax[0].plot(x, y1(x)) ax[0].set_ylabel('$y_1$', fontsize=20) ax[0].set_xlabel('$x$', fontsize=20) ax[1].plot(x, y2(x)) ax[0].set_ylabel('$y_2$', fontsize=20) ax[0].set_xlabel('$x$', fontsize=20) # ## Поиск экстемумов в Python # В зависимости от того, какой функции вы смотрите в глаза, нужно выбирать свои методы. О описанием различных ситуаций и методов. # # Для вас есть хорошая новость. Как правило мы будем работать с выпуклыми и гладкими функциями. В общем случае это делается так: # In[ ]: # Задаём функцию def f(x): return x**2 + np.exp(x) - 4 # Находим минимум x_min = opt.minimize(f, -3) x_min # In[ ]: x = np.linspace(-5, 5, 100) plt.plot(x, f(x)) plt.plot(x_min['x'], x_min['fun'], 'sb') # # Символьные вычисления # Символьные вычисления в Python выполняются в модуле `sympy` # ### Числа # In[ ]: sympy.init_printing(use_latex=True) a = sympy.Rational(1, 2) print a print a + 2 # In[ ]: print sympy.pi print sympy.pi.evalf(n=4) # ### Выражения # In[ ]: x = sympy.Symbol('x') y, z = sympy.symbols('y z') w = (x + y)**2 # In[ ]: w # In[ ]: sympy.expand(w) # In[ ]: sympy.factor(x**2 - y**2) # In[ ]: sympy.simplify(w - 2*x*y) # In[ ]: sympy.simplify(sympy.sin(x)/sympy.cos(x)) # ### Дифференцирование # In[ ]: sympy.diff(w, x) # In[ ]: sympy.diff(w, x, 2) # ### Решение уравнений # In[ ]: sympy.solve(x**2 - 4, x) # In[ ]: res = sympy.solve([x + 2*y - 2, x + 3*y - 6], x, y) # In[ ]: res # In[ ]: type(res) # При оптимизации функций, для некоторых методлов необходимо расчитать градиент функций. # Используйте `sympy` если испытывайте какие-то сложности, затем, дополнительно проверяйте с `scipy.optimize.check_grad()` # In[ ]: sympy.init_printing(use_latex=False) # # Решение задачи линейной регрессии # Рассмотрим 3 наблюдения: # In[ ]: X = np.array([[1, 1], [1, 2], [1, 3]]) y = np.array([1, 2, 2]) plt.scatter(X[:,1], y) plt.xlabel('$x_1$') plt.ylabel('$y$') # Можем ли мы провести через них прямую? # Почему? # # Как бы вы решали эту задачу, если бы нужно было провести прямую через 2 точки? # ## Normal Equation # Если система $$ A x = b $$ не имеет решения, то решайте $$A^\top A x = A^\top b$$ # In[ ]: Beta = np.linalg.inv((X.T.dot(X))).dot(X.T).dot(y) print Beta # In[ ]: y_hat = X.dot(Beta) plt.scatter(X[:,1], y) plt.plot(X[:,1], y_hat) # ## Дифференцирование # $X$ - признаковое описание наблюдений, $y$ - прогнозируемая величина # # Пусть задана функция ошибки (функция потерь) $L(\cdot)$. # Нам надо построить такой функционал $f(X)$, который будет выдавать значение наиболее близкие к $y$, иначе говоря: $$L\left(f(X) - y\right) \rightarrow\min $$ # Определим функцию потерь, как сумму квадратов разности выдаваемого ответа функционала и реального значения: # $$ L(\cdot) = \frac{1}{2n}\sum_{i=1}^n(f(x^{(i)}) - y^{(i)})^2$$ # # Так как среди всего множества моделей мы выбрали линейную регрессию, то $$f(X) = \beta_0 + \beta_1x_1$$ # Подставляем это выражение в $L(\cdot)$ и находим $\beta_0$, # $\beta_1$! # ## Градиентный спуск (gradient descent) # Градиентый спукс - это итеративный метод оптимизации функции. Он заключается в постепенном перемещении к точке экспетмума в направлении градиента этой функции в точке. # # Пусть дана функция $L(\theta)$, необходимо найти $\hat{\theta}:\ L(\hat{\theta}) = \min L(\theta)$ # # Шаги алгоритма: # # 1. Случайным образом фиксируется начальное состояние # 2. Пока алгоритм не сойдется выполняется обновление: # $$ \theta = \theta - \alpha \frac{\partial L}{\partial \theta} $$ # где $\alpha$ - скорость спуска # **Пример:** # $$L(\theta) = 3\theta^2 + 2\theta - 10$$ # Найдем градиент (в данном случае простую производую, так как у нас одна переменная) # $$ \frac{\partial L}{\partial \theta} = 6\theta + 2$$ # In[ ]: def f(x): return 3*x**2 + 2*x - 10 def grad(x): return 6*x + 2 iter_max = 500 # максимальное кол-во итераций alpha = 0.01 # скорость спуска old_min = 0 temp_min = 4 precision = 0.001 # требуемая точность i = 0 mins = [temp_min] # значений на каждой итерации алгоритма cost = [] # разность с инстинным решением # Напишите код, реализующий данный алгоритм # while abs(temp_min - old_min) > precision and i