【深度学习】Transformer分类器,CICIDS2017,入侵检测,随机森林、RFE、全连接神经网络

文章目录

      • 1 前言
      • 2 随机森林训练
      • 3 递归特征消除 RFE Recursive feature elimination
      • 4 DNN
      • 5 Transformer
        • 5.1. 输入嵌入层(Input Embedding Layer)
        • 5.2. 位置编码层(Positional Encoding Layer)
        • 5.3. Transformer编码器层(Transformer Encoder Layer)
        • 5.4. 解码层(Decoder Layer)(非Transformer的解码器)
        • 5.5. 模型的前向传播(Forward Pass)
        • 5.6. 与BERT比较
        • 5.7. 多头注意力机制
      • 6 Transformer模型训练
      • 全部代码

代码包:

https://docs.qq.com/sheet/DUEdqZ2lmbmR6UVdU?tab=BB08J2

1 前言

随着网络攻击的日益猖獗,保护网络安全变得愈发重要。传统的入侵检测系统(IDS)在应对复杂和多变的攻击模式时显得力不从心。为了应对这一挑战,我们提出了一种基于Transformer的入侵检测系统,以提高检测精度和鲁棒性。

入侵检测系统(IDS)是一种监控网络流量、识别和响应潜在安全威胁的工具。它们通过分析流量模式来检测异常行为,从而保护网络安全。然而,随着攻击技术的不断进化,传统的IDS面临着识别新型和复杂攻击的挑战。

CICIDS2017数据集介绍:
https://blog.csdn.net/yuangan1529/article/details/115024003

CICIDS2017数据集下载:
http://205.174.165.80/CICDataset/CIC-IDS-2017/Dataset/CIC-IDS-2017/PCAPs/

下载这个即可:
在这里插入图片描述
解压后:
在这里插入图片描述
执行代码包中的x01_数据分析.py,得到数据分布:
在这里插入图片描述
Heartbleed类别只有11个,而正常类别BENIGN有227w多个。BENIGN取24w出来用就够了。

执行程序包的x02得到一个总的csv文件combined_output.py,一个合并的csv文件。执行x03画出当前样本数量.py得到:

在这里插入图片描述

将我的数据文件combined_output.csv进行拆分,拆分为训练集和测试集,拆分的比例是8:2,Label列是类别(字符串形式),按照每个类别都8:2的形式拆分。对于样本数量少于500个,那么这个类别的所有数据既作为训练集,也作为测试集。

执行x05_画出训练集和测试机的样本数量直方图.py得到拆分后的数据统计:
在这里插入图片描述

2 随机森林训练

执行代码包的x06_0随机森林训练.py.

# 3. 拆分数据集
X_train, X_test, y_train, y_test = X[:len(data_train)], X[len(data_train):], y[:len(data_train)], y[len(data_train):]# 4. 训练随机森林模型
rf_model = RandomForestClassifier(class_weight='balanced', n_estimators=100, random_state=42, verbose=2, n_jobs=-1)
if os.path.exists('model/RandomForest_rf_model.pkl'):rf_model = joblib.load('model/RandomForest_rf_model.pkl')
else:rf_model.fit(X_train, y_train)# 保存模型joblib.dump(rf_model, 'model/RandomForest_rf_model.pkl')

测试结果:
整体准确率: 1.00
平均准确率: 0.93, 平均召回率: 0.95

在这里插入图片描述
随机森林模型获取特征重要程度的方法通常是通过基于树的特征重要性计算。在随机森林中,每棵树都是基于对数据的随机子样本和随机选择的特征进行训练的。当模型训练完成后,可以根据每个特征在所有树中的分裂情况和准确度变化来评估其重要性。

可以看出有些特征很重要,有些特征就不重要:

特征重要程度:
Destination Port: 0.0785
Init_Win_bytes_backward: 0.0582
Flow IAT Mean: 0.0308
min_seg_size_forward: 0.0283
Max Packet Length: 0.0273
Init_Win_bytes_forward: 0.0271
Fwd Packet Length Max: 0.0268Fwd Avg Bytes/Bulk: 0.0000
Fwd Avg Packets/Bulk: 0.0000
Fwd Avg Bulk Rate: 0.0000
Bwd Avg Bytes/Bulk: 0.0000
Bwd Avg Packets/Bulk: 0.0000
Bwd Avg Bulk Rate: 0.0000

