• Imblearn package study
  • 准备知识
    • 1 Compressed Sparse RowsCSR 压缩稀疏的行
  • 过采样Over-sampling
    • 1 实用性的例子

      • 11 朴素随机过采样
      • 12 从随机过采样到SMOTE与ADASYN
      • 13 SMOTE的变体
      • 14 数学公式
  • 下采样Under-sampling
    • 1 原型生成prototype generation
    • 2 原型选择prototype selection
      • 21 Controlled under-sampling techniques
      • 22 Cleaning under-sampling techniques
        • 221 Tomeks links
        • 222 Edited data set using nearest neighbours
        • 223 Condensed nearest neighbors and derived algorithms
        • 224 Instance hardness threshold
  • 过采样与下采样的结合
  • Ensemble的例子
    • 1 例子
    • 2 Chaining ensemble of samplers and estimators
  • 数据载入
    • 1 不平衡数据集
    • 2 生成不平衡数据
  • 参考资料

Imblearn package study

1. 准备知识

Sparse input

For sparse input the data is converted to the Compressed Sparse Rows representation (see scipy.sparse.csr_matrix) before being fed to the sampler. To avoid unnecessary memory copies, it is recommended to choose the CSR representation upstream.

1.1 Compressed Sparse Rows(CSR) 压缩稀疏的行

稀疏矩阵中存在许多0元素, 按矩阵A进行存储会占用很大的空间(内存).

CSR方法采取按行压缩的办法, 将原始的矩阵用三个数组进行表示:

data = np.array([1, 2, 3, 4, 5, 6])
indices = np.array([0, 2, 2, 0, 1, 2])
indptr = np.array([0, 2, 3, 6])

data数组: 存储着矩阵A中所有的非零元素;

indices数组: data数组中的元素在矩阵A中的列索引

indptr数组: 存储着矩阵A中每行第一个非零元素在data数组中的索引.

from scipy import sparse
mtx = sparse.csr_matrix((data,indices,indptr),shape=(3,3))
mtx.todense()Out[27]:
matrix([[1, 0, 2],[0, 0, 3],[4, 5, 6]])

为什么会有针对不平衡数据的研究? 当我们的样本数据中, 正负样本的数据占比极其不均衡的时候, 模型的效果就会偏向于多数类的结果. 具体的可参照官网利用支持向量机进行可视化不同正负样本比例情况下的模型分类结果.

2. 过采样(Over-sampling)

2.1 实用性的例子

2.1.1 朴素随机过采样

针对不平衡数据, 最简单的一种方法就是生成少数类的样本, 这其中最基本的一种方法就是: 从少数类的样本中进行随机采样来增加新的样本, RandomOverSampler 函数就能实现上述的功能.

from sklearn.datasets import make_classification
from collections import Counter
X, y = make_classification(n_samples=5000, n_features=2, n_informative=2,n_redundant=0, n_repeated=0, n_classes=3,n_clusters_per_class=1,weights=[0.01, 0.05, 0.94],class_sep=0.8, random_state=0)
Counter(y)
Out[10]: Counter({0: 64, 1: 262, 2: 4674})from imblearn.over_sampling import RandomOverSamplerros = RandomOverSampler(random_state=0)
X_resampled, y_resampled = ros.fit_sample(X, y)sorted(Counter(y_resampled).items())
Out[13]:
[(0, 4674), (1, 4674), (2, 4674)]

以上就是通过简单的随机采样少数类的样本, 使得每类样本的比例为1:1:1.

2.1.2 从随机过采样到SMOTEADASYN

相对于采样随机的方法进行过采样, 还有两种比较流行的采样少数类的方法: (i) Synthetic Minority Oversampling Technique (SMOTE); (ii) Adaptive Synthetic (ADASYN) .

SMOTE: 对于少数类样本a, 随机选择一个最近邻的样本b, 然后从a与b的连线上随机选取一个点c作为新的少数类样本;

ADASYN: 关注的是在那些基于K最近邻分类器被错误分类的原始样本附近生成新的少数类样本

from imblearn.over_sampling import SMOTE, ADASYNX_resampled_smote, y_resampled_smote = SMOTE().fit_sample(X, y)sorted(Counter(y_resampled_smote).items())
Out[29]:
[(0, 4674), (1, 4674), (2, 4674)]X_resampled_adasyn, y_resampled_adasyn = ADASYN().fit_sample(X, y)sorted(Counter(y_resampled_adasyn).items())
Out[30]:
[(0, 4674), (1, 4674), (2, 4674)]

