十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
线性模型(二)之多项式拟合
创新互联建站-专业网站定制、快速模板网站建设、高性价比海林网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式海林网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖海林地区。费用合理售后完善,10余年实体公司更值得信赖。
1. 多项式拟合问题
多项式拟合(polynominal curve fitting)是一种线性模型,模型和拟合参数的关系是线性的。多项式拟合的输入是一维的,即x=xx=x,这是多项式拟合和线性回归问题的主要区别之一。
多项式拟合的目标是构造输入xx的MM阶多项式函数,使得该多项式能够近似表示输入xx和输出yy的关系,虽然实际上xx和yy的关系并不一定是多项式,但使用足够多的阶数,总是可以逼近表示输入xx和输出yy的关系的。
多项式拟合问题的输入可以表示如下:
D={(x1,y1),(x2,y2),...,(xi,yi),...,(xN,yN)}xi∈Ryi∈R
D={(x1,y1),(x2,y2),...,(xi,yi),...,(xN,yN)}xi∈Ryi∈R
目标输出是得到一个多项式函数:
f(x)=w1x1+w2x2+wixi+...+wMxM+b=(∑i=1Mwixi)+b
f(x)=w1x1+w2x2+wixi+...+wMxM+b=(∑i=1Mwixi)+b
其中MM表示最高阶数为MM。
可见在线性拟合的模型中,共包括了(M+1)(M+1)个参数,而该模型虽然不是输入xx的线性函数,但却是(M+1)(M+1)个拟合参数的线性函数,所以称多项式拟合为线性模型。对于多项式拟合问题,其实就是要确定这(M+1)(M+1)个参数,这里先假设阶数MM是固定的(MM是一个超参数,可以用验证集来确定MM最优的值,详细的关于MM值确定的问题,后面再讨论),重点就在于如何求出这(M+1)(M+1)个参数的值。
2.优化目标
多项式拟合是利用多项式函数逼近输入xx和输出yy的函数关系,通过什么指标来衡量某个多项式函数的逼近程度呢?(其实这就是误差/损失函数)。拟合/回归问题常用的评价指标是均方误差(在机器学习中的模型评估与度量博客中,我进行了介绍)。多项式拟合问题也同样采用该评价指标,以均方误差作为误差/损失函数,误差函数越小,模型越好。
E(w,b)=1N∑i=1N[f(xi)−yi]2
E(w,b)=1N∑i=1N[f(xi)−yi]2
系数1N1N是一常数,对优化结果无影响,可以去除,即将均方误差替换为平方误差:
E(w,b)=∑i=1N[f(xi)−yi]2
E(w,b)=∑i=1N[f(xi)−yi]2
到这里,就成功把多项式拟合问题变成了最优化问题,优化问题可表示为:
argminw,bE(w,b)
argminw,bE(w,b)
即需要求得参数{w1,...,wM,b}{w1,...,wM,b}的值,使得E(w,b)E(w,b)最小化。那么如何对该最优化问题求解呢?
3. 优化问题求解
3.1 求偏导,联立方程求解
直观的想法是,直接对所有参数求偏导,令偏导为0,再联立这M+1M+1个方程求解(因为共有M+1M+1个参数,故求偏导后也是得到M+1M+1个方程)。
E(w,b)=∑i=1N[f(xi)−yi]2=∑i=1N[(w1x1i+w2x2i+wixji+...+wMxMi+b)−yi]2
E(w,b)=∑i=1N[f(xi)−yi]2=∑i=1N[(w1xi1+w2xi2+wixij+...+wMxiM+b)−yi]2
利用E(w,b)E(w,b)对各个参数求偏导,如下:
∂E(w,b)∂wj∂E(w,b)∂b=2∑i=1N[(w1x1i+w2x2i+wixji+...+wMxMi+b)−yi]xji=2∑i=1N[(w1x1i+w2x2i+wixji+...+wMxMi+b)−yi]
∂E(w,b)∂wj=2∑i=1N[(w1xi1+w2xi2+wixij+...+wMxiM+b)−yi]xij∂E(w,b)∂b=2∑i=1N[(w1xi1+w2xi2+wixij+...+wMxiM+b)−yi]
求导之后,将各个点(xi,yi)(xi,yi)的值带入偏导公式,联立方程求解即可。
针对该解法,可以举个例子详细说明,比如有两个点(2,3),(5,8)(2,3),(5,8),需要利用二阶多项式f(x)=w1x+w2x2+bf(x)=w1x+w2x2+b拟合。求解过程如下:
该二阶多项式对参数求偏导得到
∂E(w,b)∂wj∂E(w,b)∂b=2∑i=12[(w1x1i+w2x2i+b)−yi]xji=[(w1x1+w2x21+b)−y1]xj1+[(w1x2+w2x22+b)−y2]xj2=2∑i=12[(w1x1i+w2x2i+b)−yi]=[(w1x1+w2x21+b)−y1]+[(w1x2+w2x22+b)−y2]
∂E(w,b)∂wj=2∑i=12[(w1xi1+w2xi2+b)−yi]xij=[(w1x1+w2x12+b)−y1]x1j+[(w1x2+w2x22+b)−y2]x2j∂E(w,b)∂b=2∑i=12[(w1xi1+w2xi2+b)−yi]=[(w1x1+w2x12+b)−y1]+[(w1x2+w2x22+b)−y2]
将点(2,3),(5,8)(2,3),(5,8)带入方程,可以得到3个方程,
2b+7w1+29w2=117b+29w1+133w2=4629b+133w1+641w2=212
2b+7w1+29w2=117b+29w1+133w2=4629b+133w1+641w2=212
联立这三个方程求解,发现有无穷多的解,只能得到3w1+21w2=53w1+21w2=5,这三个方程是线性相关的,故没有唯一解。
该方法通过求偏导,再联立方程求解,比较复杂,看着也很不美观。那么有没有更加方便的方法呢?
3.2 最小二乘法
其实求解该最优化问题(平方和的最小值)一般会采用最小二乘法(其实最小二乘法和求偏导再联立方程求解的方法无本质区别,求偏导也是最小二乘法,只是这里介绍最小二乘的矩阵形式而已)。最小二乘法(least squares),从英文名非常容易想到,该方法就是求解平方和的最小值的方法。
可以将误差函数以矩阵的表示(NN个点,最高MM阶)为:
∥Xw−y∥2
‖Xw−y‖2
其中,把偏置bb融合到了参数ww中,
w={b,w1,w2,...,wM}
w={b,w1,w2,...,wM}
XX则表示输入矩阵,
⎡⎣⎢⎢⎢⎢11...1x1x2...xNx21x22...x2N............xM1xM2...xMN⎤⎦⎥⎥⎥⎥
[1x1x12...x1M1x2x22...x2M...............1xNxN2...xNM]
yy则表示标注向量,
y={y1,y2,...,yN}T
y={y1,y2,...,yN}T
因此,最优化问题可以重新表示为
minw∥Xw−y∥2
minw‖Xw−y‖2
对其求导,
∂∥Xw−y∥2∂w=∂(Xw−y)T(Xw−y)∂w=∂(wTXT−yT)(Xw−y)∂w=∂(wTXTXw−yTXw−wTXTy+yTy)∂w
∂‖Xw−y‖2∂w=∂(Xw−y)T(Xw−y)∂w=∂(wTXT−yT)(Xw−y)∂w=∂(wTXTXw−yTXw−wTXTy+yTy)∂w
在继续对其求导之前,需要先补充一些矩阵求导的先验知识(常见的一些矩阵求导公式可以参见转载的博客),如下:
∂xTa∂x=a∂ax∂x=aT∂xTA∂x=Ax+ATx
∂xTa∂x=a∂ax∂x=aT∂xTA∂x=Ax+ATx
根据上面的矩阵求导规则,继续进行损失函数的求导
∂∥Xw−y∥2∂w=∂(wTXTXw−yTXw−wTXTy+yTy)∂w=XTXw+(XTX)Tw−(yTX)T−XTy=2XTXw−2XTy
∂‖Xw−y‖2∂w=∂(wTXTXw−yTXw−wTXTy+yTy)∂w=XTXw+(XTX)Tw−(yTX)T−XTy=2XTXw−2XTy
其中XTXw=(XTX)TwXTXw=(XTX)Tw.令求导结果等于0,即可以求导问题的最小值。
2XTXw−2XTy=0w=(XTX)−1XTy
2XTXw−2XTy=0w=(XTX)−1XTy
再利用最小二乘法的矩阵形式对前面的例子进行求解,用二阶多项式拟合即两个点(2,3),(5,8)(2,3),(5,8)。
表示输入矩阵 XX和标签向量yy
X=[1125425]y=[38]T
X=[1241525]y=[38]T
计算XTXXTX
XTX=⎡⎣⎢272972913329133641⎤⎦⎥
XTX=[272972913329133641]
矩阵求逆,再做矩阵乘法运算
但 XTXXTX不可逆,故无唯一解。
关于矩阵的逆是否存在,可以通过判断矩阵的行列式是否为0(det(A)=?0det(A)=?0 来判断,也可以通过初等行变换,观察矩阵的行向量是否线性相关,在这个例子下,矩阵不可逆,故有无穷多解。但如果新增一个点(4,7)(4,7),则就可以解了。
其实这和数据集的点数和选择的阶数有关,如果点数小于阶数则会出现无穷解的情况,如果点数等于阶数,那么刚好有解可以完全拟合所有数据点,如果点数大于阶数,则会求的近似解。
那么对于点数小于阶数的情况,如何求解?在python的多项式拟合函数中是可以拟合的,而且效果不错,具体算法不是很了解,可以想办法参考python的ployfit()函数的实现。
4. 拟合阶数的选择
在前面的推导中,多项式的阶数被固定了,那么实际场景下应该如何选择合适的阶数MM呢?
一般会选择阶数MM小于点数NN
把训练数据分为训练集合验证集,在训练集上,同时用不同的MM值训练多个模型,然后选择在验证集误差最小的阶数script type="math/tex" id="MathJax-Element-5573"M/script
求对 x 的偏导数,视 y 为常量,对 x 求导;
求对 y 的偏导数,视 x 为常量, 对 y 求导。
则:∂f/∂x = 4-2x, ∂f/∂y = -4-2y
偏导数 f'x(x0,y0) 表示固定面上一点对 x 轴的切线斜率;偏导数 f'y(x0,y0) 表示固定面上一点对 y 轴的切线斜率。
扩展资料:
将多元函数关于一个自变量求偏导数时,就将其余的自变量看成常数,此时求导方法与一元函数导数的求法是一样的。
把 x 固定在 x0,让 y 有增量 △y ,如果极限存在那么此极限称为函数 z=(x,y) 在 (x0,y0)处对 y 的偏导数。
偏函数是将所要承载的函数作为partial()函数的第一个参数,原函数的各个参数依次作为partial()函数后续的参数,除非使用关键字参数。
通过语言描述可能无法理解偏函数是怎么使用的,那么就举一个常见的例子来说明。在这个例子里,我们实现了一个取余函数,对于整数100,取得对于不同数m的100%m的余数。
print(“字符串”),5/2和5//2的结果是不同的5/2为2.5,5//2为2.
python2需要导入from_future_import division执行普通的除法。
1/2和1//2的结果0.5和0.
%号为取模运算。
乘方运算为2**3,-2**3和-(2**3)是等价的。
from sympy import*导入库
x,y,z=symbols('x y z'),定义变量
init_printing(use_unicode=True)设置打印方式。
python的内部常量有pi,
函数simplify,simplify(sin(x)**2 + cos(x)**2)化简结果为1,
simplify((x**3 + x**2 - x - 1)/(x**2 + 2*x + 1))化简结果为x-1。化简伽马函数。simplify(gamma(x)/gamma(x - 2))得(x-2)(x-1)。
expand((x + 1)**2)展开多项式。
expand((x + 1)*(x - 2) - (x - 1)*x)
因式分解。factor(x**2*z + 4*x*y*z + 4*y**2*z)得到z*(x + 2*y)**2
from_future_import division
x,y,z,t=symbols('x y z t')定义变量,
k, m, n = symbols('k m n', integer=True)定义三个整数变量。
f, g, h = symbols('f g h', cls=Function)定义的类型为函数。
factor_list(x**2*z + 4*x*y*z + 4*y**2*z)得到一个列表,表示因式的幂,(1, [(z, 1), (x + 2*y, 2)])
expand((cos(x) + sin(x))**2)展开多项式。
expr = x*y + x - 3 + 2*x**2 - z*x**2 + x**3,collected_expr = collect(expr, x)将x合并。将x元素按阶次整合。
collected_expr.coeff(x, 2)直接取出变量collected_expr的x的二次幂的系数。
cancel()is more efficient thanfactor().
cancel((x**2 + 2*x + 1)/(x**2 + x))
,expr = (x*y**2 - 2*x*y*z + x*z**2 + y**2 - 2*y*z + z**2)/(x**2 - 1),cancel(expr)
expr = (4*x**3 + 21*x**2 + 10*x + 12)/(x**4 + 5*x**3 + 5*x**2 + 4*x),apart(expr)
asin(1)
trigsimp(sin(x)**2 + cos(x)**2)三角函数表达式化简,
trigsimp(sin(x)**4 - 2*cos(x)**2*sin(x)**2 + cos(x)**4)
trigsimp(sin(x)*tan(x)/sec(x))
trigsimp(cosh(x)**2 + sinh(x)**2)双曲函数。
三角函数展开,expand_trig(sin(x + y)),acos(x),cos(acos(x)),expand_trig(tan(2*x))
x, y = symbols('x y', positive=True)正数,a, b = symbols('a b', real=True)实数,z, t, c = symbols('z t c')定义变量的方法。
sqrt(x) == x**Rational(1, 2)判断是否相等。
powsimp(x**a*x**b)幂函数的乘法,不同幂的乘法,必须先定义a和b。powsimp(x**a*y**a)相同幂的乘法。
powsimp(t**c*z**c),注意,powsimp()refuses to do the simplification if it is not valid.
powsimp(t**c*z**c, force=True)这样的话就可以得到化简过的式子。声明强制进行化简。
(z*t)**2,sqrt(x*y)
第一个展开expand_power_exp(x**(a + b)),expand_power_base((x*y)**a)展开,
expand_power_base((z*t)**c, force=True)强制展开。
powdenest((x**a)**b),powdenest((z**a)**b),powdenest((z**a)**b, force=True)
ln(x),x, y ,z= symbols('x y z', positive=True),n = symbols('n', real=True),
expand_log(log(x*y))展开为log(x) + log(y),但是python3没有。这是因为需要将x定义为positive。这是必须的,否则不会被展开。expand_log(log(x/y)),expand_log(log(x**n))
As withpowsimp()andpowdenest(),expand_log()has aforceoption that can be used to ignore assumptions。
expand_log(log(z**2), force=True),强制展开。
logcombine(log(x) + log(y)),logcombine(n*log(x)),logcombine(n*log(z), force=True)。
factorial(n)阶乘,binomial(n, k)等于c(n,k),gamma(z)伽马函数。
hyper([1, 2], [3], z),
tan(x).rewrite(sin)得到用正弦表示的正切。factorial(x).rewrite(gamma)用伽马函数重写阶乘。
expand_func(gamma(x + 3))得到,x*(x + 1)*(x + 2)*gamma(x),
hyperexpand(hyper([1, 1], [2], z)),
combsimp(factorial(n)/factorial(n - 3))化简,combsimp(binomial(n+1, k+1)/binomial(n, k))化简。combsimp(gamma(x)*gamma(1 - x))
自定义函数
def list_to_frac(l):
expr = Integer(0)
for i in reversed(l[1:]):
expr += i
expr = 1/expr
return l[0] + expr
list_to_frac([x, y, z])结果为x + 1/z,这个结果是错误的。
syms = symbols('a0:5'),定义syms,得到的结果为(a0, a1, a2, a3, a4)。
这样也可以a0, a1, a2, a3, a4 = syms, 可能是我的操作错误 。发现python和自动缩进有关,所以一定看好自动缩进的距离。list_to_frac([1, 2, 3, 4])结果为43/30。
使用cancel可以将生成的分式化简,frac = cancel(frac)化简为一个分数线的分式。
(a0*a1*a2*a3*a4 + a0*a1*a2 + a0*a1*a4 + a0*a3*a4 + a0 + a2*a3*a4 + a2 + a4)/(a1*a2*a3*a4 + a1*a2 + a1*a4 + a3*a4 + 1)
a0, a1, a2, a3, a4 = syms定义a0到a4,frac = apart(frac, a0)可将a0提出来。frac=1/(frac-a0)将a0去掉取倒。frac = apart(frac, a1)提出a1。
help("modules"),模块的含义,help("modules yourstr")模块中包含的字符串的意思。,
help("topics"),import os.path + help("os.path"),help("list"),help("open")
# -*- coding: UTF-8 -*-声明之后就可以在ide中使用中文注释。
定义
l = list(symbols('a0:5'))定义列表得到[a0, a1, a2, a3, a4]
fromsympyimport*
x,y,z=symbols('x y z')
init_printing(use_unicode=True)
diff(cos(x),x)求导。diff(exp(x**2), x),diff(x**4, x, x, x)和diff(x**4, x, 3)等价。
diff(expr, x, y, 2, z, 4)求出表达式的y的2阶,z的4阶,x的1阶导数。和diff(expr, x, y, y, z, 4)等价。expr.diff(x, y, y, z, 4)一步到位。deriv = Derivative(expr, x, y, y, z, 4)求偏导。但是不显示。之后用deriv.doit()即可显示
integrate(cos(x), x)积分。定积分integrate(exp(-x), (x, 0, oo))无穷大用2个oo表示。integrate(exp(-x**2-y**2),(x,-oo,oo),(y,-oo,oo))二重积分。print(expr)print的使用。
expr = Integral(log(x)**2, x),expr.doit()积分得到x*log(x)**2 - 2*x*log(x) + 2*x。
integ.doit()和integ = Integral((x**4 + x**2*exp(x) - x**2 - 2*x*exp(x) - 2*x -
exp(x))*exp(x)/((x - 1)**2*(x + 1)**2*(exp(x) + 1)), x)连用。
limit(sin(x)/x,x,0),not-a-number表示nan算不出来,limit(expr, x, oo),,expr = Limit((cos(x) - 1)/x, x, 0),expr.doit()连用。左右极限limit(1/x, x, 0, '+'),limit(1/x, x, 0, '-')。。
Series Expansion级数展开。expr = exp(sin(x)),expr.series(x, 0, 4)得到1 + x + x**2/2 + O(x**4),,x*O(1)得到O(x),,expr.series(x, 0, 4).removeO()将无穷小移除。exp(x-6).series(x,x0=6),,得到
-5 + (x - 6)**2/2 + (x - 6)**3/6 + (x - 6)**4/24 + (x - 6)**5/120 + x + O((x - 6)**6, (x, 6))最高到5阶。
f=Function('f')定义函数变量和h=Symbol('h')和d2fdx2=f(x).diff(x,2)求2阶,,as_finite_diff(dfdx)函数和as_finite_diff(d2fdx2,[-3*h,-h,2*h]),,x_list=[-3,1,2]和y_list=symbols('a b c')和apply_finite_diff(1,x_list,y_list,0)。
Eq(x, y),,solveset(Eq(x**2, 1), x)解出来x,当二式相等。和solveset(Eq(x**2 - 1, 0), x)等价。solveset(x**2 - 1, x)
solveset(x**2 - x, x)解,solveset(x - x, x, domain=S.Reals)解出来定义域。solveset(exp(x), x) # No solution exists解出EmptySet()表示空集。
等式形式linsolve([x + y + z - 1, x + y + 2*z - 3 ], (x, y, z))和矩阵法linsolve(Matrix(([1, 1, 1, 1], [1, 1, 2, 3])), (x, y, z))得到{(-y - 1, y, 2)}
A*x = b 形式,M=Matrix(((1,1,1,1),(1,1,2,3))),system=A,b=M[:,:-1],M[:,-1],linsolve(system,x,y,z),,solveset(x**3 - 6*x**2 + 9*x, x)解多项式。roots(x**3 - 6*x**2 + 9*x, x),得出,{3: 2, 0: 1},有2个3的重根,1个0根。solve([x*y - 1, x - 2], x, y)解出坐标。
f, g = symbols('f g', cls=Function)函数的定义,解微分方程diffeq = Eq(f(x).diff(x, x) - 2*f(x).diff(x) + f(x), sin(x))再和dsolve(diffeq,f(x))结合。得到Eq(f(x), (C1 + C2*x)*exp(x) + cos(x)/2),dsolve(f(x).diff(x)*(1 - sin(f(x))), f(x))解出来Eq(f(x) + cos(f(x)), C1),,
Matrix([[1,-1],[3,4],[0,2]]),,Matrix([1, 2, 3])列表示。M=Matrix([[1,2,3],[3,2,1]])
N=Matrix([0,1,1])
M*N符合矩阵的乘法。M.shape显示矩阵的行列数。
M.row(0)获取M的第0行。M.col(-1)获取倒数第一列。
M.col_del(0)删掉第1列。M.row_del(1)删除第二行,序列是从0开始的。M = M.row_insert(1, Matrix([[0, 4]]))插入第二行,,M = M.col_insert(0, Matrix([1, -2]))插入第一列。
M+N矩阵相加,M*N,3*M,M**2,M**-1,N**-1表示求逆。M.T求转置。
eye(3)单位。zeros(2, 3),0矩阵,ones(3, 2)全1,diag(1, 2, 3)对角矩阵。diag(-1, ones(2, 2), Matrix([5, 7, 5]))生成Matrix([
[-1, 0, 0, 0],
[ 0, 1, 1, 0],
[ 0, 1, 1, 0],
[ 0, 0, 0, 5],
[ 0, 0, 0, 7],
[ 0, 0, 0, 5]])矩阵。
Matrix([[1, 0, 1], [2, -1, 3], [4, 3, 2]])
一行一行显示,,M.det()求行列式。M.rref()矩阵化简。得到结果为Matrix([
[1, 0, 1, 3],
[0, 1, 2/3, 1/3],
[0, 0, 0, 0]]), [0, 1])。
M = Matrix([[1, 2, 3, 0, 0], [4, 10, 0, 0, 1]]),M.nullspace()
Columnspace
M.columnspace()和M = Matrix([[1, 2, 3, 0, 0], [4, 10, 0, 0, 1]])
M = Matrix([[3, -2, 4, -2], [5, 3, -3, -2], [5, -2, 2, -2], [5, -2, -3, 3]])和M.eigenvals()得到{3: 1, -2: 1, 5: 2},,This means thatMhas eigenvalues -2, 3, and 5, and that the eigenvalues -2 and 3 have algebraic multiplicity 1 and that the eigenvalue 5 has algebraic multiplicity 2.
P, D = M.diagonalize(),P得Matrix([
[0, 1, 1, 0],
[1, 1, 1, -1],
[1, 1, 1, 0],
[1, 1, 0, 1]]),,D为Matrix([
[-2, 0, 0, 0],
[ 0, 3, 0, 0],
[ 0, 0, 5, 0],
[ 0, 0, 0, 5]])
P*D*P**-1 == M返回为True。lamda = symbols('lamda')。
lamda = symbols('lamda')定义变量,p = M.charpoly(lamda)和factor(p)
expr = x**2 + x*y,srepr(expr)可以将表达式说明计算法则,"Add(Pow(Symbol('x'), Integer(2)), Mul(Symbol('x'), Symbol('y')))"。。
x = symbols('x')和x = Symbol('x')是一样的。srepr(x**2)得到"Pow(Symbol('x'), Integer(2))"。Pow(x, 2)和Mul(x, y)得到x**2。x*y
type(2)得到class 'int',type(sympify(2))得到class 'sympy.core.numbers.Integer'..srepr(x*y)得到"Mul(Symbol('x'), Symbol('y'))"。。。
Add(Pow(x, 2), Mul(x, y))得到"Add(Mul(Integer(-1), Pow(Symbol('x'), Integer(2))), Mul(Rational(1, 2), sin(Mul(Symbol('x'), Symbol('y')))), Pow(Symbol('y'), Integer(-1)))"。。Pow函数为幂次。
expr = Add(x, x),expr.func。。Integer(2).func,class 'sympy.core.numbers.Integer',,Integer(0).func和Integer(-1).func,,,expr = 3*y**2*x和expr.func得到class 'sympy.core.mul.Mul',,expr.args将表达式分解为得到(3, x, y**2),,expr.func(*expr.args)合并。expr == expr.func(*expr.args)返回True。expr.args[2]得到y**2,expr.args[1]得到x,expr.args[0]得到3.。
expr.args[2].args得到(y, 2)。。y.args得到空括号。Integer(2).args得到空括号。
from sympy import *
E**(I*pi)+1,可以看出,I和E,pi已将在sympy内已定义。
x=Symbol('x'),,expand( E**(I*x) )不能展开,expand(exp(I*x),complex=True)可以展开,得到I*exp(-im(x))*sin(re(x)) + exp(-im(x))*cos(re(x)),,x=Symbol("x",real=True)将x定义为实数。再展开expand(exp(I*x),complex=True)得到。I*sin(x) + cos(x)。。
tmp = series(exp(I*x), x, 0, 10)和pprint(tmp)打印出来可读性好,print(tmp)可读性不好。。pprint将公式用更好看的格式打印出来,,pprint( series( cos(x), x, 0, 10) )
integrate(x*sin(x), x),,定积分integrate(x*sin(x), (x, 0, 2*pi))。。
用双重积分求解球的体积。
x, y, r = symbols('x,y,r')和2 * integrate(sqrt(r*r-x**2), (x, -r, r))计算球的体积。计算不来,是因为sympy不知道r是大于0的。r = symbols('r', positive=True)这样定义r即可。circle_area=2*integrate(sqrt(r**2-x**2),(x,-r,r))得到。circle_area=circle_area.subs(r,sqrt(r**2-x**2))将r替换。
integrate(circle_area,(x,-r,r))再积分即可。
expression.sub([(x,y),(y,x)])又换到原来的状况了。
expression.subs(x, y),,将算式中的x替换成y。。
expression.subs({x:y,u:v}) : 使用字典进行多次替换。。
expression.subs([(x,y),(u,v)]) : 使用列表进行多次替换。。
最优化
为什么要做最优化呢?因为在生活中,人们总是希望幸福值或其它达到一个极值,比如做生意时希望成本最小,收入最大,所以在很多商业情境中,都会遇到求极值的情况。
函数求根
这里「函数的根」也称「方程的根」,或「函数的零点」。
先把我们需要的包加载进来。import numpy as npimport scipy as spimport scipy.optimize as optimport matplotlib.pyplot as plt%matplotlib inline
函数求根和最优化的关系?什么时候函数是最小值或最大值?
两个问题一起回答:最优化就是求函数的最小值或最大值,同时也是极值,在求一个函数最小值或最大值时,它所在的位置肯定是导数为 0 的位置,所以要求一个函数的极值,必然要先求导,使其为 0,所以函数求根就是为了得到最大值最小值。
scipy.optimize 有什么方法可以求根?
可以用 scipy.optimize 中的 bisect 或 brentq 求根。f = lambda x: np.cos(x) - x # 定义一个匿名函数x = np.linspace(-5, 5, 1000) # 先生成 1000 个 xy = f(x) # 对应生成 1000 个 f(x)plt.plot(x, y); # 看一下这个函数长什么样子plt.axhline(0, color='k'); # 画一根横线,位置在 y=0
opt.bisect(f, -5, 5) # 求取函数的根0.7390851332155535plt.plot(x, y)plt.axhline(0, color='k')plt.scatter([_], [0], c='r', s=100); # 这里的 [_] 表示上一个 Cell 中的结果,这里是 x 轴上的位置,0 是 y 上的位置
求根有两种方法,除了上面介绍的 bisect,还有 brentq,后者比前者快很多。%timeit opt.bisect(f, -5, 5)%timeit opt.brentq(f, -5, 5)10000 loops, best of 3: 157 s per loopThe slowest run took 11.65 times longer than the fastest. This could mean that an intermediate result is being cached.10000 loops, best of 3: 35.9 s per loop
函数求最小化
求最小值就是一个最优化问题。求最大值时只需对函数做一个转换,比如加一个负号,或者取倒数,就可转成求最小值问题。所以两者是同一问题。
初始值对最优化的影响是什么?
举例来说,先定义个函数。f = lambda x: 1-np.sin(x)/xx = np.linspace(-20., 20., 1000)y = f(x)
当初始值为 3 值,使用 minimize 函数找到最小值。minimize 函数是在新版的 scipy 里,取代了以前的很多最优化函数,是个通用的接口,背后是很多方法在支撑。x0 = 3xmin = opt.minimize(f, x0).x # x0 是起始点,起始点最好离真正的最小值点不要太远plt.plot(x, y)plt.scatter(x0, f(x0), marker='o', s=300); # 起始点画出来,用圆圈表示plt.scatter(xmin, f(xmin), marker='v', s=300); # 最小值点画出来,用三角表示plt.xlim(-20, 20);
初始值为 3 时,成功找到最小值。
现在来看看初始值为 10 时,找到的最小值点。x0 = 10xmin = opt.minimize(f, x0).xplt.plot(x, y)plt.scatter(x0, f(x0), marker='o', s=300)plt.scatter(xmin, f(xmin), marker='v', s=300)plt.xlim(-20, 20);
由上图可见,当初始值为 10 时,函数找到的是局部最小值点,可见 minimize 的默认算法对起始点的依赖性。
那么怎么才能不管初始值在哪个位置,都能找到全局最小值点呢?
如何找到全局最优点?
可以使用 basinhopping 函数找到全局最优点,相关背后算法,可以看帮助文件,有提供论文的索引和出处。
我们设初始值为 10 看是否能找到全局最小值点。x0 = 10from scipy.optimize import basinhoppingxmin = basinhopping(f,x0,stepsize = 5).xplt.plot(x, y);plt.scatter(x0, f(x0), marker='o', s=300);plt.scatter(xmin, f(xmin), marker='v', s=300);plt.xlim(-20, 20);
当起始点在比较远的位置,依然成功找到了全局最小值点。
如何求多元函数最小值?
以二元函数为例,使用 minimize 求对应的最小值。def g(X): x,y = X return (x-1)**4 + 5 * (y-1)**2 - 2*x*yX_opt = opt.minimize(g, (8, 3)).x # (8,3) 是起始点print X_opt[ 1.88292611 1.37658521]fig, ax = plt.subplots(figsize=(6, 4)) # 定义画布和图形x_ = y_ = np.linspace(-1, 4, 100)X, Y = np.meshgrid(x_, y_)c = ax.contour(X, Y, g((X, Y)), 50) # 等高线图ax.plot(X_opt[0], X_opt[1], 'r*', markersize=15) # 最小点的位置是个元组ax.set_xlabel(r"$x_1$", fontsize=18)ax.set_ylabel(r"$x_2$", fontsize=18)plt.colorbar(c, ax=ax) # colorbar 表示颜色越深,高度越高fig.tight_layout()
画3D 图。from mpl_toolkits.mplot3d import Axes3Dfrom matplotlib import cmfig = plt.figure()ax = fig.gca(projection='3d')x_ = y_ = np.linspace(-1, 4, 100)X, Y = np.meshgrid(x_, y_)surf = ax.plot_surface(X, Y, g((X,Y)), rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)cset = ax.contour(X, Y, g((X,Y)), zdir='z',offset=-5, cmap=cm.coolwarm)fig.colorbar(surf, shrink=0.5, aspect=5);
曲线拟合
曲线拟合和最优化有什么关系?
曲线拟合的问题是,给定一组数据,它可能是沿着一条线散布的,这时要找到一条最优的曲线来拟合这些数据,也就是要找到最好的线来代表这些点,这里的最优是指这些点和线之间的距离是最小的,这就是为什么要用最优化问题来解决曲线拟合问题。
举例说明,给一些点,找到一条线,来拟合这些点。
先给定一些点:N = 50 # 点的个数m_true = 2 # 斜率b_true = -1 # 截距dy = 2.0 # 误差np.random.seed(0)xdata = 10 * np.random.random(N) # 50 个 x,服从均匀分布ydata = np.random.normal(b_true + m_true * xdata, dy) # dy 是标准差plt.errorbar(xdata, ydata, dy, fmt='.k', ecolor='lightgray');
上面的点整体上呈现一个线性关系,要找到一条斜线来代表这些点,这就是经典的一元线性回归。目标就是找到最好的线,使点和线的距离最短。要优化的函数是点和线之间的距离,使其最小。点是确定的,而线是可变的,线是由参数值,斜率和截距决定的,这里就是要通过优化距离找到最优的斜率和截距。
点和线的距离定义如下:def chi2(theta, x, y): return np.sum(((y - theta[0] - theta[1] * x)) ** 2)
上式就是误差平方和。
误差平方和是什么?有什么作用?
误差平方和公式为:
误差平方和大,表示真实的点和预测的线之间距离太远,说明拟合得不好,最好的线,应该是使误差平方和最小,即最优的拟合线,这里是条直线。
误差平方和就是要最小化的目标函数。
找到最优的函数,即斜率和截距。theta_guess = [0, 1] # 初始值theta_best = opt.minimize(chi2, theta_guess, args=(xdata, ydata)).xprint(theta_best)[-1.01442005 1.93854656]
上面两个输出即是预测的直线斜率和截距,我们是根据点来反推直线的斜率和截距,那么真实的斜率和截距是多少呢?-1 和 2,很接近了,差的一点是因为有噪音的引入。xfit = np.linspace(0, 10)yfit = theta_best[0] + theta_best[1] * xfitplt.errorbar(xdata, ydata, dy, fmt='.k', ecolor='lightgray');plt.plot(xfit, yfit, '-k');
最小二乘(Least Square)是什么?
上面用的是 minimize 方法,这个问题的目标函数是误差平方和,这就又有一个特定的解法,即最小二乘。
最小二乘的思想就是要使得观测点和估计点的距离的平方和达到最小,这里的“二乘”指的是用平方来度量观测点与估计点的远近(在古汉语中“平方”称为“二乘”),“最小”指的是参数的估计值要保证各个观测点与估计点的距离的平方和达到最小。
关于最小二乘估计的计算,涉及更多的数学知识,这里不想详述,其一般的过程是用目标函数对各参数求偏导数,并令其等于 0,得到一个线性方程组。具体推导过程可参考斯坦福机器学习讲义 第 7 页。def deviations(theta, x, y): return (y - theta[0] - theta[1] * x)theta_best, ier = opt.leastsq(deviations, theta_guess, args=(xdata, ydata))print(theta_best)[-1.01442016 1.93854659]
最小二乘 leastsq 的结果跟 minimize 结果一样。注意 leastsq 的第一个参数不再是误差平方和 chi2,而是误差本身 deviations,即没有平方,也没有和。yfit = theta_best[0] + theta_best[1] * xfitplt.errorbar(xdata, ydata, dy, fmt='.k', ecolor='lightgray');plt.plot(xfit, yfit, '-k');
非线性最小二乘
上面是给一些点,拟合一条直线,拟合一条曲线也是一样的。def f(x, beta0, beta1, beta2): # 首先定义一个非线性函数,有 3 个参数 return beta0 + beta1 * np.exp(-beta2 * x**2)beta = (0.25, 0.75, 0.5) # 先猜 3 个 betaxdata = np.linspace(0, 5, 50)y = f(xdata, *beta)ydata = y + 0.05 * np.random.randn(len(xdata)) # 给 y 加噪音def g(beta): return ydata - f(xdata, *beta) # 真实 y 和 预测值的差,求最优曲线时要用到beta_start = (1, 1, 1)beta_opt, beta_cov = opt.leastsq(g, beta_start)print beta_opt # 求到的 3 个最优的 beta 值[ 0.25525709 0.74270226 0.54966466]
拿估计的 beta_opt 值跟真实的 beta = (0.25, 0.75, 0.5) 值比较,差不多。fig, ax = plt.subplots()ax.scatter(xdata, ydata) # 画点ax.plot(xdata, y, 'r', lw=2) # 真实值的线ax.plot(xdata, f(xdata, *beta_opt), 'b', lw=2) # 拟合的线ax.set_xlim(0, 5)ax.set_xlabel(r"$x$", fontsize=18)ax.set_ylabel(r"$f(x, \beta)$", fontsize=18)fig.tight_layout()
除了使用最小二乘,还可以使用曲线拟合的方法,得到的结果是一样的。beta_opt, beta_cov = opt.curve_fit(f, xdata, ydata)print beta_opt[ 0.25525709 0.74270226 0.54966466]
有约束的最小化
有约束的最小化是指,要求函数最小化之外,还要满足约束条件,举例说明。
边界约束def f(X): x, y = X return (x-1)**2 + (y-1)**2 # 这是一个碗状的函数x_opt = opt.minimize(f, (0, 0), method='BFGS').x # 无约束最优化
假设有约束条件,x 和 y 要在一定的范围内,如 x 在 2 到 3 之间,y 在 0 和 2 之间。bnd_x1, bnd_x2 = (2, 3), (0, 2) # 对自变量的约束x_cons_opt = opt.minimize(f, np.array([0, 0]), method='L-BFGS-B', bounds=[bnd_x1, bnd_x2]).x # bounds 矩形约束fig, ax = plt.subplots(figsize=(6, 4))x_ = y_ = np.linspace(-1, 3, 100)X, Y = np.meshgrid(x_, y_)c = ax.contour(X, Y, f((X,Y)), 50)ax.plot(x_opt[0], x_opt[1], 'b*', markersize=15) # 没有约束下的最小值,蓝色五角星ax.plot(x_cons_opt[0], x_cons_opt[1], 'r*', markersize=15) # 有约束下的最小值,红色星星bound_rect = plt.Rectangle((bnd_x1[0], bnd_x2[0]), bnd_x1[1] - bnd_x1[0], bnd_x2[1] - bnd_x2[0], facecolor="grey")ax.add_patch(bound_rect)ax.set_xlabel(r"$x_1$", fontsize=18)ax.set_ylabel(r"$x_2$", fontsize=18)plt.colorbar(c, ax=ax)fig.tight_layout()
不等式约束
介绍下相关理论,先来看下存在等式约束的极值问题求法,比如下面的优化问题。
目标函数是 f(w),下面是等式约束,通常解法是引入拉格朗日算子,这里使用 ββ 来表示算子,得到拉格朗日公式为
l 是等式约束的个数。
然后分别对 w 和ββ 求偏导,使得偏导数等于 0,然后解出 w 和βiβi,至于为什么引入拉格朗日算子可以求出极值,原因是 f(w) 的 dw 变化方向受其他不等式的约束,dw的变化方向与f(w)的梯度垂直时才能获得极值,而且在极值处,f(w) 的梯度与其他等式梯度的线性组合平行,因此他们之间存在线性关系。(参考《最优化与KKT条件》)
对于不等式约束的极值问题
常常利用拉格朗日对偶性将原始问题转换为对偶问题,通过解对偶问题而得到原始问题的解。该方法应用在许多统计学习方法中。有兴趣的可以参阅相关资料,这里不再赘述。def f(X): return (X[0] - 1)**2 + (X[1] - 1)**2def g(X): return X[1] - 1.75 - (X[0] - 0.75)**4x_opt = opt.minimize(f, (0, 0), method='BFGS').xconstraints = [dict(type='ineq', fun=g)] # 约束采用字典定义,约束方式为不等式约束,边界用 g 表示x_cons_opt = opt.minimize(f, (0, 0), method='SLSQP', constraints=constraints).xfig, ax = plt.subplots(figsize=(6, 4))x_ = y_ = np.linspace(-1, 3, 100)X, Y = np.meshgrid(x_, y_)c = ax.contour(X, Y, f((X, Y)), 50)ax.plot(x_opt[0], x_opt[1], 'b*', markersize=15) # 蓝色星星,没有约束下的最小值ax.plot(x_, 1.75 + (x_-0.75)**4, '', markersize=15)ax.fill_between(x_, 1.75 + (x_-0.75)**4, 3, color="grey")ax.plot(x_cons_opt[0], x_cons_opt[1], 'r*', markersize=15) # 在区域约束下的最小值ax.set_ylim(-1, 3)ax.set_xlabel(r"$x_0$", fontsize=18)ax.set_ylabel(r"$x_1$", fontsize=18)plt.colorbar(c, ax=ax)fig.tight_layout()
scipy.optimize.minimize 中包括了多种最优化算法,每种算法使用范围不同,详细参考官方文档。