综合案例——手写数字图像处理算法比较

2023-10-30

手写数字图像识别各种算法的比较

1、准备工作

1.1、数据集介绍

使用到了两个手写数字的数据集:

  1. scikit-learn 中的手写数字数据集;
  2. mnist 中的手写数字数据集。
1.1.1、scikit-learn 中的手写数字数据集

首先来看一下 scikit-learn 中的手写数字数据集:

# scikit-learn 中的数据集
from sklearn.datasets import load_digits
digits = load_digits()
print(digits.keys())
dict_keys(['data', 'target', 'frame', 'feature_names', 'target_names', 'images', 'DESCR'])

包含在 scikit-learn 中的数据集通常被保存为 Bunch 对象,里面包含真实数据以及一些数据集信息。

print(digits.images.shape)
# (1797, 8, 8)

可以看到这份图像数据是一个三维矩阵:一共有 1797 个样本,每张图像都是 8 像素 × 8 像素。对前 100 张图片进行可视化:

# 对前 100 张图片进行可视化
import matplotlib.pyplot as plt

fig, axes = plt.subplots(10, 10, figsize=(8, 8),
                         subplot_kw={'xticks':[], 'yticks':[]},
                         gridspec_kw=dict(hspace=0.1, wspace=0.1))

for i, ax in enumerate(axes.flat):
    ax.imshow(digits.images[i], cmap='binary', interpolation='nearest')
    ax.text(0.05, 0.05, str(digits.target[i]),
            transform=ax.transAxes, color='green')

plt.show()

为了在 scikit-learn 中使用数据,我们需要一个维度为 [n_samples, n_features] 的二维特征矩阵——可以将每个样本图像的所有像素都作为特征,也就是将每个 8 像素 × 8 像素平铺成长度为 64 的一维数组。另外,还需要一个目标数组,用来表示每个数字的真实值(标签)。这两份数据已经放在手写数字数据集的 datatarget 属性中:

# 特征矩阵
X = digits.data
print(X.shape)		# (1797, 64)

# 目标矩阵
y = digits.target
print(y.shape)		# (1797,)

一共有 1797 个样本和 64 个特征

1.1.2、mnist 中的手写数字数据集

再来看一下 mnist 中的手写数字数据集:

# mnist 中的数据集
from keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
print(train_images.shape, test_images.shape)
# (60000, 28, 28) (10000, 28, 28)

可以看到这份图像数据也是一个三维矩阵:一共有 70000 个样本,每张图片都是 28 像素 × 28 像素。其中训练样本 60000 个,测试样本 10000 个

print(train_labels[0])
# 5
plt.imshow(train_images[0])
plt.show()

1.2、特征提取

1.2.1、主成分分析(PCA)

1、主成分分析(PCA)是一种旋转数据集的方法,旋转后的特征在统计上不相关。在做完这种旋转后,通常是根据新特征对解释数据的重要性来选择它的一个子集。PCA 最常见的一个应用就是高维数据可视化。现在我们来使用 PCA 对两种手写数据数据集进行降维可视化:

# 使用 PCA 对 digits 可视化
from sklearn.decomposition import PCA
# 保留数据的前两个主成分
pca = PCA(n_components=2)
# 对 sklearn 中的手写数字数据集拟合 PCA 模型
pca.fit(digits.data)
# 将数据变换到前两个主成分的方向上
digits_pca = pca.transform(digits.data)

print("原始维度:{}".format(str(digits.data.shape)))
# 原始维度:(1797, 64)
print("降维后的维度:{}".format(str(digits_pca.shape)))
# 降维后的维度:(1797, 2)

现在我们对前两个主成分作图:

# 对第一个和第二个主成分作图,按类别着色
plt.scatter(digits_pca[:, 0], digits_pca[:, 1], c=digits.target,
            edgecolors='none', alpha=5,
            cmap=plt.cm.get_cmap('Spectral', 10))
plt.colorbar(label='digit label', ticks=range(10))
plt.clim(-0.5, 9.5)
plt.show()

我们还可以将数据实际绘制成文本,而不是散点:

# 构建一个 PCA 模型
pca = PCA(n_components=2)
pca.fit(digits.data)
# 将数据变换到前两个主成分的方向上
digits_pca = pca.transform(digits.data)
colors = ['#476A2A', '#7851B8', '#BD3430', '#4A2D4E', '#875525',
          '#A83683', '#4E655E', '#853541', '#3A3120', '#535D8E']
plt.figure(figsize=(10, 10))
plt.xlim(digits_pca[:, 0].min(), digits_pca[:, 0].max())
plt.ylim(digits_pca[:, 1].min(), digits_pca[:, 1].max())
for i in range(len(digits.data)):
    # 将数据实际绘制成文本,而不是散点图
    plt.text(digits_pca[i, 0], digits_pca[i, 1], str(digits.target[i]),
             color = colors[digits.target[i]],
             fontdict={'weight':'bold','size':9})
plt.xlabel("First principal component")
plt.ylabel("Second principal component")
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iky7U4xf-1607432961001)(素材/手写数字/PCA将数据实际绘制成文本,而不是散点图.png)]

2、PCA 的另一个应用是特征提取特征提取背后的思想是,可以找到一种数据表示,比给定的原始表示更适合于分析特征提取很有用,它的一个很好的应用实例就是图像。图像由像素组成,通常存储为红绿蓝(RGB)强度。图像中的对象通常由上千个像素组成,它们只有放在一起才有意义

这里我们启用 PCA白化whitening)选项,它将主成分缩放到相同的尺度。变换后的结果与使用 StandardScaler 相同。白化不仅对应于旋转数据,还对应于缩放数据使其形状是圆形而不是椭圆

from sklearn.decomposition import PCA
pca = PCA(n_components=40, whiten=True, random_state=0).fit(X_train)	# 使用40个主成分表示原特征
X_train_pca = pca.transform(X_train)
X_test_pca = pca.transform(X_test)

print("X_train_pca.shape: {}".format(X_train_pca.shape))
# X_train_pca.shape: (1347, 40)
print("X_test_pca.shape: {}".format(X_test_pca.shape))
# X_test_pca.shape: (450, 40)

我们可以使用原数据(64 个特征)与特征提取后的数据分别进行拟合,查看模型的泛化效果。

