SQUAD#

This tutorial is available as an IPython notebook at Malaya/example/qa-squad.

This module only trained on standard language structure, so it is not save to use it for local language structure.

[1]:
import logging

logging.basicConfig(level=logging.INFO)
[2]:
%%time

import malaya
from pprint import pprint
INFO:numexpr.utils:NumExpr defaulting to 8 threads.
CPU times: user 5.85 s, sys: 1.15 s, total: 7 s
Wall time: 8.24 s

What is SQUAD#

Stanford Question Answering Dataset (SQuAD) is a reading comprehension dataset, eg,

{
    'title': 'Normans',
    'paragraphs': [
        {
            'context': 'Orang Norman (Norman: Nourmands; Perancis: Normands; Latin: Normanni) ialah orang-orang yang pada abad ke-10 dan ke-11 memberikan nama mereka kepada Normandy, sebuah wilayah di Perancis. Mereka diturunkan daripada Norse ("Norman" berasal daripada penyerang "Norseman") dan lanun dari Denmark, Iceland dan Norway yang, di bawah pimpinan mereka Rollo, bersetuju untuk bersumpah fealty kepada Raja Charles III dari Francia Barat. Melalui generasi asimilasi dan percampuran dengan penduduk asli Frankish dan Roman-Gaulish, keturunan mereka akan beransur-ansur bergabung dengan budaya Carolingian yang berpusat di Francia Barat. Identiti budaya dan etnik yang berbeza dari orang Norman muncul pada mulanya pada separuh pertama abad ke-10, dan ia terus berkembang pada abad-abad yang berjaya.',
            'qas': [
                {
                    'question': 'Di negara manakah Normandy berada?',
                    'answers': [
                        {'text': 'Perancis', 'answer_start': 177},
                        {'text': 'Perancis', 'answer_start': 177},
                        {'text': 'Perancis', 'answer_start': 177},
                        {'text': 'Perancis', 'answer_start': 177},
                    ],
                    'id': '56ddde6b9a695914005b9628',
                    'is_impossible': False,
                }
            ],
        }
    ],
}

So we need to give a long paragraph and multiple questions, and the model will return answers based on that paragraph with start and end spans. Read more about SQUAD dataset https://rajpurkar.github.io/SQuAD-explorer/

List available Transformer models#

[3]:
malaya.qa.available_transformer_squad()
INFO:malaya.qa:testes on translated SQUAD V2 Dev set at https://github.com/huseinzol05/malay-dataset/tree/master/question-answer/squad
[3]:
Size (MB) Quantized Size (MB) exact f1 total
tiny-bert 60.9 15.30 53.45758 56.79821 11858.0
bert 452.0 113.00 57.16810 61.48740 11858.0
albert 58.1 14.60 58.97284 63.12757 11858.0
tiny-albert 24.8 6.35 50.00843 50.00843 11858.0
xlnet 478.0 120.00 62.74245 66.56101 11858.0
alxlnet 58.4 15.60 61.97503 65.89765 11858.0

Load Transformer model#

[3]:
xlnet_model = malaya.qa.transformer_squad(model = 'xlnet')
albert_model = malaya.qa.transformer_squad(model = 'albert')

Load Quantized model#

To load 8-bit quantized model, simply pass quantized = True, default is False.

We can expect slightly accuracy drop from quantized model, and not necessary faster than normal 32-bit float model, totally depends on machine.

[4]:
quantized_xlnet_model = malaya.qa.transformer_squad(model = 'xlnet', quantized = True)
quantized_albert_model = malaya.qa.transformer_squad(model = 'albert', quantized = True)
WARNING:root:Load quantized model will cause accuracy drop.
WARNING:root:Load quantized model will cause accuracy drop.

Copy from wikipedia and news#

[5]:
# https://ms.wikipedia.org/wiki/Mohd_Najib_bin_Abdul_Razak

