新闻详情

新闻详情

首页 / 资讯中心 / 详情

mRMR特征选择实战:基于互信息的最大相关最小冗余算法与MATLAB实现

发布时间:2026/9/26 8:49:02来源:尧图网络
mRMR特征选择实战:基于互信息的最大相关最小冗余算法与MATLAB实现
简介这是一套机器学习特征选择算法的完整实现资源聚焦基于互信息的最大相关最小冗余mRMR方法附源代码、文档说明、学习报告和实验数据集面向从事相关研究的在校生、教师及企业开发者可用于课程设计、毕业设计或算法对比实验。代码部分包含C核心模块6个.cpp与6个.h、MATLAB脚本4个.m涉及PCA、PPCA和特征索引、SVM相关程序以及lung、NCI9两组CSV数据集和PDF学习报告能完整体现特征选择的预处理、计算和验证流程。压缩包共24个文件以源文件、脚本、数据集和报告文档为主另附makefile、mrmr与fast-mrmr可执行文件整体仅968KB轻量紧凑目录结构清晰。已有145人学习下载项目经作者测试运行成功答辩评审平均分达96分适合快速上手并基于现有代码做功能扩展。下载解压后可参照说明文档或mrmr源文件了解算法细节便于学习与二次开发。1. 机器学习里的特征选择为什么是 mRMR 而不是拍脑袋挑特征做基因表达谱分类时我遇到过最头疼的事不是分类器选哪个而是特征太多。几千个基因列在那里样本只有几十个直接丢给 SVM精度全靠运气。后来换成基于互信息的最大相关最小冗余mRMR做特征选择先把和类别标签相关性最强、彼此之间冗余度最小的特征挑出来再喂给分类器精度从玄学变成可复现。这套资源就是完整的 mRMR 实现包含 MATLAB 源代码、两份基因表达数据集、大作业报告和说明文档适合做机器学习课设、毕设或者刚入门特征选择的人。跟着跑一遍你就能把它换成自己的数据用。2. 最大相关最小冗余互信息公式与贪心搜索的取舍2.1 互信息能抓非线性关系的关联度量特征选择的第一步是回答一个问题凭什么说一个特征和类别标签相关最常见的做法是算皮尔逊相关系数但它只能捕捉线性关系。基因表达数据里大量存在的是剂量效应、阈值效应两个变量关系很强但不是直线关系皮尔逊系数算出来可能接近 0你直接把这个特征丢掉了血亏。互信息Mutual Information没有这个限制。它衡量的是知道一个变量之后另一个变量的不确定性减少了多少。定义式是I(X;Y) ΣΣ p(x,y) * log( p(x,y) / (p(x) * p(y)) )当 X 和 Y 完全独立时p(x,y) p(x)p(y)log 里面等于 1互信息为 0只要存在任何统计依赖互信息就大于 0。这个性质决定了它天然适合做特征和标签之间的相关性打分尤其在高维小样本的组学数据上比相关系数可靠得多。代码实现上连续变量需要先离散化。常见做法是把特征值归一化后分箱用直方图估计 joint 概率和边缘概率。你打开包里的mRMR/src能看到核心的互信息估计函数基本原理就是我下面这段的样子function mi mutual_info(x, y, n_bins) % 计算两个变量之间的互信息 % x, y: 长度相同的列向量 % n_bins: 连续变量分箱数量默认 20 if nargin 3 n_bins 20; end % 归一化到 [0, 1]避免量纲差异影响分箱 x (x - min(x)) / (max(x) - min(x) eps); y (y - min(y)) / (max(y) - min(y) eps); % 落到分箱编号1 ~ n_bins x_bin min(floor(x * n_bins) 1, n_bins); y_bin min(floor(y * n_bins) 1, n_bins); % 联合直方图和边缘直方图 joint_hist accumarray([x_bin, y_bin], 1, [n_bins, n_bins]); n numel(x); p_joint joint_hist / n; px sum(joint_hist, 2) / n; py sum(joint_hist, 1) / n; % 扩展成矩阵计算互信息 px_mat repmat(px, 1, n_bins); py_mat repmat(py, n_bins, 1); mask p_joint 0; mi sum(p_joint(mask) .* log(p_joint(mask) ./ (px_mat(mask) .* py_mat(mask)))); end分箱数n_bins是唯一需要你拍板的参数。bin 太少互信息被低估区分度消失bin 太多每个箱子里样本太少概率估计噪声很大。我一般先取 20 跑一遍再试 10 和 30看排名靠前的特征是否稳定这也是后面避坑章里要重点说的事。2.2 最大相关与最小冗余两个目标怎么同时优化如果只按互信息从大到小排选出来的一定是一堆互相高度相关的特征信息冗余严重。mRMR 的核心思想是同时优化两个目标最大相关已选特征集合 S 中的每个特征与类别 c 的互信息平均最大数学表达为D (1/|S|) * Σ I(x_i; c)。最小冗余已选特征两两之间的互信息平均最小数学表达为R (1/|S|^2) * ΣΣ I(x_i; x_j)。两个目标直接合并成一个增量搜索问题每轮从未选特征里挑一个让与标签的相关性减去与已选特征的平均冗余最大也就是最大化I(x_j; c) - (1/|S|) * Σ I(x_j; x_i)。这个过程是贪心的每一步只保证当下最优不保证全局最优但在高维特征空间里这是唯一现实的做法——穷举所有特征组合在计算上根本不可能。包里的 mRMR 核心实现逻辑大致是这样function [feat_idx, score] mrmr_select(X, y, K) % 基于互信息的最大相关最小冗余特征选择 % X: n×p 数据矩阵n 个样本p 个特征 % y: n×1 标签列向量类别用整数编码 % K: 目标特征数量 % feat_idx: 选出的特征下标按选择顺序排列 % score: 每个特征入选时的目标函数值 [n, p] size(X); K min(K, p); % 预计算每个特征与标签的互信息最大相关项 mi_class zeros(p, 1); for j 1:p mi_class(j) mutual_info(X(:, j), y, 20); end % 预计算特征两两之间的互信息最小冗余项用于后续查询 mi_feat zeros(p, p); for i 1:p for j i1:p v mutual_info(X(:, i), X(:, j), 20); mi_feat(i, j) v; mi_feat(j, i) v; end end selected false(p, 1); feat_idx zeros(K, 1); score zeros(K, 1); for t 1:K best_j -1; best_val -inf; for j 1:p if selected(j) continue; end if t 1 % 第一个特征只看与标签的相关性 val mi_class(j); else % 后续特征相关性 - 与已选特征的冗余均值 redun mean(mi_feat(j, feat_idx(1:t-1))); val mi_class(j) - redun; end if val best_val best_val val; best_j j; end end selected(best_j) true; feat_idx(t) best_j; score(t) best_val; end end注意这里特征两两互信息矩阵是 O(p²) 的复杂度p 上万时二重循环会非常慢。包里的实现做了优化但如果你要处理的是超过两万维的数据我建议先用方差过滤砍掉明显没信息的特征再跑 mRMR否则光是算两两互信息就要等到天亮。2.3 为什么选 filter 而不是 wrapper 或 embedded特征选择方法按和分类器的关系分成三类mRMR 属于 filter 派因为它只依赖统计量不依赖任何具体分类器。这三类的差别直接决定你在大作业或者毕设里怎么交代选型理由类型搜索方式计算开销与分类器关系典型代表适用场景filter统计量排序小一次算完无关mRMR、卡方、方差选择高维小样本特征上万wrapper特征子集 分类器评估大反复训练强依赖递归特征消除特征几百以内embedded训练过程内嵌选择中等依赖模型L1 正则、树模型中高维模型带正则选择 mRMR 而不是另外两派核心原因是你没有足够样本让 wrapper 类方法反复训练分类器。基因表达数据经常是几十个样本对几千个特征跑一轮递归特征消除要训练几十次分类器算完可能已经是第二天了。mRMR 一次把所有特征的排序算出来之后你想用 SVM、随机森林还是朴素贝叶斯验证都是同一份特征子集可复现性也好。embedded 方法虽然效果通常更好但它和具体模型绑死换成另一个分类器整套结论就要重跑。3. 源码拆解test.m、getFeaturesWithIndex.m 与 svm 验证链路3.1 test.m整个项目的入口顺序拿到压缩包解压之后先别急着点运行。把文件结构捋一遍你就知道作者是怎么组织一个完整特征选择实验的。test.m是主入口它的职责是把整条链路串起来读数据 - 跑 mRMR 选特征 - 用选出的特征训练 SVM - 输出精度。后面的test_pca.m是另一条对比路径用 PCA 降维后再做同样的事情两份脚本对照着看本身就是一份现成的实验设计模板。我按包里的接口风格补一个等价的主流程示例你打开test.m对照着看结构应该是一致的% test.m 主流程mRMR 特征选择 SVM 验证 % 第一步读取基因表达数据集 data csvread(test_lung_s3.csv); X data(:, 1:end-1); % 前若干列为特征 y data(:, end); % 最后一列为类别标签 % 第二步运行 mRMR选出 30 个特征 [feat_idx, score] mrmr_select(X, y, 30); fprintf(选中特征索引: %s\n, mat2str(feat_idx)); % 第三步抽取特征子集训练 SVM 做 5 折交叉验证 X_sel X(:, feat_idx); rng(42); cv cvpartition(y, Kfold, 5); acc zeros(cv.NumTestSets, 1); for i 1:cv.NumTestSets tr training(cv, i); te test(cv, i); model fitcsvm(X_sel(tr, :), y(tr), Standardize, true); pred predict(model, X_sel(te, :)); acc(i) sum(pred y(te)) / numel(y(te)); end fprintf(5-Fold CV Accuracy: %.2f%%\n, mean(acc) * 100);这套流程里最关键的设计是先 mRMR 后交叉验证。如果你把 mRMR 放在整个数据集上跑完再划分训练测试集特征选择过程已经偷看了测试集的信息这叫数据泄漏精度会虚高答辩时被老师问一句就容易翻车。正确做法是特征选择只依赖当前折的训练数据后面避坑章我再展开说。3.2 mRMR 与 src核心搜索算法放在哪个文件mRMR文件夹下面是算法的全部家当src子文件夹里是核心函数data.mrmr是运行产生的中间文件保存了特征排序结果。data.mrmr这个文件值得你注意它意味着你跑过一次之后下次想换分类器重新验证可以不重跑特征选择直接读这个文件拿特征索引省掉最耗时的那一步。包里的实际函数名以mRMR/src为准但核心逻辑就是我上面写的增量搜索。你自己实现时建议保持三个独立函数互信息估计、特征对互信息矩阵、贪心搜索分开的好处是出 bug 容易定位。互信息估计错了后面全错而互信息估计最容易出错的地方是概率估计时用hist而不是histcounts老版本 MATLAB 里hist的边界处理和histcounts不一致同一个特征在不同机器上跑出来排名不同属于典型的玄学 bug。3.3 getFeaturesWithIndex.m把索引变回特征mRMR 输出的不是特征本身而是特征下标。下标这个东西在调试时非常容易把人绕晕——索引算出来了但到底是哪几列getFeaturesWithIndex.m干的就是这件事传入原始数据矩阵和特征索引返回抽好的特征子集。函数逻辑极短就是一次矩阵索引function features getFeaturesWithIndex(data, index) % data: 原始特征矩阵每行一个样本 % index: mRMR 输出的特征下标向量 % features: 按 index 抽取出的特征子集列数等于 index 长度 features data(:, index); end这个封装本身没有技术难度但它是好习惯。直接写X(:, idx)谁都会问题在于索引语义不清晰idx 是从 1 开始的列号还是从 0 开始的是线性索引还是逻辑索引封装成函数之后接口语义固定后面换数据、换特征选择方法时不用回头猜。我自己的习惯是拿到任何特征选择代码第一件事就是写一个这种映射函数哪怕只有一行。3.4 svm 与 ppca验证链路怎么闭合svm文件夹里是分类验证部分。包里的 SVM 代码可能是老式svmtrain/svmclassify接口也可能是 libsvm 封装。如果你用的 MATLAB 版本比较新R2014a 之后svmtrain已经被fitcsvm取代直接跑test.m会报Undefined function svmtrain。这不是包的问题是版本差异把训练和预测的调用换成fitcsvmpredict即可参数上等价于老接口的默认设置。ppca.m是概率主成分分析的实现。PCA 加 mRMR 是一条常见对比实验路径先用 ppca 把高维数据压到低维再对降维结果做特征选择或直接分类用来回答线性降维之后信息还在不在这个问题。test_pca.m就是这条路径的完整脚本和test.m形成对照实验。大作业报告里如果写了两个方法对比那这组代码就是你复现报告数据的关键。4. 把 mRMR 跑在你自己的数据集上两份基因表达数据与完整流程4.1 数据集长什么样包里有test_lung_s3.csv和test_nci9_s3.csv两份基因表达数据。它们都是典型的高维小样本结构每一行是一个样本每一列是一个特征基因最后一列是类别标签。第一份是肺癌相关的表达谱数据适合做二分类实验第二份是 NCI9 细胞系数据类别数更多适合用来对比 mRMR 在多分类任务下的表现。文件场景格式用途test_lung_s3.csv肺癌表达谱CSV行为样本、列为特征、末列为标签二分类特征选择 SVM 验证test_nci9_s3.csvNCI9 细胞系CSV行为样本、列为特征、末列为标签多分类特征选择 分类器对比data.mrmrmRMR 中间结果自定义文本保存特征索引避免重复计算用csvread读这两份文件时注意一个坑MATLAB 的csvread要求文件里全是数值如果标签列是字符串比如cancer和normal这个函数直接报错。包里的数据是数值编码过的所以能读。如果你换成自己的数据标签是字符串得先用readtable读再把标签列映射成 1、2、3 的整数编码否则csvread这一步就卡住了。4.2 跑通全流程的完整步骤拿到包之后推荐的执行顺序是先跑原来的test.m确认环境没问题再动数据。下面是我按包内风格整理的完整运行流程# 1. 解压并进入项目目录 unzip Feature-Selection-master.zip cd Feature-Selection-master # 2. 用 MATLAB 打开 test.m直接运行 # 如果遇到 svmtrain 不存在把 fitcsvm 替换进去 matlab -batch run(test.m)在 MATLAB 里逐步执行的核心代码段% 读取肺腺癌数据集 data csvread(test_lung_s3.csv); % 分离特征和标签 X data(:, 1:end-1); y data(:, end); % 查看数据维度确认高维小样本结构 fprintf(样本数: %d, 特征数: %d\n, size(X, 1), size(X, 2)); % mRMR 选 20 个特征 [feat_idx, ~] mrmr_select(X, y, 20); % 查看选中了哪些特征基因 fprintf(选中的特征索引: ); fprintf(%d , feat_idx); fprintf(\n); % 用 getFeaturesWithIndex 抽特征子集 X_sel getFeaturesWithIndex(X, feat_idx); % 检查抽出来的特征矩阵大小是否正确 assert(size(X_sel, 2) 20, 特征子集维度不对);这段代码跑完后你会看到选出来的是 20 个特征下标而不是特征名。如果想看特征名需要额外读一个基因名文件包里没带但这不影响实验——特征选择实验关注的是排序和精度不是具体的基因名。4.3 输出怎么解读特征索引、score 与 SVM 精度mRMR 返回的两个输出要分清feat_idx是特征下标按选择顺序排越靠前越重要score是每轮的目标函数值也就是I(x_j;c) - mean(I(x_j;x_i))的数值。score 有可能出现负值——当某个特征与已选特征冗余度超过它和标签的相关性时目标函数就是负的这说明这个特征信息被前面的特征覆盖了选它反而有害。SVM 交叉验证精度是最终的判断依据。但只看平均精度不够还要看五折之间的波动。如果五折精度分别是 98%、60%、99%、55%、97%均值看着还行实际是模型在个别折上崩了通常意味着某个折里的测试样本分布和训练集差异大或者是样本量太小导致划分不稳定。遇到这种情况改cvpartition(y, Kfold, 5)为Leaveout留一验证每个样本单独当一次测试集虽然慢但结果稳定得多。4.4 换到自己的数据关键改动点把包里的数据换成自己的只改三处其余不用动。第一处是读文件的地方路径换成你自己的 CSV第二处是标签编码保证 y 是 1 到类别数的整数第三处是特征选择数量 K这个参数值后面单独讲怎么定。其他所有代码逻辑完全不用改。% 换成自己的数据 data csvread(my_data.csv); X data(:, 1:end-1); y data(:, end); % 检查标签是不是连续整数不是则重新编码 % unique(y) 应该返回 1、2、3... 这样的序列 [~, ~, y] unique(y); % 跑 mRMRK 先随便给 20后面再优化 [feat_idx, score] mrmr_select(X, y, 20); % 分类验证 X_sel getFeaturesWithIndex(X, feat_idx);换数据后最容易出的问题你的数据维度如果比包里的两份数据低很多比如只有 50 个特征那K30会选出近三分之二的特征mRMR 的最小冗余约束几乎不起作用。这种情况下把 K 降到 10 以内或者直接减少候选特征数量再看效果。5. mRMR 实战避坑互信息估计、归一化与 SVM 参数5.1 互信息算出来全是 0 或彼此几乎相同现象跑完mutual_info所有特征的互信息值都接近 0排序结果几乎是随机的和直接随机选特征没有区别。原因连续变量直接拿去算互信息没有做离散化或者分箱数设得太小。另一个常见原因是原始数据量纲差异过大比如某些特征取值在 0~1 之间另一些在 0~10000 之间跨特征的分箱边界不一致概率估计完全失真。解决先对每个特征做 min-max 归一化再送进互信息计算分箱数从 20 起步分别试 10、20、30看选出的特征排名是否稳定。我在 2.1 节给的那个mutual_info函数里已经内置了归一化但如果你自己实现很容易漏这一步。归一化之后务必再检查一次特征值范围确认在 [0,1] 内再继续。5.2 SVM 精度波动大同一个特征集两次结果差很远现象保持代码不变连续跑两次test.mSVM 精度差了 5 到 8 个百分点你开始怀疑特征选择是不是有随机性。原因交叉验证的划分是随机的没有固定随机种子。每次运行cvpartition都会重新划分数据不同折上的样本分布不一样精度自然不一样。mRMR 本体没有随机性但验证环节的随机性让人误以为整个流程不稳定。解决在交叉验证前加rng(42)把随机种子固定下来。种子选什么数字不重要重要的是固定。同时把随机种子设成和test.m里一致的值这样你复现报告里的精度数据时能对上号。我处理毕设项目时习惯把种子写在脚本最顶部注释标明修改此值可得到不同划分但提交版本固定 42。5.3 svmtrain 不存在导致整个流程跑不起来现象点击运行test.mMATLAB 报错svmtrain is not found或者Undefined variable svmtrain整个验证链路直接中断。原因包里用的是老版本 SVM 接口而新版本 MATLAB 用fitcsvm替代了svmtrain。你装了新版本 MATLAB接口对不上不是代码问题。解决把svmtrain换成fitcsvm把svmclassify换成predict。参数上fitcsvm(..., Standardize, true)对应老接口里的-c和缩放设置。注意fitcsvm的输入要求特征矩阵在前、标签在后和老接口的调用顺序完全一致。包里的 SVM 代码如果还有svmtype这类老参数一并删掉新版不支持。5.4 K 随手设 50精度反而比 K20 低现象特征数从 20 加到 50交叉验证精度不升反降过拟合迹象明显。原因mRMR 的增量搜索里越往后选的特征与标签的相关性越弱同时与已选特征的冗余度越来越高。K 设得过大后期入选的特征本身就没有多少判别力纯粹是凑数。对高维小样本数据特征数超过样本数的十分之一过拟合风险就开始陡增。解决把 K 当成超参数调而不是随手填。做法是画一条特征数-精度曲线取曲线上升后首次进入平台期的拐点作为最终 K 值。具体代码在下一章的 6.2 节给出。至少记住一条经验法则K 不超过 min(50, 样本数/5)数据只有 40 个样本时K 超过 8 就要开始警惕。5.5 在整份数据上跑完 mRMR 再划分交叉验证现象交叉验证精度高达 98%换到独立测试集上崩到 60%老师问起来答不上来。原因特征选择在全部样本上运行相当于在划分训练测试集之前就让 mRMR看过测试集的分布。SVM 验证时测试集里的信息和特征选择过程存在间接泄漏精度虚高。这种错误在学生的毕设答辩里出现频率极高属于典型的数据泄漏翻车现场。解决在每一折交叉验证内部重新跑 mRMR。外层循环划分数据内层先对训练部分做特征选择再用选出的特征训练分类器最后在测试部分上评估。代价是每折都要重跑一遍 mRMR慢但结果可信。代码模板rng(42); cv cvpartition(y, Kfold, 5); acc zeros(cv.NumTestSets, 1); for i 1:cv.NumTestSets tr_idx training(cv, i); te_idx test(cv, i); % 关键只对训练部分做特征选择 [idx, ~] mrmr_select(X(tr_idx, :), y(tr_idx), 20); model fitcsvm(X(tr_idx, idx), y(tr_idx), Standardize, true); pred predict(model, X(te_idx, idx)); acc(i) sum(pred y(te_idx)) / numel(y(te_idx)); end这样跑出来的精度才是实际泛化性能的可靠估计。代价是每次交叉验证要重算一次特征排序但数据集维度在几千以内时这个开销完全可接受。6. 进阶ppca 降维、特征数曲线与一套可复用的验证管线6.1 先用 ppca 降维再跑 mRMR两条路线的对比包里同时给了ppca.m和test_pca.m这是一条完整的对比实验路径。常见做法是先跑直接 mRMR 得到精度再用 ppca 把数据压到低维空间对降维后的特征跑同样的分类流程最后对比两条路线的精度差异。这组对照能说明线性降维是否保留了关键判别信息也是大作业报告里的核心实验内容。% 方案 A直接 mRMR [feat_a, ~] mrmr_select(X, y, 20); model_a fitcsvm(X(:, feat_a), y, Standardize, true); % 方案 B先 ppca 降维再 mRMR d 30; % 降到 30 维 [~, X_ppca] ppca(X, d); % 具体接口以包内注释为准 [feat_b, ~] mrmr_select(X_ppca, y, 20); model_b fitcsvm(X_ppca(:, feat_b), y, Standardize, true);结果通常有几种情况ppca 降维后再选特征精度大致持平但计算快一个数量级或者精度明显下降说明原始特征空间里存在非线性结构线性降维损伤了信息还有可能 ppca 后精度反而更高因为降维本身起到了去噪作用。不管哪种结果你都能在报告里讲出一个合理的解读这才是对比实验的价值。6.2 特征数 K 的选择画特征数-精度曲线找拐点前面避坑章里说了 K 不能拍脑袋现在给具体做法。把 K 设成一组候选值对每个 K 跑一遍完整的交叉验证画出精度曲线选第一个进入平台期的 Kks 5:5:50; acc_mean zeros(size(ks)); acc_std zeros(size(ks)); rng(42); for t 1:numel(ks) cv cvpartition(y, Kfold, 5); temp zeros(cv.NumTestSets, 1); for i 1:cv.NumTestSets tr training(cv, i); te test(cv, i); [idx, ~] mrmr_select(X(tr, :), y(tr), ks(t)); model fitcsvm(X(tr, idx), y(tr), Standardize, true); pred predict(model, X(te, idx)); temp(i) sum(pred y(te)) / numel(y(te)); end acc_mean(t) mean(temp); acc_std(t) std(temp); end % 打印每个 K 对应的精度和标准差 for t 1:numel(ks) fprintf(K%d, Acc%.2f±%.2f%%\n, ks(t), acc_mean(t)*100, acc_std(t)*100); endK 从 5 到 30 精度上升30 到 50 进入平台期就取 K30。如果曲线一直上升不拐弯说明数据本身信息量大或者样本量足够可以继续加大 K 范围重跑。如果曲线上升后明显下降那就是过拟合信号。精度曲线图贴到大作业报告里比直接说选了 30 个特征有说服力得多。6.3 把 mRMR 锻造成一套固定验证管线最后的建议是把 mRMR 做成你所有高维小样本项目的标准前置环节而不是一次性用完就扔的脚本。我从自己做课设的经验里总结的固定流程是引入数据 - 归一化 - 方差过滤粗筛 - mRMR 精筛 - 特征数曲线定 K - 交叉验证内重新跑特征选择 - SVM/随机森林对比。这套管线里每一步都有明确输出跑到哪一步出了问题你都能知道该查哪里。那次我用全特征直接跑 SVM 翻车之后就强制自己每次遇到高维数据先走一遍 mRMR再谈分类器的事。从那以后我每次做特征选择实验都会强制先跑一遍mrmr_select看排名前二十的特征稳不稳定再决定后续模型怎么搭。这份资源把整条链路都配齐了照着跑一遍再换自己的数据走通一次比看十篇理论文章都有用。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