RF的特征选择本质上就是基于信息熵的选择,就是基于树的选择,可以看这里的参考资料:
https://scikit-learn.org/stable/modules/feature_selection.html

选取重要都大于0的特征参与训练,可以减少特征维度,计算量也就少很多了。

3 递归特征消除 RFE Recursive feature elimination

给定一个将权重分配给特征的外部估计器(例如,线性模型的系数),递归特征消除(RFE)的目标是通过递归考虑越来越小的特征集来选择特征。首先,估计器在初始特征集上进行训练,并通过任何特定属性(如coef_,feature_importances_)或可调用函数获得每个特征的重要性。然后,将当前特征集中的最不重要的特征修剪掉。该过程在修剪后的集合上递归重复,直到最终达到所需选择的特征数。

RFECV 在交叉验证循环中执行 RFE,以找到最佳特征数量。更详细地说,所选特征的数量通过在不同的交叉验证拆分上拟合 RFE 选择器来自动调整(由 cv 参数提供)。使用评分器评估所选择特征数量的 RFE 选择器的性能,并将其聚合在一起。最后,得分在交叉验证的折叠中平均,所选特征的数量被设置为最大化交叉验证得分的特征数量。

RFE类一些主要的可选参数:

  1. estimator:要使用的外部估计器对象,默认为None。如果不指定,则RFE会在内部使用estimator参数指定的算法来选择特征。

  2. step:每次迭代中删除的特征数量,默认为1。较大的步长可能会加快特征选择过程,但可能会导致性能下降。

  3. verbose:控制输出的详细程度,默认为0(不输出任何信息)。如果设置为1,则在特征选择过程中会输出一些信息。

  4. importance_getter:一个可调用对象,用于获取特征的重要性。默认为'auto',表示根据估计器类型自动选择合适的方法。

  5. min_features_to_select:最小要选择的特征数量,默认为1。在特征数量小于此值时,特征选择过程将停止。

执行代码包中的x07_0RFE.py,挑选20个最重要的特征,挑选结果为:

Index([‘Destination Port’, ‘Flow Duration’, ‘Total Length of Bwd Packets’,
‘Fwd Packet Length Max’, ‘Bwd Packet Length Max’, ‘Flow IAT Mean’,
‘Flow IAT Std’, ‘Flow IAT Max’, ‘Fwd IAT Total’, ‘Fwd IAT Mean’,
‘Fwd IAT Max’, ‘Bwd Header Length’, ‘Max Packet Length’,
‘Packet Length Mean’, ‘Average Packet Size’, ‘Subflow Fwd Bytes’,
‘Subflow Bwd Bytes’, ‘Init_Win_bytes_forward’,
‘Init_Win_bytes_backward’, ‘min_seg_size_forward’],
dtype=‘object’)

4 DNN

使用这20个特征训练DNN网络,执行x08_0DNN训练.py和x08_1DNN测试.py,得到DNN准确率:

整体准确率: 0.98
==============================
dnn_classification
Macro-average Precision: 0.7794444444444445
Macro-average Recall: 0.8433333333333333

在这里插入图片描述
特征太少了,计算量是少了,但是准确度也下降了。

5 Transformer

Transformer模型最初由Vaswani等人在自然语言处理(NLP)领域提出,以其在处理序列数据方面的优越性能而闻名。与传统的循环神经网络(RNN)和长短期记忆网络(LSTM)相比,Transformer能够更高效地捕捉序列数据中的长距离依赖关系。这一特性使其在网络流量分析中表现出色。

基于Transformer架构,包括以下几个关键部分:

  1. 输入嵌入:将网络流量数据转换为固定维度的向量表示,同时保留其序列信息。
  2. 位置编码:通过位置编码保留特征序列的顺序信息。
  3. 自注意力机制:应用自注意力层,捕捉特征之间的全局依赖关系。
  4. 编码器-解码器结构:利用堆叠的编码器和解码器层提取特征并进行分类。
  5. 输出层:通过softmax层输出分类概率。
