模型介绍

上面这个图就是一个比较直观的解释。机器学习模型一般都是一个黑盒。比如某个模型要进行一些预测任务,首先对模型输入一些已知条件(Age=65,Sex=F,BP=180,BMI=40),然后模型根据输入进行训练,最终训练完的模型可以对该条件输出预测结果(Output=0.4)

所以这样模型只能得到最终的结果,至于模型内部是怎么计算的,输入的已知条件(Age=65,Sex=F,BP=180,BMI=40)是怎么对预测结果(Output=0.4)影响的,我们都没法知道

而SHAP模型就可以让我们知道这些已知条件到底对最终预测结果起到哪些影响(是对结果起到正向影响还是对结果起到了负向影响),除了SHAP模型,其实也有其他方法可以进行特征重要性的计算,比如下面这个表格里提到的,我们可以根据各种方法的优点选择适合的进行特征重要性计算

SHAP

而本文主要介绍的SHAP 属于模型事后解释的方法,它的核心思想是计算特征对模型输出的边际贡献,再从全局和局部两个层面对“黑盒模型”进行解释。SHAP构建一个加性的解释模型,所有的特征都视为“贡献者”

SHAP的全称是SHapley Additive exPlanation,SHAP是由Shapley value启发的可加性解释模型。而Shapley value起源于合作博弈论,那什么是合作博弈呢。比如说甲乙丙丁四个工人一起打工,甲和乙完成了价值100元的工件,甲、乙、丙完成了价值120元的工件,乙、丙、丁完成了价值150元的工件,甲、丁完成了价值90元的工件,那么该如何公平、合理地分配这四个人的工钱呢?Shapley提出了一个合理的计算方法,我们称每个参与者分配到的数额为Shapley value。

结合文章一开始提到的预测任务,我认为就是已知条件(Age=65,Sex=F,BP=180,BMI=40)一起完成了预测结果(Output=0.4),那么该如何公平、合理地分配这四个已知条件对预测结果的贡献呢?此时SHAP模型就会给这四个已知条件都分配一个Shapley value值,根据这个值我们就可以很好的进行理解

SHAP可以具体解决的任务

  1. 调试模型用
  2. 指导工程师做特征工程
  3. 指导数据采集的方向
  4. 指导人们做决策
  5. 建立模型和人之间的信任

这一部分参见:https://yyqing.me/post/2018/2018-09-25-kaggle-model-insights

SHAP库可用的explainers

在SHAP中进行模型解释需要先创建一个explainer,SHAP支持很多类型的explainer

  1. deep:用于计算深度学习模型,基于DeepLIFT算法,支持TensorFlow / Keras。
  2. gradient:用于深度学习模型,综合了SHAP、集成梯度、和SmoothGrad等思想,形成单一期望值方程,但速度比DeepExplainer慢,并且做出了不同的假设。 此方法基于Integrated Gradient归因方法,并支持TensorFlow / Keras / PyTorch。
  3. kernel:模型无关,适用于任何模型
  4. linear:适用于特征独立不相关的线性模型
  5. tree:适用于树模型和基于树模型的集成算法,如XGBoost,LightGBM或CatBoost

实验

在网上找了几个相关的实验跑一下加深印象,SHAP模型输出的可视化图真的是挺美观的。

红色特征使预测值更大(类似正相关),蓝色使预测值变小,紫色邻近均值。而颜色区域宽度越大,说明该特征的影响越大。

这里我们尝试通过SHAP来解释深度学习模型每一层网络对最终检查结果的影响情况。11种shap可视化图形来解释任何机器学习模型统计如下:

数据集

标准的 UCI 成人收入数据集。

import shap
X,y = shap.datasets.adult()
X_display, y_display = shap.datasets.adult(display=True)

创建Explainer并计算SHAP值

在SHAP中进行模型解释需要先创建一个explainer,SHAP支持很多类型的explainer(例如deep, gradient, kernel, linear, tree, sampling),本文使用支持常用的XGB、LGB、CatBoost等树集成算法的tree为例。

  • deep:用于计算深度学习模型,基于DeepLIFT算法
  • gradient:用于深度学习模型,综合了SHAP、集成梯度、和SmoothGrad等思想,形成单一期望值方程
  • kernel:模型无关,适用于任何模型
  • linear:适用于特征独立不相关的线性模型
  • tree:适用于树模型和基于树模型的集成算法
  • sampling :基于特征独立性假设,当你想使用的后台数据集很大时,kenel的一个很好的替代方案
