卷积神经网络 概念

这一章,我觉得最重要的便是要了解好卷积神经网络的概念,这个搞的差不多了,后面看代码才会轻松,

这儿引荐这个视频卷积神经网络

除了这个以外,咱们还能够自行去youtube ,b站 搜索关键词 加深对这个概念的了解, 这儿我就不复述了,

实例化一个小型的卷积神经网络

from tensorflow import keras
from tensorflow.keras import layers
if __name__ == '__main__':
    inputs = keras.Input(shape=(28, 28, 1))
    # 一般都是3*3 或许 5*5
    # 所以通过一次Conv 今后 28*28 会变成 26,会变小
    x = layers.Conv2D(filters=32, kernel_size=3, activation="relu")(inputs)
    # 最大汇聚下采样 一般都是除2
    x = layers.MaxPooling2D(pool_size=2)(x)
    x = layers.Conv2D(filters=64, kernel_size=3, activation="relu")(x)
    x = layers.MaxPooling2D(pool_size=2)(x)
    x = layers.Conv2D(filters=128, kernel_size=3, activation="relu")(x)
    # 将三维输出展开为1维,最终才能增加dense
    x = layers.Flatten()(x)
    outputs = layers.Dense(10, activation="softmax")(x)
    model = keras.Model(inputs=inputs, outputs=outputs)
    model.summary()

能够看下这个模型

《python深度学习》读书笔记(6) - 计算机视觉深度学习入门

其实你只需前面的视频看了,这儿模型的输出为啥是这样 就必定能够了解了。

在mnist图画上练习卷积神经网络

(images, labels), (test_images, test_labels) = mnist.load_data()
images = images.reshape(60000, 28, 28, 1).astype('float32') / 255
test_images = test_images.reshape(10000, 28, 28, 1).astype('float32') / 255
model.compile(optimizer="rmsprop", loss="sparse_categorical_crossentropy", metrics=["accuracy"])
model.fit(images, labels, epochs=5, batch_size=64)
test_loss, test_acc = model.evaluate(test_images, test_labels)
print(f"Test loss: {test_loss} test_acc:{test_acc}")

《python深度学习》读书笔记(6) - 计算机视觉深度学习入门

练习精度能够到99.1% ,比之前咱们的密布衔接型网络作用要好的太多了

猫狗分类问题

数据从kaggle上下载kaggle猫狗数据

原始的 文件夹

《python深度学习》读书笔记(6) - 计算机视觉深度学习入门

每个文件夹下都有几万张 猫和狗的图片

留意这个数据是有问题的,她的索引断掉了, 例如:

《python深度学习》读书笔记(6) - 计算机视觉深度学习入门

这会导致你的练习代码犯错 ,而且一时难以排查,所以咱们从kaggle上下载练习数据时,尤其是图画类的,最好自己写个代码遍历下,看看是不是每个文件都能够用

记住 kaggele上的数据不是彻底可信

当然你也能够用我处理好的数据 也能够github地址

梳理数据

import os, shutil, pathlib
original_dir = pathlib.Path("PetImages")
new_base_dir = pathlib.Path("cats_vs_dogs_small")
def make_subset(subset_name, start_idx, end_idx):
    for category in ("cat", "dog"):
        dir = f"{new_base_dir}/{subset_name}/{category}"
        os.makedirs(dir, exist_ok=True)
        fnames = [f"{category}/{i}.jpg" for i in range(start_idx, end_idx)]
        for fname in fnames:
            shutil.copyfile(src=f"{original_dir}/{fname}", dst=f"{new_base_dir}/{subset_name}/{fname}")
if __name__ == '__main__':
    make_subset("train", 0, 1000)
    make_subset("validation", 1000, 1500)
    make_subset("test", 1500, 2500)

咱们把这些数据梳理一下,分红 练习数据,验证数据,测试数据

树立模型