2.1.3 SMOTE的变体

相对于基本的SMOTE算法, 关注的是所有的少数类样本, 这些情况可能会导致产生次优的决策函数, 因此SMOTE就产生了一些变体: 这些方法关注在最优化决策函数边界的一些少数类样本, 然后在最近邻类的相反方向生成样本.

SMOTE函数中的kind参数控制了选择哪种变体, (i) borderline1, (ii) borderline2, (iii) svm:

from imblearn.over_sampling import SMOTE, ADASYN
X_resampled, y_resampled = SMOTE(kind='borderline1').fit_sample(X, y)print sorted(Counter(y_resampled).items())
Out[31]:
[(0, 4674), (1, 4674), (2, 4674)]

2.1.4 数学公式

SMOTE算法与ADASYN都是基于同样的算法来合成新的少数类样本: 对于少数类样本a, 从它的最近邻中选择一个样本b, 然后在两点的连线上随机生成一个新的少数类样本, 不同的是对于少数类样本的选择.

原始的SMOTEkind='regular' , 随机选取少数类的样本.

The borderline SMOTEkind='borderline1' or kind='borderline2'

此时, 少数类的样本分为三类: (i) 噪音样本(noise), 该少数类的所有最近邻样本都来自于不同于样本a的其他类别; (ii) 危险样本(in danger), 至少一半的最近邻样本来自于同一类(不同于a的类别); (iii) 安全样本(safe), 所有的最近邻样本都来自于同一个类.

这两种类型的SMOTE使用的是危险样本来生成新的样本数据, 对于 Borderline-1 SMOTE, 最近邻中的随机样本b与该少数类样本a来自于不同的类; 不同的是, 对于 Borderline-2 SMOTE , 随机样本b可以是属于任何一个类的样本;

SVM SMOTEkind='svm', 使用支持向量机分类器产生支持向量然后再生成新的少数类样本.

3. 下采样(Under-sampling)

3.1 原型生成(prototype generation)

给定数据集S, 原型生成算法将生成一个子集S’, 其中|S’| < |S|, 但是子集并非来自于原始数据集. 意思就是说: 原型生成方法将减少数据集的样本数量, 剩下的样本是由原始数据集生成的, 而不是直接来源于原始数据集.

ClusterCentroids函数实现了上述功能: 每一个类别的样本都会用K-Means算法的中心点来进行合成, 而不是随机从原始样本进行抽取.

from imblearn.under_sampling import ClusterCentroidscc = ClusterCentroids(random_state=0)
X_resampled, y_resampled = cc.fit_sample(X, y)print sorted(Counter(y_resampled).items())
Out[32]:
[(0, 64), (1, 64), (2, 64)]

ClusterCentroids函数提供了一种很高效的方法来减少样本的数量, 但需要注意的是, 该方法要求原始数据集最好能聚类成簇. 此外, 中心点的数量应该设置好, 这样下采样的簇能很好地代表原始数据.

3.2 原型选择(prototype selection)

与原型生成不同的是, 原型选择算法是直接从原始数据集中进行抽取. 抽取的方法大概可以分为两类: (i) 可控的下采样技术(the controlled under-sampling techniques) ; (ii) the cleaning under-sampling techniques(不好翻译, 就放原文, 清洗的下采样技术?). 第一类的方法可以由用户指定下采样抽取的子集中样本的数量; 第二类方法则不接受这种用户的干预.

3.2.1 Controlled under-sampling techniques

RandomUnderSampler函数是一种快速并十分简单的方式来平衡各个类别的数据: 随机选取数据的子集.

from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(random_state=0)
X_resampled, y_resampled = rus.fit_sample(X, y)print sorted(Counter(y_resampled).items())
Out[33]:
[(0, 64), (1, 64), (2, 64)]

通过设置RandomUnderSampler中的replacement=True参数, 可以实现自助法(boostrap)抽样.

import numpy as npnp.vstack({tuple(row) for row in X_resampled}).shape
Out[34]:
(192L, 2L)

很明显, 使用默认参数的时候, 采用的是不重复采样;

from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(random_state=0, replacement=True)
X_resampled, y_resampled = rus.fit_sample(X, y)print sorted(Counter(y_resampled).items())
Out[33]:
[(0, 64), (1, 64), (2, 64)]np.vstack({tuple(row) for row in X_resampled}).shape
Out[34]:
(181L, 2L)

