{"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 HỌC MÁY\n**Quora Insincere Question Classification**\n\n**Mã lớp**: INT3405 20\n\n**Họ tên**: Phan Quang Hùng\n\n**MSSV**: 18020582\n\n\n---","metadata":{}},{"cell_type":"markdown","source":"# 1. Tìm hiểu bài toán","metadata":{}},{"cell_type":"markdown","source":"## 1.1. Đặt vấn đề\n**Quora** là một nền tảng hỏi đáp được tạo ra với mục đích để mọi người có thể học hỏi và chia sẻ kiến thức. Mọi người có thể đặt câu hỏi trên trang web này và kết nối với những người có thể có những câu trả lời chất lượng và đóng góp những thông tin chi tiết, độc đáo.\n\nTuy vậy, cũng vì Quora là một nơi dành cho tất cả mọi người mà không thể tránh khỏi một thực trạng đáng buồn: các **câu hỏi mang tính độc hại** (toxic question), hay nói cách khác, được đặt ra một cách **thiếu chân thành** (insincere). Chúng được đặt ra dựa trên những định kiến, 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. Điều này đi ngược lại với tôn chỉ của Quora **\"Be Nice, Be Respectful\"** (dịch thô: hãy là một người tốt và biết tôn trọng người khác).\n\nTừ đó mà cuộc thi **Quora Insincere Questions Classification** - Phân loại những câu hỏi Quora thiếu chân thành, được ra đời.\n\nNhiệm vụ được đặt ra 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.","metadata":{}},{"cell_type":"markdown","source":"## 1.2. Dữ liệu đầu vào và đầu ra\n* **Input**: Tiêu đề câu hỏi. Định dạng văn bản (text).\n\n* **Output**: Số 1/0 tương ứng với không chân thành/chân thành.","metadata":{}},{"cell_type":"markdown","source":"## 1.4. Đánh giá\nCuộc thi sử dụng F1 Score để đánh giá. F1 Score được sử dụng có thể là vì sự mất cân bằng của dữ liệu (xem thêm ở mục 2.3. Khảo sát và phân tích), nên sẽ có ý nghĩa hơn là sử dụng accuracy đơn thuần.\n\n\nF1 score là trung bình điều hoà (Harmonic Mean) của Precision và Recall. \n\n![alt text](https://upload.wikimedia.org/wikipedia/commons/thumb/2/26/Precisionrecall.svg/525px-Precisionrecall.svg.png \"F1 Score\")","metadata":{}},{"cell_type":"markdown","source":"## 1.3. Hướng giải quyết\n\nSử dụng **GloVE** để nhúng (embedding) ngôn ngữ thành các vector, sau đó dùng Mạng nơ-ron hồi quy (RNN - Recurrent Neural Network) như **LSTM** để xử lý.","metadata":{}},{"cell_type":"markdown","source":"# 2. Phân tích dữ liệu","metadata":{}},{"cell_type":"markdown","source":"## 2.1. Import thư viện\n\nTrong số các thư viện được sử dụng, cần kể đến:\n* `nltk`: Natural Language Toolkit, một bộ thư viện và chương trình dành cho xử lý ngôn ngữ tự nhiên thống kê và biểu tượng tiếng Anh.\n* `pandas`: Thao tác với các dữ liệu dạng bảng (.csv).\n* `torch`: PyTorch, một framework được phát triển chủ yếu bởi phòng thí nghiệm Nghiên cứu AI của Facebook.","metadata":{}},{"cell_type":"code","source":"import os\nimport re\nimport nltk\nimport pandas as pd \nimport numpy as np\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\nfrom nltk.tokenize import word_tokenize\nfrom nltk.corpus import stopwords\n\nimport torch\nimport torchtext\nfrom torchtext import data\nfrom torch import nn\n# from torch.nn import functional as F\nfrom torch import optim\nfrom torch.utils.tensorboard import SummaryWriter\n\nfrom tqdm.notebook import tqdm\nfrom IPython.core.display import display, HTML\ntqdm().pandas()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-06-11T05:41:10.276022Z","iopub.execute_input":"2021-06-11T05:41:10.27634Z","iopub.status.idle":"2021-06-11T05:41:13.063566Z","shell.execute_reply.started":"2021-06-11T05:41:10.27631Z","shell.execute_reply":"2021-06-11T05:41:13.062735Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.2. Nhập dữ liệu đầu vào","metadata":{}},{"cell_type":"code","source":"DATA_PATH = '../input/quora-insincere-questions-classification/'\n\ntrain_df = pd.read_csv(os.path.join(DATA_PATH, 'train.csv'))\ntest_df  = pd.read_csv(os.path.join(DATA_PATH, 'test.csv'))","metadata":{"execution":{"iopub.status.busy":"2021-06-11T05:41:44.508557Z","iopub.execute_input":"2021-06-11T05:41:44.50892Z","iopub.status.idle":"2021-06-11T05:41:47.201812Z","shell.execute_reply.started":"2021-06-11T05:41:44.508892Z","shell.execute_reply":"2021-06-11T05:41:47.200837Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.3. Khảo sát và phân tích","metadata":{}},{"cell_type":"markdown","source":"### a. Trường dữ liệu","metadata":{}},{"cell_type":"code","source":"train_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-06-11T02:42:01.471326Z","iopub.execute_input":"2021-06-11T02:42:01.471635Z","iopub.status.idle":"2021-06-11T02:42:01.494503Z","shell.execute_reply.started":"2021-06-11T02:42:01.471604Z","shell.execute_reply":"2021-06-11T02:42:01.49355Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Dữ liệu đầu vào cho thấy 3 cột thông tin:\n* `qid`: ID của câu hỏi\n* `question_text`: tiêu đề câu hỏi, dữ liệu ta cần phân loại\n* `target`: kết quả cho thấy câu hỏi có thiếu chân thành không (1 - thiếu chân thành, 0 - chân thành)","metadata":{}},{"cell_type":"code","source":"print(\"Number of data points in training data:\", train_df.shape[0])\nprint(\"Number of data points in test data:\", test_df.shape[0])","metadata":{"execution":{"iopub.status.busy":"2021-06-11T00:14:36.138922Z","iopub.execute_input":"2021-06-11T00:14:36.139296Z","iopub.status.idle":"2021-06-11T00:14:36.146003Z","shell.execute_reply.started":"2021-06-11T00:14:36.139263Z","shell.execute_reply":"2021-06-11T00:14:36.144832Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### b. Phân bố dữ liệu","metadata":{}},{"cell_type":"code","source":"# Bar chart\nplt.subplot(1, 2, 1)\ntrain_df.groupby('target')['qid'].count().plot.bar()\nplt.grid(True)\nplt.title('Target Count')\nplt.subplots_adjust(right=1.9)\n\n# Pie Chart\nplt.subplot(1, 2, 2)\nvalues = [train_df[train_df['target']==0].shape[0], train_df[train_df['target']==1].shape[0]]\nlabels = ['Sincere questions', 'Insincere questions']\n\nplt.pie(values, labels=labels, autopct='%1.1f%%', shadow=True)\nplt.title('Target Distribution')\nplt.tight_layout()\nplt.subplots_adjust(right=1.9)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-11T00:14:38.32788Z","iopub.execute_input":"2021-06-11T00:14:38.328193Z","iopub.status.idle":"2021-06-11T00:14:38.827325Z","shell.execute_reply.started":"2021-06-11T00:14:38.328161Z","shell.execute_reply":"2021-06-11T00:14:38.826517Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Vấn đề**: Có thể thấy số lượng câu hỏi thiếu chân thành (target = 1) **thấp hơn đáng kể** so với các câu hỏi chân thành.\n\n**Giải pháp**: Đó là lý do tại sao các bài dự thi được đánh giá trên **F1 Score** giữa mục tiêu dự đoán và mục tiêu quan sát. \nVì vậy, tập dữ liệu không cân bằng sẽ không ảnh hưởng đến điểm số. \n\n\n\n","metadata":{}},{"cell_type":"markdown","source":"### c. Phân tích từ","metadata":{}},{"cell_type":"markdown","source":"Tiếp theo ta sẽ trích xuất một số đặc trưng dữ liệu cơ bản:\n* `count`: Số lượng từ \n* `mean`: Trung bình số lượng từ \n* `std`: Sample standard deviation\n* `min`: Số lượng từ nhỏ nhất\n* `max`: Số lượng từ lớn nhất\n* `50%/80%/99.99%`: Số lượng từ theo thống kê mà 50%/80%/99.99% câu hỏi có số lượng thấp hơn\n\nLấy được thông tin này trực tiếp từ lệnh `describe` của pandas.","metadata":{}},{"cell_type":"code","source":"#train_df['num_words'] = train_df['question_text'].apply(lambda x: len(str(x).split()))\n#print(\"Maximum length of a sincere question:\", max(train_df[train_df['target']==0]['num_words']))\n#print(\"Maximum length of a insincere question:\", max(train_df[train_df['target']==]['num_words']))","metadata":{"execution":{"iopub.status.busy":"2021-06-10T21:54:08.047016Z","iopub.execute_input":"2021-06-10T21:54:08.047333Z","iopub.status.idle":"2021-06-10T21:54:10.010967Z","shell.execute_reply.started":"2021-06-10T21:54:08.047302Z","shell.execute_reply":"2021-06-10T21:54:10.010081Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Phân tích dựa trên tổng số câu hỏi ở bộ dữ liệu train:","metadata":{}},{"cell_type":"code","source":"train_df['word_count']= train_df.question_text.progress_apply(lambda x: len(x.split()))\n#data_neg = train_df[train_df['target']==0]\n#data_pos = train_df[train_df['target']==1]\n\n#statistic = pd.merge(\n#    data_neg.describe(percentiles=[.8, .9999])\n#    data_pos[['word_count']].describe(percentiles=[.8, .9999]), \n#    left_index=True, right_index=True, suffixes=('_sincere', '_insincere')\n#)\ntrain_df.describe(percentiles=[.8, .9999]).apply(lambda s: s.apply('{0:.2f}'.format))","metadata":{"execution":{"iopub.status.busy":"2021-06-11T00:14:43.857126Z","iopub.execute_input":"2021-06-11T00:14:43.857452Z","iopub.status.idle":"2021-06-11T00:14:47.588124Z","shell.execute_reply.started":"2021-06-11T00:14:43.857419Z","shell.execute_reply":"2021-06-11T00:14:47.587184Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Tách bảng thành 2 cột câu hỏi chân thành và không chân thành để phân tích.","metadata":{}},{"cell_type":"code","source":"train_df['word_count']= train_df.question_text.progress_apply(lambda x: len(x.split()))\ndata_neg = train_df[train_df['target']==0]\ndata_pos = train_df[train_df['target']==1]\n\nstatistic = pd.merge(\n    data_neg[['word_count']].describe(percentiles=[.8, .9999]), \n    data_pos[['word_count']].describe(percentiles=[.8, .9999]), \n    left_index=True, right_index=True, suffixes=('_sincere', '_insincere')\n)\n#statistic.describe(percentiles=[.8, .9999]).apply(lambda s: s.apply('{0:.2f}'.format))\n\ncolLabels = statistic.columns\ncellText = statistic.round(2).values\nrowLabels = statistic.index\n\nfig, axes = plt.subplots()\n#axes = fig.add_axes([0,0,1,1])\n#axes.bar(['sincere question', 'insincere question'], train_df.target.value_counts())\nfor p in axes.patches:\n    width = p.get_width()\n    height = p.get_height()\n    percent = height / len(train_df)\n    x, y = p.get_xy() \n    axes.annotate(f'{percent:.2%}', (x + width/2, y + height + 0.01*len(train_df)), ha='center')\n    \naxes.axis('off')\nmpl_table = axes.table(cellText = cellText, colLabels=colLabels, rowLabels = rowLabels, bbox=[2, 0, 2, 1.5], )\nmpl_table.auto_set_font_size(False)\nmpl_table.set_fontsize(14)","metadata":{"execution":{"iopub.status.busy":"2021-06-11T00:14:51.77691Z","iopub.execute_input":"2021-06-11T00:14:51.777229Z","iopub.status.idle":"2021-06-11T00:14:55.915578Z","shell.execute_reply.started":"2021-06-11T00:14:51.777197Z","shell.execute_reply":"2021-06-11T00:14:55.914716Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Bộ dữ liệu:\n* **Độ dài trung bình**: 12.8\n* **Số lượng từ ít nhất**: 1\n* **Số lượng từ cao nhất**: 134\n* **50% số câu có ít hơn**: 11\n* **80% số câu có ít hơn**: 17\n* **99.99% số câu có ít hơn**: 53\n\nĐặc điểm đáng chú ý của câu thiếu chân thành:\n* **Độ dài trung bình**: 17.28\n* **50% số câu có ít hơn**: 15\n* **80% số câu có ít hơn**: 25\n\nĐặc điểm đáng chú ý của câu hỏi chân thành:\n* **Độ dài trung bình**: 12.51\n* **50% số câu có ít hơn**: 11\n* **80% số câu có ít hơn**: 16","metadata":{}},{"cell_type":"markdown","source":"**Nhận xét**:\n\nVì số lượng câu chân thành chiếm đa số, nên không ngạc nhiên khi số liệu của nó khá tương đồng số liệu của tổng bộ dữ liệu.\n\nGiữa hai class dữ liệu, có thể thấy rằng **câu không chân thành có xu hướng dài hơn câu hỏi chân thành**.","metadata":{}},{"cell_type":"markdown","source":"### d. Phân tích chữ và ký tự đặc biệt","metadata":{}},{"cell_type":"markdown","source":"Tiếp theo ta sẽ trích xuất các loại chữ và ký tự đặc biệt:\n* `capital_letters`: Chữ viết hoa \n* `special_char`: Ký tự đặc biệt \n* `unique_words`: Từ độc nhất, chỉ xuất hiện duy nhất 1 lần\n* `numerics`: Số lượng ký tự số\n* `char`: Số lượng chữ","metadata":{}},{"cell_type":"code","source":"# Number of capital_letters\ntrain_df['num_capital_let'] = train_df['question_text'].apply(lambda x: len([c for c in str(x) if c.isupper()]))\n\n# Number of special characters\ntrain_df['num_special_char'] = train_df['question_text'].str.findall(r'[^a-zA-Z0-9 ]').str.len()\n\n# Number of unique words\ntrain_df['num_unique_words'] = train_df['question_text'].apply(lambda x: len(set(str(x).split())))\n\n# Number of numerics\ntrain_df['num_numerics'] = train_df['question_text'].apply(lambda x: sum(c.isdigit() for c in x))\n\n# Number of characters\ntrain_df['num_char'] = train_df['question_text'].apply(lambda x: len(str(x)))\n\ntrain_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-06-11T00:15:04.757923Z","iopub.execute_input":"2021-06-11T00:15:04.75836Z","iopub.status.idle":"2021-06-11T00:15:26.675604Z","shell.execute_reply.started":"2021-06-11T00:15:04.758311Z","shell.execute_reply":"2021-06-11T00:15:26.67481Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def display_boxplot(_x, _y, _data, _title):\n    sns.boxplot(x=_x, y=_y, data=_data)\n    plt.grid(True)\n    plt.title(_title)","metadata":{"execution":{"iopub.status.busy":"2021-06-11T00:15:29.869973Z","iopub.execute_input":"2021-06-11T00:15:29.87028Z","iopub.status.idle":"2021-06-11T00:15:29.876374Z","shell.execute_reply.started":"2021-06-11T00:15:29.870249Z","shell.execute_reply":"2021-06-11T00:15:29.873812Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Boxplot: Number of words\nplt.subplot(2, 3, 1)\ndisplay_boxplot('target', 'word_count', train_df, 'No. of words in each class')\n\n# Boxplot: Number of chars\nplt.subplot(2, 3, 2)\ndisplay_boxplot('target', 'num_char', train_df, 'Number of characters in each class')\n\n# Boxplot: Number of unique words\nplt.subplot(2, 3, 3)\ndisplay_boxplot('target', 'num_unique_words', train_df, 'Number of unique words in each class')\n\n# Boxplot: Number of special characters\nplt.subplot(2, 3, 4)\ndisplay_boxplot('target', 'num_special_char', train_df, 'No. of special characters in each class')\n\n# Boxplot: Number of capital letters\nplt.subplot(2, 3, 6)\ndisplay_boxplot('target', 'num_capital_let', train_df, 'No. of capital letters in each class')\n\n\nplt.subplots_adjust(right=3.0)\nplt.subplots_adjust(top=2.0)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-11T00:15:32.495202Z","iopub.execute_input":"2021-06-11T00:15:32.495546Z","iopub.status.idle":"2021-06-11T00:15:34.137349Z","shell.execute_reply.started":"2021-06-11T00:15:32.495512Z","shell.execute_reply":"2021-06-11T00:15:34.13655Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Từ dữ liệu các đặc trưng trên, ta xuất ra **Correlation Matrix** để có thể rút ra thêm ý nghĩa:","metadata":{}},{"cell_type":"code","source":"# Correlation matrix\nf, ax = plt.subplots(figsize=(10, 8))\ncorr = train_df.corr()\nsns.heatmap(corr, ax=ax)\nplt.title(\"Correlation matrix\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-11T00:15:37.451601Z","iopub.execute_input":"2021-06-11T00:15:37.451929Z","iopub.status.idle":"2021-06-11T00:15:37.97522Z","shell.execute_reply.started":"2021-06-11T00:15:37.451898Z","shell.execute_reply":"2021-06-11T00:15:37.974429Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Nhận xét**: \n\nCùng với kết luận từ mục c, ta cũng thấy rằng câu hỏi thiếu chân thành xuất hiện nhiều chữ cái và ký tự hơn.\n\nCác câu hỏi thiếu chân thành cũng có vẻ có nhiều từ độc nhất (unique word) hơn.\n\nCó rất nhiều ký tự đặc biệt, cũng như các ký tự số và chữ viết hoa mà có thể thấy rằng đã làm loãng dữ liệu, không giúp ích trong việc phân loại.\n\n**Giải pháp**: Tiền xử lý dữ liệu bằng các phương pháp làm sạch cơ bản.\n","metadata":{}},{"cell_type":"markdown","source":"# 3. Tiền xử lý dữ liệu","metadata":{}},{"cell_type":"markdown","source":"## 3.1. Thiết kế giải pháp làm sạch dữ liệu","metadata":{}},{"cell_type":"markdown","source":"**Vấn đề**: Như đã nói ở phần 2, có rất nhiều chữ, ký tự, từ đặc biệt mà gây loãng dữ liệu.\n\n**Giải pháp**:\n\n* Stemming: Chuyển đổi các từ viết rút gọn về nguyên thể bằng cách xoá contraction.\n* Punctuation: Xoá các dấu câu\n* Tokenization: Phá các câu thành các từ đơn lẻ (tokenize)\n* Misspell: Vì Quora là trang web cộng đồng, các câu hỏi không được chuẩn hoá hoàn toàn, nên việc sai chính tả là rất có thể xảy ra. Bên cạnh đó là các cách viết khác của từ do sự khác biệt văn hoá, vùng miền,... (*color/colour*)\n\n* Lemmatization: Không được sử dụng vì sẽ làm sai từ gốc ban đầu trong một số trường hợp. (*caring/car/care*)\n* Stopword: Trong học máy thống kê, việc loại bỏ các stopword như *the* là cần thiết. Tuy nhiên trong trường hợp sử dụng hướng đi của em thì cần phải giữ lại nguyên vẹn tất cả các từ để có thể vector hoá tất cả, để tạo ra embedding matrix đúng nhất.","metadata":{}},{"cell_type":"code","source":"contraction_dict = {\"ain't\": \"is not\", \"aren't\": \"are not\",\"can't\": \"cannot\", \"'cause\": \"because\", \"could've\": \"could have\", \"couldn't\": \"could not\", \"didn't\": \"did not\",  \"doesn't\": \"does not\", \"don't\": \"do not\", \"hadn't\": \"had not\", \"hasn't\": \"has not\", \"haven't\": \"have not\", \"he'd\": \"he would\",\"he'll\": \"he will\", \"he's\": \"he is\", \"how'd\": \"how did\", \"how'd'y\": \"how do you\", \"how'll\": \"how will\", \"how's\": \"how is\",  \"I'd\": \"I would\", \"I'd've\": \"I would have\", \"I'll\": \"I will\", \"I'll've\": \"I will have\",\"I'm\": \"I am\", \"I've\": \"I have\", \"i'd\": \"i would\", \"i'd've\": \"i would have\", \"i'll\": \"i will\",  \"i'll've\": \"i will have\",\"i'm\": \"i am\", \"i've\": \"i have\", \"isn't\": \"is not\", \"it'd\": \"it would\", \"it'd've\": \"it would have\", \"it'll\": \"it will\", \"it'll've\": \"it will have\",\"it's\": \"it is\", \"let's\": \"let us\", \"ma'am\": \"madam\", \"mayn't\": \"may not\", \"might've\": \"might have\",\"mightn't\": \"might not\",\"mightn't've\": \"might not have\", \"must've\": \"must have\", \"mustn't\": \"must not\", \"mustn't've\": \"must not have\", \"needn't\": \"need not\", \"needn't've\": \"need not have\",\"o'clock\": \"of the clock\", \"oughtn't\": \"ought not\", \"oughtn't've\": \"ought not have\", \"shan't\": \"shall not\", \"sha'n't\": \"shall not\", \"shan't've\": \"shall not have\", \"she'd\": \"she would\", \"she'd've\": \"she would have\", \"she'll\": \"she will\", \"she'll've\": \"she will have\", \"she's\": \"she is\", \"should've\": \"should have\", \"shouldn't\": \"should not\", \"shouldn't've\": \"should not have\", \"so've\": \"so have\",\"so's\": \"so as\", \"this's\": \"this is\",\"that'd\": \"that would\", \"that'd've\": \"that would have\", \"that's\": \"that is\", \"there'd\": \"there would\", \"there'd've\": \"there would have\", \"there's\": \"there is\", \"here's\": \"here is\",\"they'd\": \"they would\", \"they'd've\": \"they would have\", \"they'll\": \"they will\", \"they'll've\": \"they will have\", \"they're\": \"they are\", \"they've\": \"they have\", \"to've\": \"to have\", \"wasn't\": \"was not\", \"we'd\": \"we would\", \"we'd've\": \"we would have\", \"we'll\": \"we will\", \"we'll've\": \"we will have\", \"we're\": \"we are\", \"we've\": \"we have\", \"weren't\": \"were not\", \"what'll\": \"what will\", \"what'll've\": \"what will have\", \"what're\": \"what are\",  \"what's\": \"what is\", \"what've\": \"what have\", \"when's\": \"when is\", \"when've\": \"when have\", \"where'd\": \"where did\", \"where's\": \"where is\", \"where've\": \"where have\", \"who'll\": \"who will\", \"who'll've\": \"who will have\", \"who's\": \"who is\", \"who've\": \"who have\", \"why's\": \"why is\", \"why've\": \"why have\", \"will've\": \"will have\", \"won't\": \"will not\", \"won't've\": \"will not have\", \"would've\": \"would have\", \"wouldn't\": \"would not\", \"wouldn't've\": \"would not have\", \"y'all\": \"you all\", \"y'all'd\": \"you all would\",\"y'all'd've\": \"you all would have\",\"y'all're\": \"you all are\",\"y'all've\": \"you all have\",\"you'd\": \"you would\", \"you'd've\": \"you would have\", \"you'll\": \"you will\", \"you'll've\": \"you will have\", \"you're\": \"you are\", \"you've\": \"you have\"}\n\n\npunctuation = [',', '.', '\"', ':', ')', '(', '-', '!', '?', '|', ';', \"'\", '$', '&', '/', '[', ']', '>', '%', '=', '#', '*', '+', '\\\\', \n    '•', '~', '@', '£', '·', '_', '{', '}', '©', '^', '®', '`', '<', '→', '°', '€', '™', '›', '♥', '←', '×', '§', '″', '′', \n    '█', '…', '“', '★', '”', '–', '●', '►', '−', '¢', '¬', '░', '¡', '¶', '↑', '±', '¿', '▾', '═', '¦', '║', '―', '¥', '▓', \n    '—', '‹', '─', '▒', '：', '⊕', '▼', '▪', '†', '■', '’', '▀', '¨', '▄', '♫', '☆', '¯', '♦', '¤', '▲', '¸', '⋅', '‘', '∞', \n    '∙', '）', '↓', '、', '│', '（', '»', '，', '♪', '╩', '╚', '・', '╦', '╣', '╔', '╗', '▬', '❤', '≤', '‡', '√', '◄', '━', \n    '⇒', '▶', '≥', '╝', '♡', '◊', '。', '✈', '≡', '☺', '✔', '↵', '≈', '✓', '♣', '☎', '℃', '◦', '└', '‟', '～', '！', '○', \n    '◆', '№', '♠', '▌', '✿', '▸', '⁄', '□', '❖', '✦', '．', '÷', '｜', '┃', '／', '￥', '╠', '↩', '✭', '▐', '☼', '☻', '┐', \n    '├', '«', '∼', '┌', '℉', '☮', '฿', '≦', '♬', '✧', '〉', '－', '⌂', '✖', '･', '◕', '※', '‖', '◀', '‰', '\\x97', '↺', \n    '∆', '┘', '┬', '╬', '،', '⌘', '⊂', '＞', '〈', '⎙', '？', '☠', '⇐', '▫', '∗', '∈', '≠', '♀', '♔', '˚', '℗', '┗', '＊', \n    '┼', '❀', '＆', '∩', '♂', '‿', '∑', '‣', '➜', '┛', '⇓', '☯', '⊖', '☀', '┳', '；', '∇', '⇑', '✰', '◇', '♯', '☞', '´', \n    '↔', '┏', '｡', '◘', '∂', '✌', '♭', '┣', '┴', '┓', '✨', '\\xa0', '˜', '❥', '┫', '℠', '✒', '［', '∫', '\\x93', '≧', '］', \n    '\\x94', '∀', '♛', '\\x96', '∨', '◎', '↻', '⇩', '＜', '≫', '✩', '✪', '♕', '؟', '₤', '☛', '╮', '␊', '＋', '┈', '％', \n    '╋', '▽', '⇨', '┻', '⊗', '￡', '।', '▂', '✯', '▇', '＿', '➤', '✞', '＝', '▷', '△', '◙', '▅', '✝', '∧', '␉', '☭', \n    '┊', '╯', '☾', '➔', '∴', '\\x92', '▃', '↳', '＾', '׳', '➢', '╭', '➡', '＠', '⊙', '☢', '˝', '∏', '„', '∥', '❝', '☐', \n    '▆', '╱', '⋙', '๏', '☁', '⇔', '▔', '\\x91', '➚', '◡', '╰', '\\x85', '♢', '˙', '۞', '✘', '✮', '☑', '⋆', 'ⓘ', '❒', '☣', '✉', '⌊', '➠', '∣', '❑', '◢', 'ⓒ', '\\x80', '〒', '∕', '▮', '⦿', '✫', '✚', '⋯', '♩', '☂', '❞', '‗', '܂', '☜', \n    '‾', '✜', '╲', '∘', '⟩', '＼', '⟨', '·', '✗', '♚', '∅', 'ⓔ', '◣', '͡', '‛', '❦', '◠', '✄', '❄', '∃', '␣', '≪', '｢', \n    '≅', '◯', '☽', '∎', '｣', '❧', '̅', 'ⓐ', '↘', '⚓', '▣', '˘', '∪', '⇢', '✍', '⊥', '＃', '⎯', '↠', '۩', '☰', '◥', \n    '⊆', '✽', '⚡', '↪', '❁', '☹', '◼', '☃', '◤', '❏', 'ⓢ', '⊱', '➝', '̣', '✡', '∠', '｀', '▴', '┤', '∝', '♏', 'ⓐ', \n    '✎', ';', '␤', '＇', '❣', '✂', '✤', 'ⓞ', '☪', '✴', '⌒', '˛', '♒', '＄', '✶', '▻', 'ⓔ', '◌', '◈', '❚', '❂', '￦', \n    '◉', '╜', '̃', '✱', '╖', '❉', 'ⓡ', '↗', 'ⓣ', '♻', '➽', '׀', '✲', '✬', '☉', '▉', '≒', '☥', '⌐', '♨', '✕', 'ⓝ', \n    '⊰', '❘', '＂', '⇧', '̵', '➪', '▁', '▏', '⊃', 'ⓛ', '‚', '♰', '́', '✏', '⏑', '̶', 'ⓢ', '⩾', '￠', '❍', '≃', '⋰', '♋', \n    '､', '̂', '❋', '✳', 'ⓤ', '╤', '▕', '⌣', '✸', '℮', '⁺', '▨', '╨', 'ⓥ', '♈', '❃', '☝', '✻', '⊇', '≻', '♘', '♞', \n    '◂', '✟', '⌠', '✠', '☚', '✥', '❊', 'ⓒ', '⌈', '❅', 'ⓡ', '♧', 'ⓞ', '▭', '❱', 'ⓣ', '∟', '☕', '♺', '∵', '⍝', 'ⓑ', \n    '✵', '✣', '٭', '♆', 'ⓘ', '∶', '⚜', '◞', '்', '✹', '➥', '↕', '̳', '∷', '✋', '➧', '∋', '̿', 'ͧ', '┅', '⥤', '⬆', '⋱', \n    '☄', '↖', '⋮', '۔', '♌', 'ⓛ', '╕', '♓', '❯', '♍', '▋', '✺', '⭐', '✾', '♊', '➣', '▿', 'ⓑ', '♉', '⏠', '◾', '▹', \n    '⩽', '↦', '╥', '⍵', '⌋', '։', '➨', '∮', '⇥', 'ⓗ', 'ⓓ', '⁻', '⎝', '⌥', '⌉', '◔', '◑', '✼', '♎', '♐', '╪', '⊚', \n    '☒', '⇤', 'ⓜ', '⎠', '◐', '⚠', '╞', '◗', '⎕', 'ⓨ', '☟', 'ⓟ', '♟', '❈', '↬', 'ⓓ', '◻', '♮', '❙', '♤', '∉', '؛', \n    '⁂', 'ⓝ', '־', '♑', '╫', '╓', '╳', '⬅', '☔', '☸', '┄', '╧', '׃', '⎢', '❆', '⋄', '⚫', '̏', '☏', '➞', '͂', '␙', 'ⓤ', '◟', '̊', '⚐', '✙', '↙', '̾', '℘', '✷', '⍺', '❌', '⊢', '▵', '✅', 'ⓖ', '☨', '▰', '╡', 'ⓜ', '☤', '∽', '╘', \n    '˹', '↨', '♙', '⬇', '♱', '⌡', '⠀', '╛', '❕', '┉', 'ⓟ', '̀', '♖', 'ⓚ', '┆', '⎜', '◜', '⚾', '⤴', '✇', '╟', '⎛', \n    '☩', '➲', '➟', 'ⓥ', 'ⓗ', '⏝', '◃', '╢', '↯', '✆', '˃', '⍴', '❇', '⚽', '╒', '̸', '♜', '☓', '➳', '⇄', '☬', '⚑', \n    '✐', '⌃', '◅', '▢', '❐', '∊', '☈', '॥', '⎮', '▩', 'ு', '⊹', '‵', '␔', '☊', '➸', '̌', '☿', '⇉', '⊳', '╙', 'ⓦ', \n    '⇣', '｛', '̄', '↝', '⎟', '▍', '❗', '״', '΄', '▞', '◁', '⛄', '⇝', '⎪', '♁', '⇠', '☇', '✊', 'ி', '｝', '⭕', '➘', \n    '⁀', '☙', '❛', '❓', '⟲', '⇀', '≲', 'ⓕ', '⎥', '\\u06dd', 'ͤ', '₋', '̱', '̎', '♝', '≳', '▙', '➭', '܀', 'ⓖ', '⇛', '▊', \n    '⇗', '̷', '⇱', '℅', 'ⓧ', '⚛', '̐', '̕', '⇌', '␀', '≌', 'ⓦ', '⊤', '̓', '☦', 'ⓕ', '▜', '➙', 'ⓨ', '⌨', '◮', '☷', \n    '◍', 'ⓚ', '≔', '⏩', '⍳', '℞', '┋', '˻', '▚', '≺', 'ْ', '▟', '➻', '̪', '⏪', '̉', '⎞', '┇', '⍟', '⇪', '▎', '⇦', '␝', \n    '⤷', '≖', '⟶', '♗', '̴', '♄', 'ͨ', '̈', '❜', '̡', '▛', '✁', '➩', 'ா', '˂', '↥', '⏎', '⎷', '̲', '➖', '↲', '⩵', '̗', '❢', \n    '≎', '⚔', '⇇', '̑', '⊿', '̖', '☍', '➹', '⥊', '⁁', '✢']\n\nmispell_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',}","metadata":{"execution":{"iopub.status.busy":"2021-06-11T05:42:11.725864Z","iopub.execute_input":"2021-06-11T05:42:11.726189Z","iopub.status.idle":"2021-06-11T05:42:11.778859Z","shell.execute_reply.started":"2021-06-11T05:42:11.726159Z","shell.execute_reply":"2021-06-11T05:42:11.777971Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def clean_text(txt, contraction_dict=contraction_dict, punctuation=punctuation, mispell_dict=mispell_dict):\n    \"\"\"\"\"\n    cleans the input text in the following steps\n    1- replace contractions\n    2- removing punctuation\n    3- spliting into words\n    4- removing stopwords\n    \"\"\"\"\"\n\n    def _get_contraction(contraction_dict):\n        contraction_re = re.compile('(%s)' % '|'.join(contraction_dict.keys()))\n        return contraction_dict, contraction_re\n\n    # replace contractions\n    def remove_contraction(text, contraction_dict):\n        contractions, contractions_re = _get_contraction(contraction_dict)\n        def replace(match):\n            return contractions[match.group(0)]\n        return contractions_re.sub(replace, text)\n    \n    # remove punctuations\n    def remove_punctuation(text):\n        txt  = \"\".join([char for char in text if char not in punctuation])\n        return re.sub(\"[^a-zA-Z0-9]+\", ' ', txt)\n    \n    # remove stopword\n    def remove_stopword(words):\n        stop_words = set(stopwords.words('english'))\n        words = [w for w in words if not w in stop_words]\n        return words\n\n    # correct mispell \n    def correct_mispell(words):\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        return words    \n    \n    # to lower case\n    def to_lower(words):\n        return words.lower()\n\n    txt = remove_contraction(txt, contraction_dict)\n    txt = remove_punctuation(txt)\n    # split into words\n    words = word_tokenize(txt)\n    # words = remove_stopword(words)\n    \n    words = correct_mispell(words)\n    \n    cleaned_text = ' '.join(words)\n    \n    # to lower case\n    cleaned_text = to_lower(cleaned_text)\n    return cleaned_text","metadata":{"execution":{"iopub.status.busy":"2021-06-11T05:42:13.647936Z","iopub.execute_input":"2021-06-11T05:42:13.648279Z","iopub.status.idle":"2021-06-11T05:42:13.662542Z","shell.execute_reply.started":"2021-06-11T05:42:13.648249Z","shell.execute_reply":"2021-06-11T05:42:13.661518Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3.2. Làm sạch dữ liệu","metadata":{}},{"cell_type":"code","source":"# Start preprocessing train dataset and test dataset\ntqdm.pandas()\ntrain_df['cleaned_question_text'] = train_df['question_text'].progress_apply(lambda txt: clean_text(txt))\ntest_df['cleaned_question_text']  = test_df['question_text'].progress_apply(lambda txt: clean_text(txt))","metadata":{"execution":{"iopub.status.busy":"2021-06-11T05:42:37.354893Z","iopub.execute_input":"2021-06-11T05:42:37.355224Z","iopub.status.idle":"2021-06-11T06:05:38.314561Z","shell.execute_reply.started":"2021-06-11T05:42:37.355197Z","shell.execute_reply":"2021-06-11T06:05:38.313776Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-06-11T00:39:31.524873Z","iopub.execute_input":"2021-06-11T00:39:31.525188Z","iopub.status.idle":"2021-06-11T00:39:31.538099Z","shell.execute_reply.started":"2021-06-11T00:39:31.525157Z","shell.execute_reply":"2021-06-11T00:39:31.537299Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Có thể nhận ra sau khi làm sạch dữ liệu có khả năng sẽ để lại các câu hỏi trống rỗng, hoặc vô nghĩa, gây tác động tiêu cực lên mô hình.","metadata":{}},{"cell_type":"code","source":"# plot and remove NaN value in train_set\nnan_rows = train_df[train_df['cleaned_question_text'].isnull(inplace=True)]\nnan_rows","metadata":{"execution":{"iopub.status.busy":"2021-06-11T03:51:35.821352Z","iopub.execute_input":"2021-06-11T03:51:35.821671Z","iopub.status.idle":"2021-06-11T03:51:35.939768Z","shell.execute_reply.started":"2021-06-11T03:51:35.821641Z","shell.execute_reply":"2021-06-11T03:51:35.93879Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = train_df[train_df['cleaned_question_text'].notna()]","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:07:03.463079Z","iopub.execute_input":"2021-06-11T06:07:03.463395Z","iopub.status.idle":"2021-06-11T06:07:03.700572Z","shell.execute_reply.started":"2021-06-11T06:07:03.463367Z","shell.execute_reply":"2021-06-11T06:07:03.699739Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# compression_opts = dict(method='zip', archive_name='out.csv')  \n# train_df.to_csv(\"./train_processed.zip\", index=False, compression=compression_opts)\n\ntrain_df.to_csv('./train_preprocessed.csv', index=False)\ntest_df.to_csv('./test_preprocessed.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:07:09.859313Z","iopub.execute_input":"2021-06-11T06:07:09.859664Z","iopub.status.idle":"2021-06-11T06:07:18.605431Z","shell.execute_reply.started":"2021-06-11T06:07:09.859617Z","shell.execute_reply":"2021-06-11T06:07:18.60443Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-06-11T00:42:50.33698Z","iopub.execute_input":"2021-06-11T00:42:50.337308Z","iopub.status.idle":"2021-06-11T00:42:50.352043Z","shell.execute_reply.started":"2021-06-11T00:42:50.337277Z","shell.execute_reply":"2021-06-11T00:42:50.351121Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Kết quả**: Có thể thấy các câu hỏi đã được chuẩn hoá làm sạch, sẵn sàng để trích xuất đặc trưng, vector hoá bộ dữ liệu.","metadata":{}},{"cell_type":"markdown","source":"## 3.3. Load dataset","metadata":{}},{"cell_type":"code","source":"TEXT  = data.Field(tokenize='spacy', batch_first=True, include_lengths=True)\nLABEL = data.LabelField(dtype = torch.int64, batch_first=True)","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:08:03.738608Z","iopub.execute_input":"2021-06-11T06:08:03.738942Z","iopub.status.idle":"2021-06-11T06:08:06.438892Z","shell.execute_reply.started":"2021-06-11T06:08:03.738912Z","shell.execute_reply":"2021-06-11T06:08:06.437763Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fields = [(None, None), (None,None), ('target', LABEL), ('text', TEXT)]\n\n# TabularDataset from torchtext only support to load from storage file\ndataset = data.TabularDataset('./train_preprocessed.csv', format = 'csv', fields = fields, skip_header = True)","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:08:09.237176Z","iopub.execute_input":"2021-06-11T06:08:09.237516Z","iopub.status.idle":"2021-06-11T06:10:39.178937Z","shell.execute_reply.started":"2021-06-11T06:08:09.237482Z","shell.execute_reply":"2021-06-11T06:10:39.178048Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# log data example\nprint(vars(dataset.examples[0]))","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:10:39.180409Z","iopub.execute_input":"2021-06-11T06:10:39.180785Z","iopub.status.idle":"2021-06-11T06:10:39.187411Z","shell.execute_reply.started":"2021-06-11T06:10:39.180746Z","shell.execute_reply":"2021-06-11T06:10:39.186361Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3.4. Chia tập dữ liệu Validation\n\nLấy tỉ lệ 8-2 để chia bộ dữ liệu ra 2 phần `training_set` và `valid_set`.\n\n* **Training Set**: sử dụng 80% bộ dữ liệu để huấn luyện\n* **Validation Set**: sử dụng 20% bộ dữ liệu để xác thực","metadata":{}},{"cell_type":"code","source":"import random\n\nSEED = 42\ntorch.manual_seed(SEED)\ntraining_set, valid_set = dataset.split(split_ratio=0.8, random_state = random.seed(SEED))","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:10:39.189369Z","iopub.execute_input":"2021-06-11T06:10:39.189766Z","iopub.status.idle":"2021-06-11T06:10:41.322191Z","shell.execute_reply.started":"2021-06-11T06:10:39.189727Z","shell.execute_reply":"2021-06-11T06:10:41.321316Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 4. Trích xuất đặc trưng","metadata":{}},{"cell_type":"markdown","source":"Để bắt đầu, ta sẽ tìm hiểu về **GloVe** - Global Vectors for Word Representation:\n\nDo số lượng đặc trưng (từ trong từ điển) là khá lớn (nhược điểm của one-hot vector, kỹ thuật biểu diễn từ bằng vector có số chiều bằng số từ vựng), nên Embedding được tạo ra để giảm số chiều của không gian đặc trưng. Cụ thể là mỗi từ sẽ được biểu diễn bằng một vector có số chiều xác định.\n\n![alt text](https://miro.medium.com/max/2456/1*gcC7b_v7OKWutYN1NAHyMQ.png \"Visualization của Embedding Matrix\")\n\n![alt text](https://miro.medium.com/max/2892/1*KdJGfpGf7eApnKuSZDv2ZQ.png)\n\n","metadata":{}},{"cell_type":"markdown","source":"Để load file embeddings sau này, ta thực hiện kéo về giải nén.","metadata":{}},{"cell_type":"code","source":"!unzip ../input/quora-insincere-questions-classification/embeddings.zip -d ./","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:10:41.323617Z","iopub.execute_input":"2021-06-11T06:10:41.324003Z","iopub.status.idle":"2021-06-11T06:14:16.956919Z","shell.execute_reply.started":"2021-06-11T06:10:41.323966Z","shell.execute_reply":"2021-06-11T06:14:16.955915Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Xoá bỏ các file không cần thiết.","metadata":{}},{"cell_type":"code","source":"#due to the space limitation of kaggle, we must clear non use embedding.\nNON_USE_DIR = ['./wiki-news-300d-1M/', './GoogleNews-vectors-negative300', './paragram_300_sl999']\n\nimport os, shutil\ndef remove_dir(path):\n    for filename in os.listdir(path):\n        file_path = os.path.join(path, filename)\n        try:\n            if os.path.isfile(file_path) or os.path.islink(file_path):\n                os.unlink(file_path)\n            elif os.path.isdir(file_path):\n                shutil.rmtree(file_path)\n        except Exception as e:\n            print('Failed to delete %s. Reason: %s' % (file_path, e))\n    os.rmdir(path)\n            \nfor dir in NON_USE_DIR:\n    remove_dir(dir)","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:14:16.960192Z","iopub.execute_input":"2021-06-11T06:14:16.960471Z","iopub.status.idle":"2021-06-11T06:14:19.50334Z","shell.execute_reply.started":"2021-06-11T06:14:16.96044Z","shell.execute_reply":"2021-06-11T06:14:19.502417Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Load file embedding **GloVe** đã giải nén.","metadata":{}},{"cell_type":"code","source":"# Load embedding as storage file\nimport torchtext.vocab as vocab\n\ncustom_embeddings = vocab.Vectors(name = './glove.840B.300d/glove.840B.300d.txt')","metadata":{"scrolled":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Tham chiếu từ điển của bộ dữ liệu (training data) để tạo ma trận nhúng cho bộ từ điển.\n\nTokenization cũng được thực hiện ở đây.","metadata":{}},{"cell_type":"code","source":"TEXT.build_vocab(training_set, min_freq=3, vectors = custom_embeddings)\nLABEL.build_vocab(training_set)\n\n#No. of unique tokens in text\nprint(\"Size of TEXT vocabulary:\",len(TEXT.vocab))\n\n#No. of unique tokens in label\nprint(\"Size of LABEL vocabulary:\",len(LABEL.vocab))\n\n#Commonly used words\nprint(TEXT.vocab.freqs.most_common(10)) ","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:20:35.785488Z","iopub.execute_input":"2021-06-11T06:20:35.785848Z","iopub.status.idle":"2021-06-11T06:20:45.48978Z","shell.execute_reply.started":"2021-06-11T06:20:35.785817Z","shell.execute_reply":"2021-06-11T06:20:45.488806Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 5. Khởi tạo mô hình huấn luyện\n\n**Vấn đề**: Xét dữ liệu ta cần xử lý là dạng văn bản, hay nói cách khác, đó là dữ liệu mang tính liên tục, khác với dữ liệu rời rạc như các tấm ảnh.\n\n**Giải pháp**: \n\nTrong việc lựa chọn mô hình huấn luyện thích hợp, em đã chọn sử dụng RNN - Recurrent Neural Network.\n\nMạng nơ ron hồi quy (RNN) là một loại Mạng nơ ron trong đó đầu ra từ các bước trước được cung cấp làm đầu vào cho bước hiện tại, do đó ghi nhớ một số thông tin về trình tự. \nNó có những hạn chế như khó nhớ các chuỗi dài hơn.\n\nViệc sử dụng RNN giúp xử lý vấn đề về dữ liệu liên tục, không giống với CNN dành cho dạng dữ liệu rời rạc.\n\n","metadata":{}},{"cell_type":"markdown","source":"## 5.1. Bidirectional LSTM Model\n","metadata":{}},{"cell_type":"code","source":"from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:21:34.054693Z","iopub.execute_input":"2021-06-11T06:21:34.055022Z","iopub.status.idle":"2021-06-11T06:21:34.059304Z","shell.execute_reply.started":"2021-06-11T06:21:34.054993Z","shell.execute_reply":"2021-06-11T06:21:34.058145Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"LSTM / GRU là phiên bản cải tiến của RNN, chuyên ghi nhớ thông tin trong một thời gian dài bằng cách sử dụng cơ chế kiểm soát mà RNN không thực hiện được. \n\n![](https://miro.medium.com/max/875/0*Q0uzj4QO5OQADSSX.png)\n\nRNN đơn hướng chỉ lưu giữ thông tin của quá khứ vì các đầu vào mà nó đã thấy là từ quá khứ. Sử dụng LSTM hai chiều sẽ chạy các đầu vào theo hai cách, một từ quá khứ đến tương lai và một từ tương lai đến quá khứ cho phép nó lưu giữ thông tin theo ngữ cảnh từ cả quá khứ và tương lai vào bất kỳ thời điểm nào. ","metadata":{}},{"cell_type":"code","source":"class BidirectionalLSTM(nn.Module):\n    def __init__(self, vocab_size, embedding_dim, hidden_size, num_layer, output_size, dropout_rate=0.1):\n        super(BidirectionalLSTM, self).__init__()\n        self.dimension = hidden_size\n        # Define layer\n        self.embedding   = nn.Embedding(vocab_size, embedding_dim)\n        self.lstm        = nn.LSTM(embedding_dim, hidden_size, num_layer, batch_first=True, bidirectional=True)\n        self.dropout     = nn.Dropout(0.1)\n        self.fc          = nn.Linear(hidden_size*2, output_size)\n        self.sigmoid     = nn.Sigmoid()\n\n    def forward(self, input, input_len):\n        # embedding word\n        x = self.embedding(input)\n        \n        x = pack_padded_sequence(x, input_len.cpu(), batch_first=True, enforce_sorted=False)\n        \n        packed_output, (hidden, cell) = self.lstm(x)\n        output, _ = pad_packed_sequence(packed_output, batch_first=True)\n        out_forward = output[range(len(output)), input_len.cpu() - 1, :self.dimension]\n        out_reverse = output[:, 0, self.dimension:]\n        out_reduced = torch.cat((out_forward, out_reverse), 1)\n        text_feature = self.dropout(out_reduced)\n\n        text_feature = self.fc(text_feature).squeeze(1)\n        return self.sigmoid(text_feature)\n        \n        \n        # Notes!\n#         output = torch.cat((hidden[-2,:,:], hidden[-1,:,:]), dim = 1)\n#         output = self.fc(output)\n#         return self.sigmoid(output)","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:21:37.146477Z","iopub.execute_input":"2021-06-11T06:21:37.146824Z","iopub.status.idle":"2021-06-11T06:21:37.156014Z","shell.execute_reply.started":"2021-06-11T06:21:37.146793Z","shell.execute_reply":"2021-06-11T06:21:37.155063Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Nhận xét**: LSTM là một mô hình tốt và đã chứng tỏ được hiệu quả của nó. Ở phần tiếp theo, em sẽ thử nghiệm một mô hình phức tạp hơn khi chồng 2 RNN với nhau, trong đó có mạng nơ ron LSTM hai chiều vừa đề cập.","metadata":{}},{"cell_type":"markdown","source":"## 5.2. Cải tiến","metadata":{}},{"cell_type":"markdown","source":"Sau khi có được kết quả từ LSTM đã sử dụng bên trên, em thực hiện nghiên cứu và thử nghiệm một cấu trúc mạng như ảnh dưới đây.\n\nĐặc điểm của nó là **xếp chồng hai mạng nơ ron LSTM và GRU hai chiều** với nhau, kết hợp thêm lớp **Dropout**, **Max Pooling** để tăng khả năng biểu diễn ngôn ngữ.","metadata":{}},{"cell_type":"markdown","source":"![LSTM model](https://i.ibb.co/XLwSTTD/download-1.png)\n\n* **LSTM**: Lọc bộ nhớ hiện tại, chắt lọc thông tin hiện tại để đưa vào bộ nhớ và kết hợp bộ nhớ và dữ liệu hiện tại để đưa ra đầu ra bằng 4 cổng.\n\n* **GRU** - Gated Recurrent Unit: Khác với LSTM, GRU chỉ có 2 cổng để chắt lọc thông tin, cổng reset và cổng update. Nó không có trạng thái cell mà chỉ có đầu ra vừa dùng để đưa ra quyết định vừa dùng để thông tin cho các bước tiếp theo.\n\n* **Dropout**: Bởi vì lượng dữ liệu đưa vào model bị lệch rất nhiều, nên ta cần sử dụng lớp Dropout.\nBỏ qua một vài unit trong suốt quá trình train trong mô hình, những unit bị bỏ qua được lựa chọn ngẫu nhiên, để tráng overfit.\n\n* **Max Pooling**: Một layer nén thông tin để đơn giản hóa thông tin đầu ra để giảm bớt số lượng neuron.","metadata":{}},{"cell_type":"code","source":"class LSTM_GRU(nn.Module):\n    def __init__(self, vocab_size, embedding_dim, hidden_size, num_layer, output_size, dropout_rate=0.1):\n        super(LSTM_GRU, self).__init__()\n        # Define layer\n        self.embedding   = nn.Embedding(vocab_size, embedding_dim)\n        self.lstm        = nn.LSTM(embedding_dim, hidden_size, num_layer, batch_first=True, bidirectional=True)\n        self.gru         = nn.GRU(hidden_size*2, hidden_size//2, num_layer, batch_first=True, bidirectional=True)\n\n        self.fc          = nn.Linear(hidden_size*3, output_size)\n        \n        self.emb_dropout = nn.Dropout2d(dropout_rate)\n        self.sigmoid     = nn.Sigmoid()\n\n    def forward(self, input, input_len):\n        # layer 1: embedding\n        x = self.embedding(input)\n        \n        # layer 2: Spatial Dropout 1D\n        embed = x.unsqueeze(2) # (N, T, 1, K)\n        embed = embed.permute(0, 3, 2, 1)  # (N, K, 1, T)\n        embed = self.emb_dropout(embed)  # (N, K, 1, T)\n        embed = embed.permute(0, 3, 2, 1)  # (N, T, 1, K)\n        x = embed.squeeze(2)  # (N, T, K)\n        \n        # layer 3: Bidirectional LSTM\n        x = pack_padded_sequence(x, input_len.cpu(), batch_first=True, enforce_sorted=False) # (N, T, K)\n        packed_lstm, h_lstm = self.lstm(x)\n        \n        # layer 4: Bidirectional GRU\n        packed_gru, h_gru = self.gru(packed_lstm)\n        \n        packed_lstm = pad_packed_sequence(packed_lstm, batch_first=True)\n        packed_gru = pad_packed_sequence(packed_gru, batch_first=True)\n        \n        # layer 5: Concat\n        x = torch.cat((packed_gru[0], packed_lstm[0]), 2)\n        \n        # layer 6: Global Average Pool\n        avg_pool = torch.mean(x, 1)\n        \n        # layer 7: Fully connected\n        x = self.fc(avg_pool)\n        x = self.sigmoid(x)\n        return x","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:21:39.893038Z","iopub.execute_input":"2021-06-11T06:21:39.893353Z","iopub.status.idle":"2021-06-11T06:21:39.90434Z","shell.execute_reply.started":"2021-06-11T06:21:39.893324Z","shell.execute_reply":"2021-06-11T06:21:39.903201Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**F1 Score** được tính như bên dưới. Chi tiết đã được viết ở phần 1.4. Đánh giá.","metadata":{}},{"cell_type":"code","source":"def binary_accuracy(preds, y):\n    #round predictions to the closest integer\n    rounded_preds = torch.round(preds)\n    \n    correct = (rounded_preds == y).float() \n    acc = correct.sum() / len(correct)\n    return acc\n\ndef f1_loss(y_true:torch.Tensor, y_pred:torch.Tensor, is_training=False) -> torch.Tensor:\n    '''Calculate F1 score. Can work with gpu tensors\n    \n    The original implmentation is written by Michal Haltuf on Kaggle.\n    \n    Returns\n    -------\n    torch.Tensor\n        `ndim` == 1. 0 <= val <= 1\n    \n    Reference\n    ---------\n    - https://www.kaggle.com/rejpalcz/best-loss-function-for-f1-score-metric\n    - https://scikit-learn.org/stable/modules/generated/sklearn.metrics.f1_score.html#sklearn.metrics.f1_score\n    - https://discuss.pytorch.org/t/calculating-precision-recall-and-f1-score-in-case-of-multi-label-classification/28265/6\n    \n    '''\n    assert y_true.ndim == 1\n    assert y_pred.ndim == 1 or y_pred.ndim == 2\n    \n    if y_pred.ndim == 2:\n        y_pred = y_pred.argmax(dim=1)\n        \n    \n    tp = (y_true * y_pred).sum().to(torch.float32)\n    tn = ((1 - y_true) * (1 - y_pred)).sum().to(torch.float32)\n    fp = ((1 - y_true) * y_pred).sum().to(torch.float32)\n    fn = (y_true * (1 - y_pred)).sum().to(torch.float32)\n    \n    epsilon = 1e-7\n    \n    precision = tp / (tp + fp + epsilon)\n    recall = tp / (tp + fn + epsilon)\n    \n    f1 = 2* (precision*recall) / (precision + recall + epsilon)\n#     f1.requires_grad = is_training\n    return f1","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:21:41.465989Z","iopub.execute_input":"2021-06-11T06:21:41.466302Z","iopub.status.idle":"2021-06-11T06:21:41.476391Z","shell.execute_reply.started":"2021-06-11T06:21:41.466272Z","shell.execute_reply":"2021-06-11T06:21:41.475525Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Quá trình train:\n\n* `load data`\n\n* `forward`: Đẩy input vào model cũng như tính loss.\n\n* `zero_grad`: Để grad trở về 0 trước khi chạy.\n\n* `backpropagation`: Đạo hàm ngược để tiến hành backpropagation.","metadata":{}},{"cell_type":"code","source":"def train(model, device, train_iterator, optimizer, loss_function):\n    model.train()\n    running_loss = 0\n    accuracy     = 0\n    for input, target in train_iterator:\n        input, input_len, target = input[0].to(device), input[1].to(device), target.to(device, dtype=torch.float32)\n\n        # forward\n        predict = model(input, input_len).squeeze()\n        loss = loss_function(predict, target)\n\n        # metric\n        accuracy     += f1_loss(predict, target)\n        running_loss += loss.item()\n        \n        # zero the gradient \n        optimizer.zero_grad()\n\n        # backpropagation + step\n        loss.backward()\n        optimizer.step()\n        \n    epoch_loss = running_loss/len(train_iterator)\n    epoch_acc  = accuracy/len(train_iterator)\n\n    return epoch_loss, epoch_acc","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:21:43.099012Z","iopub.execute_input":"2021-06-11T06:21:43.099349Z","iopub.status.idle":"2021-06-11T06:21:43.106901Z","shell.execute_reply.started":"2021-06-11T06:21:43.099317Z","shell.execute_reply":"2021-06-11T06:21:43.106022Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"`no_grad`: giúp file test không bị mô hình nhớ\n\nSau đó mô hình sẽ chạy để tính loss và accuracy.","metadata":{}},{"cell_type":"code","source":"def test(model, device, test_iterator, loss_function):\n    model.eval()\n    running_loss = 0\n    accuracy     = 0\n    \n    with torch.no_grad():\n        for input, target in test_iterator:\n            input, input_len, target = input[0].to(device), input[1].to(device), target.to(device, dtype=torch.float32)\n\n            predict = model(input, input_len).squeeze()\n            loss = loss_function(predict, target)\n\n            running_loss += loss.item()\n            accuracy     += f1_loss(predict, target)\n\n    epoch_loss = running_loss/len(test_iterator)\n    epoch_acc  = accuracy/len(test_iterator)\n    \n    return epoch_loss, epoch_acc","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:21:44.899898Z","iopub.execute_input":"2021-06-11T06:21:44.900332Z","iopub.status.idle":"2021-06-11T06:21:44.907123Z","shell.execute_reply.started":"2021-06-11T06:21:44.900292Z","shell.execute_reply":"2021-06-11T06:21:44.906075Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 6. Huấn luyện","metadata":{}},{"cell_type":"markdown","source":"## 6.1. Cài đặt thiết lập","metadata":{}},{"cell_type":"code","source":"# using cuda\ndevice = f\"cuda:{torch.cuda.current_device()}\" if torch.cuda.is_available() else \"cpu\"\nprint(\"Training on:\", torch.cuda.get_device_name(torch.cuda.current_device()) if torch.cuda.is_available() else \"cpu\")\n\n# config\nBATCH_SIZE       = 64\nVOCAB_SIZE       = len(TEXT.vocab)\nEMBEDDING_DIM    = 300\nHIDDEN_SIZE      = 128\nOUTPUT_SIZE      = 1\nNUM_LAYER        = 2\n\nN_EPOCH          = 10","metadata":{"execution":{"iopub.status.busy":"2021-06-11T07:36:29.38016Z","iopub.execute_input":"2021-06-11T07:36:29.380415Z","iopub.status.idle":"2021-06-11T07:36:29.3863Z","shell.execute_reply.started":"2021-06-11T07:36:29.380389Z","shell.execute_reply":"2021-06-11T07:36:29.385503Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Load iterator để trỏ vào giá trị trong tập dữ liệu.","metadata":{}},{"cell_type":"code","source":"#Load an iterator\ntrain_iterator = data.BucketIterator(training_set, batch_size = BATCH_SIZE, \n                                     sort_key = lambda x: len(x.text), sort=True, sort_within_batch=True, device=device)\nvalid_iterator = data.BucketIterator(valid_set, batch_size = BATCH_SIZE, \n                                     sort_key = lambda x: len(x.text), sort=True, sort_within_batch=True, device=device)\n","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:21:51.203994Z","iopub.execute_input":"2021-06-11T06:21:51.2044Z","iopub.status.idle":"2021-06-11T06:21:51.213784Z","shell.execute_reply.started":"2021-06-11T06:21:51.204362Z","shell.execute_reply":"2021-06-11T06:21:51.212749Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 6.2. Khởi tạo model","metadata":{}},{"cell_type":"markdown","source":"Em sử dụng **Adam** từ thư viện torch làm **optimizer** với mục đích cập nhật trọng số.\n\n**Loss function** sử dụng **Binary Cross entropy**, được thiết kế để thích nghi với những bài toán 2 class.\n\n\n\n","metadata":{}},{"cell_type":"markdown","source":"**Learning Rate** trong quá trình tuning đã cho thấy sẽ đạt kết quả tốt hơn nếu > 0.0003 và < 0.001. Vì vậy em lựa chọn lr=0.0004 cho version này.","metadata":{}},{"cell_type":"code","source":"# init model\nmodel = LSTM_GRU(vocab_size=VOCAB_SIZE, \n             embedding_dim=EMBEDDING_DIM, \n             hidden_size=HIDDEN_SIZE, \n             num_layer=NUM_LAYER, \n             output_size=OUTPUT_SIZE).to(device)\n\n# model.load_state_dict(torch.load('./weights.pt'))\n\n# loss function & optimizer\noptimizer = optim.AdamW(model.parameters(), lr=0.0004, weight_decay=0.07)\n\nciteration = nn.BCELoss()","metadata":{"execution":{"iopub.status.busy":"2021-06-11T07:36:29.387667Z","iopub.execute_input":"2021-06-11T07:36:29.388233Z","iopub.status.idle":"2021-06-11T07:36:29.661182Z","shell.execute_reply.started":"2021-06-11T07:36:29.388195Z","shell.execute_reply":"2021-06-11T07:36:29.6603Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Initialize the pretrained embedding\npretrained_embeddings = TEXT.vocab.vectors\nmodel.embedding.weight.data.copy_(pretrained_embeddings)\n\nprint(pretrained_embeddings.shape)","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:41:34.436064Z","iopub.execute_input":"2021-06-11T06:41:34.436394Z","iopub.status.idle":"2021-06-11T06:41:34.461573Z","shell.execute_reply.started":"2021-06-11T06:41:34.436364Z","shell.execute_reply":"2021-06-11T06:41:34.460722Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 6.3. Tiến hành huấn luyện","metadata":{}},{"cell_type":"code","source":"# Start training\nbest_accuracy = 0\n\ntrain_losses, train_res = [], []\nvalid_losses, valid_res = [], []\n\nfor epoch in range(N_EPOCH):\n    # train\n    train_loss, train_accuracy = train(model, device, train_iterator, optimizer, citeration)\n    train_losses.append(train_loss)\n    train_res.append(train_accuracy)\n    \n    # evaluate\n    test_loss, test_accuracy = test(model, device, valid_iterator, citeration)\n    valid_losses.append(test_loss)\n    valid_res.append(test_accuracy)\n    \n    #save the best model\n    if test_accuracy > best_accuracy:\n        best_accuracy = test_accuracy\n        torch.save(model.state_dict(), './weights.pt')\n    \n    print(f'Epoch {epoch+1} summary ===========================')\n    print(f'Train Loss: {train_loss:.3f} | Train F1 score: {train_accuracy*100:.2f}%')\n    print(f' Val. Loss: {test_loss:.3f} |  Val. F1 score: {test_accuracy*100:.2f}%')","metadata":{"execution":{"iopub.status.busy":"2021-06-11T06:41:36.729739Z","iopub.execute_input":"2021-06-11T06:41:36.730065Z","iopub.status.idle":"2021-06-11T07:12:34.959572Z","shell.execute_reply.started":"2021-06-11T06:41:36.730036Z","shell.execute_reply":"2021-06-11T07:12:34.957567Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"*Visualize training experiment*","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nfrom sklearn.metrics import plot_confusion_matrix","metadata":{"execution":{"iopub.status.busy":"2021-06-11T07:12:46.166214Z","iopub.execute_input":"2021-06-11T07:12:46.166537Z","iopub.status.idle":"2021-06-11T07:12:46.170252Z","shell.execute_reply.started":"2021-06-11T07:12:46.166506Z","shell.execute_reply":"2021-06-11T07:12:46.169186Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"epoch = range(0, len(train_losses))\n\nfig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, sharex=True, figsize=(24, 6))\n\nax0.plot(epoch, train_losses, 'g', label='Training loss')\nax0.plot(epoch, valid_losses, 'b', label='validation loss')\nax0.set_title('Training and Validation loss')\nax0.set_xlabel('Epochs')\nax0.set_ylabel('Loss')\nax0.legend()\n\nax1.plot(epoch, train_res, 'g', label='Training F1 score')\nax1.plot(epoch, valid_res, 'b', label='validation F1 score')\nax1.set_title('Training and Validation F1 score')\nax1.set_xlabel('Epochs')\nax1.set_ylabel('F1 score')\nax1.legend()\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-11T07:12:46.567193Z","iopub.execute_input":"2021-06-11T07:12:46.567511Z","iopub.status.idle":"2021-06-11T07:12:47.144919Z","shell.execute_reply.started":"2021-06-11T07:12:46.567481Z","shell.execute_reply":"2021-06-11T07:12:47.143874Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# plot confusion matrix\nnb_classes = 2\nconfusion_matrix = np.zeros((nb_classes, nb_classes))\n\nwith torch.no_grad():\n    for input, target in test_iterator:\n        input, input_len, target = input[0].to(device), input[1].to(device), target.to(device, dtype=torch.float32)\n\n        predict = model(input, input_len).squeeze()\n        loss = loss_function(predict, target)\n\n        \n#         _, preds = torch.max(outputs, 1)\n        for t, p in zip(target.view(-1), predict.view(-1)):\n                confusion_matrix[t.long(), p.long()] += 1\n\nplt.figure(figsize=(15,10))\n\nclass_names = list(label2class.values())\ndf_cm = pd.DataFrame(confusion_matrix, index=class_names, columns=class_names).astype(int)\nheatmap = sns.heatmap(df_cm, annot=True, fmt=\"d\")\n\nheatmap.yaxis.set_ticklabels(heatmap.yaxis.get_ticklabels(), rotation=0, ha='right',fontsize=15)\nheatmap.xaxis.set_ticklabels(heatmap.xaxis.get_ticklabels(), rotation=45, ha='right',fontsize=15)\nplt.ylabel('True label')\nplt.xlabel('Predicted label')","metadata":{"execution":{"iopub.status.busy":"2021-06-11T07:12:58.212842Z","iopub.execute_input":"2021-06-11T07:12:58.213187Z","iopub.status.idle":"2021-06-11T07:12:58.239817Z","shell.execute_reply.started":"2021-06-11T07:12:58.213157Z","shell.execute_reply":"2021-06-11T07:12:58.238291Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Khó khăn và bài học rút ra**: Có thể thấy mô hình đang bị **underfit**. Nguyên nhân có thể là:\n\n* Mô hình chưa đủ khả năng biểu diễn\n\n* Quá trình cài đặt loss function có thể chưa chính xác","metadata":{}},{"cell_type":"markdown","source":"**Giải pháp**: \n\n* Tăng số lượng parameter\n\n* Tăng cường data (Data Augmentation)","metadata":{}},{"cell_type":"markdown","source":"# 7. Dự đoán kết quả","metadata":{}},{"cell_type":"code","source":"# test dataset\ntest_df = pd.read_csv('./test_preprocessed.csv')\ntest_df.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 7.1. Helper Function","metadata":{}},{"cell_type":"code","source":"# helper function\nimport spacy\nnlp = spacy.load('en')\n\ndef predict(model, sentence):\n    tokenized = [tok.text for tok in nlp.tokenizer(sentence)]  #tokenize the sentence # nlp.tokenizer(sentence)\n    indexed   = [TEXT.vocab.stoi[t] for t in tokenized]          #convert to integer sequence\n    length = [len(indexed)]                                    #compute no. of words\n    tensor = torch.LongTensor(indexed).to(device)              #convert to tensor\n    tensor = tensor.unsqueeze(1).T                             #reshape in form of batch,no. of words\n    length_tensor = torch.LongTensor(length)                   #convert to tensor\n    \n    try:\n        prediction = model(tensor, length_tensor)              #prediction \n    except:\n        # print(\"Empty sentence:\", sentence)\n        return 0\n    \n    return prediction.item()   ","metadata":{"execution":{"iopub.status.busy":"2021-06-11T07:13:29.033773Z","iopub.execute_input":"2021-06-11T07:13:29.034238Z","iopub.status.idle":"2021-06-11T07:13:29.963423Z","shell.execute_reply.started":"2021-06-11T07:13:29.034197Z","shell.execute_reply":"2021-06-11T07:13:29.962596Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 7.2. Trả kết quả","metadata":{}},{"cell_type":"markdown","source":"**Vấn đề**: Tập dữ liệu các câu thiếu chân thành rất ít so với bộ dữ liệu.\n\n**Giải pháp**: Lựa chọn threshold bằng giá trị phù hợp để không làm sai lệch quá nhiều.\n\nTa xuất raw_prediction từ tập validation set để từ mỗi giá trị threshold, sinh ra một prediction. Tham chiếu từ đó để ra kết quả tốt nhất.\n\n**Kết quả**: Em đã chạy threshold từ 0.3 - 0.7 và thấy 0.32 có thể là con số tốt nhất.\n\n","metadata":{}},{"cell_type":"code","source":"raw_prediction, prediction = [], []\nTHRES_HOLD = 0.32\n\nmodel.load_state_dict(torch.load('./weights.pt'))\nmodel.eval()\n\nfor idx, row in test_df.iterrows():\n    pred = 0\n    raw_pred = predict(model, row['cleaned_question_text'])\n    if raw_pred >= THRES_HOLD:\n        pred = 1\n        \n    # save to list\n    prediction.append(pred)\n    raw_prediction.append(raw_pred)\n\nprint('# of prediction', len(prediction))","metadata":{"execution":{"iopub.status.busy":"2021-06-11T07:13:37.42918Z","iopub.execute_input":"2021-06-11T07:13:37.429509Z","iopub.status.idle":"2021-06-11T07:36:28.371043Z","shell.execute_reply.started":"2021-06-11T07:13:37.429477Z","shell.execute_reply":"2021-06-11T07:36:28.369525Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# merge\ntest_df['raw_prediction'] = raw_prediction\ntest_df['prediction'] = prediction\n\ntest_df.head(10)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Nộp bài:","metadata":{}},{"cell_type":"code","source":"prediction_df = test_df.drop(['question_text', 'cleaned_question_text', 'raw_prediction'], axis=1)\nprediction_df.to_csv('./submission.csv', index=False)\n\nprediction_df.head(10)","metadata":{"execution":{"iopub.status.busy":"2021-06-11T07:36:28.595632Z","iopub.execute_input":"2021-06-11T07:36:28.596011Z","iopub.status.idle":"2021-06-11T07:36:29.3772Z","shell.execute_reply.started":"2021-06-11T07:36:28.595973Z","shell.execute_reply":"2021-06-11T07:36:29.376384Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **KẾT QUẢ**\n\nHiện tại kết quả cao nhất mà em đã đạt được là 0.63 ở private score và 0.61 ở public score.","metadata":{}}]}