1.2.2、流形学习
  1. t-SNE

    t-SNE 的思想是找到数据的一个二维表示,然后尝试让在原始特征空间中距离较近的点更加靠近,原始特征中相距较远的点更加远离。

    # t-SNE
    from sklearn.manifold import TSNE
    tsne = TSNE(random_state=42)
    # 使用 fit_transform 方法
    digits_tsne = tsne.fit_transform(digits.data)
    
    plt.figure(figsize=(10, 10))
    plt.xlim(digits_tsne[:, 0].min(), digits_tsne[:, 0].max())
    plt.ylim(digits_tsne[:, 1].min(), digits_tsne[:, 1].max())
    for i in range(len(digits.data)):
        # 将数据实际绘制成文本,而不是散点图
        plt.text(digits_tsne[i, 0], digits_tsne[i, 1], str(digits.target[i]),
                 color = colors[digits.target[i]],
                 fontdict={'weight':'bold','size':9})
    plt.xlabel("t-SNE feature 0")
    plt.ylabel("t-SNE feature 1")
    plt.show()
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zuxu2xMu-1607432961003)(素材/手写数字/t-SNE绘制sklearn散点图.png)]

  2. 保距映射法(Isomap

    Isomap 寻求一种低维表示,以保持点之间的 “距离”。距离是曲面距离的推广。因此,Isomap 不是用毕达哥拉斯定理导出的距离公式来测量纯欧几里德距离,而是沿着发现的流形优化距离。

    # Isomap
    from sklearn.manifold import Isomap
    
    iso = Isomap(n_components=2)
    iso.fit(digits.data)
    digits_iso = iso.transform(digits.data)
    
    plt.scatter(digits_iso[:, 0], digits_iso[:, 1], c=digits.target,
                edgecolors='none', alpha=5,
                cmap=plt.cm.get_cmap('Spectral', 10))
    plt.colorbar(label='digit label', ticks=range(10))
    plt.clim(-0.5, 9.5)
    plt.show()
    

  3. 多维标度法(MDS

    # MDS
    from sklearn.manifold import MDS
    
    mds = MDS(n_components=2)
    # mds.fit(digits.data)
    digits_mds = mds.fit_transform(digits.data)
    
    plt.scatter(digits_mds[:, 0], digits_mds[:, 1], c=digits.target,
                edgecolors='none', alpha=5,
                cmap=plt.cm.get_cmap('Spectral', 10))
    plt.colorbar(label='digit label', ticks=range(10))
    plt.clim(-0.5, 9.5)
    plt.show()
    

各个数字在参数空间中的分离程度还是令人满意的,这其实告诉我们:用一个简单的有监督分类算法就可以完成分类。

2、高斯朴素贝叶斯分类

2.1、加载数据集

数据集选择:

选择的是使用 scikit-learn 中自带的手写数字数据集。

# 加载并可视化手写数字
from sklearn.datasets import load_digits
digits = load_digits()
print(digits.images.shape)
# (1797, 8, 8)
# 特征矩阵
X = digits.data
print(X.shape)		# (1797, 64)

# 目标矩阵
y = digits.target
print(y.shape)		# (1797,)

2.2、数字分类

先将数据集分成训练集和测试集,然后用高斯朴素贝叶斯模型来拟合:

# 将数据分成训练集和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 用高斯朴素贝叶斯模型来拟合
from sklearn.naive_bayes import GaussianNB
model = GaussianNB()
model.fit(X_train, y_train)     # 拟合数据,构建模型
y_model = model.predict(X_test)     # 做出预测

2.3、评估模型

  1. 准确率

    # 训练集的准确率
    print(model.score(X_train, y_train))	# 0.8574610244988864
    # 测试集的准确率
    print(model.score(X_test, y_test))	# 0.8333333333333334
    
  2. 混淆矩阵

    # 混淆矩阵
    import seaborn as sns
    from sklearn.metrics import confusion_matrix
    mat = confusion_matrix(y_test, y_model)
    
    sns.heatmap(mat, square=True, annot=True, cbar=False)
    plt.xlabel('predicted value')
    plt.ylabel('true value')
    

    可以看到,误判的原因在于许多数字 2 被误判成了数字 1 或 8。

  3. 交叉验证

    # 五轮交叉验证
    from sklearn.model_selection import cross_val_score
    print(cross_val_score(model, X, y, cv=5))
    '''
    [0.78055556 0.78333333 0.79387187 0.8718663  0.80501393]
    '''
    
  4. 更加直观的方式

    另一种显示模型特征的直观方式是将样本画出来,然后把预测标签放在左下角,用绿色表示预测正确,用红色表示预测错误:

    # 更直观的方式
    fig, axes = plt.subplots(10,10,figsize=(8,8),
                             subplot_kw={'xticks':[],'yticks':[]},
                             gridspec_kw=dict(hspace=0.1,wspace=0.1))
    test_images = X_test.reshape(-1,8,8)
    
    for i, ax in enumerate(axes.flat):
        ax.imshow(test_images[i],cmap='binary',interpolation='nearest')
        ax.text(0.05,0.05,str(y_model[i]),
                transform=ax.transAxes,
                color='green' if (y_test[i] == y_model[i]) else 'red')
    

2.4、使用特征提取后的数据进行分类

from sklearn.decomposition import PCA
pca = PCA(n_components=40, whiten=True, random_state=0).fit(X_train)	
X_train_pca = pca.transform(X_train)
X_test_pca = pca.transform(X_test)

# 用高斯朴素贝叶斯模型来拟合
from sklearn.naive_bayes import GaussianNB
model = GaussianNB()
model.fit(X_train_pca, y_train)     # 构建模型
y_model = model.predict(X_test_pca)     # 做出预测

# 准确率
# 训练集的准确率
print(model.score(X_train_pca, y_train))	# 0.9621380846325167
# 测试集的准确率
print(model.score(X_test_pca, y_test))		# 0.94

可以看到,准确率有了很大的提升。这说明主成分可能提供了一种更好的数据表示

2.5、总结

GaussianNB 主要用于高维数据。朴素贝叶斯模型的训练和预测速度都很快。该模型对高维稀疏数据的效果很好,对参数的鲁棒性也相对较好。朴素贝叶斯模型是很好的基准模型,常用于非常大的数据集。

附:完整代码

# 加载并可视化手写数字
from sklearn.datasets import load_digits
import matplotlib.pyplot as plt

digits = load_digits()

# 特征矩阵
X = digits.data

# 目标矩阵
y = digits.target

# 将数据分成训练集和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 用高斯朴素贝叶斯模型来拟合
from sklearn.naive_bayes import GaussianNB
model = GaussianNB()
model.fit(X_train, y_train)     # 构建模型
y_model = model.predict(X_test)     # 做出预测

# 评估模型

# 准确率
# 训练集的准确率
print(model.score(X_train, y_train))
# 测试集的准确率
print(model.score(X_test, y_test))

# 混淆矩阵
import seaborn as sns
from sklearn.metrics import confusion_matrix
mat = confusion_matrix(y_test,y_model)

sns.heatmap(mat, square=True,annot=True, cbar=False)
plt.xlabel('predicted value')
plt.ylabel('true value')

plt.show()

# 更直观的方式
fig, axes = plt.subplots(10, 10, figsize=(8, 8),
                         subplot_kw={'xticks':[], 'yticks':[]},
                         gridspec_kw=dict(hspace=0.1, wspace=0.1))
test_images = X_test.reshape(-1, 8, 8)

for i, ax in enumerate(axes.flat):
    ax.imshow(test_images[i], cmap='binary', interpolation='nearest')
    ax.text(0.05,0.05,str(y_model[i]),
            transform=ax.transAxes,
            color='green' if (y_test[i] == y_model[i]) else 'red')

plt.show()

# 五轮交叉验证
from sklearn.model_selection import cross_val_score
print(cross_val_score(model, X, y, cv=5))

# 两轮交叉验证
from sklearn.model_selection import cross_val_score
print(cross_val_score(model, X, y, cv=2))

'--------------------------------------------------------------------'
# 使用特征提取后的数据
from sklearn.decomposition import PCA
pca = PCA(n_components=40, whiten=True, random_state=0).fit(X_train)
X_train_pca = pca.transform(X_train)
X_test_pca = pca.transform(X_test)

# print("X_train_pca.shape: {}".format(X_train_pca.shape))
# print("X_test_pca.shape: {}".format(X_test_pca.shape))

# 用高斯朴素贝叶斯模型来拟合
from sklearn.naive_bayes import GaussianNB
model = GaussianNB()
model.fit(X_train_pca, y_train)     # 构建模型
y_model = model.predict(X_test_pca)     # 做出预测

# 准确率
# 训练集的准确率
print(model.score(X_train_pca, y_train))
# 测试集的准确率
print(model.score(X_test_pca, y_test))

3、KNN 算法

3.1、加载数据集

数据集选择:

选择的是使用 scikit-learn 中自带的手写数字数据集。

# 加载并可视化手写数字
from sklearn.datasets import load_digits
digits = load_digits()

3.2、使用 KNN 算法分类

# 使用 KNN 构建模型
from sklearn.model_selection import train_test_split
X = digits.data
y = digits.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=3)
clf.fit(X_train, y_train)	# 拟合数据

3.3、评估模型

# 评估模型
print("Accuracy on training set: {:.3f}".format(clf.score(X_train, y_train)))
# Accuracy on training set: 0.992
print("Accuracy on test set: {:.3f}".format(clf.score(X_test, y_test)))
# Accuracy on test set: 0.987

我们可以调整 “邻居” 的参数,来观察模型的效果:

# 调整参数
clf = KNeighborsClassifier(n_neighbors=5)
clf.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(clf.score(X_train, y_train)))
# Accuracy on training set: 0.991
print("Accuracy on test set: {:.3f}".format(clf.score(X_test, y_test)))
# Accuracy on test set: 0.980
clf = KNeighborsClassifier(n_neighbors=1)
clf.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(clf.score(X_train, y_train)))
# Accuracy on training set: 1.000
print("Accuracy on test set: {:.3f}".format(clf.score(X_test, y_test)))
# Accuracy on test set: 0.991

