使用String2String驯服文本:一个功能强大的Python库,用于字符串到字符串算法
#python #算法 #machinelearning #nlp

介绍

string2string库是一种开源工具 该库涵盖了字符串成对对齐,距离测量,词汇和语义搜索以及相似性分析。
此外,还包括各种有用的可视化工具和指标,使理解和评估这些方法的发现变得更加简单。

图书馆具有著名的算法,例如史密斯 - 沃特曼,赫希伯格,瓦格纳·菲什尔,巴特索尔,伯特斯科尔,努斯·莫里斯·普拉特和faiss搜索。
它可用于自然语言处理,生物信息学和计算机社会研究的许多工作和问题[1]。

Stanford NLP group是Stanford AI实验室的一部分,已开发了图书馆并在[1]中引入了图书馆。
图书馆的GitHub存储库有几个tutorials,您可能会发现有用。

a string 是代表数据或文本的字符(字母,数字和符号)的序列。
从日常短语到DNA序列,甚至是计算机程序,字符串都可以用来代表所有内容[1]。

安装

您可以通过运行pip install string2string通过pip安装库。


成对对准

字符串成对对齐是NLP和其他学科中使用的一种方法,可以通过突出显示它们共享和独特的特征来比较两个字符串或字符序列。
这两个字符串是对齐的,并根据共享字符的数量以及共享差距和不匹配的数量来计算相似性分数。
此过程对于定位具有相似性并计算两组字符串之间的“距离”的字符序列很有用。
拼写检查,文本分析和生物信息学序列比较(例如,DNA序列比对)只是其中的许多用途。

当前,string2string软件包提供以下对齐技术:

  • 全球对齐的Needleman-Wunsch
  • 当地对齐的史密斯 - 水手
  • hirchberg的线性空间算法全局对齐
  • 最长的常见子序列
  • 最长的常见子弦
  • 动态时间扭曲(DTW)用于时间序列对齐

在这篇文章中,我们将查看两个示例:一个用于全局对齐,一个用于时间序列对齐。

Needleman请求全球对齐算法

Needleman-Wunsch算法是一种动态编程算法,通常在生物信息学中使用,以匹配全球的两个DNA或蛋白质序列。

from string2string.alignment import NeedlemanWunsch

nw = NeedlemanWunsch()

# Define two sequences
s1 = 'ACGTGGA'
s2 = 'AGCTCGC'

aligned_s1, aligned_s2, score_matrix = nw.get_alignment(s1, s2, return_score_matrix=True)

print(f'The alignment between "{s1}" and "{s2}":')
nw.print_alignment(aligned_s1, aligned_s2)
The alignment between "ACGTGGA" and "AGCTCGC":
A | C | G | - | T | G | G | A
A | - | G | C | T | C | G | C

为了进行更有益的比较,我们可以在库中使用plot_pairwise_alignment()功能。

from string2string.misc.plotting_functions import plot_pairwise_alignment

path, s1_pieces, s2_pieces = nw.get_alignment_strings_and_indices(aligned_s1, aligned_s2)

plot_pairwise_alignment(
    seq1_pieces=s1_pieces,
    seq2_pieces=s2_pieces,
    alignment=path,
    str2colordict={
        "A": "pink", 
        "G": "lightblue", 
        "C": "lightgreen", 
        "T": "yellow", 
        "-": "lightgray"
    },
    title="",
    seq1_name="Sequence 1",
    seq2_name="Sequence 2",
)

Figure 1: Global alignment between ACGTGGA and AGCTCGC

动态的时间扭曲

dtw是比较两个时间序列的有用工具,该工具可能在速度,持续时间或两者兼而有之。
它通过计算两个序列中每对点之间的“距离”来发现这些距离的路径,从而最大程度地减少了序列之间的总差异。

让我们使用string2string库中的alignment模块来介绍一个示例。

from string2string.alignment import DTW

dtw = DTW()

x = [3, 1, 2, 2, 1]
y = [2, 0, 0, 3, 3, 1, 0]

dtw_path = dtw.get_alignment_path(x, y, distance="square_difference")
print(f"DTW path: {dtw_path}")
DTW path: [(0, 0), (1, 1), (1, 2), (2, 3), (3, 4), (4, 5), (4, 6)]

上面是我以前的帖子中借来的一个例子, An Illustrative Introduction to Dynamic Time Warping
对于那些希望深入研究主题的人,我在[2]中以视觉且易于访问的方式解释了DTW的核心概念。


搜索问题

字符串搜索是在另一个字符串中查找模式子字符串的任务。
该库提供了两种类型的搜索算法:词汇搜索和语义搜索。

词汇搜索(精确匹配搜索)

用外行的话来说,

词汇搜索是在文本中搜索某些单词或短语的行为,类似于在字典或书中搜索单词或短语。

,它没有试图弄清一串字母或单词的含义,而只是试图与它们完全匹配。
在搜索引擎和信息检索方面,词汇搜索是基于用户输入的关键字或短语找到相关资源的基本策略,而无需尝试理解所讨论的单词或短语的语言上下文。