NearMiss函数则添加了一些启发式(heuristic)的规则来选择样本, 通过设定version参数来实现三种启发式的规则.

from imblearn.under_sampling import NearMiss
nm1 = NearMiss(random_state=0, version=1)
X_resampled_nm1, y_resampled = nm1.fit_sample(X, y)print sorted(Counter(y_resampled).items())
Out[35]:
[(0, 64), (1, 64), (2, 64)]

下面通过一个例子来说明这三个启发式的选择样本的规则, 首先我们假设正样本是需要下采样的(多数类样本), 负样本是少数类的样本.

NearMiss-1: 选择离N个近邻的负样本的平均距离最小的正样本;

NearMiss-2: 选择离N个负样本最远的平均距离最小的正样本;

NearMiss-3: 是一个两段式的算法. 首先, 对于每一个负样本, 保留它们的M个近邻样本; 接着, 那些到N个近邻样本平均距离最大的正样本将被选择.

3.2.2 Cleaning under-sampling techniques

3.2.2.1 Tomek’s links

TomekLinks : 样本x与样本y来自于不同的类别, 满足以下条件, 它们之间被称之为TomekLinks; 不存在另外一个样本z, 使得d(x,z) < d(x,y) 或者 d(y,z) < d(x,y)成立. 其中d(.)表示两个样本之间的距离, 也就是说两个样本之间互为近邻关系. 这个时候, 样本x或样本y很有可能是噪声数据, 或者两个样本在边界的位置附近.

TomekLinks函数中的auto参数控制Tomek’s links中的哪些样本被剔除. 默认的ratio='auto' 移除多数类的样本, 当ratio='all'时, 两个样本均被移除.

3.2.2.2 Edited data set using nearest neighbours

EditedNearestNeighbours这种方法应用最近邻算法来编辑(edit)数据集, 找出那些与邻居不太友好的样本然后移除. 对于每一个要进行下采样的样本, 那些不满足一些准则的样本将会被移除; 他们的绝大多数(kind_sel='mode')或者全部(kind_sel='all')的近邻样本都属于同一个类, 这些样本会被保留在数据集中.

print sorted(Counter(y).items())from imblearn.under_sampling import EditedNearestNeighbours
enn = EditedNearestNeighbours(random_state=0)
X_resampled, y_resampled = enn.fit_sample(X, y)print sorted(Counter(y_resampled).items())Out[36]:
[(0, 64), (1, 262), (2, 4674)]
[(0, 64), (1, 213), (2, 4568)]

在此基础上, 延伸出了RepeatedEditedNearestNeighbours算法, 重复基础的EditedNearestNeighbours算法多次.

from imblearn.under_sampling import RepeatedEditedNearestNeighbours
renn = RepeatedEditedNearestNeighbours(random_state=0)
X_resampled, y_resampled = renn.fit_sample(X, y)print sorted(Counter(y_resampled).items())
Out[37]:
[(0, 64), (1, 208), (2, 4551)]

RepeatedEditedNearestNeighbours算法不同的是, ALLKNN算法在进行每次迭代的时候, 最近邻的数量都在增加.

from imblearn.under_sampling import AllKNN
allknn = AllKNN(random_state=0)
X_resampled, y_resampled = allknn.fit_sample(X, y)print sorted(Counter(y_resampled).items())
Out[38]:
[(0, 64), (1, 220), (2, 4601)]

3.2.2.3 Condensed nearest neighbors and derived algorithms

CondensedNearestNeighbour 使用1近邻的方法来进行迭代, 来判断一个样本是应该保留还是剔除, 具体的实现步骤如下:

  1. 集合C: 所有的少数类样本;
  2. 选择一个多数类样本(需要下采样)加入集合C, 其他的这类样本放入集合S;
  3. 使用集合S训练一个1-NN的分类器, 对集合S中的样本进行分类;
  4. 将集合S中错分的样本加入集合C;
  5. 重复上述过程, 直到没有样本再加入到集合C.
from imblearn.under_sampling import CondensedNearestNeighbour
cnn = CondensedNearestNeighbour(random_state=0)
X_resampled, y_resampled = cnn.fit_sample(X, y)print sorted(Counter(y_resampled).items())
Out[39]:
[(0, 64), (1, 24), (2, 115)]

显然, CondensedNearestNeighbour方法对噪音数据是很敏感的, 也容易加入噪音数据到集合C中.