inputs = keras.Input(shape=(180, 180, 3))
# 意图是把 0~255的 取值规模缩放到 0,1 这个区间
x = layers.Rescaling(1. / 255)(inputs)
x = layers.Conv2D(filters=32, kernel_size=3, activation="relu")(x)
x = layers.MaxPooling2D(pool_size=2)(x)
x = layers.Conv2D(filters=64, kernel_size=3, activation="relu")(x)
x = layers.MaxPooling2D(pool_size=2)(x)
x = layers.Conv2D(filters=128, kernel_size=3, activation="relu")(x)
x = layers.MaxPooling2D(pool_size=2)(x)
x = layers.Conv2D(filters=256, kernel_size=3, activation="relu")(x)
x = layers.MaxPooling2D(pool_size=2)(x)
x = layers.Conv2D(filters=256, kernel_size=3, activation="relu")(x)
x = layers.Flatten()(x)
outputs = layers.Dense(1, activation="sigmoid")(x)
model = keras.Model(inputs=inputs, outputs=outputs)
model.summary()
model.compile(loss="binary_crossentropy", optimizer="rmsprop", metrics=["accuracy"])

看下这个模型长啥样

《python深度学习》读书笔记(6) - 计算机视觉深度学习入门

生成批量数据

前面咱们是把图片分类到不同的文件夹,可是要给tensorflow 来用 还不够,咱们还要读取文件,然后转成对应的180 180 向量

好在ts提供了方便的api 能够迅速的帮咱们把这些事情做好

# image_dataset_from_directory 会帮咱们主动将图片打乱顺序,而且调理成一致大小,并打包成批量
train_dataset = image_dataset_from_directory(new_base_dir / "train", image_size=(180, 180), batch_size=32)
validation_dataset = image_dataset_from_directory(new_base_dir / "validation", image_size=(180, 180), batch_size=32)
test_dataset = image_dataset_from_directory(new_base_dir / "test", image_size=(180, 180), batch_size=32)

练习看作用

# 利用回调函数,在每轮过后保存模型,保存的都是最佳模型状态
# 留意看save和monitor这2个参数,每次保存的val_loss最好的模型
callbacks = [
    keras.callbacks.ModelCheckpoint(filepath="convent_from_scratch.keras", save_best_only=True,
                                    monitor="val_loss", )
]
history = model.fit(train_dataset, epochs=30, validation_data=validation_dataset, callbacks=callbacks)
accuracy = history.history["accuracy"]
val_accuracy = history.history["val_accuracy"]
loss = history.history["loss"]
val_loss = history.history["val_loss"]
epochs = range(1, len(accuracy) + 1)
plt.plot(epochs, accuracy, "bo", label="Training  accuracy")
plt.plot(epochs, val_accuracy, "b", label="Validation accuracy")
plt.title("Training and validation accuracy")
plt.legend()
plt.figure()
plt.plot(epochs, loss, "bo", label="Training loss")
plt.plot(epochs, val_loss, "bo", label="Validation loss")
plt.title("Training and validation loss")
plt.legend()
plt.show()

《python深度学习》读书笔记(6) - 计算机视觉深度学习入门

这儿能够看出来,练习数据的精度最终简直能到100%,验证数据大概便是70%左右。明显的过拟合现象

咱们来看下在测试数据上的精度怎么

test_model = keras.models.load_model("convent_from_scratch.keras")
test_loss, test_acc = test_model.evaluate(test_dataset)
print(f" test_acc: {test_acc:.3f}")

《python深度学习》读书笔记(6) - 计算机视觉深度学习入门

数据增强

这个概念是指 从现有的练习样本中生成更多的练习数据,做法是利用一些能够生成可信图画的随机变换来增强样本

数据曾强的目标是 在练习时不会两次查看 彻底相同的图片。

其实便是你在练习数据不够多的时分 能够利用这种办法 来增加练习数据