5.1. 输入嵌入层(Input Embedding Layer)

输入嵌入层将原始的网络流量特征映射到一个固定维度的向量空间中。

self.embedding = nn.Linear(input_dim, model_dim)
  • 公式
    E = X W e + b e E = XW_e + b_e E=XWe+be
    其中,( X ) 是输入特征矩阵,( W_e ) 和 ( b_e ) 分别是嵌入层的权重矩阵和偏置向量,( E ) 是嵌入向量。
5.2. 位置编码层(Positional Encoding Layer)

位置编码层通过添加位置编码向量来保留序列信息。位置编码向量由正弦和余弦函数生成。

class PositionalEncoding(nn.Module):def __init__(self, d_model, max_len=5000):super(PositionalEncoding, self).__init__()pe = torch.zeros(max_len, d_model)position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-torch.log(torch.tensor(10000.0)) / d_model))pe[:, 0::2] = torch.sin(position * div_term)pe[:, 1::2] = torch.cos(position * div_term)pe = pe.unsqueeze(0).transpose(0, 1)self.register_buffer('pe', pe)def forward(self, x):return x + self.pe[:x.size(0), :]
  • 公式
    P E ( p o s , 2 i ) = sin ⁡ ( p o s 1000 0 2 i / d m o d e l ) PE_{(pos, 2i)} = \sin \left( \frac{pos}{10000^{2i/d_{model}}} \right) PE(pos,2i)=sin(100002i/dmodelpos)
    P E ( p o s , 2 i + 1 ) = cos ⁡ ( p o s 1000 0 2 i / d m o d e l ) PE_{(pos, 2i+1)} = \cos \left( \frac{pos}{10000^{2i/d_{model}}} \right) PE(pos,2i+1)=cos(100002i/dmodelpos)
    其中,( pos ) 是位置,( i ) 是维度索引。

max_len=5000中的5000表示序列的最大长度。在Transformer模型中,位置编码用于为输入序列中的每个位置添加一个向量表示其位置信息。这个向量是根据位置编码函数生成的,函数中的参数包括位置和维度索引。

位置编码的作用是为模型提供序列中各个位置的相对位置信息。这对于理解序列中的顺序和间隔是至关重要的。在自注意力机制中,序列中不同位置之间的关系是由位置编码来表示的,而不是像循环神经网络或卷积神经网络那样,通过固定的滑动窗口或循环结构来捕获序列中的顺序信息。

为什么将max_len设置为5000呢?这主要是为了应对在训练和推理过程中可能遇到的最大序列长度。通常情况下,我们会将max_len设置为训练数据集中序列的最大长度,以确保所有序列都能够被正确编码。在实际应用中,如果序列长度超过了max_len,可以通过截断或其他方法进行处理。

下面举一个例子来说明位置编码的作用:

假设有一个长度为4的序列:[ a, b, c, d ],并且我们使用一个4维的位置编码。

位置编码的生成过程如下:

  1. 对于每个位置 ( pos ),生成一个长度为4的位置编码向量 ( \text{PE}_{(pos)} )。
  2. 对于每个维度索引 ( i ),根据位置 ( pos ) 计算该维度的位置编码值。

例如,对于维度索引 ( i = 0 ) 和位置 ( pos = 1 ):
PE ( 1 , 0 ) = sin ⁡ ( 1 1000 0 0 / 4 ) = sin ⁡ ( 1 ) \text{PE}_{(1, 0)} = \sin \left( \frac{1}{10000^{0/4}} \right) = \sin(1) PE(1,0)=sin(100000/41)=sin(1)

PE ( 1 , 1 ) = cos ⁡ ( 1 1000 0 0 / 4 ) = cos ⁡ ( 1 ) \text{PE}_{(1, 1)} = \cos \left( \frac{1}{10000^{0/4}} \right) = \cos(1) PE(1,1)=cos(100000/41)=cos(1)

PE ( 1 , 2 ) = sin ⁡ ( 1 1000 0 2 / 4 ) = sin ⁡ ( 1 / 100 ) \text{PE}_{(1, 2)} = \sin \left( \frac{1}{10000^{2/4}} \right) = \sin(1/100) PE(1,2)=sin(100002/41)=sin(1/100)

