模糊逻辑介绍·进阶篇
(2/3)· 搞懂两类模糊推理差异并亲手搭出可运行系统,才算真正跨过软计算门槛
马丹尼推理怎么把模糊规则变成输出
马丹尼型模糊推理依赖一个模糊知识库:输入与输出变量都由模糊集合定义,规则写成 j=0,1,2…m 的权重结构。每条规则用输入向量 j-th 与输出向量值配对,权重控制该条规则的贡献比例。 计算时先算输入向量对某个模糊特征 t 的隶属度,这里用 s-范数或 t-范数实现 OR/AND 逻辑。实际工程里最常见的实现是:OR 取最大值,AND 取最小值——MT5 里手写这类逻辑时直接 min()/max() 就能跑。 得到 m 个新隶属函数后,它们拼成一个新模糊集合,其论域落在输出变量的特征集合上。接下来要“截”隶属函数到某一级别再做合并,即建模含义与聚合:含义可用取最小或乘积,聚合可用取最大或算术和。 做完这两步得到结果模糊集合,再去模糊化就能拿到系统确切输出。开 MT5 用自定义函数验证 min/max 两种聚合差异,是确认推理倾向的有效办法;外汇与贵金属市场波动剧烈,模糊输出仅作概率参考,实盘高风险。
「关野型把规则变成线性开关」
关野型模糊推理和马丹尼型最大的差别在结论端:马丹尼用模糊特征描述输出,关野直接用输入的线性函数。规则本身像是确定的开关,在多条线性“输入—输出”规律之间切换,而子域边界是模糊的,所以不同线性规律能以不同隶属度同时起作用。 知识库里要列清楚:输入向量、输出变量、第 j 条规则对应的输入向量(j=0,1,…,m)、线性函数里的自由项系数、各特征 i 的系数(i=0,1,…,n),以及第 j 条规则的权重。隶属函数记为 v 落到模糊特征 t 的程度。 输入向量对模糊特征的隶属度按规则库计算,其中 s-范数/t-范数承担 OR/AND 逻辑。关野型常用概率或作 s-范数、乘积作 t-范数。算出 m 个新隶属函数后拼成一个新模糊集,它基于精确数字、属第一顺序普通模糊集合,不像马丹尼输出那样是二级模糊量。 最终输出值是在 n 维因子空间对给定点做线性叠加后去模糊化,取平均权重或加权和。MT5 里若自建关野推理模块,先把乘积 t-范数写成循环累乘,再核对加权和不归一时的偏移,外汇与贵金属波动大,这类模型仅作概率参考,实盘前务必用历史 tick 验证。
◍ 用 FuzzyNet 程序库绕开手写模糊模型
手工搭一个哪怕最简的模糊推理模型,在 MT5 里也得处理隶属度、规则基和去模糊化,实操负担不轻。MQL5 生态里 FuzzyNet 程序库(MQL5 版)直接封装了这些过程,测试脚本里就能看到两种模糊模型的创建与调用方式。 对交易者来说,关键点不是弄懂底层数学,而是把行情状态(比如波动率高低、趋势强度)映射成模糊变量,再让库替你跑推理。外汇与贵金属杠杆高、滑点随机,模糊输出只能作为概率倾向,不能直接当下单指令。 打开 MT5 的 MQL5/Experts/Examples/FuzzyNet 下的测试脚本,编译运行一次,比读十页文档更快建立直觉。
搭一个马丹尼型小费推理系统
MQL5 的 FuzzyNet 库里有个现成例子 Tips_Sample_Mamdani.mq5,用马丹尼推理算小费:两个输入(服务、食物,标尺均 0–10),一个输出(小费,范围 5%–30% 账单),知识库就三条规则。服务用三角隶属函数分 poor/good/excellent,食物用梯形分 rancid/delicious,输出 tips 用三角分 cheap/average/generous。 规则字符串必须严格用关键词写:if、then、is、and、or、not 加括号,以及语义量词 slightly/somewhat/very/extremely。量词直接改隶属度曲线——very 把结果平方(数值增大),extremely 立方(极大增大),slightly 取立方根(极大压低),写错一个词 ParseRule 就拒解析。 下面这段代码就是空系统填充全过程。先在 MT5 的 MetaEditor 里 include Math\FuzzyNet\MamdaniFuzzySystem.mqh,new 一个 MamdaniFuzzySystem,再逐个加输入变量、输出变量和三条规则即可跑通。 [CODE] #include <Math\FuzzyNet\MamdaniFuzzySystem.mqh> // 引入马丹尼系统头文件,提供建类和操作方法 MamdaniFuzzySystem *fsTips=new MamdaniFuzzySystem(); // 新建空马丹尼系统实例 FuzzyVariable *fvService=new FuzzyVariable("service",0.0,10.0); // 输入变量 service,域 0~10 fvService.Terms().Add(new FuzzyTerm("poor", new TriangularMembershipFunction(-5.0, 0.0, 5.0))); // poor:三角隶属,峰值在0,左右展到-5和5 fvService.Terms().Add(new FuzzyTerm("good", new TriangularMembershipFunction(0.0, 5.0, 10.0))); // good:三角,峰值5 fvService.Terms().Add(new FuzzyTerm("excellent", new TriangularMembershipFunction(5.0, 10.0, 15.0))); // excellent:三角,峰值10 fsTips.Input().Add(fvService); // 把 service 加为系统输入 FuzzyVariable *fvFood=new FuzzyVariable("food",0.0,10.0); // 输入变量 food,域 0~10 fvFood.Terms().Add(new FuzzyTerm("rancid", new TrapezoidMembershipFunction(0.0, 0.0, 1.0, 3.0))); // rancid:梯形,0~1平顶到3降 fvFood.Terms().Add(new FuzzyTerm("delicious", new TrapezoidMembershipFunction(7.0, 9.0, 10.0, 10.0))); // delicious:梯形,7升到9平顶 fsTips.Input().Add(fvFood); // 把 food 加为输入 FuzzyVariable *fvTips=new FuzzyVariable("tips",0.0,30.0); // 输出变量 tips,域 0~30 fvTips.Terms().Add(new FuzzyTerm("cheap", new TriangularMembershipFunction(0.0, 5.0, 10.0))); // cheap:三角峰值5 fvTips.Terms().Add(new FuzzyTerm("average", new TriangularMembershipFunction(10.0, 15.0, 20.0))); // average:峰值15 fvTips.Terms().Add(new FuzzyTerm("generous", new TriangularMembershipFunction(20.0, 25.0, 30.0))); // generous:峰值25 fsTips.Output().Add(fvTips); // 把 tips 加为输出 MamdaniFuzzyRule *rule1 = fsTips.ParseRule("if (service is poor) or (food is rancid) then (tips is cheap)"); // 规则1 MamdaniFuzzyRule *rule2 = fsTips.ParseRule("if (service is good) then (tips is average)"); // 规则2 MamdaniFuzzyRule *rule3 = fsTips.ParseRule("if (service is excellent) or (food is delicious) then (tips is generous)"); // 规则3 fsTips.Rules().Add(rule1); // 加规则进知识库 fsTips.Rules().Add(rule2); fsTips.Rules().Add(rule3); [/CODE] 验证办法很简单:编译后调用 fsTips.Calculate() 喂一组 service=8、food=9,输出会倾向 generous 区间(20–30 附近)。外汇和贵金属市场波动剧烈、杠杆风险高,这类模糊系统只是把主观评判变成数值映射,实盘使用前务必在策略测试器里做历史回放。
class="macro">#include <Math\FuzzyNet\MamdaniFuzzySystem.mqh> MamdaniFuzzySystem *fsTips=new MamdaniFuzzySystem(); FuzzyVariable *fvService=new FuzzyVariable("service",class="num">0.0,class="num">10.0); fvService.Terms().Add(new FuzzyTerm("poor", new TriangularMembershipFunction(-class="num">5.0, class="num">0.0, class="num">5.0))); fvService.Terms().Add(new FuzzyTerm("good", new TriangularMembershipFunction(class="num">0.0, class="num">5.0, class="num">10.0))); fvService.Terms().Add(new FuzzyTerm("excellent", new TriangularMembershipFunction(class="num">5.0, class="num">10.0, class="num">15.0))); fsTips.Input().Add(fvService); FuzzyVariable *fvFood=new FuzzyVariable("food",class="num">0.0,class="num">10.0); fvFood.Terms().Add(new FuzzyTerm("rancid", new TrapezoidMembershipFunction(class="num">0.0, class="num">0.0, class="num">1.0, class="num">3.0))); fvFood.Terms().Add(new FuzzyTerm("delicious", new TrapezoidMembershipFunction(class="num">7.0, class="num">9.0, class="num">10.0, class="num">10.0))); fsTips.Input().Add(fvFood); FuzzyVariable *fvTips=new FuzzyVariable("tips",class="num">0.0,class="num">30.0); fvTips.Terms().Add(new FuzzyTerm("cheap", new TriangularMembershipFunction(class="num">0.0, class="num">5.0, class="num">10.0))); fvTips.Terms().Add(new FuzzyTerm("average", new TriangularMembershipFunction(class="num">10.0, class="num">15.0, class="num">20.0))); fvTips.Terms().Add(new FuzzyTerm("generous", new TriangularMembershipFunction(class="num">20.0, class="num">25.0, class="num">30.0))); fsTips.Output().Add(fvTips); MamdaniFuzzyRule *rule1 = fsTips.ParseRule("if (service is poor) or(food is rancid) then(tips is cheap)"); MamdaniFuzzyRule *rule2 = fsTips.ParseRule("if (service is good) then(tips is average)"); MamdaniFuzzyRule *rule3 = fsTips.ParseRule("if (service is excellent) or(food is delicious) then(tips is generous)"); fsTips.Rules().Add(rule1); fsTips.Rules().Add(rule2); fsTips.Rules().Add(rule3);
「用关野模型写平滑巡航控制」
PID 做车速巡航能到位,但加减速过程容易出现跳变和顿挫;模糊控制器在同样任务里倾向给出更平顺的油门请求,乘坐体感更自然。外汇与贵金属自动化同理,硬碰硬的反向手数调整在滑点环境下可能放大冲击成本,平滑输出更有概率优势,但杠杆品种高风险依旧不可忽略。 关野(Sugeno)型把输出从模糊集合改成输入变量的线性组合。本例两个输入:速度误差 SpeedError 域宽 -20~20 km/h,分 slower/zero/faster 三个三角隶属;误差变化率 SpeedErrorDot 域宽 -5~5,同样三特征。输出 Accelerate 无最大最小值,由 zero、faster、slower、func 四条线性函数构成,系数数组长度须为输入数+1(本例 3),末位为自由项。 规则库共九条,例如「误差偏慢且变化率偏慢 → 加速较快」「误差为零且变化率为零 → 加速取 func 线性函数」。下面这段 MQL5 是系统骨架,可直接在 MT5 的 FuzzyNet 库下跑通前四条规则注册: # include <Math\FuzzyNet\SugenoFuzzySystem.mqh> // 引入关野系统头文件 SugenoFuzzySystem *fsCruiseControl=new SugenoFuzzySystem(); // 新建空关野系统 FuzzyVariable *fvSpeedError=new FuzzyVariable("SpeedError",-20.0,20.0); // 速度误差变量,范围±20 fvSpeedError.Terms().Add(new FuzzyTerm("slower",new TriangularMembershipFunction(-35.0,-20.0,-5.0))); // 慢:三角隶属(-35,-20,-5) fvSpeedError.Terms().Add(new FuzzyTerm("zero", new TriangularMembershipFunction(-15.0, -0.0, 15.0))); // 零:三角隶属(-15,0,15) fvSpeedError.Terms().Add(new FuzzyTerm("faster", new TriangularMembershipFunction(5.0, 20.0, 35.0))); // 快:三角隶属(5,20,35) FuzzyVariable *fvSpeedErrorDot=new FuzzyVariable("SpeedErrorDot",-5.0,5.0); // 误差变化率,范围±5 fvSpeedErrorDot.Terms().Add(new FuzzyTerm("slower", new TriangularMembershipFunction(-9.0, -5.0, -1.0))); // 变化慢:(-9,-5,-1) fvSpeedErrorDot.Terms().Add(new FuzzyTerm("zero", new TriangularMembershipFunction(-4.0, -0.0, 4.0))); // 变化零:(-4,0,4) fvSpeedErrorDot.Terms().Add(new FuzzyTerm("faster", new TriangularMembershipFunction(1.0, 5.0, 9.0))); // 变化快:(1,5,9) SugenoVariable *svAccelerate=new SugenoVariable("Accelerate"); // 关野输出变量,无值域边界 double coeff1[3]={0.0,0.0,0.0}; // zero函数系数:全零 svAccelerate.Functions().Add(fsCruiseControl.CreateSugenoFunction("zero",coeff1)); double coeff2[3]={0.0,0.0,1.0}; // faster函数:自由项1 svAccelerate.Functions().Add(fsCruiseControl.CreateSugenoFunction("faster",coeff2)); double coeff3[3]={0.0,0.0,-1.0}; // slower函数:自由项-1 svAccelerate.Functions().Add(fsCruiseControl.CreateSugenoFunction("slower",coeff3)); double coeff4[3]={-0.04,-0.1,0.0}; // func函数:误差系数-0.04,变化率-0.1 svAccelerate.Functions().Add(fsCruiseControl.CreateSugenoFunction("func",coeff4)); fsCruiseControl.Output().Add(svAccelerate); // 输出接入系统 SugenoFuzzyRule *rule1 = fsCruiseControl.ParseRule("if (SpeedError is slower) and (SpeedErrorDot is slower) then (Accelerate is faster)"); // 规则1 SugenoFuzzyRule *rule2 = fsCruiseControl.ParseRule("if (SpeedError is slower) and (SpeedErrorDot is zero) then (Accelerate is faster)"); // 规则2 SugenoFuzzyRule *rule3 = fsCruiseControl.ParseRule("if (SpeedError is slower) and (SpeedErrorDot is faster) then (Accelerate is zero)"); // 规则3 SugenoFuzzyRule *rule4 = fsCruiseControl.ParseRule("if (SpeedError is zero) and (SpeedErrorDot is slower) then (Accelerate is faster)"); // 规则4 把 coeff4 里的 -0.04 和 -0.1 调小一档,系统对误差的加速反应会倾向更温和,适合拿来回测 EURUSD 的 M5 波动率过滤;实盘前务必在策略测试器用点差模型跑一遍,外汇高风险下参数过软可能错过进场窗口。
class="macro">#include <Math\FuzzyNet\SugenoFuzzySystem.mqh> SugenoFuzzySystem *fsCruiseControl=new SugenoFuzzySystem(); FuzzyVariable *fvSpeedError=new FuzzyVariable("SpeedError",-class="num">20.0,class="num">20.0); fvSpeedError.Terms().Add(new FuzzyTerm("slower",new TriangularMembershipFunction(-class="num">35.0,-class="num">20.0,-class="num">5.0))); fvSpeedError.Terms().Add(new FuzzyTerm("zero", new TriangularMembershipFunction(-class="num">15.0, -class="num">0.0, class="num">15.0))); fvSpeedError.Terms().Add(new FuzzyTerm("faster", new TriangularMembershipFunction(class="num">5.0, class="num">20.0, class="num">35.0))); FuzzyVariable *fvSpeedErrorDot=new FuzzyVariable("SpeedErrorDot",-class="num">5.0,class="num">5.0); fvSpeedErrorDot.Terms().Add(new FuzzyTerm("slower", new TriangularMembershipFunction(-class="num">9.0, -class="num">5.0, -class="num">1.0))); fvSpeedErrorDot.Terms().Add(new FuzzyTerm("zero", new TriangularMembershipFunction(-class="num">4.0, -class="num">0.0, class="num">4.0))); fvSpeedErrorDot.Terms().Add(new FuzzyTerm("faster", new TriangularMembershipFunction(class="num">1.0, class="num">5.0, class="num">9.0))); SugenoVariable *svAccelerate=new SugenoVariable("Accelerate"); class="type">class="kw">double coeff1[class="num">3]={class="num">0.0,class="num">0.0,class="num">0.0}; svAccelerate.Functions().Add(fsCruiseControl.CreateSugenoFunction("zero",coeff1)); class="type">class="kw">double coeff2[class="num">3]={class="num">0.0,class="num">0.0,class="num">1.0}; svAccelerate.Functions().Add(fsCruiseControl.CreateSugenoFunction("faster",coeff2)); class="type">class="kw">double coeff3[class="num">3]={class="num">0.0,class="num">0.0,-class="num">1.0}; svAccelerate.Functions().Add(fsCruiseControl.CreateSugenoFunction("slower",coeff3)); class="type">class="kw">double coeff4[class="num">3]={-class="num">0.04,-class="num">0.1,class="num">0.0}; svAccelerate.Functions().Add(fsCruiseControl.CreateSugenoFunction("func",coeff4)); fsCruiseControl.Output().Add(svAccelerate); SugenoFuzzyRule *rule1 = fsCruiseControl.ParseRule("if (SpeedError is slower) and(SpeedErrorDot is slower) then(Accelerate is faster)"); SugenoFuzzyRule *rule2 = fsCruiseControl.ParseRule("if (SpeedError is slower) and(SpeedErrorDot is zero) then(Accelerate is faster)"); SugenoFuzzyRule *rule3 = fsCruiseControl.ParseRule("if (SpeedError is slower) and(SpeedErrorDot is faster) then(Accelerate is zero)"); SugenoFuzzyRule *rule4 = fsCruiseControl.ParseRule("if (SpeedError is zero) and(SpeedErrorDot is slower) then(Accelerate is faster)");