Press "Enter" to skip to content

解码着装规范 👗:深度学习用于自动化时尚物品检测

由Tamara Bellis在Unsplash上拍摄的原始照片,由作者处理。

在充满活力的电子商务世界中,时尚行业就像一条自己的T台。但如果我们能够用深度学习(DL)的精确性来解码这个T台的着装规范,而不是用设计师的眼光呢?今天,我们将揭开这个引人入胜的可能性的面纱。我们将使用一个预训练模型和PyTorch Lightning¹来自动化产品标记的过程,将耗时的任务转变为一个迅捷高效的过程。这不仅是深度学习在时尚界的幕后通行证,也是电子商务运营革命中的前排座位。所以,如果你准备好看深度学习如何解码数字时尚世界的着装规范,那就准备好吧!

本博客分为两个关键部分:“微调预训练模型”和“推理”。 “微调预训练模型”部分从下一节开始,直到“使用Pytorch Lightning进行微调”部分,我将详细演示如何调整预训练模型以更好地适应我们特定的时尚物品检测需求。与此同时,“推理”部分专门为那些希望立即利用该模型进行多种时尚物品检测的人准备。如果你渴望看到模型的实际效果,可以直接跳到推理部分。

设置舞台

为了开始我们的时装秀,我们必须通过配置Python环境来准备舞台,使用PyTorch Lightning¹ – 一个简化代码管理和加速模型训练的PyTorch封装器。此外,我们还将引入以下用于训练和推理的必要软件包:

# 安装软件包pip install torch==2.0.0pip install pytorch-lightning==2.0.1pip install datasets==2.11.0pip install transformers==4.30.1pip install huggingface_hub==0.14.1

接下来,让我们导入以下模块:

# 导入用于数据处理和可视化的必要库import randomimport numpy as npimport matplotlib.pyplot as pltimport shutilimport globimport os# 图像处理库from PIL import Image, ImageDraw# 从Hugging Face模型中心下载文件的工具from huggingface_hub import hf_hub_download# 将数据拆分为训练集和测试集的函数from sklearn.model_selection import train_test_split# 处理数据集的库import datasetsfrom datasets import load_dataset, concatenate_datasets, DatasetDict# 高性能训练的PyTorch Lightning库import lightning as plfrom lightning import Trainer# 用于构建和训练神经网络的PyTorch库import torchimport torch.nn as nn# 创建数据集迭代器的DataLoader库from torch.utils.data import DataLoader# 图像转换库from torchvision import transformsfrom torchvision.transforms import ToPILImage, ToTensor# 处理模型的Transformers库from transformers import AutoModelForObjectDetectionfrom transformers import YolosFeatureExtractor, YolosForObjectDetection

加载数据

本项目的数据集是托管在Hugging Face上的Fashionpedia数据集²,其中包含了超过46,000张带有多个时尚物品边界框注释的图像。该数据集是Fashionpedia³的一个子集,Fashionpedia是一个大规模的时尚数据集,其中每个图像都带有分割掩模和细粒度的时尚属性。这是一个丰富的数据集,非常适合我们的时尚物品检测任务。

为了使用数据集,让我们使用Hugging Face数据集库加载它。对于这个项目,我只使用了数据集的30%来演示如何为时尚物品检测任务微调现有的预训练模型:

# 加载30%的训练数据集dataset = load_dataset("detection-datasets/fashionpedia", split='train[:30%]')# 生成拆分索引train_val_test_split = dataset.train_test_split(test_size=0.2, seed=42)  # 80%用于训练,20%用于验证和测试val_test_split = train_val_test_split['test'].train_test_split(test_size=0.5, seed=42)  # 将20%平均分成验证和测试两部分# 将拆分结果存储到DatasetDictfinal_splits = DatasetDict({    'train': train_val_test_split['train'],    'val': val_test_split['train'],    'test': val_test_split['test']})# 将DatasetDict拆分为训练集、验证集和测试集train_dataset, val_dataset, test_dataset = final_splits['train'], final_splits['val'], final_splits['test']

我们的训练、验证和测试数据集应该是这样的:

(数据集({     特征: ['图像ID', '图像', '宽度', '高度', '对象'],     行数: 10949 }), 数据集({     特征: ['图像ID', '图像', '宽度', '高度', '对象'],     行数: 1369 }), 数据集({     特征: ['图像ID', '图像', '宽度', '高度', '对象'],     行数: 1369 }))

此外,让我们显示一个图像的详细信息:

test_dataset[0]

{'图像ID': 14991, '图像': <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=676x1024>, '宽度': 676, '高度': 1024, '对象': {'边界框ID': [117814, 117815, 117816, 117817, 117818],  '类别': [23, 23, 6, 1, 14],  '边界框': [[174.0, 862.0, 299.0, 969.0],   [309.0, 634.0, 420.0, 759.0],   [127.0, 530.0, 380.0, 903.0],   [226.0, 221.0, 420.0, 610.0],   [254.0, 97.0, 395.0, 199.0]],  '面积': [6797, 7354, 35017, 30388, 6175]}}

数据集的内容包括以下字段,如其数据集卡片²所述:

{'图像ID': 值(类型='int64'),'图像': 图像(解码=True),'宽度': 值(类型='int64'),'高度': 值(类型='int64'),'对象': 序列(特征={  '边界框ID': 值(类型='int64'),   '类别': 类别标签(类别数=46, 名称=['衬衫', '上衣,T恤,运动衫', '毛衣', '开襟羊毛衫', '夹克', '马甲', '裤子', '短裤', '裙子', '外套', '连衣裙', '连体衣', '披肩', '眼镜', '帽子', '头带,头饰,发饰', '领带', '手套', '手表', '腰带', '腿套', '紧身衣, 长袜', '袜子', '鞋子', '包包, 钱包', '围巾', '雨伞', '风帽', '衣领', '翻领', '肩章', '袖子', '口袋', '颈线', '扣子', '拉链', '装饰', '珠子', '蝴蝶结', '花朵', '流苏', '丝带', '铆钉', '褶边', '亮片', '流苏']),   '边界框': 序列(特征=值(类型='float64'), 长度=4),   '面积': 值(类型='int64')}, 长度=-1)}

数据字段

数据集具有以下字段²:

  • 图像ID:为每个图像分配的唯一数字ID。
  • 图像:表示图像的PIL.Image.Image类的实例。
  • 宽度:图像的宽度。
  • 高度:图像的高度。
  • 对象:包含图像中存在的对象的边界框相关的元数据的字典。
  • 边界框ID:为每个边界框注释分配的唯一数字ID。
  • 类别:对象的类别。
  • 面积:边界框覆盖的面积。
  • 边界框:对象的边界框在Pascal VOC格式中的坐标。

预训练模型和迁移学习

如前所述,在本博客中,我们将利用预训练模型的力量。这些在大型数据集上训练的模型已经学习了许多特征和模式。通过使用这些模型,我们可以利用它们的学习,并将其应用于我们的特定任务。当我们的任务的数据量有限或者我们想节省时间和计算资源时,这是特别有用的。

在我们的案例中,我们将使用预训练在YOLO-Small⁵架构上的YOLOs-Fashionpedia ⁴模型。该模型已经学会了检测各种时尚物品,使其成为我们任务的完美起点。

辅助函数

在执行此项目的过程中,我在Valentina Feruere Vega的GitHub仓库⁶中发现了一个重要资源。该仓库提供了关于Fine-tuning Fashionpedia数据集的详细指南,这是本项目的重要步骤。为了简化数据集的处理并使其与YOLO-Small模型兼容,我编写了一组辅助函数。这些函数专门设计用于调整Fashionpedia数据集中的边界框(bbox)格式(x1、x2、y1、y2)与YOLO-Small所需格式(x_center、y_center、width、height)之间的差异:

def xyxy_to_xcycwh(box):    x1, y1, x2, y2 = box.unbind(dim=1)    width = x2-x1    height = y2-y1    xc = x1 + width*0.5    yc = y1 + height*0.5    b = [xc, yc, width, height]    return torch.stack(b, dim=1)def cxcywh_to_xyxy(x):    x_c, y_c, w, h = x.unbind(1)    x1 = x_c - 0.5 * w    y1 = y_c - 0.5 * h    x2 = x_c + 0.5 * w    y2 = y_c + 0.5 * h    b = [x1, y1, x2, y2]    return torch.stack(b, dim=1)

