Skip to content

Latest commit

 

History

History
182 lines (126 loc) · 12.4 KB

使用图像特征的库存图像相似性(程序是如何比我更时尚的).md

File metadata and controls

182 lines (126 loc) · 12.4 KB

原文:Stock Image Similarity with Image Features (or How a Program is More Fashionable than Me)

对我们来说,识别模式理所当然。无论我们在学习一门新的语言,猜测我们第一次听到的歌曲的作者,还是认出一个老朋友,我们的大脑都使用了模式来简化复杂的脑力工作。事实上,我们的大脑是如此固定的使用模式作为一个快捷方式来识别,我们甚至会在随机数据中发现模式

选择当天的服饰,或者购买新的衣服是很多人更有意识地在日复一日的生活中应用模式识别的一些方法。当你评估你的衬衫是否和裤子搭配,或者一双打折的鞋子是否适合你的“装扮”时,你可能不会觉得你的大脑正使用具体化以及通过多年数据进行调整的模式,但是,你的大脑已经通过几乎你生活的每天中观察其他人如何装扮自己学会了什么是赏心悦目,什么是文化适宜。即使你像我一样,无意识地为你当天的穿着搭配服饰,你仍然使用模式识别来辨别在不同的社会环境下,哪些衣服是为社会接受的(虽然,我的妈妈在看见我努力为正式场合打扮自己后,可能会怀疑我大脑的这部分正确工作)。

任务

因此,可以安全的说,大多数人都擅长识别衣服样式和相识性 - 但是,如何让计算机执行这个任务呢?使用indico的图像特征API,我设计了它查找一个输入图片的类似服装的能力的一个简单的概念的证明。我选择了这个任务,而不是一个复杂得多的原因是:

  1. 这在概念上非常简单。寻找类似的服装是一个比查找配套的服装容易得多的问题,这意味着我可以在更少的时间内编写更整洁(更清晰)的代码。
  2. 它更普及。图像相似性是比服装匹配的多个领域特定任务拥有更广泛的应用的构建块。
  3. 对我来说,更容易评估它的工作状况。虽然我只是设计这个程序,并不会数学地评估进展,但是比之评估它是否搭配,我更相信自己可以识别服装是否相似。与此类似,虽然它仍然被客观的测量,服装相似性比服装搭配更具有普遍性。
  4. 它有更明显的应用程序。虽然大多数人并不希望一台电脑告诉他们怎样搭配他们的衣服(虽然我们当中有些人可能会从这样的建议中获益),有明显的电子商务应用进行图像相似性匹配,例如,消费者想找到与他们手中的一张照片类似的一个便宜得多的,仍有存货,或者更有道德的衣服。

准备开始

对于我的图像数据集,我从Lord and Taylor的产品摘要中选择了2000张随机的女性服饰的图片。如果你想跟着我一起,那些图片,以及代码框架和一些辅助函数,可以从GitHub库的‘skeleton’分支中克隆。一旦已经克隆了此分支,打开main.py

首先,让我们设置我们将分析多少张图片,并使用N_IMG常量进行比较。没有理由在我们计算出任何错误之前比较大量的图片;如果脚本甚至不能运行,那么我们根本就不关心结果有多漂亮!我已经在框架中设置了N_IMG值为500,单随时可以将其设置得更小,以便让它工作时使用更少的等待时间。

一旦我们设置了N_IMG,我们将需要图像进行实际的比较!我已经写了一个辅助函数make_paths_lists,它从clothing_images目录中返回了一个N_IMG图片文件名字的列表。

def run():
    paths = make_paths_list()

run()

