我有两个文本数据集。每个数据集由多个序列组成,每个序列可以有多个句子。
如何衡量两个数据集是否来自同一分布?
目的是验证从一个分布到另一个分布的迁移学习,只有在分布之间的差异具有统计学意义的情况下。
我打算使用卡方检验,但考虑到高度自由度,我不确定它是否对文本数据有帮助。
更新:示例:假设我想训练一个情绪分类模型。我在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
现在,
- train和eval 1有什么不同
- train和eval 2有什么不同
- 火车和eval 2之间的不同是偶然的吗?统计显著性和p值是多少
问题"文本A和文本B来自同一分布吗">在某种程度上定义不清。例如,这两个问题(1,2)可以被视为从同一分布(StackExchange上所有问题的分布)或从不同分布(Stack Exchange的两个不同子域的分布)生成。因此,尚不清楚您想要测试的属性是什么。
无论如何,你可以想出你选择的任何测试统计数据,在";单一来源";通过模拟,并计算测试的p值。
作为一个玩具的例子,让我们举两个小语料库:两篇来自英语维基百科的随机文章。我会在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'([^Wd]+|d+|[^ws])')
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的值是非常不寻常的,应该拒绝我的两个文本来源于同一分布的零假设。
我写了一篇文章,它与您的问题相似,但并不完全相同。https://towardsdatascience.com/a-new-way-to-bow-analysis-feature-engineering-part1-e012eba90ef
我试图解决的问题是检查一个词在类别或标签之间是否有不同的(显著的)分布。
你的问题和我上面提到的问题有一些相似之处。
- 您想要比较两个数据集源,它们可以被视为两个不同的类别
- 此外,要比较数据源,你必须比较单词,因为句子无法直接比较
因此,我提出的解决方案如下:
- 使用计数矢量器在两个数据集中创建单词特征,并从每个数据集中获取前X个单词
- 假设您的总不同单词为N,现在初始化count=0,并开始比较每个单词的分布,如果差异显著,则递增计数器。此外,在某些情况下,一个单词只存在于其中一个数据集中,这是一个很好的新单词,我的意思是,它表明它是一个显著的特征,因此,为此也增加了计数
- 假设总数是n。现在,n/n比越低,两个文本相似,反之亦然
此外,为了验证这种方法-将单个数据源的数据拆分为两个(随机抽样),并运行上述分析,如果n/n比接近0,则表明两个数据源相似,情况也是如此。
请让我知道这种方法是否有效,如果你认为这里面有任何缺陷,我很乐意思考并尝试发展它。