为了对数据集中的图像进行预处理,这些图像以PILImage对象的形式存储,我们将使用YOLOS Feature Extractor。该特征提取器将图像转换为由数值值组成的张量格式。这个预处理步骤对于在YOLOS框架内进一步分析和处理图像是必要的。

# 加载我们的特征提取器:yolos-smallfeature_extractor = YolosFeatureExtractor.from_pretrained('hustvl/yolos-small', size=816, max_size=864)

现在,我们可以应用以下转换使其与模型兼容:

def rescale_bboxes(out_bbox, size, down=True):    img_w, img_h = size    if down:        b = torch.Tensor(out_bbox) / torch.tensor([img_w, img_h, img_w, img_h], dtype=torch.float32)    if not down:        b = torch.Tensor(out_bbox) * torch.tensor([img_w, img_h, img_w, img_h], dtype=torch.float32)    return bdef transform(batch):    inputs = {}    inputs['pixel_values'] = feature_extractor(batch['image'], return_tensors='pt')['pixel_values']    labels = []    bbox = [rescale_bboxes(batch['objects'][i]['bbox'], (batch['width'][i], batch['height'][i])) for i in range(len(batch['objects']))]    bbox = [xyxy_to_xcycwh(torch.Tensor(bbox_i)) for bbox_i in bbox]    labels.append({        "boxes": bbox,        "class_labels": [object['category'] for object in batch['objects']],        "image_id": torch.Tensor([batch['image_id']]).int(),        "area": [object['area'] for object in batch['objects']],        "iscrowd": torch.Tensor([0 for _ in batch['objects']]).int(),        "orig_size": torch.Tensor([(batch['width'], batch['height'])]).int(),        "size": torch.Tensor([inputs['pixel_values'].shape[1:]])[0].int(),    })    inputs['labels'] = labels    return inputs

# 应用转换使其与模型兼容prepared_train = train_dataset.with_transform(transform)prepared_val = val_dataset.with_transform(transform)prepared_test = test_dataset.with_transform(transform)

为了简化批处理的训练过程,需要一个整合器函数。该函数接收一个输入列表,通常在这个上下文中是字典,并将它们转换为一个数据堆叠的单个输入。换句话说,一个字典列表将转换为一个字典,其中每个键的值变成一个向量。这个整合器函数在将数据组织成适合批处理的结构方面起着关键作用,可以高效地训练模型。

def collate_fn(batch):    collated = {}    collated["pixel_values"] = feature_extractor.pad([item['pixel_values'] for item in batch], return_tensors="pt")['pixel_values']    collated["labels"] = []    for item in batch:        item['labels']['boxes'] = torch.stack(item['labels']['boxes'])[0]        item['labels']['area'] = torch.Tensor(item['labels']['area'])        item['labels']['class_labels'] = torch.Tensor(item['labels']['class_labels'])[0]        item['labels']['class_labels'] = item['labels']['class_labels'].type(torch.LongTensor)        collated["labels"].append(item['labels'])    return collated

# 以下内容将用于将批次加载到模型中BATCH_SIZE = 1train_dataloader = DataLoader(prepared_train, collate_fn=collate_fn, batch_size=BATCH_SIZE)val_dataloader = DataLoader(prepared_val, collate_fn=collate_fn, batch_size=BATCH_SIZE)test_dataloader = DataLoader(prepared_test, collate_fn=collate_fn, batch_size=BATCH_SIZE)

使用PyTorch Lightning进行微调

舞台已经布置好,演员们已经准备就绪,现在是彩排的时间。

微调就像是模型的彩排。它涉及使用PyTorch Lightning¹在我们的特定任务上训练模型,让它从图像中的时尚物品中进行学习。这样,当真正的表演时间到来时,我们的模型就能准确地识别和分类时尚物品。

为了做到这一点,我们将使用PyTorch Lightning¹,它是PyTorch的一个封装,简化了训练过程。通过PyTorch Lightning¹,我们可以轻松地定义训练循环,配置优化器和设置学习率调度器。它还提供了内置支持,可以记录指标、保存检查点等等。

让我们微调一个已经在时尚物品检测任务上进行了微调的现有模型YOLOs-Fashionpedia⁴。以下是微调过程的简化版本:

class Yolos(pl.LightningModule):        def __init__(self, lr, weight_decay):        super().__init__()        self.model = AutoModelForObjectDetection.from_pretrained("valentinafeve/yolos-fashionpedia",                                                                  num_labels=46,                                                                 ignore_mismatched_sizes=True)        self.lr = lr        self.weight_decay = weight_decay    def forward(self, pixel_values):        outputs = self.model(pixel_values=pixel_values)        return outputs        def common_step(self, batch, batch_idx):        pixel_values = batch["pixel_values"]        labels = [{k: v.to(self.device) for k, v in t.items()} for t in batch["labels"]]        outputs = self.model(pixel_values=pixel_values, labels=labels)        loss = outputs.loss        loss_dict = outputs.loss_dict        return loss, loss_dict    def training_step(self, batch, batch_idx):        loss, loss_dict = self.common_step(batch, batch_idx)             self.log("training_loss", loss)        for k,v in loss_dict.items():            self.log("train_" + k, v.item())                return loss    def validation_step(self, batch, batch_idx):        loss, loss_dict = self.common_step(batch, batch_idx)             self.log("validation_loss", loss)        for k,v in loss_dict.items():            self.log("validation_" + k, v.item())                return loss    def test_step(self, batch, batch_idx):        loss, loss_dict = self.common_step(batch, batch_idx)             self.log("test_loss", loss)        for k,v in loss_dict.items():            self.log("test_" + k, v.item())                return loss    def configure_optimizers(self):        optimizer = torch.optim.AdamW(self.parameters(), lr=self.lr,                                  weight_decay=self.weight_decay)                return optimizer        def train_dataloader(self):        return train_dataloader        def val_dataloader(self):        return val_dataloader        def test_dataloader(self):        return test_dataloader

# 为了保存和以后查看日志  logger = CSVLogger("logs", name="detr_logs")    

在使用迁移学习进行微调时,像YOLO-Small这样的预训练模型会使用较低的学习率和权重衰减,以保留模型已经学到的有用特征,并防止在新任务上过拟合。较低的学习率确保在训练过程中权重更新是渐进的,不会破坏先前学到的模式,从而使模型能够微调地适应新任务。而权重衰减则有助于保持较小的权重,通过确保模型不过于依赖任何特定特征并保持泛化,从而防止过拟合。这些参数在充分利用预训练模型的能力和有效地将其适应新任务之间取得了平衡。

# 定义模型并指定学习率和权重衰减model = Yolos(lr=2.5e-5, weight_decay=1e-4)

为了说明目的,我最多训练了3个训练步骤。

# 使用PyTorch Lightning进行训练trainer = pl.Trainer(max_epochs=3, logger=logger, accelerator="cuda", devices=1)trainer.fit(model, train_dataloader, val_dataloader)

训练模型后,您现在可以保存它并用于推理。

# 保存模型trainer.save_checkpoint('./model/fashion_model.ckpt')

类别分组

一旦模型调优完成,就是展示时间!给定一个模拟时尚物品的人的图像,模型将会检测并将物品分类为46个不同的类别。

然而,直接输出原始类别标签可能不是最用户友好的结果展示方式。因此,我决定将这些类别分组为更宽泛的类别,比如“上装和外套”,“下装”和“鞋类”。例如,类别“衬衫,上衣”和“上装,T恤,运动衫”都属于“上装和外套”类别。

以下是创建分组映射的Python代码:

# 根据类型对类别进行分组
group_tops_outerwear = ['衬衫,上衣', '上装,T恤,运动衫', '毛衣', '开襟毛衣', '夹克', '背心', '外套', '斗篷', '连衣裙', '连身裤']
group_bottoms = ['裤子', '短裤', '裙子']
group_footwear = ['鞋子', '袜子']
group_accessories = ['眼镜', '帽子', '头带,头巾,发饰', '领带', '手套', '手表', '腰带', '腿套', '紧身裤,长筒袜', '包,钱包', '围巾', '雨伞']
group_clothing_details = ['兜帽', '领口', '翻领', '肩章', '袖子', '口袋', '颈线', '扣子', '拉链']
group_embellishments = ['绣花', '珠子', '蝴蝶结', '花朵', '流苏', '丝带', '铆钉', '荷叶边', '亮片', '流苏']
group_mapping = {}
for category in group_tops_outerwear:
    group_mapping[category] = '上装和外套'