explainer = shap.TreeExplainer(model)

然后计算shap_values值,计算非常简单,直接利用上面得到的解释器解释训练样本X,这里有两种形式:

输出numpy.array数组

shap_values = explainer.shap_values(X)

输出shap.Explanation对象

shap_values2 = explainer(X)

模型自带特征重要性

关于模型解释性,除了线性模型和决策树这种天生就有很好解释性的模型意外,sklean/ xgboost 中有很多模型都有importance这一接口,可以查看特征的重要性。

model = xgboost.XGBClassifier(eval_metric='mlogloss').fit(X, y)
xgboost.plot_importance(model,height = .5, max_num_features=10,show_values = False)

SHAP 特征重要性

Summary Plot

将 SHAP 值矩阵传递给条形图函数会创建一个全局特征重要性图,其中每个特征的全局重要性被视为该特征在所有给定样本中的平均绝对值。

shap.summary_plot(shap_values, X_display, plot_type="bar")

在上面两图中,可以看到由 SHAP value 计算的特征重要性与使用 scikit-learn / xgboost计算的特征重要性之间的比较,它们看起来非常相似,但它们并不相同。

Bar plot

全局条形图

特征重要性的条形图还有另一种绘制方法。

shap.plots.bar(shap_values2)

同一个shap_values,不同的计算
summary_plot中的shap_values是numpy.array数组
plots.bar中的shap_values是shap.Explanation对象

当然shap.plots.bar()还可以按照需求修改参数,绘制不同的条形图。如通过max_display参数进行控制条形图最多显示条形树数。

局部条形图

将一行 SHAP 值传递给条形图函数会创建一个局部特征重要性图,其中条形是每个特征的 SHAP 值。其中特征值是否显示,是通过参数show_data控制,默认 'auto' 特征值以灰色显示在特征名称的左侧。

shap.plots.bar(shap_values2[1], show_data=True)

队列条形图

传递解释对象的字典将为解释对象表示的每个群组创建一个多条形图,其中包含一个条形类型。下面我们使用它来分别绘制男性和女性特征重要性的全局摘要。

sex = ["Women" if shap_values2[i,"Sex"].data == 0 else "Men" for i in range(shap_values2.shape[0])]
shap.plots.bar(shap_values2.cohorts(sex).abs.mean(0))

队列条形图还有另一个比较有意思的绘图,他使用 Explanation 对象的自动群组功能来使用决策树创建一个群组。调用Explanation.cohorts(N)将创建 N 个队列,使用 sklearn DecisionTreeRegressor 最佳地分离实例的 SHAP 值。

例如将其用于成人人口普查数据,则看到低资本收益与高资本收益之间的明显区别。括号中的数字是每个队列中的实例数。

v = shap_values2.cohorts(2).abs.mean(0)
shap.plots.bar(v)

使用特征聚类

很多时候数据集中的特征存在冗余。这意味着模型可以使用任一特征并仍然获得相同的准确性。可以通过计算特征之间的相关矩阵,或使用聚类方法来找到这些特征。

在 SHAP 中通过模型损失比较来测量特征冗余。即使用shap.utils.hclust方法,并通过训练 XGBoost 模型来预测每对输入特征的结果来构建特征的层次聚类。与从无监督方法(如相关性)中获得的特征冗余相比。对典型的结构化数据集进行特征冗余度量,会更加准确。

计算聚类并传递给条形图,就可以同时可视化特征冗余结构和特征重要性。默认只会显示距离 < 0.5 的聚类部分。假设聚类中的距离大致在 0 和 1 之间缩放,其中 0 距离表示特征完全冗余,1 表示它们完全独立。

在下图中,我们看到只有关系和婚姻状况有超过 50% 的冗余,因此它们是条形图中分组的唯一特征:

clustering = shap.utils.hclust(X, y)
shap.plots.bar(shap_values2, clustering=clustering,clustering_cutoff=0.5)

Summary Plot

上面使用Summary Plot方法并设置参数plot_type="bar"绘制典型的特征重要性条形图,而他默认绘制Summary_plot图,他是结合了特征重要性和特征效果,取代了条形图。

Summary_plot 为每一个样本绘制其每个特征的Shapley value,它说明哪些特征最重要,以及它们对数据集的影响范围。

y 轴上的位置由特征确定,x 轴上的位置由每 Shapley value 确定。颜色表示特征值(红色高,蓝色低),颜色使我们能够匹配特征值的变化如何影响风险的变化。重叠点在 y 轴方向抖动,因此我们可以了解每个特征的 Shapley value分布,并且这些特征是根据它们的重要性排序的。