当前,string2string库提供以下词汇搜索算法:

  • 幼稚(蛮力)搜索算法
  • Rabin-Karp搜索算法
  • Knuth-Morris-Pratt(KMP)搜索算法(请参见下面的示例)
  • Boyer-Moore搜索算法
from string2string.search import KMPSearch

kmp_search = KMPSearch()

pattern = "Redwood tree"
text = "The gentle fluttering of a Monarch butterfly, the towering majesty of a Redwood tree, and the crashing of ocean waves are all wonders of nature."

idx = kmp_search.search(pattern=pattern, text=text)

print(f"The starting index of pattern: {idx}")
print(f'The pattern (± characters) inside the text: "{text[idx-5: idx+len(pattern)+5]}"')
The starting index of pattern: 72
The pattern (± characters) inside the text: "of a Redwood tree, and"

语义搜索

语义搜索是一种更复杂的信息检索方法,它超出了简单的单词或短语搜索。
它采用NLP(自然语言处理)来破译用户的意图并返回准确的结果。

换句话说,假设您对“如何种植苹果”感兴趣。
尽管词汇搜索可能会产生结果,包括术语“成长”和“苹果”,但语义搜索将认识到您对种植苹果树感兴趣并相应地提供结果。
然后,搜索引擎将优先考虑结果,不仅包括它正在寻找的短语,而且还提供了有关种植,修剪和收获苹果树的相关信息。

faiss的语义搜索

