{"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":"code","source":"# Load libraries\nimport re\nimport sys\nimport math\nimport string\nimport zipfile\nimport unicodedata\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nfrom tqdm import tqdm\nfrom collections import Counter\nfrom gensim.models import KeyedVectors\n\nfrom sklearn import metrics\nfrom sklearn.metrics import f1_score\nfrom sklearn.model_selection import train_test_split\n\nfrom keras import backend as K\nfrom keras.engine.topology import Layer\nfrom keras.models import Sequential, Model\nfrom keras import initializers, regularizers, constraints\nfrom keras.layers import LSTM, Dense, Bidirectional, Input, Dropout","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:05:22.829089Z","iopub.execute_input":"2021-06-10T10:05:22.829672Z","iopub.status.idle":"2021-06-10T10:05:29.579919Z","shell.execute_reply.started":"2021-06-10T10:05:22.829564Z","shell.execute_reply":"2021-06-10T10:05:29.578603Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **I. Chuẩn bị dữ liệu**","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"markdown","source":"# 1. Load bộ dữ liệu\n\n**Problem**\n\nPhải load bộ dữ liệu lên mẫu để xử lý\n\n**Solution**\n\nSử dụng hàm ```read_csv``` của thư viện pandas để load file CSV ","metadata":{}},{"cell_type":"code","source":"# Load datasets\ntrain_df = pd.read_csv('../input/quora-insincere-questions-classification/train.csv')\ntest_dataframe = pd.read_csv('../input/quora-insincere-questions-classification/test.csv')","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-06-10T10:05:29.581207Z","iopub.execute_input":"2021-06-10T10:05:29.581486Z","iopub.status.idle":"2021-06-10T10:05:35.517891Z","shell.execute_reply.started":"2021-06-10T10:05:29.58146Z","shell.execute_reply":"2021-06-10T10:05:35.516778Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. Miêu tả bộ dữ liệu\n\n**Problem**\n\nHiển thị một số đặc điểm cơ bản của của bộ dữ liệu\n\n**Solution**\n\nSau khi hoàn thành việc load dữ liệu.\n* Đơn giản nhất là hiển thị vài dòng của dữ liệu, sử dụng ```head``` để xem những dòng đầu tiên hoặc ```tail``` để quan sát những dòng cuối cùng\n* Có thể lấy được số dòng và số cột của bộ dữ liệu, sử dụng ```shape```\n* Bên cạch đó, ta có thể miêu tả một vài thống kê cơ bản hoặc sử dụng thư viện **matplotlib** để visualize các thông số của dữ liệu.\n\n**Discussion**\n\nSau khi load xong bộ, dữ liệu, phải hiểu được cấu trúc của nó cũng như các trường thông tin mà dữ liệu sở hữu. Trong bài toán phân loại mà chúng ta đang giải quyết, ta có thể thấy:\n* Dữ liệu gồm 3 trường, trong đó ta chỉ cần quan tâm đến cột **question_text** là nội dung của câu hỏi mình cần phân loại, cũng như **target** là dữ liệu kiểu BINARY với giá trị 1 là câu hỏi toxic, ngược lại 0 là câu hỏi non-toxic\n* Dữ liệu test gồm 1306122 câu hỏi\n* Sử dụng phương pháp được nêu trên, nhận thấy các câu hỏi trong bộ dữ liệu có chứa trung bình 12.8 từ trong một câu và trong một câu có trung bình 70.68 ký tự","metadata":{}},{"cell_type":"code","source":"# View first five rows\ntrain_df.head(5)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:05:35.525926Z","iopub.execute_input":"2021-06-10T10:05:35.526224Z","iopub.status.idle":"2021-06-10T10:05:35.55421Z","shell.execute_reply.started":"2021-06-10T10:05:35.526194Z","shell.execute_reply":"2021-06-10T10:05:35.552949Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Show dimensions\ntrain_df.shape","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:05:35.555981Z","iopub.execute_input":"2021-06-10T10:05:35.556461Z","iopub.status.idle":"2021-06-10T10:05:35.563507Z","shell.execute_reply.started":"2021-06-10T10:05:35.556412Z","shell.execute_reply":"2021-06-10T10:05:35.562432Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Show statistics\ntotal_word_length = 0\nfor text in train_df['question_text']:\n    total_word_length += len(text.split())\nprint('Average of Words in Questions: {:.2f} (words)'.format(total_word_length / len(train_df)))\n\ntotal_char_length = 0\nfor text in train_df['question_text']:\n    total_char_length += len(text)\nprint('Average length of Questions: {:.2f} (characters)'.format(total_char_length / len(train_df)))","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:05:35.565241Z","iopub.execute_input":"2021-06-10T10:05:35.565687Z","iopub.status.idle":"2021-06-10T10:05:37.491284Z","shell.execute_reply.started":"2021-06-10T10:05:35.565621Z","shell.execute_reply":"2021-06-10T10:05:37.490223Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot histogram of text lengths\nword_length_list = [len(x.split()) for x in train_df['question_text'] if len(x.split()) < 60]\nchar_length_list = [len(x) for x in train_df['question_text'] if len(x) < 100]\nfig, axs = plt.subplots(1, 2, sharey=True, tight_layout=True)\naxs[0].hist(word_length_list, bins=25)\naxs[0].set_title('Words in Questions')\n\naxs[1].hist(char_length_list, bins=25)\naxs[1].set_title('Length of Questions')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:05:37.493733Z","iopub.execute_input":"2021-06-10T10:05:37.494046Z","iopub.status.idle":"2021-06-10T10:05:54.56338Z","shell.execute_reply.started":"2021-06-10T10:05:37.494014Z","shell.execute_reply":"2021-06-10T10:05:54.562369Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3. Chuẩn bị dữ liệu train và dữ liệu validate\n\n**Problem**\n\nVới các bài toán học máy, ta phải train mô hình của mình, cũng như validate lại độ chính xác của nó.\n\n**Solution**\n\nTa được cung cấp 1 file csv về bộ dữ liệu train, đây là bộ dữ liệu đã có target của từng câu hỏi. Vì vậy, ta sẽ tách bộ dữ liệu đó thành 2 phần, một phần để train, phần còn lại để test, kiểm tra độ đúng đắn của mô hình.\n\nSử dụng hàm ```train_test_split``` của thư viện **sklearn** để chia bộ dữ liệu.\n\n**Discussion**\n\nRõ ràng, ta phải dùng phần lớn dữ liệu cho việc train, tuy nhiên để đảm bảo độ chính xác, cũng không được dùng quá ít dữ liệu cho việc validate. Bộ dữ liệu của bài toán cũng đã khá lớn, nên việc chia tỉ lệ này cũng sẽ không quá ảnh hưởng, ta sẽ chia theo tỉ lệ 9:1.","metadata":{}},{"cell_type":"code","source":"# Create training and validation sets\ntrain_dataframe, val_dataframe = train_test_split(train_df, test_size=0.1)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:05:54.565212Z","iopub.execute_input":"2021-06-10T10:05:54.565502Z","iopub.status.idle":"2021-06-10T10:05:55.272609Z","shell.execute_reply.started":"2021-06-10T10:05:54.565474Z","shell.execute_reply":"2021-06-10T10:05:55.271465Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **II. Xử lý dữ liệu**","metadata":{}},{"cell_type":"markdown","source":"# 1. Load bộ Embeddings\n\n**Problem**\n\nDữ liệu dạng text không thể dùng để train mô hình học máy được.\n\n**Solution**\n\nWord Embedding là quá trình đưa các từ trong câu về dạng để mô hình có thể hiểu được. Cụ thể trong bài toán này là từ dạng text, các từ sẽ được chuyển về dạng vectors đặc trưng để đưa vào mô hình.\n\nSử dụng các file Embeddings đã được cho sẵn để vectors hoá dữ liệu.\n\n**Discuss**\n\n\nỞ đây ta sẽ sử dụng bộ Embeddings **GoogleNews**","metadata":{}},{"cell_type":"code","source":"# Load embeddings\narchive = zipfile.ZipFile('/kaggle/input/quora-insincere-questions-classification/embeddings.zip', 'r')\npath=archive.open('GoogleNews-vectors-negative300/GoogleNews-vectors-negative300.bin', 'r')\n\nembeddings_index = KeyedVectors.load_word2vec_format(path, binary=True)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:05:55.274058Z","iopub.execute_input":"2021-06-10T10:05:55.274386Z","iopub.status.idle":"2021-06-10T10:07:23.862776Z","shell.execute_reply.started":"2021-06-10T10:05:55.274355Z","shell.execute_reply":"2021-06-10T10:07:23.861677Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. Kiểm tra độ phủ của tập Embeddings\n\n**Problem**\n\nSẽ có những từ sẽ không xuất hiện trong tập Embeddings. Ta cần kiểm tra độ phủ của tập Embeddings đối với tập các từ trong bộ câu hỏi trong Dataframe.\n\n**Solution**\n\nTa cần viết một hàm để xây dựng tập từ vựng cùng với tần xuất suất hiện của từ đó trong bộ câu hỏi.\n\nSau đó, cần viết thêm một hàm để đếm số lượng các từ vựng xuất hiện trong tập Embeddings cũng như độ phủ của chúng trong toàn bộ tập câu hỏi. \n\n**Discuss**\n\nTa thấy độ phủ của tập Embeddings trong bộ từ vựng không được cao. \n\nSau khi kiểm tra bộ OOV (out of vocab), ta thấy top 20 từ vựng trong bộ OOV có tần suất xuất hiện cao nhất có chứa các dấu câu cũng như các số.","metadata":{}},{"cell_type":"code","source":"def to_vocab(lines):\n    vocab = Counter()\n    for line in tqdm(lines, position=0):\n        vocab.update(line.split())\n    return vocab","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:07:23.865135Z","iopub.execute_input":"2021-06-10T10:07:23.865449Z","iopub.status.idle":"2021-06-10T10:07:23.871005Z","shell.execute_reply.started":"2021-06-10T10:07:23.865418Z","shell.execute_reply":"2021-06-10T10:07:23.869806Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def check_coverage(vocab, embeddings_index):\n    embeddings_in_vocab = 0\n    embeddings_in_all_text = 0\n    oov_in_all_text = 0\n    oov = Counter()\n    \n    for word in tqdm(vocab, position=0):\n        if word in embeddings_index:\n            embeddings_in_vocab += 1\n            embeddings_in_all_text += vocab[word]    \n        else:\n            oov[word] = vocab[word]\n            oov_in_all_text += vocab[word]\n\n    print('Found embeddings for {:.2%} of vocab'.format(embeddings_in_vocab / len(vocab)))\n    print('Found embeddings for {:.2%} of all text'.format(embeddings_in_all_text / (embeddings_in_all_text + oov_in_all_text)))\n    \n    return oov","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:07:23.872221Z","iopub.execute_input":"2021-06-10T10:07:23.872529Z","iopub.status.idle":"2021-06-10T10:07:23.935774Z","shell.execute_reply.started":"2021-06-10T10:07:23.872497Z","shell.execute_reply":"2021-06-10T10:07:23.934683Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"vocab = to_vocab(train_df['question_text'])\noov = check_coverage(vocab, embeddings_index)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:07:23.937271Z","iopub.execute_input":"2021-06-10T10:07:23.937578Z","iopub.status.idle":"2021-06-10T10:07:33.779583Z","shell.execute_reply.started":"2021-06-10T10:07:23.937549Z","shell.execute_reply":"2021-06-10T10:07:33.778512Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"oov.most_common(25)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:07:33.780801Z","iopub.execute_input":"2021-06-10T10:07:33.781088Z","iopub.status.idle":"2021-06-10T10:07:33.855186Z","shell.execute_reply.started":"2021-06-10T10:07:33.78106Z","shell.execute_reply":"2021-06-10T10:07:33.85409Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3. Xử lý dữ liệu dạng text\n\n### a. Loại bỏ dấu câu và chữ số\n\n**Problem**\n\nCần xử lý dấu câu và chữ số xuất hiện trong bộ câu hỏi để tăng độ phủ của tập Embeddings.\n\n**Solution**\n\nTa sử dụng 2 thư viện **string** và **unicodedata** để xây dựng list các dấu câu. \n\nSau đó kiểm tra từng câu hỏi, với mỗi dấu câu xuất hiện trong câu hỏi, nếu nó cũng xuất hiện trong tập Embeddings thì giữ lại, không thì bỏ nó khỏi đoạn text. \n\nTương tự với các chữ số, chỉ giữ lại các số nhỏ hơn 10 (vì nó tồn tại trong tập Embeddings), còn lại thì bỏ đi.\n\n\n**Discuss**\n\nCần chú ý rằng không nên loại bó toàn bộ dấu câu và chữ số. Bởi vì các dấu câu và chữ số cũng sẽ mang sắc thái cho câu hỏi và tăng giá trị của feature (VD: \"*Let's eat, grandpa*\" và \"*Let's eat grandpa*\" mang 2 ý nghĩa khác nhau hoàn toàn).\n\nNhư ở đây ta có thể kiểm tra thấy dấu \"?\" không tồn tại trong tập Embeddings trong khi dấu \"+\" thì có.\n\nSau khi xử lý qua 2 bước trên, kiểm tra lại ta thấy độ phủ của tập Embeddings đã tăng lên rất nhiều.\n\nTrong đó top 20 từ vựng phổ biến nhất trong tập OOV có chứa những từ gây nhầm lẫn mà ta có thể xử lý được. Ví dụ những từ bị sai chính tả như **favourite**, những từ không tồn tại trong tập embeddings như **bitcoin** hay những từ được viết tắt như **Brexit**, ta có thể thay thế những từ này bằng những từ có ý nghĩa tương tự nhưng có tồn tại trong tập Embeddings.","metadata":{}},{"cell_type":"code","source":"# Check the presence of punctuations in embeddings_index\nprint('? in embeddings_index: ', '?' in embeddings_index)\nprint('+ in embeddings_index: ', '+' in embeddings_index)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:07:33.856577Z","iopub.execute_input":"2021-06-10T10:07:33.856891Z","iopub.status.idle":"2021-06-10T10:07:33.862577Z","shell.execute_reply.started":"2021-06-10T10:07:33.856862Z","shell.execute_reply":"2021-06-10T10:07:33.861481Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Build the list of punctuations\npunctuation = [chr(i) for i in range(sys.maxunicode) if unicodedata.category(chr(i)).startswith('P')]\nfor punct in string.punctuation:\n    if punct not in punctuation:\n        punctuation.append(punct)\npunctuation_in_embeddings = [punct for punct in punctuation if punct in embeddings_index]\npunctuation_not_in_embeddings = [punct for punct in punctuation if punct not in embeddings_index]","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:07:33.864127Z","iopub.execute_input":"2021-06-10T10:07:33.864455Z","iopub.status.idle":"2021-06-10T10:07:34.40122Z","shell.execute_reply.started":"2021-06-10T10:07:33.864424Z","shell.execute_reply":"2021-06-10T10:07:34.400054Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Remove punctuations from text\ndef clean_text(x):\n    for punct in punctuation_not_in_embeddings:\n        x = x.replace(punct, ' ')\n    for punct in punctuation_in_embeddings:\n        x = x.replace(punct, f' {punct} ')\n    return x\n\n# Remove numbers from text\ndef clean_numbers(x):\n    return re.sub('[0-9]{2}', ' ', x)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:07:34.402822Z","iopub.execute_input":"2021-06-10T10:07:34.403138Z","iopub.status.idle":"2021-06-10T10:07:34.409814Z","shell.execute_reply.started":"2021-06-10T10:07:34.403107Z","shell.execute_reply":"2021-06-10T10:07:34.408269Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sentences = train_df[\"question_text\"]\n\n# Clean punctations in questions set\nsentences = [clean_text(x) for x in tqdm(sentences, position=0)]\n\n# Clean numbers in questions set\nsentences = [clean_numbers(x) for x in tqdm(sentences, position=0)]","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:07:34.411134Z","iopub.execute_input":"2021-06-10T10:07:34.411438Z","iopub.status.idle":"2021-06-10T10:10:03.226016Z","shell.execute_reply.started":"2021-06-10T10:07:34.411408Z","shell.execute_reply":"2021-06-10T10:10:03.224849Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"vocab = to_vocab(sentences)\noov = check_coverage(vocab, embeddings_index)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:10:03.227742Z","iopub.execute_input":"2021-06-10T10:10:03.228182Z","iopub.status.idle":"2021-06-10T10:10:11.692086Z","shell.execute_reply.started":"2021-06-10T10:10:03.228133Z","shell.execute_reply":"2021-06-10T10:10:11.691155Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"oov.most_common(20)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:10:11.693333Z","iopub.execute_input":"2021-06-10T10:10:11.693629Z","iopub.status.idle":"2021-06-10T10:10:11.720105Z","shell.execute_reply.started":"2021-06-10T10:10:11.693597Z","shell.execute_reply":"2021-06-10T10:10:11.718636Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### b. Xử lý dữ liệu bị thiếu\n\n**Problem**\n\nCần thay thế những từ OOV trong bộ câu hỏi thành những từ có cùng ý nghĩa ở trong bộ Embeddings.\n\n**Solution**\n\nMột số phương pháp có thể được áp dụng:\n\n* Trước tên ta thấy có những từ được viết bằng tiếng Anh-Anh, ta có thể chuyển sang từ Anh-Mỹ (Ví dụ: chuyển **favourite** thành **favorite**).\n* Tiếp theo, ta cũng thấy có những từ đã bị chia thì làm cho nó mất đi ý nghĩa gốc của nó, ta sẽ phải chuyển nó về dạng từ nguyên thể (Ví dụ: chuyển **travelling** thành **travel**)\n* Thêm nữa, sẽ có những từ được viết tắt, ta có thể viết nó ở dạng đầy đủ (Ví dụ: chuyển **Paytm** thành **Pay Through Mobile**)\n* Cuối cùng, với những từ không có trong tập Embeddings, ta có thể quy nó về một từ mang ý nghĩa tương tự và xuất hiện trong tập Embeddings (Ví dụ: chuyển **Snapchat** thành **socialmedia**)\n\nBước này, ta phải lập một bộ từ điển bằng cách in ra top những từ phổ biến nhất trong tập OOV, chọn những từ mà ta có thể \nthay thế được, loại bỏ nó ra khỏi câu rồi lặp đi lặp lại bước trên cho đến khi độ phủ của Embeddings bị chững lại và không còn tăng lên nữa, khi đó ta sẽ xây dựng được bộ từ điển tốt nhất.\n\n**Discuss**\n\nTa cũng thấy top 4 từ phổ biến nhất trong tập OOV là [to, a , of, an], đây là những từ không mang giá trị khi phân loại, ta có thể bỏ luôn 4 từ này đi.\n\nSau khi hoàn thành xử lý dữ liệu bị thiếu, ta thấy độ phủ cũng đã đạt được kết quả khá tốt.","metadata":{}},{"cell_type":"code","source":"print(\"'favourite' in embeddings_index:  \", 'favourite' in embeddings_index)\nprint(\"'favorite' in embeddings_index:    \", 'favorite' in embeddings_index)\nprint('----------------------------------------')\nprint(\"'travelling' in embeddings_index: \", 'travelling' in embeddings_index)\nprint(\"'travel' in embeddings_index:      \", 'travel' in embeddings_index)\nprint('----------------------------------------')\nprint(\"'Snapchat' in embeddings_index:   \", 'Snapchat' in embeddings_index)\nprint(\"'socialmedia' in embeddings_index: \", 'socialmedia' in embeddings_index)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:10:11.721734Z","iopub.execute_input":"2021-06-10T10:10:11.722054Z","iopub.status.idle":"2021-06-10T10:10:11.731546Z","shell.execute_reply.started":"2021-06-10T10:10:11.722024Z","shell.execute_reply":"2021-06-10T10:10:11.730471Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"confuse_dict = {\n    'grey': 'gray',\n    'litre': 'liter',\n    'labour': 'labor',\n    'favour': 'favor',\n    'colour': 'color',\n    'centre': 'center',\n    'honours': 'honor',\n    'theatre': 'theater',\n    'realise': 'realize',\n    'defence': 'defense',\n    'licence': 'license',\n    'analyse': 'analyze',\n    'practise': 'practice',\n    'behaviour': 'behavior',\n    'neighbour': 'neighbor',\n    'recognise': 'recognize',\n    'organisation':'organization',\n    \n    'Qoura': 'Quora',\n    'quora': 'Quora',\n    'Quorans': 'Quoran',\n    'infty': 'infinity',\n    'judgement': 'judge',\n    \n    'isnt': 'is not',\n    'didnt': 'did not',\n    'Whatis': 'what is',\n    'doesnt': 'does not',\n    \n    'learnt': 'learn',\n    'modelling': 'model',\n    'cancelled': 'cancel',\n    'travelled': 'travell',\n    'travelling': 'travel',\n    'aluminium': 'alumini',\n    'counselling':'counseling',\n    \n    'cheque': 'bill',\n    'upvote': 'agree',\n    'upvotes': 'agree',\n    'vape': 'cigarette',\n    'jewellery': 'jewell',\n    'Fiverr': 'freelance',\n    'programd': 'program',\n    'programme': 'program',\n    'programr': 'programer',\n    'programrs': 'programer',\n    'WeChat': 'socialmedia',\n    'Snapchat': 'socialmedia',\n    'Redmi': 'cellphone',\n    'Xiaomi': 'cellphone',\n    'OnePlus': 'cellphone',\n    'cryptos': 'crypto',\n    'bitcoin': 'crypto',\n    'Coinbase': 'crypto',\n    'bitcoins': 'crypto',\n    'ethereum': 'crypto',\n    'Ethereum': 'crypto',\n    'Blockchain': 'crypto',\n    'blockchain': 'crypto',\n    'cryptocurrency': 'crypto',\n    'cryptocurrencies': 'crypto',\n\n    '₹': 'rupee',\n    'Brexit': 'Britain exit',\n    'Paytm': 'Pay Through Mobile',\n    'KVPY': 'Kishore Vaigyanik Protsahan Yojana',\n    'GDPR': 'General Data Protection Regulation',\n    'INTJ': 'Introversion Intuition Thinking Judgment',\n}\n\ndef _get_confuse(confuse_dict):\n    confuse_re = re.compile('(%s)' % '|'.join(confuse_dict.keys()))\n    return confuse_dict, confuse_re\n\ndef replace_confuse(text):\n    confuse, confuse_re = _get_confuse(confuse_dict)\n    def replace(match):\n        return confuse[match.group(0)]\n    return confuse_re.sub(replace, text)\n\ndef replace_useless(text):\n    text_list = text.split()\n    text_list = [text for text in text_list if text not in ['a', 'to', 'of', 'and']]\n    return \" \".join(text_list)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:10:11.733578Z","iopub.execute_input":"2021-06-10T10:10:11.734024Z","iopub.status.idle":"2021-06-10T10:10:11.834281Z","shell.execute_reply.started":"2021-06-10T10:10:11.733981Z","shell.execute_reply":"2021-06-10T10:10:11.832979Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sentences = [replace_useless(x) for x in tqdm(sentences, position=0)]\nsentences = [replace_confuse(x) for x in tqdm(sentences, position=0)]\nvocab = to_vocab(sentences)\noov = check_coverage(vocab, embeddings_index)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:10:11.839214Z","iopub.execute_input":"2021-06-10T10:10:11.839687Z","iopub.status.idle":"2021-06-10T10:10:53.1782Z","shell.execute_reply.started":"2021-06-10T10:10:11.839617Z","shell.execute_reply":"2021-06-10T10:10:53.177317Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **III. Huấn luyện mô hình**","metadata":{"execution":{"iopub.status.busy":"2021-06-09T07:25:29.321659Z","iopub.execute_input":"2021-06-09T07:25:29.322117Z","iopub.status.idle":"2021-06-09T07:25:29.326241Z","shell.execute_reply.started":"2021-06-09T07:25:29.322049Z","shell.execute_reply":"2021-06-09T07:25:29.325034Z"}}},{"cell_type":"markdown","source":"# 1. Data Generator\n\n**Problem**\n\nBộ dữ liệu quá lớn, nếu train model cùng một lúc sẽ dẫn đến việc tràn bộ nhớ và làm cho notebook phải khởi động lại.\n\n**Solution**\n\nTa cần chia nhỏ tập train thành từng batch để huấn luyện dần. Batch_size càng lớn càng tốn tài nguyên tính toán trong quá trình huấn luyện. Ta sẽ lấy ngẫu nhiên và không lặp lại batch_size bộ dữ liệu từ tập huấn luyện.\n\nTrong mỗi batch sẽ chứa 2 trường thông tin:\n* Features vectors: là vector Embeddings của bộ câu hỏi\n* Target: là list mang giá trị 0 hoặc 1 thể hiện câu hỏi có toxic hay không.\n\n**Discuss**\n\nTrước khi đưa về dạng vector các câu cần được chuẩn hóa về độ dài. Việc thay đổi độ dài cũng sẽ ảnh hưởng khá lớn đến kết quả bởi nếu số lượng từ nhỏ sẽ có quá nhiều từ trong tập mẫu sẽ không có trong từ điển, còn số lượng từ quá nhiều sẽ làm độ phức tạp tính toán sẽ tăng lên. \n\nTừ histogram về số lượng các từ có trong câu mà ta đã có ở bước trước, nhận thấy chỉ cần lấy 30 từ đầu tiên của câu hỏi cũng có thể mang lại một kết quả tốt. \n\n\n","metadata":{}},{"cell_type":"code","source":"SEQ_LEN = 30\nbatch_size = 512\nempyt_emb = np.zeros(len(embeddings_index['random']))\n\ndef text_to_array(text):\n    text = ' '.join(text.split()[:SEQ_LEN])\n    text = text.split()\n    embeds = [embeddings_index[x] for x in text if x in embeddings_index]\n    embeds += [empyt_emb] * (SEQ_LEN - len(embeds))\n    return np.array(embeds, dtype=float)\n\ndef batch_gen(train_df):\n    n_batches = math.ceil(len(train_df) / batch_size)\n    while True: \n        train_df = train_df.sample(frac=1.) \n        for i in range(n_batches):\n            texts = train_df.iloc[i * batch_size: (i + 1) * batch_size, 1]\n            text_arr = np.array([text_to_array(text) for text in texts])\n            yield text_arr, np.array(train_df[\"target\"][i * batch_size:(i + 1) * batch_size])","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:10:53.179534Z","iopub.execute_input":"2021-06-10T10:10:53.179828Z","iopub.status.idle":"2021-06-10T10:10:53.190536Z","shell.execute_reply.started":"2021-06-10T10:10:53.1798Z","shell.execute_reply":"2021-06-10T10:10:53.18934Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"val_size = 10000\nval_vects = np.array([\n    text_to_array(X_text) for X_text in tqdm(\n        val_dataframe[\"question_text\"][:val_size],\n        position=0\n    )\n], dtype=float)\nval_y = np.array(val_dataframe[\"target\"][:val_size], dtype='int32')\n\ntrain_data = batch_gen(train_dataframe)\nvalidation_data=(val_vects, val_y)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:10:53.192305Z","iopub.execute_input":"2021-06-10T10:10:53.192857Z","iopub.status.idle":"2021-06-10T10:10:54.686602Z","shell.execute_reply.started":"2021-06-10T10:10:53.192813Z","shell.execute_reply":"2021-06-10T10:10:54.685416Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. Xây dựng mô hình\n\n**Problem**\n\nXây dựng mô hình để huấn luyện học máy.\n\n**Solution**\n\nVới những bài toán thuộc dạng Sequence Classification, ta có thể sử dụng mạng long short-term memory (LSTM) để xử lý.\n\nMô hình bao gồm:\n* Layer LSTM với 128 units, cho phép thông tin từ input trước được sử dụng trong tương lai.\n* Bởi vì đây là bài toán binary classification nên ta sẽ thêm output layer với 1 unit và 1 sigmoid activation function.\n\nCompile model với loss function *binary_crossentropy* và thuật toán *Adam optimization*.\n\nMột khi model đã được compile, nó có thể được *fit*.\n\n**Discuss**\n\nMột trong những điểm yếu lớn nhất của mô hình LSTM là giới hạn của bộ nhớ. Với việc tạo ra batch generate ở bước trước, ta đã xử lý được vấn đề này.\n\nTrong quá trình *fit*, mỗi **epoch** có thể được phân vùng thành các cặp pattern input-output được gọi là **batch**. Việc này xác định số lượng pattern mà mạng sẽ tiếp xúc trước khi trọng số được cập nhập trong **epoch**. Và nó cũng sẽ xử lý được vấn đề quá nhiều pattern được load vào bộ nhớ cùng 1 lúc.","metadata":{}},{"cell_type":"code","source":"# Start neural network\nmodel = Sequential()\n\n# Add a long short-term memory layer with 128 units\nmodel.add(LSTM(units=128))\n\n# Add fully connected layer with a sigmoid activation function\nmodel.add(Dense(1, activation=\"sigmoid\"))\n\n# Compile neural network\nmodel.compile(loss='binary_crossentropy', # Cross-entropy\n              optimizer='adam', # Adam optimization\n              metrics=['accuracy']) # Accuracy performance metric","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:10:54.687788Z","iopub.execute_input":"2021-06-10T10:10:54.688076Z","iopub.status.idle":"2021-06-10T10:10:54.991812Z","shell.execute_reply.started":"2021-06-10T10:10:54.688049Z","shell.execute_reply":"2021-06-10T10:10:54.990851Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Train neural network\nhistory = model.fit(train_data,\n                    epochs=20,\n                    steps_per_epoch=1000, \n                    validation_data=validation_data,\n                    verbose=True)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:15:30.401867Z","iopub.execute_input":"2021-06-10T10:15:30.402323Z","iopub.status.idle":"2021-06-10T10:17:02.4673Z","shell.execute_reply.started":"2021-06-10T10:15:30.402282Z","shell.execute_reply":"2021-06-10T10:17:02.466176Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3. Visulize accuracy và loss của mô hình","metadata":{"execution":{"iopub.status.busy":"2021-06-09T07:52:20.00808Z","iopub.execute_input":"2021-06-09T07:52:20.008761Z","iopub.status.idle":"2021-06-09T07:52:20.015778Z","shell.execute_reply.started":"2021-06-09T07:52:20.008722Z","shell.execute_reply":"2021-06-09T07:52:20.013903Z"}}},{"cell_type":"code","source":"# Get training and test loss histories\ntraining_loss = history.history[\"loss\"]\ntest_loss = history.history[\"val_loss\"]\n# Create count of the number of epochs\nepoch_count = range(1, len(training_loss) + 1)\n# Visualize loss history\nplt.plot(epoch_count, training_loss, \"r--\")\nplt.plot(epoch_count, test_loss, \"b-\")\nplt.legend([\"Training Loss\", \"Test Loss\"])\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.show()\n\n# Get training and test accuracy histories\ntraining_accuracy = history.history[\"accuracy\"]\ntest_accuracy = history.history[\"val_accuracy\"]\nplt.plot(epoch_count, training_accuracy, \"r--\")\nplt.plot(epoch_count, test_accuracy, \"b-\")\n# Visualize accuracy history\nplt.legend([\"Training Accuracy\", \"Test Accuracy\"])\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy Score\")\nplt.show();","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:17:02.46926Z","iopub.execute_input":"2021-06-10T10:17:02.469599Z","iopub.status.idle":"2021-06-10T10:17:02.859171Z","shell.execute_reply.started":"2021-06-10T10:17:02.469566Z","shell.execute_reply":"2021-06-10T10:17:02.857953Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 4. Dự đoán trong bộ dữ liệu test\n\n**Problem**\n\nSử dụng mô hình đã xây dựng được để dự đoán trong bộ test dataframe.\n\n**Solution**\n\nTìm ra một giá trị threshold sẽ mang lại giá trị F1 Score tốt nhất. Sau đó sử dụng giá trị threshold tìm được đó để dự đoán tập dữ liệu.\n\n**Discuss**\n\nTương tự với việc train model, ở đây ta cũng cần tạo ra các batch chứa dữ liệu để tránh việc tràn bộ nhớ.","metadata":{}},{"cell_type":"code","source":"pred_val_y = model.predict(val_vects, verbose=False)\n\nbest_thres = 0\nbest_thres_id = 0\n\nfor thres in np.arange(0.1, 0.901, 0.01):\n    thres = np.round(thres, 2)\n    value = metrics.f1_score(val_y, (pred_val_y>thres).astype(int))\n    if value > best_thres:\n        best_thres = value\n        best_thres_id = thres\n        \nprint(\"The best F1 score is {0} at threshold {1}\".format(best_thres, best_thres_id))","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:11:17.816176Z","iopub.execute_input":"2021-06-10T10:11:17.816509Z","iopub.status.idle":"2021-06-10T10:11:23.349128Z","shell.execute_reply.started":"2021-06-10T10:11:17.816473Z","shell.execute_reply":"2021-06-10T10:11:23.347912Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def batch_gen_for_submit(test_df):\n    n_batches = math.ceil(len(test_df) / batch_size)\n    for i in range(n_batches):\n        texts = test_df.iloc[i * batch_size: (i + 1) * batch_size, 1]\n        text_arr = [text_to_array(text) for text in texts]\n        yield np.array(text_arr, dtype=float)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:11:23.350384Z","iopub.execute_input":"2021-06-10T10:11:23.350887Z","iopub.status.idle":"2021-06-10T10:11:23.355842Z","shell.execute_reply.started":"2021-06-10T10:11:23.350854Z","shell.execute_reply":"2021-06-10T10:11:23.355126Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"all_preds = []\nfor x in tqdm(batch_gen_for_submit(test_dataframe)):\n    cc = model.predict(x, verbose=False)\n    cc = (cc > best_thres_id).astype(int)\n    for i in cc:\n        all_preds.append(i[0])","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:11:23.35692Z","iopub.execute_input":"2021-06-10T10:11:23.357363Z","iopub.status.idle":"2021-06-10T10:12:08.41663Z","shell.execute_reply.started":"2021-06-10T10:11:23.357333Z","shell.execute_reply":"2021-06-10T10:12:08.414587Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submit_df = pd.DataFrame({\"qid\": test_dataframe[\"qid\"], \"prediction\": all_preds})\nsubmit_df.head(5)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:12:08.418147Z","iopub.status.idle":"2021-06-10T10:12:08.419045Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submit_df.to_csv(\"submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2021-06-10T10:12:08.42047Z","iopub.status.idle":"2021-06-10T10:12:08.421337Z"},"trusted":true},"execution_count":null,"outputs":[]}]}