秦鹏飞(断情人)
前言
声音、文字、图片都是信息的载体,其中图片传递的信息更直观、更丰富。人们对于图片信息也更乐于接受,有研究表明人类通过视觉接收超过80%的信息。合理的利用图片,可以从中获取对我们有用的信息,以辅助我们做出决策,同时,我们也可以将这些信息再传递给他人。海拍客作为全国最大的母婴B2B2C电商平台,涉及20多万家母婴门店以及10多万商品,其中也包含了大量的图片,这些图片有各个商品的图片、门店内部陈列照片等。为了充分发挥这些图片的价值,我们对图像算法加以实践。
个性化推荐之相似主图召回
在个性化推荐系统里,第一个环节一般是召回阶段,通过召回环节,将给用户推荐的物品降到千以下规模;再往后是精排阶段,这里可以使用复杂的模型来对少量物品精准排序。但是召回商品是否和用户匹配,决定了推荐系统的上限。对于召回阶段,工业界目前常规的做法是多路召回,每一路召回采取不同的策略,如下图1所示。
图1、多路召回示意图
然而,在推荐领域更多的是利用用户的行为信息来捕获用户的习惯,然后为其推荐可能感兴趣的商品。利用商品图像之间的相似性为其推荐相似的商品,则可以很好的利用商品本身的信息,更加具有发散性。比如:某用户点击了水杯这个商品,我们不知道其具体喜欢的是哪一款水杯,但是和点击水杯长得相似的水杯很大可能会引起他的兴趣,用户可能会进入商品详情页对细节、价格、规格等进行比较,从而提升点击率和转化率。
计算主图相似度
获取相似主图商品的整体流程如下图2所示,首先,获取平台所有商品的主图(商品表中存有其主图链接);然后,通过图像分类领域预训练的一些深度网络(如:ResNet系列、DenseNet系列、squeeze网络)进行特征提取(参考附录中对ResNet的介绍);最后,利用faiss库(Facebook AI团队开源的针对聚类和相似性搜索库,支持十亿级别向量的搜索,是目前较成熟的近似近邻搜索库)检索出跟每个商品主图相似的商品。
图2、获取相似主图商品的流程图
faiss库生成的结果图如下3所示,第一列表示原始商品Id,第二列表示相似商品Id,第三列表示两个商品主图之间的距离(即向量内积)。可以看到,对于每个商品主图,与其最相似的是其自身,距离为0。图4展示了部分实验结果,针对item_id为254248的商品,右侧列出了相似的item_id的主图。当该用户对此勺子感兴趣时,也给他展示其他类型的勺子、叉子、辅助喂养勺等。
图3、faiss检索后的结果
图4、相似主图结果(图片名称是其item_id)
但是,这里会有两个问题:在全量商品中,主图相似的商品差别可能很大(例如类目差异很大,没有关联性),失去了推荐的意义;召回最终的结果应该是分数和排名而不是上述的距离。针对第一个问题,最简单的做法就是根据商品类目进行过滤,目前使用的是二级类目,后续可以根据AB实验结果进行调整。针对第二个问题,是否可以在每个商品纬度直接使用距离的最大值减去每一个距离,用这个值来表示分数然后排名呢?在i2i(以商品推商品)场景是可以的,在s2i(以门店推商品)场景则显然不可以。举个极端的例子,某个商品相似的商品很多,距离都是接近于0的值,而另一个商品相似品很少,除了自身以外最近的距离都是几十或者几百的值,不管每个门店对商品兴趣程度如何与前者分数相乘之后就会变得很小,后者则会得到一个很大的分数,根据这个分数对门店感兴趣的商品排名当然是不合理的。这里,采用的步骤如下:
(1)根据二级类目进行商品过滤; (2)排除商品自身; (3)按照每个原始商品的维度,对其相似商品的距离进行最大最小归一化; (4)用1与归一化后的值求差。 |
---|
步骤(3)和(4)可以用下式(1)表示:
(1)
通过上述操作之后,就可以在商品详情页等i2i场景进行相似主图召回了。当然,基于主图的召回也可以在以店推品的场景使用,只需要先得到门店已经有交互行为的商品,然后类似于item-based协同过滤方法,推荐这些商品的主图相似商品即可。
基于主图相似度推荐商品
如何获取门店可能感兴趣的商品呢?这里,主要是根据门店的历史行为计算门店对商品的喜好打分。将门店与商品的行为分为四种类型:曝光、点击、加购、下单,通过统计过去14天的行为,得到门店对商品的评分。不同的行为赋予不用的权重,上述四种类型分别对应1、3、5、7,为了对热门商品进行惩罚,使用TF-IDF(term frequency–inverse document frequency,一种用于信息检索与数据挖掘的常用加权技术)计算出每个商品的权重。最后,将权重与最初的得分相乘得到门店对商品的兴趣程度。具体步骤如下:
- //计算门店商品得分,曝光:1,点击:3,加购:5,下单:7
- with behavior as (
- select shop, item ,1 as score
- from 曝光表
- unioin all
- select shop, item ,3 as score
- from 点击表
- unioin all
- select shop, item ,5 as score
- from 加购表
- unioin all
- select shop item ,7 as score
- from 下单表
- )
- //通过TF-IDF对热门商品进行惩罚,将权重(weight)作为系数与原来分数相乘得到最终喜好分数
- select shop
- , item
- , weight*score as score
- from TF-IDF(behavior)
- join behavior
到此为止,既有了门店对商品的喜好程度,又有了商品主图之间的相似度分数。将门店对商品的喜好程度与上述分数相乘,就得到了最终召回分数与排名。即可以将基于主图的召回方法应用于s2i的场景了,如下图5所示。
图5、主图召回示意图(s2i)
目前,基于商品主图召回的s2i版本已在APP推荐上线AB实验,期待取得较好的实验效果,可以全量到推荐所有的场景。
门店陈列数字化之品牌识别
为了更好的服务门店,我们需要不断深入的了解线下门店的实际经营情况,例如门店主营哪些品类,主推哪些品牌,门店调性如何。有了这些数据,我们就能分析出品牌和品类的核心门店有哪些,每个区域的消费者定位是怎么样的,从而可以更加了解市场状况,做出更契合商业逻辑的决策,成为品牌和海拍客的生意参谋。
对门店陈列品牌的分析,可以帮助品牌精准进店和复购,让品牌和平台利用门店陈列数字化了解每家门店、每个货架的情况,还可以获取门店在线下的区域影响力排名,用于门店分层运营,也用于跟品牌谈营销类的深度合作。例如针对纸尿裤的摆放及售卖情况的了解,有助于我们更好地帮助门店制定销售计划,同时也有助于公司统计门店、区域的纸尿裤销售状况,通过门店对于零辅食的陈列及相关品牌的一些信息来获取门店的调性分层和竞品分析,用于我们后续一些活动与品店的精准匹配。
为此,我们通过线下门店服务人员拍摄了大量门店货架照片。但图片作为一种非结构化的数据,难以直接分析,而人工判断照片中包含的品牌又需要大量的人力,因此我们通过图像算法,为品牌识别提效,加速了门店陈列数字化的进程。
整体流程
整体方案可以分为三个步骤:
- 图片预处理
- 图片聚类并人工打标
- 训练图片品牌分类模型
其流程如图6所示,每个步骤由虚线框出。
图6、整体流程框图
我们提效的思路就是将长得像的(从技术上看就是图片相似度较高的)放在一起,从而批量提供业务人员作识别操作,进而将人工识别的品牌(在机器学习任务中通常叫做打标)沉淀下来以备后续使用(业务使用,或打标工具使用)。思路上,针对这些预先未打标的品牌(品牌信息掌握在业务侧),我们通过聚类方法完成类似Facebook“人脸识别”的功能,将我们认为的一个SPU聚集到同一个类里(具体会形成一群图片组成的一个文件夹),然后交由品控同学手动打标。标记之后的结果除了回流到品控下游的同学手中,同时反馈给我们数据算法同学。得到品牌与图片之间的对应关系之后,将其作为训练数据训练对应品牌的二分类模型。针对后续的增量图片,预处理之后,先使用分类模型判断其是否属于已建模的品牌,是则标记其品牌,否则重复品牌未知的步骤:聚类并交由品控同学手动打标,得到反馈结果之后对其建立分类模型。
图片预处理
(1)图片审核
预处理过程的第一步是我们公司的品控同学根据业务知识针对上传的门店货架图片进行审核及校准,在这个过程中我们主要评估图片拍摄是否合格,货架拍摄是否完整等,进一步会识别合格照片中的物品属于什么品牌(或达到认知SPU维度)。细节方面,我们支持了对单个图片,整体拜访图片2个维度进行审核,同时支持对货架图片的识别结果进行校准,在机器识别的基础上再次人工校准。支持对图片的品牌、品牌系列,SKU等数据进行修改修正等等,进而支持通过门店的货架商品信息,对门店的等级进行划分。当然,为了数据的准确性,这个处理过程相对比较复杂,如图7所示。
图7、品控审核报告流程图
(2)图片分割
线下BD拍摄的原始照片是整个货架的照片,在正式处理之前,我们需要将照片分割成单个物品的粒度。这里用的是Trax公司的图片分割技术,其将照片中的物品分别分割为不同的小图(例如纸尿裤场景下一个小图就是一包纸尿裤),分割后全量门店共约有800万张图片。
(3)图片过滤
在此基础上,我们会进行第二步的图片预处理,其目的是过滤掉过“不合规范”的图片。所谓不合规范,这里主要指两方面,一方面是图片在resolution上不符合主流(例如图片过小、图片过大、图片过窄等,这些若不过滤,可能会影响到图像处理技术的产出结果),另一方面则是图片中的信息量(例如纸尿裤包装,作为一个长方体存在6个面,其中正面和背面在包装设计上是元素最多信息量最大的,而4个侧面则可能只有颜色和一些比较模糊的字样、包装底部封条、无辨识度的全白侧边等等,都不是我们先行产出方法的目标对象)。所以,无论是聚类还是分类,前提上都是以我们两步预处理之后的图片作为输入。方法上,前者我们通过写一些判定规则就可以在系统中很方便地过滤掉了,过滤之后剩下3558521张有效图片;针对后者问题,我们通过建设一个二分类的模型,将图片分为可用和不可用两部分,效果如下图8所示,最终可用和不可用图片分别为2510514、1048007张。
图8、二分类结果图
聚类
针对可用的图片,进行聚类操作,其流程如图9所示。
图9、聚类流程示意图
自底向上看,首先,在第一层中,将全量图片分为X张一包,对其提取深度特征,并进行聚类操作。这里可以得到这部分图片的聚类结果,然后将聚类结果中图片张数少于p的包丢弃。我们认为,在X万张图片中其同类都不能超过p张,在全量图片中其同类也不会很多。然后,在第二层中,将多个聚类结果合并得到包含N张图片的包,再对其进行提取深层特征、聚类操作。同样的,这里将图片张数少于q的包丢弃。最后,在第三层中,将所有聚类结果合并到一起,得到包含M张图片的包,提取其浅层特征并聚类,将聚类结果中大于K张图片的结果提供给品控同学。由于最后一步中的图片数量较多,且这里的图片大概率都会有同类图片,所以本层只提取其浅层特征即可。
值得注意的是,这里我们先画出了三层结构,而具体实操过程中,我们可以根据数据特性(如数据量,里面ground-truth预估SPU数量等等)来自由设计层数及各参数。此外,针对多次聚类的结果,设计了一个自动标记品牌的工具。多次聚类时,对比已标记的包和新聚类的包,设定阈值a和百分比b,超过此阈值和百分比的包直接标记为上次已标记的品牌。具体的聚类效果如图10所示,一共有3776个文件夹(这些文件夹都包含100张以上的图片,即上文的K=100),包含图片1122183张,品控同学标记完之后,合并相同的文件夹,共覆盖1172个品牌。其中,错误的图片有18527张(即下图右中混入了其他品牌的图片),聚类准确度约为98.35%。
图10、聚类结果示意图
分类
这里的分类类别不确定,随着业务的新增还会增加,是一个开集识别的问题。这里我们简单介绍下区别 – 对于一个特定的识别问题,实际上常用的识别方法是闭集识别,识别方法上我们可以采用常见的分类器,如支持向量机分类器(SVM)、K-近邻分类器(KNN)、最大相关系数分类器(MCC)以及自适应高斯分类器(AGC)等,这个问题上的假设是输入的待测样本一定属于已知的领域范畴。 但在实际应用环境中,测试样本中常常含有未知样本,如果继续使用闭集识别系统,系统将错误地将来自未知类的测试样本识别为属于已知闭合集类之一,会导致正确率下降。而通常我们称含有未知样本的识别问题为开集识别问题,其目标是能够通过训练达到正确划分类别并且正确拒绝其他未知类别。所以回到我们纸尿裤的品牌建模这个问题上,使用单纯的二分类模型或者多分类模型,在能够保证准确率的情况下,整体预测的召回量难以保证,这就还是会造成之前Trax对一些品牌建模所产生的问题。目前使用Anomaly Detection的一种算法:Deep SVDD,在某些品牌上取得了一定的效果。
总结及展望
无论是利用门店陈列照片分析出门店相关信息还是利用商品主图之间的相似性进行推荐,都是在挖掘图片包含的信息。当然,图像算法在海拍客的应用也不止这两个,还有诸如白底图识别等,未来将有更多的发挥空间。近年来计算机视觉方向的进步将更好的助力我们实现智能化、自动化,以技术为产品赋能,实现更长远的目标。
附录
这里对上述项目用到的图像算法做简单介绍。
ResNet
随着深度卷积神经网络在图像领域的应用,人们设计了一系列的深度网络AlexNet、VGGnet、GoogLeNet等,都取得了很好的性能效果。通过观察发现这些网络的深度是逐渐增加的,那么能否简单的通过堆叠网络深度来学习更好的模型呢?
答案显然是不能。首先,增加网络深度会带来梯度消失和梯度爆炸的问题,虽然可以通过归一化初始化和Batch Normalization很大程度的解决这个问题,但是并不能消除;其次,增加网络深度,会出现退化(degradation)问题,即随着网络深度增加,精度达到饱和,继续增加深度,导致精度快速下降。针对上述问题,2015 年何恺明团队提出 ResNet模型,成功的解决了上述难题。
ResNet 的核心在于残差模块中的恒等映射结构,该结构既包含了主路竖向连接,又包含支路横向连接。如图11所示,其中x表示输入、conv表示卷积、relu表示激活函数。当网络层数过多导致主路竖向连接出现梯度消失问题时,支路横向连接可以保证梯度信息的有效性,防止网络层数过深导致的梯度消失和性能退化。一个完整的ResNet网络结构如图12所示,其中,image表述输入图片、3x3conv表示卷积核大小为3x3的卷积、后面跟的数字表示channel数、pool和avg pool分别表示最大池化和平均池化、fc则表示全联接,后面的数字表示输出纬度(即分类数)。图中的虚线表示使用1x1的卷积核调整输出通道数。
ResNet后来发展成一个很大的家族,包括有ResNet18、ResNet34、ResNet50、ResNet101、ResNet152,内部结构见表1。并且,Pytorch官方将这些网络在ImageNet数据集上充分训练,保存模型,通过torchvision.models包提供服务。
在海拍客的图像应用中,我们多次使用到预训练的ResNet网络。
(1)二分类网络
考虑到只有二分类,不需要太复杂的网络结构,这里选择ResNet34作为基础网络。预训练的网络是在ImageNet数据集上训练的,其类别数为1000,因此,需要修改网络的类别数,然后使用我们自己的数据集训练(即finetune操作)。
- model_ft = models.resnet34(pretrained=True) #加载预训练网络
- num_ftrs = model_ft.fc.in_features #获取全联接层的输入纬度
- model_ft.fc = nn.Linear(num_ftrs, 2)#修改全联接层的输入输出
- model_ft = model_ft.to(device)#将模型拷贝到GPU上
训练完成,保存新的模型。加载新网络,读入测试图片,进行分类预测。
图11、残差块内部结构.
图12、resnet18内部结构图
表1、ResNet系列网络结构说明
(2)特征提取网络
对于图像而言, 每一幅图像都具有能够区别于其他类图像的自身特征,有些是可以直观地感受到的自然特征,如亮度、边缘、纹理和色彩等;有些则是需要通过变换或处理才能得到的,如直方图以及主成份等,我们称之为图像特征。深度神经网络通过卷积操作,可以提取图像更复杂、更高层的特征,表达图片更准确。
无论是品牌识别还是主图召回,都需要提取图像的特征,计算相似度。此时,直接加载预训练的模型结构,取消最后的全联接层,输出图像特征。
- class net(nn.Module):
- def __init__(self):
- super(net, self).__init__()
- self.net = models.resnet50(pretrained=True)
- def forward(self, input):
- output = self.net.conv1(input)
- output = self.net.bn1(output)
- output = self.net.relu(output)
- output = self.net.maxpool(output)
- output = self.net.layer1(output)
- output = self.net.layer2(output)
- output = self.net.layer3(output)
- output = self.net.layer4(output)
- output = self.net.avgpool(output)
- return output
DeepSVDD
Deep SVDD的核心思想就是:利用神经网络提取数据特征,并且将正常的样本收缩在超球面(中心为C,半径为R,中心c需要提前确定)内,异常的样本远离超球面,落于球外,如图13所示。
图13、Deep SVDD示意图
Deep SVDD其包括两种模式:
soft-boundary Deep SVDD
在这种模式下,训练数据即存在正样本也存在负样本,目标函数如下所示:
目标函数的第一项表示需要最小化超球的半径R,第二项为落在超球面外的样本的损失项,其中v∈(0,1]用于权衡超球面的体积与边界损失(即,允许部分样本点落于球外),最后一项为L2正则化。
通过优化上述的目标函数让所有数据点离球心越近,为了网络学到训练数据中的共有特征,所有正常样本会距离中球心尽可能近,而异常样本会距离球心尽可能远甚至落于球外。
One-Class Deep SVDD
当训练样本大多数都为正常样本(单分类)时,可以利用该范式,目标函数如下:
目标函数第一项要求所有样本提取到的特征都要离中心尽可能近,第二项为L2正则化。
通过优化上述目标函数让所有样本的平均距离都距离中心越近,在此过程中网络也会学到共有特征。
①②两种方法在进行测试时都很简单,利用测试样本距离中心的欧式距离来决定异常分数: