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

卷积神经网络 概念

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

这里推荐这个视频卷积神经网络

除了这个以外,大家还可以自行去youtube ,b站 搜索关键词 加深对这个概念的理解, 这里我就不复述了,

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

ini 复制代码
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()

可以看下这个模型

其实你只要前面的视频看了,这里模型的输出为啥是这样 就肯定可以理解了。

在mnist图像上训练卷积神经网络

ini 复制代码
(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}")

训练精度可以到99.1% ,比之前我们的密集连接型网络效果要好的太多了

猫狗分类问题

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

原始的 文件夹

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

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

这会导致你的训练代码出错 ,而且一时难以排查,所以大家从kaggle上下载训练数据时,尤其是图像类的,最好自己写个代码遍历下,看看是不是每个文件都可以用

记住 kaggele上的数据不是完全可信

当然你也可以用我处理好的数据 也可以github地址

梳理数据

python 复制代码
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)

我们把这些数据梳理一下,分成 训练数据,验证数据,测试数据

建立模型

ini 复制代码
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"])

看下这个模型长啥样

生成批量数据

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

好在ts提供了方便的api 可以迅速的帮我们把这些事情做好

python 复制代码
# 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)

训练看效果

python 复制代码
# 利用回调函数,在每轮过后保存模型,保存的都是最佳模型状态
# 注意看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()

这里可以看出来,训练数据的精度最后几乎能到100%,验证数据大概就是70%左右。明显的过拟合现象

我们来看下在测试数据上的精度如何

ini 复制代码
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 复制代码
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

ini 复制代码
history = model.fit(train_dataset, epochs=100, validation_data=validation_dataset, callbacks=callbacks)

这里就明显能看出来过拟合的现象迟了很多,效果也更加好了。

实际的测试集效果 相比之前 提高了10个点左右 很不错的成绩

使用预训练模型

可以用之前别人训练好的模型 ,然后重新应用于某个没关系的任务,深度模型 不同问题之间具备一定的 可移植性

使用预训练模型有两种方式 特征提取和微调模型

特征提取

我们可以从ImageNet上训练的VGG16 网络 来做模型提取

python 复制代码
# 将vgg16 卷积基 实例化, 并且 
conv_base = keras.applications.vgg16.VGG16(
    # weights 模型初始化的权重点
    # 是否包含密集连接分类器,因为我们打算使用自己的猫狗分类,所以这里显然用false
    weights="imagenet", include_top=False, input_shape=(180, 180, 3)
)

这里除了这个还有其他的图像分类模型,有兴趣的可以自行查询api进行探索

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

有了它,我们就可以开始准备训练了,完整代码如下:

python 复制代码
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}")

这里可以看下验证数据集的精度:

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

当然这个精度这么高的原因是ImageNet本身有足够做的猫狗样本,也就是说 我们的预训练模型已经拥有完成当前任务所需要的知识, 如果你用其他业务的时候,往往可能不会有这么好的效果

微调预训练模型

回顾下我们之前的conv模型,可以对他进行微调

python 复制代码
conv_base.trainable = True
# 冻结除最后4层外的所有层
# 为什么不对更多的层进行微调? 原理上是可以的,但是这样做效果不好
# 卷积层中较早添加的是更通用的可复用特征,较晚添加的则是针对性更强的特征,微调较早添加的层,收益会更小
for layer in conv_base.layers[:-4]:
    layer.trainable = False

完整代码展示

python 复制代码
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}")
相关推荐
神仙别闹15 分钟前
基于tensorflow和flask的本地图片库web图片搜索引擎
前端·flask·tensorflow
HerayChen1 小时前
HbuildderX运行到手机或模拟器的Android App基座识别不到设备 mac
android·macos·智能手机
顾北川_野1 小时前
Android 手机设备的OEM-unlock解锁 和 adb push文件
android·java
hairenjing11231 小时前
在 Android 手机上从SD 卡恢复数据的 6 个有效应用程序
android·人工智能·windows·macos·智能手机
小黄人软件1 小时前
android浏览器源码 可输入地址或关键词搜索 android studio 2024 可开发可改地址
android·ide·android studio
dj15402252032 小时前
group_concat配置影响程序出bug
android·bug
周全全2 小时前
MySQL报错解决:The user specified as a definer (‘root‘@‘%‘) does not exist
android·数据库·mysql
- 羊羊不超越 -3 小时前
App渠道来源追踪方案全面分析(iOS/Android/鸿蒙)
android·ios·harmonyos
wk灬丨3 小时前
Android Kotlin Flow 冷流 热流
android·kotlin·flow