因此, OneSidedSelection 函数使用 TomekLinks 方法来剔除噪声数据(多数类样本).

from imblearn.under_sampling import OneSidedSelection
oss = OneSidedSelection(random_state=0)
X_resampled, y_resampled = oss.fit_sample(X, y)print(sorted(Counter(y_resampled).items()))
Out[39]:
[(0, 64), (1, 174), (2, 4403)]

NeighbourhoodCleaningRule 算法主要关注如何清洗数据而不是筛选(considering)他们. 因此, 该算法将使用

EditedNearestNeighbours和 3-NN分类器结果拒绝的样本之间的并集.

from imblearn.under_sampling import NeighbourhoodCleaningRule
ncr = NeighbourhoodCleaningRule(random_state=0)
X_resampled, y_resampled = ncr.fit_sample(X, y)print sorted(Counter(y_resampled).items())
Out[39]:
[(0, 64), (1, 234), (2, 4666)]

3.2.2.4 Instance hardness threshold

InstanceHardnessThreshold是一种很特殊的方法, 是在数据上运用一种分类器, 然后将概率低于阈值的样本剔除掉.

from sklearn.linear_model import LogisticRegression
from imblearn.under_sampling import InstanceHardnessThreshold
iht = InstanceHardnessThreshold(random_state=0,estimator=LogisticRegression())
X_resampled, y_resampled = iht.fit_sample(X, y)print(sorted(Counter(y_resampled).items()))
Out[39]:
[(0, 64), (1, 64), (2, 64)]

4. 过采样与下采样的结合

在之前的SMOTE方法中, 当由边界的样本与其他样本进行过采样差值时, 很容易生成一些噪音数据. 因此, 在过采样之后需要对样本进行清洗. 这样, 第三节中涉及到的TomekLink 与 EditedNearestNeighbours方法就能实现上述的要求. 所以就有了两种结合过采样与下采样的方法: (i) SMOTETomek and (ii) SMOTEENN.

from imblearn.combine import SMOTEENN
smote_enn = SMOTEENN(random_state=0)
X_resampled, y_resampled = smote_enn.fit_sample(X, y)print sorted(Counter(y_resampled).items())
Out[40]:
[(0, 4060), (1, 4381), (2, 3502)]from imblearn.combine import SMOTETomek
smote_tomek = SMOTETomek(random_state=0)
X_resampled, y_resampled = smote_tomek.fit_sample(X, y)print sorted(Counter(y_resampled).items())
Out[40]:
[(0, 4499), (1, 4566), (2, 4413)]

5. Ensemble的例子

5.1 例子

一个不均衡的数据集能够通过多个均衡的子集来实现均衡, imblearn.ensemble模块能实现上述功能.

EasyEnsemble 通过对原始的数据集进行随机下采样实现对数据集进行集成.

from imblearn.ensemble import EasyEnsemble
ee = EasyEnsemble(random_state=0, n_subsets=10)
X_resampled, y_resampled = ee.fit_sample(X, y)print X_resampled.shape
print sorted(Counter(y_resampled[0]).items())
Out[40]:
(10L, 192L, 2L)
[(0, 64), (1, 64), (2, 64)]

EasyEnsemble 有两个很重要的参数: (i) n_subsets 控制的是子集的个数 and (ii) replacement 决定是有放回还是无放回的随机采样.

与上述方法不同的是, BalanceCascade(级联平衡)的方法通过使用分类器(estimator参数)来确保那些被错分类的样本在下一次进行子集选取的时候也能被采样到. 同样, n_max_subset 参数控制子集的个数, 以及可以通过设置bootstrap=True来使用bootstraping(自助法).

from imblearn.ensemble import BalanceCascade
from sklearn.linear_model import LogisticRegression
bc = BalanceCascade(random_state=0,estimator=LogisticRegression(random_state=0),n_max_subset=4)
X_resampled, y_resampled = bc.fit_sample(X, y)print X_resampled.shapeprint sorted(Counter(y_resampled[0]).items())
Out[41]:
(4L, 192L, 2L)
[(0, 64), (1, 64), (2, 64)]

5.2 Chaining ensemble of samplers and estimators

在集成分类器中, 装袋方法(Bagging)在不同的随机选取的数据集上建立了多个估计量. 在scikit-learn中这个分类器叫做BaggingClassifier. 然而, 该分类器并不允许对每个数据集进行均衡. 因此, 在对不均衡样本进行训练的时候, 分类器其实是有偏的, 偏向于多数类.

