首页 / 资讯中心 / 文章详情

云模型MATLAB赋权代码:正向/逆向云发生器与综合评价

云模型MATLAB赋权代码:正向/逆向云发生器与综合评价 ★ FEATURED ARTICLE
简介云模型MATLAB实现面向需要将云模型理论落地到权重计算与综合评价场景的学习者提供可直接运行的脚本适合课程实验、论文复现与算法验证等用途。压缩包仅1KB共2个.m文件体量轻便其中包含主流程脚本与云变换、云滴生成相关函数围绕期望、熵、超熵等参数完成定性概念与定量数值之间的转换并可用于指标权重确定与隶属度计算。已有1581人学习下载说明其在MATLAB云模型入门与权重求取方面具有一定参考价值。读者拿到后可直接在MATLAB中打开运行结合主脚本观察参数设置对云滴分布和权重结果的影响也可将核心函数嵌入自己的评价模型、决策分析或数据预处理流程减少从公式推导到代码实现的重复工作。文件数量少、结构直接便于快速定位调用关系适合作为小体量工具脚本参考与二次修改的基础。1. 综合评价卡在赋权这一步这套云模型 MATLAB 代码能接住什么做评价类项目最常见的卡点就是权重AHP 判断矩阵一致性过不了来回调只能硬凑模糊综合评判要人先定隶属度函数定完自己都不太信。云模型换了个思路把“某个指标有多重要”当成一个带随机性的模糊概念用期望 Ex、熵 En、超熵 He 三个数去描述MATLAB 里几十行就能算完。手头这份云模型 MATLAB 代码主要干三件事正向云发生器把三个参数摊成一堆云滴逆向云发生器从专家打分反推参数再把 Ex 归一化成权重向量。做风险评估、方案优选、教学质量评价这类多指标打分的活或者课程设计里被要求“用 MATLAB 云模型算权重”这套代码可以直接当骨架改不用从零推公式。2. 工程结构与数字特征跑之前先把 Ex/En/He 对齐2.1 文件清单与外部依赖这类“直接能用”的包结构一般都很扁平一个主脚本串流程旁边拆几个函数文件命名直白到看名字就知道干嘛。常见做法是下面这个布局拿到手先对照着数一遍缺哪个补哪个文件作用是否必须forwardCloud.m正向云发生器参数转云滴必须backwardCloud.m逆向云发生器样本转参数必须cloudWeight.m专家打分矩阵算权重权重场景必须plotCloud.m云滴散点图方便出图可选demo.m / main.m演示脚本串起上面几个可选data/*.mat示例专家打分数据可选依赖上最常见的写法会用到normrnd、var、mean、std这几个其中normrnd属于 Statistics and Machine Learning Toolbox。装的是精简版 MATLAB、只有基础模块的话forwardCloud第一行就会给你一句Undefined function normrnd。替代方案不复杂randn是基础函数语义完全等价代价只是放弃和normrnd在同一个随机种子下的行为对齐% 没有统计工具箱时用 randn 手写两层正态抽样 En_i En He .* randn(n, 1); % 第一层En 本身服从 N(En, He^2) x Ex En_i .* randn(n, 1); % 第二层云滴服从 N(Ex, En_i^2)En和He是标量randn(n,1)生成 n 行 1 列标准正态样本逐元素相乘后得到 n 个En_i再拿去做第二次抽样。这样写出来的结果和normrnd版本在统计意义上看不出差别只是每次运行的具体数值会变。我一般先原样跑一遍看报错里有没有normrnd有就换这两行比装工具箱省事。另一个容易忽略的点是路径。函数文件不在当前工作目录、也不在搜索路径里MATLAB 只会报“未定义函数”不会告诉你文件其实已经找到了。跑之前先pwd看一眼或者在脚本开头显式addpath(genpath(代码目录))这一步花不了十秒钟能省掉后面半小时的自我怀疑。2.2 Ex、En、He 各自在管什么三个参数名字听着抽象落到图上其实很好认。把云滴画成散点横轴是指标取值纵轴是确定度可以理解成“这个取值的可信程度”整片点会形成一座中间高、两边低的钟形山参数名字直观含义变大时图形怎么变赋权时怎么用Ex期望云的重心横坐标最可能的取值整体左右平移归一化后就是权重En熵模糊度云滴在横轴上散开的宽度云变“胖”覆盖范围变宽反映指标重要性判断的不确定范围He超熵熵的熵En 自身的离散程度云的边缘变“毛”离群点增多反映专家意见分歧大小Ex最好理解就是这批打分的中心。En决定这座山有多宽En越大取值的模糊区间越大。关键在于He它不是给云滴加噪声而是给En本身加噪声。同样一堆点He小的时候边缘干净像一条规整的钟形曲线He大的时候边缘糊成一片会出现少量离确定度中心很远的点。做赋权的时候Ex拿去归一化成权重He用来判断专家是不是吵起来了——He明显偏大的那个指标说明专家对它的重要性分歧严重权重给得再准也是虚的。举个具体数字Ex70, En5云滴大致落在 60 到 80 之间把He从 1 提到 3大部分点还是在这个区间但两侧会甩出去一些跑到 55 或 85 的点。所以调参的顺序一直是先定Ex再定En最后用He控制边缘的松散程度反过来调会很痛苦。2.3 五分钟跑通的最小验证在改任何代码之前先做一次最小闭环验证确认正向和逆向两条链路都是通的% 第一步给三个参数生成 2000 个云滴 [x, y] forwardCloud(70, 5, 1, 2000); % 第二步画出来看形状对不对 figure; plotCloud(70, 5, 1, 2000, [0.2 0.4 0.8]); % 第三步拿这批云滴反推参数看回来多少 [Ex2, En2, He2] backwardCloud(x); fprintf(Ex%.3f En%.3f He%.3f\n, Ex2, En2, He2);预期结果是图上一个以 70 为中心、横向铺开约 20 个单位宽的点云最高点附近确定度接近 1逆向算回来的三个数大致是 70、5、1但一般不会严丝合缝地相等。这个“回不去”不是你写错了而是有限样本带来的固有偏差2 万点和 2000 点算出来的He能差出小半个数量级。第一次跑先接受这个误差等到要往论文里写数字的时候再回来处理第 4 章会专门讲怎么把它压下去。如果这一步图是空的、或者只出来一个点八成是n传成了 1或者plot用的是o而不是.点太小看不见。这种问题不值得动脑直接检查参数顺序即可。3. 正向云发生器从三个参数生成一片云滴3.1 函数实现与两层抽样正向云发生器的全部工作量就是把Ex/En/He三个数变成一堆二维点。核心只有四行但每一行都对应一个概念缩写成一行反而不好排错function [x, y] forwardCloud(Ex, En, He, n) % FORWARDCLOUD 一维正向云发生器 % Ex : 期望云的重心横坐标 % En : 熵反映模糊度 % He : 超熵反映 En 自身的离散程度 % n : 云滴个数不给就默认 1000 if nargin 4, n 1000; end if He 0 warning(forwardCloud: He 应为非负已取绝对值); He abs(He); end En_i normrnd(En, He, n, 1); % 第一层随机En 自身也是随机变量 En_i max(En_i, eps); % 防止 En_i 恰好为 0 导致后面除零 x normrnd(Ex, En_i); % 第二层随机以 En_i 为标准差生成云滴 y exp(-(x - Ex).^2 ./ (2 * En_i.^2)); % 确定度Ex 处取最大值 1 end逻辑说明上第一层normrnd(En, He)生成的不是云滴而是 n 个“临时熵”。这批临时熵本身服从均值为En、标准差为He的正态分布这正是超熵的数学定义。第二层再以Ex为均值、以每个En_i为标准差生成横坐标x得到云滴的位置。最后一行按正态隶属函数算确定度x离Ex越远y越接近 0。参数上有两个细节值得盯。En_i加max(En_i, eps)是防御性写法理论上En_i取到 0 或负数的概率极低但一旦发生2*En_i.^2就是 0y直接变Inf或NaN图上会出现一条竖线或者整片空白非常难查。He取绝对值是另一层保险超熵在定义上非负但不小心传了负数进去normrnd会直接报错退出不如自己拦一道。x normrnd(Ex, En_i)这里用的是向量形式En_i是 n×1 的列向量MATLAB 会自动按列配对输出也是 n×1。这个写法比 for 循环快一个数量级2000 个点基本是瞬间出结果10 万个点也不到一秒。3.2 为什么要抽两次而不是一次搞定初学的时候最容易产生的疑问是既然x服从正态分布为什么不直接x normrnd(Ex, En, n, 1)非要中间绕一层这一层就是云模型和普通正态分布的分界线。只抽一次得到的是一族固定宽度的钟形点不管生成多少点边缘都是光滑的。抽两次之后每个云滴的“宽度”都不太一样有的点来自En_i4的分布有的来自En_i6的分布堆起来以后钟形曲线的边缘自然就毛了。这层毛边不是噪声而是模型故意保留的随机性——它表示“这个指标的重要性判断本身就不稳定”。He控制的就是这层毛边的厚度。He 0的时候En_i恒等于En退化成普通正态分布图上是一条干净的钟形He逐渐加大边缘开始发虚离群点变多。做赋权的时候某个指标的He明显大于其他指标说明专家内部没谈拢这时候光看Ex算出来的权重是有风险的正确做法是回去重新收集一轮打分或者把这个指标的权重区间放宽。3.3 用参数对照实验把三个数看明白光看公式记不住最快的办法是固定两个、扫一个一次画四张图并排放figure; params [70 3 0.5; 70 8 0.5; 70 5 1; 70 5 4]; % 每行一组 Ex/En/He for k 1:4 subplot(2, 2, k); plotCloud(params(k,1), params(k,2), params(k,3), 3000, [0.25 0.45 0.75]); title(sprintf(Ex%d En%d He%d, params(k,1), params(k,2), params(k,3))); end跑完对照着看第一张和第二张只改了En山的宽度明显不同第三张和第四张只改了He宽度差不多但第四张边缘散得多。这四张图截下来放进报告里比写一段文字解释三个参数直观得多。plotCloud这个封装函数本身没什么技术含量就是把forwardCloud的结果和一个红色十字标在(Ex, 1)的位置function plotCloud(Ex, En, He, n, color) % PLOTCLOUD 画云滴散点图Ex 处用十字标出重心 [x, y] forwardCloud(Ex, En, He, n); plot(x, y, ., Color, color, MarkerSize, 8); hold on; plot(Ex, 1, r, MarkerSize, 12, LineWidth, 1.5); xlabel(指标取值 x); ylabel(确定度 \mu(x)); ylim([0 1.05]); grid on; endMarkerSize调大一点、透明度加一点MarkerFaceAlpha能让密集区域更好看不过这些纯属出图偏好不影响计算结果。真正要注意的是ylim([0 1.05])这行不加的话确定度接近 0 的离群点会把纵轴拉得很长图看起来就是一片稀疏散点完全看不出钟形。4. 逆向云发生器与云模型赋权专家打分怎么变成权重向量4.1 从一列样本反推三个参数逆向云发生器解决的是反问题手上只有一堆打分没有确定度要把Ex/En/He估出来。基于一阶矩的算法不需要确定度信息只用样本本身这也是这个包最可能采用的版本function [Ex, En, He] backwardCloud(x, varargin) % BACKWARDCLOUD 一维逆向云发生器基于一阶矩无需确定度 % x : 云滴样本列向量 % biased : 可选方差用 1/n 计算默认用 1/(n-1) x x(:); n numel(x); if n 3 error(backwardCloud: 样本数至少为 3当前为 %d, n); end Ex mean(x); % 期望直接用样本均值 En sqrt(pi/2) * mean(abs(x - Ex)); % 一阶绝对中心矩反推熵 if any(strcmpi(varargin, biased)) S2 var(x, 1); else S2 var(x, 0); end He2 S2 - En^2; % 二阶中心矩减去 En^2 if He2 0 warning(backwardCloud: 方差小于 En^2He 已置 0); He2 0; end He sqrt(He2); end三步逻辑对应三个公式。Ex用样本均值这一步没有争议。En那行是算法的关键对于标准云模型|x - Ex|的期望等于En * sqrt(2/pi)把样本的绝对偏差均值求出来再乘sqrt(pi/2)就还原出En。最后一步用二阶中心矩因为云滴的总方差等于En^2 He^2减掉En^2剩下的就是He^2开方得到He。参数上biased这个开关控制方差分母用 n 还是 n-1。样本量大的时候两者几乎没差别样本量小于 30 的时候用 1/(n-1) 更稳。n 3直接报错而不是返回 NaN是因为三个以下的点连方差都算不出来与其让它静默出错不如在这里断住。He2 0的警告是这套算法最常见的异常出现原因是样本方差比En^2还小数学上不可能出现负的超熵。真实场景里它通常意味着样本太集中或者样本量太小导致估计失真。后面第 5 章会专门展开讲。4.2 专家打分矩阵转权重向量赋权这一步是整个包的使用入口。常见的输入形态是一个 m×n 的矩阵m 个专家、n 个指标每个格子是 1 到 10 的重要性打分输出是一个 1×n 的权重向量function [w, cloud] cloudWeight(score) % CLOUDWEIGHT 基于云模型的指标赋权 % score : m x n 专家打分矩阵m 个专家、n 个指标 % 返回 % w : 1 x n 归一化权重和为 1 % cloud : 结构体字段 Ex/En/He 各为 1 x n [m, n] size(score); if m 3 warning(cloudWeight: 专家人数少于 3En/He 估计不稳定); end if any(score(:) 0) error(cloudWeight: 打分矩阵存在负值请检查输入); end Ex mean(score, 1); % 每个指标的期望 En sqrt(pi/2) * mean(abs(score - Ex), 1); % 每个指标的熵 S2 var(score, 0, 1); % 每个指标的样本方差 He sqrt(max(S2 - En.^2, 0)); % 每个指标的超熵 if sum(Ex) 0 error(cloudWeight: 期望之和为 0无法归一化); end w Ex / sum(Ex); % 用期望归一化作为权重 cloud struct(Ex, Ex, En, En, He, He); end逻辑说明上mean(score, 1)里的1是按列求均值也就是对每个指标把所有专家的打分平均得到 1×n 的Ex。同理mean(abs(score - Ex), 1)是逐列求绝对偏差均值var(score, 0, 1)是逐列求样本方差。三个参数都对齐到指标维度之后最后一步用Ex除以它的总和权重向量天然满足归一化。参数上max(..., 0)保证He不会因为浮点误差开出一个虚数这一点在批量处理多个指标时特别重要只要有一个指标出虚数整个权重向量就废了。sum(Ex) 0的检查是防止全零打分实际项目里更常见的是专家都打了同一个分这时候Ex全相等归一化后权重全是1/n看着没毛病但完全没有区分度得回去看数据。调用的时候一行就够score [8 6 7 9; 7 7 6 8; 9 5 7 9; 8 6 8 7]; [w, cloud] cloudWeight(score); disp(array2table(w, VariableNames, {w1,w2,w3,w4}));四列权重加起来等于 1cloud.He里哪个数明显偏大就说明那一列专家分歧大。这套流程比 AHP 省事的地方在于不需要构造判断矩阵也就不存在一致性检验这个环节——但代价是放弃了两两比较的相对信息指标一多专家打分容易趋同区分度会下降。4.3 用 He 做一轮一致性筛查权重算出来不等于能用还要回头看一眼He。我的习惯是设一个相对阈值把He / En超过 0.2 的指标挑出来[~, cloud] cloudWeight(score); cv cloud.He ./ cloud.En; % 超熵相对熵的比值 bad find(cv 0.2); if ~isempty(bad) fprintf(以下指标专家分歧偏大建议重新打分%s\n, ... strjoin(string(bad), , )); end这个 0.2 没有严格的理论依据是实际项目里试出来的经验值He远小于En说明大家意见集中He接近甚至超过En说明这批分数基本是散的。挑出来的指标通常有两种处理方式一是把分散在两侧的极端打分剔掉重算二是给这个指标单独增加一轮专家咨询。不管选哪种都比闷头拿Ex算出权重、最后被评审问“专家意见一致性怎么保证”要好。5. 避坑与排查NaN、负权重和画不出钟形的云5.1 数值类异常He 开根号、权重负数、全零现象一He算出来是NaN或者报“输入必须为非负实数”。原因S2 - En^2是负数sqrt在实数域下无解。样本量小、或者专家打分高度集中在同一个值附近时最容易出现。比如五个专家全打 7 分S2接近 0En也不大相减就成了负的。 解决先用max(S2 - En.^2, 0)兜住再补一个 warning 记录是哪个指标触发的更重要的一步是看样本量专家少于 5 人的话He本身就没有统计意义别拿它做一致性判断。现象二权重向量里出现负数或者归一化后某些指标权重是 0。原因某列打分里混进了负值或者该列期望本身就很小。也有人反过来打分用了 1 到 10 但其中一列全是 1Ex归一化后接近 0权重看着就像被吞掉了。 解决在cloudWeight里加一道any(score(:) 0)的检查如果是量纲不统一有的指标 1-10 分、有的 0-1 分先做 min-max 归一化再进函数不要指望函数内部自动处理。现象三云滴图上出现一条竖直的线或者整片空白。原因En_i抽到了 0y exp(-(x-Ex).^2/(2*0))直接变成Inf或NaNplot遇到 NaN 就断线。 解决En_i加max(En_i, eps)截断eps大概是 2.2e-16既不影响正常值又能挡住除零。这个补丁写在forwardCloud里一次改完全局生效。5.2 调用与环境类问题函数找不到、点太少、维度不对现象四Undefined function forwardCloud或normrnd。原因前一种是文件没在搜索路径里后一种是没装统计工具箱。两种错误提示一模一样处理方式完全不同。 解决先which forwardCloud确认文件能不能被找到找不到就addpath能找到但还是报normrnd未定义就换成randn手写版。分两步查比一上来就重装工具箱快。现象五逆向算回来的参数和正向输入差得离谱。原因样本量太小。200 个点算出来的He和 2000 个点能差一倍和 2 万个点差得更多。另一个常见原因是数据维度搞反了传进去的是行向量而不是列向量mean算出来是一个标量后面全乱。 解决函数第一行统一x x(:)强制转列向量这一步能挡掉九成的维度问题样本量低于 1000 的时候把n直接加到 5000 以上再对比误差会明显收窄。现象六plotCloud画出来是一个点不是一片。原因n传成了 1或者参数顺序传错把He当成了点数。MATLAB 不报错只是默默画了一个点。 解决forwardCloud里加if nargin 4, n 1000; end的默认值再在开头检查n是不是整数。参数顺序这种错误没有技巧只能靠函数签名里写清楚注释。6. 二次开发把几个 .m 文件集成成可复用的评价工具单个脚本跑通之后下一步一般是把它接进自己的项目。最省事的改造方向是把散落的函数收进一个cloudmodel包目录用命名空间隔开避免和其他工具箱撞名project/ ├── cloudmodel/ │ ├── forward.m │ ├── backward.m │ ├── weight.m │ └── similarity.m └── evaluate.m调用就变成cloudmodel.forward(70, 5, 1, 2000)一眼能看出这个函数属于哪个模块。同一个包里再补一个云相似度函数用来比较两组云参数的接近程度做方案优选的时候可以拿它当排序依据function s similarity(c1, c2) % SIMILARITY 两组云参数的相似度值域 (0, 1] % c1, c2 : struct字段为 Ex/En/He tmp (c1.Ex - c2.Ex).^2 (c1.En - c2.En).^2 (c1.He - c2.He).^2; s exp(-tmp); end这个公式是简化的欧氏距离版本够用但不是唯一写法有的文献会按各参数的物理意义加权。自己用的话先保证Ex的差异占主导就行后两项给 0.5 的系数比较合理。再往深一层可以把“打分 → 云参数 → 权重 → 综合评价得分”这条链路封成一个入口函数输入专家矩阵和指标原始值直接返回排序结果。封装的价值不在于少写几行代码而在于把中间产物cloud结构体留在函数的第二个返回值里出问题的时候能一层层往回查。我踩过最狠的一次坑就是把中间结果全丢了最后权重不对只能从原始数据重新跑一遍前后对比了三个小时才发现是某一列打分里有两个 10 分把Ex拉高了。表里这几个改造方向按投入产出比排一下改造点工作量收益加cloudmodel包半小时命名不冲突调用可读补云相似度一小时能做方案排序加一致性筛查一小时权重结果可解释封装完整评价入口半天换数据只改一个文件验证方法也要跟上。每改一个函数拿第 2 章那个最小闭环重跑一次Ex偏差控制在 2% 以内、He偏差控制在 15% 以内就算这轮改动没引入回归。别嫌麻烦改过三次以后你就会发现能自动跑一遍的验证比人眼盯着散点图靠谱得多。从那以后我每次把这类脚本接进项目都会先在demo.m里跑一遍正向、逆向、赋权三条链路确认结果和预期对得上再动正式的代码。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站