Модель посимвольной генерации ответа чатбота была кратко описана
здесь, а ее исходный текст лежит
тут.
Кроме финальной оценки качества модели через per instance accuracy (доля полностью правильно сгенерированных ответов, когда один неверный символ относит ответ к невалидным), я добавил туда расширенную диагностику с помощью
sklearn.metrics.classification_report, см. строки 693 и 694. Вот что она показывает:
precision recall f1-score support
\r 0.96 0.98 0.97 10418
- 0.00 0.00 0.00 2
5 0.97 0.98 0.97 241
0.95 0.91 0.93 4937
% 1.00 1.00 1.00 1
0 0.98 1.00 0.99 174
ъ 1.00 1.00 1.00 16
3 0.98 0.99 0.99 263
б 0.93 0.96 0.95 1053
а 0.97 0.97 0.97 6555
г 0.95 0.94 0.94 1065
в 0.95 0.95 0.95 2814
е 0.96 0.96 0.96 4879
д 0.95 0.95 0.95 2067
з 0.94 0.93 0.93 934
ж 0.94 0.95 0.94 493
й 0.95 0.95 0.95 961
и 0.96 0.96 0.96 5134
л 0.96 0.96 0.96 2975
к 0.96 0.95 0.95 3284
н 0.96 0.96 0.96 4929
м 0.95 0.95 0.95 2738
п 0.92 0.92 0.92 1537
4 0.98 0.99 0.98 243
с 0.95 0.95 0.95 3676
р 0.96 0.96 0.96 3787
у 0.97 0.97 0.97 2802
т 0.97 0.97 0.97 5224
х 0.92 0.96 0.94 578
ф 0.97 0.98 0.97 315
ч 0.97 0.97 0.97 1412
ц 0.96 0.98 0.97 402
щ 0.95 0.97 0.96 185
ш 0.95 0.97 0.96 660
ы 0.98 0.98 0.98 2484
6 0.98 0.96 0.97 130
э 0.96 0.93 0.94 163
ь 0.97 0.97 0.97 1001
я 0.98 0.98 0.98 2809
ю 0.96 0.96 0.96 567
9 0.99 0.98 0.98 151
8 1.00 0.97 0.98 120
2 0.98 0.98 0.98 406
7 1.00 0.96 0.98 140
1 0.98 0.99 0.99 572
о 0.96 0.96 0.96 7100
avg / total 0.96 0.96 0.96 92397
Символ \r это специальный маркер конца ответа.
Можно заметить, что худшая точность (0.92) наблюдается для буквы 'п'. Удивительно при этом, что это достаточно частотная буква, в частности для данного набора ответов она встречается 1537 раз. Разумеется, в тренировочном наборе она тоже весьма частотна, потому классификатор видел ее в разных контекстах много раз, и тем не менее вот такой bias...
По поводу использованной функции
sklearn.metrics.classification_report, кстати, есть одна неприятная засада. Она на входе принимает опционально наименования классов, чтобы сделать отчет более читабельным. В данном случае названиями классов являются символы. Все прекрасно до тех пор, пока передаваемые в функцию y_true и y_pred содержат одинаковое по мощности множество классов. Но если в y_pred встречаются не все классы (всякое бывает, например маленький набор для валидации или сильный дисбаланс классов), то происходит следующее. наименования классов, заботливо передаваемые в аргументе target_names, не будут правильно соотнесены с вариантами классов из y_pred, и в консоли появится предупреждение. Отчет будет содержать неправильно поименованные класса! Чтобы исключить такой неприятный ход событий, в строке 692 я пересекаю множества классов в y_true и y_pred, и затем сортирую их по возрастанию:
class_names = [encode_char(id2outchar[y]) for y in sorted(set(y_test) | set(y_pred))]
Полученный список class_names гарантирует консистентность результатов работы sklearn.metrics.classification_report.