for category in group_bottoms:
    group_mapping[category] = '下装'
for category in group_footwear:
    group_mapping[category] = '鞋类'
for category in group_accessories:
    group_mapping[category] = '配饰'
for category in group_clothing_details:
    group_mapping[category] = '服装细节'
for category in group_embellishments:
    group_mapping[category] = '装饰物'

通过这些映射,我们可以将模型的原始输出转换为更易理解的类别。用户可以输入一个模拟时尚物品的人的图像,模型将输出检测到的物品,并将其分组为“上装和外套”、“下装”、“鞋类”、“配饰”、“服装细节”和“装饰物”等类别。

以下是输出结果的预览:

上装和外套:上衣/T恤/运动衫
服装细节:口袋,颈线
下装:裤子
装饰物:铆钉
鞋类:鞋子

我还为这些类别添加了颜色编码功能,使结果更加视觉上吸引人和易于理解。

color_mapping = {
    '上装和外套': '#FFC1E0',       // 浅粉红色
    '下装': '#A7F7C0',                   // 浅绿色
    '鞋类': '#E1BEE7',                  // 浅紫色
    '配饰': '#FFD8B1',                // 浅橙色
    '服装细节': '#B3E5FC',           // 浅蓝色
    '装饰物': '#FFF9C4'              // 浅黄色
}

推理

要使用模型进行推理,特别是用于时尚物品检测和自动产品标记,只需执行以下步骤:

# 从YOLO-small模型加载预训练的特征提取器
feature_extractor = YolosFeatureExtractor.from_pretrained('hustvl/yolos-small')
# 从检查点加载模型,设置学习率和权重衰减
model_tags = Yolos.load_from_checkpoint(
    checkpoint_path='./model/fashion_model.ckpt',  // 检查点路径
    lr=2.5e-5,  // 微调的学习率
    weight_decay=1e-4  // 正则化的权重衰减
)

只需执行以下辅助函数进行推理。这只是我在本博客中提到的函数和任务的总结:

# 用于输出边界框后处理的函数
def box_cxcywh_to_xyxy(x):
    x_c, y_c, w, h = x.unbind(1)
    b = [(x_c - 0.5 * w), (y_c - 0.5 * h),
         (x_c + 0.5 * w), (y_c + 0.5 * h)]
    return torch.stack(b, dim=1)