PE ( 1 , 3 ) = cos ⁡ ( 1 1000 0 2 / 4 ) = cos ⁡ ( 1 / 100 ) \text{PE}_{(1, 3)} = \cos \left( \frac{1}{10000^{2/4}} \right) = \cos(1/100) PE(1,3)=cos(100002/41)=cos(1/100)

这样,每个位置都被赋予了一个唯一的位置编码向量,这些向量将会在模型的训练和推理过程中与输入的特征向量相结合,从而帮助模型更好地理解序列中各个位置的相对关系。

5.3. Transformer编码器层(Transformer Encoder Layer)

Transformer编码器层由多个自注意力层和前馈神经网络层堆叠而成。

encoder_layers = nn.TransformerEncoderLayer(d_model=model_dim, nhead=num_heads)
self.transformer_encoder = nn.TransformerEncoder(encoder_layers, num_layers)
  • 自注意力机制公式
    Attention ( Q , K , V ) = softmax ( Q K T d k ) V \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V Attention(Q,K,V)=softmax(dk QKT)V
    其中,( Q, K, V ) 分别是查询矩阵、键矩阵和值矩阵,( d_k ) 是键的维度。

  • 前馈神经网络公式
    FFN ( x ) = max ⁡ ( 0 , x W 1 + b 1 ) W 2 + b 2 \text{FFN}(x) = \max(0, xW_1 + b_1)W_2 + b_2 FFN(x)=max(0,xW1+b1)W2+b2
    其中,( W_1, W_2 ) 和 ( b_1, b_2 ) 是前馈神经网络的权重矩阵和偏置向量。

5.4. 解码层(Decoder Layer)(非Transformer的解码器)

解码层将编码器的输出转换为最终的分类结果。

self.decoder = nn.Linear(model_dim, num_classes)
  • 公式
    y ^ = E W d + b d \hat{y} = EW_d + b_d y^=EWd+bd
    其中,( E ) 是编码器的输出,( W_d ) 和 ( b_d ) 分别是解码层的权重矩阵和偏置向量,( \hat{y} ) 是预测的分类结果。
5.5. 模型的前向传播(Forward Pass)
    def forward(self, x):x = self.embedding(x)x = self.pos_encoder(x)x = self.transformer_encoder(x)x = self.decoder(x)return x
  1. 嵌入层输出
    E = X W e + b e E = XW_e + b_e E=XWe+be
  2. 位置编码输出
    E ′ = E + P E E' = E + PE E=E+PE
  3. 编码器输出
    Z = TransformerEncoder ( E ′ ) Z = \text{TransformerEncoder}(E') Z=TransformerEncoder(E)
  4. 解码层输出(非Transformer的解码器)
    y ^ = Z W d + b d \hat{y} = ZW_d + b_d y^=ZWd+bd
5.6. 与BERT比较

模型bert-base-uncased的基本信息是:
12-layer, 768-hidden, 12-heads, 110M parameters.

对应到我们这里可以这么初始化模型:
model = TransformerModel(input_dim=128, model_dim=768, num_heads=12, num_layers=12, num_classes=10)

model_dim、num_layers太大,会导致训练很慢。

5.7. 多头注意力机制

多头注意力机制允许模型在处理输入序列时同时关注序列中不同位置的多个方面或特征,从而提高了模型的表征能力和泛化能力。每个注意力头都可以学习到不同的注意力权重,以便捕捉输入序列中不同位置的信息,并且可以并行计算,从而加快了模型的训练速度。

公式上,多头注意力机制可以表示为:

给定一个输入序列 (X),将其通过线性变换得到查询(Query)、键(Key)、值(Value)的表示:

Q = X W Q , K = X W K , V = X W V Q = XW^Q, \quad K = XW^K, \quad V = XW^V Q=XWQ,K=XWK,V=XWV

其中 (WQ)、(WK)、(W^V) 是学习的权重矩阵。

然后,计算每个头的注意力分数 (A_i):

A i = softmax ( Q i K i T d k ) A_i = \text{softmax}\left(\frac{Q_iK_i^T}{\sqrt{d_k}}\right) Ai=softmax(dk QiKiT)

其中 (Q_i)、(K_i) 是第 (i) 个头的查询和键,(d_k) 是查询和键的维度。

最后,将每个头的注意力权重与对应的值相乘并加权求和得到最终的输出:

MultiHead ( Q , K , V ) = Concat ( head 1 , head 2 , . . . , head h ) W O \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \text{head}_2, ..., \text{head}_h)W^O MultiHead(Q,K,V)=Concat(head1,head2,...,headh)WO

