政安晨:【Keras机器学习示例演绎】(四)—— 利用迁移学习进行关键点检测

本文主要是介绍政安晨:【Keras机器学习示例演绎】(四)—— 利用迁移学习进行关键点检测,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

数据收集

导入

定义超参数

加载数据

可视化数据

准备数据生成器

定义增强变换

创建训练和验证分割

数据生成器调查

模型构建

模型编译和训练

进行预测并将其可视化

更进一步


政安晨的个人主页政安晨

欢迎 👍点赞✍评论⭐收藏

收录专栏: TensorFlow与Keras机器学习实战

希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正!

本文目标:利用数据增强和迁移学习训练关键点检测器。

关键点检测包括对物体关键部位的定位。例如,人脸的关键部位包括鼻尖、眉毛、眼角等。这些部分有助于以特征丰富的方式表示底层对象。关键点检测的应用包括姿势估计、人脸检测等。

在本例中,我们将使用斯坦福 Extra 数据集,利用迁移学习建立一个关键点检测器。

本示例需要 TensorFlow 2.4 或更高版本,以及 imgaug 库,可使用以下命令安装:

!pip install -q -U imgaug

数据收集


StanfordExtra 数据集包含 12,000 张狗的图像以及关键点和分割图。该数据集由斯坦福狗数据集发展而来。

可通过以下命令下载:

!wget -q http://vision.stanford.edu/aditya86/ImageNetDogs/images.tar

注释在 StanfordExtra 数据集中以单个 JSON 文件的形式提供,用户需要填写此表格才能访问该文件。

作者明确指示用户不要共享 JSON 文件,本示例也尊重这一愿望:您应自行获取 JSON 文件。

JSON 文件的本地版本为 stanfordextra_v12.zip。
下载文件后,我们可以解压缩。

!tar xf images.tar
!unzip -qq ~/stanfordextra_v12.zip

导入

from keras import layers
import kerasfrom imgaug.augmentables.kps import KeypointsOnImage
from imgaug.augmentables.kps import Keypoint
import imgaug.augmenters as iaafrom PIL import Image
from sklearn.model_selection import train_test_split
from matplotlib import pyplot as plt
import pandas as pd
import numpy as np
import json
import os

定义超参数

IMG_SIZE = 224
BATCH_SIZE = 64
EPOCHS = 5
NUM_KEYPOINTS = 24 * 2  # 24 pairs each having x and y coordinates

加载数据


作者还提供了一个元数据文件,其中指定了有关关键点的其他信息,如颜色信息、动物姿势名称等。我们将在 pandas 数据框中加载该文件,以提取信息用于可视化目的。

IMG_DIR = "Images"
JSON = "StanfordExtra_V12/StanfordExtra_v12.json"
KEYPOINT_DEF = ("https://github.com/benjiebob/StanfordExtra/raw/master/keypoint_definitions.csv"
)# Load the ground-truth annotations.
with open(JSON) as infile:json_data = json.load(infile)# Set up a dictionary, mapping all the ground-truth information
# with respect to the path of the image.
json_dict = {i["img_path"]: i for i in json_data}

json_dict 的单个条目如下所示:

'n02085782-Japanese_spaniel/n02085782_2886.jpg':
{'img_bbox': [205, 20, 116, 201],'img_height': 272,'img_path': 'n02085782-Japanese_spaniel/n02085782_2886.jpg','img_width': 350,'is_multiple_dogs': False,'joints': [[108.66666666666667, 252.0, 1],[147.66666666666666, 229.0, 1],[163.5, 208.5, 1],[0, 0, 0],[0, 0, 0],[0, 0, 0],[54.0, 244.0, 1],[77.33333333333333, 225.33333333333334, 1],[79.0, 196.5, 1],[0, 0, 0],[0, 0, 0],[0, 0, 0],[0, 0, 0],[0, 0, 0],[150.66666666666666, 86.66666666666667, 1],[88.66666666666667, 73.0, 1],[116.0, 106.33333333333333, 1],[109.0, 123.33333333333333, 1],[0, 0, 0],[0, 0, 0],[0, 0, 0],[0, 0, 0],[0, 0, 0],[0, 0, 0]],'seg': ...}

在这个例子中,我们感兴趣的键是:

  • img_path
  • joints

关节中总共有 24 个条目。

每个条目有 3 个值:


x 坐标
y 坐标
关键点的可见性标志(1 表示可见,0 表示不可见)


我们可以看到,关节点包含多个 [0, 0, 0] 条目,表示这些关键点没有被标记。