inputs = keras.Input(shape=(180, 180, 3))
data_aug = keras.Sequential([
    # 图片 随机抽取50%的图画 做水平反转
    layers.RandomFlip("horizontal"),
    # 图画视点改变
    layers.RandomRotation(0.1),
    # 扩大缩小
    layers.RandomZoom(0.2)
])
x = data_aug(inputs)
# 这儿不再是inputs参数了 是咱们转换过的 数据增强过的 x参数
x = layers.Rescaling(1. / 255)(x)
x = layers.Conv2D(filters=32, kernel_size=3, activation="relu")(x)
x = layers.MaxPooling2D(pool_size=2)(x)
x = layers.Conv2D(filters=64, kernel_size=3, activation="relu")(x)
x = layers.MaxPooling2D(pool_size=2)(x)
x = layers.Conv2D(filters=128, kernel_size=3, activation="relu")(x)
x = layers.MaxPooling2D(pool_size=2)(x)
x = layers.Conv2D(filters=256, kernel_size=3, activation="relu")(x)
x = layers.MaxPooling2D(pool_size=2)(x)
x = layers.Conv2D(filters=256, kernel_size=3, activation="relu")(x)
x = layers.Flatten()(x)
# 留意这儿
x = layers.Dropout(0.5)(x)

另外由于估计到 过拟合现象会较晚的出现,所以咱们 要扩大练习次数到100

history = model.fit(train_dataset, epochs=100, validation_data=validation_dataset, callbacks=callbacks)

这儿就明显能看出来过拟合的现象迟了许多,作用也愈加好了。

《python深度学习》读书笔记(6) - 计算机视觉深度学习入门

实践的测试集作用 相比之前 提高了10个点左右 很不错的成果

《python深度学习》读书笔记(6) - 计算机视觉深度学习入门

运用预练习模型

能够用之前他人练习好的模型 ,然后从头应用于某个没关系的使命,深度模型 不同问题之间具备必定的 可移植性

运用预练习模型有两种方法 特征提取和微调模型

特征提取

咱们能够从ImageNet上练习的VGG16 网络 来做模型提取

# 将vgg16 卷积基 实例化, 而且 
conv_base = keras.applications.vgg16.VGG16(
    # weights 模型初始化的权重点
    # 是否包含密布衔接分类器,由于咱们计划运用自己的猫狗分类,所以这儿显然用false
    weights="imagenet", include_top=False, input_shape=(180, 180, 3)
)

这儿除了这个还有其他的图画分类模型,有兴趣的能够自行查询api进行探索

留意看下这个模型提取出来今后 输出是 5 5 512

《python深度学习》读书笔记(6) - 计算机视觉深度学习入门

有了它,咱们就能够开端准备练习了,完整代码如下:

import os
import pathlib
import shutil
import keras
import numpy as np
from matplotlib import pyplot as plt
from tensorflow.keras import layers
from tensorflow.keras.utils import image_dataset_from_directory
original_dir = pathlib.Path("PetImages")
new_base_dir = pathlib.Path("cats_vs_dogs_small")
# 将vgg16 卷积基 实例化, 而且
conv_base = keras.applications.vgg16.VGG16(
    # weights 模型初始化的权重点
    # 是否包含密布衔接分类器,由于咱们计划运用自己的猫狗分类,所以这儿显然用false
    weights="imagenet", include_top=False, input_shape=(180, 180, 3)
)
def make_subset(subset_name, start_idx, end_idx):
    for category in ("cat", "dog"):
        dir = f"{new_base_dir}/{subset_name}/{category}"
        os.makedirs(dir, exist_ok=True)
        fnames = [f"{category}/{i}.jpg" for i in range(start_idx, end_idx)]
        for fname in fnames:
            shutil.copyfile(src=f"{original_dir}/{fname}", dst=f"{new_base_dir}/{subset_name}/{fname}")
