Pythonとディープラーニング(5):CNNと手書き数字認識

1. 説明

この記事からは、ANN よりも画像分類問題に適した畳み込みニューラル ネットワーク CNN を紹介します。

2. 畳み込み演算

コンボリューション演算のプロセスは次の図に示されており、コンボリューション カーネルを左上隅からフィッティングし、対応する数値を乗算およ​​び加算することによって、グレースケール画像 (2 次元の行列と考えることができます) が得られます。位置: 特徴マップの最初の数。
ここに画像の説明を挿入
次に、コンボリューション カーネルを移動し、対応する位置を乗算およ​​び加算することにより、ステップ サイズが 1 である特徴マップの 2 番目の結果を取得できます。
コンボリューションカーネルの移動方向は、まず右に移動して最後まで移動し、次に下に移動して先頭に戻り、さらに右に移動するという往復運動を繰り返すと、下図のような結果になります。取得できる。
ここに画像の説明を挿入
シングル チャネル入力およびマルチコア現象の場合、各コンボリューション カーネルとシングル チャネル イメージを使用して演算が実行され、最終的に次の図に示すように 2 つの特徴マップが取得されます。
ここに画像の説明を挿入
マルチチャネル状況の場合は、次の図に示すように。
ここに画像の説明を挿入
マルチチャネルの各チャネルは、コンボリューション カーネルの対応するチャネルと畳み込み処理され、すべてのチャネルの計算結果が加算されて、次の図に示すような特徴マップの結果が得られます。
ここに画像の説明を挿入
マルチチャネル マルチコア現象の場合、下図に示すように、各コンボリューション カーネルとマルチチャネルが動作して複数の特徴マップが取得されます。
ここに画像の説明を挿入
上記はすべて、ステップ サイズが 1 の場合、つまり、コンボリューション カーネルが一度に 1 ピクセルずつ移動する場合です。ストライドが 2 の場合、コンボリューション カーネルは一度に 2 ピクセルずつ移動します。

3. 埋める

上記の導入により、多層畳み込みの後、特徴マップがますます小さくなり、情報不足が生じることがわかりました。影響を軽減するために、充填操作を実行できます。 0 の円を特徴マップの外層に配置し、それを結合します。 コンボリューション カーネルは、次の図に示すような演算を実行します。
ここに画像の説明を挿入
このとき、コンボリューションカーネルのパラメータパディングは同様で、パディングを行わない場合に有効となり、デフォルトが有効となります。

4. プーリング

特徴マップが非常に大きく、ネットワークのパラメーターが多すぎる場合があるため、ネットワーク パラメーターの数を減らすために、プーリング操作が使用されます。プーリングは、最大プーリングと平均プーリングに分類できます。
最大プーリングとは、下図に示すように、出力結果としての特徴マップの各プーリング領域の最大値を求めることです。
ここに画像の説明を挿入
平均プーリングとは、下図に示すように、各プーリング領域の特徴マップを平均した結果を出力結果として求めることです。
ここに画像の説明を挿入

5. 畳み込みニューラルネットワーク戦闘 - 手書き数字認識のための CNN モデル

5.1 関連ライブラリのインポート

次のサードパーティ ライブラリは、深層学習用の Python 固有のライブラリです。

from keras.datasets import mnist
import matplotlib.pyplot as plt
from tensorflow import keras
from keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPool2D
from keras.models import Sequential
from keras.callbacks import EarlyStopping
import tensorflow as tf
from keras import optimizers, losses

5.2 データのロード

MNIST データセットをロードする

"1.加载数据"
"""
x_train是mnist训练集图片,大小的28*28的,y_train是对应的标签是数字
x_test是mnist测试集图片,大小的28*28的,y_test是对应的标签是数字
"""
(x_train, y_train), (x_test, y_test) = mnist.load_data()  # 加载mnist数据集
print('mnist_data:', x_train.shape, y_train.shape, x_test.shape, y_test.shape)  # 打印训练数据和测试数据的形状

5.3 データの前処理

