textslinger 0.2.0__tar.gz → 0.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. {textslinger-0.2.0/textslinger.egg-info → textslinger-0.2.3}/PKG-INFO +1 -1
  2. {textslinger-0.2.0 → textslinger-0.2.3}/pyproject.toml +17 -17
  3. textslinger-0.2.3/tests/conftest.py +65 -0
  4. {textslinger-0.2.0 → textslinger-0.2.3}/tests/test_causal_byte.py +240 -56
  5. {textslinger-0.2.0 → textslinger-0.2.3}/tests/test_causal_subword_local.py +2 -10
  6. {textslinger-0.2.0 → textslinger-0.2.3}/tests/test_causal_subword_repoid.py +1 -3
  7. {textslinger-0.2.0 → textslinger-0.2.3}/tests/test_constants.py +12 -6
  8. {textslinger-0.2.0 → textslinger-0.2.3}/tests/test_keystroke_savings_ngram.py +4 -6
  9. {textslinger-0.2.0 → textslinger-0.2.3}/tests/test_ngram.py +50 -133
  10. textslinger-0.2.3/tests/test_ngram_predict_words.py +706 -0
  11. textslinger-0.2.3/textslinger/__init__.py +25 -0
  12. {textslinger-0.2.0 → textslinger-0.2.3}/textslinger/causal_byte.py +135 -102
  13. {textslinger-0.2.0 → textslinger-0.2.3}/textslinger/causal_subword.py +69 -100
  14. {textslinger-0.2.0 → textslinger-0.2.3}/textslinger/eval_helper.py +42 -49
  15. {textslinger-0.2.0 → textslinger-0.2.3}/textslinger/language_model.py +43 -30
  16. textslinger-0.2.3/textslinger/ngram.py +642 -0
  17. {textslinger-0.2.0 → textslinger-0.2.3/textslinger.egg-info}/PKG-INFO +1 -1
  18. {textslinger-0.2.0 → textslinger-0.2.3}/textslinger.egg-info/SOURCES.txt +1 -0
  19. {textslinger-0.2.0 → textslinger-0.2.3}/textslinger.egg-info/top_level.txt +0 -1
  20. textslinger-0.2.0/tests/conftest.py +0 -29
  21. textslinger-0.2.0/textslinger/__init__.py +0 -2
  22. textslinger-0.2.0/textslinger/ngram.py +0 -336
  23. {textslinger-0.2.0 → textslinger-0.2.3}/LICENSE +0 -0
  24. {textslinger-0.2.0 → textslinger-0.2.3}/README.md +0 -0
  25. {textslinger-0.2.0 → textslinger-0.2.3}/setup.cfg +0 -0
  26. {textslinger-0.2.0 → textslinger-0.2.3}/tests/__init__.py +0 -0
  27. {textslinger-0.2.0 → textslinger-0.2.3}/tests/helpers.py +0 -0
  28. {textslinger-0.2.0 → textslinger-0.2.3}/tests/test_causal_subword_basic.py +0 -0
  29. {textslinger-0.2.0 → textslinger-0.2.3}/textslinger/exceptions.py +0 -0
  30. {textslinger-0.2.0 → textslinger-0.2.3}/textslinger/helpers.py +0 -0
  31. {textslinger-0.2.0 → textslinger-0.2.3}/textslinger.egg-info/dependency_links.txt +0 -0
  32. {textslinger-0.2.0 → textslinger-0.2.3}/textslinger.egg-info/requires.txt +0 -0
  33. {textslinger-0.2.0 → textslinger-0.2.3}/textslinger.egg-info/zip-safe +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: textslinger
3
- Version: 0.2.0
3
+ Version: 0.2.3
4
4
  Summary: TextSlinger: Fast and accurate text predictions in Python
5
5
  Author-email: Keith Vertanen <vertanen@mtu.edu>, Dylan Gaines <dgaine20@kennesaw.edu>, Soufia Bahmani <sbahmani@mtu.edu>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ requires = ["setuptools>=77.0.3", "wheel>=0.37.1"]
4
4
 
5
5
  [project]
6
6
  name = "textslinger"