def get_features_and_labels(dataset):
    all_features = []
    all_labels = []
    for images, labels in dataset:
        # preprocess_input 只接纳图画作为参数 所以要预处理一下
        preprocessed_images = keras.applications.vgg16.preprocess_input(images)
        features = conv_base.predict(preprocessed_images)
        all_features.append(features)
        all_labels.append(labels)
    return np.concatenate(all_features), np.concatenate(all_labels)
if __name__ == '__main__':
    # image_dataset_from_directory 会帮咱们主动将图片打乱顺序,而且调理成一致大小,并打包成批量
    train_dataset = image_dataset_from_directory(new_base_dir / "train", image_size=(180, 180), batch_size=32)
    validation_dataset = image_dataset_from_directory(new_base_dir / "validation", image_size=(180, 180), batch_size=32)
    test_dataset = image_dataset_from_directory(new_base_dir / "test", image_size=(180, 180), batch_size=32)
    train_features, train_labels = get_features_and_labels(train_dataset)
    validation_features, validation_labels = get_features_and_labels(validation_dataset)
    test_features, test_labels = get_features_and_labels(test_dataset)
    conv_base.summary()
    print(f"Train features shape: {train_features.shape}")
    # 这儿的参数是5,5,512 了 否则input对不上要犯错
    inputs = keras.Input(shape=(5, 5, 512))
    # 将特征传入dense层之前,必须先通过flattern层
    x = layers.Flatten()(inputs)
    x = layers.Dense(256)(x)
    x = layers.Dropout(0.5)(x)
    outputs = layers.Dense(1, activation="sigmoid")(x)
    model = keras.Model(inputs=inputs, outputs=outputs)
    model.compile(loss="binary_crossentropy", optimizer="rmsprop", metrics=["accuracy"])
    model.summary()
    callbacks = [
        keras.callbacks.ModelCheckpoint(filepath="feature_extraction.keras", save_best_only=True,
                                        monitor="val_loss", )
    ]
    # 这儿练习的参数 要改一下
    history = model.fit(train_features, train_labels, epochs=20, validation_data=(validation_features,validation_labels),
                        callbacks=callbacks)
    accuracy = history.history["accuracy"]
    val_accuracy = history.history["val_accuracy"]
    loss = history.history["loss"]
    val_loss = history.history["val_loss"]
    epochs = range(1, len(accuracy) + 1)
    plt.plot(epochs, accuracy, "bo", label="Training  accuracy")
    plt.plot(epochs, val_accuracy, "b", label="Validation accuracy")
    plt.title("Training and validation accuracy")
    plt.legend()
    plt.figure()
    plt.plot(epochs, loss, "bo", label="Training loss")
    plt.plot(epochs, val_loss, "bo", label="Validation loss")
    plt.title("Training and validation loss")
    plt.legend()
    plt.show()
    test_model = keras.models.load_model("feature_extraction.keras")
    test_loss, test_acc = test_model.evaluate(test_features, test_labels)
    print(f" test_acc: {test_acc:.3f}")

这儿能够看下验证数据集的精度:

《python深度学习》读书笔记(6) - 计算机视觉深度学习入门

在测试数据上的精度也有97%

《python深度学习》读书笔记(6) - 计算机视觉深度学习入门

当然这个精度这么高的原因是ImageNet自身有足够做的猫狗样本,也便是说 咱们的预练习模型已经具有完结当前使命所需要的知识, 假如你用其他事务的时分,往往或许不会有这么好的作用

微调预练习模型

回忆下咱们之前的conv模型,能够对他进行微调

conv_base.trainable = True
# 冻结除最终4层外的一切层
# 为什么不对更多的层进行微调? 原理上是能够的,可是这样做作用欠好
# 卷积层中较早增加的是更通用的可复用特征,较晚增加的则是针对性更强的特征,微调较早增加的层,收益会更小
for layer in conv_base.layers[:-4]:
    layer.trainable = False

完整代码展示