(1) 入力画像を正規化して 0-255 から 0-1 に変換する;
(2) 入力画像 (60000, 28, 28) の形状を便利な (60000, 28, 28, 1) に変換するニューラル ネットワークへの入力;
(3) ラベル y のワンホット エンコード。ニューラル ネットワークの出力は 10 個の確率値であり、y は 1 つの数値であり、損失を計算するときに対応して計算できないため、y は独立してエンコードされます。数値の行ベクトルを 10 に変換し、損失を計算します。 ワンホット エンコーディング: たとえば、値 1 の 10 個のカテゴリのワンホット エンコーディングは [0 1 0 0 0 0 0 0 0 0、つまり、1 の位置は 1、残りの位置は 0 です。

"2.数据预处理"


def preprocess(x, y):  # 数据预处理函数
    x = tf.cast(x, dtype=tf.float32) / 255.  # 将输入的图片进行归一化,从0-255变换到0-1
    x = tf.reshape(x, [28, 28, 1])
    """
    # 将输入图片的形状(60000,28,28)转换成(60000,28,28,1),
    相当于将图片拉直,便于输入给神经网络
    """
    y = tf.cast(y, dtype=tf.int32)  # 将输入图片的标签转换为int32类型
    y = tf.one_hot(y, depth=10)
    """
    # 将标签y进行独热编码,因为神经网络的输出是10个概率值,而y是1个数,
    计算loss时无法对应计算,因此将y进行独立编码成为10个数的行向量,然后进行loss的计算
    独热编码:例如数值1的10分类的独热编码是[0 1 0 0 0 0 0 0 0 0,即1的位置为1,其余位置为0
    """
    return x, y

5.4 データ処理

データがメモリにロードされた後、TensorFlow が提供するさまざまな便利な機能を利用するには、データを Dataset オブジェクトに変換する必要があります。
Dataset.from_tensor_slices を通じて、トレーニング パーツのデータ イメージ x とラベル y を Dataset オブジェクトに変換できます。

batchsz = 128  # 每次输入给神经网络的图片数
"""
数据加载进入内存后,需要转换成 Dataset 对象,才能利用 TensorFlow 提供的各种便捷功能。
通过 Dataset.from_tensor_slices 可以将训练部分的数据图片 x 和标签 y 都转换成Dataset 对象
"""
db = tf.data.Dataset.from_tensor_slices((x_train, y_train))  # 构建训练集对象
db = db.map(preprocess).shuffle(60000).batch(batchsz)  # 将数据进行预处理,随机打散和批量处理
ds_val = tf.data.Dataset.from_tensor_slices((x_test, y_test))  # 构建测试集对象
ds_val = ds_val.map(preprocess).batch(batchsz)  # 将数据进行预处理,随机打散和批量处理

5.5 ネットワークモデルの構築

2 つの畳み込み層、2 つのプーリング層、次に平坦化層 (2 次元特徴マップを完全結合層に直線化する) が構築され、その後に 3 つの完全結合層が続きます。

"3.构建网络模型"
model = Sequential([Conv2D(filters=6, kernel_size=(5, 5), activation='relu'),
                    MaxPool2D(pool_size=(2, 2), strides=2),
                    Conv2D(filters=16, kernel_size=(5, 5), activation='relu'),
                    MaxPool2D(pool_size=(2, 2), strides=2),
                    Flatten(),
                    Dense(120, activation='relu'),
                    Dense(84, activation='relu'),
                    Dense(10,activation='softmax')])

model.build(input_shape=(None, 28, 28, 1))  # 模型的输入大小
model.summary()  # 打印网络结构

5.6 モデルのコンパイル

モデルの最適化者は Adam、学習率は 0.01、
損失関数は loss.CategoricalCrossentropy、
パフォーマンス指標は精度です

"4.模型编译"
model.compile(optimizer=optimizers.Adam(lr=0.01),
                loss=tf.losses.CategoricalCrossentropy(from_logits=False),
                metrics=['accuracy']
                )
"""
模型的优化器是Adam,学习率是0.01,
损失函数是losses.CategoricalCrossentropy,
性能指标是正确率accuracy
"""

5.7 モデルのトレーニング、保存、評価

モデルのトレーニングの数は 5 で、各サイクルがテストされ、
モデルは .h5 ファイル形式で保存され、
テスト セットの正しいレートが取得されます。

"5.模型训练"
history = model.fit(db, epochs=5, validation_data=ds_val, validation_freq=1)
"""
模型训练的次数是5,每1次循环进行测试
"""
"6.模型保存"
model.save('cnn_mnist.h5')  # 以.h5文件格式保存模型

"7.模型评价"
model.evaluate(ds_val)  # 得到测试集的正确率

5.8 モデルのテスト

モデルをテストする

"8.模型测试"
sample = next(iter(ds_val))  # 取一个batchsz的测试集数据
x = sample[0]  # 测试集数据
y = sample[1]  # 测试集的标签
pred = model.predict(x)  # 将一个batchsz的测试集数据输入神经网络的结果
pred = tf.argmax(pred, axis=1)  # 每个预测的结果的概率最大值的下标,也就是预测的数字
y = tf.argmax(y, axis=1)  # 每个标签的最大值对应的下标,也就是标签对应的数字
print(pred)  # 打印预测结果
print(y)  # 打印标签数字

5.9 モデルのトレーニング結果の可視化

モデルのトレーニング結果を視覚化する

"9.模型训练时的可视化"
# 显示训练集和验证集的acc和loss曲线
acc = history.history['accuracy']  # 获取模型训练中的accuracy
val_acc = history.history['val_accuracy']  # 获取模型训练中的val_accuracy
loss = history.history['loss']  # 获取模型训练中的loss
val_loss = history.history['val_loss']  # 获取模型训练中的val_loss
# 绘值acc曲线
plt.figure(1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.legend()
# 绘制loss曲线
plt.figure(2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()  # 将结果显示出来

6. 手書き数字認識用CNNモデルの可視化結果マップ

Epoch 1/5
469/469 [==============================] - 12s 22ms/step - loss: 0.1733 - accuracy: 0.9465 - val_loss: 0.0840 - val_accuracy: 0.9763
Epoch 2/5
469/469 [==============================] - 11s 21ms/step - loss: 0.0704 - accuracy: 0.9793 - val_loss: 0.0581 - val_accuracy: 0.9819
Epoch 3/5
469/469 [==============================] - 11s 22ms/step - loss: 0.0566 - accuracy: 0.9833 - val_loss: 0.0576 - val_accuracy: 0.9844
Epoch 4/5
469/469 [==============================] - 11s 22ms/step - loss: 0.0573 - accuracy: 0.9833 - val_loss: 0.0766 - val_accuracy: 0.9784
Epoch 5/5
469/469 [==============================] - 11s 22ms/step - loss: 0.0556 - accuracy: 0.9844 - val_loss: 0.0537 - val_accuracy: 0.9830

ここに画像の説明を挿入
ここに画像の説明を挿入
上記の結果から、モデルの精度は 98% に達していることがわかります。

7. 完全なコード

from keras.datasets import mnist
import matplotlib.pyplot as plt
from tensorflow import keras
from keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPool2D
from keras.models import Sequential
from keras.callbacks import EarlyStopping
import tensorflow as tf
from keras import optimizers, losses
"1.加载数据"
"""
x_train是mnist训练集图片,大小的28*28的,y_train是对应的标签是数字
x_test是mnist测试集图片,大小的28*28的,y_test是对应的标签是数字
"""
(x_train, y_train), (x_test, y_test) = mnist.load_data()  # 加载mnist数据集
print('mnist_data:', x_train.shape, y_train.shape, x_test.shape, y_test.shape)  # 打印训练数据和测试数据的形状

"2.数据预处理"


def preprocess(x, y):  # 数据预处理函数
    x = tf.cast(x, dtype=tf.float32) / 255.  # 将输入的图片进行归一化,从0-255变换到0-1
    x = tf.reshape(x, [28, 28, 1])
    """
    # 将输入图片的形状(60000,28,28)转换成(60000,28,28,1),
    相当于将图片拉直,便于输入给神经网络
    """
    y = tf.cast(y, dtype=tf.int32)  # 将输入图片的标签转换为int32类型
    y = tf.one_hot(y, depth=10)
    """
    # 将标签y进行独热编码,因为神经网络的输出是10个概率值,而y是1个数,
    计算loss时无法对应计算,因此将y进行独立编码成为10个数的行向量,然后进行loss的计算
    独热编码:例如数值1的10分类的独热编码是[0 1 0 0 0 0 0 0 0 0,即1的位置为1,其余位置为0
    """
    return x, y


batchsz = 128  # 每次输入给神经网络的图片数
"""
数据加载进入内存后,需要转换成 Dataset 对象,才能利用 TensorFlow 提供的各种便捷功能。
通过 Dataset.from_tensor_slices 可以将训练部分的数据图片 x 和标签 y 都转换成Dataset 对象
"""
db = tf.data.Dataset.from_tensor_slices((x_train, y_train))  # 构建训练集对象
db = db.map(preprocess).shuffle(60000).batch(batchsz)  # 将数据进行预处理,随机打散和批量处理
ds_val = tf.data.Dataset.from_tensor_slices((x_test, y_test))  # 构建测试集对象
ds_val = ds_val.map(preprocess).batch(batchsz)  # 将数据进行预处理,随机打散和批量处理

"3.构建网络模型"
model = Sequential([Conv2D(filters=6, kernel_size=(5, 5), activation='relu'),
                    MaxPool2D(pool_size=(2, 2), strides=2),
                    Conv2D(filters=16, kernel_size=(5, 5), activation='relu'),
                    MaxPool2D(pool_size=(2, 2), strides=2),
                    Flatten(),
                    Dense(120, activation='relu'),
                    Dense(84, activation='relu'),
                    Dense(10,activation='softmax')])

model.build(input_shape=(None, 28, 28, 1))  # 模型的输入大小
model.summary()  # 打印网络结构

"4.模型编译"
model.compile(optimizer=optimizers.Adam(lr=0.01),
                loss=tf.losses.CategoricalCrossentropy(from_logits=False),
                metrics=['accuracy']
                )
"""
模型的优化器是Adam,学习率是0.01,
损失函数是losses.CategoricalCrossentropy,
性能指标是正确率accuracy
"""

"5.模型训练"
history = model.fit(db, epochs=5, validation_data=ds_val, validation_freq=1)
"""
模型训练的次数是5,每1次循环进行测试
"""
"6.模型保存"
model.save('cnn_mnist.h5')  # 以.h5文件格式保存模型

"7.模型评价"
model.evaluate(ds_val)  # 得到测试集的正确率

"8.模型测试"
sample = next(iter(ds_val))  # 取一个batchsz的测试集数据
x = sample[0]  # 测试集数据
y = sample[1]  # 测试集的标签
pred = model.predict(x)  # 将一个batchsz的测试集数据输入神经网络的结果
pred = tf.argmax(pred, axis=1)  # 每个预测的结果的概率最大值的下标,也就是预测的数字
y = tf.argmax(y, axis=1)  # 每个标签的最大值对应的下标,也就是标签对应的数字
print(pred)  # 打印预测结果
print(y)  # 打印标签数字

"9.模型训练时的可视化"
# 显示训练集和验证集的acc和loss曲线
acc = history.history['accuracy']  # 获取模型训练中的accuracy
val_acc = history.history['val_accuracy']  # 获取模型训练中的val_accuracy
loss = history.history['loss']  # 获取模型训练中的loss
val_loss = history.history['val_loss']  # 获取模型训练中的val_loss
# 绘值acc曲线
plt.figure(1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.legend()
# 绘制loss曲线
plt.figure(2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()  # 将结果显示出来

おすすめ

転載: blog.csdn.net/qq_47598782/article/details/131871796