7
- version = "0.2.0"
7
+ dynamic = ["version"]
8
8
  authors = [
9
9
  {name="Keith Vertanen", email="vertanen@mtu.edu"},
10
10
  {name="Dylan Gaines", email="dgaine20@kennesaw.edu"},
@@ -16,14 +16,14 @@ license-files = ["LICENSE"]
16
16
  license = "MIT"
17
17
  requires-python = ">=3.10,<3.11"
18
18
  classifiers = [
19
- 'Development Status :: 3 - Alpha',
20
- 'Topic :: Scientific/Engineering :: Human Machine Interfaces',
21
- 'Natural Language :: English',
22
- 'Intended Audience :: Science/Research',
23
- 'Intended Audience :: End Users/Desktop',
24
- 'Intended Audience :: Developers',
25
- 'Programming Language :: Python',
26
- 'Programming Language :: Python :: 3.10',
19
+ "Development Status :: 3 - Alpha",
20
+ "Topic :: Scientific/Engineering :: Human Machine Interfaces",
21
+ "Natural Language :: English",
22
+ "Intended Audience :: Science/Research",
23
+ "Intended Audience :: End Users/Desktop",
24
+ "Intended Audience :: Developers",
25
+ "Programming Language :: Python",
26
+ "Programming Language :: Python :: 3.10",
27
27
  ]
28
28
 
29
29
  dependencies = [
@@ -60,21 +60,21 @@ release = [
60
60
  ]
61
61
 
62
62
  [project.urls]
63
- "Homepage" = "https://github.com/kdv123/textslinger"
64
- "Source" = "https://github.com/kdv123/textslinger"
63
+ Homepage = "https://github.com/kdv123/textslinger"
64
+ Source = "https://github.com/kdv123/textslinger"
65
65
 
66
66
  [tool.setuptools]
67
- # https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html
68
67
  platforms = ["Linux", "Windows", "Mac OS-X"]
69
68
  include-package-data = true
70
69
  zip-safe = true
71
70
 
72
- # Python modules and packages that are included in the
73
- # distribution package (and therefore become importable)
71
+ [tool.setuptools.dynamic]
72
+ version = {attr = "textslinger.__version__"}
73
+
74
+ # Python modules and packages included in the distribution
74
75
  [tool.setuptools.packages.find]
75
76
  exclude = ["examples", "scripts"]
76
77
 
77
78
  [tool.black]
78
- line-length = 88
79
- target-version = ["py310"]
80
-
79
+ line-length = 100
80
+ target-version = ["py310"]
@@ -0,0 +1,65 @@
1
+ """
2
+ For configuration options related to running the test suite
3
+ """
4
+
5
+ import pytest
6
+ from textslinger.helpers import Device, Precision
7
+
8
+
9
+ def pytest_addoption(parser):
10
+ parser.addoption("--device", type=Device, choices=list(Device), default=Device.CPU)
11
+ parser.addoption(
12
+ "--precision", type=Precision, choices=list(Precision), default=Precision.FP32
13
+ )
14
+ parser.addoption(
15
+ "--run-optional",
16
+ action="store_true",
17
+ default=False,
18
+ help="Run optional tests that are skipped by default",
19
+ )
20
+ parser.addoption(
21
+ "--run-long",
22
+ action="store_true",
23
+ default=False,
24
+ help="Run long tests that are skipped by default",
25
+ )
26
+ parser.addoption(
27
+ "--rewrite-ref-files",
28
+ action="store_true",
29
+ default=False,
30
+ help="Rewrite reference files, e.g. files with log probabilities obtained from a model",
31
+ )
32
+
33
+
34
+ def pytest_runtest_setup(item):
35
+ # Skip optional tests by default
36
+ if "optional" in item.keywords and not item.config.getoption("--run-optional"):
37
+ pytest.skip("Skipping optional test by default")
38
+
39
+ # Skip long tests by default unless --run-long is provided
40
+ if "long" in item.keywords and not item.config.getoption("--run-long"):
41
+ pytest.skip("Skipping long test by default")
42
+
43
+
44
+ @pytest.fixture(scope="session")
45
+ def device(request):
46
+ """
47
+ Fixture to determine whether tests run on auto, cpu, cuda, or mps
48
+ """
49
+ return request.config.getoption("--device")
50
+
51
+
52
+ @pytest.fixture(scope="session")
53
+ def precision(request):
54
+ """
55
+ Fixture to set the precision models are loaded with
56
+ """
57
+ return request.config.getoption("--precision")
58
+
59
+
60
+ @pytest.fixture(scope="session")
61
+ def rewrite_ref_files(request):
62
+ """
63
+ Fixture to request some tests rewrite their reference files
64
+ """
65
+ return request.config.getoption("--rewrite-ref-files")
@@ -1,21 +1,30 @@
1
1
  from textslinger.causal_byte import (
2
2
  CausalByteLanguageModel,
3
3
  InvalidLanguageModelException,
4
+ DecodeConfigByte,
4
5
  )
5
6
  import pytest
6
- from tests.test_constants import (
7
- BYTE_LLM,
8
- BYTE_LLM_PARAMS,
9
- BYTE_LLM_TOKENS,
10
- SYMBOLS_LOWERCASE_SPACE_APOSTROPHE_ENDINGS,
11
- SYMBOLS_LOWERCASE_SPACE,
7
+ from tests.helpers import (
8
+ check_match,
9
+ check_sum_to_one,
10
+ suppress_stderr_fd,
11
+ read_float_list,
12
12
  )
13
- from tests.helpers import check_match, check_sum_to_one, suppress_stderr_fd
13
+ from tests.test_constants import *
14
+ from textslinger.eval_helper import (
15
+ load_phrases_plaintext,
16
+ filter_phrases,
17
+ normalize_phrases,
18
+ )
19
+ import copy
14
20
 
15
21
  # NOTE: if the device is changed to MPS, many tests fail because the probabilities and/or hypotheses change
16
22
  # Currently set high enough to pass on various CPUs and GPUs
17
23
  EPSILON_BYTE_PREDICT = 0.04
18
24
 
25
+ # Higher epsilon for scoring items and comparing total log prob
26
+ EPSILON_BYTE_LOG_PROB = 0.2
27
+
19
28
 
20
29
  @pytest.fixture(scope="module")
21
30
  def shared_lm(device, precision):
@@ -34,6 +43,58 @@ def shared_lm(device, precision):
34
43
  yield lm
35
44
 
36
45
 
46
+ @pytest.fixture(scope="module")
47
+ def config_default():
48
+ """
49
+ A default set for our tests since we don't want to rely on the defaults in the class
50
+ since they might get changed
51
+ """
52
+ config = DecodeConfigByte()
53
+ config.max_word_hypotheses = 256
54
+ config.beam_best = 8.0
55
+ config.beam_search_max = 300
56
+ config.max_word_len = 25
57
+ config.lm_scale = 1.0
58
+ config.ins_penalty = -20.0
59
+ config.ins_after_input_penalty = 0.0
60
+ config.del_penalty = -20.0
61
+ config.batch_size = None
62
+ yield config
63
+
64
+
65
+ @pytest.fixture(scope="module")
66
+ def config_narrow(config_default):
67
+ """
68
+ Narrow beam for fast decoding
69
+ """
70
+ config = copy.deepcopy(config_default)
71
+ config.beam_best = 5.0
72
+ config.beam_search_max = 4
73
+ yield config
74
+
75
+
76
+ @pytest.fixture(scope="module")
77
+ def config_narrower(config_default):
78
+ """
79
+ Narrow beam for fast decoding
80
+ """
81
+ config = copy.deepcopy(config_default)
82
+ config.beam_best = 1.3
83
+ config.beam_search_max = 4
84
+ yield config
85
+
86
+
87
+ @pytest.fixture(scope="module")
88
+ def config_narrowest(config_default):
89
+ """
90
+ Narrow beam for fast decoding
91
+ """
92
+ config = copy.deepcopy(config_default)
93
+ config.beam_best = 0.5
94
+ config.beam_search_max = 4
95
+ yield config
96
+
97
+
37
98
  def test_bogus_model_name():
38
99
  """
39
100
  Attempt to load with a bogus model name
@@ -65,9 +126,7 @@ def test_lowercase_symbol_set(shared_lm):
65
126
 
66
127
  # First of all size should be correct
67
128
  assert len(shared_lm.symbol_set) == len(SYMBOLS_LOWERCASE_SPACE_APOSTROPHE_ENDINGS)
68
- assert len(shared_lm.symbol_set_lower) == len(
69
- SYMBOLS_LOWERCASE_SPACE_APOSTROPHE_ENDINGS
70
- )
129
+ assert len(shared_lm.symbol_set_lower) == len(SYMBOLS_LOWERCASE_SPACE_APOSTROPHE_ENDINGS)
71
130
 
72
131
  # Check all symbols are in both the normal and lowercase symbol sets
73
132
  for symbol in SYMBOLS_LOWERCASE_SPACE_APOSTROPHE_ENDINGS:
@@ -292,7 +351,7 @@ def test_predict_symbol_endings(shared_lm):
292
351
  check_sum_to_one(predictions)
293
352
 
294
353
 
295
- def test_predict_words_no_context(shared_lm):
354
+ def test_predict_words_no_context(shared_lm, config_narrowest):
296
355
  """
297
356
  Make word predictions in the middle of a word that is likely a contraction
298
357
  """
@@ -300,14 +359,13 @@ def test_predict_words_no_context(shared_lm):
300
359
  references = ["title", "what", "let"]
301
360
  predictions = shared_lm.predict_words(
302
361
  left_context="",
303
- beam_logp_best=0.5,
304
- beam_search_max=4,
362
+ config=config_narrowest,
305
363
  word_end_symbols=[" ", ".", ",", "!", "?"],
306
364
  )
307
365
  check_match(predictions, references)
308
366
 
309
367
 
310
- def test_predict_words_full_word(shared_lm):
368
+ def test_predict_words_full_word(shared_lm, config_narrowest):
311
369
  """
312
370
  Make word predictions at the end of a word but merge word end symbols
313
371
  This causes all the end of word symbol hypotheses to collapse into blank hypothesis
@@ -316,14 +374,13 @@ def test_predict_words_full_word(shared_lm):
316
374
  references = [""]
317
375
  predictions = shared_lm.predict_words(
318
376
  left_context="Bye bye",
319
- beam_logp_best=5.0,
320
- beam_search_max=4,
377
+ config=config_narrowest,
321
378
  word_end_symbols=[" ", ".", ",", "!", "?"],
322
379
  )
323
380
  check_match(predictions, references)
324
381
 
325
382
 
326
- def test_predict_words_full_word_include_ending(shared_lm):
383
+ def test_predict_words_full_word_include_ending(shared_lm, config_narrow):
327
384
  """
328
385
  Make word predictions at the end of a word and request the ending symbol be included in results
329
386
  """
@@ -331,15 +388,14 @@ def test_predict_words_full_word_include_ending(shared_lm):
331
388
  references = [" ", ",", "!", ".", "?"]
332
389
  predictions = shared_lm.predict_words(
333
390
  left_context="Bye bye",
334
- beam_logp_best=5.0,
335
- beam_search_max=4,
391
+ config=config_narrow,
336
392
  include_ending_symbol=True,
337
393
  word_end_symbols=[" ", ".", ",", "!", "?"],
338
394
  )
339
395
  check_match(predictions, references)
340
396
 
341
397
 
342
- def test_predict_words_partial_word_with_log_probs(shared_lm):
398
+ def test_predict_words_partial_word_with_log_probs(shared_lm, config_narrow):
343
399
  """
344
400
  Make word predictions at near the end of a word
345
401
  This also checks the log probs in the results
@@ -348,15 +404,14 @@ def test_predict_words_partial_word_with_log_probs(shared_lm):
348
404
  references = [("e", -0.11844532689588708), ("", -4.672425501089463)]
349
405
  predictions = shared_lm.predict_words(
350
406
  left_context="Bye by",
351
- beam_logp_best=5.0,
352
- beam_search_max=4,
407
+ config=config_narrow,
353
408
  return_log_probs=True,
354
409
  word_end_symbols=[" ", ".", ",", "!", "?"],
355
410
  )
356
411
  check_match(predictions, references, epsilon=EPSILON_BYTE_PREDICT)
357
412
 
358
413
 
359
- def test_predict_words_partial_word_include_ending(shared_lm):
414
+ def test_predict_words_partial_word_include_ending(shared_lm, config_narrow):
360
415
  """
361
416
  Make word predictions at near the end of a word and request the ending symbol be included in results
362
417
  """
@@ -364,60 +419,59 @@ def test_predict_words_partial_word_include_ending(shared_lm):
364
419
  references = ["e ", "e,", "e!", "e.", " ", "e?"]
365
420
  predictions = shared_lm.predict_words(
366
421
  left_context="Bye by",
367
- beam_logp_best=5.0,
368
- beam_search_max=4,
422
+ config=config_narrow,
369
423
  include_ending_symbol=True,
370
424
  word_end_symbols=[" ", ".", ",", "!", "?"],
371
425
  )
372
426
  check_match(predictions, references)
373
427
 
374
428
 
375
- def test_predict_words_contraction(shared_lm):
429
+ def test_predict_words_contraction(shared_lm, config_narrower):
376
430
  """
377
431
  Make word predictions in the middle of a word that is likely a contraction
378
432
  """
379
433
  shared_lm.change_symbol_set(SYMBOLS_LOWERCASE_SPACE_APOSTROPHE_ENDINGS)
380
- references = ["d", "dn't", "g"]
434
+ references = ["d", "dn't", "e", "", "n"]
381
435
  predictions = shared_lm.predict_words(
382
436
  left_context="I do not know why you di",
383
- beam_logp_best=1.3,
437
+ config=config_narrower,
384
438
  word_end_symbols=[" ", ".", ",", "!", "?"],
385
439
  )
386
440
  check_match(predictions, references)
387
441
 
388
442
 
389
- def test_predict_words_contraction_no_apostrophe(shared_lm):
443
+ def test_predict_words_contraction_no_apostrophe(shared_lm, config_narrower):
390
444
  """
391
445
  Make word predictions in the middle of a word that is likely a contraction
392
446
  This version doesn't have apostrophe in the valid symbols
393
447
  """
394
448
  shared_lm.change_symbol_set(SYMBOLS_LOWERCASE_SPACE)
395
- references = ["d", "", "n"]
449
+ references = ["d", "e", "", "n"]
396
450
  predictions = shared_lm.predict_words(
397
451
  left_context="I do not know why you di",
398
- beam_logp_best=1.3,
452
+ config=config_narrower,
399
453
  )
400
454
  check_match(predictions, references, expected_predictions=len(references))
401
455
 
402
456
 
403
- def test_predict_words_contraction_prune_max_words(shared_lm):
457
+ def test_predict_words_contraction_prune_max_words(shared_lm, config_narrower):
404
458
  """
405
459
  Make word predictions in the middle of a word that is likely a contraction
406
460
  This version prunes based on the maximum number of word hypotheses
407
461
  """
408
462
  shared_lm.change_symbol_set(SYMBOLS_LOWERCASE_SPACE_APOSTROPHE_ENDINGS)
409
- references = ["", "n", "m"]
410
- max_word_hypos = 3
463
+ config = copy.deepcopy(config_narrower)
464
+ config.max_word_hypotheses = 3
465
+ references = ["e", "", "n"]
411
466
  predictions = shared_lm.predict_words(
412
467
  left_context="I do not know why you di",
413
- beam_logp_best=1.3,
414
- max_word_hypotheses=max_word_hypos,
468
+ config=config,
415
469
  word_end_symbols=[" ", ".", ",", "!", "?"],
416
470
  )
417
- check_match(predictions, references, expected_predictions=max_word_hypos)
471
+ check_match(predictions, references, expected_predictions=config.max_word_hypotheses)
418
472
 
419
473
 
420
- def test_predict_words_mixed_case(shared_lm):
474
+ def test_predict_words_mixed_case(shared_lm, config_narrower):
421
475
  """
422
476
  Try and predict a word that is in mixed case
423
477
  """
@@ -425,15 +479,14 @@ def test_predict_words_mixed_case(shared_lm):
425
479
  references = ["iPhone", "Apple"]
426
480
  predictions = shared_lm.predict_words(
427
481
  left_context="On Friday Apple released their latest phone the ",
428
- beam_logp_best=1.3,
429
- beam_search_max=4,
482
+ config=config_narrower,
430
483
  predict_lower=False,
431
484
  word_end_symbols=[" ", ".", ",", "!", "?"],
432
485
  )
433
486
  check_match(predictions, references)
434
487
 
435
488
 
436
- def test_predict_words_extra_endings(shared_lm):
489
+ def test_predict_words_extra_endings(shared_lm, config_narrowest):
437
490
  """
438
491
  Predict the very end of a word but with extra word endings enabled
439
492
  """
@@ -441,31 +494,30 @@ def test_predict_words_extra_endings(shared_lm):
441
494
  references = ["y", ""]
442
495
  predictions = shared_lm.predict_words(
443
496
  left_context="How are you feeling toda",
444
- beam_logp_best=0.5,
497
+ config=config_narrowest,
445
498
  word_end_symbols=[" ", ".", ",", "!", "?"],
446
- beam_search_max=4,
447
499
  )
448
500
  check_match(predictions, references, expected_predictions=len(references))
449
501
 
450
502
 
451
- def test_predict_words_extra_endings_batch(shared_lm):
503
+ def test_predict_words_extra_endings_batch(shared_lm, config_narrowest):
452
504
  """
453
505
  Predict the very end of a word but with extra word endings enabled
454
506
  Uses a smaller mini batch size during inference
455
507
  """
456
508
  shared_lm.change_symbol_set(SYMBOLS_LOWERCASE_SPACE_APOSTROPHE_ENDINGS)
509
+ config = copy.deepcopy(config_narrowest)
510
+ config.batch_size = 8
457
511
  references = ["y", ""]
458
512
  predictions = shared_lm.predict_words(
459
513
  left_context="How are you feeling toda",
460
- beam_logp_best=0.5,
514
+ config=config,
461
515
  word_end_symbols=[" ", ".", ",", "!", "?"],
462
- beam_search_max=4,
463
- batch_size=8,
464
516
  )
465
517
  check_match(predictions, references, expected_predictions=len(references))
466
518
 
467
519
 
468
- def test_predict_words_curly_apostrophe(shared_lm):
520
+ def test_predict_words_curly_apostrophe(shared_lm, config_narrowest):
469
521
  """
470
522
  Try and predict the ending of a word with curly apostrophe
471
523
  """
@@ -473,14 +525,13 @@ def test_predict_words_curly_apostrophe(shared_lm):
473
525
  references = ["'t", "t", ""]
474
526
  predictions = shared_lm.predict_words(
475
527
  left_context="You didn’t do it and I also didn",
476
- beam_logp_best=0.5,
528
+ config=config_narrowest,
477
529
  word_end_symbols=[" ", ".", ",", "!", "?"],
478
- beam_search_max=4,
479
530
  )
480
531
  check_match(predictions, references, expected_predictions=len(references))
481
532
 
482
533
 
483
- def test_predict_words_english(shared_lm):
534
+ def test_predict_words_english(shared_lm, config_narrowest):
484
535
  """
485
536
  Prediction to compare with that uses English left context
486
537
  """
@@ -489,14 +540,13 @@ def test_predict_words_english(shared_lm):
489
540
  references = ["d"]
490
541
  predictions = shared_lm.predict_words(
491
542
  left_context="Good day and I love you very much. Ad",
492
- beam_logp_best=0.5,
493
- beam_search_max=4,
543
+ config=config_narrowest,
494
544
  word_end_symbols=[" ", ".", ",", "!", "?"],
495
545
  )
496
546
  check_match(predictions, references)
497
547
 
498
548
 
499
- def test_predict_words_spanish(shared_lm):
549
+ def test_predict_words_spanish(shared_lm, config_narrowest):
500
550
  """
501
551
  Try and predict a word in Spanish using Spanish left context
502
552
  """
@@ -505,8 +555,142 @@ def test_predict_words_spanish(shared_lm):
505
555
  references = ["ios"]
506
556
  predictions = shared_lm.predict_words(
507
557
  left_context="¡Buen día y te amo mucho! Ad",
508
- beam_logp_best=0.5,
509
- beam_search_max=4,
558
+ config=config_narrowest,
510
559
  word_end_symbols=[" ", ".", ",", "!", "?"],
511
560
  )
512
561
  check_match(predictions, references)
562
+
563
+
564
+ def test_score_item(shared_lm):
565
+ """
566
+ Test computing the log prob (base e) for a single sentence
567
+ """
568
+ score = shared_lm.score_item(STR_PEOPLE_SHOCKED)
569
+ assert score == pytest.approx(LOG_PROB_BYTE_PEOPLE_SHOCKED, abs=EPSILON_BYTE_LOG_PROB)
570
+
571
+
572
+ def test_score_item_and_extension(shared_lm):
573
+ """
574
+ Score sentence and then again with additional character on end
575
+ Verify that this matches adding prediction of that single character
576
+ """
577
+ add_char = " "
578
+ score = shared_lm.score_item(STR_PEOPLE_SHOCKED)
579
+ score_plus_space = shared_lm.score_item(STR_PEOPLE_SHOCKED + add_char)
580
+ assert score > score_plus_space
581
+ char_to_log_prob = dict(
582
+ shared_lm.predict(evidence=list(STR_PEOPLE_SHOCKED), unnormalized_logprobs=True)
583
+ )
584
+ score_plus_predict = score + char_to_log_prob[add_char]
585
+ assert score_plus_predict == pytest.approx(score_plus_space, abs=EPSILON_BYTE_LOG_PROB)
586
+
587
+
588
+ def test_score_items_identical(shared_lm):
589
+ """
590
+ Test computing the log prob (base e) for multiple identical sentences
591
+ Do inference in one big batch
592
+ """
593
+ num_repeats = 7
594
+ items = [STR_PEOPLE_SHOCKED] * num_repeats
595
+ scores = shared_lm.score_items(items=items, batch_size=None)
596
+ assert len(scores) == num_repeats
597
+ for score in scores:
598
+ assert score == pytest.approx(LOG_PROB_BYTE_PEOPLE_SHOCKED, abs=EPSILON_BYTE_LOG_PROB)
599
+
600
+
601
+ def test_score_items_identical_batch_size1(shared_lm):
602
+ """
603
+ Test computing the log prob (base e) for multiple identical sentences
604
+ Do inference one item at a time
605
+ """
606
+ num_repeats = 7
607
+ items = [STR_PEOPLE_SHOCKED] * num_repeats
608
+ scores = shared_lm.score_items(items=items, batch_size=1)
609
+ assert len(scores) == num_repeats
610
+ for score in scores:
611
+ assert score == pytest.approx(LOG_PROB_BYTE_PEOPLE_SHOCKED, abs=EPSILON_BYTE_LOG_PROB)
612
+
613
+
614
+ def test_score_items_identical_batch_size2(shared_lm):
615
+ """
616
+ Test computing the log prob (base e) for multiple identical sentences
617
+ Do inference one item at a time
618
+ """
619
+ num_repeats = 7
620
+ items = [STR_PEOPLE_SHOCKED] * num_repeats
621
+ scores = shared_lm.score_items(items=items, batch_size=2)
622
+ assert len(scores) == num_repeats
623
+ for score in scores:
624
+ assert score == pytest.approx(LOG_PROB_BYTE_PEOPLE_SHOCKED, abs=EPSILON_BYTE_LOG_PROB)
625
+
626
+
627
+ def test_score_items_alternating(shared_lm):
628
+ """
629
+ Test computing the log prob (base e) for multiple alternating sentences
630
+ """
631
+ # Create a list with alternating sentences with known log probs
632
+ num_repeats = 7
633
+ items = []
634
+ for _ in range(num_repeats):
635
+ items.append(STR_PEOPLE_SHOCKED)
636
+ items.append(STR_LOWERCASE_LETTERS_SPACE)
637
+ scores = shared_lm.score_items(items=items)
638
+
639
+ for i in range(len(items)):
640
+ if i % 2 == 0:
641
+ assert scores[i] == pytest.approx(
642
+ LOG_PROB_BYTE_PEOPLE_SHOCKED, abs=EPSILON_BYTE_LOG_PROB
643
+ )
644
+ else:
645
+ assert scores[i] == pytest.approx(
646
+ LOG_PROB_BYTE_LOWERCASE_LETTERS_SPACE, abs=EPSILON_BYTE_LOG_PROB
647
+ )
648
+
649
+
650
+ def test_score_items_empty(shared_lm):
651
+ """
652
+ Test asking for scoring of empty list
653
+ """
654
+ scores = shared_lm.score_items(items=[])
655
+ assert len(scores) == 0
656
+
657
+
658
+ def test_score_items_order(shared_lm):
659
+ """
660
+ Score sentences that have an obvious order of probability
661
+ """
662
+ items = [
663
+ "The United States of America",
664
+ "the united states of america",
665
+ "The United State of America",
666
+ "The United State of Alabama",
667
+ "The United State of Amerika",
668
+ "The United State of_Amerika",
669
+ ]
670
+ scores = shared_lm.score_items(items=items)
671
+ print(scores)
672
+ for i in range(1, len(items)):
673
+ assert scores[i - 1] > scores[i]
674
+
675
+
676
+ @pytest.mark.long
677
+ def test_score_items_comm2(shared_lm, rewrite_ref_files):
678
+ """
679
+ Compute the log prob of each sentence in the COMM2 test set
680
+ We filter to sentences without numbers, lowercase, and strip symbols other than apostrophe
681
+ Compare against log probs obtained from a previous run of the model
682
+ """
683
+ phrases = load_phrases_plaintext(filename=COMM2_PHRASES, drop_first_col=True)
684
+ phrases = filter_phrases(phrases=phrases, drop_numbers=True)
685
+ phrases, _ = normalize_phrases(phrases=phrases, lower=True, strip_symbols=True)
686
+ scores = shared_lm.score_items(items=phrases, batch_size=32)
687
+ if rewrite_ref_files:
688
+ print(f"Rewriting log probs to: {COMM2_BYTE_LOG_PROBS}")
689
+ with open(COMM2_BYTE_LOG_PROBS, "w") as f:
690
+ for lp in scores:
691
+ f.write(f"{lp}\n")
692
+ known_log_probs = read_float_list(filename=COMM2_BYTE_LOG_PROBS)
693
+ assert len(scores) == len(known_log_probs)
694
+ for i in range(len(scores)):
695
+ # Note: we need a bit larger epsilon to pass on both CPU and CUDA devices
696
+ assert scores[i] == pytest.approx(known_log_probs[i], abs=2.0)
@@ -67,9 +67,7 @@ class TestCausalSubwordLocal(unittest.TestCase):
67
67
  "Distribution should not be uniform even without context.",
68
68
  )
69
69
 
70
- self.assertAlmostEqual(
71
- sum(probs), 1, places=3, msg="Probabilities should sum to 1."
72
- )
70
+ self.assertAlmostEqual(sum(probs), 1, places=3, msg="Probabilities should sum to 1.")
73
71
 
74
72
  def test_predict_middle_of_word(self):
75
73
  symbol_probs = self.lm.predict(evidence=list("This is a tes"))
@@ -77,9 +75,7 @@ class TestCausalSubwordLocal(unittest.TestCase):
77
75
 
78
76
  self.assertTrue(len(set(probs)) > 1, "Distribution should not be uniform.")
79
77
 
80
- self.assertAlmostEqual(
81
- sum(probs), 1, places=3, msg="Probabilities should sum to 1."
82
- )
78
+ self.assertAlmostEqual(sum(probs), 1, places=3, msg="Probabilities should sum to 1.")
83
79
 
84
80
  most_likely_sym, most_likely_prob = symbol_probs[0]
85
81
  self.assertAlmostEqual(
@@ -97,15 +93,11 @@ class TestCausalSubwordLocal(unittest.TestCase):
97
93
 
98
94
  """Word Prediction Tests"""
99
95
 
100
- @pytest.mark.hf
101
96
  def test_local_model_predict_words_small_beam(self):
102
97
 
103
98
  # Small, fast query: we only assert that we get at least one completion.
104
99
  words = self.lm.predict_words(
105
100
  "the ",
106
- nbest=3,
107
- max_word_len=12,
108
- max_word_hypotheses=64,
109
101
  return_log_probs=False,
110
102
  )
111
103
  assert isinstance(words, list) and len(words) > 0
@@ -22,7 +22,5 @@ def test_load_predict_word():
22
22
  device=Device.CPU,
23
23
  precision=Precision.FP32,
24
24
  )
25
- words = lm.predict_words(
26
- "a ", nbest=2, max_word_len=10, max_word_hypotheses=32, return_log_probs=False
27
- )
25
+ words = lm.predict_words("a ", nbest=2, return_log_probs=False)
28
26
  assert isinstance(words, list) and len(words) > 0