一、问题
比如要求解非线性方程组
(1)sini+sinθ=1.2
(2)θ−i=33
如果直接去求解会非常困难,但是可以尝试使用拟合的方式
pytorch虽然是深度学习常用库,但是对于求解非线性的拟合问题还是非常可靠的。
二、求解
步骤1:确定变量
首先分析方程,有两个未知数,分别是θ和i,并且它们都是角度制的
于是将它们写在一个字典中,用于存放 角度变量
''' 角度变量 ''' cfg_rad = { "i":30, "theta":40, }
另外一般变量,或者弧度制的可以放到一般变量中比如没有出现角度的情况
但是此例子没有用到一般变量
''' 一般变量 ''' cfg_constant = { }
步骤2:设置模型
这一步不用多看,其实就是将cfg中的变量,加载到模型中,也不需要修改,使用的时候直接复制就行了
class model(nn.Module): def __init__(self): super(model, self).__init__() for key,value in cfg_rad.items(): setattr(self,key,nn.Parameter(torch.tensor(value)/180*np.pi)) for key,value in cfg_constant.items(): setattr(self,key,nn.Parameter(torch.tensor(value))) def rt_cfg(self): return *cfg_rad.keys(), *cfg_constant.keys() def forward(self): return [getattr(self,tmp) for tmp in self.rt_cfg()]
步骤3:初始化变量
只需要在定义变量最佳值加入 best_i、best_theta就行
net = model() optimizer = torch.optim.SGD(net.parameters(), lr=0.001) epoch = 1000 best_loss = 10 ** 5 ''' 变量最佳值 ''' best_i = None best_theta = None
步骤4:设置目标函数
这里就是输入我们的目标方程
对于sini+sinθ=1.2,我们可以写成(sini+sinθ−1.2)2
对于θ−i=33°,写成θ−i−33/180∗π -----------------------因为传入模型的时候对角度变量手动转换成弧度制了
loss = (torch.sin(i) + torch.sin(theta) - 1.2) ** 2 + (theta - i - torch.tensor(33 / 180 * np.pi)) ** 2
步骤5:常规训练过程
注意当损失比较大难以收敛的时候,可以调整学习率参数lr
optimizer = torch.optim.SGD(net.parameters(), lr=0.001)
步骤6:查看结果
可以看到误差达到了小数点后4位
以及求解的i和θ
三、完整的代码
只需要修改加注释的部分即可
import torch import numpy as np from torch import nn ''' 角度变量 ''' cfg_rad = { "i":30, "theta":40, } ''' 一般变量 ''' cfg_constant = { } class model(nn.Module): def __init__(self): super(model, self).__init__() for key,value in cfg_rad.items(): setattr(self,key,nn.Parameter(torch.tensor(value)/180*np.pi)) for key,value in cfg_constant.items(): setattr(self,key,nn.Parameter(torch.tensor(value))) def rt_cfg(self): return *cfg_rad.keys(), *cfg_constant.keys() def forward(self): return [getattr(self,tmp) for tmp in self.rt_cfg()] def train(): net = model() optimizer = torch.optim.SGD(net.parameters(), lr=0.001) epoch = 1000 best_loss = 10 ** 5 ''' 变量最佳值 ''' best_i = None best_theta = None for _ in range(epoch): ''' 变量值 ''' i, theta = net() # theta = i+torch.tensor(33/180*np.pi) ''' 目标方程 ''' loss = (torch.sin(i) + torch.sin(theta) - 1.2) ** 2 + (theta - i - torch.tensor(33 / 180 * np.pi)) ** 2 optimizer.zero_grad() loss.backward() optimizer.step() if loss < best_loss: ''' 修改 ''' best_loss = loss best_i = i best_theta = theta print(loss) ''' 输出变量值 ''' print("i:", best_i / np.pi * 180) print("theta:", best_theta / np.pi * 180) if __name__ == '__main__': train()
四、(补充)求解规划问题
比如要添加θ≥0这一个条件时候,如何添加限制呢?
模型中的参数默认是−∞到+∞之间的,因此可以通过换元法解决。
要设置θ≥0 可以设置变量theta_mega, 其中theta_mega区间是无穷的
另theta=theta_mega**2
这样子得到的theta就是大于等于0了
让模型去学习参数theta_mega,但实际上使用变量theta就行了。
或者可以套用相关激活函数,如sigmoid,将值限制在-1~1之间,通过缩放和平移,可以将值控制到任意闭合区间。
另外除了现有的激活函数,还可以手动设计函数,控制变量的范围。
最终可以达到全局最优解。