p_wikipedia = """
Najib razak telah dipilih untuk Parlimen Malaysia pada tahun 1976,
pada usia 23 tahun, menggantikan bapanya duduk di kerusi Pekan yang berpangkalan di Pahang.
Dari tahun 1982 hingga 1986 beliau menjadi Menteri Besar (Ketua Menteri) Pahang,
sebelum memasuki persekutuan Kabinet Tun Dr Mahathir Mohamad pada tahun 1986 sebagai Menteri Kebudayaan, Belia dan Sukan.
Beliau telah berkhidmat dalam pelbagai jawatan Kabinet sepanjang baki tahun 1980-an dan 1990-an, termasuk sebagai Menteri Pertahanan dan Menteri Pelajaran.
Beliau menjadi Timbalan Perdana Menteri pada 7 Januari 2004, berkhidmat di bawah Perdana Menteri Tun Dato' Seri Abdullah Ahmad Badawi,
sebelum menggantikan Badawi setahun selepas Barisan Nasional mengalami kerugian besar dalam pilihan raya 2008.
Di bawah kepimpinan beliau, Barisan Nasional memenangi pilihan raya 2013,
walaupun buat kali pertama dalam sejarah Malaysia pembangkang memenangi majoriti undi popular.
"""
q_wikipedia = ['Siapakah Menteri Besar Pahang', 'Apakah jawatan yang pernah dipegang oleh Najib Razak']
[6]:
# https://www.malaysiakini.com/news/574914

p_news = """
Bekas perdana menteri Najib Razak mempersoalkan tindakan polis yang menurutnya tidak serta-merta mengeluarkan kenyataan berhubung dakwaan Adun Perikatan Nasional (PN) "merancang" insiden rogol.
Sedangkan, kata ahli parlimen Pekan itu, polis pantas mengeluarkan kenyataan apabila dia dilapor terlupa mengimbas MySejahtera sebelum masuk restoran.
"Berita Najib lupa scan MySejahtera tular, kenyataan polis terus keluar. Berita Dr Mahathir Mohamad lupa scan, kenyataan, polis serta-merta keluar.
"Sebab itu saya pelik kenapa pihak polis belum sempat keluar apa-apa kenyataan berhubung kes seorang gadis membuat laporan polis untuk dakwa Adun PN rancang insiden rogolnya," katanya di Facebook hari ini.
Najib merujuk dakwaan seorang wanita yang mendakwa dirogol kenalan kepada Adun Gombak Setia, Hilman Idham.
Wanita itu mendakwa ahli politik dari Bersatu berkenaan merancang insiden yang berlaku pada 5 Dis lalu.
Menurut laporan polis pada 8 Mei, mangsa mendakwa kejadian itu berlaku di sebuah hotel di Selangor, yang pada masa itu berada di bawah perintah kawalan pergerakan bersyarat (PKPB).
"""

q_news = ['siapakah yang mempersoalkan tindakan polis', 'siapakah Adun Gombak Setia']

Predict#

def predict(
    self,
    paragraph_text: str,
    question_texts: List[str],
    doc_stride: int = 128,
    max_query_length: int = 64,
    max_answer_length: int = 64,
    n_best_size: int = 20,
):
    """
    Predict Span from questions given a paragraph.

    Parameters
    ----------
    paragraph_text: str
    question_texts: List[str]
        List of questions, results really depends on case sensitive questions.
    doc_stride: int, optional (default=128)
        striding size to split a paragraph into multiple texts.
    max_query_length: int, optional (default=64)
        Maximum length if question tokens.
    max_answer_length: int, optional (default=30)
        Maximum length if answer tokens.

    Returns
    -------
    result: List[{'text': 'text', 'start': 0, 'end': 1}]
    """
[7]:
xlnet_model.predict(p_wikipedia, q_wikipedia)
[7]:
[{'text': 'Najib razak', 'start': 0, 'end': 11},
 {'text': 'Pekan yang berpangkalan di Pahang', 'start': 123, 'end': 157}]
[9]:
albert_model.predict(p_wikipedia, q_wikipedia)
[9]:
[{'text': 'Najib razak', 'start': 0, 'end': 11},
 {'text': 'Menteri Pertahanan dan Menteri Pelajaran',
  'start': 475,
  'end': 516}]