from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifierX_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
bc = BaggingClassifier(base_estimator=DecisionTreeClassifier(),random_state=0)
bc.fit(X_train, y_train) y_pred = bc.predict(X_test)
confusion_matrix(y_test, y_pred)
Out[35]:
array([[   0,    0,   12],[   0,    0,   59],[   0,    0, 1179]], dtype=int64)

BalancedBaggingClassifier 允许在训练每个基学习器之前对每个子集进行重抽样. 简而言之, 该方法结合了EasyEnsemble采样器与分类器(如BaggingClassifier)的结果.

from imblearn.ensemble import BalancedBaggingClassifier
bbc = BalancedBaggingClassifier(base_estimator=DecisionTreeClassifier(),ratio='auto',replacement=False,random_state=0)
bbc.fit(X, y) y_pred = bbc.predict(X_test)
confusion_matrix(y_test, y_pred)
Out[39]:
array([[  12,    0,    0],[   0,   55,    4],[  68,   53, 1058]], dtype=int64)

6. 数据载入

imblearn.datasets 包与sklearn.datasets 包形成了很好的互补. 该包主要有以下两个功能: (i)提供一系列的不平衡数据集来实现测试; (ii) 提供一种工具将原始的平衡数据转换为不平衡数据.

6.1 不平衡数据集

fetch_datasets 允许获取27个不均衡且二值化的数据集.

from collections import Counter
from imblearn.datasets import fetch_datasets
ecoli = fetch_datasets()['ecoli']
ecoli.data.shapeprint sorted(Counter(ecoli.target).items())
Out[40]:
[(-1, 301), (1, 35)]

6.2 生成不平衡数据

make_imbalance 方法可以使得原始的数据集变为不平衡的数据集, 主要是通过ratio参数进行控制.

from sklearn.datasets import load_iris
from imblearn.datasets import make_imbalance
iris = load_iris()
ratio = {0: 20, 1: 30, 2: 40}
X_imb, y_imb = make_imbalance(iris.data, iris.target, ratio=ratio)sorted(Counter(y_imb).items())
Out[37]:
[(0, 20), (1, 30), (2, 40)]#当类别不指定时, 所有的数据集均导入
ratio = {0: 10}
X_imb, y_imb = make_imbalance(iris.data, iris.target, ratio=ratio)sorted(Counter(y_imb).items())
Out[38]:
[(0, 10), (1, 50), (2, 50)]#同样亦可以传入自定义的比例函数
def ratio_multiplier(y):multiplier = {0: 0.5, 1: 0.7, 2: 0.95}target_stats = Counter(y)for key, value in target_stats.items():target_stats[key] = int(value * multiplier[key])return target_stats
X_imb, y_imb = make_imbalance(iris.data, iris.target,ratio=ratio_multiplier)sorted(Counter(y_imb).items())
Out[39]:
[(0, 25), (1, 35), (2, 47)]

以上就是在研究不平衡(不均衡)数据时所查询的一些资料, 内容更多的是来自于Imblearn包的官方用户手册, 主要涉及一些下采样、过采样的方法与技术.