shap.summary_plot(shap_values, X)

Beeswarm plot

同条形图一样shap也提供了另一个接口plots.beeswarm蜂群图。

蜂群图旨在显示数据集中的TOP特征如何影响模型输出的信息密集摘要。给定解释的每个实例由每个特征流上的一个点表示。点的 x 位置由该特征的 SHAP 值 ( shap_values.value[instance,feature]) 确定,并且点沿每个特征行“堆积”以显示密度。颜色用于显示特征的原始值 ( shap_values.data[instance,feature])。

在下图中,我们可以看到平均而言年龄是最重要的特征,与年轻(蓝色)人相比,收入超过 5 万美元的可能性较小。

同样可以使用max_display参数调整最多显示行数。

默认使用每个特征的 SHAP 值的平均绝对值shap_values.abs.mean(0) 对特征排序。然而,这个顺序更强调广泛的平均影响,而不是罕见但高强度的影响。如果我们想找到对个人影响较大的特征,可以按最大绝对值排序。

shap.plots.beeswarm(shap_values2, order=shap_values.abs.max(0))

另外,在绘图之前,就对shap_values取绝对值,得到与条形图类似的图形,但比条形图具有更丰富的平行线,因为条形图只是绘制蜂群图中点的平均值。

# 蜂群图
shap.plots.beeswarm(shap_values2.abs, color="shap_red")
# 条形图
shap.plots.bar(shap_values2.abs.mean(0))

还可以自定义颜色,默认使用shap.plots.colors.red_blue颜色图。

import matplotlib.pyplot as plt
shap.plots.beeswarm(shap_values, color=plt.get_cmap("cool"))

Summary_plot图中,首先看到了特征值与对预测的影响之间关系的迹象,但是要查看这种关系的确切形式,还必须查看 SHAP Dependence Plot图。

Dependence Plot

SHAP Partial dependence plot (PDP or PD plot) 依赖图显示了一个或两个特征对机器学习模型的预测结果的边际效应,它可以显示目标和特征之间的关系是线性的、单调的还是更复杂的。他们在许多样本中绘制了一个特征的值与该特征的 SHAP 值。

PDP 是一种全局方法:该方法考虑所有实例并给出关于特征与预测结果的全局关系。PDP 的一个假设是第一个特征与第二个特征不相关。如果违反此假设,则 PDP 计算的平均值将包括极不可能甚至不可能的数据点。

为了显示哪个特征可能会驱动这些交互效应,可以通过第二个特征为我们的年龄依赖性散点图着色(默认第二个特征是自动选择的,尝试挑选出与 Age 交互作用最强的特征列)。也可以通过参数interaction_index设置交互项。如果另一个特征与正在绘制的特征之间存在交互作用,它将显示为不同的垂直着色模式。

shap.dependence_plot('Age', shap_values, X, display_features=X_display,interaction_index='Capital Gain')

Dependence plot 是一个散点图,显示单个特征对整个数据集的影响。

  • 每个点都是来自数据集的单个预测(行)。
  • x 轴是数据集中的实际值。(来自 X 矩阵,存储在 中shap_values.data)。
  • y 轴是该特征的 SHAP 值(存储在 中shap_values.values),它表示该特征值对该预测的模型输出的改变程度。

Scatter plot

同样,散点图绘图依赖图,这与上面 dependence_plot 绘制基本一样。

在显示方面有些许不同,plots scatter 图底部的浅灰色区域是显示数据值分布的直方图。

在交互颜色方面。dependence_plot 默认而散点图则需要将整个 Explanation 对象传递给 color 参数。

另外,有时候在输入模型之前是字符串,为输入到模型,需要将其设置为分类编码,此时绘图是,并不能很直观地显示内容。此时可以将.display_data Explanation 对象的属性设置为我们希望在图中显示的原始数据类型。

shap_values2.display_data = X_display.values
shap.plots.scatter(shap_values2[:, "Age"], color=shap_values2[:,"Workclass"])

使用全局特征重要性排序

在只想绘制最重要的特征,却不知道其特征名或索引,此时可以使用 Explanation 对象的点链功能来计算全局特征重要性的度量,按该度量(降序)排序,然后挑选出顶部特征。

# 平均绝对均值的特征
ind_mean = shap_values2.abs.mean(0).argsort[-1]
# 平均绝对值最大的特征
ind_max = shap_values.abs.max(0).argsort[-1]
# 95% 绝对值对特征进行排序
ind_perc = shap_values.abs.percentile(95, 0).argsort[-1]
shap.plots.scatter(shap_values2[:, ind_mean])