而距离默认使用的是欧式距离

3.4、总结

  • 参数

    1. 邻居个数

      使用较小的邻居个数(比如 3 个 或 5 个)往往能得到不错的性能。

    2. 数据点之间距离的度量方法

      默认使用欧氏距离

  • 优点

    1. 容易理解,在使用复杂的技术之前,尝试此算法是一种很好地基准方法;
    2. 构建最近邻模型的速度很快,但如果训练集很大(特征数很多或者样本数很大),预测速度可能会很慢。
  • 缺点

    1. 使用 k-NN 算法时,对数据的预处理是很重要的;
    2. 对于很多特征的数据集往往效果不好,对于大多数特征取值为 0 的数据集(所谓的稀疏矩阵)来说,这一算法的效果尤其不好。

附:完整代码

# 加载并可视化手写数字
from sklearn.datasets import load_digits
digits = load_digits()

# 使用 KNN 构建模型
from sklearn.model_selection import train_test_split
X = digits.data
y = digits.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=3)
clf.fit(X_train, y_train)


# 评估模型
print("Accuracy on training set: {:.3f}".format(clf.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(clf.score(X_test, y_test)))

# 调整参数
clf = KNeighborsClassifier(n_neighbors=5)
clf.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(clf.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(clf.score(X_test, y_test)))


clf = KNeighborsClassifier(n_neighbors=1)
clf.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(clf.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(clf.score(X_test, y_test)))

'----------------------------------------------------------------'

from sklearn.decomposition import PCA
pca = PCA(n_components=40, whiten=True, random_state=0).fit(X_train)
X_train_pca = pca.transform(X_train)
X_test_pca = pca.transform(X_test)

# 使用 SVM
from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=3)
clf.fit(X_train_pca, y_train)
clf.predict(X_test_pca)

# 准确率
# 训练集的准确率
print(clf.score(X_train_pca, y_train))
# 测试集的准确率
print(clf.score(X_test_pca, y_test))

4、用随机森林识别手写数字

4.1、加载数据集

数据集选择:

选择的是使用 scikit-learn 中自带的手写数字数据集。

# 加载并可视化手写数字
from sklearn.datasets import load_digits
digits = load_digits()

4.2、用随机森林分类

# 用随机森林分类
from sklearn.model_selection import train_test_split
X = digits.data
y = digits.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(n_estimators=1000)
model.fit(X_train, y_train)		# 拟合数据,构建模型
ypred = model.predict(X_test)	# 做出预测

