创建可比较且灵活的对象指纹

时间:2014-02-07 08:42:47

标签: c# sql algorithm data-mining bigdata

我的情况

假设我有数千个对象,在本例中可能是电影。

我以很多不同的方式解析这些电影,收集有关每个电影的参数,关键字和统计数据。我们称他们为钥匙。我还为每个键分配一个权重,范围从0到1,具体取决于频率,相关性,强度,分数等。

作为一个例子,这里有一些电影世界末日的键和重量:

"Armageddon"
------------------
disaster       0.8
bruce willis   1.0
metascore      0.2
imdb score     0.4
asteroid       1.0
action         0.8
adventure      0.9
...            ...

可能有成千上万的这些键和重量,为清楚起见,这是另一部电影:

"The Fast and the Furious"
------------------
disaster       0.1
bruce willis   0.0
metascore      0.5
imdb score     0.6
asteroid       0.0
action         0.9
adventure      0.6
...            ...

我将其称为电影的指纹,我想用它们在我的数据库中查找类似的电影。

我还想象如果我愿意,可以插入除电影之外的其他内容,如文章或Facebook个人资料,并为其指定指纹。但这不应该影响我的问题。

我的问题

所以我已经走到了这一步,但现在我觉得这部分很棘手。我想把上面的指纹变成容易比较和快速的东西。我尝试创建一个数组,其中索引0 = disaster1 = bruce willis2 = metascore,其值为权重。

上面的两部电影出现了类似的内容:

[ 0.8 , 1.0 , 0.2 , ... ]
[ 0.1 , 0.0 , 0.5 , ... ]

我试过以不同的方式进行比较,只需乘以:

public double CompareFingerprints(double[] f1, double[] f2)
{
    double result = 0;

    if (f1.Length == f2.Length)
    {
        for (int i = 0; i < f1.Length; i++)
        {
            result += f1[i] * f2[i];
        }
    }

    return result;
}

或比较:

public double CompareFingerprints(double[] f1, double[] f2)
{
    double result = 0;

    if (f1.Length == f2.Length)
    {
        for (int i = 0; i < f1.Length; i++)
        {
            result += (1 - Math.Abs(f1[i] - f2[i])) / f1.Length;
        }
    }

    return result;
}

等等。

这些已经取得了非常令人满意的结果,但它们都有一个共同的问题:它们非常适合比较两部电影,但实际上,当我想要比较一部电影时,这是非常耗时的并且感觉非常糟糕指纹中包含存储在MSSQL数据库中的数千个指纹。特别是如果它应该与自动完成之类的东西一起工作,我希望在几分之一秒内返回结果。

我的问题

我在这里有正确的方法还是我以非常低效的方式重新发明轮子?我希望我的问题不是Stack Overflow的广泛问题,但我已经将其缩小了一些想法。

一些想法

  • 我的指纹真的应该是一系列重量吗?
  • 我应该查看哈希指纹吗?它可能有助于指纹存储,但比较复杂。我发现一些提示,这可能是一种有效的方法,使用Locality-sensitive hashing,但数学有点超出我的范围。
  • 我应该从SQL中获取所有数千部电影并使用结果,还是有办法将我的比较实现到SQL查询中并且只返回前100个点击?
  • sparse data representation有什么需要研究的吗? (感谢Speed8ump)
  • 我可以应用比较实际指纹时使用的方法,还是应用OCR
  • 我听说有一些软件通过在数千篇已发表的论文和之前的测试中发现相似性来检测考试作弊。他们使用什么方法?

干杯!

4 个答案:

答案 0 :(得分:3)

替代方案:特征向量

你所描述的是一个经典的特征向量。特征向量中的每列描述一个类别。您的特征向量是一种特殊的类型:它具有模糊数据,描述属于某个类别的程度。

处理此类向量时,应将fuzzy logic应用于计算。使用模糊逻辑,您必须稍微玩一下,直到找到最适合您的模糊操作的数字运算符。例如。模糊AND和OR可以用“min”和“max”或“*”和“+”计算,或者甚至用更复杂的指数运算来计算。你必须在良好的结果和快速计算之间找到适当的平衡。

不幸的是,模糊逻辑与SQL数据库不太匹配。如果你采用模糊方式,你应该考虑将所有数据保存在内存中并使用某种数字处理加速(处理器SIMD指令,CUDA / OpenCL,FPGA等)。

替代方案:Star / Snowflake Schema

另一种方法是构建经典的数据仓库方案。这非常适合现代SQL数据库。他们有很好的加速度来从中型数据仓库(最多几十亿条记录)中检索数据:

  1. Materialized views(用于数据缩减)
  2. (压缩)bitmap indexes(用于快速组合多个功能)
  3. 压缩存储(用于快速传输大量日期)
  4. 根据其功能进行定位(以物理方式分发数据)
  5. 要使用这些优化,您必须先准备好日期。

    分层维度

    您应该根据snowflake schema对您的功能进行分层排序。当以这种方式排序数据时(并且您具有相应的索引),数据库可以使用一组新的优化,例如, bitmap filtering

    以这种方式组织的数据应该主要是只读的。数据库将需要对特殊类型的查询非常快速的数据结构,但更新也非常昂贵。

    一个例子是位图索引。位图索引是二进制矩阵。矩阵的行是数据库中一个表的行。列是此表中一行的可能值。矩阵中的条目是1,当表中相应行中的列作为根据矩阵列的值时。否则为0。

    位图索引将以压缩二进制格式存储。对于数据库,通过使用快速二进制处理(通过在处理器SIMD指令中使用ANDing或ORing二进制值或甚至OpenCL / CUDA等)来组合多个位图索引非常容易。

    有一种特殊的位图索引可以跨越多个表,因此称为位图连接索引。它们专为在雪花模式中组织的数据而构建。

    尺寸缩减

    您还应该使用降维来减少必须存储的要素数量。为此,您可以使用principal component analysis等技术。通过这种方式,您可以将多个高度耦合的特征组合到一个人工特征中,并完全删除不会改变其价值的特征。

    离散维度成员

    对于模糊逻辑,使用浮点数很好。但是,在将数据存储在数据仓库中时,最好将其减少到可能的值。位图索引和分区仅适用于有限数量的值。您可以使用分类算法来实现此目的,例如self organizing feature mapsparticle swarm optimizations

    备选方案3:混合方法

    您可以轻松组合上述两种方法。您可以使用精简描述(更少的维度,更少的成员)将日期存储在数据仓库中。每个数据集都包含原始功能。当您从数据仓库中检索数据集时,您可以使用备选方案1中的技术来操作完整描述,例如:根据当前背景确定竞争的最佳候选人。

答案 1 :(得分:2)

想法很酷,这样我就能找到布鲁斯的所有优秀(imdb&gt; 5.5)电影,他扮演主角(布鲁斯威利斯> 0.9),这是行动(行动&gt; 0.5)而不是恐怖(恐怖&lt; 0.1)。我讨厌恐怖。

你的想法:

  • 权重数组很糟糕,因为如果你获得越来越多的密钥,并且如果影片没有这个角色,那么它仍然必须有一个值(0),这是浪费空间(想象百万的附在每部电影上的键。)
  • 散列没有意义,因为你不会按精确值访问任何东西,你总是会将密钥与用户输入的值进行比较,其中很多都是可选的(这意味着你不关心它们是否为0或10)。
  • 取决于,见下文。

我认为你需要的是一种Tag系统(如SO),你可以轻松地添加新标签(例如,对于新演员或者会有比蓝光更好的东西)或HD等)。所以带有标签[id] - [name]的表格。

然后你的电影必须有一个字段,存储一个零到百万个标签的字典[id] - [得分]。这应该是一个blob(或者有没有办法在SQL数据库中保存字典或数组?)或数组(如果你的标记id从0开始并递增1你不需要键,而是索引)。

