Kesalahan Tatabahasa with Tagging#

This tutorial is available as an IPython notebook at Malaya/example/tatabahasa-tagging.

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

[1]:
import malaya
from pprint import pprint
/home/husein/dev/malaya/malaya/tokenizer.py:214: FutureWarning: Possible nested set at position 3397
  self.tok = re.compile(r'({})'.format('|'.join(pipeline)))
/home/husein/dev/malaya/malaya/tokenizer.py:214: FutureWarning: Possible nested set at position 3927
  self.tok = re.compile(r'({})'.format('|'.join(pipeline)))

List available HuggingFace models#

[2]:
malaya.tatabahasa.available_huggingface
[2]:
{'mesolitica/finetune-tatabahasa-t5-tiny-standard-bahasa-cased': {'Size (MB)': 139,
  'exactly-match': 0.7665198237,
  'f1': 0.970908229,
  'exactly-match-tags': 0.87404885863,
  'f1-tags': 0.9878723587,
  'Suggested length': 256},
 'mesolitica/finetune-tatabahasa-t5-small-standard-bahasa-cased': {'Size (MB)': 242,
  'exactly-match': 0.8145774929,
  'f1': 0.9781278,
  'exactly-match-tags': 0.89447336,
  'f1-tags': 0.990597377,
  'Suggested length': 256},
 'mesolitica/finetune-tatabahasa-t5-base-standard-bahasa-cased': {'Size (MB)': 892,
  'exactly-match': 0.760913095,
  'f1': 0.970136249,
  'exactly-match-tags': 0.865839,
  'f1-tags': 0.9868999035,
  'Suggested length': 256}}

Supported kesalahan tatabahasa#

For full description, check out https://tatabahasabm.tripod.com/tata/salahtata.htm

[3]:
malaya.tatabahasa.describe
[3]:
[{'class': 0, 'Description': 'PAD', 'salah': '', 'betul': ''},
 {'class': 1, 'Description': 'kesambungan subwords', 'salah': '', 'betul': ''},
 {'class': 2, 'Description': 'tiada kesalahan', 'salah': '', 'betul': ''},
 {'class': 3,
  'Description': 'kesalahan frasa nama, Perkara yang diterangkan mesti mendahului "penerang"',
  'salah': 'Cili sos',
  'betul': 'sos cili'},
 {'class': 4,
  'Description': 'kesalahan kata jamak',
  'salah': 'mereka-mereka',
  'betul': 'mereka'},
 {'class': 5,
  'Description': 'kesalahan kata penguat',
  'salah': 'sangat tinggi sekali',
  'betul': 'sangat tinggi'},
 {'class': 6,
  'Description': 'kata adjektif dan imbuhan "ter" tanpa penguat.',
  'salah': 'Sani mendapat markah yang tertinggi sekali.',
  'betul': 'Sani mendapat markah yang tertinggi.'},
 {'class': 7,
  'Description': 'kesalahan kata hubung',
  'salah': 'Sally sedang membaca bila saya tiba di rumahnya.',
  'betul': 'Sally sedang membaca apabila saya tiba di rumahnya.'},
 {'class': 8,
  'Description': 'kesalahan kata bilangan',
  'salah': 'Beribu peniaga tidak membayar cukai pendapatan.',
  'betul': 'Beribu-ribu peniaga tidak membayar cukai pendapatan'},
 {'class': 9,
  'Description': 'kesalahan kata sendi',
  'salah': 'Umar telah berpindah daripada sekolah ini bulan lalu.',
  'betul': 'Umar telah berpindah dari sekolah ini bulan lalu.'},
 {'class': 10,
  'Description': 'kesalahan penjodoh bilangan',
  'salah': 'Setiap orang pelajar',
  'betul': 'Setiap pelajar.'},
 {'class': 11,
  'Description': 'kesalahan kata ganti diri',
  'salah': 'Pencuri itu telah ditangkap. Beliau dibawa ke balai polis.',
  'betul': 'Pencuri itu telah ditangkap. Dia dibawa ke balai polis.'},
 {'class': 12,
  'Description': 'kesalahan ayat pasif',
  'salah': 'Cerpen itu telah dikarang oleh saya.',
  'betul': 'Cerpen itu telah saya karang.'},
 {'class': 13,
  'Description': 'kesalahan kata tanya',
  'salah': 'Kamu berasal dari manakah ?',
  'betul': 'Kamu berasal dari mana ?'},
 {'class': 14,
  'Description': 'kesalahan tanda baca',
  'salah': 'Kamu berasal dari manakah .',
  'betul': 'Kamu berasal dari mana ?'},
 {'class': 15,
  'Description': 'kesalahan kata kerja tak transitif',
  'salah': 'Dia kata kepada saya',
  'betul': 'Dia berkata kepada saya'},
 {'class': 16,
  'Description': 'kesalahan kata kerja transitif',
  'salah': 'Dia suka baca buku',
  'betul': 'Dia suka membaca buku'},
 {'class': 17,
  'Description': 'penggunaan kata yang tidak tepat',
  'salah': 'Tembuk Besar negeri Cina dibina oleh Shih Huang Ti.',
  'betul': 'Tembok Besar negeri Cina dibina oleh Shih Huang Ti'}]