4.3、评估模型

  1. 查看分类器的分类结果报告

    # 分类结果报告
    from sklearn import metrics
    print(metrics.classification_report(ypred, y_test))
    '''
                  precision    recall  f1-score   support
    
               0       1.00      0.97      0.99        38
               1       1.00      0.98      0.99        44
               2       0.95      1.00      0.98        42
               3       0.98      0.96      0.97        46
               4       0.97      1.00      0.99        37
               5       0.98      0.98      0.98        48
               6       1.00      1.00      1.00        52
               7       1.00      0.96      0.98        50
               8       0.94      0.98      0.96        46
               9       0.98      0.98      0.98        47
    
        accuracy                           0.98       450
       macro avg       0.98      0.98      0.98       450
    weighted avg       0.98      0.98      0.98       450
    '''
    
    # 准确率
    print(model.score(X_train, y_train))	# 1.0
    print(model.score(X_test, y_test))		# 0.98
    

    在训练集上的准确率为 1.0,可能存在过拟合。但在测试集上的表现也还可以。

  2. 混淆矩阵

    # 混淆矩阵
    import seaborn as sns
    from sklearn.metrics import confusion_matrix
    mat = confusion_matrix(y_test, ypred)
    sns.heatmap(mat.T, square=True, annot=True, fmt='d', cbar=False)
    plt.xlabel('true label')
    plt.ylabel('predicted label')
    

4.4、总结

  • 参数

    需要调节的重要参数有 n_estimatorsmax_features,可能还包括预剪枝选项(如 max_depth

    • n_estimators 总是越大越好。对更多的树取平均可以降低过拟合,从而得到鲁棒性更好的集成。不过收益是递减的,而且树越多需要的内存也越多,训练时间也越长。常用的经验法则就是“在你的时间 / 内存允许的情况下尽量多”。
    • max_features 决定每棵树的随机性大小,较小的 max_features 可以降低过拟合。一般来说,好的经验就是使用默认值:对于分类,默认值是 max_features=sqrt(n_features)对于回归,默认值是 max_features=n_features。增大 max_featuresmax_leaf_nodes 有时也可以提高性能。它还可以大大降低用于训练和预测的时间和空间要求。
  • 优点

    用于回归和分类的随机森林是目前应用最广泛的机器学习方法之一。这种方法非常强大,通常不需要反复调节参数就可以给出很好的结果,也不需要对数据进行缩放

    从本质上看,随机森林拥有决策树的所有优点,同时弥补了决策树的一些缺陷。

  • 缺点

    仍然使用决策树的一个原因是需要决策过程的紧凑表示。基本上不可能对几十棵甚至上百棵树做出详细解释,随机森林中树的深度往往比决策树还要大(因为用到了特征子集)。因此,如果你需要以可视化的方式向非专家总结预测过程,那么选择单棵决策树可能更好。

    虽然在大型数据集上构建随机森林可能比较费时间,但在一台计算机的多个 CPU 内核上并行计算也很容易。如果你用的是多核处理器(几乎所有的现代化计算机都是),你可以用 n_jobs 参数来调节使用的内核个数。使用更多的 CPU 内核,可以让速度线性增加(使用 2 个内核,随机森林的训练速度会加倍),但设置 n_jobs 大于内核个数是没有用的。你可以设置 n_jobs=-1 来使用计算机的所有内核

    你应该记住,随机森林本质上是随机的,设置不同的随机状态(或者不设置 random_state 参数)可以彻底改变构建的模型。森林中的树越多,它对随机状态选择的鲁棒性就越好。如果你希望结果可以重现,固定 random_state 是很重要的

    对于维度非常高的稀疏数据(比如文本数据),随机森林的表现往往不是很好。对于这种数据,使用线性模型可能更合适。即使是非常大的数据集,随机森林的表现通常也很好,训练过程很容易并行在功能强大的计算机的多个 CPU 内核上。不过,随机森林需要更大的内存,训练和预测的速度也比线性模型要慢对一个应用来说,如果时间和内存很重要的话,那么换用线性模型可能更为明智

附:完整代码

# 加载并可视化手写数字
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
digits = load_digits()

# 用随机森林分类
from sklearn.model_selection import train_test_split
X = digits.data
y = digits.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(n_estimators=1000)
model.fit(X_train, y_train)
ypred = model.predict(X_test)

# 分类结果报告
from sklearn import metrics
print(metrics.classification_report(ypred, y_test))

# 混淆矩阵
import seaborn as sns
from sklearn.metrics import confusion_matrix
mat = confusion_matrix(y_test, ypred)
sns.heatmap(mat.T, square=True, annot=True, fmt='d', cbar=False)
plt.xlabel('true label')
plt.ylabel('predicted label')

plt.show()

5、梯度提升回归树(梯度提升机)

5.1、加载数据集

数据集选择:

选择的是使用 scikit-learn 中自带的手写数字数据集。

# 加载并可视化手写数字
from sklearn.datasets import load_digits
digits = load_digits()

5.2、用梯度提升回归树(梯度提升机)进行分类

# 用梯度提升回归树(梯度提升机)分类
from sklearn.model_selection import train_test_split
X = digits.data
y = digits.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

from sklearn.ensemble import GradientBoostingClassifier
gbrt = GradientBoostingClassifier(random_state=0)
gbrt.fit(X_train, y_train)	# 拟合数据

5.3、评估模型

# 评估模型
print("Accuracy on training set: {:.3f}".format(gbrt.score(X_train, y_train)))
# Accuracy on training set: 1.000
print("Accuracy on test set: {:.3f}".format(gbrt.score(X_test, y_test)))
# Accuracy on test set: 0.956

由于训练精度达到 100%,所以很可能存在过拟合。为了降低过拟合,我们可以限制最大深度来加强预剪枝,也可以降低学习率

# 限制最大深度来加强预剪枝
gbrt = GradientBoostingClassifier(random_state=0, max_depth=1)
gbrt.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(gbrt.score(X_train, y_train)))
# Accuracy on training set: 0.975
print("Accuracy on test set: {:.3f}".format(gbrt.score(X_test, y_test)))
# Accuracy on test set: 0.913
# 降低学习率
gbrt = GradientBoostingClassifier(random_state=0, learning_rate=0.01)
gbrt.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(gbrt.score(X_train, y_train)))
# Accuracy on training set: 0.965
print("Accuracy on test set: {:.3f}".format(gbrt.score(X_test, y_test)))
# Accuracy on test set: 0.891