在本例中,我们将同时考虑非可见和未标记的关键点,以便进行迷你批量学习。

# Load the metdata definition file and preview it.
keypoint_def = pd.read_csv(KEYPOINT_DEF)
keypoint_def.head()# Extract the colours and labels.
colours = keypoint_def["Hex colour"].values.tolist()
colours = ["#" + colour for colour in colours]
labels = keypoint_def["Name"].values.tolist()# Utility for reading an image and for getting its annotations.
def get_dog(name):data = json_dict[name]img_data = plt.imread(os.path.join(IMG_DIR, data["img_path"]))# If the image is RGBA convert it to RGB.if img_data.shape[-1] == 4:img_data = img_data.astype(np.uint8)img_data = Image.fromarray(img_data)img_data = np.array(img_data.convert("RGB"))data["img_data"] = img_datareturn data

可视化数据


现在,我们编写一个实用程序来可视化图像及其关键点。

# Parts of this code come from here:
# https://github.com/benjiebob/StanfordExtra/blob/master/demo.ipynb
def visualize_keypoints(images, keypoints):fig, axes = plt.subplots(nrows=len(images), ncols=2, figsize=(16, 12))[ax.axis("off") for ax in np.ravel(axes)]for (ax_orig, ax_all), image, current_keypoint in zip(axes, images, keypoints):ax_orig.imshow(image)ax_all.imshow(image)# If the keypoints were formed by `imgaug` then the coordinates need# to be iterated differently.if isinstance(current_keypoint, KeypointsOnImage):for idx, kp in enumerate(current_keypoint.keypoints):ax_all.scatter([kp.x],[kp.y],c=colours[idx],marker="x",s=50,linewidths=5,)else:current_keypoint = np.array(current_keypoint)# Since the last entry is the visibility flag, we discard it.current_keypoint = current_keypoint[:, :2]for idx, (x, y) in enumerate(current_keypoint):ax_all.scatter([x], [y], c=colours[idx], marker="x", s=50, linewidths=5)plt.tight_layout(pad=2.0)plt.show()# Select four samples randomly for visualization.
samples = list(json_dict.keys())
num_samples = 4
selected_samples = np.random.choice(samples, num_samples, replace=False)images, keypoints = [], []for sample in selected_samples:data = get_dog(sample)image = data["img_data"]keypoint = data["joints"]images.append(image)keypoints.append(keypoint)visualize_keypoints(images, keypoints)

从图中可以看出,我们的图像大小并不均匀,这在现实世界的大多数情况下都是意料之中的。

但是,如果我们将这些图像的大小调整为统一形状(例如 (224 x 224)),它们的地面实况注释也会受到影响。

如果我们对图像进行任何几何变换(例如水平翻转),情况也是一样。幸运的是,imgaug 提供了可以处理这个问题的实用程序。

在后文中,我们将编写一个继承 keras.utils.Sequence 类的数据生成器,使用 imgaug 对成批数据进行数据增强。

准备数据生成器

class KeyPointsDataset(keras.utils.PyDataset):def __init__(self, image_keys, aug, batch_size=BATCH_SIZE, train=True, **kwargs):super().__init__(**kwargs)self.image_keys = image_keysself.aug = augself.batch_size = batch_sizeself.train = trainself.on_epoch_end()def __len__(self):return len(self.image_keys) // self.batch_sizedef on_epoch_end(self):self.indexes = np.arange(len(self.image_keys))if self.train:np.random.shuffle(self.indexes)def __getitem__(self, index):indexes = self.indexes[index * self.batch_size : (index + 1) * self.batch_size]image_keys_temp = [self.image_keys[k] for k in indexes](images, keypoints) = self.__data_generation(image_keys_temp)return (images, keypoints)def __data_generation(self, image_keys_temp):batch_images = np.empty((self.batch_size, IMG_SIZE, IMG_SIZE, 3), dtype="int")batch_keypoints = np.empty((self.batch_size, 1, 1, NUM_KEYPOINTS), dtype="float32")for i, key in enumerate(image_keys_temp):data = get_dog(key)current_keypoint = np.array(data["joints"])[:, :2]kps = []# To apply our data augmentation pipeline, we first need to# form Keypoint objects with the original coordinates.for j in range(0, len(current_keypoint)):kps.append(Keypoint(x=current_keypoint[j][0], y=current_keypoint[j][1]))# We then project the original image and its keypoint coordinates.current_image = data["img_data"]kps_obj = KeypointsOnImage(kps, shape=current_image.shape)# Apply the augmentation pipeline.(new_image, new_kps_obj) = self.aug(image=current_image, keypoints=kps_obj)batch_images[i,] = new_image# Parse the coordinates from the new keypoint object.kp_temp = []for keypoint in new_kps_obj:kp_temp.append(np.nan_to_num(keypoint.x))kp_temp.append(np.nan_to_num(keypoint.y))# More on why this reshaping later.batch_keypoints[i,] = np.array(kp_temp).reshape(1, 1, 24 * 2)# Scale the coordinates to [0, 1] range.batch_keypoints = batch_keypoints / IMG_SIZEreturn (batch_images, batch_keypoints)