trae 配置 gradle springboot 项目:TaoToken 统一 Key 接入 settings.json 骨架与验证 2026/9/26 9:30:35

trae 配置 gradle springboot 项目:TaoToken 统一 Key 接入 settings.json 骨架与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
ContentProvider权限设置实战:从AndroidManifest到TaoToken配置的完整避坑指南 2026/9/26 9:30:29

ContentProvider权限设置实战:从AndroidManifest到TaoToken配置的完整避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
CSS 实现禁止点击的方法:TaoToken 配置 settings.json 骨架与验证动作 2026/9/26 9:30:16

CSS 实现禁止点击的方法:TaoToken 配置 settings.json 骨架与验证动作

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
vscode插件图标怎么设置:package.json 与 vsce 发布配置全流程 2026/9/26 9:30:16

vscode插件图标怎么设置:package.json 与 vsce 发布配置全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
振动筛跨行业应用图谱:从砂石厂到食品医药的选型与运维指南 2026/9/26 9:30:16

振动筛跨行业应用图谱:从砂石厂到食品医药的选型与运维指南

去年在南方一家面粉厂做工艺回访,车间里那台嗡嗡作响、装在不锈钢支架上的"清粉筛",我一眼就看出是振动筛的骨架——电机、偏心块、弹簧总成、筛箱,一个部件都不陌生,只不过筛网换成了180目的精细网,外壳做了…

阅读更多 →
我用 AI 写了一个月代码:Copilot 与 Cursor 的真实对比测评,附 TaoToken 统一 Key 配置 2026/9/26 9:30:16

我用 AI 写了一个月代码:Copilot 与 Cursor 的真实对比测评,附 TaoToken 统一 Key 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