另外还可以自定义图形属性,详情可参加官方文档。

模型预测的可解释性

Force plot

Local 可解释性提供了预测的细节,侧重于解释单个预测是如何生成的。它可以帮助决策者信任模型,并且解释各个特征是如何影响模型单次的决策。

单个预测的解释可视化

SHAP force plot 提供了单一模型预测的可解释性,可用于误差分析,找到对特定实例预测的解释。

# 如果不想用JS,传入matplotlib=True
shap.force_plot(explainer.expected_value, shap_values[0,:], X_display.iloc[0,:])

尝试分析此图。

  • 模型输出值:-5.89
  • 基值:模型输出与训练数据的平均值(explainer.expected_value)
  • 绘图箭头下方数字是此实例的特征值。如Age=39,Education-Num=13
  • 将预测推高的特征用红色表示,将预测推低的特征用蓝色表示
  • 箭头越长,特征对输出的影响越大。通过 x 轴上刻度值可以看到影响的减少或增加量。

多个预测的解释可视化

如果对多个样本进行解释,将上述形式旋转90度然后水平并排放置,得到力图的变体,我们可以看到整个数据集的 explanations :

通过上图中上方和左方选项卡,可以任意选择单个变量的多个样本对模型输出结果的影响。如下Age前80个样本,对模型输出结果f(x)的影响。

Interaction Values

interaction value是将SHAP值推广到更高阶交互的一种方法。树模型实现了快速、精确的两两交互计算,这将为每个预测返回一个矩阵,其中主要影响在对角线上,交互影响在对角线外。这些数值往往揭示了有趣的隐藏关系(交互作用)。

shap_interaction_values = explainer.shap_interaction_values(X)
shap.summary_plot(shap_interaction_values, X)

Decision plot

SHAP 决策图显示复杂模型如何得出其预测(即模型如何做出决策)。决策图是 SHAP value 的文字表示,使其易于解读。

决策图显示的信息与力图基本相同,都可以有效地解释上述模型的预测。而且很容易识别出主要影响的大小和方向。

决策图比力图更清晰和直观,尤其是要分析的特征比较多的时候。在力图中,当预测变量的数量较多时,信息可能看起来非常紧凑。

explainer = shap.TreeExplainer(model)
expected_value = explainer.expected_value
# 限制20个样本
features = X.iloc[range(20)]
# 展示第一条样本
shap_values = explainer.shap_values(features)[1]shap.decision_plot(expected_value, shap_values, features_display)

决策图中间灰色垂直直线标记了模型的基础值,彩色线是预测,表示每个特征是否将输出值移动到高于或低于平均预测的值。特征值在预测线旁边以供参考。从图的底部开始,预测线显示 SHAP value 如何从基础值累积到图顶部的模型最终分数。

shap_values = explainer.shap_values(features)
y_pred = (shap_values.sum(1) + expected_value) > 0
misclassified = y_pred != y[select]
shap.decision_plot(expected_value, shap_values, features_display, link='logit', highlight=misclassified)

决策图支持将对link='logit'数几率转换为概率。
使用虚线样式highlight=misclassified突出显示一个错误分类的观察结果。

通过单独绘制来检查错误分类的观察结果。绘制单个观测值时,会显示其相应的特征值。

shap.decision_plot(expected_value, shap_values[misclassified], features_display[misclassified],link='logit', highlight=0)

错误分类观察的力图如下所示。在这种情况下,决策图和力图都可以有效地显示模型如何得出其决策。

shap.force_plot(expected_value, shap_values[misclassified], features_display[misclassified],link='logit', matplotlib=True)

决策图的基本作用

  1. 大量特征效果清晰展示。
  2. 可视化多输出预测。
  3. 显示交互的累积效果。
  4. 探索一系列特征值的特征效果。
  5. 进行异常值检测。
  6. 确定典型的预测路径。
  7. 比较和对比几个模型的预测。

如需要具体了解每种作用的方法,建议去官网决策图查看每种作用所举的例子。

使用 SHAP 值进行异常值检测

这里只介绍一个异常检测的例子。

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=random_state)
d_train = lgb.Dataset(X_train, label=y_train)
d_test = lgb.Dataset(X_test, label=y_test)params = {"max_bin": 512, "learning_rate": 0.05,"boosting_type": "gbdt", "objective": "binary","metric": "binary_logloss", "verbose": -1,"min_data": 100, "random_state": 1"boost_from_average": True, "num_leaves": 10 }model = lgb.train(params, d_train, 10000, valid_sets=[d_test], early_stopping_rounds=50, verbose_eval=1000)
explainer = shap.TreeExplainer(model)
expected_value = explainer.expected_value[1]features = X_test.iloc[range(20)]
features_display = X_display.loc[features.index]
shap_values = explainer.shap_values(features)[1]y_pred = model.predict(X_test)
r = shap.decision_plot(expected_value, explainer.shap_values(T)[1], X_test[(y_pred >= 0.03) & (y_pred <= 0.1)], feature_order='hclust', return_objects=True)

将决策图叠加在一起有助于根据 SHAP value 定位异常值。在上图中,你可以看到一个不同数据集的示例,用于使用SHAP决策图进行异常值检测。

Heatmap plot

热图旨在使用监督聚类和热图显示数据集的总体子结构。监督聚类涉及的不是通过数据点的原始特征值而是通过它们的 shap values 对数据点进行聚类。默认使用 shap.utils.hclust_ordering 进行聚类。

绘图时,将 SHAP 值矩阵传递给热图绘图函数。得到的图中, x 轴上是实例、y 轴上是模型输入以及色标上是编码的 SHAP 值。默认情况下,样本使用 shap.order.hclust 排序,它基于层次聚类并根据解释相似性对样本进行排序。

这将因相同原因和具有相同模型输出的样本被分组在一起,如下图中对capital gain影响较大的人被分组在一起了。

shap.plots.heatmap(shap_values)

在热图矩阵上方是模型的输出,灰色虚线是基线(.base_value),图右侧的条形图是每个模型输入的全局重要性(默认用shap.order.abs.mean整体重要性来度量)

更改排序顺序和全局特征重要性值

通过给feature_values参数传递一组值来改变衡量特征整体重要性的方式(以及它们的排序顺序)。默认情况下feature_values=shap.Explanation.abs.mean(0),还可以在所有样本中按特征的最大绝对值进行排序。

shap.plots.heatmap(shap_values2, feature_values=shap_values.abs.max(0))

通过给instance_order参数传递一组值控制实例的顺序。默认情况下,设置instance_order=shap.Explanation.hclust(0)将具有相似解释的样本分组在一起。还可以按所有特征的 SHAP 值总和排序。

shap.plots.heatmap(shap_values2, instance_order=shap_values.sum(1))

Waterfall plot

瀑布图旨在显示单个预测的解释,因此将解释对象的单行作为输入。瀑布图从底部的模型输出的预期值开始,每一行显示每个特征的是正(红色)或负(蓝色)贡献,即如何将值从数据集上的模型预期输出值推动到模型预测的输出值。

shap.plots.waterfall(shap_values2[5])

这里值得注意拥有 2,174 美元的资本收益的人会比每年赚取超过 5 万美元的人的预测概率明显低很多。这里由于waterfall绘图仅显示了单个样本数据,因此我们无法看到资本收益变化的影响。可以使用scatter图来展示资本收益的低值是如何比根本没有资本收益更负面地预测收入。

shap.plots.scatter(shap_values2[:,"Capital Gain"])

参考文章

[1] https://shap.readthedocs.io/en/latest/index.html
[2]https://www.bilibili.com/read/cv116