faiss(Facebook AI相似性搜索)是一种有效的相似性搜索工具,可用于处理具有数值表示的高维数据[3]。
string2string库有一个由Facebook开发的Faiss库的包装器(请参阅GitHub repository

简而言之,FAISS搜索基于“分数”对结果进行排名,代表两个对象相似的程度。
该分数可以根据其与所需目标的近距离/相关性来解释和确定搜索结果的优先级。

让我们看看如何在string2string库中使用Faiss搜索。
在这里,我们有一个语料库[语料库(Corpora的复数)是一个大型且结构化的文本,用于语言研究,NLP和ML申请。] 11个句子,我们将通过查询目标句子来进行语义搜索了解与这些句子有多近/相关。

corpus = {"text": [
    "A warm cup of tea in the morning helps me start the day right.",
    "Staying active is important for maintaining a healthy lifestyle.",
    "I find inspiration in trying out new activities or hobbies.",
    "The view from my window is always a source of inspiration.",
    "The encouragement from my loved ones keeps me going.",
    "The novel I've picked up recently has been a page-turner.",
    "Listening to podcasts helps me stay focused during work.",
    "I can't wait to explore the new art gallery downtown.",
    "Meditating in a peaceful environment brings clarity to my thoughts.",
    "I believe empathy is a crucial quality to possess.",
    "I like to exercise a few times a week."
    ]
}

query = "I enjoy walking early morning before I start my work."

让我们初始化FaissSearch对象。 Facebook的Bart大型模型是FaissSearch对象的默认模型和标记。

from string2string.search import FaissSearch

faiss_search = FaissSearch(
    model_name_or_path = "facebook/bart-large",
    tokenizer_name_or_path = "facebook/bart-large",
)

faiss_search.initialize_corpus(
    corpus=corpus,
    section="text", 
    embedding_type="mean_pooling",
)

让我们在语料库中找到与查询的前3个最相似的句子,并打印它们以及它们的相似性分数。

top_k_similar_answers = 3
most_similar_results = faiss_search.search(
    query=query,
    k=top_k_similar_answers,
)

print(f"Query: {query}\n")
for i in range(top_k_similar_answers):
    print(f'Result {i+1} (score={most_similar_results["score"][i]:.2f}): "{most_similar_results["text"][i]}"')
Query: I enjoy walking early morning before I start my work.

Result 1 (score=208.49): "I find inspiration in trying out new activities or hobbies."
Result 2 (score=218.21): "I like to exercise a few times a week."
Result 3 (score=225.96): "I can't wait to explore the new art gallery downtown."

距离

字符串距离是量化两个使用距离函数的两个条件不同程度的任务。
当前,string2string库提供以下距离功能:

  • Levenshtein编辑距离
  • Damrau-Levenshtein编辑距离
  • 锤距
  • jaccard距离^[不要与jaccard相似性系数混淆。 jaccard距离= 1 -jaccard系数]

Levenshtein编辑距离

levenshtein编辑距离或简单的编辑距离是将一个字符串转换为另一个字符串所需的插入,删除或替换的最小数量。

from string2string.distance import LevenshteinEditDistance

edit_dist = LevenshteinEditDistance()

# Create two strings
s1 = "The beautiful cherry blossoms bloom in the spring time."
s2 = "The beutiful cherry blosoms bloom in the spring time."

# Let's compute the edit distance between the two strings and measure the computation time
edit_dist_score  = edit_dist.compute(s1, s2, method="dynamic-programming")

print(f'The distance between the following two sentences is {edit_dist_score}:') #\n- "{s1}"\n- "{s2}"')
print(f'"The beautiful cherry blossoms bloom in the spring time."')
print(f'"The beutiful cherry blosoms bloom in the spring time."')
The distance between the following two sentences is 2.0:
"The beautiful cherry blossoms bloom in the spring time."
"The beutiful cherry blosoms bloom in the spring time."

jaccard索引

jaccard索引可用于量化单词或令牌集之间的相似性,并且通常用于文档相似性或主题建模等任务中。
例如,jaccard索引可用于测量两个不同文档中单词集之间的重叠,或在文档集合中识别最相似的主题。

from string2string.distance import JaccardIndex 

jaccard_dist = JaccardIndex()

# Suppose we have two documents
doc1 = ["red", "green", "blue", "yellow", "purple", "pink"]
doc2 = ["green", "orange", "cyan", "red"]

jaccard_dist_score = jaccard_dist.compute(doc1, doc2)

print(f"Jaccard distance between doc1 and doc2: {jaccard_dist_score:.2f}")
Jaccard distance between doc1 and doc2: 0.75

相似

简单地说,字符串相似性决定了两个文本(或字符序列)链接或相似的程度。
举例来说,以下一对句子:

  • “猫坐在垫子上。”
  • “猫坐在地毯上。”

尽管不完全相同,但这些陈述共享词汇并传达了连接的意义。
基于字符串相似性分析的方法揭示并量化了此类文本配对之间的相似程度。

字符串相似性和距离 之间存在A duality ,这意味着它们可以互换使用[1]。

string2string库的similarly模块当前提供以下算法:

  • 余弦相似性
  • bertscore
  • Bartscore
  • jaro相似性
  • lcsubsequence相似性

让我们介绍以下四个句子的bertscore相似性算法的示例:

  1. 面包店出售各种美味的糕点和面包。
  2. 公园设有游乐场,步行小径和野餐区。
  3. 节日展示了来自世界各地的独立电影。
  4. 面包店有各种美味的面包和糕点。

句子1和2在语义上是相似的,因为两者都是关于面包店和糕点的。
因此,我们应该期望两者之间的相似性得分很高。

让我们在库中实现上述示例。

from string2string.similarity import BERTScore
from string2string.misc import ModelEmbeddings

bert_score = BERTScore(model_name_or_path="bert-base-uncased")
bart_model = ModelEmbeddings(model_name_or_path="facebook/bart-large")

sentences = [
    "The bakery sells a variety of delicious pastries and bread.", 
    "The park features a playground, walking trails, and picnic areas.", 
    "The festival showcases independent movies from around the world.", 
    "A range of tasty bread and pastries are available at the bakery.", 
]

embeds = [
    bart_model.get_embeddings(
        sentence, embedding_type='mean_pooling'
    ) for sentence in sentences
]

# Define source and target sentences (to compute BERTScore for each pair)
source_sentences, target_sentences = [], []
for i in range(len(sentences)):
    for j in range(len(sentences)):
        source_sentences.append(sentences[i])
        target_sentences.append(sentences[j])

# You can rewrite above in a more concise way using itertools.product
# from itertools import product
# source_sentences, target_sentences = map(
#   list, zip(*product(sentences, repeat=2))
# )

bertscore_similarity_scores = bert_score.compute(
    source_sentences,
    target_sentences,
)
bertscore_precision_scores = bertscore_similarity_scores['precision'].reshape(
    len(sentences), len(sentences)
)

我们可以使用库中提供的plot_heatmap()函数可视化每对句子之间的相似性。

from string2string.misc.plotting_functions import plot_heatmap

plot_ticks = [f"Sentence {i + 1}" for i in range(len(sentences))]

# We can also visualize the BERTScore similarity scores using a heatmap
plot_heatmap(
    bertscore_precision_scores,
    title="",
    x_ticks=plot_ticks,
    y_ticks=plot_ticks,
    x_label="",
    y_label="",
    valfmt="{x:.2f}",
    cmap="Blues",
)

Figure 2: Semantic similarity (BERTScore) between sentences

如上所述,正如我们预期的那样,句子1和4更相似(使用bertscore算法)。

ð您可以在GitHub上找到此博客文章的Jupyter笔记本。


结论

string2string python库是一种开源工具,为字符串到弦问题提供了完整的有效方法。
特别是,该库有四个主要模块来解决以下任务:1。成对对齐包括全局和本地对齐; 2. 距离测量; 3. 词汇和语义搜索;和4. 相似性分析
该库在每个类别中提供各种算法,并提供有用的可视化工具。

参考

[1] M. Suzgun,S。M。Shieber和D. Jurafsky,string2String:一个现代的Python图书馆,用于字符串到弦乐算法,2023年,可用:http://arxiv.org/abs/2304.14395

[2] E. Alizadeh,《动态时间扭曲的说明性介绍》,2020年。https://ealizadeh.com/blog/introduction-to-dynamic-time-warping/

[3] J. Johnson,M。Douze和H.Jâgou,与GPU的十亿个相似性搜索,GPU,IEEE大数据交易,第1卷。 7,不。 3,第535页,2019年。