当您搜索电影,匹配指纹条件时,您必须从每个电影的数据库中读取指纹。这应该比SQL查询要慢,但仍然可以(每部电影可能有100-1000个标签,这使得它只需要几KB),除非你必须通过网络传输这些数据,然后考虑使用服务器应用程序。也许存储过程可以提供帮助。

答案 2 :(得分:1)

我认为哈希是您正在寻找的,哈希表为您提供了O(1)的插入,删除和搜索。
我有一个类似的情况,我不得不散列八个不同整数的数组。我使用了C ++ boost库中的以下代码。

size_t getHashValue ()const{

        size_t seed = 0;
        for (auto  v : board)
            seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);

        return seed;


    }

我的数组被称为board,这是foreachC++循环的语法,size_t只是一个无符号整数,其余部分与C#
请注意,因为我有不同的值,我可以很容易地将值本身用作哈希函数,这样我就可以保证数组中每个元素的哈希值。

因为不是这种情况,您需要更改代码以包含数组中每个条目的哈希值,以构建整个数组的哈希值,如下所示:

foreach (float entry in array)
    // hashOf is something you would need to do 
    seed ^= hashOf(entry) + 0x9e3779b9 + (seed << 6) + (seed >> 2); 

如果您的条目在小数点后只有一位数,则可以乘以10并将问题移到整数域。 希望这会有所帮助。

修改

请参阅此问题以获取散列小数值:C# Decimal.GetHashCode() and Double.GetHashCode() equal

这种方法的性能继承了散列函数,函数的概率分布越均等,就越能获得更好的性能。 但恕我直言哈希表是最好的see this

答案 3 :(得分:1)

指纹格式
关于你的第一个问题,你是否应该使用一系列权重,这可以达到你想要的细节水平。由于缺乏更好的术语,一系列权重将提供最高的指纹“分辨率”;它允许对两部给定电影的相似程度进行更精细的测量。 Sinatr建议使用标签代替权重具有很多优化潜力,但它基本上限制了你的权重0或1,因此在0.3-0.7范围内代表现有权重时遇到麻烦。您必须自己决定转到具有较少细节的表示的性能增益是否超过这些表示所具有的降低的比较精度。

<强>散列
关于你的第二个问题,我恐怕无法提供太多指导。我不熟悉在这种情况下使用散列,但我不知道如何轻松地比较它们;在大多数用途中,哈希的重点在于它们不易被反转以了解原始输入。

SQL优化
对于第3个问题,用于获取比较候选的SQL查询可能是性能优化潜力的丰富来源,特别是如果您了解指纹的某些特征。特别是如果高重量或低重量是相对罕见的,那么你可以用它们来淘汰很多不良候选人。例如,如果你使用的是电影,你会发现很多重量为0(大多数电影不包含布鲁斯威利斯)。您可以查看候选影片中高于.8左右的任何权重(您需要进行一些微调以确定适用于您的数据集的确切值)然后让您的SQL查询排除结果在这些键的至少一部分中具有0(同样,该部分将需要微调)。这使您可以快速丢弃在SQL查询阶段不太可能匹配的结果,而不是对它们进行完整(昂贵)的比较。

其他选项
根据对象指纹变化的频率,可能有效的另一种方法是预先计算指纹比较值。然后获得最佳候选者是来自索引表的单个查询:SELECT id1, id2, comparison FROM precomputed WHERE (id1 = foo OR id2 = foo) AND comparison > cutoff ORDER BY comparison DESC。预先计算新对象的比较将是添加它的过程的一部分,因此如果能够快速添加对象是优先考虑的话,那么这种方法可能效果不佳。或者,您可以在计算它们之后简单地缓存值,而不是预先计算它们。这对初始搜索没有任何作用,但后来的搜索获得了好处,添加对象仍然很便宜。