{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# **Báo cáo bài tập lớn môn Học máy**\n**Giảng viên:** Trần Quốc Long<br>\n**Lớp môn học:** 2021II_INT3405_20<br>\n**Sinh viên:** Vũ Thành Đạt<br>\n**MSSV:** 18020294<br>","metadata":{}},{"cell_type":"markdown","source":"# **Mô tả bài toán**\n**Quora** là một nền tảng cho phép mọi người học hỏi lẫn nhau. Trên **Quora**, mọi người có thể đặt câu hỏi và kết nối với những người khác, những người đóng góp thông tin chi tiết độc đáo và câu trả lời chất lượng. Một thách thức quan trọng là loại bỏ những câu hỏi thiếu chân thành - những câu hỏi được đặt ra dựa trên những tiền đề sai lầm hoặc có ý định đưa ra một tuyên bố hơn là tìm kiếm những câu trả lời hữu ích.\n\n**Quora Insincere Question Classification** là một bài toán của **Quora** đặt ra, sử dụng sự trợ giúp từ cộng đồng, giúp họ phân loại những câu hỏi không chân thành, đi ngược lại với chính sách của họ là _\"Be Nice, Be Respecfull\"_\n\nNhiệm vụ của bài toán là sử dụng tập dữ liệu mà **Quora** cung cấp để phân loại đâu là những câu hỏi mang hàm ý không chân thành, mang nội dung xấu độc, gây hiểu lầm.\n\n- **Input**: Câu hỏi dưới dạng văn bản (plain text)\n- **Output**: Yes/No","metadata":{}},{"cell_type":"markdown","source":"# **Nội Dung Bài Báo Cáo**\n**Bước 1.** Khảo sát dữ liệu<br>\n**Bước 2.** Tiền Xử Lý Dữ Liệu<br>\n**Bước 3.** Build tập Vocab<br>\n**Bước 4.** Chuẩn bị Model<br>\n**Bước 5.** Huấn luyện và Dự đoán<br>\n**Bước 6.** So sánh và mở rộng<br>","metadata":{"id":"rKrUIX0WVCsY"}},{"cell_type":"markdown","source":"# **Hướng Tiếp Cận**\n- Tiền xử lý bộ dữ liệu\n    - Bỏ đi các biểu thức toán học và các đường dẫn liên kết\n    - Bỏ đi các ký tự đặc biệt, các chữ số\n    - Sửa những từ sai chính tả và extend các từ viết tắt\n    - Bỏ đi stop_words trong câu\n    - Lemming các từ có trong câu (có thể hiểu là chuyển các từ về dạng nguyên thể)\n    \n> **Mục đích**: Loại bỏ đi các từ gây nhiễu cho bộ dữ liệu và mở rộng độ bao phủ của tập Vocab với bộ dữ liệu\n- Xây dựng tập vocab: \n    - Sử dụng các file trong embeddings.zip mà quora cung cấp để xây dựng tập vocab \n    - Các file embeddings có dạng text, mỗi dòng chứa 1 word và đi kèm đó là vector của nó đã được pretrained\n    - Xây tập vocab từ các word có trong từ điển và vector đi kèm với chúng\n    - Kiểm tra độ phủ của vocab xây dựng dựa trên tập embeddings với dữ liệu được cho\n- Xây dựng ma trận embeddings:\n    - Sử dụng file pretrained để tạo ra ma trận embeddings dựa vào các từ có trong tập vocab\n    - Lấy các từ để lookup vector descriptor của các từ đó và xây ma trận\n- Tạo model và huấn luyện dự đoán\n- Kết hợp kết quả của nhiều model với các ma trận embeddings đã xây dựng và cho ra kết quả tốt nhất","metadata":{}},{"cell_type":"markdown","source":"# **1. Khảo sát dữ liệu**","metadata":{}},{"cell_type":"code","source":"import pandas as pd \nimport seaborn as sns\nimport re\nimport gc\nimport os\nimport numpy as np\nimport operator\nfrom wordcloud import WordCloud, STOPWORDS\nfrom tqdm import tqdm\nimport matplotlib.pyplot as plt\npd_ctx = pd.option_context('display.max_colwidth', 100)\n\nimport nltk\nfrom nltk.stem import PorterStemmer, SnowballStemmer, WordNetLemmatizer\n\nfrom gensim.models import KeyedVectors\n\nimport tensorflow as tf\n\nfrom sklearn.model_selection import train_test_split\nimport sklearn.metrics as metrics\nfrom keras import optimizers\nfrom keras.layers import Dense, Input, LSTM, Embedding, Dropout, Activation, Conv1D,GRU\nfrom keras.layers import Bidirectional, GlobalMaxPool1D, SpatialDropout1D, GlobalMaxPooling1D, Concatenate\nfrom keras.models import Model,load_model\nfrom keras.utils import plot_model\nfrom keras.models import Sequential\nfrom keras.callbacks import ModelCheckpoint,EarlyStopping\nfrom keras.optimizers import Adam\nfrom keras.preprocessing.sequence import pad_sequences","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:37.605439Z","iopub.execute_input":"2021-06-06T10:03:37.605901Z","iopub.status.idle":"2021-06-06T10:03:45.407016Z","shell.execute_reply.started":"2021-06-06T10:03:37.605782Z","shell.execute_reply":"2021-06-06T10:03:45.406103Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Xác định các tập ```train```, ```test``` và khởi tạo một tập chứa dữ liệu của cả 2 để về sau check độ bao phủ của vocab với tập dữ liệu đã sinh","metadata":{}},{"cell_type":"code","source":"df_train = pd.read_csv('/kaggle/input/quora-insincere-questions-classification/train.csv')\ndf_test = pd.read_csv('/kaggle/input/quora-insincere-questions-classification/test.csv')\nquora_data = df_train['question_text'].append(df_test['question_text'])","metadata":{"id":"6XGNYHcCUpJW","execution":{"iopub.status.busy":"2021-06-06T10:03:45.408528Z","iopub.execute_input":"2021-06-06T10:03:45.409121Z","iopub.status.idle":"2021-06-06T10:03:51.600068Z","shell.execute_reply.started":"2021-06-06T10:03:45.409077Z","shell.execute_reply":"2021-06-06T10:03:51.599027Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **a. Khảo sát dữ liệu trong tập **train****","metadata":{}},{"cell_type":"code","source":"print(\"\\033[1mTrain set info\\033[0m\")\nprint(df_train.info())","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:51.602156Z","iopub.execute_input":"2021-06-06T10:03:51.60263Z","iopub.status.idle":"2021-06-06T10:03:51.868626Z","shell.execute_reply.started":"2021-06-06T10:03:51.602585Z","shell.execute_reply":"2021-06-06T10:03:51.867514Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ở trong bộ dữ liệu có 3 trường, và khả năng dữ liệu train nằm ở trường **```question_text```** <br>\nVậy ta sẽ thử khảo sát dữ liệu trong trường **```question_text```**","metadata":{}},{"cell_type":"code","source":"# Kiểm tra các trường dữ liệu của câu hỏi được đánh sincere\nprint(\"\\033[1mSincere Questions: \\033[0m\")\ndisplay(df_train[df_train['target']==0].head())\n# Kiểm tra các trường dữ liệu của câu hỏi được đánh sincere\nprint(\"\\033[1mInsincere Questions: \\033[0m\")\ndisplay(df_train[df_train['target']==1].head())","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:51.870194Z","iopub.execute_input":"2021-06-06T10:03:51.870463Z","iopub.status.idle":"2021-06-06T10:03:52.037965Z","shell.execute_reply.started":"2021-06-06T10:03:51.870437Z","shell.execute_reply":"2021-06-06T10:03:52.036908Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Có thể thấy rằng tập train có 3 trường dữ liệu\n- **```qid```**: định danh id cho câu hỏi\n- **```question_text```**: nội dung câu hỏi\n- **```target```**: phân lớp câu hỏi\n    - 0: câu hỏi mang tính chất chân thành (sincere)\n    - 1: câu hỏi không mang tính chất chân thành (insincere)\n    \nKhá may mắn khi tập dữ liệu **Quora** cung cấp không có bất kì trường dữ liệu nào có giá trị bất thường (```null```, ```none```, ```missing```)\n    \nTập dữ liệu train bao gồm **1306122** dòng dữ liệu, gần **1.31 triệu dòng**, khá lớn. Cần sử dụng mô hình có hiệu quả tính toán nhanh, nên trong bài này em sẽ không sử dụng mô hình với thuật toán ensemble","metadata":{}},{"cell_type":"markdown","source":"Theo dự đoán thì trường **```target```** sẽ là đánh dấu các câu hỏi sincere và insincere nên cần xem thử là trong trường **```target```** đó có những giá trị gì","metadata":{}},{"cell_type":"code","source":"df_train.target.value_counts()","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:52.039288Z","iopub.execute_input":"2021-06-06T10:03:52.039613Z","iopub.status.idle":"2021-06-06T10:03:52.05744Z","shell.execute_reply.started":"2021-06-06T10:03:52.039581Z","shell.execute_reply":"2021-06-06T10:03:52.056438Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Có thể thấy trong trường target có 2 giá trị là ```0``` và ```1```<br>\nDự đoán thì những câu hỏi đánh dấu ```0``` là những câu hỏi sincere, tương tự ```1``` là insincere<br>\nVậy có thể xem như đây là bài toán phân lớp nhị phân<br>\nNhưng vẫn cần phải có đánh giá sâu hơn về bộ dữ liệu này, nhìn nhanh qua có thể thấy bộ dữ liệu train được cho khá là mất cân bằng","metadata":{}},{"cell_type":"code","source":"pos_len = len(df_train[df_train['target'] == 1])\nneg_len = len(df_train[df_train['target'] == 0])\ntotal = len(df_train)\nprint(\"\\033[1mTotal = \\033[0m\", total)\nprint(\"\\033[1mSincere questions:\\033[0m {neg} ({percent: .2f}% )\".format(neg = neg_len, percent = neg_len / total * 100))\nprint(\"\\033[1mInsincere questions:\\033[0m {pos} ({percent: .2f}% )\".format(pos = pos_len, percent = pos_len / total * 100))\nfig = plt.figure()\nax = fig.add_axes([0,0,1,1])\nax.bar(['sincere', 'insincere'], df_train.target.value_counts())\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:52.058702Z","iopub.execute_input":"2021-06-06T10:03:52.059032Z","iopub.status.idle":"2021-06-06T10:03:52.324837Z","shell.execute_reply.started":"2021-06-06T10:03:52.059004Z","shell.execute_reply":"2021-06-06T10:03:52.323923Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Qua biểu đồ có thể dễ dàng nhận thấy tập dữ liệu train bị mất cân bằng nhiều:\n- Lớp câu hỏi sincere có **1225312** (chiếm **93.81%**) số lượng dữ liệu\n- Lớp câu hỏi insincere có **80810** (chiếm **6.19%**) số lượng dữ liệu\n\nCó thể thấy tập dữ liệu bị lệch nhiều (lên tới **15 lần**) cho nên cần phải sử dụng metrics f1_score để đánh giá độ hiệu quả của mô hình hơn là so **accuracy**<br>\nDo **f1_score** quan tâm đến phân bố của dữ liệu nên trong bài này nó sẽ mang nhiều ý nghĩa hơn là **accuracy**.<br>\nVà trong cuộc thi **Kaggle** cũng bảo là sử dụng **f1_score** thay vì **accuracy**\n\nTìm hiểu thêm về **[F1 Score](https://en.wikipedia.org/wiki/F-score)**<br>\nF1 score là sự **harmonic mean** của **Precision** và **Recall**. F1 Score nhận giá trị trong khoảng ```0``` - ```1```<br>\n$$Precision = \\frac{TP}{TP+FP}$$\n$$Recall = \\frac{TP}{TP+FN}$$\n$$F1 = \\frac{2}{Recall^{-1} + Precision^{-1}}$$\n\n**Nhận xét:**<br>\nCó thể coi đây là bài toán phân loại nhị phân. Thường thì ta sẽ nghĩ đến các mô hình tuyến tính như **Logistic Regression** hay **SVM** để phân loại. Nhưng đây là bài toán thiên về tiền xử lý do đầu vào là chuỗi các text, nên quan trọng là việc mình xử lý chuỗi như thế nào. Trong bài này em sẽ sử dụng thằng **Keras** với **LSTM Model** và **Embedings** các chuỗi text để giảm chiều dữ liệu","metadata":{}},{"cell_type":"markdown","source":"### **b. Khảo sát dữ liệu trong tập **test**** ","metadata":{}},{"cell_type":"code","source":"df_test.info()","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:52.326087Z","iopub.execute_input":"2021-06-06T10:03:52.326389Z","iopub.status.idle":"2021-06-06T10:03:52.406976Z","shell.execute_reply.started":"2021-06-06T10:03:52.326359Z","shell.execute_reply":"2021-06-06T10:03:52.406002Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ở trong bộ dữ liệu test thì chỉ có 2 trường dữ liệu là **```qid```** đại diện cho id của câu hỏi<br>\nVà **```question_text```** đại diện cho dữ liệu test<br>\nCũng may mắn khi mà ở trong tập **df_test** lại không có dữ liệu kì lạ nào cả (```null```, ```none```, ```missing```)<br>\nVà cái ta cần quan tâm ở đây chỉ là trường dữ liệu **```question_text```** để đưa vào model","metadata":{}},{"cell_type":"markdown","source":"**Reference**: https://stackoverflow.com/questions/29576430/shuffle-dataframe-rows","metadata":{}},{"cell_type":"code","source":"# Shuffle tập train để kiểm tra những giá trị ngẫu nhiên\ntrain = df_train.sample(frac=1).reset_index(drop=True)\ndisplay(train.sample(n=10, random_state=344))","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:52.40941Z","iopub.execute_input":"2021-06-06T10:03:52.409701Z","iopub.status.idle":"2021-06-06T10:03:53.308687Z","shell.execute_reply.started":"2021-06-06T10:03:52.40967Z","shell.execute_reply":"2021-06-06T10:03:53.307621Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Mỗi lần shuffle lại cho ra một kết quả khác, nhưng nhìn chung qua vài lần thực nghiệm quan sát, em nhận thấy trong tập dữ liệu ở trên:\n- Có nhiều câu mà các từ trong đó viết sai chính tả, lẫn lộn giữa tiếng Anh-Anh và tiếng Anh-Mỹ\n- Nhiều câu sử dụng các từ viết tắt: He's mà đáng nhẽ nên là He is.\n- Có một số từ viết tắt cho tên các tổ chức, hay các chuẩn mã hoá kiểu RSA, DES/3DES, ...\n- Nhiều câu sử dụng các ký tự đặc biệt cho nên sẽ gây ảnh hưởng lớn tới mô hình dự đoán\n\nVề mặt nội dung hàm ý thì có thể không ảnh hưởng nhưng sẽ ảnh hưởng khi xâp tập từ điển, cùng một ý nghĩa nhưng các từ lại được tính nhiều lần giá trị\n\n> **Có thể thấy rằng, bộ dữ liệu đã cho có khá nhiều nhiễu. Việc ta cần phải làm là loại bỏ nhiều nhiều nhất có thể**\n\n**Cách xử lý**\n- Loại bỏ stopwords có trong câu\n- Stem các từ có trong câu (driver, driving, driven, drove -> drive)\n- Chuẩn hoá các từ về dạng lowercase\n- Loại bỏ các ký tự đặc biệt\n- Loại bỏ các đường dẫn liên kết, loại bỏ các công thức toán học, ...","metadata":{}},{"cell_type":"markdown","source":"### **c. Nhận xét**\nNhưng có thể thấy một điều là bộ dữ liệu **train** và **test** dữ liệu đầu vào đều ở dạng plain text và mô hình của chúng ta sẽ không thể hiểu nổi<br>\nVậy hướng giải quyết ở đây em nghĩ là là sử dụng mã hoá **one_hot** đưa dữ liệu về dạng binary để cho mô hình có thể hiểu được","metadata":{}},{"cell_type":"markdown","source":"# **2. Tiền xử lý dữ liệu**","metadata":{}},{"cell_type":"markdown","source":"Với bài toán liên quan đến ngôn ngữ thì hiệu quả của mô hình phụ thuộc lớn vào việc tiền xử lý dữ liệu, tức loại bỏ đi nhiễu ở trong bộ dữ liệu.<br>\nVà phương pháp phổ biến đối với các bài toán liên quan đến ngôn ngữ được trình bày ở dưới như sau","metadata":{}},{"cell_type":"markdown","source":"#### **Reference**: https://www.kaggle.com/canming/ensemble-mean-iii-64-36\n**Cách làm:**<br>\nSử dụng thư viện xử lý ngôn ngữ tự nhiên nltk trong việc preprocess data<br>\nỞ đây yêu cần phải sử dụng wordnet, một thư viện từ đồng nghĩa, trái nghĩa, nltk punkt được yêu cầu để tokenize words<br>\n- Bước 1: ```clean_tag()```: loại bỏ đi các biểu thức toán học, các địa chỉ liên kết\n- Bước 2: ```clean_puncts()```: loại bỏ đi các ký tự đặc biệt có trong câu\n- Bước 3: ```correct_misspell()```: trong bộ dữ liệu của **Quora** do đây là các câu hỏi của người dùng nên không tránh khỏi việc gõ sai, hay là có những từ không chuẩn ví dụ là lẫn lộn giữa tiếng Anh-Anh với tiếng Anh-Mỹ cho nên cần phải fix những từ này và đưa nó về dạng chuẩn\n- Bước 4: ```remove_stopwords()```: loại bỏ đi các stopwords có trong câu\n- Bước 5: ```clean_contractions()```: chuyển những từ viết tắt về dạng đầy đủ vốn có\n- Bước 6: ```lemming_words()```: lemming các từ về dạng nguyên bản của nó\n\n\n**Vấn đề:**<br>\nTuy nhiên đời không như là mơ, cuộc thi này của **Quora** không cho phép sử dụng Internet nên việc lemming word bằng cách lookup từ điển này sẽ không hoạt động<br>\n**Giải pháp:**<br>\nCần tìm ra một phương pháp lemming words mới","metadata":{}},{"cell_type":"markdown","source":"### **a. Định nghĩa các hàm xử lý**","metadata":{}},{"cell_type":"code","source":"### do việc lemming words yêu cầu sử dụng thư viện wordnet để lookup các từ có trong đó\n# nltk.download('wordnet')\n# nltk.download('punkt')","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:53.311046Z","iopub.execute_input":"2021-06-06T10:03:53.311455Z","iopub.status.idle":"2021-06-06T10:03:53.314767Z","shell.execute_reply.started":"2021-06-06T10:03:53.311413Z","shell.execute_reply":"2021-06-06T10:03:53.314066Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ở trong hàm **```clean_tag()```** em sẽ loại bỏ bằng các biểu thức toán học, và nếu có liên kết trong câu thì cũng sẽ loại bỏ đi và thay thế bằng URL<br>\nDo các biểu thức toán học và đường dẫn liên kết không có nhiều giá trị về mặt ý nghĩa trong phân loại câu học sincere hay không sincere mà lại làm nhiễu bộ dữ liệu khá nhiều","metadata":{}},{"cell_type":"code","source":"def clean_tag(x):\n  if '[math]' in x:\n    x = re.sub('\\[math\\].*?math\\]', 'MATH EQUATION', x) #replacing with [MATH EQUATION]\n    \n  if 'http' in x or 'www' in x:\n    x = re.sub('(?:(?:https?|ftp):\\/\\/)?[\\w/\\-?=%.]+\\.[\\w/\\-?=%.]+', 'URL', x) #replacing with [url]\n  return x","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:53.315997Z","iopub.execute_input":"2021-06-06T10:03:53.316299Z","iopub.status.idle":"2021-06-06T10:03:53.328343Z","shell.execute_reply.started":"2021-06-06T10:03:53.316272Z","shell.execute_reply":"2021-06-06T10:03:53.327061Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Trong hàm **```clean_puncts()```** này thì em sẽ loại bỏ đi các kí tự đặc biệt và các emoji cảm xúc<br>\nBởi vì việc loại bỏ emoji sẽ không làm ảnh hưởng nhiều tới ý nghĩa của câu, tương tự như các dấu câu và kí tự đặc biệt<br>\nCũng như ý ở trên, có quá nhiều emoji và ký tự đặc biệt làm nhiễu bộ dữ liệu khá nhiều","metadata":{}},{"cell_type":"code","source":"puncts = [',', '.', '\"', ':', ')', '(', '-', '!', '?', '|', ';', \"'\", '$', '&', '/', '[', ']', '>', '%', '=', '#', '*', '+', '\\\\', \n        '•', '~', '@', '£', '·', '_', '{', '}', '©', '^', '®', '`', '<', '→', '°', '€', '™', '›', '♥', '←', '×', '§', '″', '′', \n        '█', '…', '“', '★', '”', '–', '●', '►', '−', '¢', '¬', '░', '¡', '¶', '↑', '±', '¿', '▾', '═', '¦', '║', '―', '¥', '▓', \n        '—', '‹', '─', '▒', '：', '⊕', '▼', '▪', '†', '■', '’', '▀', '¨', '▄', '♫', '☆', '¯', '♦', '¤', '▲', '¸', '⋅', '‘', '∞', \n        '∙', '）', '↓', '、', '│', '（', '»', '，', '♪', '╩', '╚', '・', '╦', '╣', '╔', '╗', '▬', '❤', '≤', '‡', '√', '◄', '━', \n        '⇒', '▶', '≥', '╝', '♡', '◊', '。', '✈', '≡', '☺', '✔', '↵', '≈', '✓', '♣', '☎', '℃', '◦', '└', '‟', '～', '！', '○', \n        '◆', '№', '♠', '▌', '✿', '▸', '⁄', '□', '❖', '✦', '．', '÷', '｜', '┃', '／', '￥', '╠', '↩', '✭', '▐', '☼', '☻', '┐', \n        '├', '«', '∼', '┌', '℉', '☮', '฿', '≦', '♬', '✧', '〉', '－', '⌂', '✖', '･', '◕', '※', '‖', '◀', '‰', '\\x97', '↺', \n        '∆', '┘', '┬', '╬', '،', '⌘', '⊂', '＞', '〈', '⎙', '？', '☠', '⇐', '▫', '∗', '∈', '≠', '♀', '♔', '˚', '℗', '┗', '＊', \n        '┼', '❀', '＆', '∩', '♂', '‿', '∑', '‣', '➜', '┛', '⇓', '☯', '⊖', '☀', '┳', '；', '∇', '⇑', '✰', '◇', '♯', '☞', '´', \n        '↔', '┏', '｡', '◘', '∂', '✌', '♭', '┣', '┴', '┓', '✨', '\\xa0', '˜', '❥', '┫', '℠', '✒', '［', '∫', '\\x93', '≧', '］', \n        '\\x94', '∀', '♛', '\\x96', '∨', '◎', '↻', '⇩', '＜', '≫', '✩', '✪', '♕', '؟', '₤', '☛', '╮', '␊', '＋', '┈', '％', \n        '╋', '▽', '⇨', '┻', '⊗', '￡', '।', '▂', '✯', '▇', '＿', '➤', '✞', '＝', '▷', '△', '◙', '▅', '✝', '∧', '␉', '☭', \n        '┊', '╯', '☾', '➔', '∴', '\\x92', '▃', '↳', '＾', '׳', '➢', '╭', '➡', '＠', '⊙', '☢', '˝', '∏', '„', '∥', '❝', '☐', \n        '▆', '╱', '⋙', '๏', '☁', '⇔', '▔', '\\x91', '➚', '◡', '╰', '\\x85', '♢', '˙', '۞', '✘', '✮', '☑', '⋆', 'ⓘ', '❒', \n        '☣', '✉', '⌊', '➠', '∣', '❑', '◢', 'ⓒ', '\\x80', '〒', '∕', '▮', '⦿', '✫', '✚', '⋯', '♩', '☂', '❞', '‗', '܂', '☜', \n        '‾', '✜', '╲', '∘', '⟩', '＼', '⟨', '·', '✗', '♚', '∅', 'ⓔ', '◣', '͡', '‛', '❦', '◠', '✄', '❄', '∃', '␣', '≪', '｢', \n        '≅', '◯', '☽', '∎', '｣', '❧', '̅', 'ⓐ', '↘', '⚓', '▣', '˘', '∪', '⇢', '✍', '⊥', '＃', '⎯', '↠', '۩', '☰', '◥', \n        '⊆', '✽', '⚡', '↪', '❁', '☹', '◼', '☃', '◤', '❏', 'ⓢ', '⊱', '➝', '̣', '✡', '∠', '｀', '▴', '┤', '∝', '♏', 'ⓐ', \n        '✎', ';', '␤', '＇', '❣', '✂', '✤', 'ⓞ', '☪', '✴', '⌒', '˛', '♒', '＄', '✶', '▻', 'ⓔ', '◌', '◈', '❚', '❂', '￦', \n        '◉', '╜', '̃', '✱', '╖', '❉', 'ⓡ', '↗', 'ⓣ', '♻', '➽', '׀', '✲', '✬', '☉', '▉', '≒', '☥', '⌐', '♨', '✕', 'ⓝ', \n        '⊰', '❘', '＂', '⇧', '̵', '➪', '▁', '▏', '⊃', 'ⓛ', '‚', '♰', '́', '✏', '⏑', '̶', 'ⓢ', '⩾', '￠', '❍', '≃', '⋰', '♋', \n        '､', '̂', '❋', '✳', 'ⓤ', '╤', '▕', '⌣', '✸', '℮', '⁺', '▨', '╨', 'ⓥ', '♈', '❃', '☝', '✻', '⊇', '≻', '♘', '♞', \n        '◂', '✟', '⌠', '✠', '☚', '✥', '❊', 'ⓒ', '⌈', '❅', 'ⓡ', '♧', 'ⓞ', '▭', '❱', 'ⓣ', '∟', '☕', '♺', '∵', '⍝', 'ⓑ', \n        '✵', '✣', '٭', '♆', 'ⓘ', '∶', '⚜', '◞', '்', '✹', '➥', '↕', '̳', '∷', '✋', '➧', '∋', '̿', 'ͧ', '┅', '⥤', '⬆', '⋱', \n        '☄', '↖', '⋮', '۔', '♌', 'ⓛ', '╕', '♓', '❯', '♍', '▋', '✺', '⭐', '✾', '♊', '➣', '▿', 'ⓑ', '♉', '⏠', '◾', '▹', \n        '⩽', '↦', '╥', '⍵', '⌋', '։', '➨', '∮', '⇥', 'ⓗ', 'ⓓ', '⁻', '⎝', '⌥', '⌉', '◔', '◑', '✼', '♎', '♐', '╪', '⊚', \n        '☒', '⇤', 'ⓜ', '⎠', '◐', '⚠', '╞', '◗', '⎕', 'ⓨ', '☟', 'ⓟ', '♟', '❈', '↬', 'ⓓ', '◻', '♮', '❙', '♤', '∉', '؛', \n        '⁂', 'ⓝ', '־', '♑', '╫', '╓', '╳', '⬅', '☔', '☸', '┄', '╧', '׃', '⎢', '❆', '⋄', '⚫', '̏', '☏', '➞', '͂', '␙', \n        'ⓤ', '◟', '̊', '⚐', '✙', '↙', '̾', '℘', '✷', '⍺', '❌', '⊢', '▵', '✅', 'ⓖ', '☨', '▰', '╡', 'ⓜ', '☤', '∽', '╘', \n        '˹', '↨', '♙', '⬇', '♱', '⌡', '⠀', '╛', '❕', '┉', 'ⓟ', '̀', '♖', 'ⓚ', '┆', '⎜', '◜', '⚾', '⤴', '✇', '╟', '⎛', \n        '☩', '➲', '➟', 'ⓥ', 'ⓗ', '⏝', '◃', '╢', '↯', '✆', '˃', '⍴', '❇', '⚽', '╒', '̸', '♜', '☓', '➳', '⇄', '☬', '⚑', \n        '✐', '⌃', '◅', '▢', '❐', '∊', '☈', '॥', '⎮', '▩', 'ு', '⊹', '‵', '␔', '☊', '➸', '̌', '☿', '⇉', '⊳', '╙', 'ⓦ', \n        '⇣', '｛', '̄', '↝', '⎟', '▍', '❗', '״', '΄', '▞', '◁', '⛄', '⇝', '⎪', '♁', '⇠', '☇', '✊', 'ி', '｝', '⭕', '➘', \n        '⁀', '☙', '❛', '❓', '⟲', '⇀', '≲', 'ⓕ', '⎥', '\\u06dd', 'ͤ', '₋', '̱', '̎', '♝', '≳', '▙', '➭', '܀', 'ⓖ', '⇛', '▊', \n        '⇗', '̷', '⇱', '℅', 'ⓧ', '⚛', '̐', '̕', '⇌', '␀', '≌', 'ⓦ', '⊤', '̓', '☦', 'ⓕ', '▜', '➙', 'ⓨ', '⌨', '◮', '☷', \n        '◍', 'ⓚ', '≔', '⏩', '⍳', '℞', '┋', '˻', '▚', '≺', 'ْ', '▟', '➻', '̪', '⏪', '̉', '⎞', '┇', '⍟', '⇪', '▎', '⇦', '␝', \n        '⤷', '≖', '⟶', '♗', '̴', '♄', 'ͨ', '̈', '❜', '̡', '▛', '✁', '➩', 'ா', '˂', '↥', '⏎', '⎷', '̲', '➖', '↲', '⩵', '̗', '❢', \n        '≎', '⚔', '⇇', '̑', '⊿', '̖', '☍', '➹', '⥊', '⁁', '✢']\n\ndef clean_punct(x):\n  x = str(x)\n  for punct in puncts:\n    if punct in x:\n      x = x.replace(punct, ' ')\n    return x","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:53.330055Z","iopub.execute_input":"2021-06-06T10:03:53.330376Z","iopub.status.idle":"2021-06-06T10:03:53.370391Z","shell.execute_reply.started":"2021-06-06T10:03:53.330346Z","shell.execute_reply":"2021-06-06T10:03:53.369316Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Sửa những từ dễ gây hiểu lầm **```correct_misspell()```**, ở trong mảng định nghĩa sẵn thì em sẽ convert tiếng Anh-Anh sang tiếng Anh-Mỹ, bên cạnh đó sửa sai cho những từ dễ viết sai<br>\nBởi vì Quora fetch các câu hỏi của người dùng nên việc sai chính tả là không thể tránh khỏi<br>\nMục đích là để convert sang càng nhiều từ càng tốt, qua đó đạt được độ phủ cao với vocab được build từ tệp GoogleNews đính kèm trong file ```embeddings.zip```","metadata":{}},{"cell_type":"code","source":"mispell_dict = {'colour': 'color', 'centre': 'center', 'favourite': 'favorite', 'travelling': 'traveling', 'counselling': 'counseling', 'theatre': 'theater', 'cancelled': 'canceled', 'labour': 'labor', 'organisation': 'organization', 'wwii': 'world war 2', 'citicise': 'criticize', 'youtu ': 'youtube ', 'Qoura': 'Quora', 'sallary': 'salary', 'Whta': 'What', 'narcisist': 'narcissist', 'howdo': 'how do', 'whatare': 'what are', 'howcan': 'how can', 'howmuch': 'how much', 'howmany': 'how many', 'whydo': 'why do', 'doI': 'do I', 'theBest': 'the best', 'howdoes': 'how does', 'mastrubation': 'masturbation', 'mastrubate': 'masturbate', \"mastrubating\": 'masturbating', 'pennis': 'penis', 'Etherium': 'bitcoin', 'narcissit': 'narcissist', 'bigdata': 'big data', '2k17': '2017', '2k18': '2018', 'qouta': 'quota', 'exboyfriend': 'ex boyfriend', 'airhostess': 'air hostess', \"whst\": 'what', 'watsapp': 'whatsapp', 'demonitisation': 'demonetization', 'demonitization': 'demonetization', 'demonetisation': 'demonetization', \n                'electroneum':'bitcoin','nanodegree':'degree','hotstar':'star','dream11':'dream','ftre':'fire','tensorflow':'framework','unocoin':'bitcoin',\n                'lnmiit':'limit','unacademy':'academy','altcoin':'bitcoin','altcoins':'bitcoin','litecoin':'bitcoin','coinbase':'bitcoin','cryptocurency':'cryptocurrency',\n                'simpliv':'simple','quoras':'quora','schizoids':'psychopath','remainers':'remainder','twinflame':'soulmate','quorans':'quora','brexit':'demonetized',\n                'iiest':'institute','dceu':'comics','pessat':'exam','uceed':'college','bhakts':'devotee','boruto':'anime',\n                'cryptocoin':'bitcoin','blockchains':'blockchain','fiancee':'fiance','redmi':'smartphone','oneplus':'smartphone','qoura':'quora','deepmind':'framework','ryzen':'cpu','whattsapp':'whatsapp',\n                'undertale':'adventure','zenfone':'smartphone','cryptocurencies':'cryptocurrencies','koinex':'bitcoin','zebpay':'bitcoin','binance':'bitcoin','whtsapp':'whatsapp',\n                'reactjs':'framework','bittrex':'bitcoin','bitconnect':'bitcoin','bitfinex':'bitcoin','yourquote':'your quote','whyis':'why is','jiophone':'smartphone',\n                'dogecoin':'bitcoin','onecoin':'bitcoin','poloniex':'bitcoin','7700k':'cpu','angular2':'framework','segwit2x':'bitcoin','hashflare':'bitcoin','940mx':'gpu',\n                'openai':'framework','hashflare':'bitcoin','1050ti':'gpu','nearbuy':'near buy','freebitco':'bitcoin','antminer':'bitcoin','filecoin':'bitcoin','whatapp':'whatsapp',\n                'empowr':'empower','1080ti':'gpu','crytocurrency':'cryptocurrency','8700k':'cpu','whatsaap':'whatsapp','g4560':'cpu','payymoney':'pay money',\n                'fuckboys':'fuck boys','intenship':'internship','zcash':'bitcoin','demonatisation':'demonetization','narcicist':'narcissist','mastuburation':'masturbation',\n                'trignometric':'trigonometric','cryptocurreny':'cryptocurrency','howdid':'how did','crytocurrencies':'cryptocurrencies','phycopath':'psychopath',\n                'bytecoin':'bitcoin','possesiveness':'possessiveness','scollege':'college','humanties':'humanities','altacoin':'bitcoin','demonitised':'demonetized',\n                'brasília':'brazilia','accolite':'accolyte','econimics':'economics','varrier':'warrier','quroa':'quora','statergy':'strategy','langague':'language',\n                'splatoon':'game','7600k':'cpu','gate2018':'gate 2018','in2018':'in 2018','narcassist':'narcissist','jiocoin':'bitcoin','hnlu':'hulu','7300hq':'cpu',\n                'weatern':'western','interledger':'blockchain','deplation':'deflation', 'cryptocurrencies':'cryptocurrency', 'bitcoin':'blockchain cryptocurrency',}\n\ndef correct_mispell(x):\n  words = x.split()\n  for i in range(0, len(words)):\n    if mispell_dict.get(words[i]) is not None:\n      words[i] = mispell_dict.get(words[i])\n    elif mispell_dict.get(words[i].lower()) is not None:\n      words[i] = mispell_dict.get(words[i].lower())\n        \n  words = \" \".join(words)\n  return words","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:53.371614Z","iopub.execute_input":"2021-06-06T10:03:53.371929Z","iopub.status.idle":"2021-06-06T10:03:53.391168Z","shell.execute_reply.started":"2021-06-06T10:03:53.3719Z","shell.execute_reply":"2021-06-06T10:03:53.390428Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**```remove_stopwords()```**: tức là những từ, chữ dạng *'do'*, *'does'*, *'did'*, *'should'*, ...<br>\nTuy nhiên qua lần chạy mà không loại bỏ stopwords thì kết quả thu nhận lại khả thi hơn với f1_score đạt trung bình ```0.64```<br>\nVới lần chạy mà loại bỏ stopwords thì kết quả thu nhận lại được với f1_score loanh quanh ```0.63x``` (x học tiểu học) và cao nhất là ```0.633``` qua nhiều lần thử<br>\n- Qua đó có thể đưa ra kết luận rằng việc loại bỏ stopwords trong mô hình em xây dựng ở bài này đã làm mất đi một phần ý nghĩa của câu và qua đó giảm độ chính xác của mô hình\n- Có vẻ như việc loại bỏ stop_words đã gián tiếp loại bỏ đi những từ có trong vocab được build từ GoogleNews và làm giảm độ phủ của vocab đối với dữ liệu được xử lý","metadata":{}},{"cell_type":"code","source":"def remove_stopwords(x):\n  x = [word for word in x.split() if word not in STOPWORDS]\n  x = ' '.join(x)\n  return x","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:53.392013Z","iopub.execute_input":"2021-06-06T10:03:53.392261Z","iopub.status.idle":"2021-06-06T10:03:53.409502Z","shell.execute_reply.started":"2021-06-06T10:03:53.392236Z","shell.execute_reply":"2021-06-06T10:03:53.40853Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**```clean_contractions()```**: Map những từ viết tắt sang dạng hoàn chỉnh, tránh sự hiểu lầm và không làm mất đi ý nghĩa của câu hỏi<br>\nMục đích là để đạt được độ phủ tốt nhất của vocab được build từ file embeddings","metadata":{}},{"cell_type":"code","source":"contraction_mapping = {\n \"I'm\": 'I am',\n \"I'm'a\": 'I am about to',\n \"I'm'o\": 'I am going to',\n \"I've\": 'I have',\n \"I'll\": 'I will',\n \"I'll've\": 'I will have',\n \"I'd\": 'I would',\n \"I'd've\": 'I would have',\n \"i'm\": 'i am',\n \"i'm'a\": 'i am about to',\n \"i'm'o\": 'i am going to',\n \"i've\": 'i have',\n \"i'll\": 'i will',\n \"i'll've\": 'i will have',\n \"i'd\": 'i would',\n \"i'd've\": 'i would have',\n 'Whatcha': 'What are you',\n 'whatcha': 'what are you',\n \"amn't\": 'am not',\n \"ain't\": 'are not',\n \"aren't\": 'are not',\n \"'cause\": 'because',\n \"can't\": 'can not',\n \"can't've\": 'can not have',\n \"could've\": 'could have',\n \"couldn't\": 'could not',\n \"couldn't've\": 'could not have',\n \"daren't\": 'dare not',\n \"daresn't\": 'dare not',\n \"dasn't\": 'dare not',\n \"didn't\": 'did not',\n 'didn’t': 'did not',\n \"don't\": 'do not',\n 'don’t': 'do not',\n \"doesn't\": 'does not',\n \"e'er\": 'ever',\n \"everyone's\": 'everyone is',\n 'finna': 'fixing to',\n 'gimme': 'give me',\n \"gon't\": 'go not',\n 'gonna': 'going to',\n 'gotta': 'got to',\n \"hadn't\": 'had not',\n \"hadn't've\": 'had not have',\n \"hasn't\": 'has not',\n \"haven't\": 'have not',\n \"he've\": 'he have',\n \"he's\": 'he is',\n \"he'll\": 'he will',\n \"he'll've\": 'he will have',\n \"he'd\": 'he would',\n \"he'd've\": 'he would have',\n \"here's\": 'here is',\n \"how're\": 'how are',\n \"how'd\": 'how did',\n \"how'd'y\": 'how do you',\n \"how's\": 'how is',\n \"how'll\": 'how will',\n \"isn't\": 'is not',\n \"it's\": 'it is',\n \"'tis\": 'it is',\n \"'twas\": 'it was',\n \"it'll\": 'it will',\n \"it'll've\": 'it will have',\n \"it'd\": 'it would',\n \"it'd've\": 'it would have',\n 'kinda': 'kind of',\n \"let's\": 'let us',\n 'luv': 'love',\n \"ma'am\": 'madam',\n \"may've\": 'may have',\n \"mayn't\": 'may not',\n \"might've\": 'might have',\n \"mightn't\": 'might not',\n \"mightn't've\": 'might not have',\n \"must've\": 'must have',\n \"mustn't\": 'must not',\n \"mustn't've\": 'must not have',\n \"needn't\": 'need not',\n \"needn't've\": 'need not have',\n \"ne'er\": 'never',\n \"o'\": 'of',\n \"o'clock\": 'of the clock',\n \"ol'\": 'old',\n \"oughtn't\": 'ought not',\n \"oughtn't've\": 'ought not have',\n \"o'er\": 'over',\n \"shan't\": 'shall not',\n \"sha'n't\": 'shall not',\n \"shalln't\": 'shall not',\n \"shan't've\": 'shall not have',\n \"she's\": 'she is',\n \"she'll\": 'she will',\n \"she'd\": 'she would',\n \"she'd've\": 'she would have',\n \"should've\": 'should have',\n \"shouldn't\": 'should not',\n \"shouldn't've\": 'should not have',\n \"so've\": 'so have',\n \"so's\": 'so is',\n \"somebody's\": 'somebody is',\n \"someone's\": 'someone is',\n \"something's\": 'something is',\n 'sux': 'sucks',\n \"that're\": 'that are',\n \"that's\": 'that is',\n \"that'll\": 'that will',\n \"that'd\": 'that would',\n \"that'd've\": 'that would have',\n 'em': 'them',\n \"there're\": 'there are',\n \"there's\": 'there is',\n \"there'll\": 'there will',\n \"there'd\": 'there would',\n \"there'd've\": 'there would have',\n \"these're\": 'these are',\n \"they're\": 'they are',\n \"they've\": 'they have',\n \"they'll\": 'they will',\n \"they'll've\": 'they will have',\n \"they'd\": 'they would',\n \"they'd've\": 'they would have',\n \"this's\": 'this is',\n \"those're\": 'those are',\n \"to've\": 'to have',\n 'wanna': 'want to',\n \"wasn't\": 'was not',\n \"we're\": 'we are',\n \"we've\": 'we have',\n \"we'll\": 'we will',\n \"we'll've\": 'we will have',\n \"we'd\": 'we would',\n \"we'd've\": 'we would have',\n \"weren't\": 'were not',\n \"what're\": 'what are',\n \"what'd\": 'what did',\n \"what've\": 'what have',\n \"what's\": 'what is',\n \"what'll\": 'what will',\n \"what'll've\": 'what will have',\n \"when've\": 'when have',\n \"when's\": 'when is',\n \"where're\": 'where are',\n \"where'd\": 'where did',\n \"where've\": 'where have',\n \"where's\": 'where is',\n \"which's\": 'which is',\n \"who're\": 'who are',\n \"who've\": 'who have',\n \"who's\": 'who is',\n \"who'll\": 'who will',\n \"who'll've\": 'who will have',\n \"who'd\": 'who would',\n \"who'd've\": 'who would have',\n \"why're\": 'why are',\n \"why'd\": 'why did',\n \"why've\": 'why have',\n \"why's\": 'why is',\n \"will've\": 'will have',\n \"won't\": 'will not',\n \"won't've\": 'will not have',\n \"would've\": 'would have',\n \"wouldn't\": 'would not',\n \"wouldn't've\": 'would not have',\n \"y'all\": 'you all',\n \"y'all're\": 'you all are',\n \"y'all've\": 'you all have',\n \"y'all'd\": 'you all would',\n \"y'all'd've\": 'you all would have',\n \"you're\": 'you are',\n \"you've\": 'you have',\n \"you'll've\": 'you shall have',\n \"you'll\": 'you will',\n \"you'd\": 'you would',\n \"you'd've\": 'you would have',\n 'jan.': 'january',\n 'feb.': 'february',\n 'mar.': 'march',\n 'apr.': 'april',\n 'jun.': 'june',\n 'jul.': 'july',\n 'aug.': 'august',\n 'sep.': 'september',\n 'oct.': 'october',\n 'nov.': 'november',\n 'dec.': 'december',\n 'I’m': 'I am',\n 'I’m’a': 'I am about to',\n 'I’m’o': 'I am going to',\n 'I’ve': 'I have',\n 'I’ll': 'I will',\n 'I’ll’ve': 'I will have',\n 'I’d': 'I would',\n 'I’d’ve': 'I would have',\n 'i’m': 'i am',\n 'i’m’a': 'i am about to',\n 'i’m’o': 'i am going to',\n 'i’ve': 'i have',\n 'i’ll': 'i will',\n 'i’ll’ve': 'i will have',\n 'i’d': 'i would',\n 'i’d’ve': 'i would have',\n 'amn’t': 'am not',\n 'ain’t': 'are not',\n 'aren’t': 'are not',\n '’cause': 'because',\n 'can’t': 'can not',\n 'can’t’ve': 'can not have',\n 'could’ve': 'could have',\n 'couldn’t': 'could not',\n 'couldn’t’ve': 'could not have',\n 'daren’t': 'dare not',\n 'daresn’t': 'dare not',\n 'dasn’t': 'dare not',\n 'doesn’t': 'does not',\n 'e’er': 'ever',\n 'everyone’s': 'everyone is',\n 'gon’t': 'go not',\n 'hadn’t': 'had not',\n 'hadn’t’ve': 'had not have',\n 'hasn’t': 'has not',\n 'haven’t': 'have not',\n 'he’ve': 'he have',\n 'he’s': 'he is',\n 'he’ll': 'he will',\n 'he’ll’ve': 'he will have',\n 'he’d': 'he would',\n 'he’d’ve': 'he would have',\n 'here’s': 'here is',\n 'how’re': 'how are',\n 'how’d': 'how did',\n 'how’d’y': 'how do you',\n 'how’s': 'how is',\n 'how’ll': 'how will',\n 'isn’t': 'is not',\n 'it’s': 'it is',\n '’tis': 'it is',\n '’twas': 'it was',\n 'it’ll': 'it will',\n 'it’ll’ve': 'it will have',\n 'it’d': 'it would',\n 'it’d’ve': 'it would have',\n 'let’s': 'let us',\n 'ma’am': 'madam',\n 'may’ve': 'may have',\n 'mayn’t': 'may not',\n 'might’ve': 'might have',\n 'mightn’t': 'might not',\n 'mightn’t’ve': 'might not have',\n 'must’ve': 'must have',\n 'mustn’t': 'must not',\n 'mustn’t’ve': 'must not have',\n 'needn’t': 'need not',\n 'needn’t’ve': 'need not have',\n 'ne’er': 'never',\n 'o’': 'of',\n 'o’clock': 'of the clock',\n 'ol’': 'old',\n 'oughtn’t': 'ought not',\n 'oughtn’t’ve': 'ought not have',\n 'o’er': 'over',\n 'shan’t': 'shall not',\n 'sha’n’t': 'shall not',\n 'shalln’t': 'shall not',\n 'shan’t’ve': 'shall not have',\n 'she’s': 'she is',\n 'she’ll': 'she will',\n 'she’d': 'she would',\n 'she’d’ve': 'she would have',\n 'should’ve': 'should have',\n 'shouldn’t': 'should not',\n 'shouldn’t’ve': 'should not have',\n 'so’ve': 'so have',\n 'so’s': 'so is',\n 'somebody’s': 'somebody is',\n 'someone’s': 'someone is',\n 'something’s': 'something is',\n 'that’re': 'that are',\n 'that’s': 'that is',\n 'that’ll': 'that will',\n 'that’d': 'that would',\n 'that’d’ve': 'that would have',\n 'there’re': 'there are',\n 'there’s': 'there is',\n 'there’ll': 'there will',\n 'there’d': 'there would',\n 'there’d’ve': 'there would have',\n 'these’re': 'these are',\n 'they’re': 'they are',\n 'they’ve': 'they have',\n 'they’ll': 'they will',\n 'they’ll’ve': 'they will have',\n 'they’d': 'they would',\n 'they’d’ve': 'they would have',\n 'this’s': 'this is',\n 'those’re': 'those are',\n 'to’ve': 'to have',\n 'wasn’t': 'was not',\n 'we’re': 'we are',\n 'we’ve': 'we have',\n 'we’ll': 'we will',\n 'we’ll’ve': 'we will have',\n 'we’d': 'we would',\n 'we’d’ve': 'we would have',\n 'weren’t': 'were not',\n 'what’re': 'what are',\n 'what’d': 'what did',\n 'what’ve': 'what have',\n 'what’s': 'what is',\n 'what’ll': 'what will',\n 'what’ll’ve': 'what will have',\n 'when’ve': 'when have',\n 'when’s': 'when is',\n 'where’re': 'where are',\n 'where’d': 'where did',\n 'where’ve': 'where have',\n 'where’s': 'where is',\n 'which’s': 'which is',\n 'who’re': 'who are',\n 'who’ve': 'who have',\n 'who’s': 'who is',\n 'who’ll': 'who will',\n 'who’ll’ve': 'who will have',\n 'who’d': 'who would',\n 'who’d’ve': 'who would have',\n 'why’re': 'why are',\n 'why’d': 'why did',\n 'why’ve': 'why have',\n 'why’s': 'why is',\n 'will’ve': 'will have',\n 'won’t': 'will not',\n 'won’t’ve': 'will not have',\n 'would’ve': 'would have',\n 'wouldn’t': 'would not',\n 'wouldn’t’ve': 'would not have',\n 'y’all': 'you all',\n 'y’all’re': 'you all are',\n 'y’all’ve': 'you all have',\n 'y’all’d': 'you all would',\n 'y’all’d’ve': 'you all would have',\n 'you’re': 'you are',\n 'you’ve': 'you have',\n 'you’ll’ve': 'you shall have',\n 'you’ll': 'you will',\n 'you’d': 'you would',\n 'you’d’ve': 'you would have'\n}\n\ndef clean_contractions(text):\n#     text = text.lower()\n    specials = [\"’\", \"‘\", \"´\", \"`\"]\n    for s in specials:\n        text = text.replace(s, \"'\")\n    \n    text = ' '.join([contraction_mapping[t] if t in contraction_mapping else t for t in text.split(\" \")])\n    return text","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:53.411059Z","iopub.execute_input":"2021-06-06T10:03:53.411362Z","iopub.status.idle":"2021-06-06T10:03:53.451818Z","shell.execute_reply.started":"2021-06-06T10:03:53.411335Z","shell.execute_reply":"2021-06-06T10:03:53.450764Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**```lemma_text()```**: để loại bỏ những từ có các digit cuối kiểu s/es/ed mà không làm mất đi nhiều ý nghĩa của câu (eg: words -> word)\nViệc sử dụng lemming thay vì stemming sẽ phần nào cải thiện độ chính xác của mô hình do:\n- **Stemming** cắt đi phần kí tự đặc biệt cuối từ một cách máy móc\n- **Lemming** thì lại thông qua từ đó, look up trong bảng từ vựng được thiết kế trước và thay thế vào chỗ đó\nDo vậy thì tuy lemming yêu cầu thời gian xử lý lâu hơn, nhưng đem lại giá trị cao hơn về độ chính xác của mô hình","metadata":{}},{"cell_type":"code","source":"# lemmatizer = WordNetLemmatizer()\n# def lemma_text(x):\n#   x = x.split()\n#   x = [lemmatizer.lemmatize(word) for word in x]\n#   x = ' '.join(x)\n#   return x","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:53.452955Z","iopub.execute_input":"2021-06-06T10:03:53.453246Z","iopub.status.idle":"2021-06-06T10:03:53.471135Z","shell.execute_reply.started":"2021-06-06T10:03:53.453218Z","shell.execute_reply":"2021-06-06T10:03:53.469708Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Merge các hàm tiền xử lý vào một hàm duy nhất để thao tác","metadata":{}},{"cell_type":"code","source":"def data_cleaning(x):\n  x = clean_tag(x)\n  x = clean_punct(x)\n  x = correct_mispell(x)\n  x = remove_stopwords(x)\n  x = clean_contractions(x)\n#   x = lemma_text(x)\n  return x","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:53.472434Z","iopub.execute_input":"2021-06-06T10:03:53.472756Z","iopub.status.idle":"2021-06-06T10:03:53.483057Z","shell.execute_reply.started":"2021-06-06T10:03:53.472726Z","shell.execute_reply":"2021-06-06T10:03:53.482038Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ở đây em sẽ tạo ra một hàm tiền xử lý khác với mục đích giữ lại nhiều từ nhất có thể, nhằm đạt được độ phủ cao với file pretrained<br>\nỞ trong hàm này, các công việc bao gồm:\n- ```clean_contractions()```: chuyển những từ viết tắt về dạng đầy đủ vốn có\n- loại bỏ đi các số trong câu\n\nỞ trong hàm này em tránh việc loại bỏ đi các từ như ở hàm trên (```remove_stopwords()```, ```clean_tag()```, ```clean_puncts()```, ```correct_mispell()```)<br>\nVà em sẽ không convert words sang dạng ```lowercase```","metadata":{}},{"cell_type":"code","source":"def Preprocess(doc):\n    corpus=[]\n    for text in tqdm(doc):\n        text=clean_contractions(text)\n        text=correct_mispell(text)\n        text=re.sub(r'[^a-z0-9A-Z]',\" \",text)\n        text=re.sub(r'[0-9]{1}',\"#\",text)\n        text=re.sub(r'[0-9]{2}','##',text)\n        text=re.sub(r'[0-9]{3}','###',text)\n        text=re.sub(r'[0-9]{4}','####',text)\n        text=re.sub(r'[0-9]{5,}','#####',text)\n        corpus.append(text)\n    return corpus","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:53.484403Z","iopub.execute_input":"2021-06-06T10:03:53.484815Z","iopub.status.idle":"2021-06-06T10:03:53.495258Z","shell.execute_reply.started":"2021-06-06T10:03:53.484766Z","shell.execute_reply":"2021-06-06T10:03:53.494326Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **b. Tiền xử lý các tập dữ liệu**\nCông đoạn tiền xử lý khá nhanh khi tốn trung bình là 30 giây","metadata":{}},{"cell_type":"code","source":"tqdm.pandas(desc=\"progress-bar\")\ndf_test['question_text_cleaned'] = df_test['question_text'].progress_map(lambda x: data_cleaning(x))\ndf_train['question_text_cleaned'] = df_train['question_text'].progress_map(lambda x: data_cleaning(x))","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:03:53.496651Z","iopub.execute_input":"2021-06-06T10:03:53.497076Z","iopub.status.idle":"2021-06-06T10:04:24.621155Z","shell.execute_reply.started":"2021-06-06T10:03:53.497045Z","shell.execute_reply":"2021-06-06T10:04:24.620239Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Kiểm tra lại xem các hàm có hoạt động đúng như kỳ vọng không","metadata":{}},{"cell_type":"code","source":"df_train.head(5)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:04:24.62244Z","iopub.execute_input":"2021-06-06T10:04:24.622724Z","iopub.status.idle":"2021-06-06T10:04:24.633964Z","shell.execute_reply.started":"2021-06-06T10:04:24.622698Z","shell.execute_reply":"2021-06-06T10:04:24.632875Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Có thể thấy việc loại bỏ nhiễu theo hàm tiền xử lý ```data_clean()``` đã hoạt động tốt, chuẩn hoá các câu một cách chính xác<br>\nTuy nhiên lại gây một trở ngại là nó làm mất đi khá nhiều từ, có thể nó không quá quan trọng nhưng ít nhiều cũng mang một phần ý nghĩa<br>\nTa sẽ so sánh hiệu năng giữa 2 hàm tiền xử lý ở bước sau, khi ta build tập ```vocabulary``` và kiểm tra độ phủ đối với tập dữ liệu được xử lý","metadata":{}},{"cell_type":"markdown","source":"# **3. Build tập vocab**","metadata":{}},{"cell_type":"markdown","source":"Ở đây em sẽ cố định sử dụng thằng **Google News** làm tiêu chuẩn<br>\nKiểm tra word embeddings đã giải nén với **Google News** đính kèm trong file embeddings.zip<br>\nĐịnh nghĩa các hàm để build vocab từ file **Google News** và hàm kiểm tra độ bao phủ của vocab đối với tập dữ liệu đã được preprocess<br>\n&nbsp;<br>\nTrong bài này, em sẽ giải nén tệp embedding để sử dụng tập các vector với các từ cho sẵn<br>\nCác từ trong file embeddings đều đính kèm một vector, và em sẽ xây ma trận embeddings dựa vào file embeddings đó","metadata":{}},{"cell_type":"code","source":"%%time\n### unzipping all the pretrained embeddings\n!unzip ../input/quora-insincere-questions-classification/embeddings.zip","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:04:24.635326Z","iopub.execute_input":"2021-06-06T10:04:24.63572Z","iopub.status.idle":"2021-06-06T10:07:54.593039Z","shell.execute_reply.started":"2021-06-06T10:04:24.635678Z","shell.execute_reply":"2021-06-06T10:07:54.59187Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!du -h ./","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:07:54.594731Z","iopub.execute_input":"2021-06-06T10:07:54.595035Z","iopub.status.idle":"2021-06-06T10:07:55.337839Z","shell.execute_reply.started":"2021-06-06T10:07:54.595004Z","shell.execute_reply":"2021-06-06T10:07:55.336334Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"File **Google News** có kích thước khá nhỏ: ```3.4G``` nhưng nó lại ở dạng binary của word embeddings nên đã có thư viện KeyedVector hỗ trợ và việc load khá nhanh nên em sẽ sử dụng thằng **Google News** này làm tham chiếu cho các embeddings khác trong tương lai<br>\nỞ dưới em sẽ định nghĩa các hàm build ```vocab``` và check độ phủ<br>\nDo embeddings là file có word đính kèm vector mô tả cho nó nên em sẽ lookup các từ có trong câu với file embeddings và tìm vector của chúng","metadata":{}},{"cell_type":"code","source":"%%time\n\n### Loading tập Google News Pretrained Embeddings vào bộ nhớ\nfile_name=\"./GoogleNews-vectors-negative300/GoogleNews-vectors-negative300.bin\"\nmodel_embed=KeyedVectors.load_word2vec_format(file_name,binary=True)\n# model_embed = load_embed1('./glove.840B.300d/glove.840B.300d.txt')\n\n### Xây dựng tập từ vựng dựa trên dữ liệu của tập Google News\ndef vocab_build(corpus):\n    vocab={}\n    for text in tqdm(corpus):\n        for word in text.split():\n            try:\n                vocab[word]+=1\n            except KeyError:\n                vocab[word]=1\n    return vocab\n\n\n### Kiểm tra tập Vocabulary xem tập vocab đó bao phủ bao nhiêu phần trăm tập dữ liệu của mình\ndef check_voc(vocab,model):\n    embed_words=[]\n    out_vocab={}\n    total_words=0\n    total_text=0\n    for i in tqdm(vocab):\n        try:\n            vec=model[i]\n            embed_words.append(vec)\n            total_words+=vocab[i]\n        except KeyError:\n            out_vocab[i]=vocab[i]\n            total_text+=vocab[i]\n    print(\"The {:.2f}% of vocabularies have Covered of corpus\".format(100*len(embed_words)/len(vocab)))\n    print(\"The {:.2f}% of total text had coverded \".format((100*total_words/(total_words+total_text))))\n    return out_vocab","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:07:55.339584Z","iopub.execute_input":"2021-06-06T10:07:55.339922Z","iopub.status.idle":"2021-06-06T10:09:12.548213Z","shell.execute_reply.started":"2021-06-06T10:07:55.339884Z","shell.execute_reply":"2021-06-06T10:09:12.545664Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Xây dựng tập vocab và kiểm tra độ bao phủ của tập vocab với tập vocab của pretrained model từ Google News<br>\nTrong block code này em sẽ xây tập vocab dựa trên bộ dữ liệu được xử lý với hàm preprocess thứ nhất là ```data_clean()```","metadata":{}},{"cell_type":"code","source":"### xây tập vocab và kiểm tra độ phủ của tập vocab với dữ liệu đã được xử lý\ntotal_text=pd.concat([df_train.question_text_cleaned,df_test.question_text_cleaned])\nvocabulary=vocab_build(total_text)\noov=check_voc(vocabulary,model_embed) #oov: out of vocab","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:09:12.559029Z","iopub.execute_input":"2021-06-06T10:09:12.559719Z","iopub.status.idle":"2021-06-06T10:09:24.795442Z","shell.execute_reply.started":"2021-06-06T10:09:12.559609Z","shell.execute_reply":"2021-06-06T10:09:24.794318Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Với hàm preprocess thứ nhất là ```data_clean()``` thì thầy có thể thấy kết quả độ bao phủ đạt **81.04%**, hơi thấp một chút dù dữ liệu đã được xử lý từ trước<br>\nNhưng tệ hại là tập vocab chỉ bao phủ **25.40%** corpus<br>\n**Nhận xét:**<br>\nCó thể là do việc đụng chạm tới nhiều từ trong câu khi loại bỏ nó, và sửa lỗi sai chính tả. Điều này đã khiến giảm đi đáng kể số lượng các từ được tìm thấy trong file pretrained\\\n**Giải pháp:**<br>\nSử dụng một hàm Preprocess mới mà ít đụng chạm tới các từ, chỉ xử lý một cách đơn giản các câu","metadata":{}},{"cell_type":"markdown","source":"Trong block code này, em sẽ sử dụng hàm Preprocess thứ 2 (hàm process đơn giản hơn)","metadata":{}},{"cell_type":"code","source":"df_test['question_text_cleaned_2'] = Preprocess(df_test['question_text'])\ndf_train['question_text_cleaned_2'] = Preprocess(df_train['question_text'])\ntotal_text_2=pd.concat([df_train.question_text_cleaned_2,df_test.question_text_cleaned_2])\nvocabulary2=vocab_build(total_text_2)\noov2=check_voc(vocabulary2, model_embed)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:09:24.798493Z","iopub.execute_input":"2021-06-06T10:09:24.798886Z","iopub.status.idle":"2021-06-06T10:10:44.642305Z","shell.execute_reply.started":"2021-06-06T10:09:24.798852Z","shell.execute_reply":"2021-06-06T10:10:44.641328Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Như thầy có thể thấy hiệu quả được cải thiện một cách rõ rệt:\n- Độ phủ của vocab với corpus tăng gấp **2.5 lần** lên **61.37%**\n- Độ phủ tăng lên **90.81%**, một con số khá ấn tượng\n\nNếu như chuyển các từ về dạng ```lowercase``` thì 2 con số tương đương là **38.85%** với corpus và **89.47%** với vocab.<br>\nNgười Anh đặt cái tôi của họ rất cao, nên trong tất cả các câu văn của họ thì từ 'I' (chỉ bản thân người nói) luôn được đặt ở trạng thái in hoa ```uppercase```nên nếu chúng ta chuẩn hoá các từ về ```lowercase``` thì sẽ không lookup được từ đó trong file pretrained embeddings và sẽ gây ra hiện tượng thiếu sót trong ma trận embeddings mà chúng ta chuẩn bị xây dựng","metadata":{}},{"cell_type":"markdown","source":"In ra các từ và số lần xuất hiện trong Vocabulary<br>\nVà do đó những câu mà với nhiều lần xuất hiện của các từ này có khả năng cao là các câu không đạt tiêu chuẩn","metadata":{}},{"cell_type":"code","source":"sort_oov=dict(sorted(oov2.items(), key=operator.itemgetter(1),reverse=True))\ndict(list(sort_oov.items())[:50])","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:10:44.643397Z","iopub.execute_input":"2021-06-06T10:10:44.643717Z","iopub.status.idle":"2021-06-06T10:10:44.800401Z","shell.execute_reply.started":"2021-06-06T10:10:44.643686Z","shell.execute_reply":"2021-06-06T10:10:44.799227Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Xoá các biến không dùng tới","metadata":{}},{"cell_type":"code","source":"del oov, oov2,sort_oov,total_text,total_text_2\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:10:44.802937Z","iopub.execute_input":"2021-06-06T10:10:44.803355Z","iopub.status.idle":"2021-06-06T10:10:45.22452Z","shell.execute_reply.started":"2021-06-06T10:10:44.80331Z","shell.execute_reply":"2021-06-06T10:10:45.2235Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Lấy ra index của từ trong tập từ điển phục vụ cho mã hóa onehot<br>\nỞ dưới đây em sẽ định nghĩa 2 hàm là:\n- ```get_word_index()```: lấy ra index của words trong tập từ vựng\n- ```fit_one_hot()```: mã hoá word_index sang dạng onehot dựa vào corpus","metadata":{}},{"cell_type":"code","source":"def get_word_index(vocab):\n    word_index=dict((w,i+1) for i,w in enumerate(vocab.keys()))\n    return word_index\ndef fit_one_hot(word_index,corpus):\n    sent=[]\n    for text in tqdm(corpus):\n        li=[]\n        for word in text.split():\n            try:\n                li.append(word_index[word])\n            except KeyError:\n                li.append(0)\n        sent.append(li)\n    return sent","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:10:45.225703Z","iopub.execute_input":"2021-06-06T10:10:45.225992Z","iopub.status.idle":"2021-06-06T10:10:45.23246Z","shell.execute_reply.started":"2021-06-06T10:10:45.225965Z","shell.execute_reply":"2021-06-06T10:10:45.231769Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Từ word_index ánh xạ sang tập train và encode sang mã hóa onehot\\\nChắc chắn không thể tránh khỏi việc các chuỗi có độ dài không bằng nhau nên việc cần phải làm là padding tất cả các chuỗi\\\nCắt đi các chuỗi có độ dài lớn hơn 40 và bù số 0 và những chuỗi có độ dài nhỏ hơn 40","metadata":{}},{"cell_type":"code","source":"train,val=train_test_split(df_train,test_size=0.2,stratify=df_train.target,random_state=123)\nvocab_size=len(vocabulary2)+1\nmax_len=40\n\nword_index=get_word_index(vocabulary2)\n### Chuẩn bị dữ liệu đã được xử lý\ntrain_text=train['question_text_cleaned_2']\nval_text=val['question_text_cleaned_2']\ntest_text=df_test['question_text_cleaned_2']\n\n### mã hóa câu trong tập train sang dạng onehot cho dễ xử lý\nencodes=fit_one_hot(word_index,train_text)\ntrain_padded=pad_sequences(encodes,maxlen=max_len,padding=\"post\")\n\n### mã hóa câu trong tập validation sang dạng onehot cho dễ xử lý\nencodes_=fit_one_hot(word_index,val_text)\nval_padded=pad_sequences(encodes_,maxlen=max_len,padding=\"post\")\n\n### mã hóa câu trong tập test sang dạng onehot cho dễ xử lý\nencodes__=fit_one_hot(word_index,test_text)\ntest_padded=pad_sequences(encodes__,maxlen=max_len,padding=\"post\")","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:10:45.233596Z","iopub.execute_input":"2021-06-06T10:10:45.233987Z","iopub.status.idle":"2021-06-06T10:11:15.85447Z","shell.execute_reply.started":"2021-06-06T10:10:45.233958Z","shell.execute_reply":"2021-06-06T10:11:15.853528Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Xây dựng ma trận embeddings dựa trên tập từ vựng có trước ở trong file embeddings<br>\nỞ đây mỗi hàng sẽ có vector embeddings cho mỗi từ duy nhất","metadata":{}},{"cell_type":"code","source":"count=0\nembedding_mat=np.zeros((vocab_size,300))\nfor word,i in tqdm(word_index.items()):\n    try:\n        vec=model_embed[word]\n        embedding_mat[i]=vec\n    except KeyError:\n        count+=1\n        continue\n\nprint(\"Number of Out of Vocabulary\",count)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:11:15.856051Z","iopub.execute_input":"2021-06-06T10:11:15.856467Z","iopub.status.idle":"2021-06-06T10:11:17.608368Z","shell.execute_reply.started":"2021-06-06T10:11:15.856422Z","shell.execute_reply":"2021-06-06T10:11:17.607362Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Như có thể thấy đối với tập pretrained **Google News** dù độ phủ khá cao nhưng số lượng từ nằm ngoài tập embeddings khá là lớn khi lên tới 99931 từ<br>\nVà cần phải có sự cải thiện về số lượng từ ```out_of_vocab``` nếu không thì hiệu quả của model sẽ kém.","metadata":{}},{"cell_type":"markdown","source":"# **4. Chuẩn bị Model**","metadata":{}},{"cell_type":"markdown","source":"Chuẩn bị model để train, ở đây em sử dụng Keras một phần vì nó có thể sử dụng với GPU và TPU<br>\nBên cạnh đó Keras hỗ trợ build model LSTM:\n\n- LSTM là một mạng cải tiến của RNN nhằm giải quyết các vấn đề nhớ các bước dài của RNN (Mạng RNN chứa các vòng lặp bên trong cho phép thông tin có thể lưu lại được nhằm giải quyết vấn đề nhớ thông tin của mạng nơ ron truyển thống)\n- Có thể coi LSTM là một dạng đặc biết của mạng nơ ron hồi quy, nó có khả năng học được các phụ thuộc xa \n- LSTM được thiết kế để tránh được vấn đề phụ thuộc xa (long-term dependency). Việc nhớ thông tin trong suốt thời gian dài là đặc tính mặc định của chúng, chứ ta không cần phải huấn luyện nó để có thể nhớ được. Tức là ngay nội tại của nó đã có thể ghi nhớ được mà không cần bất kì can thiệp nào.\n\n**Mô hình RNN**\n![rnn](https://colah.github.io/posts/2015-08-Understanding-LSTMs/img/LSTM3-SimpleRNN.png)\n\n> Mọi mạng hồi quy đều có dạng là một chuỗi các mô-đun lặp đi lặp lại của mạng nơ-ron. Với mạng RNN chuẩn, các mô-dun này có cấu trúc rất đơn giản, thường là một tầng tanh\n\n**Mô hình LSTM**\n![lstm](https://colah.github.io/posts/2015-08-Understanding-LSTMs/img/LSTM3-chain.png)\n\n> LSTM cũng có kiến trúc dạng chuỗi như vậy, nhưng các mô-đun trong nó có cấu trúc khác với mạng RNN chuẩn. Thay vì chỉ có một tầng mạng nơ-ron, chúng có tới 4 tầng tương tác với nhau một cách rất đặc biệt\n\nSử dụng embedding layer, mục đích là để embedding sang một không gian mới có chiều nhỏ hơn, giảm chiều dữ liệu<br>\nBidirectional(LSTM) để xây model LSTM<br>\nLSTM cũng là mạng CNN nên cần qua 2 lớp là Convo1D và Pool1D (convolution và pooling)","metadata":{}},{"cell_type":"code","source":"def get_model_origin(matrix):\n    inp = Input(shape=(max_len,))\n    x = Embedding(vocab_size,300,weights=[matrix],input_length=max_len,trainable=False)(inp)\n    x = Bidirectional(LSTM(128, return_sequences=True))(x)\n    x = Conv1D(64,3,activation=\"relu\")(x)\n    x = GlobalMaxPool1D()(x)\n    x = Dense(128, activation=\"relu\")(x)\n    x = Dropout(0.2)(x)\n    x = Dense(1, activation=\"sigmoid\")(x)\n    model = Model(inputs=inp, outputs=x)\n    return model","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:11:17.610082Z","iopub.execute_input":"2021-06-06T10:11:17.610511Z","iopub.status.idle":"2021-06-06T10:11:17.618468Z","shell.execute_reply.started":"2021-06-06T10:11:17.610464Z","shell.execute_reply":"2021-06-06T10:11:17.617492Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Sau khi build model ta được:<br>\n&nbsp;<br>\n![](https://scontent-hkt1-1.xx.fbcdn.net/v/t1.15752-9/188040073_326765032379284_902345591056315145_n.png?_nc_cat=101&ccb=1-3&_nc_sid=ae9488&_nc_ohc=4BVyEHYLeQ4AX9of6VB&_nc_ht=scontent-hkt1-1.xx&oh=dc02410144f78632c7701dcc691dac97&oe=60DD8312)","metadata":{}},{"cell_type":"markdown","source":"Gói việc tạo model vào một hàm ném vào **```stategy.scope()```** để enable khả năng chạy với TPU, tăng tốc độ train","metadata":{}},{"cell_type":"code","source":"opt=Adam(learning_rate=0.001)\nBATCH_SIZE = 1024\nbin_loss=tf.keras.losses.BinaryCrossentropy(\n                                            from_logits=False, \n                                            label_smoothing=0,\n                                            name='binary_crossentropy'\n                                        )\n\n### Xác định điểm callback để giảm learning rate, và restore lại trọng số tốt nhất kề trước \nearly_stopping=tf.keras.callbacks.EarlyStopping(\n                                                monitor=\"val_loss\",\n                                                patience=3,\n                                                mode=\"min\",\n                                                restore_best_weights=True\n                                              )\n### Giảm learning rate khi model không được cải thiên (càng học càng ngu)\nreduce_lr=tf.keras.callbacks.ReduceLROnPlateau(\n                                                monitor=\"val_loss\",\n                                                factor=0.2,\n                                                patience=2,\n                                                verbose=1,\n                                                mode=\"auto\"\n                                            )\n\nmy_callbacks=[early_stopping,reduce_lr]","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:11:17.619689Z","iopub.execute_input":"2021-06-06T10:11:17.620009Z","iopub.status.idle":"2021-06-06T10:11:17.642413Z","shell.execute_reply.started":"2021-06-06T10:11:17.619979Z","shell.execute_reply":"2021-06-06T10:11:17.641397Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ở trong hàm này em sử dụng Optimizer là ```Adam```. Về cơ bản ```Adam``` là sự kết hợp của ```Momentum``` và ```RMSprop```. Em sẽ không đi sâu vào 2 optimizer ```Momentum``` và ```RMSprop```.<br>\nCó thể hiểu ```Momentum``` là một quả cầu lao xuống dốc theo trọng lực, còn ```Adam``` như một quả cầu sắt rất nặng và có ma sát.<br>\nGiống với ```Adadelta``` và ```RMSprop```, nó duy trì trung bình bình phương độ dốc (slope) quá khứ vt và cũng đồng thời duy trì trung bình độ dốc quá khứ mt, giống ```Momentum```.\n\n<p align=\"center\">\n  <img width=\"300\" src=\"https://images.viblo.asia/1848e210-757a-4fd3-a662-2834b9c68f45.png\">\n</p>\n\nVới công thức update:\n$$g_n \\leftarrow \\nabla f(\\theta_{n-1})$$\n$$m_n \\leftarrow (\\frac{\\beta_1}{1-\\beta_1^{n}}).m_{n-1} + (\\frac{1 - \\beta_1}{1-\\beta_1^{n}}).g_n$$\n$$v_n \\leftarrow (\\frac{\\beta_2}{1-\\beta_2^{n}}).v_{n-1} + (\\frac{1 - \\beta_2}{1-\\beta_2^{n}}).g_n \\odot g_n$$\n$$\\theta_n \\leftarrow \\theta_{n-1} - \\frac{a.m_n}{\\sqrt{v_n} + \\epsilon}$$\n\nHiểu một cách ngắn gọn thì ```Adam``` là optimizer tốt nhất hiện nay\n\nĐánh giá độ mất mát thông tin dựa trên metric ```binary_cross_entropy```\n\nTrong quá trình model học, nếu ta fix cứng ```epoch``` thì nhiều khả năng sẽ gặp tình trạng khi model đạt một ngưỡng đủ tốt sẽ bắt đầu quá trình học sai (tức càng học thì độ hiệu quả càng giảm). Nên ở đây em sẽ tạo một hàm callbacks để phục hồi lại trọng số tốt nhất kề trước mà model học được \n\nVà em sẽ giảm learning rate mỗi lần model đi qua một ```epoch``` để tăng độ hiệu quả của mô hình, với mỗi lần giảm 90%<br>\nTức là: $$LR = LR*factor$$","metadata":{}},{"cell_type":"markdown","source":"Chạy chay thì mô hình này tốn khá nhiều thời gian nên em đã sử dụng TPU cho nó nhanh hơn tí 🤦‍♀️","metadata":{}},{"cell_type":"code","source":"strategy = None\n\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect() # TPU detection\n    strategy = tf.distribute.TPUStrategy(tpu)\n    print('Use TPU')\nexcept ValueError:\n    if len(tf.config.list_physical_devices('GPU')) > 0:\n        strategy = tf.distribute.MirroredStrategy()\n        print('Use GPU')\n    else:\n        strategy = tf.distribute.get_strategy()\n        print('Use CPU')","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:11:17.644034Z","iopub.execute_input":"2021-06-06T10:11:17.644431Z","iopub.status.idle":"2021-06-06T10:11:22.870963Z","shell.execute_reply.started":"2021-06-06T10:11:17.644385Z","shell.execute_reply":"2021-06-06T10:11:22.869896Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **5. Huấn luyện và Dự đoán**","metadata":{}},{"cell_type":"markdown","source":"### **a. Thử nghiệm model 1**","metadata":{}},{"cell_type":"markdown","source":"Compile model với 30 epoch cho máu, batch_size để ở mức cao để tận dụng sức mạnh tính toán của TPU và sử dụng với tập validation để validate dữ liệu, truyền vào tham số callback để mô hình càng học càng khôn chứ không được ngu đi<br>\nMột phần do em có cậu bạn cũng train với bọn Keras nhưng khi f1 score đạt được tốt rồi thì nó bắt đầu triệu chứng ngu đi khi mà loss tằng (overfitting)","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    google_model = get_model_origin(embedding_mat)\n    google_model.compile(loss=bin_loss, optimizer=opt, metrics=['accuracy'])\nhistory=google_model.fit(train_padded, train.target, batch_size=BATCH_SIZE, epochs=30, validation_data=(val_padded, val.target),callbacks=my_callbacks)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:11:22.87243Z","iopub.execute_input":"2021-06-06T10:11:22.873045Z","iopub.status.idle":"2021-06-06T10:15:10.490423Z","shell.execute_reply.started":"2021-06-06T10:11:22.872998Z","shell.execute_reply":"2021-06-06T10:15:10.489257Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"In ra f1 score, ```f1_score``` tốt nhất ở đây là ```0.673``` tức ```67.3%``` ứng với hàm Preprocess thứ 2 tức là không đụng chạm nhiều đến câu hỏi<br>\nTương ứng là f1_score = ```0.633``` tức ```63.3%``` đối với hàm Preprocess đầu tiên khi loại bỏ ```stop_words```, ```lemma_words```, ...\nCó thể nhận xét rằng:\n- Với cách tiếp cận dựa trên pretrained thì việc tiền xử lý dữ liệu quá nhiều thực sự có ảnh hưởng lớn tới hiệu năng của model khi mà nó làm mất đi phần nhiều ý nghĩa các các từ có trong câu hỏi\n- Có thể do cách cài đặt khác nhau nhưng về cơ bản với cách tiếp cận dựa vào pretrained thì ta không nên xử lý nhiễu của dữ liệu nhiều, có chăng là loại bỏ đi các digits, các công thức toán và các đường dẫn liên kết ","metadata":{}},{"cell_type":"code","source":"# summarize history for loss\nplt.figure(figsize=(15, 5)).add_subplot(1, 2, 1)\nplt.plot(history.history['loss'])\nplt.plot(history.history['val_loss'])\nplt.title('model loss')\nplt.ylabel('loss')\nplt.xlabel('epoch')\nplt.legend(['train', 'test'], loc='upper left')\n# plt.show()\n# summarize history for accuracy\nplt.figure(figsize=(15, 5)).add_subplot(1, 2, 2)\nplt.plot(history.history['accuracy'])\nplt.plot(history.history['val_accuracy'])\nplt.title('model accuracy')\nplt.ylabel('accuracy')\nplt.xlabel('epoch')\nplt.legend(['train', 'test'], loc='upper left')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:15:10.492311Z","iopub.execute_input":"2021-06-06T10:15:10.492643Z","iopub.status.idle":"2021-06-06T10:15:10.861857Z","shell.execute_reply.started":"2021-06-06T10:15:10.49261Z","shell.execute_reply":"2021-06-06T10:15:10.860601Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Có thể thấy được rằng mô hình hoạt động khá hiệu quả với tập **```train```** khi mà ```loss``` giảm đều và ```accuracy``` tăng đều qua các epoch<br>\nTuy nhiên thì với tập **```test```** lại hoạt động không được hiệu quả khi ```loss``` và ```accuracy``` có hình dạng trồi sụt nhìn như sóng biển. Và tệ hơn là với tập **```test```** thì ```loss``` về các epoch cuối lại tăng khá cao.<br>\nVậy thì có thể kết luận là do **model** đã xây dựng **hoạt động chưa hiệu quả**, cần phải xem xét lại<br>","metadata":{}},{"cell_type":"markdown","source":"Lấy ra ```thresh_hold``` cho ```result``` tốt nhất và sử dụng nó để submit lên Kaggle","metadata":{}},{"cell_type":"code","source":"google_y_pre=google_model.predict(val_padded, verbose=1)\nbest_score = 0\nbest_thresh = 0\nfor thresh in np.arange(0.1,0.5,0.01):\n    if(best_score < metrics.f1_score(val.target,(google_y_pre>thresh).astype(int))):\n        best_score = metrics.f1_score(val.target,(google_y_pre>thresh))\n        best_thresh = round(thresh, 2)\n    print(\"threshold {0:2.2f} f1 score:{1:2.3f}\".format(thresh,metrics.f1_score(val.target,(google_y_pre>thresh).astype(int))))\nprint(\"\\033[1mBest result {0:2.3f} in thresh_hold {1:2.2f}\\033[0m\".format(best_score, best_thresh))","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:15:10.86376Z","iopub.execute_input":"2021-06-06T10:15:10.86425Z","iopub.status.idle":"2021-06-06T10:16:25.719025Z","shell.execute_reply.started":"2021-06-06T10:15:10.864203Z","shell.execute_reply":"2021-06-06T10:16:25.717957Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Như thầy có thể thấy thì kết quả tốt nhất loanh quanh ở ngưỡng tối đa **```0.67x```**<br> (x>3)\nTa sẽ sử dụng ngưỡng trung bình là **```0.675```** này để tham chiếu với các ma trận embeddings được xây dựng với 3 anh còn lại.","metadata":{}},{"cell_type":"code","source":"del model_embed, history, best_score, best_thresh, google_model, google_y_pre\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:16:25.72077Z","iopub.execute_input":"2021-06-06T10:16:25.721217Z","iopub.status.idle":"2021-06-06T10:16:26.886049Z","shell.execute_reply.started":"2021-06-06T10:16:25.721174Z","shell.execute_reply":"2021-06-06T10:16:26.884926Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **b. Thử nghiệm model 2**","metadata":{}},{"cell_type":"markdown","source":"Bây giờ em sẽ thử nghiệm một model mới, dựa trên nguồn tham khảo từ tác giả **[taziz437](https://www.kaggle.com/taziz437)** với bài dự thi [TariqAziz](https://www.kaggle.com/taziz437/tariqaziz) <br>\nVới **model** cũ tức ```get_model_origin``` thì em đã build model với mô hình **LSTM** 128 chiều<br>\nCòn với tác giả **[taziz437](https://www.kaggle.com/taziz437)** anh ấy đã kết hợp mô hình **LSTM** 256 chiều và **GRU** 128 chiều để tạo model với 2 lớp **Pooling1D**<br>\nTheo cảm quan đánh giá thì có vẻ **model** này sẽ cho một hiệu năng tốt hơn và ổn định hơn so với **model** mà em đã tự tạo ở trên.<br>\nDông dài thì cũng đến thế, em sẽ thử nghiệm model mới ở ngay bên dưới.","metadata":{}},{"cell_type":"code","source":"def get_model(matrix):\n    inp = Input(shape=(max_len,))\n    x = Embedding(vocab_size, 300, weights=[matrix], trainable=False)(inp)\n    x = SpatialDropout1D(0.3)(x)\n    x1 = Bidirectional(LSTM(256, return_sequences=True))(x)\n    x2 = Bidirectional(GRU(128, return_sequences=True))(x1)\n    max_pool1 = GlobalMaxPooling1D()(x1)\n    max_pool2 = GlobalMaxPooling1D()(x2)\n    conc = Concatenate()([max_pool1, max_pool2])\n    predictions = Dense(1, activation='sigmoid')(conc)\n    model = Model(inputs=inp, outputs=predictions)\n    adam = optimizers.Adam(lr=0.001)\n    model.compile(optimizer=adam, loss='binary_crossentropy', metrics=['accuracy'])\n    return model","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:16:26.887719Z","iopub.execute_input":"2021-06-06T10:16:26.888162Z","iopub.status.idle":"2021-06-06T10:16:26.897145Z","shell.execute_reply.started":"2021-06-06T10:16:26.888118Z","shell.execute_reply":"2021-06-06T10:16:26.895927Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Model được thiết kế với cùng ```optimizer``` là ```Adam``` với ```learning_rate=0.001``` và với metric ```accuracy``` và hàm ```loss``` được đánh giá dựa trên ```binary_crossentropy```","metadata":{}},{"cell_type":"code","source":"strategy = None\n\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect() # TPU detection\n    strategy = tf.distribute.TPUStrategy(tpu)\n    print('Use TPU')\nexcept ValueError:\n    if len(tf.config.list_physical_devices('GPU')) > 0:\n        strategy = tf.distribute.MirroredStrategy()\n        print('Use GPU')\n    else:\n        strategy = tf.distribute.get_strategy()\n        print('Use CPU')","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:16:26.898558Z","iopub.execute_input":"2021-06-06T10:16:26.89889Z","iopub.status.idle":"2021-06-06T10:16:32.355989Z","shell.execute_reply.started":"2021-06-06T10:16:26.898852Z","shell.execute_reply":"2021-06-06T10:16:32.354886Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Nếu không khởi tạo lại ```strategy.scope()``` thì em bị gặp lỗi là ```loss``` của model mới sẽ cao hơn mức bình thường dẫn đến việc ```model``` phải chạy qua nhiều epoch hơn và tốn thời gian để train hơn<br>\nNên em phải thực hiện việc init lại ```strategy.scope()``` mỗi lần tạo và compile một model mới.","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    google_model = get_model(embedding_mat)\n    google_model.compile(loss=bin_loss, optimizer=Adam(lr=0.001), metrics=['accuracy'])\nhistory=google_model.fit(train_padded, train.target, batch_size=BATCH_SIZE, epochs=30, validation_data=(val_padded, val.target),callbacks=my_callbacks)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:16:32.357467Z","iopub.execute_input":"2021-06-06T10:16:32.358094Z","iopub.status.idle":"2021-06-06T10:22:13.417593Z","shell.execute_reply.started":"2021-06-06T10:16:32.358048Z","shell.execute_reply":"2021-06-06T10:22:13.416585Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Có thể thấy rằng model mới hoạt động hiệu quả hơn hẳn model cũ khi mà nó đã đẩy được ```f1_score``` lên thêm 0.01 tức 1% từ mức ```0.67``` làm cơ sở lên mức ```0.68x``` (x bé tí teo).<br>\nTuy nhiên đây được coi là mức cải thiện khá tốt so với model cũ, ```loss``` của tập validate cũng nhỏ hơn","metadata":{}},{"cell_type":"code","source":"# summarize history for loss\nplt.figure(figsize=(15, 5)).add_subplot(1, 2, 1)\nplt.plot(history.history['loss'])\nplt.plot(history.history['val_loss'])\nplt.title('model loss')\nplt.ylabel('loss')\nplt.xlabel('epoch')\nplt.legend(['train', 'test'], loc='upper left')\n# plt.show()\n# summarize history for accuracy\nplt.figure(figsize=(15, 5)).add_subplot(1, 2, 2)\nplt.plot(history.history['accuracy'])\nplt.plot(history.history['val_accuracy'])\nplt.title('model accuracy')\nplt.ylabel('accuracy')\nplt.xlabel('epoch')\nplt.legend(['train', 'test'], loc='upper left')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:22:13.419518Z","iopub.execute_input":"2021-06-06T10:22:13.419998Z","iopub.status.idle":"2021-06-06T10:22:13.73807Z","shell.execute_reply.started":"2021-06-06T10:22:13.419953Z","shell.execute_reply":"2021-06-06T10:22:13.736982Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Thấy được một điều rõ ràng là ```loss``` của tập validate đã giảm đi và có hình dạng ổn định hơn so với model cũ khi mà về những ```epoch``` cuối thì loss không tăng nhiều như model cũ.<br>\n```accuracy``` cũng được cải thiện hơn phần nào và cũng ổn định hơn một chút, ở mức cao","metadata":{}},{"cell_type":"code","source":"google_y_pre=google_model.predict(val_padded, verbose=1)\nbest_score = 0\nbest_thresh = 0\nfor thresh in np.arange(0.1,0.5,0.01):\n    if(best_score < metrics.f1_score(val.target,(google_y_pre>thresh).astype(int))):\n        best_score = metrics.f1_score(val.target,(google_y_pre>thresh))\n        best_thresh = round(thresh, 2)\n    print(\"threshold {0:2.2f} f1 score:{1:2.3f}\".format(thresh,metrics.f1_score(val.target,(google_y_pre>thresh).astype(int))))\nprint(\"\\033[1mBest result {0:2.3f} in thresh_hold {1:2.2f}\\033[0m\".format(best_score, best_thresh))","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:22:13.739392Z","iopub.execute_input":"2021-06-06T10:22:13.739701Z","iopub.status.idle":"2021-06-06T10:23:33.092028Z","shell.execute_reply.started":"2021-06-06T10:22:13.739671Z","shell.execute_reply":"2021-06-06T10:23:33.090997Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Model cũ khá chật vật để đạt mức maximum là ```0.675``` thì model mới dễ dàng đạt được mức trên ```0.68``` của ```f1_score``` và xấp xỉ ở ngưỡng ```0.69```<br>\nVậy có thể yên tâm sử dụng model mới bởi vì hiệu suất và độ ổn định của nó","metadata":{}},{"cell_type":"code","source":"threshold=best_thresh\ngoogle_y_test_pre=google_model.predict(test_padded, batch_size=BATCH_SIZE, verbose=1)\ngoogle_y_test_pre=(google_y_test_pre>thresh).astype(int)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:23:33.093656Z","iopub.execute_input":"2021-06-06T10:23:33.093978Z","iopub.status.idle":"2021-06-06T10:23:37.950347Z","shell.execute_reply.started":"2021-06-06T10:23:33.093947Z","shell.execute_reply":"2021-06-06T10:23:37.949456Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del embedding_mat, history, best_score, best_thresh\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:23:37.951942Z","iopub.execute_input":"2021-06-06T10:23:37.952522Z","iopub.status.idle":"2021-06-06T10:23:39.588186Z","shell.execute_reply.started":"2021-06-06T10:23:37.952473Z","shell.execute_reply":"2021-06-06T10:23:39.58727Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **6. So sánh, mở rộng**\nDo sử dụng 1 pretrained là **Google News** nên kết quả thu được ở mức là **```0.68x``` (x<=3)** f1_score<br>\n&nbsp;<br>\nBởi vì 3 thằng còn lại (không phải **Google News**) đều không ở dạng binary và không phải ở dạng chuẩn word_vector nên không đọc được bằng KeyedVectors, em sẽ định nghĩa một hàm để đọc các file embeddings còn lại <br>\nTuy nhiên do tự định nghĩa nên thời gian load dữ liệu vào biến khá là lâu","metadata":{}},{"cell_type":"markdown","source":"#### **Reference:** https://www.kaggle.com/theoviel/improve-your-score-with-some-text-preprocessing","metadata":{}},{"cell_type":"markdown","source":"Do có thằng ```wiki``` là ngoại đạo với đuôi là ```.vec``` nên cần định nghĩa riêng cho nó 1 ```statement```","metadata":{}},{"cell_type":"code","source":"def load_embed(file):\n    def get_coefs(word,*arr):\n        return word, np.asarray(arr, dtype='float32')\n\n    if file == '../input/embeddings/wiki-news-300d-1M/wiki-news-300d-1M.vec':\n        embeddings_index = dict(get_coefs(*o.split(\" \")) for o in open(file) if len(o)>100)\n    else:\n        embeddings_index = dict(get_coefs(*o.split(\" \")) for o in open(file, encoding='latin'))\n\n    return embeddings_index","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:23:39.589457Z","iopub.execute_input":"2021-06-06T10:23:39.58974Z","iopub.status.idle":"2021-06-06T10:23:39.595616Z","shell.execute_reply.started":"2021-06-06T10:23:39.589713Z","shell.execute_reply":"2021-06-06T10:23:39.594907Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!tree -h ./","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:23:39.596734Z","iopub.execute_input":"2021-06-06T10:23:39.597136Z","iopub.status.idle":"2021-06-06T10:23:40.486474Z","shell.execute_reply.started":"2021-06-06T10:23:39.597108Z","shell.execute_reply":"2021-06-06T10:23:40.485387Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"glove_path = './glove.840B.300d/glove.840B.300d.txt'\nparagram_path = './paragram_300_sl999/paragram_300_sl999.txt'\nwiki_path = './wiki-news-300d-1M/wiki-news-300d-1M.vec'","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:23:40.488039Z","iopub.execute_input":"2021-06-06T10:23:40.488477Z","iopub.status.idle":"2021-06-06T10:23:40.492602Z","shell.execute_reply.started":"2021-06-06T10:23:40.488443Z","shell.execute_reply":"2021-06-06T10:23:40.491749Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Load các file embeddings vào biến<br>\nTuy nhiên do load thủ công không qua thư viện nên việc load khá là chậm, mất khoảng 9' để load cả 3 anh<br>\nBọn ```KeyedVector``` có hỗ trợ load ```glove``` nhưng phải covert qua binary nên về tốc độ thì cũng không hơn anh thủ công là mấy","metadata":{}},{"cell_type":"code","source":"%%time\nglove_embed = load_embed(glove_path)\nprint(\"\\033[1mGlove Coverage: \\033[0m]\")\noov_glove = check_voc(vocabulary2, glove_embed)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:23:40.494232Z","iopub.execute_input":"2021-06-06T10:23:40.494555Z","iopub.status.idle":"2021-06-06T10:27:49.737427Z","shell.execute_reply.started":"2021-06-06T10:23:40.494527Z","shell.execute_reply":"2021-06-06T10:27:49.736299Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Có thể dễ dàng nhận thấy anh **Google News** cho độ phủ không tốt bằng anh **Glove** khi anh **Glove** đạt độ phủ trên **99.39%** và **72.81%** với corpus qua đó có thể đoán được rằng ma trận embeddings của anh **Glove** sẽ giúp cho mô hình đạt được hiệu suất cao hơn","metadata":{}},{"cell_type":"markdown","source":"### **a. Glove**","metadata":{}},{"cell_type":"code","source":"strategy = None\n\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect() # TPU detection\n    strategy = tf.distribute.TPUStrategy(tpu)\n    print('Use TPU')\nexcept ValueError:\n    if len(tf.config.list_physical_devices('GPU')) > 0:\n        strategy = tf.distribute.MirroredStrategy()\n        print('Use GPU')\n    else:\n        strategy = tf.distribute.get_strategy()\n        print('Use CPU')","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:27:49.738954Z","iopub.execute_input":"2021-06-06T10:27:49.739235Z","iopub.status.idle":"2021-06-06T10:27:55.354127Z","shell.execute_reply.started":"2021-06-06T10:27:49.739209Z","shell.execute_reply":"2021-06-06T10:27:55.352885Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Lần đầu chạy không định nghĩa lại ```strategy.scope()``` thì em gặp một hiện tượng lạ đó là với việc build model mới và chạy trong ```strategy.scope()``` thì model có vẻ bị sai khá nhiều khi ```loss``` lên tới mức ```0.22``` với ```compile``` lần 1 và ```loss``` ở mức ```0.27``` với lần ```compile``` thứ 2<br>\nDo vậy em đoán khả năng là do thằng ```strategy.scope()``` giữ lại một số giá trị gì đó nên em đã định nghĩa lại thằng ```strategy.scope()```","metadata":{}},{"cell_type":"code","source":"count=0\nglove_embedding_mat=np.zeros((vocab_size,300))\nfor word,i in tqdm(word_index.items()):\n    try:\n        vec=glove_embed[word]\n        glove_embedding_mat[i]=vec\n    except KeyError:\n        count+=1\n        continue\n\nprint(\"Number of Out of Vocabulary\",count)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:27:55.355561Z","iopub.execute_input":"2021-06-06T10:27:55.35608Z","iopub.status.idle":"2021-06-06T10:27:57.175459Z","shell.execute_reply.started":"2021-06-06T10:27:55.356045Z","shell.execute_reply":"2021-06-06T10:27:57.17253Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Có thể thấy được rằng số từ ```out_of_vocab``` giảm đi khá nhiều, ở mức 70336 từ, và qua đó có thể kỳ vọng mô hình đem lại độ chính xác cao hơn.<br>\nVà em vẫn chưa có cách để xử lý những từ ```out_of_vocab``` như này 🥺🥺🥺","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    glove_model = get_model(glove_embedding_mat)\n    glove_model.compile(loss=bin_loss, optimizer=Adam(lr=0.001), metrics=['accuracy'])\nglove_history=glove_model.fit(train_padded, train.target, batch_size=BATCH_SIZE, epochs=30, validation_data=(val_padded, val.target),callbacks=my_callbacks)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:27:57.176862Z","iopub.execute_input":"2021-06-06T10:27:57.177155Z","iopub.status.idle":"2021-06-06T10:32:36.27764Z","shell.execute_reply.started":"2021-06-06T10:27:57.177127Z","shell.execute_reply":"2021-06-06T10:32:36.276598Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Khi truyền optimizer bằng biến ```opt``` cũ thì đã xảy ra lỗi và bắt buộc phải tự pass lại metrics mới vào trong ```model.compile()``` do đó em đã truyền vào optimizer là ```Adam``` với ```learning_rate``` bằng ```0.001``` giống ở trên để đảm bảo sự công bằng cho thử nghiệm","metadata":{}},{"cell_type":"code","source":"# summarize history for loss\nplt.figure(figsize=(15, 5)).add_subplot(1, 2, 1)\nplt.plot(glove_history.history['loss'])\nplt.plot(glove_history.history['val_loss'])\nplt.title('model loss')\nplt.ylabel('loss')\nplt.xlabel('epoch')\nplt.legend(['train', 'test'], loc='upper left')\n# plt.show()\n# summarize history for accuracy\nplt.figure(figsize=(15, 5)).add_subplot(1, 2, 2)\nplt.plot(glove_history.history['accuracy'])\nplt.plot(glove_history.history['val_accuracy'])\nplt.title('model accuracy')\nplt.ylabel('accuracy')\nplt.xlabel('epoch')\nplt.legend(['train', 'test'], loc='upper left')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:32:36.279257Z","iopub.execute_input":"2021-06-06T10:32:36.279755Z","iopub.status.idle":"2021-06-06T10:32:36.648191Z","shell.execute_reply.started":"2021-06-06T10:32:36.27972Z","shell.execute_reply":"2021-06-06T10:32:36.647429Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Khi sử dụng mô hình với ma trận embeddings sinh ra từ **Glove** thì nó cũng cho ra một kết quả khá giống với **Google News** khi mà **```train```** thì mô hình hoạt động khá hiệu quả <br>\nTuy nhiên cũng như **Google News** thì đối với tập test, càng về epoch cuối thì ```loss``` càng tăng cao, tuy nhiên có sự cải thiện hơn **Google News** khi mà ```accuracy``` có hình dạng ổn định hơn một chút.","metadata":{}},{"cell_type":"code","source":"glove_y_pre=glove_model.predict(val_padded, verbose=1)\nbest_score = 0\nbest_thresh = 0\nfor thresh in np.arange(0.1,0.5,0.01):\n    if(best_score < metrics.f1_score(val.target,(glove_y_pre>thresh).astype(int))):\n        best_score = metrics.f1_score(val.target,(glove_y_pre>thresh))\n        best_thresh = round(thresh, 2)\n    print(\"threshold {0:2.2f} f1 score:{1:2.3f}\".format(thresh,metrics.f1_score(val.target,(glove_y_pre>thresh).astype(int))))\nprint(\"\\033[1mBest result {0:2.3f} in thresh_hold {1:2.2f}\\033[0m\".format(best_score, best_thresh))","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:32:36.649207Z","iopub.execute_input":"2021-06-06T10:32:36.649601Z","iopub.status.idle":"2021-06-06T10:33:58.025236Z","shell.execute_reply.started":"2021-06-06T10:32:36.649572Z","shell.execute_reply":"2021-06-06T10:33:58.02414Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Rõ ràng khi sử dụng với file embeddings **Glove** thì kết quả đã có sự cải thiện với ```f1_score``` loanh quanh **```0.69```** tức **69%**<br>\nTuy nhiên sự cải thiện là không nhiều <br>\nĐiều này có thể kết luận rằng số từ ```out_of_vocab``` và độ phủ của vocab thực sự ảnh hưởng tới hiệu năng của model.<br>","metadata":{}},{"cell_type":"code","source":"threshold=best_thresh\nglove_y_test_pre=glove_model.predict(test_padded, batch_size=BATCH_SIZE, verbose=1)\nglove_y_test_pre=(glove_y_test_pre>thresh).astype(int)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:33:58.026651Z","iopub.execute_input":"2021-06-06T10:33:58.02697Z","iopub.status.idle":"2021-06-06T10:34:03.533197Z","shell.execute_reply.started":"2021-06-06T10:33:58.026941Z","shell.execute_reply":"2021-06-06T10:34:03.531846Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del glove_embed, glove_embedding_mat, glove_history, best_score, best_thresh\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:34:03.535067Z","iopub.execute_input":"2021-06-06T10:34:03.535481Z","iopub.status.idle":"2021-06-06T10:34:06.874078Z","shell.execute_reply.started":"2021-06-06T10:34:03.535436Z","shell.execute_reply":"2021-06-06T10:34:06.8731Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **b. Paragram**","metadata":{}},{"cell_type":"code","source":"strategy = None\n\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect() # TPU detection\n    strategy = tf.distribute.TPUStrategy(tpu)\n    print('Use TPU')\nexcept ValueError:\n    if len(tf.config.list_physical_devices('GPU')) > 0:\n        strategy = tf.distribute.MirroredStrategy()\n        print('Use GPU')\n    else:\n        strategy = tf.distribute.get_strategy()\n        print('Use CPU')","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:34:06.877706Z","iopub.execute_input":"2021-06-06T10:34:06.878049Z","iopub.status.idle":"2021-06-06T10:34:12.489074Z","shell.execute_reply.started":"2021-06-06T10:34:06.87802Z","shell.execute_reply":"2021-06-06T10:34:12.48786Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nparagram_embed = load_embed(paragram_path)\nprint(\"\\033[1mParagram Coverage: \\033[0m]\")\noov_paragram = check_voc(vocabulary2, paragram_embed)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:34:12.491186Z","iopub.execute_input":"2021-06-06T10:34:12.491626Z","iopub.status.idle":"2021-06-06T10:37:26.70045Z","shell.execute_reply.started":"2021-06-06T10:34:12.49158Z","shell.execute_reply":"2021-06-06T10:37:26.699367Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Có thể thấy rằng độ phủ của anh **Paragram** còn tốt hơn anh **Glove** khi đạt tới **81.11%** và độ phủ với ```corpus``` đạt tới **38.97%**.<br> Kì vọng là sẽ tạo ra ma trận embeddings giúp cho model có hiệu năng tệ nhất","metadata":{}},{"cell_type":"code","source":"count=0\npara_embedding_mat=np.zeros((vocab_size,300))\nfor word,i in tqdm(word_index.items()):\n    try:\n        vec=paragram_embed[word.lower()]\n        para_embedding_mat[i]=vec\n    except KeyError:\n        count+=1\n        continue\n\nprint(\"Number of Out of Vocabulary\",count)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:37:26.701715Z","iopub.execute_input":"2021-06-06T10:37:26.702017Z","iopub.status.idle":"2021-06-06T10:37:28.009629Z","shell.execute_reply.started":"2021-06-06T10:37:26.701989Z","shell.execute_reply":"2021-06-06T10:37:28.008648Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"So với 2 file embeddings ở trên là **Google News** và **Glove** thì số lượng từ nằm ngoài tập vocab ```out_of_vocab``` tăng lên khá nhiều khi đạt con số 157867 từ nếu không chuẩn hoá hết sang dạng lowercase<br>\nBên cạnh đó thì **Paragram** cho độ phủ tệ nhất với **81.11%**. nếu không chuẩn hoá sang lowercase<br>\nCó thể kỳ vọng với ma trận embeddings sinh ra từ **Paragram** thì mô hình sẽ đạt hiệu quả tệ nhất.<br>\nTuy nhiên khi chuẩn hoá sang lowercase và lookup thì độ phủ của **Paragram** tăng lên rõ rệt và có số từ ```out_of_vocab``` giảm mạnh xuống còn 61842 từ, do vậy ma trận embeddings sẽ giúp model đạt hiệu quả cao hơn","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    para_model = get_model(para_embedding_mat)\n    para_model.compile(loss=bin_loss, optimizer=Adam(lr=0.001), metrics=['accuracy'])\npara_history=para_model.fit(train_padded, train.target, batch_size=BATCH_SIZE, epochs=30, validation_data=(val_padded, val.target),callbacks=my_callbacks)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:37:28.011517Z","iopub.execute_input":"2021-06-06T10:37:28.011963Z","iopub.status.idle":"2021-06-06T10:42:05.970112Z","shell.execute_reply.started":"2021-06-06T10:37:28.011919Z","shell.execute_reply":"2021-06-06T10:42:05.968655Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ở ```epoch``` cuối cùng thì đúng như dự đoán khi mà ```loss``` của mô hình đi kèm với ma trận embeddings của **Paragram** đạt được ```loss``` ở mức thấp nhất là ```0.5x``` (x học tiểu học) và đi kèm với đó là ```accuracy``` cao đạt mức ```0.9791```.<br>\nTuy nhiên điều quan trọng hơn đó chính là đánh giá hiệu năng của mô hình thông qua ```f1_score```","metadata":{}},{"cell_type":"code","source":"# summarize history for loss\nplt.figure(figsize=(15, 5)).add_subplot(1, 2, 1)\nplt.plot(para_history.history['loss'])\nplt.plot(para_history.history['val_loss'])\nplt.title('model loss')\nplt.ylabel('loss')\nplt.xlabel('epoch')\nplt.legend(['train', 'test'], loc='upper left')\n# plt.show()\n# summarize history for accuracy\nplt.figure(figsize=(15, 5)).add_subplot(1, 2, 2)\nplt.plot(para_history.history['accuracy'])\nplt.plot(para_history.history['val_accuracy'])\nplt.title('model accuracy')\nplt.ylabel('accuracy')\nplt.xlabel('epoch')\nplt.legend(['train', 'test'], loc='upper left')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:42:05.971982Z","iopub.execute_input":"2021-06-06T10:42:05.972309Z","iopub.status.idle":"2021-06-06T10:42:06.407006Z","shell.execute_reply.started":"2021-06-06T10:42:05.972276Z","shell.execute_reply":"2021-06-06T10:42:06.405931Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Cũng như 2 anh ở trên thì mô hình với ma trận embeddings của **Paragram** lại cho ```loss``` của tập **```test```** tăng dần về những epoch cuối.<br>\nTuy nhiên đây vẫn là kết quả chấp nhận được do sự chênh lệch không quá nhiều","metadata":{}},{"cell_type":"code","source":"para_y_pre=para_model.predict(val_padded, verbose=1)\nbest_score = 0\nbest_thresh = 0\nfor thresh in np.arange(0.1,0.5,0.01):\n    if(best_score < metrics.f1_score(val.target,(para_y_pre>thresh).astype(int))):\n        best_score = metrics.f1_score(val.target,(para_y_pre>thresh))\n        best_thresh = round(thresh, 2)\n    print(\"threshold {0:2.2f} f1 score:{1:2.3f}\".format(thresh,metrics.f1_score(val.target,(para_y_pre>thresh).astype(int))))\nprint(\"\\033[1mBest result {0:2.3f} in thresh_hold {1:2.2f}\\033[0m\".format(best_score, best_thresh))","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:42:06.408509Z","iopub.execute_input":"2021-06-06T10:42:06.408897Z","iopub.status.idle":"2021-06-06T10:43:28.266584Z","shell.execute_reply.started":"2021-06-06T10:42:06.408865Z","shell.execute_reply":"2021-06-06T10:43:28.265603Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Qua nhiều lần thử nghiệm có thể thấy rằng anh **Paragram** cho hiệu năng không như kì vọng khi anh ấy đạt độ phủ tốt nhất, oov thấp nhất **(khi chuẩn hoá text về ```lowercase```)**, tuy nhiên model khi train với ma trận embeddings của anh **Paragram** cho hiệu năng vẫn kém hơn anh **Glove**","metadata":{}},{"cell_type":"code","source":"threshold=best_thresh\npara_y_test_pre=para_model.predict(test_padded, batch_size=BATCH_SIZE, verbose=1)\npara_y_test_pre=(para_y_test_pre>thresh).astype(int)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:43:28.268272Z","iopub.execute_input":"2021-06-06T10:43:28.268709Z","iopub.status.idle":"2021-06-06T10:43:33.224817Z","shell.execute_reply.started":"2021-06-06T10:43:28.268654Z","shell.execute_reply":"2021-06-06T10:43:33.223409Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del paragram_embed, para_embedding_mat, para_history, best_score, best_thresh\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:43:33.226455Z","iopub.execute_input":"2021-06-06T10:43:33.226778Z","iopub.status.idle":"2021-06-06T10:43:36.140075Z","shell.execute_reply.started":"2021-06-06T10:43:33.226748Z","shell.execute_reply":"2021-06-06T10:43:36.138978Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **c. Wiki**","metadata":{}},{"cell_type":"code","source":"strategy = None\n\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect() # TPU detection\n    strategy = tf.distribute.TPUStrategy(tpu)\n    print('Use TPU')\nexcept ValueError:\n    if len(tf.config.list_physical_devices('GPU')) > 0:\n        strategy = tf.distribute.MirroredStrategy()\n        print('Use GPU')\n    else:\n        strategy = tf.distribute.get_strategy()\n        print('Use CPU')","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:43:36.1416Z","iopub.execute_input":"2021-06-06T10:43:36.142043Z","iopub.status.idle":"2021-06-06T10:43:41.863573Z","shell.execute_reply.started":"2021-06-06T10:43:36.142Z","shell.execute_reply":"2021-06-06T10:43:41.862491Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nwiki_embed = load_embed(wiki_path)\nprint(\"\\033[1mWiki Coverage: \\033[0m]\")\noov_wiki = check_voc(vocabulary2, wiki_embed)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:43:41.864821Z","iopub.execute_input":"2021-06-06T10:43:41.865099Z","iopub.status.idle":"2021-06-06T10:45:34.141163Z","shell.execute_reply.started":"2021-06-06T10:43:41.865072Z","shell.execute_reply":"2021-06-06T10:45:34.139852Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"So với 2 anh **Glove** thì độ phủ của anh **Wiki** thấp hơn một chút, tuy nhiên qua ví dụ của anh **Paragram** thì không nói trước được điều gì","metadata":{}},{"cell_type":"code","source":"count=0\nwiki_embedding_mat=np.zeros((vocab_size,300))\nfor word,i in tqdm(word_index.items()):\n    try:\n        vec=wiki_embed[word]\n        wiki_embedding_mat[i]=vec\n    except KeyError:\n        count+=1\n        continue\n\nprint(\"Number of Out of Vocabulary\",count)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:45:34.143003Z","iopub.execute_input":"2021-06-06T10:45:34.143436Z","iopub.status.idle":"2021-06-06T10:45:35.534107Z","shell.execute_reply.started":"2021-06-06T10:45:34.143391Z","shell.execute_reply":"2021-06-06T10:45:35.532879Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Số lượng từ của anh **Wiki** cũng ở mức khá cao khi gần tiệm cận với anh **Google News**, vậy có thể là ma trận embeddings sẽ có nhiều từ không tìm được và hiệu năng model cũng sẽ không tốt bằng anh **Glove**","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    wiki_model = get_model(wiki_embedding_mat)\n    wiki_model.compile(loss=bin_loss, optimizer=Adam(lr=0.001), metrics=['accuracy'])\nwiki_history=wiki_model.fit(train_padded, train.target, batch_size=BATCH_SIZE, epochs=30, validation_data=(val_padded, val.target),callbacks=my_callbacks)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:45:35.535811Z","iopub.execute_input":"2021-06-06T10:45:35.536237Z","iopub.status.idle":"2021-06-06T10:51:46.883475Z","shell.execute_reply.started":"2021-06-06T10:45:35.536193Z","shell.execute_reply":"2021-06-06T10:51:46.882381Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# summarize history for loss\nplt.figure(figsize=(15, 5)).add_subplot(1, 2, 1)\nplt.plot(wiki_history.history['loss'])\nplt.plot(wiki_history.history['val_loss'])\nplt.title('model loss')\nplt.ylabel('loss')\nplt.xlabel('epoch')\nplt.legend(['train', 'test'], loc='upper left')\n# plt.show()\n# summarize history for accuracy\nplt.figure(figsize=(15, 5)).add_subplot(1, 2, 2)\nplt.plot(wiki_history.history['accuracy'])\nplt.plot(wiki_history.history['val_accuracy'])\nplt.title('model accuracy')\nplt.ylabel('accuracy')\nplt.xlabel('epoch')\nplt.legend(['train', 'test'], loc='upper left')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:51:46.885325Z","iopub.execute_input":"2021-06-06T10:51:46.885824Z","iopub.status.idle":"2021-06-06T10:51:47.27236Z","shell.execute_reply.started":"2021-06-06T10:51:46.885714Z","shell.execute_reply":"2021-06-06T10:51:47.271645Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Cũng như 3 anh trên là **Glove**, **Paragram** và **Google News** thì anh **Wiki** này cũng có ```loss``` của tập test tăng dần về epoch cuối và ```accuracy``` không được cải thiện nhiều.<br>\nVậy nên ta cần phải config lại model, để đạt được hiệu quả cao hơn","metadata":{}},{"cell_type":"code","source":"wiki_y_pre=wiki_model.predict(val_padded, verbose=1)\nbest_score = 0\nbest_thresh = 0\nfor thresh in np.arange(0.1,0.5,0.01):\n    if(best_score < metrics.f1_score(val.target,(wiki_y_pre>thresh).astype(int))):\n        best_score = metrics.f1_score(val.target,(wiki_y_pre>thresh))\n        best_thresh = round(thresh, 2)\n    print(\"threshold {0:2.2f} f1 score:{1:2.3f}\".format(thresh,metrics.f1_score(val.target,(wiki_y_pre>thresh).astype(int))))\nprint(\"\\033[1mBest result {0:2.3f} in thresh_hold {1:2.2f}\\033[0m\".format(best_score, best_thresh))","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:51:47.278362Z","iopub.execute_input":"2021-06-06T10:51:47.278775Z","iopub.status.idle":"2021-06-06T10:53:06.882771Z","shell.execute_reply.started":"2021-06-06T10:51:47.278745Z","shell.execute_reply":"2021-06-06T10:53:06.881742Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Tuy độ phủ không đạt được như anh **Paragram** và số lượng từ ```out_of_vocab``` xấp xỉ anh **Google News** nhưng anh **Wiki** này cho hiệu năng rất đáng nể khi mà ma trận embeddings của anh này giúp model đạt ```f1_score``` xấp xỉ anh **Glove** tuy rằng **Glove** vẫn có phần nhỉnh hơn","metadata":{}},{"cell_type":"code","source":"threshold=best_thresh\nwiki_y_test_pre=wiki_model.predict(test_padded, batch_size=BATCH_SIZE, verbose=1)\nwiki_y_test_pre=(wiki_y_test_pre>thresh).astype(int)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:53:06.884455Z","iopub.execute_input":"2021-06-06T10:53:06.884748Z","iopub.status.idle":"2021-06-06T10:53:11.811065Z","shell.execute_reply.started":"2021-06-06T10:53:06.884718Z","shell.execute_reply":"2021-06-06T10:53:11.810207Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del wiki_embed, wiki_embedding_mat, wiki_history, best_score, best_thresh\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:53:11.812667Z","iopub.execute_input":"2021-06-06T10:53:11.813463Z","iopub.status.idle":"2021-06-06T10:53:14.310319Z","shell.execute_reply.started":"2021-06-06T10:53:11.813392Z","shell.execute_reply":"2021-06-06T10:53:14.309318Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Oke vậy thì có thể thấy được rằng hiệu năng của model với các ma trận embeddings đến từ vị trí của 4 anh là **Google News**, **Glove**, **Paragram** và **Wiki** khá tương đồng với nhau, duy chỉ có anh **Google News** là thể hiện kém hơn 3 anh còn lại, cả về độ phủ, cả về ```f1_score``` và những yếu tố khác.<br>\nTuy nhiên chúng ta lại có thể thấy được rằng, với cả 4 model này tại sao lại không kếp hợp chúng vào với nhau để tạo ra một ```predict``` hiệu quả, thì dưới đây chính là sự kết hợp của cả 4 models<br>\nThuật ngữ chuyên ngành gọi là **Stack Models Prediction**<br>\nVới **Stack Models Prediction** thì chúng ta có nhiều chiến lược, nhưng ở đây em sẽ nhân mỗi thằng ```predict``` với cùng một hệ số.<br>\nDo có 4 thằng nên hệ số cho mỗi thằng là ```0.25``` và ta được một ```predict``` mới.<br>\nVà việc ghép các **predicts** lại hoạt động khá hiệu quả khi ```f1_score``` của mô hình đạt tới **70%** tức ```0.70```","metadata":{}},{"cell_type":"code","source":"y_pre=0.25*(google_y_pre + glove_y_pre + para_y_pre + wiki_y_pre)\n# y_pre=0.20*google_y_pre + 0.35*glove_y_pre + 0.15*para_y_pre + 0.30*wiki_y_pre\nbest_score = 0\nbest_thresh = 0\nfor thresh in np.arange(0.1,0.5,0.01):\n    if(best_score < metrics.f1_score(val.target,(y_pre>thresh).astype(int))):\n        best_score = metrics.f1_score(val.target,(y_pre>thresh))\n        best_thresh = round(thresh, 2)\n    print(\"threshold {0:2.2f} f1 score:{1:2.3f}\".format(thresh,metrics.f1_score(val.target,(y_pre>thresh).astype(int))))\nprint(\"\\033[1mBest result {0:2.3f} in thresh_hold {1:2.2f}\\033[0m\".format(best_score, best_thresh))","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:56:27.092269Z","iopub.execute_input":"2021-06-06T10:56:27.092655Z","iopub.status.idle":"2021-06-06T10:56:35.477673Z","shell.execute_reply.started":"2021-06-06T10:56:27.092624Z","shell.execute_reply":"2021-06-06T10:56:35.476634Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# y_test_pre = 0.25 * (google_y_test_pre + glove_y_test_pre + para_y_test_pre + wiki_y_test_pre)\ny_test_pre = 0.2*google_y_test_pre + 0.3*glove_y_test_pre + 0.2*para_y_test_pre + 0.3*wiki_y_test_pre\ny_test_pre=(y_test_pre>thresh).astype(int)\n### Tạo File submission\nsubmit=pd.DataFrame()\nsubmit[\"qid\"]=df_test.qid\nsubmit[\"prediction\"]=y_test_pre\nsubmit.to_csv(\"submission.csv\",index=False)","metadata":{"execution":{"iopub.status.busy":"2021-06-06T10:53:22.714725Z","iopub.execute_input":"2021-06-06T10:53:22.715082Z","iopub.status.idle":"2021-06-06T10:53:23.673931Z","shell.execute_reply.started":"2021-06-06T10:53:22.715051Z","shell.execute_reply":"2021-06-06T10:53:23.672842Z"},"trusted":true},"execution_count":null,"outputs":[]}]}