import pathlib
import keras
import numpy as np
from matplotlib import pyplot as plt
from tensorflow.keras import layers
from tensorflow.keras.utils import image_dataset_from_directory
original_dir = pathlib.Path("PetImages")
new_base_dir = pathlib.Path("cats_vs_dogs_small")
# 将vgg16 卷积基 实例化, 而且
conv_base = keras.applications.vgg16.VGG16(
    # weights 模型初始化的权重点
    # 是否包含密布衔接分类器,由于咱们计划运用自己的猫狗分类,所以这儿显然用false
    weights="imagenet", include_top=False, input_shape=(180, 180, 3)
)
def get_features_and_labels(dataset):
    all_features = []
    all_labels = []
    for images, labels in dataset:
        # preprocess_input 只接纳图画作为参数 所以要预处理一下
        preprocessed_images = keras.applications.vgg16.preprocess_input(images)
        features = conv_base.predict(preprocessed_images)
        all_features.append(features)
        all_labels.append(labels)
    return np.concatenate(all_features), np.concatenate(all_labels)
if __name__ == '__main__':
    conv_base.trainable = True
    # 冻结除最终4层外的一切层
    # 为什么不对更多的层进行微调? 原理上是能够的,可是这样做作用欠好
    # 卷积层中较早增加的是更通用的可复用特征,较晚增加的则是针对性更强的特征,微调较早增加的层,收益会更小
    for layer in conv_base.layers[:-4]:
        layer.trainable = False
    # image_dataset_from_directory 会帮咱们主动将图片打乱顺序,而且调理成一致大小,并打包成批量
    train_dataset = image_dataset_from_directory(new_base_dir / "train", image_size=(180, 180), batch_size=32)
    validation_dataset = image_dataset_from_directory(new_base_dir / "validation", image_size=(180, 180), batch_size=32)
    test_dataset = image_dataset_from_directory(new_base_dir / "test", image_size=(180, 180), batch_size=32)
    train_features, train_labels = get_features_and_labels(train_dataset)
    validation_features, validation_labels = get_features_and_labels(validation_dataset)
    test_features, test_labels = get_features_and_labels(test_dataset)
    conv_base.summary()
    print(f"Train features shape: {train_features.shape}")
    # 这儿的参数是5,5,512 了 否则input对不上要犯错
    inputs = keras.Input(shape=(5, 5, 512))
    # 将特征传入dense层之前,必须先通过flattern层
    x = layers.Flatten()(inputs)
    x = layers.Dense(256)(x)
    x = layers.Dropout(0.5)(x)
    outputs = layers.Dense(1, activation="sigmoid")(x)
    model = keras.Model(inputs=inputs, outputs=outputs)
    model.compile(loss="binary_crossentropy", optimizer=keras.optimizers.RMSprop(learning_rate=1e-5), metrics=["accuracy"])
    model.summary()
    callbacks = [
        keras.callbacks.ModelCheckpoint(filepath="fine_tuning.keras", save_best_only=True,
                                        monitor="val_loss", )
    ]
    # 这儿练习的参数 要改一下
    history = model.fit(train_features, train_labels, epochs=30, validation_data=(validation_features, validation_labels),
                        callbacks=callbacks)
    accuracy = history.history["accuracy"]
    val_accuracy = history.history["val_accuracy"]
    loss = history.history["loss"]
    val_loss = history.history["val_loss"]
    epochs = range(1, len(accuracy) + 1)
    plt.plot(epochs, accuracy, "bo", label="Training  accuracy")
    plt.plot(epochs, val_accuracy, "b", label="Validation accuracy")
    plt.title("Training and validation accuracy")
    plt.legend()
    plt.figure()
    plt.plot(epochs, loss, "bo", label="Training loss")
    plt.plot(epochs, val_loss, "bo", label="Validation loss")
    plt.title("Training and validation loss")
    plt.legend()
    plt.show()
    test_model = keras.models.load_model("fine_tuning.keras")
    test_loss, test_acc = test_model.evaluate(test_features, test_labels)
    print(f" test_acc: {test_acc:.3f}")