幸运的是,Python库indicoio所有的图像函数都能接受路径作为参数,所以我们甚至不需要读入任何图像数据!我们想要为这些图片的每一个获得图像特征向量,所以我们将需要用到Python库中的[image_feature](http://image_feature)方法。

使用indico

首先,我们需要安装Python库indicoio。要做到这点,只需要进入你的终端,然后使用pip进行安装:

$ pip install indicoio

如果你碰到问题,检查docsInstallation部分。

一旦安装了客户端库,那么使用快速入门指南获取一个indico API key。我喜欢在一个环境变量,$INDICO_API_KEY,中设置我的API key,这样,该客户端库将自动知道去查找,但是,你也可以将你的API key放进你的配置文件中,或在你调用API时直接传递。

真棒!现在,我们准备调用indico,以获得我们500张图片的图像特征!通过为代码添加indico库导入来开始。

import math
import os
from random import sample
import cPickle as pickle

from scipy import spatial
from PIL import Image
import numpy as np
import indicoio

现在,让我们更新我们的方法make_feats(paths),它接收一个图像文件路径的列表,并应该返回一个图像特征向量的列表。indico应该能够处理大小为500的批量请求,所以现在,该方法看起来是这样的:

def make_feats(paths):
    return indicoio.image_features(paths, batch=True, v=3)

我们设置batch=True,因为我们传递的是一个图片列表,而不是一张图片,并设置v=3来使用版本3,它是我们的图像特征模型当前最近的版本。

我们的run()函数现在应该看起来是这样的:

def run():
    paths = make_paths_list()
    feats = make_feats(paths)

run()

充实我们的代码

好极了!500张服装的素材图片的每一张我们都有一个图像特征(准确来说,4096)列表。这如何帮助我们呢?我们将如何使用那些信息向量来预测那些衣服是最相似的?图像之间的相似性将以特征向量之间的相似性被捕捉,所以,天真地,我们可以通过确定两件衣服的特征向量之间的欧几里得距离来猜测这两件衣服如何相似。在第4096维空间的距离越小,这两件衣服就更有可能相似!

为了验证这一理论,我写了一个辅助函数calculate_sim(feat)。该函数接收一个特征向量列表,确定特征向量两两之间的欧几里得距离,然后返回从最小到最大的图像索引列表的已排序列表。例如,第5张图片是输入特征向量数组的下标为4的元素。在输出数组中,下标为4的元素可能是一个图像索引及其距离的列表,从(0,4)开始,因为第5张图片有一个从其自身的零欧几里得距离。要获得类似的排名,像这样更新你的run()代码:

def run():
    paths = make_paths_list()
    feats = make_feats(paths)
    similarity_rankings = calculate_sim(feats)

run()

最后,让我们看看我们的相似性排名表现如何!如下更新我们的代码将随机从数据中选择三张图片,然后生成一个2×5网格,它包含每一个的10个最佳匹配(包括图片自身)。

def run():
    paths = make_paths_list()
    feats = make_feats(paths)
    similarity_image = calculate_sim(feats)

    chosen_images = sample(xrange(N_IMG), 3)
    for k in range(len(chosen_images)):
        chosen_img = chosen_images[k]
        similarity_image(chosen_img, similarity_rankings, paths)

run()

最后,是时候运行我们的代码了!再次打开你的终端,然后调用脚本。

$ python main.py

该代码应需要一点时间来运行,但最终,应该会弹出三张图片。我的第一个看起来是这样的:

clothing-similarity-results-1

不算太坏,但可以更好!最前面的那张斑点裙子图片是其他图片匹配的图片。大多数的匹配是由相似长度的裙子,但有两个是看起来完全不像的白色衬衫。我有信心可以简单通过查找更多地图片来做得更好。

让我们的代码更健壮

在提高我们的输入图片数量之前,必须对我们的代码做些修改来准备。首先,它要花很长的时间来生成那些路径和相似排名数组。为了最大限度地减少那些需要重新计算的频率,如下更新运行代码:

def run():
    try:
        paths = pickle.load(open('paths.pkl', 'rb'))
    except IOError:
        paths = make_paths_list()
        pickle.dump(paths, open('paths.pkl', 'wb'))

    try:
        similarity_rankings = pickle.load(open('similarity_rankings.pkl', 'rb'))
    except: IOError:
        feats = make_feats(paths)
        similarity_rankings = calculate_sim(feats)
        pickle.dump(similarity_rankings, open('similarity_rankings.pkl', 'wb'))

    chosen_images = sample(xrange(N_IMG), 3)
    for k in range(len(chosen_images)):
        chosen_img = chosen_images[k]
        similarity_image(chosen_img, similarity_rankings, paths)

run()

当我们提高分析的图片数量时,可能对于indico来说,在一个请求中处理而不超时已经太多了。因为我们知道,服务器可以处理500张图片,我们可以如下更新make_feats,因此不管N_IMG多大,它都将运行:

def make_feats(paths):
    chunks = [paths[x:x+100] for x in xrange(0, len(paths), 100)]
    feats = []
    for chunk in chunks:
        feats.extend(indicoio.image_features(chunk, batch=True, v=3))
    return feats

包含更多的图片

最后,让我们在再次运行我们的代码之前增加N_IMG的值至1000。总体来说,结果应该看起来更好!我保存了斑点裙子的索引,然后再次检查10个最接近的匹配:

clothing-similarity-results-2

好多了!随着可选的图片变多,前十个(或者九个,如果你不算上图片自身的话)匹配都是相似长度和品味的裙子。第二个最佳匹配甚至是另一个黑白斑点的裙子!

最后一次,让我们将N_IMG增加至1500。确保删除相似性排名和路径pickle,这样它们将用N_IMG=1500重新进行计算。这最终为该斑点裙子生成下列匹配:

clothing-similarity-results-3

为啥它可以工作

理解为什么我们简单的最邻近搜索的工作原理以及它的作用的关键是,理解图像的特征向量。因为我们让indico api完成繁重的机器学习工作,那些向量是来自一个黑盒子的一些神秘的东东。

def make_feats(paths):
    return indicoio.image_features(paths, batch=True, v=3)

每个图像的图像特征向量是一个包含4096个浮点数的数组。其中每个浮点数代表图像的一个特性,比如颜色属性,清晰度,或水平条纹。其中许多功能是更抽象的,不会是人们会使用的特性,至少不会是有意识的使用,来分辨图像。通过找到与目标之间最小的欧几里得距离的图像,我们最小化这些4096个属性之间的差异!

做更多

如果你喜欢这个教程,那么值得探索其他图像相似性预测的应用。类似的技术也可以被用于解决各种各样的问题,包括图像分类和边缘检测。如果你使用我们的API进行一些很酷的编程,那么让indico团队知道吧!此外,如果你想玩一玩这个脚本更完善的一个版本,看看在我们网站上的服装相似性演示。

其他可视化图像相似性

作为奖励,看看这个很酷的库存图像t-SNE可视化!

t-SNE visualization of clothing similarity

好奇它如何工作的?或者想自己玩一玩?看看使用t-SNE进行可视化,以学习你需要开始的一切。