SHAP 可视化解释机器学习模型简介相关推荐

  1. 独家 | 在R中使用LIME解释机器学习模型

    作者:PURVAHUILGOL 翻译:陈丹 校对:欧阳锦 本文约3200字,建议阅读15分钟 本文为大家介绍如何在R中使用LIME来解释机器学习模型,并提供了相关代码. 关键词:机器学习模型解释.R语 ...

  2. python 博弈论 库_6个Python库解释机器学习模型并建立信任

    在机器学习模型中建立信任的案例 全球道路上大约有12亿辆汽车.这是一个令人毛骨悚然的问题-您认为实际上有多少驾驶员了解车辆的内部运行情况? 正如您可能已经猜到的,答案只有少数几个人.我们不需要了解驾驶 ...

  3. 【赠书】金融领域可解释机器学习模型与实践

    ‍‍ 今天要给大家介绍的书是<可解释机器学习:模型.方法与实践>,涵盖了可解释机器学习前沿的研究成果及行业成功应用经验. 本书内容 本书分为三部分: 第一部分为背景,阐述黑盒模型存在的问题 ...

  4. 在自然对话中解释机器学习模型——通过建立一个对话式的XAI代理;保护峰值:关于尖峰神经网络对对抗性示例的可转移性和安全性;SUNet:用于全景分段的具有规模意识的统一网络;一种新型的可用于主体转移脑机

    可解释的机器学习 中文标题:在自然对话中解释机器学习模型--通过建立一个对话式的XAI代理 英文标题:Explaining Machine Learning Models in Natural Con ...

  5. LIME:一种解释机器学习模型的方法

    在本文中,我们将介绍一种方法,用来解释这篇论文中的任何一种分类器的预测结果,并且用开源包来实现. 动机:我们为什么要理解预测结果? 机器学习如今是非常火的一个话题.随着计算机在围棋等游戏中击败人类专家 ...

  6. 沙普利值(Shapley value)是怎么解释机器学习模型的?

    Shapley value 我们先介绍一下沙普利值.沙普利值来源于合作博弈,cooperative game (coalitional game).区别于传统博弈认为个体之间是相互独立的并分析其纳什均 ...

  7. 【机器学习】机器学习模型解释神器:Shapash

    什么是 Shapash 模型可解释性和可理解性一直是许多研究论文和开源项目的关注的重点.并且很多项目中都配备了数据专家和训练有素的专业人员.Shapash 是一个 Python 库,用于描述 AI 模 ...

  8. 5 大常用机器学习模型类型总结

    本文介绍了 5 大常用机器学习模型类型:集合学习算法,解释型算法,聚类算法,降维算法,相似性算法,并简要介绍了每种类型中最广泛使用的算法模型.我们希望本文可以做到以下三点: 1.应用性. 涉及到应用问 ...

  9. SHAP | 机器学习模型解释库

    来源:大邓和他的Python SHAP机器学习模型解释库 想象一下,你正试图训练一个机器学习模型来预测广告是否被特定的人点击.在收到关于某人的一些信息后,模型预测某人会不会点击广告. 但是为什么模型会 ...

  10. 独家 | 用XGBoost入门可解释机器学习

    作者:Scott Lundberg 翻译:和中华 校对:张一然 本文长度为4300字,建议阅读8分钟 本文为大家介绍用XGBoost解释机器学习. 这是一个故事,关于错误地解释机器学习模型的危险以及正 ...

最新文章

  1. Java多线程并发常用类实例之:exchanger
  2. 科沃斯旺宝与阿里云联合参加通信展
  3. ubuntu mysql 5.7_Ubuntu 18.04 安装mysql5.7
  4. (7)操作系统安全机制一
  5. caffe学习笔记--跑个SampleCode
  6. 有关Spring缓存性能的更多信息
  7. 计算机线性输入录音原理,耳机输出的模拟信号-怎样把声音通过线路录入电脑?比方说,收音机的耳机输出孔,接线(串 爱问知识人...
  8. “要么你去驾驭生命,要么生命驾驭你。你的心态决定谁是坐骑,谁是骑师。”...
  9. OpenGL ES渲染管线与着色器
  10. 2021年8月下旬好文收藏
  11. Fedora上配置一个安全FTP
  12. 大学生数学建模赛题解析及优秀论文-2021电工杯A题高铁牵引供电系统运行数据分析及等值建模(附Python代码)
  13. 北京调频(FM)无线广播发射频率/频道表 (转载)
  14. energy plus matlab,Energyplus教程系列1—Energyplus到底能干啥.ppt
  15. 启程Objectvie-C(绿柠檬学习笔记)
  16. DCT算法的原理和优化
  17. 如何更改HomePod使用的Apple ID?
  18. 微软发布Windows 10:连Windows 7都能免费升级了
  19. 基于Unity引擎利用OpenCV和MediaPipe的面部表情和人体运动捕捉系统
  20. 及时备份数据可以保护计算机系统吗,电脑数据的保护和备份

热门文章

  1. 代码重构(一)原理和规范
  2. 数据结构课程设计——逆波兰表达式的计算
  3. 3 - Error writing file 'C:\Windows\TEMP\MY18F3.tmp' (Errcode: 28)
  4. 全志V3S H.264 对视频进行编码的过程
  5. 1052 Linked List Sorting (25 分)-PAT甲级
  6. 小白通过JDBC在AndroidStudio一步步来访问MYSQL数据库
  7. .htaccess wp博客 静态网页 永久链接一步步来
  8. 西南石油大学天空教室_学府之旅 | 西南石油大学
  9. 2022年南京大学软件工程专硕上岸经验帖
  10. 金融,财务,融资相关知识(三)