在这里插入图片描述

这样,通过多头注意力机制,模型可以同时关注输入序列的不同方面,更好地捕捉序列中的关键信息。

6 Transformer模型训练

训练Transformer模型,执行代码包的x09_0_trasformer训练.py,得到训练结果:
在这里插入图片描述
执行x10_0_trasformer训练_webattack算一类.py:
在这里插入图片描述
执行x11_0_trasformer训练_作为2个类别训练.py
在这里插入图片描述

全部代码

https://docs.qq.com/sheet/DUEdqZ2lmbmR6UVdU?tab=BB08J2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/27330.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

堆的实现及其应用

堆的概念 堆是完全二叉树,分为大堆和小堆。大堆:任何一个父亲都大于等于孩子,小堆:任何一个父亲都小于等于孩子。 堆的实现 目录 typedef int HPDataType;typedef struct Heap { HPDataType* a;int size;int capacity; }HP;//交…

C语言之操作符

目录 一、二进制 原码、反码、补码 二、移位操作符 位操作符 三、 逗号表达式 四、下标访问[]、函数调用() 五. 操作符的属性 整型提升 算术转换 六、总结 一、二进制 其实2进制、8进制、10进制、16进制是数值的不同表示形式而已。 其实10进制是生活中经常使用的&am…

类别朴素贝叶斯CategoricalNB和西瓜数据集

CategoricalNB 1 CategoricalNB原理以及用法2 数据集2.1 西瓜数据集2.2 LabelEncoder2.3 OrdinalEncoder 3 代码实现 1 CategoricalNB原理以及用法 (1)具体原理 具体原理可看:贝叶斯分类器原理 sklearn之CategoricalNB对条件概率的原理如下&…

粉丝经济时代:微信订阅号如何助力中小企业增长

在数字化浪潮席卷全球的今天,微信订阅号凭借其独特的优势,成为了中小企业数字化出海的重要工具。作为NetFarmer,我们致力于帮助企业充分利用这一平台,推动业务发展和市场拓展。今天将深入探讨微信订阅号的概念、用途、使用方法、适…

mac安装高版本git(更新git)

问题 问题:新下载的idea,此idea的版本较高,但是在工作发现这个版本的git存在一定漏洞会导致一些信息泄露问题。 1.安装Homebrew 对于Mac更新git,最简单的就是使用brew命令。所以我们首先下载homebrew。已下载的同学忽略直接下一…

【数据结构陈越版笔记】进阶实验1-3.1:两个有序序列的中位数

我这答案做的可能不对,如果不对,欢迎大家指出错误,思路大部分直接写在注释中了。 进阶实验1-3.1:两个有序序列的中位数 已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列 A 0 , A 1 , . . . , A n −…

ES升级--05--快照生成 和备份

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 备份ES数据1.关闭集群自动均衡2.执行同步刷新3.停止集群节点的Elasticsearch服务4.修改Elasticsearch配置文件,开启快照功能,配置仓库目录为…

微信群发机器人.使用指南.

0.简介 1.介绍 微信群发机器人是用来群发微信消息的工具,通过控制电脑的键盘和鼠标操作微信app来实现群发.支持的消息类型有:文字,图片,视频,文件,小程序,位置等. 群发机器人也可以将微信联系人中的信息保存到电脑csv表格中,以供分析. 因其是通过模拟用户操作鼠标键盘来实现群…

the histogram of cross-entropy loss values 交叉熵损失值的直方图以及cross-entropy loss交叉熵损失

交叉熵损失值的直方图在机器学习和深度学习中有几个重要的作用和用途: 评估模型性能: 直方图可以帮助评估模型在训练数据和测试数据上的性能。通过观察损失值的分布,可以了解模型在不同数据集上的表现情况。例如,损失值分布的形状和范围可以反…