Right now we only able to predict up to 15 different kesalahan tatabahasa, hopefully in the future we can scale this up.

Load HuggingFace model#

def huggingface(
    model: str = 'mesolitica/finetune-tatabahasa-t5-small-standard-bahasa-cased',
    force_check: bool = True,
    **kwargs,
):
    """
    Load HuggingFace model to fix kesalahan tatabahasa.

    Parameters
    ----------
    model: str, optional (default='mesolitica/finetune-tatabahasa-t5-small-standard-bahasa-cased')
        Check available models at `malaya.tatabahasa.available_huggingface`.
    force_check: bool, optional (default=True)
        Force check model one of malaya model.
        Set to False if you have your own huggingface model.

    Returns
    -------
    result: malaya.torch_model.huggingface.Tatabahasa
    """
[4]:
model = malaya.tatabahasa.huggingface()
Loading the tokenizer from the `special_tokens_map.json` and the `added_tokens.json` will be removed in `transformers 5`,  it is kept for forward compatibility, but it is recommended to update your `tokenizer_config.json` by uploading it again. You will see the new `added_tokens_decoder` attribute that will store the relevant information.
You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. If you see this, DO NOT PANIC! This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thouroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565

Predict#

def generate(
    self,
    strings: List[str],
    **kwargs,
):
    """
    Fix kesalahan tatatabahasa.

    Parameters
    ----------
    strings : List[str]
    **kwargs: vector arguments pass to huggingface `generate` method.
        Read more at https://huggingface.co/docs/transformers/main_classes/text_generation
        Fix kesalahan tatabahasa supported all decoding methods except beam.

    Returns
    -------
    result: List[Tuple[str, int]]
    """

Randomly picked string in bahasa melayu wikipedia.

[5]:
# https://ms.wikipedia.org/wiki/Bola_sepak
string = 'Pada amnya, hanya penjaga gol sahaja yang dibenarkan menyentuh bola dengan tangan di dalam kawasan golnya'
[6]:
model.generate([string], max_length = 256)
[6]:
[[('Pada', 2),
  ('amnya', 2),
  (',', 2),
  ('hanya', 2),
  ('penjaga', 2),
  ('gol', 2),
  ('sahaja', 2),
  ('yang', 2),
  ('dibenarkan', 2),
  ('menyentuh', 2),
  ('bola', 2),
  ('dengan', 2),
  ('tangan', 2),
  ('di', 2),
  ('dalam', 2),
  ('kawasan', 2),
  ('golnya', 2)]]

Now assumed we have kesalahan frasa nama, from penjaga gol become gol penjaga.

[7]:
# https://ms.wikipedia.org/wiki/Bola_sepak
string = 'Pada amnya, hanya gol penjaga sahaja yang dibenarkan menyentuh bola dengan tangan di dalam kawasan golnya'
[8]:
model.generate([string], max_length = 256)
[8]:
[[('Pada', 2),
  ('amnya', 2),
  (',', 2),
  ('hanya', 2),
  ('penjaga', 3),
  ('gol', 3),
  ('sahaja', 2),
  ('yang', 2),
  ('dibenarkan', 2),
  ('menyentuh', 2),
  ('bola', 2),
  ('dengan', 2),
  ('tangan', 2),
  ('di', 2),
  ('dalam', 2),
  ('kawasan', 2),
  ('golnya', 2)]]