def rescale_bboxes(out_bbox, size):
    img_w, img_h = size
    b = box_cxcywh_to_xyxy(out_bbox)
    b = b * torch.tensor([img_w, img_h, img_w, img_h

展示时间!

让我们来处理一张图片。为此,请使用process_images()函数。

  • image_path:接受一个图片路径列表,您可以将图片路径字符串放在列表中。例如:["images/0.jpg"]。
  • threshold:默认为70%或0.7。它用于仅保留最大置信度大于阈值的预测。
  • show_image:默认为True。这是控制是否要显示带有检测到的时尚物品的边界框的图片(True),还是不显示(False)。

就这样!让我们在一张单独的图片上试试:

# 单张图片,show_image=TrueIMAGE_PATH = ["images/sample_01.jpg"]process_images(IMAGE_PATH, threshold=0.8, show_image=True);
输出1:检测到并打印的单张图片中的时尚物品。原始照片由Napat Saeng在Unsplash上提供。

如果您想将其用于自动化的时尚物品标记并仅获取打印结果,您可以输入 show_image=False

# 单张图片,show_image=FalseIMAGE_PATH = ["images/sample_01.jpg"]process_images(IMAGE_PATH, threshold=0.8, show_image=False);

输出2:images/sample_01.jpg配饰:手表、眼镜服装细节:口袋、袖子上衣和外套:上衣/衬衫/运动衫裤子

最后,如果您想用于多张图片,您可以在 image_paths 参数中指定多个图片路径,或使用以下 process_images_in_directory() 函数,并将其提供给您的图片目录路径。

选项1:多个图片路径

# 多张图片,show_image=TrueIMAGE_PATH = ["images/sample_01.jpg", "images/sample_02.jpg", "images/sample_03.jpg", "images/sample_04.jpg"]process_images(IMAGE_PATH, threshold=0.8, show_image=False);

选项2:使用您的图片目录路径

def process_images_in_directory(directory='./images', threshold=0.70, show_image=True):    # 定义您感兴趣的图片文件的扩展名    image_extensions = ['jpg', 'png', 'jpeg', 'JPG', 'PNG']    # 使用列表推导式为每个扩展名创建一个文件列表,然后将这些列表合并    image_files = [glob.glob(f'{directory}/*.{ext}') for ext in image_extensions]    image_paths = [item for sublist in image_files for item in sublist]  # 展平列表    return process_images(image_paths, threshold=threshold, show_image=show_image)process_images_in_directory(directory='./images', threshold=0.8, show_image=True)

这两个选项都会输出以下内容:

输出#3-1:检测到并打印的第一张图片。原始照片由Javier Contreras在Unsplash上提供。
输出#3–2:检测到并打印的第二张图片。原始照片由Khaled Ghareeb在Unsplash上提供。
输出结果#3–3:检测到并打印的第三张包含时尚物品的图片。原始照片由Tamara Bellis在Unsplash上拍摄
输出结果#3–4:检测到并打印的最后一张包含时尚物品的图片。原始照片由Napat Saeng在Unsplash上拍摄

总结

这个项目在电子商务领域的潜在影响非常大。通过自动化时尚物品检测,我们可以加快上传新产品的过程,提高运营效率。这不仅仅是识别时尚物品,更是通过提供准确的产品描述和促进高效的产品搜索来改善客户体验。

此外,这项技术还可以扩展到其他应用,如库存管理、趋势分析和个性化购物体验。这是关于理解时尚语言并将其运用到我们的优势。

结论

在这个探索中,我们解码了时尚行业的着装规范。我们利用DL的精确性、预训练模型和PyTorch Lightning自动化进行时尚物品检测的过程,展示了我们如何利用深度学习来进行自动化产品标记。这可以将耗时的任务转变为迅速高效的过程。

通过解码着装规范,我们在电子商务领域打开了无限的可能性。这只是一个开始。随着我们继续改进这些技术,我们可以期待更多创新和高效的解决方案。

注意事项

本博文旨在教育目的,旨在提供使用预训练模型进行时尚行业对象检测过程的高级概述。实际实施可能需要额外的步骤和考虑,具体取决于项目的特定要求。但鉴于自动化任务的好处,这是一段令人兴奋的旅程,在我毕业后我肯定会继续进行。

您可以通过我的GitHub存储库访问我的文件:https://github.com/erikaglacson/Multiple_Object_Detection.git。

参考文献

  • [1] PyTorch Lightning. (n.d.). 检索自https://www.pytorchlightning.ai/
  • [2] Blin, J. (n.d.). Fashionpedia. Hugging Face. 2023年6月27日检索自https://huggingface.co/datasets/detection-datasets/fashionpedia
  • [3] Jia, M., Shi, M., Sirotenko, M., Cui, Y., Cardie, C., Hariharan, B., Adam, H., & Belongie, S. (2020). Fashionpedia: Ontology, Segmentation, and an Attribute Localization Dataset. In Proceedings of the European Conference on Computer Vision (ECCV).
  • [4] Feruere Vega, V. (n.d.). YOLOS-fashionpedia. Hugging Face. 2023年6月27日检索自https://huggingface.co/valentinafeve/yolos-fashionpedia
  • [5] Wang, X. (n.d.). YOLOS-small. Hugging Face. 2023年6月27日检索自https://huggingface.co/hustvl/yolos-small
  • [6] Feruere Vega, V. (2023, June 13). Fine tunning YOLOs for fashion [源代码]. GitHub. https://github.com/valentinafeve/fine_tunning_YOLOS_for_fashion
Leave a Reply

Your email address will not be published. Required fields are marked *