5.4、总结

  • 参数

    梯度提升树模型的主要参数包括树的数量 n_estimators 和学习率 learning_ratelearning_rate 用于控制每棵树对前一棵树的错误的纠正强度。这两个参数高度相关,因为 learning_rate 越低,就需要更多的树来构建具有相似复杂度的模型。随机森林的 n_estimators 值总是越大越好,但梯度提升不同,增大 n_estimators 会导致模型更加复杂,进而可能导致过拟合。通常的做法是根据时间和内存的预算选择合适的 n_estimators,然后对不同的 learning_rate 进行遍历

    另一个重要参数是 max_depth(或 max_leaf_nodes),用于降低每棵树的复杂度。梯度提升模型的 max_depth 通常都设置得很小,一般不超过 5。

  • 优点

    梯度提成回归树是另一种集成方法,通过合并对个决策树来构建一个更为强大的模型。与随机森林方法不同,梯度提升采用连续的方式构造树,每棵树都试图纠正前一棵树的错误。默认情况下,梯度提升回归树没有随机化,而是采用了强预剪枝。梯度提升树通常使用深度很小(1 到 5 之间)的树,这样模型占用的内存更少,预测速度也更快。

    梯度提升背后的主要思想是合并许多简单的模型(在这个语境中叫做弱学习器),比如深度很小的树。每棵树只能对部分数据做出好的预测,因此,添加的树越来越多,可以不断迭代提高性能。

  • 缺点

    其主要缺点是需要仔细调参,而且训练时间可能会比较长。与其他基于树的模型类似,这一算法不需要对数据进行缩放就可以表现得很好,而且也适用于二元特征与连续特征同时存在的数据集。与其他基于树的模型相同,它也通常不适用于高维稀疏数据

附:完整代码

# 加载并可视化手写数字
from sklearn.datasets import load_digits
digits = load_digits()

# 用梯度提升回归树(梯度提升机)分类
from sklearn.model_selection import train_test_split
X = digits.data
y = digits.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

from sklearn.ensemble import GradientBoostingClassifier
gbrt = GradientBoostingClassifier(random_state=0)
gbrt.fit(X_train, y_train)

# 评估模型
print("Accuracy on training set: {:.3f}".format(gbrt.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(gbrt.score(X_test, y_test)))


# 限制最大深度来加强预剪枝
gbrt = GradientBoostingClassifier(random_state=0, max_depth=1)
gbrt.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(gbrt.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(gbrt.score(X_test, y_test)))

# 降低学习率
gbrt = GradientBoostingClassifier(random_state=0, learning_rate=0.1, n_estimators=5)
gbrt.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(gbrt.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(gbrt.score(X_test, y_test)))

6、SVM

6.1、加载数据集

数据集选择:

选择的是使用 scikit-learn 中自带的手写数字数据集。

# 加载并可视化手写数字
from sklearn.datasets import load_digits
digits = load_digits()

6.2、使用 SVM 进行分类

# 使用 SVM
from sklearn.model_selection import train_test_split
X = digits.data
y = digits.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

from sklearn.svm import SVC
svm = SVC(kernel='rbf', C=10, gamma=0.1)	# 使用 “高斯核”,gamma 系数取 0.1
svm.fit(X_train, y_train)

6.3、评估数据

print("Accuracy on training set: {:.3f}".format(svm.score(X_train, y_train)))
# Accuracy on training set: 1.000
print("Accuracy on test set: {:.3f}".format(svm.score(X_test, y_test)))
# Accuracy on test set: 0.084

训练集准确率为 1.000,而测试集准确率很小,说明模型太过拟合。我们需要调整参数。

svm = SVC(kernel='rbf', C=100, gamma=0.001)
svm.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(svm.score(X_train, y_train)))
# Accuracy on training set: 1.000
print("Accuracy on test set: {:.3f}".format(svm.score(X_test, y_test)))
# Accuracy on test set: 0.993

虽然可能会存在一些过拟合,但在测试集上表现的也不错。

6.4、使用特征提取过的数据

from sklearn.decomposition import PCA
pca = PCA(n_components=40, whiten=True, random_state=0).fit(X_train)
X_train_pca = pca.transform(X_train)
X_test_pca = pca.transform(X_test)

# 使用 SVM
from sklearn.svm import SVC
svm = SVC(kernel='rbf', C=10, gamma=0.1)
svm.fit(X_train_pca, y_train)

# 准确率
# 训练集的准确率
print(svm.score(X_train_pca, y_train))	# # 1.0
# 测试集的准确率
print(svm.score(X_test_pca, y_test))	# 0.9222222222222223

相比如之前系数为 “kernel='rbf', C=10, gamma=0.1” 时的准确率提高了更多。SVM 对数据的缩放十分敏感,这里我们使用白化(系数 “whiten=True”)也起到了缩放的作用。

6.4、总结

  • 参数

    核 SVM 的重要参数正则化参数 C核的选择以及与核相关的参数。虽然我们主要讲的是 RBF 核,但 scikit-learn 中还有其他选择。RBF 核只有一个参数 gamma,它是高斯核宽度的倒数gammaC 控制的都是模型复杂度,较大的值都对应更为复杂的模型。因此,这两个参数的设定通常是强烈相关的,应该同时调节。

  • 优点

    核支持向量机是非常强大的模型,在各种数据集上的表现都很好。SVM 允许决策边界很复杂,即使数据只有几个特征它在低维数据和高维数据(即很少特征和很多特征)上的表现都很好

  • 缺点

    对样本个数的缩放表现不好。在有多达 10 000 个样本的数据上运行 SVM 可能表现良好,但如果数据量达到 100 000 甚至更大,在运行时间和内存使用方面可能会面临挑战。

    SVM 的另一个缺点是,预处理数据和调参都需要非常小心。这也是为什么如今很多应用中用的都是基于树的模型,比如随机森林或梯度提升(需要很少的预处理,甚至不需要预处理)。此外,SVM 模型很难检查,可能很难理解为什么会这么预测,而且也难以将模型向非专家进行解释。

