【算法调研】AI领域相似性度量

文本领域:

文本领域的求相似度的方法有很多,我这里只介绍三种方法,并给出部分我跑通了的代码实验。

余弦相似度(Cosine Similarity):

余弦相似度是一种常用于度量两个向量之间相似性的方法,它通常用于文本挖掘和自然语言处理等领域。在文本领域中,可以使用词袋模型或TF-IDF(Term Frequency-Inverse Document Frequency)来表示文本向量,然后通过余弦相似度来计算文本之间的相似程度。

以下是对余弦相似度的详细讲解:

  1. 词袋模型(Bag of Words Model):

    词袋模型是一种简单而常用的文本表示方法,它将文本表示为一个由单词构成的集合,忽略了单词在文本中的顺序和语法结构,只关注单词的出现频率。

  2. TF-IDF表示法:

    TF-IDF是一种用于信息检索和文本挖掘的常用方法,它结合了词频(TF)和逆文档频率(IDF)两个因素来表示单词的重要性。TF表示单词在当前文本中出现的频率,IDF表示单词在整个文本集合中的稀有程度。

  3. 计算余弦相似度:

    余弦相似度是通过计算两个向量之间的夹角来度量它们之间的相似性的方法。在文本领域中,如果将每个文本表示为一个向量,其中每个维度对应一个单词或特征,那么两个文本之间的余弦相似度可以通过以下公式计算:

cosine_similarity ( A , B ) = A ⋅ B ∥ A ∥ ∥ B ∥ \text{cosine\_similarity}(\mathbf{A}, \mathbf{B}) = \frac{\mathbf{A} \cdot \mathbf{B}}{\|\mathbf{A}\| \|\mathbf{B}\|} cosine_similarity(A,B)=∥A∥∥B∥A⋅B​

  1. 解释:
    • 如果两个文本之间的夹角接近于0度,则余弦相似度接近于1,表示它们非常相似。
    • 如果两个文本之间的夹角接近于90度,则余弦相似度接近于0,表示它们非常不相似。
    • 如果两个文本之间的夹角为180度,则余弦相似度为-1,表示它们是完全相反的。

因此,通过计算文本之间的余弦相似度,可以快速有效地比较它们之间的相似性,进而用于文本分类、聚类、信息检索等任务中。

实验代码,采用TF-IDF来表示词向量。

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
# 示例文本
documents = [
    "This is the first document.",
    "This document is the second document.",
    "And this is the third one.",
    "Is this the first document?",
]
# 使用TF-IDF向量化文本
tfidf_vectorizer = TfidfVectorizer()
tfidf_matrix = tfidf_vectorizer.fit_transform(documents)
# 计算文本之间的余弦相似度
cosine_similarities = cosine_similarity(tfidf_matrix, tfidf_matrix)
# 打印相似度矩阵
print("Cosine Similarity Matrix:")
print(cosine_similarities)

输出结果

Cosine Similarity Matrix:
[[1.         0.64692568 0.30777187 1.        ]
 [0.64692568 1.         0.22523955 0.64692568]
 [0.30777187 0.22523955 1.         0.30777187]
 [1.         0.64692568 0.30777187 1.        ]]

注意到第一句话和第四句话非常相近,其余弦相似度为1

Jaccard相似度(Jaccard Similarity):

基于两个集合的交集与并集之间的比例来度量文本之间的相似性。常用于文本的集合表示,如n-gram模型。

Jaccard相似度实验代码:

def jaccard_similarity(set1, set2):
    intersection = len(set1.intersection(set2))
    union = len(set1.union(set2))
    return intersection / union if union != 0 else 0
# 示例文本
text1 = "apple banana mango"
text2 = "banana orange pineapple"
# 将文本转换为n-gram集合
n = 2
set1 = set([text1[i:i+n] for i in range(len(text1)-n+1)])
set2 = set([text2[i:i+n] for i in range(len(text2)-n+1)])
# 计算Jaccard相似度
similarity = jaccard_similarity(set1, set2)
print("Jaccard Similarity:", similarity)

在这段代码中,我们定义了一个jaccard_similarity函数来计算Jaccard相似度。然后,给出了两个示例文本text1和text2。接着,我们使用n-gram模型将文本转换为n-gram集合,并计算它们之间的Jaccard相似度。最后,打印出了计算得到的相似度值。

输出结果

Jaccard Similarity: 0.43478260869565216
  1. 基于深度学习的方法:
    • 使用预训练的词嵌入模型(如Word2Vec、GloVe、BERT等)来表示文本,并通过神经网络模型来学习文本之间的相似性。

图像领域:

图像领域的求相似度的方法也有很多,我这里只介绍三种方法,并给出部分我跑通了的代码实验。

  1. 结构相似性指数(Structural Similarity Index,SSIM):

    结构相似性指数(SSIM)是一种广泛用于图像质量评价和图像相似性比较的指标。它不仅考虑了图像的亮度和对比度,还考虑了图像的结构信息,因此在评估图像相似性时更加全面和准确。

SSIM 的计算基于以下三个方面的比较:

  1. 亮度(Luminance):图像的亮度是指图像的平均亮度水平。SSIM 对比两个图像的亮度信息,以确保它们在视觉上保持一致。

  2. 对比度(Contrast):图像的对比度反映了图像中灰度级别的差异程度。SSIM 通过比较两个图像的对比度来捕捉它们的差异。

  3. 结构(Structure):图像的结构信息反映了图像中像素之间的空间关系。SSIM 使用局部窗口来比较图像的结构信息,以确保它们在视觉上的相似性。