[9]:
string = 'Sani mendapat markah yang tertinggi sekali.'
string1 = 'Hassan ialah peserta yang termuda sekali dalam pertandingan itu.'
model.generate([string, string1], max_length = 256)
[9]:
[[('Sani', 2),
  ('mendapat', 2),
  ('markah', 2),
  ('yang', 2),
  ('tertinggi', 6),
  ('.', 2)],
 [('Hassan', 2),
  ('ialah', 2),
  ('peserta', 2),
  ('yang', 2),
  ('termuda', 6),
  ('dalam', 2),
  ('pertandingan', 2),
  ('itu', 2),
  ('.', 2)]]
[10]:
string = 'Dia kata kepada saya.'
model.generate([string], max_length = 256)
[10]:
[[('Dia', 2), ('berkata', 15), ('kepada', 2), ('saya', 2), ('.', 2)]]
[11]:
import pickle

with open('tests/dataset-tatabahasa.pkl', 'rb') as fopen:
    test_set = pickle.load(fopen)

len(test_set)
[11]:
100
[12]:
def get_xy(row):
    x, y, tag = [], [], []

    for i in range(len(row[0])):
        t = [row[0][i][0]]
        y.extend(t)
        t = [row[1][i][0]]
        x.extend(t)
        tag.extend([row[1][i][1]] * len(t))

    return ' '.join(x), ' '.join(y), tag
[13]:
x, y, t = get_xy(test_set[0])
x, y, t
[13]:
('Dirk Jan Klaas " Klaas-Jan " Huntelaar ( lahir 12 Ogos 1983 ) merupakan pemain bola sepak Belanda yang bermain seperti posisi penyerang !',
 'Dirk Jan Klaas " Klaas-Jan " Huntelaar ( lahir 12 Ogos 1983 ) merupakan pemain bola sepak Belanda yang bermain di posisi penyerang .',
 [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 9, 2, 2, 14])
[14]:
model.generate([x], max_length = 256)
[14]:
[[('Dirk', 2),
  ('Jan', 2),
  ('Klaas', 2),
  ('"', 2),
  ('Klaas-Jan', 2),
  ('"', 2),
  ('Huntelaar', 2),
  ('(', 2),
  ('lahir', 2),
  ('12', 2),
  ('Ogos', 2),
  ('1983', 2),
  (')', 2),
  ('merupakan', 2),
  ('pemain', 2),
  ('bola', 2),
  ('sepak', 2),
  ('Belanda', 2),
  ('yang', 2),
  ('bermain', 2),
  ('di', 9),
  ('posisi', 2),
  ('penyerang', 2),
  ('.', 14)]]