附:完整代码

# 加载并可视化手写数字
from sklearn.datasets import load_digits
digits = load_digits()

# 使用 SVM
from sklearn.model_selection import train_test_split
X = digits.data
y = digits.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

from sklearn.svm import SVC
svm = SVC(kernel='rbf', C=10, gamma=0.1)
svm.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(svm.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(svm.score(X_test, y_test)))

svm = SVC(kernel='rbf', C=100, gamma=0.001)
svm.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(svm.score(X_train, y_train)))
print("Accuracy on test set: {:.3f}".format(svm.score(X_test, y_test)))

'--------------------------------------------------------------------------'
# 使用特征提取后的数据
from sklearn.decomposition import PCA
pca = PCA(n_components=40, whiten=True, random_state=0).fit(X_train)
X_train_pca = pca.transform(X_train)
X_test_pca = pca.transform(X_test)

# 使用 SVM
from sklearn.svm import SVC
svm = SVC(kernel='rbf', C=10, gamma=0.1)
svm.fit(X_train_pca, y_train)

# 准确率
# 训练集的准确率
print(svm.score(X_train_pca, y_train))
# 测试集的准确率
print(svm.score(X_test_pca, y_test))

7、神经网络

7.1、加载数据集

数据集选择:

选择使用 minst 数据集中的手写数字数据集。

导入相应的库:

from keras.utils import to_categorical
from keras import models, layers, regularizers
from keras.optimizers import RMSprop
from keras.datasets import mnist
import matplotlib.pyplot as plt
# 加载数据集
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
print(train_images.shape, test_images.shape)
# (60000, 28, 28) (10000, 28, 28)

将图片由二维铺开成一维:

# 将图片由二维铺开成一维
train_images = train_images.reshape((60000, 28*28)).astype('float')
test_images = test_images.reshape((10000, 28*28)).astype('float')
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

7.2、搭建一个神经网络(示例)

# 搭建神经网络
network = models.Sequential()
network.add(layers.Dense(units=15, activation='relu', input_shape=(28*28, ),))
network.add(layers.Dense(units=10, activation='softmax'))

7.3、神经网络训练

7.3.1、编译确定优化器和损失函数
# 编译步骤
network.compile(optimizer=RMSprop(lr=0.001), loss='categorical_crossentropy', metrics=['accuracy'])
7.3.2、训练网络:确定训练的数据、训练的轮数和每次训练的样本数等
# 训练网络,用fit函数, epochs表示训练多少个回合, batch_size表示每次训练给多大的数据
network.fit(train_images, train_labels, epochs=20, batch_size=128, verbose=2)

7.4、用训练好的模型进行预测,并在测试集上做出评价

# 来在测试集上测试一下模型的性能吧
y_pre = network.predict(test_images[:5])
print(y_pre, test_labels[:5])
test_loss, test_accuracy = network.evaluate(test_images, test_labels)
print("test_loss:", test_loss, "    test_accuracy:", test_accuracy)

