@@ -379,6 +379,98 @@ def mean_absolute_percentage_error(
379
379
return np .mean (absolute_percentage_diff )
380
380
381
381
382
+ def perplexity_loss (
383
+ y_true : np .ndarray , y_pred : np .ndarray , epsilon : float = 1e-7
384
+ ) -> float :
385
+ """
386
+ Calculate the perplexity for the y_true and y_pred.
387
+
388
+ Compute the Perplexity which useful in predicting language model
389
+ accuracy in Natural Language Processing (NLP.)
390
+ Perplexity is measure of how certain the model in its predictions.
391
+
392
+ Perplexity Loss = exp(-1/N (Σ ln(p(x)))
393
+
394
+ Reference:
395
+ https://en.wikipedia.org/wiki/Perplexity
396
+
397
+ Args:
398
+ y_true: Actual label encoded sentences of shape (batch_size, sentence_length)
399
+ y_pred: Predicted sentences of shape (batch_size, sentence_length, vocab_size)
400
+ epsilon: Small floating point number to avoid getting inf for log(0)
401
+
402
+ Returns:
403
+ Perplexity loss between y_true and y_pred.
404
+
405
+ >>> y_true = np.array([[1, 4], [2, 3]])
406
+ >>> y_pred = np.array(
407
+ ... [[[0.28, 0.19, 0.21 , 0.15, 0.15],
408
+ ... [0.24, 0.19, 0.09, 0.18, 0.27]],
409
+ ... [[0.03, 0.26, 0.21, 0.18, 0.30],
410
+ ... [0.28, 0.10, 0.33, 0.15, 0.12]]]
411
+ ... )
412
+ >>> perplexity_loss(y_true, y_pred)
413
+ 5.0247347775367945
414
+ >>> y_true = np.array([[1, 4], [2, 3]])
415
+ >>> y_pred = np.array(
416
+ ... [[[0.28, 0.19, 0.21 , 0.15, 0.15],
417
+ ... [0.24, 0.19, 0.09, 0.18, 0.27],
418
+ ... [0.30, 0.10, 0.20, 0.15, 0.25]],
419
+ ... [[0.03, 0.26, 0.21, 0.18, 0.30],
420
+ ... [0.28, 0.10, 0.33, 0.15, 0.12],
421
+ ... [0.30, 0.10, 0.20, 0.15, 0.25]],]
422
+ ... )
423
+ >>> perplexity_loss(y_true, y_pred)
424
+ Traceback (most recent call last):
425
+ ...
426
+ ValueError: Sentence length of y_true and y_pred must be equal.
427
+ >>> y_true = np.array([[1, 4], [2, 11]])
428
+ >>> y_pred = np.array(
429
+ ... [[[0.28, 0.19, 0.21 , 0.15, 0.15],
430
+ ... [0.24, 0.19, 0.09, 0.18, 0.27]],
431
+ ... [[0.03, 0.26, 0.21, 0.18, 0.30],
432
+ ... [0.28, 0.10, 0.33, 0.15, 0.12]]]
433
+ ... )
434
+ >>> perplexity_loss(y_true, y_pred)
435
+ Traceback (most recent call last):
436
+ ...
437
+ ValueError: Label value must not be greater than vocabulary size.
438
+ >>> y_true = np.array([[1, 4]])
439
+ >>> y_pred = np.array(
440
+ ... [[[0.28, 0.19, 0.21 , 0.15, 0.15],
441
+ ... [0.24, 0.19, 0.09, 0.18, 0.27]],
442
+ ... [[0.03, 0.26, 0.21, 0.18, 0.30],
443
+ ... [0.28, 0.10, 0.33, 0.15, 0.12]]]
444
+ ... )
445
+ >>> perplexity_loss(y_true, y_pred)
446
+ Traceback (most recent call last):
447
+ ...
448
+ ValueError: Batch size of y_true and y_pred must be equal.
449
+ """
450
+
451
+ vocab_size = y_pred .shape [2 ]
452
+
453
+ if y_true .shape [0 ] != y_pred .shape [0 ]:
454
+ raise ValueError ("Batch size of y_true and y_pred must be equal." )
455
+ if y_true .shape [1 ] != y_pred .shape [1 ]:
456
+ raise ValueError ("Sentence length of y_true and y_pred must be equal." )
457
+ if np .max (y_true ) > vocab_size :
458
+ raise ValueError ("Label value must not be greater than vocabulary size." )
459
+
460
+ # Matrix to select prediction value only for true class
461
+ filter_matrix = np .array (
462
+ [[list (np .eye (vocab_size )[word ]) for word in sentence ] for sentence in y_true ]
463
+ )
464
+
465
+ # Getting the matrix containing prediction for only true class
466
+ true_class_pred = np .sum (y_pred * filter_matrix , axis = 2 ).clip (epsilon , 1 )
467
+
468
+ # Calculating perplexity for each sentence
469
+ perp_losses = np .exp (np .negative (np .mean (np .log (true_class_pred ), axis = 1 )))
470
+
471
+ return np .mean (perp_losses )
472
+
473
+
382
474
if __name__ == "__main__" :
383
475
import doctest
384
476
0 commit comments