Jetons un coup d'œil à l'intérieur du pipeline de de réponse aux questions.
Le pipeline de réponse aux questions peut extraire les réponses aux questions d'un contexte
ou d'un passage de texte donné, comme cette partie du README du dépôt Transformers.
Cela fonctionne aussi pour des contextes très longs, même si la réponse est à la toute fin, comme dans
cet exemple.
Dans cette vidéo, nous allons voir pourquoi !
Le pipeline de réponse aux questions suit les mêmes étapes que les autres pipelines : la question
et le contexte sont tokenisés sous la forme d'une paire de phrases, transmis au modèle, puis un post-traitement
est appliqué.
Les étapes de tokenisation et de modèle doivent être familières.
Nous utilisons la classe automatique adaptée à la réponse aux questions au lieu de la classification de séquence,
mais une différence clé avec la classification de texte est que notre modèle génère deux tenseurs nommés
logits de début et logits de fin.
Pourquoi donc?
Eh bien, c'est ainsi que le modèle trouve la réponse à la question.
Examinons d'abord les entrées du modèle.
Il s'agit de chiffres associés à la tokenisation de la question suivis du contexte (avec
les tokens spéciaux habituels [CLS] et [SEP]).
La réponse fait partie de ces tokens.
Nous demandons donc au modèle de prédire quel token commence la réponse et lequel termine la réponse.
Pour nos deux sorties logit, les étiquettes théoriques sont les vecteurs rose et violet.
Pour convertir ces logits en probabilités, nous devrons appliquer une SoftMax, comme dans le
pipeline de classification de texte.
Nous masquons simplement les tokens qui ne font pas partie du contexte avant de le faire, laissant
le token [CLS] initial démasqué car nous l'utilisons pour prédire une réponse impossible.
Voici à quoi cela ressemble en termes de code.
On utilise un grand nombre négatif pour le masquage, puisque son exponentielle sera alors 0.
Maintenant la probabilité pour chaque position de début et de fin correspondant à une réponse possible,
on donne un score qui est le produit des probabilités de début et des probabilités de fin
à ces positions.
Bien entendu, un indice de début supérieur à un indice de fin correspond à une réponse impossible.
Voici le code pour trouver le meilleur score pour une réponse possible.
Une fois que nous avons les positions de début et de fin des tokens, nous utilisons l'association de décalage fournis
par notre tokenizer pour trouver la plage de caractères dans le contexte initial et obtenir notre réponse !
Désormais, lorsque le contexte est long, il peut être tronqué par le tokenizer.
Cela pourrait entraîner une partie de la réponse, ou pire, la réponse entière, étant tronquée.
Nous ne supprimons donc pas les tokens tronqués, mais construisons de nouvelles caractéristiques avec eux.
Chacune de ces caractéristiques contient la question, puis un morceau de texte dans le contexte.
Si nous prenons des morceaux de textes disjoints, nous pourrions nous retrouver avec la réponse divisée entre
deux caractéristiques.
Donc, à la place, nous prenons des morceaux de textes qui se chevauchent, pour nous assurer qu'au moins un des morceaux
contiendra entièrement la réponse à la question.
Les tokenizers font tout cela pour nous automatiquement avec l'option `return_overflowing_tokens`.
L'argument `stride` contrôle le nombre de tokens qui se chevauchent.
Voici comment notre très long contexte est tronqué en deux caractéristiques avec un certain chevauchement.
En appliquant le même post-traitement que nous avons vu précédemment pour chaque caractéristique, nous obtenons la réponse
avec un score pour chacun d'eux, et nous prenons la réponse avec le meilleur score comme solution finale.