Python sklearn 实现过采样和欠采样相关推荐

  1. 学习记录609@python实现数据样本的过采样与欠采样

    对于分类数据集而言,往往类别会有比较大的差异,比如分析贷款逾期的数据,往往没有逾期的数据远远大于逾期的数据,因此样本会存在不均衡的情况,这样对于数据的训练不利,因此可以使用某些方法对数据集进行调整,分 ...

  2. 机器学习算法 03 —— 逻辑回归算法(精确率和召回率、ROC曲线和AUC指标、过采样和欠采样)

    文章目录 系列文章 逻辑回归 1 逻辑回归介绍 1.1 逻辑回归原理 输入 激活函数 1.2 损失以及优化 2 逻辑回归API介绍 3 案例:肿瘤良性恶性预测 4 分类模型的评估 4.1 分类评估方法 ...

  3. 机器学习(三十):过采样和欠采样技术

    当我们的训练数据的类别分布严重偏斜时,我们面临的分类不平衡问题.不平衡可能影响我们的机器学习算法的一种方式是当我们的算法完全忽略少数类时.这是一个问题的原因是因为少数类通常是我们最感兴趣的类.例如,在 ...

  4. 数据不平衡、不平衡采样、调整分类阈值、过采样、欠采样、SMOTE、EasyEnsemble、加入数据平衡的流程、代价敏感学习BalanceCascade、

    数据不平衡.不平衡采样.调整分类阈值.过采样.欠采样.SMOTE.EasyEnsemble.加入数据平衡的流程.BalanceCascade.代价敏感学习 目录

  5. 过采样与欠采样图像重采样(上采样下采样)

    参考文章: https://blog.csdn.net/majinlei121/article/details/46742339 https://blog.csdn.net/Chaolei3/arti ...

  6. 数据处理 过采样与欠采样 SMOTE与随机采样 达到样本均衡化

    文章目录 ✌ 过采样与欠采样 1.✌ 采样介绍 2.✌ 过采样 2.1 随机采样: 2.2 SMOTE采样: 3.✌ 欠采样 4.✌ 代码演示 1.1 ✌ 创建数据 1.2 ✌ 随机采样 1.3 ✌ ...

  7. 彻底分清机器学习中的上采样、下采样、过采样、欠采样【总结】

    今天看了篇中文的硕士论文,读着读着感觉有点奇怪,仔细一看原来他把下采样和欠采样搞混了,这里笔者就详细区分一下各个名称的概念. 文章目录 1. 上采样&下采样 2.过采样&欠采样 3.信 ...

  8. 两分钟带你彻底明白机器学习中的过采样和欠采样是什么意思?

    观点1 是不同数据有不同定义,可分为空间/非空间数据.空间数据指空间上邻近的数据含有相关信息,可以用信号处理滤波的方法提取出这些相关信号,比如图像,声音.非空间数据指数据不是空间上的邻居,不能提取空间 ...

  9. 降采样,过采样,欠采样,子采样,下采样

    这几天看了一篇将关于降采样,过采样,欠采样,子采样,下采样 的文章,写的挺好的,直接给出链接,文章比较长不贴过来了. http://blog.sina.com.cn/s/blog_4b146a9c01 ...

  10. ADC的过采样与欠采样

    在选择一个 ADC时,采样频率 fs是我们最优先考虑的参数.连续的模拟信号以时间间隔 ts = 1/fs被采样,究竟什么样的采样频率才能保证精确地描述原始模拟信号?很显然,同一时间段内采样越多(采样频 ...

最新文章

  1. Hololens2-Unity3D开发(一)
  2. Java :BufferedWriter类和BufferedReader类的构造方法、主要方法
  3. JAVA高精度计算工具
  4. C++ 类设计核查表
  5. 3t硬盘坏道检测需要多久_卤素检测报告需要多久更新一次
  6. 黑鱼资源网完整版织梦整站源码打包分享
  7. 3d打印 开源_公开地图以实现可持续性,在农场进行3D打印以及更多开源新闻
  8. 战斗民族的Yandex开始首次雪地无人车路测 | 视频
  9. matlab boundaries和fchcode函数无法执行的解决办法 未定义与 'double' 类型的输入参数相对应的函数 'boundaries'
  10. C语言memset学习
  11. 华为eNSP静态路由原理与配置实例详解
  12. Windows 常用快捷键 打开cmd DOS命令
  13. 【数据】【自动化交易】Python获取中国股市行情和指数
  14. 机器学习模型设计准则:“无免费午餐”定理和“奥卡姆剃刀”原则
  15. 单例模式 Windows下防止多开简介
  16. KSO-.NETCore中配置swagger分级
  17. 物联网项目——具体分工
  18. 漫话算法[回溯]:从《大话西游》到掌握回溯思想!
  19. 用 Kotlin 的函数式编程 替代 GOF 设计模式
  20. matlab、python打字变红,格式错误问题

热门文章

  1. Alcor(安国)AU6387量产修复(u盘修复)
  2. java cxf调用webservice_Java调用WebService方法总结(7)--CXF调用WebService
  3. python3.6中文版下载-Python3.6.6官方版
  4. 从零开发区块链应用(一)--golang配置文件管理工具viper
  5. 国内外邮箱、邮件服务大全 常见+非主流+免费+加密+域名邮箱
  6. 微积分(二)——曲线积分与曲面积分笔记
  7. 计算机软件研究方法与技术路线,毕业论文研究方法与技术路线
  8. 安装VS2010 SP1失败,安装VS2010 SP1时出现回滚,
  9. Proteus十字路口交通灯
  10. 基于exchange 2010迁移exchange 2016搭建共存环境