[15]:
x, y, t = get_xy(test_set[-1])
x, y, t
[15]:
('Pada tahun 2002 , kedua-dua gol beliau menduduki tempat ke-6 dalam 100 Greatest Sporting Moments oleh saluran Channel 4 UK .',
 'Pada tahun 2002 , kedua-dua gol ini menduduki tempat ke-6 dalam 100 Greatest Sporting Moments oleh saluran Channel 4 UK .',
 [2, 2, 2, 2, 2, 2, 11, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
[16]:
model.generate([x], max_length = 256)
[16]:
[[('Pada', 2),
  ('tahun', 2),
  ('2002', 2),
  (',', 2),
  ('kedua-dua', 2),
  ('gol', 2),
  ('beliau', 11),
  ('menduduki', 2),
  ('tempat', 2),
  ('ke-6', 2),
  ('dalam', 2),
  ('100', 2),
  ('Greatest', 2),
  ('Sporting', 2),
  ('Moments', 2),
  ('oleh', 2),
  ('saluran', 2),
  ('Channel', 2),
  ('4', 2),
  ('UK', 2),
  ('.', 2)]]
[17]:
x, y, t = get_xy(test_set[-2])
x, y, t
[17]:
('Gol inilah yang bergelar Goal of the Century dengan undian Internet 2000 sejak FIFA .',
 'Gol inilah yang bergelar Goal of the Century di undian Internet 2000 oleh FIFA .',
 [2, 2, 2, 2, 2, 2, 2, 2, 9, 2, 2, 2, 9, 2, 2])
[18]:
model.generate([x], max_length = 256)
[18]:
[[('Gol', 2),
  ('inilah', 2),
  ('yang', 2),
  ('bergelar', 2),
  ('Goal', 2),
  ('of', 2),
  ('the', 2),
  ('Century', 2),
  ('dalam', 9),
  ('undian', 2),
  ('Internet', 2),
  ('2000', 2),
  ('oleh', 9),
  ('FIFA', 2),
  ('.', 2)]]
[19]:
x, y, t = get_xy(test_set[-3])
x, y, t
[19]:
('Beliau mengambil bola dalam kawasan kepul diri lalu pusing dan luru lebih separuh padang sambil menyentuh bola 11 kali , memintas lima pemain England : ( Glenn Hoddle , Peter Reid , Kenny Sansom , Terry Butcher , dan Terry Fenwick ) serta penjaga gawang Peter Shilton .',
 'Beliau mengambil bola di kawasan pasukan diri lalu berpusing-pusing dan meluru lebih separuh padang sambil menyentuh bola 11 kali , memintas lima pemain England : ( Glenn Hoddle , Peter Reid , Kenny Sansom , Terry Butcher , dan Terry Fenwick ) serta penjaga gawang Peter Shilton .',
 [2,
  2,
  2,
  9,
  2,
  10,
  2,
  2,
  15,
  2,
  15,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2,
  2])
[20]:
model.generate([x], max_length = 256)
[20]:
[[('Beliau', 2),
  ('mengambil', 2),
  ('bola', 2),
  ('dalam', 2),
  ('kawasan', 2),
  ('pasukan', 10),
  ('berdiri', 15),
  ('lalu', 2),
  ('berpusing', 15),
  ('dan', 2),
  ('meluru', 15),
  ('lebih', 2),
  ('separuh', 2),
  ('padang', 2),
  ('sambil', 2),
  ('menyentuh', 2),
  ('bola', 2),
  ('11', 2),
  ('kali', 2),
  (',', 2),
  ('memintas', 2),
  ('lima', 2),
  ('pemain', 2),
  ('England', 2),
  (':', 2),
  ('(', 2),
  ('Glenn', 2),
  ('Hoddle', 2),
  (',', 2),
  ('Peter', 2),
  ('Reid', 2),
  (',', 2),
  ('Kenny', 2),
  ('Sansom', 2),
  (',', 2),
  ('Terry', 2),
  ('Butcher', 2),
  (',', 2),
  ('dan', 2),
  ('Terry', 2),
  ('Fenwick', 2),
  (')', 2),
  ('serta', 2),
  ('penjaga', 2),
  ('gawang', 2),
  ('Peter', 2),
  ('Shilton', 2),
  ('.', 2)]]

More examples#

I just copy pasted from https://ms.wikipedia.org/wiki/Kesalahan_biasa_tatabahasa_Melayu

[21]:
string = 'Tidak ada apa yang mereka risaukan waktu itu.'
model.generate([string], max_length = 256)
[21]:
[[('Tidak', 2),
  ('ada', 2),
  ('apa', 2),
  ('yang', 2),
  ('mereka', 2),
  ('risaukan', 2),
  ('waktu', 2),
  ('itu.', 2)]]
[22]:
string = 'Ayahnya setuju walaupun melanggar syarat yang dia sendiri menetapkan.'
model.generate([string], max_length = 256)
[22]:
[[('Ayahnya', 2),
  ('setuju', 2),
  ('dan', 7),
  ('melanggar', 2),
  ('syarat', 2),
  ('yang', 2),
  ('dia', 2),
  ('sendiri', 2),
  ('menetapkan', 2),
  ('.', 2)]]
[23]:
string = 'Semuanya dia kenal.'
model.generate([string], max_length = 256)
[23]:
[[('Semuanya', 2), ('dia', 2), ('kenal', 2), ('.', 2)]]
[24]:
string = 'Dia menjawab seperti disuruh-suruh oleh kuasa yang dia tidak tahu dari mana puncanya.'
model.generate([string], max_length = 256)
[24]:
[[('Dia', 2),
  ('menjawab', 2),
  ('seperti', 2),
  ('disuruh-suruh', 2),
  ('oleh', 2),
  ('kuasa', 2),
  ('yang', 2),
  ('dia', 2),
  ('tidak', 2),
  ('tahu', 2),
  ('dari', 2),
  ('mana', 2),
  ('puncanya.', 2)]]
[25]:
string = 'Bola ini ditendang oleh saya.'
model.generate([string], max_length = 256)
[25]:
[[('Bola', 2),
  ('ini', 2),
  ('ditendang', 2),
  ('oleh', 2),
  ('saya', 2),
  ('.', 2)]]
[26]:
string = 'Makanan ini kamu telah makan?'
model.generate([string], max_length = 256)
[26]:
[[('Makanan', 2),
  ('ini', 11),
  ('kamu', 2),
  ('telah', 2),
  ('makan', 2),
  ('.', 14)]]
[27]:
string = 'Segala perubahan yang berlaku kita akan menghadapi sama-sama.'
model.generate([string], max_length = 256)
[27]:
[[('Segala', 2),
  ('perubahan', 2),
  ('yang', 2),
  ('berlaku', 2),
  ('kita', 2),
  ('akan', 2),
  ('menghadapi', 2),
  ('sama-sama', 2),
  ('.', 2)]]
[28]:
string = 'Kakak saya sedang memasak gulai nangka. Dia menyenduk seketul nangka gulai dan menyuruh saya merasanya.'
model.generate([string], max_length = 256)
[28]:
[[('Kakak', 2),
  ('saya', 2),
  ('sedang', 2),
  ('memasak', 2),
  ('gulai', 2),
  ('nangka.', 2),
  ('Dia', 2),
  ('menyenduk', 2),
  ('seketul', 2),
  ('gulai', 3),
  ('nangka', 3),
  ('dan', 2),
  ('menyuruh', 2),
  ('saya', 2),
  ('merasanya.', 2)]]
[29]:
string = 'Sally sedang membaca bila saya tiba di rumahnya.'
model.generate([string], max_length = 256)
[29]:
[[('Sally', 2),
  ('sedang', 2),
  ('membaca', 2),
  ('bila', 2),
  ('saya', 11),
  ('tiba', 2),
  ('di', 2),
  ('rumahnya.', 2)]]
[30]:
string = 'Badannya besar kecuali kakinya kecil.'
model.generate([string], max_length = 256)
[30]:
[[('Badannya', 2),
  ('besar', 2),
  ('dan', 7),
  ('kakinya', 2),
  ('kecil', 2),
  ('.', 2)]]
[31]:
string = 'Beribu peniaga tidak membayar cukai pendapatan.'
model.generate([string], max_length = 256)
[31]:
[[('Beribu', 2),
  ('peniaga', 2),
  ('tidak', 2),
  ('membayar', 2),
  ('cukai', 2),
  ('pendapatan.', 2)]]
[32]:
string = 'Setengah remaja suka membuang masa di pasar raya.'
model.generate([string], max_length = 256)
[32]:
[[('Setengah', 2),
  ('remaja', 2),
  ('suka', 2),
  ('membuang', 2),
  ('masa', 2),
  ('di', 2),
  ('pasar', 2),
  ('raya.', 2)]]
[33]:
string = 'Umar telah berpindah daripada sekolah ini bulan lalu.'
model.generate([string], max_length = 256)
[33]:
[[('Umar', 2),
  ('telah', 2),
  ('berpindah', 2),
  ('ke', 9),
  ('sekolah', 2),
  ('ini', 2),
  ('bulan', 2),
  ('lalu', 2),
  ('.', 2)]]
[34]:
string = 'Para-para peserta sedang berbaris.'
model.generate([string], max_length = 256)
[34]:
[[('Para', 4), ('peserta', 2), ('sedang', 2), ('berbaris', 2), ('.', 2)]]