test_dht_experts.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import asyncio
  2. import random
  3. import numpy as np
  4. import pytest
  5. import hivemind
  6. import hivemind.server.expert_uid
  7. from hivemind import LOCALHOST, declare_experts, get_experts
  8. from hivemind.client.beam_search import MoEBeamSearcher
  9. from hivemind.server.expert_uid import UidEndpoint, is_valid_uid, is_valid_prefix, split_uid
  10. @pytest.mark.forked
  11. def test_store_get_experts():
  12. peers = [hivemind.DHT(start=True)]
  13. for i in range(10):
  14. neighbors_i = [f'{LOCALHOST}:{node.port}' for node in random.sample(peers, min(3, len(peers)))]
  15. peers.append(hivemind.DHT(initial_peers=neighbors_i, start=True))
  16. first_peer = random.choice(peers)
  17. other_peer = random.choice(peers)
  18. expert_uids = [f"my_expert.{i}" for i in range(50)]
  19. batch_size = 10
  20. for batch_start in range(0, len(expert_uids), batch_size):
  21. hivemind.declare_experts(first_peer, expert_uids[batch_start: batch_start + batch_size], 'localhost:1234')
  22. found = get_experts(other_peer, random.sample(expert_uids, 5) + ['foo', 'bar'])
  23. assert all(res is not None for res in found[:-2]), "Could not find some existing experts"
  24. assert all(res is None for res in found[-2:]), "Found non-existing experts"
  25. other_expert, other_port = "my_other_expert.1337", random.randint(1000, 9999)
  26. hivemind.declare_experts(other_peer, [other_expert], f'that_host:{other_port}')
  27. first_notfound, first_found = get_experts(first_peer, ['foobar', other_expert])
  28. assert isinstance(first_found, hivemind.RemoteExpert)
  29. assert first_found.endpoint == f'that_host:{other_port}'
  30. for peer in peers:
  31. peer.shutdown()
  32. @pytest.mark.forked
  33. def test_beam_search(dht_size=20, total_experts=128, batch_size=32, initial_peers=3, beam_size=4, parallel_rpc=4,
  34. grid_dims=(32, 32, 32)):
  35. dht = []
  36. for i in range(dht_size):
  37. neighbors_i = [f'{LOCALHOST}:{node.port}' for node in random.sample(dht, min(initial_peers, len(dht)))]
  38. dht.append(hivemind.DHT(start=True, initial_peers=neighbors_i, parallel_rpc=parallel_rpc))
  39. real_experts = sorted({
  40. 'expert.' + '.'.join([str(random.randint(0, dim - 1)) for dim in grid_dims])
  41. for _ in range(total_experts)
  42. })
  43. for batch_start in range(0, len(real_experts), batch_size):
  44. declare_experts(random.choice(dht), real_experts[batch_start: batch_start + batch_size], wait=True,
  45. endpoint=f"host{batch_start // batch_size}:{random.randint(0, 65536)}")
  46. neighbors_i = [f'{LOCALHOST}:{node.port}' for node in random.sample(dht, min(initial_peers, len(dht)))]
  47. you = hivemind.DHT(start=True, initial_peers=neighbors_i, parallel_rpc=parallel_rpc)
  48. beam_search = MoEBeamSearcher(you, 'expert.', grid_dims)
  49. for i in range(10):
  50. topk_experts = beam_search.find_best_experts([np.random.randn(dim) for dim in grid_dims], beam_size)
  51. assert all(isinstance(e, hivemind.RemoteExpert) for e in topk_experts)
  52. assert len(topk_experts) == beam_size
  53. for i in range(10):
  54. batch_experts = beam_search.batch_find_best_experts([np.random.randn(batch_size, dim) for dim in grid_dims],
  55. beam_size=beam_size)
  56. assert isinstance(batch_experts, list) and len(batch_experts) == batch_size
  57. assert all(isinstance(e, hivemind.RemoteExpert) for experts in batch_experts for e in experts)
  58. assert all(len(experts) == beam_size for experts in batch_experts)
  59. @pytest.mark.forked
  60. def test_dht_single_node():
  61. node = hivemind.DHT(start=True)
  62. beam_search = MoEBeamSearcher(node, 'expert.', grid_size=(10,))
  63. assert all(declare_experts(node, ['expert.1', 'expert.2', 'expert.3'], f"{hivemind.LOCALHOST}:1337").values())
  64. assert len(declare_experts(node, ["ffn.1", "ffn.2"], endpoint="that_place")) == 4
  65. assert len(declare_experts(node, ['e.1.2.3', 'e.1.2.5', 'e.2.0'], f"{hivemind.LOCALHOST}:42")) == 7
  66. for expert in get_experts(node, ['expert.3', 'expert.2']):
  67. assert expert.endpoint == f"{hivemind.LOCALHOST}:1337"
  68. assert all(declare_experts(node, ['expert.5', 'expert.2'], f"{hivemind.LOCALHOST}:1337").values())
  69. found_experts = beam_search.find_best_experts([(0., 1., 2., 3., 4., 5., 6., 7., 8.)], beam_size=2)
  70. assert len(found_experts) == 2 and [expert.uid for expert in found_experts] == ['expert.5', 'expert.3']
  71. successors = beam_search.get_active_successors(['e.1.2.', 'e.2.', 'e.4.5.'])
  72. assert len(successors['e.1.2.']) == 2
  73. assert successors['e.1.2.'][3] == UidEndpoint('e.1.2.3', f'{LOCALHOST}:42')
  74. assert successors['e.1.2.'][5] == UidEndpoint('e.1.2.5', f'{LOCALHOST}:42')
  75. assert len(successors['e.2.']) == 1 and successors['e.2.'][0] == UidEndpoint('e.2.0', f'{LOCALHOST}:42')
  76. assert successors['e.4.5.'] == {}
  77. initial_beam = beam_search.get_initial_beam((3, 2, 1, 0, -1, -2, -3), beam_size=3)
  78. assert len(initial_beam) == 3
  79. assert initial_beam[0][:2] == (2.0, 'expert.1.')
  80. assert initial_beam[1][:2] == (1.0, 'expert.2.')
  81. assert initial_beam[2][:2] == (0.0, 'expert.3.')
  82. with pytest.raises(AssertionError):
  83. beam_search = MoEBeamSearcher(node, 'expert.1.ffn', (2, 2))
  84. with pytest.raises(AssertionError):
  85. beam_search.get_active_successors(['e.1.2.', 'e.2', 'e.4.5.'])
  86. def test_uid_patterns():
  87. valid_experts = ["expert.1", "expert.0", "expert.0.0.1", "expert.1337", "ffn.12.34.56.78.90",
  88. "transformer.3.2.1.0", "transformer_encoder.2", "transformer::encoder.2", "T®@nsf0rmE®🤗.321",
  89. "🤗.321", "0.1.2", "00.1.2", "7070.3.2.1.0", "block2.1.23", "LAYER.1.0.1"]
  90. valid_prefixes = ["expert.", "e.1.", "e.2.", "e.1.2.3.", "ololo.123.456.789.10."]
  91. valid_prefixes.extend([f"{uid}." for uid in valid_experts])
  92. valid_prefixes.extend([split_uid(uid)[0] for uid in valid_experts])
  93. for uid in valid_experts:
  94. assert is_valid_uid(uid), f"UID {uid} is valid, but was perceived as invalid"
  95. for pfx in valid_prefixes:
  96. assert is_valid_prefix(pfx), f"Prefix {pfx} is valid, but was perceived as invalid"
  97. invalid = ["", ".", "expert.-1", "xxx.a", "expert.1x", "expert_ffn.1.abc1", "some.123.01", "expert.123.01",
  98. "e1", "e..1", "e", "e.1.2.3..4", "ffn.1..1", ".123", ".1.2.3.", ".expert", "transformer.encoder.2",
  99. "T®@nsf0rmE®.🤗.321", "layer::123", "expert.0.1.2.suffix", "0.1.2.suffix", "expert.1 something",
  100. "expert.1\n", "expert.1\n2", "expert.1 ", "expert.1\nexpert.2", "'expert.1'", '"expert.1"']
  101. invalid_experts = invalid + valid_prefixes + ["0", "123456"]
  102. invalid_prefixes = invalid + valid_experts + ["expert", ".🤗", ".expert"]
  103. for uid in invalid_experts:
  104. assert not is_valid_uid(uid), f"UID {uid} is not valid, but was perceived as valid"
  105. for pfx in invalid_prefixes:
  106. assert not is_valid_prefix(pfx), f"Prefix {pfx} is not valid, but was perceived as valid"
  107. @pytest.mark.forked
  108. @pytest.mark.asyncio
  109. async def test_negative_caching():
  110. peers = []
  111. for i in range(10):
  112. neighbors_i = [f'{LOCALHOST}:{node.port}' for node in random.sample(peers, min(3, len(peers)))]
  113. peers.append(hivemind.DHT(initial_peers=neighbors_i, cache_locally=False, start=True))
  114. writer_peer = random.choice(peers)
  115. assert all(hivemind.declare_experts(writer_peer, ['ffn.1.2.3', 'ffn.3.4.5'], 'myaddr:1234').values())
  116. neighbors_i = [f'{LOCALHOST}:{node.port}' for node in random.sample(peers, min(3, len(peers)))]
  117. neg_caching_peer = hivemind.DHT(initial_peers=neighbors_i, cache_locally=False, start=True)
  118. beam_search = MoEBeamSearcher(neg_caching_peer, uid_prefix='ffn.', grid_size=(10, 10, 10), negative_caching=True)
  119. # get prefixes by the peer with negative caching. Cache "no data" entries for ffn.0.*, ffn.2.*, ffn.4.*, ffn.5.*
  120. assert len(beam_search.get_initial_beam(scores=[.1, .2, .3, .4, .5, .6], beam_size=3)) == 2
  121. node = await hivemind.DHTNode.create(initial_peers=neighbors_i)
  122. fetched = await asyncio.gather(*(node.get(f'ffn.{i}.') for i in range(10)))
  123. for i in range(6):
  124. assert fetched[i] is not None, f"node should have cached ffn.{i}."
  125. for i in range(6, len(fetched)):
  126. assert fetched[i] is None, f"node shouldn't have cached ffn.{i}."