要进一步了解如何在 imgaug 中使用关键点,请查看本文。

定义增强变换

train_aug = iaa.Sequential([iaa.Resize(IMG_SIZE, interpolation="linear"),iaa.Fliplr(0.3),# `Sometimes()` applies a function randomly to the inputs with# a given probability (0.3, in this case).iaa.Sometimes(0.3, iaa.Affine(rotate=10, scale=(0.5, 0.7))),]
)test_aug = iaa.Sequential([iaa.Resize(IMG_SIZE, interpolation="linear")])

创建训练和验证分割

np.random.shuffle(samples)
train_keys, validation_keys = (samples[int(len(samples) * 0.15) :],samples[: int(len(samples) * 0.15)],
)

数据生成器调查

train_dataset = KeyPointsDataset(train_keys, train_aug, workers=2, use_multiprocessing=True
)
validation_dataset = KeyPointsDataset(validation_keys, test_aug, train=False, workers=2, use_multiprocessing=True
)print(f"Total batches in training set: {len(train_dataset)}")
print(f"Total batches in validation set: {len(validation_dataset)}")sample_images, sample_keypoints = next(iter(train_dataset))
assert sample_keypoints.max() == 1.0
assert sample_keypoints.min() == 0.0sample_keypoints = sample_keypoints[:4].reshape(-1, 24, 2) * IMG_SIZE
visualize_keypoints(sample_images[:4], sample_keypoints)

演绎展示:
 

Total batches in training set: 166
Total batches in validation set: 29

模型构建


Stanford dogs 数据集(StanfordExtra 数据集基于该数据集)是使用 ImageNet-1k 数据集构建的。因此,在 ImageNet-1k 数据集上预训练的模型很可能对这项任务有用。我们将使用在该数据集上预先训练好的 MobileNetV2 作为骨干,从图像中提取有意义的特征,然后将这些特征传递给自定义回归头用于预测坐标。

def get_model():# Load the pre-trained weights of MobileNetV2 and freeze the weightsbackbone = keras.applications.MobileNetV2(weights="imagenet",include_top=False,input_shape=(IMG_SIZE, IMG_SIZE, 3),)backbone.trainable = Falseinputs = layers.Input((IMG_SIZE, IMG_SIZE, 3))x = keras.applications.mobilenet_v2.preprocess_input(inputs)x = backbone(x)x = layers.Dropout(0.3)(x)x = layers.SeparableConv2D(NUM_KEYPOINTS, kernel_size=5, strides=1, activation="relu")(x)outputs = layers.SeparableConv2D(NUM_KEYPOINTS, kernel_size=3, strides=1, activation="sigmoid")(x)return keras.Model(inputs, outputs, name="keypoint_detector")

我们定制的网络是全卷积网络,因此与具有全连接密集层的相同版本网络相比,它对参数更加友好。

get_model().summary()

演绎展示:

Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet_v2/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_224_no_top.h59406464/9406464 ━━━━━━━━━━━━━━━━━━━━ 0s 0us/step

请注意网络的输出形状:(None, 1, 1, 48)。

这就是我们重塑坐标的原因:batch_keypoints[i, :] = np.array(kp_temp).reshape(1, 1, 24 * 2)

模型编译和训练


在本例中,我们只对网络进行 5 个历元的训练。

model = get_model()
model.compile(loss="mse", optimizer=keras.optimizers.Adam(1e-4))
model.fit(train_dataset, validation_data=validation_dataset, epochs=EPOCHS)
Epoch 1/5166/166 ━━━━━━━━━━━━━━━━━━━━ 84s 415ms/step - loss: 0.1110 - val_loss: 0.0959
Epoch 2/5166/166 ━━━━━━━━━━━━━━━━━━━━ 79s 472ms/step - loss: 0.0874 - val_loss: 0.0802
Epoch 3/5166/166 ━━━━━━━━━━━━━━━━━━━━ 78s 463ms/step - loss: 0.0789 - val_loss: 0.0765
Epoch 4/5166/166 ━━━━━━━━━━━━━━━━━━━━ 78s 467ms/step - loss: 0.0769 - val_loss: 0.0731
Epoch 5/5166/166 ━━━━━━━━━━━━━━━━━━━━ 77s 464ms/step - loss: 0.0753 - val_loss: 0.0712<keras.src.callbacks.history.History at 0x7fb5c4299ae0>

进行预测并将其可视化

sample_val_images, sample_val_keypoints = next(iter(validation_dataset))
sample_val_images = sample_val_images[:4]
sample_val_keypoints = sample_val_keypoints[:4].reshape(-1, 24, 2) * IMG_SIZE
predictions = model.predict(sample_val_images).reshape(-1, 24, 2) * IMG_SIZE# Ground-truth
visualize_keypoints(sample_val_images, sample_val_keypoints)# Predictions
visualize_keypoints(sample_val_images, predictions)

演绎展示:

 1/1 ━━━━━━━━━━━━━━━━━━━━ 7s 7s/step

通过更多的训练,预测结果可能会有所改善。

更进一步


尝试使用 imgaug 的其他增强变换,研究其对结果的影响。

在这里,我们从预训练网络中线性转移了特征,也就是说,我们没有对其进行微调。

我们鼓励你在这项任务中对其进行微调,看看是否能提高性能。您还可以尝试不同的架构,看看它们对最终性能有什么影响。


这篇关于政安晨:【Keras机器学习示例演绎】(四)—— 利用迁移学习进行关键点检测的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/920084

相关文章

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1

利用python实现对excel文件进行加密

《利用python实现对excel文件进行加密》由于文件内容的私密性,需要对Excel文件进行加密,保护文件以免给第三方看到,本文将以Python语言为例,和大家讲讲如何对Excel文件进行加密,感兴... 目录前言方法一:使用pywin32库(仅限Windows)方法二:使用msoffcrypto-too

pandas中位数填充空值的实现示例

《pandas中位数填充空值的实现示例》中位数填充是一种简单而有效的方法,用于填充数据集中缺失的值,本文就来介绍一下pandas中位数填充空值的实现,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是中位数填充?为什么选择中位数填充?示例数据结果分析完整代码总结在数据分析和机器学习过程中,处理缺失数

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

Pandas使用AdaBoost进行分类的实现

《Pandas使用AdaBoost进行分类的实现》Pandas和AdaBoost分类算法,可以高效地进行数据预处理和分类任务,本文主要介绍了Pandas使用AdaBoost进行分类的实现,具有一定的参... 目录什么是 AdaBoost?使用 AdaBoost 的步骤安装必要的库步骤一:数据准备步骤二:模型

Pandas统计每行数据中的空值的方法示例

《Pandas统计每行数据中的空值的方法示例》处理缺失数据(NaN值)是一个非常常见的问题,本文主要介绍了Pandas统计每行数据中的空值的方法示例,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是空值?为什么要统计空值?准备工作创建示例数据统计每行空值数量进一步分析www.chinasem.cn处

使用Pandas进行均值填充的实现

《使用Pandas进行均值填充的实现》缺失数据(NaN值)是一个常见的问题,我们可以通过多种方法来处理缺失数据,其中一种常用的方法是均值填充,本文主要介绍了使用Pandas进行均值填充的实现,感兴趣的... 目录什么是均值填充?为什么选择均值填充?均值填充的步骤实际代码示例总结在数据分析和处理过程中,缺失数

利用Python调试串口的示例代码

《利用Python调试串口的示例代码》在嵌入式开发、物联网设备调试过程中,串口通信是最基础的调试手段本文将带你用Python+ttkbootstrap打造一款高颜值、多功能的串口调试助手,需要的可以了... 目录概述:为什么需要专业的串口调试工具项目架构设计1.1 技术栈选型1.2 关键类说明1.3 线程模

Python使用getopt处理命令行参数示例解析(最佳实践)

《Python使用getopt处理命令行参数示例解析(最佳实践)》getopt模块是Python标准库中一个简单但强大的命令行参数处理工具,它特别适合那些需要快速实现基本命令行参数解析的场景,或者需要... 目录为什么需要处理命令行参数?getopt模块基础实际应用示例与其他参数处理方式的比较常见问http

Android实现在线预览office文档的示例详解

《Android实现在线预览office文档的示例详解》在移动端展示在线Office文档(如Word、Excel、PPT)是一项常见需求,这篇文章为大家重点介绍了两种方案的实现方法,希望对大家有一定的... 目录一、项目概述二、相关技术知识三、实现思路3.1 方案一:WebView + Office Onl