<jupyter_start><jupyter_text>Text Classification using ALBERT-MongolianСайн байна уу?Энэхүү notebook нь [`ALBERT-Mongolian`](https://huggingface.co/bayartsogt/albert-mongolian) ашиглан хэрхэн `TF Keras` дээр текст ангилал хийхийг харуулах болно.Notebook-ийн төгсгөлд та eduge.mn сайтын мэдээний өгөгдлийг **~90%** таах чадвартай модельтэй болох ба энэхүү үр дүн нь ердөө эхлэл ба цааш сайжрах бүрэн боломжтой юм.Notebook-ийн дараалал:1. [Бэлтгэл ажил](scrollTo=caMvdHeDJGOx)2. [Өгөгдөл](scrollTo=lUXesMU1c5Sq)3. [Tokenize хийх](scrollTo=PJsBXhkvc7L5)4. [Модель ба сургалт](scrollTo=1LEnvmjDdNlZ)5. [Үр дүн](scrollTo=FmJPTVHI4E6g)```used lang: python 3.6TF version: 2.1author: <NAME>```  Бэлтгэл ажил<jupyter_code>! pip install -q transformers
! pip install -q tensorflow==2.1.0 # 2.2 дээр тест хийгээгүй болно!<jupyter_output>[K     |████████████████████████████████| 778kB 3.3MB/s 
[K     |████████████████████████████████| 3.0MB 11.6MB/s 
[K     |████████████████████████████████| 890kB 17.4MB/s 
[K     |████████████████████████████████| 1.1MB 19.3MB/s 
[?25h  Building wheel for sacremoses (setup.py) ... [?25l[?25hdone
Collecting tensorflow==2.1.0
[?25l  Downloading https://files.pythonhosted.org/packages/85/d4/c0cd1057b331bc38b65478302114194bd8e1b9c2bbc06e300935c0e93d90/tensorflow-2.1.0-cp36-cp36m-manylinux2010_x86_64.whl (421.8MB)
[K     |████████████████████████████████| 421.8MB 38kB/s 
[?25hRequirement already satisfied: numpy<2.0,>=1.16.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.1.0) (1.18.5)
Requirement already satisfied: wheel>=0.26; python_version >= "3" in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.1.0) (0.34.2)
Requirement already satisfied: six>=1.12.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.1.0) (1.15.0)
Requirement already sati[...]<jupyter_text>Хэрэгтэй сангуудаа import хийж оруулж ирье!<jupyter_code>import gc
import logging

import numpy as np
import pandas as pd

from time import time
from tqdm import tqdm

import seaborn as sns
import matplotlib.pyplot as plt

import tensorflow as tf
from transformers import TFAlbertModel, AlbertTokenizer

from sklearn.model_selection import train_test_split

print(tf.__version__)<jupyter_output>/usr/local/lib/python3.6/dist-packages/statsmodels/tools/_testing.py:19: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.
  import pandas.util.testing as tm<jupyter_text>TPU-г ачааллая<jupyter_code># Detect hardware, return appropriate distribution strategy
try:
    # TPU detection. No parameters necessary if TPU_NAME environment 
    # variable is set. On Kaggle this is always the case.
    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()  
    print('Running on TPU ', tpu.master())
except ValueError:
    tpu = None

if tpu:
    tf.config.experimental_connect_to_cluster(tpu)
    tf.tpu.experimental.initialize_tpu_system(tpu)
    strategy = tf.distribute.experimental.TPUStrategy(tpu)
else:
    # default distribution strategy in Tensorflow. Works on CPU and single GPU.
    strategy = tf.distribute.get_strategy() 

print("REPLICAS: ", strategy.num_replicas_in_sync)<jupyter_output>INFO:absl:Entering into master device scope: /job:worker/replica:0/task:0/device:CPU:0<jupyter_text>ӨгөгдөлЭнэ удаагийн сургалтыг **Эдүгэ** сайтын дата дээр туршиж үзэцгээе.Мөн туршилтын датаг [tugstugi/mongolian-nlp](https://github.com/tugstugi/mongolian-nlp) репогоос татаж авсан болно.<jupyter_code># downloading data
! wget https://github.com/tugstugi/mongolian-nlp/raw/master/datasets/eduge.csv.gz
! gunzip eduge.csv.gz<jupyter_output>--2020-07-26 09:59:28--  https://github.com/tugstugi/mongolian-nlp/raw/master/datasets/eduge.csv.gz
Resolving github.com (github.com)... 172.16.31.10
Connecting to github.com (github.com)|172.16.31.10|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://raw.githubusercontent.com/tugstugi/mongolian-nlp/master/datasets/eduge.csv.gz [following]
--2020-07-26 09:59:28--  https://raw.githubusercontent.com/tugstugi/mongolian-nlp/master/datasets/eduge.csv.gz
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 172.16.31.10, 192.168.3.11, 172.16.31.10, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|172.16.31.10|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 73951267 (71M) [application/octet-stream]
Saving to: ‘eduge.csv.gz’

eduge.csv.gz        100%[===================>]  70.53M  82.2MB/s    in 0.9s    

2020-07-26 09:59:30 (82.2 MB/s) - ‘eduge.csv.gz’ saved [73951267/73951267]<jupyter_text>Энэхүү ажил нь eduge датаг 9 ангилалд хуваах ба хэрвээ та хүсвэл 2 ангилалд буюу binary classification асуудалд ашигласан ч болно<jupyter_code># eduge_data = pd.read_csv("./eduge.csv", nrows=7500) # binary classification болгохыг хүсвэл энэ мөрийг ажиллуулна
eduge_data = pd.read_csv("./eduge.csv")
eduge_data.head(2)
print("Энэхүү дата доторх *label* утгууд нь:", eduge_data[" label"].unique())
sns.countplot(data=eduge_data, y=" label");<jupyter_output>Энэхүү дата доторх *label* утгууд нь: ['урлаг соёл' 'эдийн засаг' 'эрүүл мэнд' 'хууль' 'улс төр' 'спорт'
 'технологи' 'боловсрол' 'байгал орчин']<jupyter_text>Дээрх histogram-аас харахад `спорт, улс төр, эдийн засаг` зэрэг ангилалд 12'000-аас их дата байгаа нь харагдаж байна.<jupyter_code>id2label = {i:label for i, label in enumerate(eduge_data[" label"].unique())}
label2id = {label:i for i, label in enumerate(eduge_data[" label"].unique())}
print(id2label)
print(label2id)
eduge_data[" label"] = eduge_data[" label"].apply(lambda x: label2id[x])
eduge_data.head(2)<jupyter_output><empty_output><jupyter_text>Датаныхаа 20%-ийг сургасан моделио тест хийхэд ашиглахаар үлдээе!<jupyter_code># data split
X_train, X_test, y_train, y_test = train_test_split(eduge_data["news"], eduge_data[" label"], test_size=0.2, random_state=42)
print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)<jupyter_output>(60528,) (15133,) (60528,) (15133,)<jupyter_text>Tokenize хийх ALBERT-Mongolian-ыг анх сургахдаа хамгийн уртдаа 512 урттай оролт ордог байхаар сургасан ба энэхүү туршилтад ч тэр setting-ээ дагаад MAX_LEN-г 512 оор тохируулж өгье. Ингэснээр 512 оос урт оролт орж ирвэл tokenize хийгээд эхний 512 тэмдэгтийг л цааш модельруу явуулах юм.<jupyter_code>MAX_LEN = 512<jupyter_output><empty_output><jupyter_text>Доор ALBERT-Mongolian-ийг сургахад ашигласан моделийг оруулж ирж байна.<jupyter_code># read tokenizer
tokenizer = AlbertTokenizer.from_pretrained("bayartsogt/albert-mongolian")<jupyter_output><empty_output><jupyter_text>Transformers суурьтай моделийн оролтын хэлбэрийн доор харуулж байна.<jupyter_code># Жишээ + тайлбар
texts = ["Шинэ Монгол ч сайн сургууль шүү"]
texts_encoded = tokenizer.batch_encode_plus(texts, max_length=MAX_LEN, padding=True, truncation=True, return_tensors="np")
print("Жишээ:")
print([tokenizer.cls_token] + tokenizer.tokenize(texts[0]) + [tokenizer.eos_token] + [tokenizer.pad_token])
print(texts_encoded["input_ids"][0])
print(texts_encoded["token_type_ids"][0])
print(texts_encoded["attention_mask"][0])

# sharvaa ахын блогийг энд тавьчихвал амар юм байна!<jupyter_output>Жишээ:
['[CLS]', '▁шинэ', '▁монгол', '▁ч', '▁са', 'ин', '▁сургууль', '▁шүү', '[SEP]', '<pad>']
[    2   161   396    19 12331   848   479   164     3]
[0 0 0 0 0 0 0 0 0]
[1 1 1 1 1 1 1 1 1]<jupyter_text>`[CLS]` тэмдэгт хамгийн урд нэмэгдсэн байгаа. Сургалтыг явцад ALBERT Mongolian-ий бүх гаралтыг бус энэхүү тэмдэгтийг илэрхийлэх векторыг энэхүү оролтыг илэрхүүлнэ гэж үзээд цааш явах юм.Доорх хүснэгтийн дагуу: * `input_ids` гэдэг нь тухайн token-ий index-ийг заана.* `token_type` гэдэг нь `[SEP]` урд байна уу хойно байн уу гэдгийг заана. Манай тохиолдолд байнга `0` байна* `attention_mask` гэдэг нь ямар нэг үгийг масклах үгүйг заана. Манай тохиолдолд ямар ч үг масклах буюу нуух шаардагагүй тул бүгд `1` байна.| |[CLS]|▁шинэ|▁монгол|▁ч|▁са|ин|▁сургууль|▁шүү|[SEP]||---|---|---|---|---|---|---|---|---|---||input_ids|2|161|396|19|12331|848|479|164|3||token_type_ids|0|0|0|0|0|0|0|0|0||attention_mask|1|1|1|1|1|1|1|1|1|<jupyter_code>%%time
X_train_encoded = tokenizer.batch_encode_plus(list(X_train), max_length=MAX_LEN, padding=True, truncation=True, return_tensors="np")
X_test_encoded  = tokenizer.batch_encode_plus(list(X_test), max_length=MAX_LEN, padding=True, truncation=True, return_tensors="np")

print("size:", X_train_encoded["input_ids"].shape)
print("size:", X_test_encoded["input_ids"].shape)<jupyter_output>size: (60528, 512)
size: (15133, 512)
CPU times: user 4min 48s, sys: 12.9 s, total: 5min 1s
Wall time: 5min 2s<jupyter_text>Модель ба сургалт<jupyter_code>print("нийт ангилалын тоо:", len(label2id))<jupyter_output>нийт ангилалын тоо: 9<jupyter_text>Сонгосон архитектур нь:`INPUT(512) => ALBERT-MONGOLIAN(768) => DENSE_LAYER(9)`ба ALBERT-Mongolian-ийг transformers-санг ашиглан дуудаж оруулж ирсэн байна.<jupyter_code>def build_model():

    with strategy.scope(): # TPU RAM дээр модель оо үүсгэх үед ашиглана
        ids = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)
        att = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)
        tok = tf.keras.layers.Input((MAX_LEN,), dtype=tf.int32)

        bert_model = TFAlbertModel.from_pretrained('bayartsogt/albert-mongolian')
        
        x = bert_model(ids, attention_mask=att, token_type_ids=tok)[1] # getting class token value
        x = tf.keras.layers.Dropout(0.2)(x)
        
        if len(label2id) == 2:
            x = tf.keras.layers.Dense(1, activation="sigmoid")(x)
        else:
            x = tf.keras.layers.Dense(len(label2id), activation="softmax")(x)
        
        model = tf.keras.models.Model(inputs=[ids, att, tok], outputs=[x])

        optimizer = tf.keras.optimizers.Adam(learning_rate=1e-5)
        loss_name = "binary_crossentropy" if len(label2id) == 2 else "sparse_categorical_crossentropy"

        print("Loss:", loss_name)
        model.compile(loss=loss_name, 
                      optimizer=optimizer, 
                      metrics=["accuracy"])

    return model
tf.keras.backend.clear_session()
tf.tpu.experimental.initialize_tpu_system(tpu)

model = build_model()
model.summary()<jupyter_output>WARNING:tensorflow:TPU system 10.81.81.218:8470 has already been initialized. Reinitializing the TPU can cause previously created variables on TPU to be lost.<jupyter_text>СургалтДээрх моделио ашиглан сургалт хийе.Энэхүү хэсэгт та early stopping, learning rate scheduler зэрэг арга ашиглан үр дүнг сайжруулах бүрэн боломжтой юм.Сургалт хэрхэн явж байгаа хянахын тулд сургалтад бэлдсэн датаныхаа 80%-ийг train-нд 20%-ийг validation хийхэд ашиглая.<jupyter_code>BATCH_SIZE = 8 * strategy.num_replicas_in_sync
EPOCHS = 5
VAL_SPLIT = 0.2

STEPS_PER_EPOCH = int(X_train_encoded["input_ids"].shape[0] * (1-VAL_SPLIT) / BATCH_SIZE)

history = model.fit(x=[X_train_encoded["input_ids"], X_train_encoded["attention_mask"], X_train_encoded["token_type_ids"]], 
                    y=[np.array(y_train)], 
                    steps_per_epoch=STEPS_PER_EPOCH,
                    epochs=EPOCHS,
                    batch_size=BATCH_SIZE,
                    validation_split=VAL_SPLIT,
                    verbose=1)
plt.plot(history.history["accuracy"])
plt.plot(history.history["val_accuracy"])
plt.show()<jupyter_output><empty_output><jupyter_text>Дээрх графикаас харахад манай модель дөнгөж эхлээд л overfit болсон харагдаж байна.Энд байга код нь ойлгоход амар байлгах үүднээс overfit-ийг багасгах ямар нэг арга хэрэглээгүй болно.Та өөрийн drive дээрээ хуулаад дуртайгаараа тоглох боломжтой байгаа.<jupyter_code># хадгалах
file_path = "albert-mongolian-finetuned.hdf5"
model.save_weights(file_path)
del model
gc.collect()<jupyter_output><empty_output><jupyter_text>Үр дүнОдоо сургасан моделоо ашиглаад тест дата дээр хэрхэн таамаглал хийж байгааг харцгаая!<jupyter_code>trained_model = build_model()
trained_model.load_weights(file_path)
prediction = trained_model.predict(x=[X_test_encoded["input_ids"], X_test_encoded["attention_mask"], X_test_encoded["token_type_ids"]], batch_size=16, verbose=1)
# prediction
from sklearn.metrics import classification_report

if len(label2id) == 2:
    threshold = 0.5
    y_pred = np.where(prediction>threshold, 1, 0)
else:
    y_pred = np.argmax(prediction, 1)
print(classification_report(np.array(y_test), y_pred))<jupyter_output>precision    recall  f1-score   support

           0       0.93      0.94      0.94      1457
           1       0.83      0.88      0.86      2509
           2       0.89      0.92      0.90      1159
           3       0.89      0.87      0.88      1651
           4       0.92      0.85      0.89      2647
           5       0.98      0.98      0.98      2736
           6       0.88      0.93      0.91      1102
           7       0.80      0.80      0.80       873
           8       0.85      0.83      0.84       999

    accuracy                           0.90     15133
   macro avg       0.89      0.89      0.89     15133
weighted avg       0.90      0.90      0.90     15133
{'fraction_non_alphanumeric': 0.13339445335778136, 'fraction_numerical': 0.0550080220032088, 'mean_word_length': 4.3625563293732075, 'pattern_counts': {'":': 0, '<': 57, '<?xml version=': 0, '>': 64, 'https://': 7, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 7, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 11, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '229072', 'license': 'Apache-2.0', 'n_tokens_mistral': 6133, 'n_tokens_neox': 5864, 'n_words': 1016, 'path': 'text_classification_ALBERT_Mongolian.ipynb', 'repo_name': 'bayartsogt-ya/albert-mongolian', 'size': '13111'}