[10]:
quantized_xlnet_model.predict(p_wikipedia, q_wikipedia)
[10]:
[{'text': 'Najib razak', 'start': 0, 'end': 11},
 {'text': 'Pekan yang berpangkalan di Pahang', 'start': 123, 'end': 157}]
[11]:
quantized_albert_model.predict(p_wikipedia, q_wikipedia)
[11]:
[{'text': 'Najib razak', 'start': 0, 'end': 11},
 {'text': 'Menteri Pertahanan dan Menteri Pelajaran',
  'start': 475,
  'end': 516}]
[12]:
xlnet_model.predict(p_news, q_news)
[12]:
[{'text': 'Bekas perdana menteri Najib Razak', 'start': 0, 'end': 33},
 {'text': 'Hilman Idham', 'start': 791, 'end': 804}]
[13]:
albert_model.predict(p_news, q_news)
[13]:
[{'text': 'Bekas perdana menteri Najib Razak', 'start': 0, 'end': 33},
 {'text': 'Hilman Idham', 'start': 791, 'end': 804}]
[14]:
quantized_xlnet_model.predict(p_news, q_news)
[14]:
[{'text': 'Bekas perdana menteri Najib Razak', 'start': 0, 'end': 33},
 {'text': 'Hilman Idham', 'start': 791, 'end': 804}]
[15]:
quantized_albert_model.predict(p_news, q_news)
[15]:
[{'text': 'Bekas perdana menteri Najib Razak', 'start': 0, 'end': 33},
 {'text': 'Hilman Idham', 'start': 791, 'end': 804}]

Vectorize#

Let say you want to visualize sentence / word level in lower dimension, you can use model.vectorize,

def vectorize(self, strings: List[str], method: str = 'first'):
    """
    vectorize list of strings.

    Parameters
    ----------
    strings: List[str]
    method : str, optional (default='first')
        Vectorization layer supported. Allowed values:

        * ``'last'`` - vector from last sequence.
        * ``'first'`` - vector from first sequence.
        * ``'mean'`` - average vectors from all sequences.
        * ``'word'`` - average vectors based on tokens.

    Returns
    -------
    result: np.array
    """

Sentence level#

[25]:
strings = ['Siapakah Menteri Besar Pahang',
           'Apakah jawatan yang pernah dipegang oleh Najib Razak',
           'Najib razak',
           'Menteri Pertahanan dan Menteri Pelajaran',
           'Bekas perdana menteri Najib Razak',
           'Hilman Idham',
           'Tun Dr Mahathir Mohamad pada tahun 1986',
           'Berita Najib lupa scan MySejahtera tular, kenyataan polis terus keluar']
[26]:
r = quantized_xlnet_model.vectorize(strings, method = 'first')
[27]:
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt

tsne = TSNE().fit_transform(r)
tsne.shape
[27]:
(8, 2)
[28]:
plt.figure(figsize = (7, 7))
plt.scatter(tsne[:, 0], tsne[:, 1])
labels = strings
for label, x, y in zip(
    labels, tsne[:, 0], tsne[:, 1]
):
    label = (
        '%s, %.3f' % (label[0], label[1])
        if isinstance(label, list)
        else label
    )
    plt.annotate(
        label,
        xy = (x, y),
        xytext = (0, 0),
        textcoords = 'offset points',
    )
_images/load-qa-squad_29_0.png

Word level#

[33]:
r = quantized_xlnet_model.vectorize(strings, method = 'word')
[34]:
x, y = [], []
for row in r:
    x.extend([i[0] for i in row])
    y.extend([i[1] for i in row])
[35]:
tsne = TSNE().fit_transform(y)
tsne.shape
[35]:
(43, 2)
[36]:
plt.figure(figsize = (7, 7))
plt.scatter(tsne[:, 0], tsne[:, 1])
labels = x
for label, x, y in zip(
    labels, tsne[:, 0], tsne[:, 1]
):
    label = (
        '%s, %.3f' % (label[0], label[1])
        if isinstance(label, list)
        else label
    )
    plt.annotate(
        label,
        xy = (x, y),
        xytext = (0, 0),
        textcoords = 'offset points',
    )
_images/load-qa-squad_34_0.png