C++中extern “C“的用法

目的 extern "C"是经常用到的东西,面试题目也经常出现,然则,实际用时,还是经常遗忘,因此,深入的了解一下,以增强记忆。 extern "C"指令非常有用,因为C和C的近亲…

Android MediaMetadataRetriever获取视频宽高,Java

Android MediaMetadataRetriever获取视频宽高,Java public static int[] getVideoSize(Context ctx, Uri uri) {MediaMetadataRetriever retriever new MediaMetadataRetriever();int[] size {-1, -1}; //宽,高try {retriever.setDataSource(ctx, uri)…

双向转发检测BFD(学习笔记)

定义 双向转发检测BFD(Bidirectional Forwarding Detection)是一种全网统一的检测机制,用于快速检测、监控网络中链路或者IP路由的转发连通状况 BFD检测机制 BFD的检测机制是两个系统建立BFD会话,并沿它们之间的路径周期性发送B…

Java 开发实例:Spring Boot+AOP+注解+Redis防重复提交(防抖)

文章目录 1. 环境准备2. 引入依赖3. 配置Redis4. 创建防重复提交注解5. 实现AOP切面6. 创建示例Controller7. 测试8. 进一步优化8.1 自定义异常处理8.2 提升Redis的健壮性 9. 总结 🎉欢迎来到Java学习路线专栏~探索Java中的静态变量与实例变量 ☆* o(≧▽≦)o *☆嗨…

King Media 8.2 中文版安装

King Media-Viral Magazine News Video是一个用于架设社交网站的php脚本,能让您创建一个视频、新闻和图像的新颖社交网站。 功能 支持:从Url、Youtube、Vimeo、Vine、Instagram、Metacafe、DailyMotion上传和分享图片通过Facebook、谷歌、雅虎、Github和…

EC20通信模块升级失败 Quectel QDLoader 9008

这里写自定义目录标题 usb驱动下载固件和升级软件下载开始升级上述过程升级失败,出现Quectel QDLoader 9008寻找解决方案,事了QPS t不行,最终使用这个Quectel_Customer_FW_Download_Tool软件解决下载链接: 所有下载驱动、固件、软…

C++数据结构02 队列及其应用

目录 队列及其特点 利用数组模拟队列的基本操作 创建队列 空队条件 元素入队 元素出队 模拟超市收银问题 队列操作 初始化 入队操作 出队操作 取出队首元素 STL模板中队列的基本使用 训练:约瑟夫问题 参考程序 队列及其特点 队列是一种特殊的线性表&am…

Mac如何卸载掉系统自带的预装软件吗 Mac第三方软件无法卸载是为什么 macbook系统软件怎么删除?

在使用Mac电脑时,有时候我们会发现系统预装的一些应用并不常用或者不符合个人需求,想要将它们卸载掉。然而,对于系统自带的软件,卸载并不简单,需要谨慎对待以免影响系统稳定性和功能正常运行。 一、Mac可以卸载掉系统自…

Android安全开发之 Provider 组件安全

Android系统中的Content Provider组件是一种用于在不同应用之间共享数据的机制。它提供了一种安全、可控的方式,允许应用访问其他应用的数据。然而,如果Provider组件的安全措施没有得到妥善实现,则可能会导致严重的安全漏洞,例如数…

比利时海外媒体宣发,发稿促进媒体通稿发布新形势-大舍传媒

引言 随着全球化的推进,海外媒体的影响力也日益增强。在这一背景下,比利时海外媒体的宣发工作成为了媒体通稿发布的新形势。大舍传媒作为一家专注于宣传推广的公司,一直致力于与比利时博伊克邮报(boicpost)合作&#…

用数据说话,效果好上一万倍,不是空口说白话的“好很多”

作为一名大数据开发者,我深知数据的有很大的魔力(我这句话就没用数据,听上去很无力)。数据不仅仅是数字和图表,它还能赋予我们强大的说服力和权威感。让我给你详细讲讲数据如何让理论插上翅膀。 目录 数据的“靠谱”…