7.5、完整的代码

  • 使用全连接神经网络

    from keras.utils import to_categorical
    from keras import models, layers, regularizers
    from keras.optimizers import RMSprop
    from keras.datasets import mnist
    import matplotlib.pyplot as plt
    
    # 加载数据集
    (train_images, train_labels), (test_images, test_labels) = mnist.load_data()
    
    train_images = train_images.reshape((60000, 28*28)).astype('float')
    test_images = test_images.reshape((10000, 28*28)).astype('float')
    train_labels = to_categorical(train_labels)
    test_labels = to_categorical(test_labels)
    
    network = models.Sequential()
    network.add(layers.Dense(units=128, activation='relu', input_shape=(28*28, ),
                             kernel_regularizer=regularizers.l1(0.0001)))
    network.add(layers.Dropout(0.01))
    network.add(layers.Dense(units=32, activation='relu',
                             kernel_regularizer=regularizers.l1(0.0001)))
    network.add(layers.Dropout(0.01))
    network.add(layers.Dense(units=10, activation='softmax'))
    
    # 编译步骤
    network.compile(optimizer=RMSprop(lr=0.001), loss='categorical_crossentropy', metrics=['accuracy'])
    
    # 训练网络,用fit函数, epochs表示训练多少个回合, batch_size表示每次训练给多大的数据
    network.fit(train_images, train_labels, epochs=20, batch_size=128, verbose=2)
    
    # 来在测试集上测试一下模型的性能吧
    test_loss, test_accuracy = network.evaluate(test_images, test_labels)
    print("test_loss:", test_loss, "    test_accuracy:", test_accuracy)
    
    Epoch 1/20
    469/469 - 1s - loss: 2.6369 - accuracy: 0.7405
    Epoch 2/20
    469/469 - 0s - loss: 0.7591 - accuracy: 0.8705
    Epoch 3/20
    469/469 - 0s - loss: 0.6083 - accuracy: 0.9010
    Epoch 4/20
    469/469 - 0s - loss: 0.5309 - accuracy: 0.9165
    Epoch 5/20
    469/469 - 0s - loss: 0.4678 - accuracy: 0.9299
    Epoch 6/20
    469/469 - 0s - loss: 0.4241 - accuracy: 0.9391
    Epoch 7/20
    469/469 - 0s - loss: 0.3930 - accuracy: 0.9429
    Epoch 8/20
    469/469 - 0s - loss: 0.3764 - accuracy: 0.9462
    Epoch 9/20
    469/469 - 0s - loss: 0.3455 - accuracy: 0.9530
    Epoch 10/20
    469/469 - 0s - loss: 0.3166 - accuracy: 0.9566
    Epoch 11/20
    469/469 - 0s - loss: 0.3156 - accuracy: 0.9560
    Epoch 12/20
    469/469 - 0s - loss: 0.2935 - accuracy: 0.9600
    Epoch 13/20
    469/469 - 0s - loss: 0.2822 - accuracy: 0.9618
    Epoch 14/20
    469/469 - 0s - loss: 0.2698 - accuracy: 0.9623
    Epoch 15/20
    469/469 - 0s - loss: 0.2666 - accuracy: 0.9636
    Epoch 16/20
    469/469 - 0s - loss: 0.2548 - accuracy: 0.9657
    Epoch 17/20
    469/469 - 0s - loss: 0.2482 - accuracy: 0.9661
    Epoch 18/20
    469/469 - 0s - loss: 0.2415 - accuracy: 0.9663
    Epoch 19/20
    469/469 - 0s - loss: 0.2359 - accuracy: 0.9687
    Epoch 20/20
    469/469 - 0s - loss: 0.2315 - accuracy: 0.9680
    313/313 [==============================] - 0s 389us/step - loss: 0.2709 - accuracy: 0.9667
    test_loss: 0.27094602584838867     test_accuracy: 0.96670001745224
    
  • 使用卷积神经网络

    from keras.utils import to_categorical
    from keras import models, layers
    from keras.optimizers import RMSprop
    from keras.datasets import mnist
    # 加载数据集
    (train_images, train_labels), (test_images, test_labels) = mnist.load_data()
    
    # 搭建LeNet网络
    def LeNet():
        network = models.Sequential()
        network.add(layers.Conv2D(filters=6, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1)))
        network.add(layers.AveragePooling2D((2, 2)))
        network.add(layers.Conv2D(filters=16, kernel_size=(3, 3), activation='relu'))
        network.add(layers.AveragePooling2D((2, 2)))
        network.add(layers.Conv2D(filters=120, kernel_size=(3, 3), activation='relu'))
        network.add(layers.Flatten())
        network.add(layers.Dense(84, activation='relu'))
        network.add(layers.Dense(10, activation='softmax'))
        return network
    network = LeNet()
    network.compile(optimizer=RMSprop(lr=0.001), loss='categorical_crossentropy', metrics=['accuracy'])
    
    train_images = train_images.reshape((60000, 28, 28, 1)).astype('float') / 255
    test_images = test_images.reshape((10000, 28, 28, 1)).astype('float') / 255
    train_labels = to_categorical(train_labels)
    test_labels = to_categorical(test_labels)
    
    # 训练网络,用fit函数, epochs表示训练多少个回合, batch_size表示每次训练给多大的数据
    network.fit(train_images, train_labels, epochs=10, batch_size=128, verbose=2)
    test_loss, test_accuracy = network.evaluate(test_images, test_labels)
    print("test_loss:", test_loss, "    test_accuracy:", test_accuracy)
    
    Epoch 1/10
    469/469 - 4s - loss: 0.3614 - accuracy: 0.8931
    Epoch 2/10
    469/469 - 4s - loss: 0.0982 - accuracy: 0.9702
    Epoch 3/10
    469/469 - 4s - loss: 0.0617 - accuracy: 0.9811
    Epoch 4/10
    469/469 - 4s - loss: 0.0478 - accuracy: 0.9851
    Epoch 5/10
    469/469 - 4s - loss: 0.0380 - accuracy: 0.9883
    Epoch 6/10
    469/469 - 4s - loss: 0.0313 - accuracy: 0.9900
    Epoch 7/10
    469/469 - 4s - loss: 0.0267 - accuracy: 0.9916
    Epoch 8/10
    469/469 - 4s - loss: 0.0233 - accuracy: 0.9926
    Epoch 9/10
    469/469 - 4s - loss: 0.0196 - accuracy: 0.9938
    Epoch 10/10
    469/469 - 4s - loss: 0.0173 - accuracy: 0.9946
    313/313 [==============================] - 0s 1ms/step - loss: 0.0325 - accuracy: 0.9897
    test_loss: 0.03245333582162857     test_accuracy: 0.9897000193595886
    

7.6、总结

  • 参数

    估计神经网络的复杂度最重要的参数是层数和每层的隐单元个数。你应该首先设置 1 个或 2 个隐层,然后可以逐步增加。每个隐层的结点个数通常与输入特征个数接近,但在几千个结点时很少会多于特征个数。

    在考虑神经网络的模型复杂度时,一个有用的度量是学到的权重(或系数)的个数

    神经网络调参的常用方法是,首先创建一个大到足以过拟合的网络,确保这个网络可以对任务进行学习。知道训练数据可以被学习之后,要么缩小网络,要么增大 alpha 来增强正则化,这可以提高泛化性能。

    在我们的实验中,主要关注模型的定义:层数、每层的结点个数、正则化和非线性。这些内容定义了我们想要学习的模型。还有一个问题是,如何学习模型或用来学习参数的算法,这一点由 solver 参数设定。solver 有两个好用的选项。默认选项是 'adam',在大多数情况下效果都很好,但对数据的缩放相当敏感(因此,始终将数据缩放为均值为 0、方差为 1 是很重要的)。另一个选项是 'lbfgs',其鲁棒性相当好,但在大型模型或大型数据集上的时间会比较长。还有更高级的 'sgd' 选项,许多深度学习研究人员都会用到。'sgd' 选项还有许多其他参数需要调节,以便获得最佳结果。你可以在用户指南中找到所有这些参数及其定义。当你开始使用 MLP 时,我们建议使用 'adam''lbfgs'

  • 优点

    它的主要优点之一是能够获取大量数据中包含的信息,并构建无比复杂的模型。给定足够的计算时间和数据,并且仔细调节参数,神经网络通常可以打败其他机器学习算法(无论是分类任务还是回归任务)。

  • 缺点

    神经网络——特别是功能强大的大型神经网络——通常需要很长的训练时间。它还需要仔细地预处理数据,正如我们这里所看到的。与 SVM 类似,神经网络在“均匀”数据上的性能最好,其中“均匀”是指所有特征都具有相似的含义。如果数据包含不同种类的特征,那么基于树的模型可能表现得更好。神经网络调参本身也是一门艺术。


8、对比

高斯朴素贝叶斯模型 KNN 随机森林 梯度提升回归树 SVM 神经网络
原数据测试集准确率 0.8333333333333334 0.991 0.98 0.956 0.084 0.9897000193595886
特征提取后的数据测试集准确率 0.94 0.9222222222222223

注:表中的准确率都是在默认参数下。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

综合案例——手写数字图像处理算法比较 的相关文章