SSIM 的计算公式通常表示为:

SSIM ( x , y ) = ( 2 μ x μ y + c 1 ) ( 2 σ x y + c 2 ) ( μ x 2 + μ y 2 + c 1 ) ( σ x 2 + σ y 2 + c 2 ) \text{SSIM}(x, y) = \frac{{(2\mu_x\mu_y + c_1)(2\sigma_{xy} + c_2)}}{{(\mu_x^2 + \mu_y^2 + c_1)(\sigma_x^2 + \sigma_y^2 + c_2)}} SSIM(x,y)=(μx2​+μy2​+c1​)(σx2​+σy2​+c2​)(2μx​μy​+c1​)(2σxy​+c2​)​

其中:

  • (x) 和 (y) 是要比较的两个图像。
  • μ x \mu_x μx​ 和 μ y \mu_y μy​ 是 x x x 和 y y y 的平均值。
  • σ x \sigma_x σx​ 和 σ y \sigma_y σy​ 是 x x x 和 y y y 的标准差。
  • σ x y \sigma_{xy} σxy​ 是 x x x 和 y y y 之间的协方差。
  • c 1 c_1 c1​ 和 c 2 c_2 c2​ 是用于稳定计算的常数。

    SSIM 的取值范围是 [ − 1 , 1 ] [-1, 1] [−1,1],其中 1 表示两个图像完全相同,0 表示两个图像没有任何相似性,-1 表示两个图像完全相反。

    总的来说,SSIM 能够比较图像的亮度、对比度和结构信息,并给出一个综合的相似性评分,因此在图像质量评价和图像相似性比较中被广泛应用。

    1. 卷积神经网络(Convolutional Neural Network,CNN):

    使用预训练的CNN模型(如VGG、ResNet、Inception等)来提取图像特征,并通过欧氏距离或余弦相似度来度量图像之间的相似性。

    这里利用reset18预训练权重,将两张对比照片提取特征生成两个一维特征向量,计算其向量的余弦相似度。

    import torch
    import torch.nn as nn
    import torchvision.models as models
    import torchvision.transforms as transforms
    import torch.nn.functional as F
    from PIL import Image
    # 加载预训练的模型
    model = models.resnet18(pretrained=True)
    # 删除最后一层(分类层)
    model = nn.Sequential(*list(model.children())[:-1])
    # 设置模型为评估模式
    model.eval()
    # 图像预处理
    transform = transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
    ])
    # 加载并预处理图像
    def load_and_preprocess_image(image_path):
        image = Image.open(image_path)
        image = transform(image)
        return image.unsqueeze(0)
    # 提取特征
    def extract_features(image):
        with torch.no_grad():
            features = model(image)
        return features.view(features.size(0), -1)
    # 计算余弦相似度
    def cosine_similarity(feature1, feature2):
        return F.cosine_similarity(feature1, feature2).item()
    # 两个图像文件路径
    image1_path = 'image1.jpg'
    image2_path = 'image2.jpg'
    # 加载并预处理图像
    image1 = load_and_preprocess_image(image1_path)
    image2 = load_and_preprocess_image(image2_path)
    # 提取特征
    feature1 = extract_features(image1)
    feature2 = extract_features(image2)
    # 计算余弦相似度
    similarity = cosine_similarity(feature1, feature2)
    print("Cosine similarity between the two images:", similarity)
    

    运行结果

    Cosine similarity between the two images: 0.8413233757019043
    
    1. 感知哈希算法(Perceptual Hashing):

    将图像转换为固定长度的哈希码,通过比较哈希码之间的汉明距离来度量图像之间的相似性。

    通常情况下,汉明码相似度的计算公式如下所示:

    相似度 = 1 − 汉明距离 哈希码长度 \text{相似度} = 1 - \frac{\text{汉明距离}}{\text{哈希码长度}} 相似度=1−哈希码长度汉明距离​

    根据这个公式,相似度的取值范围应该在0到1之间,其中1表示完全相似,0表示完全不同。

    from PIL import Image
    import imagehash
    # 加载并打开两张图像
    image1 = Image.open('1.jpg')
    image2 = Image.open('2.jpg')
    # 预处理调成相同大小的图片
    image2 = image2.resize(image1.size)
    # 计算哈希码
    hash1 = imagehash.phash(image1)
    hash2 = imagehash.phash(image2)
    # 计算哈希码长度
    hash_length = len(hash1.hash)
    # 计算汉明距离
    hamming_distance = hash1 - hash2
    # 计算相似度
    similarity = 1 - hamming_distance / (hash_length * 64)  # 64 是哈希码中的比特位数
    # 打印相似度
    print("Similarity between the two images:", similarity)
    

    输出结果

    Similarity between the two images: 0.97265625
    

    汉明距离说明这两张图片非常相似,这两张图片视觉上确实比较相似,但是这两张蒙娜丽莎的图片在内容上却大相径庭。说明仅仅通过哈希码不能很好的区别这两张图片的相似程度。

    这些方法都有各自的优缺点,选择合适的方法取决于具体的应用场景和需求。