numpy 从列表中随机选择,避免给定元素(负采样)

v09wglhw  于 2023-10-19  发布在  其他
关注(0)|答案(3)|浏览(156)

我有一个项目对的列表,其中每个项目都由一个数字id索引。
例如
| 项目1|项目2|
| --|--|
| 1 | 1 |
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 3 | 2 |
| 4 | 5 |
| ......你好。|......你好。|
数字对不一定从0开始排列,并且可能不包括其间的所有值(例如,项目7可能存在,即使项目6不在数据集中)。
我从这个列表中随机抽样,并希望创建多个负样本(即,不存在的样本)。
目前,我的代码看起来像这样,但花的时间太长了:

  1. import pandas as pd
  2. import numpy as np
  3. num_neg_samples = 4
  4. # sample data
  5. rng = np.random.default_rng()
  6. all_pairs = pd.DataFrame({
  7. 'TARGET_ID': rng.choice(1_000_000, size=500_000, replace=True),
  8. 'CONTEXT_ID': rng.choice(1_000_000, size=500_000, replace=True)
  9. })
  10. pair_sample = all_pairs.sample(10240)
  11. print(pair_sample.head())
  12. import time
  13. start = time.time()
  14. targets = np.repeat(pair_sample['TARGET_ID'].values,(num_neg_samples+1),axis=0)
  15. contexts = pair_sample['CONTEXT_ID']
  16. negids = []
  17. print(f'{len(pair_sample["TARGET_ID"])} target_ids to create neg samples for')
  18. for index, target_id in enumerate(pair_sample['TARGET_ID']):
  19. neg_samples = rng.choice(
  20. all_pairs.loc[all_pairs['TARGET_ID'] != target_id, 'TARGET_ID'].values,
  21. size=num_neg_samples
  22. )
  23. negids.append(neg_samples)
  24. print(time.time() - start, 'seconds')
  25. batch = (negatives, contexts, targets)

测试结果:

  1. TARGET_ID CONTEXT_ID
  2. 252373 5238 953345
  3. 290732 589947 869541
  4. 124135 365468 373147
  5. 129140 566125 542728
  6. 450409 688717 936377
  7. 10240 target_ids to create neg samples for
  8. 26.611750602722168 seconds

每轮训练我抽取10240对。因此,我希望最终得到40960个负对,在我的数据集中每个“目标项”有4个。
有没有人有好的方法来加速这段代码?任何帮助都非常感谢。
编辑:随着问题的出现:成对项是一起出现在搜索结果中的项。我想训练一个类似于word 2 vec或自动编码器的模型,它生成一个嵌入,这个嵌入对于在搜索结果中一起出现的项目是相似的。为了改进嵌入,我也想用负样本进行训练,即。不同时出现在搜索结果中的成对项目。
编辑2:请注意,我可用的对可能包括重复,即。相同的一对每个项目ID将在item_1item_2列中出现多次。

af7jpaap

af7jpaap1#

对DataFrame进行切片的成本很高。使用python和sets对我来说快了5倍:

  1. num_neg_samples = 4
  2. targets = set(pair_sample['TARGET_ID'])
  3. negids = [rng.choice(list(targets-{t}), size=num_neg_samples, replace=False)
  4. for t in pair_sample['TARGET_ID']]

输出量:

  1. [array([563401, 169859, 180204, 953531]),
  2. array([310634, 27685, 992711, 801847]),
  3. array([519938, 717685, 150933, 466746]),
  4. array([496852, 370208, 930286, 293928]),
  5. ...
  6. ]

使用python的random.sample快10倍:

  1. num_neg_samples = 4
  2. targets = set(pair_sample['TARGET_ID'])
  3. negids = [random.sample(list(targets-{t}), k=num_neg_samples)
  4. for t in pair_sample['TARGET_ID']]
展开查看全部
xkftehaa

xkftehaa2#

您可以通过使用concurrent.futures.ThreadPoolExecutor在并行线程中收集过滤后的负样本来加快处理速度。我在我的机器上获得了4-5倍的速度提升(初始方法:~93秒,螺纹:约20秒):

  1. import time
  2. from concurrent import futures
  3. from functools import partial
  4. def select_neg_sample(target_id, all_pairs, size=4):
  5. return rng.choice(
  6. all_pairs.loc[all_pairs['TARGET_ID'] != target_id, 'TARGET_ID'].values,
  7. size=size
  8. )
  9. start = time.time()
  10. targets = np.repeat(pair_sample['TARGET_ID'].values, (num_neg_samples + 1), axis=0)
  11. contexts = pair_sample['CONTEXT_ID']
  12. print(f'{len(pair_sample["TARGET_ID"])} target_ids to create neg samples for')
  13. with futures.ThreadPoolExecutor() as executor:
  14. it = executor.map(partial(select_neg_sample, all_pairs=all_pairs),
  15. pair_sample['TARGET_ID'])
  16. neg_ids = list(it)
  17. print(time.time() - start, 'seconds')
  18. print(f'neg_ids size: {len(neg_ids)}, last entry: {neg_ids[-1]}')
  1. TARGET_ID CONTEXT_ID
  2. 294231 704025 731665
  3. 22308 430988 862487
  4. 484542 70459 196505
  5. 119601 762881 339710
  6. 309284 459537 433841
  7. 10240 target_ids to create neg samples for
  8. 19.128324270248413 seconds
  9. neg_ids size: 10240, last entry: [ 33955 538037 312413 692326]
展开查看全部
w8biq8rn

w8biq8rn3#

根据@mozway的回答和这个,我在一个相关的帖子中发现:https://stackoverflow.com/a/44349133/5240684,我想出了以下,这是足够的我的情况:

  1. import pandas as pd
  2. import numpy as np
  3. import time
  4. num_neg_samples = 4
  5. # sample data
  6. rng = np.random.default_rng()
  7. all_pairs = pd.DataFrame({
  8. 'TARGET_ID': rng.choice(100_000, size=500_000, replace=True),
  9. 'CONTEXT_ID': rng.choice(100_000, size=500_000, replace=True)
  10. })
  11. pair_sample = all_pairs.sample(10240)
  12. print(pair_sample.head())
  13. start = time.time()
  14. unique_targets = all_pairs['TARGET_ID'].unique()
  15. # index to be able to use range
  16. unique_targets_indexed = dict(zip(unique_targets, range(len(unique_targets))))
  17. targets = pair_sample['TARGET_ID'].values
  18. contexts = pair_sample['CONTEXT_ID'].values
  19. negative_targets = np.repeat(pair_sample['TARGET_ID'].values,(num_neg_samples),axis=0)
  20. negative_contexts = [unique_targets[random_neg(0,len(unique_targets), unique_targets_indexed[t])] for t in negative_targets]
  21. print(f'\nTook {time.time() - start} seconds')

测试结果:

  1. TARGET_ID CONTEXT_ID
  2. 284483 70099 74132
  3. 164208 3090 13895
  4. 186524 89871 81281
  5. 270224 46461 56410
  6. 470391 7434 81272
  7. Took 0.19039297103881836 seconds

即使从1 mio不同的目标id中采样,我的机器上的处理时间也增加到只有大约0.31秒。
基本思想是将目标id的索引设置为从0到len(unique_targets)的范围,这样我们就可以从一个范围而不是从一个集合中采样一个随机整数,因为这样更快。
这样,我最终得到batch = (contexts, targets, negative_contexts, negative_targets)作为我的批处理。
谢谢你给我指明了正确的方向。

展开查看全部

相关问题