随机推荐

  • 推荐一款提高工作效率的屏幕扩展软件—Splashtop Wired XDisplay

    一 导读 使用Splashtop Wired XDisplay让闲置的手机平板成为电脑的第二块屏幕Splashtop Wired XDisplay 好文章 记得 收藏 点赞 关注 如果你和我一样抱怨着显示器太小 Idea编程软件占用了太多工
  • 03 ZooKeeper底层原理剖析与命令实战

    文章目录 03 ZooKeeper底层原理剖析与命令实战 1 Znode数据结构 1 1 目录结构 1 2 节点类型 2 ZK客户端命令行操作 3 ZooKeeper会话 4 事件监听机制原理剖析 5 广播模式剖析 6 ZooKeeper集
  • vs报错:TRACKER : 错误 TRK0005: 未能找到: “CL.exe”。系统找不到指定的文件

    平台 win 10 错误 如题 解决 1 参考 Windows11下配置Visual Studio2022 环境变量 Windows下配置Visual Studio 通用 2 1 运行 C Program Files Microsoft V
  • java定义一个类_Java类的定义格式:Java如何定义一个类

    在面向对象的思想中 最核心的就是对象 为了在程序中创建对象 首先需要定义一个类 类是对象的抽象 它用于描述一组对象的共同特征和行为 例如人都有姓名 年龄 性别等特征 还有学习 工作 购物等行为 以面向对象的编程思想 就可以将某一类中共同的特
  • 图的深度优先搜索和广度优先搜索

    图的深度优先搜索算法 dfs 图的深度优先搜索算法是以一个顶点为起始点开始挖掘 一个分支挖掘完 再继续挖掘下一个分支 在实现上需要用到栈 因此 图的深度优先搜索算法有很多条路径 取决于哪个顶点最先入栈 实现思路 非递归 dfs可以用栈来进行
  • 解决QT中TCP传数据的困惑

    问题一 当传输数据过大时 readAll不能读完这一条完整的报文 报文格式如下 解决方法 针对此问题 可以通过一个全局变量 将多次读到的数据合并 问题二 粘包现象之多次write对应一次read 深入查看ReadyRead 信号的产生条件可
  • CNN笔记:通俗理解卷积神经网络

    通俗理解卷积神经网络 cs231n与5月dl班课程笔记 1 前言 2012年我在北京组织过8期machine learning读书会 那时 机器学习 非常火 很多人都对其抱有巨大的热情 当我2013年再次来到北京时 有一个词似乎比 机器学习
  • make编译出错Relocations in generic ELF (EM: 62)

    参考 编译出错Relocations in generic ELF EM 62 main o Relocations in generic ELF EM 62 错误信息是 通过查看文件 main o 发现ELF 64bit x86 64 在
  • ClickHouse副本同步及分布式DDL的原理

    相信了解大数据的小伙伴们都知道 基本上所有的分布式存储系统都有一个共同的特点 将庞大的数据量分成多个小块存储在不同的机器上 通常称为分片 每个分片为了保证它数据不丢失 它们又有各自副本 ClickHouse也不例外 一起来看看ClickHo
  • 5.Mybatis 缓存详解

    5 1 一级缓存 一级缓存 也叫本地缓存 默认会开启 并且不能控制 想要关闭一级缓存可以在select标签上配置flushCache true 一级缓存存在于 SqlSession 的生命周期中 在同一个 SqlSession 中查询时 M
  • 微信小程序之如何注册微信小程序

    注册小程序帐号 在微信公众平台官网首页 mp weixin qq com 点击右上角的 立即注册 按钮 选择注册的帐号类型 显示了4选项 我们现在是注册小程序 所以选择小程序 填写邮箱和密码 请填写未注册过公众平台 开放平台 企业号 未绑定
  • VMware虚拟机安装ubuntu20.04缓慢、卡顿

    背景 VMware Workstation版本是14 本地虚拟机上ubuntu版本是16 04的 有32位和64位 编译原理课程需要18或20以上的版本 决定再装一个20的 出现各种问题 进入黑屏 等三五分钟才缓过来 选择语言中文后 安装卡
  • C# ModbusTcp(协议)

    以下用例为读取线圈 可读取多个连续的线圈 01功能码 起始地址 var inputAddress 4 读取长度 var inputReadLength 8 var station BitConverter GetBytes 4 0 从站地址
  • 编译OpenCV以及openc_contrib提示缺少boostdesc_bgm.i文件出错的解决

    编译OpenCV 以及 openc contrib 提示缺少boostdesc bgm i文件出错的解决 错误 opencv contrib modules xfeatures2d src boostdesc cpp 673 20 fata
  • 软件测试与质量学习笔记3--白盒测试

    1 白盒测试的两种常用技术 覆盖测试 在测试过程中 以覆盖某些程序元素为测试目标的测试 路径测试 从流程图上讲 程序的一次执行对应于从入口到出口的一条路径 针对路径的测试即为路径测试 从广义的角度讲 任何有关路径分析的测试都可以被称为路径测
  • MATLAB大小写字符的转换

    输入一个字符 若为大写字母 则输出其对应的小写字母 若为小写字母 则输入其对应的大写字母 若为数字字符则输出其对应的数值 若为其他字符则原样输出 第一种 clear clc n input enter a number n if n gt
  • 基于STM32的ESP8266使用教程(四)(短篇)

    写在前面 在上一篇中介绍了通过单片机发送指令控制WIFI模块 本来想写一下时钟 I O口及串口的初始化函数 但是因为项目年代久远 一年前做的 怕有错误 误导博友 故不给出代码 见谅 本章要介绍Android端通过WIFI与手机建立连接并通信
  • 代码示例:面向对象——封装、继承、多态(多态的四种类型)

    面向对象 封装 继承 多态 多态的四种类型 1 封装 把客观事物封装成抽象的类 并且类可以把自己的数据和方法只让可信的类或者对象操作 对不可信的进行信息隐藏 public 所有实体都可以访问 protected 只允许本类 和子类 的成员函
  • 【动态规划】---入门(一)

    文章目录 前言 一 斐波那契数列 二 爬楼梯 总结 前言 动态规划入门学习文章记录及总结 动态规划算法的基本思想是 将待求解的问题分解成若干个相互联系的子问题 先求解子问题 然后从这些子问题的解得到原问题的解 对于重复出现的子问题 只在第一
  • 综合案例——手写数字图像处理算法比较

    手写数字图像识别各种算法的比较 1 准备工作 1 1 数据集介绍 使用到了两个手写数字的数据集 scikit learn 中的手写数字数据集 mnist 中的手写数字数据集 1 1 1 scikit learn 中的手写数字数据集 首先来看