如何验证两个文本数据集是否来自不同的分布?

How to verify if two text datasets are from different distribution?

我有两个文本数据集。每个数据集由多个序列组成,每个序列可以有多个句子。

如何衡量两个数据集是否来自同一分布?

目的是仅当分布之间的差异具有统计显着性时,才验证从一种分布到另一种分布的迁移学习。

我正在考虑使用卡方检验,但不确定它是否有助于考虑到高自由度的文本数据。

更新: 例子: 假设我想训练一个情感分类模型。我在 IMDb 数据集上训练模型并在 IMDb 和 Yelp 数据集上进行评估。我发现我在 IMDb 上训练的模型在 Yelp 上仍然表现良好。但问题是这些数据集有何不同?

训练数据集:https://www.kaggle.com/columbine/imdb-dataset-sentiment-analysis-in-csv-format?select=Train.csv

评估 1:https://www.kaggle.com/columbine/imdb-dataset-sentiment-analysis-in-csv-format?select=Valid.csv

评估 2:https://www.kaggle.com/omkarsabnis/sentiment-analysis-on-the-yelp-reviews-dataset

现在,

  1. train 和 eval 1 有何不同?
  2. train 和 eval 2 有何不同?
  3. train 和 eval 2 之间的差异是偶然的吗?什么是统计意义和 p 值?

我写了一篇文章,跟你的问题类似,但不完全一样。 https://towardsdatascience.com/a-new-way-to-bow-analysis-feature-engineering-part1-e012eba90ef

我试图解决的问题是检查一个词在类别或标签中是否具有不同(显着)分布。

您的问题与我上面提到的问题有一些相似之处。

  • 您想比较两个来源的数据集,可以将其视为两个不同的类别
  • 此外,要比较数据源,您必须比较单词,因为不能直接比较句子

因此,我建议的解决方案如下:

  • 使用 count-vectorizer 在两个数据集中创建单词特征,并从每个数据集中获取前 X 个单词
  • 假设您有 N 个不同的单词,现在初始化 count=0 并开始比较每个单词的分布,如果差异显着则增加计数器。此外,在某些情况下,一个词只存在于一个数据集中,这是一个很好的新词,我的意思是它表明它是一个显着特征,因此,为此也会增加计数
  • 假设总数是n。现在,较低的是 n/N 比率,相似的两个文本是 vice-a-versa

此外,为了验证这种方法 - 将来自单一来源的数据分成两个(随机抽样)和 运行 上述分析,如果 n/N 比率接近于 0,这表明两个数据源相似,也是如此。

请让我知道这种方法是否有效,如果您认为其中有任何缺陷,我很乐意思考并尝试改进它。

问题 “文本 A 和文本 B 是否来自同一分布?” 不知何故定义不明确。例如,这两个问题(1,2)可以看作是从相同的分布(StackExchange 上所有问题的分布)或不同的分布(StackExchange 的两个不同子域的分布)生成的。所以不清楚你要测试的属性是什么

无论如何,您可以提出您选择的任何测试统计数据,通过模拟在“单一来源”的情况下估算其分布,并计算测试的 p-value。

作为玩具示例,让我们以两个小语料库为例:来自英文维基百科的两篇随机文章。我会在 Python

内完成
import requests
from bs4 import BeautifulSoup
urls = [
    'https://en.wikipedia.org/wiki/Nanjing_(Liao_dynasty)', 
    'https://en.wikipedia.org/wiki/United_States_Passport_Card'
]
texts = [BeautifulSoup(requests.get(u).text).find('div', {'class': 'mw-parser-output'}).text for u in urls]

现在我使用原始分词器来计算文本中的单个单词,并使用单词相对频率的均方根差作为我的测试统计量。您可以使用任何其他统计数据,只要您一致地计算它即可。

import re
from collections import Counter
from copy import deepcopy
TOKEN = re.compile(r'([^\W\d]+|\d+|[^\w\s])')
counters = [Counter(re.findall(TOKEN, t)) for t in texts]
print([sum(c.values()) for c in counters])  
# [5068, 4053]: texts are of approximately the same size

def word_freq_rmse(c1, c2):
    result = 0
    vocab = set(c1.keys()).union(set(c2.keys()))
    n1, n2 = sum(c1.values()), sum(c2.values())
    n = len(vocab)
    for word in vocab:
        result += (c1[word]/n1 - c2[word]/n2)**2 / n
    return result**0.5

print(word_freq_rmse(*counters))
# rmse is 0.001178, but is this a small or large difference?

我得到的值为 0.001178,但我不知道是否相差很大。所以我需要在零假设下模拟这个检验统计量的分布:当两个文本都来自同一个分布时。为了模拟它,我将两个文本合并为一个,然后随机拆分它们,并在比较这两个随机部分时计算我的统计量。

import random
tokens = [tok for t in texts for tok in re.findall(TOKEN, t)]
split = sum(counters[0].values())
distribution = []
for i in range(1000):
    random.shuffle(tokens)
    c1 = Counter(tokens[:split])
    c2 = Counter(tokens[split:])
    distribution.append(word_freq_rmse(c1, c2))

现在我可以看到在原假设下我观察到的检验统计值有多么不寻常:

observed = word_freq_rmse(*counters)
p_value = sum(x >= observed for x in distribution) / len(distribution)
print(p_value)  # it is 0.0
print(observed, max(distribution), sum(distribution) / len(distribution)) # 0.0011  0.0006 0.0004

我们看到,当文本来自同一分布时,我的测试统计量平均为 0.0004,几乎从未超过 0.0006,因此 0.0011 的值非常不寻常,零假设我的两个文本来自同一个应该拒绝分发。